@pisell/materials 1.8.34 → 1.8.36
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/build/lowcode/assets-daily.json +11 -11
- package/build/lowcode/assets-dev.json +2 -2
- package/build/lowcode/assets-prod.json +11 -11
- package/build/lowcode/index.js +1 -1
- package/build/lowcode/meta.js +2 -2
- package/build/lowcode/preview.js +1 -1
- package/build/lowcode/render/default/view.css +1 -1
- package/build/lowcode/render/default/view.js +35 -35
- package/build/lowcode/view.css +1 -1
- package/build/lowcode/view.js +35 -35
- package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.js +25 -10
- package/es/components/pisellFloorMapLayout/appearance/floorMapAppearance.d.ts +4 -0
- package/es/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +22 -0
- package/es/components/pisellFloorMapLayout/components/EditableItemLayer.js +3 -2
- package/es/components/pisellFloorMapLayout/components/ItemLayer.js +7 -4
- package/es/components/pisellFloorMapLayout/components/ItemLayer.less +10 -0
- package/es/components/pisellFloorMapLayout/components/ViewControls.js +6 -5
- package/es/components/pisellFloorMapLayout/components/ViewControls.less +29 -2
- package/es/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +1 -0
- package/es/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +3 -1
- package/es/components/pisellFloorMapLayout/types.d.ts +21 -2
- package/es/components/pisellRecordBoard/index.d.ts +1 -1
- package/es/components/pisellRecordBoard/shellFrame/Calendar/BookingCalendar.js +280 -230
- package/es/components/pisellRecordBoard/shellFrame/Calendar/const/bookingCalendarConstants.js +18 -1
- package/es/components/pisellRecordBoard/shellFrame/Calendar/hooks/useBookingCalendarDayOverlayLayout.js +74 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/modal/BookingCalendarBlockPanel.js +61 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/modal/BookingCalendarOverflowListLayer.js +26 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/utils/bookingCalendarDateUtils.js +11 -1
- package/es/components/pisellRecordBoard/shellFrame/Calendar/utils/bookingCalendarSelectionUtils.js +17 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/BookingCalendarDaySelectionBar.js +12 -8
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/BookingCalendarDayView.js +3 -12
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/BookingCalendarResourceView.js +612 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/day/useBookingCalendarDayHourRemaining.js +33 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/day/useBookingCalendarDaySelection.js +37 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/resource/useBookingCalendarResourceCellToggle.js +18 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/resource/useBookingCalendarResourceDayHeaderRemaining.js +33 -0
- package/es/components/pisellRecordBoard/shellFrame/Calendar/view/useBookingCalendarGoToDayFromCell.js +29 -0
- package/es/components/pisellRecordBoard/types.d.ts +38 -6
- package/es/components/pisellReservationSchedule/PisellReservationSchedule.js +19 -3
- package/es/components/pisellReservationSchedule/PisellReservationSchedule.less +23 -0
- package/es/index.d.ts +2 -2
- package/es/locales/en-US.js +6 -5
- package/es/locales/ja.js +1 -0
- package/es/locales/pt.js +1 -0
- package/es/locales/zh-CN.js +1 -0
- package/es/locales/zh-TW.js +1 -0
- package/es/pisell-materials.tw.css +2413 -2
- package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.js +25 -10
- package/lib/components/pisellFloorMapLayout/appearance/floorMapAppearance.d.ts +4 -0
- package/lib/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +22 -0
- package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.js +3 -2
- package/lib/components/pisellFloorMapLayout/components/ItemLayer.js +7 -4
- package/lib/components/pisellFloorMapLayout/components/ItemLayer.less +10 -0
- package/lib/components/pisellFloorMapLayout/components/ViewControls.js +6 -5
- package/lib/components/pisellFloorMapLayout/components/ViewControls.less +29 -2
- package/lib/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +1 -0
- package/lib/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +3 -1
- package/lib/components/pisellFloorMapLayout/types.d.ts +21 -2
- package/lib/components/pisellRecordBoard/index.d.ts +1 -1
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/BookingCalendar.js +277 -227
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/const/bookingCalendarConstants.js +18 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/hooks/useBookingCalendarDayOverlayLayout.js +75 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/modal/BookingCalendarBlockPanel.js +63 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/modal/BookingCalendarOverflowListLayer.js +28 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/utils/bookingCalendarDateUtils.js +11 -1
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/utils/bookingCalendarSelectionUtils.js +17 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/BookingCalendarDaySelectionBar.js +12 -8
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/BookingCalendarDayView.js +3 -12
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/BookingCalendarResourceView.js +614 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/day/useBookingCalendarDayHourRemaining.js +34 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/day/useBookingCalendarDaySelection.js +38 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/resource/useBookingCalendarResourceCellToggle.js +19 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/resource/useBookingCalendarResourceDayHeaderRemaining.js +34 -0
- package/lib/components/pisellRecordBoard/shellFrame/Calendar/view/useBookingCalendarGoToDayFromCell.js +30 -0
- package/lib/components/pisellRecordBoard/types.d.ts +38 -6
- package/lib/components/pisellReservationSchedule/PisellReservationSchedule.js +19 -3
- package/lib/components/pisellReservationSchedule/PisellReservationSchedule.less +23 -0
- package/lib/index.d.ts +2 -2
- package/lib/locales/en-US.js +6 -5
- package/lib/locales/ja.js +1 -0
- package/lib/locales/pt.js +1 -0
- package/lib/locales/zh-CN.js +1 -0
- package/lib/locales/zh-TW.js +1 -0
- package/lib/pisell-materials.tw.css +2413 -2
- package/lowcode/pisell-record-board-calendar-view/meta.ts +8 -0
- package/package.json +1 -1
|
@@ -2,25 +2,33 @@ import { _objectSpread2 } from "../../../../_virtual/_@oxc-project_runtime@0.122
|
|
|
2
2
|
import { _asyncToGenerator } from "../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js";
|
|
3
3
|
import { buildCalendarTimelineSlotStartsFromRange } from "../../../pisellTimeNavigator/utils/index.js";
|
|
4
4
|
import { useCalendarPersist } from "./calendarPersistGuard.js";
|
|
5
|
-
import { DEFAULT_CALENDAR_TIMELINE
|
|
5
|
+
import { DEFAULT_CALENDAR_TIMELINE } from "./const/bookingCalendarConstants.js";
|
|
6
6
|
import { tCal } from "./calendarI18n.js";
|
|
7
|
-
import { addDays,
|
|
7
|
+
import { addDays, dayAndHourToDayjs, fmtDate, groupSelectedSlots, isFreeSelectionContiguousSameResource, isHourInTimelineSlots, monthCalendarDays, monthGrid, rangeDays, startOfWeek } from "./utils/bookingCalendarDateUtils.js";
|
|
8
8
|
import { MoveConfirmModal } from "./modal/MoveConfirmModal.js";
|
|
9
|
+
import { BookingCalendarBlockPanel } from "./modal/BookingCalendarBlockPanel.js";
|
|
9
10
|
import { buildGroupedCourtRows } from "./bookingCalendarViewShared.js";
|
|
10
|
-
import {
|
|
11
|
+
import { BookingCalendarOverflowListLayer } from "./modal/BookingCalendarOverflowListLayer.js";
|
|
11
12
|
import { BookingCalendarMonthView } from "./view/BookingCalendarMonthView.js";
|
|
12
13
|
import { BookingCalendarWeekView } from "./view/BookingCalendarWeekView.js";
|
|
13
14
|
import { BookingCalendarDayView } from "./view/BookingCalendarDayView.js";
|
|
15
|
+
import { BookingCalendarResourceView } from "./view/BookingCalendarResourceView.js";
|
|
16
|
+
import { BookingCalendarDaySelectionBar } from "./view/BookingCalendarDaySelectionBar.js";
|
|
17
|
+
import { isResourceSelectionContiguousSameCourt } from "./utils/bookingCalendarSelectionUtils.js";
|
|
18
|
+
import { useBookingCalendarDayOverlayLayout } from "./hooks/useBookingCalendarDayOverlayLayout.js";
|
|
19
|
+
import { useBookingCalendarDayHourRemaining } from "./view/day/useBookingCalendarDayHourRemaining.js";
|
|
20
|
+
import { useBookingCalendarDaySelection } from "./view/day/useBookingCalendarDaySelection.js";
|
|
21
|
+
import { useBookingCalendarResourceDayHeaderRemaining } from "./view/resource/useBookingCalendarResourceDayHeaderRemaining.js";
|
|
22
|
+
import { useBookingCalendarResourceCellToggle } from "./view/resource/useBookingCalendarResourceCellToggle.js";
|
|
23
|
+
import { useBookingCalendarGoToDayFromCell } from "./view/useBookingCalendarGoToDayFromCell.js";
|
|
14
24
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
15
|
-
import {
|
|
16
|
-
import { CloseOutlined } from "@ant-design/icons";
|
|
25
|
+
import { Spin, message } from "antd";
|
|
17
26
|
import "./BookingCalendar.less";
|
|
18
27
|
//#region src/components/pisellRecordBoard/shellFrame/Calendar/BookingCalendar.tsx
|
|
19
28
|
/**
|
|
20
29
|
* RecordBoard 预约日历:月 / 周 / 日三视图,资源分组与折叠、锁台、拖拽改期、日视图多 lane overlay。
|
|
21
30
|
* 受控:`calendarView`、`anchorDate`、`bookings` 等见 {@link RecordBoardCalendarProps};未传时由内部 state 驱动(便于 Story)。
|
|
22
31
|
*/
|
|
23
|
-
const { RangePicker } = DatePicker;
|
|
24
32
|
/** 看板日历根组件:内含主题 CSS 与 Spin,按 `view` 挂载月 / 周 / 日子组件 */
|
|
25
33
|
function BookingCalendar(props = {}) {
|
|
26
34
|
var _hourSlots$;
|
|
@@ -55,8 +63,10 @@ function BookingCalendar(props = {}) {
|
|
|
55
63
|
return Math.min(1440, n);
|
|
56
64
|
}, [props.slotStepMinutes]);
|
|
57
65
|
const slotStepHours = resolvedSlotMinutes / 60;
|
|
58
|
-
/**
|
|
66
|
+
/** 每槽一列固定为「原整点列」宽度,细粒度仅增加列数,由容器横向滚动承载(日/周等时间轴) */
|
|
59
67
|
const slotColWidth = 92;
|
|
68
|
+
/** 资源视图:按「日」分列,单列单独加宽 */
|
|
69
|
+
const resourceViewSlotColWidth = 102;
|
|
60
70
|
const timelinePxPerHour = 92 / slotStepHours;
|
|
61
71
|
/** 切换月/周/日:先 `onCalendarViewChange`,无受控回调时再 `setInternalView` */
|
|
62
72
|
const commitView = (v) => {
|
|
@@ -82,6 +92,9 @@ function BookingCalendar(props = {}) {
|
|
|
82
92
|
if (view === "month") {
|
|
83
93
|
rangeStart = new Date(a.getFullYear(), a.getMonth(), 1);
|
|
84
94
|
rangeEnd = new Date(a.getFullYear(), a.getMonth() + 1, 0);
|
|
95
|
+
} else if (view === "resource") {
|
|
96
|
+
rangeStart = new Date(a.getFullYear(), a.getMonth(), 1);
|
|
97
|
+
rangeEnd = new Date(a.getFullYear(), a.getMonth() + 1, 0);
|
|
85
98
|
} else if (view === "week") {
|
|
86
99
|
rangeStart = startOfWeek(a);
|
|
87
100
|
rangeEnd = addDays(rangeStart, 6);
|
|
@@ -102,6 +115,7 @@ function BookingCalendar(props = {}) {
|
|
|
102
115
|
]);
|
|
103
116
|
const [selectedFreeSlots, setSelectedFreeSlots] = useState([]);
|
|
104
117
|
const [selectedBlockSlots, setSelectedBlockSlots] = useState([]);
|
|
118
|
+
const [selectedResourceCells, setSelectedResourceCells] = useState([]);
|
|
105
119
|
const [focusedCourtId, setFocusedCourtId] = useState(null);
|
|
106
120
|
const [showBlockPanel, setShowBlockPanel] = useState(false);
|
|
107
121
|
const [blockFormName, setBlockFormName] = useState(() => tCal("pisell2.recordBoard.calendar.block.defaultTitle"));
|
|
@@ -125,11 +139,14 @@ function BookingCalendar(props = {}) {
|
|
|
125
139
|
* 供日视图将横向 scroll 定位到「当前时间」列。
|
|
126
140
|
*/
|
|
127
141
|
const [dayTimelineScrollToNowToken, setDayTimelineScrollToNowToken] = useState(0);
|
|
142
|
+
const [resourceMonthScrollToTodayToken, setResourceMonthScrollToTodayToken] = useState(0);
|
|
128
143
|
const prevCalendarViewRef = useRef(null);
|
|
144
|
+
const prevViewForSlotIsolationRef = useRef(view);
|
|
129
145
|
/** 任意入口切入 `day` 时递增一次(含首屏即为日视图:`prev === null`) */
|
|
130
146
|
useLayoutEffect(() => {
|
|
131
147
|
const prev = prevCalendarViewRef.current;
|
|
132
148
|
if (view === "day" && (prev === null || prev !== "day")) setDayTimelineScrollToNowToken((t) => t + 1);
|
|
149
|
+
if (view === "resource" && (prev === null || prev !== "resource")) setResourceMonthScrollToTodayToken((t) => t + 1);
|
|
133
150
|
prevCalendarViewRef.current = view;
|
|
134
151
|
}, [view]);
|
|
135
152
|
const blockPanelRef = useRef(null);
|
|
@@ -154,79 +171,45 @@ function BookingCalendar(props = {}) {
|
|
|
154
171
|
resolvedSlotMinutes
|
|
155
172
|
]);
|
|
156
173
|
/**
|
|
157
|
-
*
|
|
158
|
-
* 周/月不读其中大部分字段,但为单 hook 依赖仍随 `visibleCourtRows` 重算。
|
|
174
|
+
* 日视图 overlay 行高 / 并行 lane / +N 溢出(详见 hook 内注释)。
|
|
159
175
|
*/
|
|
160
|
-
const { dayRowHeights,
|
|
161
|
-
const defaultHeights = visibleCourtRows.map((row) => row.kind === "group" ? 38 : 44);
|
|
162
|
-
let overlays = buildOverlayBookings(visibleCourtRows, dayKey, bookings, buildDayRowTopMap(visibleCourtRows, defaultHeights), hourSlots, defaultHeights, slotStepHours);
|
|
163
|
-
const expandedHeights = visibleCourtRows.map((row, i) => {
|
|
164
|
-
if (row.kind !== "court") return defaultHeights[i];
|
|
165
|
-
if (!overlays.some((o) => o.courtId === row.id)) return defaultHeights[i];
|
|
166
|
-
return dayCourtOverlayRowHeightPx(effectiveDayOverlayDisplayLaneCount(maxConcurrentBookingsOnCourt(overlays, row.id)));
|
|
167
|
-
});
|
|
168
|
-
const tops2 = buildDayRowTopMap(visibleCourtRows, expandedHeights);
|
|
169
|
-
overlays = buildOverlayBookings(visibleCourtRows, dayKey, bookings, tops2, hourSlots, expandedHeights, slotStepHours);
|
|
170
|
-
const courtDayDisplayLaneCount = {};
|
|
171
|
-
const hiddenDayOverlayIdSet = /* @__PURE__ */ new Set();
|
|
172
|
-
const dayOverlayOverflowPluses = [];
|
|
173
|
-
for (const row of visibleCourtRows) {
|
|
174
|
-
if (row.kind !== "court") continue;
|
|
175
|
-
const cid = row.id;
|
|
176
|
-
courtDayDisplayLaneCount[cid] = effectiveDayOverlayDisplayLaneCount(maxConcurrentBookingsOnCourt(overlays, cid));
|
|
177
|
-
const clusters = clusterDayCourtOverlaysByOverlap(overlays, cid);
|
|
178
|
-
for (const cluster of clusters) {
|
|
179
|
-
if (cluster.length <= 3) continue;
|
|
180
|
-
const sortedByLane = [...cluster].sort((a, b) => {
|
|
181
|
-
var _a$lane, _b$lane;
|
|
182
|
-
return ((_a$lane = a.lane) !== null && _a$lane !== void 0 ? _a$lane : 0) - ((_b$lane = b.lane) !== null && _b$lane !== void 0 ? _b$lane : 0) || a.start - b.start;
|
|
183
|
-
});
|
|
184
|
-
for (let i = 3; i < sortedByLane.length; i++) {
|
|
185
|
-
const o = sortedByLane[i];
|
|
186
|
-
hiddenDayOverlayIdSet.add(dayOverlayHiddenKey(o.item, dayKey, o.courtId));
|
|
187
|
-
}
|
|
188
|
-
const hidden = sortedByLane.slice(3);
|
|
189
|
-
let minS = Infinity;
|
|
190
|
-
let maxE = -Infinity;
|
|
191
|
-
for (const o of cluster) {
|
|
192
|
-
minS = Math.min(minS, o.start);
|
|
193
|
-
maxE = Math.max(maxE, o.start + o.span);
|
|
194
|
-
}
|
|
195
|
-
const anchor = hidden[0];
|
|
196
|
-
dayOverlayOverflowPluses.push({
|
|
197
|
-
key: `${cid}-${minS}-${maxE}-${dayKey}`,
|
|
198
|
-
courtId: cid,
|
|
199
|
-
rowTop: anchor.rowTop,
|
|
200
|
-
rowHeight: anchor.rowHeight,
|
|
201
|
-
start: minS,
|
|
202
|
-
span: maxE - minS,
|
|
203
|
-
plusCount: hidden.length,
|
|
204
|
-
overflowItems: hidden.map((o) => o.item)
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return {
|
|
209
|
-
dayRowHeights: expandedHeights,
|
|
210
|
-
dayRowTopMap: tops2,
|
|
211
|
-
overlayBookings: overlays,
|
|
212
|
-
courtDayDisplayLaneCount,
|
|
213
|
-
hiddenDayOverlayIdSet,
|
|
214
|
-
dayOverlayOverflowPluses
|
|
215
|
-
};
|
|
216
|
-
}, [
|
|
217
|
-
visibleCourtRows,
|
|
218
|
-
dayKey,
|
|
219
|
-
bookings,
|
|
220
|
-
hourSlots,
|
|
221
|
-
slotStepHours
|
|
222
|
-
]);
|
|
176
|
+
const { dayRowHeights, overlayBookings, courtDayDisplayLaneCount, hiddenDayOverlayIdSet, dayOverlayOverflowPluses } = useBookingCalendarDayOverlayLayout(visibleCourtRows, dayKey, bookings, hourSlots, slotStepHours);
|
|
223
177
|
/** 周视图表头对应的 7 个自然日 */
|
|
224
178
|
const weekDays = useMemo(() => rangeDays(anchorDate), [anchorDate]);
|
|
179
|
+
/** 资源视图:当月每日一列 */
|
|
180
|
+
const resourceMonthDays = useMemo(() => monthCalendarDays(anchorDate), [anchorDate]);
|
|
225
181
|
useMemo(() => new Set(weekDays.map((d) => fmtDate(d))), [weekDays]);
|
|
226
182
|
const firstTimelineHour = (_hourSlots$ = hourSlots[0]) !== null && _hourSlots$ !== void 0 ? _hourSlots$ : 0;
|
|
227
183
|
const endTimelineExclusive = hourSlots.length ? hourSlots[hourSlots.length - 1] + slotStepHours : 24;
|
|
228
184
|
/** 槽位起始小时是否落在当前营业/时间轴配置内 */
|
|
229
|
-
const isBusinessHour = (h) => isHourInTimelineSlots(h, hourSlots);
|
|
185
|
+
const isBusinessHour = useCallback((h) => isHourInTimelineSlots(h, hourSlots), [hourSlots]);
|
|
186
|
+
const dayHourRemaining = useBookingCalendarDayHourRemaining(visibleCourts, dayKey, bookings, hourSlots, slotStepHours, isBusinessHour);
|
|
187
|
+
const resourceDayHeaderRemaining = useBookingCalendarResourceDayHeaderRemaining(resourceMonthDays, visibleCourts, bookings, hourSlots, slotStepHours);
|
|
188
|
+
/**
|
|
189
|
+
* 资源视图:选区是否可触发 Block / Create Booking。
|
|
190
|
+
* - 任何选中 → SelectionBar 显示「Cancel」。
|
|
191
|
+
* - 同 court + 连续多天(含单日) → 才显示「Block / Create」按钮。
|
|
192
|
+
*/
|
|
193
|
+
const resourceSelectionContiguous = useMemo(() => isResourceSelectionContiguousSameCourt(selectedResourceCells), [selectedResourceCells]);
|
|
194
|
+
const resourceSelectionBarMeta = useMemo(() => {
|
|
195
|
+
if (selectedResourceCells.length === 0) return null;
|
|
196
|
+
return {
|
|
197
|
+
freePickCount: selectedResourceCells.length,
|
|
198
|
+
blockPickCount: selectedResourceCells.length,
|
|
199
|
+
freeAllowsBlockPanel: resourceSelectionContiguous
|
|
200
|
+
};
|
|
201
|
+
}, [selectedResourceCells, resourceSelectionContiguous]);
|
|
202
|
+
/** 日视图与资源视图选中隔离:切换视图时清空另一侧 */
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (prevViewForSlotIsolationRef.current !== view) {
|
|
205
|
+
if (view === "day") setSelectedResourceCells([]);
|
|
206
|
+
if (view === "resource") {
|
|
207
|
+
setSelectedFreeSlots([]);
|
|
208
|
+
setSelectedBlockSlots([]);
|
|
209
|
+
}
|
|
210
|
+
prevViewForSlotIsolationRef.current = view;
|
|
211
|
+
}
|
|
212
|
+
}, [view]);
|
|
230
213
|
/** 月历每格摘要(占用点、锁台等),供月视图渲染 */
|
|
231
214
|
const monthDays = useMemo(() => monthGrid(anchorDate, bookings, courts, hourSlots, slotStepHours), [
|
|
232
215
|
anchorDate,
|
|
@@ -235,29 +218,6 @@ function BookingCalendar(props = {}) {
|
|
|
235
218
|
hourSlots,
|
|
236
219
|
slotStepHours
|
|
237
220
|
]);
|
|
238
|
-
/** 日视图表头:每个整点列上仍有空的资源数占比(按全部可见资源计,与分组折叠无关) */
|
|
239
|
-
const dayHourRemaining = useMemo(() => {
|
|
240
|
-
const total = visibleCourts.length;
|
|
241
|
-
return Object.fromEntries(hourSlots.map((hour) => {
|
|
242
|
-
if (!isBusinessHour(hour)) return [hour, {
|
|
243
|
-
remaining: 0,
|
|
244
|
-
total,
|
|
245
|
-
ratio: 0
|
|
246
|
-
}];
|
|
247
|
-
const remaining = visibleCourts.filter((court) => !dayOccupancy(court.id, dayKey, bookings, hourSlots, slotStepHours)[hour]).length;
|
|
248
|
-
return [hour, {
|
|
249
|
-
remaining,
|
|
250
|
-
total,
|
|
251
|
-
ratio: total > 0 ? remaining / total : 0
|
|
252
|
-
}];
|
|
253
|
-
}));
|
|
254
|
-
}, [
|
|
255
|
-
visibleCourts,
|
|
256
|
-
dayKey,
|
|
257
|
-
bookings,
|
|
258
|
-
hourSlots,
|
|
259
|
-
slotStepHours
|
|
260
|
-
]);
|
|
261
221
|
/** 日视图内容区总高度(表头 + 各资源行叠加以 overlay 抬高后的高度) */
|
|
262
222
|
const dayContentHeight = 56 + dayRowHeights.reduce((sum, h) => sum + h, 0);
|
|
263
223
|
/** 点击文档空白处关闭锁台面版、溢出浮层、改期确认等面板 */
|
|
@@ -281,16 +241,39 @@ function BookingCalendar(props = {}) {
|
|
|
281
241
|
/** 锁台面版打开时若选区不再连续同资源则自动收起(避免与表单状态不一致) */
|
|
282
242
|
useEffect(() => {
|
|
283
243
|
if (!showBlockPanel) return;
|
|
244
|
+
if (view === "resource") {
|
|
245
|
+
if (!resourceSelectionContiguous) setShowBlockPanel(false);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
284
248
|
if (!isFreeSelectionContiguousSameResource(selectedFreeSlots, slotStepHours)) setShowBlockPanel(false);
|
|
285
249
|
}, [
|
|
286
250
|
showBlockPanel,
|
|
251
|
+
view,
|
|
287
252
|
selectedFreeSlots,
|
|
253
|
+
resourceSelectionContiguous,
|
|
288
254
|
slotStepHours
|
|
289
255
|
]);
|
|
290
256
|
/** 打开锁台面版时用当前选中的空闲连续段预填资源与时间范围(仅内置锁台面版) */
|
|
291
257
|
useEffect(() => {
|
|
292
258
|
if (props.onBlockedTimePanelRequest) return;
|
|
293
|
-
if (!showBlockPanel
|
|
259
|
+
if (!showBlockPanel) return;
|
|
260
|
+
if (view === "resource") {
|
|
261
|
+
var _bizHours$, _bizHours;
|
|
262
|
+
if (!resourceSelectionContiguous) return;
|
|
263
|
+
const sortedDates = [...selectedResourceCells].map((s) => s.dateKey).sort();
|
|
264
|
+
const firstDate = sortedDates[0];
|
|
265
|
+
const lastDate = sortedDates[sortedDates.length - 1];
|
|
266
|
+
const bizHours = hourSlots.filter((h) => isHourInTimelineSlots(h, hourSlots));
|
|
267
|
+
const h0 = (_bizHours$ = bizHours[0]) !== null && _bizHours$ !== void 0 ? _bizHours$ : 0;
|
|
268
|
+
const hLast = (_bizHours = bizHours[bizHours.length - 1]) !== null && _bizHours !== void 0 ? _bizHours : h0;
|
|
269
|
+
const slotStep = slotStepHours > 0 ? slotStepHours : 1;
|
|
270
|
+
setBlockResourceId(String(selectedResourceCells[0].courtId));
|
|
271
|
+
setBlockTimeRange([dayAndHourToDayjs(firstDate, h0), dayAndHourToDayjs(lastDate, hLast + slotStep)]);
|
|
272
|
+
setBlockFormName(tCal("pisell2.recordBoard.calendar.block.defaultTitle"));
|
|
273
|
+
setBlockModalNote("");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (selectedFreeSlots.length === 0) return;
|
|
294
277
|
const g = groupSelectedSlots(selectedFreeSlots, slotStepHours)[0];
|
|
295
278
|
if (!g) return;
|
|
296
279
|
setBlockResourceId(String(g.courtId));
|
|
@@ -299,8 +282,13 @@ function BookingCalendar(props = {}) {
|
|
|
299
282
|
setBlockModalNote("");
|
|
300
283
|
}, [
|
|
301
284
|
showBlockPanel,
|
|
285
|
+
view,
|
|
302
286
|
selectedFreeSlots,
|
|
303
|
-
|
|
287
|
+
selectedResourceCells,
|
|
288
|
+
resourceSelectionContiguous,
|
|
289
|
+
hourSlots,
|
|
290
|
+
slotStepHours,
|
|
291
|
+
props.onBlockedTimePanelRequest
|
|
304
292
|
]);
|
|
305
293
|
/** 周/月跳入日视图时的短动画标记,超时清除 */
|
|
306
294
|
useEffect(() => {
|
|
@@ -308,47 +296,15 @@ function BookingCalendar(props = {}) {
|
|
|
308
296
|
const timer = setTimeout(() => setTransitioningToDay(null), 280);
|
|
309
297
|
return () => clearTimeout(timer);
|
|
310
298
|
}, [transitioningToDay]);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
/** 当前日锚定下,该资源该槽是否在「锁台多选」集合中 */
|
|
316
|
-
function isBlockSelected(courtId, hour) {
|
|
317
|
-
return selectedBlockSlots.some((slot) => slot.courtId === courtId && slot.date === dayKey && slot.hour === hour);
|
|
318
|
-
}
|
|
319
|
-
/** 从周/月单元格调起日视图:更新锚定日、可选聚焦资源、记录过渡动画方向 */
|
|
320
|
-
function goToDayFromCell(day, courtId, source) {
|
|
321
|
-
setNavDirection(day > anchorDate ? 1 : -1);
|
|
322
|
-
setTransitioningToDay({ source });
|
|
323
|
-
/** 周/月只是“跳转查看”,不应把日视图多选状态带过去造成误解(像是系统替用户选了格子) */
|
|
324
|
-
setSelectedFreeSlots([]);
|
|
325
|
-
setSelectedBlockSlots([]);
|
|
326
|
-
commitAnchor(day);
|
|
327
|
-
setFocusedCourtId(courtId || null);
|
|
328
|
-
commitView("day");
|
|
329
|
-
}
|
|
299
|
+
const goToDayFromCell = useBookingCalendarGoToDayFromCell(anchorDate, commitAnchor, commitView, setNavDirection, setTransitioningToDay, setSelectedFreeSlots, setSelectedBlockSlots, setSelectedResourceCells, setFocusedCourtId);
|
|
300
|
+
const toggleResourceCell = useBookingCalendarResourceCellToggle(setSelectedResourceCells);
|
|
301
|
+
const { isFreeSelected, isBlockSelected, toggleSlot } = useBookingCalendarDaySelection(dayKey, bookings, hourSlots, slotStepHours, courts, selectedFreeSlots, selectedBlockSlots, setSelectedFreeSlots, setSelectedBlockSlots);
|
|
330
302
|
/** 卡片详情:业务若 `onBookingClick` 返回 true 则视为已消费;否则关闭溢出气泡 */
|
|
331
|
-
|
|
303
|
+
const openBookingDetail = useCallback((item) => {
|
|
332
304
|
var _props$onBookingClick;
|
|
333
305
|
if (((_props$onBookingClick = props.onBookingClick) === null || _props$onBookingClick === void 0 ? void 0 : _props$onBookingClick.call(props, item)) === true) return;
|
|
334
306
|
setOverflowPopover(null);
|
|
335
|
-
}
|
|
336
|
-
/** 日视图格子点击:空闲槽进多选;锁台槽进锁台多选;已约不动 */
|
|
337
|
-
function toggleSlot(courtId, hour) {
|
|
338
|
-
const existing = dayOccupancy(courtId, dayKey, bookings, hourSlots, slotStepHours)[hour];
|
|
339
|
-
const court = courts.find((c) => c.id === courtId);
|
|
340
|
-
const slot = {
|
|
341
|
-
courtId,
|
|
342
|
-
court: court === null || court === void 0 ? void 0 : court.name,
|
|
343
|
-
date: dayKey,
|
|
344
|
-
hour
|
|
345
|
-
};
|
|
346
|
-
if (!existing) {
|
|
347
|
-
setSelectedFreeSlots((prev) => prev.some((s) => sameSlot(s, slot)) ? prev.filter((s) => !sameSlot(s, slot)) : [...prev, slot]);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
if (existing.kind === "block") setSelectedBlockSlots((prev) => prev.some((s) => sameSlot(s, slot)) ? prev.filter((s) => !sameSlot(s, slot)) : [...prev, slot]);
|
|
351
|
-
}
|
|
307
|
+
}, [props.onBookingClick]);
|
|
352
308
|
/** 清空空闲与锁台多选 */
|
|
353
309
|
function deselectAll() {
|
|
354
310
|
setSelectedFreeSlots([]);
|
|
@@ -356,9 +312,27 @@ function BookingCalendar(props = {}) {
|
|
|
356
312
|
}
|
|
357
313
|
/** 将连续空闲选区合并为一条或多条本地预约(演示/未接写接口时) */
|
|
358
314
|
function createBookingFromSelection() {
|
|
359
|
-
if (!allowPersist("createBooking")) return;
|
|
360
315
|
if (selectedFreeSlots.length === 0) return;
|
|
361
|
-
const
|
|
316
|
+
const groups = groupSelectedSlots(selectedFreeSlots, slotStepHours);
|
|
317
|
+
const host = props.onCreateBookingFromSelection;
|
|
318
|
+
if (host) {
|
|
319
|
+
_asyncToGenerator(function* () {
|
|
320
|
+
try {
|
|
321
|
+
if ((yield Promise.resolve(host({
|
|
322
|
+
mode: "day",
|
|
323
|
+
groups: groups.map((g) => ({
|
|
324
|
+
courtId: g.courtId,
|
|
325
|
+
date: g.date,
|
|
326
|
+
startHour: g.startHour,
|
|
327
|
+
endHour: g.endHour
|
|
328
|
+
}))
|
|
329
|
+
}))) !== false) setSelectedFreeSlots([]);
|
|
330
|
+
} catch (_unused) {}
|
|
331
|
+
})();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (!allowPersist("createBooking")) return;
|
|
335
|
+
const newBookings = groups.map((slot, idx) => ({
|
|
362
336
|
id: `new-${slot.courtId}-${slot.date}-${slot.startHour}-${idx}`,
|
|
363
337
|
courtId: slot.courtId,
|
|
364
338
|
date: slot.date,
|
|
@@ -370,17 +344,48 @@ function BookingCalendar(props = {}) {
|
|
|
370
344
|
setBookings((prev) => [...newBookings, ...prev]);
|
|
371
345
|
setSelectedFreeSlots([]);
|
|
372
346
|
}
|
|
373
|
-
/**
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
347
|
+
/**
|
|
348
|
+
* 资源视图:连续多天 → 一条跨日整天预约,时间 = 营业开始 → 营业结束。
|
|
349
|
+
* 单日 → 同样按整天写入。仅在 `resourceSelectionContiguous` 为 true 时执行。
|
|
350
|
+
*/
|
|
351
|
+
function createBookingFromResourceSelection() {
|
|
352
|
+
var _bizHours$2, _bizHours2;
|
|
353
|
+
if (!resourceSelectionContiguous) return;
|
|
354
|
+
const sortedDates = [...selectedResourceCells].map((s) => s.dateKey).sort();
|
|
355
|
+
const firstDate = sortedDates[0];
|
|
356
|
+
const lastDate = sortedDates[sortedDates.length - 1];
|
|
357
|
+
const courtId = selectedResourceCells[0].courtId;
|
|
358
|
+
const bizHours = hourSlots.filter((h) => isHourInTimelineSlots(h, hourSlots));
|
|
359
|
+
const h0 = (_bizHours$2 = bizHours[0]) !== null && _bizHours$2 !== void 0 ? _bizHours$2 : 0;
|
|
360
|
+
const endHour = ((_bizHours2 = bizHours[bizHours.length - 1]) !== null && _bizHours2 !== void 0 ? _bizHours2 : h0) + (slotStepHours > 0 ? slotStepHours : 1);
|
|
361
|
+
const host = props.onCreateBookingFromSelection;
|
|
362
|
+
if (host) {
|
|
363
|
+
_asyncToGenerator(function* () {
|
|
364
|
+
try {
|
|
365
|
+
if ((yield Promise.resolve(host({
|
|
366
|
+
mode: "resource",
|
|
367
|
+
courtId,
|
|
368
|
+
dates: sortedDates,
|
|
369
|
+
startHour: h0,
|
|
370
|
+
endHour
|
|
371
|
+
}))) !== false) setSelectedResourceCells([]);
|
|
372
|
+
} catch (_unused2) {}
|
|
373
|
+
})();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (!allowPersist("createBooking")) return;
|
|
377
|
+
const newBooking = {
|
|
378
|
+
id: `new-${courtId}-${firstDate}-${Date.now()}`,
|
|
379
|
+
courtId,
|
|
380
|
+
date: firstDate,
|
|
381
|
+
endDate: lastDate !== firstDate ? lastDate : void 0,
|
|
382
|
+
startHour: h0,
|
|
383
|
+
endHour,
|
|
384
|
+
name: tCal("pisell2.recordBoard.calendar.common.newBookingName"),
|
|
385
|
+
kind: "booking"
|
|
386
|
+
};
|
|
387
|
+
setBookings((prev) => [newBooking, ...prev]);
|
|
388
|
+
setSelectedResourceCells([]);
|
|
384
389
|
}
|
|
385
390
|
/** 屏蔽:宿主 lock 弹窗,或内置锁台面版 */
|
|
386
391
|
const handleOpenBlockPanelFromSelection = useCallback(_asyncToGenerator(function* () {
|
|
@@ -389,20 +394,21 @@ function BookingCalendar(props = {}) {
|
|
|
389
394
|
if (!g) return;
|
|
390
395
|
if (props.onBlockedTimePanelRequest) {
|
|
391
396
|
try {
|
|
397
|
+
const slotsPayload = selectedFreeSlots.map((s) => {
|
|
398
|
+
const slot = {
|
|
399
|
+
courtId: s.courtId,
|
|
400
|
+
date: s.date,
|
|
401
|
+
hour: s.hour
|
|
402
|
+
};
|
|
403
|
+
const extra = s;
|
|
404
|
+
if (extra.court !== void 0 && extra.court !== "") slot.court = extra.court;
|
|
405
|
+
return slot;
|
|
406
|
+
});
|
|
392
407
|
const payload = {
|
|
393
408
|
slotStepHours,
|
|
394
409
|
slotStepMinutes: resolvedSlotMinutes,
|
|
395
|
-
dayKey,
|
|
396
|
-
selectedFreeSlots:
|
|
397
|
-
const slot = {
|
|
398
|
-
courtId: s.courtId,
|
|
399
|
-
date: s.date,
|
|
400
|
-
hour: s.hour
|
|
401
|
-
};
|
|
402
|
-
const extra = s;
|
|
403
|
-
if (extra.court !== void 0 && extra.court !== "") slot.court = extra.court;
|
|
404
|
-
return slot;
|
|
405
|
-
}),
|
|
410
|
+
dayKey: g.date,
|
|
411
|
+
selectedFreeSlots: slotsPayload,
|
|
406
412
|
mergedRange: {
|
|
407
413
|
courtId: g.courtId,
|
|
408
414
|
date: g.date,
|
|
@@ -411,7 +417,7 @@ function BookingCalendar(props = {}) {
|
|
|
411
417
|
}
|
|
412
418
|
};
|
|
413
419
|
if ((yield props.onBlockedTimePanelRequest(payload)) !== false) setSelectedFreeSlots([]);
|
|
414
|
-
} catch (
|
|
420
|
+
} catch (_unused3) {}
|
|
415
421
|
return;
|
|
416
422
|
}
|
|
417
423
|
setShowBlockPanel(true);
|
|
@@ -419,7 +425,56 @@ function BookingCalendar(props = {}) {
|
|
|
419
425
|
selectedFreeSlots,
|
|
420
426
|
slotStepHours,
|
|
421
427
|
resolvedSlotMinutes,
|
|
422
|
-
|
|
428
|
+
props.onBlockedTimePanelRequest
|
|
429
|
+
]);
|
|
430
|
+
/**
|
|
431
|
+
* 资源视图:仅在「同 court + 连续多天」时打开 Block 面板,按整天预填资源 + 跨日时间范围。
|
|
432
|
+
*/
|
|
433
|
+
const handleOpenBlockPanelFromResource = useCallback(_asyncToGenerator(function* () {
|
|
434
|
+
var _bizHours$3, _bizHours3;
|
|
435
|
+
if (!resourceSelectionContiguous) return;
|
|
436
|
+
const sortedDates = [...selectedResourceCells].map((s) => s.dateKey).sort();
|
|
437
|
+
const firstDate = sortedDates[0];
|
|
438
|
+
const lastDate = sortedDates[sortedDates.length - 1];
|
|
439
|
+
const courtId = selectedResourceCells[0].courtId;
|
|
440
|
+
const court = courts.find((c) => c.id === courtId);
|
|
441
|
+
const bizHours = hourSlots.filter((h) => isHourInTimelineSlots(h, hourSlots));
|
|
442
|
+
const h0 = (_bizHours$3 = bizHours[0]) !== null && _bizHours$3 !== void 0 ? _bizHours$3 : 0;
|
|
443
|
+
const hLast = (_bizHours3 = bizHours[bizHours.length - 1]) !== null && _bizHours3 !== void 0 ? _bizHours3 : h0;
|
|
444
|
+
const slotStep = slotStepHours > 0 ? slotStepHours : 1;
|
|
445
|
+
const startHour = h0;
|
|
446
|
+
const endHour = hLast + slotStep;
|
|
447
|
+
if (props.onBlockedTimePanelRequest) {
|
|
448
|
+
try {
|
|
449
|
+
const payload = {
|
|
450
|
+
slotStepHours,
|
|
451
|
+
slotStepMinutes: resolvedSlotMinutes,
|
|
452
|
+
dayKey: firstDate,
|
|
453
|
+
selectedFreeSlots: sortedDates.map((d) => _objectSpread2({
|
|
454
|
+
courtId,
|
|
455
|
+
date: d,
|
|
456
|
+
hour: startHour
|
|
457
|
+
}, (court === null || court === void 0 ? void 0 : court.name) ? { court: court.name } : {})),
|
|
458
|
+
mergedRange: {
|
|
459
|
+
courtId,
|
|
460
|
+
date: firstDate,
|
|
461
|
+
endDate: lastDate !== firstDate ? lastDate : void 0,
|
|
462
|
+
startHour,
|
|
463
|
+
endHour
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
if ((yield props.onBlockedTimePanelRequest(payload)) !== false) setSelectedResourceCells([]);
|
|
467
|
+
} catch (_unused4) {}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
setShowBlockPanel(true);
|
|
471
|
+
}), [
|
|
472
|
+
selectedResourceCells,
|
|
473
|
+
resourceSelectionContiguous,
|
|
474
|
+
hourSlots,
|
|
475
|
+
slotStepHours,
|
|
476
|
+
courts,
|
|
477
|
+
resolvedSlotMinutes,
|
|
423
478
|
props.onBlockedTimePanelRequest
|
|
424
479
|
]);
|
|
425
480
|
/** 提交锁台:优先走 `onBlockedTimeSave`;否则写入本地 block 片段 */
|
|
@@ -458,6 +513,7 @@ function BookingCalendar(props = {}) {
|
|
|
458
513
|
if ((yield props.onBlockedTimeSave(payload)) === false) return;
|
|
459
514
|
setShowBlockPanel(false);
|
|
460
515
|
setSelectedFreeSlots([]);
|
|
516
|
+
setSelectedResourceCells([]);
|
|
461
517
|
setBlockModalNote("");
|
|
462
518
|
} finally {
|
|
463
519
|
setBlockSaving(false);
|
|
@@ -484,18 +540,19 @@ function BookingCalendar(props = {}) {
|
|
|
484
540
|
if (!allowPersist("createBlock")) return;
|
|
485
541
|
setBookings((prev) => [nextBlock, ...prev]);
|
|
486
542
|
setSelectedFreeSlots([]);
|
|
543
|
+
setSelectedResourceCells([]);
|
|
487
544
|
setShowBlockPanel(false);
|
|
488
545
|
setBlockModalNote("");
|
|
489
546
|
});
|
|
490
547
|
return _saveBlockedTime.apply(this, arguments);
|
|
491
548
|
}
|
|
492
549
|
/** 拖拽改期松手:宿主接管改约,或弹出内置确认框 */
|
|
493
|
-
|
|
550
|
+
const handleDropMove = useCallback((targetCourtId, targetDate, targetHour) => {
|
|
494
551
|
if (!draggingItem) return;
|
|
495
552
|
const duration = draggingItem.endHour - draggingItem.startHour;
|
|
496
553
|
const nextStart = targetHour;
|
|
497
554
|
const nextEnd = targetHour + duration;
|
|
498
|
-
if (!
|
|
555
|
+
if (!isHourInTimelineSlots(nextStart, hourSlots) || nextEnd > endTimelineExclusive) return;
|
|
499
556
|
const targetCourt = targetCourtId !== null && targetCourtId !== void 0 ? targetCourtId : draggingItem.courtId;
|
|
500
557
|
const itemSnapshot = draggingItem;
|
|
501
558
|
const draft = {
|
|
@@ -518,12 +575,17 @@ function BookingCalendar(props = {}) {
|
|
|
518
575
|
targetHour
|
|
519
576
|
});
|
|
520
577
|
setMoveDraft(draft);
|
|
521
|
-
}
|
|
578
|
+
}, [
|
|
579
|
+
draggingItem,
|
|
580
|
+
hourSlots,
|
|
581
|
+
endTimelineExclusive,
|
|
582
|
+
props.onBookingMoveRequest
|
|
583
|
+
]);
|
|
522
584
|
/** 周/月格 drop:保持原开始小时,仅换日或资源 */
|
|
523
|
-
|
|
585
|
+
const handleGridDropMove = useCallback((targetDate, targetCourtId = null) => {
|
|
524
586
|
if (!draggingItem || draggingItem.kind === "block") return;
|
|
525
587
|
handleDropMove(targetCourtId !== null && targetCourtId !== void 0 ? targetCourtId : draggingItem.courtId, targetDate, draggingItem.startHour);
|
|
526
|
-
}
|
|
588
|
+
}, [draggingItem, handleDropMove]);
|
|
527
589
|
/** 确认改期:在 bookings 中平移该条的起止小时与 court/date */
|
|
528
590
|
function confirmMove() {
|
|
529
591
|
if (!moveConfirm || !moveDraft) return;
|
|
@@ -582,6 +644,29 @@ function BookingCalendar(props = {}) {
|
|
|
582
644
|
overflowListModal,
|
|
583
645
|
setOverflowListModal,
|
|
584
646
|
overflowPanelRef
|
|
647
|
+
}), view === "resource" && /* @__PURE__ */ React.createElement(BookingCalendarResourceView, {
|
|
648
|
+
calendarProps: props,
|
|
649
|
+
isLight,
|
|
650
|
+
anchorDate,
|
|
651
|
+
monthDays: resourceMonthDays,
|
|
652
|
+
slotColWidth: resourceViewSlotColWidth,
|
|
653
|
+
bookings,
|
|
654
|
+
hourSlots,
|
|
655
|
+
slotStepHours,
|
|
656
|
+
visibleCourts,
|
|
657
|
+
visibleCourtRows,
|
|
658
|
+
handleGridDropMove,
|
|
659
|
+
openBookingDetail,
|
|
660
|
+
setDraggingItem,
|
|
661
|
+
setCollapsedGroups,
|
|
662
|
+
overflowPopover,
|
|
663
|
+
setOverflowPopover,
|
|
664
|
+
setOverflowListModal,
|
|
665
|
+
overflowPanelRef,
|
|
666
|
+
resourceMonthScrollToTodayToken,
|
|
667
|
+
resourceDayHeaderRemaining,
|
|
668
|
+
toggleResourceCell,
|
|
669
|
+
selectedResourceCells
|
|
585
670
|
}), view === "day" && /* @__PURE__ */ React.createElement(BookingCalendarDayView, {
|
|
586
671
|
calendarProps: props,
|
|
587
672
|
isLight,
|
|
@@ -619,73 +704,38 @@ function BookingCalendar(props = {}) {
|
|
|
619
704
|
setOverflowListModal,
|
|
620
705
|
selectedFreeSlots,
|
|
621
706
|
selectedBlockSlots,
|
|
622
|
-
deselectAll,
|
|
623
|
-
openSelectedBlocks,
|
|
624
|
-
onOpenBlockPanel: handleOpenBlockPanelFromSelection,
|
|
625
|
-
createBookingFromSelection,
|
|
626
707
|
focusedCourtId,
|
|
627
708
|
dayTimelineScrollToNowToken
|
|
628
|
-
}))),
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
"
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
showTime: {
|
|
656
|
-
format: "HH:mm",
|
|
657
|
-
minuteStep: resolvedSlotMinutes >= 60 ? 60 : Math.max(1, resolvedSlotMinutes)
|
|
658
|
-
},
|
|
659
|
-
format: "YYYY-MM-DD HH:mm",
|
|
660
|
-
value: blockTimeRange !== null && blockTimeRange !== void 0 ? blockTimeRange : void 0,
|
|
661
|
-
onChange: (v) => {
|
|
662
|
-
var _ref;
|
|
663
|
-
return setBlockTimeRange((_ref = v) !== null && _ref !== void 0 ? _ref : null);
|
|
664
|
-
}
|
|
665
|
-
}))), /* @__PURE__ */ React.createElement("div", { className: "mt-6 flex flex-col-reverse sm:flex-row items-stretch sm:items-center justify-end gap-3" }, /* @__PURE__ */ React.createElement("button", {
|
|
666
|
-
type: "button",
|
|
667
|
-
disabled: blockSaving,
|
|
668
|
-
onClick: () => setShowBlockPanel(false),
|
|
669
|
-
className: `rounded-xl px-4 py-2 ${isLight ? "btn-neutral" : "border border-zinc-700 text-zinc-300"}`
|
|
670
|
-
}, tCal("pisell2.recordBoard.calendar.common.cancel")), /* @__PURE__ */ React.createElement("button", {
|
|
671
|
-
type: "button",
|
|
672
|
-
disabled: blockSaving,
|
|
673
|
-
onClick: () => void saveBlockedTime(),
|
|
674
|
-
className: `rounded-xl px-5 py-3 text-sm font-semibold disabled:opacity-50 ${isLight ? "btn-brand" : "bg-violet-600 text-white"}`
|
|
675
|
-
}, blockSaving ? tCal("pisell2.recordBoard.calendar.common.saving") : tCal("pisell2.recordBoard.calendar.common.save"))))), overflowListModal && /* @__PURE__ */ React.createElement("div", { className: "fixed inset-0 z-[69] bg-black/45 backdrop-blur-sm flex items-center justify-center p-4" }, /* @__PURE__ */ React.createElement("div", {
|
|
676
|
-
ref: overflowPanelRef,
|
|
677
|
-
className: `flex w-full max-w-[520px] max-h-[72vh] flex-col overflow-hidden rounded-3xl border p-5 shadow-2xl ${isLight ? "theme-panel theme-border soft-shadow" : "border-zinc-700 bg-[#121217]"}`
|
|
678
|
-
}, /* @__PURE__ */ React.createElement("div", { className: "mb-4 flex shrink-0 items-start justify-between" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "text-xl font-semibold" }, tCal("pisell2.recordBoard.calendar.overflow.bookingsForDay")), /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-sm text-zinc-400" }, overflowListModal.title)), /* @__PURE__ */ React.createElement("button", {
|
|
679
|
-
onClick: () => setOverflowListModal(null),
|
|
680
|
-
className: "rounded-xl border border-zinc-700 px-3 py-2 text-zinc-300"
|
|
681
|
-
}, tCal("pisell2.recordBoard.calendar.common.close"))), /* @__PURE__ */ React.createElement("div", { className: "min-h-0 flex-1 overflow-hidden px-0.5" }, /* @__PURE__ */ React.createElement(OverflowListModalVirtualList, {
|
|
682
|
-
items: overflowListModal.items,
|
|
683
|
-
dateKey: overflowListModal.date,
|
|
684
|
-
listWidth: 468,
|
|
685
|
-
onPickItem: openBookingDetail,
|
|
709
|
+
}))), view === "day" && (selectedFreeSlots.length > 0 || selectedBlockSlots.length > 0) || view === "resource" && selectedResourceCells.length > 0 ? /* @__PURE__ */ React.createElement(BookingCalendarDaySelectionBar, {
|
|
710
|
+
slotStepHours,
|
|
711
|
+
selectedFreeSlots: view === "resource" ? [] : selectedFreeSlots,
|
|
712
|
+
resourceSelectionSummary: view === "resource" ? resourceSelectionBarMeta : void 0,
|
|
713
|
+
deselectAll: view === "resource" ? () => setSelectedResourceCells([]) : deselectAll,
|
|
714
|
+
onOpenBlockPanel: view === "resource" ? handleOpenBlockPanelFromResource : handleOpenBlockPanelFromSelection,
|
|
715
|
+
createBookingFromSelection: view === "resource" ? createBookingFromResourceSelection : createBookingFromSelection
|
|
716
|
+
}) : null, showBlockPanel && !props.onBlockedTimePanelRequest && (view === "resource" ? resourceSelectionContiguous : selectedFreeSlots.length > 0 && isFreeSelectionContiguousSameResource(selectedFreeSlots, slotStepHours)) && /* @__PURE__ */ React.createElement(BookingCalendarBlockPanel, {
|
|
717
|
+
blockPanelRef,
|
|
718
|
+
isLight,
|
|
719
|
+
blockFormName,
|
|
720
|
+
setBlockFormName,
|
|
721
|
+
blockModalNote,
|
|
722
|
+
setBlockModalNote,
|
|
723
|
+
blockResourceId,
|
|
724
|
+
setBlockResourceId,
|
|
725
|
+
blockTimeRange,
|
|
726
|
+
setBlockTimeRange,
|
|
727
|
+
resolvedSlotMinutes,
|
|
728
|
+
visibleCourts,
|
|
729
|
+
blockSaving,
|
|
730
|
+
onClose: () => setShowBlockPanel(false),
|
|
731
|
+
onSave: saveBlockedTime
|
|
732
|
+
}), overflowListModal && /* @__PURE__ */ React.createElement(BookingCalendarOverflowListLayer, {
|
|
733
|
+
overflowPanelRef,
|
|
734
|
+
isLight,
|
|
735
|
+
modal: overflowListModal,
|
|
686
736
|
onClose: () => setOverflowListModal(null),
|
|
687
|
-
|
|
688
|
-
})
|
|
737
|
+
onPickItem: openBookingDetail
|
|
738
|
+
}), moveConfirm && /* @__PURE__ */ React.createElement(MoveConfirmModal, {
|
|
689
739
|
moveConfirm,
|
|
690
740
|
moveDraft,
|
|
691
741
|
setMoveDraft,
|