Skip to content

Commit e146747

Browse files
authored
βœ¨πŸ“ˆ Global Admin Dashboard – Charts, Stats, metrics, distribution (#63)
* feat: Add Admin Dashboard with statistics and charts - Updated package.json to include recharts for charting capabilities. - Created AdminDashboardPage component to display employee statistics. - Implemented API endpoint for fetching admin dashboard statistics. - Added routes for admin dashboard in both client and server. - Enhanced NavBar to include navigation to Admin Dashboard for global admins. - Integrated charts for employee status, gender distribution, and performance metrics. - Added recent employees table to display the latest additions. * feat: Enhance NavBar with dynamic navigation buttons for Admin Dashboard and Provinces * feat: Update NavBar to include back navigation to Provinces and refine province data sorting * feat: enhance admin dashboard with new stat cards, absence overview, and expanded employee table - Add new backend metrics: active/inactive/on-leave employees, new hires this month, absence overview by branch - Update frontend dashboard: 8 stat cards, new charts, improved employee table - Improve data visualizations and responsive layout - Bugfix: show all provinces in chart * feat: remove Education Distribution chart from Admin Dashboard * refactor: branch distribution now grouped by province * fix: correct formatting in Admin Dashboard for province data display * feat: track recently edited employee performances instead of newly added employees * fix: resolve loading state handling in Admin Dashboard * feat: group absence overview, rank distribution, and performance metrics by province; remove recent employees table * chore: update AdminDashboardPage formatting and sync with latest backend grouping * feat: remove New Hires (This Month) and Total Provinces stat cards * fix: replace briefcase icon with truck icon for Truck Drivers card * feat: add province filter to admin dashboard to reduce page length * feat: replace dropdown with clickable province cards for better filtering UX * fix: add defensive null checks to province filter to prevent white screen errors * feat: hide province-specific details when 'All' provinces selected, showing only global metrics * fix: add defensive null checks to prevent white screen error on province filter click * fix: add optional chaining to employeeDistribution nested properties to prevent undefined errors * fix: add guard clause to Gender Distribution chart to prevent rendering empty data * fix: add try-catch error handling and logging to catch render errors in dashboard * fix: create array copy before sorting to handle read-only arrays from backend * style: make admin dashboard UI consistent with design constants for card styling and chart heights * style: finalize updates to AdminDashboardPage.tsx for consistent UI styling * fix: ensure province text colors invert when "all" is selected * chore: commit latest changes to AdminDashboardPage.tsx
1 parent b755c90 commit e146747

File tree

10 files changed

+1731
-5
lines changed

10 files changed

+1731
-5
lines changed

β€Žclient/package-lock.jsonβ€Ž

Lines changed: 377 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žclient/package.jsonβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"dayjs": "^1.11.19",
2121
"react": "^19.2.0",
2222
"react-dom": "^19.2.0",
23-
"react-router-dom": "^7.10.1"
23+
"react-router-dom": "^7.10.1",
24+
"recharts": "^3.6.0"
2425
},
2526
"devDependencies": {
2627
"@eslint/js": "^9.39.1",

β€Žclient/src/App.tsxβ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Routes, Route, Navigate } from "react-router-dom";
22
import LoginForm from "./pages/LoginFormPage";
33
import ProtectedRoute from "./components/ProtectedRoute";
44
import GlobalAdminDashboard from "./pages/GlobalAdminDashboardPage";
5+
import AdminDashboard from "./pages/AdminDashboardPage";
56
import ProvinceEmployeesPage from "./pages/ProvinceEmployeesPage";
67
import EmployeePage from "./pages/EmployeePage";
78
import NewEmployeeFormPage from "./pages/NewEmployeeFormPage";
@@ -19,6 +20,14 @@ function App() {
1920
</ProtectedRoute>
2021
}
2122
/>
23+
<Route
24+
path={ROUTES.ADMIN_DASHBOARD}
25+
element={
26+
<ProtectedRoute>
27+
<AdminDashboard />
28+
</ProtectedRoute>
29+
}
30+
/>
2231
<Route
2332
path={ROUTES.PROVINCE_EMPLOYEES}
2433
element={

β€Žclient/src/api/api.tsβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ export const globalApi = {
9292
responseType: 'blob'
9393
});
9494
return response.data;
95-
}
95+
},
96+
getDashboardStats: () =>
97+
api
98+
.get<ApiResponse<any>>(API_ENDPOINTS.ADMIN_DASHBOARD_STATS)
99+
.then(unwrap)
96100
};
97101

98102
export default api;

β€Žclient/src/components/NavBar.tsxβ€Ž

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useNavigate } from "react-router-dom";
1+
import { useNavigate, useLocation } from "react-router-dom";
22
import { useState } from "react";
33
import AppBar from "@mui/material/AppBar";
44
import Toolbar from "@mui/material/Toolbar";
@@ -10,8 +10,11 @@ import useMediaQuery from "@mui/material/useMediaQuery";
1010
import { useTheme } from "@mui/material/styles";
1111
import LogoutIcon from "@mui/icons-material/Logout";
1212
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
13+
import DashboardIcon from "@mui/icons-material/Dashboard";
14+
import LocationOnIcon from "@mui/icons-material/LocationOn";
1315
import { authApi } from "../api/api";
1416
import { ROUTES } from "../const/endpoints";
17+
import { useIsGlobalAdmin } from "../hooks/useAuth";
1518

1619
type NavBarProps = {
1720
title?: string;
@@ -27,9 +30,14 @@ export default function NavBar({
2730
backLabel = "Back",
2831
}: NavBarProps) {
2932
const navigate = useNavigate();
33+
const location = useLocation();
3034
const theme = useTheme();
3135
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
3236
const [loading, setLoading] = useState(false);
37+
const { isGlobalAdmin } = useIsGlobalAdmin();
38+
39+
const isOnDashboard = location.pathname === ROUTES.ADMIN_DASHBOARD;
40+
const isOnProvinces = location.pathname === ROUTES.PROVINCES;
3341

3442
const handleLogout = async () => {
3543
setLoading(true);
@@ -80,6 +88,71 @@ export default function NavBar({
8088
>
8189
{title}
8290
</Typography>
91+
{isGlobalAdmin && (
92+
<Box sx={{ ml: 1.5 }}>
93+
{isOnDashboard ? (
94+
isMobile ? (
95+
<IconButton
96+
color="inherit"
97+
onClick={() => navigate(ROUTES.PROVINCES)}
98+
size="small"
99+
aria-label="Provinces"
100+
title="Provinces"
101+
>
102+
<LocationOnIcon />
103+
</IconButton>
104+
) : (
105+
<Button
106+
color="inherit"
107+
variant="outlined"
108+
onClick={() => navigate(ROUTES.PROVINCES)}
109+
startIcon={<LocationOnIcon />}
110+
size="small"
111+
sx={{
112+
borderColor: "rgba(255, 255, 255, 0.3)",
113+
"&:hover": {
114+
borderColor: "rgba(255, 255, 255, 0.5)",
115+
backgroundColor: "rgba(255, 255, 255, 0.1)",
116+
},
117+
}}
118+
aria-label="Provinces"
119+
>
120+
Provinces
121+
</Button>
122+
)
123+
) : isOnProvinces ? (
124+
isMobile ? (
125+
<IconButton
126+
color="inherit"
127+
onClick={() => navigate(ROUTES.ADMIN_DASHBOARD)}
128+
size="small"
129+
aria-label="Admin Dashboard"
130+
title="Admin Dashboard"
131+
>
132+
<DashboardIcon />
133+
</IconButton>
134+
) : (
135+
<Button
136+
color="inherit"
137+
variant="outlined"
138+
onClick={() => navigate(ROUTES.ADMIN_DASHBOARD)}
139+
startIcon={<DashboardIcon />}
140+
size="small"
141+
sx={{
142+
borderColor: "rgba(255, 255, 255, 0.3)",
143+
"&:hover": {
144+
borderColor: "rgba(255, 255, 255, 0.5)",
145+
backgroundColor: "rgba(255, 255, 255, 0.1)",
146+
},
147+
}}
148+
aria-label="Admin Dashboard"
149+
>
150+
Dashboard
151+
</Button>
152+
)
153+
) : null}
154+
</Box>
155+
)}
83156
{showLogout && (
84157
<Box sx={{ ml: 1.5 }}>
85158
{isMobile ? (

β€Žclient/src/const/endpoints.tsβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const ROUTES = {
22
ROOT: "/",
33
PROVINCES: "/provinces",
4+
ADMIN_DASHBOARD: "/admin-dashboard",
45
PROVINCE_EMPLOYEES: "/provinces/:provinceId/employees",
56
PROVINCE_EMPLOYEE_DETAIL: "/provinces/:provinceId/employees/:employeeId",
67
PROVINCE_EMPLOYEE_NEW: "/provinces/:provinceId/employees/new"
@@ -17,7 +18,8 @@ export const API_ENDPOINTS = {
1718
provinceEmployeeById: (provinceId: string, employeeId: string) => `/provinces/${provinceId}/employees/${employeeId}`,
1819
CLEAR_ALL_PERFORMANCES: "/employees/clear-performances",
1920
GLOBAL_SETTINGS: "/global-settings",
20-
TOGGLE_PERFORMANCE_LOCK: "/global-settings/toggle-performance-lock"
21+
TOGGLE_PERFORMANCE_LOCK: "/global-settings/toggle-performance-lock",
22+
ADMIN_DASHBOARD_STATS: "/admin-dashboard/stats"
2123
} as const;
2224

2325
export default { ROUTES, API_ENDPOINTS };

0 commit comments

Comments
Β (0)