173 lines
5.5 KiB
Vue
173 lines
5.5 KiB
Vue
<script setup lang="ts">
|
|
import { z } from 'zod'
|
|
import type { FormSubmitEvent } from '#ui/types'
|
|
import { useStaffTime } from '~/composables/useStaffTime'
|
|
|
|
const props = defineProps({
|
|
modelValue: { type: Boolean, default: false },
|
|
entry: { type: Object, default: null },
|
|
defaultUserId: { type: String, default: null }
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue', 'saved'])
|
|
|
|
// 💡 createEntry importieren
|
|
const { update, createEntry } = useStaffTime()
|
|
const { $dayjs } = useNuxtApp()
|
|
const toast = useToast()
|
|
|
|
const loading = ref(false)
|
|
|
|
const types = [
|
|
{ label: 'Arbeitszeit', value: 'work' },
|
|
{ label: 'Pause', value: 'pause' },
|
|
{ label: 'Urlaub', value: 'vacation' },
|
|
{ label: 'Krankheit', value: 'sick' },
|
|
{ label: 'Feiertag', value: 'holiday' },
|
|
{ label: 'Sonstiges', value: 'other' }
|
|
]
|
|
|
|
const state = reactive({
|
|
start_date: '',
|
|
start_time: '',
|
|
end_date: '',
|
|
end_time: '',
|
|
type: 'work',
|
|
description: ''
|
|
})
|
|
|
|
const schema = z.object({
|
|
start_date: z.string().min(1, 'Datum erforderlich'),
|
|
start_time: z.string().min(1, 'Zeit erforderlich'),
|
|
type: z.string(),
|
|
description: z.string().optional()
|
|
})
|
|
|
|
const isOpen = computed({
|
|
get: () => props.modelValue,
|
|
set: (value) => emit('update:modelValue', value)
|
|
})
|
|
|
|
const toDateStr = (dateStr: string) => dateStr ? $dayjs(dateStr).format('YYYY-MM-DD') : ''
|
|
const toTimeStr = (dateStr: string) => dateStr ? $dayjs(dateStr).format('HH:mm') : ''
|
|
|
|
watch(() => props.entry, (newVal) => {
|
|
if (newVal) {
|
|
// EDIT
|
|
state.start_date = toDateStr(newVal.started_at)
|
|
state.start_time = toTimeStr(newVal.started_at)
|
|
state.end_date = newVal.stopped_at ? toDateStr(newVal.stopped_at) : ''
|
|
state.end_time = newVal.stopped_at ? toTimeStr(newVal.stopped_at) : ''
|
|
state.type = newVal.type || 'work'
|
|
state.description = newVal.description || ''
|
|
} else {
|
|
// CREATE (Standardwerte: Heute)
|
|
const now = $dayjs()
|
|
state.start_date = now.format('YYYY-MM-DD')
|
|
state.start_time = now.format('HH:mm')
|
|
state.end_date = ''
|
|
state.end_time = ''
|
|
state.type = 'work'
|
|
state.description = ''
|
|
}
|
|
}, { immediate: true })
|
|
|
|
async function onSubmit(event: FormSubmitEvent<any>) {
|
|
loading.value = true
|
|
try {
|
|
// 1. Datum und Zeit kombinieren
|
|
const startIso = $dayjs(`${state.start_date} ${state.start_time}`).toISOString()
|
|
|
|
let endIso = null
|
|
if (state.end_date && state.end_time) {
|
|
endIso = $dayjs(`${state.end_date} ${state.end_time}`).toISOString()
|
|
|
|
if ($dayjs(endIso).isBefore($dayjs(startIso))) {
|
|
throw new Error("Endzeitpunkt muss nach dem Startzeitpunkt liegen.")
|
|
}
|
|
}
|
|
|
|
if (props.entry) {
|
|
// 🟢 UPDATE (Bearbeiten)
|
|
await update(props.entry, {
|
|
start: startIso,
|
|
end: endIso,
|
|
type: state.type,
|
|
description: state.description
|
|
})
|
|
toast.add({ title: 'Eintrag aktualisiert', color: 'green' })
|
|
} else {
|
|
// 🟢 CREATE (Neu Erstellen)
|
|
// 💡 HIER WAR DER FEHLER: Wir nutzen jetzt createEntry mit den Daten aus dem Formular
|
|
await createEntry({
|
|
start: startIso, // Die eingegebene Startzeit
|
|
end: endIso, // Die eingegebene Endzeit (oder null)
|
|
type: state.type,
|
|
description: state.description,
|
|
user_id: props.defaultUserId
|
|
})
|
|
|
|
toast.add({ title: 'Zeit manuell erfasst', color: 'green' })
|
|
}
|
|
|
|
emit('saved')
|
|
isOpen.value = false
|
|
} catch (error: any) {
|
|
toast.add({ title: 'Fehler', description: error.message, color: 'red' })
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UModal v-model="isOpen">
|
|
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
|
{{ entry ? 'Eintrag bearbeiten' : 'Neue Zeit erfassen' }}
|
|
</h3>
|
|
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
|
|
</div>
|
|
</template>
|
|
|
|
{{props}}
|
|
|
|
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
|
|
|
<UFormGroup label="Typ" name="type">
|
|
<USelectMenu v-model="state.type" :options="types" value-attribute="value" option-attribute="label" />
|
|
</UFormGroup>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<UFormGroup label="Start Datum" name="start_date">
|
|
<UInput type="date" v-model="state.start_date" />
|
|
</UFormGroup>
|
|
<UFormGroup label="Start Zeit" name="start_time">
|
|
<UInput type="time" v-model="state.start_time" />
|
|
</UFormGroup>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<UFormGroup label="Ende Datum" name="end_date">
|
|
<UInput type="date" v-model="state.end_date" />
|
|
</UFormGroup>
|
|
<UFormGroup label="Ende Zeit" name="end_time">
|
|
<UInput type="time" v-model="state.end_time" />
|
|
</UFormGroup>
|
|
</div>
|
|
<p class="text-xs text-gray-500 -mt-2">Leer lassen, wenn die Zeit noch läuft.</p>
|
|
|
|
<UFormGroup label="Beschreibung / Notiz" name="description">
|
|
<UTextarea v-model="state.description" placeholder="Was wurde gemacht?" />
|
|
</UFormGroup>
|
|
|
|
<div class="flex justify-end gap-2 pt-4">
|
|
<UButton label="Abbrechen" color="gray" variant="ghost" @click="isOpen = false" />
|
|
<UButton type="submit" label="Speichern" color="primary" :loading="loading" />
|
|
</div>
|
|
</UForm>
|
|
</UCard>
|
|
</UModal>
|
|
</template> |