From 2a1fb1eeceb629c188de5477b09b384fd364c6fe Mon Sep 17 00:00:00 2001 From: vikingowl Date: Sat, 18 Apr 2026 07:54:18 +0200 Subject: [PATCH] feat(web): admin discovery review queue with accept/reject --- .../routes/admin/discovery/+page.server.ts | 76 +++++++++++++++ web/src/routes/admin/discovery/+page.svelte | 96 +++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 web/src/routes/admin/discovery/+page.server.ts create mode 100644 web/src/routes/admin/discovery/+page.svelte diff --git a/web/src/routes/admin/discovery/+page.server.ts b/web/src/routes/admin/discovery/+page.server.ts new file mode 100644 index 0000000..cf0427e --- /dev/null +++ b/web/src/routes/admin/discovery/+page.server.ts @@ -0,0 +1,76 @@ +import type { PageServerLoad, Actions } from './$types.js'; +import { redirect, fail } from '@sveltejs/kit'; +import { serverFetch } from '$lib/api/client.server.js'; + +type DiscoveredMarket = { + id: string; + markt_name: string; + stadt: string; + bundesland: string; + land: string; + start_datum: string | null; + end_datum: string | null; + website: string; + quellen: string[]; + extraktion: string; + hinweis: string; + matched_series_id: string | null; + discovered_at: string; +}; + +export const load: PageServerLoad = async ({ cookies, url }) => { + const limit = Number(url.searchParams.get('limit') ?? 50); + const offset = Number(url.searchParams.get('offset') ?? 0); + const res = await serverFetch( + `/admin/discovery/queue?limit=${limit}&offset=${offset}`, + cookies + ); + return { queue: res.data, limit, offset }; +}; + +export const actions: Actions = { + accept: async ({ request, cookies, fetch }) => { + const form = await request.formData(); + const id = String(form.get('id') ?? ''); + if (!id) return fail(400, { error: 'missing id' }); + + try { + const res = await serverFetch<{ series_id: string; edition_id: string }>( + `/admin/discovery/queue/${id}/accept`, + cookies, + { method: 'POST', fetch } + ); + // Fire research asynchronously — don't await, the edit page handles streaming suggestions. + void serverFetch(`/admin/markets/${res.data.edition_id}/research`, cookies, { + method: 'POST', + fetch + }).catch(() => { + // Silent: the edit page shows a toast if research hasn't completed. + }); + redirect(303, `/admin/maerkte/${res.data.edition_id}/edit`); + } catch (err) { + if (err instanceof Response || (err as { status?: number })?.status === 303) throw err; + const message = err instanceof Error ? err.message : 'Accept fehlgeschlagen.'; + return fail(500, { error: message }); + } + }, + + reject: async ({ request, cookies, fetch }) => { + const form = await request.formData(); + const id = String(form.get('id') ?? ''); + const reason = String(form.get('reason') ?? ''); + if (!id) return fail(400, { error: 'missing id' }); + + try { + await serverFetch(`/admin/discovery/queue/${id}/reject`, cookies, { + method: 'POST', + body: JSON.stringify({ reason }), + fetch + }); + return { success: true }; + } catch (err) { + const message = err instanceof Error ? err.message : 'Reject fehlgeschlagen.'; + return fail(500, { error: message }); + } + } +}; diff --git a/web/src/routes/admin/discovery/+page.svelte b/web/src/routes/admin/discovery/+page.svelte new file mode 100644 index 0000000..85622de --- /dev/null +++ b/web/src/routes/admin/discovery/+page.svelte @@ -0,0 +1,96 @@ + + + + Admin · Discovery Queue + + +
+

Discovery Queue

+

+ {data.queue.length} pending · showing from offset {data.offset} +

+ + {#if data.queue.length === 0} +

+ Keine Einträge in der Warteschlange. +

+ {:else} + + + + + + + + + + + + + + + + {#each data.queue as row (row.id)} + + + + + + + + + + + + {/each} + +
LandRegionMarktStadtDatumWebsiteQuellenExtraktionAktion
{row.land}{row.bundesland}{row.markt_name}{row.stadt} + {#if row.start_datum} + {row.start_datum}{row.end_datum ? ` – ${row.end_datum}` : ''} + {:else} + + {/if} + + {#if row.website} + link + {:else} + + {/if} + {row.quellen?.length ?? 0} + + {row.extraktion || '—'} + + +
+ + +
+
+ + +
+
+ {/if} +