Redone Times
This commit is contained in:
@@ -4,6 +4,7 @@ const exports = ref([])
|
||||
|
||||
const auth = useAuthStore()
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
|
||||
const setupPage = async () => {
|
||||
exports.value = await useNuxtApp().$api("/api/exports",{
|
||||
@@ -56,7 +57,10 @@ const createExport = async () => {
|
||||
<template #right>
|
||||
<UButton
|
||||
@click="showCreateExportModal = true"
|
||||
>+ Export</UButton>
|
||||
>+ DATEV</UButton>
|
||||
<UButton
|
||||
@click="router.push('/export/create/sepa')"
|
||||
>+ SEPA</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<UTable
|
||||
234
pages/staff/time/[id]/evaluate.vue
Normal file
234
pages/staff/time/[id]/evaluate.vue
Normal file
@@ -0,0 +1,234 @@
|
||||
<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>
|
||||
173
pages/staff/time/index.vue
Normal file
173
pages/staff/time/index.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<script setup lang="ts">
|
||||
import { useStaffTime } from '~/composables/useStaffTime'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
|
||||
const { list, start, stop } = useStaffTime()
|
||||
const auth = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
const entries = ref([])
|
||||
const active = computed(() => entries.value.find(e => !e.stopped_at && e.user_id === auth.user.id))
|
||||
const loading = ref(false)
|
||||
const showModal = ref(false)
|
||||
const editEntry = ref(null)
|
||||
|
||||
// 👥 Nutzer-Filter (nur für Berechtigte)
|
||||
const users = ref([])
|
||||
const selectedUser = ref<string | null>(null)
|
||||
|
||||
const canViewAll = computed(() => auth.permissions.includes('staff.time.read_all'))
|
||||
|
||||
async function loadUsers() {
|
||||
if (!canViewAll.value) return
|
||||
// Beispiel: User aus Supabase holen
|
||||
const res = (await useNuxtApp().$api("/api/tenant/profiles")).data
|
||||
users.value = res
|
||||
}
|
||||
|
||||
async function load() {
|
||||
entries.value = await list(
|
||||
canViewAll.value && selectedUser.value ? { user_id: selectedUser.value } : undefined
|
||||
)
|
||||
}
|
||||
|
||||
async function handleStart() {
|
||||
loading.value = true
|
||||
await start('Arbeitszeit gestartet')
|
||||
await load()
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
async function handleStop() {
|
||||
if (!active.value) return
|
||||
loading.value = true
|
||||
await stop(active.value.id)
|
||||
await load()
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
function handleEdit(entry: any) {
|
||||
editEntry.value = entry
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadUsers()
|
||||
await load()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardNavbar title="Zeiterfassung" :badge="entries.length" />
|
||||
|
||||
<!-- TOOLBAR -->
|
||||
<UDashboardToolbar>
|
||||
<template #left>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-clock" class="text-primary-500" />
|
||||
<span v-if="active" class="text-primary-600 font-medium">
|
||||
Läuft seit {{ useNuxtApp().$dayjs(active.started_at).format('HH:mm') }}
|
||||
</span>
|
||||
<span v-else class="text-gray-500">Keine aktive Zeit</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<UButton
|
||||
v-if="active"
|
||||
color="red"
|
||||
icon="i-heroicons-stop"
|
||||
:loading="loading"
|
||||
label="Stoppen"
|
||||
@click="handleStop"
|
||||
/>
|
||||
<UButton
|
||||
v-else
|
||||
color="green"
|
||||
icon="i-heroicons-play"
|
||||
:loading="loading"
|
||||
label="Starten"
|
||||
@click="handleStart"
|
||||
/>
|
||||
<UButton
|
||||
color="primary"
|
||||
icon="i-heroicons-plus"
|
||||
label="Zeit"
|
||||
@click="() => { editEntry = null; showModal = true }"
|
||||
/>
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
<UDashboardToolbar>
|
||||
<template #left>
|
||||
<!-- 👥 User-Filter (nur bei Berechtigung) -->
|
||||
<div v-if="canViewAll" class="flex items-center gap-2">
|
||||
<USelectMenu
|
||||
v-model="selectedUser"
|
||||
:options="[
|
||||
{ label: 'Alle Benutzer', value: null },
|
||||
...users.map(u => ({ label: u.full_name || u.email, value: u.user_id }))
|
||||
]"
|
||||
placeholder="Benutzer auswählen"
|
||||
value-attribute="value"
|
||||
option-attribute="label"
|
||||
class="min-w-[220px]"
|
||||
@change="load"
|
||||
/>
|
||||
|
||||
<!-- 🔹 Button zur Auswertung -->
|
||||
<UButton
|
||||
v-if="selectedUser"
|
||||
color="gray"
|
||||
icon="i-heroicons-chart-bar"
|
||||
label="Auswertung"
|
||||
variant="soft"
|
||||
@click="router.push(`/staff/time/${selectedUser}/evaluate`)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
|
||||
<!-- TABELLE -->
|
||||
<UDashboardPanelContent>
|
||||
<UTable
|
||||
:rows="entries"
|
||||
@select="(row) => handleEdit(row)"
|
||||
:columns="[
|
||||
{ key: 'state', label: 'Status' },
|
||||
{ key: 'started_at', label: 'Start' },
|
||||
{ key: 'stopped_at', label: 'Ende' },
|
||||
{ key: 'duration_minutes', label: 'Dauer' },
|
||||
{ key: 'description', label: 'Beschreibung' },
|
||||
...(canViewAll ? [{ key: 'user_name', label: 'Benutzer' }] : []),
|
||||
{ key: 'actions', label: '' },
|
||||
]"
|
||||
>
|
||||
<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 #started_at-data="{ row }">
|
||||
{{ useNuxtApp().$dayjs(row.started_at).format('DD.MM.YY HH:mm') }}
|
||||
</template>
|
||||
<template #stopped_at-data="{ row }">
|
||||
<span v-if="row.stopped_at">
|
||||
{{ useNuxtApp().$dayjs(row.stopped_at).format('DD.MM.YY HH:mm') }}
|
||||
</span>
|
||||
<span v-else class="text-primary-500 font-medium">läuft...</span>
|
||||
</template>
|
||||
<template #duration_minutes-data="{ row }">
|
||||
{{ row.duration_minutes ? useFormatDuration(row.duration_minutes) : '-' }}
|
||||
</template>
|
||||
<template #user_name-data="{ row }">
|
||||
{{ row.user_id ? users.find(i => i.user_id === row.user_id).full_name : '-' }}
|
||||
</template>
|
||||
<template #actions-data="{ row }">
|
||||
<UButton variant="ghost" icon="i-heroicons-pencil-square" @click="handleEdit(row)" />
|
||||
</template>
|
||||
</UTable>
|
||||
</UDashboardPanelContent>
|
||||
|
||||
<!-- MODAL -->
|
||||
<StaffTimeEntryModal v-model="showModal" :entry="editEntry" @saved="load" />
|
||||
</template>
|
||||
@@ -1,150 +0,0 @@
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const profileStore = useProfileStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const id = ref(route.params.id ? route.params.id : null )
|
||||
|
||||
const mode = ref(route.params.mode || "show")
|
||||
const itemInfo = ref({
|
||||
startDate: new Date(),
|
||||
endDate: new Date(),
|
||||
profile: profileStore.activeProfile.id
|
||||
})
|
||||
const oldItemInfo = ref({})
|
||||
|
||||
const setupPage = () => {
|
||||
if(route.params.id && mode.value === 'edit') {
|
||||
itemInfo.value = dataStore.getWorkingTimeById(Number(route.params.id))
|
||||
//setStartEnd()
|
||||
}
|
||||
oldItemInfo.value = itemInfo.value
|
||||
|
||||
if(route.query) {
|
||||
if(route.query.profile) itemInfo.value.profile = route.query.profile
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*const setStartEnd = () => {
|
||||
console.log("test")
|
||||
console.log(String(itemInfo.value.date).split("T")[0])
|
||||
itemInfo.value.date = String(itemInfo.value.date).split("T")[0]
|
||||
itemInfo.value.start = new Date(itemInfo.value.date + "T" + itemInfo.value.start)
|
||||
itemInfo.value.end = new Date(itemInfo.value.date + "T" + itemInfo.value.end)
|
||||
}*/
|
||||
|
||||
setupPage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardNavbar
|
||||
:title="mode === 'show' ? `Anwesenheit: ${itemInfo.profile}` : (itemInfo.id ? 'Anwesenheit bearbeiten' :'Anwesenheit erstellen')"
|
||||
>
|
||||
<template #left>
|
||||
<UButton
|
||||
icon="i-heroicons-chevron-left"
|
||||
variant="outline"
|
||||
@click="router.push(`/workingtimes`)"
|
||||
>
|
||||
Anwesenheiten
|
||||
</UButton>
|
||||
</template>
|
||||
<template #center>
|
||||
<h1
|
||||
v-if="itemInfo"
|
||||
class="text-xl font-medium"
|
||||
>{{itemInfo.id ? 'Anwesenheit bearbeiten' : 'Anwesenheit erstellen'}}</h1>
|
||||
</template>
|
||||
<template #right>
|
||||
<UButton
|
||||
color="rose"
|
||||
v-if="mode === 'edit'"
|
||||
@click="router.push('/workingtimes')"
|
||||
>
|
||||
Abbrechen
|
||||
</UButton>
|
||||
<UButton
|
||||
v-if="mode === 'edit' && itemInfo.id"
|
||||
@click="dataStore.updateItem('workingtimes',itemInfo)"
|
||||
>
|
||||
Speichern
|
||||
</UButton>
|
||||
<UButton
|
||||
v-if="mode === 'edit' && itemInfo.id"
|
||||
@click="dataStore.updateItem('workingtimes',{...itemInfo, approved: true})"
|
||||
>
|
||||
Genehmigen & Speichern
|
||||
</UButton>
|
||||
<UButton
|
||||
v-else-if="mode === 'edit' && !itemInfo.id"
|
||||
@click="dataStore.createNewItem('workingtimes',itemInfo)"
|
||||
>
|
||||
Erstellen
|
||||
</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UForm class="p-5">
|
||||
<UFormGroup
|
||||
label="Mitarbeiter:"
|
||||
>
|
||||
<USelectMenu
|
||||
:options="profileStore.profiles"
|
||||
v-model="itemInfo.profile"
|
||||
option-attribute="fullName"
|
||||
value-attribute="id"
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Start:">
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton
|
||||
icon="i-heroicons-calendar-days-20-solid"
|
||||
:label="itemInfo.startDate ? dayjs(itemInfo.startDate).format('HH:mm') : 'Datum auswählen'"
|
||||
variant="outline"
|
||||
/>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="itemInfo.startDate" @close="itemInfo.endDate = itemInfo.startDate" mode="dateTime"/>
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Ende:">
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton
|
||||
variant="outline"
|
||||
icon="i-heroicons-calendar-days-20-solid"
|
||||
:label="itemInfo.endDate ? dayjs(itemInfo.endDate).format('HH:mm') : 'Datum auswählen'"
|
||||
/>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="itemInfo.endDate" @close="close" mode="time"/>
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Genehmigt:"
|
||||
>
|
||||
<UCheckbox
|
||||
v-model="itemInfo.approved"
|
||||
/>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Notizen:"
|
||||
>
|
||||
<UTextarea
|
||||
v-model="itemInfo.notes"
|
||||
/>
|
||||
</UFormGroup>
|
||||
</UForm>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,291 +0,0 @@
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||
import isoWeek from "dayjs/plugin/isoWeek";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
|
||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||
import {useCreateWorkingTimesPdf} from "~/composables/useWorkingTimePDFGenerator.js";
|
||||
import {useFunctions} from "~/composables/useFunctions.js";
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(isoWeek)
|
||||
dayjs.extend(isBetween)
|
||||
dayjs.extend(isSameOrAfter)
|
||||
dayjs.extend(isSameOrBefore)
|
||||
|
||||
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const profileStore = useProfileStore()
|
||||
const supabase = useSupabaseClient()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const itemInfo = ref({})
|
||||
const oldItemInfo = ref({})
|
||||
|
||||
const workingtimes = ref([])
|
||||
const absencerequests = ref([])
|
||||
|
||||
const workingTimeInfo = ref(null)
|
||||
const selectedPresetRange = ref("Dieser Monat bis heute")
|
||||
const selectedStartDay = ref("")
|
||||
const selectedEndDay = ref("")
|
||||
|
||||
const setupPage = async () => {
|
||||
if(route.params.id) itemInfo.value = profileStore.getProfileById(route.params.id)
|
||||
//if(itemInfo.value.id) oldItemInfo.value = JSON.parse(JSON.stringify(itemInfo.value))
|
||||
workingtimes.value = (await supabase.from("workingtimes").select().eq("profile",itemInfo.value.id).order("startDate",{ascending:false})).data
|
||||
absencerequests.value = (await supabase.from("absencerequests").select().eq("profile",itemInfo.value.id).order("startDate",{ascending: false})).data
|
||||
|
||||
await loadWorkingTimeInfo()
|
||||
|
||||
}
|
||||
|
||||
const loadWorkingTimeInfo = async () => {
|
||||
workingTimeInfo.value = await useFunctions().getWorkingTimesEvaluationData(route.params.id,selectedStartDay.value,selectedEndDay.value)
|
||||
console.log(workingTimeInfo.value)
|
||||
openTab.value = 0
|
||||
}
|
||||
|
||||
|
||||
const changeRange = () => {
|
||||
let selector = "M"
|
||||
let subtract = 0
|
||||
|
||||
if(selectedPresetRange.value === "Diese Woche") {
|
||||
selector = "isoWeek"
|
||||
subtract = 0
|
||||
|
||||
} else if(selectedPresetRange.value === "Dieser Monat") {
|
||||
selector = "M"
|
||||
subtract = 0
|
||||
|
||||
} else if(selectedPresetRange.value === "Dieser Monat bis heute") {
|
||||
selector = "M"
|
||||
subtract = 0
|
||||
|
||||
} else if(selectedPresetRange.value === "Dieses Jahr") {
|
||||
selector = "y"
|
||||
subtract = 0
|
||||
} else if(selectedPresetRange.value === "Letzte Woche") {
|
||||
selector = "isoWeek"
|
||||
subtract = 1
|
||||
} else if(selectedPresetRange.value === "Letzter Monat") {
|
||||
selector = "M"
|
||||
subtract = 1
|
||||
} else if(selectedPresetRange.value === "Letztes Jahr") {
|
||||
selector = "y"
|
||||
subtract = 1
|
||||
}
|
||||
|
||||
selectedStartDay.value = dayjs().subtract(subtract,selector === "isoWeek" ? "week" : selector).startOf(selector).format("YYYY-MM-DD")
|
||||
if(selectedPresetRange.value === "Dieser Monat bis heute") {
|
||||
selectedEndDay.value = dayjs().format("YYYY-MM-DD")
|
||||
} else {
|
||||
selectedEndDay.value = dayjs().subtract(subtract,selector === "isoWeek" ? "week" : selector).endOf(selector).format("YYYY-MM-DD")
|
||||
|
||||
}
|
||||
|
||||
openTab.value = 0
|
||||
loadWorkingTimeInfo()
|
||||
}
|
||||
|
||||
const getDuration = (time) => {
|
||||
const minutes = Math.floor(dayjs(time.endDate).diff(dayjs(time.startDate),'minutes',true))
|
||||
const hours = Math.floor(minutes/60)
|
||||
return {
|
||||
//dezimal: dez,
|
||||
hours: hours,
|
||||
minutes: minutes,
|
||||
composed: `${hours}:${String(minutes % 60).padStart(2,"0")} h`
|
||||
}
|
||||
}
|
||||
|
||||
const showDocument = ref(false)
|
||||
const uri = ref("")
|
||||
const generateDocument = async () => {
|
||||
const ownTenant = profileStore.ownTenant
|
||||
const path = (await supabase.from("letterheads").select().eq("tenant",profileStore.currentTenant)).data[0].path
|
||||
console.log(path)
|
||||
|
||||
const {data,error} = await supabase.storage.from("files").download(path)
|
||||
|
||||
uri.value = await useCreateWorkingTimesPdf({
|
||||
profile: profileStore.getProfileById(route.params.id).fullName,
|
||||
...workingTimeInfo.value}, await data.arrayBuffer())
|
||||
//alert(uri.value)
|
||||
showDocument.value = true
|
||||
}
|
||||
|
||||
const generateEvaluation = async () => {
|
||||
await generateDocument()
|
||||
}
|
||||
|
||||
const openTab = ref(0)
|
||||
const onTabChange = async (index) => {
|
||||
if(index === 1) {
|
||||
await generateEvaluation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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(`/workingtimes`)"
|
||||
>
|
||||
Anwesenheiten
|
||||
</UButton>
|
||||
</template>
|
||||
<template #center>
|
||||
<h1
|
||||
v-if="itemInfo"
|
||||
:class="['text-xl','font-medium']"
|
||||
>{{itemInfo ? `Auswertung Anwesenheiten: ${itemInfo.fullName}` : ``}}</h1>
|
||||
</template>
|
||||
|
||||
</UDashboardNavbar>
|
||||
<UDashboardToolbar>
|
||||
<template #left>
|
||||
<UFormGroup
|
||||
label="Vorlage:"
|
||||
>
|
||||
<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 auswä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 auswählen'"
|
||||
/>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="selectedEndDay" @close="loadWorkingTimeInfo" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
<UDashboardPanelContent>
|
||||
<UTabs
|
||||
:items="[{label: 'Information'},{label: 'Bericht'}]"
|
||||
@change="onTabChange"
|
||||
v-model="openTab"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<div v-if="item.label === 'Information'">
|
||||
<UCard class="truncate my-5" v-if="workingTimeInfo">
|
||||
<template #header>
|
||||
Zusammenfassung
|
||||
</template>
|
||||
<p>Eingreicht: {{Math.floor(workingTimeInfo.sumWorkingMinutesEingereicht/60)}}:{{String(workingTimeInfo.sumWorkingMinutesEingereicht % 60).padStart(2,"0")}} h</p>
|
||||
<p>Genehmigt: {{Math.floor(workingTimeInfo.sumWorkingMinutesApproved/60)}}:{{String(workingTimeInfo.sumWorkingMinutesApproved % 60).padStart(2,"0")}} h</p>
|
||||
<p>Feiertagsausgleich: {{Math.floor(workingTimeInfo.sumWorkingMinutesRecreationDays/60)}}:{{String(workingTimeInfo.sumWorkingMinutesRecreationDays % 60).padStart(2,"0")}} h / {{workingTimeInfo.sumRecreationDays}} Tage</p>
|
||||
<p>Urlaubs-/Berufsschulausgleich: {{Math.floor(workingTimeInfo.sumWorkingMinutesVacationDays/60)}}:{{String(workingTimeInfo.sumWorkingMinutesVacationDays % 60).padStart(2,"0")}} h / {{workingTimeInfo.sumVacationDays}} Tage</p>
|
||||
<p>Krankheitsausgleich: {{Math.floor(workingTimeInfo.sumWorkingMinutesSickDays/60)}}:{{String(workingTimeInfo.sumWorkingMinutesSickDays % 60).padStart(2,"0")}} h / {{workingTimeInfo.sumSickDays}} Tage</p>
|
||||
<p>Soll Stunden: {{Math.floor(workingTimeInfo.timeSpanWorkingMinutes/60)}}:{{String(workingTimeInfo.timeSpanWorkingMinutes % 60 ).padStart(2,"0")}} h</p>
|
||||
|
||||
<!-- <p>Abwesend: </p>
|
||||
|
||||
<p>Ausgleich:</p>
|
||||
-->
|
||||
<p>Inoffizielles Saldo(eingereichte Stunden): {{Math.sign(workingTimeInfo.saldoInOfficial) === 1 ? "+" : "-"}} {{Math.floor(Math.abs(workingTimeInfo.saldoInOfficial/60))}}:{{String(Math.abs(workingTimeInfo.saldoInOfficial) % 60).padStart(2,"0")}} h</p>
|
||||
<p>Saldo(genehmigte Stunden): {{Math.sign(workingTimeInfo.saldo) === 1 ? "+" : "-"}} {{Math.floor(Math.abs(workingTimeInfo.saldo/60))}}:{{String(Math.abs(workingTimeInfo.saldo) % 60).padStart(2,"0")}} h</p>
|
||||
</UCard>
|
||||
|
||||
<div style="overflow-y: scroll; height: 45vh">
|
||||
<UTable
|
||||
v-if="workingTimeInfo"
|
||||
:rows="workingTimeInfo.times"
|
||||
@select="(row) => router.push(`/workingtimes/edit/${row.id}`)"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: `Keine Anwesenheiten anzuzeigen` }"
|
||||
:columns="[
|
||||
{
|
||||
key: 'state',
|
||||
label: 'Status'
|
||||
}, {
|
||||
key: 'approved',
|
||||
label: 'Genehmigt'
|
||||
}, {
|
||||
key: 'start',
|
||||
label: 'Start'
|
||||
}, {
|
||||
key: 'end',
|
||||
label: 'Ende'
|
||||
}, {
|
||||
key: 'duration',
|
||||
label: 'Dauer'
|
||||
}, {
|
||||
key: 'notes',
|
||||
label: 'Notizen'
|
||||
}
|
||||
]"
|
||||
>
|
||||
<template #profile-data="{row}">
|
||||
{{profileStore.profiles.find(profile => profile.id === row.profile) ? profileStore.profiles.find(profile => profile.id === row.profile).fullName : row.profile }}
|
||||
</template>
|
||||
<template #approved-data="{row}">
|
||||
<span v-if="row.approved" class="text-primary-500">Ja</span>
|
||||
<span v-else class="text-rose-600">Nein</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}">
|
||||
{{getDuration(row).composed}}
|
||||
</template>
|
||||
</UTable>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div v-else-if="item.label === 'Bericht'">
|
||||
<UProgress animation="carousel" v-if="!showDocument"/>
|
||||
<object
|
||||
:data="uri"
|
||||
v-else
|
||||
type="application/pdf"
|
||||
class="w-full previewDocument"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UTabs>
|
||||
</UDashboardPanelContent>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.previewDocument {
|
||||
height: 80vh;
|
||||
}
|
||||
</style>
|
||||
@@ -1,327 +0,0 @@
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat"
|
||||
import {useCapacitor} from "~/composables/useCapacitor.js";
|
||||
import {setPageLayout} from "#app";
|
||||
import FloatingActionButton from "~/components/mobile/FloatingActionButton.vue";
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const supabase = useSupabaseClient()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
|
||||
const platform = await useCapacitor().getIsPhone() ? "mobile" : "default"
|
||||
|
||||
|
||||
const filterUser = ref(auth.profile.old_profile_id || "c149b249-5baf-43a2-9cc0-1947d3710a43")
|
||||
const profiles = ref([])
|
||||
const selectableProfiles = ref([])
|
||||
|
||||
const workingtimes = ref([])
|
||||
|
||||
const setupPage = async () => {
|
||||
if(platform === "mobile") {
|
||||
setPageLayout("mobile")
|
||||
}
|
||||
|
||||
if(route.query) {
|
||||
if(route.query.profile) filterUser.value = route.query.profile
|
||||
}
|
||||
|
||||
//workingtimes.value = (await supabase.from("workingtimes").select().eq("profile",filterUser.value).order("startDate",{ascending: false})).data
|
||||
workingtimes.value = (await useEntities("workingtimes").select(null,"startDate",false))
|
||||
|
||||
let res = await useNuxtApp().$api("/api/tenant/profiles")
|
||||
console.log(res)
|
||||
|
||||
|
||||
profiles.value = res.data
|
||||
selectableProfiles.value = res.data.filter(i => i.old_profile_id)
|
||||
|
||||
console.log(profiles.value)
|
||||
console.log(selectableProfiles.value)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const changeFilterUser = async () => {
|
||||
await router.push(`/workingtimes/?profile=${filterUser.value}`)
|
||||
await setupPage()
|
||||
}
|
||||
|
||||
setupPage()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const filteredRows = computed(() => {
|
||||
|
||||
let times = workingtimes.value
|
||||
|
||||
times = times.filter(i => i.profile === filterUser.value)
|
||||
|
||||
|
||||
return times/*.map(i => {
|
||||
return {
|
||||
...i,
|
||||
disabledExpand: i.approved
|
||||
}
|
||||
})*/
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
const itemInfo = ref({
|
||||
profile: "",
|
||||
start: new Date(),
|
||||
end: "",
|
||||
notes: null,
|
||||
})
|
||||
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key:"state",
|
||||
label: "Status"
|
||||
},
|
||||
{
|
||||
key: "approved",
|
||||
label: "Genehmigt"
|
||||
},
|
||||
{
|
||||
key: "profile",
|
||||
label: "Mitarbeiter"
|
||||
},
|
||||
{
|
||||
key: "date",
|
||||
label: "Datum"
|
||||
},
|
||||
{
|
||||
key:"startDate",
|
||||
label:"Start"
|
||||
},
|
||||
{
|
||||
key: "endDate",
|
||||
label: "Ende"
|
||||
},
|
||||
{
|
||||
key: "duration",
|
||||
label: "Dauer"
|
||||
},
|
||||
{
|
||||
key: "notes",
|
||||
label: "Notizen"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const getDuration = (time) => {
|
||||
const minutes = Math.floor(dayjs(time.endDate).diff(dayjs(time.startDate),'minutes',true))
|
||||
const hours = Math.floor(minutes/60)
|
||||
return {
|
||||
//dezimal: dez,
|
||||
hours: hours,
|
||||
minutes: minutes,
|
||||
composed: `${hours}:${String(minutes % 60).padStart(2,"0")} h`
|
||||
}
|
||||
}
|
||||
|
||||
const updateWorkingTime = async (data) => {
|
||||
await dataStore.updateItem('workingtimes',data)
|
||||
await setupPage()
|
||||
}
|
||||
|
||||
const toggleRow = (row) => {
|
||||
if(expand.value.openedRows.includes(row)){
|
||||
expand.value.openedRows = []
|
||||
} else {
|
||||
expand.value.openedRows = [row]
|
||||
}
|
||||
}
|
||||
|
||||
const expand = ref({
|
||||
openedRows: [],
|
||||
row: {}
|
||||
})
|
||||
|
||||
const setEndDate = (row) => {
|
||||
row.startDate = dayjs(row.startDate).toISOString()
|
||||
|
||||
row.endDate = dayjs(row.endDate).set("year", dayjs(row.startDate).get("year")).set("month", dayjs(row.startDate).get("month")).set("date", dayjs(row.startDate).get("date"))
|
||||
|
||||
row.endDate = dayjs(row.endDate).toISOString()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <FloatingActionButton
|
||||
:label="`+ Anwesenheit`"
|
||||
variant="outline"
|
||||
v-if="platform === 'mobile'"
|
||||
@click="router.push(`/workingtimes/create`)"
|
||||
:pos="0"
|
||||
/>
|
||||
<FloatingActionButton
|
||||
:label="`Auswertung`"
|
||||
variant="outline"
|
||||
v-if="platform === 'mobile'"
|
||||
@click="router.push(`/workingtimes/evaluate/${profileStore.activeProfile.id}`)"
|
||||
:pos="1"
|
||||
/>-->
|
||||
<UDashboardNavbar title="Anwesenheiten" v-if="platform !== 'mobile'" :badge="filteredRows.length">
|
||||
<template #right>
|
||||
<UButton
|
||||
@click="router.push(`/workingtimes/edit?profile=${filterUser}`)"
|
||||
disabled
|
||||
>
|
||||
+ Anwesenheit
|
||||
</UButton>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<UDashboardNavbar title="Anwesenheiten" v-else>
|
||||
<template #toggle>
|
||||
<div></div>
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
<UDashboardToolbar v-if="platform !== 'mobile'">
|
||||
<template #left>
|
||||
|
||||
<USelectMenu
|
||||
:options="selectableProfiles"
|
||||
option-attribute="full_name"
|
||||
value-attribute="old_profile_id"
|
||||
v-model="filterUser"
|
||||
@change="changeFilterUser"
|
||||
>
|
||||
<template #label>
|
||||
{{ selectableProfiles.find(i => i.old_profile_id === filterUser) ? selectableProfiles.find(i => i.old_profile_id === filterUser).full_name : "Kein Benutzer ausgewählt"}}
|
||||
</template>
|
||||
</USelectMenu>
|
||||
<UButton
|
||||
@click="router.push(`/workingtimes/evaluate/${filterUser}`)"
|
||||
>
|
||||
Auswertung
|
||||
</UButton>
|
||||
</template>
|
||||
</UDashboardToolbar>
|
||||
|
||||
<UTable
|
||||
class="mt-3"
|
||||
:columns="columns"
|
||||
:rows="filteredRows"
|
||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Noch keine Einträge' }"
|
||||
@select="(i) => toggleRow(i)"
|
||||
v-model:expand="expand"
|
||||
:multiple-expand="false"
|
||||
|
||||
>
|
||||
<template #expand="{ row }">
|
||||
<InputGroup class="p-4">
|
||||
<UTooltip text="Genehmigen & Speichern" v-if="!row.approved">
|
||||
<UButton
|
||||
@click="updateWorkingTime({...row, approved: true})"
|
||||
icon="i-heroicons-check"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UTooltip text="Speichern" v-if="!row.approved">
|
||||
<UButton
|
||||
@click="updateWorkingTime(row)"
|
||||
icon="i-mdi-content-save"
|
||||
variant="outline"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UTooltip text="Bearbeiten">
|
||||
<UButton
|
||||
@click="router.push(`/workingtimes/edit/${row.id}`)"
|
||||
variant="outline"
|
||||
icon="i-heroicons-pencil-solid"
|
||||
/>
|
||||
</UTooltip>
|
||||
</InputGroup>
|
||||
<div class="p-4" v-if="!row.approved">
|
||||
<UFormGroup label="Start:">
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton
|
||||
icon="i-heroicons-calendar-days-20-solid"
|
||||
:label="row.startDate ? dayjs(row.startDate).format('HH:mm') : 'Datum auswählen'"
|
||||
variant="outline"
|
||||
/>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="row.startDate" @close="setEndDate(row)" mode="dateTime"/>
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
<UFormGroup label="Ende:">
|
||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
||||
<UButton
|
||||
:disabled="!row.endDate"
|
||||
variant="outline"
|
||||
icon="i-heroicons-calendar-days-20-solid"
|
||||
:label="row.endDate ? dayjs(row.endDate).format('HH:mm') : 'Datum auswählen'"
|
||||
/>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<LazyDatePicker v-model="row.endDate" @close="close" mode="time"/>
|
||||
</template>
|
||||
</UPopover>
|
||||
</UFormGroup>
|
||||
<UFormGroup
|
||||
label="Notizen:"
|
||||
>
|
||||
<UTextarea
|
||||
v-model="row.notes"
|
||||
/>
|
||||
</UFormGroup>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="px-4 pb-4" v-else>
|
||||
<!--
|
||||
<p><span class="font-bold">Mitarbeitende/r:</span> {{profileStore.getProfileById(row.profile).fullName}}</p>
|
||||
-->
|
||||
<p><span class="font-bold">Start:</span> {{dayjs(row.startDate).format("DD.MM.YYYY HH:mm")}}</p>
|
||||
<p><span class="font-bold">Ende:</span> {{dayjs(row.endDate).format("DD.MM.YYYY HH:mm")}}</p>
|
||||
<p><span class="font-bold">Genehmigt:</span> {{row.approved ? "Ja" : "Nein"}}</p>
|
||||
<p><span class="font-bold">Notizen:</span> {{row.notes}}</p>
|
||||
</div>
|
||||
</template>
|
||||
<template #profile-data="{row}">
|
||||
{{ profiles.find(i => i.old_profile_id === row.profile) ? profiles.find(i => i.old_profile_id === row.profile).full_name : ""}}
|
||||
</template>
|
||||
<template #approved-data="{row}">
|
||||
<span v-if="row.approved" class="text-primary-500">Ja</span>
|
||||
<span v-else class="text-rose-600">Nein</span>
|
||||
</template>
|
||||
<template #date-data="{row}">
|
||||
{{dayjs(row.startDate).format("DD.MM.YYYY")}}
|
||||
</template>
|
||||
<template #startDate-data="{row}">
|
||||
{{dayjs(row.startDate).format("HH:mm")}} Uhr
|
||||
</template>
|
||||
<template #endDate-data="{row}">
|
||||
{{row.endDate ? dayjs(row.endDate).format("HH:mm") + " Uhr" : ""}}
|
||||
</template>
|
||||
<template #duration-data="{row}">
|
||||
{{row.endDate ? getDuration(row).composed : ""}}
|
||||
</template>
|
||||
</UTable>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user