Files
familienarchiv/frontend/src/routes/persons/[id]/PersonMergePanel.svelte
Marcel f162c9b55b
Some checks failed
CI / Unit & Component Tests (push) Failing after 3m2s
CI / OCR Service Tests (push) Successful in 33s
CI / Backend Unit Tests (push) Failing after 2m56s
CI / Unit & Component Tests (pull_request) Failing after 3m3s
CI / OCR Service Tests (pull_request) Successful in 31s
CI / Backend Unit Tests (pull_request) Failing after 2m56s
feat(persons): show merge panel inline on edit page, remove Gefahrenzone accordion
Closes #342. The PersonDangerZone collapsible wrapper is removed; PersonMergePanel
is now rendered directly in the edit page with its own red border (border-red-200),
preserving the {#key person.id} state-reset behaviour and the two-step merge flow.

Fix PersonTypeahead mock to use Svelte 5 functional stub (not Svelte 3/4 $$ internals).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 21:49:38 +02:00

84 lines
2.5 KiB
Svelte

<script lang="ts">
import { enhance } from '$app/forms';
import PersonTypeahead from '$lib/components/PersonTypeahead.svelte';
import { m } from '$lib/paraglide/messages.js';
let {
person,
form
}: {
person: { displayName: string };
form?: { mergeError?: string } | null;
} = $props();
let mergeTargetId = $state('');
let showMergeConfirm = $state(false);
</script>
<div class="mb-10 overflow-hidden rounded-sm border border-red-200 bg-surface shadow-sm">
<div class="p-6 md:p-8">
<h2 class="mb-1 font-serif text-lg text-ink">{m.person_merge_heading()}</h2>
<p class="mb-5 font-sans text-sm text-ink-2">
{m.person_merge_description()}
</p>
{#if form?.mergeError}
<p class="mb-4 rounded border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">
{form.mergeError}
</p>
{/if}
<form method="POST" action="?/merge" use:enhance>
<input type="hidden" name="targetPersonId" bind:value={mergeTargetId} />
<div class="flex flex-col items-end gap-3 sm:flex-row">
<div class="flex-1">
<PersonTypeahead
name="_targetPersonDisplay"
label={m.person_merge_target_label()}
value={mergeTargetId}
onchange={(value) => {
mergeTargetId = value;
showMergeConfirm = false;
}}
/>
</div>
{#if !showMergeConfirm}
<button
type="button"
disabled={!mergeTargetId}
onclick={() => (showMergeConfirm = true)}
class="rounded border border-red-300 px-4 py-2 text-sm font-bold tracking-widest text-red-600 uppercase transition-colors hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-40"
>
{m.person_btn_merge()}
</button>
{:else}
<div class="flex gap-2">
<button
type="submit"
class="rounded bg-red-600 px-4 py-2 text-sm font-bold tracking-widest text-white uppercase transition-colors hover:bg-red-700"
>
{m.person_btn_merge_confirm()}
</button>
<button
type="button"
onclick={() => (showMergeConfirm = false)}
class="rounded border border-line px-4 py-2 text-sm font-bold tracking-widest text-ink-2 uppercase transition-colors hover:bg-muted"
>
{m.btn_cancel()}
</button>
</div>
{/if}
</div>
{#if showMergeConfirm}
<p class="mt-3 rounded border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700">
{m.person_merge_warning()} <strong>{person.displayName}</strong>
{m.person_merge_will_be_deleted()}
</p>
{/if}
</form>
</div>
</div>