diff --git a/spaces/components/DatePicker.vue b/spaces/components/DatePicker.vue index 9aa4133..ef212cc 100644 --- a/spaces/components/DatePicker.vue +++ b/spaces/components/DatePicker.vue @@ -26,7 +26,7 @@ const date = computed({ const attrs = [{ key: 'today', highlight: { - color: 'blue', + color: 'green', fillMode: 'outline', class: '!bg-gray-100 dark:!bg-gray-800' }, diff --git a/spaces/layouts/default.vue b/spaces/layouts/default.vue index 8077865..9d03589 100644 --- a/spaces/layouts/default.vue +++ b/spaces/layouts/default.vue @@ -305,6 +305,10 @@ let links = [ label: "Belege", to: "/receipts", icon: "i-heroicons-document-text" + },{ + label: "Bank", + to: "/banking", + icon: "i-heroicons-document-text" },{ label: "Projekte", to: "/projects", diff --git a/spaces/pages/absenceRequests/[mode]/[[id]].vue b/spaces/pages/absenceRequests/[mode]/[[id]].vue index 3e9da67..a476016 100644 --- a/spaces/pages/absenceRequests/[mode]/[[id]].vue +++ b/spaces/pages/absenceRequests/[mode]/[[id]].vue @@ -16,7 +16,7 @@ let currentItem = ref(null) //Working const mode = ref(route.params.mode || "show") const itemInfo = ref({ - approved: null + approved: "Offen" }) const states = ["Offen","Genehmigt", "Abgelehnt"] @@ -152,7 +152,7 @@ setupPage() :search-attributes="['fullName']" > diff --git a/spaces/pages/employees/timetracking.vue b/spaces/pages/employees/timetracking.vue index 70b3b18..b3163e5 100644 --- a/spaces/pages/employees/timetracking.vue +++ b/spaces/pages/employees/timetracking.vue @@ -420,8 +420,6 @@ const setState = async (newState) => { - - 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" +dayjs.extend(customParseFormat) +dayjs.extend(isoWeek) +dayjs.extend(isBetween) const dataStore = useDataStore() const route = useRoute() @@ -10,7 +17,98 @@ const setupPage = () => { if(route.params.id) itemInfo.value = dataStore.getProfileById(route.params.id) } +const selectedPresetRange = ref("Dieser Monat") +const selectedStartDay = ref("") +const selectedEndDay = ref("") + +const changeRange = () => { + let selector = "w" + 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 === "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).startOf(selector).format("YYYY-MM-DD") + selectedEndDay.value = dayjs().subtract(subtract,selector).endOf(selector).format("YYYY-MM-DD") + +} + + +const workingTimeInfo = computed(() => { + + let times = dataStore.getWorkingTimesByProfileId(itemInfo.value.id) + + times = times.filter(i => dayjs(i.date).isBetween(selectedStartDay.value,selectedEndDay.value,'day')) + + + let weekFactor = 4.35 + let monthlyWorkingHours = itemInfo.value.weeklyWorkingHours * weekFactor + + //Eingreicht + let sumWorkingMinutesEingereicht = 0 + dataStore.getWorkingTimesByProfileId(itemInfo.value.id).filter(i => !i.approved).forEach(time => { + const minutes = Math.floor(dayjs(time.end, "HH:mm:ssZ").diff(dayjs(time.start, "HH:mm:ssZ"),'minutes')) + sumWorkingMinutesEingereicht = sumWorkingMinutesEingereicht + minutes + }) + + //Bestätigt + let sumWorkingMinutesApproved = 0 + dataStore.getWorkingTimesByProfileId(itemInfo.value.id).filter(i => i.approved).forEach(time => { + const minutes = Math.floor(dayjs(time.end, "HH:mm:ssZ").diff(dayjs(time.start, "HH:mm:ssZ"),'minutes')) + sumWorkingMinutesApproved = sumWorkingMinutesApproved + minutes + }) + + + //Saldo + let saldo = (sumWorkingMinutesApproved / 60).toFixed(2) - monthlyWorkingHours + let saldoInOfficial = ((sumWorkingMinutesApproved + sumWorkingMinutesEingereicht) / 60).toFixed(2) - monthlyWorkingHours + + + + + return { + monthlyWorkingHours, + sumWorkingMinutesEingereicht, + sumWorkingMinutesApproved, + saldo, + saldoInOfficial + } +}) + +const getDuration = (time) => { + const minutes = Math.floor(dayjs(time.end, "HH:mm:ssZ").diff(dayjs(time.start, "HH:mm:ssZ"),'minutes',true)) + const hours = Math.floor(minutes/60) + return { + //dezimal: dez, + hours: hours, + minutes: minutes, + composed: `${hours}:${String(minutes % 60).padStart(2,"0")} h` + } +} + + + setupPage() +changeRange() @@ -97,6 +195,97 @@ setupPage() v-if="itemInfo" :element-id="itemInfo.id" /> + +
+ + + + + + + + + + + + + + + + + + + + +
+

Eingreicht: {{Math.floor(workingTimeInfo.sumWorkingMinutesEingereicht/60)}}:{{String(workingTimeInfo.sumWorkingMinutesEingereicht % 60).padStart(2,"0")}} h

+

Bestätigt: {{Math.floor(workingTimeInfo.sumWorkingMinutesApproved/60)}}:{{String(workingTimeInfo.sumWorkingMinutesApproved % 60).padStart(2,"0")}} h

+

Soll Stunden: {{workingTimeInfo.monthlyWorkingHours}} h

+

Abwesend:

+

Ausgleich:

+

Inoffizielles Saldo: {{workingTimeInfo.saldoInOfficial}} h

+

Saldo: {{workingTimeInfo.saldo}} h

+
+ + + + + + + + + + +
@@ -115,6 +304,14 @@ setupPage() v-model="itemInfo.weeklyWorkingHours" /> + + + import dayjs from "dayjs"; +import customParseFormat from "dayjs/plugin/customParseFormat" + +dayjs.extend(customParseFormat) + +import VueDatePicker from '@vuepic/vue-datepicker' +import '@vuepic/vue-datepicker/dist/main.css' + + +definePageMeta({ + middleware: "auth" +}) const dataStore = useDataStore() +const supabase = useSupabaseClient() +const user = useSupabaseUser() +const toast = useToast() -const templateColumns = [ + +const timeInfo = ref({ + user: "", + start: "", + end: null, + notes: null, +}) + +const filterUser = ref(user.value.id || "") + + +const filteredRows = computed(() => { + + let times = dataStore.workingtimes + + if(dataStore.hasRight('viewTimes')) { + if(filterUser.value !== "") { + times = times.filter(i => i.profile === filterUser.value) + } + } else if(dataStore.hasRight('viewOwnTimes')) { + times = times.filter(i => i.profile === user.value.id) + } else { + times = [] + } + + return times + +}) + + + +const itemInfo = ref({ + user: "", + start: new Date(), + end: "", + notes: null, + state: "Entwurf" +}) + + +const columns = [ { - key: "state", - label: "Status" + key:"state", + label: "Status", + sortable:true + }, + { + key: "profile", + label: "Mitarbeiter", + sortable:true }, { - key: "user", - label: "Mitarbeiter" - }, { key: "date", - label: "Datum" - }, { - key: "start", - label: "Start" - }, { + label: "Datum", + sortable:true + }, + { + key:"start", + label:"Start", + sortable:true + }, + { key: "end", - label: "Ende" - }, { - key: "device", - label: "Gerät" + label: "Ende", + sortable:true + }, + { + key: "duration", + label: "Dauer", + sortable:true + }, + { + key: "notes", + label: "Notizen", + sortable:true } ] -const selectedColumns = ref(templateColumns) -const columns = computed(() => templateColumns.filter((column) => selectedColumns.value.includes(column))) + +const runningTimeInfo = ref({}) +const showConfigTimeModal = ref(false) +const configTimeMode = ref("create") + + + +const startTime = async () => { + console.log("started") + timeInfo.value = { + user: user.value.id, + start: dayjs().format("HH:mm:ssZ"), + date: dayjs().format("YYYY-MM-DD"), + tenant: dataStore.currentTenant, + state: "Im Web gestartet" + } + + const {data,error} = await supabase + .from("workingtimes") + .insert([timeInfo.value]) + .select() + + if(error) { + console.log(error) + } else if(data) { + //timeInfo.value = data[0] + await dataStore.fetchWorkingTimes() + runningTimeInfo.value = dataStore.times.find(time => time.profile === user.value.id && !time.end) + } + +} + +const stopStartedTime = async () => { + //console.log(runningTimeInfo.value) + + runningTimeInfo.value.end = dayjs().format("HH:mm:ssZ") + runningTimeInfo.value.state = "Im Web gestoppt" + + /*const mapNumRange = (num, inMin, inMax, outMin, outMax) => + ((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;*/ + + //runningTimeInfo.value.duration = Math.round(mapNumRange(Math.abs(new Date(runningTimeInfo.value.end) - new Date(runningTimeInfo.value.start))/1000/60,0,60,0,1)*100)/100 + + const {data,error} = await supabase + .from("workingtimes") + .update(runningTimeInfo.value) + .eq('id',runningTimeInfo.value.id) + .select() + console.log(data) + + if(error) { + console.log(error) + } else { + toast.add({title: "Zeit erfolgreich gestoppt"}) + runningTimeInfo.value = {} + dataStore.fetchWorkingTimes() + } +} + +if(dataStore.workingtimes.find(time => time.profile == user.value.id && !time.end)) { + runningTimeInfo.value = dataStore.workingtimes.find(time => time.profile == user.value.id && !time.end) +} + + +const createTime = async () => { + const {data,error} = await supabase + .from("times") + .insert({...itemInfo.value, tenant: dataStore.currentTenant}) + .select() + + if(error) { + console.log(error) + } else if(data) { + itemInfo.value = {} + toast.add({title: "Zeit erfolgreich erstellt"}) + showConfigTimeModal.value = false + await dataStore.fetchTimes() + + } +} + +const updateTime = async () => { + const {error} = await supabase + .from("workingTimes") + .update(itemInfo.value) + .eq('id',itemInfo.value.id) + + if(error) { + console.log(error) + } + + toast.add({title: "Zeit erfolgreich gespeichert"}) + showConfigTimeModal.value = false + await dataStore.fetchTimes() +} + +const format = (date) => { + let dateFormat = dayjs(date).format("DD.MM.YY HH:mm") + + return `${dateFormat}`; +} + +const getDuration = (time) => { + const minutes = Math.floor(dayjs(time.end, "HH:mm:ssZ").diff(dayjs(time.start, "HH:mm:ssZ"),'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 setState = async (newState) => { + itemInfo.value.state = newState + await updateTime() +}