Files
Khalid Abdi 913a217e1d feat(meetings): live invite, scheduling calendar, Discord-style redesign
- 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>
2026-06-20 02:07:41 +03:00

72 lines
1.8 KiB
TypeScript

import { apiFetch } from "@/lib/api-client";
// A persistent staff meeting room (Discord-style voice/video channel). The live
// call (participants/media) is ephemeral and runs over the socket; this is just
// the room list.
export type MeetingRoom = {
id: string;
name: string;
createdAt: string;
};
export function listMeetingRooms(): Promise<MeetingRoom[]> {
return apiFetch<MeetingRoom[]>("/api/meetings");
}
export function createMeetingRoom(name: string): Promise<MeetingRoom> {
return apiFetch<MeetingRoom>("/api/meetings", {
method: "POST",
body: JSON.stringify({ name }),
});
}
export function deleteMeetingRoom(id: string): Promise<void> {
return apiFetch<void>(`/api/meetings/${encodeURIComponent(id)}`, {
method: "DELETE",
});
}
// Max peers in a mesh call — mirrors the backend cap (mesh degrades past ~4).
export const MAX_CALL_PEERS = 4;
// A remote peer in a live call.
export type CallPeer = {
socketId: string;
userId: string;
userName: string;
};
// --- Scheduled meetings (calendar) -----------------------------------------
export type ScheduledMeeting = {
id: string;
title: string;
date: string; // YYYY-MM-DD
time: string; // HH:mm
participants: string[];
participantNames: string[];
createdBy: string | null;
};
export function listMeetingEvents(): Promise<ScheduledMeeting[]> {
return apiFetch<ScheduledMeeting[]>("/api/meetings/events");
}
export function createMeetingEvent(input: {
title: string;
date: string;
time: string;
participants: string[];
}): Promise<ScheduledMeeting> {
return apiFetch<ScheduledMeeting>("/api/meetings/events", {
method: "POST",
body: JSON.stringify(input),
});
}
export function deleteMeetingEvent(id: string): Promise<void> {
return apiFetch<void>(`/api/meetings/events/${encodeURIComponent(id)}`, {
method: "DELETE",
});
}