"use client" import { useAppSelector } from "@/lib/hooks"; import { useGetAttendanceRangeQuery, useGetMonthlyAttendanceQuery, useGetOrganizationAttendanceQuery } from "@/services/api"; import { ExclamationCircleIcon } from "@heroicons/react/24/outline"; import { ComposedChart, Bar, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell, BarChart } from 'recharts'; import { formatDate } from "date-fns"; import { PageLoader } from "@/components/Loader"; import {CustomLegendHorizontal, CustomLegendVertical} from "@/components/CustomLegendPie"; import { useLocale } from "@/lib/hooks/useLocale"; export default function AbsensiPage() { const { t } = useLocale(); const filter = useAppSelector(state => state.filter.filter); const {data: attendanceSummary, isLoading: isLoadingAttendanceSummary, isFetching: isFetchingAttendanceSummary} = useGetOrganizationAttendanceQuery(filter); const {data: attendanceRangeStaff, isLoading: isLoadingAttendanceRangeStaff, isFetching: isFetchingAttendanceRangeStaff} = useGetAttendanceRangeQuery({ ...filter, job_name: "STAFF" }); const {data: attendanceRangeNonStaff, isLoading: isLoadingAttendanceRangeNonStaff, isFetching: isFetchingAttendanceRangeNonStaff} = useGetAttendanceRangeQuery({ ...filter, job_name: "NON-STAFF" }); const {data: attendanceRangeHarvester, isLoading: isLoadingAttendanceRangeHarvester, isFetching: isFetchingAttendanceRangeHarvester} = useGetAttendanceRangeQuery({ ...filter, job_name: "HARVESTER" }); const {data: attendanceRangeMaintenance, isLoading: isLoadingAttendanceRangeMaintenance, isFetching: isFetchingAttendanceRangeMaintenance} = useGetAttendanceRangeQuery({ ...filter, job_name: "MAINTENANCE" }); const {data: montlyAttendance, isLoading: isLoadingMonthlyAttendance, isFetching: isFetchingMonthlyAttendance} = useGetMonthlyAttendanceQuery(filter); // Combine all loading states (both initial loading and refetching) const isLoading = isLoadingAttendanceSummary || isLoadingAttendanceRangeStaff || isLoadingAttendanceRangeNonStaff || isLoadingAttendanceRangeHarvester || isLoadingAttendanceRangeMaintenance || isLoadingMonthlyAttendance || isFetchingAttendanceSummary || isFetchingAttendanceRangeStaff || isFetchingAttendanceRangeNonStaff || isFetchingAttendanceRangeHarvester || isFetchingAttendanceRangeMaintenance || isFetchingMonthlyAttendance; // Show loader while any data is loading if (isLoading) { return ; } return (
{t('attendance.kehadiranStaff')}
{attendanceRangeStaff ? ( ({name: e.range, value: e.count, label: `${e.range} HK`}))} cx="50%" cy="50%" innerRadius="40%" outerRadius="70%" paddingAngle={0} dataKey="value" // label={({name, value}) => `${name} HK`} labelLine={false} > {attendanceRangeStaff.map((entry, index) => { const colors = ["#E65550","#8A5FB1","#69C9C9","#F08431","#F6C421", "#3B82F6"]; return ; })} [`${Number(value).toLocaleString('id-ID')} ${t('common.employees')}`, name]} /> ({name: e.range, value: e.count, label: `${e.range} HK`}))}/>} verticalAlign="bottom" height={36} iconType="circle" /> ) : (
{t('common.dataNotAvailable')}
)}
{t('attendance.kehadiranNonStaff')}
{attendanceRangeNonStaff ? ( ({name: e.range, value: e.count, label: `${e.range} HK`}))} cx="50%" cy="50%" innerRadius="40%" outerRadius="70%" paddingAngle={0} dataKey="value" // label={({name, value}) => `${name} HK`} labelLine={false} > {attendanceRangeNonStaff.map((entry, index) => { const colors = ["#E65550","#8A5FB1","#69C9C9","#F08431","#F6C421", "#3B82F6"]; return ; })} [`${Number(value).toLocaleString('id-ID')} ${t('common.employees')}`, name]} /> ({name: e.range, value: Number(e.count).toLocaleString('id-ID'), label: `${e.range} HK`}))}/>} verticalAlign="bottom" height={36} iconType="circle" /> ) : (
{t('common.dataNotAvailable')}
)}
{t('attendance.kehadiranPemanen')}
{attendanceRangeHarvester ? ( ({name: e.range, value: e.count, label: `${e.range} HK`}))} cx="50%" cy="50%" innerRadius="65%" outerRadius="90%" paddingAngle={0} dataKey="value" // label={({name, value}) => `${name} HK`} labelLine={false} > {attendanceRangeHarvester.map((entry, index) => { const colors = ["#E65550","#8A5FB1","#69C9C9","#F08431","#F6C421","#3B82F6"]; return ; })} [`${value} ${t('common.employees')}`, name]} /> ({name: e.range, value: e.count, label: `${e.range} HK`}))}/>} verticalAlign="middle" align="right" layout="vertical" iconType="circle" wrapperStyle={{ paddingLeft: '20px' }} /> ) : (
{t('common.dataNotAvailable')}
)}
{t('attendance.kehadiranPerawatan')}
{attendanceRangeMaintenance ? ( ({name: e.range, value: e.count, label: `${e.range} HK`}))} cx="50%" cy="50%" innerRadius="65%" outerRadius="90%" paddingAngle={0} dataKey="value" // label={({name, value}) => `${name} HK`} labelLine={false} > {attendanceRangeMaintenance.map((entry, index) => { const colors = ["#E65550","#8A5FB1","#69C9C9","#F08431","#F6C421","#3B82F6"]; return ; })} [`${Number(value).toLocaleString('id-ID')} ${t('common.employees')}`, name]} /> ({name: e.range, value: Number(e.count).toLocaleString('id-ID'), label: `${e.range} HK`}))}/>} verticalAlign="middle" align="right" layout="vertical" iconType="circle" wrapperStyle={{ paddingLeft: '20px' }} /> ) : (
{t('common.dataNotAvailable')}
)}
{t('attendance.dataKehadiranKaryawan')}
{attendanceSummary ? ( ({ ...item, countPercent: item.workdays > 0 ? (item.count / item.workdays) * 100 : 0, absentPercent: item.workdays > 0 ? (item.absent / item.workdays) * 100 : 0 })).sort((a, b) => b.countPercent - a.countPercent)} margin={{ top: 20, right: 30, left: 20, bottom: 5 }} > {/* */} `${value}%`} domain={[0, 100]} hide /> { const orgData = attendanceSummary.find(e => e.organization_code === props.payload.organization_code); if (name === t('attendance.hadir')) { return [`${Number(orgData?.count || 0).toLocaleString('id-ID')} ${t('common.hariKerja')}`, t('attendance.hadir')]; } else { return [`${Number(orgData?.absent || 0).toLocaleString('id-ID')} ${t('common.hariKerja')}`, t('attendance.tidakHadir')]; } }} labelFormatter={(label) => { const orgData = attendanceSummary.find(e => e.organization_code === label); const percentage = Math.round(orgData?.count! / orgData?.workdays! * 100); return `${orgData?.organization_name} (${percentage}%)`; }} /> ) : (
{t('common.dataNotAvailable')}
)}
{t('attendance.dataAbsensiPerbulan')}
{montlyAttendance && ( ({ ...item, percentage: item.workdays > 0 ? Math.round((item.count / item.workdays) * 100) : 0, month: formatDate(new Date(item.date), "MMM") }))} margin={{ top: 20, right: 30, left: 20, bottom: 5 }} > {/* */} { if (payload && payload[0]) { return formatDate(new Date(payload[0].payload.date), "MMMM yyyy"); } return value; }} formatter={(value, name) => { if (name === t('attendance.persentase')) { return [`${value}%`, name]; } return [value, name]; }} /> )}
); }