From 0f14f7ac3d806795a496b61b48b9f4d509e768e6 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 29 Apr 2026 16:33:39 +0200 Subject: [PATCH] =?UTF-8?q?Verf=C3=BCgbarkeitshinweise=20f=C3=BCr=20Mitarb?= =?UTF-8?q?eiter=20und=20Plantafel-Details=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0034_profile_availability_note.sql | 2 + backend/db/schema/auth_profiles.ts | 1 + frontend/pages/organisation/plantafel.vue | 567 ++++++++++-------- frontend/pages/staff/profiles/[id].vue | 9 + 4 files changed, 332 insertions(+), 247 deletions(-) create mode 100644 backend/db/migrations/0034_profile_availability_note.sql diff --git a/backend/db/migrations/0034_profile_availability_note.sql b/backend/db/migrations/0034_profile_availability_note.sql new file mode 100644 index 0000000..ba13b5c --- /dev/null +++ b/backend/db/migrations/0034_profile_availability_note.sql @@ -0,0 +1,2 @@ +ALTER TABLE "auth_profiles" +ADD COLUMN "availability_note" text; diff --git a/backend/db/schema/auth_profiles.ts b/backend/db/schema/auth_profiles.ts index c013429..572fd2f 100644 --- a/backend/db/schema/auth_profiles.ts +++ b/backend/db/schema/auth_profiles.ts @@ -74,6 +74,7 @@ export const authProfiles = pgTable("auth_profiles", { contract_type: text("contract_type"), position: text("position"), qualification: text("qualification"), + availability_note: text("availability_note"), address_street: text("address_street"), address_zip: text("address_zip"), diff --git a/frontend/pages/organisation/plantafel.vue b/frontend/pages/organisation/plantafel.vue index d1e81aa..40d0536 100644 --- a/frontend/pages/organisation/plantafel.vue +++ b/frontend/pages/organisation/plantafel.vue @@ -4,6 +4,7 @@ import FullCalendar from "@fullcalendar/vue3" import interactionPlugin from "@fullcalendar/interaction" import resourceTimelinePlugin from "@fullcalendar/resource-timeline" import { parseDate } from "@internationalized/date" +import { useDraggable } from "@vueuse/core" const router = useRouter() const auth = useAuthStore() @@ -31,6 +32,8 @@ const profiles = ref([]) const inventoryitems = ref([]) const savingQuickConfig = ref(false) const isQuickConfigModalOpen = ref(false) +const quickConfigWindowEl = ref(null) +const profileDetailsWindowEl = ref(null) const showQuickConfigEditor = ref(true) const showQuickPresetManagement = ref(false) const quickEntryConfig = reactive({ @@ -49,6 +52,14 @@ const quickEntryColorOptions = [ { label: "Schwarz", value: "#111827" } ] +const { style: quickConfigWindowStyle } = useDraggable(quickConfigWindowEl, { + initialValue: { x: 120, y: 100 } +}) + +const { style: profileDetailsWindowStyle } = useDraggable(profileDetailsWindowEl, { + initialValue: { x: 220, y: 120 } +}) + const isAbsenceModalOpen = ref(false) const isProfileDetailsModalOpen = ref(false) const loadingProfileVacation = ref(false) @@ -157,6 +168,20 @@ const tenantCalendarConfig = computed(() => || {} ) +const resolvedPlanningBoardConfig = computed(() => { + const config = tenantCalendarConfig.value?.planningBoard || {} + const normalizedStartTime = /^\d{2}:\d{2}$/.test(String(config.startTime || "")) ? String(config.startTime) : "06:00" + const normalizedEndTime = /^\d{2}:\d{2}$/.test(String(config.endTime || "")) ? String(config.endTime) : "21:00" + const normalizedSlotMinutes = Number(config.slotMinutes) + const allowedSlotMinutes = [15, 30, 60, 120, 180] + + return { + startTime: normalizedStartTime, + endTime: normalizedEndTime, + slotMinutes: allowedSlotMinutes.includes(normalizedSlotMinutes) ? normalizedSlotMinutes : 180 + } +}) + const resolvedQuickEntryConfig = computed(() => { const config = tenantCalendarConfig.value?.quickEntry || {} @@ -215,6 +240,9 @@ const selectedProfileRemainingVacationDays = computed(() => { return Number((selectedProfileAnnualLeaveDays.value - selectedProfileVacationDaysTaken.value).toFixed(2)) }) +const selectedProfileAvailabilityNote = computed(() => + selectedProfile.value?.availability_note?.trim() || "Keine Verfügbarkeitshinweise hinterlegt" +) const visibleResources = computed(() => { if (selectedType.value === "all") return resources.value @@ -294,16 +322,20 @@ const calendarOptions = computed(() => ({ type: "resourceTimeline", duration: { days: 1 }, buttonText: "Tag", - slotDuration: { hours: 1 } + slotDuration: { minutes: resolvedPlanningBoardConfig.value.slotMinutes }, + snapDuration: { minutes: resolvedPlanningBoardConfig.value.slotMinutes }, + slotMinTime: `${resolvedPlanningBoardConfig.value.startTime}:00`, + slotMaxTime: `${resolvedPlanningBoardConfig.value.endTime}:00` }, resourceTimelineWeek: { type: "resourceTimeline", duration: { days: 7 }, buttonText: "Woche", - slotDuration: { hours: 3 }, + slotDuration: { minutes: resolvedPlanningBoardConfig.value.slotMinutes }, + snapDuration: { minutes: resolvedPlanningBoardConfig.value.slotMinutes }, weekends: false, - slotMinTime: "06:00:00", - slotMaxTime: "21:00:00" + slotMinTime: `${resolvedPlanningBoardConfig.value.startTime}:00`, + slotMaxTime: `${resolvedPlanningBoardConfig.value.endTime}:00` }, resourceTimelineMonth: { type: "resourceTimeline", @@ -991,121 +1023,62 @@ onMounted(() => { /> - - - + +
+ + +
+ + + + + + + + + +
+
+ +
+ +
+ + +
+
+
+ +
+
+
+

Vorschau

+
+ {{ quickEntryConfig.name || "Quick-Eintrag" }} +
+
+ + Vorlage speichern + +
+
+
+
+ + + +
+ +
+ - - - + +
+
+
+

Urlaub {{ currentVacationYear }}

+

Anzeige auf Basis der aktuellen Zeitauswertung.

+
+ + Resturlaub + +
+ +
+ + + +
+ +
+
+
Urlaubsanspruch
+
{{ formatProfileNumber(selectedProfileAnnualLeaveDays) }}
+
+
+
Bereits verplant/genehmigt
+
{{ formatProfileNumber(selectedProfileVacationDaysTaken) }}
+
+
+
Resturlaub
+
+ {{ selectedProfileRemainingVacationDays === null ? "Nicht hinterlegt" : formatProfileNumber(selectedProfileRemainingVacationDays) }} +
+
+
+
+ + +
+
+
+

Verfügbarkeitshinweis

+

Z. B. bevorzugte Einsatzzeiten oder bekannte Einschränkungen.

+
+ + Verfügbarkeit + +
+

+ {{ selectedProfileAvailabilityNote }} +

+
+ + + +
+ + Schließen + +
+ +
+ +
+ diff --git a/frontend/pages/staff/profiles/[id].vue b/frontend/pages/staff/profiles/[id].vue index 7286ea3..63d5b29 100644 --- a/frontend/pages/staff/profiles/[id].vue +++ b/frontend/pages/staff/profiles/[id].vue @@ -548,6 +548,15 @@ onMounted(async () => { + + + +