aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-07-31 14:15:15 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-07-31 14:15:15 +0200
commit59c840c21798d515bfc375bead87e690cd70900a (patch)
tree9d16a64e0fa1827183aa3df06803b049cc7cafbe /src
parent11fe0bb7bb41e453ea293b6512a9629f64892e58 (diff)
feat(catalog): add in-memory case-insensitive search filter
Adds a search input to the Catalog page that filters items by name using a simple case-insensitive substring match. Empty categories are hidden when filtering.
Diffstat (limited to 'src')
-rw-r--r--src/routes/catalog/+page.svelte126
1 files changed, 80 insertions, 46 deletions
diff --git a/src/routes/catalog/+page.svelte b/src/routes/catalog/+page.svelte
index aa02ef5..ebc1819 100644
--- a/src/routes/catalog/+page.svelte
+++ b/src/routes/catalog/+page.svelte
@@ -6,6 +6,15 @@
let catalogItems = $derived(data?.items ?? []);
let selectedIds = new SvelteSet<number>();
+ let searchQuery = $state('');
+
+ let filteredItems = $derived(
+ searchQuery.trim() === ''
+ ? catalogItems
+ : catalogItems.filter((item) =>
+ item.name.toLowerCase().includes(searchQuery.toLowerCase()),
+ ),
+ );
function toggleId(id: number) {
if (selectedIds.has(id)) selectedIds.delete(id);
@@ -18,9 +27,12 @@
const key = item.categoryName ?? 'Non catégorisé';
(groups[key] ??= []).push(item);
}
- const result: [string, typeof catalogItems][] = data.categories.map(
- (cat) => [cat.name, groups[cat.name] ?? []],
- );
+ const result: [string, typeof catalogItems][] = [];
+ for (const cat of data.categories) {
+ if (groups[cat.name]?.length > 0) {
+ result.push([cat.name, groups[cat.name]]);
+ }
+ }
if (groups['Non catégorisé']?.length > 0) {
result.unshift(['Non catégorisé', groups['Non catégorisé']]);
}
@@ -68,49 +80,60 @@
{#if catalogItems.length === 0}
<p class="empty">Aucun article</p>
{:else}
- <form method="POST" action="?/toggle" use:enhance>
- {#each groupByCategory(catalogItems) as [cat, items] (cat)}
- <h3 class="category-name">{cat}</h3>
- <ul class="item-list">
- {#each items as item (item.id)}
- <li class="item-row item-row--fixed">
- <input
- type="checkbox"
- class="item-check"
- checked={selectedIds.has(item.id)}
- onchange={() => toggleId(item.id)}
- />
- <button
- class="item-btn item-btn--grow"
- name="id"
- value={item.id}
- type="submit"
- aria-pressed={item.checked}
- >
- <span class="item-text">
- <span class:checked={item.checked}>{item.name}</span>
- {#if item.bestRef}
- <span class="best-ref"
- >{item.bestRef.reference}
- {#if item.bestRef.price && item.bestRef.unit}
- &nbsp;{item.bestRef.price.toFixed(2)}
- {item.bestRef.unit}
- {/if}
- {#if item.bestRef.qualityScore != null}
- &nbsp;({item.bestRef.qualityScore}/100)
- {/if}
- </span>
- {/if}
- </span>
- </button>
- <a class="ref-link" href="/grocery/{item.id}?from=/catalog"
- >&rarr;</a
- >
- </li>
- {/each}
- </ul>
- {/each}
- </form>
+ <input
+ type="search"
+ bind:value={searchQuery}
+ placeholder="Rechercher un article..."
+ class="search-input"
+ />
+
+ {#if filteredItems.length === 0}
+ <p class="empty">Aucun résultat</p>
+ {:else}
+ <form method="POST" action="?/toggle" use:enhance>
+ {#each groupByCategory(filteredItems) as [cat, items] (cat)}
+ <h3 class="category-name">{cat}</h3>
+ <ul class="item-list">
+ {#each items as item (item.id)}
+ <li class="item-row item-row--fixed">
+ <input
+ type="checkbox"
+ class="item-check"
+ checked={selectedIds.has(item.id)}
+ onchange={() => toggleId(item.id)}
+ />
+ <button
+ class="item-btn item-btn--grow"
+ name="id"
+ value={item.id}
+ type="submit"
+ aria-pressed={item.checked}
+ >
+ <span class="item-text">
+ <span class:checked={item.checked}>{item.name}</span>
+ {#if item.bestRef}
+ <span class="best-ref"
+ >{item.bestRef.reference}
+ {#if item.bestRef.price && item.bestRef.unit}
+ &nbsp;{item.bestRef.price.toFixed(2)}
+ {item.bestRef.unit}
+ {/if}
+ {#if item.bestRef.qualityScore != null}
+ &nbsp;({item.bestRef.qualityScore}/100)
+ {/if}
+ </span>
+ {/if}
+ </span>
+ </button>
+ <a class="ref-link" href="/grocery/{item.id}?from=/catalog"
+ >&rarr;</a
+ >
+ </li>
+ {/each}
+ </ul>
+ {/each}
+ </form>
+ {/if}
{/if}
</main>
@@ -173,4 +196,15 @@
margin-top: 1rem;
padding: 2rem;
}
+
+ .search-input {
+ width: 100%;
+ padding: 0.75rem 1rem;
+ border: 1px solid var(--border);
+ border-radius: 0.5rem;
+ background: var(--surface);
+ color: var(--text);
+ font-size: 1rem;
+ margin-top: 1rem;
+ }
</style>