Commit 1710b41f by Nabiilah Putri Safa

edit log

1 parent 17be0b75
<script setup lang="ts">
import { onMounted, ref, watchEffect } from 'vue'
import { useKeycloakStore } from '@/@core/stores/keycloakStore'
// Store Keycloak
const keycloakStore = useKeycloakStore()
// Data dan state
const items = ref<any[]>([])
const loading = ref(false)
const searchQuery = ref('')
// Fungsi ambil tanggal dari shift_start atau shift_end
const getTanggal = (waktu: string) => (waktu ? waktu.split(' ')[0] : '-')
// Fungsi ambil jam dari start_time atau end_time, kasih "-" kalau kosong
const getJam = (waktu: string) => {
if (!waktu)
return '-'
return waktu.length >= 5 ? waktu.slice(0, 5) : waktu
}
// Fungsi ambil nama hari dari tanggal
const getNamaHari = (tanggal: string) => {
if (!tanggal || tanggal === '-')
return '-'
const date = new Date(tanggal)
if (isNaN(date.getTime()))
return '-'
return new Intl.DateTimeFormat('id-ID', { weekday: 'long' }).format(date)
}
const getJadwalShift = (start: string, end: string) => {
const jamStart = start && start.length >= 5 ? start.slice(0, 5) : '-'
const jamEnd = end && end.length >= 5 ? end.slice(0, 5) : '-'
return `${jamStart} - ${jamEnd}`
}
const getStatus = (start: string | undefined, end: string | undefined) => {
if (!start && !end)
return 'Tidak Ada'
if (!start || !end)
return 'Belum Hitung'
return 'On Time'
<script lang="ts" setup>
import { useKeycloakStore } from "@core/stores/keycloakStore";
const keycloakStore = useKeycloakStore();
const isAuthenticated = computed(() => keycloakStore.authenticated);
interface ShiftData {
shift_date: string;
shift_start: string;
shift_end: string;
shift: string;
start_time?: string;
end_time?: string;
}
// Header tabel
const logHeaders = [
{ title: 'TANGGAL', key: 'tanggal' },
{ title: 'NAMA HARI', key: 'namaHari' },
{ title: 'SHIFT', key: 'shift' },
{ title: 'JADWAL SHIFT', key: 'jadwalShift' },
{ title: 'MULAI AKTUAL', key: 'start_time' },
{ title: 'SELESAI AKTUAL', key: 'end_time' },
{ title: 'STATUS', key: 'status' },
]
// Fungsi ambil data dari API
async function getData() {
loading.value = true
items.value = []
const shifts = ref<ShiftData[]>([]);
const loading = ref(false);
const error = ref("");
const searchQuery = ref("");
const headersShift = [
{ title: "Tanggal", key: "shift_date", sortable: true },
{ title: "Nama Hari", key: "day_name", sortable: false },
{ title: "Shift", key: "shift", sortable: false },
{ title: "Jadwal Shift", key: "shift_schedule", sortable: false },
{ title: "Mulai Aktual", key: "start_time", sortable: false },
{ title: "Selesai Aktual", key: "end_time", sortable: false },
{ title: "Status", key: "status", sortable: false },
];
async function fetchShiftData() {
loading.value = true;
error.value = "";
try {
const apiEndpoint = 'https://api.ui.ac.id/my/hr/attendance'
const apiEndpoint = `https://api.ui.ac.id/my/hr/attendance`;
const response = await fetch(apiEndpoint, {
headers: {
Authorization: `Bearer ${keycloakStore.accessToken}`,
},
})
});
if (!response.ok)
throw new Error('Gagal mengambil data')
const dataku = await response.json()
if (!response.ok) {
throw new Error("Gagal fetch data");
}
// Tambahkan properti tanggal, namaHari, mulai aktual, dan selesai aktual ke setiap item
items.value = dataku.map((item: any) => {
const tanggal = getTanggal(item.shift_start) || getTanggal(item.shift_end)
const data = await response.json();
return {
shifts.value = data
.map((item: any) => ({
...item,
tanggal,
namaHari: getNamaHari(tanggal),
jadwalShift: getJadwalShift(item.shift_start, item.shift_end),
start_time: getJam(item.start_time),
end_time: getJam(item.end_time),
status: getStatus(item.start_time, item.end_time),
}
})
}
catch (err) {
console.error('Gagal mengambil data:', err)
}
finally {
loading.value = false
shift_start: `${item.shift_date} ${item.shift_start || "00:00"}`,
shift_end: `${item.shift_date} ${item.shift_end || "00:00"}`,
}))
.sort((a, b) => new Date(b.shift_date).getTime() - new Date(a.shift_date).getTime());
} catch (err: any) {
error.value = err.message || "Terjadi kesalahan saat mengambil data";
} finally {
loading.value = false;
}
}
// Fetch data saat mounted
function getDayName(date: string) {
const days = ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"];
const dayIndex = new Date(date).getDay();
return days[dayIndex];
}
const resolveDayColor = (day: string) => {
if (day === "Sabtu" || day === "Minggu") return "error";
};
function getHour(time: string | undefined) {
if (!time) return "-";
const parts = time.split(" ");
return parts.length === 2 ? parts[1] : time;
}
function getStatus(start: string | undefined, end: string | undefined) {
if (!start && !end) return "Tidak Ada";
if (!start || !end) return "Belum Hitung";
return "On Time";
}
const resolveStatusColor = (status: string) => {
if (status === "On Time") return "success";
if (status === "Belum Hitung") return "primary";
if (status === "Tidak Ada") return "error";
};
const filteredShifts = computed(() => {
if (!searchQuery.value) return shifts.value;
return shifts.value.filter((shift) => {
const dayName = getDayName(shift.shift_date);
const status = getStatus(shift.start_time, shift.end_time);
return (
shift.shift_date.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
dayName.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
shift.shift.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
shift.start_time?.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
shift.end_time?.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
status.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});
});
onMounted(() => {
keycloakStore.refresh()
getData()
})
// Auto refresh data saat token berubah
watchEffect(async () => {
if (!keycloakStore.accessToken)
return
await getData()
})
const filteredItems = computed(() => {
if (!searchQuery.value)
return items.value
const query = searchQuery.value.toLowerCase()
return items.value.filter(item =>
logHeaders.some(
header => item[header.key] && String(item[header.key]).toLowerCase().includes(query),
),
)
})
fetchShiftData();
});
</script>
<template>
<VCard
title="Log Absen"
class="recentnamaHariCard"
<AppCardActions
:title="`Log Absen`"
class="jadwalShift"
action-collapsed
action-remove
>
<!-- Search Input -->
<div class="search-container mb-4 pl-2 pr-2">
<VTextField
v-model="searchQuery"
......@@ -145,36 +132,101 @@ const filteredItems = computed(() => {
outlined
/>
</div>
<VDataTable
:headers="logHeaders"
:items="filteredItems"
hide-default-footer
fixed-header
item-value="tanggal"
:sort-by="['tanggal']"
:sort-asc="[true]"
:headers="headersShift"
:items="filteredShifts"
:loading="loading"
loading-text="Memuat data..."
>
<template #item.namaHari="{ item }">
<!-- Tanggal -->
<template #item.shift_date="{ item }">
{{ item.shift_date }}
</template>
<!-- Nama Hari -->
<template #item.day_name="{ item }">
<VChip
:color="item.namaHari === 'Sabtu' || item.namaHari === 'Minggu' ? 'error' : 'default'"
:class="`text-${item.namaHari === 'Sabtu' || item.namaHari === 'Minggu' ? 'error' : 'default'}`"
:color="resolveDayColor(getDayName(item.shift_date))"
:class="`text-${resolveDayColor(getDayName(item.shift_date))}`"
size="small"
class="font-weight-medium"
>
{{ item.namaHari }}
{{ getDayName(item.shift_date) }}
</VChip>
</template>
<!-- Jadwal Shift -->
<template #item.shift_schedule="{ item }">
{{ `${getHour(item.shift_start)} - ${getHour(item.shift_end)}` }}
</template>
<!-- Mulai Aktual -->
<template #item.start_time="{ item }">
{{ getHour(item.start_time) }}
</template>
<!-- Selesai Aktual -->
<template #item.end_time="{ item }">
{{ getHour(item.end_time) }}
</template>
<!-- Status -->
<template #item.status="{ item }">
<VChip
:color="item.status === 'Tidak Ada' ? 'error' : 'primary'"
:class="`text-${item.status === 'Tidak Ada' ? 'error' : 'primary'}`"
:color="resolveStatusColor(getStatus(item.start_time, item.end_time))"
:class="`text-${resolveStatusColor(getStatus(item.start_time, item.end_time))}`"
size="small"
class="font-weight-medium"
>
{{ item.status }}
{{ getStatus(item.start_time, item.end_time) }}
</VChip>
</template>
</VDataTable>
</VCard>
</AppCardActions>
</template>
<style lang="scss">
.jadwalShift {
.v-table {
&--density-default {
.v-table__wrapper {
table {
thead {
th {
background-color: #f5f5f5;
border-block-end: 2px solid #ddd;
color: #2c2c2c;
font-weight: 600;
padding-block: 12px;
padding-inline: 1.5em;
}
}
tbody {
tr {
td {
border-block-end: 1px solid #eee;
min-block-size: auto;
padding-block: 8px;
padding-inline: 1em;
vertical-align: top;
&.vti-table__td--Jadwal {
color: #4a4a4a;
font-weight: 500;
}
}
}
}
}
}
}
}
}
.search-container {
display: flex;
justify-content: flex-end;
}
</style>
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!