Family Home Lab: portal, dsh (chat+plugins), transcriber, music/media tools, home dash
This commit is contained in:
71
portal/templates/partials/_sections.html
Normal file
71
portal/templates/partials/_sections.html
Normal file
@@ -0,0 +1,71 @@
|
||||
{# Shared tool grid. Renders the home-dash sections + a client-side search filter.
|
||||
NOTE: no HTMX auto-poll here — re-swapping the whole grid every 30s made cards
|
||||
vanish on scroll and wiped the typed search. Status dots are static per catalogue. #}
|
||||
<div id="tool-grids">
|
||||
<div class="dash-search">
|
||||
<input class="text-input" type="search" id="dashFilter"
|
||||
placeholder="Search the home dash…"
|
||||
data-filter-input
|
||||
oninput="window.__dashFilter=this.value;applyDashFilter()">
|
||||
</div>
|
||||
{% for section in sections %}
|
||||
<section class="section" data-dash-section>
|
||||
<div class="section-head">
|
||||
<span class="category-pill">
|
||||
<span class="pill-dot" style="background:{{ section.color }}"></span>
|
||||
{{ section.label }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="tool-grid">
|
||||
{% for tool in section.tools %}
|
||||
<a class="tool-card" href="/tool/{{ tool.id }}" data-dash-card>
|
||||
<div class="tool-card-top">
|
||||
<span class="tool-icon" style="background:{{ section.color }}">{{ tool.name[:1]|upper }}</span>
|
||||
<span class="status-dot status-{{ tool.status }}"></span>
|
||||
</div>
|
||||
<h3 class="tool-name">{{ tool.name }}</h3>
|
||||
<p class="tool-desc">{{ tool.description }}</p>
|
||||
<div class="tool-card-bottom">
|
||||
<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
||||
<span class="launch-chip">Open in tab</span>
|
||||
{% if tool.lan %}<span class="tag-chip" title="Only reachable on your home network">LAN</span>{% endif %}
|
||||
{% if tool.login %}<span class="tag-chip tag-chip-login" title="This app has its own login — Family Home Lab auth is not passed through">Login</span>{% endif %}
|
||||
{% if tool.admin %}<span class="tag-chip tag-chip-admin" title="Admins only">Admin</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
<p id="dashNoResults" class="faint" style="display:none;margin-top:var(--space-lg)">
|
||||
No tools match "<span id="dashNoResultsQ"></span>".
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
function apply() {
|
||||
var q = (window.__dashFilter || "").trim().toLowerCase();
|
||||
var any = false;
|
||||
document.querySelectorAll("[data-dash-section]").forEach(function (sec) {
|
||||
var vis = false;
|
||||
sec.querySelectorAll("[data-dash-card]").forEach(function (card) {
|
||||
var hit = !q || (card.textContent || "").toLowerCase().indexOf(q) !== -1;
|
||||
card.classList.toggle("hidden", !hit);
|
||||
if (hit) vis = true;
|
||||
});
|
||||
sec.classList.toggle("hidden-section", !vis);
|
||||
if (vis) any = true;
|
||||
});
|
||||
var noRes = document.getElementById("dashNoResults");
|
||||
var q2 = document.getElementById("dashNoResultsQ");
|
||||
if (noRes) noRes.style.display = (q && !any) ? "block" : "none";
|
||||
if (q2 && q) q2.textContent = window.__dashFilter;
|
||||
}
|
||||
window.applyDashFilter = apply;
|
||||
window.__dashFilter = window.__dashFilter || "";
|
||||
var inp = document.querySelector("[data-filter-input]");
|
||||
if (inp) inp.value = window.__dashFilter;
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user