Redone Profiles Edit
This commit is contained in:
@@ -1,97 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
const dataStore = useDataStore()
|
|
||||||
|
|
||||||
const itemInfo = ref({})
|
|
||||||
|
|
||||||
const createProfile = async () => {
|
|
||||||
let data = {
|
|
||||||
fullName: `${itemInfo.value.firstName} ${itemInfo.value.lastName}`,
|
|
||||||
...itemInfo.value
|
|
||||||
}
|
|
||||||
|
|
||||||
await dataStore.createNewItem("profiles", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UDashboardNavbar title="Mitarbeiter erstellen">
|
|
||||||
<template #right>
|
|
||||||
<UButton
|
|
||||||
color="rose"
|
|
||||||
@click="router.push(`/profiles`)"
|
|
||||||
>
|
|
||||||
Abbrechen
|
|
||||||
</UButton>
|
|
||||||
<UButton
|
|
||||||
@click="createProfile"
|
|
||||||
>
|
|
||||||
Erstellen
|
|
||||||
</UButton>
|
|
||||||
</template>
|
|
||||||
</UDashboardNavbar>
|
|
||||||
|
|
||||||
<UForm
|
|
||||||
class="p-5"
|
|
||||||
>
|
|
||||||
<UFormGroup
|
|
||||||
label="Anrede"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
required
|
|
||||||
v-model="itemInfo.salutation"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Vorname"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
required
|
|
||||||
v-model="itemInfo.firstName"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Nachname"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
required
|
|
||||||
v-model="itemInfo.lastName"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Mitarbeiter Nummer"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.employeeNumber"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="E-Mail"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.email"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Handynummer"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.mobileTel"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Festnetznummer"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.fixedTel"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
</UForm>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,637 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import axios from "axios"
|
|
||||||
import HistoryDisplay from "~/components/HistoryDisplay.vue";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
|
||||||
import isoWeek from "dayjs/plugin/isoWeek"
|
|
||||||
import isBetween from "dayjs/plugin/isBetween"
|
|
||||||
import DocumentList from "~/components/DocumentList.vue";
|
|
||||||
import DocumentUpload from "~/components/DocumentUpload.vue";
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
dayjs.extend(customParseFormat)
|
|
||||||
dayjs.extend(isoWeek)
|
|
||||||
dayjs.extend(isBetween)
|
|
||||||
|
|
||||||
const dataStore = useDataStore()
|
|
||||||
const profileStore = useProfileStore()
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const supabase = useSupabaseClient()
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
const itemInfo = ref({
|
|
||||||
weeklyRegularWorkingHours: {
|
|
||||||
"1":0,
|
|
||||||
"2":0,
|
|
||||||
"3":0,
|
|
||||||
"4":0,
|
|
||||||
"5":0,
|
|
||||||
"6":0,
|
|
||||||
"7":0,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const oldItemInfo = ref({})
|
|
||||||
|
|
||||||
const setupPage = async () => {
|
|
||||||
|
|
||||||
console.log(itemInfo.value)
|
|
||||||
|
|
||||||
if(route.params.id) {
|
|
||||||
itemInfo.value = await useSupabaseSelectSingle("profiles",route.params.id,"*, documents(*), checks(*)")
|
|
||||||
|
|
||||||
if(Object.keys(itemInfo.value.weeklyRegularWorkingHours).length === 0) {
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours = {
|
|
||||||
"1":0,
|
|
||||||
"2":0,
|
|
||||||
"3":0,
|
|
||||||
"4":0,
|
|
||||||
"5":0,
|
|
||||||
"6":0,
|
|
||||||
"7":0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!itemInfo.value.weeklyWorkingHours) itemInfo.value.weeklyWorkingHours = 0
|
|
||||||
if(!itemInfo.value.weeklyWorkingDays) itemInfo.value.weeklyWorkingDays = 0
|
|
||||||
if(!itemInfo.value.annualPaidLeaveDays) itemInfo.value.annualPaidLeaveDays = 0
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(itemInfo.value.id) oldItemInfo.value = JSON.parse(JSON.stringify(itemInfo.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
setupPage()
|
|
||||||
|
|
||||||
const emailSignature = ref(profileStore.activeProfile.emailSignature)
|
|
||||||
const tiptapLoaded = ref(false)
|
|
||||||
const contentSaved = ref(true)
|
|
||||||
const contentChanged = async (content) => {
|
|
||||||
emailSignature.value = content.html
|
|
||||||
if(tiptapLoaded.value === true) {
|
|
||||||
contentSaved.value = false
|
|
||||||
} else {
|
|
||||||
tiptapLoaded.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const saveSignature = async () => {
|
|
||||||
const {data,error} = await supabase.from("profiles").update({emailSignature: emailSignature.value}).eq("id", profileStore.activeProfile.id)
|
|
||||||
|
|
||||||
if(error) {
|
|
||||||
toast.add({title: "Fehler beim Speichern der Signatur", color:"rose"})
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
contentSaved.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const addToNewsletter = async () => {
|
|
||||||
const {data,error} = await axios({
|
|
||||||
url: "https://newsletter.fedeo.io/api/public/subscription",
|
|
||||||
method: "post",
|
|
||||||
data: {
|
|
||||||
email: profileStore.activeProfile.email,
|
|
||||||
name: profileStore.activeProfile.name,
|
|
||||||
list_uuids: ["b97453fd-14b2-4b25-8f9b-b83847317ea3"],
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const calcWeeklyWorkingHours = () => {
|
|
||||||
itemInfo.value.weeklyWorkingHours =
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[1] +
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[2] +
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[3] +
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[4] +
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[5] +
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[6] +
|
|
||||||
itemInfo.value.weeklyRegularWorkingHours[7]
|
|
||||||
}
|
|
||||||
|
|
||||||
const colorMode = useColorMode()
|
|
||||||
const isLight = computed({
|
|
||||||
get() {
|
|
||||||
return colorMode.value !== 'dark'
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const saveProfile = async () => {
|
|
||||||
let data = {...itemInfo.value, fullName: itemInfo.value.firstName + ' ' + itemInfo.value.lastName}
|
|
||||||
delete data.checks
|
|
||||||
delete data.documents
|
|
||||||
|
|
||||||
await dataStore.updateItem('profiles',data,oldItemInfo.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const addAutomaticHourCorrection = async () => {
|
|
||||||
itemInfo.value.automaticHourCorrections.push({mode: 'correct', id: uuidv4(), weekday: 1, bookingTimeUTC: {hour: 0, minute: 0}, rangeStartUTC: {hour: 0, minute: 0}, rangeEndUTC: {hour: 0, minute: 0}})
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UDashboardNavbar
|
|
||||||
:title="itemInfo.fullName"
|
|
||||||
>
|
|
||||||
<template #left>
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-chevron-left"
|
|
||||||
variant="outline"
|
|
||||||
@click="router.push(`/profiles`)"
|
|
||||||
>
|
|
||||||
Mitarbeiter
|
|
||||||
</UButton>
|
|
||||||
</template>
|
|
||||||
<template #center>
|
|
||||||
<h1
|
|
||||||
v-if="itemInfo"
|
|
||||||
:class="['text-xl','font-medium'/*, ... true ? ['text-primary'] : ['text-rose-500']*/]"
|
|
||||||
>{{itemInfo ? `Mitarbeiter: ${itemInfo.fullName}` : ''}}</h1>
|
|
||||||
</template>
|
|
||||||
</UDashboardNavbar>
|
|
||||||
<UTabs
|
|
||||||
v-if="itemInfo.id"
|
|
||||||
class="p-5"
|
|
||||||
:items="[
|
|
||||||
{
|
|
||||||
label: 'Informationen'
|
|
||||||
},{
|
|
||||||
label: 'Logbuch'
|
|
||||||
},{
|
|
||||||
label: 'Zeiterfassung'
|
|
||||||
},{
|
|
||||||
label: 'Vertragsdaten'
|
|
||||||
},{
|
|
||||||
label: 'Dokumente'
|
|
||||||
},{
|
|
||||||
label: 'Überprüfungen'
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<template #item="{item}">
|
|
||||||
<UCard class="mt-5">
|
|
||||||
<div v-if="item.label === 'Informationen'">
|
|
||||||
<Toolbar>
|
|
||||||
<UButton
|
|
||||||
@click="saveProfile"
|
|
||||||
>
|
|
||||||
Speichern
|
|
||||||
</UButton>
|
|
||||||
</Toolbar>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Anrede"
|
|
||||||
class="w-60"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.salutation"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Vorname"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.firstName"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Nachname"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.lastName"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Mitarbeiternummer"
|
|
||||||
class="w-60"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.employeeNumber"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="E-Mail"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.email"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Benutzername"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.username"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Festnetz Telefon"
|
|
||||||
class="w-60"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.fixedTel"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Handy"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.mobileTel"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Geburtstag:"
|
|
||||||
>
|
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
|
||||||
:label="itemInfo.birthday ? dayjs(itemInfo.birthday).format('DD.MM.YYYY') : 'Datum auswählen'"
|
|
||||||
variant="outline"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template #panel="{ close }">
|
|
||||||
<LazyDatePicker v-model="itemInfo.birthday" />
|
|
||||||
</template>
|
|
||||||
</UPopover>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Kleidergröße Oberteil"
|
|
||||||
class="w-60"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.clothingSizeTop"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Kleidergröße Hose"
|
|
||||||
class="w-60"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.clothingSizeBottom"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Schuhgröße"
|
|
||||||
class="w-60"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.clothingSizeShoe"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
|
|
||||||
<UDivider class="my-5" v-if="profileStore.activeProfile.id === itemInfo.id">
|
|
||||||
Helligkeitseinstellung
|
|
||||||
</UDivider>
|
|
||||||
|
|
||||||
<UButton
|
|
||||||
v-if="profileStore.activeProfile.id === itemInfo.id"
|
|
||||||
:icon="isLight ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
|
|
||||||
color="white"
|
|
||||||
variant="outline"
|
|
||||||
aria-label="Theme"
|
|
||||||
@click="isLight = !isLight"
|
|
||||||
>
|
|
||||||
{{!isLight ? "Hell" : "Dunkel"}}
|
|
||||||
</UButton>
|
|
||||||
|
|
||||||
<UDivider class="my-5">
|
|
||||||
E-Mail Signatur
|
|
||||||
</UDivider>
|
|
||||||
|
|
||||||
<UButton
|
|
||||||
variant="outline"
|
|
||||||
v-if="!contentSaved"
|
|
||||||
@click="saveSignature"
|
|
||||||
>
|
|
||||||
Speichern
|
|
||||||
</UButton>
|
|
||||||
|
|
||||||
<Tiptap
|
|
||||||
@updateContent="contentChanged"
|
|
||||||
:preloadedContent="emailSignature"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!--<UDivider>Newsletter</UDivider>
|
|
||||||
|
|
||||||
<UButton
|
|
||||||
@click="addToNewsletter"
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
In Newsletter eintragen
|
|
||||||
</UButton>-->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div v-if="item.label === 'Logbuch'">
|
|
||||||
<HistoryDisplay
|
|
||||||
type="profile"
|
|
||||||
v-if="itemInfo"
|
|
||||||
:element-id="itemInfo.id"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="item.label === 'Vertragsdaten'">
|
|
||||||
<Toolbar>
|
|
||||||
<UButton
|
|
||||||
@click="saveProfile"
|
|
||||||
>
|
|
||||||
Speichern
|
|
||||||
</UButton>
|
|
||||||
</Toolbar>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Eintrittsdatum:"
|
|
||||||
>
|
|
||||||
<UPopover :popper="{ placement: 'bottom-start' }">
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-calendar-days-20-solid"
|
|
||||||
:label="itemInfo.entryDate ? dayjs(itemInfo.entryDate).format('DD.MM.YYYY') : 'Datum auswählen'"
|
|
||||||
variant="outline"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template #panel="{ close }">
|
|
||||||
<LazyDatePicker v-model="itemInfo.entryDate" />
|
|
||||||
</template>
|
|
||||||
</UPopover>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Wöchentliche Arbeitszeit"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyWorkingHours"
|
|
||||||
type="number"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<!-- <UFormGroup
|
|
||||||
label="Durchschnittliche Arbeitstage pro Woche"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyWorkingDays"
|
|
||||||
/>
|
|
||||||
</UFormGroup>-->
|
|
||||||
<UFormGroup
|
|
||||||
label="Urlaubstage"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.annualPaidLeaveDays"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
<UDivider class="my-3">Regelarbeitszeiten</UDivider>
|
|
||||||
<InputGroup class="w-full">
|
|
||||||
<UFormGroup
|
|
||||||
label="Montag"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[1]"
|
|
||||||
type="number"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
|
|
||||||
/>
|
|
||||||
</UFormGroup><UFormGroup
|
|
||||||
label="Dienstag"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[2]"
|
|
||||||
type="number"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Mittwoch"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[3]"
|
|
||||||
type="number"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Donnerstag"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[4]"
|
|
||||||
type="number"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Freitag"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[5]"
|
|
||||||
type="number"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Samstag"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[6]"
|
|
||||||
type="number"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Sonntag"
|
|
||||||
class="flex-auto"
|
|
||||||
>
|
|
||||||
<UInput
|
|
||||||
v-model="itemInfo.weeklyRegularWorkingHours[7]"
|
|
||||||
type="number"
|
|
||||||
@change="calcWeeklyWorkingHours"
|
|
||||||
@keyup="calcWeeklyWorkingHours"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="item.label === 'Dokumente'">
|
|
||||||
<DocumentUpload
|
|
||||||
type="profile"
|
|
||||||
:element-id="itemInfo.id"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DocumentList :documents="dataStore.getDocumentsByProfileId(itemInfo.id)"/>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="item.label === 'Überprüfungen'">
|
|
||||||
<UTable
|
|
||||||
:rows="itemInfo.checks"
|
|
||||||
:columns="[{key:'name',label: 'Name'},{key:'rhythm',label: 'Rhythmus'},{key:'description',label: 'Beschreibung'}]"
|
|
||||||
class="w-full"
|
|
||||||
:ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }"
|
|
||||||
@select="(i) => router.push(`/checks/show/${i.id}`) "
|
|
||||||
:empty-state="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Überprüfungen anzuzeigen' }"
|
|
||||||
>
|
|
||||||
<template #rhythm-data="{row}">
|
|
||||||
{{row.distance}}
|
|
||||||
<span v-if="row.distanceUnit === 'dayjs'">Tage</span>
|
|
||||||
<span v-if="row.distanceUnit === 'years'">Jahre</span>
|
|
||||||
</template>
|
|
||||||
</UTable>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="item.label === 'Zeiterfassung'">
|
|
||||||
<UButton
|
|
||||||
@click="addAutomaticHourCorrection"
|
|
||||||
>
|
|
||||||
+ Korrektur
|
|
||||||
</UButton>
|
|
||||||
<UButton
|
|
||||||
@click="saveProfile"
|
|
||||||
class="ml-3"
|
|
||||||
>
|
|
||||||
Speichern
|
|
||||||
</UButton>
|
|
||||||
|
|
||||||
<UDivider class="my-3" />
|
|
||||||
|
|
||||||
<InputGroup class="w-full" v-for="item in itemInfo.automaticHourCorrections">
|
|
||||||
<UFormGroup
|
|
||||||
label="Modus"
|
|
||||||
>
|
|
||||||
<USelectMenu
|
|
||||||
v-model="item.mode"
|
|
||||||
:options="[{key: 'correct', label: 'Korrigieren'},{key: 'automatic-checkout', label: 'Automatisches ausbuchen'}]"
|
|
||||||
option-attribute="label"
|
|
||||||
value-attribute="key"
|
|
||||||
class="w-80"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Wochentag"
|
|
||||||
>
|
|
||||||
<USelectMenu
|
|
||||||
v-model="item.weekday"
|
|
||||||
:options="[{key: 1, label: 'Montag'},{key: 2, label: 'Dienstag'},{key: 3, label: 'Mittwoch'},{key: 4, label: 'Donnerstag'},{key: 5, label: 'Freitag'},{key: 6, label: 'Samstag'},{key: 7, label: 'Sonntag'},]"
|
|
||||||
option-attribute="label"
|
|
||||||
value-attribute="key"
|
|
||||||
class="w-40"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Zeitraum Start"
|
|
||||||
v-if="item.mode === 'correct'"
|
|
||||||
>
|
|
||||||
<InputGroup>
|
|
||||||
<UInput
|
|
||||||
v-model="item.rangeStartUTC.hour"
|
|
||||||
placeholder="Stunde UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
<UInput
|
|
||||||
v-model="item.rangeStartUTC.minute"
|
|
||||||
placeholder="Minute UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Zeitraum Ende"
|
|
||||||
v-if="item.mode === 'correct'"
|
|
||||||
>
|
|
||||||
<InputGroup>
|
|
||||||
<UInput
|
|
||||||
v-model="item.rangeEndUTC.hour"
|
|
||||||
placeholder="Stunde UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
<UInput
|
|
||||||
v-model="item.rangeEndUTC.minute"
|
|
||||||
placeholder="Minute UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Korrigieren zu"
|
|
||||||
v-if="item.mode === 'correct'"
|
|
||||||
>
|
|
||||||
<InputGroup>
|
|
||||||
<UInput
|
|
||||||
v-model="item.bookingTimeUTC.hour"
|
|
||||||
placeholder="Stunde UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
<UInput
|
|
||||||
v-model="item.bookingTimeUTC.minute"
|
|
||||||
placeholder="Minute UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Ausbuchen"
|
|
||||||
v-if="item.mode === 'automatic-checkout'"
|
|
||||||
>
|
|
||||||
<InputGroup>
|
|
||||||
<UInput
|
|
||||||
v-model="item.bookingTimeUTC.hour"
|
|
||||||
placeholder="Stunde UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
<UInput
|
|
||||||
v-model="item.bookingTimeUTC.minute"
|
|
||||||
placeholder="Minute UTC"
|
|
||||||
class="w-20"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</UFormGroup>
|
|
||||||
<UFormGroup
|
|
||||||
label="Entfernen"
|
|
||||||
>
|
|
||||||
<UButton
|
|
||||||
icon="i-heroicons-x-mark"
|
|
||||||
variant="outline"
|
|
||||||
color="red"
|
|
||||||
@click="itemInfo.automaticHourCorrections = itemInfo.automaticHourCorrections.filter(i => i.id !== item.id)"
|
|
||||||
/>
|
|
||||||
</UFormGroup>
|
|
||||||
|
|
||||||
</InputGroup>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</UCard>
|
|
||||||
</template>
|
|
||||||
</UTabs>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
318
pages/staff/profiles/[id].vue
Normal file
318
pages/staff/profiles/[id].vue
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const toast = useToast()
|
||||||
|
const { $api } = useNuxtApp()
|
||||||
|
|
||||||
|
const id = route.params.id as string
|
||||||
|
const profile = ref<any>(null)
|
||||||
|
const pending = ref(true)
|
||||||
|
const saving = ref(false)
|
||||||
|
|
||||||
|
/** Profil laden **/
|
||||||
|
async function fetchProfile() {
|
||||||
|
pending.value = true
|
||||||
|
try {
|
||||||
|
profile.value = await $api(`/api/profiles/${id}`)
|
||||||
|
ensureWorkingHoursStructure()
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('[fetchProfile]', err)
|
||||||
|
toast.add({
|
||||||
|
title: 'Fehler beim Laden',
|
||||||
|
description: err?.data?.error || err?.message || 'Unbekannter Fehler',
|
||||||
|
color: 'red'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
pending.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Profil speichern **/
|
||||||
|
async function saveProfile() {
|
||||||
|
if (saving.value) return
|
||||||
|
saving.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await $api(`/api/profiles/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: profile.value
|
||||||
|
})
|
||||||
|
toast.add({ title: 'Profil gespeichert', color: 'green' })
|
||||||
|
fetchProfile()
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('[saveProfile]', err)
|
||||||
|
toast.add({
|
||||||
|
title: 'Fehler beim Speichern',
|
||||||
|
description: err?.data?.error || err?.message || 'Unbekannter Fehler',
|
||||||
|
color: 'red'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
saving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const weekdays = [
|
||||||
|
{ key: '1', label: 'Montag' },
|
||||||
|
{ key: '2', label: 'Dienstag' },
|
||||||
|
{ key: '3', label: 'Mittwoch' },
|
||||||
|
{ key: '4', label: 'Donnerstag' },
|
||||||
|
{ key: '5', label: 'Freitag' },
|
||||||
|
{ key: '6', label: 'Samstag' },
|
||||||
|
{ key: '7', label: 'Sonntag' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const bundeslaender = [
|
||||||
|
{ code: 'DE-BW', name: 'Baden-Württemberg' },
|
||||||
|
{ code: 'DE-BY', name: 'Bayern' },
|
||||||
|
{ code: 'DE-BE', name: 'Berlin' },
|
||||||
|
{ code: 'DE-BB', name: 'Brandenburg' },
|
||||||
|
{ code: 'DE-HB', name: 'Bremen' },
|
||||||
|
{ code: 'DE-HH', name: 'Hamburg' },
|
||||||
|
{ code: 'DE-HE', name: 'Hessen' },
|
||||||
|
{ code: 'DE-MV', name: 'Mecklenburg-Vorpommern' },
|
||||||
|
{ code: 'DE-NI', name: 'Niedersachsen' },
|
||||||
|
{ code: 'DE-NW', name: 'Nordrhein-Westfalen' },
|
||||||
|
{ code: 'DE-RP', name: 'Rheinland-Pfalz' },
|
||||||
|
{ code: 'DE-SL', name: 'Saarland' },
|
||||||
|
{ code: 'DE-SN', name: 'Sachsen' },
|
||||||
|
{ code: 'DE-ST', name: 'Sachsen-Anhalt' },
|
||||||
|
{ code: 'DE-SH', name: 'Schleswig-Holstein' },
|
||||||
|
{ code: 'DE-TH', name: 'Thüringen' }
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
// Sicherstellen, dass das JSON-Feld existiert
|
||||||
|
function ensureWorkingHoursStructure() {
|
||||||
|
if (!profile.value.weekly_regular_working_hours) {
|
||||||
|
profile.value.weekly_regular_working_hours = {}
|
||||||
|
}
|
||||||
|
for (const { key } of weekdays) {
|
||||||
|
if (profile.value.weekly_regular_working_hours[key] == null) {
|
||||||
|
profile.value.weekly_regular_working_hours[key] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalculateWeeklyHours() {
|
||||||
|
if (!profile.value?.weekly_regular_working_hours) return
|
||||||
|
|
||||||
|
const total = Object.values(profile.value.weekly_regular_working_hours).reduce(
|
||||||
|
(sum: number, val: any) => {
|
||||||
|
const num = parseFloat(val)
|
||||||
|
return sum + (isNaN(num) ? 0 : num)
|
||||||
|
},
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
profile.value.weekly_working_hours = Number(total.toFixed(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkZip = async () => {
|
||||||
|
const zipData = await useFunctions().useZipCheck(profile.value.address_zip)
|
||||||
|
profile.value.address_city = zipData.short
|
||||||
|
profile.value.state_code = zipData.state_code
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetchProfile)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- Haupt-Navigation -->
|
||||||
|
<UDashboardNavbar title="Mitarbeiter">
|
||||||
|
<template #left>
|
||||||
|
<UButton
|
||||||
|
color="primary"
|
||||||
|
variant="outline"
|
||||||
|
@click="navigateTo(`/staff/profiles`)"
|
||||||
|
icon="i-heroicons-chevron-left"
|
||||||
|
|
||||||
|
>
|
||||||
|
Mitarbeiter
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
<template #center>
|
||||||
|
<h1 class="text-xl font-medium truncate">
|
||||||
|
Mitarbeiter bearbeiten: {{ profile?.full_name || '' }}
|
||||||
|
</h1>
|
||||||
|
</template>
|
||||||
|
</UDashboardNavbar>
|
||||||
|
|
||||||
|
<!-- Toolbar -->
|
||||||
|
<UDashboardToolbar>
|
||||||
|
<template #right>
|
||||||
|
<UButton
|
||||||
|
icon="i-mdi-content-save"
|
||||||
|
color="primary"
|
||||||
|
:loading="saving"
|
||||||
|
@click="saveProfile"
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
</UDashboardToolbar>
|
||||||
|
|
||||||
|
<!-- Inhalt -->
|
||||||
|
<UDashboardPanelContent>
|
||||||
|
<UCard v-if="!pending && profile">
|
||||||
|
<div class="flex items-center gap-4 mb-6">
|
||||||
|
<UAvatar size="xl" :alt="profile.full_name" />
|
||||||
|
<div>
|
||||||
|
<h2 class="text-xl font-semibold text-gray-900">{{ profile.full_name }}</h2>
|
||||||
|
<p class="text-sm text-gray-500">{{ profile.employee_number || '–' }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UDivider label="Persönliche Daten" />
|
||||||
|
|
||||||
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
|
<UFormGroup label="Vorname">
|
||||||
|
<UInput v-model="profile.first_name" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Nachname">
|
||||||
|
<UInput v-model="profile.last_name" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="E-Mail">
|
||||||
|
<UInput v-model="profile.email" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Telefon (Mobil)">
|
||||||
|
<UInput v-model="profile.mobile_tel" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Telefon (Festnetz)">
|
||||||
|
<UInput v-model="profile.fixed_tel" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Geburtstag">
|
||||||
|
<UInput type="date" v-model="profile.birthday" />
|
||||||
|
</UFormGroup>
|
||||||
|
</UForm>
|
||||||
|
</UCard>
|
||||||
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
|
<UDivider label="Vertragsinformationen" />
|
||||||
|
|
||||||
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
|
<UFormGroup label="Vertragsart">
|
||||||
|
<UInput v-model="profile.contract_type"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Status">
|
||||||
|
<UInput v-model="profile.status"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Position">
|
||||||
|
<UInput v-model="profile.position"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Qualifikation">
|
||||||
|
<UInput v-model="profile.qualification"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Eintrittsdatum">
|
||||||
|
<UInput type="date" v-model="profile.entry_date" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Wöchentliche Arbeitszeit (Std)">
|
||||||
|
<UInput type="number" v-model="profile.weekly_working_hours" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Bezahlte Urlaubstage (Jahr)">
|
||||||
|
<UInput type="number" v-model="profile.annual_paid_leave_days" />
|
||||||
|
</UFormGroup>
|
||||||
|
<UFormGroup label="Aktiv">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<UToggle v-model="profile.active" color="primary" />
|
||||||
|
<span class="text-sm text-gray-600">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</UFormGroup>
|
||||||
|
</UForm>
|
||||||
|
|
||||||
|
</UCard>
|
||||||
|
|
||||||
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
|
<UDivider label="Adresse & Standort" />
|
||||||
|
|
||||||
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
|
<UFormGroup label="Straße und Hausnummer">
|
||||||
|
<UInput v-model="profile.address_street"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="PLZ">
|
||||||
|
<UInput type="text" v-model="profile.address_zip" @focusout="checkZip"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Ort">
|
||||||
|
<UInput v-model="profile.address_city"/>
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Bundesland">
|
||||||
|
<USelectMenu
|
||||||
|
v-model="profile.state_code"
|
||||||
|
:options="bundeslaender"
|
||||||
|
value-attribute="code"
|
||||||
|
option-attribute="name"
|
||||||
|
placeholder="Bundesland auswählen"
|
||||||
|
/>
|
||||||
|
</UFormGroup>
|
||||||
|
</UForm>
|
||||||
|
</UCard>
|
||||||
|
|
||||||
|
|
||||||
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
|
<UDivider label="Wöchentliche Arbeitsstunden" />
|
||||||
|
|
||||||
|
<div class="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
<div
|
||||||
|
v-for="day in weekdays"
|
||||||
|
:key="day.key"
|
||||||
|
:class="[...profile.weekly_regular_working_hours[day.key] === 0 ? ['bg-gray-100'] : ['bg-gray-100','border-green-400'], 'flex items-center justify-between border rounded-lg p-3 bg-gray-50']"
|
||||||
|
>
|
||||||
|
<span class="font-medium text-gray-700">{{ day.label }}</span>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<UInput
|
||||||
|
type="number"
|
||||||
|
size="sm"
|
||||||
|
min="0"
|
||||||
|
max="24"
|
||||||
|
step="0.25"
|
||||||
|
v-model.number="profile.weekly_regular_working_hours[day.key]"
|
||||||
|
placeholder="0"
|
||||||
|
class="w-24"
|
||||||
|
@change="recalculateWeeklyHours"
|
||||||
|
/>
|
||||||
|
<span class="text-gray-400 text-sm">Std</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UCard>
|
||||||
|
|
||||||
|
<UCard v-if="!pending && profile" class="mt-3">
|
||||||
|
<UDivider label="Sonstiges" />
|
||||||
|
<UForm :state="profile" @submit.prevent="saveProfile" class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
|
||||||
|
<UFormGroup label="Kleidergröße (Oberteil)">
|
||||||
|
<UInput v-model="profile.clothing_size_top" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Kleidergröße (Hose)">
|
||||||
|
<UInput v-model="profile.clothing_size_bottom" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Schuhgröße">
|
||||||
|
<UInput v-model="profile.clothing_size_shoe" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Token-ID">
|
||||||
|
<UInput v-model="profile.token_id" />
|
||||||
|
</UFormGroup>
|
||||||
|
</UForm>
|
||||||
|
</UCard>
|
||||||
|
|
||||||
|
<USkeleton v-if="pending" height="300px" />
|
||||||
|
</UDashboardPanelContent>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const dataStore = useDataStore()
|
|
||||||
const profileStore = useProfileStore()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
@@ -40,10 +38,10 @@
|
|||||||
</UButton>
|
</UButton>
|
||||||
</template>
|
</template>
|
||||||
</UDashboardNavbar>
|
</UDashboardNavbar>
|
||||||
|
|
||||||
<UTable
|
<UTable
|
||||||
:rows="items"
|
:rows="items"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@select="(i) => navigateTo(`/staff/profiles/${i.id}`)"
|
||||||
>
|
>
|
||||||
|
|
||||||
</UTable>
|
</UTable>
|
||||||
Reference in New Issue
Block a user