@soulbatical/tetra-ui 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/planner/components/AvailabilityEditor.js +4 -4
- package/dist/planner/components/AvailabilityEditor.js.map +1 -1
- package/dist/planner/components/TeamPlanner.d.ts +6 -17
- package/dist/planner/components/TeamPlanner.d.ts.map +1 -1
- package/dist/planner/components/TeamPlanner.js +115 -84
- package/dist/planner/components/TeamPlanner.js.map +1 -1
- package/dist/planner/components/WeekCalendar.d.ts +5 -30
- package/dist/planner/components/WeekCalendar.d.ts.map +1 -1
- package/dist/planner/components/WeekCalendar.js +72 -126
- package/dist/planner/components/WeekCalendar.js.map +1 -1
- package/package.json +2 -1
|
@@ -16,7 +16,7 @@ import { usePlannerApi } from '../hooks/usePlannerApi.js';
|
|
|
16
16
|
const DEFAULT_DAYS = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'];
|
|
17
17
|
const DEFAULT_BLOCK = { startTime: '09:00', endTime: '17:00' };
|
|
18
18
|
export function AvailabilityEditor({ apiBase, userId, className = '', onSaved, labels, }) {
|
|
19
|
-
const
|
|
19
|
+
const { getAvailability, setAvailability: saveAvailability } = usePlannerApi({ apiBase });
|
|
20
20
|
const dayNames = labels?.dayNames || DEFAULT_DAYS;
|
|
21
21
|
const [schedule, setSchedule] = useState(Array.from({ length: 7 }, () => ({ enabled: false, blocks: [{ ...DEFAULT_BLOCK }] })));
|
|
22
22
|
const [saving, setSaving] = useState(false);
|
|
@@ -24,7 +24,7 @@ export function AvailabilityEditor({ apiBase, userId, className = '', onSaved, l
|
|
|
24
24
|
// ── Load existing availability ──────────────────────────
|
|
25
25
|
const loadAvailability = useCallback(async () => {
|
|
26
26
|
try {
|
|
27
|
-
const slots = await
|
|
27
|
+
const slots = await getAvailability(userId);
|
|
28
28
|
const newSchedule = Array.from({ length: 7 }, () => ({
|
|
29
29
|
enabled: false,
|
|
30
30
|
blocks: [{ ...DEFAULT_BLOCK }],
|
|
@@ -50,7 +50,7 @@ export function AvailabilityEditor({ apiBase, userId, className = '', onSaved, l
|
|
|
50
50
|
catch {
|
|
51
51
|
// keep defaults
|
|
52
52
|
}
|
|
53
|
-
}, [
|
|
53
|
+
}, [getAvailability, userId]);
|
|
54
54
|
useEffect(() => { loadAvailability(); }, [loadAvailability]);
|
|
55
55
|
// ── Save ────────────────────────────────────────────────
|
|
56
56
|
const handleSave = async () => {
|
|
@@ -65,7 +65,7 @@ export function AvailabilityEditor({ apiBase, userId, className = '', onSaved, l
|
|
|
65
65
|
}))
|
|
66
66
|
: []);
|
|
67
67
|
try {
|
|
68
|
-
const res = await
|
|
68
|
+
const res = await saveAvailability(slots);
|
|
69
69
|
if (res.success) {
|
|
70
70
|
setSaveStatus('saved');
|
|
71
71
|
onSaved?.();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvailabilityEditor.js","sourceRoot":"","sources":["../../../src/planner/components/AvailabilityEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,YAAY,CAAC;;AAEb,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,aAAa,EAAyB,MAAM,2BAA2B,CAAC;AA4BjF,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACtG,MAAM,aAAa,GAAc,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE1E,MAAM,UAAU,kBAAkB,CAAC,EACjC,OAAO,EACP,MAAM,EACN,SAAS,GAAG,EAAE,EACd,OAAO,EACP,MAAM,GACkB;IACxB,MAAM,
|
|
1
|
+
{"version":3,"file":"AvailabilityEditor.js","sourceRoot":"","sources":["../../../src/planner/components/AvailabilityEditor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TeamPlanner —
|
|
2
|
+
* TeamPlanner — Horizontal resource timeline (DayPilot-powered)
|
|
3
3
|
*
|
|
4
|
-
* Shows all team members as
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```tsx
|
|
9
|
-
* <TeamPlanner
|
|
10
|
-
* apiBase="/planner"
|
|
11
|
-
* onBookSlot={(ownerId, date, startTime, endTime) => { ... }}
|
|
12
|
-
* />
|
|
13
|
-
* ```
|
|
4
|
+
* Shows all team members as rows with a horizontal time axis.
|
|
5
|
+
* Supports day/week/month zoom. Powered by DayPilot Lite (Apache 2.0).
|
|
14
6
|
*/
|
|
15
7
|
import { type Appointment } from '../hooks/usePlannerApi.js';
|
|
16
8
|
export interface TeamPlannerProps {
|
|
@@ -20,11 +12,8 @@ export interface TeamPlannerProps {
|
|
|
20
12
|
onBookSlot?: (ownerId: string, date: string, startTime: string, endTime: string) => void;
|
|
21
13
|
onAppointmentClick?: (appointment: Appointment) => void;
|
|
22
14
|
className?: string;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
vacation?: string;
|
|
26
|
-
noTeamMembers?: string;
|
|
27
|
-
};
|
|
15
|
+
locale?: string;
|
|
16
|
+
defaultZoom?: 'day' | 'week' | 'month';
|
|
28
17
|
}
|
|
29
|
-
export declare function TeamPlanner({ apiBase, startHour, endHour, onBookSlot, onAppointmentClick, className,
|
|
18
|
+
export declare function TeamPlanner({ apiBase, startHour, endHour, onBookSlot, onAppointmentClick, className, locale, defaultZoom, }: TeamPlannerProps): import("react/jsx-runtime").JSX.Element;
|
|
30
19
|
//# sourceMappingURL=TeamPlanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamPlanner.d.ts","sourceRoot":"","sources":["../../../src/planner/components/TeamPlanner.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"TeamPlanner.d.ts","sourceRoot":"","sources":["../../../src/planner/components/TeamPlanner.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAiB,KAAK,WAAW,EAAyB,MAAM,2BAA2B,CAAC;AAEnG,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzF,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CACxC;AAkDD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,SAAa,EACb,OAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,SAAc,EACd,MAAgB,EAChB,WAAmB,GACpB,EAAE,gBAAgB,2CAuIlB"}
|
|
@@ -1,110 +1,141 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TeamPlanner —
|
|
2
|
+
* TeamPlanner — Horizontal resource timeline (DayPilot-powered)
|
|
3
3
|
*
|
|
4
|
-
* Shows all team members as
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```tsx
|
|
9
|
-
* <TeamPlanner
|
|
10
|
-
* apiBase="/planner"
|
|
11
|
-
* onBookSlot={(ownerId, date, startTime, endTime) => { ... }}
|
|
12
|
-
* />
|
|
13
|
-
* ```
|
|
4
|
+
* Shows all team members as rows with a horizontal time axis.
|
|
5
|
+
* Supports day/week/month zoom. Powered by DayPilot Lite (Apache 2.0).
|
|
14
6
|
*/
|
|
15
7
|
'use client';
|
|
16
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
-
import { useState, useEffect, useCallback,
|
|
9
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
10
|
+
import { DayPilot, DayPilotScheduler, DayPilotNavigator } from '@daypilot/daypilot-lite-react';
|
|
18
11
|
import { usePlannerApi } from '../hooks/usePlannerApi.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
function isToday(dateStr) {
|
|
27
|
-
return dateStr === new Date().toISOString().substring(0, 10);
|
|
28
|
-
}
|
|
29
|
-
function timeToY(time, startHour) {
|
|
30
|
-
const [h, m] = time.split(':').map(Number);
|
|
31
|
-
return ((h - startHour) + m / 60) * HOUR_HEIGHT;
|
|
12
|
+
function appointmentColor(status) {
|
|
13
|
+
switch (status) {
|
|
14
|
+
case 'scheduled': return '#3b82f6';
|
|
15
|
+
case 'completed': return '#22c55e';
|
|
16
|
+
case 'cancelled': return '#9ca3af';
|
|
17
|
+
default: return '#3b82f6';
|
|
18
|
+
}
|
|
32
19
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
function getZoomConfig(zoom) {
|
|
21
|
+
switch (zoom) {
|
|
22
|
+
case 'day':
|
|
23
|
+
return {
|
|
24
|
+
scale: 'CellDuration',
|
|
25
|
+
cellDuration: 30,
|
|
26
|
+
days: 1,
|
|
27
|
+
timeHeaders: [
|
|
28
|
+
{ groupBy: 'Day', format: 'dddd d MMMM' },
|
|
29
|
+
{ groupBy: 'Hour' },
|
|
30
|
+
],
|
|
31
|
+
cellWidth: 60,
|
|
32
|
+
};
|
|
33
|
+
case 'week':
|
|
34
|
+
return {
|
|
35
|
+
scale: 'CellDuration',
|
|
36
|
+
cellDuration: 60,
|
|
37
|
+
days: 7,
|
|
38
|
+
timeHeaders: [
|
|
39
|
+
{ groupBy: 'Day', format: 'ddd d/M' },
|
|
40
|
+
{ groupBy: 'Hour', format: 'HH' },
|
|
41
|
+
],
|
|
42
|
+
cellWidth: 30,
|
|
43
|
+
};
|
|
44
|
+
case 'month':
|
|
45
|
+
return {
|
|
46
|
+
scale: 'Day',
|
|
47
|
+
days: 31,
|
|
48
|
+
timeHeaders: [
|
|
49
|
+
{ groupBy: 'Month' },
|
|
50
|
+
{ groupBy: 'Day', format: 'd' },
|
|
51
|
+
],
|
|
52
|
+
cellWidth: 40,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
37
55
|
}
|
|
38
|
-
export function TeamPlanner({ apiBase, startHour = 8, endHour = 18, onBookSlot, onAppointmentClick, className = '',
|
|
39
|
-
const
|
|
40
|
-
const
|
|
56
|
+
export function TeamPlanner({ apiBase, startHour = 8, endHour = 18, onBookSlot, onAppointmentClick, className = '', locale = 'nl-nl', defaultZoom = 'day', }) {
|
|
57
|
+
const { getTeamAvailability, getAppointments } = usePlannerApi({ apiBase });
|
|
58
|
+
const schedulerRef = useRef(null);
|
|
59
|
+
const [startDate, setStartDate] = useState(() => DayPilot.Date.today());
|
|
60
|
+
const [zoom, setZoom] = useState(defaultZoom);
|
|
41
61
|
const [teamData, setTeamData] = useState(null);
|
|
42
62
|
const [appointments, setAppointments] = useState([]);
|
|
43
63
|
const [loading, setLoading] = useState(true);
|
|
44
|
-
const hours = useMemo(() => Array.from({ length: endHour - startHour }, (_, i) => startHour + i), [startHour, endHour]);
|
|
45
|
-
const totalHeight = hours.length * HOUR_HEIGHT;
|
|
46
64
|
const fetchData = useCallback(async () => {
|
|
47
65
|
setLoading(true);
|
|
48
66
|
try {
|
|
67
|
+
const dateStr = startDate.toString('yyyy-MM-dd');
|
|
49
68
|
const [team, appts] = await Promise.all([
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
getTeamAvailability(dateStr),
|
|
70
|
+
getAppointments(),
|
|
52
71
|
]);
|
|
53
72
|
setTeamData(team);
|
|
54
73
|
setAppointments(appts);
|
|
55
74
|
}
|
|
56
|
-
catch {
|
|
57
|
-
// silent
|
|
58
|
-
}
|
|
75
|
+
catch { /* silent */ }
|
|
59
76
|
finally {
|
|
60
77
|
setLoading(false);
|
|
61
78
|
}
|
|
62
|
-
}, [
|
|
79
|
+
}, [getTeamAvailability, getAppointments, startDate]);
|
|
63
80
|
useEffect(() => { fetchData(); }, [fetchData]);
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
a.
|
|
72
|
-
a.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
const resources = (teamData?.owners || []).map((owner) => ({
|
|
82
|
+
id: owner.id,
|
|
83
|
+
name: owner.name || 'Unnamed',
|
|
84
|
+
}));
|
|
85
|
+
const events = appointments
|
|
86
|
+
.filter((a) => a.status !== 'cancelled')
|
|
87
|
+
.map((a) => ({
|
|
88
|
+
id: a.id,
|
|
89
|
+
text: a.title,
|
|
90
|
+
start: new DayPilot.Date(a.startTime),
|
|
91
|
+
end: new DayPilot.Date(a.endTime),
|
|
92
|
+
resource: a.organizerId,
|
|
93
|
+
backColor: appointmentColor(a.status),
|
|
94
|
+
barColor: appointmentColor(a.status),
|
|
95
|
+
fontColor: '#ffffff',
|
|
96
|
+
data: a,
|
|
97
|
+
}));
|
|
98
|
+
const zoomConfig = getZoomConfig(zoom);
|
|
99
|
+
const schedulerConfig = {
|
|
100
|
+
startDate,
|
|
101
|
+
locale,
|
|
102
|
+
...zoomConfig,
|
|
103
|
+
eventHeight: 32,
|
|
104
|
+
rowHeaderWidth: 150,
|
|
105
|
+
resources,
|
|
106
|
+
events,
|
|
107
|
+
treeEnabled: false,
|
|
108
|
+
businessBeginsHour: startHour,
|
|
109
|
+
businessEndsHour: endHour,
|
|
110
|
+
showNonBusiness: false,
|
|
111
|
+
eventMoveHandling: 'Disabled',
|
|
112
|
+
eventResizeHandling: 'Disabled',
|
|
113
|
+
timeRangeSelectedHandling: 'Enabled',
|
|
114
|
+
onTimeRangeSelected: (args) => {
|
|
115
|
+
if (onBookSlot) {
|
|
116
|
+
onBookSlot(args.resource, args.start.toString('yyyy-MM-dd'), args.start.toString('HH:mm'), args.end.toString('HH:mm'));
|
|
117
|
+
}
|
|
118
|
+
schedulerRef.current?.clearSelection();
|
|
119
|
+
},
|
|
120
|
+
onEventClick: (args) => {
|
|
121
|
+
if (onAppointmentClick && args.e?.data?.data) {
|
|
122
|
+
onAppointmentClick(args.e.data.data);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
onBeforeEventRender: (args) => {
|
|
126
|
+
args.data.borderRadius = '4px';
|
|
127
|
+
args.data.fontColor = '#ffffff';
|
|
128
|
+
},
|
|
129
|
+
onBeforeRowHeaderRender: (args) => {
|
|
130
|
+
const owner = teamData?.owners?.find((o) => o.id === args.row?.data?.id);
|
|
131
|
+
if (owner?.vacationDay) {
|
|
132
|
+
args.row.backColor = '#fef3c7';
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
controlRef: (ref) => { schedulerRef.current = ref; },
|
|
79
136
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (!isToday(currentDate))
|
|
84
|
-
return null;
|
|
85
|
-
const now = new Date();
|
|
86
|
-
const h = now.getHours();
|
|
87
|
-
const m = now.getMinutes();
|
|
88
|
-
if (h < startHour || h >= endHour)
|
|
89
|
-
return null;
|
|
90
|
-
return ((h - startHour) + m / 60) * HOUR_HEIGHT;
|
|
91
|
-
}, [currentDate, startHour, endHour]);
|
|
92
|
-
return (_jsxs("div", { className: `flex flex-col bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden ${className}`, children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: () => goToDay(-1), className: "p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.75 19.5L8.25 12l7.5-7.5" }) }) }), _jsx("button", { onClick: goToToday, className: "px-3 py-1 text-sm font-medium rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", children: labels?.today || 'Vandaag' }), _jsx("button", { onClick: () => goToDay(1), className: "p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) })] }), _jsx("div", { className: `text-sm font-medium ${isToday(currentDate) ? 'text-blue-600 dark:text-blue-400' : 'text-gray-700 dark:text-gray-300'}`, children: formatDateFull(currentDate) }), _jsxs("div", { className: "text-xs text-gray-400", children: [owners.length, " ", owners.length === 1 ? 'persoon' : 'personen'] })] }), _jsxs("div", { className: `grid border-b border-gray-200 dark:border-gray-700`, style: { gridTemplateColumns: `60px repeat(${colCount}, 1fr)` }, children: [_jsx("div", { className: "border-r border-gray-200 dark:border-gray-700" }), owners.map((owner) => (_jsx("div", { className: "py-2 px-2 text-center border-r border-gray-100 dark:border-gray-800", children: _jsxs("div", { className: "flex flex-col items-center gap-1", children: [owner.avatarUrl ? (_jsx("img", { src: owner.avatarUrl, alt: "", className: "w-8 h-8 rounded-full" })) : (_jsx("div", { className: "w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center text-xs font-medium text-gray-600 dark:text-gray-300", children: getInitials(owner.name) })), _jsx("div", { className: "text-xs font-medium text-gray-700 dark:text-gray-300 truncate max-w-[80px]", children: owner.name || 'Unnamed' }), owner.vacationDay && (_jsx("span", { className: "text-[10px] px-1.5 py-0.5 rounded-full bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400", children: labels?.vacation || 'Vakantie' }))] }) }, owner.id)))] }), owners.length === 0 && !loading ? (_jsx("div", { className: "py-12 text-center text-gray-400", children: labels?.noTeamMembers || 'Geen teamleden gevonden' })) : (_jsx("div", { className: "overflow-y-auto", style: { maxHeight: '600px' }, children: _jsxs("div", { className: `grid`, style: { gridTemplateColumns: `60px repeat(${colCount}, 1fr)`, height: totalHeight }, children: [_jsx("div", { className: "relative border-r border-gray-200 dark:border-gray-700", children: hours.map((hour) => (_jsxs("div", { className: "absolute w-full text-right pr-2 text-xs text-gray-400 dark:text-gray-500 -translate-y-1/2", style: { top: (hour - startHour) * HOUR_HEIGHT }, children: [String(hour).padStart(2, '0'), ":00"] }, hour))) }), owners.map((owner) => {
|
|
93
|
-
const ownerAppts = getOwnerAppointments(owner.id);
|
|
94
|
-
const ownerSlots = owner.slots || [];
|
|
95
|
-
return (_jsxs("div", { className: `relative border-r border-gray-100 dark:border-gray-800 ${owner.vacationDay ? 'bg-amber-50/30 dark:bg-amber-950/10' : ''}`, children: [hours.map((hour) => (_jsx("div", { className: `absolute w-full border-t border-gray-100 dark:border-gray-800 ${owner.vacationDay ? '' : 'cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50'} transition-colors`, style: { top: (hour - startHour) * HOUR_HEIGHT, height: HOUR_HEIGHT }, onClick: () => !owner.vacationDay && handleCellClick(owner.id, hour) }, hour))), ownerSlots.map((slot, idx) => {
|
|
96
|
-
const top = timeToY(slot.startTime, startHour);
|
|
97
|
-
const bottom = timeToY(slot.endTime, startHour);
|
|
98
|
-
const height = bottom - top;
|
|
99
|
-
if (height <= 0)
|
|
100
|
-
return null;
|
|
101
|
-
return (_jsx("div", { className: "absolute left-0 right-0 bg-green-100/40 dark:bg-green-900/20 border-l-2 border-green-400/50 pointer-events-none", style: { top, height } }, idx));
|
|
102
|
-
}), ownerAppts.map((appt) => {
|
|
103
|
-
const top = timeToY(appt.startTime.substring(11, 16), startHour);
|
|
104
|
-
const bottom = timeToY(appt.endTime.substring(11, 16), startHour);
|
|
105
|
-
const height = Math.max(bottom - top, 20);
|
|
106
|
-
return (_jsxs("div", { className: "absolute left-1 right-1 rounded-md px-1.5 py-0.5 text-white text-xs cursor-pointer bg-blue-500/90 hover:bg-blue-600/90 border-l-2 border-blue-600 shadow-sm transition-colors", style: { top, height, zIndex: 10 }, onClick: (e) => { e.stopPropagation(); onAppointmentClick?.(appt); }, title: appt.title, children: [_jsx("div", { className: "font-medium truncate", children: appt.title }), height > 30 && (_jsxs("div", { className: "text-[10px] opacity-80", children: [appt.startTime.substring(11, 16), " - ", appt.endTime.substring(11, 16)] }))] }, appt.id));
|
|
107
|
-
}), nowY !== null && (_jsx("div", { className: "absolute left-0 right-0 z-20 pointer-events-none", style: { top: nowY }, children: _jsx("div", { className: "h-[2px] bg-red-500/50" }) }))] }, owner.id));
|
|
108
|
-
})] }) })), loading && (_jsx("div", { className: "absolute inset-0 bg-white/60 dark:bg-gray-900/60 flex items-center justify-center z-30", children: _jsx("div", { className: "w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" }) }))] }));
|
|
137
|
+
return (_jsxs("div", { className: `flex flex-col gap-4 ${className}`, children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(DayPilotNavigator, { selectMode: zoom === 'month' ? 'Month' : 'Day', showMonths: 1, skipMonths: 1, startDate: startDate, locale: locale, onTimeRangeSelected: (args) => setStartDate(args.start) }), _jsx("div", { className: "flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded-lg p-1", children: ['day', 'week', 'month'].map((z) => (_jsx("button", { onClick: () => setZoom(z), className: `px-3 py-1 text-xs font-medium rounded-md transition-colors ${zoom === z
|
|
138
|
+
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm'
|
|
139
|
+
: 'text-gray-500 dark:text-gray-400 hover:text-gray-700'}`, children: z === 'day' ? 'Dag' : z === 'week' ? 'Week' : 'Maand' }, z))) })] }), loading && resources.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-64", children: _jsx("div", { className: "w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" }) })) : (_jsx(DayPilotScheduler, { ...schedulerConfig })), _jsxs("div", { className: "text-[10px] text-gray-400 text-right", children: ["Powered by ", _jsx("a", { href: "https://www.daypilot.org/", target: "_blank", rel: "noopener noreferrer", className: "underline hover:text-gray-600", children: "DayPilot" })] })] }));
|
|
109
140
|
}
|
|
110
141
|
//# sourceMappingURL=TeamPlanner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamPlanner.js","sourceRoot":"","sources":["../../../src/planner/components/TeamPlanner.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"TeamPlanner.js","sourceRoot":"","sources":["../../../src/planner/components/TeamPlanner.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,CAAC;;AAEb,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,aAAa,EAA2C,MAAM,2BAA2B,CAAC;AAenG,SAAS,gBAAgB,CAAC,MAAc;IACtC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW,CAAC,CAAC,OAAO,SAAS,CAAC;QACnC,KAAK,WAAW,CAAC,CAAC,OAAO,SAAS,CAAC;QACnC,KAAK,WAAW,CAAC,CAAC,OAAO,SAAS,CAAC;QACnC,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAe;IACpC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,KAAK;YACR,OAAO;gBACL,KAAK,EAAE,cAAc;gBACrB,YAAY,EAAE,EAAE;gBAChB,IAAI,EAAE,CAAC;gBACP,WAAW,EAAE;oBACX,EAAE,OAAO,EAAE,KAAc,EAAE,MAAM,EAAE,aAAa,EAAE;oBAClD,EAAE,OAAO,EAAE,MAAe,EAAE;iBAC7B;gBACD,SAAS,EAAE,EAAE;aACd,CAAC;QACJ,KAAK,MAAM;YACT,OAAO;gBACL,KAAK,EAAE,cAAc;gBACrB,YAAY,EAAE,EAAE;gBAChB,IAAI,EAAE,CAAC;gBACP,WAAW,EAAE;oBACX,EAAE,OAAO,EAAE,KAAc,EAAE,MAAM,EAAE,SAAS,EAAE;oBAC9C,EAAE,OAAO,EAAE,MAAe,EAAE,MAAM,EAAE,IAAI,EAAE;iBAC3C;gBACD,SAAS,EAAE,EAAE;aACd,CAAC;QACJ,KAAK,OAAO;YACV,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,IAAI,EAAE,EAAE;gBACR,WAAW,EAAE;oBACX,EAAE,OAAO,EAAE,OAAgB,EAAE;oBAC7B,EAAE,OAAO,EAAE,KAAc,EAAE,MAAM,EAAE,GAAG,EAAE;iBACzC;gBACD,SAAS,EAAE,EAAE;aACd,CAAC;IACN,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAC1B,OAAO,EACP,SAAS,GAAG,CAAC,EACb,OAAO,GAAG,EAAE,EACZ,UAAU,EACV,kBAAkB,EAClB,SAAS,GAAG,EAAE,EACd,MAAM,GAAG,OAAO,EAChB,WAAW,GAAG,KAAK,GACF;IACjB,MAAM,EAAE,mBAAmB,EAAE,eAAe,EAAE,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,MAAM,CAAM,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACxE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAY,WAAW,CAAC,CAAC;IACzD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAA0B,IAAI,CAAC,CAAC;IACxE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAgB,EAAE,CAAC,CAAC;IACpE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7C,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACvC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACtC,mBAAmB,CAAC,OAAO,CAAC;gBAC5B,eAAe,EAAE;aAClB,CAAC,CAAC;YACH,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,eAAe,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;gBAChB,CAAC;YAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAAC,CAAC;IAChC,CAAC,EAAE,CAAC,mBAAmB,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;IAEtD,SAAS,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAE/C,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACzD,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,SAAS;KAC9B,CAAC,CAAC,CAAC;IAEJ,MAAM,MAAM,GAAG,YAAY;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;SACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,KAAK;QACb,KAAK,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACrC,GAAG,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QACjC,QAAQ,EAAE,CAAC,CAAC,WAAW;QACvB,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC;QACrC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC;QACpC,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,CAAC;KACR,CAAC,CAAC,CAAC;IAEN,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,eAAe,GAAQ;QAC3B,SAAS;QACT,MAAM;QACN,GAAG,UAAU;QACb,WAAW,EAAE,EAAE;QACf,cAAc,EAAE,GAAG;QACnB,SAAS;QACT,MAAM;QACN,WAAW,EAAE,KAAK;QAClB,kBAAkB,EAAE,SAAS;QAC7B,gBAAgB,EAAE,OAAO;QACzB,eAAe,EAAE,KAAK;QACtB,iBAAiB,EAAE,UAAU;QAC7B,mBAAmB,EAAE,UAAU;QAC/B,yBAAyB,EAAE,SAAS;QACpC,mBAAmB,EAAE,CAAC,IAAS,EAAE,EAAE;YACjC,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CACR,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EACjC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAC5B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC3B,CAAC;YACJ,CAAC;YACD,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;QACzC,CAAC;QACD,YAAY,EAAE,CAAC,IAAS,EAAE,EAAE;YAC1B,IAAI,kBAAkB,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBAC7C,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QACD,mBAAmB,EAAE,CAAC,IAAS,EAAE,EAAE;YACjC,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAClC,CAAC;QACD,uBAAuB,EAAE,CAAC,IAAS,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YACzE,IAAI,KAAK,EAAE,WAAW,EAAE,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;YACjC,CAAC;QACH,CAAC;QACD,UAAU,EAAE,CAAC,GAAQ,EAAE,EAAE,GAAG,YAAY,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC;KAC1D,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,uBAAuB,SAAS,EAAE,aAEhD,eAAK,SAAS,EAAC,mCAAmC,aAChD,KAAC,iBAAiB,IAChB,UAAU,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAC9C,UAAU,EAAE,CAAC,EACb,UAAU,EAAE,CAAC,EACb,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,CAAC,IAAS,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,GAC5D,EAEF,cAAK,SAAS,EAAC,qEAAqE,YAChF,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC9C,iBAEE,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EACzB,SAAS,EAAE,8DACT,IAAI,KAAK,CAAC;gCACR,CAAC,CAAC,sEAAsE;gCACxE,CAAC,CAAC,sDACN,EAAE,YAED,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IARjD,CAAC,CASC,CACV,CAAC,GACE,IACF,EAGL,OAAO,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACnC,cAAK,SAAS,EAAC,uCAAuC,YACpD,cAAK,SAAS,EAAC,iFAAiF,GAAG,GAC/F,CACP,CAAC,CAAC,CAAC,CACF,KAAC,iBAAiB,OAAK,eAAe,GAAI,CAC3C,EAED,eAAK,SAAS,EAAC,sCAAsC,4BACxC,YAAG,IAAI,EAAC,2BAA2B,EAAC,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,EAAC,SAAS,EAAC,+BAA+B,yBAAa,IAC3I,IACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -1,44 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WeekCalendar — Personal agenda week view
|
|
2
|
+
* WeekCalendar — Personal agenda week view (DayPilot-powered)
|
|
3
3
|
*
|
|
4
|
-
* Shows a
|
|
5
|
-
*
|
|
6
|
-
* - Appointments as colored blocks
|
|
7
|
-
* - Availability as background shading
|
|
8
|
-
* - Vacation days grayed out
|
|
9
|
-
* - Click on empty slot → create appointment
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* ```tsx
|
|
13
|
-
* <WeekCalendar
|
|
14
|
-
* apiBase="/planner"
|
|
15
|
-
* userId={user.id}
|
|
16
|
-
* onCreateAppointment={(date, startTime, endTime) => { ... }}
|
|
17
|
-
* />
|
|
18
|
-
* ```
|
|
4
|
+
* Shows a week view with appointments, availability backgrounds, and click-to-create.
|
|
5
|
+
* Powered by DayPilot Lite (Apache 2.0).
|
|
19
6
|
*/
|
|
20
7
|
import { type Appointment } from '../hooks/usePlannerApi.js';
|
|
21
8
|
export interface WeekCalendarProps {
|
|
22
|
-
/** API base path (default: '/planner') */
|
|
23
9
|
apiBase?: string;
|
|
24
|
-
/** Current user ID (to fetch own availability) */
|
|
25
10
|
userId: string;
|
|
26
|
-
/** Start hour of the day (default: 8) */
|
|
27
11
|
startHour?: number;
|
|
28
|
-
/** End hour of the day (default: 18) */
|
|
29
12
|
endHour?: number;
|
|
30
|
-
/** Callback when clicking an empty slot */
|
|
31
13
|
onCreateAppointment?: (date: string, startTime: string, endTime: string) => void;
|
|
32
|
-
/** Callback when clicking an appointment */
|
|
33
14
|
onAppointmentClick?: (appointment: Appointment) => void;
|
|
34
|
-
/** Additional CSS class */
|
|
35
15
|
className?: string;
|
|
36
|
-
|
|
37
|
-
labels?: {
|
|
38
|
-
today?: string;
|
|
39
|
-
weekDays?: string[];
|
|
40
|
-
noAppointments?: string;
|
|
41
|
-
};
|
|
16
|
+
locale?: string;
|
|
42
17
|
}
|
|
43
|
-
export declare function WeekCalendar({ apiBase, userId, startHour, endHour, onCreateAppointment, onAppointmentClick, className,
|
|
18
|
+
export declare function WeekCalendar({ apiBase, userId, startHour, endHour, onCreateAppointment, onAppointmentClick, className, locale, }: WeekCalendarProps): import("react/jsx-runtime").JSX.Element;
|
|
44
19
|
//# sourceMappingURL=WeekCalendar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeekCalendar.d.ts","sourceRoot":"","sources":["../../../src/planner/components/WeekCalendar.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"WeekCalendar.d.ts","sourceRoot":"","sources":["../../../src/planner/components/WeekCalendar.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAiB,KAAK,WAAW,EAAyB,MAAM,2BAA2B,CAAC;AAEnG,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjF,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EACN,SAAa,EACb,OAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,SAAc,EACd,MAAgB,GACjB,EAAE,iBAAiB,2CAqGnB"}
|
|
@@ -1,151 +1,97 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* WeekCalendar — Personal agenda week view
|
|
2
|
+
* WeekCalendar — Personal agenda week view (DayPilot-powered)
|
|
3
3
|
*
|
|
4
|
-
* Shows a
|
|
5
|
-
*
|
|
6
|
-
* - Appointments as colored blocks
|
|
7
|
-
* - Availability as background shading
|
|
8
|
-
* - Vacation days grayed out
|
|
9
|
-
* - Click on empty slot → create appointment
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* ```tsx
|
|
13
|
-
* <WeekCalendar
|
|
14
|
-
* apiBase="/planner"
|
|
15
|
-
* userId={user.id}
|
|
16
|
-
* onCreateAppointment={(date, startTime, endTime) => { ... }}
|
|
17
|
-
* />
|
|
18
|
-
* ```
|
|
4
|
+
* Shows a week view with appointments, availability backgrounds, and click-to-create.
|
|
5
|
+
* Powered by DayPilot Lite (Apache 2.0).
|
|
19
6
|
*/
|
|
20
7
|
'use client';
|
|
21
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
|
-
import { useState, useEffect, useCallback,
|
|
9
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
10
|
+
import { DayPilot, DayPilotCalendar, DayPilotNavigator } from '@daypilot/daypilot-lite-react';
|
|
23
11
|
import { usePlannerApi } from '../hooks/usePlannerApi.js';
|
|
24
|
-
// ─── Helpers ────────────────────────────────────────────────
|
|
25
|
-
const DEFAULT_WEEKDAYS = ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'];
|
|
26
|
-
const HOUR_HEIGHT = 60; // px per hour
|
|
27
|
-
function getWeekDates(baseDate) {
|
|
28
|
-
const dates = [];
|
|
29
|
-
const monday = new Date(baseDate);
|
|
30
|
-
const day = monday.getDay();
|
|
31
|
-
const diff = day === 0 ? -6 : 1 - day;
|
|
32
|
-
monday.setDate(monday.getDate() + diff);
|
|
33
|
-
for (let i = 0; i < 7; i++) {
|
|
34
|
-
const d = new Date(monday);
|
|
35
|
-
d.setDate(d.getDate() + i);
|
|
36
|
-
dates.push(d.toISOString().substring(0, 10));
|
|
37
|
-
}
|
|
38
|
-
return dates;
|
|
39
|
-
}
|
|
40
|
-
function formatDate(dateStr) {
|
|
41
|
-
const d = new Date(dateStr + 'T00:00:00');
|
|
42
|
-
return `${d.getDate()}`;
|
|
43
|
-
}
|
|
44
|
-
function formatMonth(dateStr) {
|
|
45
|
-
const d = new Date(dateStr + 'T00:00:00');
|
|
46
|
-
const months = ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];
|
|
47
|
-
return months[d.getMonth()];
|
|
48
|
-
}
|
|
49
|
-
function timeToY(time, startHour) {
|
|
50
|
-
const [h, m] = time.split(':').map(Number);
|
|
51
|
-
return ((h - startHour) + m / 60) * HOUR_HEIGHT;
|
|
52
|
-
}
|
|
53
|
-
function isToday(dateStr) {
|
|
54
|
-
return dateStr === new Date().toISOString().substring(0, 10);
|
|
55
|
-
}
|
|
56
12
|
function appointmentColor(status) {
|
|
57
13
|
switch (status) {
|
|
58
|
-
case 'scheduled': return '
|
|
59
|
-
case 'completed': return '
|
|
60
|
-
case 'cancelled': return '
|
|
61
|
-
default: return '
|
|
14
|
+
case 'scheduled': return '#3b82f6';
|
|
15
|
+
case 'completed': return '#22c55e';
|
|
16
|
+
case 'cancelled': return '#9ca3af';
|
|
17
|
+
default: return '#3b82f6';
|
|
62
18
|
}
|
|
63
19
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const [currentDate, setCurrentDate] = useState(() => new Date());
|
|
20
|
+
export function WeekCalendar({ apiBase, userId, startHour = 8, endHour = 18, onCreateAppointment, onAppointmentClick, className = '', locale = 'nl-nl', }) {
|
|
21
|
+
const { getAppointments, getAvailability: getAvail } = usePlannerApi({ apiBase });
|
|
22
|
+
const calendarRef = useRef(null);
|
|
23
|
+
const [startDate, setStartDate] = useState(() => DayPilot.Date.today().firstDayOfWeek());
|
|
69
24
|
const [appointments, setAppointments] = useState([]);
|
|
70
25
|
const [availability, setAvailability] = useState([]);
|
|
71
|
-
const [loading, setLoading] = useState(true);
|
|
72
|
-
const weekDates = useMemo(() => getWeekDates(currentDate), [currentDate]);
|
|
73
|
-
const hours = useMemo(() => Array.from({ length: endHour - startHour }, (_, i) => startHour + i), [startHour, endHour]);
|
|
74
|
-
const totalHeight = hours.length * HOUR_HEIGHT;
|
|
75
|
-
// ── Fetch data ──────────────────────────────────────────
|
|
76
26
|
const fetchData = useCallback(async () => {
|
|
77
|
-
setLoading(true);
|
|
78
27
|
try {
|
|
79
28
|
const [appts, avail] = await Promise.all([
|
|
80
|
-
|
|
81
|
-
|
|
29
|
+
getAppointments(),
|
|
30
|
+
getAvail(userId),
|
|
82
31
|
]);
|
|
83
32
|
setAppointments(appts);
|
|
84
33
|
setAvailability(avail);
|
|
85
34
|
}
|
|
86
|
-
catch {
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
setLoading(false);
|
|
91
|
-
}
|
|
92
|
-
}, [api, userId]);
|
|
35
|
+
catch { /* silent */ }
|
|
36
|
+
}, [getAppointments, getAvail, userId]);
|
|
93
37
|
useEffect(() => { fetchData(); }, [fetchData]);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
38
|
+
const events = appointments
|
|
39
|
+
.filter((a) => a.status !== 'cancelled')
|
|
40
|
+
.map((a) => ({
|
|
41
|
+
id: a.id,
|
|
42
|
+
text: a.title,
|
|
43
|
+
start: new DayPilot.Date(a.startTime),
|
|
44
|
+
end: new DayPilot.Date(a.endTime),
|
|
45
|
+
backColor: appointmentColor(a.status),
|
|
46
|
+
barColor: appointmentColor(a.status),
|
|
47
|
+
fontColor: '#ffffff',
|
|
48
|
+
data: a,
|
|
49
|
+
}));
|
|
50
|
+
const onBeforeCellRender = useCallback((args) => {
|
|
51
|
+
const dayOfWeek = args.cell.start.getDayOfWeek();
|
|
52
|
+
const cellMinutes = args.cell.start.getHours() * 60 + args.cell.start.getMinutes();
|
|
53
|
+
const isAvailable = availability.some((slot) => {
|
|
54
|
+
if (slot.dayOfWeek !== dayOfWeek || !slot.isAvailable)
|
|
55
|
+
return false;
|
|
56
|
+
const slotStart = parseInt(slot.startTime.split(':')[0]) * 60 + parseInt(slot.startTime.split(':')[1]);
|
|
57
|
+
const slotEnd = parseInt(slot.endTime.split(':')[0]) * 60 + parseInt(slot.endTime.split(':')[1]);
|
|
58
|
+
return cellMinutes >= slotStart && cellMinutes < slotEnd;
|
|
100
59
|
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// ── Get appointments for a specific date ────────────────
|
|
104
|
-
const getAppointmentsForDate = (dateStr) => appointments.filter((a) => a.startTime.substring(0, 10) === dateStr && a.status !== 'cancelled');
|
|
105
|
-
// ── Get availability blocks for a day of week ───────────
|
|
106
|
-
const getAvailabilityForDay = (dayOfWeek) => availability.filter((s) => s.dayOfWeek === dayOfWeek && s.isAvailable);
|
|
107
|
-
// ── Click on empty cell ─────────────────────────────────
|
|
108
|
-
const handleCellClick = (dateStr, hour) => {
|
|
109
|
-
if (onCreateAppointment) {
|
|
110
|
-
const startTime = `${String(hour).padStart(2, '0')}:00`;
|
|
111
|
-
const endTime = `${String(hour + 1).padStart(2, '0')}:00`;
|
|
112
|
-
onCreateAppointment(dateStr, startTime, endTime);
|
|
60
|
+
if (isAvailable) {
|
|
61
|
+
args.cell.backColor = '#f0fdf4';
|
|
113
62
|
}
|
|
63
|
+
}, [availability]);
|
|
64
|
+
const calendarConfig = {
|
|
65
|
+
viewType: 'Week',
|
|
66
|
+
startDate,
|
|
67
|
+
locale,
|
|
68
|
+
events,
|
|
69
|
+
businessBeginsHour: startHour,
|
|
70
|
+
businessEndsHour: endHour,
|
|
71
|
+
showNonBusiness: false,
|
|
72
|
+
cellHeight: 30,
|
|
73
|
+
headerHeight: 40,
|
|
74
|
+
eventMoveHandling: 'Disabled',
|
|
75
|
+
eventResizeHandling: 'Disabled',
|
|
76
|
+
timeRangeSelectedHandling: 'Enabled',
|
|
77
|
+
onBeforeCellRender,
|
|
78
|
+
onTimeRangeSelected: (args) => {
|
|
79
|
+
if (onCreateAppointment) {
|
|
80
|
+
onCreateAppointment(args.start.toString('yyyy-MM-dd'), args.start.toString('HH:mm'), args.end.toString('HH:mm'));
|
|
81
|
+
}
|
|
82
|
+
calendarRef.current?.clearSelection();
|
|
83
|
+
},
|
|
84
|
+
onEventClick: (args) => {
|
|
85
|
+
if (onAppointmentClick && args.e?.data?.data) {
|
|
86
|
+
onAppointmentClick(args.e.data.data);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
onBeforeEventRender: (args) => {
|
|
90
|
+
args.data.borderRadius = '6px';
|
|
91
|
+
args.data.fontColor = '#ffffff';
|
|
92
|
+
},
|
|
93
|
+
controlRef: (ref) => { calendarRef.current = ref; },
|
|
114
94
|
};
|
|
115
|
-
|
|
116
|
-
const nowY = useMemo(() => {
|
|
117
|
-
const now = new Date();
|
|
118
|
-
const h = now.getHours();
|
|
119
|
-
const m = now.getMinutes();
|
|
120
|
-
if (h < startHour || h >= endHour)
|
|
121
|
-
return null;
|
|
122
|
-
return ((h - startHour) + m / 60) * HOUR_HEIGHT;
|
|
123
|
-
}, [startHour, endHour]);
|
|
124
|
-
const todayStr = new Date().toISOString().substring(0, 10);
|
|
125
|
-
return (_jsxs("div", { className: `flex flex-col bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden ${className}`, children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: () => goToWeek(-1), className: "p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.75 19.5L8.25 12l7.5-7.5" }) }) }), _jsx("button", { onClick: goToToday, className: "px-3 py-1 text-sm font-medium rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", children: labels?.today || 'Vandaag' }), _jsx("button", { onClick: () => goToWeek(1), className: "p-1.5 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) })] }), _jsxs("div", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: [formatDate(weekDates[0]), " ", formatMonth(weekDates[0]), " \u2014 ", formatDate(weekDates[6]), " ", formatMonth(weekDates[6])] })] }), _jsxs("div", { className: "grid grid-cols-[60px_repeat(7,1fr)] border-b border-gray-200 dark:border-gray-700", children: [_jsx("div", { className: "border-r border-gray-200 dark:border-gray-700" }), weekDates.map((dateStr, i) => {
|
|
126
|
-
const dayOfWeek = new Date(dateStr + 'T00:00:00').getDay();
|
|
127
|
-
const today = isToday(dateStr);
|
|
128
|
-
return (_jsxs("div", { className: `py-2 px-1 text-center border-r border-gray-100 dark:border-gray-800 ${today ? 'bg-blue-50 dark:bg-blue-950/30' : ''}`, children: [_jsx("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: weekDays[dayOfWeek] }), _jsx("div", { className: `text-lg font-semibold ${today ? 'text-blue-600 dark:text-blue-400' : 'text-gray-900 dark:text-gray-100'}`, children: formatDate(dateStr) })] }, dateStr));
|
|
129
|
-
})] }), _jsx("div", { className: "overflow-y-auto", style: { maxHeight: '600px' }, children: _jsxs("div", { className: "grid grid-cols-[60px_repeat(7,1fr)]", style: { height: totalHeight }, children: [_jsx("div", { className: "relative border-r border-gray-200 dark:border-gray-700", children: hours.map((hour) => (_jsxs("div", { className: "absolute w-full text-right pr-2 text-xs text-gray-400 dark:text-gray-500 -translate-y-1/2", style: { top: (hour - startHour) * HOUR_HEIGHT }, children: [String(hour).padStart(2, '0'), ":00"] }, hour))) }), weekDates.map((dateStr) => {
|
|
130
|
-
const dayOfWeek = new Date(dateStr + 'T00:00:00').getDay();
|
|
131
|
-
const dayAppts = getAppointmentsForDate(dateStr);
|
|
132
|
-
const dayAvail = getAvailabilityForDay(dayOfWeek);
|
|
133
|
-
const today = isToday(dateStr);
|
|
134
|
-
return (_jsxs("div", { className: `relative border-r border-gray-100 dark:border-gray-800 ${today ? 'bg-blue-50/30 dark:bg-blue-950/10' : ''}`, children: [hours.map((hour) => (_jsx("div", { className: "absolute w-full border-t border-gray-100 dark:border-gray-800 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors", style: { top: (hour - startHour) * HOUR_HEIGHT, height: HOUR_HEIGHT }, onClick: () => handleCellClick(dateStr, hour) }, hour))), dayAvail.map((slot, idx) => {
|
|
135
|
-
const top = timeToY(slot.startTime.substring(0, 5), startHour);
|
|
136
|
-
const bottom = timeToY(slot.endTime.substring(0, 5), startHour);
|
|
137
|
-
const height = bottom - top;
|
|
138
|
-
if (height <= 0)
|
|
139
|
-
return null;
|
|
140
|
-
return (_jsx("div", { className: "absolute left-0 right-0 bg-green-100/40 dark:bg-green-900/20 border-l-2 border-green-400/50 pointer-events-none", style: { top, height } }, idx));
|
|
141
|
-
}), dayAppts.map((appt) => {
|
|
142
|
-
const apptTime = appt.startTime.substring(11, 16);
|
|
143
|
-
const apptEndTime = appt.endTime.substring(11, 16);
|
|
144
|
-
const top = timeToY(apptTime, startHour);
|
|
145
|
-
const bottom = timeToY(apptEndTime, startHour);
|
|
146
|
-
const height = Math.max(bottom - top, 20);
|
|
147
|
-
return (_jsxs("div", { className: `absolute left-1 right-1 rounded-md px-1.5 py-0.5 text-white text-xs cursor-pointer border-l-3 shadow-sm transition-colors ${appointmentColor(appt.status)}`, style: { top, height, zIndex: 10 }, onClick: (e) => { e.stopPropagation(); onAppointmentClick?.(appt); }, title: `${appt.title}\n${apptTime} - ${apptEndTime}`, children: [_jsx("div", { className: "font-medium truncate", children: appt.title }), height > 30 && (_jsxs("div", { className: "text-[10px] opacity-80", children: [apptTime, " - ", apptEndTime] }))] }, appt.id));
|
|
148
|
-
}), today && nowY !== null && (_jsx("div", { className: "absolute left-0 right-0 z-20 pointer-events-none", style: { top: nowY }, children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "w-2 h-2 rounded-full bg-red-500 -ml-1" }), _jsx("div", { className: "flex-1 h-[2px] bg-red-500" })] }) }))] }, dateStr));
|
|
149
|
-
})] }) }), loading && (_jsx("div", { className: "absolute inset-0 bg-white/60 dark:bg-gray-900/60 flex items-center justify-center z-30", children: _jsx("div", { className: "w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" }) }))] }));
|
|
95
|
+
return (_jsxs("div", { className: `flex gap-4 ${className}`, children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(DayPilotNavigator, { selectMode: "Week", showMonths: 1, skipMonths: 1, startDate: startDate, locale: locale, onTimeRangeSelected: (args) => setStartDate(args.start) }) }), _jsx("div", { className: "flex-1 min-w-0", children: _jsx(DayPilotCalendar, { ...calendarConfig }) })] }));
|
|
150
96
|
}
|
|
151
97
|
//# sourceMappingURL=WeekCalendar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeekCalendar.js","sourceRoot":"","sources":["../../../src/planner/components/WeekCalendar.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"WeekCalendar.js","sourceRoot":"","sources":["../../../src/planner/components/WeekCalendar.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "restricted"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.1.
|
|
7
|
+
"version": "0.1.7",
|
|
8
8
|
"description": "Shared React frontend framework for Tetra platform projects (Soulbatical BV) — config-driven components, hooks, providers, and styling",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"main": "dist/index.js",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
+
"@daypilot/daypilot-lite-react": "^5.4.1",
|
|
64
65
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
65
66
|
"@radix-ui/react-select": "^2.2.6",
|
|
66
67
|
"@radix-ui/react-slot": "^1.2.4",
|