@aglyn/plugins-commerce 1.0.0-beta.278 → 1.0.0-beta.282

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 (212) hide show
  1. package/package.json +17 -14
  2. package/src/lib/components/commerce-console-page.js +33 -1
  3. package/src/lib/components/commerce-console-page.js.map +1 -1
  4. package/src/lib/components/console/payments-settings-card.component.js +1 -1
  5. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  6. package/src/lib/components/console/pos/pos-hardware-api.d.ts +122 -0
  7. package/src/lib/components/console/pos/pos-hardware-api.js +90 -0
  8. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -0
  9. package/src/lib/components/console/pos/pos-reader-splash-api.d.ts +58 -0
  10. package/src/lib/components/console/pos/pos-reader-splash-api.js +101 -0
  11. package/src/lib/components/console/pos/pos-reader-splash-api.js.map +1 -0
  12. package/src/lib/components/console/pos-devices-card.component.js +5 -0
  13. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  14. package/src/lib/components/console/pos-hardware-card.component.d.ts +26 -0
  15. package/src/lib/components/console/pos-hardware-card.component.js +456 -0
  16. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -0
  17. package/src/lib/components/console/pos-hardware-orders-staff-page.component.d.ts +14 -0
  18. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +519 -0
  19. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -0
  20. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.d.ts +27 -0
  21. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +444 -0
  22. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -0
  23. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.d.ts +9 -0
  24. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js +37 -0
  25. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js.map +1 -0
  26. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +24 -0
  27. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +346 -0
  28. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -0
  29. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +5 -1
  30. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +83 -4
  31. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -1
  32. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.d.ts +35 -0
  33. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js +546 -0
  34. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js.map +1 -0
  35. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +2 -1
  36. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +20 -1
  37. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  38. package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +1 -1
  39. package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -1
  40. package/src/lib/components/console/pos-page.component.js +6 -3
  41. package/src/lib/components/console/pos-page.component.js.map +1 -1
  42. package/src/lib/components/console/pos-reader-splash-setting.component.d.ts +22 -0
  43. package/src/lib/components/console/pos-reader-splash-setting.component.js +275 -0
  44. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -0
  45. package/src/lib/components/console/pos-reader-splash-staff-page.component.d.ts +14 -0
  46. package/src/lib/components/console/pos-reader-splash-staff-page.component.js +301 -0
  47. package/src/lib/components/console/pos-reader-splash-staff-page.component.js.map +1 -0
  48. package/src/lib/components/console/pos-zones.d.ts +37 -3
  49. package/src/lib/components/console/pos-zones.js +11 -1
  50. package/src/lib/components/console/pos-zones.js.map +1 -1
  51. package/src/lib/components/console/product-check-in-fields.component.d.ts +20 -0
  52. package/src/lib/components/console/product-check-in-fields.component.js +172 -0
  53. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -0
  54. package/src/lib/components/console/product-editor-dialog.component.js +48 -9
  55. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  56. package/src/lib/components/console/product-register-fields.component.d.ts +12 -0
  57. package/src/lib/components/console/product-register-fields.component.js +39 -1
  58. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  59. package/src/lib/components/console/products-hub-card.component.js +21 -6
  60. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  61. package/src/lib/components/console/reservations-card.component.js +239 -70
  62. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  63. package/src/lib/components/reservation-widget.js +16 -0
  64. package/src/lib/components/reservation-widget.js.map +1 -1
  65. package/src/lib/constants/operator-alerts.d.ts +6 -0
  66. package/src/lib/constants/operator-alerts.js +20 -1
  67. package/src/lib/constants/operator-alerts.js.map +1 -1
  68. package/src/lib/declarations.console-server.js +7 -0
  69. package/src/lib/declarations.console-server.js.map +1 -1
  70. package/src/lib/declarations.d.ts +26 -0
  71. package/src/lib/declarations.js +34 -0
  72. package/src/lib/declarations.js.map +1 -0
  73. package/src/lib/model/commerce-check-in-product.d.ts +37 -0
  74. package/src/lib/model/commerce-check-in-product.js +49 -0
  75. package/src/lib/model/commerce-check-in-product.js.map +1 -0
  76. package/src/lib/model/commerce-check-in.d.ts +187 -0
  77. package/src/lib/model/commerce-check-in.js +468 -0
  78. package/src/lib/model/commerce-check-in.js.map +1 -0
  79. package/src/lib/model/commerce-pos-day-report.d.ts +209 -0
  80. package/src/lib/model/commerce-pos-day-report.js +572 -0
  81. package/src/lib/model/commerce-pos-day-report.js.map +1 -0
  82. package/src/lib/model/commerce-pos-offline.d.ts +65 -3
  83. package/src/lib/model/commerce-pos-offline.js +104 -7
  84. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  85. package/src/lib/model/commerce-pos-ops.d.ts +3 -0
  86. package/src/lib/model/commerce-pos-ops.js +1 -0
  87. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  88. package/src/lib/model/commerce-pos.d.ts +43 -3
  89. package/src/lib/model/commerce-pos.js +73 -5
  90. package/src/lib/model/commerce-pos.js.map +1 -1
  91. package/src/lib/model/commerce-reservations.d.ts +73 -7
  92. package/src/lib/model/commerce-reservations.js +90 -10
  93. package/src/lib/model/commerce-reservations.js.map +1 -1
  94. package/src/lib/model/commerce.d.ts +40 -0
  95. package/src/lib/model/commerce.js +22 -4
  96. package/src/lib/model/commerce.js.map +1 -1
  97. package/src/lib/model/index.d.ts +1 -0
  98. package/src/lib/model/index.js +1 -0
  99. package/src/lib/model/index.js.map +1 -1
  100. package/src/lib/model/plugin-permissions.d.ts +1 -1
  101. package/src/lib/model/plugin-permissions.js +2 -1
  102. package/src/lib/model/plugin-permissions.js.map +1 -1
  103. package/src/lib/model/pos-hardware.d.ts +288 -0
  104. package/src/lib/model/pos-hardware.js +499 -0
  105. package/src/lib/model/pos-hardware.js.map +1 -0
  106. package/src/lib/model/pos-offline-card-limits.d.ts +20 -0
  107. package/src/lib/model/pos-offline-card-limits.js +28 -0
  108. package/src/lib/model/pos-offline-card-limits.js.map +1 -0
  109. package/src/lib/model/pos-reader-splash.d.ts +114 -0
  110. package/src/lib/model/pos-reader-splash.js +141 -0
  111. package/src/lib/model/pos-reader-splash.js.map +1 -0
  112. package/src/lib/model/product-write.d.ts +6 -0
  113. package/src/lib/plugin-config.d.ts +10 -0
  114. package/src/lib/plugin-config.js +40 -0
  115. package/src/lib/plugin-config.js.map +1 -1
  116. package/src/lib/plugin.js +28 -0
  117. package/src/lib/plugin.js.map +1 -1
  118. package/src/lib/server/cart-checkout.js +3 -1
  119. package/src/lib/server/cart-checkout.js.map +1 -1
  120. package/src/lib/server/cart.js +12 -1
  121. package/src/lib/server/cart.js.map +1 -1
  122. package/src/lib/server/catalog.js +49 -12
  123. package/src/lib/server/catalog.js.map +1 -1
  124. package/src/lib/server/checkout-extras.js +1 -1
  125. package/src/lib/server/checkout-extras.js.map +1 -1
  126. package/src/lib/server/checkout.js +7 -0
  127. package/src/lib/server/checkout.js.map +1 -1
  128. package/src/lib/server/person-eraser.d.ts +8 -1
  129. package/src/lib/server/person-eraser.js +23 -1
  130. package/src/lib/server/person-eraser.js.map +1 -1
  131. package/src/lib/server/pos-check-in.d.ts +49 -0
  132. package/src/lib/server/pos-check-in.js +652 -0
  133. package/src/lib/server/pos-check-in.js.map +1 -0
  134. package/src/lib/server/pos-day-report.d.ts +46 -0
  135. package/src/lib/server/pos-day-report.js +299 -0
  136. package/src/lib/server/pos-day-report.js.map +1 -0
  137. package/src/lib/server/pos-hardware-admin.d.ts +50 -0
  138. package/src/lib/server/pos-hardware-admin.js +366 -0
  139. package/src/lib/server/pos-hardware-admin.js.map +1 -0
  140. package/src/lib/server/pos-hardware-notify.d.ts +27 -0
  141. package/src/lib/server/pos-hardware-notify.js +142 -0
  142. package/src/lib/server/pos-hardware-notify.js.map +1 -0
  143. package/src/lib/server/pos-hardware-user-eraser.d.ts +26 -0
  144. package/src/lib/server/pos-hardware-user-eraser.js +67 -0
  145. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -0
  146. package/src/lib/server/pos-hardware-webhook.d.ts +31 -0
  147. package/src/lib/server/pos-hardware-webhook.js +176 -0
  148. package/src/lib/server/pos-hardware-webhook.js.map +1 -0
  149. package/src/lib/server/pos-hardware.d.ts +58 -0
  150. package/src/lib/server/pos-hardware.js +234 -0
  151. package/src/lib/server/pos-hardware.js.map +1 -0
  152. package/src/lib/server/pos-offline-card-reconcile.d.ts +38 -0
  153. package/src/lib/server/pos-offline-card-reconcile.js +58 -0
  154. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -0
  155. package/src/lib/server/pos-offline-sync.d.ts +32 -0
  156. package/src/lib/server/pos-offline-sync.js +191 -15
  157. package/src/lib/server/pos-offline-sync.js.map +1 -1
  158. package/src/lib/server/pos-ops-routes.d.ts +4 -2
  159. package/src/lib/server/pos-ops-routes.js +14 -2
  160. package/src/lib/server/pos-ops-routes.js.map +1 -1
  161. package/src/lib/server/pos-order.js +39 -51
  162. package/src/lib/server/pos-order.js.map +1 -1
  163. package/src/lib/server/pos-payment.js +8 -4
  164. package/src/lib/server/pos-payment.js.map +1 -1
  165. package/src/lib/server/pos-reader-splash-admin.d.ts +36 -0
  166. package/src/lib/server/pos-reader-splash-admin.js +295 -0
  167. package/src/lib/server/pos-reader-splash-admin.js.map +1 -0
  168. package/src/lib/server/pos-reader-splash-render.d.ts +40 -0
  169. package/src/lib/server/pos-reader-splash-render.js +138 -0
  170. package/src/lib/server/pos-reader-splash-render.js.map +1 -0
  171. package/src/lib/server/pos-reader-splash-server.d.ts +32 -0
  172. package/src/lib/server/pos-reader-splash-server.js +32 -0
  173. package/src/lib/server/pos-reader-splash-server.js.map +1 -0
  174. package/src/lib/server/pos-reader-splash.d.ts +105 -0
  175. package/src/lib/server/pos-reader-splash.js +548 -0
  176. package/src/lib/server/pos-reader-splash.js.map +1 -0
  177. package/src/lib/server/pos-sale.d.ts +2 -1
  178. package/src/lib/server/pos-sale.js +3 -1
  179. package/src/lib/server/pos-sale.js.map +1 -1
  180. package/src/lib/server/pos-terminal.d.ts +67 -4
  181. package/src/lib/server/pos-terminal.js +239 -32
  182. package/src/lib/server/pos-terminal.js.map +1 -1
  183. package/src/lib/server/process-restock.js +4 -0
  184. package/src/lib/server/process-restock.js.map +1 -1
  185. package/src/lib/server/product-card.js +2 -2
  186. package/src/lib/server/product-card.js.map +1 -1
  187. package/src/lib/server/product-catalog.d.ts +2 -0
  188. package/src/lib/server/product-catalog.js +11 -4
  189. package/src/lib/server/product-catalog.js.map +1 -1
  190. package/src/lib/server/product.js +3 -1
  191. package/src/lib/server/product.js.map +1 -1
  192. package/src/lib/server/related.js +8 -1
  193. package/src/lib/server/related.js.map +1 -1
  194. package/src/lib/server/reservation-availability-query.d.ts +35 -0
  195. package/src/lib/server/reservation-availability-query.js +34 -0
  196. package/src/lib/server/reservation-availability-query.js.map +1 -0
  197. package/src/lib/server/reservation-availability.d.ts +21 -0
  198. package/src/lib/server/reservation-availability.js +64 -10
  199. package/src/lib/server/reservation-availability.js.map +1 -1
  200. package/src/lib/server/reservation-walk-in.d.ts +28 -0
  201. package/src/lib/server/reservation-walk-in.js +166 -0
  202. package/src/lib/server/reservation-walk-in.js.map +1 -0
  203. package/src/lib/server/reserve.js +13 -10
  204. package/src/lib/server/reserve.js.map +1 -1
  205. package/src/lib/server/reviews.js +3 -1
  206. package/src/lib/server/reviews.js.map +1 -1
  207. package/src/lib/server/site-page-resolver.js +5 -2
  208. package/src/lib/server/site-page-resolver.js.map +1 -1
  209. package/src/lib/server.js +26 -0
  210. package/src/lib/server.js.map +1 -1
  211. package/src/lib/testing/fake-firestore.js +72 -0
  212. package/src/lib/testing/fake-firestore.js.map +1 -1
@@ -0,0 +1,346 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
+ import { pluginDocsHelp } from "@aglyn/aglyn";
20
+ import { CardDisplay } from "@aglyn/shared-ui-jsx";
21
+ import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component";
22
+ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
23
+ import { useFirestore, useFirestoreCollection, useUser } from "@aglyn/tenant-feature-instance";
24
+ import { Alert, Button, Chip, Stack, Typography } from "@mui/material";
25
+ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
26
+ import { DatePicker } from "@mui/x-date-pickers/DatePicker";
27
+ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
28
+ import { collection, documentId, limit, orderBy, query } from "firebase/firestore";
29
+ import { useCallback, useEffect, useMemo, useState } from "react";
30
+ import { checkInItemLabel, checkInTimeLabel, describeWristband, POS_CHECK_IN_ROUTE } from "../../../model/commerce-check-in.js";
31
+ import { escapeHtml } from "../../../utils/escape-html.js";
32
+ import CommerceStatTile from "../commerce-stat-tile.component.js";
33
+ import { EntitlementGatedCard } from "../entitlement-gate.component.js";
34
+ import { POS_RECEIPT_STYLES } from "./pos-receipt.js";
35
+ import { printPosDocument } from "./pos-shift-report.component.js";
36
+ const HEADER = "Today's check-ins";
37
+ const HELP = pluginDocsHelp('posOperations', {
38
+ anchor: '#check-in-at-the-window'
39
+ });
40
+ const DAY_FORMAT = 'YYYY-MM-DD';
41
+ /** The day's list as a printable 80 mm document, like the day totals. */ export function posCheckInsDocument(list) {
42
+ const rows = list.checkIns.map((row)=>`<div class="row"><span>${escapeHtml(`${row.customerName || row.customerEmail || 'Customer'} · ${checkInItemLabel(row)}`)}</span>` + `<span>${escapeHtml(row.status === 'checked_in' ? [
43
+ checkInTimeLabel(row.checkedInAtMs, list.timeZone),
44
+ describeWristband(row.wristband)
45
+ ].filter(Boolean).join(' ') : '—')}</span></div>`).join('');
46
+ return '<!doctype html><html><head><meta charset="utf-8">' + `<title>CHECK-INS</title><style>${POS_RECEIPT_STYLES}</style></head><body>` + `<div class="receipt"><div class="center"><div class="title">CHECK-INS</div>` + `<div class="muted">${escapeHtml(list.dayKey)} · ${list.totals.checkedIn} of ${list.totals.expected} checked in</div></div>` + `<hr class="rule">${rows}</div></body></html>`;
47
+ }
48
+ function PosCheckInsBody(props) {
49
+ const { hostId, canManagePos = false } = props;
50
+ const { data: user } = useUser();
51
+ const [day, setDay] = useState(null);
52
+ const [answer, setAnswer] = useState({
53
+ state: 'idle'
54
+ });
55
+ const [exporting, setExporting] = useState(false);
56
+ const url = useCallback((format)=>{
57
+ const params = new URLSearchParams({
58
+ hostId
59
+ });
60
+ if (day == null ? void 0 : day.isValid()) params.set('day', day.format(DAY_FORMAT));
61
+ if (format) params.set('format', format);
62
+ return `${POS_CHECK_IN_ROUTE}?${params.toString()}`;
63
+ }, [
64
+ hostId,
65
+ day
66
+ ]);
67
+ const load = useCallback(async ()=>{
68
+ if (!user || !canManagePos) return;
69
+ setAnswer({
70
+ state: 'loading'
71
+ });
72
+ try {
73
+ const response = await authorizedFetch(user, url(), {
74
+ method: 'GET'
75
+ });
76
+ const body = await response.json().catch(()=>({}));
77
+ setAnswer(response.ok && body.checkIns ? {
78
+ state: 'ready',
79
+ list: body
80
+ } : {
81
+ state: 'error',
82
+ error: body.error || 'The check-ins could not be read.'
83
+ });
84
+ } catch (unused) {
85
+ setAnswer({
86
+ state: 'error',
87
+ error: 'The check-ins could not be read. Check the connection.'
88
+ });
89
+ }
90
+ }, [
91
+ user,
92
+ canManagePos,
93
+ url
94
+ ]);
95
+ useEffect(()=>{
96
+ void load();
97
+ }, [
98
+ load
99
+ ]);
100
+ const list = answer.state === 'ready' ? answer.list : null;
101
+ const columns = useMemo(()=>[
102
+ {
103
+ field: 'customerName',
104
+ headerName: 'Name',
105
+ flex: 1,
106
+ minWidth: 150,
107
+ valueGetter: (_value, row)=>row.customerName || row.customerEmail || ''
108
+ },
109
+ {
110
+ field: 'itemName',
111
+ headerName: 'Item',
112
+ flex: 1,
113
+ minWidth: 160,
114
+ valueGetter: (_value, row)=>checkInItemLabel(row)
115
+ },
116
+ {
117
+ field: 'orderLabel',
118
+ headerName: 'Order',
119
+ minWidth: 100,
120
+ valueGetter: (_value, row)=>{
121
+ var _row_orderLabel;
122
+ return (_row_orderLabel = row.orderLabel) != null ? _row_orderLabel : row.source === 'reservation' ? 'Stay' : '';
123
+ }
124
+ },
125
+ {
126
+ field: 'status',
127
+ headerName: 'Status',
128
+ minWidth: 120,
129
+ renderCell: ({ row })=>row.status === 'checked_in' ? /*#__PURE__*/ _jsx(Chip, {
130
+ size: "small",
131
+ color: "success",
132
+ label: "Checked in"
133
+ }) : /*#__PURE__*/ _jsx(Chip, {
134
+ size: "small",
135
+ variant: "outlined",
136
+ label: "Expected"
137
+ })
138
+ },
139
+ {
140
+ field: 'checkedInAtMs',
141
+ headerName: 'Checked in at',
142
+ type: 'number',
143
+ minWidth: 120,
144
+ valueFormatter: (value)=>list ? checkInTimeLabel(value, list.timeZone) : ''
145
+ },
146
+ {
147
+ field: 'checkedInByName',
148
+ headerName: 'By',
149
+ minWidth: 120
150
+ },
151
+ {
152
+ field: 'wristband',
153
+ headerName: 'Wristband',
154
+ minWidth: 120,
155
+ valueGetter: (_value, row)=>describeWristband(row.wristband)
156
+ }
157
+ ], [
158
+ list
159
+ ]);
160
+ const rows = useMemo(()=>list ? list.checkIns.map((row)=>_extends({}, row, {
161
+ $id: row.id
162
+ })) : [], [
163
+ list
164
+ ]);
165
+ const exportCsv = async ()=>{
166
+ if (!user || !list) return;
167
+ setExporting(true);
168
+ try {
169
+ const response = await authorizedFetch(user, url('csv'), {
170
+ method: 'GET'
171
+ });
172
+ if (!response.ok) {
173
+ const body = await response.json().catch(()=>({}));
174
+ setAnswer({
175
+ state: 'error',
176
+ error: body.error || 'The spreadsheet could not be made.'
177
+ });
178
+ return;
179
+ }
180
+ const blob = new Blob([
181
+ await response.text()
182
+ ], {
183
+ type: 'text/csv;charset=utf-8'
184
+ });
185
+ const href = URL.createObjectURL(blob);
186
+ const link = document.createElement('a');
187
+ link.href = href;
188
+ link.download = `check-ins-${list.dayKey}.csv`;
189
+ link.click();
190
+ window.setTimeout(()=>URL.revokeObjectURL(href), 1000);
191
+ } finally{
192
+ setExporting(false);
193
+ }
194
+ };
195
+ return /*#__PURE__*/ _jsx(CardDisplay, {
196
+ header: HEADER,
197
+ help: HELP,
198
+ HeaderProps: {
199
+ action: /*#__PURE__*/ _jsxs(Stack, {
200
+ direction: "row",
201
+ spacing: 1,
202
+ children: [
203
+ /*#__PURE__*/ _jsx(Button, {
204
+ size: "small",
205
+ onClick: ()=>void load(),
206
+ disabled: !canManagePos || answer.state === 'loading',
207
+ children: 'Refresh'
208
+ }),
209
+ /*#__PURE__*/ _jsx(Button, {
210
+ size: "small",
211
+ onClick: ()=>void exportCsv(),
212
+ disabled: !list || exporting,
213
+ children: 'Export CSV'
214
+ }),
215
+ /*#__PURE__*/ _jsx(Button, {
216
+ size: "small",
217
+ onClick: ()=>list && printPosDocument(posCheckInsDocument(list)),
218
+ disabled: !list,
219
+ children: 'Print'
220
+ })
221
+ ]
222
+ })
223
+ },
224
+ contentGutterX: true,
225
+ contentGutterY: true,
226
+ children: /*#__PURE__*/ _jsxs(Stack, {
227
+ spacing: 2.5,
228
+ children: [
229
+ !canManagePos ? /*#__PURE__*/ _jsx(Alert, {
230
+ severity: "info",
231
+ children: 'Check-ins are for members who can use the register: a site admin or editor with the "Use the point of sale" permission.'
232
+ }) : null,
233
+ /*#__PURE__*/ _jsx(LocalizationProvider, {
234
+ dateAdapter: AdapterDayjs,
235
+ children: /*#__PURE__*/ _jsxs(Stack, {
236
+ direction: {
237
+ xs: 'column',
238
+ sm: 'row'
239
+ },
240
+ spacing: 1.5,
241
+ sx: {
242
+ alignItems: {
243
+ sm: 'center'
244
+ }
245
+ },
246
+ children: [
247
+ /*#__PURE__*/ _jsx(DatePicker, {
248
+ label: "Day",
249
+ value: day,
250
+ onChange: setDay,
251
+ disabled: !canManagePos,
252
+ slotProps: {
253
+ textField: {
254
+ size: 'small'
255
+ }
256
+ }
257
+ }),
258
+ /*#__PURE__*/ _jsx(Button, {
259
+ size: "small",
260
+ disabled: !canManagePos || !day,
261
+ onClick: ()=>setDay(null),
262
+ children: 'Today'
263
+ })
264
+ ]
265
+ })
266
+ }),
267
+ answer.state === 'loading' ? /*#__PURE__*/ _jsx(Typography, {
268
+ variant: "body2",
269
+ color: "text.secondary",
270
+ children: 'Reading who is due…'
271
+ }) : null,
272
+ answer.state === 'error' ? /*#__PURE__*/ _jsx(Alert, {
273
+ severity: "warning",
274
+ children: answer.error
275
+ }) : null,
276
+ list ? /*#__PURE__*/ _jsxs(_Fragment, {
277
+ children: [
278
+ /*#__PURE__*/ _jsx(Typography, {
279
+ variant: "body2",
280
+ color: "text.secondary",
281
+ children: `${list.dayKey} · ${list.timeZone} · passes and stays that need check-in`
282
+ }),
283
+ list.truncated ? /*#__PURE__*/ _jsx(Alert, {
284
+ severity: "warning",
285
+ children: 'This day has more check-ins than one list reads; some are missing here.'
286
+ }) : null,
287
+ /*#__PURE__*/ _jsxs(Stack, {
288
+ direction: "row",
289
+ spacing: 3,
290
+ sx: {
291
+ flexWrap: 'wrap',
292
+ rowGap: 1.5
293
+ },
294
+ children: [
295
+ /*#__PURE__*/ _jsx(CommerceStatTile, {
296
+ label: "Expected",
297
+ value: String(list.totals.expected)
298
+ }),
299
+ /*#__PURE__*/ _jsx(CommerceStatTile, {
300
+ label: "Checked in",
301
+ value: String(list.totals.checkedIn)
302
+ }),
303
+ /*#__PURE__*/ _jsx(CommerceStatTile, {
304
+ label: "Still to come",
305
+ value: String(list.totals.expected - list.totals.checkedIn)
306
+ })
307
+ ]
308
+ }),
309
+ /*#__PURE__*/ _jsx(ListTable, {
310
+ "aria-label": "Check-ins on this day",
311
+ rows: rows,
312
+ columns: columns,
313
+ noRowsLabel: "Nobody is due to check in on this day"
314
+ })
315
+ ]
316
+ }) : null
317
+ ]
318
+ })
319
+ });
320
+ }
321
+ /**
322
+ * Today's check-ins (AGL-4043): beside the day totals, for a store with a
323
+ * register, on a plan with the point of sale.
324
+ */ export function PosCheckInsCard(props) {
325
+ const firestore = useFirestore();
326
+ // Whether the store has a register at all, as the day totals ask it.
327
+ const { data: registers, status } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', props.hostId, 'registers'), orderBy(documentId()), limit(1)), [
328
+ firestore,
329
+ props.hostId
330
+ ], {
331
+ idField: '$id'
332
+ });
333
+ if (status === 'success' && !(registers == null ? void 0 : registers.length)) return null;
334
+ return /*#__PURE__*/ _jsx(EntitlementGatedCard, {
335
+ hostId: props.hostId,
336
+ feature: "pos",
337
+ header: HEADER,
338
+ help: HELP,
339
+ upsell: 'Checking in passes, camping and stays at the window, with wristbands, comes with the point of sale.',
340
+ children: /*#__PURE__*/ _jsx(PosCheckInsBody, _extends({}, props))
341
+ });
342
+ }
343
+ PosCheckInsCard.displayName = 'PosCheckInsCard';
344
+ export default PosCheckInsCard;
345
+
346
+ //# sourceMappingURL=pos-check-ins-card.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-ops/pos-check-ins-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useFirestore, useFirestoreCollection, useUser } from '@aglyn/tenant-feature-instance'\nimport { Alert, Button, Chip, Stack, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'\nimport { DatePicker } from '@mui/x-date-pickers/DatePicker'\nimport { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'\nimport type { Dayjs } from 'dayjs'\nimport { collection, documentId, limit, orderBy, query } from 'firebase/firestore'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n checkInItemLabel,\n checkInTimeLabel,\n describeWristband,\n POS_CHECK_IN_ROUTE,\n type CheckInDayTotals,\n type CheckInRow,\n} from '../../../model/commerce-check-in'\nimport { escapeHtml } from '../../../utils/escape-html'\nimport CommerceStatTile from '../commerce-stat-tile.component'\nimport { EntitlementGatedCard } from '../entitlement-gate.component'\nimport { POS_RECEIPT_STYLES } from './pos-receipt'\nimport { printPosDocument } from './pos-shift-report.component'\n\n/*==========================================\n * TODAY'S CHECK-INS (AGL-4043): who is due at the window on a day, who is\n * checked in, by whom and with which wristband, from\n * `GET /api/commerce/pos-check-in`. The day is a whole, bounded list, so\n * the grid sorts it by any column itself.\n *=========================================*/\n\nexport interface PosCheckInsCardProps {\n hostId: string\n /** The viewer may work the register: what the route's gate asks. */\n canManagePos?: boolean\n}\n\nconst HEADER = \"Today's check-ins\"\nconst HELP = pluginDocsHelp('posOperations', { anchor: '#check-in-at-the-window' })\nconst DAY_FORMAT = 'YYYY-MM-DD'\n\ninterface DayList {\n dayKey: string\n timeZone: string\n totals: CheckInDayTotals\n checkIns: CheckInRow[]\n truncated?: boolean\n}\n\ntype Answer =\n | { state: 'idle' | 'loading' }\n | { state: 'ready'; list: DayList }\n | { state: 'error'; error: string }\n\n/** The day's list as a printable 80 mm document, like the day totals. */\nexport function posCheckInsDocument(list: DayList): string {\n const rows = list.checkIns\n .map(\n (row) =>\n `<div class=\"row\"><span>${escapeHtml(`${row.customerName || row.customerEmail || 'Customer'} · ${checkInItemLabel(row)}`)}</span>` +\n `<span>${escapeHtml(\n row.status === 'checked_in'\n ? [checkInTimeLabel(row.checkedInAtMs, list.timeZone), describeWristband(row.wristband)].filter(Boolean).join(' ')\n : '—',\n )}</span></div>`,\n )\n .join('')\n return (\n '<!doctype html><html><head><meta charset=\"utf-8\">' +\n `<title>CHECK-INS</title><style>${POS_RECEIPT_STYLES}</style></head><body>` +\n `<div class=\"receipt\"><div class=\"center\"><div class=\"title\">CHECK-INS</div>` +\n `<div class=\"muted\">${escapeHtml(list.dayKey)} · ${list.totals.checkedIn} of ${list.totals.expected} checked in</div></div>` +\n `<hr class=\"rule\">${rows}</div></body></html>`\n )\n}\n\nfunction PosCheckInsBody(props: PosCheckInsCardProps) {\n const { hostId, canManagePos = false } = props\n const { data: user } = useUser()\n const [day, setDay] = useState<Dayjs | null>(null)\n const [answer, setAnswer] = useState<Answer>({ state: 'idle' })\n const [exporting, setExporting] = useState(false)\n\n const url = useCallback(\n (format?: 'csv') => {\n const params = new URLSearchParams({ hostId })\n if (day?.isValid()) params.set('day', day.format(DAY_FORMAT))\n if (format) params.set('format', format)\n return `${POS_CHECK_IN_ROUTE}?${params.toString()}`\n },\n [hostId, day],\n )\n\n const load = useCallback(async () => {\n if (!user || !canManagePos) return\n setAnswer({ state: 'loading' })\n try {\n const response = await authorizedFetch(user as never, url(), { method: 'GET' })\n const body = (await response.json().catch(() => ({}))) as Partial<DayList> & { error?: string }\n setAnswer(\n response.ok && body.checkIns\n ? { state: 'ready', list: body as DayList }\n : { state: 'error', error: body.error || 'The check-ins could not be read.' },\n )\n } catch {\n setAnswer({ state: 'error', error: 'The check-ins could not be read. Check the connection.' })\n }\n }, [user, canManagePos, url])\n\n useEffect(() => {\n void load()\n }, [load])\n\n const list = answer.state === 'ready' ? answer.list : null\n const columns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'customerName',\n headerName: 'Name',\n flex: 1,\n minWidth: 150,\n valueGetter: (_value: unknown, row: CheckInRow) => row.customerName || row.customerEmail || '',\n },\n {\n field: 'itemName',\n headerName: 'Item',\n flex: 1,\n minWidth: 160,\n valueGetter: (_value: unknown, row: CheckInRow) => checkInItemLabel(row),\n },\n {\n field: 'orderLabel',\n headerName: 'Order',\n minWidth: 100,\n valueGetter: (_value: unknown, row: CheckInRow) => row.orderLabel ?? (row.source === 'reservation' ? 'Stay' : ''),\n },\n {\n field: 'status',\n headerName: 'Status',\n minWidth: 120,\n renderCell: ({ row }: { row: CheckInRow }) =>\n row.status === 'checked_in' ? (\n <Chip size=\"small\" color=\"success\" label=\"Checked in\" />\n ) : (\n <Chip size=\"small\" variant=\"outlined\" label=\"Expected\" />\n ),\n },\n {\n field: 'checkedInAtMs',\n headerName: 'Checked in at',\n type: 'number',\n minWidth: 120,\n valueFormatter: (value: number | null) => (list ? checkInTimeLabel(value, list.timeZone) : ''),\n },\n { field: 'checkedInByName', headerName: 'By', minWidth: 120 },\n {\n field: 'wristband',\n headerName: 'Wristband',\n minWidth: 120,\n valueGetter: (_value: unknown, row: CheckInRow) => describeWristband(row.wristband),\n },\n ],\n [list],\n )\n const rows = useMemo(() => (list ? list.checkIns.map((row) => ({ ...row, $id: row.id })) : []), [list])\n\n const exportCsv = async () => {\n if (!user || !list) return\n setExporting(true)\n try {\n const response = await authorizedFetch(user as never, url('csv'), { method: 'GET' })\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as { error?: string }\n setAnswer({ state: 'error', error: body.error || 'The spreadsheet could not be made.' })\n return\n }\n const blob = new Blob([await response.text()], { type: 'text/csv;charset=utf-8' })\n const href = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = href\n link.download = `check-ins-${list.dayKey}.csv`\n link.click()\n window.setTimeout(() => URL.revokeObjectURL(href), 1000)\n } finally {\n setExporting(false)\n }\n }\n\n return (\n <CardDisplay\n header={HEADER}\n help={HELP}\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" onClick={() => void load()} disabled={!canManagePos || answer.state === 'loading'}>\n {'Refresh'}\n </Button>\n <Button size=\"small\" onClick={() => void exportCsv()} disabled={!list || exporting}>\n {'Export CSV'}\n </Button>\n <Button size=\"small\" onClick={() => list && printPosDocument(posCheckInsDocument(list))} disabled={!list}>\n {'Print'}\n </Button>\n </Stack>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2.5}>\n {!canManagePos ? (\n <Alert severity=\"info\">\n {'Check-ins are for members who can use the register: a site admin or editor with the \"Use the point of sale\" permission.'}\n </Alert>\n ) : null}\n <LocalizationProvider dateAdapter={AdapterDayjs}>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} sx={{ alignItems: { sm: 'center' } }}>\n <DatePicker\n label=\"Day\"\n value={day}\n onChange={setDay}\n disabled={!canManagePos}\n slotProps={{ textField: { size: 'small' } }}\n />\n <Button size=\"small\" disabled={!canManagePos || !day} onClick={() => setDay(null)}>\n {'Today'}\n </Button>\n </Stack>\n </LocalizationProvider>\n {answer.state === 'loading' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Reading who is due…'}\n </Typography>\n ) : null}\n {answer.state === 'error' ? <Alert severity=\"warning\">{answer.error}</Alert> : null}\n {list ? (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${list.dayKey} · ${list.timeZone} · passes and stays that need check-in`}\n </Typography>\n {list.truncated ? (\n <Alert severity=\"warning\">\n {'This day has more check-ins than one list reads; some are missing here.'}\n </Alert>\n ) : null}\n <Stack direction=\"row\" spacing={3} sx={{ flexWrap: 'wrap', rowGap: 1.5 }}>\n <CommerceStatTile label=\"Expected\" value={String(list.totals.expected)} />\n <CommerceStatTile label=\"Checked in\" value={String(list.totals.checkedIn)} />\n <CommerceStatTile label=\"Still to come\" value={String(list.totals.expected - list.totals.checkedIn)} />\n </Stack>\n <ListTable\n aria-label=\"Check-ins on this day\"\n rows={rows}\n columns={columns}\n noRowsLabel=\"Nobody is due to check in on this day\"\n />\n </>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\n\n/**\n * Today's check-ins (AGL-4043): beside the day totals, for a store with a\n * register, on a plan with the point of sale.\n */\nexport function PosCheckInsCard(props: PosCheckInsCardProps) {\n const firestore = useFirestore()\n // Whether the store has a register at all, as the day totals ask it.\n const { data: registers, status } = useFirestoreCollection<any>(\n () =>\n query(collection(firestore, 'hosts', props.hostId, 'registers'), orderBy(documentId()), limit(1)),\n [firestore, props.hostId],\n { idField: '$id' },\n )\n if (status === 'success' && !registers?.length) return null\n return (\n <EntitlementGatedCard\n hostId={props.hostId}\n feature=\"pos\"\n header={HEADER}\n help={HELP}\n upsell={'Checking in passes, camping and stays at the window, with wristbands, comes with the point of sale.'}\n >\n <PosCheckInsBody {...props} />\n </EntitlementGatedCard>\n )\n}\n\nPosCheckInsCard.displayName = 'PosCheckInsCard'\n\nexport default PosCheckInsCard\n"],"names":["pluginDocsHelp","CardDisplay","ListTable","authorizedFetch","useFirestore","useFirestoreCollection","useUser","Alert","Button","Chip","Stack","Typography","AdapterDayjs","DatePicker","LocalizationProvider","collection","documentId","limit","orderBy","query","useCallback","useEffect","useMemo","useState","checkInItemLabel","checkInTimeLabel","describeWristband","POS_CHECK_IN_ROUTE","escapeHtml","CommerceStatTile","EntitlementGatedCard","POS_RECEIPT_STYLES","printPosDocument","HEADER","HELP","anchor","DAY_FORMAT","posCheckInsDocument","list","rows","checkIns","map","row","customerName","customerEmail","status","checkedInAtMs","timeZone","wristband","filter","Boolean","join","dayKey","totals","checkedIn","expected","PosCheckInsBody","props","hostId","canManagePos","data","user","day","setDay","answer","setAnswer","state","exporting","setExporting","url","format","params","URLSearchParams","isValid","set","toString","load","response","method","body","json","catch","ok","error","columns","field","headerName","flex","minWidth","valueGetter","_value","orderLabel","source","renderCell","size","color","label","variant","type","valueFormatter","value","$id","id","exportCsv","blob","Blob","text","href","URL","createObjectURL","link","document","createElement","download","click","window","setTimeout","revokeObjectURL","header","help","HeaderProps","action","direction","spacing","onClick","disabled","contentGutterX","contentGutterY","severity","dateAdapter","xs","sm","sx","alignItems","onChange","slotProps","textField","truncated","flexWrap","rowGap","String","aria-label","noRowsLabel","PosCheckInsCard","firestore","registers","idField","length","feature","upsell","displayName"],"mappings":"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"}
@@ -26,11 +26,15 @@ export interface PosCustomerLookupProps {
26
26
  hostId: string;
27
27
  value: PosSelectedCustomer | null;
28
28
  onChange: (customer: PosSelectedCustomer | null) => void;
29
+ /** The register and its PIN's cashier, for who checks the customer in (AGL-4043). */
30
+ registerId?: string;
31
+ cashierAssertion?: string;
29
32
  }
30
33
  /**
31
34
  * The customer at the register (AGL-3609): search the workspace's people by
32
35
  * name, email or phone, see what they have bought here before, attach them
33
- * to the sale — or add a new one in a few taps. Where the workspace keeps no
36
+ * to the sale — or add a new one in a few taps — and check them in when they
37
+ * have a pass or a stay due today (AGL-4043). Where the workspace keeps no
34
38
  * people, it falls back to an email typed for the receipt.
35
39
  */
36
40
  export declare function PosCustomerLookup(props: PosCustomerLookupProps): import("react").JSX.Element;
@@ -18,17 +18,20 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
19
  import { Alert, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, List, ListItemButton, ListItemText, Stack, TextField, Typography } from "@mui/material";
20
20
  import { useUser } from "@aglyn/tenant-feature-instance";
21
- import { useEffect, useRef, useState } from "react";
21
+ import { useCallback, useEffect, useRef, useState } from "react";
22
22
  import { posMoney } from "../../../model/commerce-pos-ops.js";
23
+ import { PosCheckInDialog } from "./pos-check-in-dialog.component.js";
24
+ import { PosCheckInZone } from "./pos-check-in-zone.component.js";
23
25
  import { callPosOps } from "./pos-ops-api.js";
24
26
  /** How long typing pauses before the register asks. */ const SEARCH_DEBOUNCE_MS = 250;
25
27
  /**
26
28
  * The customer at the register (AGL-3609): search the workspace's people by
27
29
  * name, email or phone, see what they have bought here before, attach them
28
- * to the sale — or add a new one in a few taps. Where the workspace keeps no
30
+ * to the sale — or add a new one in a few taps — and check them in when they
31
+ * have a pass or a stay due today (AGL-4043). Where the workspace keeps no
29
32
  * people, it falls back to an email typed for the receipt.
30
33
  */ export function PosCustomerLookup(props) {
31
- const { hostId, value, onChange } = props;
34
+ const { hostId, value, onChange, registerId, cashierAssertion } = props;
32
35
  const { data: user } = useUser();
33
36
  const [text, setText] = useState('');
34
37
  const [results, setResults] = useState([]);
@@ -93,6 +96,48 @@ import { callPosOps } from "./pos-ops-api.js";
93
96
  user,
94
97
  hostId
95
98
  ]);
99
+ // What the attached customer has to check in today (AGL-4043). The panel
100
+ // asks on every customer, so the server brings the day in line only on a
101
+ // day the window is already checking people in.
102
+ const [due, setDue] = useState(null);
103
+ const [checkingIn, setCheckingIn] = useState(false);
104
+ const contactId = (value == null ? void 0 : value.kind) === 'contact' ? value.id : '';
105
+ const refreshDue = useCallback(()=>{
106
+ if (!(value == null ? void 0 : value.email) && !contactId || !user) return undefined;
107
+ let active = true;
108
+ void callPosOps(user, 'pos-check-in', _extends({
109
+ hostId,
110
+ action: 'customer',
111
+ reconcile: 'if-used'
112
+ }, (value == null ? void 0 : value.email) ? {
113
+ email: value.email
114
+ } : {
115
+ contactId
116
+ })).then((answer)=>{
117
+ var _answer_body_checkIns;
118
+ if (!active || !answer.ok || !answer.body.dayKey) return;
119
+ const rows = (_answer_body_checkIns = answer.body.checkIns) != null ? _answer_body_checkIns : [];
120
+ setDue({
121
+ dayKey: answer.body.dayKey,
122
+ waiting: rows.filter((row)=>row.status === 'expected').length,
123
+ total: rows.length
124
+ });
125
+ });
126
+ return ()=>{
127
+ active = false;
128
+ };
129
+ }, [
130
+ value == null ? void 0 : value.email,
131
+ contactId,
132
+ user,
133
+ hostId
134
+ ]);
135
+ useEffect(()=>{
136
+ setDue(null);
137
+ return refreshDue();
138
+ }, [
139
+ refreshDue
140
+ ]);
96
141
  const pick = (customer)=>{
97
142
  onChange(customer);
98
143
  setText('');
@@ -137,7 +182,41 @@ import { callPosOps } from "./pos-ops-api.js";
137
182
  variant: "caption",
138
183
  color: "text.secondary",
139
184
  children: stats.orderCount ? `${stats.orderCount} past ${stats.orderCount === 1 ? 'order' : 'orders'} · ${posMoney(stats.lifetimeSpendCents)} spent` : 'First order here'
140
- }) : null
185
+ }) : null,
186
+ (due == null ? void 0 : due.waiting) ? /*#__PURE__*/ _jsx(Button, {
187
+ size: "small",
188
+ variant: "outlined",
189
+ onClick: ()=>setCheckingIn(true),
190
+ children: `Check in (${due.waiting})`
191
+ }) : null,
192
+ due ? /*#__PURE__*/ _jsx(PosCheckInZone, {
193
+ hostId: hostId,
194
+ orgId: undefined,
195
+ contactId: contactId || null,
196
+ email: value.email ? value.email.toLowerCase() : null,
197
+ dayKey: due.dayKey,
198
+ name: value.name || null,
199
+ unitsDue: due.total,
200
+ placement: "register-customer"
201
+ }) : null,
202
+ /*#__PURE__*/ _jsx(PosCheckInDialog, _extends({
203
+ open: checkingIn,
204
+ hostId: hostId
205
+ }, registerId ? {
206
+ registerId
207
+ } : {}, cashierAssertion ? {
208
+ cashierAssertion
209
+ } : {}, {
210
+ initialCustomer: value.email ? {
211
+ email: value.email
212
+ } : {
213
+ contactId: contactId || null
214
+ },
215
+ onClose: ()=>{
216
+ setCheckingIn(false);
217
+ refreshDue();
218
+ }
219
+ }))
141
220
  ]
142
221
  });
143
222
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-ops/pos-customer-lookup.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n List,\n ListItemButton,\n ListItemText,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useEffect, useRef, useState } from 'react'\nimport { posMoney } from '../../../model/commerce-pos-ops'\nimport { callPosOps } from './pos-ops-api'\n\n/** The customer a sale is rung for. `id` is empty when no record system keeps people. */\nexport interface PosSelectedCustomer {\n kind: string\n id: string\n name: string\n email: string | null\n phone: string | null\n}\n\nexport interface PosCustomerLookupProps {\n hostId: string\n value: PosSelectedCustomer | null\n onChange: (customer: PosSelectedCustomer | null) => void\n}\n\n/** How long typing pauses before the register asks. */\nconst SEARCH_DEBOUNCE_MS = 250\n\n/**\n * The customer at the register (AGL-3609): search the workspace's people by\n * name, email or phone, see what they have bought here before, attach them\n * to the sale — or add a new one in a few taps. Where the workspace keeps no\n * people, it falls back to an email typed for the receipt.\n */\nexport function PosCustomerLookup(props: PosCustomerLookupProps) {\n const { hostId, value, onChange } = props\n const { data: user } = useUser()\n const [text, setText] = useState('')\n const [results, setResults] = useState<PosSelectedCustomer[]>([])\n const [available, setAvailable] = useState(true)\n const [stats, setStats] = useState<{ orderCount: number; lifetimeSpendCents: number } | null>(null)\n const [adding, setAdding] = useState(false)\n const [draft, setDraft] = useState({ name: '', email: '', phone: '' })\n const [error, setError] = useState('')\n const [busy, setBusy] = useState(false)\n const latest = useRef(0)\n\n useEffect(() => {\n const typed = text.trim()\n if (typed.length < 2 || !user) {\n setResults([])\n return\n }\n const ticket = ++latest.current\n const timer = window.setTimeout(() => {\n void callPosOps<{ available?: boolean; customers?: PosSelectedCustomer[] }>(user, 'pos-customer', {\n hostId,\n action: 'search',\n text: typed,\n }).then((answer) => {\n if (ticket !== latest.current) return\n setAvailable(answer.body.available !== false)\n setResults(answer.ok ? (answer.body.customers ?? []) : [])\n })\n }, SEARCH_DEBOUNCE_MS)\n return () => window.clearTimeout(timer)\n }, [text, user, hostId])\n\n useEffect(() => {\n setStats(null)\n if (!value?.email || !user) return\n let active = true\n void callPosOps<{ orderCount?: number; lifetimeSpendCents?: number }>(user, 'pos-customer', {\n hostId,\n action: 'stats',\n email: value.email,\n }).then((answer) => {\n if (active && answer.ok) {\n setStats({\n orderCount: Number(answer.body.orderCount ?? 0),\n lifetimeSpendCents: Number(answer.body.lifetimeSpendCents ?? 0),\n })\n }\n })\n return () => {\n active = false\n }\n }, [value?.email, user, hostId])\n\n const pick = (customer: PosSelectedCustomer) => {\n onChange(customer)\n setText('')\n setResults([])\n }\n\n const typedEmail = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(text.trim()) ? text.trim().toLowerCase() : ''\n\n const create = async () => {\n setBusy(true)\n setError('')\n const answer = await callPosOps<{ customer?: PosSelectedCustomer }>(user, 'pos-customer', {\n hostId,\n action: 'create',\n ...draft,\n })\n setBusy(false)\n if (!answer.ok || !answer.body.customer) return setError(answer.body.error ?? 'Could not add the customer.')\n pick(answer.body.customer)\n setAdding(false)\n }\n\n if (value) {\n return (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Chip\n label={value.name || value.email || 'Customer'}\n onDelete={() => onChange(null)}\n color=\"primary\"\n variant=\"outlined\"\n />\n {value.name && value.email ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {value.email}\n </Typography>\n ) : null}\n {stats ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {stats.orderCount\n ? `${stats.orderCount} past ${stats.orderCount === 1 ? 'order' : 'orders'} · ${posMoney(\n stats.lifetimeSpendCents,\n )} spent`\n : 'First order here'}\n </Typography>\n ) : null}\n </Stack>\n )\n }\n\n return (\n <Stack spacing={0.5}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Customer\"\n placeholder={available ? 'Name, email or phone' : 'Email for the receipt'}\n value={text}\n onChange={(event) => setText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !available && typedEmail) {\n pick({ kind: 'none', id: '', name: '', email: typedEmail, phone: null })\n }\n }}\n sx={{ flex: 1 }}\n />\n <Button\n size=\"small\"\n onClick={() => {\n setDraft({ name: '', email: typedEmail, phone: '' })\n setError('')\n setAdding(true)\n }}\n >\n {'New customer'}\n </Button>\n </Stack>\n {results.length ? (\n <List dense disablePadding>\n {results.map((customer) => (\n <ListItemButton key={`${customer.kind}:${customer.id}`} onClick={() => pick(customer)}>\n <ListItemText\n primary={customer.name || customer.email || 'Unnamed'}\n secondary={[customer.email, customer.phone].filter(Boolean).join(' · ')}\n />\n </ListItemButton>\n ))}\n </List>\n ) : null}\n {!available && typedEmail ? (\n <Button size=\"small\" onClick={() => pick({ kind: 'none', id: '', name: '', email: typedEmail, phone: null })} sx={{ alignSelf: 'flex-start' }}>\n {`Use ${typedEmail}`}\n </Button>\n ) : null}\n <Dialog open={adding} onClose={() => setAdding(false)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'New customer'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n autoFocus\n label=\"Name\"\n value={draft.name}\n onChange={(event) => setDraft({ ...draft, name: event.target.value })}\n />\n <TextField\n label=\"Email\"\n type=\"email\"\n value={draft.email}\n onChange={(event) => setDraft({ ...draft, email: event.target.value })}\n />\n <TextField\n label=\"Phone (optional)\"\n type=\"tel\"\n value={draft.phone}\n onChange={(event) => setDraft({ ...draft, phone: event.target.value })}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Adding a customer does not sign them up for marketing email.'}\n </Typography>\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdding(false)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={busy} onClick={create}>\n {'Add customer'}\n </Button>\n </DialogActions>\n </Dialog>\n </Stack>\n )\n}\n\nPosCustomerLookup.displayName = 'PosCustomerLookup'\n\nexport default PosCustomerLookup\n"],"names":["Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","List","ListItemButton","ListItemText","Stack","TextField","Typography","useUser","useEffect","useRef","useState","posMoney","callPosOps","SEARCH_DEBOUNCE_MS","PosCustomerLookup","props","hostId","value","onChange","data","user","text","setText","results","setResults","available","setAvailable","stats","setStats","adding","setAdding","draft","setDraft","name","email","phone","error","setError","busy","setBusy","latest","typed","trim","length","ticket","current","timer","window","setTimeout","action","then","answer","body","ok","customers","clearTimeout","active","orderCount","Number","lifetimeSpendCents","pick","customer","typedEmail","test","toLowerCase","create","direction","spacing","useFlexGap","sx","alignItems","flexWrap","label","onDelete","color","variant","size","placeholder","event","target","onKeyDown","key","kind","id","flex","onClick","dense","disablePadding","map","primary","secondary","filter","Boolean","join","alignSelf","open","onClose","maxWidth","fullWidth","pt","autoFocus","type","severity","disabled","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-ops/pos-customer-lookup.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n List,\n ListItemButton,\n ListItemText,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { CheckInRow } from '../../../model/commerce-check-in'\nimport { posMoney } from '../../../model/commerce-pos-ops'\nimport { PosCheckInDialog } from './pos-check-in-dialog.component'\nimport { PosCheckInZone } from './pos-check-in-zone.component'\nimport { callPosOps } from './pos-ops-api'\n\n/** The customer a sale is rung for. `id` is empty when no record system keeps people. */\nexport interface PosSelectedCustomer {\n kind: string\n id: string\n name: string\n email: string | null\n phone: string | null\n}\n\nexport interface PosCustomerLookupProps {\n hostId: string\n value: PosSelectedCustomer | null\n onChange: (customer: PosSelectedCustomer | null) => void\n /** The register and its PIN's cashier, for who checks the customer in (AGL-4043). */\n registerId?: string\n cashierAssertion?: string\n}\n\n/** How long typing pauses before the register asks. */\nconst SEARCH_DEBOUNCE_MS = 250\n\n/**\n * The customer at the register (AGL-3609): search the workspace's people by\n * name, email or phone, see what they have bought here before, attach them\n * to the sale — or add a new one in a few taps — and check them in when they\n * have a pass or a stay due today (AGL-4043). Where the workspace keeps no\n * people, it falls back to an email typed for the receipt.\n */\nexport function PosCustomerLookup(props: PosCustomerLookupProps) {\n const { hostId, value, onChange, registerId, cashierAssertion } = props\n const { data: user } = useUser()\n const [text, setText] = useState('')\n const [results, setResults] = useState<PosSelectedCustomer[]>([])\n const [available, setAvailable] = useState(true)\n const [stats, setStats] = useState<{ orderCount: number; lifetimeSpendCents: number } | null>(null)\n const [adding, setAdding] = useState(false)\n const [draft, setDraft] = useState({ name: '', email: '', phone: '' })\n const [error, setError] = useState('')\n const [busy, setBusy] = useState(false)\n const latest = useRef(0)\n\n useEffect(() => {\n const typed = text.trim()\n if (typed.length < 2 || !user) {\n setResults([])\n return\n }\n const ticket = ++latest.current\n const timer = window.setTimeout(() => {\n void callPosOps<{ available?: boolean; customers?: PosSelectedCustomer[] }>(user, 'pos-customer', {\n hostId,\n action: 'search',\n text: typed,\n }).then((answer) => {\n if (ticket !== latest.current) return\n setAvailable(answer.body.available !== false)\n setResults(answer.ok ? (answer.body.customers ?? []) : [])\n })\n }, SEARCH_DEBOUNCE_MS)\n return () => window.clearTimeout(timer)\n }, [text, user, hostId])\n\n useEffect(() => {\n setStats(null)\n if (!value?.email || !user) return\n let active = true\n void callPosOps<{ orderCount?: number; lifetimeSpendCents?: number }>(user, 'pos-customer', {\n hostId,\n action: 'stats',\n email: value.email,\n }).then((answer) => {\n if (active && answer.ok) {\n setStats({\n orderCount: Number(answer.body.orderCount ?? 0),\n lifetimeSpendCents: Number(answer.body.lifetimeSpendCents ?? 0),\n })\n }\n })\n return () => {\n active = false\n }\n }, [value?.email, user, hostId])\n\n // What the attached customer has to check in today (AGL-4043). The panel\n // asks on every customer, so the server brings the day in line only on a\n // day the window is already checking people in.\n const [due, setDue] = useState<{ dayKey: string; waiting: number; total: number } | null>(null)\n const [checkingIn, setCheckingIn] = useState(false)\n const contactId = value?.kind === 'contact' ? value.id : ''\n const refreshDue = useCallback(() => {\n if ((!value?.email && !contactId) || !user) return undefined\n let active = true\n void callPosOps<{ dayKey?: string; checkIns?: CheckInRow[] }>(user, 'pos-check-in', {\n hostId,\n action: 'customer',\n reconcile: 'if-used',\n ...(value?.email ? { email: value.email } : { contactId }),\n }).then((answer) => {\n if (!active || !answer.ok || !answer.body.dayKey) return\n const rows = answer.body.checkIns ?? []\n setDue({\n dayKey: answer.body.dayKey,\n waiting: rows.filter((row) => row.status === 'expected').length,\n total: rows.length,\n })\n })\n return () => {\n active = false\n }\n }, [value?.email, contactId, user, hostId])\n useEffect(() => {\n setDue(null)\n return refreshDue()\n }, [refreshDue])\n\n const pick = (customer: PosSelectedCustomer) => {\n onChange(customer)\n setText('')\n setResults([])\n }\n\n const typedEmail = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(text.trim()) ? text.trim().toLowerCase() : ''\n\n const create = async () => {\n setBusy(true)\n setError('')\n const answer = await callPosOps<{ customer?: PosSelectedCustomer }>(user, 'pos-customer', {\n hostId,\n action: 'create',\n ...draft,\n })\n setBusy(false)\n if (!answer.ok || !answer.body.customer) return setError(answer.body.error ?? 'Could not add the customer.')\n pick(answer.body.customer)\n setAdding(false)\n }\n\n if (value) {\n return (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Chip\n label={value.name || value.email || 'Customer'}\n onDelete={() => onChange(null)}\n color=\"primary\"\n variant=\"outlined\"\n />\n {value.name && value.email ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {value.email}\n </Typography>\n ) : null}\n {stats ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {stats.orderCount\n ? `${stats.orderCount} past ${stats.orderCount === 1 ? 'order' : 'orders'} · ${posMoney(\n stats.lifetimeSpendCents,\n )} spent`\n : 'First order here'}\n </Typography>\n ) : null}\n {due?.waiting ? (\n <Button size=\"small\" variant=\"outlined\" onClick={() => setCheckingIn(true)}>\n {`Check in (${due.waiting})`}\n </Button>\n ) : null}\n {due ? (\n <PosCheckInZone\n hostId={hostId}\n orgId={undefined}\n contactId={contactId || null}\n email={value.email ? value.email.toLowerCase() : null}\n dayKey={due.dayKey}\n name={value.name || null}\n unitsDue={due.total}\n placement=\"register-customer\"\n />\n ) : null}\n <PosCheckInDialog\n open={checkingIn}\n hostId={hostId}\n {...(registerId ? { registerId } : {})}\n {...(cashierAssertion ? { cashierAssertion } : {})}\n initialCustomer={value.email ? { email: value.email } : { contactId: contactId || null }}\n onClose={() => {\n setCheckingIn(false)\n refreshDue()\n }}\n />\n </Stack>\n )\n }\n\n return (\n <Stack spacing={0.5}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Customer\"\n placeholder={available ? 'Name, email or phone' : 'Email for the receipt'}\n value={text}\n onChange={(event) => setText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !available && typedEmail) {\n pick({ kind: 'none', id: '', name: '', email: typedEmail, phone: null })\n }\n }}\n sx={{ flex: 1 }}\n />\n <Button\n size=\"small\"\n onClick={() => {\n setDraft({ name: '', email: typedEmail, phone: '' })\n setError('')\n setAdding(true)\n }}\n >\n {'New customer'}\n </Button>\n </Stack>\n {results.length ? (\n <List dense disablePadding>\n {results.map((customer) => (\n <ListItemButton key={`${customer.kind}:${customer.id}`} onClick={() => pick(customer)}>\n <ListItemText\n primary={customer.name || customer.email || 'Unnamed'}\n secondary={[customer.email, customer.phone].filter(Boolean).join(' · ')}\n />\n </ListItemButton>\n ))}\n </List>\n ) : null}\n {!available && typedEmail ? (\n <Button size=\"small\" onClick={() => pick({ kind: 'none', id: '', name: '', email: typedEmail, phone: null })} sx={{ alignSelf: 'flex-start' }}>\n {`Use ${typedEmail}`}\n </Button>\n ) : null}\n <Dialog open={adding} onClose={() => setAdding(false)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'New customer'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n autoFocus\n label=\"Name\"\n value={draft.name}\n onChange={(event) => setDraft({ ...draft, name: event.target.value })}\n />\n <TextField\n label=\"Email\"\n type=\"email\"\n value={draft.email}\n onChange={(event) => setDraft({ ...draft, email: event.target.value })}\n />\n <TextField\n label=\"Phone (optional)\"\n type=\"tel\"\n value={draft.phone}\n onChange={(event) => setDraft({ ...draft, phone: event.target.value })}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Adding a customer does not sign them up for marketing email.'}\n </Typography>\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdding(false)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={busy} onClick={create}>\n {'Add customer'}\n </Button>\n </DialogActions>\n </Dialog>\n </Stack>\n )\n}\n\nPosCustomerLookup.displayName = 'PosCustomerLookup'\n\nexport default PosCustomerLookup\n"],"names":["Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","List","ListItemButton","ListItemText","Stack","TextField","Typography","useUser","useCallback","useEffect","useRef","useState","posMoney","PosCheckInDialog","PosCheckInZone","callPosOps","SEARCH_DEBOUNCE_MS","PosCustomerLookup","props","hostId","value","onChange","registerId","cashierAssertion","data","user","text","setText","results","setResults","available","setAvailable","stats","setStats","adding","setAdding","draft","setDraft","name","email","phone","error","setError","busy","setBusy","latest","typed","trim","length","ticket","current","timer","window","setTimeout","action","then","answer","body","ok","customers","clearTimeout","active","orderCount","Number","lifetimeSpendCents","due","setDue","checkingIn","setCheckingIn","contactId","kind","id","refreshDue","undefined","reconcile","dayKey","rows","checkIns","waiting","filter","row","status","total","pick","customer","typedEmail","test","toLowerCase","create","direction","spacing","useFlexGap","sx","alignItems","flexWrap","label","onDelete","color","variant","size","onClick","orgId","unitsDue","placement","open","initialCustomer","onClose","placeholder","event","target","onKeyDown","key","flex","dense","disablePadding","map","primary","secondary","Boolean","join","alignSelf","maxWidth","fullWidth","pt","autoFocus","type","severity","disabled","displayName"],"mappings":"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"}