"use client" import { useMemo, useState } from "react" import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { LogInIcon, LogOutIcon, TimerIcon, SearchIcon } from "lucide-react" import { toast } from "sonner" import { useAuthApi } from "@/shared/useApi" import { Card, CardContent } from "@/shared/components/ui/card" import { Avatar, AvatarFallback } from "@/shared/components/ui/avatar" import { Badge } from "@/shared/components/ui/badge" import { Button } from "@/shared/components/ui/button" import { Input } from "@/shared/components/ui/input" import DashboardPage from "@/base/components/layout/dashboard/dashboard-page" import { EMPLOYEE_ROUTES } from "@garage/api" type Employee = { id: number first_name?: string last_name?: string email?: string designation?: string department?: { id?: number; name?: string } track_attendance?: boolean has_active_time_sheet?: boolean active_time_sheet?: { id?: number; clock_in?: string; date?: string } | null status?: string } function initialsOf(first?: string | null, last?: string | null) { const f = (first ?? "").trim()[0] ?? "" const l = (last ?? "").trim()[0] ?? "" return (f + l).toUpperCase() || "?" } function formatTime(value: unknown) { if (!value || typeof value !== "string") return "—" return value.length >= 5 ? value.slice(0, 5) : value } export default function TimeClocksPage() { const api = useAuthApi() const queryClient = useQueryClient() const [search, setSearch] = useState("") const employeesQuery = useQuery({ queryKey: [EMPLOYEE_ROUTES.INDEX, "time-clocks", { per_page: 100, status: "active" }], queryFn: () => api.employees.list({ per_page: 100, status: "active" } as any) as Promise<{ data: Employee[] }>, }) const employees: Employee[] = useMemo(() => { const rows: Employee[] = (employeesQuery.data as any)?.data ?? [] if (!search) return rows const q = search.toLowerCase() return rows.filter((e) => { const name = `${e.first_name ?? ""} ${e.last_name ?? ""}`.toLowerCase() return name.includes(q) || (e.email ?? "").toLowerCase().includes(q) }) }, [employeesQuery.data, search]) const refetch = () => queryClient.invalidateQueries({ queryKey: [EMPLOYEE_ROUTES.INDEX, "time-clocks", { per_page: 100, status: "active" }] }) const clockInMutation = useMutation({ mutationFn: (employeeId: number) => api.timeSheets.clockIn({ employee_id: employeeId } as any), onSuccess: () => { toast.success("Clocked in") refetch() }, onError: (err: any) => toast.error(err?.payload?.message ?? err?.message ?? "Failed to clock in"), }) const clockOutMutation = useMutation({ mutationFn: (timeSheetId: number) => api.timeSheets.clockOut({ time_sheet_id: timeSheetId } as any), onSuccess: () => { toast.success("Clocked out") refetch() }, onError: (err: any) => toast.error(err?.payload?.message ?? err?.message ?? "Failed to clock out"), }) return ( setSearch(e.target.value)} placeholder="Search employees..." className="pl-8" /> ), }} > {employeesQuery.isLoading ? (

Loading...

) : employees.length === 0 ? (

No employees found.

) : (
{employees.map((emp) => { const fullName = `${emp.first_name ?? ""} ${emp.last_name ?? ""}`.trim() || emp.email const isClockedIn = !!emp.has_active_time_sheet const activeId = emp.active_time_sheet?.id const clockedInAt = emp.active_time_sheet?.clock_in return (
{initialsOf(emp.first_name, emp.last_name)}
{fullName}
{emp.designation ?? emp.department?.name ?? emp.email}
{isClockedIn ? ( Since {formatTime(clockedInAt)} ) : ( Idle )}
{isClockedIn && activeId ? ( ) : ( )}
) })}
)}
) }