103 lines
4.5 KiB
TypeScript
103 lines
4.5 KiB
TypeScript
import { Clock, LogIn, LogOut } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
|
|
type TodayAttendance = {
|
|
id: number;
|
|
attendance_date: string;
|
|
check_in_at: string | null;
|
|
check_out_at: string | null;
|
|
check_in_photo: string | null;
|
|
check_out_photo: string | null;
|
|
check_in_latitude: number;
|
|
check_in_longitude: number;
|
|
check_out_latitude: number | null;
|
|
check_out_longitude: number | null;
|
|
work_duration_minutes: number | null;
|
|
} | null;
|
|
|
|
type AttendanceCardProps = {
|
|
todayAttendance: TodayAttendance;
|
|
isOnLeave: boolean;
|
|
canCheckIn: boolean;
|
|
onCheckIn?: () => void;
|
|
onCheckOut?: () => void;
|
|
};
|
|
|
|
export function AttendanceCard({ todayAttendance, isOnLeave, canCheckIn, onCheckIn, onCheckOut }: AttendanceCardProps) {
|
|
const hasCheckedIn = !!todayAttendance?.check_in_at;
|
|
const hasCheckedOut = !!todayAttendance?.check_out_at;
|
|
|
|
function formatTime(dateStr: string | null): string {
|
|
if (!dateStr) {
|
|
return '-';
|
|
}
|
|
|
|
const d = new Date(dateStr);
|
|
|
|
return d.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex size-8 items-center justify-center rounded-md bg-muted">
|
|
<Clock className="size-4 text-muted-foreground" />
|
|
</div>
|
|
<CardTitle>Presensi Hari Ini</CardTitle>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{isOnLeave ? (
|
|
<div className="rounded-md bg-yellow-50 p-3 text-sm text-yellow-700 dark:bg-yellow-900/20 dark:text-yellow-400">
|
|
Anda sedang cuti hari ini.
|
|
</div>
|
|
) : !canCheckIn ? (
|
|
<div className="rounded-md bg-muted p-3 text-sm text-muted-foreground">
|
|
Anda tidak memiliki akses presensi.
|
|
</div>
|
|
) : (
|
|
<div className="space-y-3">
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div className="rounded-md bg-muted p-3">
|
|
<p className="text-xs text-muted-foreground">Masuk</p>
|
|
<p className="text-lg font-semibold">{formatTime(todayAttendance?.check_in_at ?? null)}</p>
|
|
</div>
|
|
<div className="rounded-md bg-muted p-3">
|
|
<p className="text-xs text-muted-foreground">Pulang</p>
|
|
<p className="text-lg font-semibold">{formatTime(todayAttendance?.check_out_at ?? null)}</p>
|
|
</div>
|
|
</div>
|
|
{todayAttendance?.work_duration_minutes != null && (
|
|
<div className="rounded-md bg-muted p-3">
|
|
<p className="text-xs text-muted-foreground">Durasi Kerja</p>
|
|
<p className="text-lg font-semibold">
|
|
{Math.floor(todayAttendance.work_duration_minutes / 60)}j {todayAttendance.work_duration_minutes % 60}m
|
|
</p>
|
|
</div>
|
|
)}
|
|
<div className="flex gap-2">
|
|
{!hasCheckedIn ? (
|
|
<Button onClick={onCheckIn} className="flex-1">
|
|
<LogIn className="mr-2 size-4" />
|
|
Presensi Masuk
|
|
</Button>
|
|
) : !hasCheckedOut ? (
|
|
<Button onClick={onCheckOut} className="flex-1" variant="destructive">
|
|
<LogOut className="mr-2 size-4" />
|
|
Presensi Pulang
|
|
</Button>
|
|
) : (
|
|
<div className="flex-1 rounded-md bg-green-50 p-3 text-center text-sm text-green-700 dark:bg-green-900/20 dark:text-green-400">
|
|
Presensi selesai untuk hari ini.
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|