@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.
Files changed (37) hide show
  1. package/dist/analog-clock-picker.js +379 -3
  2. package/dist/analog-clock-picker.js.map +1 -1
  3. package/dist/api.d.ts +9 -1
  4. package/dist/api.js +84 -1
  5. package/dist/api.js.map +1 -1
  6. package/dist/booking-schedule-calendars.d.ts +33 -35
  7. package/dist/booking-schedule-calendars.js +716 -432
  8. package/dist/booking-schedule-calendars.js.map +1 -1
  9. package/dist/booking-schedule.d.ts +71 -0
  10. package/dist/booking-schedule.js +3 -0
  11. package/dist/booking-schedule.js.map +1 -0
  12. package/dist/booking-space-layout-picker.d.ts +20 -0
  13. package/dist/booking-space-layout-picker.js +68 -0
  14. package/dist/booking-space-layout-picker.js.map +1 -0
  15. package/dist/chat-stick-to-bottom.js +1 -1
  16. package/dist/chunk-CKCXQBHQ.js +764 -0
  17. package/dist/chunk-CKCXQBHQ.js.map +1 -0
  18. package/dist/{chunk-H3CNZCYU.js → chunk-F7MU5T46.js} +186 -62
  19. package/dist/chunk-F7MU5T46.js.map +1 -0
  20. package/dist/chunk-OTSA5ROL.js +209 -0
  21. package/dist/chunk-OTSA5ROL.js.map +1 -0
  22. package/dist/file-viewer-dialog.js +1 -1
  23. package/dist/floor-switcher.js +1 -1
  24. package/dist/image-picker-field.d.ts +2 -1
  25. package/dist/image-picker-field.js +77 -28
  26. package/dist/image-picker-field.js.map +1 -1
  27. package/dist/layout-canvas-view-DOjyUNJH.d.ts +65 -0
  28. package/dist/layout-canvas-view.d.ts +2 -57
  29. package/dist/layout-canvas-view.js +7 -741
  30. package/dist/layout-canvas-view.js.map +1 -1
  31. package/dist/platform-support-message-composer.js +1 -1
  32. package/dist/ui.d.ts +19 -10
  33. package/dist/ui.js +1 -1
  34. package/package.json +9 -2
  35. package/dist/chunk-34332W2P.js +0 -381
  36. package/dist/chunk-34332W2P.js.map +0 -1
  37. package/dist/chunk-H3CNZCYU.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { AnalogClockPicker } from './chunk-34332W2P.js';
2
- import { formatTimeForDisplay } from './chunk-H3CNZCYU.js';
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.toISOString().split("T")[0];
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((MIN_WEEK_START || PRICING_TODAY) + "T00:00:00");
157
- const selectedMonStr = weekFrom || null;
158
- const hoverMonStr = hoverMon;
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__ */ jsxs("div", { className: "mb-1 flex items-center justify-between px-1", children: [
182
- /* @__PURE__ */ jsx(
183
- "button",
184
- {
185
- type: "button",
186
- onClick: prevMonth,
187
- className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
188
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
189
- }
190
- ),
191
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
192
- MONTH_NAMES[calMonth],
193
- " ",
194
- calYear
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
- "button",
198
- {
199
- type: "button",
200
- onClick: nextMonth,
201
- className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-muted-foreground",
202
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
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("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)) }),
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 = MIN_MONTH || `${(/* @__PURE__ */ new Date()).getFullYear()}-01`;
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__ */ jsxs("div", { className: cn("flex items-center justify-between", flat ? "px-1" : "px-4 pb-2 pt-4"), children: [
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 a month to set the start"
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
- "Now click to set the end month",
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__ */ jsx(
345
- "div",
346
- {
347
- className: cn("grid grid-cols-4", flat ? "gap-2 py-1" : "px-2 pb-3 pt-1"),
348
- style: flat ? void 0 : { gap: 0 },
349
- children: ABBR.map((label, i) => {
350
- const ymStr = toYm(calYear, i);
351
- const disabled = ymStr < minYm;
352
- const inRange = !disabled && ymStr >= dispFrom && ymStr <= dispTo;
353
- const isFrom = ymStr === dispFrom;
354
- const isTo = ymStr === dispTo;
355
- const showStrip = inRange && !isSingle;
356
- const sLeft = isFrom ? "left-1/2" : "left-0";
357
- const sRight = isTo ? "right-1/2" : "right-0";
358
- const filled = (isFrom || isTo) && !disabled;
359
- return /* @__PURE__ */ jsxs(
360
- "div",
361
- {
362
- className: cn(
363
- "relative",
364
- flat ? "h-14" : "h-12",
365
- disabled ? "cursor-not-allowed" : "cursor-pointer"
366
- ),
367
- onMouseEnter: () => !disabled && setHoverYm(ymStr),
368
- onMouseLeave: () => setHoverYm(null),
369
- onClick: () => handleClick(ymStr),
370
- children: [
371
- showStrip ? /* @__PURE__ */ jsx("div", { className: `absolute bottom-1.5 top-1.5 bg-blue-100 ${sLeft} ${sRight}` }) : null,
372
- /* @__PURE__ */ jsx(
373
- "div",
374
- {
375
- className: cn(
376
- "relative z-10 flex w-full items-center justify-center",
377
- flat ? "h-14" : "h-12"
378
- ),
379
- children: /* @__PURE__ */ jsx(
380
- "span",
381
- {
382
- className: `flex h-9 w-9 items-center justify-center rounded-full text-xs font-medium transition-all duration-100
383
- ${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"}`,
384
- children: label
385
- }
386
- )
387
- }
388
- )
389
- ]
390
- },
391
- i
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: phase === "from" && actualCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { 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: "No months selected" })
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
- IS_PARTIAL_MONTH ? /* @__PURE__ */ jsxs(ScheduleInfoBanner, { children: [
438
- "Today is ",
439
- new Date(PRICING_TODAY).toLocaleDateString("en-US", { month: "long", day: "numeric" }),
440
- ". Current month is partially elapsed \u2014 earliest bookable month is",
441
- " ",
442
- /* @__PURE__ */ jsx("strong", { children: monthLabel(MIN_MONTH) }),
443
- "."
444
- ] }) : null,
445
- /* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Select month range" }),
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
- isCurrentWeekPartial ? /* @__PURE__ */ jsxs(ScheduleInfoBanner, { children: [
466
- "This week started on a ",
467
- new Date(PRICING_TODAY).toLocaleDateString("en-US", { weekday: "long" }),
468
- " \u2014 earliest full week begins",
469
- " ",
470
- /* @__PURE__ */ jsx("strong", { children: new Date(MIN_WEEK_START).toLocaleDateString("en-US", {
471
- month: "long",
472
- day: "numeric"
473
- }) }),
474
- "."
475
- ] }) : null,
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: "Starting week" }),
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
- onChange: (v) => onChange({ weekFrom: v })
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 = PRICING_TODAY,
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: "mb-1 flex items-center justify-between px-1", children: [
560
- /* @__PURE__ */ jsx(
561
- "button",
562
- {
563
- type: "button",
564
- onClick: prevMonth,
565
- className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
566
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 12, strokeWidth: 2.25 })
567
- }
568
- ),
569
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold text-muted-foreground", children: [
570
- MONTH_NAMES[calMonth],
571
- " ",
572
- calYear
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
- "button",
576
- {
577
- type: "button",
578
- onClick: nextMonth,
579
- className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent",
580
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 12, strokeWidth: 2.25 })
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 = PRICING_TODAY,
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__ */ 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)) }),
737
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: days.map((d) => {
738
- const ds = dayStr(d);
739
- const isCurrentMonth = d.getMonth() === calMonth;
740
- const isSelectable = ds >= minDate;
741
- const inRange = isBillableDay(ds, dispStart, dispEnd);
742
- const isStart = ds === dispStart;
743
- const isEnd = ds === lastBillableDay && dispEnd && diffDays(dispStart, dispEnd) > 1;
744
- const isSingle = dispStart && dispEnd && diffDays(dispStart, dispEnd) === 1 && ds === dispStart;
745
- return /* @__PURE__ */ jsxs(
746
- "button",
747
- {
748
- type: "button",
749
- disabled: !isSelectable,
750
- onMouseEnter: () => isSelectable && setHoverDay(ds),
751
- onMouseLeave: () => setHoverDay(null),
752
- onClick: () => isSelectable && handleClick(ds),
753
- className: cn(
754
- "relative flex h-9 items-center justify-center",
755
- !isSelectable && "cursor-not-allowed"
756
- ),
757
- children: [
758
- inRange && !isSingle ? /* @__PURE__ */ jsx(
759
- "span",
760
- {
761
- className: cn(
762
- "absolute inset-y-1.5 bg-blue-100",
763
- isStart ? "left-1/2 right-0 rounded-l-full" : isEnd ? "left-0 right-1/2 rounded-r-full" : "inset-x-0"
764
- )
765
- }
766
- ) : null,
767
- /* @__PURE__ */ jsx(
768
- "span",
769
- {
770
- className: cn(
771
- "relative z-10 flex h-8 w-8 items-center justify-center rounded-full text-[11px] transition-colors",
772
- (isStart || isEnd || isSingle) && "bg-blue-500 font-semibold text-white shadow-sm",
773
- !isStart && !isEnd && !isSingle && inRange && "font-medium text-blue-700 dark:text-blue-400",
774
- !inRange && isSelectable && isCurrentMonth && "text-foreground hover:bg-accent",
775
- !inRange && isSelectable && !isCurrentMonth && "text-muted-foreground hover:bg-accent",
776
- !isSelectable && isCurrentMonth && "text-muted-foreground/40",
777
- !isSelectable && !isCurrentMonth && "text-muted-foreground/30"
778
- ),
779
- children: d.getDate()
780
- }
781
- )
782
- ]
783
- },
784
- ds
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("div", { className: "flex gap-2", children: [
811
- { key: true, label: "Single day" },
812
- { key: false, label: "Multiple days" }
813
- ].map(({ key, label }) => /* @__PURE__ */ jsx(
814
- "button",
887
+ /* @__PURE__ */ jsx(
888
+ CheckboxField,
815
889
  {
816
- type: "button",
817
- onClick: () => {
818
- const start = booking.startDate || PRICING_TODAY;
819
- if (key) {
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
- } else {
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
- className: cn(
827
- "flex h-9 flex-1 items-center justify-center rounded-xl border text-sm font-medium transition-colors",
828
- oneDay === key ? "border-blue-400 bg-blue-50 text-blue-600 dark:bg-blue-950/40 dark:text-blue-400" : "border-border bg-card text-muted-foreground hover:bg-accent"
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(() => scheduleDate ? "time" : "date");
871
- useEffect(() => {
872
- if (!scheduleDate) setStep("date");
873
- }, [scheduleDate]);
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
- !scheduleDate && "text-muted-foreground/60"
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: PRICING_TODAY,
1108
+ minDate: todayLocalYmd(),
906
1109
  onChange: (v) => {
907
- onChange({ scheduleDate: v });
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: "Pick a time" }),
932
- /* @__PURE__ */ jsx("div", { className: "flex justify-center rounded-2xl border border-border bg-card py-6", children: /* @__PURE__ */ jsx(
933
- AnalogClockPicker,
1195
+ /* @__PURE__ */ jsx(ScheduleFieldLabel, { children: "Date" }),
1196
+ /* @__PURE__ */ jsx(
1197
+ DayCalendarPicker,
934
1198
  {
935
- value: scheduleTime,
936
- onChange: (v) => onChange({ scheduleTime: v }),
937
- timeFormat
1199
+ flat,
1200
+ selectedDate: scheduleDate,
1201
+ minDate,
1202
+ onChange: handleDateChange
938
1203
  }
939
- ) })
1204
+ )
940
1205
  ] }),
941
- scheduleDate && scheduleTime ? /* @__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: [
942
- /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: "Scheduled for" }),
943
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
944
- formatDateLabel(scheduleDate),
945
- " \xB7 ",
946
- formatTimeForDisplay(scheduleTime, timeFormat)
947
- ] })
948
- ] }) }) : null
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