@dialiq/calendar-component 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BookingDetailsModal.d.ts +11 -0
- package/dist/BookingDetailsModal.d.ts.map +1 -0
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/components/CalendarHeader.d.ts +14 -0
- package/dist/components/CalendarHeader.d.ts.map +1 -0
- package/dist/components/MonthView.d.ts +14 -0
- package/dist/components/MonthView.d.ts.map +1 -0
- package/dist/components/TimeGrid.d.ts +15 -0
- package/dist/components/TimeGrid.d.ts.map +1 -0
- package/dist/index.esm.js +484 -146
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +483 -145
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/calendarUtils.d.ts +12 -0
- package/dist/utils/calendarUtils.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
1
|
+
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
/******************************************************************************
|
|
4
4
|
Copyright (c) Microsoft Corporation.
|
|
@@ -7416,8 +7416,8 @@ function styleInject(css, ref) {
|
|
|
7416
7416
|
}
|
|
7417
7417
|
}
|
|
7418
7418
|
|
|
7419
|
-
var css_248z$
|
|
7420
|
-
styleInject(css_248z$
|
|
7419
|
+
var css_248z$2 = ".modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 1000;\r\n padding: 20px;\r\n}\r\n\r\n.modal-content {\r\n background: #ffffff;\r\n border-radius: 8px;\r\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);\r\n width: 100%;\r\n max-width: 500px;\r\n max-height: 90vh;\r\n overflow-y: auto;\r\n}\r\n\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 20px;\r\n border-bottom: 1px solid #e9ecef;\r\n}\r\n\r\n.modal-header h3 {\r\n margin: 0;\r\n font-size: 20px;\r\n font-weight: 600;\r\n color: #212529;\r\n}\r\n\r\n.modal-close-btn {\r\n background: none;\r\n border: none;\r\n font-size: 32px;\r\n line-height: 1;\r\n color: #6c757d;\r\n cursor: pointer;\r\n padding: 0;\r\n width: 32px;\r\n height: 32px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 4px;\r\n transition: background-color 0.2s;\r\n}\r\n\r\n.modal-close-btn:hover {\r\n background: #e9ecef;\r\n color: #212529;\r\n}\r\n\r\n.modal-body {\r\n padding: 20px;\r\n}\r\n\r\n.modal-footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: 12px;\r\n padding: 20px;\r\n border-top: 1px solid #e9ecef;\r\n}\r\n\r\n.modal-error {\r\n background: #f8d7da;\r\n color: #842029;\r\n padding: 12px;\r\n border-radius: 4px;\r\n margin-bottom: 16px;\r\n font-size: 14px;\r\n}\r\n\r\n.form-group {\r\n margin-bottom: 16px;\r\n}\r\n\r\n.form-group label {\r\n display: block;\r\n font-weight: 500;\r\n font-size: 14px;\r\n color: #212529;\r\n margin-bottom: 6px;\r\n}\r\n\r\n.form-group input,\r\n.form-group textarea {\r\n width: 100%;\r\n padding: 10px 12px;\r\n border: 1px solid #ced4da;\r\n border-radius: 4px;\r\n font-size: 14px;\r\n font-family: inherit;\r\n color: #212529;\r\n transition: border-color 0.2s;\r\n box-sizing: border-box;\r\n}\r\n\r\n.form-group input:focus,\r\n.form-group textarea:focus {\r\n outline: none;\r\n border-color: #0d6efd;\r\n box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.1);\r\n}\r\n\r\n.form-group textarea {\r\n resize: vertical;\r\n}\r\n\r\n.form-group small {\r\n display: block;\r\n margin-top: 4px;\r\n font-size: 12px;\r\n color: #6c757d;\r\n}\r\n\r\n.form-row {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 12px;\r\n}\r\n\r\n.modal-btn {\r\n padding: 10px 20px;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: all 0.2s;\r\n}\r\n\r\n.modal-btn:disabled {\r\n opacity: 0.6;\r\n cursor: not-allowed;\r\n}\r\n\r\n.modal-btn-primary {\r\n background: #0d6efd;\r\n color: #ffffff;\r\n}\r\n\r\n.modal-btn-primary:hover:not(:disabled) {\r\n background: #0b5ed7;\r\n}\r\n\r\n.modal-btn-secondary {\r\n background: #6c757d;\r\n color: #ffffff;\r\n}\r\n\r\n.modal-btn-secondary:hover:not(:disabled) {\r\n background: #5c636a;\r\n}\r\n\r\n@media (max-width: 576px) {\r\n .modal-content {\r\n max-width: 100%;\r\n }\r\n\r\n .form-row {\r\n grid-template-columns: 1fr;\r\n }\r\n}\r\n";
|
|
7420
|
+
styleInject(css_248z$2);
|
|
7421
7421
|
|
|
7422
7422
|
const CreateBookingModal = ({ businessId, apiBaseUrl, initialDate, participants: defaultParticipants, onClose, onBookingCreated, timezone = moment$1.tz.guess(), businessHours, }) => {
|
|
7423
7423
|
const [title, setTitle] = useState('');
|
|
@@ -7529,7 +7529,454 @@ const CreateBookingModal = ({ businessId, apiBaseUrl, initialDate, participants:
|
|
|
7529
7529
|
React.createElement("button", { type: "submit", className: "modal-btn modal-btn-primary", disabled: loading }, loading ? 'Creating...' : 'Create Booking'))))));
|
|
7530
7530
|
};
|
|
7531
7531
|
|
|
7532
|
-
var css_248z = "
|
|
7532
|
+
var css_248z$1 = "/* BookingDetailsModal - extends base modal styles from CreateBookingModal.css */\r\n\r\n.booking-details-modal {\r\n max-width: 500px;\r\n}\r\n\r\n.detail-section {\r\n padding: 12px 0;\r\n border-bottom: 1px solid #e9ecef;\r\n}\r\n\r\n.detail-section:last-child {\r\n border-bottom: none;\r\n}\r\n\r\n.detail-row {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: flex-start;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.detail-row:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.detail-row.full-width {\r\n flex-direction: column;\r\n}\r\n\r\n.detail-label {\r\n font-weight: 500;\r\n color: #6c757d;\r\n font-size: 14px;\r\n min-width: 100px;\r\n}\r\n\r\n.detail-label-muted {\r\n color: #adb5bd;\r\n font-size: 12px;\r\n}\r\n\r\n.detail-value {\r\n font-weight: 500;\r\n color: #212529;\r\n font-size: 14px;\r\n text-align: right;\r\n}\r\n\r\n.detail-value-muted {\r\n color: #adb5bd;\r\n font-size: 12px;\r\n font-family: monospace;\r\n}\r\n\r\n.detail-description {\r\n margin: 8px 0 0 0;\r\n color: #495057;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n white-space: pre-wrap;\r\n}\r\n\r\n.participants-list {\r\n margin: 8px 0 0 0;\r\n padding-left: 20px;\r\n list-style-type: disc;\r\n}\r\n\r\n.participant-item {\r\n color: #495057;\r\n font-size: 14px;\r\n margin-bottom: 4px;\r\n}\r\n\r\n@media (max-width: 576px) {\r\n .booking-details-modal {\r\n max-width: 100%;\r\n }\r\n\r\n .detail-row {\r\n flex-direction: column;\r\n gap: 4px;\r\n }\r\n\r\n .detail-value {\r\n text-align: left;\r\n }\r\n}\r\n";
|
|
7533
|
+
styleInject(css_248z$1);
|
|
7534
|
+
|
|
7535
|
+
const BookingDetailsModal = ({ booking, timezone, onClose, }) => {
|
|
7536
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
7537
|
+
const startMoment = moment$1(booking.start).tz(timezone);
|
|
7538
|
+
const endMoment = moment$1(booking.end).tz(timezone);
|
|
7539
|
+
const durationMinutes = endMoment.diff(startMoment, 'minutes');
|
|
7540
|
+
return (React.createElement("div", { className: "modal-overlay", onClick: onClose },
|
|
7541
|
+
React.createElement("div", { className: "modal-content booking-details-modal", onClick: (e) => e.stopPropagation() },
|
|
7542
|
+
React.createElement("div", { className: "modal-header" },
|
|
7543
|
+
React.createElement("h3", null, ((_a = booking.metadata) === null || _a === void 0 ? void 0 : _a.title) || 'Untitled Booking'),
|
|
7544
|
+
React.createElement("button", { className: "modal-close-btn", onClick: onClose }, "\u00D7")),
|
|
7545
|
+
React.createElement("div", { className: "modal-body" },
|
|
7546
|
+
React.createElement("div", { className: "detail-section" },
|
|
7547
|
+
React.createElement("div", { className: "detail-row" },
|
|
7548
|
+
React.createElement("span", { className: "detail-label" }, "Date:"),
|
|
7549
|
+
React.createElement("span", { className: "detail-value" }, startMoment.format('dddd, MMMM D, YYYY'))),
|
|
7550
|
+
React.createElement("div", { className: "detail-row" },
|
|
7551
|
+
React.createElement("span", { className: "detail-label" }, "Time:"),
|
|
7552
|
+
React.createElement("span", { className: "detail-value" },
|
|
7553
|
+
startMoment.format('h:mm A'),
|
|
7554
|
+
" - ",
|
|
7555
|
+
endMoment.format('h:mm A'))),
|
|
7556
|
+
React.createElement("div", { className: "detail-row" },
|
|
7557
|
+
React.createElement("span", { className: "detail-label" }, "Duration:"),
|
|
7558
|
+
React.createElement("span", { className: "detail-value" }, durationMinutes >= 60
|
|
7559
|
+
? `${Math.floor(durationMinutes / 60)}h ${durationMinutes % 60}m`
|
|
7560
|
+
: `${durationMinutes} minutes`))),
|
|
7561
|
+
((_b = booking.metadata) === null || _b === void 0 ? void 0 : _b.description) && (React.createElement("div", { className: "detail-section" },
|
|
7562
|
+
React.createElement("div", { className: "detail-row full-width" },
|
|
7563
|
+
React.createElement("span", { className: "detail-label" }, "Description:"),
|
|
7564
|
+
React.createElement("p", { className: "detail-description" }, booking.metadata.description)))),
|
|
7565
|
+
((booking.staff || ((_c = booking.metadata) === null || _c === void 0 ? void 0 : _c.staff)) || (booking.room || ((_d = booking.metadata) === null || _d === void 0 ? void 0 : _d.room))) && (React.createElement("div", { className: "detail-section" },
|
|
7566
|
+
(booking.staff || ((_e = booking.metadata) === null || _e === void 0 ? void 0 : _e.staff)) && (React.createElement("div", { className: "detail-row" },
|
|
7567
|
+
React.createElement("span", { className: "detail-label" }, "Staff:"),
|
|
7568
|
+
React.createElement("span", { className: "detail-value" }, booking.staff || ((_f = booking.metadata) === null || _f === void 0 ? void 0 : _f.staff)))),
|
|
7569
|
+
(booking.room || ((_g = booking.metadata) === null || _g === void 0 ? void 0 : _g.room)) && (React.createElement("div", { className: "detail-row" },
|
|
7570
|
+
React.createElement("span", { className: "detail-label" }, "Room:"),
|
|
7571
|
+
React.createElement("span", { className: "detail-value" }, booking.room || ((_h = booking.metadata) === null || _h === void 0 ? void 0 : _h.room)))))),
|
|
7572
|
+
booking.participants && booking.participants.length > 0 && (React.createElement("div", { className: "detail-section" },
|
|
7573
|
+
React.createElement("div", { className: "detail-row full-width" },
|
|
7574
|
+
React.createElement("span", { className: "detail-label" }, "Participants:"),
|
|
7575
|
+
React.createElement("ul", { className: "participants-list" }, booking.participants.map((participant, index) => (React.createElement("li", { key: index, className: "participant-item" }, participant))))))),
|
|
7576
|
+
((_j = booking.metadata) === null || _j === void 0 ? void 0 : _j.organizer) && (React.createElement("div", { className: "detail-section" },
|
|
7577
|
+
React.createElement("div", { className: "detail-row" },
|
|
7578
|
+
React.createElement("span", { className: "detail-label" }, "Organizer:"),
|
|
7579
|
+
React.createElement("span", { className: "detail-value" }, booking.metadata.organizer)))),
|
|
7580
|
+
React.createElement("div", { className: "detail-section" },
|
|
7581
|
+
React.createElement("div", { className: "detail-row" },
|
|
7582
|
+
React.createElement("span", { className: "detail-label detail-label-muted" }, "Meeting ID:"),
|
|
7583
|
+
React.createElement("span", { className: "detail-value detail-value-muted" }, booking.meeting_id)))),
|
|
7584
|
+
React.createElement("div", { className: "modal-footer" },
|
|
7585
|
+
React.createElement("button", { className: "modal-btn modal-btn-secondary", onClick: onClose }, "Close")))));
|
|
7586
|
+
};
|
|
7587
|
+
|
|
7588
|
+
const getWeekDays = (currentDate, view, businessHours) => {
|
|
7589
|
+
const startOfWeek = currentDate.clone().startOf('week');
|
|
7590
|
+
const days = [];
|
|
7591
|
+
// Generate all 7 days of the week first
|
|
7592
|
+
for (let i = 0; i < 7; i++) {
|
|
7593
|
+
days.push(startOfWeek.clone().add(i, 'days'));
|
|
7594
|
+
}
|
|
7595
|
+
if (view === 'workWeek') {
|
|
7596
|
+
if (businessHours) {
|
|
7597
|
+
// Filter based on business hours
|
|
7598
|
+
return days.filter(day => {
|
|
7599
|
+
var _a;
|
|
7600
|
+
const dayName = day.format('dddd');
|
|
7601
|
+
return !((_a = businessHours[dayName]) === null || _a === void 0 ? void 0 : _a.isClosed);
|
|
7602
|
+
});
|
|
7603
|
+
}
|
|
7604
|
+
else {
|
|
7605
|
+
// Default to 7 days (24/7) if no business hours provided
|
|
7606
|
+
return days;
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
return days;
|
|
7610
|
+
};
|
|
7611
|
+
const calculateEventPositions = (bookings, timezone) => {
|
|
7612
|
+
const positions = new Map();
|
|
7613
|
+
// Sort bookings by start time
|
|
7614
|
+
const sortedBookings = [...bookings].sort((a, b) => {
|
|
7615
|
+
const startA = moment$1(a.start).tz(timezone);
|
|
7616
|
+
const startB = moment$1(b.start).tz(timezone);
|
|
7617
|
+
return startA.diff(startB);
|
|
7618
|
+
});
|
|
7619
|
+
// Group overlapping events
|
|
7620
|
+
const columns = [];
|
|
7621
|
+
sortedBookings.forEach(booking => {
|
|
7622
|
+
const start = moment$1(booking.start).tz(timezone);
|
|
7623
|
+
const end = moment$1(booking.end).tz(timezone);
|
|
7624
|
+
// Simple vertical position calculation (0-24 hours mapped to 0-100%)
|
|
7625
|
+
const startMinutes = start.hours() * 60 + start.minutes();
|
|
7626
|
+
const durationMinutes = end.diff(start, 'minutes');
|
|
7627
|
+
const top = (startMinutes / 1440) * 100;
|
|
7628
|
+
const height = (durationMinutes / 1440) * 100;
|
|
7629
|
+
// Overlap logic: Find a column where this event fits
|
|
7630
|
+
let placed = false;
|
|
7631
|
+
for (let i = 0; i < columns.length; i++) {
|
|
7632
|
+
const column = columns[i];
|
|
7633
|
+
const lastInColumn = column[column.length - 1];
|
|
7634
|
+
const lastInColumnEnd = moment$1(lastInColumn.end).tz(timezone);
|
|
7635
|
+
if (start.isSameOrAfter(lastInColumnEnd)) {
|
|
7636
|
+
column.push(booking);
|
|
7637
|
+
placed = true;
|
|
7638
|
+
break;
|
|
7639
|
+
}
|
|
7640
|
+
}
|
|
7641
|
+
if (!placed) {
|
|
7642
|
+
columns.push([booking]);
|
|
7643
|
+
}
|
|
7644
|
+
// Store temporary data to be refined
|
|
7645
|
+
positions.set(booking.meeting_id, { top, height, left: 0, width: 0 }); // Placeholder
|
|
7646
|
+
});
|
|
7647
|
+
// Refine positions based on columns
|
|
7648
|
+
// Let's try a standard "expand to fill" algorithm
|
|
7649
|
+
// Iterate through sorted bookings again to find "clusters"
|
|
7650
|
+
let cluster = [];
|
|
7651
|
+
let clusterEnd = null;
|
|
7652
|
+
for (let i = 0; i < sortedBookings.length; i++) {
|
|
7653
|
+
const booking = sortedBookings[i];
|
|
7654
|
+
const start = moment$1(booking.start).tz(timezone);
|
|
7655
|
+
const end = moment$1(booking.end).tz(timezone);
|
|
7656
|
+
if (clusterEnd === null || start.isBefore(clusterEnd)) {
|
|
7657
|
+
cluster.push(booking);
|
|
7658
|
+
if (clusterEnd === null || end.isAfter(clusterEnd)) {
|
|
7659
|
+
clusterEnd = end;
|
|
7660
|
+
}
|
|
7661
|
+
}
|
|
7662
|
+
else {
|
|
7663
|
+
// Process completed cluster
|
|
7664
|
+
processCluster(cluster, positions, timezone);
|
|
7665
|
+
cluster = [booking];
|
|
7666
|
+
clusterEnd = end;
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
// Process last cluster
|
|
7670
|
+
if (cluster.length > 0) {
|
|
7671
|
+
processCluster(cluster, positions, timezone);
|
|
7672
|
+
}
|
|
7673
|
+
return positions;
|
|
7674
|
+
};
|
|
7675
|
+
const processCluster = (cluster, positions, timezone) => {
|
|
7676
|
+
// This is a simplified layout algorithm.
|
|
7677
|
+
// We will use a "columns" approach where we pack events into columns.
|
|
7678
|
+
const columns = [];
|
|
7679
|
+
cluster.forEach(booking => {
|
|
7680
|
+
const start = moment$1(booking.start).tz(timezone);
|
|
7681
|
+
let placed = false;
|
|
7682
|
+
for (let i = 0; i < columns.length; i++) {
|
|
7683
|
+
const lastInCol = columns[i][columns[i].length - 1];
|
|
7684
|
+
const lastEnd = moment$1(lastInCol.end).tz(timezone);
|
|
7685
|
+
if (start.isSameOrAfter(lastEnd)) {
|
|
7686
|
+
columns[i].push(booking);
|
|
7687
|
+
placed = true;
|
|
7688
|
+
break;
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7691
|
+
if (!placed) {
|
|
7692
|
+
columns.push([booking]);
|
|
7693
|
+
}
|
|
7694
|
+
});
|
|
7695
|
+
const numColumns = columns.length;
|
|
7696
|
+
const width = 100 / numColumns;
|
|
7697
|
+
columns.forEach((col, colIndex) => {
|
|
7698
|
+
col.forEach(booking => {
|
|
7699
|
+
const pos = positions.get(booking.meeting_id);
|
|
7700
|
+
if (pos) {
|
|
7701
|
+
pos.left = colIndex * width;
|
|
7702
|
+
pos.width = width;
|
|
7703
|
+
positions.set(booking.meeting_id, pos);
|
|
7704
|
+
}
|
|
7705
|
+
});
|
|
7706
|
+
});
|
|
7707
|
+
};
|
|
7708
|
+
const isMobile = () => {
|
|
7709
|
+
if (typeof window !== 'undefined') {
|
|
7710
|
+
return window.innerWidth < 768;
|
|
7711
|
+
}
|
|
7712
|
+
return false;
|
|
7713
|
+
};
|
|
7714
|
+
|
|
7715
|
+
const CalendarHeader = ({ title, currentView, onViewChange, onNext, onPrevious, onToday, onCreateClick, dateRangeText, }) => {
|
|
7716
|
+
const mobile = isMobile();
|
|
7717
|
+
return (React.createElement("div", { className: "calendar-header" },
|
|
7718
|
+
React.createElement("div", { className: "calendar-title-section" },
|
|
7719
|
+
React.createElement("h2", { className: "calendar-title" }, title),
|
|
7720
|
+
React.createElement("div", { className: "calendar-view-toggle" },
|
|
7721
|
+
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'month' ? 'active' : ''}`, onClick: () => onViewChange('month'), disabled: mobile, style: mobile ? { display: 'none' } : {} }, "Month"),
|
|
7722
|
+
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'week' ? 'active' : ''}`, onClick: () => onViewChange('week'), disabled: mobile, style: mobile ? { display: 'none' } : {} }, "Week"),
|
|
7723
|
+
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'workWeek' ? 'active' : ''}`, onClick: () => onViewChange('workWeek'), disabled: mobile, style: mobile ? { display: 'none' } : {} }, "Work Week"),
|
|
7724
|
+
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'day' ? 'active' : ''}`, onClick: () => onViewChange('day') }, "Day")),
|
|
7725
|
+
React.createElement("button", { className: "calendar-btn calendar-btn-create", onClick: onCreateClick }, "+ Create Booking")),
|
|
7726
|
+
React.createElement("div", { className: "calendar-navigation" },
|
|
7727
|
+
React.createElement("button", { className: "calendar-btn", onClick: onPrevious }, "\u2039"),
|
|
7728
|
+
React.createElement("button", { className: "calendar-btn", onClick: onToday }, "Today"),
|
|
7729
|
+
React.createElement("button", { className: "calendar-btn", onClick: onNext }, "\u203A"),
|
|
7730
|
+
React.createElement("span", { className: "calendar-current-month" }, dateRangeText))));
|
|
7731
|
+
};
|
|
7732
|
+
|
|
7733
|
+
const TimeGrid = ({ currentDate, view, bookings, timezone, onBookingClick, onTimeSlotClick, businessHours, }) => {
|
|
7734
|
+
const days = useMemo(() => {
|
|
7735
|
+
if (view === 'day') {
|
|
7736
|
+
return [moment$1(currentDate).tz(timezone)];
|
|
7737
|
+
}
|
|
7738
|
+
return getWeekDays(moment$1(currentDate).tz(timezone), view, businessHours);
|
|
7739
|
+
}, [currentDate, view, timezone, businessHours]);
|
|
7740
|
+
const { displayStart, displayEnd, timeLabels } = useMemo(() => {
|
|
7741
|
+
if (!businessHours) {
|
|
7742
|
+
return {
|
|
7743
|
+
displayStart: 0,
|
|
7744
|
+
displayEnd: 1440,
|
|
7745
|
+
timeLabels: Array.from({ length: 24 }, (_, i) => i * 60)
|
|
7746
|
+
};
|
|
7747
|
+
}
|
|
7748
|
+
let longestDayOpen = 0;
|
|
7749
|
+
let longestDayClose = 0;
|
|
7750
|
+
let maxDuration = 0;
|
|
7751
|
+
days.forEach(day => {
|
|
7752
|
+
const dayName = day.format('dddd');
|
|
7753
|
+
const dayHours = businessHours[dayName];
|
|
7754
|
+
console.log('Day:', dayName, 'Hours:', dayHours);
|
|
7755
|
+
if (!(dayHours === null || dayHours === void 0 ? void 0 : dayHours.isClosed) && (dayHours === null || dayHours === void 0 ? void 0 : dayHours.openTime) && (dayHours === null || dayHours === void 0 ? void 0 : dayHours.closeTime)) {
|
|
7756
|
+
const openTime = moment$1(dayHours.openTime, ['h:mm A', 'hh:mm A'], true);
|
|
7757
|
+
const closeTime = moment$1(dayHours.closeTime, ['h:mm A', 'hh:mm A'], true);
|
|
7758
|
+
if (!openTime.isValid() || !closeTime.isValid()) {
|
|
7759
|
+
console.warn('Invalid time format:', dayHours.openTime, dayHours.closeTime);
|
|
7760
|
+
return;
|
|
7761
|
+
}
|
|
7762
|
+
const openMinutes = openTime.hours() * 60 + openTime.minutes();
|
|
7763
|
+
const closeMinutes = closeTime.hours() * 60 + closeTime.minutes();
|
|
7764
|
+
const duration = closeMinutes - openMinutes;
|
|
7765
|
+
console.log(' Open:', dayHours.openTime, '->', openMinutes, '| Close:', dayHours.closeTime, '->', closeMinutes, '| Duration:', duration);
|
|
7766
|
+
if (openMinutes < closeMinutes && duration > maxDuration) {
|
|
7767
|
+
maxDuration = duration;
|
|
7768
|
+
longestDayOpen = openMinutes;
|
|
7769
|
+
longestDayClose = closeMinutes;
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
});
|
|
7773
|
+
console.log('Longest: open =', longestDayOpen, 'close =', longestDayClose);
|
|
7774
|
+
if (maxDuration === 0) {
|
|
7775
|
+
return {
|
|
7776
|
+
displayStart: 0,
|
|
7777
|
+
displayEnd: 1440,
|
|
7778
|
+
timeLabels: Array.from({ length: 24 }, (_, i) => i * 60)
|
|
7779
|
+
};
|
|
7780
|
+
}
|
|
7781
|
+
const displayStart = Math.max(0, longestDayOpen - 60);
|
|
7782
|
+
const displayEnd = Math.min(1440, longestDayClose + 60);
|
|
7783
|
+
const labels = [];
|
|
7784
|
+
const startHour = Math.floor(displayStart / 60);
|
|
7785
|
+
const endHour = Math.floor(displayEnd / 60);
|
|
7786
|
+
for (let h = startHour; h <= endHour; h++) {
|
|
7787
|
+
const hourInMinutes = h * 60;
|
|
7788
|
+
if (hourInMinutes < displayEnd && hourInMinutes >= displayStart) {
|
|
7789
|
+
labels.push(hourInMinutes);
|
|
7790
|
+
}
|
|
7791
|
+
}
|
|
7792
|
+
return { displayStart, displayEnd, timeLabels: labels };
|
|
7793
|
+
}, [days, businessHours]);
|
|
7794
|
+
const visibleBookings = useMemo(() => {
|
|
7795
|
+
if (days.length === 0)
|
|
7796
|
+
return [];
|
|
7797
|
+
const start = days[0].clone().startOf('day');
|
|
7798
|
+
const end = days[days.length - 1].clone().endOf('day');
|
|
7799
|
+
return bookings.filter(b => {
|
|
7800
|
+
const bStart = moment$1(b.start).tz(timezone);
|
|
7801
|
+
const bEnd = moment$1(b.end).tz(timezone);
|
|
7802
|
+
return bStart.isBefore(end) && bEnd.isAfter(start);
|
|
7803
|
+
});
|
|
7804
|
+
}, [bookings, days, timezone]);
|
|
7805
|
+
const dayPositions = useMemo(() => {
|
|
7806
|
+
const positions = new Map();
|
|
7807
|
+
const totalMinutes = displayEnd - displayStart;
|
|
7808
|
+
days.forEach(day => {
|
|
7809
|
+
const dayBookings = visibleBookings.filter(b => moment$1(b.start).tz(timezone).isSame(day, 'day'));
|
|
7810
|
+
const dayPos = calculateEventPositions(dayBookings, timezone);
|
|
7811
|
+
dayPos.forEach((pos, id) => {
|
|
7812
|
+
const startMinutes = (pos.top / 100) * 1440;
|
|
7813
|
+
const durationMinutes = (pos.height / 100) * 1440;
|
|
7814
|
+
const newTop = ((startMinutes - displayStart) / totalMinutes) * 100;
|
|
7815
|
+
const newHeight = (durationMinutes / totalMinutes) * 100;
|
|
7816
|
+
positions.set(id, Object.assign(Object.assign({}, pos), { top: Math.max(0, newTop), height: newHeight }));
|
|
7817
|
+
});
|
|
7818
|
+
});
|
|
7819
|
+
return positions;
|
|
7820
|
+
}, [visibleBookings, days, timezone, displayStart, displayEnd]);
|
|
7821
|
+
return (React.createElement("div", { className: "time-grid-container" },
|
|
7822
|
+
React.createElement("div", { className: "time-grid-header" },
|
|
7823
|
+
React.createElement("div", { className: "time-column-header", style: { width: '60px', minWidth: '60px' } }),
|
|
7824
|
+
days.map(day => {
|
|
7825
|
+
var _a;
|
|
7826
|
+
const dayName = day.format('dddd');
|
|
7827
|
+
const isClosed = (_a = businessHours === null || businessHours === void 0 ? void 0 : businessHours[dayName]) === null || _a === void 0 ? void 0 : _a.isClosed;
|
|
7828
|
+
const isToday = day.isSame(moment$1().tz(timezone), 'day');
|
|
7829
|
+
return (React.createElement("div", { key: day.toString(), className: `day-column-header ${isToday ? 'today' : ''} ${isClosed ? 'closed' : ''}` },
|
|
7830
|
+
React.createElement("div", { className: "day-name" }, day.format('ddd')),
|
|
7831
|
+
React.createElement("div", { className: "day-number" }, day.format('D')),
|
|
7832
|
+
isClosed && React.createElement("span", { className: "closed-label" }, "Closed")));
|
|
7833
|
+
})),
|
|
7834
|
+
React.createElement("div", { className: "time-grid-body" },
|
|
7835
|
+
React.createElement("div", { className: "time-labels-column" }, timeLabels.map(minutes => (React.createElement("div", { key: minutes, className: "time-label" }, moment$1().startOf('day').add(minutes, 'minutes').format('h A'))))),
|
|
7836
|
+
React.createElement("div", { className: "days-grid", style: { height: `${(displayEnd - displayStart)}px` } },
|
|
7837
|
+
React.createElement("div", { className: "grid-lines" }, timeLabels.map(minutes => (React.createElement("div", { key: minutes, className: "grid-hour-row" })))),
|
|
7838
|
+
days.map(day => {
|
|
7839
|
+
const dayName = day.format('dddd');
|
|
7840
|
+
const businessDay = businessHours === null || businessHours === void 0 ? void 0 : businessHours[dayName];
|
|
7841
|
+
const isClosed = businessDay === null || businessDay === void 0 ? void 0 : businessDay.isClosed;
|
|
7842
|
+
let closedBlocks = [];
|
|
7843
|
+
if (!isClosed && businessDay && businessDay.openTime && businessDay.closeTime) {
|
|
7844
|
+
const openTime = moment$1(businessDay.openTime, ['h:mm A', 'hh:mm A'], true);
|
|
7845
|
+
const closeTime = moment$1(businessDay.closeTime, ['h:mm A', 'hh:mm A'], true);
|
|
7846
|
+
const openMinutes = openTime.hours() * 60 + openTime.minutes();
|
|
7847
|
+
const closeMinutes = closeTime.hours() * 60 + closeTime.minutes();
|
|
7848
|
+
const totalMinutes = displayEnd - displayStart;
|
|
7849
|
+
if (openMinutes < closeMinutes) {
|
|
7850
|
+
const preOpenEnd = openMinutes - 60;
|
|
7851
|
+
if (preOpenEnd > displayStart) {
|
|
7852
|
+
const height = ((preOpenEnd - displayStart) / totalMinutes) * 100;
|
|
7853
|
+
closedBlocks.push(React.createElement("div", { key: "pre-open", className: "calendar-closed-block", style: { top: '0%', height: `${height}%` } },
|
|
7854
|
+
React.createElement("span", { className: "closed-block-label" }, "Closed")));
|
|
7855
|
+
}
|
|
7856
|
+
const bufferStart = Math.max(displayStart, openMinutes - 60);
|
|
7857
|
+
const bufferEnd = openMinutes;
|
|
7858
|
+
if (bufferStart < bufferEnd) {
|
|
7859
|
+
const top = ((bufferStart - displayStart) / totalMinutes) * 100;
|
|
7860
|
+
const height = ((bufferEnd - bufferStart) / totalMinutes) * 100;
|
|
7861
|
+
closedBlocks.push(React.createElement("div", { key: "morning", className: "calendar-closed-block", style: { top: `${top}%`, height: `${height}%` } },
|
|
7862
|
+
React.createElement("span", { className: "closed-block-label" }, "Closed")));
|
|
7863
|
+
}
|
|
7864
|
+
const bufferCloseStart = closeMinutes;
|
|
7865
|
+
const bufferCloseEnd = Math.min(displayEnd, closeMinutes + 60);
|
|
7866
|
+
if (bufferCloseStart < bufferCloseEnd) {
|
|
7867
|
+
const top = ((bufferCloseStart - displayStart) / totalMinutes) * 100;
|
|
7868
|
+
const height = ((bufferCloseEnd - bufferCloseStart) / totalMinutes) * 100;
|
|
7869
|
+
closedBlocks.push(React.createElement("div", { key: "evening", className: "calendar-closed-block", style: { top: `${top}%`, height: `${height}%` } },
|
|
7870
|
+
React.createElement("span", { className: "closed-block-label" }, "Closed")));
|
|
7871
|
+
}
|
|
7872
|
+
const postCloseStart = closeMinutes + 60;
|
|
7873
|
+
if (postCloseStart < displayEnd) {
|
|
7874
|
+
const top = ((postCloseStart - displayStart) / totalMinutes) * 100;
|
|
7875
|
+
const height = ((displayEnd - postCloseStart) / totalMinutes) * 100;
|
|
7876
|
+
closedBlocks.push(React.createElement("div", { key: "post-close", className: "calendar-closed-block", style: { top: `${top}%`, height: `${height}%` } },
|
|
7877
|
+
React.createElement("span", { className: "closed-block-label" }, "Closed")));
|
|
7878
|
+
}
|
|
7879
|
+
}
|
|
7880
|
+
}
|
|
7881
|
+
return (React.createElement("div", { key: day.toString(), className: `day-column ${isClosed ? 'closed-column' : ''}`, onClick: (e) => {
|
|
7882
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
7883
|
+
const y = e.clientY - rect.top;
|
|
7884
|
+
const height = rect.height;
|
|
7885
|
+
const percentage = y / height;
|
|
7886
|
+
const totalMinutes = displayEnd - displayStart;
|
|
7887
|
+
const minutesFromDisplayStart = percentage * totalMinutes;
|
|
7888
|
+
const minutesFromMidnight = displayStart + minutesFromDisplayStart;
|
|
7889
|
+
const clickedTime = day.clone().startOf('day').add(minutesFromMidnight, 'minutes');
|
|
7890
|
+
if (!isClosed && businessDay && businessDay.openTime && businessDay.closeTime) {
|
|
7891
|
+
const openTime = moment$1(businessDay.openTime, ['h:mm A', 'hh:mm A'], true);
|
|
7892
|
+
const closeTime = moment$1(businessDay.closeTime, ['h:mm A', 'hh:mm A'], true);
|
|
7893
|
+
const clickedMinutes = clickedTime.hours() * 60 + clickedTime.minutes();
|
|
7894
|
+
const openMinutes = openTime.hours() * 60 + openTime.minutes();
|
|
7895
|
+
const closeMinutes = closeTime.hours() * 60 + closeTime.minutes();
|
|
7896
|
+
if (clickedMinutes < openMinutes || clickedMinutes > closeMinutes) {
|
|
7897
|
+
return;
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7900
|
+
if (!isClosed)
|
|
7901
|
+
onTimeSlotClick(clickedTime);
|
|
7902
|
+
} },
|
|
7903
|
+
closedBlocks,
|
|
7904
|
+
visibleBookings
|
|
7905
|
+
.filter(b => moment$1(b.start).tz(timezone).isSame(day, 'day'))
|
|
7906
|
+
.map(booking => {
|
|
7907
|
+
var _a;
|
|
7908
|
+
const pos = dayPositions.get(booking.meeting_id);
|
|
7909
|
+
if (!pos)
|
|
7910
|
+
return null;
|
|
7911
|
+
return (React.createElement("div", { key: booking.meeting_id, className: `calendar-event ${booking.className || ''}`, style: {
|
|
7912
|
+
top: `${pos.top}%`,
|
|
7913
|
+
height: `${pos.height}%`,
|
|
7914
|
+
left: `${pos.left}%`,
|
|
7915
|
+
width: `${pos.width}%`,
|
|
7916
|
+
position: 'absolute'
|
|
7917
|
+
}, onClick: (e) => {
|
|
7918
|
+
e.stopPropagation();
|
|
7919
|
+
if (onBookingClick)
|
|
7920
|
+
onBookingClick(booking);
|
|
7921
|
+
} },
|
|
7922
|
+
React.createElement("div", { className: "event-content" },
|
|
7923
|
+
React.createElement("div", { className: "event-title" }, ((_a = booking.metadata) === null || _a === void 0 ? void 0 : _a.title) || 'Untitled'),
|
|
7924
|
+
React.createElement("div", { className: "event-time" },
|
|
7925
|
+
moment$1(booking.start).tz(timezone).format('h:mm A'),
|
|
7926
|
+
" - ",
|
|
7927
|
+
moment$1(booking.end).tz(timezone).format('h:mm A')))));
|
|
7928
|
+
})));
|
|
7929
|
+
})))));
|
|
7930
|
+
};
|
|
7931
|
+
|
|
7932
|
+
const MonthView = ({ currentDate, bookings, timezone, onBookingClick, onDateClick, businessHours, }) => {
|
|
7933
|
+
var _a;
|
|
7934
|
+
const startOfMonth = moment$1(currentDate).tz(timezone).startOf('month');
|
|
7935
|
+
const endOfMonth = moment$1(currentDate).tz(timezone).endOf('month');
|
|
7936
|
+
const startDate = startOfMonth.clone().startOf('week');
|
|
7937
|
+
const endDate = endOfMonth.clone().endOf('week');
|
|
7938
|
+
const rows = [];
|
|
7939
|
+
let days = [];
|
|
7940
|
+
let day = startDate.clone();
|
|
7941
|
+
while (day.isSameOrBefore(endDate, 'day')) {
|
|
7942
|
+
for (let i = 0; i < 7; i++) {
|
|
7943
|
+
const currentDay = day.clone();
|
|
7944
|
+
const dayBookings = bookings.filter(b => moment$1(b.start).tz(timezone).isSame(currentDay, 'day'));
|
|
7945
|
+
const isCurrentMonth = currentDay.isSame(startOfMonth, 'month');
|
|
7946
|
+
const isToday = currentDay.isSame(moment$1().tz(timezone), 'day');
|
|
7947
|
+
const dayName = currentDay.format('dddd');
|
|
7948
|
+
const isClosed = (_a = businessHours === null || businessHours === void 0 ? void 0 : businessHours[dayName]) === null || _a === void 0 ? void 0 : _a.isClosed;
|
|
7949
|
+
days.push(React.createElement("div", { key: day.toString(), className: `calendar-cell ${!isCurrentMonth ? 'calendar-cell-disabled' : ''} ${isToday ? 'calendar-cell-today' : ''} ${isClosed ? 'calendar-cell-closed' : ''}`, onClick: () => isCurrentMonth && !isClosed && onDateClick(currentDay) },
|
|
7950
|
+
React.createElement("div", { className: "calendar-cell-header" },
|
|
7951
|
+
React.createElement("span", { className: "calendar-cell-number" }, currentDay.format('D')),
|
|
7952
|
+
isClosed && React.createElement("span", { className: "calendar-closed-label-small" }, "Closed")),
|
|
7953
|
+
React.createElement("div", { className: "calendar-cell-bookings" },
|
|
7954
|
+
dayBookings.slice(0, 3).map((booking) => {
|
|
7955
|
+
var _a;
|
|
7956
|
+
return (React.createElement("div", { key: booking.meeting_id, className: `calendar-booking ${booking.className || ''}`, onClick: (e) => {
|
|
7957
|
+
e.stopPropagation();
|
|
7958
|
+
if (onBookingClick) {
|
|
7959
|
+
onBookingClick(booking);
|
|
7960
|
+
}
|
|
7961
|
+
} },
|
|
7962
|
+
React.createElement("span", { className: "calendar-booking-time" }, moment$1(booking.start).tz(timezone).format('HH:mm')),
|
|
7963
|
+
React.createElement("span", { className: "calendar-booking-title" }, ((_a = booking.metadata) === null || _a === void 0 ? void 0 : _a.title) || 'Untitled')));
|
|
7964
|
+
}),
|
|
7965
|
+
dayBookings.length > 3 && (React.createElement("div", { className: "calendar-booking-more" },
|
|
7966
|
+
"+",
|
|
7967
|
+
dayBookings.length - 3,
|
|
7968
|
+
" more")))));
|
|
7969
|
+
day.add(1, 'days');
|
|
7970
|
+
}
|
|
7971
|
+
rows.push(React.createElement("div", { key: day.toString(), className: "calendar-row" }, days));
|
|
7972
|
+
days = [];
|
|
7973
|
+
}
|
|
7974
|
+
return (React.createElement("div", { className: "month-view-container" },
|
|
7975
|
+
React.createElement("div", { className: "calendar-days-row" }, ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => (React.createElement("div", { key: d, className: "calendar-day-header" }, d)))),
|
|
7976
|
+
React.createElement("div", { className: "calendar-body" }, rows)));
|
|
7977
|
+
};
|
|
7978
|
+
|
|
7979
|
+
var css_248z = ".calendar-container {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n background-color: #fff;\n display: flex;\n flex-direction: column;\n height: 100%;\n /* Use 100% height of parent */\n max-height: 100vh;\n /* Ensure it doesn't exceed viewport */\n min-height: 600px;\n /* Minimum height fallback */\n /* Minimum height fallback */\n overflow: hidden;\n}\n\n/* Header */\n.calendar-header {\n padding: 16px;\n border-bottom: 1px solid #e0e0e0;\n background-color: #fff;\n}\n\n.calendar-title-section {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n flex-wrap: wrap;\n gap: 10px;\n}\n\n.calendar-title {\n margin: 0;\n font-size: 1.5rem;\n color: #333;\n}\n\n.calendar-view-toggle {\n display: flex;\n background-color: #f5f5f5;\n border-radius: 6px;\n padding: 2px;\n}\n\n.calendar-btn {\n padding: 8px 16px;\n border: 1px solid #e0e0e0;\n background-color: #fff;\n cursor: pointer;\n border-radius: 4px;\n font-size: 14px;\n transition: all 0.2s;\n}\n\n.calendar-btn:hover {\n background-color: #f5f5f5;\n}\n\n.calendar-btn-view {\n border: none;\n background: transparent;\n border-radius: 4px;\n padding: 6px 12px;\n}\n\n.calendar-btn-view.active {\n background-color: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n font-weight: 600;\n}\n\n.calendar-btn-create {\n background-color: #0078d4;\n color: white;\n border: none;\n}\n\n.calendar-btn-create:hover {\n background-color: #106ebe;\n}\n\n.calendar-navigation {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.calendar-current-month {\n font-size: 1.1rem;\n font-weight: 600;\n margin-left: 16px;\n color: #333;\n}\n\n/* Content Area */\n.calendar-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n/* Month View */\n.month-view-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n /* Prevent container scrolling, let body scroll */\n}\n\n.calendar-days-row {\n display: grid;\n grid-template-columns: repeat(7, 1fr);\n /* Ensure equal columns */\n border-bottom: 1px solid #e0e0e0;\n background-color: #f9f9f9;\n}\n\n.calendar-day-header {\n padding: 10px;\n text-align: center;\n font-weight: 600;\n color: #666;\n font-size: 0.9rem;\n overflow: hidden;\n /* Prevent overflow */\n text-overflow: ellipsis;\n}\n\n.calendar-body {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow-y: auto;\n /* Scrollable body */\n}\n\n.calendar-row {\n display: grid;\n grid-template-columns: repeat(7, 1fr);\n /* Ensure equal columns */\n flex: 1;\n min-height: 100px;\n /* Minimum row height */\n border-bottom: 1px solid #e0e0e0;\n}\n\n.calendar-cell {\n border-right: 1px solid #e0e0e0;\n padding: 8px;\n position: relative;\n cursor: pointer;\n transition: background-color 0.2s;\n overflow: hidden;\n /* Prevent cell expansion */\n min-width: 0;\n /* Allow shrinking below content size */\n}\n\n.calendar-cell:hover {\n background-color: #fcfcfc;\n}\n\n.calendar-cell-disabled {\n background-color: #f9f9f9;\n color: #999;\n}\n\n.calendar-cell-today {\n background-color: #e6f2ff;\n}\n\n.calendar-cell-closed {\n background-color: #f5f5f5;\n cursor: not-allowed;\n}\n\n.calendar-cell-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 4px;\n}\n\n.calendar-cell-number {\n font-weight: 600;\n font-size: 0.9rem;\n}\n\n.calendar-closed-label-small {\n font-size: 0.7rem;\n color: #d13438;\n background: #fde7e9;\n padding: 2px 4px;\n border-radius: 3px;\n}\n\n.calendar-cell-bookings {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.calendar-booking {\n background-color: #e1dfdd;\n /* Default neutral color */\n border-left: 3px solid #0078d4;\n padding: 4px 6px;\n border-radius: 2px;\n font-size: 0.8rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n position: relative;\n /* For tooltip positioning */\n}\n\n.calendar-booking:hover {\n background-color: #d0d0d0;\n}\n\n.calendar-booking-time {\n font-weight: 600;\n margin-right: 4px;\n}\n\n.calendar-booking-more {\n font-size: 0.8rem;\n color: #666;\n padding: 2px 4px;\n}\n\n/* Time Grid (Day/Week/WorkWeek) */\n.time-grid-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n}\n\n.time-grid-header {\n display: flex;\n border-bottom: 1px solid #e0e0e0;\n padding-right: 17px;\n /* Adjust for scrollbar */\n}\n\n.time-column-header {\n width: 60px;\n /* Width of time labels column */\n flex-shrink: 0;\n border-right: 1px solid #e0e0e0;\n}\n\n.day-column-header {\n flex: 1;\n text-align: center;\n padding: 8px;\n border-right: 1px solid #e0e0e0;\n background-color: #f9f9f9;\n min-width: 0;\n /* Prevent expansion */\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.day-column-header.today {\n background-color: #e6f2ff;\n color: #0078d4;\n}\n\n.day-column-header.closed {\n background-color: #f0f0f0;\n color: #999;\n}\n\n.day-name {\n font-size: 0.8rem;\n text-transform: uppercase;\n color: #666;\n}\n\n.day-number {\n font-size: 1.2rem;\n font-weight: 600;\n}\n\n.time-grid-body {\n display: flex;\n flex: 1;\n overflow-y: auto;\n position: relative;\n}\n\n.time-labels-column {\n width: 60px;\n flex-shrink: 0;\n border-right: 1px solid #e0e0e0;\n background-color: #fff;\n}\n\n.time-label {\n height: 60px;\n /* 1 hour height */\n text-align: right;\n padding-right: 8px;\n font-size: 0.75rem;\n color: #666;\n justify-content: center;\n}\n\n.days-grid {\n flex: 1;\n display: flex;\n position: relative;\n /* 24 hours * 60px/hour = 1440px total height */\n height: 1440px;\n}\n\n.grid-lines {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 0;\n}\n\n.grid-hour-row {\n height: 60px;\n border-bottom: 1px solid #e0e0e0;\n box-sizing: border-box;\n}\n\n.grid-hour-row:last-child {\n border-bottom: none;\n}\n\n.day-column {\n flex: 1;\n border-right: 1px solid #e0e0e0;\n position: relative;\n height: 100%;\n min-width: 0;\n /* Critical for flex containers to not expand based on content */\n}\n\n.day-column.closed-column {\n background-color: repeating-linear-gradient(45deg,\n #fbfbfb,\n #fbfbfb 10px,\n #f5f5f5 10px,\n #f5f5f5 20px);\n}\n\n.calendar-event {\n background-color: #e1dfdd;\n border-left: 4px solid #0078d4;\n border-radius: 3px;\n padding: 2px 4px;\n font-size: 0.75rem;\n overflow: hidden;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n z-index: 1;\n transition: transform 0.1s, z-index 0.1s;\n}\n\n.calendar-event:hover {\n z-index: 10;\n /* Bring to front on hover */\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.event-content {\n height: 100%;\n overflow: hidden;\n}\n\n.event-title {\n font-weight: 600;\n margin-bottom: 2px;\n}\n\n.event-time {\n font-size: 0.7rem;\n color: #555;\n}\n\n/* Tooltip Styles */\n.event-tooltip {\n display: none;\n position: absolute;\n left: 100%;\n top: 0;\n background: white;\n border: 1px solid #ccc;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 10px;\n border-radius: 4px;\n width: 200px;\n z-index: 100;\n pointer-events: none;\n}\n\n/* Show tooltip on hover */\n.calendar-booking:hover .event-tooltip,\n.calendar-event:hover .event-tooltip {\n display: block;\n}\n\n/* Adjust tooltip position if it goes offscreen (simple right-side check) */\n/* Note: A real production app would use a library like Popper.js */\n.day-column:last-child .event-tooltip,\n.day-column:nth-last-child(2) .event-tooltip {\n left: auto;\n right: 100%;\n}\n\n.tooltip-title {\n font-weight: bold;\n margin-bottom: 8px;\n border-bottom: 1px solid #eee;\n padding-bottom: 4px;\n}\n\n.tooltip-row {\n display: flex;\n justify-content: space-between;\n margin-bottom: 4px;\n font-size: 0.85rem;\n}\n\n.tooltip-label {\n color: #666;\n margin-right: 8px;\n}\n\n.tooltip-value {\n font-weight: 500;\n text-align: right;\n}\n\n/* Loading & Error */\n.calendar-loading,\n.calendar-error {\n padding: 20px;\n text-align: center;\n color: #666;\n}\n\n.calendar-error {\n color: #d13438;\n}\n\n/* Closed Block Styles */\n.calendar-closed-block {\n position: absolute;\n left: 0;\n right: 0;\n background-color: #f0f0f0;\n background-image: repeating-linear-gradient(45deg,\n #f0f0f0,\n #f0f0f0 10px,\n #e8e8e8 10px,\n #e8e8e8 20px);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 5;\n /* Below events but above grid lines */\n pointer-events: none;\n /* Allow clicks to pass through if needed, though we block clicks in JS */\n border-bottom: 1px solid #ddd;\n border-top: 1px solid #ddd;\n}\n\n.closed-block-label {\n background-color: rgba(255, 255, 255, 0.8);\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 0.8rem;\n color: #888;\n font-weight: 500;\n}";
|
|
7533
7980
|
styleInject(css_248z);
|
|
7534
7981
|
|
|
7535
7982
|
const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defaultView = 'month', onBookingCreate, onBookingClick, participants = [], timezone = moment$1.tz.guess(), businessHours, }) => {
|
|
@@ -7540,6 +7987,19 @@ const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defa
|
|
|
7540
7987
|
const [error, setError] = useState(null);
|
|
7541
7988
|
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
7542
7989
|
const [selectedDate, setSelectedDate] = useState(null);
|
|
7990
|
+
const [selectedBooking, setSelectedBooking] = useState(null);
|
|
7991
|
+
// Mobile responsiveness logic
|
|
7992
|
+
useEffect(() => {
|
|
7993
|
+
const handleResize = () => {
|
|
7994
|
+
if (isMobile()) {
|
|
7995
|
+
setCurrentView('day');
|
|
7996
|
+
}
|
|
7997
|
+
};
|
|
7998
|
+
// Initial check
|
|
7999
|
+
handleResize();
|
|
8000
|
+
window.addEventListener('resize', handleResize);
|
|
8001
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
8002
|
+
}, []);
|
|
7543
8003
|
// Update currentDate when timezone changes
|
|
7544
8004
|
useEffect(() => {
|
|
7545
8005
|
setCurrentDate((prev) => moment$1(prev).tz(timezone));
|
|
@@ -7561,6 +8021,10 @@ const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defa
|
|
|
7561
8021
|
startDate = current.clone().startOf('week');
|
|
7562
8022
|
endDate = current.clone().endOf('week');
|
|
7563
8023
|
}
|
|
8024
|
+
else if (currentView === 'workWeek') {
|
|
8025
|
+
startDate = current.clone().startOf('week'); // Fetch full week to be safe
|
|
8026
|
+
endDate = current.clone().endOf('week');
|
|
8027
|
+
}
|
|
7564
8028
|
else {
|
|
7565
8029
|
startDate = current.clone().startOf('day');
|
|
7566
8030
|
endDate = current.clone().endOf('day');
|
|
@@ -7589,7 +8053,7 @@ const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defa
|
|
|
7589
8053
|
if (currentView === 'month') {
|
|
7590
8054
|
newDate.subtract(1, 'months');
|
|
7591
8055
|
}
|
|
7592
|
-
else if (currentView === 'week') {
|
|
8056
|
+
else if (currentView === 'week' || currentView === 'workWeek') {
|
|
7593
8057
|
newDate.subtract(1, 'weeks');
|
|
7594
8058
|
}
|
|
7595
8059
|
else {
|
|
@@ -7602,7 +8066,7 @@ const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defa
|
|
|
7602
8066
|
if (currentView === 'month') {
|
|
7603
8067
|
newDate.add(1, 'months');
|
|
7604
8068
|
}
|
|
7605
|
-
else if (currentView === 'week') {
|
|
8069
|
+
else if (currentView === 'week' || currentView === 'workWeek') {
|
|
7606
8070
|
newDate.add(1, 'weeks');
|
|
7607
8071
|
}
|
|
7608
8072
|
else {
|
|
@@ -7625,18 +8089,19 @@ const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defa
|
|
|
7625
8089
|
onBookingCreate(booking);
|
|
7626
8090
|
}
|
|
7627
8091
|
});
|
|
7628
|
-
const
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
8092
|
+
const handleInternalBookingClick = (booking) => {
|
|
8093
|
+
setSelectedBooking(booking);
|
|
8094
|
+
// Also call the external callback if provided
|
|
8095
|
+
if (onBookingClick) {
|
|
8096
|
+
onBookingClick(booking);
|
|
8097
|
+
}
|
|
7633
8098
|
};
|
|
7634
8099
|
const getHeaderDateFormat = () => {
|
|
7635
8100
|
const current = moment$1(currentDate).tz(timezone);
|
|
7636
8101
|
if (currentView === 'month') {
|
|
7637
8102
|
return current.format('MMMM YYYY');
|
|
7638
8103
|
}
|
|
7639
|
-
else if (currentView === 'week') {
|
|
8104
|
+
else if (currentView === 'week' || currentView === 'workWeek') {
|
|
7640
8105
|
const weekStart = current.clone().startOf('week');
|
|
7641
8106
|
const weekEnd = current.clone().endOf('week');
|
|
7642
8107
|
return `${weekStart.format('MMM D')} - ${weekEnd.format('MMM D, YYYY')}`;
|
|
@@ -7645,143 +8110,16 @@ const Calendar = ({ businessId, resourceId, title = 'Calendar', apiBaseUrl, defa
|
|
|
7645
8110
|
return current.format('MMMM D, YYYY');
|
|
7646
8111
|
}
|
|
7647
8112
|
};
|
|
7648
|
-
const renderHeader = () => {
|
|
7649
|
-
return (React.createElement("div", { className: "calendar-header" },
|
|
7650
|
-
React.createElement("div", { className: "calendar-title-section" },
|
|
7651
|
-
React.createElement("h2", { className: "calendar-title" }, title),
|
|
7652
|
-
React.createElement("div", { className: "calendar-view-toggle" },
|
|
7653
|
-
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'month' ? 'active' : ''}`, onClick: () => setCurrentView('month') }, "Month"),
|
|
7654
|
-
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'week' ? 'active' : ''}`, onClick: () => setCurrentView('week') }, "Week"),
|
|
7655
|
-
React.createElement("button", { className: `calendar-btn calendar-btn-view ${currentView === 'day' ? 'active' : ''}`, onClick: () => setCurrentView('day') }, "Day")),
|
|
7656
|
-
React.createElement("button", { className: "calendar-btn calendar-btn-create", onClick: () => setShowCreateModal(true) }, "+ Create Booking")),
|
|
7657
|
-
React.createElement("div", { className: "calendar-navigation" },
|
|
7658
|
-
React.createElement("button", { className: "calendar-btn", onClick: handlePrevious }, "\u2039"),
|
|
7659
|
-
React.createElement("button", { className: "calendar-btn", onClick: handleToday }, "Today"),
|
|
7660
|
-
React.createElement("button", { className: "calendar-btn", onClick: handleNext }, "\u203A"),
|
|
7661
|
-
React.createElement("span", { className: "calendar-current-month" }, getHeaderDateFormat()))));
|
|
7662
|
-
};
|
|
7663
|
-
const renderDaysOfWeek = () => {
|
|
7664
|
-
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
7665
|
-
return (React.createElement("div", { className: "calendar-days-row" }, days.map((day) => (React.createElement("div", { key: day, className: "calendar-day-header" }, day)))));
|
|
7666
|
-
};
|
|
7667
|
-
const renderMonthView = () => {
|
|
7668
|
-
var _a;
|
|
7669
|
-
const current = moment$1(currentDate).tz(timezone);
|
|
7670
|
-
const monthStart = current.clone().startOf('month');
|
|
7671
|
-
const monthEnd = current.clone().endOf('month');
|
|
7672
|
-
const startDate = monthStart.clone().startOf('week');
|
|
7673
|
-
const endDate = monthEnd.clone().endOf('week');
|
|
7674
|
-
const rows = [];
|
|
7675
|
-
let days = [];
|
|
7676
|
-
let day = startDate.clone();
|
|
7677
|
-
while (day.isSameOrBefore(endDate, 'day')) {
|
|
7678
|
-
for (let i = 0; i < 7; i++) {
|
|
7679
|
-
const currentDay = day.clone();
|
|
7680
|
-
const dayBookings = getBookingsForDay(currentDay);
|
|
7681
|
-
const isCurrentMonth = currentDay.isSame(monthStart, 'month');
|
|
7682
|
-
const isToday = currentDay.isSame(moment$1().tz(timezone), 'day');
|
|
7683
|
-
// Check business hours
|
|
7684
|
-
const dayName = currentDay.format('dddd');
|
|
7685
|
-
const isClosed = (_a = businessHours === null || businessHours === void 0 ? void 0 : businessHours[dayName]) === null || _a === void 0 ? void 0 : _a.isClosed;
|
|
7686
|
-
days.push(React.createElement("div", { key: day.toString(), className: `calendar-cell ${!isCurrentMonth ? 'calendar-cell-disabled' : ''} ${isToday ? 'calendar-cell-today' : ''} ${isClosed ? 'calendar-cell-closed' : ''}`, onClick: () => isCurrentMonth && !isClosed && handleDateClick(currentDay), style: isClosed ? { backgroundColor: '#f5f5f5', cursor: 'not-allowed' } : {} },
|
|
7687
|
-
React.createElement("div", { className: "calendar-cell-number" }, currentDay.format('D')),
|
|
7688
|
-
isClosed && React.createElement("div", { className: "calendar-closed-label" }, "Closed"),
|
|
7689
|
-
React.createElement("div", { className: "calendar-cell-bookings" },
|
|
7690
|
-
dayBookings.slice(0, 3).map((booking) => {
|
|
7691
|
-
var _a, _b;
|
|
7692
|
-
return (React.createElement("div", { key: booking.meeting_id, className: "calendar-booking", onClick: (e) => {
|
|
7693
|
-
e.stopPropagation();
|
|
7694
|
-
if (onBookingClick) {
|
|
7695
|
-
onBookingClick(booking);
|
|
7696
|
-
}
|
|
7697
|
-
}, title: ((_a = booking.metadata) === null || _a === void 0 ? void 0 : _a.title) || 'Untitled' },
|
|
7698
|
-
React.createElement("span", { className: "calendar-booking-time" }, moment$1(booking.start).tz(timezone).format('HH:mm')),
|
|
7699
|
-
React.createElement("span", { className: "calendar-booking-title" }, ((_b = booking.metadata) === null || _b === void 0 ? void 0 : _b.title) || 'Untitled')));
|
|
7700
|
-
}),
|
|
7701
|
-
dayBookings.length > 3 && (React.createElement("div", { className: "calendar-booking-more" },
|
|
7702
|
-
"+",
|
|
7703
|
-
dayBookings.length - 3,
|
|
7704
|
-
" more")))));
|
|
7705
|
-
day.add(1, 'days');
|
|
7706
|
-
}
|
|
7707
|
-
rows.push(React.createElement("div", { key: day.toString(), className: "calendar-row" }, days));
|
|
7708
|
-
days = [];
|
|
7709
|
-
}
|
|
7710
|
-
return React.createElement("div", { className: "calendar-body" }, rows);
|
|
7711
|
-
};
|
|
7712
|
-
const renderWeekView = () => {
|
|
7713
|
-
var _a;
|
|
7714
|
-
const current = moment$1(currentDate).tz(timezone);
|
|
7715
|
-
const weekStart = current.clone().startOf('week');
|
|
7716
|
-
const days = [];
|
|
7717
|
-
for (let i = 0; i < 7; i++) {
|
|
7718
|
-
const day = weekStart.clone().add(i, 'days');
|
|
7719
|
-
const dayBookings = getBookingsForDay(day);
|
|
7720
|
-
const isToday = day.isSame(moment$1().tz(timezone), 'day');
|
|
7721
|
-
const dayName = day.format('dddd');
|
|
7722
|
-
const isClosed = (_a = businessHours === null || businessHours === void 0 ? void 0 : businessHours[dayName]) === null || _a === void 0 ? void 0 : _a.isClosed;
|
|
7723
|
-
days.push(React.createElement("div", { key: day.toString(), className: `calendar-week-day ${isToday ? 'calendar-cell-today' : ''}`, onClick: () => !isClosed && handleDateClick(day), style: isClosed ? { backgroundColor: '#f5f5f5' } : {} },
|
|
7724
|
-
React.createElement("div", { className: "calendar-week-day-header" },
|
|
7725
|
-
React.createElement("div", { className: "calendar-week-day-name" }, day.format('EEE')),
|
|
7726
|
-
React.createElement("div", { className: "calendar-week-day-number" }, day.format('D')),
|
|
7727
|
-
isClosed && React.createElement("div", { className: "calendar-closed-label-small" }, "Closed")),
|
|
7728
|
-
React.createElement("div", { className: "calendar-week-day-bookings" }, dayBookings.map((booking) => {
|
|
7729
|
-
var _a, _b;
|
|
7730
|
-
return (React.createElement("div", { key: booking.meeting_id, className: "calendar-booking calendar-week-booking", onClick: (e) => {
|
|
7731
|
-
e.stopPropagation();
|
|
7732
|
-
if (onBookingClick) {
|
|
7733
|
-
onBookingClick(booking);
|
|
7734
|
-
}
|
|
7735
|
-
}, title: ((_a = booking.metadata) === null || _a === void 0 ? void 0 : _a.title) || 'Untitled' },
|
|
7736
|
-
React.createElement("span", { className: "calendar-booking-time" }, moment$1(booking.start).tz(timezone).format('HH:mm')),
|
|
7737
|
-
React.createElement("span", { className: "calendar-booking-title" }, ((_b = booking.metadata) === null || _b === void 0 ? void 0 : _b.title) || 'Untitled')));
|
|
7738
|
-
}))));
|
|
7739
|
-
}
|
|
7740
|
-
return React.createElement("div", { className: "calendar-week-view" }, days);
|
|
7741
|
-
};
|
|
7742
|
-
const renderDayView = () => {
|
|
7743
|
-
const current = moment$1(currentDate).tz(timezone);
|
|
7744
|
-
const dayBookings = getBookingsForDay(current);
|
|
7745
|
-
const isToday = current.isSame(moment$1().tz(timezone), 'day');
|
|
7746
|
-
const dayName = current.format('dddd');
|
|
7747
|
-
const businessDay = businessHours === null || businessHours === void 0 ? void 0 : businessHours[dayName];
|
|
7748
|
-
const isClosed = businessDay === null || businessDay === void 0 ? void 0 : businessDay.isClosed;
|
|
7749
|
-
return (React.createElement("div", { className: "calendar-day-view" },
|
|
7750
|
-
React.createElement("div", { className: `calendar-day-container ${isToday ? 'calendar-cell-today' : ''}` },
|
|
7751
|
-
React.createElement("div", { className: "calendar-day-header" },
|
|
7752
|
-
React.createElement("div", { className: "calendar-day-name" }, current.format('dddd')),
|
|
7753
|
-
React.createElement("div", { className: "calendar-day-date" }, current.format('MMMM D, YYYY')),
|
|
7754
|
-
isClosed ? (React.createElement("div", { className: "calendar-status-closed" }, "Closed")) : businessDay ? (React.createElement("div", { className: "calendar-status-open" },
|
|
7755
|
-
"Open: ",
|
|
7756
|
-
businessDay.openTime,
|
|
7757
|
-
" - ",
|
|
7758
|
-
businessDay.closeTime)) : null),
|
|
7759
|
-
React.createElement("div", { className: "calendar-day-bookings" }, dayBookings.length === 0 ? (React.createElement("div", { className: "calendar-no-bookings" }, "No bookings for this day")) : (dayBookings.map((booking) => {
|
|
7760
|
-
var _a, _b, _c;
|
|
7761
|
-
return (React.createElement("div", { key: booking.meeting_id, className: "calendar-booking calendar-day-booking", onClick: () => {
|
|
7762
|
-
if (onBookingClick) {
|
|
7763
|
-
onBookingClick(booking);
|
|
7764
|
-
}
|
|
7765
|
-
}, title: ((_a = booking.metadata) === null || _a === void 0 ? void 0 : _a.title) || 'Untitled' },
|
|
7766
|
-
React.createElement("span", { className: "calendar-booking-time" },
|
|
7767
|
-
moment$1(booking.start).tz(timezone).format('HH:mm'),
|
|
7768
|
-
" - ",
|
|
7769
|
-
moment$1(booking.end).tz(timezone).format('HH:mm')),
|
|
7770
|
-
React.createElement("span", { className: "calendar-booking-title" }, ((_b = booking.metadata) === null || _b === void 0 ? void 0 : _b.title) || 'Untitled'),
|
|
7771
|
-
((_c = booking.metadata) === null || _c === void 0 ? void 0 : _c.description) && (React.createElement("span", { className: "calendar-booking-description" }, booking.metadata.description))));
|
|
7772
|
-
}))),
|
|
7773
|
-
!isClosed && (React.createElement("button", { className: "calendar-btn calendar-day-add-btn", onClick: () => handleDateClick(current) }, "+ Add Booking")))));
|
|
7774
|
-
};
|
|
7775
8113
|
return (React.createElement("div", { className: "calendar-container" },
|
|
7776
|
-
|
|
8114
|
+
React.createElement(CalendarHeader, { title: title, currentView: currentView, onViewChange: setCurrentView, onNext: handleNext, onPrevious: handlePrevious, onToday: handleToday, onCreateClick: () => {
|
|
8115
|
+
setSelectedDate(moment$1().tz(timezone).toDate());
|
|
8116
|
+
setShowCreateModal(true);
|
|
8117
|
+
}, dateRangeText: getHeaderDateFormat() }),
|
|
7777
8118
|
error && React.createElement("div", { className: "calendar-error" }, error),
|
|
7778
8119
|
loading && React.createElement("div", { className: "calendar-loading" }, "Loading..."),
|
|
7779
|
-
currentView === 'month'
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
currentView === 'week' && renderWeekView(),
|
|
7783
|
-
currentView === 'day' && renderDayView(),
|
|
7784
|
-
showCreateModal && selectedDate && (React.createElement(CreateBookingModal, { businessId: businessId, apiBaseUrl: apiBaseUrl, initialDate: selectedDate, participants: participants, onClose: () => setShowCreateModal(false), onBookingCreated: handleBookingCreated, timezone: timezone, businessHours: businessHours }))));
|
|
8120
|
+
React.createElement("div", { className: "calendar-content" }, currentView === 'month' ? (React.createElement(MonthView, { currentDate: currentDate, bookings: bookings, timezone: timezone, onBookingClick: handleInternalBookingClick, onDateClick: handleDateClick, businessHours: businessHours })) : (React.createElement(TimeGrid, { currentDate: currentDate, view: currentView, bookings: bookings, timezone: timezone, onBookingClick: handleInternalBookingClick, onTimeSlotClick: handleDateClick, businessHours: businessHours }))),
|
|
8121
|
+
showCreateModal && selectedDate && (React.createElement(CreateBookingModal, { businessId: businessId, apiBaseUrl: apiBaseUrl, initialDate: selectedDate, participants: participants, onClose: () => setShowCreateModal(false), onBookingCreated: handleBookingCreated, timezone: timezone, businessHours: businessHours })),
|
|
8122
|
+
selectedBooking && (React.createElement(BookingDetailsModal, { booking: selectedBooking, timezone: timezone, onClose: () => setSelectedBooking(null) }))));
|
|
7785
8123
|
};
|
|
7786
8124
|
|
|
7787
8125
|
export { Calendar, createBooking, getAllBookings, getMeetingDetails, getResourceSchedule };
|