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:
@@ -2381,6 +2381,14 @@ export interface components {
|
|||||||
documentCount?: number;
|
documentCount?: number;
|
||||||
alias?: string;
|
alias?: string;
|
||||||
notes?: 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;
|
personType?: string;
|
||||||
familyMember?: boolean;
|
familyMember?: boolean;
|
||||||
provisional?: boolean;
|
provisional?: boolean;
|
||||||
@@ -2490,10 +2498,10 @@ export interface components {
|
|||||||
/** Format: int32 */
|
/** Format: int32 */
|
||||||
number?: number;
|
number?: number;
|
||||||
sort?: components["schemas"]["SortObject"];
|
sort?: components["schemas"]["SortObject"];
|
||||||
first?: boolean;
|
|
||||||
last?: boolean;
|
|
||||||
/** Format: int32 */
|
/** Format: int32 */
|
||||||
numberOfElements?: number;
|
numberOfElements?: number;
|
||||||
|
first?: boolean;
|
||||||
|
last?: boolean;
|
||||||
empty?: boolean;
|
empty?: boolean;
|
||||||
};
|
};
|
||||||
PageableObject: {
|
PageableObject: {
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import { m } from '$lib/paraglide/messages.js';
|
|||||||
// — see Felix #3 on PR #629.
|
// — see Felix #3 on PR #629.
|
||||||
import { MAX_QUERY_LENGTH } from './mentionConstants';
|
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
|
// The dropdown receives a single reactive state object. PersonMentionEditor
|
||||||
// mutates fields on this object (model.items = ..., etc.) and Svelte's $state
|
// mutates fields on this object (model.items = ..., etc.) and Svelte's $state
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import MentionDropdownFixture from './MentionDropdown.test-fixture.svelte';
|
|||||||
import { m } from '$lib/paraglide/messages.js';
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
import type { components } from '$lib/generated/api';
|
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);
|
afterEach(cleanup);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { untrack } from 'svelte';
|
|||||||
import MentionDropdown from './MentionDropdown.svelte';
|
import MentionDropdown from './MentionDropdown.svelte';
|
||||||
import type { components } from '$lib/generated/api';
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
type Person = components['schemas']['Person'];
|
type Person = components['schemas']['PersonSummaryDTO'];
|
||||||
type DropdownState = {
|
type DropdownState = {
|
||||||
items: Person[];
|
items: Person[];
|
||||||
command: (item: Person) => void;
|
command: (item: Person) => void;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import MentionDropdown from './MentionDropdown.svelte';
|
|||||||
import { createMentionNodeView } from './mentionNodeView';
|
import { createMentionNodeView } from './mentionNodeView';
|
||||||
import { MAX_QUERY_LENGTH, SEARCH_DEBOUNCE_MS, SEARCH_RESULT_LIMIT } from './mentionConstants';
|
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 = {
|
type Props = {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -216,14 +218,15 @@ const controller = createMentionController();
|
|||||||
// reflected data-person-id or the search input).
|
// reflected data-person-id or the search input).
|
||||||
function commitRelink(pos: number): CommitFn {
|
function commitRelink(pos: number): CommitFn {
|
||||||
return (item: Person) => {
|
return (item: Person) => {
|
||||||
if (!editor) return;
|
if (!editor || !item.id) return;
|
||||||
|
const personId = item.id;
|
||||||
editor
|
editor
|
||||||
.chain()
|
.chain()
|
||||||
.focus()
|
.focus()
|
||||||
.command(({ tr, state }) => {
|
.command(({ tr, state }) => {
|
||||||
const node = state.doc.nodeAt(pos);
|
const node = state.doc.nodeAt(pos);
|
||||||
if (!node || node.type.name !== 'mention') return false;
|
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;
|
return true;
|
||||||
})
|
})
|
||||||
.run();
|
.run();
|
||||||
@@ -364,9 +367,11 @@ onMount(() => {
|
|||||||
render() {
|
render() {
|
||||||
const buildFreshCommit = (loose: LooseRenderProps): CommitFn => {
|
const buildFreshCommit = (loose: LooseRenderProps): CommitFn => {
|
||||||
const clippedQuery = loose.query.slice(0, MAX_QUERY_LENGTH);
|
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 });
|
loose.command({ personId: item.id, displayName: clippedQuery });
|
||||||
};
|
};
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
onStart(renderProps) {
|
onStart(renderProps) {
|
||||||
const loose = renderProps as unknown as LooseRenderProps;
|
const loose = renderProps as unknown as LooseRenderProps;
|
||||||
|
|||||||
@@ -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.
|
// module so the test cannot drift from production. Sara on PR #629 round 3.
|
||||||
import { SEARCH_DEBOUNCE_MS } from './mentionConstants';
|
import { SEARCH_DEBOUNCE_MS } from './mentionConstants';
|
||||||
|
|
||||||
type Person = components['schemas']['Person'];
|
type Person = components['schemas']['PersonSummaryDTO'];
|
||||||
type PersonMention = components['schemas']['PersonMention'];
|
type PersonMention = components['schemas']['PersonMention'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user