diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-07-28 22:22:12 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-07-28 22:25:37 +0200 |
| commit | 7e5535f9834fca2594c5bade425ecee00c32bdb8 (patch) | |
| tree | ef0d22ef1e3926250e96e28c6add377593e43add /src | |
| parent | 7d55b5d859e8339205494f12f4b34b6e44a44f50 (diff) | |
feat: autocomplete search with datalist to prevent duplicate items
Diffstat (limited to 'src')
| -rw-r--r-- | src/routes/+page.server.ts | 10 | ||||
| -rw-r--r-- | src/routes/+page.svelte | 28 | ||||
| -rw-r--r-- | src/routes/catalog/+page.svelte | 3 |
3 files changed, 33 insertions, 8 deletions
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index b0ddff0..1e6f60b 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,6 +1,7 @@ import { fail, redirect, error } from '@sveltejs/kit'; import { loadUnchecked, + loadAll, toggle, addOrUncheck, } from '$lib/server/services/groceries'; @@ -16,10 +17,17 @@ import type { Actions, PageServerLoad } from './$types'; export interface HomeOutput { items: EnrichedGrocery[]; categories: Category[]; + catalog: EnrichedGrocery[]; } export const load: PageServerLoad = async () => { - return loadUnchecked(); + const unchecked = await loadUnchecked(); + const all = await loadAll(); + return { + items: unchecked.items, + categories: unchecked.categories, + catalog: all.items, + }; }; export const actions: Actions = { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e4f78c5..a151a9b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -9,7 +9,6 @@ // eslint-disable-next-line svelte/prefer-writable-derived let localItems = $state(data?.items ?? []); let addError = $state(''); - // Sync local list with server data when it changes externally (toggle, navigation) $effect(() => { localItems = data?.items ?? []; @@ -81,13 +80,29 @@ bind:value={name} placeholder="Ajouter un article..." autocomplete="off" + list="item-suggestions" onkeydown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAdd(false); } }} + oninput={(e) => { + const val = e.currentTarget.value.trim().toLowerCase(); + const match = data.catalog.find( + (item) => item.name.toLowerCase() === val, + ); + if (match) { + name = match.name; + handleAdd(false); + } + }} /> + <datalist id="item-suggestions"> + {#each data.catalog as item (item.id)} + <option value={item.name}></option> + {/each} + </datalist> <button type="button" aria-label="Ajouter" onclick={() => handleAdd(false)} >+</button > @@ -123,7 +138,8 @@ <span class="best-ref" >{item.bestRef.reference} {#if item.bestRef.price && item.bestRef.unit} - {item.bestRef.price.toFixed(2)} {item.bestRef.unit} + {item.bestRef.price.toFixed(2)} + {item.bestRef.unit} {/if} {#if item.bestRef.qualityScore != null} ({item.bestRef.qualityScore}/100) @@ -175,25 +191,25 @@ transition: all 0.2s ease; } - .add-bar button:nth-child(2) { + .add-bar > button:nth-of-type(1) { background: var(--accent); color: var(--white); box-shadow: 0 1px 2px var(--shadow); } - .add-bar button:nth-child(2):hover { + .add-bar > button:nth-of-type(1):hover { background: var(--accent-hover); transform: translateY(-1px); box-shadow: 0 4px 8px var(--shadow-accent-lg); } - .add-bar button:nth-child(3) { + .add-bar > button:nth-of-type(2) { background: var(--surface); color: var(--text-secondary); border: 1.5px solid var(--border); } - .add-bar button:nth-child(3):hover { + .add-bar > button:nth-of-type(2):hover { border-color: var(--accent); color: var(--text); } diff --git a/src/routes/catalog/+page.svelte b/src/routes/catalog/+page.svelte index 525c2ed..7d3ebbe 100644 --- a/src/routes/catalog/+page.svelte +++ b/src/routes/catalog/+page.svelte @@ -38,7 +38,8 @@ <span class="best-ref" >{item.bestRef.reference} {#if item.bestRef.price && item.bestRef.unit} - {item.bestRef.price.toFixed(2)} {item.bestRef.unit} + {item.bestRef.price.toFixed(2)} + {item.bestRef.unit} {/if} {#if item.bestRef.qualityScore != null} ({item.bestRef.qualityScore}/100) |
