319 lines
11 KiB
TypeScript
319 lines
11 KiB
TypeScript
import { useEffect, useMemo, useCallback } from 'react';
|
|
import { RefreshCw } from 'lucide-react';
|
|
import { type DateRange } from '@/services/dashboard.service';
|
|
import { useAuth, hasManagementAccess } from '@/contexts/AuthContext';
|
|
|
|
// Custom Hooks
|
|
import { useDashboardFilters } from './hooks/useDashboardFilters';
|
|
import { useDashboardPagination } from './hooks/useDashboardPagination';
|
|
import { useDashboardData } from './hooks/useDashboardData';
|
|
import { useDashboardExports } from './hooks/useDashboardExports';
|
|
|
|
// Components
|
|
import { DashboardHero } from './components/DashboardHero';
|
|
import { DashboardFiltersBar } from './components/DashboardFiltersBar';
|
|
import { AdminKPICards } from './components/sections/AdminKPICards';
|
|
import { UserKPICards } from './components/sections/UserKPICards';
|
|
import { CriticalAlertsSection } from './components/sections/CriticalAlertsSection';
|
|
import { RecentActivitySection } from './components/sections/RecentActivitySection';
|
|
import { AdminAnalyticsSection } from './components/sections/AdminAnalyticsSection';
|
|
import { UserMetricsSection } from './components/sections/UserMetricsSection';
|
|
import { PriorityDistributionReport } from './components/sections/PriorityDistributionReport';
|
|
import { TATBreachReport } from './components/sections/TATBreachReport';
|
|
import { UpcomingDeadlinesSection } from './components/sections/UpcomingDeadlinesSection';
|
|
import { AIRemarkUtilizationReport } from './components/sections/AIRemarkUtilizationReport';
|
|
import { ApproverPerformanceReport } from './components/sections/ApproverPerformanceReport';
|
|
|
|
// Utils
|
|
import { buildFilterUrl, getQuickActions } from './utils/dashboardNavigation';
|
|
import { getBreachedRequests, getUpcomingDeadlinesNotBreached } from './utils/dashboardCalculations';
|
|
|
|
interface DashboardProps {
|
|
onNavigate?: (page: string) => void;
|
|
onNewRequest?: () => void;
|
|
}
|
|
|
|
|
|
export function Dashboard({ onNavigate, onNewRequest }: DashboardProps) {
|
|
const { user } = useAuth();
|
|
|
|
// Determine user role
|
|
const isAdmin = useMemo(() => hasManagementAccess(user), [user]);
|
|
|
|
// Filters
|
|
const filters = useDashboardFilters();
|
|
const { dateRange, customStartDate, customEndDate, showCustomDatePicker, handleDateRangeChange, handleApplyCustomDate, resetCustomDates, setCustomStartDate, setCustomEndDate, setShowCustomDatePicker } = filters;
|
|
|
|
// Pagination
|
|
const pagination = useDashboardPagination();
|
|
const {
|
|
activity: activityPagination,
|
|
critical: criticalPagination,
|
|
deadlines: deadlinesPagination,
|
|
approver: approverPagination,
|
|
updateActivityPagination,
|
|
updateCriticalPagination,
|
|
updateDeadlinesPagination,
|
|
updateApproverPagination,
|
|
handleActivityPageChange,
|
|
handleCriticalPageChange,
|
|
handleDeadlinesPageChange,
|
|
handleApproverPageChange,
|
|
} = pagination;
|
|
|
|
// Data fetching
|
|
const dashboardData = useDashboardData({
|
|
isAdmin,
|
|
dateRange,
|
|
customStartDate,
|
|
customEndDate,
|
|
onPaginationUpdate: {
|
|
activity: updateActivityPagination,
|
|
critical: updateCriticalPagination,
|
|
deadlines: updateDeadlinesPagination,
|
|
approver: updateApproverPagination,
|
|
},
|
|
});
|
|
const {
|
|
kpis,
|
|
recentActivity,
|
|
criticalRequests,
|
|
departmentStats,
|
|
priorityDistribution,
|
|
upcomingDeadlines,
|
|
aiRemarkUtilization,
|
|
approverPerformance,
|
|
loading,
|
|
refreshing,
|
|
fetchDashboardData,
|
|
fetchRecentActivities,
|
|
fetchCriticalRequests,
|
|
fetchUpcomingDeadlines,
|
|
fetchApproverPerformance,
|
|
} = dashboardData;
|
|
|
|
// Exports
|
|
const exports = useDashboardExports();
|
|
const { exportingDeptStats, exportingApproverPerformance, handleExportDepartmentStats, handleExportApproverPerformance } = exports;
|
|
|
|
// Calculated values
|
|
const breachedRequests = useMemo(() => getBreachedRequests(criticalRequests), [criticalRequests]);
|
|
const upcomingDeadlinesNotBreached = useMemo(() => getUpcomingDeadlinesNotBreached(upcomingDeadlines), [upcomingDeadlines]);
|
|
|
|
// Handlers
|
|
const handleRefresh = useCallback(() => {
|
|
if (dateRange === 'custom' && customStartDate && customEndDate) {
|
|
fetchDashboardData(true);
|
|
} else {
|
|
fetchDashboardData(true);
|
|
}
|
|
}, [dateRange, customStartDate, customEndDate, fetchDashboardData]);
|
|
|
|
const handleApplyCustomDateWrapper = useCallback(() => {
|
|
handleApplyCustomDate(() => {
|
|
if (customStartDate && customEndDate) {
|
|
fetchDashboardData(false);
|
|
}
|
|
});
|
|
}, [customStartDate, customEndDate, handleApplyCustomDate, fetchDashboardData]);
|
|
|
|
const handleActivityPageChangeWrapper = useCallback((newPage: number) => {
|
|
handleActivityPageChange(newPage, fetchRecentActivities);
|
|
}, [handleActivityPageChange, fetchRecentActivities]);
|
|
|
|
const handleCriticalPageChangeWrapper = useCallback((newPage: number) => {
|
|
handleCriticalPageChange(newPage, fetchCriticalRequests);
|
|
}, [handleCriticalPageChange, fetchCriticalRequests]);
|
|
|
|
const handleDeadlinesPageChangeWrapper = useCallback((newPage: number) => {
|
|
handleDeadlinesPageChange(newPage, fetchUpcomingDeadlines);
|
|
}, [handleDeadlinesPageChange, fetchUpcomingDeadlines]);
|
|
|
|
const handleApproverPageChangeWrapper = useCallback((newPage: number) => {
|
|
handleApproverPageChange(newPage, () => fetchApproverPerformance(newPage));
|
|
}, [handleApproverPageChange, fetchApproverPerformance]);
|
|
|
|
useEffect(() => {
|
|
if (dateRange === 'custom') {
|
|
// Don't auto-fetch for custom dates until user applies them
|
|
if (customStartDate && customEndDate) {
|
|
fetchDashboardData(false);
|
|
}
|
|
} else {
|
|
fetchDashboardData(false);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [dateRange, customStartDate, customEndDate]);
|
|
|
|
// Quick actions
|
|
const quickActions = useMemo(() => getQuickActions(isAdmin, onNewRequest, onNavigate), [isAdmin, onNewRequest, onNavigate]);
|
|
|
|
// KPI click handler
|
|
const handleKPIClick = useCallback((filters: {
|
|
status?: string;
|
|
priority?: string;
|
|
slaCompliance?: string;
|
|
department?: string;
|
|
dateRange?: DateRange;
|
|
startDate?: Date;
|
|
endDate?: Date;
|
|
targetPage?: 'requests' | 'open-requests' | 'my-requests';
|
|
}) => {
|
|
const url = buildFilterUrl(filters);
|
|
onNavigate?.(url);
|
|
}, [onNavigate]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-screen">
|
|
<div className="flex flex-col items-center gap-4">
|
|
<RefreshCw className="w-8 h-8 animate-spin text-blue-600" />
|
|
<p className="text-muted-foreground">Loading dashboard...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4 sm:space-y-6 max-w-7xl mx-auto p-3 sm:p-4" data-testid="dashboard">
|
|
<DashboardHero isAdmin={isAdmin} quickActions={quickActions} />
|
|
|
|
<DashboardFiltersBar
|
|
isAdmin={isAdmin}
|
|
dateRange={dateRange}
|
|
customStartDate={customStartDate}
|
|
customEndDate={customEndDate}
|
|
showCustomDatePicker={showCustomDatePicker}
|
|
refreshing={refreshing}
|
|
onDateRangeChange={handleDateRangeChange}
|
|
onCustomStartDateChange={setCustomStartDate}
|
|
onCustomEndDateChange={setCustomEndDate}
|
|
onShowCustomDatePickerChange={setShowCustomDatePicker}
|
|
onApplyCustomDate={handleApplyCustomDateWrapper}
|
|
onResetCustomDates={resetCustomDates}
|
|
onRefresh={handleRefresh}
|
|
/>
|
|
|
|
{/* KPI Cards Section */}
|
|
{isAdmin ? (
|
|
<AdminKPICards
|
|
kpis={kpis}
|
|
priorityDistribution={priorityDistribution}
|
|
dateRange={dateRange}
|
|
customStartDate={customStartDate}
|
|
customEndDate={customEndDate}
|
|
onKPIClick={handleKPIClick}
|
|
/>
|
|
) : (
|
|
<UserKPICards
|
|
kpis={kpis}
|
|
criticalRequests={criticalRequests}
|
|
dateRange={dateRange}
|
|
customStartDate={customStartDate}
|
|
customEndDate={customEndDate}
|
|
onKPIClick={handleKPIClick}
|
|
onNavigate={onNavigate}
|
|
userId={(user as any)?.userId}
|
|
userDisplayName={(user as any)?.displayName || (user as any)?.email}
|
|
/>
|
|
)}
|
|
|
|
{/* Alerts and Activity Section */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 h-[90vh] min-h-[720px] lg:h-[60vh] lg:min-h-[480px]" data-testid="dashboard-alerts-activity">
|
|
<CriticalAlertsSection
|
|
isAdmin={isAdmin}
|
|
breachedRequests={breachedRequests}
|
|
pagination={criticalPagination}
|
|
onPageChange={handleCriticalPageChangeWrapper}
|
|
onNavigate={onNavigate}
|
|
/>
|
|
<RecentActivitySection
|
|
isAdmin={isAdmin}
|
|
recentActivity={recentActivity}
|
|
pagination={activityPagination}
|
|
refreshing={refreshing}
|
|
onPageChange={handleActivityPageChangeWrapper}
|
|
onRefresh={handleRefresh}
|
|
onNavigate={onNavigate}
|
|
currentUserId={(user as any)?.userId}
|
|
currentUserDisplayName={(user as any)?.displayName}
|
|
currentUserEmail={(user as any)?.email}
|
|
/>
|
|
</div>
|
|
|
|
{/* ADMIN - Additional Analytics */}
|
|
{isAdmin && kpis && (
|
|
<AdminAnalyticsSection
|
|
kpis={kpis}
|
|
upcomingDeadlines={upcomingDeadlines}
|
|
criticalRequests={criticalRequests}
|
|
departmentStats={departmentStats}
|
|
dateRange={dateRange}
|
|
customStartDate={customStartDate}
|
|
customEndDate={customEndDate}
|
|
exportingDeptStats={exportingDeptStats}
|
|
onKPIClick={handleKPIClick}
|
|
onExportDepartmentStats={handleExportDepartmentStats}
|
|
/>
|
|
)}
|
|
|
|
{/* NORMAL USER - Personal Metrics */}
|
|
{!isAdmin && kpis && <UserMetricsSection kpis={kpis} />}
|
|
|
|
{/* Priority Distribution Report */}
|
|
{isAdmin && priorityDistribution.length > 0 && (
|
|
<PriorityDistributionReport
|
|
priorityDistribution={priorityDistribution}
|
|
onNavigate={onNavigate}
|
|
/>
|
|
)}
|
|
|
|
{/* TAT Breach Report */}
|
|
{isAdmin && breachedRequests.length > 0 && (
|
|
<TATBreachReport
|
|
breachedRequests={breachedRequests}
|
|
pagination={criticalPagination}
|
|
dateRange={dateRange}
|
|
customStartDate={customStartDate}
|
|
customEndDate={customEndDate}
|
|
onPageChange={handleCriticalPageChangeWrapper}
|
|
onKPIClick={handleKPIClick}
|
|
onNavigate={onNavigate}
|
|
/>
|
|
)}
|
|
|
|
{/* Upcoming Deadlines / Workflow Aging */}
|
|
{upcomingDeadlinesNotBreached.length > 0 && (
|
|
<UpcomingDeadlinesSection
|
|
isAdmin={isAdmin}
|
|
upcomingDeadlines={upcomingDeadlinesNotBreached}
|
|
pagination={deadlinesPagination}
|
|
onPageChange={handleDeadlinesPageChangeWrapper}
|
|
onNavigate={onNavigate}
|
|
/>
|
|
)}
|
|
|
|
{/* AI Remark Utilization Report (Admin Only) */}
|
|
{isAdmin && aiRemarkUtilization && (
|
|
<AIRemarkUtilizationReport aiRemarkUtilization={aiRemarkUtilization} />
|
|
)}
|
|
|
|
{/* Approver Performance Report (Admin Only) */}
|
|
{isAdmin && approverPerformance.length > 0 && (
|
|
<ApproverPerformanceReport
|
|
approverPerformance={approverPerformance}
|
|
pagination={approverPagination}
|
|
dateRange={dateRange}
|
|
customStartDate={customStartDate}
|
|
customEndDate={customEndDate}
|
|
loading={loading}
|
|
exportingApproverPerformance={exportingApproverPerformance}
|
|
onPageChange={handleApproverPageChangeWrapper}
|
|
onExport={handleExportApproverPerformance}
|
|
onNavigate={onNavigate}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|