mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
feat(reminders): preventive-care surface — wann, was, wo
Add the Vorsorge page and section: an upcoming-reminders list sorted by server-computed next-due, with a create form (measurement-linked or free-text, cadence, notify hour, location) and a per-card Erledigt and delete action. Cards stay neutral-coloured regardless of due state — the status reads through a discreet badge only, never an alarming tint. Copy ships in all six locales.
This commit is contained in:
1 parent
8058d81590
commit
c6045482c3
11 files changed
+826
No files matched your search
@@ -0,0 +1,97 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* v1.17.1 — data hook for Vorsorge (measurement) reminders.
|
||||
*
|
||||
* Wraps the `/api/measurement-reminders` CRUD + satisfy surface behind
|
||||
* TanStack Query. Every mutation invalidates the one
|
||||
* `measurementReminders()` root so the section list and the dashboard
|
||||
* tile repaint in lockstep. The DTO is the server-authoritative shape —
|
||||
* the client renders `nextDueAt` as-is, never recomputing.
|
||||
*/
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiDelete, apiGet, apiPatch, apiPost } from "@/lib/api/api-fetch";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
|
||||
export interface MeasurementReminder {
|
||||
id: string;
|
||||
label: string;
|
||||
measurementType: string | null;
|
||||
intervalDays: number | null;
|
||||
rrule: string | null;
|
||||
anchorDate: string | null;
|
||||
notifyHour: number;
|
||||
location: string | null;
|
||||
nextDueAt: string | null;
|
||||
lastSatisfiedAt: string | null;
|
||||
enabled: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface CreateMeasurementReminderBody {
|
||||
label: string;
|
||||
measurementType?: string | null;
|
||||
intervalDays?: number | null;
|
||||
rrule?: string | null;
|
||||
anchorDate?: string | null;
|
||||
notifyHour?: number;
|
||||
location?: string | null;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export type UpdateMeasurementReminderBody = Partial<
|
||||
CreateMeasurementReminderBody
|
||||
>;
|
||||
|
||||
const BASE = "/api/measurement-reminders";
|
||||
|
||||
export function useMeasurementReminders(enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.measurementReminders(),
|
||||
queryFn: () => apiGet<MeasurementReminder[]>(BASE),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
export function useMeasurementReminderMutations() {
|
||||
const qc = useQueryClient();
|
||||
const invalidate = () =>
|
||||
qc.invalidateQueries({ queryKey: queryKeys.measurementReminders() });
|
||||
|
||||
const create = useMutation({
|
||||
mutationKey: queryKeys.measurementReminderCreate(),
|
||||
mutationFn: (body: CreateMeasurementReminderBody) =>
|
||||
apiPost<MeasurementReminder>(BASE, body),
|
||||
onSuccess: invalidate,
|
||||
});
|
||||
|
||||
const update = useMutation({
|
||||
mutationKey: queryKeys.measurementReminderUpdate(),
|
||||
mutationFn: ({
|
||||
id,
|
||||
body,
|
||||
}: {
|
||||
id: string;
|
||||
body: UpdateMeasurementReminderBody;
|
||||
}) => apiPatch<MeasurementReminder>(`${BASE}/${id}`, body),
|
||||
onSuccess: invalidate,
|
||||
});
|
||||
|
||||
const remove = useMutation({
|
||||
mutationKey: queryKeys.measurementReminderDelete(),
|
||||
mutationFn: (id: string) =>
|
||||
apiDelete<{ deleted: boolean }>(`${BASE}/${id}`),
|
||||
onSuccess: invalidate,
|
||||
});
|
||||
|
||||
const satisfy = useMutation({
|
||||
mutationKey: queryKeys.measurementReminderSatisfy(),
|
||||
mutationFn: (id: string) =>
|
||||
apiPost<MeasurementReminder>(`${BASE}/${id}/satisfy`),
|
||||
onSuccess: invalidate,
|
||||
});
|
||||
|
||||
return { create, update, remove, satisfy };
|
||||
}
|
||||
Reference in new issue
Block a user