mirror of
https://github.com/temetro/temetro.git
synced 2026-08-27 19:06:52 +00:00
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>
This commit is contained in:
@@ -737,7 +737,34 @@
|
||||
"startVideo": "Start video",
|
||||
"stopVideo": "Stop video",
|
||||
"shareScreen": "Share screen",
|
||||
"leave": "Leave call"
|
||||
"leave": "Leave call",
|
||||
"calendar": "Calendar",
|
||||
"calendarEmpty": "No meetings on this day.",
|
||||
"invite": {
|
||||
"add": "Add people",
|
||||
"title": "Invite to call",
|
||||
"description": "Ring a team member into {{room}}.",
|
||||
"search": "Search team…",
|
||||
"noMembers": "No other members.",
|
||||
"ring": "Ring",
|
||||
"invited": "Invited",
|
||||
"join": "Join",
|
||||
"toastTitle": "{{name}} invited you to a call"
|
||||
},
|
||||
"schedule": {
|
||||
"cta": "Schedule meeting",
|
||||
"title": "Schedule a meeting",
|
||||
"description": "Plan a meeting and invite your team.",
|
||||
"titleLabel": "Title",
|
||||
"titlePlaceholder": "e.g. Weekly sync",
|
||||
"date": "Date",
|
||||
"time": "Time",
|
||||
"participants": "Participants",
|
||||
"save": "Schedule",
|
||||
"saving": "Scheduling…",
|
||||
"failedTitle": "Couldn't schedule meeting",
|
||||
"failedBody": "Please try again."
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"inbox": "Inbox",
|
||||
|
||||
@@ -35,3 +35,37 @@ export type CallPeer = {
|
||||
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",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ export function notificationHref(n: Notification): string | 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:
|
||||
|
||||
Reference in New Issue
Block a user