feat(ui): tag/date filters and tag chips in the meetings list (T8.3, FR-SEARCH-2)

This commit is contained in:
iamdoubz committed 2026-07-02 10:38:54 -05:00
1 parent eb17cc68e0
commit bf74a3cc92
1 file changed
+67
+67
View File
@@ -12,6 +12,28 @@
searchTimer = setTimeout(() => meetings.search(query), 250);
}
// Tag/date filters (T8.3, FR-SEARCH-2) apply to the plain list, not
// full-text search — changing one drops out of search mode so the
// filtered list is immediately visible rather than hidden behind results.
let tagFilter = $state("");
let fromFilter = $state("");
let toFilter = $state("");
function toUnix(dateStr: string): number | undefined {
if (!dateStr) return undefined;
return Math.floor(new Date(dateStr).getTime() / 1000);
}
function onFilterChange() {
query = "";
meetings.search(null);
meetings.load({
tag: tagFilter || undefined,
from: toUnix(fromFilter),
to: toUnix(toFilter),
});
}
// Unified view: full-text results (with snippets) while searching, else the
// plain list. Same underlying fields either way.
let displayItems = $derived<(MeetingListItem | SearchHit)[]>(
@@ -61,6 +83,16 @@
bind:value={query}
oninput={onSearchInput}
/>
<div class="filters">
<select aria-label="Filter by tag" bind:value={tagFilter} onchange={onFilterChange}>
<option value="">All tags</option>
{#each meetings.allTags as t (t)}
<option value={t}>{t}</option>
{/each}
</select>
<input type="date" aria-label="From date" bind:value={fromFilter} onchange={onFilterChange} />
<input type="date" aria-label="To date" bind:value={toFilter} onchange={onFilterChange} />
</div>
{#if meetings.searching}
<p class="muted">Searching…</p>
{:else if displayItems.length === 0 && meetings.loading}
@@ -91,6 +123,13 @@
{#if snippetOf(m)}
<span class="snippet">{snippetOf(m)}</span>
{/if}
{#if m.tags.length > 0}
<span class="row tags">
{#each m.tags as t (t)}
<span class="chip">{t}</span>
{/each}
</span>
{/if}
</button>
<div class="row actions">
{#if m.status === "recovering"}
@@ -138,6 +177,34 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.filters {
display: flex;
gap: 0.3rem;
margin-bottom: 0.5rem;
}
.filters select,
.filters input {
flex: 1;
min-width: 0;
padding: 0.25rem;
font-size: 0.78rem;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 5px;
}
.tags {
justify-content: flex-start;
flex-wrap: wrap;
margin-top: 0.2rem;
}
.chip {
font-size: 0.68rem;
padding: 0.05rem 0.35rem;
border-radius: 8px;
background: color-mix(in srgb, var(--muted) 15%, transparent);
color: var(--muted);
}
ul {
list-style: none;
padding: 0;