- {#each data.markets as market}
+ {#each data.groups as group}
+ {@const latest = group.editions[0]}
+ {@const hasMultiple = group.editions.length > 1}
+ {@const isExpanded = expandedSeries.has(group.series_id)}
+
- |
- {market.name}
+ |
+ {#if hasMultiple}
+
+ {/if}
|
- {market.city} |
+
+ {latest.name}
+ {#if hasMultiple}
+
+ ({group.editions.length} Ausgaben)
+
+ {/if}
+ |
+ {latest.city} |
- {statusLabels[market.status]}
+ {statusLabels[latest.status]}
|
-
- {formatDate(market.start_date)} - {formatDate(market.end_date)}
+ |
+ {latest.year}
|
- {formatDate(market.created_at)}
+ {formatDate(latest.start_date)} - {formatDate(latest.end_date)}
+ |
+
+ {formatDate(latest.created_at)}
|
|
+
+ {#if hasMultiple && isExpanded}
+ {#each group.editions.slice(1) as edition}
+
+ |
+
+ {edition.name}
+ |
+
+ {edition.city}
+ |
+
+
+ {statusLabels[edition.status]}
+
+ |
+
+ {edition.year}
+ |
+
+ {formatDate(edition.start_date)} - {formatDate(edition.end_date)}
+ |
+
+ {formatDate(edition.created_at)}
+ |
+
+
+ |
+
+ {/each}
+ {/if}
{:else}
- |
+ |
Keine Märkte gefunden.
|
@@ -201,7 +317,7 @@
{#if data.meta && data.meta.total_pages > 1}
- Seite {data.meta.page} von {data.meta.total_pages} ({data.meta.total} Einträge)
+ Seite {data.meta.page} von {data.meta.total_pages} ({data.meta.total} Serien)
{#if data.meta.page > 1}
diff --git a/web/src/routes/admin/maerkte/[id]/+page.server.ts b/web/src/routes/admin/maerkte/[id]/+page.server.ts
index 7b35454..0fa3bb7 100644
--- a/web/src/routes/admin/maerkte/[id]/+page.server.ts
+++ b/web/src/routes/admin/maerkte/[id]/+page.server.ts
@@ -1,6 +1,11 @@
import { fail } from '@sveltejs/kit';
import { serverFetch } from '$lib/api/client.server.js';
-import type { AdminMarketDetail, DuplicateMarket } from '$lib/api/types.js';
+import type {
+ AdminMarketDetail,
+ DuplicateMarket,
+ EditionStatus,
+ SeriesEditionsResponse
+} from '$lib/api/types.js';
import type { Actions, PageServerLoad } from './$types.js';
export const load: PageServerLoad = async ({ params, cookies }) => {
@@ -8,7 +13,7 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
const market = res.data;
let duplicates: DuplicateMarket[] = [];
- if (market.status === 'pending') {
+ if (market.status === 'rumored') {
try {
const dupRes = await serverFetch
(
`/admin/markets/${params.id}/duplicates`,
@@ -20,7 +25,25 @@ export const load: PageServerLoad = async ({ params, cookies }) => {
}
}
- return { market, duplicates };
+ // Fetch other editions for this series
+ let editions: { id: string; year: number; status: EditionStatus }[] = [];
+ try {
+ const edRes = await serverFetch(
+ `/admin/series/${market.series_id}/editions`,
+ cookies
+ );
+ // The response wraps editions inside { series, editions }
+ const raw = edRes.data as unknown as SeriesEditionsResponse;
+ editions = raw.editions.map((e) => ({
+ id: e.id,
+ year: e.year,
+ status: e.status as EditionStatus
+ }));
+ } catch {
+ // Non-fatal
+ }
+
+ return { market, duplicates, editions };
};
export const actions: Actions = {
@@ -59,5 +82,33 @@ export const actions: Actions = {
const message = err instanceof Error ? err.message : 'Loeschen fehlgeschlagen.';
return fail(500, { error: message });
}
+ },
+
+ createEdition: async ({ request, cookies, fetch }) => {
+ const form = await request.formData();
+ const seriesId = form.get('series_id')?.toString() ?? '';
+ const startDate = form.get('start_date')?.toString() ?? '';
+ const endDate = form.get('end_date')?.toString() ?? '';
+
+ if (!seriesId || !startDate || !endDate) {
+ return fail(400, { error: 'Start- und Enddatum sind erforderlich.' });
+ }
+
+ try {
+ const res = await serverFetch(
+ `/admin/series/${seriesId}/editions`,
+ cookies,
+ {
+ method: 'POST',
+ body: JSON.stringify({ start_date: startDate, end_date: endDate }),
+ fetch
+ }
+ );
+
+ return { created: true, newEditionId: res.data.id };
+ } catch (err) {
+ const message = err instanceof Error ? err.message : 'Edition erstellen fehlgeschlagen.';
+ return fail(500, { error: message });
+ }
}
};
diff --git a/web/src/routes/admin/maerkte/[id]/+page.svelte b/web/src/routes/admin/maerkte/[id]/+page.svelte
index 2db9bad..c982982 100644
--- a/web/src/routes/admin/maerkte/[id]/+page.svelte
+++ b/web/src/routes/admin/maerkte/[id]/+page.svelte
@@ -3,22 +3,29 @@
import { goto } from '$app/navigation';
import Button from '$lib/components/ui/Button.svelte';
import Alert from '$lib/components/ui/Alert.svelte';
- import type { MarketStatus } from '$lib/api/types.js';
+ import type { EditionStatus } from '$lib/api/types.js';
let { data, form } = $props();
let loading = $state(false);
+ let showNewEdition = $state(false);
- const statusLabels: Record = {
- pending: 'Ausstehend',
- approved: 'Genehmigt',
- rejected: 'Abgelehnt'
+ const statusLabels: Record = {
+ rumored: 'Ausstehend',
+ confirmed: 'Bestätigt',
+ active: 'Aktiv',
+ completed: 'Abgeschlossen',
+ cancelled: 'Abgesagt',
+ archived: 'Archiviert'
};
- const statusColors: Record = {
- pending: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200',
- approved: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
- rejected: 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200'
+ const statusColors: Record = {
+ rumored: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200',
+ confirmed: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
+ active: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
+ completed: 'bg-stone-100 text-stone-600 dark:bg-stone-800 dark:text-stone-400',
+ cancelled: 'bg-danger-100 text-danger-800 dark:bg-danger-900 dark:text-danger-200',
+ archived: 'bg-stone-100 text-stone-500 dark:bg-stone-800 dark:text-stone-500'
};
const chfCountries = new Set(['CH', 'LI']);
@@ -34,10 +41,15 @@
return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
}
+ const isReviewable = $derived(data.market.status === 'rumored');
+
$effect(() => {
if (form?.deleted) {
goto('/admin/maerkte');
}
+ if (form?.created && form?.newEditionId) {
+ goto(`/admin/maerkte/${form.newEditionId}`);
+ }
});
@@ -55,8 +67,48 @@
← Zurück zur Liste
{data.market.name}
+
+ Edition {data.market.year}
+ {#if data.market.series_name !== data.market.name}
+ · Serie: {data.market.series_name}
+ {/if}
+
+ {#if data.editions && data.editions.length > 1}
+
+
+ {data.editions.length} Editionen
+
+
+
+ {/if}
+
@@ -80,13 +132,79 @@
+ {#if showNewEdition}
+
+
+ Neue Edition für "{data.market.series_name || data.market.name}"
+
+
+
+ {/if}
+
{#if form?.error}
{form.error}
{/if}
{#if form?.success}
- Status erfolgreich auf "{form.action === 'approved' ? 'Genehmigt' : 'Abgelehnt'}" geändert.
+ Status erfolgreich auf "{form.action === 'approved' ? 'Bestätigt' : 'Abgesagt'}" geändert.
{/if}
@@ -126,7 +244,7 @@
- {#if data.market.status === 'pending'}
+ {#if isReviewable}