Merge pull request 'Add percentage fields to appointment stats and update dashboard components to display these metrics.' (#63) from feat/booking-panel into master

Reviewed-on: http://35.207.46.142/ATTUNE-HEART-THERAPY/website/pulls/63
This commit is contained in:
Hammond 2025-12-05 14:14:54 +00:00
commit 423d339aff
3 changed files with 64 additions and 35 deletions

View File

@ -39,6 +39,12 @@ interface DashboardStats {
active_upcoming_meetings: number;
total_revenue: number;
monthly_revenue: number;
// Percentage fields from API
users_pct?: number;
scheduled_pct?: number;
completed_pct?: number;
pending_review_pct?: number;
rejected_pct?: number;
trends: {
total_users: string;
active_users: string;
@ -133,6 +139,12 @@ export default function Dashboard() {
active_upcoming_meetings: activeUpcomingMeetings,
total_revenue: totalRevenue,
monthly_revenue: monthlyRevenue,
// Include percentage fields from API
users_pct: appointmentStats?.users_pct,
scheduled_pct: appointmentStats?.scheduled_pct,
completed_pct: appointmentStats?.completed_pct,
pending_review_pct: appointmentStats?.pending_review_pct,
rejected_pct: appointmentStats?.rejected_pct,
trends,
});
} catch (error) {
@ -148,6 +160,11 @@ export default function Dashboard() {
active_upcoming_meetings: 0,
total_revenue: 0,
monthly_revenue: 0,
users_pct: undefined,
scheduled_pct: undefined,
completed_pct: undefined,
pending_review_pct: undefined,
rejected_pct: undefined,
trends: {
total_users: "0%",
active_users: "0%",
@ -172,7 +189,7 @@ export default function Dashboard() {
title: "Total Users",
value: stats?.total_users ?? 0,
icon: Users,
trend: stats?.trends.total_users ?? "0%",
trend: stats?.users_pct !== undefined ? `${Math.round(stats.users_pct)}%` : undefined,
trendUp: true,
},
{
@ -193,28 +210,28 @@ export default function Dashboard() {
title: "Upcoming Bookings",
value: stats?.upcoming_bookings ?? 0,
icon: CalendarCheck,
trend: stats?.trends.upcoming_bookings ?? "0",
trend: stats?.scheduled_pct !== undefined ? `${Math.round(stats.scheduled_pct)}%` : undefined,
trendUp: true,
},
{
title: "Completed Bookings",
value: stats?.completed_bookings ?? 0,
icon: CalendarCheck,
trend: stats?.trends.completed_bookings ?? "0%",
trend: stats?.completed_pct !== undefined ? `${Math.round(stats.completed_pct)}%` : undefined,
trendUp: true,
},
{
title: "Cancelled Bookings",
value: stats?.cancelled_bookings ?? 0,
icon: CalendarX,
trend: stats?.trends.cancelled_bookings ?? "0%",
trend: stats?.rejected_pct !== undefined ? `${Math.round(stats.rejected_pct)}%` : undefined,
trendUp: false,
},
{
title: "Active Upcoming Meetings",
value: stats?.active_upcoming_meetings ?? 0,
icon: CalendarCheck,
trend: "0",
trend: undefined,
trendUp: true,
},
];
@ -284,8 +301,7 @@ export default function Dashboard() {
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? "bg-gray-700" : "bg-gray-50"}`}>
<Icon className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? "text-gray-200" : "text-gray-600"}`} />
</div>
{/* Percentage badge commented out */}
{/* {card.trend && (
{card.trend && (
<div className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${getTrendClasses(card.trendUp)}`}>
{card.trendUp ? (
<ArrowUpRight className="w-3 h-3" />
@ -294,7 +310,7 @@ export default function Dashboard() {
)}
<span className="hidden sm:inline">{card.trend}</span>
</div>
)} */}
)}
</div>
<div>

View File

@ -146,9 +146,13 @@ export default function UserDashboard() {
if (stats) {
return {
scheduled: stats.scheduled || 0,
scheduled_pct: stats.scheduled_pct,
completed: stats.completed || 0,
completed_pct: stats.completed_pct,
pending_review: stats.pending_review || 0,
pending_review_pct: stats.pending_review_pct,
rejected: stats.rejected || 0,
rejected_pct: stats.rejected_pct,
total_requests: stats.total_requests || 0,
completion_rate: stats.completion_rate || 0,
};
@ -223,13 +227,14 @@ export default function UserDashboard() {
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
<CalendarCheck className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
</div>
{/* Percentage badge commented out */}
{/* <div
{displayStats.scheduled_pct !== undefined && (
<div
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${isDark ? "bg-green-900/30 text-green-400" : "bg-green-50 text-green-700"}`}
>
<ArrowUpRight className="w-3 h-3" />
<span>{displayStats.scheduled > 0 ? `+${displayStats.scheduled}` : "0"}</span>
</div> */}
<span>{`${Math.round(displayStats.scheduled_pct)}%`}</span>
</div>
)}
</div>
<div>
<h3 className={`text-xs font-medium mb-1 sm:mb-2 uppercase tracking-wider ${isDark ? 'text-rose-400' : 'text-rose-600'}`}>
@ -248,13 +253,14 @@ export default function UserDashboard() {
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
<CheckCircle2 className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
</div>
{/* Percentage badge commented out */}
{/* <div
{displayStats.completed_pct !== undefined && (
<div
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${isDark ? "bg-green-900/30 text-green-400" : "bg-green-50 text-green-700"}`}
>
<ArrowUpRight className="w-3 h-3" />
<span>{displayStats.completed > 0 ? `+${displayStats.completed}` : "0"}</span>
</div> */}
<span>{`${Math.round(displayStats.completed_pct)}%`}</span>
</div>
)}
</div>
<div>
<h3 className={`text-xs font-medium mb-1 sm:mb-2 uppercase tracking-wider ${isDark ? 'text-rose-400' : 'text-rose-600'}`}>
@ -273,13 +279,7 @@ export default function UserDashboard() {
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
<Calendar className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
</div>
{/* Percentage badge commented out */}
{/* <div
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${isDark ? "bg-green-900/30 text-green-400" : "bg-green-50 text-green-700"}`}
>
<ArrowUpRight className="w-3 h-3" />
<span>{`${Math.round(displayStats.completion_rate || 0)}%`}</span>
</div> */}
{/* No percentage badge for total appointments */}
</div>
<div>
<h3 className={`text-xs font-medium mb-1 sm:mb-2 uppercase tracking-wider ${isDark ? 'text-rose-400' : 'text-rose-600'}`}>
@ -298,13 +298,14 @@ export default function UserDashboard() {
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
<Calendar className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
</div>
{/* Percentage badge commented out */}
{/* <div
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${isDark ? "bg-red-900/30 text-red-400" : "bg-red-50 text-red-700"}`}
{displayStats.pending_review_pct !== undefined && (
<div
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${isDark ? "bg-yellow-900/30 text-yellow-400" : "bg-yellow-50 text-yellow-700"}`}
>
<ArrowUpRight className="w-3 h-3" />
<span>{displayStats.pending_review > 0 ? `${displayStats.pending_review}` : "0"}</span>
</div> */}
<span>{`${Math.round(displayStats.pending_review_pct)}%`}</span>
</div>
)}
</div>
<div>
<h3 className={`text-xs font-medium mb-1 sm:mb-2 uppercase tracking-wider ${isDark ? 'text-rose-400' : 'text-rose-600'}`}>

View File

@ -140,24 +140,36 @@ export interface MatchingAvailability {
export interface AppointmentStats {
total_requests: number;
pending_review: number;
pending_review_pct?: number;
scheduled: number;
scheduled_pct?: number;
rejected: number;
rejected_pct?: number;
completed: number;
completed_pct?: number;
completion_rate: number;
users?: number; // Total users count from API
users_pct?: number;
active_upcoming_meetings?: number;
availability_coverage?: number;
availability_coverage_pct?: number;
available_days_count?: number;
jitsi_meetings_created?: number;
meetings_with_video?: number;
meetings_with_video_pct?: number;
video_meetings?: number;
}
export interface UserAppointmentStats {
total_requests: number;
pending_review: number;
pending_review_pct?: number;
scheduled: number;
scheduled_pct?: number;
rejected: number;
rejected_pct?: number;
completed: number;
completed_pct?: number;
completion_rate: number;
email?: string;
}