155 lines
5.3 KiB
Vue
155 lines
5.3 KiB
Vue
<script setup lang="ts">
|
|
import { Head } from '@inertiajs/vue3';
|
|
import { Clock, LogIn, LogOut } from '@lucide/vue';
|
|
import { computed, ref } from 'vue';
|
|
import AttendanceCalendar from '@/components/admin/hr/attendances/AttendanceCalendar.vue';
|
|
import AttendanceWebcamModal from '@/components/admin/hr/attendances/AttendanceWebcamModal.vue';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
|
import type { AttendanceListItem, CalendarRange, TodayAttendance } from '@/types/attendance';
|
|
|
|
const props = defineProps<{
|
|
attendances: AttendanceListItem[];
|
|
todayAttendance: TodayAttendance;
|
|
isOnLeave: boolean;
|
|
canCheckIn: boolean;
|
|
canManageAll: boolean;
|
|
calendarRange: CalendarRange;
|
|
}>();
|
|
|
|
const webcamModalOpen = ref(false);
|
|
const webcamMode = ref<'check-in' | 'check-out'>('check-in');
|
|
|
|
const canCheckInToday = computed(() => props.canCheckIn && props.todayAttendance === null && !props.isOnLeave);
|
|
const canCheckOutToday = computed(() => (
|
|
props.canCheckIn
|
|
&& !props.isOnLeave
|
|
&& props.todayAttendance !== null
|
|
&& props.todayAttendance.check_out_at === null
|
|
));
|
|
|
|
const todayStatusLabel = computed(() => {
|
|
if (props.isOnLeave) {
|
|
return 'Sedang cuti';
|
|
}
|
|
|
|
if (!props.todayAttendance) {
|
|
return 'Belum presensi';
|
|
}
|
|
|
|
if (props.todayAttendance.check_out_at) {
|
|
return 'Selesai';
|
|
}
|
|
|
|
return 'Sudah masuk';
|
|
});
|
|
|
|
const todayStatusVariant = computed(() => {
|
|
if (props.isOnLeave) {
|
|
return 'secondary' as const;
|
|
}
|
|
|
|
if (!props.todayAttendance) {
|
|
return 'secondary' as const;
|
|
}
|
|
|
|
if (props.todayAttendance.check_out_at) {
|
|
return 'default' as const;
|
|
}
|
|
|
|
return 'outline' as const;
|
|
});
|
|
|
|
function openCheckInModal() {
|
|
webcamMode.value = 'check-in';
|
|
webcamModalOpen.value = true;
|
|
}
|
|
|
|
function openCheckOutModal() {
|
|
webcamMode.value = 'check-out';
|
|
webcamModalOpen.value = true;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
|
|
<Head title="Presensi" />
|
|
|
|
<AdminLayout>
|
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
<div class="space-y-1">
|
|
<h2 class="text-2xl font-bold tracking-tight">
|
|
Presensi
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<Card v-if="canCheckIn">
|
|
<CardHeader>
|
|
<CardTitle class="flex items-center gap-2">
|
|
<Clock class="size-5" />
|
|
Presensi Hari Ini
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Gunakan kamera perangkat untuk presensi masuk dan pulang.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent class="space-y-4">
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<Badge :variant="todayStatusVariant">
|
|
{{ todayStatusLabel }}
|
|
</Badge>
|
|
|
|
<span v-if="isOnLeave" class="text-muted-foreground text-sm">
|
|
Anda sedang dalam masa cuti. Presensi tidak diperlukan.
|
|
</span>
|
|
|
|
<span v-if="todayAttendance?.check_in_at_formatted" class="text-muted-foreground text-sm">
|
|
Masuk: {{ todayAttendance.check_in_at_formatted }}
|
|
</span>
|
|
|
|
<span v-if="todayAttendance?.check_out_at_formatted" class="text-muted-foreground text-sm">
|
|
Pulang: {{ todayAttendance.check_out_at_formatted }}
|
|
</span>
|
|
|
|
<span v-if="todayAttendance?.work_duration_formatted" class="text-muted-foreground text-sm">
|
|
Durasi: {{ todayAttendance.work_duration_formatted }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
<Button :disabled="!canCheckInToday" @click="openCheckInModal">
|
|
<LogIn class="size-4" />
|
|
Presensi Masuk
|
|
</Button>
|
|
|
|
<Button variant="outline" :disabled="!canCheckOutToday" @click="openCheckOutModal">
|
|
<LogOut class="size-4" />
|
|
Presensi Pulang
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card class="min-w-0 overflow-hidden">
|
|
<CardHeader class="border-b">
|
|
<CardTitle>Riwayat Presensi</CardTitle>
|
|
<CardDescription v-if="canManageAll">
|
|
Kalender presensi seluruh pegawai. Klik entri untuk melihat detail.
|
|
</CardDescription>
|
|
<CardDescription v-else>
|
|
Kalender riwayat presensi Anda. Klik entri untuk melihat detail.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent class="min-w-0">
|
|
<AttendanceCalendar :attendances="attendances" :calendar-range="calendarRange"
|
|
:can-manage-all="canManageAll" />
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<AttendanceWebcamModal v-if="canCheckIn" v-model:open="webcamModalOpen" :mode="webcamMode" />
|
|
</AdminLayout>
|
|
</template>
|