@aglyn/plugins-commerce 1.0.0-beta.269 → 1.0.0-beta.271

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 (38) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/console/entitlement-gate.component.d.ts +11 -16
  3. package/src/lib/components/console/entitlement-gate.component.js +15 -25
  4. package/src/lib/components/console/entitlement-gate.component.js.map +1 -1
  5. package/src/lib/components/console/payment-methods-card.component.js +7 -0
  6. package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
  7. package/src/lib/components/console/payments-settings-card.component.d.ts +2 -0
  8. package/src/lib/components/console/payments-settings-card.component.js +2 -0
  9. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  10. package/src/lib/components/console/pos/pos-kiosk-readiness.component.d.ts +58 -0
  11. package/src/lib/components/console/pos/pos-kiosk-readiness.component.js +146 -0
  12. package/src/lib/components/console/pos/pos-kiosk-readiness.component.js.map +1 -0
  13. package/src/lib/components/console/pos/pos-pairing.component.d.ts +12 -2
  14. package/src/lib/components/console/pos/pos-pairing.component.js +14 -1
  15. package/src/lib/components/console/pos/pos-pairing.component.js.map +1 -1
  16. package/src/lib/components/console/pos-devices-card.component.js +13 -0
  17. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  18. package/src/lib/contracts.d.ts +237 -0
  19. package/src/lib/contracts.js +46 -0
  20. package/src/lib/contracts.js.map +1 -0
  21. package/src/lib/declarations.console-server.js +8 -0
  22. package/src/lib/declarations.console-server.js.map +1 -1
  23. package/src/lib/model/commerce-record-routes.d.ts +7 -0
  24. package/src/lib/model/commerce-record-routes.js +7 -0
  25. package/src/lib/model/commerce-record-routes.js.map +1 -1
  26. package/src/lib/model/commerce-server-search-sources.d.ts +30 -0
  27. package/src/lib/model/commerce-server-search-sources.js +57 -0
  28. package/src/lib/model/commerce-server-search-sources.js.map +1 -0
  29. package/src/lib/model/order-events.d.ts +8 -127
  30. package/src/lib/model/order-events.js +11 -24
  31. package/src/lib/model/order-events.js.map +1 -1
  32. package/src/lib/model/order-history.d.ts +7 -40
  33. package/src/lib/model/order-history.js +7 -5
  34. package/src/lib/model/order-history.js.map +1 -1
  35. package/src/lib/native-nav.d.ts +30 -0
  36. package/src/lib/native-nav.js +127 -0
  37. package/src/lib/native-nav.js.map +1 -0
  38. package/src/lib/server/order-event-triggers.d.ts +2 -2
@@ -0,0 +1,146 @@
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 { jsx as _jsx } from "react/jsx-runtime";
18
+ import { buildRoute, Route } from "@aglyn/aglyn";
19
+ import { PrerequisiteNotice } from "@aglyn/shared-ui-jsx/components/prerequisite-notice.component";
20
+ import { useConsoleHostRoute, useHostOrgId, useMerchantPayments, useSitePluginConfig, useStoreSettingsHref } from "@aglyn/tenant-feature-instance";
21
+ import { Stack } from "@mui/material";
22
+ import { useEffect, useState } from "react";
23
+ import { BUNDLE_ID } from "../../../constants/bundle-common.js";
24
+ import { posReadersCall } from "./pos-api.js";
25
+ /** The element id the POS devices card carries, for a link that lands on its card readers. */ export const POS_DEVICES_ANCHOR = 'pos-devices';
26
+ /**
27
+ * What stands between a kiosk on this register and taking payment, most
28
+ * serious first. It mirrors the kiosk's own answer (`server/pos-kiosk.ts`
29
+ * `context`): a reader is offered only where the deployment takes card
30
+ * payments and the register has one, and pay at the counter only where it is
31
+ * switched on. Pure.
32
+ */ export function posKioskGaps(facts) {
33
+ const gaps = [];
34
+ const cardReady = facts.cardsOffered && facts.registerReaders > 0 && !facts.stripeBlocked;
35
+ if (!facts.payAtCounter && !cardReady) gaps.push('no-payment');
36
+ if (!facts.cardsOffered) return gaps;
37
+ if (facts.stripeBlocked) gaps.push('stripe');
38
+ if (facts.registerReaders === 0) gaps.push('reader');
39
+ else if (facts.registerReaders > 1) gaps.push('reader-choice');
40
+ return gaps;
41
+ }
42
+ /**
43
+ * Says, while a kiosk is being paired, what it still needs to take payment
44
+ * (AGL-3982). The shopper at a kiosk with no way to pay reads "Payment is not
45
+ * set up on this kiosk yet"; this is where the merchant learns why, with a
46
+ * link to each fix: Stripe on the Payments page, a reader on the POS devices
47
+ * card, and pay at the counter under Admin → Plugins → Commerce.
48
+ *
49
+ * Nothing is said until every answer is in: a notice that flashes while the
50
+ * readers or the settings load would tell a ready store to set up.
51
+ */ export function PosKioskReadiness({ user, hostId, registerId, registerName }) {
52
+ var _ref;
53
+ const payments = useMerchantPayments(hostId);
54
+ const orgId = useHostOrgId(hostId);
55
+ const { config, ready: configReady } = useSitePluginConfig(orgId, hostId, BUNDLE_ID);
56
+ const { orgSlug, subdomain } = useConsoleHostRoute(hostId);
57
+ const posSettingsHref = useStoreSettingsHref(hostId, 'pos');
58
+ // `error` when the readers could not be listed: then nothing is said, since
59
+ // "no reader" would be a guess.
60
+ const [readers, setReaders] = useState(null);
61
+ // The person, for the load's key: a signed-in session carries its uid.
62
+ const uid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : '';
63
+ useEffect(()=>{
64
+ if (!user || !registerId) return;
65
+ let active = true;
66
+ void posReadersCall(user, {
67
+ action: 'list',
68
+ hostId
69
+ }).then((answer)=>{
70
+ var _ref;
71
+ if (!active) return;
72
+ setReaders({
73
+ available: Boolean(answer == null ? void 0 : answer.available),
74
+ count: ((_ref = answer == null ? void 0 : answer.readers) != null ? _ref : []).filter((reader)=>reader.registerId === registerId).length
75
+ });
76
+ }).catch(()=>{
77
+ if (active) setReaders('error');
78
+ });
79
+ return ()=>{
80
+ active = false;
81
+ };
82
+ // Keyed on the person, not the session object (see the register page).
83
+ // eslint-disable-next-line react-hooks/exhaustive-deps
84
+ }, [
85
+ uid,
86
+ hostId,
87
+ registerId
88
+ ]);
89
+ if (!readers || readers === 'error' || !configReady || !payments.settled) return null;
90
+ // `posKioskSettings`'s own reading (pos-kiosk-config.ts), spelled out here:
91
+ // importing it puts the kiosk's settings module in the plugin's eager
92
+ // chunk, and this card is drawn only on the pairing dialog.
93
+ const payAtCounter = config['posKioskPayAtCounter'] !== false;
94
+ const gaps = posKioskGaps({
95
+ cardsOffered: readers.available,
96
+ registerReaders: readers.count,
97
+ payAtCounter,
98
+ stripeBlocked: payments.blocked
99
+ });
100
+ if (!gaps.length) return null;
101
+ const register = registerName || 'this register';
102
+ const devicesHref = posSettingsHref ? `${posSettingsHref}#${POS_DEVICES_ANCHOR}` : undefined;
103
+ const kioskSettingsHref = orgSlug && subdomain ? buildRoute(Route.HOST_ADMIN_PLUGIN, {
104
+ orgSlug,
105
+ host: subdomain,
106
+ pluginRef: BUNDLE_ID
107
+ }) : undefined;
108
+ return /*#__PURE__*/ _jsx(Stack, {
109
+ spacing: 1,
110
+ "data-testid": "pos-kiosk-readiness",
111
+ children: gaps.map((gap)=>{
112
+ switch(gap){
113
+ case 'no-payment':
114
+ return /*#__PURE__*/ _jsx(PrerequisiteNotice, {
115
+ severity: "warning",
116
+ href: kioskSettingsHref,
117
+ actionLabel: "Open Commerce settings",
118
+ children: 'This kiosk can’t take payment yet: customers will see “Payment is not set up on this kiosk yet.” ' + 'Turn on “Kiosk: let customers pay at the counter” under Admin → Plugins → Commerce' + (readers.available ? ', or finish card payments below.' : '.')
119
+ }, gap);
120
+ case 'stripe':
121
+ var _payments_href, _payments_reason;
122
+ return /*#__PURE__*/ _jsx(PrerequisiteNotice, {
123
+ href: (_payments_href = payments.href) != null ? _payments_href : undefined,
124
+ actionLabel: "Open Payments",
125
+ children: `${(_payments_reason = payments.reason) != null ? _payments_reason : ''} Until then the kiosk can’t take cards` + (payAtCounter ? '; customers pay at the counter.' : '.')
126
+ }, gap);
127
+ case 'reader':
128
+ return /*#__PURE__*/ _jsx(PrerequisiteNotice, {
129
+ href: devicesHref,
130
+ actionLabel: "Open POS devices",
131
+ children: `No card reader is assigned to ${register}. Assign one under POS devices so customers can pay by card at the kiosk.`
132
+ }, gap);
133
+ case 'reader-choice':
134
+ return /*#__PURE__*/ _jsx(PrerequisiteNotice, {
135
+ children: `${register === 'this register' ? 'This register' : register} has ${readers.count} card readers. ` + 'Once the kiosk is paired, choose the one it uses from its staff menu.'
136
+ }, gap);
137
+ default:
138
+ return null;
139
+ }
140
+ })
141
+ });
142
+ }
143
+ PosKioskReadiness.displayName = 'PosKioskReadiness';
144
+ export default PosKioskReadiness;
145
+
146
+ //# sourceMappingURL=pos-kiosk-readiness.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos/pos-kiosk-readiness.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 { buildRoute, Route } from '@aglyn/aglyn'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\nimport {\n useConsoleHostRoute,\n useHostOrgId,\n useMerchantPayments,\n useSitePluginConfig,\n useStoreSettingsHref,\n} from '@aglyn/tenant-feature-instance'\nimport { Stack } from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { BUNDLE_ID } from '../../../constants/bundle-common'\nimport { posReadersCall } from './pos-api'\n\ntype User = Parameters<typeof posReadersCall>[0]\n\n/** The element id the POS devices card carries, for a link that lands on its card readers. */\nexport const POS_DEVICES_ANCHOR = 'pos-devices'\n\n/**\n * One thing that keeps a kiosk from taking payment, or narrows how:\n *\n * - `no-payment` — pay at the counter is off and no card path is ready, so\n * the shopper is told \"Payment is not set up on this kiosk yet\";\n * - `stripe` — card payments wait on the workspace's Stripe account;\n * - `reader` — no card reader is assigned to the register;\n * - `reader-choice` — several are, and the kiosk sends cards to the one its\n * staff menu picks.\n */\nexport type PosKioskGap = 'no-payment' | 'stripe' | 'reader' | 'reader-choice'\n\nexport interface PosKioskReadinessFacts {\n /**\n * The deployment takes card payments at a register at all (the readers\n * route's `available`). Where it does not, nothing about readers or Stripe\n * is the merchant's to fix, and only pay at the counter is left.\n */\n cardsOffered: boolean\n /** Card readers assigned to this register. A reader on \"Any register\" is not one the kiosk uses. */\n registerReaders: number\n /** \"Kiosk: let customers pay at the counter\", as the site resolves it. */\n payAtCounter: boolean\n /** The workspace's Stripe account can not take card payments yet (settled, never while pending). */\n stripeBlocked: boolean\n}\n\n/**\n * What stands between a kiosk on this register and taking payment, most\n * serious first. It mirrors the kiosk's own answer (`server/pos-kiosk.ts`\n * `context`): a reader is offered only where the deployment takes card\n * payments and the register has one, and pay at the counter only where it is\n * switched on. Pure.\n */\nexport function posKioskGaps(facts: PosKioskReadinessFacts): PosKioskGap[] {\n const gaps: PosKioskGap[] = []\n const cardReady = facts.cardsOffered && facts.registerReaders > 0 && !facts.stripeBlocked\n if (!facts.payAtCounter && !cardReady) gaps.push('no-payment')\n if (!facts.cardsOffered) return gaps\n if (facts.stripeBlocked) gaps.push('stripe')\n if (facts.registerReaders === 0) gaps.push('reader')\n else if (facts.registerReaders > 1) gaps.push('reader-choice')\n return gaps\n}\n\nexport interface PosKioskReadinessProps {\n user: User | null | undefined\n hostId: string\n registerId: string\n registerName?: string\n}\n\n/**\n * Says, while a kiosk is being paired, what it still needs to take payment\n * (AGL-3982). The shopper at a kiosk with no way to pay reads \"Payment is not\n * set up on this kiosk yet\"; this is where the merchant learns why, with a\n * link to each fix: Stripe on the Payments page, a reader on the POS devices\n * card, and pay at the counter under Admin → Plugins → Commerce.\n *\n * Nothing is said until every answer is in: a notice that flashes while the\n * readers or the settings load would tell a ready store to set up.\n */\nexport function PosKioskReadiness({ user, hostId, registerId, registerName }: PosKioskReadinessProps) {\n const payments = useMerchantPayments(hostId)\n const orgId = useHostOrgId(hostId)\n const { config, ready: configReady } = useSitePluginConfig(orgId, hostId, BUNDLE_ID)\n const { orgSlug, subdomain } = useConsoleHostRoute(hostId)\n const posSettingsHref = useStoreSettingsHref(hostId, 'pos')\n // `error` when the readers could not be listed: then nothing is said, since\n // \"no reader\" would be a guess.\n const [readers, setReaders] = useState<{ available: boolean; count: number } | 'error' | null>(null)\n // The person, for the load's key: a signed-in session carries its uid.\n const uid = (user as { uid?: string } | null | undefined)?.uid ?? ''\n\n useEffect(() => {\n if (!user || !registerId) return\n let active = true\n void posReadersCall<{ available?: boolean; readers?: Array<{ registerId: string | null }> }>(user, {\n action: 'list',\n hostId,\n })\n .then((answer) => {\n if (!active) return\n setReaders({\n available: Boolean(answer?.available),\n count: (answer?.readers ?? []).filter((reader) => reader.registerId === registerId).length,\n })\n })\n .catch(() => {\n if (active) setReaders('error')\n })\n return () => {\n active = false\n }\n // Keyed on the person, not the session object (see the register page).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId, registerId])\n\n if (!readers || readers === 'error' || !configReady || !payments.settled) return null\n // `posKioskSettings`'s own reading (pos-kiosk-config.ts), spelled out here:\n // importing it puts the kiosk's settings module in the plugin's eager\n // chunk, and this card is drawn only on the pairing dialog.\n const payAtCounter = config['posKioskPayAtCounter'] !== false\n const gaps = posKioskGaps({\n cardsOffered: readers.available,\n registerReaders: readers.count,\n payAtCounter,\n stripeBlocked: payments.blocked,\n })\n if (!gaps.length) return null\n\n const register = registerName || 'this register'\n const devicesHref = posSettingsHref ? `${posSettingsHref}#${POS_DEVICES_ANCHOR}` : undefined\n const kioskSettingsHref =\n orgSlug && subdomain\n ? buildRoute(Route.HOST_ADMIN_PLUGIN, { orgSlug, host: subdomain, pluginRef: BUNDLE_ID })\n : undefined\n return (\n <Stack spacing={1} data-testid=\"pos-kiosk-readiness\">\n {gaps.map((gap) => {\n switch (gap) {\n case 'no-payment':\n return (\n <PrerequisiteNotice key={gap} severity=\"warning\" href={kioskSettingsHref} actionLabel=\"Open Commerce settings\">\n {'This kiosk can’t take payment yet: customers will see “Payment is not set up on this kiosk yet.” ' +\n 'Turn on “Kiosk: let customers pay at the counter” under Admin → Plugins → Commerce' +\n (readers.available ? ', or finish card payments below.' : '.')}\n </PrerequisiteNotice>\n )\n case 'stripe':\n return (\n <PrerequisiteNotice key={gap} href={payments.href ?? undefined} actionLabel=\"Open Payments\">\n {`${payments.reason ?? ''} Until then the kiosk can’t take cards` +\n (payAtCounter ? '; customers pay at the counter.' : '.')}\n </PrerequisiteNotice>\n )\n case 'reader':\n return (\n <PrerequisiteNotice key={gap} href={devicesHref} actionLabel=\"Open POS devices\">\n {`No card reader is assigned to ${register}. Assign one under POS devices so customers can pay by card at the kiosk.`}\n </PrerequisiteNotice>\n )\n case 'reader-choice':\n return (\n <PrerequisiteNotice key={gap}>\n {`${register === 'this register' ? 'This register' : register} has ${readers.count} card readers. ` +\n 'Once the kiosk is paired, choose the one it uses from its staff menu.'}\n </PrerequisiteNotice>\n )\n default:\n return null\n }\n })}\n </Stack>\n )\n}\nPosKioskReadiness.displayName = 'PosKioskReadiness'\n\nexport default PosKioskReadiness\n"],"names":["buildRoute","Route","PrerequisiteNotice","useConsoleHostRoute","useHostOrgId","useMerchantPayments","useSitePluginConfig","useStoreSettingsHref","Stack","useEffect","useState","BUNDLE_ID","posReadersCall","POS_DEVICES_ANCHOR","posKioskGaps","facts","gaps","cardReady","cardsOffered","registerReaders","stripeBlocked","payAtCounter","push","PosKioskReadiness","user","hostId","registerId","registerName","payments","orgId","config","ready","configReady","orgSlug","subdomain","posSettingsHref","readers","setReaders","uid","active","action","then","answer","available","Boolean","count","filter","reader","length","catch","settled","blocked","register","devicesHref","undefined","kioskSettingsHref","HOST_ADMIN_PLUGIN","host","pluginRef","spacing","data-testid","map","gap","severity","href","actionLabel","reason","displayName"],"mappings":"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"}
@@ -25,9 +25,19 @@ export interface PosPairingDialogProps {
25
25
  pairing: PosPairingState | null;
26
26
  onModeChange: (mode: CommerceModel.PosDeviceMode) => void;
27
27
  onClose: () => void;
28
+ /**
29
+ * Who is pairing, and for which site: a kiosk code then also says what the
30
+ * kiosk still needs to take payment (AGL-3982). Without them it shows the
31
+ * code alone.
32
+ */
33
+ user?: User | null;
34
+ hostId?: string;
28
35
  }
29
- /** The code, the address to enter it at, and the display-or-kiosk choice. */
30
- export declare function PosPairingDialog({ pairing, onModeChange, onClose }: PosPairingDialogProps): import("react").JSX.Element;
36
+ /**
37
+ * The code, the address to enter it at, and the display-or-kiosk choice; for
38
+ * a kiosk, what it still needs to take payment.
39
+ */
40
+ export declare function PosPairingDialog({ pairing, onModeChange, onClose, user, hostId }: PosPairingDialogProps): import("react").JSX.Element;
31
41
  export declare namespace PosPairingDialog {
32
42
  var displayName: string;
33
43
  }
@@ -20,6 +20,7 @@ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, ToggleButton
20
20
  import { useCallback, useState } from "react";
21
21
  import * as CommerceModel from "../../../model/index.js";
22
22
  import { posDisplayCall, PosRequestError } from "./pos-api.js";
23
+ import { PosKioskReadiness } from "./pos-kiosk-readiness.component.js";
23
24
  /**
24
25
  * A fresh six-digit code that pairs a tablet to one register, as a customer
25
26
  * display or a self-service kiosk (AGL-3608, AGL-3623). The server holds the
@@ -37,7 +38,10 @@ import { posDisplayCall, PosRequestError } from "./pos-api.js";
37
38
  return `${origin}/kiosk/commerce/${mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`;
38
39
  }
39
40
  const TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60000);
40
- /** The code, the address to enter it at, and the display-or-kiosk choice. */ export function PosPairingDialog({ pairing, onModeChange, onClose }) {
41
+ /**
42
+ * The code, the address to enter it at, and the display-or-kiosk choice; for
43
+ * a kiosk, what it still needs to take payment.
44
+ */ export function PosPairingDialog({ pairing, onModeChange, onClose, user, hostId }) {
41
45
  var _ref;
42
46
  const mode = (_ref = pairing == null ? void 0 : pairing.mode) != null ? _ref : 'display';
43
47
  return /*#__PURE__*/ _jsxs(Dialog, {
@@ -89,6 +93,13 @@ const TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60000)
89
93
  })
90
94
  ]
91
95
  }),
96
+ mode === 'kiosk' && pairing && hostId ? /*#__PURE__*/ _jsx(PosKioskReadiness, _extends({
97
+ user: user,
98
+ hostId: hostId,
99
+ registerId: pairing.registerId
100
+ }, pairing.registerName ? {
101
+ registerName: pairing.registerName
102
+ } : {})) : null,
92
103
  /*#__PURE__*/ _jsx(Typography, {
93
104
  variant: "body2",
94
105
  color: "text.secondary",
@@ -149,6 +160,8 @@ PosPairingDialog.displayName = 'PosPairingDialog';
149
160
  const close = useCallback(()=>setPairing(null), []);
150
161
  const dialog = /*#__PURE__*/ _jsx(PosPairingDialog, {
151
162
  pairing: pairing,
163
+ user: user,
164
+ hostId: hostId,
152
165
  onClose: close,
153
166
  onModeChange: (mode)=>{
154
167
  if (pairing) void open(pairing.registerId, mode, pairing.registerName);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos/pos-pairing.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 {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport * as CommerceModel from '../../../model'\nimport { posDisplayCall, PosRequestError } from './pos-api'\n\ntype User = Parameters<typeof posDisplayCall>[0]\n\n/** A code on screen, and the register and kind of device it pairs. */\nexport interface PosPairingState {\n registerId: string\n registerName?: string\n mode: CommerceModel.PosDeviceMode\n code: string\n expiresAtMs: number\n}\n\n/**\n * A fresh six-digit code that pairs a tablet to one register, as a customer\n * display or a self-service kiosk (AGL-3608, AGL-3623). The server holds the\n * gate: the register's role and `managePos` checks and the plan's `pos`.\n */\nexport function requestPosPairingCode(\n user: User,\n hostId: string,\n registerId: string,\n mode: CommerceModel.PosDeviceMode = 'display',\n): Promise<{ code: string; expiresAtMs: number }> {\n return posDisplayCall<{ code: string; expiresAtMs: number }>(user, {\n action: 'pairing-code',\n hostId,\n registerId,\n mode,\n })\n}\n\n/** The address a tablet opens to enter the code. */\nexport function posPairingAddress(mode: CommerceModel.PosDeviceMode): string {\n const origin = typeof window === 'undefined' ? '' : window.location.origin\n return `${origin}/kiosk/commerce/${mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`\n}\n\nconst TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60_000)\n\nexport interface PosPairingDialogProps {\n pairing: PosPairingState | null\n onModeChange: (mode: CommerceModel.PosDeviceMode) => void\n onClose: () => void\n}\n\n/** The code, the address to enter it at, and the display-or-kiosk choice. */\nexport function PosPairingDialog({ pairing, onModeChange, onClose }: PosPairingDialogProps) {\n const mode = pairing?.mode ?? 'display'\n return (\n <Dialog open={Boolean(pairing)} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>\n {mode === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'}\n {pairing?.registerName ? (\n <Typography component=\"span\" variant=\"body2\" color=\"text.secondary\" sx={{ display: 'block' }}>\n {`For ${pairing.registerName}`}\n </Typography>\n ) : null}\n </DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <ToggleButtonGroup\n exclusive\n fullWidth\n size=\"small\"\n value={mode}\n onChange={(_event, next: CommerceModel.PosDeviceMode | null) => {\n if (next && next !== mode) onModeChange(next)\n }}\n sx={{ mt: 1 }}\n >\n <ToggleButton value=\"display\">{'Customer display'}</ToggleButton>\n <ToggleButton value=\"kiosk\">{'Self-service kiosk'}</ToggleButton>\n </ToggleButtonGroup>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(mode === 'kiosk'\n ? 'On the tablet customers order from, open this address and enter the code. '\n : 'On the tablet facing your customer, open this address and enter the code. ') +\n `The code works once and expires in ${TTL_MINUTES} minutes.`}\n </Typography>\n <Typography variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {posPairingAddress(mode)}\n </Typography>\n <Typography variant=\"h3\" component=\"p\" sx={{ textAlign: 'center', letterSpacing: 8 }}>\n {pairing?.code}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\nPosPairingDialog.displayName = 'PosPairingDialog'\n\nexport interface UsePosPairingOptions {\n user: User | null | undefined\n hostId: string\n /** Told the reason a code could not be made. */\n onError: (message: string) => void\n}\n\n/**\n * The one pairing flow, for the register and the POS devices card alike:\n * `open(registerId)` asks for a code and shows it; switching between\n * customer display and kiosk asks for a code of that kind. Render `dialog`.\n */\nexport function usePosPairing({ user, hostId, onError }: UsePosPairingOptions): {\n pairing: PosPairingState | null\n open: (registerId: string, mode?: CommerceModel.PosDeviceMode, registerName?: string) => Promise<void>\n close: () => void\n dialog: ReactNode\n} {\n const [pairing, setPairing] = useState<PosPairingState | null>(null)\n\n const open = useCallback(\n async (registerId: string, mode: CommerceModel.PosDeviceMode = 'display', registerName?: string) => {\n if (!user || !registerId) return\n try {\n const answer = await requestPosPairingCode(user, hostId, registerId, mode)\n setPairing({ ...answer, registerId, mode, ...(registerName ? { registerName } : {}) })\n } catch (error) {\n onError(error instanceof PosRequestError ? error.message : 'Could not make a code')\n }\n },\n [user, hostId, onError],\n )\n const close = useCallback(() => setPairing(null), [])\n\n const dialog = (\n <PosPairingDialog\n pairing={pairing}\n onClose={close}\n onModeChange={(mode) => {\n if (pairing) void open(pairing.registerId, mode, pairing.registerName)\n }}\n />\n )\n return { pairing, open, close, dialog }\n}\n\n/** The element id the POS registers card carries, for a link that lands on it. */\nexport const POS_REGISTERS_ANCHOR = 'pos-registers'\n\n/**\n * Where a register is created — the POS registers card, on Products → Point\n * of sale (`…/products/pos`, AGL-3713) — from the register page's own console\n * path (`…/pos`). Undefined when that path is not the register's.\n */\nexport function posRegistersSettingsHref(posBasePath: string | undefined): string | undefined {\n if (!posBasePath || !/\\/pos\\/?$/.test(posBasePath)) return undefined\n return `${posBasePath.replace(/\\/pos\\/?$/, '/products/pos')}#${POS_REGISTERS_ANCHOR}`\n}\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","ToggleButton","ToggleButtonGroup","Typography","useCallback","useState","CommerceModel","posDisplayCall","PosRequestError","requestPosPairingCode","user","hostId","registerId","mode","action","posPairingAddress","origin","window","location","TTL_MINUTES","Math","round","POS_DISPLAY_PAIRING_TTL_MS","PosPairingDialog","pairing","onModeChange","onClose","open","Boolean","maxWidth","fullWidth","registerName","component","variant","color","sx","display","flexDirection","gap","exclusive","size","value","onChange","_event","next","mt","wordBreak","textAlign","letterSpacing","code","onClick","displayName","usePosPairing","onError","setPairing","answer","error","message","close","dialog","POS_REGISTERS_ANCHOR","posRegistersSettingsHref","posBasePath","test","undefined","replace"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos/pos-pairing.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 {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport * as CommerceModel from '../../../model'\nimport { posDisplayCall, PosRequestError } from './pos-api'\nimport { PosKioskReadiness } from './pos-kiosk-readiness.component'\n\ntype User = Parameters<typeof posDisplayCall>[0]\n\n/** A code on screen, and the register and kind of device it pairs. */\nexport interface PosPairingState {\n registerId: string\n registerName?: string\n mode: CommerceModel.PosDeviceMode\n code: string\n expiresAtMs: number\n}\n\n/**\n * A fresh six-digit code that pairs a tablet to one register, as a customer\n * display or a self-service kiosk (AGL-3608, AGL-3623). The server holds the\n * gate: the register's role and `managePos` checks and the plan's `pos`.\n */\nexport function requestPosPairingCode(\n user: User,\n hostId: string,\n registerId: string,\n mode: CommerceModel.PosDeviceMode = 'display',\n): Promise<{ code: string; expiresAtMs: number }> {\n return posDisplayCall<{ code: string; expiresAtMs: number }>(user, {\n action: 'pairing-code',\n hostId,\n registerId,\n mode,\n })\n}\n\n/** The address a tablet opens to enter the code. */\nexport function posPairingAddress(mode: CommerceModel.PosDeviceMode): string {\n const origin = typeof window === 'undefined' ? '' : window.location.origin\n return `${origin}/kiosk/commerce/${mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`\n}\n\nconst TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60_000)\n\nexport interface PosPairingDialogProps {\n pairing: PosPairingState | null\n onModeChange: (mode: CommerceModel.PosDeviceMode) => void\n onClose: () => void\n /**\n * Who is pairing, and for which site: a kiosk code then also says what the\n * kiosk still needs to take payment (AGL-3982). Without them it shows the\n * code alone.\n */\n user?: User | null\n hostId?: string\n}\n\n/**\n * The code, the address to enter it at, and the display-or-kiosk choice; for\n * a kiosk, what it still needs to take payment.\n */\nexport function PosPairingDialog({ pairing, onModeChange, onClose, user, hostId }: PosPairingDialogProps) {\n const mode = pairing?.mode ?? 'display'\n return (\n <Dialog open={Boolean(pairing)} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>\n {mode === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'}\n {pairing?.registerName ? (\n <Typography component=\"span\" variant=\"body2\" color=\"text.secondary\" sx={{ display: 'block' }}>\n {`For ${pairing.registerName}`}\n </Typography>\n ) : null}\n </DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <ToggleButtonGroup\n exclusive\n fullWidth\n size=\"small\"\n value={mode}\n onChange={(_event, next: CommerceModel.PosDeviceMode | null) => {\n if (next && next !== mode) onModeChange(next)\n }}\n sx={{ mt: 1 }}\n >\n <ToggleButton value=\"display\">{'Customer display'}</ToggleButton>\n <ToggleButton value=\"kiosk\">{'Self-service kiosk'}</ToggleButton>\n </ToggleButtonGroup>\n {mode === 'kiosk' && pairing && hostId ? (\n <PosKioskReadiness\n user={user}\n hostId={hostId}\n registerId={pairing.registerId}\n {...(pairing.registerName ? { registerName: pairing.registerName } : {})}\n />\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(mode === 'kiosk'\n ? 'On the tablet customers order from, open this address and enter the code. '\n : 'On the tablet facing your customer, open this address and enter the code. ') +\n `The code works once and expires in ${TTL_MINUTES} minutes.`}\n </Typography>\n <Typography variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {posPairingAddress(mode)}\n </Typography>\n <Typography variant=\"h3\" component=\"p\" sx={{ textAlign: 'center', letterSpacing: 8 }}>\n {pairing?.code}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\nPosPairingDialog.displayName = 'PosPairingDialog'\n\nexport interface UsePosPairingOptions {\n user: User | null | undefined\n hostId: string\n /** Told the reason a code could not be made. */\n onError: (message: string) => void\n}\n\n/**\n * The one pairing flow, for the register and the POS devices card alike:\n * `open(registerId)` asks for a code and shows it; switching between\n * customer display and kiosk asks for a code of that kind. Render `dialog`.\n */\nexport function usePosPairing({ user, hostId, onError }: UsePosPairingOptions): {\n pairing: PosPairingState | null\n open: (registerId: string, mode?: CommerceModel.PosDeviceMode, registerName?: string) => Promise<void>\n close: () => void\n dialog: ReactNode\n} {\n const [pairing, setPairing] = useState<PosPairingState | null>(null)\n\n const open = useCallback(\n async (registerId: string, mode: CommerceModel.PosDeviceMode = 'display', registerName?: string) => {\n if (!user || !registerId) return\n try {\n const answer = await requestPosPairingCode(user, hostId, registerId, mode)\n setPairing({ ...answer, registerId, mode, ...(registerName ? { registerName } : {}) })\n } catch (error) {\n onError(error instanceof PosRequestError ? error.message : 'Could not make a code')\n }\n },\n [user, hostId, onError],\n )\n const close = useCallback(() => setPairing(null), [])\n\n const dialog = (\n <PosPairingDialog\n pairing={pairing}\n user={user}\n hostId={hostId}\n onClose={close}\n onModeChange={(mode) => {\n if (pairing) void open(pairing.registerId, mode, pairing.registerName)\n }}\n />\n )\n return { pairing, open, close, dialog }\n}\n\n/** The element id the POS registers card carries, for a link that lands on it. */\nexport const POS_REGISTERS_ANCHOR = 'pos-registers'\n\n/**\n * Where a register is created — the POS registers card, on Products → Point\n * of sale (`…/products/pos`, AGL-3713) — from the register page's own console\n * path (`…/pos`). Undefined when that path is not the register's.\n */\nexport function posRegistersSettingsHref(posBasePath: string | undefined): string | undefined {\n if (!posBasePath || !/\\/pos\\/?$/.test(posBasePath)) return undefined\n return `${posBasePath.replace(/\\/pos\\/?$/, '/products/pos')}#${POS_REGISTERS_ANCHOR}`\n}\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","ToggleButton","ToggleButtonGroup","Typography","useCallback","useState","CommerceModel","posDisplayCall","PosRequestError","PosKioskReadiness","requestPosPairingCode","user","hostId","registerId","mode","action","posPairingAddress","origin","window","location","TTL_MINUTES","Math","round","POS_DISPLAY_PAIRING_TTL_MS","PosPairingDialog","pairing","onModeChange","onClose","open","Boolean","maxWidth","fullWidth","registerName","component","variant","color","sx","display","flexDirection","gap","exclusive","size","value","onChange","_event","next","mt","wordBreak","textAlign","letterSpacing","code","onClick","displayName","usePosPairing","onError","setPairing","answer","error","message","close","dialog","POS_REGISTERS_ANCHOR","posRegistersSettingsHref","posBasePath","test","undefined","replace"],"mappings":"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"}
@@ -25,6 +25,7 @@ import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, M
25
25
  import { collection, limit, query } from "firebase/firestore";
26
26
  import { useCallback, useEffect, useState } from "react";
27
27
  import { posDisplayCall, posReadersCall, PosRequestError } from "./pos/pos-api.js";
28
+ import { POS_DEVICES_ANCHOR } from "./pos/pos-kiosk-readiness.component.js";
28
29
  import { POS_REGISTERS_ANCHOR, usePosPairing } from "./pos/pos-pairing.component.js";
29
30
  const EMPTY_ADDRESS = {
30
31
  line1: '',
@@ -176,7 +177,19 @@ const EMPTY_ADDRESS = {
176
177
  };
177
178
  const readers = (_ref1 = answer == null ? void 0 : answer.readers) != null ? _ref1 : [];
178
179
  const showReaders = Boolean(answer == null ? void 0 : answer.available);
180
+ /*
181
+ * Arrived by a kiosk's "Open POS devices" (AGL-3982): this card renders
182
+ * after the page has already tried to scroll to the anchor, so it brings
183
+ * itself into view.
184
+ */ useEffect(()=>{
185
+ var _document_getElementById_scrollIntoView, _document_getElementById;
186
+ if (typeof window === 'undefined' || window.location.hash !== `#${POS_DEVICES_ANCHOR}`) return;
187
+ (_document_getElementById = document.getElementById(POS_DEVICES_ANCHOR)) == null ? void 0 : (_document_getElementById_scrollIntoView = _document_getElementById.scrollIntoView) == null ? void 0 : _document_getElementById_scrollIntoView.call(_document_getElementById, {
188
+ block: 'start'
189
+ });
190
+ }, []);
179
191
  return /*#__PURE__*/ _jsxs(CardDisplay, {
192
+ id: POS_DEVICES_ANCHOR,
180
193
  header: 'POS devices',
181
194
  help: pluginDocsHelp('pos', {
182
195
  anchor: '#card-readers'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-devices-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 * as Aglyn from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, useFirestoreCollection, useOrgPlan, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { posDisplayCall, posReadersCall, PosRequestError } from './pos/pos-api'\nimport { POS_REGISTERS_ANCHOR, usePosPairing } from './pos/pos-pairing.component'\n\ninterface ReaderRow {\n id: string\n label: string\n registerId: string | null\n deviceType: string | null\n serialNumber: string | null\n status: string\n livemode: boolean\n}\n\ninterface ReadersAnswer {\n available: boolean\n testMode: boolean\n locationReady: boolean\n readers: ReaderRow[]\n}\n\ninterface DisplayRow {\n id: string\n label: string\n createdAtMs: number\n lastSeenAtMs: number\n}\n\nexport interface PosDevicesCardProps {\n hostId: string\n /**\n * Whether the viewer may work this site's register: `managePos`, as the\n * shell resolved it, on a site role that may run it (admin or editor).\n * Pairing a tablet is refused by the server otherwise, so the card says why\n * rather than offering a button that fails. Absent reads as not allowed.\n */\n canManagePos?: boolean\n}\n\nconst EMPTY_ADDRESS = { line1: '', line2: '', city: '', state: '', postalCode: '', country: 'US' }\n\n/**\n * The register's hardware (AGL-3607, AGL-3608): Stripe Terminal card readers\n * — registered with the code a reader shows, bound to a register, renamed,\n * removed — and the customer displays paired to each register, which can be\n * signed out. Readers appear only where card readers are offered at all.\n *\n * Each register pairs a new customer display or self-service kiosk here,\n * through the same flow as the register's own Pair display (AGL-3712); a site\n * with no register is sent to the card that makes one.\n */\nexport function PosDevicesCard({ hostId, canManagePos = false }: PosDevicesCardProps) {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Not \"no registers\" until the listen has answered.\n const noRegisters = registerDocs !== undefined && registerDocs.length === 0\n const registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const [answer, setAnswer] = useState<ReadersAnswer | null>(null)\n const [displays, setDisplays] = useState<Record<string, DisplayRow[]>>({})\n const [adding, setAdding] = useState(false)\n const [code, setCode] = useState('')\n const [label, setLabel] = useState('')\n const [registerId, setRegisterId] = useState('')\n const [address, setAddress] = useState(EMPTY_ADDRESS)\n const [busy, setBusy] = useState(false)\n const [renaming, setRenaming] = useState<{ id: string; label: string } | null>(null)\n const { org, ready: planReady } = useOrgPlan(hostId)\n const posEntitled = planReady && Aglyn.checkEntitlement(org as any, 'pos')\n\n const notify = useCallback(\n (error: unknown, fallback: string) =>\n enqueueSnackbar(error instanceof PosRequestError ? error.message : fallback, {\n variant: 'warning',\n persist: false,\n }),\n [enqueueSnackbar],\n )\n\n const onPairingError = useCallback(\n (message: string) => enqueueSnackbar(message, { variant: 'warning', persist: false }),\n [enqueueSnackbar],\n )\n const pairingFlow = usePosPairing({ user, hostId, onError: onPairingError })\n /** Why pairing is not offered to this viewer, or null when it is. */\n const pairBlocked = !planReady\n ? 'Checking your plan…'\n : !posEntitled\n ? 'Point of Sale is not on your plan. Upgrade in Billing to pair displays and kiosks.'\n : !canManagePos\n ? 'Only an admin or editor with the \"Use the point of sale\" permission can pair displays and kiosks.'\n : null\n\n const load = useCallback(\n async (refresh = false) => {\n if (!user) return\n try {\n setAnswer(await posReadersCall<ReadersAnswer>(user, { action: 'list', hostId, refresh }))\n } catch {\n setAnswer(null)\n }\n },\n [user, hostId],\n )\n const uid = user?.uid ?? ''\n useEffect(() => {\n void load(true)\n // Keyed on the person, not the session object (see the register page).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId])\n\n const registerIds = registers.map((register: any) => register.$id).join(',')\n useEffect(() => {\n if (!user || !registerIds) return\n let active = true\n void Promise.all(\n registerIds.split(',').map(async (id) => {\n const result = await posDisplayCall<{ displays: DisplayRow[] }>(user, {\n action: 'displays',\n hostId,\n registerId: id,\n }).catch(() => ({ displays: [] as DisplayRow[] }))\n return [id, result.displays] as const\n }),\n ).then((entries) => {\n if (active) setDisplays(Object.fromEntries(entries))\n })\n return () => {\n active = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId, registerIds])\n\n const registerName = (id: string | null) =>\n registers.find((register: any) => register.$id === id)?.name ?? null\n\n const addReader = async () => {\n if (!user) return\n setBusy(true)\n try {\n await posReadersCall(user, {\n action: 'register',\n hostId,\n registrationCode: code.trim(),\n label: label.trim(),\n ...(registerId ? { registerId } : {}),\n ...(answer?.locationReady ? {} : { address }),\n })\n setAdding(false)\n setCode('')\n setLabel('')\n setRegisterId('')\n await load()\n } catch (error) {\n notify(error, 'The reader could not be added')\n } finally {\n setBusy(false)\n }\n }\n\n const readers = answer?.readers ?? []\n const showReaders = Boolean(answer?.available)\n\n return (\n <CardDisplay\n header={'POS devices'}\n help={pluginDocsHelp('pos', { anchor: '#card-readers' })}\n HeaderProps={\n showReaders || noRegisters\n ? {\n action: (\n <Stack direction=\"row\" spacing={1}>\n {noRegisters ? (\n <Button size=\"small\" href={`#${POS_REGISTERS_ANCHOR}`}>\n {'Add a register'}\n </Button>\n ) : null}\n {showReaders ? (\n <Button size=\"small\" onClick={() => setAdding(true)}>\n {'Add card reader'}\n </Button>\n ) : null}\n </Stack>\n ),\n }\n : undefined\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {showReaders ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Card readers'}</Typography>\n {readers.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {answer?.testMode\n ? 'No card readers yet. In test mode, add one with the code \"simulated-wpe\" to try a simulated reader.'\n : 'No card readers yet. Add a Stripe Reader S700 or BBPOS WisePOS E with the code it shows on screen.'}\n </Typography>\n ) : (\n readers.map((reader) => (\n <Stack\n key={reader.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1, minWidth: 160 }}>\n {reader.label}\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {` · ${registerName(reader.registerId) ?? 'Any register'}` +\n (reader.livemode ? '' : ' · test')}\n </Typography>\n </Typography>\n <Chip\n size=\"small\"\n label={reader.status === 'online' ? 'Online' : 'Offline'}\n color={reader.status === 'online' ? 'success' : 'default'}\n />\n <TextField\n select\n size=\"small\"\n label=\"Register\"\n value={reader.registerId ?? ''}\n onChange={async (event) => {\n if (!user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: reader.id,\n registerId: event.target.value,\n })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be updated')\n }\n }}\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n <Button\n size=\"small\"\n onClick={() => setRenaming({ id: reader.id, label: reader.label })}\n >\n {'Rename'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n const confirmed = await confirm({\n title: 'Remove this card reader?',\n description:\n `\"${reader.label}\" is removed from your account and stops taking payments. ` +\n 'To use it again, register it with a new code.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed || !user) return\n try {\n await posReadersCall(user, { action: 'remove', hostId, readerId: reader.id })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be removed')\n }\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n </Stack>\n ) : null}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Customer displays and kiosks'}</Typography>\n {noRegisters ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'A display or kiosk pairs to a register. Add a register first.'}\n </Typography>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {pairBlocked ??\n 'Pair display gives you a code. Open the address it shows on the tablet and enter the code.'}\n </Typography>\n {registers.map((register: any) => (\n <Stack key={register.$id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1, fontWeight: 'fontWeightMedium' }} noWrap>\n {register.name}\n </Typography>\n <Button\n size=\"small\"\n disabled={Boolean(pairBlocked)}\n onClick={() => void pairingFlow.open(register.$id, 'display', register.name)}\n aria-label={`Pair display to ${register.name}`}\n >\n {'Pair display'}\n </Button>\n </Stack>\n {(displays[register.$id] ?? []).map((display) => (\n <Stack key={`${register.$id}:${display.id}`} direction=\"row\" spacing={1} sx={{ alignItems: 'center', pl: 2 }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {display.label}\n </Typography>\n <Chip\n size=\"small\"\n label={Date.now() - display.lastSeenAtMs < 120_000 ? 'Connected' : 'Not connected'}\n color={Date.now() - display.lastSeenAtMs < 120_000 ? 'success' : 'default'}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n if (!user) return\n try {\n await posDisplayCall(user, {\n action: 'revoke',\n hostId,\n registerId: register.$id,\n displayId: display.id,\n })\n setDisplays((prev) => ({\n ...prev,\n [register.$id]: (prev[register.$id] ?? []).filter((entry) => entry.id !== display.id),\n }))\n } catch (error) {\n notify(error, 'The display could not be signed out')\n }\n }}\n >\n {'Sign out'}\n </Button>\n </Stack>\n ))}\n </Stack>\n ))}\n </>\n )}\n </Stack>\n </Stack>\n\n <Dialog open={Boolean(renaming)} onClose={() => setRenaming(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'Rename card reader'}</DialogTitle>\n <DialogContent>\n <TextField\n label=\"Name\"\n value={renaming?.label ?? ''}\n onChange={(event) => setRenaming((prev) => (prev ? { ...prev, label: event.target.value } : prev))}\n fullWidth\n autoFocus\n sx={{ mt: 1 }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setRenaming(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={!renaming?.label.trim()}\n onClick={async () => {\n if (!renaming || !user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: renaming.id,\n label: renaming.label.trim(),\n })\n setRenaming(null)\n await load()\n } catch (error) {\n notify(error, 'The reader could not be renamed')\n }\n }}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={adding} onClose={() => setAdding(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{'Add a card reader'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {'On the reader, open Settings and choose Generate pairing code, then enter the code it shows.'}\n </Typography>\n <TextField\n label=\"Registration code\"\n value={code}\n onChange={(event) => setCode(event.target.value)}\n autoFocus\n slotProps={{ htmlInput: { autoComplete: 'off' } }}\n />\n <TextField label=\"Name\" placeholder=\"Front counter reader\" value={label} onChange={(event) => setLabel(event.target.value)} />\n <TextField select label=\"Register\" value={registerId} onChange={(event) => setRegisterId(event.target.value)}>\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n {answer && !answer.locationReady ? (\n <Box sx={{ display: 'grid', gap: 1.5, gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' } }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ gridColumn: '1 / -1' }}>\n {'Where the reader is used. Stripe sets the reader up for this country.'}\n </Typography>\n <TextField label=\"Street\" value={address.line1} onChange={(event) => setAddress({ ...address, line1: event.target.value })} sx={{ gridColumn: '1 / -1' }} />\n <TextField label=\"City\" value={address.city} onChange={(event) => setAddress({ ...address, city: event.target.value })} />\n <TextField label=\"State\" value={address.state} onChange={(event) => setAddress({ ...address, state: event.target.value })} />\n <TextField label=\"Postal code\" value={address.postalCode} onChange={(event) => setAddress({ ...address, postalCode: event.target.value })} />\n <TextField\n label=\"Country\"\n value={address.country}\n onChange={(event) => setAddress({ ...address, country: event.target.value.toUpperCase().slice(0, 2) })}\n />\n </Box>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdding(false)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={busy || !code.trim()} onClick={() => void addReader()}>\n {'Add reader'}\n </Button>\n </DialogActions>\n </Dialog>\n {pairingFlow.dialog}\n </CardDisplay>\n )\n}\nPosDevicesCard.displayName = 'PosDevicesCard'\n\nexport default PosDevicesCard\n"],"names":["Aglyn","pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","useFirestore","useFirestoreCollection","useOrgPlan","useUser","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","query","useCallback","useEffect","useState","posDisplayCall","posReadersCall","PosRequestError","POS_REGISTERS_ANCHOR","usePosPairing","EMPTY_ADDRESS","line1","line2","city","state","postalCode","country","PosDevicesCard","hostId","canManagePos","firestore","data","user","enqueueSnackbar","confirm","registerDocs","idField","noRegisters","undefined","length","registers","sort","a","b","String","name","localeCompare","answer","setAnswer","displays","setDisplays","adding","setAdding","code","setCode","label","setLabel","registerId","setRegisterId","address","setAddress","busy","setBusy","renaming","setRenaming","org","ready","planReady","posEntitled","checkEntitlement","notify","error","fallback","message","variant","persist","onPairingError","pairingFlow","onError","pairBlocked","load","refresh","action","uid","registerIds","map","register","$id","join","active","Promise","all","split","id","result","catch","then","entries","Object","fromEntries","registerName","find","addReader","registrationCode","trim","locationReady","readers","showReaders","Boolean","available","header","help","anchor","HeaderProps","direction","spacing","size","href","onClick","contentGutterX","contentGutterY","color","testMode","reader","sx","alignItems","flexWrap","rowGap","flex","minWidth","component","livemode","status","select","value","onChange","event","readerId","target","confirmed","title","description","confirmationText","confirmationButtonProps","fontWeight","noWrap","disabled","open","aria-label","display","pl","Date","now","lastSeenAtMs","displayId","prev","filter","entry","onClose","maxWidth","fullWidth","autoFocus","mt","flexDirection","gap","slotProps","htmlInput","autoComplete","placeholder","gridTemplateColumns","xs","sm","gridColumn","toUpperCase","slice","dialog","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-devices-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 * as Aglyn from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, useFirestoreCollection, useOrgPlan, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { posDisplayCall, posReadersCall, PosRequestError } from './pos/pos-api'\nimport { POS_DEVICES_ANCHOR } from './pos/pos-kiosk-readiness.component'\nimport { POS_REGISTERS_ANCHOR, usePosPairing } from './pos/pos-pairing.component'\n\ninterface ReaderRow {\n id: string\n label: string\n registerId: string | null\n deviceType: string | null\n serialNumber: string | null\n status: string\n livemode: boolean\n}\n\ninterface ReadersAnswer {\n available: boolean\n testMode: boolean\n locationReady: boolean\n readers: ReaderRow[]\n}\n\ninterface DisplayRow {\n id: string\n label: string\n createdAtMs: number\n lastSeenAtMs: number\n}\n\nexport interface PosDevicesCardProps {\n hostId: string\n /**\n * Whether the viewer may work this site's register: `managePos`, as the\n * shell resolved it, on a site role that may run it (admin or editor).\n * Pairing a tablet is refused by the server otherwise, so the card says why\n * rather than offering a button that fails. Absent reads as not allowed.\n */\n canManagePos?: boolean\n}\n\nconst EMPTY_ADDRESS = { line1: '', line2: '', city: '', state: '', postalCode: '', country: 'US' }\n\n/**\n * The register's hardware (AGL-3607, AGL-3608): Stripe Terminal card readers\n * — registered with the code a reader shows, bound to a register, renamed,\n * removed — and the customer displays paired to each register, which can be\n * signed out. Readers appear only where card readers are offered at all.\n *\n * Each register pairs a new customer display or self-service kiosk here,\n * through the same flow as the register's own Pair display (AGL-3712); a site\n * with no register is sent to the card that makes one.\n */\nexport function PosDevicesCard({ hostId, canManagePos = false }: PosDevicesCardProps) {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Not \"no registers\" until the listen has answered.\n const noRegisters = registerDocs !== undefined && registerDocs.length === 0\n const registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const [answer, setAnswer] = useState<ReadersAnswer | null>(null)\n const [displays, setDisplays] = useState<Record<string, DisplayRow[]>>({})\n const [adding, setAdding] = useState(false)\n const [code, setCode] = useState('')\n const [label, setLabel] = useState('')\n const [registerId, setRegisterId] = useState('')\n const [address, setAddress] = useState(EMPTY_ADDRESS)\n const [busy, setBusy] = useState(false)\n const [renaming, setRenaming] = useState<{ id: string; label: string } | null>(null)\n const { org, ready: planReady } = useOrgPlan(hostId)\n const posEntitled = planReady && Aglyn.checkEntitlement(org as any, 'pos')\n\n const notify = useCallback(\n (error: unknown, fallback: string) =>\n enqueueSnackbar(error instanceof PosRequestError ? error.message : fallback, {\n variant: 'warning',\n persist: false,\n }),\n [enqueueSnackbar],\n )\n\n const onPairingError = useCallback(\n (message: string) => enqueueSnackbar(message, { variant: 'warning', persist: false }),\n [enqueueSnackbar],\n )\n const pairingFlow = usePosPairing({ user, hostId, onError: onPairingError })\n /** Why pairing is not offered to this viewer, or null when it is. */\n const pairBlocked = !planReady\n ? 'Checking your plan…'\n : !posEntitled\n ? 'Point of Sale is not on your plan. Upgrade in Billing to pair displays and kiosks.'\n : !canManagePos\n ? 'Only an admin or editor with the \"Use the point of sale\" permission can pair displays and kiosks.'\n : null\n\n const load = useCallback(\n async (refresh = false) => {\n if (!user) return\n try {\n setAnswer(await posReadersCall<ReadersAnswer>(user, { action: 'list', hostId, refresh }))\n } catch {\n setAnswer(null)\n }\n },\n [user, hostId],\n )\n const uid = user?.uid ?? ''\n useEffect(() => {\n void load(true)\n // Keyed on the person, not the session object (see the register page).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId])\n\n const registerIds = registers.map((register: any) => register.$id).join(',')\n useEffect(() => {\n if (!user || !registerIds) return\n let active = true\n void Promise.all(\n registerIds.split(',').map(async (id) => {\n const result = await posDisplayCall<{ displays: DisplayRow[] }>(user, {\n action: 'displays',\n hostId,\n registerId: id,\n }).catch(() => ({ displays: [] as DisplayRow[] }))\n return [id, result.displays] as const\n }),\n ).then((entries) => {\n if (active) setDisplays(Object.fromEntries(entries))\n })\n return () => {\n active = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId, registerIds])\n\n const registerName = (id: string | null) =>\n registers.find((register: any) => register.$id === id)?.name ?? null\n\n const addReader = async () => {\n if (!user) return\n setBusy(true)\n try {\n await posReadersCall(user, {\n action: 'register',\n hostId,\n registrationCode: code.trim(),\n label: label.trim(),\n ...(registerId ? { registerId } : {}),\n ...(answer?.locationReady ? {} : { address }),\n })\n setAdding(false)\n setCode('')\n setLabel('')\n setRegisterId('')\n await load()\n } catch (error) {\n notify(error, 'The reader could not be added')\n } finally {\n setBusy(false)\n }\n }\n\n const readers = answer?.readers ?? []\n const showReaders = Boolean(answer?.available)\n\n /*\n * Arrived by a kiosk's \"Open POS devices\" (AGL-3982): this card renders\n * after the page has already tried to scroll to the anchor, so it brings\n * itself into view.\n */\n useEffect(() => {\n if (typeof window === 'undefined' || window.location.hash !== `#${POS_DEVICES_ANCHOR}`) return\n document.getElementById(POS_DEVICES_ANCHOR)?.scrollIntoView?.({ block: 'start' })\n }, [])\n\n return (\n <CardDisplay\n id={POS_DEVICES_ANCHOR}\n header={'POS devices'}\n help={pluginDocsHelp('pos', { anchor: '#card-readers' })}\n HeaderProps={\n showReaders || noRegisters\n ? {\n action: (\n <Stack direction=\"row\" spacing={1}>\n {noRegisters ? (\n <Button size=\"small\" href={`#${POS_REGISTERS_ANCHOR}`}>\n {'Add a register'}\n </Button>\n ) : null}\n {showReaders ? (\n <Button size=\"small\" onClick={() => setAdding(true)}>\n {'Add card reader'}\n </Button>\n ) : null}\n </Stack>\n ),\n }\n : undefined\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {showReaders ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Card readers'}</Typography>\n {readers.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {answer?.testMode\n ? 'No card readers yet. In test mode, add one with the code \"simulated-wpe\" to try a simulated reader.'\n : 'No card readers yet. Add a Stripe Reader S700 or BBPOS WisePOS E with the code it shows on screen.'}\n </Typography>\n ) : (\n readers.map((reader) => (\n <Stack\n key={reader.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1, minWidth: 160 }}>\n {reader.label}\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {` · ${registerName(reader.registerId) ?? 'Any register'}` +\n (reader.livemode ? '' : ' · test')}\n </Typography>\n </Typography>\n <Chip\n size=\"small\"\n label={reader.status === 'online' ? 'Online' : 'Offline'}\n color={reader.status === 'online' ? 'success' : 'default'}\n />\n <TextField\n select\n size=\"small\"\n label=\"Register\"\n value={reader.registerId ?? ''}\n onChange={async (event) => {\n if (!user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: reader.id,\n registerId: event.target.value,\n })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be updated')\n }\n }}\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n <Button\n size=\"small\"\n onClick={() => setRenaming({ id: reader.id, label: reader.label })}\n >\n {'Rename'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n const confirmed = await confirm({\n title: 'Remove this card reader?',\n description:\n `\"${reader.label}\" is removed from your account and stops taking payments. ` +\n 'To use it again, register it with a new code.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed || !user) return\n try {\n await posReadersCall(user, { action: 'remove', hostId, readerId: reader.id })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be removed')\n }\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n </Stack>\n ) : null}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Customer displays and kiosks'}</Typography>\n {noRegisters ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'A display or kiosk pairs to a register. Add a register first.'}\n </Typography>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {pairBlocked ??\n 'Pair display gives you a code. Open the address it shows on the tablet and enter the code.'}\n </Typography>\n {registers.map((register: any) => (\n <Stack key={register.$id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1, fontWeight: 'fontWeightMedium' }} noWrap>\n {register.name}\n </Typography>\n <Button\n size=\"small\"\n disabled={Boolean(pairBlocked)}\n onClick={() => void pairingFlow.open(register.$id, 'display', register.name)}\n aria-label={`Pair display to ${register.name}`}\n >\n {'Pair display'}\n </Button>\n </Stack>\n {(displays[register.$id] ?? []).map((display) => (\n <Stack key={`${register.$id}:${display.id}`} direction=\"row\" spacing={1} sx={{ alignItems: 'center', pl: 2 }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {display.label}\n </Typography>\n <Chip\n size=\"small\"\n label={Date.now() - display.lastSeenAtMs < 120_000 ? 'Connected' : 'Not connected'}\n color={Date.now() - display.lastSeenAtMs < 120_000 ? 'success' : 'default'}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n if (!user) return\n try {\n await posDisplayCall(user, {\n action: 'revoke',\n hostId,\n registerId: register.$id,\n displayId: display.id,\n })\n setDisplays((prev) => ({\n ...prev,\n [register.$id]: (prev[register.$id] ?? []).filter((entry) => entry.id !== display.id),\n }))\n } catch (error) {\n notify(error, 'The display could not be signed out')\n }\n }}\n >\n {'Sign out'}\n </Button>\n </Stack>\n ))}\n </Stack>\n ))}\n </>\n )}\n </Stack>\n </Stack>\n\n <Dialog open={Boolean(renaming)} onClose={() => setRenaming(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'Rename card reader'}</DialogTitle>\n <DialogContent>\n <TextField\n label=\"Name\"\n value={renaming?.label ?? ''}\n onChange={(event) => setRenaming((prev) => (prev ? { ...prev, label: event.target.value } : prev))}\n fullWidth\n autoFocus\n sx={{ mt: 1 }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setRenaming(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={!renaming?.label.trim()}\n onClick={async () => {\n if (!renaming || !user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: renaming.id,\n label: renaming.label.trim(),\n })\n setRenaming(null)\n await load()\n } catch (error) {\n notify(error, 'The reader could not be renamed')\n }\n }}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={adding} onClose={() => setAdding(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{'Add a card reader'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {'On the reader, open Settings and choose Generate pairing code, then enter the code it shows.'}\n </Typography>\n <TextField\n label=\"Registration code\"\n value={code}\n onChange={(event) => setCode(event.target.value)}\n autoFocus\n slotProps={{ htmlInput: { autoComplete: 'off' } }}\n />\n <TextField label=\"Name\" placeholder=\"Front counter reader\" value={label} onChange={(event) => setLabel(event.target.value)} />\n <TextField select label=\"Register\" value={registerId} onChange={(event) => setRegisterId(event.target.value)}>\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n {answer && !answer.locationReady ? (\n <Box sx={{ display: 'grid', gap: 1.5, gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' } }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ gridColumn: '1 / -1' }}>\n {'Where the reader is used. Stripe sets the reader up for this country.'}\n </Typography>\n <TextField label=\"Street\" value={address.line1} onChange={(event) => setAddress({ ...address, line1: event.target.value })} sx={{ gridColumn: '1 / -1' }} />\n <TextField label=\"City\" value={address.city} onChange={(event) => setAddress({ ...address, city: event.target.value })} />\n <TextField label=\"State\" value={address.state} onChange={(event) => setAddress({ ...address, state: event.target.value })} />\n <TextField label=\"Postal code\" value={address.postalCode} onChange={(event) => setAddress({ ...address, postalCode: event.target.value })} />\n <TextField\n label=\"Country\"\n value={address.country}\n onChange={(event) => setAddress({ ...address, country: event.target.value.toUpperCase().slice(0, 2) })}\n />\n </Box>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdding(false)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={busy || !code.trim()} onClick={() => void addReader()}>\n {'Add reader'}\n </Button>\n </DialogActions>\n </Dialog>\n {pairingFlow.dialog}\n </CardDisplay>\n )\n}\nPosDevicesCard.displayName = 'PosDevicesCard'\n\nexport default PosDevicesCard\n"],"names":["Aglyn","pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","useFirestore","useFirestoreCollection","useOrgPlan","useUser","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","query","useCallback","useEffect","useState","posDisplayCall","posReadersCall","PosRequestError","POS_DEVICES_ANCHOR","POS_REGISTERS_ANCHOR","usePosPairing","EMPTY_ADDRESS","line1","line2","city","state","postalCode","country","PosDevicesCard","hostId","canManagePos","firestore","data","user","enqueueSnackbar","confirm","registerDocs","idField","noRegisters","undefined","length","registers","sort","a","b","String","name","localeCompare","answer","setAnswer","displays","setDisplays","adding","setAdding","code","setCode","label","setLabel","registerId","setRegisterId","address","setAddress","busy","setBusy","renaming","setRenaming","org","ready","planReady","posEntitled","checkEntitlement","notify","error","fallback","message","variant","persist","onPairingError","pairingFlow","onError","pairBlocked","load","refresh","action","uid","registerIds","map","register","$id","join","active","Promise","all","split","id","result","catch","then","entries","Object","fromEntries","registerName","find","addReader","registrationCode","trim","locationReady","readers","showReaders","Boolean","available","document","window","location","hash","getElementById","scrollIntoView","block","header","help","anchor","HeaderProps","direction","spacing","size","href","onClick","contentGutterX","contentGutterY","color","testMode","reader","sx","alignItems","flexWrap","rowGap","flex","minWidth","component","livemode","status","select","value","onChange","event","readerId","target","confirmed","title","description","confirmationText","confirmationButtonProps","fontWeight","noWrap","disabled","open","aria-label","display","pl","Date","now","lastSeenAtMs","displayId","prev","filter","entry","onClose","maxWidth","fullWidth","autoFocus","mt","flexDirection","gap","slotProps","htmlInput","autoComplete","placeholder","gridTemplateColumns","xs","sm","gridColumn","toUpperCase","slice","dialog","displayName"],"mappings":"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"}