mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
913a217e1d
- Live invite: ring a clinic member into a room (socket call:invite + a bell notification); the invitee gets a toast with a Join action. Notifications of type "meeting" deep-link to /messages/meetings?room=, which auto-joins. - Scheduling: new scheduled_meetings table + /api/meetings/events (list mine, create, delete); a Calendar tab on the Meetings page with a month picker (meeting-day dots), the day's agenda, and a Schedule-meeting dialog (title/date/time/participants). - Redesign: rounded control bar with tooltips (mic/cam/screen/invite + separated red Leave), speaking ring on tiles (Web Audio), and live room-occupancy counts via call:presence broadcasts. Migration 0025. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { apiFetch } from "@/lib/api-client";
|
|
|
|
// A notification for the signed-in user. Mirrors the backend
|
|
// `src/types/notification.ts`.
|
|
export type Notification = {
|
|
id: string;
|
|
type: string;
|
|
text: string;
|
|
read: boolean;
|
|
entityType: string | null;
|
|
entityId: string | null;
|
|
actorName: string | null;
|
|
actorInitials: string | null;
|
|
createdAt: string;
|
|
};
|
|
|
|
export type NotificationsResponse = {
|
|
notifications: Notification[];
|
|
unread: number;
|
|
};
|
|
|
|
export function listNotifications(): Promise<NotificationsResponse> {
|
|
return apiFetch<NotificationsResponse>("/api/notifications");
|
|
}
|
|
|
|
export function markNotificationRead(id: string): Promise<void> {
|
|
return apiFetch<void>(`/api/notifications/${id}/read`, { method: "PATCH" });
|
|
}
|
|
|
|
export function markAllNotificationsRead(): Promise<void> {
|
|
return apiFetch<void>("/api/notifications/read-all", { method: "POST" });
|
|
}
|
|
|
|
// Maps a notification to the in-app route where the event occurred, so the
|
|
// popover can navigate the user there on click. Returns null when there's no
|
|
// meaningful destination (the item then renders as non-clickable).
|
|
export function notificationHref(n: Notification): string | null {
|
|
if (!n.entityId) return null;
|
|
switch (n.entityType) {
|
|
case "conversation":
|
|
return `/messages?conversation=${encodeURIComponent(n.entityId)}`;
|
|
case "meeting":
|
|
return `/messages/meetings?room=${encodeURIComponent(n.entityId)}`;
|
|
case "patient":
|
|
return `/patients?file=${encodeURIComponent(n.entityId)}`;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|