235 lines
7.9 KiB
Vue
235 lines
7.9 KiB
Vue
<script setup lang="ts">
|
|
const { $dayjs } = useNuxtApp()
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
const auth = useAuthStore()
|
|
|
|
// 🔹 State
|
|
const workingtimes = ref([])
|
|
const absencerequests = ref([])
|
|
const workingTimeInfo = ref(null)
|
|
|
|
const selectedPresetRange = ref("Dieser Monat bis heute")
|
|
const selectedStartDay = ref("")
|
|
const selectedEndDay = ref("")
|
|
const openTab = ref(0)
|
|
|
|
const showDocument = ref(false)
|
|
const uri = ref("")
|
|
const itemInfo = ref({})
|
|
|
|
function formatMinutesToHHMM(minutes = 0) {
|
|
const h = Math.floor(minutes / 60)
|
|
const m = Math.floor(minutes % 60)
|
|
return `${h}:${String(m).padStart(2, "0")} h`
|
|
}
|
|
|
|
// 📅 Zeitraumumschaltung
|
|
function changeRange() {
|
|
const rangeMap = {
|
|
"Diese Woche": { selector: "isoWeek", subtract: 0 },
|
|
"Dieser Monat": { selector: "M", subtract: 0 },
|
|
"Dieser Monat bis heute": { selector: "M", subtract: 0 },
|
|
"Dieses Jahr": { selector: "y", subtract: 0 },
|
|
"Letzte Woche": { selector: "isoWeek", subtract: 1 },
|
|
"Letzter Monat": { selector: "M", subtract: 1 },
|
|
"Letztes Jahr": { selector: "y", subtract: 1 }
|
|
}
|
|
|
|
const { selector, subtract } = rangeMap[selectedPresetRange.value] || { selector: "M", subtract: 0 }
|
|
|
|
selectedStartDay.value = $dayjs()
|
|
.subtract(subtract, selector === "isoWeek" ? "week" : selector)
|
|
.startOf(selector)
|
|
.format("YYYY-MM-DD")
|
|
|
|
selectedEndDay.value =
|
|
selectedPresetRange.value === "Dieser Monat bis heute"
|
|
? $dayjs().format("YYYY-MM-DD")
|
|
: $dayjs()
|
|
.subtract(subtract, selector === "isoWeek" ? "week" : selector)
|
|
.endOf(selector)
|
|
.format("YYYY-MM-DD")
|
|
|
|
loadWorkingTimeInfo()
|
|
}
|
|
|
|
const profile = ref(null)
|
|
|
|
// 📊 Daten laden
|
|
async function setupPage() {
|
|
await changeRange()
|
|
|
|
profile.value = (await useNuxtApp().$api("/api/tenant/profiles")).data.find(i => i.user_id === route.params.id)
|
|
|
|
console.log(profile.value)
|
|
|
|
}
|
|
|
|
async function loadWorkingTimeInfo() {
|
|
workingTimeInfo.value = await useFunctions().getWorkingTimesEvaluationData(
|
|
route.params.id,
|
|
selectedStartDay.value,
|
|
selectedEndDay.value
|
|
)
|
|
openTab.value = 0
|
|
}
|
|
|
|
// 📄 PDF generieren
|
|
async function generateDocument() {
|
|
const path = (await useEntities("letterheads").select("*"))[0].path // TODO SELECT
|
|
|
|
uri.value = await useFunctions().useCreatePDF({
|
|
full_name: profile.value.full_name,
|
|
...workingTimeInfo.value}, path, "timesheet")
|
|
|
|
|
|
showDocument.value = true
|
|
}
|
|
|
|
async function onTabChange(index: number) {
|
|
if (index === 1) await generateDocument()
|
|
}
|
|
|
|
// Initialisierung
|
|
await setupPage()
|
|
changeRange()
|
|
</script>
|
|
|
|
<template>
|
|
<UDashboardNavbar :ui="{ center: 'flex items-stretch gap-1.5 min-w-0' }">
|
|
<template #left>
|
|
<UButton
|
|
icon="i-heroicons-chevron-left"
|
|
variant="outline"
|
|
@click="router.push('/staff/time')"
|
|
>
|
|
Anwesenheiten
|
|
</UButton>
|
|
</template>
|
|
|
|
<template #center>
|
|
<h1 class="text-xl font-medium truncate">
|
|
Auswertung Anwesenheiten: {{ profile?.full_name || '' }}
|
|
</h1>
|
|
</template>
|
|
</UDashboardNavbar>
|
|
|
|
<UDashboardToolbar>
|
|
<template #left>
|
|
<UFormGroup label="Zeitraum:">
|
|
<USelectMenu
|
|
:options="[
|
|
'Dieser Monat bis heute',
|
|
'Diese Woche',
|
|
'Dieser Monat',
|
|
'Dieses Jahr',
|
|
'Letzte Woche',
|
|
'Letzter Monat',
|
|
'Letztes Jahr'
|
|
]"
|
|
v-model="selectedPresetRange"
|
|
@change="changeRange"
|
|
/>
|
|
</UFormGroup>
|
|
|
|
<UFormGroup label="Start:">
|
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
|
<UButton
|
|
icon="i-heroicons-calendar-days-20-solid"
|
|
:label="selectedStartDay ? $dayjs(selectedStartDay).format('DD.MM.YYYY') : 'Datum wählen'"
|
|
/>
|
|
<template #panel="{ close }">
|
|
<LazyDatePicker v-model="selectedStartDay" @close="loadWorkingTimeInfo" />
|
|
</template>
|
|
</UPopover>
|
|
</UFormGroup>
|
|
|
|
<UFormGroup label="Ende:">
|
|
<UPopover :popper="{ placement: 'bottom-start' }">
|
|
<UButton
|
|
icon="i-heroicons-calendar-days-20-solid"
|
|
:label="selectedEndDay ? $dayjs(selectedEndDay).format('DD.MM.YYYY') : 'Datum wählen'"
|
|
/>
|
|
<template #panel="{ close }">
|
|
<LazyDatePicker v-model="selectedEndDay" @close="loadWorkingTimeInfo" />
|
|
</template>
|
|
</UPopover>
|
|
</UFormGroup>
|
|
</template>
|
|
</UDashboardToolbar>
|
|
|
|
<UDashboardPanelContent>
|
|
<UTabs
|
|
:items="[{ label: 'Information' }, { label: 'Bericht' }]"
|
|
v-model="openTab"
|
|
@change="onTabChange"
|
|
>
|
|
<template #item="{ item }">
|
|
<div v-if="item.label === 'Information'">
|
|
<UCard v-if="workingTimeInfo" class="my-5">
|
|
<template #header>
|
|
<h3 class="text-base font-semibold">Zusammenfassung</h3>
|
|
</template>
|
|
<div class="grid grid-cols-2 gap-3 text-sm">
|
|
<p>Eingereicht: <b>{{ formatMinutesToHHMM(workingTimeInfo.sumWorkingMinutesEingereicht) }}</b></p>
|
|
<p>Genehmigt: <b>{{ formatMinutesToHHMM(workingTimeInfo.sumWorkingMinutesApproved) }}</b></p>
|
|
<p>Feiertagsausgleich: <b>{{ formatMinutesToHHMM(workingTimeInfo.sumWorkingMinutesRecreationDays) }}</b> / {{ workingTimeInfo.sumRecreationDays }} Tage</p>
|
|
<p>Urlaubs-/Berufsschulausgleich: <b>{{ formatMinutesToHHMM(workingTimeInfo.sumWorkingMinutesVacationDays) }}</b> / {{ workingTimeInfo.sumVacationDays }} Tage</p>
|
|
<p>Krankheitsausgleich: <b>{{ formatMinutesToHHMM(workingTimeInfo.sumWorkingMinutesSickDays) }}</b> / {{ workingTimeInfo.sumSickDays }} Tage</p>
|
|
<p>Soll-Stunden: <b>{{ formatMinutesToHHMM(workingTimeInfo.timeSpanWorkingMinutes) }}</b></p>
|
|
<p class="col-span-2">
|
|
Inoffizielles Saldo: <b>{{ (workingTimeInfo.saldoInOfficial >= 0 ? '+' : '-') + formatMinutesToHHMM(Math.abs(workingTimeInfo.saldoInOfficial)) }}</b>
|
|
</p>
|
|
<p class="col-span-2">
|
|
Saldo: <b>{{ (workingTimeInfo.saldo >= 0 ? '+' : '-') + formatMinutesToHHMM(Math.abs(workingTimeInfo.saldo)) }}</b>
|
|
</p>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UDashboardPanel>
|
|
<UTable
|
|
v-if="workingTimeInfo"
|
|
:rows="workingTimeInfo.times"
|
|
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Anwesenheiten' }"
|
|
:columns="[
|
|
{ key: 'state', label: 'Status' },
|
|
{ key: 'start', label: 'Start' },
|
|
{ key: 'end', label: 'Ende' },
|
|
{ key: 'duration', label: 'Dauer' },
|
|
{ key: 'description', label: 'Beschreibung' }
|
|
]"
|
|
@select="(row) => router.push(`/workingtimes/edit/${row.id}`)"
|
|
>
|
|
<template #state-data="{row}">
|
|
<span v-if="row.state === 'approved'" class="text-primary-500">Genehmigt</span>
|
|
<span v-else-if="row.state === 'submitted'" class="text-cyan-500">Eingereicht</span>
|
|
<span v-else-if="row.state === 'draft'" class="text-red-500">Entwurf</span>
|
|
</template>
|
|
|
|
<template #start-data="{ row }">
|
|
{{ $dayjs(row.startDate).format('HH:mm DD.MM.YY') }} Uhr
|
|
</template>
|
|
|
|
<template #end-data="{ row }">
|
|
{{ $dayjs(row.endDate).format('HH:mm DD.MM.YY') }} Uhr
|
|
</template>
|
|
|
|
<template #duration-data="{ row }">
|
|
{{ useFormatDuration(row.duration_minutes) }}
|
|
</template>
|
|
</UTable>
|
|
</UDashboardPanel>
|
|
</div>
|
|
|
|
<div v-else-if="item.label === 'Bericht'">
|
|
<PDFViewer
|
|
v-if="showDocument"
|
|
:uri="uri"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</UTabs>
|
|
</UDashboardPanelContent>
|
|
</template>
|