@spacesync/client 0.1.7 → 0.1.10
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 -35
- package/dist/booking-schedule-calendars.js +716 -432
- package/dist/booking-schedule-calendars.js.map +1 -1
- package/dist/booking-schedule.d.ts +71 -0
- package/dist/booking-schedule.js +3 -0
- package/dist/booking-schedule.js.map +1 -0
- 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/chunk-OTSA5ROL.js +209 -0
- package/dist/chunk-OTSA5ROL.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 +9 -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,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { formatTimeForDisplay } from './chunk-
|
|
1
|
+
import { currentWeekStart, PRICING_TODAY, weekCountBetween, weekRangeLabel, currentMonthYm, monthCount, monthLabel, addMonthsYm, todayLocalYmd, addDays, diffDays, MIN_WEEK_START, localDateYmd } from './chunk-OTSA5ROL.js';
|
|
2
|
+
import { CheckboxField, hourlyEndTimes, addMinutesToHm, formatTimeForDisplay, snapScheduleEndTime, TimeField } from './chunk-F7MU5T46.js';
|
|
3
3
|
import './chunk-RV5PMZYZ.js';
|
|
4
4
|
import './chunk-L65F4EMK.js';
|
|
5
5
|
import './chunk-PBNBLCC2.js';
|
|
@@ -15,65 +15,6 @@ import { useState, useEffect } from 'react';
|
|
|
15
15
|
import { Info, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
16
16
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
17
17
|
|
|
18
|
-
// src/lib/booking-schedule.ts
|
|
19
|
-
var PRICING_TODAY = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
20
|
-
function computeMinMonth() {
|
|
21
|
-
const d = /* @__PURE__ */ new Date();
|
|
22
|
-
if (d.getDate() === 1) {
|
|
23
|
-
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
24
|
-
}
|
|
25
|
-
const n = new Date(d.getFullYear(), d.getMonth() + 1, 1);
|
|
26
|
-
return `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, "0")}`;
|
|
27
|
-
}
|
|
28
|
-
function computeMinWeekStart() {
|
|
29
|
-
const d = /* @__PURE__ */ new Date();
|
|
30
|
-
const day = d.getDay();
|
|
31
|
-
if (day === 1) return d.toISOString().split("T")[0];
|
|
32
|
-
const skip = day === 0 ? 1 : 8 - day;
|
|
33
|
-
d.setDate(d.getDate() + skip);
|
|
34
|
-
return d.toISOString().split("T")[0];
|
|
35
|
-
}
|
|
36
|
-
var MIN_MONTH = computeMinMonth() || `${(/* @__PURE__ */ new Date()).getFullYear()}-${String((/* @__PURE__ */ new Date()).getMonth() + 2).padStart(2, "0")}`;
|
|
37
|
-
var MIN_WEEK_START = computeMinWeekStart() || PRICING_TODAY;
|
|
38
|
-
var IS_PARTIAL_MONTH = (/* @__PURE__ */ new Date()).getDate() !== 1;
|
|
39
|
-
function monthLabel(ym) {
|
|
40
|
-
const [y, m] = ym.split("-").map(Number);
|
|
41
|
-
return new Date(y, m - 1, 1).toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
42
|
-
}
|
|
43
|
-
function monthCount(from, to) {
|
|
44
|
-
if (!from || !to || !from.includes("-") || !to.includes("-")) return 0;
|
|
45
|
-
const [fy, fm] = from.split("-").map(Number);
|
|
46
|
-
const [ty, tm] = to.split("-").map(Number);
|
|
47
|
-
return (ty - fy) * 12 + (tm - fm) + 1;
|
|
48
|
-
}
|
|
49
|
-
function weekRangeLabel(weekFrom, weekCount) {
|
|
50
|
-
if (!weekFrom || isNaN(new Date(weekFrom).getTime())) return "\u2014";
|
|
51
|
-
const end = new Date(weekFrom);
|
|
52
|
-
end.setDate(end.getDate() + weekCount * 7 - 1);
|
|
53
|
-
const s = new Date(weekFrom).toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
54
|
-
const e = end.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
55
|
-
return `${s} \u2013 ${e}`;
|
|
56
|
-
}
|
|
57
|
-
function parseDateOnly(date) {
|
|
58
|
-
const [y, m, d] = date.split("-").map(Number);
|
|
59
|
-
return new Date(y, m - 1, d);
|
|
60
|
-
}
|
|
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
|
-
function diffDays(a, b) {
|
|
68
|
-
const da = parseDateOnly(a);
|
|
69
|
-
const db = parseDateOnly(b);
|
|
70
|
-
return Math.max(0, Math.round((db.getTime() - da.getTime()) / 864e5));
|
|
71
|
-
}
|
|
72
|
-
function addDays(date, days) {
|
|
73
|
-
const d = parseDateOnly(date);
|
|
74
|
-
d.setDate(d.getDate() + days);
|
|
75
|
-
return formatDateOnly(d);
|
|
76
|
-
}
|
|
77
18
|
var MONTH_NAMES = [
|
|
78
19
|
"January",
|
|
79
20
|
"February",
|
|
@@ -89,7 +30,22 @@ var MONTH_NAMES = [
|
|
|
89
30
|
"December"
|
|
90
31
|
];
|
|
91
32
|
function dayStr(d) {
|
|
92
|
-
return d
|
|
33
|
+
return localDateYmd(d);
|
|
34
|
+
}
|
|
35
|
+
var calendarBlockClass = "flex flex-col overflow-hidden rounded-xl border border-border/60";
|
|
36
|
+
var calendarNavRowClass = "flex items-center justify-between border-b border-border/40 px-2 py-1.5";
|
|
37
|
+
var calendarWeekdayRowClass = "grid grid-cols-7 border-b border-border/40";
|
|
38
|
+
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";
|
|
39
|
+
function cellGridLines(index, total, cols) {
|
|
40
|
+
return cn(
|
|
41
|
+
"border-border/40",
|
|
42
|
+
index % cols !== cols - 1 && "border-r",
|
|
43
|
+
index < total - cols && "border-b"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function weekEndMonday(weekFrom, weekCount) {
|
|
47
|
+
if (!weekFrom || weekCount < 1) return weekFrom;
|
|
48
|
+
return addDays(weekFrom, (weekCount - 1) * 7);
|
|
93
49
|
}
|
|
94
50
|
function getCalendarGrid(year, month) {
|
|
95
51
|
const firstDay = new Date(year, month, 1);
|
|
@@ -145,6 +101,8 @@ function ScheduleStepper({
|
|
|
145
101
|
}
|
|
146
102
|
function WeekCalendarPicker({
|
|
147
103
|
weekFrom,
|
|
104
|
+
weekCount,
|
|
105
|
+
rangeMode,
|
|
148
106
|
onChange,
|
|
149
107
|
flat = false
|
|
150
108
|
}) {
|
|
@@ -152,10 +110,22 @@ function WeekCalendarPicker({
|
|
|
152
110
|
const [calYear, setCalYear] = useState(initDate.getFullYear());
|
|
153
111
|
const [calMonth, setCalMonth] = useState(initDate.getMonth());
|
|
154
112
|
const [hoverMon, setHoverMon] = useState(null);
|
|
113
|
+
const [phase, setPhase] = useState("from");
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
setPhase("from");
|
|
116
|
+
}, [rangeMode]);
|
|
155
117
|
const days = getCalendarGrid(calYear, calMonth);
|
|
156
|
-
const minDate = /* @__PURE__ */ new Date((
|
|
157
|
-
const
|
|
158
|
-
const
|
|
118
|
+
const minDate = /* @__PURE__ */ new Date((currentWeekStart() || PRICING_TODAY) + "T00:00:00");
|
|
119
|
+
const endMon = weekEndMonday(weekFrom, weekCount);
|
|
120
|
+
const [dispFrom, dispTo] = (() => {
|
|
121
|
+
if (!rangeMode || !weekFrom) return [weekFrom, endMon];
|
|
122
|
+
if (phase === "to" && hoverMon) {
|
|
123
|
+
if (hoverMon >= weekFrom) return [weekFrom, hoverMon];
|
|
124
|
+
return [hoverMon, hoverMon];
|
|
125
|
+
}
|
|
126
|
+
return [weekFrom, endMon];
|
|
127
|
+
})();
|
|
128
|
+
const previewCount = rangeMode && phase === "to" && hoverMon && hoverMon >= weekFrom ? weekCountBetween(weekFrom, hoverMon) : weekCount;
|
|
159
129
|
const prevMonth = () => {
|
|
160
130
|
if (calMonth === 0) {
|
|
161
131
|
setCalYear((y) => y - 1);
|
|
@@ -170,6 +140,24 @@ function WeekCalendarPicker({
|
|
|
170
140
|
};
|
|
171
141
|
const weeks = [];
|
|
172
142
|
for (let i = 0; i < days.length; i += 7) weeks.push(days.slice(i, i + 7));
|
|
143
|
+
const handleWeekClick = (monStr) => {
|
|
144
|
+
if (!rangeMode) {
|
|
145
|
+
onChange(monStr, 1);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (phase === "from") {
|
|
149
|
+
onChange(monStr, 1);
|
|
150
|
+
setPhase("to");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (monStr >= weekFrom) {
|
|
154
|
+
onChange(weekFrom, weekCountBetween(weekFrom, monStr));
|
|
155
|
+
setPhase("from");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
onChange(monStr, 1);
|
|
159
|
+
setPhase("to");
|
|
160
|
+
};
|
|
173
161
|
return /* @__PURE__ */ jsxs(
|
|
174
162
|
"div",
|
|
175
163
|
{
|
|
@@ -178,70 +166,113 @@ function WeekCalendarPicker({
|
|
|
178
166
|
flat ? "gap-3" : "gap-1 rounded-2xl border border-border bg-card p-3"
|
|
179
167
|
),
|
|
180
168
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
169
|
+
rangeMode ? /* @__PURE__ */ jsx(
|
|
170
|
+
"div",
|
|
171
|
+
{
|
|
172
|
+
className: cn(
|
|
173
|
+
"flex items-center gap-2 px-1 text-[11px] font-medium",
|
|
174
|
+
phase === "to" ? "text-blue-600 dark:text-blue-400" : "text-muted-foreground"
|
|
175
|
+
),
|
|
176
|
+
children: phase === "from" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
177
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-muted-foreground/50" }),
|
|
178
|
+
"Click the first week"
|
|
179
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
180
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-blue-500" }),
|
|
181
|
+
"Click the last week",
|
|
182
|
+
/* @__PURE__ */ jsx(
|
|
183
|
+
"button",
|
|
184
|
+
{
|
|
185
|
+
type: "button",
|
|
186
|
+
onClick: () => {
|
|
187
|
+
onChange(weekFrom, 1);
|
|
188
|
+
setPhase("to");
|
|
189
|
+
},
|
|
190
|
+
className: "ml-auto text-[10px] text-blue-400 underline underline-offset-2 hover:text-blue-600 dark:text-blue-400",
|
|
191
|
+
children: "Reset"
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
] })
|
|
195
|
+
}
|
|
196
|
+
) : null,
|
|
197
|
+
/* @__PURE__ */ jsxs("div", { className: calendarBlockClass, children: [
|
|
198
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
199
|
+
/* @__PURE__ */ jsx(
|
|
200
|
+
"button",
|
|
201
|
+
{
|
|
202
|
+
type: "button",
|
|
203
|
+
onClick: prevMonth,
|
|
204
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
205
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
209
|
+
MONTH_NAMES[calMonth],
|
|
210
|
+
" ",
|
|
211
|
+
calYear
|
|
212
|
+
] }),
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
214
|
+
"button",
|
|
215
|
+
{
|
|
216
|
+
type: "button",
|
|
217
|
+
onClick: nextMonth,
|
|
218
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
219
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
220
|
+
}
|
|
221
|
+
)
|
|
195
222
|
] }),
|
|
196
|
-
/* @__PURE__ */ jsx(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
/* @__PURE__ */ jsx("div", { className: calendarWeekdayRowClass, children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: calendarWeekdayCellClass, children: d }, d)) }),
|
|
224
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col divide-y divide-border/40", children: weeks.map((week, wi) => {
|
|
225
|
+
const mondayDate = week[0];
|
|
226
|
+
const monStr = dayStr(mondayDate);
|
|
227
|
+
const isSelectable = mondayDate >= minDate;
|
|
228
|
+
const isStartWeek = Boolean(weekFrom) && monStr === dispFrom;
|
|
229
|
+
const isEndWeek = Boolean(weekFrom) && monStr === dispTo;
|
|
230
|
+
const inRange = rangeMode && Boolean(weekFrom) && monStr >= dispFrom && monStr <= dispTo;
|
|
231
|
+
const isSelected = rangeMode ? inRange : monStr === weekFrom;
|
|
232
|
+
return /* @__PURE__ */ jsx(
|
|
233
|
+
"div",
|
|
234
|
+
{
|
|
235
|
+
className: cn(
|
|
236
|
+
"relative flex cursor-pointer transition-colors",
|
|
237
|
+
isSelected && (isStartWeek || isEndWeek) && "bg-blue-100/80",
|
|
238
|
+
isSelected && !isStartWeek && !isEndWeek && "bg-blue-50",
|
|
239
|
+
isSelectable ? "hover:bg-accent/60" : "cursor-not-allowed opacity-60"
|
|
240
|
+
),
|
|
241
|
+
onMouseEnter: () => isSelectable && setHoverMon(monStr),
|
|
242
|
+
onMouseLeave: () => setHoverMon(null),
|
|
243
|
+
onClick: () => isSelectable && handleWeekClick(monStr),
|
|
244
|
+
children: week.map((d, di) => {
|
|
245
|
+
const isCurrentMonth = d.getMonth() === calMonth;
|
|
246
|
+
const isMon = di === 0;
|
|
247
|
+
const isSun = di === 6;
|
|
248
|
+
const filled = rangeMode ? isStartWeek && isMon || isEndWeek && isSun : isSelected && isMon;
|
|
249
|
+
return /* @__PURE__ */ jsx(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
className: "relative z-10 flex h-9 flex-1 items-center justify-center border-r border-border/40 last:border-r-0",
|
|
253
|
+
children: /* @__PURE__ */ jsx(
|
|
254
|
+
"span",
|
|
255
|
+
{
|
|
256
|
+
className: cn(
|
|
257
|
+
"flex h-7 w-7 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
258
|
+
isCurrentMonth ? "text-muted-foreground" : "text-muted-foreground/50",
|
|
259
|
+
!isSelectable && "text-muted-foreground/40",
|
|
260
|
+
isSelected && "font-medium text-blue-700 dark:text-blue-300",
|
|
261
|
+
filled && "bg-blue-500 font-semibold text-white shadow-sm"
|
|
262
|
+
),
|
|
263
|
+
children: d.getDate()
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
},
|
|
267
|
+
di
|
|
268
|
+
);
|
|
269
|
+
})
|
|
270
|
+
},
|
|
271
|
+
wi
|
|
272
|
+
);
|
|
273
|
+
}) })
|
|
205
274
|
] }),
|
|
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" })
|
|
275
|
+
/* @__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
276
|
]
|
|
246
277
|
}
|
|
247
278
|
);
|
|
@@ -250,16 +281,21 @@ function MonthRangeCalendar({
|
|
|
250
281
|
monthFrom,
|
|
251
282
|
monthTo,
|
|
252
283
|
onChange,
|
|
284
|
+
rangeMode,
|
|
253
285
|
flat = false
|
|
254
286
|
}) {
|
|
255
287
|
const initYear = monthFrom ? parseInt(monthFrom.split("-")[0], 10) : (/* @__PURE__ */ new Date()).getFullYear();
|
|
256
288
|
const [calYear, setCalYear] = useState(initYear);
|
|
257
289
|
const [phase, setPhase] = useState("from");
|
|
258
290
|
const [hoverYm, setHoverYm] = useState(null);
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
setPhase("from");
|
|
293
|
+
}, [rangeMode]);
|
|
259
294
|
const ABBR = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
260
295
|
const toYm = (y, m) => `${y}-${String(m + 1).padStart(2, "0")}`;
|
|
261
|
-
const minYm =
|
|
296
|
+
const minYm = currentMonthYm() || `${(/* @__PURE__ */ new Date()).getFullYear()}-01`;
|
|
262
297
|
const [dispFrom, dispTo] = (() => {
|
|
298
|
+
if (!rangeMode) return [monthFrom, monthFrom || monthTo];
|
|
263
299
|
if (phase === "to" && hoverYm) {
|
|
264
300
|
if (hoverYm >= monthFrom) return [monthFrom, hoverYm];
|
|
265
301
|
return [hoverYm, hoverYm];
|
|
@@ -269,6 +305,10 @@ function MonthRangeCalendar({
|
|
|
269
305
|
const isSingle = dispFrom === dispTo;
|
|
270
306
|
const handleClick = (ymStr) => {
|
|
271
307
|
if (ymStr < minYm) return;
|
|
308
|
+
if (!rangeMode) {
|
|
309
|
+
onChange(ymStr, ymStr);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
272
312
|
if (phase === "from") {
|
|
273
313
|
onChange(ymStr, ymStr);
|
|
274
314
|
setPhase("to");
|
|
@@ -277,6 +317,7 @@ function MonthRangeCalendar({
|
|
|
277
317
|
setPhase("from");
|
|
278
318
|
} else {
|
|
279
319
|
onChange(ymStr, ymStr);
|
|
320
|
+
setPhase("to");
|
|
280
321
|
}
|
|
281
322
|
};
|
|
282
323
|
const actualCount = monthCount(monthFrom, monthTo);
|
|
@@ -289,28 +330,7 @@ function MonthRangeCalendar({
|
|
|
289
330
|
flat ? "flex flex-col gap-5" : "overflow-hidden rounded-2xl border border-border bg-card"
|
|
290
331
|
),
|
|
291
332
|
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(
|
|
333
|
+
rangeMode ? /* @__PURE__ */ jsx(
|
|
314
334
|
"div",
|
|
315
335
|
{
|
|
316
336
|
className: cn(
|
|
@@ -322,10 +342,10 @@ function MonthRangeCalendar({
|
|
|
322
342
|
),
|
|
323
343
|
children: phase === "from" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
324
344
|
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-muted-foreground/50" }),
|
|
325
|
-
"Click
|
|
345
|
+
"Click the first month"
|
|
326
346
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
327
347
|
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-blue-500" }),
|
|
328
|
-
"
|
|
348
|
+
"Click the last month",
|
|
329
349
|
/* @__PURE__ */ jsx(
|
|
330
350
|
"button",
|
|
331
351
|
{
|
|
@@ -340,59 +360,76 @@ function MonthRangeCalendar({
|
|
|
340
360
|
)
|
|
341
361
|
] })
|
|
342
362
|
}
|
|
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
|
-
|
|
363
|
+
) : null,
|
|
364
|
+
/* @__PURE__ */ jsxs("div", { className: cn(calendarBlockClass, !flat && "mx-3 mb-3"), children: [
|
|
365
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
366
|
+
/* @__PURE__ */ jsx(
|
|
367
|
+
"button",
|
|
368
|
+
{
|
|
369
|
+
type: "button",
|
|
370
|
+
onClick: () => setCalYear((y) => y - 1),
|
|
371
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
372
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
373
|
+
}
|
|
374
|
+
),
|
|
375
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-muted-foreground", children: calYear }),
|
|
376
|
+
/* @__PURE__ */ jsx(
|
|
377
|
+
"button",
|
|
378
|
+
{
|
|
379
|
+
type: "button",
|
|
380
|
+
onClick: () => setCalYear((y) => y + 1),
|
|
381
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
|
|
382
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
] }),
|
|
386
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4", children: ABBR.map((label, i) => {
|
|
387
|
+
const ymStr = toYm(calYear, i);
|
|
388
|
+
const disabled = ymStr < minYm;
|
|
389
|
+
const inRange = !disabled && ymStr >= dispFrom && ymStr <= dispTo;
|
|
390
|
+
const isFrom = ymStr === dispFrom;
|
|
391
|
+
const isTo = ymStr === dispTo;
|
|
392
|
+
const showStrip = rangeMode && inRange && !isSingle;
|
|
393
|
+
const sLeft = isFrom ? "left-1/2" : "left-0";
|
|
394
|
+
const sRight = isTo ? "right-1/2" : "right-0";
|
|
395
|
+
const filled = (isFrom || isTo || !rangeMode && ymStr === monthFrom) && !disabled;
|
|
396
|
+
return /* @__PURE__ */ jsxs(
|
|
397
|
+
"div",
|
|
398
|
+
{
|
|
399
|
+
className: cn(
|
|
400
|
+
"relative",
|
|
401
|
+
cellGridLines(i, 12, 4),
|
|
402
|
+
flat ? "h-14" : "h-12",
|
|
403
|
+
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
|
404
|
+
),
|
|
405
|
+
onMouseEnter: () => !disabled && setHoverYm(ymStr),
|
|
406
|
+
onMouseLeave: () => setHoverYm(null),
|
|
407
|
+
onClick: () => handleClick(ymStr),
|
|
408
|
+
children: [
|
|
409
|
+
showStrip ? /* @__PURE__ */ jsx("div", { className: `absolute bottom-1.5 top-1.5 bg-blue-100 ${sLeft} ${sRight}` }) : null,
|
|
410
|
+
/* @__PURE__ */ jsx(
|
|
411
|
+
"div",
|
|
412
|
+
{
|
|
413
|
+
className: cn(
|
|
414
|
+
"relative z-10 flex w-full items-center justify-center",
|
|
415
|
+
flat ? "h-14" : "h-12"
|
|
416
|
+
),
|
|
417
|
+
children: /* @__PURE__ */ jsx(
|
|
418
|
+
"span",
|
|
419
|
+
{
|
|
420
|
+
className: `flex h-9 w-9 items-center justify-center rounded-full text-xs font-medium transition-all duration-100
|
|
421
|
+
${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"}`,
|
|
422
|
+
children: label
|
|
423
|
+
}
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
]
|
|
428
|
+
},
|
|
429
|
+
i
|
|
430
|
+
);
|
|
431
|
+
}) })
|
|
432
|
+
] }),
|
|
396
433
|
/* @__PURE__ */ jsx(
|
|
397
434
|
"div",
|
|
398
435
|
{
|
|
@@ -400,10 +437,13 @@ function MonthRangeCalendar({
|
|
|
400
437
|
"flex items-center justify-between transition-colors",
|
|
401
438
|
flat ? "border-t border-border/70 pt-4 text-xs" : cn(
|
|
402
439
|
"border-t px-4 py-2.5",
|
|
403
|
-
phase === "from" && actualCount > 0 ? "border-border bg-muted" : "border-transparent"
|
|
440
|
+
rangeMode && phase === "from" && actualCount > 0 ? "border-border bg-muted" : "border-transparent"
|
|
404
441
|
)
|
|
405
442
|
),
|
|
406
|
-
children:
|
|
443
|
+
children: !rangeMode && monthFrom ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
444
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: monthLabel(monthFrom) }),
|
|
445
|
+
/* @__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" })
|
|
446
|
+
] }) : rangeMode && phase === "from" && actualCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
407
447
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
408
448
|
monthLabel(monthFrom),
|
|
409
449
|
" \u2192 ",
|
|
@@ -414,14 +454,14 @@ function MonthRangeCalendar({
|
|
|
414
454
|
" month",
|
|
415
455
|
actualCount !== 1 ? "s" : ""
|
|
416
456
|
] })
|
|
417
|
-
] }) : phase === "to" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
457
|
+
] }) : rangeMode && phase === "to" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
418
458
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: hoverYm && hoverYm >= monthFrom ? `${monthLabel(monthFrom)} \u2192 ${monthLabel(hoverYm)}` : `Start: ${monthLabel(monthFrom)}` }),
|
|
419
459
|
hoverYm && hoverYm >= monthFrom && previewCount > 0 ? /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-blue-400", children: [
|
|
420
460
|
previewCount,
|
|
421
461
|
" month",
|
|
422
462
|
previewCount !== 1 ? "s" : ""
|
|
423
463
|
] }) : null
|
|
424
|
-
] }) : /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/60", children: "
|
|
464
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground/60", children: rangeMode ? "Select a month range" : "Click a month to book" })
|
|
425
465
|
}
|
|
426
466
|
)
|
|
427
467
|
]
|
|
@@ -433,25 +473,58 @@ function MonthPeriodPicker({
|
|
|
433
473
|
onChange,
|
|
434
474
|
flat = true
|
|
435
475
|
}) {
|
|
476
|
+
const [rangeMode, setRangeMode] = useState(
|
|
477
|
+
() => Boolean(booking.monthFrom && booking.monthTo && booking.monthFrom !== booking.monthTo)
|
|
478
|
+
);
|
|
479
|
+
useEffect(() => {
|
|
480
|
+
if (booking.monthFrom && booking.monthTo && booking.monthFrom !== booking.monthTo) {
|
|
481
|
+
setRangeMode(true);
|
|
482
|
+
}
|
|
483
|
+
}, [booking.monthFrom, booking.monthTo]);
|
|
484
|
+
const monthCountSelected = monthCount(booking.monthFrom, booking.monthTo);
|
|
436
485
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", flat ? "gap-5" : "gap-3"), children: [
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
486
|
+
/* @__PURE__ */ jsx(ScheduleInfoBanner, { children: "Current month is available at full price, or choose a future month." }),
|
|
487
|
+
/* @__PURE__ */ jsx(
|
|
488
|
+
CheckboxField,
|
|
489
|
+
{
|
|
490
|
+
size: "sm",
|
|
491
|
+
label: "Select multiple months",
|
|
492
|
+
className: "w-fit items-center",
|
|
493
|
+
checked: rangeMode,
|
|
494
|
+
onCheckedChange: (checked) => {
|
|
495
|
+
setRangeMode(checked);
|
|
496
|
+
const from = booking.monthFrom || currentMonthYm();
|
|
497
|
+
if (!checked) {
|
|
498
|
+
onChange({ monthFrom: from, monthTo: from });
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const to = booking.monthTo && booking.monthTo > from ? booking.monthTo : addMonthsYm(from, 1);
|
|
502
|
+
onChange({ monthFrom: from, monthTo: to });
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
),
|
|
506
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: rangeMode ? "Select month range" : "Select month" }),
|
|
446
507
|
/* @__PURE__ */ jsx(
|
|
447
508
|
MonthRangeCalendar,
|
|
448
509
|
{
|
|
449
510
|
flat,
|
|
511
|
+
rangeMode,
|
|
450
512
|
monthFrom: booking.monthFrom,
|
|
451
513
|
monthTo: booking.monthTo,
|
|
452
514
|
onChange: (from, to) => onChange({ monthFrom: from, monthTo: to })
|
|
453
515
|
}
|
|
454
|
-
)
|
|
516
|
+
),
|
|
517
|
+
monthCountSelected > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-xl border border-border bg-muted px-3 py-2.5", children: [
|
|
518
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
519
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Period" }),
|
|
520
|
+
/* @__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) })
|
|
521
|
+
] }),
|
|
522
|
+
/* @__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: [
|
|
523
|
+
monthCountSelected,
|
|
524
|
+
" month",
|
|
525
|
+
monthCountSelected !== 1 ? "s" : ""
|
|
526
|
+
] })
|
|
527
|
+
] }) : null
|
|
455
528
|
] });
|
|
456
529
|
}
|
|
457
530
|
function WeekPeriodPicker({
|
|
@@ -459,49 +532,47 @@ function WeekPeriodPicker({
|
|
|
459
532
|
onChange,
|
|
460
533
|
flat = true
|
|
461
534
|
}) {
|
|
535
|
+
const [rangeMode, setRangeMode] = useState(() => booking.weekCount > 1);
|
|
536
|
+
useEffect(() => {
|
|
537
|
+
if (booking.weekCount > 1) setRangeMode(true);
|
|
538
|
+
}, [booking.weekCount]);
|
|
462
539
|
const rangeStr = weekRangeLabel(booking.weekFrom, booking.weekCount);
|
|
463
|
-
const isCurrentWeekPartial = new Date(PRICING_TODAY).getDay() !== 1;
|
|
464
540
|
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
|
-
|
|
541
|
+
/* @__PURE__ */ jsx(ScheduleInfoBanner, { children: "Current week is available at full price, or choose a future week." }),
|
|
542
|
+
/* @__PURE__ */ jsx(
|
|
543
|
+
CheckboxField,
|
|
544
|
+
{
|
|
545
|
+
size: "sm",
|
|
546
|
+
label: "Select multiple weeks",
|
|
547
|
+
className: "w-fit items-center",
|
|
548
|
+
checked: rangeMode,
|
|
549
|
+
onCheckedChange: (checked) => {
|
|
550
|
+
setRangeMode(checked);
|
|
551
|
+
const from = booking.weekFrom || currentWeekStart();
|
|
552
|
+
if (!checked) {
|
|
553
|
+
onChange({ weekFrom: from, weekCount: 1 });
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
onChange({
|
|
557
|
+
weekFrom: from,
|
|
558
|
+
weekCount: Math.max(2, booking.weekCount)
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
),
|
|
476
563
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
477
|
-
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "
|
|
564
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: rangeMode ? "Select week range" : "Select week" }),
|
|
478
565
|
/* @__PURE__ */ jsx(
|
|
479
566
|
WeekCalendarPicker,
|
|
480
567
|
{
|
|
481
568
|
flat,
|
|
569
|
+
rangeMode,
|
|
482
570
|
weekFrom: booking.weekFrom,
|
|
483
|
-
|
|
571
|
+
weekCount: booking.weekCount,
|
|
572
|
+
onChange: (weekFrom, weekCount) => onChange({ weekFrom, weekCount })
|
|
484
573
|
}
|
|
485
574
|
)
|
|
486
575
|
] }),
|
|
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
576
|
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
577
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
507
578
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Period" }),
|
|
@@ -529,7 +600,7 @@ function isBillableDay(day, start, end) {
|
|
|
529
600
|
function DayCalendarPicker({
|
|
530
601
|
selectedDate,
|
|
531
602
|
onChange,
|
|
532
|
-
minDate =
|
|
603
|
+
minDate = todayLocalYmd(),
|
|
533
604
|
flat = false
|
|
534
605
|
}) {
|
|
535
606
|
const initDate = /* @__PURE__ */ new Date((selectedDate || minDate) + "T00:00:00");
|
|
@@ -556,65 +627,68 @@ function DayCalendarPicker({
|
|
|
556
627
|
flat ? "gap-3" : "gap-1 rounded-2xl border border-border bg-card p-3"
|
|
557
628
|
),
|
|
558
629
|
children: [
|
|
559
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
560
|
-
/* @__PURE__ */
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
630
|
+
/* @__PURE__ */ jsxs("div", { className: calendarBlockClass, children: [
|
|
631
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
632
|
+
/* @__PURE__ */ jsx(
|
|
633
|
+
"button",
|
|
634
|
+
{
|
|
635
|
+
type: "button",
|
|
636
|
+
onClick: prevMonth,
|
|
637
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
638
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
639
|
+
}
|
|
640
|
+
),
|
|
641
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
642
|
+
MONTH_NAMES[calMonth],
|
|
643
|
+
" ",
|
|
644
|
+
calYear
|
|
645
|
+
] }),
|
|
646
|
+
/* @__PURE__ */ jsx(
|
|
647
|
+
"button",
|
|
648
|
+
{
|
|
649
|
+
type: "button",
|
|
650
|
+
onClick: nextMonth,
|
|
651
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
652
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
653
|
+
}
|
|
654
|
+
)
|
|
573
655
|
] }),
|
|
574
|
-
/* @__PURE__ */ jsx(
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
656
|
+
/* @__PURE__ */ jsx("div", { className: calendarWeekdayRowClass, children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: calendarWeekdayCellClass, children: d }, d)) }),
|
|
657
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((d, i) => {
|
|
658
|
+
const ds = dayStr(d);
|
|
659
|
+
const isCurrentMonth = d.getMonth() === calMonth;
|
|
660
|
+
const isSelectable = ds >= minDate;
|
|
661
|
+
const isSelected = ds === selectedDate;
|
|
662
|
+
return /* @__PURE__ */ jsx(
|
|
663
|
+
"button",
|
|
664
|
+
{
|
|
665
|
+
type: "button",
|
|
666
|
+
disabled: !isSelectable,
|
|
667
|
+
onClick: () => isSelectable && onChange(ds),
|
|
668
|
+
className: cn(
|
|
669
|
+
"flex h-9 items-center justify-center",
|
|
670
|
+
cellGridLines(i, days.length, 7),
|
|
671
|
+
!isSelectable && "cursor-not-allowed"
|
|
672
|
+
),
|
|
673
|
+
children: /* @__PURE__ */ jsx(
|
|
674
|
+
"span",
|
|
675
|
+
{
|
|
676
|
+
className: cn(
|
|
677
|
+
"flex h-8 w-8 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
678
|
+
isSelectable && isCurrentMonth && "text-foreground hover:bg-accent",
|
|
679
|
+
isSelectable && !isCurrentMonth && "text-muted-foreground hover:bg-accent",
|
|
680
|
+
!isSelectable && isCurrentMonth && "text-muted-foreground/40",
|
|
681
|
+
!isSelectable && !isCurrentMonth && "text-muted-foreground/30",
|
|
682
|
+
isSelected && "bg-blue-500 font-semibold text-white shadow-sm"
|
|
683
|
+
),
|
|
684
|
+
children: d.getDate()
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
},
|
|
688
|
+
ds
|
|
689
|
+
);
|
|
690
|
+
}) })
|
|
583
691
|
] }),
|
|
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
692
|
selectedDate ? /* @__PURE__ */ jsxs("p", { className: "mt-1 text-center text-[10px] text-muted-foreground", children: [
|
|
619
693
|
"Selected ",
|
|
620
694
|
formatDateLabel(selectedDate)
|
|
@@ -627,7 +701,7 @@ function DayRangeCalendar({
|
|
|
627
701
|
startDate,
|
|
628
702
|
endDate,
|
|
629
703
|
onChange,
|
|
630
|
-
minDate =
|
|
704
|
+
minDate = currentWeekStart(),
|
|
631
705
|
flat = false
|
|
632
706
|
}) {
|
|
633
707
|
const initDate = /* @__PURE__ */ new Date((startDate || minDate) + "T00:00:00");
|
|
@@ -680,31 +754,6 @@ function DayRangeCalendar({
|
|
|
680
754
|
flat ? "gap-3" : "gap-1 rounded-2xl border border-border bg-card p-3"
|
|
681
755
|
),
|
|
682
756
|
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
757
|
/* @__PURE__ */ jsx(
|
|
709
758
|
"div",
|
|
710
759
|
{
|
|
@@ -733,57 +782,85 @@ function DayRangeCalendar({
|
|
|
733
782
|
] })
|
|
734
783
|
}
|
|
735
784
|
),
|
|
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
|
-
|
|
785
|
+
/* @__PURE__ */ jsxs("div", { className: calendarBlockClass, children: [
|
|
786
|
+
/* @__PURE__ */ jsxs("div", { className: calendarNavRowClass, children: [
|
|
787
|
+
/* @__PURE__ */ jsx(
|
|
788
|
+
"button",
|
|
789
|
+
{
|
|
790
|
+
type: "button",
|
|
791
|
+
onClick: prevMonth,
|
|
792
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
793
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
|
|
794
|
+
}
|
|
795
|
+
),
|
|
796
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
|
|
797
|
+
MONTH_NAMES[calMonth],
|
|
798
|
+
" ",
|
|
799
|
+
calYear
|
|
800
|
+
] }),
|
|
801
|
+
/* @__PURE__ */ jsx(
|
|
802
|
+
"button",
|
|
803
|
+
{
|
|
804
|
+
type: "button",
|
|
805
|
+
onClick: nextMonth,
|
|
806
|
+
className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
|
|
807
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
] }),
|
|
811
|
+
/* @__PURE__ */ jsx("div", { className: calendarWeekdayRowClass, children: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => /* @__PURE__ */ jsx("span", { className: calendarWeekdayCellClass, children: d }, d)) }),
|
|
812
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((d, i) => {
|
|
813
|
+
const ds = dayStr(d);
|
|
814
|
+
const isCurrentMonth = d.getMonth() === calMonth;
|
|
815
|
+
const isSelectable = ds >= minDate;
|
|
816
|
+
const inRange = isBillableDay(ds, dispStart, dispEnd);
|
|
817
|
+
const isStart = ds === dispStart;
|
|
818
|
+
const isEnd = ds === lastBillableDay && dispEnd && diffDays(dispStart, dispEnd) > 1;
|
|
819
|
+
const isSingle = dispStart && dispEnd && diffDays(dispStart, dispEnd) === 1 && ds === dispStart;
|
|
820
|
+
return /* @__PURE__ */ jsxs(
|
|
821
|
+
"button",
|
|
822
|
+
{
|
|
823
|
+
type: "button",
|
|
824
|
+
disabled: !isSelectable,
|
|
825
|
+
onMouseEnter: () => isSelectable && setHoverDay(ds),
|
|
826
|
+
onMouseLeave: () => setHoverDay(null),
|
|
827
|
+
onClick: () => isSelectable && handleClick(ds),
|
|
828
|
+
className: cn(
|
|
829
|
+
"relative flex h-9 items-center justify-center",
|
|
830
|
+
cellGridLines(i, days.length, 7),
|
|
831
|
+
!isSelectable && "cursor-not-allowed"
|
|
832
|
+
),
|
|
833
|
+
children: [
|
|
834
|
+
inRange && !isSingle ? /* @__PURE__ */ jsx(
|
|
835
|
+
"span",
|
|
836
|
+
{
|
|
837
|
+
className: cn(
|
|
838
|
+
"absolute inset-y-1.5 bg-blue-100",
|
|
839
|
+
isStart ? "left-1/2 right-0 rounded-l-full" : isEnd ? "left-0 right-1/2 rounded-r-full" : "inset-x-0"
|
|
840
|
+
)
|
|
841
|
+
}
|
|
842
|
+
) : null,
|
|
843
|
+
/* @__PURE__ */ jsx(
|
|
844
|
+
"span",
|
|
845
|
+
{
|
|
846
|
+
className: cn(
|
|
847
|
+
"relative z-10 flex h-8 w-8 items-center justify-center rounded-full text-[11px] transition-colors",
|
|
848
|
+
!inRange && isSelectable && isCurrentMonth && "text-foreground hover:bg-accent",
|
|
849
|
+
!inRange && isSelectable && !isCurrentMonth && "text-muted-foreground hover:bg-accent",
|
|
850
|
+
!isSelectable && isCurrentMonth && "text-muted-foreground/40",
|
|
851
|
+
!isSelectable && !isCurrentMonth && "text-muted-foreground/30",
|
|
852
|
+
inRange && "font-medium text-blue-700 dark:text-blue-300",
|
|
853
|
+
(isStart || isEnd || isSingle) && "bg-blue-500 font-semibold text-white shadow-sm"
|
|
854
|
+
),
|
|
855
|
+
children: d.getDate()
|
|
856
|
+
}
|
|
857
|
+
)
|
|
858
|
+
]
|
|
859
|
+
},
|
|
860
|
+
ds
|
|
861
|
+
);
|
|
862
|
+
}) })
|
|
863
|
+
] }),
|
|
787
864
|
/* @__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
865
|
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
789
866
|
formatDateLabel(startDate),
|
|
@@ -807,30 +884,24 @@ function DayPeriodPicker({
|
|
|
807
884
|
const oneDay = booking.oneDayBooking ?? true;
|
|
808
885
|
const dayCount = oneDay ? 1 : diffDays(booking.startDate, booking.endDate);
|
|
809
886
|
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",
|
|
887
|
+
/* @__PURE__ */ jsx(
|
|
888
|
+
CheckboxField,
|
|
815
889
|
{
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
890
|
+
size: "sm",
|
|
891
|
+
label: "Select a date range",
|
|
892
|
+
className: "w-fit items-center",
|
|
893
|
+
checked: !oneDay,
|
|
894
|
+
onCheckedChange: (checked) => {
|
|
895
|
+
const start = booking.startDate || todayLocalYmd();
|
|
896
|
+
if (!checked) {
|
|
820
897
|
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 });
|
|
898
|
+
return;
|
|
824
899
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
children: label
|
|
831
|
-
},
|
|
832
|
-
String(key)
|
|
833
|
-
)) }),
|
|
900
|
+
const end = booking.endDate && booking.endDate > start && diffDays(start, booking.endDate) > 1 ? booking.endDate : addDays(start, 7);
|
|
901
|
+
onChange({ oneDayBooking: false, startDate: start, endDate: end });
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
),
|
|
834
905
|
oneDay ? /* @__PURE__ */ jsx(
|
|
835
906
|
DayCalendarPicker,
|
|
836
907
|
{
|
|
@@ -860,17 +931,149 @@ function DayPeriodPicker({
|
|
|
860
931
|
] }) : null
|
|
861
932
|
] });
|
|
862
933
|
}
|
|
934
|
+
function HourlyTimeRangeFields({
|
|
935
|
+
fromTime,
|
|
936
|
+
scheduleEndTime,
|
|
937
|
+
chargeBy,
|
|
938
|
+
timeFormat,
|
|
939
|
+
toOptions,
|
|
940
|
+
minTo,
|
|
941
|
+
onFromChange,
|
|
942
|
+
onToChange,
|
|
943
|
+
vertical = false
|
|
944
|
+
}) {
|
|
945
|
+
const fields = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
946
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
947
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "From" }),
|
|
948
|
+
/* @__PURE__ */ jsx(TimeField, { value: fromTime, onChange: onFromChange, timeFormat })
|
|
949
|
+
] }),
|
|
950
|
+
vertical ? /* @__PURE__ */ jsx("div", { className: "h-px bg-border", "aria-hidden": true }) : null,
|
|
951
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
952
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "To" }),
|
|
953
|
+
chargeBy === "hours" ? /* @__PURE__ */ jsx(
|
|
954
|
+
TimeField,
|
|
955
|
+
{
|
|
956
|
+
value: scheduleEndTime,
|
|
957
|
+
onChange: onToChange,
|
|
958
|
+
timeFormat,
|
|
959
|
+
allowedTimes: toOptions,
|
|
960
|
+
disabled: toOptions.length === 0,
|
|
961
|
+
placeholder: toOptions.length === 0 ? "No later times" : "Select time"
|
|
962
|
+
}
|
|
963
|
+
) : /* @__PURE__ */ jsx(
|
|
964
|
+
TimeField,
|
|
965
|
+
{
|
|
966
|
+
value: scheduleEndTime,
|
|
967
|
+
onChange: onToChange,
|
|
968
|
+
timeFormat,
|
|
969
|
+
minTime: minTo,
|
|
970
|
+
disabled: !minTo,
|
|
971
|
+
placeholder: minTo ? "Select time" : "No later times"
|
|
972
|
+
}
|
|
973
|
+
)
|
|
974
|
+
] })
|
|
975
|
+
] });
|
|
976
|
+
if (vertical) {
|
|
977
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: fields });
|
|
978
|
+
}
|
|
979
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: fields });
|
|
980
|
+
}
|
|
863
981
|
function HourlySchedulePicker({
|
|
864
982
|
scheduleDate,
|
|
865
983
|
scheduleTime,
|
|
984
|
+
scheduleEndTime = "",
|
|
866
985
|
onChange,
|
|
867
986
|
timeFormat = "24h",
|
|
868
|
-
flat = true
|
|
987
|
+
flat = true,
|
|
988
|
+
chargeBy = "hours",
|
|
989
|
+
splitLayout = false,
|
|
990
|
+
stackLayout = false
|
|
869
991
|
}) {
|
|
870
|
-
const [step, setStep] = useState(
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
992
|
+
const [step, setStep] = useState("date");
|
|
993
|
+
const fromTime = scheduleTime || "09:00";
|
|
994
|
+
const toOptions = chargeBy === "hours" ? hourlyEndTimes(fromTime) : [];
|
|
995
|
+
const minTo = chargeBy === "minutes" ? addMinutesToHm(fromTime, 1) ?? void 0 : void 0;
|
|
996
|
+
const handleFromChange = (nextFrom) => {
|
|
997
|
+
onChange({
|
|
998
|
+
scheduleTime: nextFrom,
|
|
999
|
+
scheduleEndTime: snapScheduleEndTime(nextFrom, scheduleEndTime, chargeBy)
|
|
1000
|
+
});
|
|
1001
|
+
};
|
|
1002
|
+
const handleDateChange = (v) => {
|
|
1003
|
+
const nextFrom = scheduleTime || "09:00";
|
|
1004
|
+
onChange({
|
|
1005
|
+
scheduleDate: v,
|
|
1006
|
+
scheduleTime: nextFrom,
|
|
1007
|
+
scheduleEndTime: scheduleEndTime || snapScheduleEndTime(nextFrom, "", chargeBy) || "10:00"
|
|
1008
|
+
});
|
|
1009
|
+
};
|
|
1010
|
+
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: [
|
|
1011
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Scheduled for" }),
|
|
1012
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
|
|
1013
|
+
formatDateLabel(scheduleDate),
|
|
1014
|
+
" \xB7 ",
|
|
1015
|
+
formatTimeForDisplay(fromTime, timeFormat),
|
|
1016
|
+
" \u2192",
|
|
1017
|
+
" ",
|
|
1018
|
+
formatTimeForDisplay(scheduleEndTime, timeFormat)
|
|
1019
|
+
] })
|
|
1020
|
+
] }) }) : null;
|
|
1021
|
+
const timeFields = /* @__PURE__ */ jsx(
|
|
1022
|
+
HourlyTimeRangeFields,
|
|
1023
|
+
{
|
|
1024
|
+
fromTime,
|
|
1025
|
+
scheduleEndTime,
|
|
1026
|
+
chargeBy,
|
|
1027
|
+
timeFormat,
|
|
1028
|
+
toOptions,
|
|
1029
|
+
minTo,
|
|
1030
|
+
onFromChange: handleFromChange,
|
|
1031
|
+
onToChange: (v) => onChange({ scheduleEndTime: v }),
|
|
1032
|
+
vertical: splitLayout || stackLayout
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
if (stackLayout) {
|
|
1036
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
1037
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
1038
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1039
|
+
/* @__PURE__ */ jsx(
|
|
1040
|
+
DayCalendarPicker,
|
|
1041
|
+
{
|
|
1042
|
+
flat,
|
|
1043
|
+
selectedDate: scheduleDate,
|
|
1044
|
+
minDate: todayLocalYmd(),
|
|
1045
|
+
onChange: handleDateChange
|
|
1046
|
+
}
|
|
1047
|
+
)
|
|
1048
|
+
] }),
|
|
1049
|
+
timeFields,
|
|
1050
|
+
summary
|
|
1051
|
+
] });
|
|
1052
|
+
}
|
|
1053
|
+
if (splitLayout) {
|
|
1054
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
1055
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5 sm:flex-row sm:items-start", children: [
|
|
1056
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1057
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1058
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx(
|
|
1059
|
+
DayCalendarPicker,
|
|
1060
|
+
{
|
|
1061
|
+
flat,
|
|
1062
|
+
selectedDate: scheduleDate,
|
|
1063
|
+
minDate: todayLocalYmd(),
|
|
1064
|
+
onChange: handleDateChange
|
|
1065
|
+
}
|
|
1066
|
+
) })
|
|
1067
|
+
] }),
|
|
1068
|
+
/* @__PURE__ */ jsx("div", { className: "hidden w-px shrink-0 self-stretch bg-border sm:block", "aria-hidden": true }),
|
|
1069
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-[9.5rem] shrink-0 sm:w-44", children: [
|
|
1070
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Time" }),
|
|
1071
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: timeFields })
|
|
1072
|
+
] })
|
|
1073
|
+
] }),
|
|
1074
|
+
summary
|
|
1075
|
+
] });
|
|
1076
|
+
}
|
|
874
1077
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
875
1078
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
|
|
876
1079
|
/* @__PURE__ */ jsx(
|
|
@@ -889,7 +1092,7 @@ function HourlySchedulePicker({
|
|
|
889
1092
|
{
|
|
890
1093
|
className: cn(
|
|
891
1094
|
step === "time" && "font-semibold text-blue-600 dark:text-blue-400",
|
|
892
|
-
|
|
1095
|
+
step !== "time" && "text-muted-foreground/60"
|
|
893
1096
|
),
|
|
894
1097
|
children: "Time"
|
|
895
1098
|
}
|
|
@@ -902,9 +1105,9 @@ function HourlySchedulePicker({
|
|
|
902
1105
|
{
|
|
903
1106
|
flat,
|
|
904
1107
|
selectedDate: scheduleDate,
|
|
905
|
-
minDate:
|
|
1108
|
+
minDate: todayLocalYmd(),
|
|
906
1109
|
onChange: (v) => {
|
|
907
|
-
|
|
1110
|
+
handleDateChange(v);
|
|
908
1111
|
setStep("time");
|
|
909
1112
|
}
|
|
910
1113
|
}
|
|
@@ -927,29 +1130,110 @@ function HourlySchedulePicker({
|
|
|
927
1130
|
]
|
|
928
1131
|
}
|
|
929
1132
|
),
|
|
1133
|
+
timeFields,
|
|
1134
|
+
summary
|
|
1135
|
+
] })
|
|
1136
|
+
] });
|
|
1137
|
+
}
|
|
1138
|
+
function ServiceNeededAtPicker({
|
|
1139
|
+
scheduleDate,
|
|
1140
|
+
scheduleTime,
|
|
1141
|
+
onChange,
|
|
1142
|
+
timeFormat = "24h",
|
|
1143
|
+
flat = true,
|
|
1144
|
+
stackLayout = true,
|
|
1145
|
+
minDate = todayLocalYmd(),
|
|
1146
|
+
preparationMinutes = 0
|
|
1147
|
+
}) {
|
|
1148
|
+
const neededTime = scheduleTime || "09:00";
|
|
1149
|
+
const todayYmd = todayLocalYmd();
|
|
1150
|
+
const minTimeToday = preparationMinutes > 0 && scheduleDate === todayYmd ? minNeededTimeHm(preparationMinutes) : void 0;
|
|
1151
|
+
const syncNeeded = (date, time) => {
|
|
1152
|
+
let nextTime = time;
|
|
1153
|
+
if (preparationMinutes > 0 && date === todayYmd) {
|
|
1154
|
+
const floor = minNeededTimeHm(preparationMinutes);
|
|
1155
|
+
if (floor && nextTime < floor) nextTime = floor;
|
|
1156
|
+
}
|
|
1157
|
+
onChange({
|
|
1158
|
+
scheduleDate: date,
|
|
1159
|
+
scheduleTime: nextTime,
|
|
1160
|
+
scheduleEndTime: nextTime,
|
|
1161
|
+
startDate: date,
|
|
1162
|
+
endDate: date
|
|
1163
|
+
});
|
|
1164
|
+
};
|
|
1165
|
+
const handleDateChange = (v) => syncNeeded(v, neededTime);
|
|
1166
|
+
const handleTimeChange = (v) => {
|
|
1167
|
+
if (!scheduleDate) return;
|
|
1168
|
+
syncNeeded(scheduleDate, v);
|
|
1169
|
+
};
|
|
1170
|
+
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: [
|
|
1171
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Needed at" }),
|
|
1172
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
|
|
1173
|
+
formatDateLabel(scheduleDate),
|
|
1174
|
+
" \xB7 ",
|
|
1175
|
+
formatTimeForDisplay(neededTime, timeFormat)
|
|
1176
|
+
] })
|
|
1177
|
+
] }) }) : null;
|
|
1178
|
+
const timeField = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
1179
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Needed at" }),
|
|
1180
|
+
/* @__PURE__ */ jsx(
|
|
1181
|
+
TimeField,
|
|
1182
|
+
{
|
|
1183
|
+
value: neededTime,
|
|
1184
|
+
onChange: handleTimeChange,
|
|
1185
|
+
timeFormat,
|
|
1186
|
+
minTime: minTimeToday,
|
|
1187
|
+
disabled: Boolean(minTimeToday && !minTimeToday),
|
|
1188
|
+
placeholder: minTimeToday ? "Select time" : void 0
|
|
1189
|
+
}
|
|
1190
|
+
)
|
|
1191
|
+
] });
|
|
1192
|
+
if (stackLayout) {
|
|
1193
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-5", flat ? "" : "rounded-2xl border border-border bg-card p-4"), children: [
|
|
930
1194
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
931
|
-
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "
|
|
932
|
-
/* @__PURE__ */ jsx(
|
|
933
|
-
|
|
1195
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1196
|
+
/* @__PURE__ */ jsx(
|
|
1197
|
+
DayCalendarPicker,
|
|
934
1198
|
{
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1199
|
+
flat,
|
|
1200
|
+
selectedDate: scheduleDate,
|
|
1201
|
+
minDate,
|
|
1202
|
+
onChange: handleDateChange
|
|
938
1203
|
}
|
|
939
|
-
)
|
|
1204
|
+
)
|
|
940
1205
|
] }),
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1206
|
+
timeField,
|
|
1207
|
+
summary
|
|
1208
|
+
] });
|
|
1209
|
+
}
|
|
1210
|
+
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: [
|
|
1211
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1212
|
+
/* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
|
|
1213
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx(
|
|
1214
|
+
DayCalendarPicker,
|
|
1215
|
+
{
|
|
1216
|
+
flat,
|
|
1217
|
+
selectedDate: scheduleDate,
|
|
1218
|
+
minDate,
|
|
1219
|
+
onChange: handleDateChange
|
|
1220
|
+
}
|
|
1221
|
+
) })
|
|
1222
|
+
] }),
|
|
1223
|
+
/* @__PURE__ */ jsx("div", { className: "hidden w-px shrink-0 self-stretch bg-border sm:block", "aria-hidden": true }),
|
|
1224
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-[9.5rem] shrink-0 sm:w-44", children: timeField }),
|
|
1225
|
+
summary ? /* @__PURE__ */ jsx("div", { className: "w-full", children: summary }) : null
|
|
950
1226
|
] });
|
|
951
1227
|
}
|
|
1228
|
+
function minNeededTimeHm(prepMinutes, now = /* @__PURE__ */ new Date()) {
|
|
1229
|
+
const earliest = new Date(now.getTime() + prepMinutes * 6e4);
|
|
1230
|
+
if (earliest.getSeconds() > 0 || earliest.getMilliseconds() > 0) {
|
|
1231
|
+
earliest.setMinutes(earliest.getMinutes() + 1);
|
|
1232
|
+
}
|
|
1233
|
+
earliest.setSeconds(0, 0);
|
|
1234
|
+
return `${String(earliest.getHours()).padStart(2, "0")}:${String(earliest.getMinutes()).padStart(2, "0")}`;
|
|
1235
|
+
}
|
|
952
1236
|
|
|
953
|
-
export { DayCalendarPicker, DayPeriodPicker, DayRangeCalendar, HourlySchedulePicker, MonthPeriodPicker, MonthRangeCalendar, ScheduleFieldLabel, ScheduleInfoBanner, ScheduleStepper, WeekCalendarPicker, WeekPeriodPicker };
|
|
1237
|
+
export { DayCalendarPicker, DayPeriodPicker, DayRangeCalendar, HourlySchedulePicker, MonthPeriodPicker, MonthRangeCalendar, ScheduleFieldLabel, ScheduleInfoBanner, ScheduleStepper, ServiceNeededAtPicker, WeekCalendarPicker, WeekPeriodPicker };
|
|
954
1238
|
//# sourceMappingURL=booking-schedule-calendars.js.map
|
|
955
1239
|
//# sourceMappingURL=booking-schedule-calendars.js.map
|