From 2e6acceb331073acdeba1f4313a10f13f0af2edb Mon Sep 17 00:00:00 2001 From: vikingowl Date: Fri, 27 Feb 2026 11:04:31 +0100 Subject: [PATCH] feat: add admin panel, market submission form, and legal updates - Admin layout with auth guard at /admin - Admin market list with status filter tabs (pending/approved/rejected) - Admin market detail/review with approve/reject actions - Admin market create and edit forms with shared MarketForm component - Anonymous market submission form at /markt/einreichen with Turnstile - Optional latitude/longitude fields on submission form - Admin and submission types added to API types - requireAdmin guard for role-based frontend access - Header/mobile nav updated with admin and submission links - Auth layout redirect removed to re-enable login flow - Login form action renamed to fix named actions conflict - Impressum updated with user-submitted content section - Datenschutz updated with submission form and Turnstile sections --- web/.env.example | 3 + web/src/lib/api/types.ts | 62 +++++ web/src/lib/auth/guard.ts | 11 +- .../lib/components/admin/MarketForm.svelte | 139 ++++++++++ web/src/lib/components/auth/LoginForm.svelte | 2 +- web/src/lib/components/layout/Header.svelte | 8 + .../lib/components/layout/MobileNav.svelte | 18 +- web/src/routes/admin/+layout.server.ts | 6 + web/src/routes/admin/+layout.svelte | 47 ++++ web/src/routes/admin/maerkte/+page.server.ts | 19 ++ web/src/routes/admin/maerkte/+page.svelte | 153 +++++++++++ .../routes/admin/maerkte/[id]/+page.server.ts | 48 ++++ .../routes/admin/maerkte/[id]/+page.svelte | 237 ++++++++++++++++++ .../maerkte/[id]/bearbeiten/+page.server.ts | 58 +++++ .../maerkte/[id]/bearbeiten/+page.svelte | 36 +++ .../routes/admin/maerkte/neu/+page.server.ts | 44 ++++ web/src/routes/admin/maerkte/neu/+page.svelte | 36 +++ web/src/routes/auth/+layout.server.ts | 4 +- web/src/routes/auth/anmelden/+page.server.ts | 2 +- web/src/routes/datenschutz/+page.svelte | 58 ++++- web/src/routes/impressum/+page.svelte | 12 + .../routes/markt/einreichen/+page.server.ts | 88 +++++++ web/src/routes/markt/einreichen/+page.svelte | 214 ++++++++++++++++ 23 files changed, 1292 insertions(+), 13 deletions(-) create mode 100644 web/src/lib/components/admin/MarketForm.svelte create mode 100644 web/src/routes/admin/+layout.server.ts create mode 100644 web/src/routes/admin/+layout.svelte create mode 100644 web/src/routes/admin/maerkte/+page.server.ts create mode 100644 web/src/routes/admin/maerkte/+page.svelte create mode 100644 web/src/routes/admin/maerkte/[id]/+page.server.ts create mode 100644 web/src/routes/admin/maerkte/[id]/+page.svelte create mode 100644 web/src/routes/admin/maerkte/[id]/bearbeiten/+page.server.ts create mode 100644 web/src/routes/admin/maerkte/[id]/bearbeiten/+page.svelte create mode 100644 web/src/routes/admin/maerkte/neu/+page.server.ts create mode 100644 web/src/routes/admin/maerkte/neu/+page.svelte create mode 100644 web/src/routes/markt/einreichen/+page.server.ts create mode 100644 web/src/routes/markt/einreichen/+page.svelte diff --git a/web/.env.example b/web/.env.example index 0cda0f2..b6bc592 100644 --- a/web/.env.example +++ b/web/.env.example @@ -1 +1,4 @@ PUBLIC_API_BASE_URL=http://localhost:8080 + +# Cloudflare Turnstile (site key - public, safe to expose) +PUBLIC_TURNSTILE_SITE_KEY= diff --git a/web/src/lib/api/types.ts b/web/src/lib/api/types.ts index e927936..a1ec59f 100644 --- a/web/src/lib/api/types.ts +++ b/web/src/lib/api/types.ts @@ -97,6 +97,68 @@ export interface ProfileData { created_at: string; } +// Admin types +export type MarketStatus = 'pending' | 'approved' | 'rejected'; + +export interface AdminMarketSummary { + id: string; + slug: string; + name: string; + city: string; + state: string; + status: MarketStatus; + start_date: string; + end_date: string; + organizer_name: string; + submitter_name: string; + created_at: string; +} + +export interface AdminMarketDetail { + id: string; + slug: string; + name: string; + description: string; + street: string; + city: string; + state: string; + zip: string; + country: string; + latitude: number; + longitude: number; + start_date: string; + end_date: string; + opening_hours: OpeningHoursEntry[] | null; + admission_info: AdmissionInfo | null; + website: string; + organizer_name: string; + image_url: string; + status: MarketStatus; + submitter_email?: string; + submitter_name: string; + admin_notes: string; + reviewed_at?: string; + reviewed_by?: string; + created_at: string; + updated_at: string; +} + +export interface SubmitMarketRequest { + name: string; + description: string; + city: string; + state: string; + zip: string; + country: string; + start_date: string; + end_date: string; + website: string; + organizer_name: string; + submitter_email: string; + submitter_name: string; + turnstile_token: string; +} + // Search params (mirrors backend SearchParams) export interface MarketSearchParams { lat?: number; diff --git a/web/src/lib/auth/guard.ts b/web/src/lib/auth/guard.ts index 3abcb19..84e6003 100644 --- a/web/src/lib/auth/guard.ts +++ b/web/src/lib/auth/guard.ts @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit'; +import { error, redirect } from '@sveltejs/kit'; import type { ServerLoadEvent } from '@sveltejs/kit'; export function requireAuth(event: ServerLoadEvent): void { @@ -6,3 +6,12 @@ export function requireAuth(event: ServerLoadEvent): void { redirect(302, `/auth/anmelden?redirect=${encodeURIComponent(event.url.pathname)}`); } } + +export function requireAdmin(event: ServerLoadEvent): void { + if (!event.locals.user) { + redirect(302, `/auth/anmelden?redirect=${encodeURIComponent(event.url.pathname)}`); + } + if (event.locals.user.role !== 'admin') { + error(403, 'Zugriff verweigert'); + } +} diff --git a/web/src/lib/components/admin/MarketForm.svelte b/web/src/lib/components/admin/MarketForm.svelte new file mode 100644 index 0000000..aca21dd --- /dev/null +++ b/web/src/lib/components/admin/MarketForm.svelte @@ -0,0 +1,139 @@ + + +{#if error} + +{/if} + +
+
+ Allgemein + + + +
+ + +
+
+ +
+ Standort + + + +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+
+ +
+ Zeitraum + +
+ + +
+
+ +
+ Weitere Infos + + + + + + +
+ +
+ + + + +
+
diff --git a/web/src/lib/components/auth/LoginForm.svelte b/web/src/lib/components/auth/LoginForm.svelte index a44778f..a372e6a 100644 --- a/web/src/lib/components/auth/LoginForm.svelte +++ b/web/src/lib/components/auth/LoginForm.svelte @@ -15,7 +15,7 @@
{ loading = true; return async ({ update }) => { diff --git a/web/src/lib/components/layout/Header.svelte b/web/src/lib/components/layout/Header.svelte index 5a53b89..2b2acc8 100644 --- a/web/src/lib/components/layout/Header.svelte +++ b/web/src/lib/components/layout/Header.svelte @@ -37,6 +37,14 @@