diff --git a/src/client/src/routes/+page.svelte b/src/client/src/routes/+page.svelte index dec6c13..9b0084e 100644 --- a/src/client/src/routes/+page.svelte +++ b/src/client/src/routes/+page.svelte @@ -183,11 +183,15 @@ : selectedStoreIds.has(store.id) ? 'text-text-primary' : 'text-text-tertiary'}">{store.name} - {#if isBroken} - failing - {:else if store.last_scrape_ok === null} - untested - {/if} + + {#if isBroken} + failing + {:else if store.last_scrape_ok === null} + untested + {:else if store.avg_duration_ms} + ~{store.avg_duration_ms}ms + {/if} + {/each} diff --git a/src/client/src/routes/results/+page.svelte b/src/client/src/routes/results/+page.svelte index 134f33a..fe8ef6d 100644 --- a/src/client/src/routes/results/+page.svelte +++ b/src/client/src/routes/results/+page.svelte @@ -123,6 +123,37 @@ excludedStores = next; } + function retryStore(storeId, storeName) { + // Mark store as searching again + storeProgress = storeProgress.map((s) => + s.id === storeId ? { ...s, status: 'searching', error: null, resultCount: 0, duration: 0 } : s + ); + + // Remove old results from this store + results = results.filter((r) => r.storeId !== storeId); + + // Re-search just this store + searchProductsStream(query, { stores: String(storeId) }, (event) => { + switch (event.type) { + case 'store_complete': + results = [...results, ...event.results]; + storeProgress = storeProgress.map((s) => + s.id === event.storeId + ? { ...s, status: 'done', resultCount: event.resultCount, duration: event.duration } + : s + ); + break; + case 'store_error': + storeProgress = storeProgress.map((s) => + s.id === event.storeId + ? { ...s, status: 'error', error: event.error, duration: event.duration } + : s + ); + break; + } + }); + } + let shareMessage = $state(''); async function handleShare() { @@ -221,14 +252,17 @@
{#each storeProgress as store} -
+ ? 'bg-emerald-500/10 border-emerald-500/20 text-emerald-400 hover:bg-emerald-500/20 cursor-pointer' + : 'bg-red-500/10 border-red-500/20 text-red-400 hover:bg-red-500/20 cursor-pointer'}"> - {#if store.status === 'searching'} @@ -254,9 +288,9 @@ {store.resultCount} products {store.duration}ms {:else} - {store.error} + {store.error} {/if} -
+ {/each}
diff --git a/src/server/models/store.ts b/src/server/models/store.ts index 25025a9..84b235a 100644 --- a/src/server/models/store.ts +++ b/src/server/models/store.ts @@ -63,7 +63,8 @@ function slugify(text: string): string { export function getAllStores(): StoreWithCategory[] { return queryAll(` - SELECT s.*, c.name as category_name, c.color as category_color + SELECT s.*, c.name as category_name, c.color as category_color, + (SELECT ROUND(AVG(duration_ms)) FROM scrape_logs WHERE store_id = s.id AND success = 1) as avg_duration_ms FROM stores s LEFT JOIN categories c ON s.category_id = c.id ORDER BY s.name