feat: redesign document edit form for accessibility and usability
Full UX overhaul of the edit page targeting non-technical users: - Grouped fields into four labelled sections (Wer & Wann, Beschreibung, Transkription, Datei) - Replaced date text field with native <input type="date"> - Replaced sender <select> with PersonTypeahead component - Replaced receiver multi-select with new PersonMultiSelect (chip-based) - Sticky save bar at bottom with cancel/save actions - Aligned all colours to brand palette (no more blue-*) - Fixed a11y: replaced invalid <label> on compound components with <p> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,190 +1,198 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { enhance } from '$app/forms';
|
import { enhance } from '$app/forms';
|
||||||
import TagInput from '$lib/components/TagInput.svelte';
|
import TagInput from '$lib/components/TagInput.svelte';
|
||||||
export let data;
|
import PersonTypeahead from '$lib/components/PersonTypeahead.svelte';
|
||||||
export let form; // Rückgabe der Action (Fehler etc.)
|
import PersonMultiSelect from '$lib/components/PersonMultiSelect.svelte';
|
||||||
|
|
||||||
let { document: doc, persons } = data;
|
export let data;
|
||||||
let tags = doc.tags ? doc.tags.map(t => t.name) : [];
|
export let form;
|
||||||
|
|
||||||
|
let { document: doc } = data;
|
||||||
|
let tags = doc.tags ? doc.tags.map((t: any) => t.name) : [];
|
||||||
|
let senderId = doc.sender?.id ?? '';
|
||||||
|
let selectedReceivers = doc.receivers ?? [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="max-w-4xl mx-auto p-6 bg-white shadow mt-10 rounded-lg">
|
<div class="max-w-4xl mx-auto py-8 px-4">
|
||||||
<h1 class="text-2xl font-bold mb-6">Dokument bearbeiten</h1>
|
|
||||||
|
|
||||||
{#if form?.message}
|
<!-- Heading -->
|
||||||
<div class="bg-red-100 text-red-700 p-3 rounded mb-4">{form.message}</div>
|
<div class="mb-6">
|
||||||
{/if}
|
<a href="/documents/{doc.id}" class="inline-flex items-center text-xs font-bold uppercase tracking-widest text-gray-500 hover:text-brand-navy transition-colors group mb-4">
|
||||||
|
<svg class="w-4 h-4 mr-2 transform group-hover:-translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
||||||
|
</svg>
|
||||||
|
Zurück zum Dokument
|
||||||
|
</a>
|
||||||
|
<h1 class="text-3xl font-serif text-brand-navy">
|
||||||
|
Bearbeiten — <span class="text-brand-navy/70">{doc.title || doc.originalFilename}</span>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data" use:enhance class="space-y-6">
|
{#if form?.error}
|
||||||
<!-- Datei Austausch -->
|
<div class="bg-red-50 text-red-700 border border-red-200 p-4 rounded mb-6">{form.error}</div>
|
||||||
<div class="bg-blue-50 p-4 rounded border border-blue-100">
|
{/if}
|
||||||
<label for="file-upload" class="block text-sm font-medium text-gray-700 mb-2"
|
|
||||||
>Datei ersetzen (optional)</label
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<span class="text-xs text-gray-500">Aktuell: {doc.originalFilename}</span>
|
|
||||||
<!-- ID hinzugefügt -->
|
|
||||||
<input
|
|
||||||
id="file-upload"
|
|
||||||
type="file"
|
|
||||||
name="file"
|
|
||||||
class="block w-full text-sm text-gray-500
|
|
||||||
file:mr-4 file:py-2 file:px-4
|
|
||||||
file:rounded-full file:border-0
|
|
||||||
file:text-sm file:font-semibold
|
|
||||||
file:bg-blue-50 file:text-blue-700
|
|
||||||
hover:file:bg-blue-100"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<form method="POST" enctype="multipart/form-data" use:enhance class="space-y-6 pb-20">
|
||||||
<!-- Titel -->
|
|
||||||
<div>
|
|
||||||
<label for="title" class="block text-sm font-medium text-gray-700">Titel</label>
|
|
||||||
<!-- ID hinzugefügt -->
|
|
||||||
<input
|
|
||||||
id="title"
|
|
||||||
type="text"
|
|
||||||
name="title"
|
|
||||||
value={doc.title || ''}
|
|
||||||
required
|
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<!-- ── Section 1: Wer & Wann ── -->
|
||||||
<label for="documentLocation" class="block text-sm font-medium text-gray-700"
|
<div class="bg-white shadow-sm border border-brand-sand rounded-sm p-6">
|
||||||
>Dokumentenort</label
|
<h2 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-5">Wer & Wann</h2>
|
||||||
>
|
|
||||||
<!-- ID hinzugefügt -->
|
|
||||||
<input
|
|
||||||
id="documentLocation"
|
|
||||||
type="text"
|
|
||||||
name="documentLocation"
|
|
||||||
value={doc.documentLocation || ''}
|
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Datum -->
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||||
<div>
|
|
||||||
<label for="documentDate" class="block text-sm font-medium text-gray-700">Datum</label>
|
|
||||||
<!-- ID hinzugefügt -->
|
|
||||||
<input
|
|
||||||
id="documentDate"
|
|
||||||
type="text"
|
|
||||||
name="documentDate"
|
|
||||||
value={doc.documentDate || ''}
|
|
||||||
placeholder="YYYY-MM-DD"
|
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Ort -->
|
<!-- Datum -->
|
||||||
<div>
|
<div>
|
||||||
<label for="location" class="block text-sm font-medium text-gray-700">Ort</label>
|
<label for="documentDate" class="block text-sm font-medium text-gray-700 mb-1">Datum</label>
|
||||||
<!-- ID hinzugefügt -->
|
<input
|
||||||
<input
|
id="documentDate"
|
||||||
id="location"
|
type="date"
|
||||||
type="text"
|
name="documentDate"
|
||||||
name="location"
|
value={doc.documentDate || ''}
|
||||||
value={doc.location || ''}
|
class="block w-full rounded border-gray-300 shadow-sm p-2 border focus:border-brand-navy focus:ring-brand-navy text-sm"
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Sender -->
|
<!-- Ort -->
|
||||||
<div>
|
<div>
|
||||||
<label for="senderId" class="block text-sm font-medium text-gray-700">Absender</label>
|
<label for="location" class="block text-sm font-medium text-gray-700 mb-1">Ort</label>
|
||||||
<!-- ID hinzugefügt -->
|
<input
|
||||||
<select
|
id="location"
|
||||||
id="senderId"
|
type="text"
|
||||||
name="senderId"
|
name="location"
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
|
value={doc.location || ''}
|
||||||
>
|
placeholder="z.B. Berlin, Wien…"
|
||||||
<option value="">-- Unbekannt --</option>
|
class="block w-full rounded border-gray-300 shadow-sm p-2 border focus:border-brand-navy focus:ring-brand-navy text-sm"
|
||||||
{#each persons as person}
|
/>
|
||||||
<option value={person.id} selected={doc.sender?.id === person.id}>
|
</div>
|
||||||
{person.firstName}
|
|
||||||
{person.lastName}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Empfänger (Multi-Select) -->
|
<!-- Absender -->
|
||||||
<div>
|
<div>
|
||||||
<label for="receiverIds" class="block text-sm font-medium text-gray-700"
|
<PersonTypeahead
|
||||||
>Empfänger (Strg+Klick für mehrere)</label
|
name="senderId"
|
||||||
>
|
label="Absender"
|
||||||
<!-- ID hinzugefügt -->
|
bind:value={senderId}
|
||||||
<select
|
initialName={doc.sender ? `${doc.sender.firstName} ${doc.sender.lastName}` : ''}
|
||||||
id="receiverIds"
|
/>
|
||||||
name="receiverIds"
|
</div>
|
||||||
multiple
|
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm h-32"
|
|
||||||
>
|
|
||||||
{#each persons as person}
|
|
||||||
<option value={person.id} selected={doc.receivers?.some((r) => r.id === person.id)}>
|
|
||||||
{person.firstName}
|
|
||||||
{person.lastName}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<!-- Empfänger -->
|
||||||
<label class="block text-sm font-bold text-brand-navy uppercase tracking-widest mb-2">
|
<div>
|
||||||
Schlagworte
|
<p class="block text-sm font-medium text-gray-700 mb-1">Empfänger</p>
|
||||||
</label>
|
<PersonMultiSelect bind:selectedPersons={selectedReceivers} />
|
||||||
<TagInput bind:tags />
|
</div>
|
||||||
<input type="hidden" name="tags" value={tags.join(',')} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Inhalt -->
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<label for="summary" class="block text-sm font-medium text-gray-700">Inhalt</label>
|
|
||||||
<!-- ID hinzugefügt -->
|
|
||||||
<textarea
|
|
||||||
id="summary"
|
|
||||||
name="summary"
|
|
||||||
rows="2"
|
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm font-serif"
|
|
||||||
>{doc.summary || ''}</textarea
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Transkription -->
|
<!-- ── Section 2: Beschreibung ── -->
|
||||||
<div>
|
<div class="bg-white shadow-sm border border-brand-sand rounded-sm p-6">
|
||||||
<label for="transcription" class="block text-sm font-medium text-gray-700"
|
<h2 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-5">Beschreibung</h2>
|
||||||
>Transkription</label
|
|
||||||
>
|
|
||||||
<!-- ID hinzugefügt -->
|
|
||||||
<textarea
|
|
||||||
id="transcription"
|
|
||||||
name="transcription"
|
|
||||||
rows="10"
|
|
||||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm font-serif"
|
|
||||||
>{doc.transcription || ''}</textarea
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
<div class="space-y-5">
|
||||||
<div class="flex justify-end gap-4">
|
|
||||||
<a
|
<!-- Titel -->
|
||||||
href="/documents/{doc.id}"
|
<div>
|
||||||
class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50"
|
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">Titel *</label>
|
||||||
>
|
<input
|
||||||
Abbrechen
|
id="title"
|
||||||
</a>
|
type="text"
|
||||||
<button
|
name="title"
|
||||||
type="submit"
|
value={doc.title || ''}
|
||||||
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700"
|
required
|
||||||
>
|
class="block w-full rounded border-gray-300 shadow-sm p-2 border focus:border-brand-navy focus:ring-brand-navy text-sm"
|
||||||
Speichern
|
/>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
<!-- Aufbewahrungsort -->
|
||||||
|
<div>
|
||||||
|
<label for="documentLocation" class="block text-sm font-medium text-gray-700 mb-1">Aufbewahrungsort</label>
|
||||||
|
<input
|
||||||
|
id="documentLocation"
|
||||||
|
type="text"
|
||||||
|
name="documentLocation"
|
||||||
|
value={doc.documentLocation || ''}
|
||||||
|
placeholder="z.B. Schrank 3, Mappe B"
|
||||||
|
class="block w-full rounded border-gray-300 shadow-sm p-2 border focus:border-brand-navy focus:ring-brand-navy text-sm"
|
||||||
|
/>
|
||||||
|
<p class="mt-1 text-xs text-gray-400">Wo befindet sich das Originaldokument?</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Schlagworte -->
|
||||||
|
<div>
|
||||||
|
<p class="block text-sm font-medium text-gray-700 mb-1">Schlagworte</p>
|
||||||
|
<TagInput bind:tags />
|
||||||
|
<input type="hidden" name="tags" value={tags.join(',')} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Inhalt -->
|
||||||
|
<div>
|
||||||
|
<label for="summary" class="block text-sm font-medium text-gray-700 mb-1">Inhalt</label>
|
||||||
|
<textarea
|
||||||
|
id="summary"
|
||||||
|
name="summary"
|
||||||
|
rows="5"
|
||||||
|
placeholder="Kurze Beschreibung des Inhalts…"
|
||||||
|
class="block w-full rounded border-gray-300 shadow-sm p-2 border focus:border-brand-navy focus:ring-brand-navy text-sm font-serif"
|
||||||
|
>{doc.summary || ''}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Section 3: Transkription ── -->
|
||||||
|
<div class="bg-white shadow-sm border border-brand-sand rounded-sm p-6">
|
||||||
|
<h2 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-5">Transkription</h2>
|
||||||
|
<textarea
|
||||||
|
id="transcription"
|
||||||
|
name="transcription"
|
||||||
|
rows="12"
|
||||||
|
placeholder="Vollständiger Text des Dokuments…"
|
||||||
|
class="block w-full rounded border-gray-300 shadow-sm p-2 border focus:border-brand-navy focus:ring-brand-navy text-sm font-serif"
|
||||||
|
>{doc.transcription || ''}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Section 4: Datei ── -->
|
||||||
|
<div class="bg-white shadow-sm border border-brand-sand rounded-sm p-6">
|
||||||
|
<h2 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-5">Datei</h2>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-3 mb-4 text-sm text-gray-600 bg-brand-sand/20 rounded px-3 py-2">
|
||||||
|
<svg class="w-4 h-4 text-brand-navy flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||||
|
</svg>
|
||||||
|
<span>Aktuelle Datei: <strong class="text-brand-navy font-medium">{doc.originalFilename}</strong></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="file-upload" class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Neue Datei hochladen <span class="font-normal text-gray-400">(ersetzt die aktuelle Datei)</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="file-upload"
|
||||||
|
type="file"
|
||||||
|
name="file"
|
||||||
|
class="block w-full text-sm text-gray-500
|
||||||
|
file:mr-4 file:py-2 file:px-4
|
||||||
|
file:rounded file:border-0
|
||||||
|
file:text-sm file:font-semibold
|
||||||
|
file:bg-brand-sand/40 file:text-brand-navy
|
||||||
|
hover:file:bg-brand-sand/60 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Sticky Save Bar ── -->
|
||||||
|
<div class="sticky bottom-0 z-10 bg-white border-t border-brand-sand shadow-[0_-2px_8px_rgba(0,0,0,0.06)] -mx-4 px-6 py-4 flex items-center justify-between">
|
||||||
|
<a
|
||||||
|
href="/documents/{doc.id}"
|
||||||
|
class="text-sm text-gray-500 hover:text-brand-navy transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Abbrechen
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="px-6 py-2 bg-brand-navy text-white text-sm font-bold uppercase tracking-widest rounded hover:bg-brand-navy/80 transition-colors"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user