Files
tessera-ctl/apps/web/src/lib/reminders-api.ts
T
schalli 6879c756f2 feat(260929-if2): Erinnerung zusaetzlich per E-Mail, Doku und Aenderungsliste
- E-Mail-Planer: Anspruch vor dem Senden (genau eine Mail je Faelligkeit, hoechstens 3 Versuche), Systemlesen nur fuer die Kandidatenabfrage
- MailService.sendReminderEmail (Berliner Zeit, nur Text), GET /reminders/email-status, Haken im Formular mit Erklaerung
- Zugriffsklassifikation und Erlaubnisliste fuer forSystem nachgezogen, Aenderungsliste und Anwenderanleitung

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:11:22 +02:00

94 lines
3.1 KiB
TypeScript

/**
* Erinnerungen — API-Klient (quick-260929-if2). Spiegelt den
* `RemindersController` der API. Alle Aufrufe mit `credentials: 'include'`
* (Cookie-Anmeldung), Muster `favorites-api.ts`.
*/
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
/** Ereignis nach jeder erfolgreichen Aenderung — der globale Melder laedt daraufhin neu. */
export const REMINDERS_CHANGED_EVENT = 'tessera:reminders-changed';
/** Eine Erinnerung; Zeitangaben als ISO-Zeichenketten (so liefert sie die API). */
export interface Reminder {
id: string;
title: string;
description: string;
dueAt: string;
emailEnabled: boolean;
createdAt: string;
updatedAt: string;
}
/** Ein abgewiesener Aufruf; `status` ist der HTTP-Status (401, 404, 409 ...). */
export class ReminderRequestError extends Error {
public readonly status: number;
constructor(status: number) {
super(`Reminder request failed (${status})`);
this.name = 'ReminderRequestError';
this.status = status;
}
}
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${API_URL}${path}`, { credentials: 'include', ...init });
if (!res.ok) throw new ReminderRequestError(res.status);
return res.json() as Promise<T>;
}
function jsonInit(method: string, body: unknown): RequestInit {
return {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
};
}
/** Die eigenen Erinnerungen, die naechste Faelligkeit zuerst. */
export function listReminders(): Promise<Reminder[]> {
return request<Reminder[]>('/reminders');
}
export interface ReminderInput {
title: string;
description?: string;
/** ISO-8601-Zeitpunkt. */
dueAt: string;
/** Zusaetzlich per E-Mail erinnern; nur senden, wenn es sich aendert oder `true` ist. */
emailEnabled?: boolean;
}
export function createReminder(input: ReminderInput): Promise<Reminder> {
return request<Reminder>('/reminders', jsonInit('POST', input));
}
export type ReminderPatch = Partial<ReminderInput>;
export function updateReminder(id: string, patch: ReminderPatch): Promise<Reminder> {
return request<Reminder>(`/reminders/${encodeURIComponent(id)}`, jsonInit('PATCH', patch));
}
/** „Spaeter erinnern“: neue Faelligkeit (ISO), nur bei einer faelligen Erinnerung. */
export function snoozeReminder(id: string, dueAt: string): Promise<Reminder> {
return request<Reminder>(
`/reminders/${encodeURIComponent(id)}/snooze`,
jsonInit('POST', { dueAt }),
);
}
/** Loescht die Erinnerung — bedient „Loeschen“ (vorher) und „Erledigt“ (nachher). */
export function deleteReminder(id: string): Promise<{ deleted: true }> {
return request<{ deleted: true }>(`/reminders/${encodeURIComponent(id)}`, { method: 'DELETE' });
}
/** Ob die E-Mail-Erinnerung fuer diesen Benutzer moeglich ist (Versand eingerichtet, Adresse vorhanden). */
export interface ReminderEmailStatus {
smtpConfigured: boolean;
hasEmail: boolean;
}
export function getReminderEmailStatus(): Promise<ReminderEmailStatus> {
return request<ReminderEmailStatus>('/reminders/email-status');
}