// t-paliad-216 Slice B (initial) + t-paliad-217 Slice C (rewrite) — // modal for the "Suggest changes" approval action. // // The approver authors a counter-proposal: edits any field on the // underlying deadline / appointment AND/OR leaves a free-text note. On // submit the caller POSTs to /api/approval-requests/{id}/suggest-changes, // which closes the OLD row as `changes_requested` and spawns a NEW pending // row authored by the approver carrying counter_payload as its payload. // // Scope (t-paliad-217 m's Q1 Reading A — 2026-05-20): // - Every editable field on the entity is in the form, not just the // date allowlist that triggers approval (t-paliad-138 §Q4). The // backend's counter-allowlist (buildCounterSetClauses in // approval_service.go) accepts the wider set: // deadline: title, due_date, original_due_date, warning_date, // description, notes, rule_code, event_type_ids // appointment: title, start_at, end_at, description, location, // appointment_type // - Lifecycle restriction: update-only. shape-list.ts hides the // suggest_changes button for create / complete / delete; this modal // refuses to open on them as defence-in-depth. // // Built on the unified openModal() primitive (t-paliad-217 Slice A) — // the primitive owns ESC, focus, backdrop, close button, browser // back-button, mobile takeover. This module only constructs the body. // // API: // const result = await openApprovalEditModal({ // entityType: "deadline", // lifecycleEvent: "update", // payload: {...}, // requester's proposed values (= current entity row) // preImage: {...}, // pre-mutation values (for "vorher" diff hints) // }); // if (result) { // // result.counterPayload + result.note ready to POST // } else { // // user cancelled // } import { t } from "../i18n"; import { attachEventTypePicker, fetchEventTypes, type PickerHandle, } from "../event-types"; import { openModal } from "./modal"; export interface ApprovalEditModalArgs { entityType: "deadline" | "appointment"; lifecycleEvent: string; payload: Record | null; preImage: Record | null; // Optional context for the read-only context section. The caller can // hydrate these from the row's API response (project_title, // requester_name, requested_at) when available; the modal degrades // gracefully when they're missing. projectTitle?: string; requesterName?: string; requestedAt?: string; } export interface ApprovalEditModalResult { counterPayload: Record; note: string; } // FieldSpec — one editable input row. The type determines the // (or