fix(person): type mention items as PersonSummaryDTO, regenerate api

The dropdown and editor typed /api/persons list items as the full Person
entity. The actual wire shape is PersonSummaryDTO, which until the
previous commit had no date fields - so the life-date subtitle rendered
blank in production while fixtures (built from the entity type) kept the
tests green. Retype items as the summary projection and guard the two
personId consumers against the schema-optional id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-12 19:27:13 +02:00
parent 92672dbb71
commit 82af906608
6 changed files with 27 additions and 10 deletions

View File

@@ -2381,6 +2381,14 @@ export interface components {
documentCount?: number;
alias?: string;
notes?: string;
/** Format: date */
birthDate?: string;
/** @enum {string} */
birthDatePrecision?: "DAY" | "MONTH" | "SEASON" | "YEAR" | "RANGE" | "APPROX" | "UNKNOWN";
/** Format: date */
deathDate?: string;
/** @enum {string} */
deathDatePrecision?: "DAY" | "MONTH" | "SEASON" | "YEAR" | "RANGE" | "APPROX" | "UNKNOWN";
personType?: string;
familyMember?: boolean;
provisional?: boolean;
@@ -2490,10 +2498,10 @@ export interface components {
/** Format: int32 */
number?: number;
sort?: components["schemas"]["SortObject"];
first?: boolean;
last?: boolean;
/** Format: int32 */
numberOfElements?: number;
first?: boolean;
last?: boolean;
empty?: boolean;
};
PageableObject: {

View File

@@ -15,7 +15,9 @@ import { m } from '$lib/paraglide/messages.js';
// — see Felix #3 on PR #629.
import { MAX_QUERY_LENGTH } from './mentionConstants';
type Person = components['schemas']['Person'];
// PersonSummaryDTO, not Person: /api/persons list items are the summary projection.
// Typing them as the full entity hid a runtime bug (missing date fields, #812).
type Person = components['schemas']['PersonSummaryDTO'];
// The dropdown receives a single reactive state object. PersonMentionEditor
// mutates fields on this object (model.items = ..., etc.) and Svelte's $state

View File

@@ -7,7 +7,9 @@ import MentionDropdownFixture from './MentionDropdown.test-fixture.svelte';
import { m } from '$lib/paraglide/messages.js';
import type { components } from '$lib/generated/api';
type Person = components['schemas']['Person'];
// PersonSummaryDTO mirrors the real runtime shape: /api/persons list items are
// the summary projection, not the full entity (#812).
type Person = components['schemas']['PersonSummaryDTO'];
afterEach(cleanup);

View File

@@ -3,7 +3,7 @@ import { untrack } from 'svelte';
import MentionDropdown from './MentionDropdown.svelte';
import type { components } from '$lib/generated/api';
type Person = components['schemas']['Person'];
type Person = components['schemas']['PersonSummaryDTO'];
type DropdownState = {
items: Person[];
command: (item: Person) => void;

View File

@@ -12,7 +12,9 @@ import MentionDropdown from './MentionDropdown.svelte';
import { createMentionNodeView } from './mentionNodeView';
import { MAX_QUERY_LENGTH, SEARCH_DEBOUNCE_MS, SEARCH_RESULT_LIMIT } from './mentionConstants';
type Person = components['schemas']['Person'];
// PersonSummaryDTO, not Person: /api/persons list items are the summary projection.
// Typing them as the full entity hid a runtime bug (missing date fields, #812).
type Person = components['schemas']['PersonSummaryDTO'];
type Props = {
value: string;
@@ -216,14 +218,15 @@ const controller = createMentionController();
// reflected data-person-id or the search input).
function commitRelink(pos: number): CommitFn {
return (item: Person) => {
if (!editor) return;
if (!editor || !item.id) return;
const personId = item.id;
editor
.chain()
.focus()
.command(({ tr, state }) => {
const node = state.doc.nodeAt(pos);
if (!node || node.type.name !== 'mention') return false;
tr.setNodeMarkup(pos, undefined, { ...node.attrs, personId: item.id });
tr.setNodeMarkup(pos, undefined, { ...node.attrs, personId });
return true;
})
.run();
@@ -364,9 +367,11 @@ onMount(() => {
render() {
const buildFreshCommit = (loose: LooseRenderProps): CommitFn => {
const clippedQuery = loose.query.slice(0, MAX_QUERY_LENGTH);
return (item: Person) =>
return (item: Person) => {
if (!item.id) return;
loose.command({ personId: item.id, displayName: clippedQuery });
};
};
return {
onStart(renderProps) {
const loose = renderProps as unknown as LooseRenderProps;

View File

@@ -16,7 +16,7 @@ import { m } from '$lib/paraglide/messages.js';
// module so the test cannot drift from production. Sara on PR #629 round 3.
import { SEARCH_DEBOUNCE_MS } from './mentionConstants';
type Person = components['schemas']['Person'];
type Person = components['schemas']['PersonSummaryDTO'];
type PersonMention = components['schemas']['PersonMention'];
/**