Add meeting status indication to appointment detail pages and dashboard stats

- Implemented logic to display a "Meeting has ended" button on both admin and user appointment detail pages when the meeting has concluded.
- Updated the dashboard to include a new statistic for active upcoming meetings, enhancing the overview of current appointments.
- Adjusted appointment stats model to accommodate the new active upcoming meetings metric, ensuring accurate data representation.
This commit is contained in:
iamkiddy 2025-12-05 13:03:58 +00:00
parent f1cd73fe5d
commit f40ced5fb0
4 changed files with 58 additions and 6 deletions

View File

@ -697,6 +697,23 @@ export default function AppointmentDetailPage() {
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gradient-to-br from-blue-900/20 to-purple-900/20 border-blue-800/30" : "bg-gradient-to-br from-blue-50 to-purple-50 border-blue-200"}`}> <div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gradient-to-br from-blue-900/20 to-purple-900/20 border-blue-800/30" : "bg-gradient-to-br from-blue-50 to-purple-50 border-blue-200"}`}>
<div className="p-6 space-y-3"> <div className="p-6 space-y-3">
{(() => { {(() => {
// Check if meeting has ended
const endedAt = appointment.meeting_ended_at;
const hasEnded = endedAt != null && endedAt !== "";
// If meeting has ended, show "Meeting has ended"
if (hasEnded) {
return (
<button
disabled
className={`flex items-center justify-center gap-2 w-full cursor-not-allowed h-12 rounded-lg text-base font-medium transition-colors ${isDark ? "bg-gray-700 text-gray-500" : "bg-gray-300 text-gray-500"}`}
>
<Video className="w-5 h-5" />
Meeting has ended
</button>
);
}
// Check if can join as moderator (handle both boolean and string values) // Check if can join as moderator (handle both boolean and string values)
const canJoinAsModerator = appointment.can_join_as_moderator === true || appointment.can_join_as_moderator === "true"; const canJoinAsModerator = appointment.can_join_as_moderator === true || appointment.can_join_as_moderator === "true";
// Check if meeting has started (handle both field names) // Check if meeting has started (handle both field names)

View File

@ -36,6 +36,7 @@ interface DashboardStats {
upcoming_bookings: number; upcoming_bookings: number;
completed_bookings: number; completed_bookings: number;
cancelled_bookings: number; cancelled_bookings: number;
active_upcoming_meetings: number;
total_revenue: number; total_revenue: number;
monthly_revenue: number; monthly_revenue: number;
trends: { trends: {
@ -78,10 +79,12 @@ export default function Dashboard() {
const totalBookings = appointmentStats?.total_requests || appointments.length; const totalBookings = appointmentStats?.total_requests || appointments.length;
const upcomingBookings = appointmentStats?.scheduled || const upcomingBookings = appointmentStats?.scheduled ||
appointments.filter((apt) => apt.status === "scheduled").length; appointments.filter((apt) => apt.status === "scheduled").length;
// Completed bookings - not in API status types, so set to 0 // Completed bookings from API stats
const completedBookings = 0; const completedBookings = appointmentStats?.completed ||
appointments.filter((apt) => apt.status === "completed").length;
const cancelledBookings = appointmentStats?.rejected || const cancelledBookings = appointmentStats?.rejected ||
appointments.filter((apt) => apt.status === "rejected").length; appointments.filter((apt) => apt.status === "rejected").length;
const activeUpcomingMeetings = appointmentStats?.active_upcoming_meetings || 0;
// Calculate revenue (assuming appointments have amount field, defaulting to 0) // Calculate revenue (assuming appointments have amount field, defaulting to 0)
const now = new Date(); const now = new Date();
@ -127,6 +130,7 @@ export default function Dashboard() {
upcoming_bookings: upcomingBookings, upcoming_bookings: upcomingBookings,
completed_bookings: completedBookings, completed_bookings: completedBookings,
cancelled_bookings: cancelledBookings, cancelled_bookings: cancelledBookings,
active_upcoming_meetings: activeUpcomingMeetings,
total_revenue: totalRevenue, total_revenue: totalRevenue,
monthly_revenue: monthlyRevenue, monthly_revenue: monthlyRevenue,
trends, trends,
@ -139,10 +143,11 @@ export default function Dashboard() {
active_users: 0, active_users: 0,
total_bookings: 0, total_bookings: 0,
upcoming_bookings: 0, upcoming_bookings: 0,
completed_bookings: 0, completed_bookings: 0,
cancelled_bookings: 0, cancelled_bookings: 0,
total_revenue: 0, active_upcoming_meetings: 0,
monthly_revenue: 0, total_revenue: 0,
monthly_revenue: 0,
trends: { trends: {
total_users: "0%", total_users: "0%",
active_users: "0%", active_users: "0%",
@ -205,6 +210,13 @@ export default function Dashboard() {
trend: stats?.trends.cancelled_bookings ?? "0%", trend: stats?.trends.cancelled_bookings ?? "0%",
trendUp: false, trendUp: false,
}, },
{
title: "Active Upcoming Meetings",
value: stats?.active_upcoming_meetings ?? 0,
icon: CalendarCheck,
trend: "0",
trendUp: true,
},
]; ];

View File

@ -545,6 +545,23 @@ export default function UserAppointmentDetailPage() {
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gradient-to-br from-blue-900/20 to-purple-900/20 border-blue-800/30" : "bg-gradient-to-br from-blue-50 to-purple-50 border-blue-200"}`}> <div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gradient-to-br from-blue-900/20 to-purple-900/20 border-blue-800/30" : "bg-gradient-to-br from-blue-50 to-purple-50 border-blue-200"}`}>
<div className="p-6"> <div className="p-6">
{(() => { {(() => {
// Check if meeting has ended
const endedAt = appointment.meeting_ended_at;
const hasEnded = endedAt != null && endedAt !== "";
// If meeting has ended, show "Meeting has ended"
if (hasEnded) {
return (
<button
disabled
className={`flex items-center justify-center gap-2 w-full cursor-not-allowed h-12 rounded-lg text-base font-medium transition-colors ${isDark ? "bg-gray-700 text-gray-500" : "bg-gray-300 text-gray-500"}`}
>
<Video className="w-5 h-5" />
Meeting has ended
</button>
);
}
// Check if can join as participant (handle both boolean and string values) // Check if can join as participant (handle both boolean and string values)
const canJoinAsParticipant = appointment.can_join_as_participant === true || appointment.can_join_as_participant === "true"; const canJoinAsParticipant = appointment.can_join_as_participant === true || appointment.can_join_as_participant === "true";
// Check if meeting has started (handle both field names) // Check if meeting has started (handle both field names)

View File

@ -142,8 +142,14 @@ export interface AppointmentStats {
pending_review: number; pending_review: number;
scheduled: number; scheduled: number;
rejected: number; rejected: number;
completed: number;
completion_rate: number; completion_rate: number;
users?: number; // Total users count from API users?: number; // Total users count from API
active_upcoming_meetings?: number;
availability_coverage?: number;
available_days_count?: number;
jitsi_meetings_created?: number;
meetings_with_video?: number;
} }
export interface UserAppointmentStats { export interface UserAppointmentStats {