Re_Figma_Code/src/pages/Dashboard/Dashboard.tsx

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>
);
}