diff --git a/apps/portal/pages/admin/scheduling.vue b/apps/portal/pages/admin/scheduling.vue index 7215ea8..9896df1 100644 --- a/apps/portal/pages/admin/scheduling.vue +++ b/apps/portal/pages/admin/scheduling.vue @@ -30,7 +30,8 @@ interface EventType { } interface MinuteInterval { startMinute: number; endMinute: number } interface WeeklyRule { dayOfWeek: number; intervals: MinuteInterval[] } -interface Availability { _id: string; name: string; timezone: string; weeklyRules: WeeklyRule[] } +interface DateOverride { date: string; isUnavailable: boolean; intervals: MinuteInterval[] } +interface Availability { _id: string; name: string; timezone: string; weeklyRules: WeeklyRule[]; dateOverrides?: DateOverride[] } interface Booking { _id: string status: string @@ -169,10 +170,14 @@ async function submitHost() { const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] const availOpen = ref(false) const availBusy = ref(false) +// Date-override editor rows mirror the backend shape but hold times as HH:MM +// for the controls; we convert to minutes on submit. +interface OverrideRow { date: string; isUnavailable: boolean; start: string; end: string } const availForm = reactive({ name: 'Working hours', timezone: 'Europe/Copenhagen', days: DAYS.map((_, i) => ({ enabled: i >= 1 && i <= 5, start: '09:00', end: '17:00' })), + overrides: [] as OverrideRow[], }) const availEditingId = ref(null) function openAvail(a?: Availability) { @@ -186,14 +191,30 @@ function openAvail(a?: Availability) { ? { enabled: true, start: minToTime(intv.startMinute), end: minToTime(intv.endMinute) } : { enabled: false, start: '09:00', end: '17:00' } }) + availForm.overrides = (a.dateOverrides ?? []).map((o) => { + const intv = o.intervals?.[0] + return { + date: o.date, + isUnavailable: o.isUnavailable, + start: intv ? minToTime(intv.startMinute) : '09:00', + end: intv ? minToTime(intv.endMinute) : '17:00', + } + }) } else { availEditingId.value = null availForm.name = 'Working hours' availForm.timezone = selectedHost.value?.timezone ?? 'Europe/Copenhagen' availForm.days = DAYS.map((_, i) => ({ enabled: i >= 1 && i <= 5, start: '09:00', end: '17:00' })) + availForm.overrides = [] } availOpen.value = true } +function addOverride() { + availForm.overrides.push({ date: '', isUnavailable: false, start: '09:00', end: '17:00' }) +} +function removeOverride(idx: number) { + availForm.overrides.splice(idx, 1) +} const timeToMin = (t: string) => { const [h, m] = t.split(':').map(Number); return h * 60 + m } async function submitAvail() { if (!selectedHostId.value) return @@ -203,7 +224,15 @@ async function submitAvail() { .map((d, i) => ({ dayOfWeek: i, enabled: d.enabled, start: d.start, end: d.end })) .filter((d) => d.enabled && timeToMin(d.end) > timeToMin(d.start)) .map((d) => ({ dayOfWeek: d.dayOfWeek, intervals: [{ startMinute: timeToMin(d.start), endMinute: timeToMin(d.end) }] })) - const body = { name: availForm.name, timezone: availForm.timezone, weeklyRules } + const dateOverrides: DateOverride[] = availForm.overrides + .filter((o) => /^\d{4}-\d{2}-\d{2}$/.test(o.date)) + .filter((o) => o.isUnavailable || timeToMin(o.end) > timeToMin(o.start)) + .map((o) => ({ + date: o.date, + isUnavailable: o.isUnavailable, + intervals: o.isUnavailable ? [] : [{ startMinute: timeToMin(o.start), endMinute: timeToMin(o.end) }], + })) + const body = { name: availForm.name, timezone: availForm.timezone, weeklyRules, dateOverrides } if (availEditingId.value) { await request(`${base.value}/availability/${availEditingId.value}`, { method: 'PATCH', body }) toast.ok('Availability updated') @@ -568,6 +597,21 @@ const detailTabs = computed(() => [ +
+
+ Date overrides + Add override +
+

Block specific dates or set custom hours that replace the weekly rules.

+
+ + + + + + +
+