@spacesync/client 0.1.7 → 0.1.9
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/analog-clock-picker.js +379 -3
- package/dist/analog-clock-picker.js.map +1 -1
- package/dist/api.d.ts +9 -1
- package/dist/api.js +84 -1
- package/dist/api.js.map +1 -1
- package/dist/booking-schedule-calendars.d.ts +33 -7
- package/dist/booking-schedule-calendars.js +762 -398
- package/dist/booking-schedule-calendars.js.map +1 -1
- package/dist/booking-space-layout-picker.d.ts +20 -0
- package/dist/booking-space-layout-picker.js +68 -0
- package/dist/booking-space-layout-picker.js.map +1 -0
- package/dist/chat-stick-to-bottom.js +1 -1
- package/dist/chunk-CKCXQBHQ.js +764 -0
- package/dist/chunk-CKCXQBHQ.js.map +1 -0
- package/dist/{chunk-H3CNZCYU.js → chunk-F7MU5T46.js} +186 -62
- package/dist/chunk-F7MU5T46.js.map +1 -0
- package/dist/file-viewer-dialog.js +1 -1
- package/dist/floor-switcher.js +1 -1
- package/dist/image-picker-field.d.ts +2 -1
- package/dist/image-picker-field.js +77 -28
- package/dist/image-picker-field.js.map +1 -1
- package/dist/layout-canvas-view-DOjyUNJH.d.ts +65 -0
- package/dist/layout-canvas-view.d.ts +2 -57
- package/dist/layout-canvas-view.js +7 -741
- package/dist/layout-canvas-view.js.map +1 -1
- package/dist/platform-support-message-composer.js +1 -1
- package/dist/ui.d.ts +19 -10
- package/dist/ui.js +1 -1
- package/package.json +5 -2
- package/dist/chunk-34332W2P.js +0 -381
- package/dist/chunk-34332W2P.js.map +0 -1
- package/dist/chunk-H3CNZCYU.js.map +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { formatTimeForDisplay } from './chunk-H3CNZCYU.js';
|
|
1
|
+
import { CheckboxField, hourlyEndTimes, addMinutesToHm, formatTimeForDisplay, snapScheduleEndTime, TimeField } from './chunk-F7MU5T46.js';
|
|
3
2
|
import './chunk-RV5PMZYZ.js';
|
|
4
3
|
import './chunk-L65F4EMK.js';
|
|
5
4
|
import './chunk-PBNBLCC2.js';
|
|
@@ -16,26 +15,44 @@ import { Info, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
|
16
15
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
17
16
|
|
|
18
17
|
// src/lib/booking-schedule.ts
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
18
|
+
function localDateYmd(d) {
|
|
19
|
+
const y = d.getFullYear();
|
|
20
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
21
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
22
|
+
return `${y}-${m}-${day}`;
|
|
23
|
+
}
|
|
24
|
+
function todayLocalYmd(now = /* @__PURE__ */ new Date()) {
|
|
25
|
+
return localDateYmd(now);
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
var PRICING_TODAY = todayLocalYmd();
|
|
28
|
+
function currentMonthYm(now = /* @__PURE__ */ new Date()) {
|
|
29
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
30
|
+
}
|
|
31
|
+
function currentWeekStart(now = /* @__PURE__ */ new Date()) {
|
|
32
|
+
const d = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
30
33
|
const day = d.getDay();
|
|
31
|
-
|
|
34
|
+
const offset = day === 0 ? -6 : 1 - day;
|
|
35
|
+
d.setDate(d.getDate() + offset);
|
|
36
|
+
return localDateYmd(d);
|
|
37
|
+
}
|
|
38
|
+
function computeMinMonth(now = /* @__PURE__ */ new Date()) {
|
|
39
|
+
if (now.getDate() === 1) {
|
|
40
|
+
return currentMonthYm(now);
|
|
41
|
+
}
|
|
42
|
+
const n = new Date(now.getFullYear(), now.getMonth() + 1, 1);
|
|
43
|
+
return currentMonthYm(n);
|
|
44
|
+
}
|
|
45
|
+
function computeMinWeekStart(now = /* @__PURE__ */ new Date()) {
|
|
46
|
+
const day = now.getDay();
|
|
47
|
+
if (day === 1) return todayLocalYmd(now);
|
|
48
|
+
const d = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
32
49
|
const skip = day === 0 ? 1 : 8 - day;
|
|
33
50
|
d.setDate(d.getDate() + skip);
|
|
34
|
-
return d
|
|
51
|
+
return localDateYmd(d);
|
|
35
52
|
}
|
|
36
|
-
|
|
37
|
-
var MIN_WEEK_START = computeMinWeekStart()
|
|
38
|
-
|
|
53
|
+
computeMinMonth();
|
|
54
|
+
var MIN_WEEK_START = computeMinWeekStart();
|
|
55
|
+
(/* @__PURE__ */ new Date()).getDate() !== 1;
|
|
39
56
|
function monthLabel(ym) {
|
|
40
57
|
const [y, m] = ym.split("-").map(Number);
|
|
41
58
|
return new Date(y, m - 1, 1).toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
@@ -47,23 +64,27 @@ function monthCount(from, to) {
|
|
|
47
64
|
return (ty - fy) * 12 + (tm - fm) + 1;
|
|
48
65
|
}
|
|
49
66
|
function weekRangeLabel(weekFrom, weekCount) {
|
|
50
|
-
if (!weekFrom || isNaN(new Date(weekFrom).getTime())) return "\u2014";
|
|
51
|
-
const end =
|
|
67
|
+
if (!weekFrom || isNaN((/* @__PURE__ */ new Date(weekFrom + "T00:00:00")).getTime())) return "\u2014";
|
|
68
|
+
const end = parseDateOnly(weekFrom);
|
|
52
69
|
end.setDate(end.getDate() + weekCount * 7 - 1);
|
|
53
|
-
const s =
|
|
70
|
+
const s = parseDateOnly(weekFrom).toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
54
71
|
const e = end.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
55
72
|
return `${s} \u2013 ${e}`;
|
|
56
73
|
}
|
|
74
|
+
function weekCountBetween(weekFrom, weekToMon) {
|
|
75
|
+
if (!weekFrom || !weekToMon) return 0;
|
|
76
|
+
if (weekToMon < weekFrom) return 1;
|
|
77
|
+
return Math.floor(diffDays(weekFrom, weekToMon) / 7) + 1;
|
|
78
|
+
}
|
|
79
|
+
function addMonthsYm(ym, delta) {
|
|
80
|
+
const [y, m] = ym.split("-").map(Number);
|
|
81
|
+
const d = new Date(y, (m ?? 1) - 1 + delta, 1);
|
|
82
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
83
|
+
}
|
|
57
84
|
function parseDateOnly(date) {
|
|
58
85
|
const [y, m, d] = date.split("-").map(Number);
|
|
59
86
|
return new Date(y, m - 1, d);
|
|
60
87
|
}
|
|
61
|
-
function formatDateOnly(d) {
|
|
62
|
-
const y = d.getFullYear();
|
|
63
|
-
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
64
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
65
|
-
return `${y}-${m}-${day}`;
|
|
66
|
-
}
|
|
67
88
|
function diffDays(a, b) {
|
|
68
89
|
const da = parseDateOnly(a);
|
|
69
90
|
const db = parseDateOnly(b);
|
|
@@ -72,7 +93,7 @@ function diffDays(a, b) {
|
|
|
72
93
|
function addDays(date, days) {
|
|
73
94
|
const d = parseDateOnly(date);
|
|
74
95
|
d.setDate(d.getDate() + days);
|
|
75
|
-
return
|
|
96
|
+
return localDateYmd(d);
|
|
76
97
|
}
|
|
77
98
|
var MONTH_NAMES = [
|
|
78
99
|
"January",
|
|
@@ -89,7 +110,22 @@ var MONTH_NAMES = [
|
|
|
89
110
|
"December"
|
|
90
111
|
];
|
|
91
112
|
function dayStr(d) {
|
|
92
|
-
return d
|
|
113
|
+
return localDateYmd(d);
|
|
114
|
+
}
|
|
115
|
+
var calendarBlockClass = "flex flex-col overflow-hidden rounded-xl border border-border/60";
|
|
116
|
+
var calendarNavRowClass = "flex items-center justify-between border-b border-border/40 px-2 py-1.5";
|
|
117
|
+
var calendarWeekdayRowClass = "grid grid-cols-7 border-b border-border/40";
|
|
118
|
+
var calendarWeekdayCellClass = "flex h-9 items-center justify-center border-r border-border/40 text-[10px] font-medium text-muted-foreground last:border-r-0";
|
|
119
|
+
function cellGridLines(index, total, cols) {
|
|
120
|
+
return cn(
|
|
121
|
+
"border-border/40",
|
|
122
|
+
index % cols !== cols - 1 && "border-r",
|
|
123
|
+
index < total - cols && "border-b"
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
function weekEndMonday(weekFrom, weekCount) {
|
|
127
|
+
if (!weekFrom || weekCount < 1) return weekFrom;
|
|
128
|
+
return addDays(weekFrom, (weekCount - 1) * 7);
|
|
93
129
|
}
|
|
94
130
|
function getCalendarGrid(year, month) {
|
|
95
131
|
const firstDay = new Date(year, month, 1);
|
|
@@ -145,6 +181,8 @@ function ScheduleStepper({
|
|
|
145
181
|
}
|
|
146
182
|
function WeekCalendarPicker({
|
|
147
183
|
weekFrom,
|
|
184
|
+
weekCount,
|
|
185
|
+
rangeMode,
|
|
148
186
|
onChange,
|
|
149
187
|
flat = false
|
|
150
188
|
}) {
|
|
@@ -152,10 +190,22 @@ function WeekCalendarPicker({
|
|
|
152
190
|
const [calYear, setCalYear] = useState(initDate.getFullYear());
|
|
153
191
|
const [calMonth, setCalMonth] = useState(initDate.getMonth());
|
|
154
192
|
const [hoverMon, setHoverMon] = useState(null);
|
|
193
|
+
const [phase, setPhase] = useState("from");
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
setPhase("from");
|
|
196
|
+
}, [rangeMode]);
|
|
155
197
|
const days = getCalendarGrid(calYear, calMonth);
|
|
156
|
-
const minDate = /* @__PURE__ */ new Date((
|
|
157
|
-
const
|
|
158
|
-
const
|
|
198
|
+
const minDate = /* @__PURE__ */ new Date((currentWeekStart() || PRICING_TODAY) + "T00:00:00");
|
|
199
|
+
const endMon = weekEndMonday(weekFrom, weekCount);
|
|
200
|
+
const [dispFrom, dispTo] = (() => {
|
|
201
|
+
if (!rangeMode || !weekFrom) return [weekFrom, endMon];
|
|
202
|
+
if (phase === "to" && hoverMon) {
|
|
203
|
+
if (hoverMon >= weekFrom) return [weekFrom, hoverMon];
|
|
204
|
+
return [hoverMon, hoverMon];
|
|
205
|
+
}
|
|
206
|
+
return [weekFrom, endMon];
|
|
207
|
+
})();
|
|
208
|
+
const previewCount = rangeMode && phase === "to" && hoverMon && hoverMon >= weekFrom ? weekCountBetween(weekFrom, hoverMon) : weekCount;
|
|
159
209
|
const prevMonth = () => {
|
|
160
210
|
if (calMonth === 0) {
|
|
161
211
|
setCalYear((y) => y - 1);
|
|
@@ -170,6 +220,24 @@ function WeekCalendarPicker({
|
|
|
170
220
|
};
|
|
171
221
|
const weeks = [];
|
|
172
222
|
for (let i = 0; i < days.length; i += 7) weeks.push(days.slice(i, i + 7));
|
|
223
|
+
const handleWeekClick = (monStr) => {
|
|
224
|
+
if (!rangeMode) {
|
|
225
|
+
onChange(monStr, 1);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (phase === "from") {
|
|
229
|
+
onChange(monStr, 1);
|
|
230
|
+
setPhase("to");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (monStr >= weekFrom) {
|
|
234
|
+
onChange(weekFrom, weekCountBetween(weekFrom, monStr));
|
|
235
|
+
setPhase("from");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
onChange(monStr, 1);
|
|
239
|
+
setPhase("to");
|
|
240
|
+
};
|
|
173
241
|
return /* @__PURE__ */ jsxs(
|
|
174
242
|
"div",
|
|
175
243
|
{
|
|
@@ -178,70 +246,113 @@ function WeekCalendarPicker({
|
|
|
178
246
|
flat ? "gap-3" : "gap-1 rounded-2xl border border-border bg-card p-3"
|
|
179
247
|
),
|
|
180
248
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
249
|
+
rangeMode ? /* @__PURE__ */ jsx(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
className: cn(
|
|
253
|
+
"flex items-center gap-2 px-1 text-[11px] font-medium",
|
|
254
|
+
phase === "to" ? "text-blue-600 dark:text-blue-400" : "text-muted-foreground"
|
|
255
|
+
),
|
|
256
|
+
children: phase === "from" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
257
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-muted-foreground/50" }),
|
|
258
|
+
"Click the first week"
|
|
259
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
260
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-blue-500" }),
|
|
261
|
+
"Click the last week",
|
|
262
|
+
/* @__PURE__ */ jsx(
|
|
263
|
+
"button",
|
|
264
|
+
{
|
|
265
|
+
type: "button",
|
|
266
|
+
onClick: () => {
|
|
267
|
+
onChange(weekFrom, 1);
|
|
268
|
+
setPhase("to");
|
|
269
|
+
},
|
|
270
|
+
className: "ml-auto text-[10px] text-blue-400 underline underline-offset-2 hover:text-blue-600 dark:text-blue-400",
|
|
271
|
+
children: "Reset"
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
] })
|
|
275
|
+
}
|
|
276
|
+
) : null,
|
|
277
|
+
/* @__PURE__ */ jsxs("div", { className: calendarBlockClass, children: [
|
|
278
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
279
|
+
/* @__PURE__ */ jsx(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
type: "button",
|
|
283
|
+
onClick: prevMonth,
|
|
284
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
285
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
286
|
+
}
|
|
287
|
+
),
|
|
288
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
289
|
+
MONTH_NAMES[calMonth],
|
|
290
|
+
" ",
|
|
291
|
+
calYear
|
|
292
|
+
] }),
|
|
293
|
+
/* @__PURE__ */ jsx(
|
|
294
|
+
"button",
|
|
295
|
+
{
|
|
296
|
+
type: "button",
|
|
297
|
+
onClick: nextMonth,
|
|
298
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
299
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
300
|
+
}
|
|
301
|
+
)
|
|
195
302
|
] }),
|
|
196
|
-
/* @__PURE__ */ jsx(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
303
|
+
/* @__PURE__ */ jsx("div", { className: calendarWeekdayRowClass, children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: calendarWeekdayCellClass, children: d }, d)) }),
|
|
304
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col divide-y divide-border/40", children: weeks.map((week, wi) => {
|
|
305
|
+
const mondayDate = week[0];
|
|
306
|
+
const monStr = dayStr(mondayDate);
|
|
307
|
+
const isSelectable = mondayDate >= minDate;
|
|
308
|
+
const isStartWeek = Boolean(weekFrom) && monStr === dispFrom;
|
|
309
|
+
const isEndWeek = Boolean(weekFrom) && monStr === dispTo;
|
|
310
|
+
const inRange = rangeMode && Boolean(weekFrom) && monStr >= dispFrom && monStr <= dispTo;
|
|
311
|
+
const isSelected = rangeMode ? inRange : monStr === weekFrom;
|
|
312
|
+
return /* @__PURE__ */ jsx(
|
|
313
|
+
"div",
|
|
314
|
+
{
|
|
315
|
+
className: cn(
|
|
316
|
+
"relative flex cursor-pointer transition-colors",
|
|
317
|
+
isSelected && (isStartWeek || isEndWeek) && "bg-blue-100/80",
|
|
318
|
+
isSelected && !isStartWeek && !isEndWeek && "bg-blue-50",
|
|
319
|
+
isSelectable ? "hover:bg-accent/60" : "cursor-not-allowed opacity-60"
|
|
320
|
+
),
|
|
321
|
+
onMouseEnter: () => isSelectable && setHoverMon(monStr),
|
|
322
|
+
onMouseLeave: () => setHoverMon(null),
|
|
323
|
+
onClick: () => isSelectable && handleWeekClick(monStr),
|
|
324
|
+
children: week.map((d, di) => {
|
|
325
|
+
const isCurrentMonth = d.getMonth() === calMonth;
|
|
326
|
+
const isMon = di === 0;
|
|
327
|
+
const isSun = di === 6;
|
|
328
|
+
const filled = rangeMode ? isStartWeek && isMon || isEndWeek && isSun : isSelected && isMon;
|
|
329
|
+
return /* @__PURE__ */ jsx(
|
|
330
|
+
"div",
|
|
331
|
+
{
|
|
332
|
+
className: "relative z-10 flex h-9 flex-1 items-center justify-center border-r border-border/40 last:border-r-0",
|
|
333
|
+
children: /* @__PURE__ */ jsx(
|
|
334
|
+
"span",
|
|
335
|
+
{
|
|
336
|
+
className: cn(
|
|
337
|
+
"flex h-7 w-7 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
338
|
+
isCurrentMonth ? "text-muted-foreground" : "text-muted-foreground/50",
|
|
339
|
+
!isSelectable && "text-muted-foreground/40",
|
|
340
|
+
isSelected && "font-medium text-blue-700 dark:text-blue-300",
|
|
341
|
+
filled && "bg-blue-500 font-semibold text-white shadow-sm"
|
|
342
|
+
),
|
|
343
|
+
children: d.getDate()
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
},
|
|
347
|
+
di
|
|
348
|
+
);
|
|
349
|
+
})
|
|
350
|
+
},
|
|
351
|
+
wi
|
|
352
|
+
);
|
|
353
|
+
}) })
|
|
205
354
|
] }),
|
|
206
|
-
/* @__PURE__ */ jsx("
|
|
207
|
-
weeks.map((week, wi) => {
|
|
208
|
-
const mondayDate = week[0];
|
|
209
|
-
const monStr = dayStr(mondayDate);
|
|
210
|
-
const isSelectable = mondayDate >= minDate;
|
|
211
|
-
const isSelected = monStr === selectedMonStr;
|
|
212
|
-
const isHovered = monStr === hoverMonStr && isSelectable && !isSelected;
|
|
213
|
-
return /* @__PURE__ */ jsx(
|
|
214
|
-
"div",
|
|
215
|
-
{
|
|
216
|
-
className: `flex cursor-pointer overflow-hidden rounded-xl transition-colors
|
|
217
|
-
${isSelected ? "bg-blue-100" : isHovered ? "bg-blue-50" : isSelectable ? "hover:bg-accent" : "cursor-not-allowed"}`,
|
|
218
|
-
onMouseEnter: () => isSelectable && setHoverMon(monStr),
|
|
219
|
-
onMouseLeave: () => setHoverMon(null),
|
|
220
|
-
onClick: () => isSelectable && onChange(monStr),
|
|
221
|
-
children: week.map((d, di) => {
|
|
222
|
-
const isCurrentMonth = d.getMonth() === calMonth;
|
|
223
|
-
const isMon = di === 0;
|
|
224
|
-
const isSelectedMon = isSelected && isMon;
|
|
225
|
-
return /* @__PURE__ */ jsx("div", { className: "flex h-8 flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
226
|
-
"span",
|
|
227
|
-
{
|
|
228
|
-
className: `
|
|
229
|
-
flex h-7 w-7 items-center justify-center rounded-full text-[11px] transition-colors
|
|
230
|
-
${isSelectedMon ? "bg-blue-500 font-semibold text-white" : ""}
|
|
231
|
-
${!isSelectedMon && isSelected ? "text-blue-700 dark:text-blue-400" : ""}
|
|
232
|
-
${!isSelected && isSelectable && isCurrentMonth ? "text-muted-foreground" : ""}
|
|
233
|
-
${!isSelectable && isCurrentMonth ? "text-muted-foreground/60" : ""}
|
|
234
|
-
${!isCurrentMonth ? "text-muted-foreground/60" : ""}
|
|
235
|
-
`,
|
|
236
|
-
children: d.getDate()
|
|
237
|
-
}
|
|
238
|
-
) }, di);
|
|
239
|
-
})
|
|
240
|
-
},
|
|
241
|
-
wi
|
|
242
|
-
);
|
|
243
|
-
}),
|
|
244
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-center text-[10px] text-muted-foreground", children: "Click any row to select that week" })
|
|
355
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-center text-[10px] text-muted-foreground", children: rangeMode ? phase === "from" ? "Click a week to start the range" : previewCount > 1 ? `${previewCount} weeks \xB7 ${weekRangeLabel(dispFrom, previewCount)}` : "Click a week to finish the range" : weekFrom ? `Selected week of ${formatDateLabel(weekFrom)}` : "Click any row to select that week" })
|
|
245
356
|
]
|
|
246
357
|
}
|
|
247
358
|
);
|
|
@@ -250,16 +361,21 @@ function MonthRangeCalendar({
|
|
|
250
361
|
monthFrom,
|
|
251
362
|
monthTo,
|
|
252
363
|
onChange,
|
|
364
|
+
rangeMode,
|
|
253
365
|
flat = false
|
|
254
366
|
}) {
|
|
255
367
|
const initYear = monthFrom ? parseInt(monthFrom.split("-")[0], 10) : (/* @__PURE__ */ new Date()).getFullYear();
|
|
256
368
|
const [calYear, setCalYear] = useState(initYear);
|
|
257
369
|
const [phase, setPhase] = useState("from");
|
|
258
370
|
const [hoverYm, setHoverYm] = useState(null);
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
setPhase("from");
|
|
373
|
+
}, [rangeMode]);
|
|
259
374
|
const ABBR = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
260
375
|
const toYm = (y, m) => `${y}-${String(m + 1).padStart(2, "0")}`;
|
|
261
|
-
const minYm =
|
|
376
|
+
const minYm = currentMonthYm() || `${(/* @__PURE__ */ new Date()).getFullYear()}-01`;
|
|
262
377
|
const [dispFrom, dispTo] = (() => {
|
|
378
|
+
if (!rangeMode) return [monthFrom, monthFrom || monthTo];
|
|
263
379
|
if (phase === "to" && hoverYm) {
|
|
264
380
|
if (hoverYm >= monthFrom) return [monthFrom, hoverYm];
|
|
265
381
|
return [hoverYm, hoverYm];
|
|
@@ -269,6 +385,10 @@ function MonthRangeCalendar({
|
|
|
269
385
|
const isSingle = dispFrom === dispTo;
|
|
270
386
|
const handleClick = (ymStr) => {
|
|
271
387
|
if (ymStr < minYm) return;
|
|
388
|
+
if (!rangeMode) {
|
|
389
|
+
onChange(ymStr, ymStr);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
272
392
|
if (phase === "from") {
|
|
273
393
|
onChange(ymStr, ymStr);
|
|
274
394
|
setPhase("to");
|
|
@@ -277,6 +397,7 @@ function MonthRangeCalendar({
|
|
|
277
397
|
setPhase("from");
|
|
278
398
|
} else {
|
|
279
399
|
onChange(ymStr, ymStr);
|
|
400
|
+
setPhase("to");
|
|
280
401
|
}
|
|
281
402
|
};
|
|
282
403
|
const actualCount = monthCount(monthFrom, monthTo);
|
|
@@ -289,28 +410,7 @@ function MonthRangeCalendar({
|
|
|
289
410
|
flat ? "flex flex-col gap-5" : "overflow-hidden rounded-2xl border border-border bg-card"
|
|
290
411
|
),
|
|
291
412
|
children: [
|
|
292
|
-
/* @__PURE__ */
|
|
293
|
-
/* @__PURE__ */ jsx(
|
|
294
|
-
"button",
|
|
295
|
-
{
|
|
296
|
-
type: "button",
|
|
297
|
-
onClick: () => setCalYear((y) => y - 1),
|
|
298
|
-
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
299
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
300
|
-
}
|
|
301
|
-
),
|
|
302
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-muted-foreground", children: calYear }),
|
|
303
|
-
/* @__PURE__ */ jsx(
|
|
304
|
-
"button",
|
|
305
|
-
{
|
|
306
|
-
type: "button",
|
|
307
|
-
onClick: () => setCalYear((y) => y + 1),
|
|
308
|
-
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
309
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
310
|
-
}
|
|
311
|
-
)
|
|
312
|
-
] }),
|
|
313
|
-
/* @__PURE__ */ jsx(
|
|
413
|
+
rangeMode ? /* @__PURE__ */ jsx(
|
|
314
414
|
"div",
|
|
315
415
|
{
|
|
316
416
|
className: cn(
|
|
@@ -322,10 +422,10 @@ function MonthRangeCalendar({
|
|
|
322
422
|
),
|
|
323
423
|
children: phase === "from" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
324
424
|
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-muted-foreground/50" }),
|
|
325
|
-
"Click
|
|
425
|
+
"Click the first month"
|
|
326
426
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
327
427
|
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-blue-500" }),
|
|
328
|
-
"
|
|
428
|
+
"Click the last month",
|
|
329
429
|
/* @__PURE__ */ jsx(
|
|
330
430
|
"button",
|
|
331
431
|
{
|
|
@@ -340,59 +440,76 @@ function MonthRangeCalendar({
|
|
|
340
440
|
)
|
|
341
441
|
] })
|
|
342
442
|
}
|
|
343
|
-
),
|
|
344
|
-
/* @__PURE__ */
|
|
345
|
-
"div",
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
443
|
+
) : null,
|
|
444
|
+
/* @__PURE__ */ jsxs("div", { className: cn(calendarBlockClass, !flat && "mx-3 mb-3"), children: [
|
|
445
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
446
|
+
/* @__PURE__ */ jsx(
|
|
447
|
+
"button",
|
|
448
|
+
{
|
|
449
|
+
type: "button",
|
|
450
|
+
onClick: () => setCalYear((y) => y - 1),
|
|
451
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
452
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
453
|
+
}
|
|
454
|
+
),
|
|
455
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-muted-foreground", children: calYear }),
|
|
456
|
+
/* @__PURE__ */ jsx(
|
|
457
|
+
"button",
|
|
458
|
+
{
|
|
459
|
+
type: "button",
|
|
460
|
+
onClick: () => setCalYear((y) => y + 1),
|
|
461
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
462
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
463
|
+
}
|
|
464
|
+
)
|
|
465
|
+
] }),
|
|
466
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4", children: ABBR.map((label, i) => {
|
|
467
|
+
const ymStr = toYm(calYear, i);
|
|
468
|
+
const disabled = ymStr < minYm;
|
|
469
|
+
const inRange = !disabled && ymStr >= dispFrom && ymStr <= dispTo;
|
|
470
|
+
const isFrom = ymStr === dispFrom;
|
|
471
|
+
const isTo = ymStr === dispTo;
|
|
472
|
+
const showStrip = rangeMode && inRange && !isSingle;
|
|
473
|
+
const sLeft = isFrom ? "left-1/2" : "left-0";
|
|
474
|
+
const sRight = isTo ? "right-1/2" : "right-0";
|
|
475
|
+
const filled = (isFrom || isTo || !rangeMode && ymStr === monthFrom) && !disabled;
|
|
476
|
+
return /* @__PURE__ */ jsxs(
|
|
477
|
+
"div",
|
|
478
|
+
{
|
|
479
|
+
className: cn(
|
|
480
|
+
"relative",
|
|
481
|
+
cellGridLines(i, 12, 4),
|
|
482
|
+
flat ? "h-14" : "h-12",
|
|
483
|
+
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
|
484
|
+
),
|
|
485
|
+
onMouseEnter: () => !disabled && setHoverYm(ymStr),
|
|
486
|
+
onMouseLeave: () => setHoverYm(null),
|
|
487
|
+
onClick: () => handleClick(ymStr),
|
|
488
|
+
children: [
|
|
489
|
+
showStrip ? /* @__PURE__ */ jsx("div", { className: `absolute bottom-1.5 top-1.5 bg-blue-100 ${sLeft} ${sRight}` }) : null,
|
|
490
|
+
/* @__PURE__ */ jsx(
|
|
491
|
+
"div",
|
|
492
|
+
{
|
|
493
|
+
className: cn(
|
|
494
|
+
"relative z-10 flex w-full items-center justify-center",
|
|
495
|
+
flat ? "h-14" : "h-12"
|
|
496
|
+
),
|
|
497
|
+
children: /* @__PURE__ */ jsx(
|
|
498
|
+
"span",
|
|
499
|
+
{
|
|
500
|
+
className: `flex h-9 w-9 items-center justify-center rounded-full text-xs font-medium transition-all duration-100
|
|
501
|
+
${filled ? "bg-blue-500 text-white shadow-md shadow-blue-200" : inRange ? "text-blue-700 dark:text-blue-400" : !disabled ? "text-muted-foreground hover:bg-accent" : "text-muted-foreground/60"}`,
|
|
502
|
+
children: label
|
|
503
|
+
}
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
)
|
|
507
|
+
]
|
|
508
|
+
},
|
|
509
|
+
i
|
|
510
|
+
);
|
|
511
|
+
}) })
|
|
512
|
+
] }),
|
|
396
513
|
/* @__PURE__ */ jsx(
|
|
397
514
|
"div",
|
|
398
515
|
{
|
|
@@ -400,10 +517,13 @@ function MonthRangeCalendar({
|
|
|
400
517
|
"flex items-center justify-between transition-colors",
|
|
401
518
|
flat ? "border-t border-border/70 pt-4 text-xs" : cn(
|
|
402
519
|
"border-t px-4 py-2.5",
|
|
403
|
-
phase === "from" && actualCount > 0 ? "border-border bg-muted" : "border-transparent"
|
|
520
|
+
rangeMode && phase === "from" && actualCount > 0 ? "border-border bg-muted" : "border-transparent"
|
|
404
521
|
)
|
|
405
522
|
),
|
|
406
|
-
children:
|
|
523
|
+
children: !rangeMode && monthFrom ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
524
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: monthLabel(monthFrom) }),
|
|
525
|
+
/* @__PURE__ */ jsx("span", { className: "rounded-full bg-blue-50 px-2 py-0.5 text-[10px] font-semibold text-blue-600 dark:bg-blue-950/40 dark:text-blue-400", children: "1 month" })
|
|
526
|
+
] }) : rangeMode && phase === "from" && actualCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
407
527
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
408
528
|
monthLabel(monthFrom),
|
|
409
529
|
" \u2192 ",
|
|
@@ -414,14 +534,14 @@ function MonthRangeCalendar({
|
|
|
414
534
|
" month",
|
|
415
535
|
actualCount !== 1 ? "s" : ""
|
|
416
536
|
] })
|
|
417
|
-
] }) : phase === "to" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
537
|
+
] }) : rangeMode && phase === "to" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
418
538
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: hoverYm && hoverYm >= monthFrom ? `${monthLabel(monthFrom)} \u2192 ${monthLabel(hoverYm)}` : `Start: ${monthLabel(monthFrom)}` }),
|
|
419
539
|
hoverYm && hoverYm >= monthFrom && previewCount > 0 ? /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-blue-400", children: [
|
|
420
540
|
previewCount,
|
|
421
541
|
" month",
|
|
422
542
|
previewCount !== 1 ? "s" : ""
|
|
423
543
|
] }) : null
|
|
424
|
-
] }) : /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/60", children: "
|
|
544
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/60", children: rangeMode ? "Select a month range" : "Click a month to book" })
|
|
425
545
|
}
|
|
426
546
|
)
|
|
427
547
|
]
|
|
@@ -433,25 +553,58 @@ function MonthPeriodPicker({
|
|
|
433
553
|
onChange,
|
|
434
554
|
flat = true
|
|
435
555
|
}) {
|
|
556
|
+
const [rangeMode, setRangeMode] = useState(
|
|
557
|
+
() => Boolean(booking.monthFrom && booking.monthTo && booking.monthFrom !== booking.monthTo)
|
|
558
|
+
);
|
|
559
|
+
useEffect(() => {
|
|
560
|
+
if (booking.monthFrom && booking.monthTo && booking.monthFrom !== booking.monthTo) {
|
|
561
|
+
setRangeMode(true);
|
|
562
|
+
}
|
|
563
|
+
}, [booking.monthFrom, booking.monthTo]);
|
|
564
|
+
const monthCountSelected = monthCount(booking.monthFrom, booking.monthTo);
|
|
436
565
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", flat ? "gap-5" : "gap-3"), children: [
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
566
|
+
/* @__PURE__ */ jsx(ScheduleInfoBanner, { children: "Current month is available at full price, or choose a future month." }),
|
|
567
|
+
/* @__PURE__ */ jsx(
|
|
568
|
+
CheckboxField,
|
|
569
|
+
{
|
|
570
|
+
size: "sm",
|
|
571
|
+
label: "Select multiple months",
|
|
572
|
+
className: "w-fit items-center",
|
|
573
|
+
checked: rangeMode,
|
|
574
|
+
onCheckedChange: (checked) => {
|
|
575
|
+
setRangeMode(checked);
|
|
576
|
+
const from = booking.monthFrom || currentMonthYm();
|
|
577
|
+
if (!checked) {
|
|
578
|
+
onChange({ monthFrom: from, monthTo: from });
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const to = booking.monthTo && booking.monthTo > from ? booking.monthTo : addMonthsYm(from, 1);
|
|
582
|
+
onChange({ monthFrom: from, monthTo: to });
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
),
|
|
586
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: rangeMode ? "Select month range" : "Select month" }),
|
|
446
587
|
/* @__PURE__ */ jsx(
|
|
447
588
|
MonthRangeCalendar,
|
|
448
589
|
{
|
|
449
590
|
flat,
|
|
591
|
+
rangeMode,
|
|
450
592
|
monthFrom: booking.monthFrom,
|
|
451
593
|
monthTo: booking.monthTo,
|
|
452
594
|
onChange: (from, to) => onChange({ monthFrom: from, monthTo: to })
|
|
453
595
|
}
|
|
454
|
-
)
|
|
596
|
+
),
|
|
597
|
+
monthCountSelected > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-xl border border-border bg-muted px-3 py-2.5", children: [
|
|
598
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
599
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Period" }),
|
|
600
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: rangeMode && monthCountSelected > 1 ? `${monthLabel(booking.monthFrom)} \u2192 ${monthLabel(booking.monthTo)}` : monthLabel(booking.monthFrom) })
|
|
601
|
+
] }),
|
|
602
|
+
/* @__PURE__ */ jsxs("span", { className: "rounded-lg bg-blue-50 px-2 py-1 text-xs font-semibold text-blue-600 dark:bg-blue-950/40 dark:text-blue-400", children: [
|
|
603
|
+
monthCountSelected,
|
|
604
|
+
" month",
|
|
605
|
+
monthCountSelected !== 1 ? "s" : ""
|
|
606
|
+
] })
|
|
607
|
+
] }) : null
|
|
455
608
|
] });
|
|
456
609
|
}
|
|
457
610
|
function WeekPeriodPicker({
|
|
@@ -459,49 +612,47 @@ function WeekPeriodPicker({
|
|
|
459
612
|
onChange,
|
|
460
613
|
flat = true
|
|
461
614
|
}) {
|
|
615
|
+
const [rangeMode, setRangeMode] = useState(() => booking.weekCount > 1);
|
|
616
|
+
useEffect(() => {
|
|
617
|
+
if (booking.weekCount > 1) setRangeMode(true);
|
|
618
|
+
}, [booking.weekCount]);
|
|
462
619
|
const rangeStr = weekRangeLabel(booking.weekFrom, booking.weekCount);
|
|
463
|
-
const isCurrentWeekPartial = new Date(PRICING_TODAY).getDay() !== 1;
|
|
464
620
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", flat ? "gap-5" : "gap-3"), children: [
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
621
|
+
/* @__PURE__ */ jsx(ScheduleInfoBanner, { children: "Current week is available at full price, or choose a future week." }),
|
|
622
|
+
/* @__PURE__ */ jsx(
|
|
623
|
+
CheckboxField,
|
|
624
|
+
{
|
|
625
|
+
size: "sm",
|
|
626
|
+
label: "Select multiple weeks",
|
|
627
|
+
className: "w-fit items-center",
|
|
628
|
+
checked: rangeMode,
|
|
629
|
+
onCheckedChange: (checked) => {
|
|
630
|
+
setRangeMode(checked);
|
|
631
|
+
const from = booking.weekFrom || currentWeekStart();
|
|
632
|
+
if (!checked) {
|
|
633
|
+
onChange({ weekFrom: from, weekCount: 1 });
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
onChange({
|
|
637
|
+
weekFrom: from,
|
|
638
|
+
weekCount: Math.max(2, booking.weekCount)
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
),
|
|
476
643
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
477
|
-
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "
|
|
644
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: rangeMode ? "Select week range" : "Select week" }),
|
|
478
645
|
/* @__PURE__ */ jsx(
|
|
479
646
|
WeekCalendarPicker,
|
|
480
647
|
{
|
|
481
648
|
flat,
|
|
649
|
+
rangeMode,
|
|
482
650
|
weekFrom: booking.weekFrom,
|
|
483
|
-
|
|
651
|
+
weekCount: booking.weekCount,
|
|
652
|
+
onChange: (weekFrom, weekCount) => onChange({ weekFrom, weekCount })
|
|
484
653
|
}
|
|
485
654
|
)
|
|
486
655
|
] }),
|
|
487
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
488
|
-
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Number of weeks" }),
|
|
489
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
490
|
-
/* @__PURE__ */ jsx(
|
|
491
|
-
ScheduleStepper,
|
|
492
|
-
{
|
|
493
|
-
value: booking.weekCount,
|
|
494
|
-
max: 52,
|
|
495
|
-
onChange: (v) => onChange({ weekCount: v })
|
|
496
|
-
}
|
|
497
|
-
),
|
|
498
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
499
|
-
booking.weekCount,
|
|
500
|
-
" week",
|
|
501
|
-
booking.weekCount !== 1 ? "s" : ""
|
|
502
|
-
] })
|
|
503
|
-
] })
|
|
504
|
-
] }),
|
|
505
656
|
booking.weekFrom && booking.weekCount > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-xl border border-border bg-muted px-3 py-2.5", children: [
|
|
506
657
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
507
658
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Period" }),
|
|
@@ -529,7 +680,7 @@ function isBillableDay(day, start, end) {
|
|
|
529
680
|
function DayCalendarPicker({
|
|
530
681
|
selectedDate,
|
|
531
682
|
onChange,
|
|
532
|
-
minDate =
|
|
683
|
+
minDate = todayLocalYmd(),
|
|
533
684
|
flat = false
|
|
534
685
|
}) {
|
|
535
686
|
const initDate = /* @__PURE__ */ new Date((selectedDate || minDate) + "T00:00:00");
|
|
@@ -556,65 +707,68 @@ function DayCalendarPicker({
|
|
|
556
707
|
flat ? "gap-3" : "gap-1 rounded-2xl border border-border bg-card p-3"
|
|
557
708
|
),
|
|
558
709
|
children: [
|
|
559
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
560
|
-
/* @__PURE__ */
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
710
|
+
/* @__PURE__ */ jsxs("div", { className: calendarBlockClass, children: [
|
|
711
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
712
|
+
/* @__PURE__ */ jsx(
|
|
713
|
+
"button",
|
|
714
|
+
{
|
|
715
|
+
type: "button",
|
|
716
|
+
onClick: prevMonth,
|
|
717
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
718
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
719
|
+
}
|
|
720
|
+
),
|
|
721
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
722
|
+
MONTH_NAMES[calMonth],
|
|
723
|
+
" ",
|
|
724
|
+
calYear
|
|
725
|
+
] }),
|
|
726
|
+
/* @__PURE__ */ jsx(
|
|
727
|
+
"button",
|
|
728
|
+
{
|
|
729
|
+
type: "button",
|
|
730
|
+
onClick: nextMonth,
|
|
731
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
732
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
733
|
+
}
|
|
734
|
+
)
|
|
573
735
|
] }),
|
|
574
|
-
/* @__PURE__ */ jsx(
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
736
|
+
/* @__PURE__ */ jsx("div", { className: calendarWeekdayRowClass, children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: calendarWeekdayCellClass, children: d }, d)) }),
|
|
737
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((d, i) => {
|
|
738
|
+
const ds = dayStr(d);
|
|
739
|
+
const isCurrentMonth = d.getMonth() === calMonth;
|
|
740
|
+
const isSelectable = ds >= minDate;
|
|
741
|
+
const isSelected = ds === selectedDate;
|
|
742
|
+
return /* @__PURE__ */ jsx(
|
|
743
|
+
"button",
|
|
744
|
+
{
|
|
745
|
+
type: "button",
|
|
746
|
+
disabled: !isSelectable,
|
|
747
|
+
onClick: () => isSelectable && onChange(ds),
|
|
748
|
+
className: cn(
|
|
749
|
+
"flex h-9 items-center justify-center",
|
|
750
|
+
cellGridLines(i, days.length, 7),
|
|
751
|
+
!isSelectable && "cursor-not-allowed"
|
|
752
|
+
),
|
|
753
|
+
children: /* @__PURE__ */ jsx(
|
|
754
|
+
"span",
|
|
755
|
+
{
|
|
756
|
+
className: cn(
|
|
757
|
+
"flex h-8 w-8 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
758
|
+
isSelectable && isCurrentMonth && "text-foreground hover:bg-accent",
|
|
759
|
+
isSelectable && !isCurrentMonth && "text-muted-foreground hover:bg-accent",
|
|
760
|
+
!isSelectable && isCurrentMonth && "text-muted-foreground/40",
|
|
761
|
+
!isSelectable && !isCurrentMonth && "text-muted-foreground/30",
|
|
762
|
+
isSelected && "bg-blue-500 font-semibold text-white shadow-sm"
|
|
763
|
+
),
|
|
764
|
+
children: d.getDate()
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
},
|
|
768
|
+
ds
|
|
769
|
+
);
|
|
770
|
+
}) })
|
|
583
771
|
] }),
|
|
584
|
-
/* @__PURE__ */ jsx("div", { className: "mb-0.5 grid grid-cols-7", children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: "py-0.5 text-center text-[10px] font-medium text-muted-foreground", children: d }, d)) }),
|
|
585
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((d) => {
|
|
586
|
-
const ds = dayStr(d);
|
|
587
|
-
const isCurrentMonth = d.getMonth() === calMonth;
|
|
588
|
-
const isSelectable = ds >= minDate;
|
|
589
|
-
const isSelected = ds === selectedDate;
|
|
590
|
-
return /* @__PURE__ */ jsx(
|
|
591
|
-
"button",
|
|
592
|
-
{
|
|
593
|
-
type: "button",
|
|
594
|
-
disabled: !isSelectable,
|
|
595
|
-
onClick: () => isSelectable && onChange(ds),
|
|
596
|
-
className: cn(
|
|
597
|
-
"flex h-9 items-center justify-center",
|
|
598
|
-
!isSelectable && "cursor-not-allowed"
|
|
599
|
-
),
|
|
600
|
-
children: /* @__PURE__ */ jsx(
|
|
601
|
-
"span",
|
|
602
|
-
{
|
|
603
|
-
className: cn(
|
|
604
|
-
"flex h-8 w-8 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
605
|
-
isSelected && "bg-blue-500 font-semibold text-white shadow-sm",
|
|
606
|
-
!isSelected && isSelectable && isCurrentMonth && "text-foreground hover:bg-accent",
|
|
607
|
-
!isSelected && isSelectable && !isCurrentMonth && "text-muted-foreground hover:bg-accent",
|
|
608
|
-
!isSelectable && isCurrentMonth && "text-muted-foreground/40",
|
|
609
|
-
!isSelectable && !isCurrentMonth && "text-muted-foreground/30"
|
|
610
|
-
),
|
|
611
|
-
children: d.getDate()
|
|
612
|
-
}
|
|
613
|
-
)
|
|
614
|
-
},
|
|
615
|
-
ds
|
|
616
|
-
);
|
|
617
|
-
}) }),
|
|
618
772
|
selectedDate ? /* @__PURE__ */ jsxs("p", { className: "mt-1 text-center text-[10px] text-muted-foreground", children: [
|
|
619
773
|
"Selected ",
|
|
620
774
|
formatDateLabel(selectedDate)
|
|
@@ -627,7 +781,7 @@ function DayRangeCalendar({
|
|
|
627
781
|
startDate,
|
|
628
782
|
endDate,
|
|
629
783
|
onChange,
|
|
630
|
-
minDate =
|
|
784
|
+
minDate = currentWeekStart(),
|
|
631
785
|
flat = false
|
|
632
786
|
}) {
|
|
633
787
|
const initDate = /* @__PURE__ */ new Date((startDate || minDate) + "T00:00:00");
|
|
@@ -680,31 +834,6 @@ function DayRangeCalendar({
|
|
|
680
834
|
flat ? "gap-3" : "gap-1 rounded-2xl border border-border bg-card p-3"
|
|
681
835
|
),
|
|
682
836
|
children: [
|
|
683
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between px-1", children: [
|
|
684
|
-
/* @__PURE__ */ jsx(
|
|
685
|
-
"button",
|
|
686
|
-
{
|
|
687
|
-
type: "button",
|
|
688
|
-
onClick: prevMonth,
|
|
689
|
-
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
690
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
691
|
-
}
|
|
692
|
-
),
|
|
693
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
694
|
-
MONTH_NAMES[calMonth],
|
|
695
|
-
" ",
|
|
696
|
-
calYear
|
|
697
|
-
] }),
|
|
698
|
-
/* @__PURE__ */ jsx(
|
|
699
|
-
"button",
|
|
700
|
-
{
|
|
701
|
-
type: "button",
|
|
702
|
-
onClick: nextMonth,
|
|
703
|
-
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
704
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
705
|
-
}
|
|
706
|
-
)
|
|
707
|
-
] }),
|
|
708
837
|
/* @__PURE__ */ jsx(
|
|
709
838
|
"div",
|
|
710
839
|
{
|
|
@@ -733,57 +862,85 @@ function DayRangeCalendar({
|
|
|
733
862
|
] })
|
|
734
863
|
}
|
|
735
864
|
),
|
|
736
|
-
/* @__PURE__ */
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
{
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
865
|
+
/* @__PURE__ */ jsxs("div", { className: calendarBlockClass, children: [
|
|
866
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
867
|
+
/* @__PURE__ */ jsx(
|
|
868
|
+
"button",
|
|
869
|
+
{
|
|
870
|
+
type: "button",
|
|
871
|
+
onClick: prevMonth,
|
|
872
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
873
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
874
|
+
}
|
|
875
|
+
),
|
|
876
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
877
|
+
MONTH_NAMES[calMonth],
|
|
878
|
+
" ",
|
|
879
|
+
calYear
|
|
880
|
+
] }),
|
|
881
|
+
/* @__PURE__ */ jsx(
|
|
882
|
+
"button",
|
|
883
|
+
{
|
|
884
|
+
type: "button",
|
|
885
|
+
onClick: nextMonth,
|
|
886
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
887
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
888
|
+
}
|
|
889
|
+
)
|
|
890
|
+
] }),
|
|
891
|
+
/* @__PURE__ */ jsx("div", { className: calendarWeekdayRowClass, children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: calendarWeekdayCellClass, children: d }, d)) }),
|
|
892
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((d, i) => {
|
|
893
|
+
const ds = dayStr(d);
|
|
894
|
+
const isCurrentMonth = d.getMonth() === calMonth;
|
|
895
|
+
const isSelectable = ds >= minDate;
|
|
896
|
+
const inRange = isBillableDay(ds, dispStart, dispEnd);
|
|
897
|
+
const isStart = ds === dispStart;
|
|
898
|
+
const isEnd = ds === lastBillableDay && dispEnd && diffDays(dispStart, dispEnd) > 1;
|
|
899
|
+
const isSingle = dispStart && dispEnd && diffDays(dispStart, dispEnd) === 1 && ds === dispStart;
|
|
900
|
+
return /* @__PURE__ */ jsxs(
|
|
901
|
+
"button",
|
|
902
|
+
{
|
|
903
|
+
type: "button",
|
|
904
|
+
disabled: !isSelectable,
|
|
905
|
+
onMouseEnter: () => isSelectable && setHoverDay(ds),
|
|
906
|
+
onMouseLeave: () => setHoverDay(null),
|
|
907
|
+
onClick: () => isSelectable && handleClick(ds),
|
|
908
|
+
className: cn(
|
|
909
|
+
"relative flex h-9 items-center justify-center",
|
|
910
|
+
cellGridLines(i, days.length, 7),
|
|
911
|
+
!isSelectable && "cursor-not-allowed"
|
|
912
|
+
),
|
|
913
|
+
children: [
|
|
914
|
+
inRange && !isSingle ? /* @__PURE__ */ jsx(
|
|
915
|
+
"span",
|
|
916
|
+
{
|
|
917
|
+
className: cn(
|
|
918
|
+
"absolute inset-y-1.5 bg-blue-100",
|
|
919
|
+
isStart ? "left-1/2 right-0 rounded-l-full" : isEnd ? "left-0 right-1/2 rounded-r-full" : "inset-x-0"
|
|
920
|
+
)
|
|
921
|
+
}
|
|
922
|
+
) : null,
|
|
923
|
+
/* @__PURE__ */ jsx(
|
|
924
|
+
"span",
|
|
925
|
+
{
|
|
926
|
+
className: cn(
|
|
927
|
+
"relative z-10 flex h-8 w-8 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
928
|
+
!inRange && isSelectable && isCurrentMonth && "text-foreground hover:bg-accent",
|
|
929
|
+
!inRange && isSelectable && !isCurrentMonth && "text-muted-foreground hover:bg-accent",
|
|
930
|
+
!isSelectable && isCurrentMonth && "text-muted-foreground/40",
|
|
931
|
+
!isSelectable && !isCurrentMonth && "text-muted-foreground/30",
|
|
932
|
+
inRange && "font-medium text-blue-700 dark:text-blue-300",
|
|
933
|
+
(isStart || isEnd || isSingle) && "bg-blue-500 font-semibold text-white shadow-sm"
|
|
934
|
+
),
|
|
935
|
+
children: d.getDate()
|
|
936
|
+
}
|
|
937
|
+
)
|
|
938
|
+
]
|
|
939
|
+
},
|
|
940
|
+
ds
|
|
941
|
+
);
|
|
942
|
+
}) })
|
|
943
|
+
] }),
|
|
787
944
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-t border-border/70 pt-3 text-xs", children: dayCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
788
945
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
789
946
|
formatDateLabel(startDate),
|
|
@@ -807,30 +964,24 @@ function DayPeriodPicker({
|
|
|
807
964
|
const oneDay = booking.oneDayBooking ?? true;
|
|
808
965
|
const dayCount = oneDay ? 1 : diffDays(booking.startDate, booking.endDate);
|
|
809
966
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", flat ? "gap-5" : "gap-3"), children: [
|
|
810
|
-
/* @__PURE__ */ jsx(
|
|
811
|
-
|
|
812
|
-
{ key: false, label: "Multiple days" }
|
|
813
|
-
].map(({ key, label }) => /* @__PURE__ */ jsx(
|
|
814
|
-
"button",
|
|
967
|
+
/* @__PURE__ */ jsx(
|
|
968
|
+
CheckboxField,
|
|
815
969
|
{
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
970
|
+
size: "sm",
|
|
971
|
+
label: "Select a date range",
|
|
972
|
+
className: "w-fit items-center",
|
|
973
|
+
checked: !oneDay,
|
|
974
|
+
onCheckedChange: (checked) => {
|
|
975
|
+
const start = booking.startDate || todayLocalYmd();
|
|
976
|
+
if (!checked) {
|
|
820
977
|
onChange({ oneDayBooking: true, startDate: start, endDate: addDays(start, 1) });
|
|
821
|
-
|
|
822
|
-
const end = booking.endDate && booking.endDate > start && diffDays(start, booking.endDate) > 1 ? booking.endDate : addDays(start, 7);
|
|
823
|
-
onChange({ oneDayBooking: false, startDate: start, endDate: end });
|
|
978
|
+
return;
|
|
824
979
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
children: label
|
|
831
|
-
},
|
|
832
|
-
String(key)
|
|
833
|
-
)) }),
|
|
980
|
+
const end = booking.endDate && booking.endDate > start && diffDays(start, booking.endDate) > 1 ? booking.endDate : addDays(start, 7);
|
|
981
|
+
onChange({ oneDayBooking: false, startDate: start, endDate: end });
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
),
|
|
834
985
|
oneDay ? /* @__PURE__ */ jsx(
|
|
835
986
|
DayCalendarPicker,
|
|
836
987
|
{
|
|
@@ -860,17 +1011,149 @@ function DayPeriodPicker({
|
|
|
860
1011
|
] }) : null
|
|
861
1012
|
] });
|
|
862
1013
|
}
|
|
1014
|
+
function HourlyTimeRangeFields({
|
|
1015
|
+
fromTime,
|
|
1016
|
+
scheduleEndTime,
|
|
1017
|
+
chargeBy,
|
|
1018
|
+
timeFormat,
|
|
1019
|
+
toOptions,
|
|
1020
|
+
minTo,
|
|
1021
|
+
onFromChange,
|
|
1022
|
+
onToChange,
|
|
1023
|
+
vertical = false
|
|
1024
|
+
}) {
|
|
1025
|
+
const fields = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1026
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
1027
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "From" }),
|
|
1028
|
+
/* @__PURE__ */ jsx(TimeField, { value: fromTime, onChange: onFromChange, timeFormat })
|
|
1029
|
+
] }),
|
|
1030
|
+
vertical ? /* @__PURE__ */ jsx("div", { className: "h-px bg-border", "aria-hidden": true }) : null,
|
|
1031
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
1032
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "To" }),
|
|
1033
|
+
chargeBy === "hours" ? /* @__PURE__ */ jsx(
|
|
1034
|
+
TimeField,
|
|
1035
|
+
{
|
|
1036
|
+
value: scheduleEndTime,
|
|
1037
|
+
onChange: onToChange,
|
|
1038
|
+
timeFormat,
|
|
1039
|
+
allowedTimes: toOptions,
|
|
1040
|
+
disabled: toOptions.length === 0,
|
|
1041
|
+
placeholder: toOptions.length === 0 ? "No later times" : "Select time"
|
|
1042
|
+
}
|
|
1043
|
+
) : /* @__PURE__ */ jsx(
|
|
1044
|
+
TimeField,
|
|
1045
|
+
{
|
|
1046
|
+
value: scheduleEndTime,
|
|
1047
|
+
onChange: onToChange,
|
|
1048
|
+
timeFormat,
|
|
1049
|
+
minTime: minTo,
|
|
1050
|
+
disabled: !minTo,
|
|
1051
|
+
placeholder: minTo ? "Select time" : "No later times"
|
|
1052
|
+
}
|
|
1053
|
+
)
|
|
1054
|
+
] })
|
|
1055
|
+
] });
|
|
1056
|
+
if (vertical) {
|
|
1057
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: fields });
|
|
1058
|
+
}
|
|
1059
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: fields });
|
|
1060
|
+
}
|
|
863
1061
|
function HourlySchedulePicker({
|
|
864
1062
|
scheduleDate,
|
|
865
1063
|
scheduleTime,
|
|
1064
|
+
scheduleEndTime = "",
|
|
866
1065
|
onChange,
|
|
867
1066
|
timeFormat = "24h",
|
|
868
|
-
flat = true
|
|
1067
|
+
flat = true,
|
|
1068
|
+
chargeBy = "hours",
|
|
1069
|
+
splitLayout = false,
|
|
1070
|
+
stackLayout = false
|
|
869
1071
|
}) {
|
|
870
|
-
const [step, setStep] = useState(
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1072
|
+
const [step, setStep] = useState("date");
|
|
1073
|
+
const fromTime = scheduleTime || "09:00";
|
|
1074
|
+
const toOptions = chargeBy === "hours" ? hourlyEndTimes(fromTime) : [];
|
|
1075
|
+
const minTo = chargeBy === "minutes" ? addMinutesToHm(fromTime, 1) ?? void 0 : void 0;
|
|
1076
|
+
const handleFromChange = (nextFrom) => {
|
|
1077
|
+
onChange({
|
|
1078
|
+
scheduleTime: nextFrom,
|
|
1079
|
+
scheduleEndTime: snapScheduleEndTime(nextFrom, scheduleEndTime, chargeBy)
|
|
1080
|
+
});
|
|
1081
|
+
};
|
|
1082
|
+
const handleDateChange = (v) => {
|
|
1083
|
+
const nextFrom = scheduleTime || "09:00";
|
|
1084
|
+
onChange({
|
|
1085
|
+
scheduleDate: v,
|
|
1086
|
+
scheduleTime: nextFrom,
|
|
1087
|
+
scheduleEndTime: scheduleEndTime || snapScheduleEndTime(nextFrom, "", chargeBy) || "10:00"
|
|
1088
|
+
});
|
|
1089
|
+
};
|
|
1090
|
+
const summary = scheduleDate && fromTime && scheduleEndTime ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between rounded-xl border border-border bg-muted px-3 py-2.5", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
1091
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Scheduled for" }),
|
|
1092
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
|
|
1093
|
+
formatDateLabel(scheduleDate),
|
|
1094
|
+
" \xB7 ",
|
|
1095
|
+
formatTimeForDisplay(fromTime, timeFormat),
|
|
1096
|
+
" \u2192",
|
|
1097
|
+
" ",
|
|
1098
|
+
formatTimeForDisplay(scheduleEndTime, timeFormat)
|
|
1099
|
+
] })
|
|
1100
|
+
] }) }) : null;
|
|
1101
|
+
const timeFields = /* @__PURE__ */ jsx(
|
|
1102
|
+
HourlyTimeRangeFields,
|
|
1103
|
+
{
|
|
1104
|
+
fromTime,
|
|
1105
|
+
scheduleEndTime,
|
|
1106
|
+
chargeBy,
|
|
1107
|
+
timeFormat,
|
|
1108
|
+
toOptions,
|
|
1109
|
+
minTo,
|
|
1110
|
+
onFromChange: handleFromChange,
|
|
1111
|
+
onToChange: (v) => onChange({ scheduleEndTime: v }),
|
|
1112
|
+
vertical: splitLayout || stackLayout
|
|
1113
|
+
}
|
|
1114
|
+
);
|
|
1115
|
+
if (stackLayout) {
|
|
1116
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
1117
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
1118
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1119
|
+
/* @__PURE__ */ jsx(
|
|
1120
|
+
DayCalendarPicker,
|
|
1121
|
+
{
|
|
1122
|
+
flat,
|
|
1123
|
+
selectedDate: scheduleDate,
|
|
1124
|
+
minDate: todayLocalYmd(),
|
|
1125
|
+
onChange: handleDateChange
|
|
1126
|
+
}
|
|
1127
|
+
)
|
|
1128
|
+
] }),
|
|
1129
|
+
timeFields,
|
|
1130
|
+
summary
|
|
1131
|
+
] });
|
|
1132
|
+
}
|
|
1133
|
+
if (splitLayout) {
|
|
1134
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
1135
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5 sm:flex-row sm:items-start", children: [
|
|
1136
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1137
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1138
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx(
|
|
1139
|
+
DayCalendarPicker,
|
|
1140
|
+
{
|
|
1141
|
+
flat,
|
|
1142
|
+
selectedDate: scheduleDate,
|
|
1143
|
+
minDate: todayLocalYmd(),
|
|
1144
|
+
onChange: handleDateChange
|
|
1145
|
+
}
|
|
1146
|
+
) })
|
|
1147
|
+
] }),
|
|
1148
|
+
/* @__PURE__ */ jsx("div", { className: "hidden w-px shrink-0 self-stretch bg-border sm:block", "aria-hidden": true }),
|
|
1149
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-[9.5rem] shrink-0 sm:w-44", children: [
|
|
1150
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Time" }),
|
|
1151
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: timeFields })
|
|
1152
|
+
] })
|
|
1153
|
+
] }),
|
|
1154
|
+
summary
|
|
1155
|
+
] });
|
|
1156
|
+
}
|
|
874
1157
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
875
1158
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
|
|
876
1159
|
/* @__PURE__ */ jsx(
|
|
@@ -889,7 +1172,7 @@ function HourlySchedulePicker({
|
|
|
889
1172
|
{
|
|
890
1173
|
className: cn(
|
|
891
1174
|
step === "time" && "font-semibold text-blue-600 dark:text-blue-400",
|
|
892
|
-
|
|
1175
|
+
step !== "time" && "text-muted-foreground/60"
|
|
893
1176
|
),
|
|
894
1177
|
children: "Time"
|
|
895
1178
|
}
|
|
@@ -902,9 +1185,9 @@ function HourlySchedulePicker({
|
|
|
902
1185
|
{
|
|
903
1186
|
flat,
|
|
904
1187
|
selectedDate: scheduleDate,
|
|
905
|
-
minDate:
|
|
1188
|
+
minDate: todayLocalYmd(),
|
|
906
1189
|
onChange: (v) => {
|
|
907
|
-
|
|
1190
|
+
handleDateChange(v);
|
|
908
1191
|
setStep("time");
|
|
909
1192
|
}
|
|
910
1193
|
}
|
|
@@ -927,29 +1210,110 @@ function HourlySchedulePicker({
|
|
|
927
1210
|
]
|
|
928
1211
|
}
|
|
929
1212
|
),
|
|
1213
|
+
timeFields,
|
|
1214
|
+
summary
|
|
1215
|
+
] })
|
|
1216
|
+
] });
|
|
1217
|
+
}
|
|
1218
|
+
function ServiceNeededAtPicker({
|
|
1219
|
+
scheduleDate,
|
|
1220
|
+
scheduleTime,
|
|
1221
|
+
onChange,
|
|
1222
|
+
timeFormat = "24h",
|
|
1223
|
+
flat = true,
|
|
1224
|
+
stackLayout = true,
|
|
1225
|
+
minDate = todayLocalYmd(),
|
|
1226
|
+
preparationMinutes = 0
|
|
1227
|
+
}) {
|
|
1228
|
+
const neededTime = scheduleTime || "09:00";
|
|
1229
|
+
const todayYmd = todayLocalYmd();
|
|
1230
|
+
const minTimeToday = preparationMinutes > 0 && scheduleDate === todayYmd ? minNeededTimeHm(preparationMinutes) : void 0;
|
|
1231
|
+
const syncNeeded = (date, time) => {
|
|
1232
|
+
let nextTime = time;
|
|
1233
|
+
if (preparationMinutes > 0 && date === todayYmd) {
|
|
1234
|
+
const floor = minNeededTimeHm(preparationMinutes);
|
|
1235
|
+
if (floor && nextTime < floor) nextTime = floor;
|
|
1236
|
+
}
|
|
1237
|
+
onChange({
|
|
1238
|
+
scheduleDate: date,
|
|
1239
|
+
scheduleTime: nextTime,
|
|
1240
|
+
scheduleEndTime: nextTime,
|
|
1241
|
+
startDate: date,
|
|
1242
|
+
endDate: date
|
|
1243
|
+
});
|
|
1244
|
+
};
|
|
1245
|
+
const handleDateChange = (v) => syncNeeded(v, neededTime);
|
|
1246
|
+
const handleTimeChange = (v) => {
|
|
1247
|
+
if (!scheduleDate) return;
|
|
1248
|
+
syncNeeded(scheduleDate, v);
|
|
1249
|
+
};
|
|
1250
|
+
const summary = scheduleDate && neededTime ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between rounded-xl border border-border bg-muted px-3 py-2.5", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
1251
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Needed at" }),
|
|
1252
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
|
|
1253
|
+
formatDateLabel(scheduleDate),
|
|
1254
|
+
" \xB7 ",
|
|
1255
|
+
formatTimeForDisplay(neededTime, timeFormat)
|
|
1256
|
+
] })
|
|
1257
|
+
] }) }) : null;
|
|
1258
|
+
const timeField = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
1259
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Needed at" }),
|
|
1260
|
+
/* @__PURE__ */ jsx(
|
|
1261
|
+
TimeField,
|
|
1262
|
+
{
|
|
1263
|
+
value: neededTime,
|
|
1264
|
+
onChange: handleTimeChange,
|
|
1265
|
+
timeFormat,
|
|
1266
|
+
minTime: minTimeToday,
|
|
1267
|
+
disabled: Boolean(minTimeToday && !minTimeToday),
|
|
1268
|
+
placeholder: minTimeToday ? "Select time" : void 0
|
|
1269
|
+
}
|
|
1270
|
+
)
|
|
1271
|
+
] });
|
|
1272
|
+
if (stackLayout) {
|
|
1273
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
930
1274
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
931
|
-
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "
|
|
932
|
-
/* @__PURE__ */ jsx(
|
|
933
|
-
|
|
1275
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1276
|
+
/* @__PURE__ */ jsx(
|
|
1277
|
+
DayCalendarPicker,
|
|
934
1278
|
{
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1279
|
+
flat,
|
|
1280
|
+
selectedDate: scheduleDate,
|
|
1281
|
+
minDate,
|
|
1282
|
+
onChange: handleDateChange
|
|
938
1283
|
}
|
|
939
|
-
)
|
|
1284
|
+
)
|
|
940
1285
|
] }),
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1286
|
+
timeField,
|
|
1287
|
+
summary
|
|
1288
|
+
] });
|
|
1289
|
+
}
|
|
1290
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5 sm:flex-row sm:items-start", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
1291
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1292
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1293
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx(
|
|
1294
|
+
DayCalendarPicker,
|
|
1295
|
+
{
|
|
1296
|
+
flat,
|
|
1297
|
+
selectedDate: scheduleDate,
|
|
1298
|
+
minDate,
|
|
1299
|
+
onChange: handleDateChange
|
|
1300
|
+
}
|
|
1301
|
+
) })
|
|
1302
|
+
] }),
|
|
1303
|
+
/* @__PURE__ */ jsx("div", { className: "hidden w-px shrink-0 self-stretch bg-border sm:block", "aria-hidden": true }),
|
|
1304
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-[9.5rem] shrink-0 sm:w-44", children: timeField }),
|
|
1305
|
+
summary ? /* @__PURE__ */ jsx("div", { className: "w-full", children: summary }) : null
|
|
950
1306
|
] });
|
|
951
1307
|
}
|
|
1308
|
+
function minNeededTimeHm(prepMinutes, now = /* @__PURE__ */ new Date()) {
|
|
1309
|
+
const earliest = new Date(now.getTime() + prepMinutes * 6e4);
|
|
1310
|
+
if (earliest.getSeconds() > 0 || earliest.getMilliseconds() > 0) {
|
|
1311
|
+
earliest.setMinutes(earliest.getMinutes() + 1);
|
|
1312
|
+
}
|
|
1313
|
+
earliest.setSeconds(0, 0);
|
|
1314
|
+
return `${String(earliest.getHours()).padStart(2, "0")}:${String(earliest.getMinutes()).padStart(2, "0")}`;
|
|
1315
|
+
}
|
|
952
1316
|
|
|
953
|
-
export { DayCalendarPicker, DayPeriodPicker, DayRangeCalendar, HourlySchedulePicker, MonthPeriodPicker, MonthRangeCalendar, ScheduleFieldLabel, ScheduleInfoBanner, ScheduleStepper, WeekCalendarPicker, WeekPeriodPicker };
|
|
1317
|
+
export { DayCalendarPicker, DayPeriodPicker, DayRangeCalendar, HourlySchedulePicker, MonthPeriodPicker, MonthRangeCalendar, ScheduleFieldLabel, ScheduleInfoBanner, ScheduleStepper, ServiceNeededAtPicker, WeekCalendarPicker, WeekPeriodPicker };
|
|
954
1318
|
//# sourceMappingURL=booking-schedule-calendars.js.map
|
|
955
1319
|
//# sourceMappingURL=booking-schedule-calendars.js.map
|