diff --git a/webui/src/api/scheduling.ts b/webui/src/api/scheduling.ts index 2339dc8..83a30d3 100644 --- a/webui/src/api/scheduling.ts +++ b/webui/src/api/scheduling.ts @@ -27,6 +27,7 @@ export type SchedulingCandidateSlot = { export type SchedulingParticipant = { id: string; + revision?: string | null; is_current_participant: boolean; respondent_id?: string | null; display_name?: string | null; @@ -168,6 +169,7 @@ export type SchedulingCandidateSlotReconcilePayload = SchedulingCandidateSlotPay export type SchedulingParticipantReconcilePayload = SchedulingParticipantPayload & { id?: string; + revision?: string; }; export type SchedulingCandidateSlotUpdatePayload = { diff --git a/webui/src/features/scheduling/SchedulingPage.tsx b/webui/src/features/scheduling/SchedulingPage.tsx index e229abc..346dd7a 100644 --- a/webui/src/features/scheduling/SchedulingPage.tsx +++ b/webui/src/features/scheduling/SchedulingPage.tsx @@ -1696,7 +1696,9 @@ function notificationKindLabel(kind: string): string { invitation: "i18n:govoplan-scheduling.invitation.6306ef74", reminder: "i18n:govoplan-scheduling.reminder.b87a1929", decision: "i18n:govoplan-scheduling.decision.7f59a1f1", - cancellation: "i18n:govoplan-scheduling.cancellation.319aaae4" + cancellation: "i18n:govoplan-scheduling.cancellation.319aaae4", + participant_removed: "i18n:govoplan-scheduling.participant_removed.0cf4ec4c", + participant_replaced: "i18n:govoplan-scheduling.participant_replaced.2623752d" } as Record)[kind] ?? kind; } diff --git a/webui/src/features/scheduling/schedulingViewModel.ts b/webui/src/features/scheduling/schedulingViewModel.ts index a15e833..e1f032c 100644 --- a/webui/src/features/scheduling/schedulingViewModel.ts +++ b/webui/src/features/scheduling/schedulingViewModel.ts @@ -11,6 +11,7 @@ const DIRECTORY_SELECTION_METADATA_KEY = "directory_selection"; export type SchedulingParticipantDraft = PeoplePickerItem & { draftId: string; sourceId?: string; + revision?: string; respondent_id?: string | null; display_name: string; email: string; @@ -95,6 +96,7 @@ export function participantDraftFromResponse( source_revision: selection?.source_revision ?? null, draftId, sourceId: participant.id, + revision: participant.revision ?? undefined, respondent_id: participant.respondent_id, participant_type: normalizedParticipantType(participant.participant_type), required: participant.required ?? true, @@ -129,9 +131,11 @@ export function participantDraftsFromPicker( export function participantPayload( participant: SchedulingParticipantDraft -): SchedulingParticipantPayload & { id?: string } { +): SchedulingParticipantPayload & { id?: string; revision?: string } { return { - ...(participant.sourceId ? { id: participant.sourceId } : {}), + ...(participant.sourceId + ? { id: participant.sourceId, revision: participant.revision } + : {}), respondent_id: participant.respondent_id ?? null, display_name: participant.display_name.trim() || null, email: participant.email.trim() || null, diff --git a/webui/tests/scheduling-view-model.test.ts b/webui/tests/scheduling-view-model.test.ts index e44c0ea..9e40d4a 100644 --- a/webui/tests/scheduling-view-model.test.ts +++ b/webui/tests/scheduling-view-model.test.ts @@ -70,6 +70,7 @@ test("maps visible account and contact selections into bounded scheduling partic test("reconstructs saved directory selections and preserves existing reconciliation identity", () => { const responseParticipant = { id: "stored-participant", + revision: "a".repeat(64), is_current_participant: false, respondent_id: "account-2", display_name: "Ada Account", @@ -95,6 +96,7 @@ test("reconstructs saved directory selections and preserves existing reconciliat assert.equal(draft.sourceId, "stored-participant"); assert.equal(draft.identityLocked, true); assert.equal(participantPayload(draft).id, "stored-participant"); + assert.equal(participantPayload(draft).revision, "a".repeat(64)); }); const now = new Date("2026-07-20T10:00:00Z");