payload-reserve 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +61 -1141
  2. package/dist/collections/Customers.d.ts +3 -0
  3. package/dist/collections/Customers.js +58 -0
  4. package/dist/collections/Customers.js.map +1 -0
  5. package/dist/collections/Reservations.js +129 -35
  6. package/dist/collections/Reservations.js.map +1 -1
  7. package/dist/collections/Resources.js +70 -2
  8. package/dist/collections/Resources.js.map +1 -1
  9. package/dist/collections/Schedules.js +4 -1
  10. package/dist/collections/Schedules.js.map +1 -1
  11. package/dist/collections/Services.js +59 -2
  12. package/dist/collections/Services.js.map +1 -1
  13. package/dist/components/AvailabilityOverview/AvailabilityOverview.module.css +31 -0
  14. package/dist/components/AvailabilityOverview/index.js +59 -8
  15. package/dist/components/AvailabilityOverview/index.js.map +1 -1
  16. package/dist/components/CalendarView/CalendarView.module.css +171 -0
  17. package/dist/components/CalendarView/index.js +547 -38
  18. package/dist/components/CalendarView/index.js.map +1 -1
  19. package/dist/components/CustomerField/index.js +24 -10
  20. package/dist/components/CustomerField/index.js.map +1 -1
  21. package/dist/components/DashboardWidget/DashboardWidgetServer.js +21 -11
  22. package/dist/components/DashboardWidget/DashboardWidgetServer.js.map +1 -1
  23. package/dist/defaults.d.ts +1 -2
  24. package/dist/defaults.js +22 -4
  25. package/dist/defaults.js.map +1 -1
  26. package/dist/endpoints/cancelBooking.d.ts +3 -0
  27. package/dist/endpoints/cancelBooking.js +37 -0
  28. package/dist/endpoints/cancelBooking.js.map +1 -0
  29. package/dist/endpoints/checkAvailability.d.ts +3 -0
  30. package/dist/endpoints/checkAvailability.js +37 -0
  31. package/dist/endpoints/checkAvailability.js.map +1 -0
  32. package/dist/endpoints/createBooking.d.ts +3 -0
  33. package/dist/endpoints/createBooking.js +32 -0
  34. package/dist/endpoints/createBooking.js.map +1 -0
  35. package/dist/endpoints/customerSearch.js +76 -58
  36. package/dist/endpoints/customerSearch.js.map +1 -1
  37. package/dist/endpoints/getSlots.d.ts +3 -0
  38. package/dist/endpoints/getSlots.js +51 -0
  39. package/dist/endpoints/getSlots.js.map +1 -0
  40. package/dist/hooks/index.d.ts +2 -0
  41. package/dist/hooks/index.js +2 -0
  42. package/dist/hooks/index.js.map +1 -1
  43. package/dist/hooks/reservations/calculateEndTime.js +75 -9
  44. package/dist/hooks/reservations/calculateEndTime.js.map +1 -1
  45. package/dist/hooks/reservations/checkIdempotency.d.ts +3 -0
  46. package/dist/hooks/reservations/checkIdempotency.js +32 -0
  47. package/dist/hooks/reservations/checkIdempotency.js.map +1 -0
  48. package/dist/hooks/reservations/onStatusChange.d.ts +3 -0
  49. package/dist/hooks/reservations/onStatusChange.js +41 -0
  50. package/dist/hooks/reservations/onStatusChange.js.map +1 -0
  51. package/dist/hooks/reservations/validateCancellation.js +1 -1
  52. package/dist/hooks/reservations/validateCancellation.js.map +1 -1
  53. package/dist/hooks/reservations/validateConflicts.js +34 -56
  54. package/dist/hooks/reservations/validateConflicts.js.map +1 -1
  55. package/dist/hooks/reservations/validateStatusTransition.d.ts +2 -1
  56. package/dist/hooks/reservations/validateStatusTransition.js +29 -6
  57. package/dist/hooks/reservations/validateStatusTransition.js.map +1 -1
  58. package/dist/index.d.ts +5 -1
  59. package/dist/index.js +3 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/plugin.js +49 -54
  62. package/dist/plugin.js.map +1 -1
  63. package/dist/services/AvailabilityService.d.ts +57 -0
  64. package/dist/services/AvailabilityService.js +232 -0
  65. package/dist/services/AvailabilityService.js.map +1 -0
  66. package/dist/services/index.d.ts +1 -0
  67. package/dist/services/index.js +3 -0
  68. package/dist/services/index.js.map +1 -0
  69. package/dist/translations/en.json +33 -1
  70. package/dist/translations/index.d.ts +5 -0
  71. package/dist/translations/index.js.map +1 -1
  72. package/dist/types.d.ts +84 -6
  73. package/dist/types.js +29 -9
  74. package/dist/types.js.map +1 -1
  75. package/dist/utilities/ownerAccess.d.ts +24 -0
  76. package/dist/utilities/ownerAccess.js +128 -0
  77. package/dist/utilities/ownerAccess.js.map +1 -0
  78. package/dist/utilities/resolveReservationItems.d.ts +18 -0
  79. package/dist/utilities/resolveReservationItems.js +45 -0
  80. package/dist/utilities/resolveReservationItems.js.map +1 -0
  81. package/package.json +12 -9
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/CalendarView/index.tsx"],"sourcesContent":["'use client'\nimport type { AdminViewServerProps } from 'payload'\n\nimport { useConfig, useDocumentDrawer, useTranslation } from '@payloadcms/ui'\nimport React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { PluginT } from '../../translations/index.js'\n\nimport styles from './CalendarView.module.css'\n\ntype ViewMode = 'day' | 'month' | 'week'\n\ntype Reservation = {\n customer?: { name?: string } | string\n endTime?: string\n id: string\n resource?: { name?: string } | string\n service?: { name?: string } | string\n startTime: string\n status: string\n}\n\nconst STATUS_CLASS_MAP: Record<string, string> = {\n cancelled: styles.statusCancelled,\n completed: styles.statusCompleted,\n confirmed: styles.statusConfirmed,\n 'no-show': styles.statusNoShow,\n pending: styles.statusPending,\n}\n\nconst STATUS_COLORS: Record<string, string> = {\n cancelled: '#e5e7eb',\n completed: '#d1fae5',\n confirmed: '#dbeafe',\n 'no-show': '#fee2e2',\n pending: '#fef3c7',\n}\n\nexport const CalendarView: React.FC<AdminViewServerProps> = () => {\n const { config } = useConfig()\n const { t: _t } = useTranslation()\n const t = _t as PluginT\n\n const STATUS_LABELS = useMemo<Record<string, string>>(\n () => ({\n cancelled: t('reservation:statusCancelled'),\n completed: t('reservation:statusCompleted'),\n confirmed: t('reservation:statusConfirmed'),\n 'no-show': t('reservation:statusNoShowLabel'),\n pending: t('reservation:statusPending'),\n }),\n [t],\n )\n const slugs = config.admin?.custom?.reservationSlugs\n const reservationSlug = slugs?.reservations ?? 'reservations'\n\n const [currentDate, setCurrentDate] = useState(() => new Date())\n const [viewMode, setViewMode] = useState<ViewMode>('month')\n const [reservations, setReservations] = useState<Reservation[]>([])\n const [loading, setLoading] = useState(true)\n const [drawerDocId, setDrawerDocId] = useState<null | string>(null)\n const [initialData, setInitialData] = useState<Record<string, unknown> | undefined>(undefined)\n\n const [DocumentDrawer, , { openDrawer }] = useDocumentDrawer({\n id: drawerDocId ?? undefined,\n collectionSlug: reservationSlug,\n })\n\n const pendingDrawerOpen = useRef(false)\n\n useEffect(() => {\n if (pendingDrawerOpen.current) {\n pendingDrawerOpen.current = false\n openDrawer()\n }\n })\n\n const { rangeEnd, rangeStart } = useMemo(() => {\n const start = new Date(currentDate)\n const end = new Date(currentDate)\n\n if (viewMode === 'month') {\n start.setDate(1)\n start.setDate(start.getDate() - start.getDay())\n end.setMonth(end.getMonth() + 1, 0)\n end.setDate(end.getDate() + (6 - end.getDay()))\n } else if (viewMode === 'week') {\n const dayOfWeek = start.getDay()\n start.setDate(start.getDate() - dayOfWeek)\n end.setDate(start.getDate() + 6)\n }\n start.setHours(0, 0, 0, 0)\n end.setHours(23, 59, 59, 999)\n\n return { rangeEnd: end, rangeStart: start }\n }, [currentDate, viewMode])\n\n const fetchReservations = useCallback(async () => {\n setLoading(true)\n try {\n const apiUrl = `${config.serverURL ?? ''}${config.routes.api}/${reservationSlug}`\n const params = new URLSearchParams({\n depth: '1',\n limit: '500',\n sort: 'startTime',\n 'where[startTime][greater_than_equal]': rangeStart.toISOString(),\n 'where[startTime][less_than_equal]': rangeEnd.toISOString(),\n })\n const response = await fetch(`${apiUrl}?${params}`)\n const result = await response.json()\n setReservations(result.docs ?? [])\n } catch {\n setReservations([])\n }\n setLoading(false)\n }, [rangeStart, rangeEnd, config.routes.api, config.serverURL, reservationSlug])\n\n useEffect(() => {\n void fetchReservations()\n }, [fetchReservations])\n\n const handleEventClick = useCallback((e: React.MouseEvent, id: string) => {\n e.stopPropagation()\n setDrawerDocId(id)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }, [])\n\n const handleEventKeyDown = useCallback((e: React.KeyboardEvent, id: string) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n e.stopPropagation()\n setDrawerDocId(id)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }\n }, [])\n\n const handleCreateNew = useCallback(() => {\n setDrawerDocId(null)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }, [])\n\n const handleDateClick = useCallback((date: Date) => {\n setDrawerDocId(null)\n setInitialData({ startTime: date.toISOString() })\n pendingDrawerOpen.current = true\n }, [])\n\n const navigate = useCallback(\n (direction: -1 | 1) => {\n setCurrentDate((prev) => {\n const next = new Date(prev)\n if (viewMode === 'month') {\n next.setMonth(next.getMonth() + direction)\n } else if (viewMode === 'week') {\n next.setDate(next.getDate() + 7 * direction)\n } else {\n next.setDate(next.getDate() + direction)\n }\n return next\n })\n },\n [viewMode],\n )\n\n const goToToday = useCallback(() => setCurrentDate(new Date()), [])\n\n const getResName = (field: { name?: string } | string | undefined): string => {\n if (!field) {return ''}\n if (typeof field === 'string') {return ''}\n return field.name ?? ''\n }\n\n const getEventLabel = (r: Reservation, compact: boolean) => {\n const time = new Date(r.startTime).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })\n const serviceName = getResName(r.service)\n if (compact) {\n return `${time} ${serviceName}`.trim()\n }\n const customerName = getResName(r.customer)\n const parts = [time, serviceName, customerName].filter(Boolean)\n return parts.join(' - ')\n }\n\n const getEventTooltip = (r: Reservation): string => {\n const serviceName = getResName(r.service) || t('reservation:calendarUnknownService')\n const startStr = new Date(r.startTime).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })\n const endStr = r.endTime\n ? new Date(r.endTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n : '?'\n const customerName = getResName(r.customer) || t('reservation:calendarUnknownCustomer')\n const resourceName = getResName(r.resource) || t('reservation:calendarUnknownResource')\n const status = STATUS_LABELS[r.status] ?? r.status\n return `${serviceName}\\n${startStr} - ${endStr}\\n${t('reservation:tooltipCustomer')} ${customerName}\\n${t('reservation:tooltipResource')} ${resourceName}\\n${t('reservation:tooltipStatus')} ${status}`\n }\n\n const renderEventItem = (r: Reservation, compact: boolean) => (\n <div\n className={`${styles.eventItem} ${STATUS_CLASS_MAP[r.status] ?? ''}`}\n key={r.id}\n onClick={(e) => handleEventClick(e, r.id)}\n onKeyDown={(e) => handleEventKeyDown(e, r.id)}\n role=\"button\"\n tabIndex={0}\n title={getEventTooltip(r)}\n >\n {getEventLabel(r, compact)}\n </div>\n )\n\n const renderStatusLegend = () => (\n <div className={styles.statusLegend}>\n {Object.entries(STATUS_LABELS).map(([key, label]) => (\n <div className={styles.legendItem} key={key}>\n <span className={styles.legendDot} style={{ background: STATUS_COLORS[key] }} />\n {label}\n </div>\n ))}\n </div>\n )\n\n const renderCurrentTimeLine = (cellDate: Date, cellHour: number) => {\n const now = new Date()\n if (\n now.getFullYear() !== cellDate.getFullYear() ||\n now.getMonth() !== cellDate.getMonth() ||\n now.getDate() !== cellDate.getDate() ||\n now.getHours() !== cellHour\n ) {\n return null\n }\n const topPercent = (now.getMinutes() / 60) * 100\n return <div className={styles.currentTimeLine} style={{ top: `${topPercent}%` }} />\n }\n\n const renderMonthView = () => {\n const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1)\n const startDay = new Date(firstDay)\n startDay.setDate(startDay.getDate() - startDay.getDay())\n\n const days: Date[] = []\n const d = new Date(startDay)\n for (let i = 0; i < 42; i++) {\n days.push(new Date(d))\n d.setDate(d.getDate() + 1)\n }\n\n const today = new Date()\n const todayStr = `${today.getFullYear()}-${today.getMonth()}-${today.getDate()}`\n\n return (\n <div className={styles.monthGrid}>\n {[\n t('reservation:dayShortSun'),\n t('reservation:dayShortMon'),\n t('reservation:dayShortTue'),\n t('reservation:dayShortWed'),\n t('reservation:dayShortThu'),\n t('reservation:dayShortFri'),\n t('reservation:dayShortSat'),\n ].map((d) => (\n <div className={styles.dayHeader} key={d}>\n {d}\n </div>\n ))}\n {days.map((day, i) => {\n const dayStr = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`\n const isToday = dayStr === todayStr\n const isOtherMonth = day.getMonth() !== currentDate.getMonth()\n const dayReservations = reservations.filter((r) => {\n const rDate = new Date(r.startTime)\n return (\n rDate.getFullYear() === day.getFullYear() &&\n rDate.getMonth() === day.getMonth() &&\n rDate.getDate() === day.getDate()\n )\n })\n\n const clickDate = new Date(day)\n clickDate.setHours(9, 0, 0, 0)\n\n return (\n <div\n className={`${styles.dayCell} ${isOtherMonth ? styles.dayCellOtherMonth : ''} ${isToday ? styles.dayCellToday : ''}`}\n key={i}\n onClick={() => handleDateClick(clickDate)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleDateClick(clickDate)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n <div className={styles.dayNumber}>{day.getDate()}</div>\n {dayReservations.map((r) => renderEventItem(r, true))}\n </div>\n )\n })}\n </div>\n )\n }\n\n const renderWeekView = () => {\n const startOfWeek = new Date(currentDate)\n startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay())\n startOfWeek.setHours(0, 0, 0, 0)\n\n const weekDays: Date[] = []\n for (let i = 0; i < 7; i++) {\n const d = new Date(startOfWeek)\n d.setDate(d.getDate() + i)\n weekDays.push(d)\n }\n\n const hours = Array.from({ length: 12 }, (_, i) => i + 7)\n\n return (\n <div className={styles.weekView}>\n <div className={styles.dayHeader} />\n {weekDays.map((d, i) => (\n <div className={styles.dayHeader} key={i}>\n {d.toLocaleDateString([], { day: 'numeric', month: 'numeric', weekday: 'short' })}\n </div>\n ))}\n {hours.map((hour) => (\n <Fragment key={`row-${hour}`}>\n <div className={styles.timeLabel}>\n {hour.toString().padStart(2, '0')}:00\n </div>\n {weekDays.map((day, di) => {\n const cellReservations = reservations.filter((r) => {\n const rDate = new Date(r.startTime)\n return (\n rDate.getFullYear() === day.getFullYear() &&\n rDate.getMonth() === day.getMonth() &&\n rDate.getDate() === day.getDate() &&\n rDate.getHours() === hour\n )\n })\n const clickDate = new Date(day)\n clickDate.setHours(hour, 0, 0, 0)\n return (\n <div\n className={styles.weekCell}\n key={`cell-${hour}-${di}`}\n onClick={() => handleDateClick(clickDate)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleDateClick(clickDate)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {renderCurrentTimeLine(day, hour)}\n {cellReservations.map((r) => renderEventItem(r, false))}\n </div>\n )\n })}\n </Fragment>\n ))}\n </div>\n )\n }\n\n const renderDayView = () => {\n const hours = Array.from({ length: 14 }, (_, i) => i + 7)\n\n return (\n <div className={styles.dayView}>\n {hours.map((hour) => {\n const hourReservations = reservations.filter((r) => {\n const rDate = new Date(r.startTime)\n return (\n rDate.getFullYear() === currentDate.getFullYear() &&\n rDate.getMonth() === currentDate.getMonth() &&\n rDate.getDate() === currentDate.getDate() &&\n rDate.getHours() === hour\n )\n })\n const clickDate = new Date(currentDate)\n clickDate.setHours(hour, 0, 0, 0)\n return (\n <Fragment key={`row-${hour}`}>\n <div className={styles.timeLabel}>\n {hour.toString().padStart(2, '0')}:00\n </div>\n <div\n className={styles.dayViewCell}\n onClick={() => handleDateClick(clickDate)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleDateClick(clickDate)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {renderCurrentTimeLine(currentDate, hour)}\n {hourReservations.map((r) => renderEventItem(r, false))}\n </div>\n </Fragment>\n )\n })}\n </div>\n )\n }\n\n const dateLabel = useMemo(() => {\n if (viewMode === 'month') {\n return currentDate.toLocaleDateString([], { month: 'long', year: 'numeric' })\n }\n if (viewMode === 'week') {\n const startOfWeek = new Date(currentDate)\n startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay())\n const endOfWeek = new Date(startOfWeek)\n endOfWeek.setDate(endOfWeek.getDate() + 6)\n return `${startOfWeek.toLocaleDateString([], { day: 'numeric', month: 'short' })} - ${endOfWeek.toLocaleDateString([], { day: 'numeric', month: 'short', year: 'numeric' })}`\n }\n return currentDate.toLocaleDateString([], {\n day: 'numeric',\n month: 'long',\n weekday: 'long',\n year: 'numeric',\n })\n }, [currentDate, viewMode])\n\n if (loading) {\n return <div className={styles.loading}>{t('reservation:calendarLoading')}</div>\n }\n\n return (\n <div className={styles.wrapper}>\n <div className={styles.header}>\n <div className={styles.navButtons}>\n <button className={styles.navButton} onClick={() => navigate(-1)} type=\"button\">\n &larr;\n </button>\n <button className={styles.navButton} onClick={goToToday} type=\"button\">\n {t('reservation:calendarToday')}\n </button>\n <button className={styles.navButton} onClick={() => navigate(1)} type=\"button\">\n &rarr;\n </button>\n <span className={styles.currentDate}>{dateLabel}</span>\n </div>\n <div className={styles.viewToggle}>\n <button className={styles.createButton} onClick={handleCreateNew} type=\"button\">\n {t('reservation:calendarCreateNew')}\n </button>\n {([\n { key: 'month' as ViewMode, label: t('reservation:calendarMonth') },\n { key: 'week' as ViewMode, label: t('reservation:calendarWeek') },\n { key: 'day' as ViewMode, label: t('reservation:calendarDay') },\n ]).map(({ key, label }) => (\n <button\n className={`${styles.viewToggleButton} ${viewMode === key ? styles.viewToggleButtonActive : ''}`}\n key={key}\n onClick={() => setViewMode(key)}\n type=\"button\"\n >\n {label}\n </button>\n ))}\n </div>\n </div>\n {renderStatusLegend()}\n {viewMode === 'month' && renderMonthView()}\n {viewMode === 'week' && renderWeekView()}\n {viewMode === 'day' && renderDayView()}\n <DocumentDrawer initialData={initialData} onSave={fetchReservations} />\n </div>\n )\n}\n"],"names":["useConfig","useDocumentDrawer","useTranslation","React","Fragment","useCallback","useEffect","useMemo","useRef","useState","styles","STATUS_CLASS_MAP","cancelled","statusCancelled","completed","statusCompleted","confirmed","statusConfirmed","statusNoShow","pending","statusPending","STATUS_COLORS","CalendarView","config","t","_t","STATUS_LABELS","slugs","admin","custom","reservationSlugs","reservationSlug","reservations","currentDate","setCurrentDate","Date","viewMode","setViewMode","setReservations","loading","setLoading","drawerDocId","setDrawerDocId","initialData","setInitialData","undefined","DocumentDrawer","openDrawer","id","collectionSlug","pendingDrawerOpen","current","rangeEnd","rangeStart","start","end","setDate","getDate","getDay","setMonth","getMonth","dayOfWeek","setHours","fetchReservations","apiUrl","serverURL","routes","api","params","URLSearchParams","depth","limit","sort","toISOString","response","fetch","result","json","docs","handleEventClick","e","stopPropagation","handleEventKeyDown","key","preventDefault","handleCreateNew","handleDateClick","date","startTime","navigate","direction","prev","next","goToToday","getResName","field","name","getEventLabel","r","compact","time","toLocaleTimeString","hour","minute","serviceName","service","trim","customerName","customer","parts","filter","Boolean","join","getEventTooltip","startStr","endStr","endTime","resourceName","resource","status","renderEventItem","div","className","eventItem","onClick","onKeyDown","role","tabIndex","title","renderStatusLegend","statusLegend","Object","entries","map","label","legendItem","span","legendDot","style","background","renderCurrentTimeLine","cellDate","cellHour","now","getFullYear","getHours","topPercent","getMinutes","currentTimeLine","top","renderMonthView","firstDay","startDay","days","d","i","push","today","todayStr","monthGrid","dayHeader","day","dayStr","isToday","isOtherMonth","dayReservations","rDate","clickDate","dayCell","dayCellOtherMonth","dayCellToday","dayNumber","renderWeekView","startOfWeek","weekDays","hours","Array","from","length","_","weekView","toLocaleDateString","month","weekday","timeLabel","toString","padStart","di","cellReservations","weekCell","renderDayView","dayView","hourReservations","dayViewCell","dateLabel","year","endOfWeek","wrapper","header","navButtons","button","navButton","type","viewToggle","createButton","viewToggleButton","viewToggleButtonActive","onSave"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/CalendarView/index.tsx"],"sourcesContent":["'use client'\nimport type { AdminViewServerProps } from 'payload'\n\nimport { useConfig, useDocumentDrawer, useTranslation } from '@payloadcms/ui'\nimport React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { PluginT } from '../../translations/index.js'\n\nimport styles from './CalendarView.module.css'\n\ntype ViewMode = 'day' | 'month' | 'pending' | 'week'\n\ntype ReservationItem = {\n endTime?: string\n guestCount?: number\n resource?: { name?: string } | string\n service?: { name?: string } | string\n startTime?: string\n}\n\ntype Reservation = {\n customer?: { firstName?: string; lastName?: string; name?: string } | string\n endTime?: string\n id: string\n items?: ReservationItem[]\n resource?: { name?: string } | string\n service?: { name?: string } | string\n startTime: string\n status: string\n}\n\n// Built-in status → CSS class map (for known statuses; custom statuses use inline style)\nconst STATUS_CLASS_MAP: Record<string, string> = {\n cancelled: styles.statusCancelled,\n completed: styles.statusCompleted,\n confirmed: styles.statusConfirmed,\n 'no-show': styles.statusNoShow,\n pending: styles.statusPending,\n}\n\n// Built-in default colors for known statuses\nconst BUILTIN_STATUS_COLORS: Record<string, string> = {\n cancelled: '#e5e7eb',\n completed: '#d1fae5',\n confirmed: '#dbeafe',\n 'no-show': '#fee2e2',\n pending: '#fef3c7',\n}\n\n// Palette for auto-assigning colors to custom statuses\nconst CUSTOM_STATUS_PALETTE = ['#fde68a', '#c7d2fe', '#a7f3d0', '#fca5a5', '#fdba74']\n\nexport const CalendarView: React.FC<AdminViewServerProps> = () => {\n const { config } = useConfig()\n const { t: _t } = useTranslation()\n const t = _t as PluginT\n\n const slugs = config.admin?.custom?.reservationSlugs\n const reservationSlug = slugs?.reservations ?? 'reservations'\n const apiUrl = `${config.serverURL ?? ''}${config.routes.api}/${reservationSlug}`\n\n const statusMachine = config.admin?.custom?.reservationStatusMachine as\n | {\n blockingStatuses?: string[]\n defaultStatus?: string\n statuses?: string[]\n terminalStatuses?: string[]\n transitions?: Record<string, string[]>\n }\n | undefined\n\n // The initial/pending status (what \"pending\" view shows)\n const defaultStatus = statusMachine?.defaultStatus ?? 'pending'\n\n // Build STATUS_COLORS dynamically: built-ins first, then auto-assign palette for custom statuses\n const STATUS_COLORS = useMemo<Record<string, string>>(() => {\n const colors = { ...BUILTIN_STATUS_COLORS }\n const statuses = statusMachine?.statuses ?? []\n statuses.forEach((s, i) => {\n if (!colors[s]) {\n colors[s] = CUSTOM_STATUS_PALETTE[i % CUSTOM_STATUS_PALETTE.length]\n }\n })\n return colors\n }, [statusMachine])\n\n // Derive confirm/cancel target statuses from config transitions\n // \"confirm\" = first non-terminal transition from defaultStatus\n // \"cancel\" = first terminal transition from defaultStatus (or fallback: 'cancelled')\n const { cancelStatus, confirmStatus } = useMemo(() => {\n const terminalStatuses = statusMachine?.terminalStatuses ?? ['completed', 'cancelled', 'no-show']\n const transitions = statusMachine?.transitions ?? {}\n const defaultTransitions: string[] = transitions[defaultStatus] ?? []\n\n const nonTerminal = defaultTransitions.find((s) => !terminalStatuses.includes(s))\n const terminal = defaultTransitions.find((s) => terminalStatuses.includes(s))\n\n return {\n cancelStatus: terminal ?? 'cancelled',\n confirmStatus: nonTerminal ?? 'confirmed',\n }\n }, [statusMachine, defaultStatus])\n\n const STATUS_LABELS = useMemo<Record<string, string>>(() => {\n const statuses = statusMachine?.statuses ?? [\n 'pending',\n 'confirmed',\n 'completed',\n 'cancelled',\n 'no-show',\n ]\n const labels: Record<string, string> = {}\n for (const s of statuses) {\n // Attempt to look up a translation key, e.g. reservation:statusPending\n const key = `reservation:status${s.charAt(0).toUpperCase() + s.slice(1).replace(/-./g, (m) => m[1].toUpperCase())}`\n const translated = t(key)\n // If translation returns the key itself, it's missing — fall back to capitalized status name\n labels[s] = translated !== key ? translated : s.charAt(0).toUpperCase() + s.slice(1)\n }\n return labels\n }, [statusMachine, t])\n\n const [currentDate, setCurrentDate] = useState(() => new Date())\n const [viewMode, setViewMode] = useState<ViewMode>('month')\n const [reservations, setReservations] = useState<Reservation[]>([])\n const [loading, setLoading] = useState(true)\n const [drawerDocId, setDrawerDocId] = useState<null | string>(null)\n const [initialData, setInitialData] = useState<Record<string, unknown> | undefined>(undefined)\n\n // Pending tab state\n const [pendingReservations, setPendingReservations] = useState<Reservation[]>([])\n const [pendingCount, setPendingCount] = useState(0)\n const [selectedIds, setSelectedIds] = useState<Set<string>>(() => new Set())\n const [confirmingIds, setConfirmingIds] = useState<Set<string>>(() => new Set())\n const [actionFeedback, setActionFeedback] = useState<{\n message: string\n type: 'error' | 'success'\n } | null>(null)\n\n const [DocumentDrawer, , { openDrawer }] = useDocumentDrawer({\n id: drawerDocId ?? undefined,\n collectionSlug: reservationSlug,\n })\n\n const pendingDrawerOpen = useRef(false)\n\n useEffect(() => {\n if (pendingDrawerOpen.current) {\n pendingDrawerOpen.current = false\n openDrawer()\n }\n })\n\n const { rangeEnd, rangeStart } = useMemo(() => {\n const start = new Date(currentDate)\n const end = new Date(currentDate)\n\n if (viewMode === 'month') {\n start.setDate(1)\n start.setDate(start.getDate() - start.getDay())\n end.setMonth(end.getMonth() + 1, 0)\n end.setDate(end.getDate() + (6 - end.getDay()))\n } else if (viewMode === 'week') {\n const dayOfWeek = start.getDay()\n start.setDate(start.getDate() - dayOfWeek)\n end.setDate(start.getDate() + 6)\n }\n start.setHours(0, 0, 0, 0)\n end.setHours(23, 59, 59, 999)\n\n return { rangeEnd: end, rangeStart: start }\n }, [currentDate, viewMode])\n\n const fetchReservations = useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n depth: '1',\n limit: '500',\n sort: 'startTime',\n 'where[startTime][greater_than_equal]': rangeStart.toISOString(),\n 'where[startTime][less_than_equal]': rangeEnd.toISOString(),\n })\n const response = await fetch(`${apiUrl}?${params}`)\n const result = await response.json()\n setReservations(result.docs ?? [])\n } catch {\n setReservations([])\n }\n setLoading(false)\n }, [rangeStart, rangeEnd, apiUrl])\n\n useEffect(() => {\n void fetchReservations()\n }, [fetchReservations])\n\n // Fetch pending count (always, for badge) — uses defaultStatus from config\n const fetchPendingCount = useCallback(async () => {\n try {\n const params = new URLSearchParams({\n limit: '0',\n 'where[status][equals]': defaultStatus,\n })\n const response = await fetch(`${apiUrl}?${params}`)\n const result = await response.json()\n setPendingCount(result.totalDocs ?? 0)\n } catch {\n // silently ignore\n }\n }, [apiUrl, defaultStatus])\n\n useEffect(() => {\n void fetchPendingCount()\n }, [fetchPendingCount])\n\n // Fetch pending reservations when tab is active — uses defaultStatus from config\n const fetchPendingReservations = useCallback(async () => {\n try {\n const params = new URLSearchParams({\n depth: '1',\n limit: '500',\n sort: 'startTime',\n 'where[status][equals]': defaultStatus,\n })\n const response = await fetch(`${apiUrl}?${params}`)\n const result = await response.json()\n setPendingReservations(result.docs ?? [])\n } catch {\n setPendingReservations([])\n }\n }, [apiUrl, defaultStatus])\n\n useEffect(() => {\n if (viewMode === 'pending') {\n void fetchPendingReservations()\n }\n }, [viewMode, fetchPendingReservations])\n\n // Clear selection when leaving pending view\n useEffect(() => {\n if (viewMode !== 'pending') {\n setSelectedIds(new Set())\n setActionFeedback(null)\n }\n }, [viewMode])\n\n // Auto-clear feedback toast\n useEffect(() => {\n if (!actionFeedback) {return}\n const timer = setTimeout(() => setActionFeedback(null), 4000)\n return () => clearTimeout(timer)\n }, [actionFeedback])\n\n const patchReservation = useCallback(\n async (id: string, data: Record<string, unknown>): Promise<boolean> => {\n try {\n const response = await fetch(`${apiUrl}/${id}`, {\n body: JSON.stringify(data),\n headers: { 'Content-Type': 'application/json' },\n method: 'PATCH',\n })\n return response.ok\n } catch {\n return false\n }\n },\n [apiUrl],\n )\n\n // Uses confirmStatus derived from config transitions\n const handleQuickConfirm = useCallback(\n async (id: string) => {\n setConfirmingIds((prev) => new Set(prev).add(id))\n const ok = await patchReservation(id, { status: confirmStatus })\n setConfirmingIds((prev) => {\n const next = new Set(prev)\n next.delete(id)\n return next\n })\n setActionFeedback({\n type: ok ? 'success' : 'error',\n message: ok\n ? t('reservation:pendingConfirmSuccess')\n : t('reservation:pendingConfirmError'),\n })\n if (ok) {\n setSelectedIds((prev) => {\n const next = new Set(prev)\n next.delete(id)\n return next\n })\n void fetchPendingReservations()\n void fetchPendingCount()\n }\n },\n [patchReservation, fetchPendingReservations, fetchPendingCount, t, confirmStatus],\n )\n\n // Uses cancelStatus derived from config transitions\n const handleQuickCancel = useCallback(\n async (id: string) => {\n setConfirmingIds((prev) => new Set(prev).add(id))\n const ok = await patchReservation(id, { status: cancelStatus })\n setConfirmingIds((prev) => {\n const next = new Set(prev)\n next.delete(id)\n return next\n })\n setActionFeedback({\n type: ok ? 'success' : 'error',\n message: ok\n ? t('reservation:pendingCancelSuccess')\n : t('reservation:pendingCancelError'),\n })\n if (ok) {\n setSelectedIds((prev) => {\n const next = new Set(prev)\n next.delete(id)\n return next\n })\n void fetchPendingReservations()\n void fetchPendingCount()\n }\n },\n [patchReservation, fetchPendingReservations, fetchPendingCount, t, cancelStatus],\n )\n\n const confirmSelected = useCallback(async () => {\n const ids = Array.from(selectedIds)\n if (ids.length === 0) {return}\n\n setConfirmingIds((prev) => {\n const next = new Set(prev)\n for (const id of ids) {next.add(id)}\n return next\n })\n\n const results = await Promise.allSettled(\n ids.map((id) => patchReservation(id, { status: confirmStatus })),\n )\n\n setConfirmingIds((prev) => {\n const next = new Set(prev)\n for (const id of ids) {next.delete(id)}\n return next\n })\n\n const succeeded = results.filter(\n (r) => r.status === 'fulfilled' && r.value,\n ).length\n const failed = ids.length - succeeded\n\n if (failed === 0) {\n setActionFeedback({\n type: 'success',\n message: `${succeeded} ${t('reservation:pendingConfirmSuccess').toLowerCase()}`,\n })\n } else {\n setActionFeedback({\n type: failed === ids.length ? 'error' : 'success',\n message: t('reservation:pendingBulkConfirmSuccess')\n .replace('{{succeeded}}', String(succeeded))\n .replace('{{failed}}', String(failed)),\n })\n }\n\n setSelectedIds(new Set())\n void fetchPendingReservations()\n void fetchPendingCount()\n }, [selectedIds, patchReservation, fetchPendingReservations, fetchPendingCount, t, confirmStatus])\n\n const handleEventClick = useCallback((e: React.MouseEvent, id: string) => {\n e.stopPropagation()\n setDrawerDocId(id)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }, [])\n\n const handleEventKeyDown = useCallback((e: React.KeyboardEvent, id: string) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n e.stopPropagation()\n setDrawerDocId(id)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }\n }, [])\n\n const handleCreateNew = useCallback(() => {\n setDrawerDocId(null)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }, [])\n\n const handleDateClick = useCallback((date: Date) => {\n setDrawerDocId(null)\n setInitialData({ startTime: date.toISOString() })\n pendingDrawerOpen.current = true\n }, [])\n\n const openDocDrawer = useCallback((id: string) => {\n setDrawerDocId(id)\n setInitialData(undefined)\n pendingDrawerOpen.current = true\n }, [])\n\n const navigate = useCallback(\n (direction: -1 | 1) => {\n setCurrentDate((prev) => {\n const next = new Date(prev)\n if (viewMode === 'month') {\n next.setMonth(next.getMonth() + direction)\n } else if (viewMode === 'week') {\n next.setDate(next.getDate() + 7 * direction)\n } else {\n next.setDate(next.getDate() + direction)\n }\n return next\n })\n },\n [viewMode],\n )\n\n const goToToday = useCallback(() => setCurrentDate(new Date()), [])\n\n const getResName = (field: { name?: string } | string | undefined): string => {\n if (!field) {return ''}\n if (typeof field === 'string') {return ''}\n return field.name ?? ''\n }\n\n const getCustomerName = (field: Reservation['customer']): string => {\n if (!field) {return ''}\n if (typeof field === 'string') {return ''}\n const parts = [field.firstName, field.lastName].filter(Boolean)\n return parts.length > 0 ? parts.join(' ') : (field.name ?? '')\n }\n\n const getEventLabel = (r: Reservation, compact: boolean) => {\n const time = new Date(r.startTime).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })\n const serviceName = getResName(r.service)\n if (compact) {\n return `${time} ${serviceName}`.trim()\n }\n const customerName = getCustomerName(r.customer)\n const parts = [time, serviceName, customerName].filter(Boolean)\n return parts.join(' - ')\n }\n\n // Returns all resource names for a reservation — from items array if present, otherwise top-level resource\n const getResourceNames = (r: Reservation): string[] => {\n if (r.items && r.items.length > 0) {\n const names = r.items\n .map((item) => getResName(item.resource))\n .filter((name) => name.length > 0)\n if (names.length > 0) {return names}\n }\n const single = getResName(r.resource)\n return single ? [single] : []\n }\n\n const getEventTooltip = (r: Reservation): string => {\n const serviceName = getResName(r.service) || t('reservation:calendarUnknownService')\n const startStr = new Date(r.startTime).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })\n const endStr = r.endTime\n ? new Date(r.endTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n : '?'\n const customerName = getCustomerName(r.customer) || t('reservation:calendarUnknownCustomer')\n const resourceNames = getResourceNames(r)\n const resourceStr =\n resourceNames.length > 0\n ? resourceNames.join(', ')\n : t('reservation:calendarUnknownResource')\n const status = STATUS_LABELS[r.status] ?? r.status\n return [\n serviceName,\n `${startStr} - ${endStr}`,\n `${t('reservation:tooltipCustomer')} ${customerName}`,\n `${t('reservation:tooltipResource')} ${resourceStr}`,\n `${t('reservation:tooltipStatus')} ${status}`,\n ].join('\\n')\n }\n\n const renderEventItem = (r: Reservation, compact: boolean) => {\n // Use CSS class for built-in statuses; also apply inline background for custom statuses\n const cssClass = STATUS_CLASS_MAP[r.status] ?? ''\n const color = STATUS_COLORS[r.status]\n // Only apply inline style when there's no CSS class (custom statuses) or as a supplement\n const inlineStyle = cssClass ? undefined : { background: color }\n return (\n <div\n className={`${styles.eventItem} ${cssClass}`}\n key={r.id}\n onClick={(e) => handleEventClick(e, r.id)}\n onKeyDown={(e) => handleEventKeyDown(e, r.id)}\n role=\"button\"\n style={inlineStyle}\n tabIndex={0}\n title={getEventTooltip(r)}\n >\n {getEventLabel(r, compact)}\n </div>\n )\n }\n\n // Dynamic legend: iterates all statuses from the status machine config\n const renderStatusLegend = () => {\n const statuses = statusMachine?.statuses ?? Object.keys(BUILTIN_STATUS_COLORS)\n return (\n <div className={styles.statusLegend}>\n {statuses.map((key) => (\n <div className={styles.legendItem} key={key}>\n <span className={styles.legendDot} style={{ background: STATUS_COLORS[key] }} />\n {STATUS_LABELS[key] ?? key}\n </div>\n ))}\n </div>\n )\n }\n\n const renderCurrentTimeLine = (cellDate: Date, cellHour: number) => {\n const now = new Date()\n if (\n now.getFullYear() !== cellDate.getFullYear() ||\n now.getMonth() !== cellDate.getMonth() ||\n now.getDate() !== cellDate.getDate() ||\n now.getHours() !== cellHour\n ) {\n return null\n }\n const topPercent = (now.getMinutes() / 60) * 100\n return <div className={styles.currentTimeLine} style={{ top: `${topPercent}%` }} />\n }\n\n const renderMonthView = () => {\n const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1)\n const startDay = new Date(firstDay)\n startDay.setDate(startDay.getDate() - startDay.getDay())\n\n const days: Date[] = []\n const d = new Date(startDay)\n for (let i = 0; i < 42; i++) {\n days.push(new Date(d))\n d.setDate(d.getDate() + 1)\n }\n\n const today = new Date()\n const todayStr = `${today.getFullYear()}-${today.getMonth()}-${today.getDate()}`\n\n return (\n <div className={styles.monthGrid}>\n {[\n t('reservation:dayShortSun'),\n t('reservation:dayShortMon'),\n t('reservation:dayShortTue'),\n t('reservation:dayShortWed'),\n t('reservation:dayShortThu'),\n t('reservation:dayShortFri'),\n t('reservation:dayShortSat'),\n ].map((d) => (\n <div className={styles.dayHeader} key={d}>\n {d}\n </div>\n ))}\n {days.map((day, i) => {\n const dayStr = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`\n const isToday = dayStr === todayStr\n const isOtherMonth = day.getMonth() !== currentDate.getMonth()\n const dayReservations = reservations.filter((r) => {\n const rDate = new Date(r.startTime)\n return (\n rDate.getFullYear() === day.getFullYear() &&\n rDate.getMonth() === day.getMonth() &&\n rDate.getDate() === day.getDate()\n )\n })\n\n const clickDate = new Date(day)\n clickDate.setHours(9, 0, 0, 0)\n\n return (\n <div\n className={`${styles.dayCell} ${isOtherMonth ? styles.dayCellOtherMonth : ''} ${isToday ? styles.dayCellToday : ''}`}\n key={i}\n onClick={() => handleDateClick(clickDate)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleDateClick(clickDate)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n <div className={styles.dayNumber}>{day.getDate()}</div>\n {dayReservations.map((r) => renderEventItem(r, true))}\n </div>\n )\n })}\n </div>\n )\n }\n\n const renderWeekView = () => {\n const startOfWeek = new Date(currentDate)\n startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay())\n startOfWeek.setHours(0, 0, 0, 0)\n\n const weekDays: Date[] = []\n for (let i = 0; i < 7; i++) {\n const d = new Date(startOfWeek)\n d.setDate(d.getDate() + i)\n weekDays.push(d)\n }\n\n const hours = Array.from({ length: 12 }, (_, i) => i + 7)\n\n return (\n <div className={styles.weekView}>\n <div className={styles.dayHeader} />\n {weekDays.map((d, i) => (\n <div className={styles.dayHeader} key={i}>\n {d.toLocaleDateString([], { day: 'numeric', month: 'numeric', weekday: 'short' })}\n </div>\n ))}\n {hours.map((hour) => (\n <Fragment key={`row-${hour}`}>\n <div className={styles.timeLabel}>\n {hour.toString().padStart(2, '0')}:00\n </div>\n {weekDays.map((day, di) => {\n const cellReservations = reservations.filter((r) => {\n const rDate = new Date(r.startTime)\n return (\n rDate.getFullYear() === day.getFullYear() &&\n rDate.getMonth() === day.getMonth() &&\n rDate.getDate() === day.getDate() &&\n rDate.getHours() === hour\n )\n })\n const clickDate = new Date(day)\n clickDate.setHours(hour, 0, 0, 0)\n return (\n <div\n className={styles.weekCell}\n key={`cell-${hour}-${di}`}\n onClick={() => handleDateClick(clickDate)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleDateClick(clickDate)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {renderCurrentTimeLine(day, hour)}\n {cellReservations.map((r) => renderEventItem(r, false))}\n </div>\n )\n })}\n </Fragment>\n ))}\n </div>\n )\n }\n\n const renderDayView = () => {\n const hours = Array.from({ length: 14 }, (_, i) => i + 7)\n\n return (\n <div className={styles.dayView}>\n {hours.map((hour) => {\n const hourReservations = reservations.filter((r) => {\n const rDate = new Date(r.startTime)\n return (\n rDate.getFullYear() === currentDate.getFullYear() &&\n rDate.getMonth() === currentDate.getMonth() &&\n rDate.getDate() === currentDate.getDate() &&\n rDate.getHours() === hour\n )\n })\n const clickDate = new Date(currentDate)\n clickDate.setHours(hour, 0, 0, 0)\n return (\n <Fragment key={`row-${hour}`}>\n <div className={styles.timeLabel}>\n {hour.toString().padStart(2, '0')}:00\n </div>\n <div\n className={styles.dayViewCell}\n onClick={() => handleDateClick(clickDate)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleDateClick(clickDate)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {renderCurrentTimeLine(currentDate, hour)}\n {hourReservations.map((r) => renderEventItem(r, false))}\n </div>\n </Fragment>\n )\n })}\n </div>\n )\n }\n\n const renderPendingView = () => {\n if (pendingReservations.length === 0) {\n return <div className={styles.pendingEmpty}>{t('reservation:pendingEmpty')}</div>\n }\n\n const allSelected =\n pendingReservations.length > 0 &&\n pendingReservations.every((r) => selectedIds.has(r.id))\n\n const toggleSelectAll = () => {\n if (allSelected) {\n setSelectedIds(new Set())\n } else {\n setSelectedIds(new Set(pendingReservations.map((r) => r.id)))\n }\n }\n\n const toggleSelect = (id: string) => {\n setSelectedIds((prev) => {\n const next = new Set(prev)\n if (next.has(id)) {\n next.delete(id)\n } else {\n next.add(id)\n }\n return next\n })\n }\n\n const formatDateTime = (iso: string) => {\n const d = new Date(iso)\n return d.toLocaleString([], {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'short',\n year: 'numeric',\n })\n }\n\n return (\n <div className={styles.pendingView}>\n <div className={styles.pendingToolbar}>\n <label className={styles.selectAllLabel}>\n <input\n aria-label={t('reservation:pendingSelectAll')}\n checked={allSelected}\n onChange={toggleSelectAll}\n type=\"checkbox\"\n />\n {t('reservation:pendingSelectAll')}\n </label>\n {selectedIds.size > 0 && (\n <button\n className={styles.bulkConfirmButton}\n disabled={confirmingIds.size > 0}\n onClick={() => void confirmSelected()}\n type=\"button\"\n >\n {confirmingIds.size > 0\n ? t('reservation:pendingConfirming')\n : t('reservation:pendingConfirmSelected').replace(\n '{{count}}',\n String(selectedIds.size),\n )}\n </button>\n )}\n </div>\n {actionFeedback && (\n <div\n className={`${styles.feedbackToast} ${actionFeedback.type === 'success' ? styles.feedbackSuccess : styles.feedbackError}`}\n >\n {actionFeedback.message}\n </div>\n )}\n <table className={styles.pendingTable}>\n <thead>\n <tr>\n <th aria-label={t('reservation:pendingSelectAll')} className={styles.pendingTh} />\n <th className={styles.pendingTh}>{t('reservation:fieldCustomer')}</th>\n <th className={styles.pendingTh}>{t('reservation:fieldService')}</th>\n <th className={styles.pendingTh}>{t('reservation:fieldResource')}</th>\n <th className={styles.pendingTh}>{t('reservation:pendingDateTime')}</th>\n <th className={styles.pendingTh}>{t('reservation:pendingActions')}</th>\n </tr>\n </thead>\n <tbody>\n {pendingReservations.map((r) => {\n const isConfirming = confirmingIds.has(r.id)\n // Show all resources from items array if present, else top-level resource\n const resourceDisplay =\n getResourceNames(r).join(', ') || t('reservation:calendarUnknownResource')\n return (\n <tr className={styles.pendingRow} key={r.id}>\n <td className={styles.pendingTd}>\n <input\n aria-label={getCustomerName(r.customer) || r.id}\n checked={selectedIds.has(r.id)}\n onChange={() => toggleSelect(r.id)}\n type=\"checkbox\"\n />\n </td>\n <td className={styles.pendingTd}>\n <span\n className={styles.pendingCustomerLink}\n onClick={() => openDocDrawer(r.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDocDrawer(r.id)\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {getCustomerName(r.customer) || t('reservation:calendarUnknownCustomer')}\n </span>\n </td>\n <td className={styles.pendingTd}>\n {getResName(r.service) || t('reservation:calendarUnknownService')}\n </td>\n <td className={styles.pendingTd}>{resourceDisplay}</td>\n <td className={styles.pendingTd}>{formatDateTime(r.startTime)}</td>\n <td className={styles.pendingTd}>\n <button\n className={styles.confirmButton}\n disabled={isConfirming}\n onClick={() => void handleQuickConfirm(r.id)}\n title={t('reservation:pendingConfirm')}\n type=\"button\"\n >\n &#x2713;\n </button>\n <button\n className={styles.cancelButton}\n disabled={isConfirming}\n onClick={() => void handleQuickCancel(r.id)}\n title={t('reservation:pendingCancel')}\n type=\"button\"\n >\n &#x2717;\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n }\n\n const dateLabel = useMemo(() => {\n if (viewMode === 'month') {\n return currentDate.toLocaleDateString([], { month: 'long', year: 'numeric' })\n }\n if (viewMode === 'week') {\n const startOfWeek = new Date(currentDate)\n startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay())\n const endOfWeek = new Date(startOfWeek)\n endOfWeek.setDate(endOfWeek.getDate() + 6)\n return `${startOfWeek.toLocaleDateString([], { day: 'numeric', month: 'short' })} - ${endOfWeek.toLocaleDateString([], { day: 'numeric', month: 'short', year: 'numeric' })}`\n }\n return currentDate.toLocaleDateString([], {\n day: 'numeric',\n month: 'long',\n weekday: 'long',\n year: 'numeric',\n })\n }, [currentDate, viewMode])\n\n const handleDrawerSave = useCallback(() => {\n void fetchReservations()\n void fetchPendingCount()\n if (viewMode === 'pending') {\n void fetchPendingReservations()\n }\n }, [fetchReservations, fetchPendingCount, fetchPendingReservations, viewMode])\n\n return (\n <div className={styles.wrapper}>\n <div className={styles.header}>\n {viewMode !== 'pending' && (\n <div className={styles.navButtons}>\n <button className={styles.navButton} onClick={() => navigate(-1)} type=\"button\">\n &larr;\n </button>\n <button className={styles.navButton} onClick={goToToday} type=\"button\">\n {t('reservation:calendarToday')}\n </button>\n <button className={styles.navButton} onClick={() => navigate(1)} type=\"button\">\n &rarr;\n </button>\n <span className={styles.currentDate}>{dateLabel}</span>\n </div>\n )}\n {viewMode === 'pending' && <div />}\n <div className={styles.viewToggle}>\n <button className={styles.createButton} onClick={handleCreateNew} type=\"button\">\n {t('reservation:calendarCreateNew')}\n </button>\n {([\n { key: 'month' as ViewMode, label: t('reservation:calendarMonth') },\n { key: 'week' as ViewMode, label: t('reservation:calendarWeek') },\n { key: 'day' as ViewMode, label: t('reservation:calendarDay') },\n { key: 'pending' as ViewMode, label: t('reservation:calendarPending') },\n ]).map(({ key, label }) => (\n <button\n className={`${styles.viewToggleButton} ${viewMode === key ? styles.viewToggleButtonActive : ''}`}\n key={key}\n onClick={() => setViewMode(key)}\n type=\"button\"\n >\n {label}\n {key === 'pending' && pendingCount > 0 && (\n <span className={styles.pendingBadge}>{pendingCount}</span>\n )}\n </button>\n ))}\n </div>\n </div>\n {viewMode !== 'pending' && renderStatusLegend()}\n {loading && viewMode !== 'pending' ? (\n <div className={styles.loading}>{t('reservation:calendarLoading')}</div>\n ) : (\n <>\n {viewMode === 'month' && renderMonthView()}\n {viewMode === 'week' && renderWeekView()}\n {viewMode === 'day' && renderDayView()}\n </>\n )}\n {viewMode === 'pending' && renderPendingView()}\n <DocumentDrawer initialData={initialData} onSave={handleDrawerSave} />\n </div>\n )\n}\n"],"names":["useConfig","useDocumentDrawer","useTranslation","React","Fragment","useCallback","useEffect","useMemo","useRef","useState","styles","STATUS_CLASS_MAP","cancelled","statusCancelled","completed","statusCompleted","confirmed","statusConfirmed","statusNoShow","pending","statusPending","BUILTIN_STATUS_COLORS","CUSTOM_STATUS_PALETTE","CalendarView","config","t","_t","slugs","admin","custom","reservationSlugs","reservationSlug","reservations","apiUrl","serverURL","routes","api","statusMachine","reservationStatusMachine","defaultStatus","STATUS_COLORS","colors","statuses","forEach","s","i","length","cancelStatus","confirmStatus","terminalStatuses","transitions","defaultTransitions","nonTerminal","find","includes","terminal","STATUS_LABELS","labels","key","charAt","toUpperCase","slice","replace","m","translated","currentDate","setCurrentDate","Date","viewMode","setViewMode","setReservations","loading","setLoading","drawerDocId","setDrawerDocId","initialData","setInitialData","undefined","pendingReservations","setPendingReservations","pendingCount","setPendingCount","selectedIds","setSelectedIds","Set","confirmingIds","setConfirmingIds","actionFeedback","setActionFeedback","DocumentDrawer","openDrawer","id","collectionSlug","pendingDrawerOpen","current","rangeEnd","rangeStart","start","end","setDate","getDate","getDay","setMonth","getMonth","dayOfWeek","setHours","fetchReservations","params","URLSearchParams","depth","limit","sort","toISOString","response","fetch","result","json","docs","fetchPendingCount","totalDocs","fetchPendingReservations","timer","setTimeout","clearTimeout","patchReservation","data","body","JSON","stringify","headers","method","ok","handleQuickConfirm","prev","add","status","next","delete","type","message","handleQuickCancel","confirmSelected","ids","Array","from","results","Promise","allSettled","map","succeeded","filter","r","value","failed","toLowerCase","String","handleEventClick","e","stopPropagation","handleEventKeyDown","preventDefault","handleCreateNew","handleDateClick","date","startTime","openDocDrawer","navigate","direction","goToToday","getResName","field","name","getCustomerName","parts","firstName","lastName","Boolean","join","getEventLabel","compact","time","toLocaleTimeString","hour","minute","serviceName","service","trim","customerName","customer","getResourceNames","items","names","item","resource","single","getEventTooltip","startStr","endStr","endTime","resourceNames","resourceStr","renderEventItem","cssClass","color","inlineStyle","background","div","className","eventItem","onClick","onKeyDown","role","style","tabIndex","title","renderStatusLegend","Object","keys","statusLegend","legendItem","span","legendDot","renderCurrentTimeLine","cellDate","cellHour","now","getFullYear","getHours","topPercent","getMinutes","currentTimeLine","top","renderMonthView","firstDay","startDay","days","d","push","today","todayStr","monthGrid","dayHeader","day","dayStr","isToday","isOtherMonth","dayReservations","rDate","clickDate","dayCell","dayCellOtherMonth","dayCellToday","dayNumber","renderWeekView","startOfWeek","weekDays","hours","_","weekView","toLocaleDateString","month","weekday","timeLabel","toString","padStart","di","cellReservations","weekCell","renderDayView","dayView","hourReservations","dayViewCell","renderPendingView","pendingEmpty","allSelected","every","has","toggleSelectAll","toggleSelect","formatDateTime","iso","toLocaleString","year","pendingView","pendingToolbar","label","selectAllLabel","input","aria-label","checked","onChange","size","button","bulkConfirmButton","disabled","feedbackToast","feedbackSuccess","feedbackError","table","pendingTable","thead","tr","th","pendingTh","tbody","isConfirming","resourceDisplay","pendingRow","td","pendingTd","pendingCustomerLink","confirmButton","cancelButton","dateLabel","endOfWeek","handleDrawerSave","wrapper","header","navButtons","navButton","viewToggle","createButton","viewToggleButton","viewToggleButtonActive","pendingBadge","onSave"],"mappings":"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"}
@@ -3,6 +3,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { FieldLabel, useConfig, useDocumentDrawer, useField, useTranslation } from '@payloadcms/ui';
4
4
  import React, { useCallback, useEffect, useRef, useState } from 'react';
5
5
  import styles from './CustomerField.module.css';
6
+ const getDisplayName = (customer)=>{
7
+ if (customer.name) {
8
+ return customer.name;
9
+ }
10
+ const fullName = [
11
+ customer.firstName,
12
+ customer.lastName
13
+ ].filter(Boolean).join(' ');
14
+ return fullName || customer.email;
15
+ };
6
16
  export const CustomerField = ({ field, path: pathProp })=>{
7
17
  const fieldPath = pathProp ?? field?.name ?? 'customer';
8
18
  const { config } = useConfig();
@@ -12,7 +22,7 @@ export const CustomerField = ({ field, path: pathProp })=>{
12
22
  path: fieldPath
13
23
  });
14
24
  const slugs = config.admin?.custom?.reservationSlugs;
15
- const userCollection = slugs?.userCollection ?? 'users';
25
+ const customersCollection = slugs?.customers ?? 'customers';
16
26
  const [search, setSearch] = useState('');
17
27
  const [results, setResults] = useState([]);
18
28
  const [selectedCustomer, setSelectedCustomer] = useState(null);
@@ -21,7 +31,7 @@ export const CustomerField = ({ field, path: pathProp })=>{
21
31
  const wrapperRef = useRef(null);
22
32
  const debounceRef = useRef(null);
23
33
  const [DocumentDrawer, , { openDrawer }] = useDocumentDrawer({
24
- collectionSlug: userCollection
34
+ collectionSlug: customersCollection
25
35
  });
26
36
  // Fetch selected customer details when value changes
27
37
  useEffect(()=>{
@@ -35,14 +45,16 @@ export const CustomerField = ({ field, path: pathProp })=>{
35
45
  }
36
46
  const fetchCustomer = async ()=>{
37
47
  try {
38
- const res = await fetch(`/api/${userCollection}/${value}`);
48
+ const res = await fetch(`/api/${customersCollection}/${value}`);
39
49
  if (res.ok) {
40
50
  const doc = await res.json();
41
51
  setSelectedCustomer({
42
52
  id: doc.id,
43
- name: doc.name ?? '',
53
+ name: doc.name ?? undefined,
44
54
  email: doc.email ?? '',
45
- phone: doc.phone ?? ''
55
+ firstName: doc.firstName ?? undefined,
56
+ lastName: doc.lastName ?? undefined,
57
+ phone: doc.phone ?? undefined
46
58
  });
47
59
  }
48
60
  } catch {
@@ -52,7 +64,7 @@ export const CustomerField = ({ field, path: pathProp })=>{
52
64
  void fetchCustomer();
53
65
  }, [
54
66
  value,
55
- userCollection,
67
+ customersCollection,
56
68
  selectedCustomer?.id
57
69
  ]);
58
70
  // Debounced search
@@ -129,9 +141,11 @@ export const CustomerField = ({ field, path: pathProp })=>{
129
141
  const handleDrawerSave = useCallback(({ doc })=>{
130
142
  const customer = {
131
143
  id: doc.id,
132
- name: doc.name ?? '',
144
+ name: doc.name ?? undefined,
133
145
  email: doc.email ?? '',
134
- phone: doc.phone ?? ''
146
+ firstName: doc.firstName ?? undefined,
147
+ lastName: doc.lastName ?? undefined,
148
+ phone: doc.phone ?? undefined
135
149
  };
136
150
  setValue(customer.id);
137
151
  setSelectedCustomer(customer);
@@ -155,7 +169,7 @@ export const CustomerField = ({ field, path: pathProp })=>{
155
169
  children: [
156
170
  /*#__PURE__*/ _jsx("span", {
157
171
  className: styles.selectedName,
158
- children: selectedCustomer.name || selectedCustomer.email
172
+ children: getDisplayName(selectedCustomer)
159
173
  }),
160
174
  /*#__PURE__*/ _jsx("span", {
161
175
  className: styles.selectedMeta,
@@ -206,7 +220,7 @@ export const CustomerField = ({ field, path: pathProp })=>{
206
220
  children: [
207
221
  /*#__PURE__*/ _jsx("span", {
208
222
  className: styles.optionName,
209
- children: customer.name || customer.email
223
+ children: getDisplayName(customer)
210
224
  }),
211
225
  /*#__PURE__*/ _jsx("span", {
212
226
  className: styles.optionMeta,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/CustomerField/index.tsx"],"sourcesContent":["'use client'\nimport type { RelationshipFieldClientComponent } from 'payload'\n\nimport { FieldLabel, useConfig, useDocumentDrawer, useField, useTranslation } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { PluginT } from '../../translations/index.js'\n\nimport styles from './CustomerField.module.css'\n\ntype CustomerDoc = {\n email: string\n id: string\n name: string\n phone: string\n}\n\nexport const CustomerField: RelationshipFieldClientComponent = ({ field, path: pathProp }) => {\n const fieldPath = pathProp ?? field?.name ?? 'customer'\n const { config } = useConfig()\n const { t: _t } = useTranslation()\n const t = _t as PluginT\n\n const { setValue, value } = useField<string>({ path: fieldPath })\n\n const slugs = config.admin?.custom?.reservationSlugs\n const userCollection: string = slugs?.userCollection ?? 'users'\n\n const [search, setSearch] = useState('')\n const [results, setResults] = useState<CustomerDoc[]>([])\n const [selectedCustomer, setSelectedCustomer] = useState<CustomerDoc | null>(null)\n const [isOpen, setIsOpen] = useState(false)\n const [loading, setLoading] = useState(false)\n\n const wrapperRef = useRef<HTMLDivElement>(null)\n const debounceRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n\n const [DocumentDrawer, , { openDrawer }] = useDocumentDrawer({\n collectionSlug: userCollection,\n })\n\n // Fetch selected customer details when value changes\n useEffect(() => {\n if (!value) {\n setSelectedCustomer(null)\n return\n }\n\n // If we already have the selected customer data, skip fetch\n if (selectedCustomer?.id === value) {return}\n\n const fetchCustomer = async () => {\n try {\n const res = await fetch(`/api/${userCollection}/${value}`)\n if (res.ok) {\n const doc = await res.json()\n setSelectedCustomer({\n id: doc.id,\n name: doc.name ?? '',\n email: doc.email ?? '',\n phone: doc.phone ?? '',\n })\n }\n } catch {\n // Silently fail — the field will still show the ID\n }\n }\n void fetchCustomer()\n }, [value, userCollection, selectedCustomer?.id])\n\n // Debounced search\n const doSearch = useCallback(\n async (query: string) => {\n setLoading(true)\n try {\n const params = new URLSearchParams({ limit: '10', search: query })\n const res = await fetch(`/api/reservation-customer-search?${params.toString()}`)\n if (res.ok) {\n const data = await res.json()\n setResults(data.docs)\n }\n } catch {\n setResults([])\n } finally {\n setLoading(false)\n }\n },\n [],\n )\n\n const handleSearchChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setSearch(val)\n\n if (debounceRef.current) {\n clearTimeout(debounceRef.current)\n }\n\n debounceRef.current = setTimeout(() => {\n void doSearch(val)\n }, 300)\n },\n [doSearch],\n )\n\n // Click outside to close\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) {\n setIsOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [])\n\n const handleSelect = useCallback(\n (customer: CustomerDoc) => {\n setValue(customer.id)\n setSelectedCustomer(customer)\n setIsOpen(false)\n setSearch('')\n },\n [setValue],\n )\n\n const handleClear = useCallback(() => {\n setValue(null as unknown as string)\n setSelectedCustomer(null)\n setSearch('')\n }, [setValue])\n\n const handleFocus = useCallback(() => {\n setIsOpen(true)\n if (results.length === 0) {\n void doSearch('')\n }\n }, [doSearch, results.length])\n\n const handleCreate = useCallback(() => {\n setIsOpen(false)\n openDrawer()\n }, [openDrawer])\n\n const handleDrawerSave = useCallback(\n ({ doc }: { doc: Record<string, unknown> }) => {\n const customer: CustomerDoc = {\n id: doc.id as string,\n name: (doc.name as string) ?? '',\n email: (doc.email as string) ?? '',\n phone: (doc.phone as string) ?? '',\n }\n setValue(customer.id)\n setSelectedCustomer(customer)\n },\n [setValue],\n )\n\n return (\n <div className={styles.wrapper} ref={wrapperRef}>\n <FieldLabel label={field?.label ?? t('reservation:fieldCustomer')} path={fieldPath} required={field?.required} />\n\n {selectedCustomer ? (\n <div className={styles.selected}>\n <div className={styles.selectedInfo}>\n <span className={styles.selectedName}>{selectedCustomer.name || selectedCustomer.email}</span>\n <span className={styles.selectedMeta}>\n {[selectedCustomer.phone, selectedCustomer.email].filter(Boolean).join(' · ')}\n </span>\n </div>\n <button\n className={styles.clearButton}\n onClick={handleClear}\n type=\"button\"\n >\n {t('reservation:fieldCustomerClear')}\n </button>\n </div>\n ) : (\n <input\n aria-label={t('reservation:fieldCustomerSearch')}\n className={styles.searchInput}\n onChange={handleSearchChange}\n onFocus={handleFocus}\n placeholder={t('reservation:fieldCustomerSearch')}\n type=\"text\"\n value={search}\n />\n )}\n\n {isOpen && !selectedCustomer && (\n <div className={styles.dropdown}>\n {loading && results.length === 0 ? (\n <div className={styles.noResults}>...</div>\n ) : results.length === 0 && search ? (\n <div className={styles.noResults}>{t('reservation:fieldCustomerNoResults')}</div>\n ) : (\n results.map((customer) => (\n <div\n aria-selected={false}\n className={styles.option}\n key={customer.id}\n onClick={() => handleSelect(customer)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleSelect(customer)\n }\n }}\n role=\"option\"\n tabIndex={0}\n >\n <span className={styles.optionName}>{customer.name || customer.email}</span>\n <span className={styles.optionMeta}>\n {[customer.phone, customer.email].filter(Boolean).join(' · ')}\n </span>\n </div>\n ))\n )}\n <button\n className={styles.createButton}\n onClick={handleCreate}\n type=\"button\"\n >\n + {t('reservation:fieldCustomerCreateNew')}\n </button>\n </div>\n )}\n\n <DocumentDrawer onSave={handleDrawerSave} />\n </div>\n )\n}\n"],"names":["FieldLabel","useConfig","useDocumentDrawer","useField","useTranslation","React","useCallback","useEffect","useRef","useState","styles","CustomerField","field","path","pathProp","fieldPath","name","config","t","_t","setValue","value","slugs","admin","custom","reservationSlugs","userCollection","search","setSearch","results","setResults","selectedCustomer","setSelectedCustomer","isOpen","setIsOpen","loading","setLoading","wrapperRef","debounceRef","DocumentDrawer","openDrawer","collectionSlug","id","fetchCustomer","res","fetch","ok","doc","json","email","phone","doSearch","query","params","URLSearchParams","limit","toString","data","docs","handleSearchChange","e","val","target","current","clearTimeout","setTimeout","handleClickOutside","contains","document","addEventListener","removeEventListener","handleSelect","customer","handleClear","handleFocus","length","handleCreate","handleDrawerSave","div","className","wrapper","ref","label","required","selected","selectedInfo","span","selectedName","selectedMeta","filter","Boolean","join","button","clearButton","onClick","type","input","aria-label","searchInput","onChange","onFocus","placeholder","dropdown","noResults","map","aria-selected","option","onKeyDown","key","preventDefault","role","tabIndex","optionName","optionMeta","createButton","onSave"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/CustomerField/index.tsx"],"sourcesContent":["'use client'\nimport type { RelationshipFieldClientComponent } from 'payload'\n\nimport { FieldLabel, useConfig, useDocumentDrawer, useField, useTranslation } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { PluginT } from '../../translations/index.js'\n\nimport styles from './CustomerField.module.css'\n\ntype CustomerDoc = {\n email: string\n firstName?: string\n id: string\n lastName?: string\n name?: string\n phone?: string\n}\n\nconst getDisplayName = (customer: CustomerDoc): string => {\n if (customer.name) {return customer.name}\n const fullName = [customer.firstName, customer.lastName].filter(Boolean).join(' ')\n return fullName || customer.email\n}\n\nexport const CustomerField: RelationshipFieldClientComponent = ({ field, path: pathProp }) => {\n const fieldPath = pathProp ?? field?.name ?? 'customer'\n const { config } = useConfig()\n const { t: _t } = useTranslation()\n const t = _t as PluginT\n\n const { setValue, value } = useField<string>({ path: fieldPath })\n\n const slugs = config.admin?.custom?.reservationSlugs\n const customersCollection: string = slugs?.customers ?? 'customers'\n\n const [search, setSearch] = useState('')\n const [results, setResults] = useState<CustomerDoc[]>([])\n const [selectedCustomer, setSelectedCustomer] = useState<CustomerDoc | null>(null)\n const [isOpen, setIsOpen] = useState(false)\n const [loading, setLoading] = useState(false)\n\n const wrapperRef = useRef<HTMLDivElement>(null)\n const debounceRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n\n const [DocumentDrawer, , { openDrawer }] = useDocumentDrawer({\n collectionSlug: customersCollection,\n })\n\n // Fetch selected customer details when value changes\n useEffect(() => {\n if (!value) {\n setSelectedCustomer(null)\n return\n }\n\n // If we already have the selected customer data, skip fetch\n if (selectedCustomer?.id === value) {return}\n\n const fetchCustomer = async () => {\n try {\n const res = await fetch(`/api/${customersCollection}/${value}`)\n if (res.ok) {\n const doc = await res.json()\n setSelectedCustomer({\n id: doc.id,\n name: doc.name ?? undefined,\n email: doc.email ?? '',\n firstName: doc.firstName ?? undefined,\n lastName: doc.lastName ?? undefined,\n phone: doc.phone ?? undefined,\n })\n }\n } catch {\n // Silently fail — the field will still show the ID\n }\n }\n void fetchCustomer()\n }, [value, customersCollection, selectedCustomer?.id])\n\n // Debounced search\n const doSearch = useCallback(\n async (query: string) => {\n setLoading(true)\n try {\n const params = new URLSearchParams({ limit: '10', search: query })\n const res = await fetch(`/api/reservation-customer-search?${params.toString()}`)\n if (res.ok) {\n const data = await res.json()\n setResults(data.docs)\n }\n } catch {\n setResults([])\n } finally {\n setLoading(false)\n }\n },\n [],\n )\n\n const handleSearchChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setSearch(val)\n\n if (debounceRef.current) {\n clearTimeout(debounceRef.current)\n }\n\n debounceRef.current = setTimeout(() => {\n void doSearch(val)\n }, 300)\n },\n [doSearch],\n )\n\n // Click outside to close\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) {\n setIsOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [])\n\n const handleSelect = useCallback(\n (customer: CustomerDoc) => {\n setValue(customer.id)\n setSelectedCustomer(customer)\n setIsOpen(false)\n setSearch('')\n },\n [setValue],\n )\n\n const handleClear = useCallback(() => {\n setValue(null as unknown as string)\n setSelectedCustomer(null)\n setSearch('')\n }, [setValue])\n\n const handleFocus = useCallback(() => {\n setIsOpen(true)\n if (results.length === 0) {\n void doSearch('')\n }\n }, [doSearch, results.length])\n\n const handleCreate = useCallback(() => {\n setIsOpen(false)\n openDrawer()\n }, [openDrawer])\n\n const handleDrawerSave = useCallback(\n ({ doc }: { doc: Record<string, unknown> }) => {\n const customer: CustomerDoc = {\n id: doc.id as string,\n name: (doc.name as string) ?? undefined,\n email: (doc.email as string) ?? '',\n firstName: (doc.firstName as string) ?? undefined,\n lastName: (doc.lastName as string) ?? undefined,\n phone: (doc.phone as string) ?? undefined,\n }\n setValue(customer.id)\n setSelectedCustomer(customer)\n },\n [setValue],\n )\n\n return (\n <div className={styles.wrapper} ref={wrapperRef}>\n <FieldLabel label={field?.label ?? t('reservation:fieldCustomer')} path={fieldPath} required={field?.required} />\n\n {selectedCustomer ? (\n <div className={styles.selected}>\n <div className={styles.selectedInfo}>\n <span className={styles.selectedName}>{getDisplayName(selectedCustomer)}</span>\n <span className={styles.selectedMeta}>\n {[selectedCustomer.phone, selectedCustomer.email].filter(Boolean).join(' · ')}\n </span>\n </div>\n <button\n className={styles.clearButton}\n onClick={handleClear}\n type=\"button\"\n >\n {t('reservation:fieldCustomerClear')}\n </button>\n </div>\n ) : (\n <input\n aria-label={t('reservation:fieldCustomerSearch')}\n className={styles.searchInput}\n onChange={handleSearchChange}\n onFocus={handleFocus}\n placeholder={t('reservation:fieldCustomerSearch')}\n type=\"text\"\n value={search}\n />\n )}\n\n {isOpen && !selectedCustomer && (\n <div className={styles.dropdown}>\n {loading && results.length === 0 ? (\n <div className={styles.noResults}>...</div>\n ) : results.length === 0 && search ? (\n <div className={styles.noResults}>{t('reservation:fieldCustomerNoResults')}</div>\n ) : (\n results.map((customer) => (\n <div\n aria-selected={false}\n className={styles.option}\n key={customer.id}\n onClick={() => handleSelect(customer)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleSelect(customer)\n }\n }}\n role=\"option\"\n tabIndex={0}\n >\n <span className={styles.optionName}>{getDisplayName(customer)}</span>\n <span className={styles.optionMeta}>\n {[customer.phone, customer.email].filter(Boolean).join(' · ')}\n </span>\n </div>\n ))\n )}\n <button\n className={styles.createButton}\n onClick={handleCreate}\n type=\"button\"\n >\n + {t('reservation:fieldCustomerCreateNew')}\n </button>\n </div>\n )}\n\n <DocumentDrawer onSave={handleDrawerSave} />\n </div>\n )\n}\n"],"names":["FieldLabel","useConfig","useDocumentDrawer","useField","useTranslation","React","useCallback","useEffect","useRef","useState","styles","getDisplayName","customer","name","fullName","firstName","lastName","filter","Boolean","join","email","CustomerField","field","path","pathProp","fieldPath","config","t","_t","setValue","value","slugs","admin","custom","reservationSlugs","customersCollection","customers","search","setSearch","results","setResults","selectedCustomer","setSelectedCustomer","isOpen","setIsOpen","loading","setLoading","wrapperRef","debounceRef","DocumentDrawer","openDrawer","collectionSlug","id","fetchCustomer","res","fetch","ok","doc","json","undefined","phone","doSearch","query","params","URLSearchParams","limit","toString","data","docs","handleSearchChange","e","val","target","current","clearTimeout","setTimeout","handleClickOutside","contains","document","addEventListener","removeEventListener","handleSelect","handleClear","handleFocus","length","handleCreate","handleDrawerSave","div","className","wrapper","ref","label","required","selected","selectedInfo","span","selectedName","selectedMeta","button","clearButton","onClick","type","input","aria-label","searchInput","onChange","onFocus","placeholder","dropdown","noResults","map","aria-selected","option","onKeyDown","key","preventDefault","role","tabIndex","optionName","optionMeta","createButton","onSave"],"mappings":"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"}
@@ -8,6 +8,12 @@ export const DashboardWidgetServer = async (props)=>{
8
8
  if (!slugs) {
9
9
  return null;
10
10
  }
11
+ // Read status machine from config — never hardcode status values
12
+ const statusMachine = payload.config.admin?.custom?.reservationStatusMachine;
13
+ const blockingStatuses = statusMachine?.blockingStatuses ?? [];
14
+ const terminalStatuses = statusMachine?.terminalStatuses ?? [];
15
+ const blockingSet = new Set(blockingStatuses);
16
+ const terminalSet = new Set(terminalStatuses);
11
17
  const now = new Date();
12
18
  const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
13
19
  const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
@@ -23,10 +29,14 @@ export const DashboardWidgetServer = async (props)=>{
23
29
  }
24
30
  });
25
31
  const total = todayReservations.length;
26
- const upcoming = todayReservations.filter((r)=>r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' && new Date(r.startTime) > now).length;
27
- const completed = todayReservations.filter((r)=>r.status === 'completed').length;
28
- const cancelled = todayReservations.filter((r)=>r.status === 'cancelled').length;
29
- const nextAppointment = todayReservations.find((r)=>r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' && new Date(r.startTime) > now);
32
+ // Active = reservations in blockingStatuses (they hold a slot, past or future)
33
+ const active = todayReservations.filter((r)=>blockingSet.has(r.status)).length;
34
+ // Upcoming = active (blocking) reservations that haven't started yet
35
+ const upcoming = todayReservations.filter((r)=>blockingSet.has(r.status) && new Date(r.startTime) > now).length;
36
+ // Terminal = reservations in terminalStatuses (completed, cancelled, no-show, etc.)
37
+ const terminal = todayReservations.filter((r)=>terminalSet.has(r.status)).length;
38
+ // Next appointment = the earliest upcoming blocking reservation
39
+ const nextAppointment = todayReservations.find((r)=>blockingSet.has(r.status) && new Date(r.startTime) > now);
30
40
  return /*#__PURE__*/ _jsxs("div", {
31
41
  className: styles.wrapper,
32
42
  children: [
@@ -55,11 +65,11 @@ export const DashboardWidgetServer = async (props)=>{
55
65
  children: [
56
66
  /*#__PURE__*/ _jsx("span", {
57
67
  className: styles.statValue,
58
- children: upcoming
68
+ children: active
59
69
  }),
60
70
  /*#__PURE__*/ _jsx("span", {
61
71
  className: styles.statLabel,
62
- children: t('reservation:dashboardUpcoming')
72
+ children: t('reservation:dashboardActive')
63
73
  })
64
74
  ]
65
75
  }),
@@ -68,11 +78,11 @@ export const DashboardWidgetServer = async (props)=>{
68
78
  children: [
69
79
  /*#__PURE__*/ _jsx("span", {
70
80
  className: styles.statValue,
71
- children: completed
81
+ children: upcoming
72
82
  }),
73
83
  /*#__PURE__*/ _jsx("span", {
74
84
  className: styles.statLabel,
75
- children: t('reservation:dashboardCompleted')
85
+ children: t('reservation:dashboardUpcoming')
76
86
  })
77
87
  ]
78
88
  }),
@@ -81,11 +91,11 @@ export const DashboardWidgetServer = async (props)=>{
81
91
  children: [
82
92
  /*#__PURE__*/ _jsx("span", {
83
93
  className: styles.statValue,
84
- children: cancelled
94
+ children: terminal
85
95
  }),
86
96
  /*#__PURE__*/ _jsx("span", {
87
97
  className: styles.statLabel,
88
- children: t('reservation:dashboardCancelled')
98
+ children: t('reservation:dashboardTerminal')
89
99
  })
90
100
  ]
91
101
  })
@@ -100,7 +110,7 @@ export const DashboardWidgetServer = async (props)=>{
100
110
  /*#__PURE__*/ _jsxs("p", {
101
111
  children: [
102
112
  t('reservation:dashboardTime'),
103
- " ",
113
+ ' ',
104
114
  new Date(nextAppointment.startTime).toLocaleTimeString([], {
105
115
  hour: '2-digit',
106
116
  minute: '2-digit'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/DashboardWidget/DashboardWidgetServer.tsx"],"sourcesContent":["import type { WidgetServerProps } from 'payload'\n\nimport type { PluginT } from '../../translations/index.js'\n\nimport styles from './DashboardWidget.module.css'\n\nexport const DashboardWidgetServer = async (props: WidgetServerProps) => {\n const { req } = props\n const { i18n, payload } = req\n const t = i18n.t as PluginT\n\n const slugs = payload.config.admin?.custom?.reservationSlugs\n if (!slugs) {\n return null\n }\n\n const now = new Date()\n const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)\n\n const { docs: todayReservations } = await payload.find({\n collection: slugs.reservations,\n limit: 100,\n sort: 'startTime',\n where: {\n startTime: {\n greater_than_equal: startOfDay.toISOString(),\n less_than: endOfDay.toISOString(),\n },\n },\n })\n\n const total = todayReservations.length\n const upcoming = todayReservations.filter(\n (r: Record<string, unknown>) =>\n r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' &&\n new Date(r.startTime as string) > now,\n ).length\n const completed = todayReservations.filter(\n (r: Record<string, unknown>) => r.status === 'completed',\n ).length\n const cancelled = todayReservations.filter(\n (r: Record<string, unknown>) => r.status === 'cancelled',\n ).length\n\n const nextAppointment = todayReservations.find(\n (r: Record<string, unknown>) =>\n r.status !== 'completed' && r.status !== 'cancelled' && r.status !== 'no-show' &&\n new Date(r.startTime as string) > now,\n )\n\n return (\n <div className={styles.wrapper}>\n <h3 className={styles.title}>{t('reservation:dashboardTitle')}</h3>\n <div className={styles.statsGrid}>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{total}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardTotal')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{upcoming}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardUpcoming')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{completed}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardCompleted')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{cancelled}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardCancelled')}</span>\n </div>\n </div>\n {nextAppointment ? (\n <div className={styles.nextAppointment}>\n <strong>{t('reservation:dashboardNextAppointment')}</strong>\n <p>\n {t('reservation:dashboardTime')} {new Date(nextAppointment.startTime as string).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n </p>\n <p>{t('reservation:dashboardStatus')} {nextAppointment.status as string}</p>\n </div>\n ) : (\n <p className={styles.noData}>{t('reservation:dashboardNoUpcoming')}</p>\n )}\n </div>\n )\n}\n"],"names":["styles","DashboardWidgetServer","props","req","i18n","payload","t","slugs","config","admin","custom","reservationSlugs","now","Date","startOfDay","getFullYear","getMonth","getDate","endOfDay","docs","todayReservations","find","collection","reservations","limit","sort","where","startTime","greater_than_equal","toISOString","less_than","total","length","upcoming","filter","r","status","completed","cancelled","nextAppointment","div","className","wrapper","h3","title","statsGrid","statCard","span","statValue","statLabel","strong","p","toLocaleTimeString","hour","minute","noData"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/DashboardWidget/DashboardWidgetServer.tsx"],"sourcesContent":["import type { WidgetServerProps } from 'payload'\n\nimport type { PluginT } from '../../translations/index.js'\nimport type { StatusMachineConfig } from '../../types.js'\n\nimport styles from './DashboardWidget.module.css'\n\nexport const DashboardWidgetServer = async (props: WidgetServerProps) => {\n const { req } = props\n const { i18n, payload } = req\n const t = i18n.t as PluginT\n\n const slugs = payload.config.admin?.custom?.reservationSlugs\n if (!slugs) {\n return null\n }\n\n // Read status machine from config — never hardcode status values\n const statusMachine: StatusMachineConfig | undefined =\n payload.config.admin?.custom?.reservationStatusMachine\n const blockingStatuses: string[] = statusMachine?.blockingStatuses ?? []\n const terminalStatuses: string[] = statusMachine?.terminalStatuses ?? []\n const blockingSet = new Set(blockingStatuses)\n const terminalSet = new Set(terminalStatuses)\n\n const now = new Date()\n const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)\n\n const { docs: todayReservations } = await payload.find({\n collection: slugs.reservations,\n limit: 100,\n sort: 'startTime',\n where: {\n startTime: {\n greater_than_equal: startOfDay.toISOString(),\n less_than: endOfDay.toISOString(),\n },\n },\n })\n\n const total = todayReservations.length\n\n // Active = reservations in blockingStatuses (they hold a slot, past or future)\n const active = todayReservations.filter((r: Record<string, unknown>) =>\n blockingSet.has(r.status as string),\n ).length\n\n // Upcoming = active (blocking) reservations that haven't started yet\n const upcoming = todayReservations.filter(\n (r: Record<string, unknown>) =>\n blockingSet.has(r.status as string) && new Date(r.startTime as string) > now,\n ).length\n\n // Terminal = reservations in terminalStatuses (completed, cancelled, no-show, etc.)\n const terminal = todayReservations.filter((r: Record<string, unknown>) =>\n terminalSet.has(r.status as string),\n ).length\n\n // Next appointment = the earliest upcoming blocking reservation\n const nextAppointment = todayReservations.find(\n (r: Record<string, unknown>) =>\n blockingSet.has(r.status as string) && new Date(r.startTime as string) > now,\n )\n\n return (\n <div className={styles.wrapper}>\n <h3 className={styles.title}>{t('reservation:dashboardTitle')}</h3>\n <div className={styles.statsGrid}>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{total}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardTotal')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{active}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardActive')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{upcoming}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardUpcoming')}</span>\n </div>\n <div className={styles.statCard}>\n <span className={styles.statValue}>{terminal}</span>\n <span className={styles.statLabel}>{t('reservation:dashboardTerminal')}</span>\n </div>\n </div>\n {nextAppointment ? (\n <div className={styles.nextAppointment}>\n <strong>{t('reservation:dashboardNextAppointment')}</strong>\n <p>\n {t('reservation:dashboardTime')}{' '}\n {new Date(nextAppointment.startTime as string).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })}\n </p>\n <p>\n {t('reservation:dashboardStatus')} {nextAppointment.status as string}\n </p>\n </div>\n ) : (\n <p className={styles.noData}>{t('reservation:dashboardNoUpcoming')}</p>\n )}\n </div>\n )\n}\n"],"names":["styles","DashboardWidgetServer","props","req","i18n","payload","t","slugs","config","admin","custom","reservationSlugs","statusMachine","reservationStatusMachine","blockingStatuses","terminalStatuses","blockingSet","Set","terminalSet","now","Date","startOfDay","getFullYear","getMonth","getDate","endOfDay","docs","todayReservations","find","collection","reservations","limit","sort","where","startTime","greater_than_equal","toISOString","less_than","total","length","active","filter","r","has","status","upcoming","terminal","nextAppointment","div","className","wrapper","h3","title","statsGrid","statCard","span","statValue","statLabel","strong","p","toLocaleTimeString","hour","minute","noData"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { ReservationPluginConfig, ResolvedReservationPluginConfig } from './types.js';
2
2
  export declare const DEFAULT_SLUGS: {
3
+ readonly customers: "customers";
3
4
  readonly media: "media";
4
5
  readonly reservations: "reservations";
5
6
  readonly resources: "resources";
@@ -9,6 +10,4 @@ export declare const DEFAULT_SLUGS: {
9
10
  export declare const DEFAULT_ADMIN_GROUP = "Reservations";
10
11
  export declare const DEFAULT_BUFFER_TIME = 0;
11
12
  export declare const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24;
12
- export declare const DEFAULT_USER_COLLECTION = "users";
13
- export declare const DEFAULT_CUSTOMER_ROLE: false | string;
14
13
  export declare function resolveConfig(pluginOptions: ReservationPluginConfig): ResolvedReservationPluginConfig;
package/dist/defaults.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { DEFAULT_STATUS_MACHINE } from './types.js';
1
2
  export const DEFAULT_SLUGS = {
3
+ customers: 'customers',
2
4
  media: 'media',
3
5
  reservations: 'reservations',
4
6
  resources: 'resources',
@@ -8,25 +10,41 @@ export const DEFAULT_SLUGS = {
8
10
  export const DEFAULT_ADMIN_GROUP = 'Reservations';
9
11
  export const DEFAULT_BUFFER_TIME = 0;
10
12
  export const DEFAULT_CANCELLATION_NOTICE_PERIOD = 24;
11
- export const DEFAULT_USER_COLLECTION = 'users';
12
- export const DEFAULT_CUSTOMER_ROLE = false;
13
13
  export function resolveConfig(pluginOptions) {
14
+ const userStatusMachine = pluginOptions.statusMachine;
15
+ const rom = pluginOptions.resourceOwnerMode;
14
16
  return {
15
17
  access: pluginOptions.access ?? {},
16
18
  adminGroup: pluginOptions.adminGroup ?? DEFAULT_ADMIN_GROUP,
17
19
  cancellationNoticePeriod: pluginOptions.cancellationNoticePeriod ?? DEFAULT_CANCELLATION_NOTICE_PERIOD,
18
- customerRole: pluginOptions.customerRole ?? DEFAULT_CUSTOMER_ROLE,
19
20
  defaultBufferTime: pluginOptions.defaultBufferTime ?? DEFAULT_BUFFER_TIME,
20
21
  disabled: pluginOptions.disabled ?? false,
22
+ extraReservationFields: pluginOptions.extraReservationFields ?? [],
23
+ hooks: pluginOptions.hooks ?? {},
21
24
  localized: false,
25
+ resourceOwnerMode: rom ? {
26
+ adminRoles: rom.adminRoles ?? [],
27
+ ownedServices: rom.ownedServices ?? false,
28
+ ownerField: rom.ownerField ?? 'owner'
29
+ } : undefined,
22
30
  slugs: {
31
+ customers: pluginOptions.slugs?.customers ?? DEFAULT_SLUGS.customers,
23
32
  media: pluginOptions.slugs?.media ?? DEFAULT_SLUGS.media,
24
33
  reservations: pluginOptions.slugs?.reservations ?? DEFAULT_SLUGS.reservations,
25
34
  resources: pluginOptions.slugs?.resources ?? DEFAULT_SLUGS.resources,
26
35
  schedules: pluginOptions.slugs?.schedules ?? DEFAULT_SLUGS.schedules,
27
36
  services: pluginOptions.slugs?.services ?? DEFAULT_SLUGS.services
28
37
  },
29
- userCollection: pluginOptions.userCollection ?? DEFAULT_USER_COLLECTION
38
+ statusMachine: userStatusMachine ? {
39
+ blockingStatuses: userStatusMachine.blockingStatuses ?? DEFAULT_STATUS_MACHINE.blockingStatuses,
40
+ defaultStatus: userStatusMachine.defaultStatus ?? DEFAULT_STATUS_MACHINE.defaultStatus,
41
+ statuses: userStatusMachine.statuses ?? DEFAULT_STATUS_MACHINE.statuses,
42
+ terminalStatuses: userStatusMachine.terminalStatuses ?? DEFAULT_STATUS_MACHINE.terminalStatuses,
43
+ transitions: userStatusMachine.transitions ?? DEFAULT_STATUS_MACHINE.transitions
44
+ } : {
45
+ ...DEFAULT_STATUS_MACHINE
46
+ },
47
+ userCollection: pluginOptions.userCollection ?? undefined
30
48
  };
31
49
  }
32
50