import { Layout } from "@/components/layout/Layout";
import type { ReactElement } from "react";
import {
FileCheck,
Briefcase,
FileText,
Users,
Bell,
} from "lucide-react";
import { QuickActions } from "@/features/dashboard/components/QuickActions";
import { RecentActivity } from "@/features/dashboard/components/RecentActivity";
import { cn } from "@/lib/utils";
import { useState, useEffect } from "react";
import { workflowService } from "@/services/workflow-service";
import { dashboardService, type TenantDashboardStats } from "@/services/dashboard-service";
import type { WorkflowTask } from "@/types/workflow";
import { useNavigate } from "react-router-dom";
interface StatCardProps {
icon: React.ComponentType<{ className?: string; strokeWidth?: number }>;
value: string | number;
label: string;
badge?: {
text: string;
variant: 'success' | 'warning' | 'info' | 'error';
};
}
const StatCard = ({
icon: Icon,
value,
label,
badge
}: StatCardProps): ReactElement => {
return (
{/* Interaction Gradient */}
{badge && (
{badge.text}
)}
);
};
const TaskCard = ({ task }: { task: WorkflowTask }) => {
const navigate = useNavigate();
const formatDeadline = (dueDate: string) => {
const now = new Date();
const due = new Date(dueDate);
const diffTime = due.getTime() - now.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) return 'Due today';
if (diffDays === 1) return 'Tomorrow';
if (diffDays < 0) return 'Overdue';
return `In ${diffDays} Days`;
};
const handleView = () => {
if (task.entity.type.toLowerCase() === 'document') {
navigate(`/tenant/documents/${task.entity.id}`);
}
};
return (
{task.entity.type}
{formatDeadline(task.due_at)}
{task.entity.name}
{task.step.name} • {task.assignment?.assigned_role || task.assignment?.assigned_to_name || 'Unassigned'}
);
};
/*
const CAPASummaryChart = () => {
const data = [
{ name: 'Jan', open: 35, inProgress: 48, closed: 25, trend: 15 },
{ name: 'Feb', open: 28, inProgress: 35, closed: 20, trend: 12 },
{ name: 'Mar', open: 45, inProgress: 75, closed: 38, trend: 32 },
{ name: 'Apr', open: 40, inProgress: 65, closed: 42, trend: 28 },
{ name: 'May', open: 55, inProgress: 95, closed: 78, trend: 52 },
{ name: 'Jun', open: 42, inProgress: 82, closed: 72, trend: 45 },
{ name: 'Jul', open: 38, inProgress: 70, closed: 65, trend: 38 },
{ name: 'Aug', open: 48, inProgress: 94, closed: 82, trend: 48 },
{ name: 'Sep', open: 32, inProgress: 65, closed: 58, trend: 35 },
{ name: 'Oct', open: 44, inProgress: 88, closed: 85, trend: 58 },
{ name: 'Nov', open: 52, inProgress: 92, closed: 98, trend: 62 },
{ name: 'Dec', open: 60, inProgress: 105, closed: 115, trend: 58 },
];
return (
);
};
*/
const Dashboard = (): ReactElement => {
const navigate = useNavigate();
const [tasks, setTasks] = useState([]);
const [stats, setStats] = useState(null);
const [loading, setLoading] = useState(true);
const [tasksLoading, setTasksLoading] = useState(true);
useEffect(() => {
fetchDashboardData();
}, []);
const fetchDashboardData = async () => {
setLoading(true);
setTasksLoading(true);
try {
// Fetch tasks independently to avoid one failing the other
workflowService.listTasks({ limit: 3 })
.then(response => {
console.log("[Dashboard] Tasks response:", response);
if (response.success && Array.isArray(response.data)) {
setTasks(response.data);
}
})
.catch(error => {
console.error("Error fetching tasks:", error);
})
.finally(() => {
setTasksLoading(false);
});
// Fetch statistics independently
dashboardService.getTenantStatistics()
.then(response => {
if (response.success) {
setStats(response.data);
}
})
.catch(error => {
console.error("Error fetching dashboard statistics:", error);
})
.finally(() => {
setLoading(false);
});
} catch (error) {
console.error("Critical error in fetchDashboardData:", error);
setLoading(false);
setTasksLoading(false);
}
};
const statCards: StatCardProps[] = [
stats?.documentsCount !== undefined && {
icon: FileText,
value: stats.documentsCount,
label: "Total Documents",
badge: { text: "Controlled", variant: "info" }
},
stats?.pendingTasks !== undefined && {
icon: FileCheck,
value: stats.pendingTasks,
label: "My Tasks",
badge: { text: "Action Needed", variant: "warning" }
},
stats?.usersCount !== undefined && {
icon: Users,
value: stats.usersCount,
label: "Total Users",
badge: { text: "Team Members", variant: "success" }
},
stats?.unreadNotificationsCount !== undefined && {
icon: Bell,
value: stats.unreadNotificationsCount,
label: "Notifications",
badge: { text: "Unread", variant: "error" }
},
stats?.activeModulesCount !== undefined && {
icon: Briefcase,
value: stats.activeModulesCount,
label: "Running Modules",
badge: { text: "Operational", variant: "success" }
},
// Training Compliance is still static/placeholder for now
// {
// icon: GraduationCap,
// value: "94%",
// label: "Compliance",
// badge: { text: "Target Met", variant: "success" }
// },
].filter(Boolean) as StatCardProps[];
return (
{/* Main Content Area (Left) */}
{/* Stats Grid */}
{loading ? (
Loading statistics...
) : statCards.length > 0 ? (
statCards.map((card, index) => (
))
) : (
No statistics available
)}
{/* CAPA Summary Card (Commented out for now) */}
{/*
CAPA Summary
Data Range
*/}
{/* Recent Activity Card */}
{/* Sidebar area (Right) */}
{/* My Tasks Card */}
My Tasks
{tasksLoading ? (
Loading tasks...
) : tasks.length > 0 ? (
tasks.map((task) => (
))
) : (
No pending tasks
)}
{/* Quick Actions Card */}
);
};
export default Dashboard;