Confirming a provisional person was a one-click write — easy to fat-finger on a touchscreen and irreversible (the person disappears from the review list, with no obvious undo path). Mirror the destructive-delete pattern with a non-destructive confirm dialog (destructive: false) so the action requires a second deliberate click. New i18n keys (persons_review_confirm_confirm_title/text/button) added to all three locales (de, en, es). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
171 lines
5.6 KiB
Svelte
171 lines
5.6 KiB
Svelte
<script lang="ts">
|
|
import { enhance } from '$app/forms';
|
|
import { m } from '$lib/paraglide/messages.js';
|
|
import { getConfirmService } from '$lib/shared/services/confirm.svelte.js';
|
|
import PersonTypeahead from '$lib/person/PersonTypeahead.svelte';
|
|
import type { components } from '$lib/generated/api';
|
|
|
|
type Person = components['schemas']['PersonSummaryDTO'];
|
|
|
|
let { person }: { person: Person } = $props();
|
|
|
|
const { confirm } = getConfirmService();
|
|
|
|
let mode = $state<'idle' | 'rename' | 'merge'>('idle');
|
|
let renameFirstName = $state(person.firstName ?? '');
|
|
let renameLastName = $state(person.lastName ?? '');
|
|
let mergeTargetId = $state('');
|
|
|
|
const documentCount = $derived(person.documentCount ?? 0);
|
|
|
|
const actionBtn =
|
|
'inline-flex min-h-[44px] items-center justify-center rounded-sm border border-line bg-surface px-3 py-2 font-sans text-sm font-semibold text-ink transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-2 focus-visible:outline-none';
|
|
const deleteBtn =
|
|
'inline-flex min-h-[44px] items-center justify-center rounded-sm border border-danger px-3 py-2 font-sans text-sm font-semibold text-danger transition-colors hover:bg-danger/10 focus-visible:ring-2 focus-visible:ring-danger focus-visible:ring-offset-2 focus-visible:outline-none';
|
|
</script>
|
|
|
|
<li class="flex flex-col gap-3 rounded-sm border border-line bg-surface p-4 shadow-sm">
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<!-- Neutral placeholder avatar — these rows are unconfirmed by definition. -->
|
|
<div
|
|
class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-muted text-ink-2"
|
|
>
|
|
<img
|
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Account-MD.svg"
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="h-5 w-5 opacity-70"
|
|
/>
|
|
</div>
|
|
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate font-serif text-base font-bold text-ink">{person.displayName}</p>
|
|
<p class="font-sans text-sm text-ink-2">
|
|
{documentCount === 1
|
|
? m.person_card_doc_count_one()
|
|
: m.person_card_doc_count_many({ count: documentCount })}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
<button
|
|
type="button"
|
|
class={actionBtn}
|
|
onclick={() => (mode = mode === 'merge' ? 'idle' : 'merge')}
|
|
>
|
|
{m.persons_review_action_merge()}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class={actionBtn}
|
|
onclick={() => (mode = mode === 'rename' ? 'idle' : 'rename')}
|
|
>
|
|
{m.persons_review_action_rename()}
|
|
</button>
|
|
<form
|
|
method="POST"
|
|
action="?/confirm"
|
|
use:enhance={async ({ cancel }) => {
|
|
const ok = await confirm({
|
|
title: m.persons_review_confirm_confirm_title(),
|
|
body: m.persons_review_confirm_confirm_text(),
|
|
confirmLabel: m.persons_review_confirm_confirm_button(),
|
|
destructive: false
|
|
});
|
|
if (!ok) cancel();
|
|
}}
|
|
>
|
|
<input type="hidden" name="id" value={person.id} />
|
|
<button type="submit" class={actionBtn}>{m.persons_review_action_confirm()}</button>
|
|
</form>
|
|
<form
|
|
method="POST"
|
|
action="?/delete"
|
|
use:enhance={async ({ cancel }) => {
|
|
const ok = await confirm({
|
|
title: m.persons_review_delete_confirm_title(),
|
|
body: m.persons_review_delete_confirm_text(),
|
|
confirmLabel: m.persons_review_delete_confirm_button(),
|
|
destructive: true
|
|
});
|
|
if (!ok) cancel();
|
|
}}
|
|
>
|
|
<input type="hidden" name="id" value={person.id} />
|
|
<button type="submit" class={deleteBtn}>{m.persons_review_action_delete()}</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{#if mode === 'rename'}
|
|
<form
|
|
method="POST"
|
|
action="?/rename"
|
|
use:enhance={() => {
|
|
return () => {
|
|
mode = 'idle';
|
|
};
|
|
}}
|
|
class="flex flex-wrap items-end gap-2"
|
|
>
|
|
<input type="hidden" name="id" value={person.id} />
|
|
<input type="hidden" name="personType" value={person.personType ?? 'PERSON'} />
|
|
<label class="flex flex-col gap-1 text-sm">
|
|
<span class="font-sans text-ink-2">{m.persons_field_first_name()}</span>
|
|
<input
|
|
name="firstName"
|
|
bind:value={renameFirstName}
|
|
class="rounded-sm border border-line bg-surface px-3 py-2 text-sm text-ink focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:outline-none"
|
|
/>
|
|
</label>
|
|
<label class="flex flex-1 flex-col gap-1 text-sm">
|
|
<span class="font-sans text-ink-2">{m.persons_field_last_name()}</span>
|
|
<input
|
|
name="lastName"
|
|
required
|
|
bind:value={renameLastName}
|
|
class="rounded-sm border border-line bg-surface px-3 py-2 text-sm text-ink focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:outline-none"
|
|
/>
|
|
</label>
|
|
<button type="submit" class={actionBtn}>{m.persons_review_action_save()}</button>
|
|
<button type="button" class={actionBtn} onclick={() => (mode = 'idle')}>
|
|
{m.persons_review_action_cancel()}
|
|
</button>
|
|
</form>
|
|
{/if}
|
|
|
|
{#if mode === 'merge'}
|
|
<form
|
|
method="POST"
|
|
action="?/merge"
|
|
use:enhance={() => {
|
|
return () => {
|
|
mode = 'idle';
|
|
};
|
|
}}
|
|
class="flex flex-wrap items-end gap-2"
|
|
>
|
|
<input type="hidden" name="id" value={person.id} />
|
|
<input type="hidden" name="targetPersonId" bind:value={mergeTargetId} />
|
|
<div class="flex-1">
|
|
<PersonTypeahead
|
|
name="_mergeTargetDisplay"
|
|
label={m.persons_review_merge_label()}
|
|
value={mergeTargetId}
|
|
onchange={(value) => (mergeTargetId = value)}
|
|
/>
|
|
</div>
|
|
<button
|
|
type="submit"
|
|
disabled={!mergeTargetId}
|
|
class="{actionBtn} disabled:cursor-not-allowed disabled:opacity-40"
|
|
>
|
|
{m.persons_review_action_merge()}
|
|
</button>
|
|
<button type="button" class={actionBtn} onclick={() => (mode = 'idle')}>
|
|
{m.persons_review_action_cancel()}
|
|
</button>
|
|
</form>
|
|
{/if}
|
|
</li>
|