'use client'; import React, { useState } from 'react'; import { AttendanceWithEmployee, AttendanceType, AttendanceStatus } from '@/types/attendance'; import { Avatar } from '@/components/ui/Avatar'; import { Badge, BadgeVariant } from '@/components/ui/Badge'; import { Button } from '@/components/ui/Button'; import { Search, CheckCircle, XCircle, Trash2, ExternalLink, Building2, Home, Clock, FileHeart, CalendarDays, Plane, AlertCircle, Calendar, Download, Eye, } from 'lucide-react'; interface AttendanceTableProps { attendances: AttendanceWithEmployee[]; isSuperadmin: boolean; onUpdateStatus: (id: string, status: AttendanceStatus) => Promise; onDelete: (id: string) => Promise; } export const AttendanceTable: React.FC = ({ attendances, isSuperadmin, onUpdateStatus, onDelete, }) => { const [searchTerm, setSearchTerm] = useState(''); const [typeFilter, setTypeFilter] = useState('ALL'); const [statusFilter, setStatusFilter] = useState('ALL'); const [dateFilter, setDateFilter] = useState(''); const filtered = attendances.filter((item) => { const empName = item.employee?.full_name || ''; const empNik = item.employee?.nik || ''; const notes = item.reason_or_notes || ''; const matchesSearch = empName.toLowerCase().includes(searchTerm.toLowerCase()) || empNik.toLowerCase().includes(searchTerm.toLowerCase()) || notes.toLowerCase().includes(searchTerm.toLowerCase()); const matchesType = typeFilter === 'ALL' || item.type === typeFilter; const matchesStatus = statusFilter === 'ALL' || item.status === statusFilter; const matchesDate = !dateFilter || item.date === dateFilter; return matchesSearch && matchesType && matchesStatus && matchesDate; }); const getTypeBadgeVariant = (type: AttendanceType): BadgeVariant => { switch (type) { case 'PRESENT': return 'present'; case 'SICK': return 'sick'; case 'ANNUAL_LEAVE': return 'leave'; case 'LATE_PERMIT': return 'late'; case 'OFFICIAL_TRAVEL': return 'travel'; default: return 'neutral'; } }; const getTypeIcon = (type: AttendanceType) => { switch (type) { case 'PRESENT': return ; case 'SICK': return ; case 'ANNUAL_LEAVE': return ; case 'LATE_PERMIT': return ; case 'OFFICIAL_TRAVEL': return ; default: return ; } }; return (
{/* Filters Bar */}
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 text-sm bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary" />
{/* Specific Date Filter */}
setDateFilter(e.target.value)} className="bg-transparent text-xs font-semibold text-slate-700 focus:outline-none" /> {dateFilter && ( )}
{/* Type Filter */} {/* Status Filter */}
{/* Unified Table */}
{isSuperadmin && } {filtered.length === 0 ? ( ) : ( filtered.map((item) => ( {/* Date */} {/* Employee */} {/* Type & Mode */} {/* Timestamps */} {/* Reason & Attachment */} {/* Status */} {/* HR Superadmin Actions */} {isSuperadmin && ( )} )) )}
Tanggal Karyawan Klasifikasi Log Clock In / Out Keterangan / Lampiran StatusAksi HR
Tidak ada log absensi atau izin yang sesuai dengan filter.
{item.date}

{item.employee?.full_name || 'Unknown'}

{item.employee?.department}

{getTypeIcon(item.type)} {item.type.replace(/_/g, ' ')}
{item.work_mode !== 'OFF' && (
{item.work_mode === 'WFO' ? ( <> Office (WFO) ) : ( <> Remote (WFH) )}
)}
{item.clock_in ? (

In: {item.clock_in} {item.late_minutes > 0 && ( (+{item.late_minutes}m) )}

Out: {item.clock_out || (item.status === 'APPROVED' ? 'Aktif' : '-')}

) : ( - )}

{item.reason_or_notes || '-'}

{item.attachment_url && ( )}
{item.status}
{item.status === 'PENDING' && ( <> )}
); };