From e678f923d9007c63d3526cc9aff1f70f22e71872 Mon Sep 17 00:00:00 2001 From: Tinashe-Austin Date: Thu, 26 Sep 2024 22:15:08 +0200 Subject: [PATCH] Refactor WorkRatioChart and ActiveEmployeeCard components-Exhausted But we keep pushing ah - Replaced Spinner component with Skeleton component for loading state in WorkRatioChart component. - Replaced Spinner component with Skeleton component for loading state in ActiveEmployeeCard component. --- .../aiDashGraphs/BookingLevelCalendar.tsx | 20 +- .../aiDashGraphs/BuildingTower.tsx | 96 +++-- .../workerStats/ActiveEmployeeCard.tsx | 8 +- .../workerStats/AverageHoursChart.tsx | 23 +- .../components/workerStats/HoursDashboard.tsx | 21 +- .../workerStats/LeastActiveEmployeeCard.tsx | 9 +- .../workerStats/PeakOfficeHoursChart.tsx | 117 ++++-- .../components/workerStats/WorkRatioChart.tsx | 12 +- .../src/pages/worker-dash/WorkerDashboard.tsx | 357 +++++++++--------- 9 files changed, 418 insertions(+), 245 deletions(-) diff --git a/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BookingLevelCalendar.tsx b/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BookingLevelCalendar.tsx index b8a80020..c0b91f81 100644 --- a/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BookingLevelCalendar.tsx +++ b/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BookingLevelCalendar.tsx @@ -24,7 +24,7 @@ const BookingLevelCalendar = () => { const daysInMonth = new Date(year, month + 1, 0).getDate(); const fetchPromises = []; - for (let day = 1; day <= daysInMonth; day++) { + for (let day = 0; day <= daysInMonth + 1; day++) { // Fetch one extra day const date = new Date(year, month, day); const formattedDate = date.toISOString().split('T')[0]; fetchPromises.push( @@ -41,7 +41,15 @@ const BookingLevelCalendar = () => { try { const results = await Promise.all(fetchPromises); const newBookingData = Object.assign({}, ...results); - setBookingData(newBookingData); + // Shift all predictions down by one day + const shiftedBookingData = Object.entries(newBookingData).reduce((acc: { [key: string]: BookingData }, [date, data]) => { + const shiftedDate = new Date(date); + shiftedDate.setDate(shiftedDate.getDate() - 1); + const shiftedDateString = shiftedDate.toISOString().split('T')[0]; + acc[shiftedDateString] = data as BookingData; + return acc; + }, {}); + setBookingData(shiftedBookingData); } catch (error) { console.error('Error fetching booking data:', error); } @@ -53,7 +61,11 @@ const BookingLevelCalendar = () => { const response = await fetch(`https://date.nager.at/api/v3/PublicHolidays/${year}/ZA`); const holidays = await response.json(); const holidayMap = holidays.reduce((acc: HolidayData, holiday: { date: string; name: string }) => { - acc[holiday.date] = holiday.name; + // Move the holiday one day back + const holidayDate = new Date(holiday.date); + holidayDate.setDate(holidayDate.getDate() - 1); + const adjustedDate = holidayDate.toISOString().split('T')[0]; + acc[adjustedDate] = holiday.name; return acc; }, {}); setHolidayData(holidayMap); @@ -103,7 +115,7 @@ const BookingLevelCalendar = () => { {holiday && ( <>

{holiday}

-

Note: AI predictions may be less accurate on holidays.

+

Note: Predictions on Special days may be inaccurate.

)} {dayData ? ( diff --git a/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BuildingTower.tsx b/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BuildingTower.tsx index 3513cccf..02afd23e 100644 --- a/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BuildingTower.tsx +++ b/frontend/occupi-web/src/components/aiDashboard/aiDashGraphs/BuildingTower.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import { OrbitControls, Text } from '@react-three/drei'; import { Vector3, Group, Color } from 'three'; @@ -11,20 +11,25 @@ interface FloorProps { depth: number; } -function Floor({ position, occupancy, floorNumber, width, depth }: FloorProps) { - const [, setHovered] = useState(false) +interface Room { + floorNo: string; + maxOccupancy: number; +} + +const Floor: React.FC = ({ position, occupancy, floorNumber, width, depth }) => { + const [, setHovered] = useState(false); - const getColor = (value: number) => { + const getColor = (value: number): Vector3 => { const colors = [ [0.2, 0.8, 0.2], // Green (1) [0.5, 0.8, 0.2], // Yellow-green (2) [0.8, 0.8, 0.2], // Yellow (3) [0.8, 0.5, 0.2], // Orange (4) - [0.8, 0.2, 0.2] // Red (5) - ] - const index = Math.max(0, Math.min(Math.floor(value) - 1, 4)) - return new Vector3(...colors[index]) - } + [0.8, 0.2, 0.2] // Red (5) + ]; + const index = Math.max(0, Math.min(Math.floor(value) - 1, 4)); + return new Vector3(...colors[index]); + }; return ( @@ -40,6 +45,8 @@ function Floor({ position, occupancy, floorNumber, width, depth }: FloorProps) { rotation={[0, -Math.PI / 2, 0]} fontSize={0.2} color="black" + anchorX="center" + anchorY="middle" > {`Floor ${floorNumber}`} @@ -48,6 +55,8 @@ function Floor({ position, occupancy, floorNumber, width, depth }: FloorProps) { rotation={[0, Math.PI / 2, 0]} fontSize={0.2} color="black" + anchorX="center" + anchorY="middle" > {`Occupancy: ${occupancy}/5`} @@ -56,6 +65,8 @@ function Floor({ position, occupancy, floorNumber, width, depth }: FloorProps) { rotation={[0, Math.PI, 0]} fontSize={0.2} color="black" + anchorX="center" + anchorY="middle" > {`Floor ${floorNumber}`} @@ -63,34 +74,36 @@ function Floor({ position, occupancy, floorNumber, width, depth }: FloorProps) { position={[0, 0, -depth / 2 - 0.2]} fontSize={0.2} color="black" + anchorX="center" + anchorY="middle" > {`Occupancy: ${occupancy}/5`} - ) -} + ); +}; interface BuildingProps { floors: number[]; } -function Building({ floors }: BuildingProps) { - const groupRef = useRef(null) - const baseWidth = 4 - const baseDepth = 4 - const shrinkFactor = 0.05 +const Building: React.FC = ({ floors }) => { + const groupRef = useRef(null); + const baseWidth = 4; + const baseDepth = 4; + const shrinkFactor = 0.05; useFrame(() => { if (groupRef.current) { - groupRef.current.rotation.y += 0.002 + groupRef.current.rotation.y += 0.002; } - }) + }); return ( {floors.map((occupancy: number, index: number) => { - const width = baseWidth - index * shrinkFactor - const depth = baseDepth - index * shrinkFactor + const width = baseWidth - index * shrinkFactor; + const depth = baseDepth - index * shrinkFactor; return ( - ) + ); })} - ) -} + ); +}; -export default function BuildingTower() { - const occupancyData = [5, 4, 3, 3, 2, 2, 1] +const BuildingTower: React.FC = () => { + const [occupancyData, setOccupancyData] = useState([]); + + useEffect(() => { + const fetchData = async () => { + try { + // In a real scenario, you would fetch this data from your API + const response = await fetch('/api/view-rooms'); + const data = await response.json(); + + const rooms: Room[] = data.data; + const highestFloor = Math.max(...rooms.map(room => parseInt(room.floorNo))); + + // Calculate occupancy for each floor + const occupancy = Array(highestFloor + 1).fill(0); + rooms.forEach((room: Room) => { + const floor = parseInt(room.floorNo); + occupancy[floor] = Math.max(occupancy[floor], room.maxOccupancy); + }); + + // Remove the ground floor (index 0) and reverse the array so the highest floor is first + setOccupancyData(occupancy.slice(1).reverse()); + } catch (error) { + console.error('Error fetching room data:', error); + } + }; + + fetchData(); + }, []); return (
@@ -118,5 +158,7 @@ export default function BuildingTower() {
- ) -} \ No newline at end of file + ); +}; + +export default BuildingTower; \ No newline at end of file diff --git a/frontend/occupi-web/src/components/workerStats/ActiveEmployeeCard.tsx b/frontend/occupi-web/src/components/workerStats/ActiveEmployeeCard.tsx index 035a94b7..b48c09c3 100644 --- a/frontend/occupi-web/src/components/workerStats/ActiveEmployeeCard.tsx +++ b/frontend/occupi-web/src/components/workerStats/ActiveEmployeeCard.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Card, CardHeader, CardBody, Avatar, Button, Spinner, Progress } from "@nextui-org/react"; +import { Card, CardHeader, CardBody, Avatar, Button, Progress, Skeleton } from "@nextui-org/react"; import { ChevronDown, ChevronUp, Clock, Calendar, TrendingUp } from "lucide-react"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { getMostActiveEmployee, MostActiveEmployeeData } from 'WorkerStatsService'; @@ -36,8 +36,10 @@ const ActiveEmployeeCard = () => { if (isLoading) { return ( - - + + +
+
); } diff --git a/frontend/occupi-web/src/components/workerStats/AverageHoursChart.tsx b/frontend/occupi-web/src/components/workerStats/AverageHoursChart.tsx index 14d11794..b063a487 100644 --- a/frontend/occupi-web/src/components/workerStats/AverageHoursChart.tsx +++ b/frontend/occupi-web/src/components/workerStats/AverageHoursChart.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Card, CardHeader, CardBody, Spinner } from "@nextui-org/react"; +import { Card, CardHeader, CardBody, Skeleton } from "@nextui-org/react"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { getAverageHours } from 'WorkerStatsService'; @@ -29,7 +29,26 @@ const AverageHoursChart = () => { fetchData(); }, []); - if (loading) return ; + if (loading) { + return ( +
+ + + + +
+ + +
+
+ +
+
+
+ +
+ ); + } if (error) return
{error}
; return ( diff --git a/frontend/occupi-web/src/components/workerStats/HoursDashboard.tsx b/frontend/occupi-web/src/components/workerStats/HoursDashboard.tsx index d947bf55..5e240c5d 100644 --- a/frontend/occupi-web/src/components/workerStats/HoursDashboard.tsx +++ b/frontend/occupi-web/src/components/workerStats/HoursDashboard.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Card, CardHeader, CardBody, Spinner } from "@nextui-org/react"; +import { Card, CardHeader, CardBody, Skeleton } from "@nextui-org/react"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { getHours } from 'WorkerStatsService'; @@ -30,9 +30,22 @@ const HoursDashboard = () => { if (loading) { return ( - - - +
+ + + + +
+ + +
+
+ +
+
+
+ +
); } diff --git a/frontend/occupi-web/src/components/workerStats/LeastActiveEmployeeCard.tsx b/frontend/occupi-web/src/components/workerStats/LeastActiveEmployeeCard.tsx index de195ea3..acbe90c5 100644 --- a/frontend/occupi-web/src/components/workerStats/LeastActiveEmployeeCard.tsx +++ b/frontend/occupi-web/src/components/workerStats/LeastActiveEmployeeCard.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Card, CardHeader, CardBody, Avatar, Button, Spinner, Progress } from "@nextui-org/react"; +import { Card, CardHeader, CardBody, Avatar, Button, Progress, Skeleton } from "@nextui-org/react"; import { ChevronDown, ChevronUp, Clock, Calendar, TrendingDown } from "lucide-react"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { getLeastActiveEmployee, LeastActiveEmployeeData } from 'WorkerStatsService'; @@ -36,8 +36,11 @@ const LeastActiveEmployeeCard = () => { if (isLoading) { return ( - - + + + +
+
); } diff --git a/frontend/occupi-web/src/components/workerStats/PeakOfficeHoursChart.tsx b/frontend/occupi-web/src/components/workerStats/PeakOfficeHoursChart.tsx index 877660f8..bd8c8472 100644 --- a/frontend/occupi-web/src/components/workerStats/PeakOfficeHoursChart.tsx +++ b/frontend/occupi-web/src/components/workerStats/PeakOfficeHoursChart.tsx @@ -1,6 +1,6 @@ -import { useState, useEffect } from 'react'; -import { Card, CardHeader, CardBody, Spinner } from "@nextui-org/react"; -import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; +import React from 'react'; +import { Card, CardBody, Chip, Tooltip, Skeleton } from "@nextui-org/react"; +import { Info } from 'lucide-react'; // Importing the Info icon import { getPeakOfficeHours } from 'WorkerStatsService'; interface PeakOfficeHoursResponse { @@ -9,12 +9,12 @@ interface PeakOfficeHoursResponse { }[]; } -const PeakOfficeHoursChart = () => { - const [data, setData] = useState<{ weekday: string; hours: number[] }[]>([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); +const PeakOfficeHoursCard = () => { + const [data, setData] = React.useState<{ weekday: string; hours: number[] }[]>([]); + const [loading, setLoading] = React.useState(true); + const [error, setError] = React.useState(null); - useEffect(() => { + React.useEffect(() => { const fetchData = async () => { try { const response = await getPeakOfficeHours({}); @@ -30,30 +30,93 @@ const PeakOfficeHoursChart = () => { fetchData(); }, []); - if (loading) return ; - if (error) return
{error}
; + if (loading) { + return ( +
+ + + + +
+ + +
+
+ +
+
+
+ +
+ ); + } + if (error) return
{error}
; + + const sortedData = [...data].sort((a, b) => { + const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + return days.indexOf(a.weekday) - days.indexOf(b.weekday); + }); + + const getColorForHour = (hour: number) => { + if (hour < 9) return "primary"; + if (hour < 12) return "success"; + if (hour < 15) return "warning"; + return "danger"; + }; + + const colorLegend = [ + { color: "primary", label: "Early Morning (12 AM - 8 AM)" }, + { color: "success", label: "Morning (9 AM - 11 AM)" }, + { color: "warning", label: "Afternoon (12 PM - 2 PM)" }, + { color: "danger", label: "Late Afternoon & Evening (3 PM - 11 PM)" }, + ]; return ( - - -

Peak Office Hours by Weekday

-
+ - - - - - - - - - - - - +
+

Peak Office Hours

+ + {/* Using Info icon from lucide-react */} + +
+ +
+ {sortedData.map((day) => ( + + +

{day.weekday}

+
+ {day.hours.map((hour, index) => ( + + {hour}:00 + + ))} +
+
+
+ ))} +
+ +
+

Color Legend

+
+ {colorLegend.map(({ color, label }) => ( +
+ + {label} +
+ ))} +
+
); }; -export default PeakOfficeHoursChart; \ No newline at end of file +export default PeakOfficeHoursCard; diff --git a/frontend/occupi-web/src/components/workerStats/WorkRatioChart.tsx b/frontend/occupi-web/src/components/workerStats/WorkRatioChart.tsx index 74dc9d67..f35a7e98 100644 --- a/frontend/occupi-web/src/components/workerStats/WorkRatioChart.tsx +++ b/frontend/occupi-web/src/components/workerStats/WorkRatioChart.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Card, CardHeader, CardBody, Spinner } from "@nextui-org/react"; +import { Card, CardHeader, CardBody, Skeleton } from "@nextui-org/react"; import { RadarChart, Radar, PolarGrid, PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer, Tooltip, Legend } from 'recharts'; import { getWorkRatio } from 'WorkerStatsService'; @@ -35,7 +35,15 @@ const WorkRatioChart = () => { fetchData(); }, []); - if (loading) return ; + if (loading) { + return ( + + +
+
+
+ ); + } if (error) return
{error}
; return ( diff --git a/frontend/occupi-web/src/pages/worker-dash/WorkerDashboard.tsx b/frontend/occupi-web/src/pages/worker-dash/WorkerDashboard.tsx index 5d01ef23..993d7920 100644 --- a/frontend/occupi-web/src/pages/worker-dash/WorkerDashboard.tsx +++ b/frontend/occupi-web/src/pages/worker-dash/WorkerDashboard.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect } from "react"; -import { Spinner } from "@nextui-org/react"; import { Button } from "@nextui-org/react"; import { Dropdown, @@ -7,22 +6,10 @@ import { DropdownMenu, DropdownItem, } from "@nextui-org/dropdown"; -import { - Clock, - Users, - BarChart2, - Sunrise, - Sunset, - User, - UserMinus, -} from "lucide-react"; +import { Clock, Users, BarChart2, Sunrise, Sunset } from "lucide-react"; import * as workerStats from "WorkerStatsService"; -import { Responsive, WidthProvider, Layout, Layouts } from "react-grid-layout"; -import "react-grid-layout/css/styles.css"; -import "react-resizable/css/styles.css"; import type { Selection } from "@nextui-org/react"; import { - AiDashCard, ActiveEmployeeCard, LeastActiveEmployeeCard, WorkRatioChart, @@ -30,26 +17,75 @@ import { HoursDashboard, AverageHoursChart, TopNav, + AiDashCard, } from "@components/index"; +import { AI_loader } from "@assets/index"; -const ResponsiveGridLayout = WidthProvider(Responsive); +// New individual AiDashCard components +const TotalHoursCard: React.FC<{ + stat: string | null; + onRemove: () => void; +}> = ({ stat, onRemove }) => ( + } + stat={stat || "N/A"} + trend={5} + onRemove={onRemove} + /> +); -const defaultVisibleCards = [ - "hours", - "averageHours", - "workRatio", - "peakOfficeHours", -]; +const AverageHoursCard: React.FC<{ + stat: string | null; + onRemove: () => void; +}> = ({ stat, onRemove }) => ( + } + stat={stat || "N/A"} + trend={-2} + onRemove={onRemove} + /> +); -const defaultLayouts: Layouts = { - lg: defaultVisibleCards.map((id, index) => ({ - i: id, - x: index * 3, - y: 0, - w: 3, - h: 2, - })), -}; +const WorkRatioCard: React.FC<{ + stat: string | null; + onRemove: () => void; +}> = ({ stat, onRemove }) => ( + } + stat={stat || "N/A"} + trend={3} + onRemove={onRemove} + /> +); + +const ArrivalDepartureCard: React.FC<{ + stat: string | null; + onRemove: () => void; +}> = ({ stat, onRemove }) => ( + } + stat={stat || "N/A"} + onRemove={onRemove} + trend={0} + /> +); + +const InOfficeRateCard: React.FC<{ + stat: string | null; + onRemove: () => void; +}> = ({ stat, onRemove }) => ( + } + stat={stat || "N/A"} + trend={-1} + onRemove={onRemove} + /> +); interface WorkerStats { hours: string | null; @@ -74,15 +110,11 @@ const WorkerStatsDashboard: React.FC = () => { leastActiveEmployee: null, }); const [loading, setLoading] = useState(true); - const [layouts, setLayouts] = useState(() => { - const savedLayouts = localStorage.getItem("workerStatsDashboardLayouts"); - return savedLayouts ? JSON.parse(savedLayouts) : defaultLayouts; - }); const [visibleCards, setVisibleCards] = useState(() => { const savedVisibleCards = localStorage.getItem("workerStatsVisibleCards"); return savedVisibleCards ? JSON.parse(savedVisibleCards) - : defaultVisibleCards; + : ["hours", "averageHours"]; }); const [selectedKeys, setSelectedKeys] = React.useState( new Set(visibleCards) @@ -103,7 +135,11 @@ const WorkerStatsDashboard: React.FC = () => { ] as const; const results = await Promise.all( statNames.map((stat) => { - const method = workerStats[`get${stat.charAt(0).toUpperCase() + stat.slice(1)}` as keyof typeof workerStats] as unknown as () => Promise<{ data: [string] }>; + const method = workerStats[ + `get${ + stat.charAt(0).toUpperCase() + stat.slice(1) + }` as keyof typeof workerStats + ] as unknown as () => Promise<{ data: [string] }>; return method(); }) ); @@ -126,55 +162,51 @@ const WorkerStatsDashboard: React.FC = () => { }, []); useEffect(() => { - localStorage.setItem( - "workerStatsDashboardLayouts", - JSON.stringify(layouts) - ); localStorage.setItem( "workerStatsVisibleCards", JSON.stringify(visibleCards) ); - }, [layouts, visibleCards]); + }, [visibleCards]); const formatStat = (stat: string, value: unknown): string => { switch (stat) { case "hours": case "averageHours": - return typeof value === "object" && value !== null && "overallTotal" in value + return typeof value === "object" && + value !== null && + "overallTotal" in value ? (value.overallTotal as number).toFixed(2) : "N/A"; case "workRatio": - return typeof value === "number" ? `${(value * 100).toFixed(2)}%` : "N/A"; + return typeof value === "number" + ? `${(value * 100).toFixed(2)}%` + : "N/A"; case "peakOfficeHours": - return typeof value === "object" && value !== null && "peakHours" in value - ? value.peakHours as string + return typeof value === "object" && + value !== null && + "peakHours" in value + ? (value.peakHours as string) : "N/A"; case "arrivalDepartureAverage": - return typeof value === "object" && value !== null && - "overallavgArrival" in value && "overallavgDeparture" in value + return typeof value === "object" && + value !== null && + "overallavgArrival" in value && + "overallavgDeparture" in value ? `${value.overallavgArrival} - ${value.overallavgDeparture}` : "N/A"; case "mostActiveEmployee": case "leastActiveEmployee": return typeof value === "object" && value !== null && "email" in value - ? value.email as string + ? (value.email as string) : "N/A"; default: return "N/A"; } }; - const onLayoutChange = (_currentLayout: Layout[], allLayouts: Layouts) => { - setLayouts(allLayouts); - }; - const handleAddCard = (cardId: string) => { if (!visibleCards.includes(cardId)) { setVisibleCards((prev) => [...prev, cardId]); - setLayouts((prev) => ({ - ...prev, - lg: [...prev.lg, { i: cardId, x: 0, y: Infinity, w: 3, h: 2 }], - })); } }; @@ -185,101 +217,54 @@ const WorkerStatsDashboard: React.FC = () => { newKeys.delete(cardId); return newKeys; }); - setLayouts((prev) => ({ - ...prev, - lg: prev.lg.filter((item) => item.i !== cardId), - })); }; if (loading) { - return Loading stats...; + return ( +
+
+ Loading +
Loading Statistics from Occubot...
+
+
+ + + ); } - const cardData = [ - { - id: "hours", - title: "Total Hours", - icon: , - stat: stats.hours, - trend: 5, - }, - { - id: "averageHours", - title: "Average Hours", - icon: , - stat: stats.averageHours, - trend: -2, - }, - { - id: "workRatio", - title: "Work Ratio", - icon: , - stat: stats.workRatio, - trend: 3, - }, - { - id: "peakOfficeHours", - title: "Peak Office Hours", - icon: , - stat: stats.peakOfficeHours, - trend: 0, - }, - { - id: "arrivalDepartureAverage", - title: "Avg Arrival - Departure", - icon: , - stat: stats.arrivalDepartureAverage, - trend: 1, - }, - { - id: "inOfficeRate", - title: "In-Office Rate", - icon: , - stat: stats.inOfficeRate, - trend: -1, - }, - { - id: "mostActiveEmployee", - title: "Most Active Employee", - icon: , - stat: stats.mostActiveEmployee, - trend: 2, - }, - { - id: "leastActiveEmployee", - title: "Least Active Employee", - icon: , - stat: stats.leastActiveEmployee, - trend: -3, - }, - ]; - return ( -
- - Employee Statistics - - See all Your Employee Statistics from Occubot - -
} searchQuery={""} onChange={function (): void { - throw new Error("Function not implemented."); - } } +
+ + Employee Statistics + + See all Your Employee Statistics from Occubot + +
+ } + searchQuery="" + onChange={() => {}} />
- + { }); }} > - {cardData.map((card) => ( - - {card.title} - - ))} + }> + Total Hours + + }> + Average Hours + + }> + Work Ratio + + } + > + Avg Arrival - Departure + + }> + In-Office Rate +
- - {cardData.map( - (card) => - visibleCards.includes(card.id) && ( -
- handleRemoveCard(card.id)} - /> -
- ) +
+ {visibleCards.includes("workRatio") && ( + handleRemoveCard("workRatio")} + /> + )} + {visibleCards.includes("averageHours") && ( + handleRemoveCard("averageHours")} + /> + )} + {visibleCards.includes("arrivalDepartureAverage") && ( + handleRemoveCard("arrivalDepartureAverage")} + /> + )} + {visibleCards.includes("inOfficeRate") && ( + handleRemoveCard("inOfficeRate")} + /> )} - +
-
+
-
+
-
+
- - - + +
+
+ {visibleCards.includes("hours") && ( + handleRemoveCard("hours")} + /> + )} +
+ + +
+ +
+ + +
); }; -export default WorkerStatsDashboard; \ No newline at end of file +export default WorkerStatsDashboard;