import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import BulkAttendanceModal from "../components/BulkAttendanceModal"; import ShiftFormModal from "../components/ShiftFormModal"; import AttendanceShiftTable from "../components/AttendanceShiftTable"; import useModalLock from "../hooks/useModalLock"; import useAttendanceAdmin from "../hooks/useAttendanceAdmin"; import { formatMinutes, formatHoursDecimal } from "../utils/attendanceHelpers"; import { Button, Card, ConfirmDialog, FilterBar, LoadingState, MonthField, PageEnter, Select, StatCard, StatusChip, ProgressBar, type ProgressColor, type StatCardColor, } from "../ui"; interface UserTotalData { name: string; minutes: number; working: boolean; vacation_hours: number; sick_hours: number; unpaid_hours: number; fund: number | null; worked_hours: number; // Raw worked hours incl. sub-day remainders (set by computeUserTotals in // useAttendanceAdmin); used for the Fond "used" calculation below. worked_hours_raw?: number; covered: number; missing: number; overtime: number; // mzda-style summary fields (set by computeUserTotals in useAttendanceAdmin) odpracovano?: number; vcetne_svatku?: number; prescas?: number; svatek?: number; worked_holiday_hours?: number; } /** Fond "used" total mirrors the Fond footer line below: * real_worked + vacation + worked_holiday + free_holiday. */ function getFondUsed(data: UserTotalData): number { const realWorked = data.worked_hours_raw ?? data.worked_hours; return ( realWorked + (data.vacation_hours ?? 0) + (data.worked_holiday_hours ?? 0) + (data.svatek ?? 0) ); } /** Maps the Fond delta (used − fund) to a ProgressBar color token, mirroring * the legacy getFundBarBackground conditional: over fund → warning, exactly * at fund → success, under fund → primary (was the accent gradient). */ function getFundBarColor(data: UserTotalData): ProgressColor { const delta = getFondUsed(data) - (data.fund ?? 0); if (delta > 0) return "warning"; if (delta >= 0) return "success"; return "primary"; } const PrintIcon = ( ); const AddIcon = ( ); export default function AttendanceAdmin() { const alert = useAlert(); const { hasPermission } = useAuth(); const { loading, month, setMonth, filterUserId, setFilterUserId, data, hasData, showBulkModal, setShowBulkModal, bulkSubmitting, bulkForm, setBulkForm, showCreateModal, setShowCreateModal, createForm, setCreateForm, showEditModal, setShowEditModal, editingRecord, editForm, setEditForm, deleteConfirm, setDeleteConfirm, projectList, createProjectLogs, setCreateProjectLogs, editProjectLogs, setEditProjectLogs, openCreateModal, handleCreateShiftDateChange, handleCreateSubmit, openBulkModal, toggleBulkUser, toggleAllBulkUsers, handleBulkSubmit, openEditModal, handleEditSubmit, handleDelete, handlePrint, } = useAttendanceAdmin({ alert }); useModalLock(showBulkModal); useModalLock(showEditModal); useModalLock(showCreateModal); if (!hasPermission("attendance.manage")) return ; // Show spinner only on initial load (no data yet), not on filter changes const isInitialLoad = loading && data.records.length === 0 && Object.keys(data.user_totals).length === 0; if (isInitialLoad) { return ; } const hasTotals = Object.keys(data.user_totals).length > 0; return ( Správa docházky {hasData && ( )} {/* Filters */} setMonth(val)} />