Add meeting status indication to appointment detail pages and dashboard stats #61
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user