Add presence override editor with live consequence count

Wire the Schedule tab's Overrides card: Add opens the ADMIN-SPEC §6
pushed editor for the selected child, and the trash button confirms
before calling DELETE. The editor counts the upcoming tasks an Away
override removes from POST /presence-overrides/consequence (debounced,
stale responses dropped, never for an invalid range) and keeps Save off
until that count is shown. Saving sends only the create request, then
refetches the overrides and schedules; a 422 keeps the form and shows
the API detail.

Task 4ae0c879-2bc3-4aee-a084-5e48b41c2085

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 22:56:42 -04:00
co-authored by Claude Opus 5.5
parent 7b5177d055
commit c1d9e99f36
8 changed files with 1109 additions and 15 deletions
+44
View File
@@ -80,3 +80,47 @@ export async function fetchPresenceOverrides(
);
return body.overrides;
}
export interface PresenceOverrideRange {
child_id: number;
/** "YYYY-MM-DD", inclusive. */
start_date: string;
/** "YYYY-MM-DD", inclusive; never before start_date. */
end_date: string;
is_present: boolean;
}
export interface PresenceOverrideBody extends PresenceOverrideRange {
note?: string;
}
export const PRESENCE_OVERRIDE_CONSEQUENCE_PATH = `${PRESENCE_OVERRIDES_PATH}/consequence`;
function postJson(path: string, body: unknown): Promise<Response> {
return apiFetch(path, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
}
/**
* How many open upcoming tasks saving this override would remove. Computed
* by the API before anything is saved; completed instances never count.
*/
export async function previewOverrideConsequence(range: PresenceOverrideRange): Promise<number> {
const body = await readJson<{ removed: number }>(
await postJson(PRESENCE_OVERRIDE_CONSEQUENCE_PATH, range),
);
return body.removed;
}
export async function createPresenceOverride(body: PresenceOverrideBody): Promise<PresenceOverride> {
return readJson<PresenceOverride>(await postJson(PRESENCE_OVERRIDES_PATH, body));
}
export async function deletePresenceOverride(overrideId: number): Promise<void> {
await readJson<{ status: string }>(
await apiFetch(`${PRESENCE_OVERRIDES_PATH}/${overrideId}`, { method: "DELETE" }),
);
}