feat(MentionDropdown): restore "Neue Person anlegen" empty-state link

The Tiptap rewrite dropped the inline "create new person" affordance the
textarea-era component used to render. Without it the workflow regresses:
transcriber must close the dropdown, navigate to /persons/new, come back,
re-type the query. The m.person_mention_create_new() key is still in all
three locale files — add the link back as a 44px-tall row with a top
border separating it from the empty-state message.

target=_blank keeps document/editor state intact; rel=noopener prevents
reverse-tabnabbing. mousedown preventDefault keeps the editor focused
(the dropdown row pattern used for option rows).

Test: empty-state renders a link to /persons/new with the localised label.

Leonie #5621 (Major) + Elicit OQ-373-04.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-29 16:20:42 +02:00
parent f1932fd5f6
commit e6844c403c
2 changed files with 29 additions and 0 deletions

View File

@@ -122,6 +122,22 @@ function selectItem(item: Person) {
<p class="px-3 py-2.5 font-sans text-sm text-ink-3">
{m.person_mention_popup_empty()}
</p>
<!--
Empty-state escape hatch — without it the transcriber has to close
the dropdown, navigate to /persons/new, come back, and re-type the
query. target=_blank keeps the document and editor state intact;
rel=noopener prevents reverse-tabnabbing on the new tab. Leonie #5621.
-->
<a
href="/persons/new"
target="_blank"
rel="noopener"
class="flex min-h-[44px] items-center gap-2 border-t border-line px-3 py-2.5 font-sans text-sm font-medium text-brand-navy hover:bg-canvas focus:bg-canvas focus:outline-none"
onmousedown={(e) => e.preventDefault()}
>
{m.person_mention_create_new()}
<span aria-hidden="true"></span>
</a>
{:else}
{#each model.items as person, i (person.id)}
<div

View File

@@ -145,6 +145,19 @@ describe('PersonMentionEditor — typeahead', () => {
await expect.element(page.getByText('Keine Personen gefunden')).toBeInTheDocument();
});
});
it('offers a "create new person" link in the empty state', async () => {
mockFetchEmpty();
renderHost();
await userEvent.type(page.getByRole('textbox'), '@xyz');
await vi.waitFor(async () => {
const link = page.getByRole('link', { name: /Neue Person anlegen/ });
await expect.element(link).toBeVisible();
await expect.element(link).toHaveAttribute('href', '/persons/new');
});
});
});
// ─── AC-1: typed text becomes displayName, not DB name ───────────────────────