import { useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { CalendarDays, Plus, LogIn, LogOut } from "lucide-react";
import {
getTimesheet, getAttendance, getLeave, getOvertime, punch, createLeave,
createOvertime, cancelLeave,
} from "@/lib/api";
import {
Card, Button, Badge, Stat, PageHeader, Modal, Field, Input, Select,
Textarea, Tabs, Progress, EmptyState, LoadingState,
} from "@/components/ui";
import {
formatDate, formatTime, minutesToHM, REQ_STATUS_META, LEAVE_LABELS, classNames,
} from "@/lib/format";
import type { LeaveType } from "@/types";
const THIS_MONTH = new Date().toISOString().slice(0, 7);
export function AttendancePage() {
const qc = useQueryClient();
const [tab, setTab] = useState("timesheet");
const [leaveOpen, setLeaveOpen] = useState(false);
const [otOpen, setOtOpen] = useState(false);
const tsQ = useQuery({ queryKey: ["timesheet"], queryFn: () => getTimesheet({}) });
const attQ = useQuery({ queryKey: ["attendance", THIS_MONTH], queryFn: () => getAttendance({ month: THIS_MONTH }) });
const leaveQ = useQuery({ queryKey: ["leave-mine"], queryFn: () => getLeave() });
const otQ = useQuery({ queryKey: ["ot-mine"], queryFn: () => getOvertime() });
const punchM = useMutation({
mutationFn: punch,
onSuccess: () => { qc.invalidateQueries({ queryKey: ["attendance", THIS_MONTH] }); qc.invalidateQueries({ queryKey: ["timesheet"] }); },
});
const ts = tsQ.data;
return (
} onClick={() => punchM.mutate()}>출근
} onClick={() => punchM.mutate()}>퇴근
}
/>
{ts && (
월 근로 달성률
{ts.fulfillmentPct.toFixed(0)}%
)}
l.status === "pending").length },
{ key: "overtime", label: "초과근무", badge: otQ.data?.filter((o) => o.status === "pending").length },
]}
/>
{tab === "leave" && } onClick={() => setLeaveOpen(true)}>휴가 신청}
{tab === "overtime" && } onClick={() => setOtOpen(true)}>초과근무 신청}
{tab === "timesheet" && (
attQ.isLoading ?
: (attQ.data?.length ?? 0) === 0 ?
} /> : (
| 날짜 | 출근 | 퇴근 | 근무시간 | 비고 |
{attQ.data!.map((a) => (
| {formatDate(a.date)} |
{formatTime(a.clockIn)} |
{formatTime(a.clockOut)} |
{minutesToHM(a.workMinutes)} |
{a.note || "—"} |
))}
)
)}
{tab === "leave" && (
leaveQ.isLoading ?
: (leaveQ.data?.length ?? 0) === 0 ?
: (
| 종류 | 기간 | 일수 | 사유 | 상태 | |
{leaveQ.data!.map((l) => {
const m = REQ_STATUS_META[l.status];
return (
| {LEAVE_LABELS[l.type]} |
{formatDate(l.startDate)}{l.endDate && l.endDate !== l.startDate ? ` ~ ${formatDate(l.endDate)}` : ""} |
{l.days}일 |
{l.reason} |
|
{l.status === "pending" && } |
);
})}
)
)}
{tab === "overtime" && (
otQ.isLoading ?
: (otQ.data?.length ?? 0) === 0 ?
: (
| 날짜 | 시간 | 사유 | 상태 |
{otQ.data!.map((o) => {
const m = REQ_STATUS_META[o.status];
return (
| {formatDate(o.date)} |
{minutesToHM(o.minutes)} |
{o.reason} |
|
);
})}
)
)}
setLeaveOpen(false)} onDone={() => qc.invalidateQueries({ queryKey: ["leave-mine"] })} />
setOtOpen(false)} onDone={() => qc.invalidateQueries({ queryKey: ["ot-mine"] })} />
);
}
function LeaveModal({ open, onClose, onDone }: { open: boolean; onClose: () => void; onDone: () => void }) {
const [type, setType] = useState("annual");
const [startDate, setStart] = useState("");
const [endDate, setEnd] = useState("");
const [reason, setReason] = useState("");
const m = useMutation({
mutationFn: () => createLeave({ type, startDate, endDate: endDate || startDate, reason }),
onSuccess: () => { onDone(); onClose(); setReason(""); },
});
return (
>}
>
);
}
function OvertimeModal({ open, onClose, onDone }: { open: boolean; onClose: () => void; onDone: () => void }) {
const [date, setDate] = useState("");
const [hours, setHours] = useState("2");
const [reason, setReason] = useState("");
const m = useMutation({
mutationFn: () => createOvertime({ date, minutes: Math.round(parseFloat(hours) * 60), reason }),
onSuccess: () => { onDone(); onClose(); setReason(""); },
});
return (
>}
>
);
}