feat(geschichten): add /geschichten routes (index, detail, new, edit)

- /geschichten — published-stories index with filter pills + "+ Neue Geschichte"
  for BLOG_WRITERs; supports ?personId and ?documentId pre-filtering
- /geschichten/[id] — reader detail with sanitised {@html} body, person and
  document chip sections, BLOG_WRITER edit/delete with confirm dialog
- /geschichten/new — editor with optional ?personId and ?documentId pre-fill
  (silent ignore on unknown IDs to avoid leaking entity existence)
- /geschichten/[id]/edit — editor populated from existing story; BLOG_WRITE
  guard redirects readers to the detail page

All routes load via createApiClient(fetch) with !response.ok error handling
following the project pattern; PATCH/DELETE go through raw fetch which the
Vite dev proxy / Caddy production proxy authenticates via cookie.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-02 17:54:31 +02:00
parent 9e6efacbcb
commit fe1014a08a
8 changed files with 492 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { m } from '$lib/paraglide/messages.js';
import GeschichteEditor from '$lib/components/GeschichteEditor.svelte';
import BackButton from '$lib/components/BackButton.svelte';
import { getErrorMessage } from '$lib/errors';
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
let submitting = $state(false);
let errorMessage: string | null = $state(null);
async function handleSubmit(payload: {
title: string;
body: string;
status: 'DRAFT' | 'PUBLISHED';
personIds: string[];
documentIds: string[];
}) {
submitting = true;
errorMessage = null;
try {
const res = await fetch(`/api/geschichten/${data.geschichte.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!res.ok) {
const code = (await res.json().catch(() => ({})))?.code;
errorMessage = getErrorMessage(code);
return;
}
goto(`/geschichten/${data.geschichte.id}`);
} finally {
submitting = false;
}
}
</script>
<div class="mx-auto max-w-5xl px-4 py-8">
<div class="mb-6">
<BackButton />
</div>
<h1 class="mb-6 font-serif text-3xl font-bold text-ink">
{m.btn_edit()}: {data.geschichte.title}
</h1>
{#if errorMessage}
<div
class="mb-4 rounded border border-danger bg-danger/10 p-3 text-sm text-danger"
role="alert"
>
{errorMessage}
</div>
{/if}
<GeschichteEditor geschichte={data.geschichte} onSubmit={handleSubmit} submitting={submitting} />
</div>