Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2m40s
CI / OCR Service Tests (pull_request) Successful in 22s
CI / fail2ban Regex (pull_request) Has been cancelled
CI / Semgrep Security Scan (pull_request) Has been cancelled
CI / Compose Bucket Idempotency (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
86 lines
2.4 KiB
Svelte
86 lines
2.4 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { m } from '$lib/paraglide/messages.js';
|
|
import { getErrorMessage } from '$lib/shared/errors';
|
|
import { csrfFetch } from '$lib/shared/cookies';
|
|
|
|
let title = $state('');
|
|
let titleTouched = $state(false);
|
|
let submitting = $state(false);
|
|
let errorMessage: string | null = $state(null);
|
|
|
|
const titleEmpty = $derived(title.trim().length === 0);
|
|
const showTitleError = $derived(titleEmpty && titleTouched);
|
|
|
|
async function handleSubmit(e: SubmitEvent) {
|
|
e.preventDefault();
|
|
titleTouched = true;
|
|
if (titleEmpty) return;
|
|
|
|
submitting = true;
|
|
errorMessage = null;
|
|
try {
|
|
const res = await csrfFetch('/api/geschichten', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
title: title.trim(),
|
|
type: 'JOURNEY',
|
|
status: 'DRAFT',
|
|
body: '',
|
|
personIds: []
|
|
})
|
|
});
|
|
if (!res.ok) {
|
|
const code = (await res.json().catch(() => ({})))?.code;
|
|
errorMessage = getErrorMessage(code);
|
|
return;
|
|
}
|
|
const created = await res.json();
|
|
goto(`/geschichten/${created.id}/edit`);
|
|
} finally {
|
|
submitting = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<form onsubmit={handleSubmit} class="max-w-lg">
|
|
{#if errorMessage}
|
|
<div
|
|
class="mb-4 rounded border border-danger bg-danger/10 p-3 text-sm text-danger"
|
|
role="alert"
|
|
>
|
|
{errorMessage}
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="mb-4">
|
|
<input
|
|
type="text"
|
|
bind:value={title}
|
|
onblur={() => (titleTouched = true)}
|
|
placeholder={m.geschichte_editor_title_placeholder()}
|
|
aria-invalid={showTitleError}
|
|
class="block w-full rounded border px-3 py-2 font-serif text-lg text-ink placeholder:text-ink-3 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {showTitleError
|
|
? 'border-danger'
|
|
: 'border-line'}"
|
|
/>
|
|
{#if showTitleError}
|
|
<p class="mt-1 font-sans text-xs text-danger">{m.geschichte_editor_title_required()}</p>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="flex items-center gap-4">
|
|
<button
|
|
type="submit"
|
|
disabled={submitting}
|
|
class="rounded bg-brand-navy px-4 py-2 font-sans text-sm font-medium text-white hover:opacity-90 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring disabled:opacity-50"
|
|
>
|
|
{m.journey_create_submit()}
|
|
</button>
|
|
<a href="/geschichten/new" class="font-sans text-sm text-ink-3 underline hover:text-ink">
|
|
{m.journey_placeholder_back()}
|
|
</a>
|
|
</div>
|
|
</form>
|