import { useAlert } from '../context/AlertContext'
import { useAuth } from '../context/AuthContext'
import Forbidden from '../components/Forbidden'
import { motion } from 'framer-motion'
import ConfirmModal from '../components/ConfirmModal'
import AdminDatePicker from '../components/AdminDatePicker'
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 FormField from '../components/FormField'
import { formatMinutes } from '../utils/attendanceHelpers'
interface UserTotalData {
name: string
minutes: number
working: boolean
vacation_hours: number
sick_hours: number
holiday_hours: number
unpaid_hours: number
fund: number | null
worked_hours: number
covered: number
missing: number
overtime: number
}
function getFundBarBackground(data: UserTotalData) {
if (data.overtime > 0) return 'linear-gradient(135deg, var(--warning), #d97706)'
if (data.covered >= (data.fund ?? 0)) return 'linear-gradient(135deg, var(--success), #059669)'
return 'var(--gradient)'
}
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.admin')) return
// Show skeleton 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 (
{[0, 1, 2].map(i => (
))}
{[0, 1, 2, 3, 4].map(i => (
))}
)
}
return (
Správa docházky
{hasData && (
)}
{/* Filters */}
setMonth(val)}
/>
{/* User Totals */}
{Object.keys(data.user_totals).length > 0 && (
{Object.entries(data.user_totals).map(([uid, userData]) => {
const ut = userData as UserTotalData
return (
{ut.name}
{ut.working ? '\u2713' : '\u2717'}
{formatMinutes(ut.minutes)}
odpracováno
{ut.vacation_hours > 0 && (
Dov: {ut.vacation_hours}h
)}
{ut.sick_hours > 0 && (
Nem: {ut.sick_hours}h
)}
{ut.holiday_hours > 0 && (
Sv: {ut.holiday_hours}h
)}
{ut.unpaid_hours > 0 && (
Nep: {ut.unpaid_hours}h
)}
{ut.fund !== null && (
Fond: {ut.worked_hours}h / {ut.fund}h
{ut.overtime > 0 && (
+{ut.overtime}h
)}
{ut.overtime <= 0 && ut.missing > 0 && (
-{ut.missing}h
)}
)}
{data.leave_balances[uid] && (
Zbývá dovolené: {data.leave_balances[uid].vacation_remaining.toFixed(1)}h / {data.leave_balances[uid].vacation_total}h
)}
)
})}
)}
{/* Records Table */}
setDeleteConfirm({ show: true, record })}
/>
{/* Modals */}
setShowBulkModal(false)}
form={bulkForm}
setForm={setBulkForm}
users={data.users}
onSubmit={handleBulkSubmit}
submitting={bulkSubmitting}
toggleUser={toggleBulkUser}
toggleAllUsers={toggleAllBulkUsers}
/>
setShowCreateModal(false)}
onSubmit={handleCreateSubmit}
form={createForm}
setForm={setCreateForm}
projectLogs={createProjectLogs}
setProjectLogs={setCreateProjectLogs}
projectList={projectList}
users={data.users}
onShiftDateChange={handleCreateShiftDateChange}
editingRecord={null}
/>
setShowEditModal(false)}
onSubmit={handleEditSubmit}
form={editForm}
setForm={setEditForm}
projectLogs={editProjectLogs}
setProjectLogs={setEditProjectLogs}
projectList={projectList}
users={data.users}
onShiftDateChange={handleCreateShiftDateChange}
editingRecord={editingRecord}
/>
setDeleteConfirm({ show: false, record: null })}
onConfirm={handleDelete}
title="Smazat záznam"
message="Opravdu chcete smazat tento záznam docházky?"
confirmText="Smazat"
confirmVariant="danger"
/>
)
}