@aglyn/plugins-commerce 1.0.0-beta.249 → 1.0.0-beta.252

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 (52) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/commerce-console-page.d.ts +1 -1
  3. package/src/lib/components/commerce-console-page.js +139 -175
  4. package/src/lib/components/commerce-console-page.js.map +1 -1
  5. package/src/lib/components/commerce-console-sections.d.ts +30 -2
  6. package/src/lib/components/commerce-console-sections.js +82 -7
  7. package/src/lib/components/commerce-console-sections.js.map +1 -1
  8. package/src/lib/components/console/discounts-card.component.js +36 -3
  9. package/src/lib/components/console/discounts-card.component.js.map +1 -1
  10. package/src/lib/components/console/host-coupons-card.component.js +1 -1
  11. package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
  12. package/src/lib/components/console/pos/pos-pairing.component.d.ts +59 -0
  13. package/src/lib/components/console/pos/pos-pairing.component.js +174 -0
  14. package/src/lib/components/console/pos/pos-pairing.component.js.map +1 -0
  15. package/src/lib/components/console/pos/use-pos-display.d.ts +0 -8
  16. package/src/lib/components/console/pos/use-pos-display.js +2 -16
  17. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  18. package/src/lib/components/console/pos-devices-card.component.d.ts +12 -1
  19. package/src/lib/components/console/pos-devices-card.component.js +132 -59
  20. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  21. package/src/lib/components/console/pos-page.component.d.ts +1 -1
  22. package/src/lib/components/console/pos-page.component.js +37 -86
  23. package/src/lib/components/console/pos-page.component.js.map +1 -1
  24. package/src/lib/components/console/registers-card.component.js +13 -0
  25. package/src/lib/components/console/registers-card.component.js.map +1 -1
  26. package/src/lib/components/console/shipping-settings-card.component.js +6 -11
  27. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  28. package/src/lib/components/console/store-zones.d.ts +15 -0
  29. package/src/lib/components/console/store-zones.js +36 -4
  30. package/src/lib/components/console/store-zones.js.map +1 -1
  31. package/src/lib/components/console/suppliers-card.component.js +2 -2
  32. package/src/lib/components/console/suppliers-card.component.js.map +1 -1
  33. package/src/lib/model/commerce-record-routes.js +10 -3
  34. package/src/lib/model/commerce-record-routes.js.map +1 -1
  35. package/src/lib/model/commerce-shipping.d.ts +16 -0
  36. package/src/lib/model/commerce-shipping.js +21 -0
  37. package/src/lib/model/commerce-shipping.js.map +1 -1
  38. package/src/lib/model/commerce-tax-decision.js +2 -2
  39. package/src/lib/model/commerce-tax-decision.js.map +1 -1
  40. package/src/lib/plugin.js +4 -1
  41. package/src/lib/plugin.js.map +1 -1
  42. package/src/lib/server/billing-webhook.js +13 -0
  43. package/src/lib/server/billing-webhook.js.map +1 -1
  44. package/src/lib/server/order-webhooks.js +1 -1
  45. package/src/lib/server/order-webhooks.js.map +1 -1
  46. package/src/lib/server/shippingeasy.js +1 -1
  47. package/src/lib/server/shippingeasy.js.map +1 -1
  48. package/src/lib/server/store-setup-ai-capability.js +10 -2
  49. package/src/lib/server/store-setup-ai-capability.js.map +1 -1
  50. package/src/lib/server/supplier-routing.d.ts +86 -0
  51. package/src/lib/server/supplier-routing.js +175 -0
  52. package/src/lib/server/supplier-routing.js.map +1 -0
@@ -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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, useFirestoreCollection, 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'\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\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 */\nexport function PosDevicesCard({ hostId }: 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 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\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 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\n ? {\n action: (\n <Button size=\"small\" onClick={() => setAdding(true)}>\n {'Add card reader'}\n </Button>\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 <Typography variant=\"body2\" color=\"text.secondary\">\n {'Pair a tablet from the register: tap Pair display, choose Customer display or Self-service kiosk, and enter the code on the tablet.'}\n </Typography>\n {registers.flatMap((register: any) =>\n (displays[register.$id] ?? []).map((display) => (\n <Stack key={`${register.$id}:${display.id}`} direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {`${display.label} · ${register.name}`}\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 )}\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 </CardDisplay>\n )\n}\nPosDevicesCard.displayName = 'PosDevicesCard'\n\nexport default PosDevicesCard\n"],"names":["pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","useFirestore","useFirestoreCollection","useUser","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","query","useCallback","useEffect","useState","posDisplayCall","posReadersCall","PosRequestError","EMPTY_ADDRESS","line1","line2","city","state","postalCode","country","PosDevicesCard","hostId","firestore","data","user","enqueueSnackbar","confirm","registerDocs","idField","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","notify","error","fallback","message","variant","persist","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","size","onClick","undefined","contentGutterX","contentGutterY","spacing","length","color","testMode","reader","direction","sx","alignItems","flexWrap","rowGap","flex","minWidth","component","livemode","status","select","value","onChange","event","readerId","target","confirmed","title","description","confirmationText","confirmationButtonProps","flatMap","display","Date","now","lastSeenAtMs","displayId","prev","filter","entry","open","onClose","maxWidth","fullWidth","autoFocus","mt","disabled","flexDirection","gap","slotProps","htmlInput","autoComplete","placeholder","gridTemplateColumns","xs","sm","gridColumn","toUpperCase","slice","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SAASA,cAAc,QAAQ,eAAc;AAC7C,SAASC,WAAW,EAAEC,sBAAsB,QAAQ,uBAAsB;AAC1E,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,YAAY,EAAEC,sBAAsB,EAAEC,OAAO,QAAQ,iCAAgC;AAC9F,SACEC,GAAG,EACHC,MAAM,EACNC,IAAI,EACJC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,QAAQ,EACRC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,UAAU,EAAEC,KAAK,EAAEC,KAAK,QAAQ,qBAAoB;AAC7D,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AACxD,SAASC,cAAc,EAAEC,cAAc,EAAEC,eAAe,QAAQ,mBAAe;AA8B/E,MAAMC,gBAAgB;IAAEC,OAAO;IAAIC,OAAO;IAAIC,MAAM;IAAIC,OAAO;IAAIC,YAAY;IAAIC,SAAS;AAAK;AAEjG;;;;;CAKC,GACD,OAAO,SAASC,eAAe,EAAEC,MAAM,EAAuB;;IAC5D,MAAMC,YAAYhC;IAClB,MAAM,EAAEiC,MAAMC,IAAI,EAAE,GAAGhC;IACvB,MAAM,EAAEiC,eAAe,EAAE,GAAGpC;IAC5B,MAAM,EAAEqC,OAAO,EAAE,GAAGtC;IACpB,MAAM,EAAEmC,MAAMI,YAAY,EAAE,GAAGpC,uBAC7B,IAAMe,MAAMF,WAAWkB,WAAW,SAASD,QAAQ,cAAchB,MAAM,MACvE;QAACiB;QAAWD;KAAO,EACnB;QAAEO,SAAS;IAAM;IAEnB,MAAMC,YAAY;WAAKF,uBAAAA,eAAgB,EAAE;KAAE,CAACG,IAAI,CAAC,CAACC,GAAQC;YACjDD,SAAmCC;eAA1CC,QAAOF,UAAAA,EAAEG,IAAI,YAANH,UAAU,IAAII,aAAa,CAACF,QAAOD,UAAAA,EAAEE,IAAI,YAANF,UAAU;;IAEtD,MAAM,CAACI,QAAQC,UAAU,GAAG5B,SAA+B;IAC3D,MAAM,CAAC6B,UAAUC,YAAY,GAAG9B,SAAuC,CAAC;IACxE,MAAM,CAAC+B,QAAQC,UAAU,GAAGhC,SAAS;IACrC,MAAM,CAACiC,MAAMC,QAAQ,GAAGlC,SAAS;IACjC,MAAM,CAACmC,OAAOC,SAAS,GAAGpC,SAAS;IACnC,MAAM,CAACqC,YAAYC,cAAc,GAAGtC,SAAS;IAC7C,MAAM,CAACuC,SAASC,WAAW,GAAGxC,SAASI;IACvC,MAAM,CAACqC,MAAMC,QAAQ,GAAG1C,SAAS;IACjC,MAAM,CAAC2C,UAAUC,YAAY,GAAG5C,SAA+C;IAE/E,MAAM6C,SAAS/C,YACb,CAACgD,OAAgBC,WACf/B,gBAAgB8B,iBAAiB3C,kBAAkB2C,MAAME,OAAO,GAAGD,UAAU;YAC3EE,SAAS;YACTC,SAAS;QACX,IACF;QAAClC;KAAgB;IAGnB,MAAMmC,OAAOrD,YACX,OAAOsD,UAAU,KAAK;QACpB,IAAI,CAACrC,MAAM;QACX,IAAI;YACFa,UAAU,MAAM1B,eAA8Ba,MAAM;gBAAEsC,QAAQ;gBAAQzC;gBAAQwC;YAAQ;QACxF,EAAE,eAAM;YACNxB,UAAU;QACZ;IACF,GACA;QAACb;QAAMH;KAAO;IAEhB,MAAM0C,cAAMvC,wBAAAA,KAAMuC,GAAG,mBAAI;IACzBvD,UAAU;QACR,KAAKoD,KAAK;IACV,uEAAuE;IACvE,uDAAuD;IACzD,GAAG;QAACG;QAAK1C;KAAO;IAEhB,MAAM2C,cAAcnC,UAAUoC,GAAG,CAAC,CAACC,WAAkBA,SAASC,GAAG,EAAEC,IAAI,CAAC;IACxE5D,UAAU;QACR,IAAI,CAACgB,QAAQ,CAACwC,aAAa;QAC3B,IAAIK,SAAS;QACb,KAAKC,QAAQC,GAAG,CACdP,YAAYQ,KAAK,CAAC,KAAKP,GAAG,CAAC,OAAOQ;YAChC,MAAMC,SAAS,MAAMhE,eAA2Cc,MAAM;gBACpEsC,QAAQ;gBACRzC;gBACAyB,YAAY2B;YACd,GAAGE,KAAK,CAAC,IAAO,CAAA;oBAAErC,UAAU,EAAE;gBAAiB,CAAA;YAC/C,OAAO;gBAACmC;gBAAIC,OAAOpC,QAAQ;aAAC;QAC9B,IACAsC,IAAI,CAAC,CAACC;YACN,IAAIR,QAAQ9B,YAAYuC,OAAOC,WAAW,CAACF;QAC7C;QACA,OAAO;YACLR,SAAS;QACX;IACA,uDAAuD;IACzD,GAAG;QAACN;QAAK1C;QAAQ2C;KAAY;IAE7B,MAAMgB,eAAe,CAACP;;YACpB5C;wBAAAA,kBAAAA,UAAUoD,IAAI,CAAC,CAACf,WAAkBA,SAASC,GAAG,KAAKM,wBAAnD5C,gBAAwDK,IAAI,mBAAI;;IAElE,MAAMgD,YAAY;QAChB,IAAI,CAAC1D,MAAM;QACX2B,QAAQ;QACR,IAAI;YACF,MAAMxC,eAAea,MAAM;gBACzBsC,QAAQ;gBACRzC;gBACA8D,kBAAkBzC,KAAK0C,IAAI;gBAC3BxC,OAAOA,MAAMwC,IAAI;eACbtC,aAAa;gBAAEA;YAAW,IAAI,CAAC,GAC/BV,CAAAA,0BAAAA,OAAQiD,aAAa,IAAG,CAAC,IAAI;gBAAErC;YAAQ;YAE7CP,UAAU;YACVE,QAAQ;YACRE,SAAS;YACTE,cAAc;YACd,MAAMa;QACR,EAAE,OAAOL,OAAO;YACdD,OAAOC,OAAO;QAChB,SAAU;YACRJ,QAAQ;QACV;IACF;IAEA,MAAMmC,mBAAUlD,0BAAAA,OAAQkD,OAAO,oBAAI,EAAE;IACrC,MAAMC,cAAcC,QAAQpD,0BAAAA,OAAQqD,SAAS;IAE7C,qBACE,MAACtG;QACCuG,QAAQ;QACRC,MAAMzG,eAAe,OAAO;YAAE0G,QAAQ;QAAgB;QACtDC,aACEN,cACI;YACEzB,sBACE,KAACpE;gBAAOoG,MAAK;gBAAQC,SAAS,IAAMtD,UAAU;0BAC3C;;QAGP,IACAuD;QAENC,cAAc;QACdC,cAAc;;0BAEd,MAACjG;gBAAMkG,SAAS;;oBACbZ,4BACC,MAACtF;wBAAMkG,SAAS;;0CACd,KAAChG;gCAAWuD,SAAQ;0CAAa;;4BAChC4B,QAAQc,MAAM,KAAK,kBAClB,KAACjG;gCAAWuD,SAAQ;gCAAQ2C,OAAM;0CAC/BjE,CAAAA,0BAAAA,OAAQkE,QAAQ,IACb,wGACA;iCAGNhB,QAAQrB,GAAG,CAAC,CAACsC;oCAUEvB,eAaFuB;qDAtBX,MAACtG;oCAECuG,WAAU;oCACVL,SAAS;oCACTM,IAAI;wCAAEC,YAAY;wCAAUC,UAAU;wCAAQC,QAAQ;oCAAE;;sDAExD,MAACzG;4CAAWuD,SAAQ;4CAAQ+C,IAAI;gDAAEI,MAAM;gDAAGC,UAAU;4CAAI;;gDACtDP,OAAO3D,KAAK;8DACb,KAACzC;oDAAW4G,WAAU;oDAAOrD,SAAQ;oDAAU2C,OAAM;8DAClD,CAAC,GAAG,GAAErB,gBAAAA,aAAauB,OAAOzD,UAAU,aAA9BkC,gBAAmC,gBAAgB,GACvDuB,CAAAA,OAAOS,QAAQ,GAAG,KAAK,SAAQ;;;;sDAGtC,KAACrH;4CACCmG,MAAK;4CACLlD,OAAO2D,OAAOU,MAAM,KAAK,WAAW,WAAW;4CAC/CZ,OAAOE,OAAOU,MAAM,KAAK,WAAW,YAAY;;sDAElD,MAAC/G;4CACCgH,MAAM;4CACNpB,MAAK;4CACLlD,OAAM;4CACNuE,KAAK,GAAEZ,qBAAAA,OAAOzD,UAAU,YAAjByD,qBAAqB;4CAC5Ba,UAAU,OAAOC;gDACf,IAAI,CAAC7F,MAAM;gDACX,IAAI;oDACF,MAAMb,eAAea,MAAM;wDACzBsC,QAAQ;wDACRzC;wDACAiG,UAAUf,OAAO9B,EAAE;wDACnB3B,YAAYuE,MAAME,MAAM,CAACJ,KAAK;oDAChC;oDACA,MAAMvD;gDACR,EAAE,OAAOL,OAAO;oDACdD,OAAOC,OAAO;gDAChB;4CACF;4CACAkD,IAAI;gDAAEK,UAAU;4CAAI;;8DAEpB,KAAC9G;oDAASmH,OAAM;8DAAI;;gDACnBtF,UAAUoC,GAAG,CAAC,CAACC,yBACd,KAAClE;wDAA4BmH,OAAOjD,SAASC,GAAG;kEAC7CD,SAAShC,IAAI;uDADDgC,SAASC,GAAG;;;sDAK/B,KAACzE;4CACCoG,MAAK;4CACLC,SAAS,IAAM1C,YAAY;oDAAEoB,IAAI8B,OAAO9B,EAAE;oDAAE7B,OAAO2D,OAAO3D,KAAK;gDAAC;sDAE/D;;sDAEH,KAAClD;4CACCoG,MAAK;4CACLO,OAAM;4CACNN,SAAS;gDACP,MAAMyB,YAAY,MAAM9F,QAAQ;oDAC9B+F,OAAO;oDACPC,aACE,CAAC,CAAC,EAAEnB,OAAO3D,KAAK,CAAC,0DAA0D,CAAC,GAC5E;oDACF+E,kBAAkB;oDAClBC,yBAAyB;wDAAEvB,OAAO;oDAAQ;gDAC5C,GACGzB,IAAI,CAAC,IAAM,MACXD,KAAK,CAAC,IAAM;gDACf,IAAI,CAAC6C,aAAa,CAAChG,MAAM;gDACzB,IAAI;oDACF,MAAMb,eAAea,MAAM;wDAAEsC,QAAQ;wDAAUzC;wDAAQiG,UAAUf,OAAO9B,EAAE;oDAAC;oDAC3E,MAAMb;gDACR,EAAE,OAAOL,OAAO;oDACdD,OAAOC,OAAO;gDAChB;4CACF;sDAEC;;;mCA1EEgD,OAAO9B,EAAE;;;yBAgFpB;kCACJ,MAACxE;wBAAMkG,SAAS;;0CACd,KAAChG;gCAAWuD,SAAQ;0CAAa;;0CACjC,KAACvD;gCAAWuD,SAAQ;gCAAQ2C,OAAM;0CAC/B;;4BAEFxE,UAAUgG,OAAO,CAAC,CAAC3D;oCACjB5B;uCAAD,EAACA,yBAAAA,QAAQ,CAAC4B,SAASC,GAAG,CAAC,YAAtB7B,yBAA0B,EAAE,EAAE2B,GAAG,CAAC,CAAC6D,wBAClC,MAAC7H;wCAA4CuG,WAAU;wCAAML,SAAS;wCAAGM,IAAI;4CAAEC,YAAY;wCAAS;;0DAClG,KAACvG;gDAAWuD,SAAQ;gDAAQ+C,IAAI;oDAAEI,MAAM;gDAAE;0DACvC,GAAGiB,QAAQlF,KAAK,CAAC,GAAG,EAAEsB,SAAShC,IAAI,EAAE;;0DAExC,KAACvC;gDACCmG,MAAK;gDACLlD,OAAOmF,KAAKC,GAAG,KAAKF,QAAQG,YAAY,GAAG,SAAU,cAAc;gDACnE5B,OAAO0B,KAAKC,GAAG,KAAKF,QAAQG,YAAY,GAAG,SAAU,YAAY;;0DAEnE,KAACvI;gDACCoG,MAAK;gDACLO,OAAM;gDACNN,SAAS;oDACP,IAAI,CAACvE,MAAM;oDACX,IAAI;wDACF,MAAMd,eAAec,MAAM;4DACzBsC,QAAQ;4DACRzC;4DACAyB,YAAYoB,SAASC,GAAG;4DACxB+D,WAAWJ,QAAQrD,EAAE;wDACvB;wDACAlC,YAAY,CAAC4F;gEAEMA;mEAFI,aAClBA;gEACH,CAACjE,SAASC,GAAG,CAAC,EAAE,EAACgE,qBAAAA,IAAI,CAACjE,SAASC,GAAG,CAAC,YAAlBgE,qBAAsB,EAAE,EAAEC,MAAM,CAAC,CAACC,QAAUA,MAAM5D,EAAE,KAAKqD,QAAQrD,EAAE;;;oDAExF,EAAE,OAAOlB,OAAO;wDACdD,OAAOC,OAAO;oDAChB;gDACF;0DAEC;;;uCA9BO,GAAGW,SAASC,GAAG,CAAC,CAAC,EAAE2D,QAAQrD,EAAE,EAAE;;;;;;0BAsCnD,MAAC7E;gBAAO0I,MAAM9C,QAAQpC;gBAAWmF,SAAS,IAAMlF,YAAY;gBAAOmF,UAAS;gBAAKC,SAAS;;kCACxF,KAAC1I;kCAAa;;kCACd,KAACD;kCACC,cAAA,KAACI;4BACC0C,OAAM;4BACNuE,KAAK,WAAE/D,4BAAAA,SAAUR,KAAK,oBAAI;4BAC1BwE,UAAU,CAACC,QAAUhE,YAAY,CAAC8E,OAAUA,OAAO,aAAKA;wCAAMvF,OAAOyE,MAAME,MAAM,CAACJ,KAAK;yCAAKgB;4BAC5FM,SAAS;4BACTC,SAAS;4BACTjC,IAAI;gCAAEkC,IAAI;4BAAE;;;kCAGhB,MAAC9I;;0CACC,KAACH;gCAAOqG,SAAS,IAAM1C,YAAY;0CAAQ;;0CAC3C,KAAC3D;gCACCgE,SAAQ;gCACRkF,UAAU,EAACxF,4BAAAA,SAAUR,KAAK,CAACwC,IAAI;gCAC/BW,SAAS;oCACP,IAAI,CAAC3C,YAAY,CAAC5B,MAAM;oCACxB,IAAI;wCACF,MAAMb,eAAea,MAAM;4CACzBsC,QAAQ;4CACRzC;4CACAiG,UAAUlE,SAASqB,EAAE;4CACrB7B,OAAOQ,SAASR,KAAK,CAACwC,IAAI;wCAC5B;wCACA/B,YAAY;wCACZ,MAAMO;oCACR,EAAE,OAAOL,OAAO;wCACdD,OAAOC,OAAO;oCAChB;gCACF;0CAEC;;;;;;0BAIP,MAAC3D;gBAAO0I,MAAM9F;gBAAQ+F,SAAS,IAAM9F,UAAU;gBAAQ+F,UAAS;gBAAKC,SAAS;;kCAC5E,KAAC1I;kCAAa;;kCACd,MAACD;wBAAc2G,IAAI;4BAAEqB,SAAS;4BAAQe,eAAe;4BAAUC,KAAK;wBAAE;;0CACpE,KAAC3I;gCAAWuD,SAAQ;gCAAQ2C,OAAM;gCAAiBI,IAAI;oCAAEkC,IAAI;gCAAE;0CAC5D;;0CAEH,KAACzI;gCACC0C,OAAM;gCACNuE,OAAOzE;gCACP0E,UAAU,CAACC,QAAU1E,QAAQ0E,MAAME,MAAM,CAACJ,KAAK;gCAC/CuB,SAAS;gCACTK,WAAW;oCAAEC,WAAW;wCAAEC,cAAc;oCAAM;gCAAE;;0CAElD,KAAC/I;gCAAU0C,OAAM;gCAAOsG,aAAY;gCAAuB/B,OAAOvE;gCAAOwE,UAAU,CAACC,QAAUxE,SAASwE,MAAME,MAAM,CAACJ,KAAK;;0CACzH,MAACjH;gCAAUgH,MAAM;gCAACtE,OAAM;gCAAWuE,OAAOrE;gCAAYsE,UAAU,CAACC,QAAUtE,cAAcsE,MAAME,MAAM,CAACJ,KAAK;;kDACzG,KAACnH;wCAASmH,OAAM;kDAAI;;oCACnBtF,UAAUoC,GAAG,CAAC,CAACC,yBACd,KAAClE;4CAA4BmH,OAAOjD,SAASC,GAAG;sDAC7CD,SAAShC,IAAI;2CADDgC,SAASC,GAAG;;;4BAK9B/B,UAAU,CAACA,OAAOiD,aAAa,iBAC9B,MAAC5F;gCAAIgH,IAAI;oCAAEqB,SAAS;oCAAQgB,KAAK;oCAAKK,qBAAqB;wCAAEC,IAAI;wCAAOC,IAAI;oCAAU;gCAAE;;kDACtF,KAAClJ;wCAAWuD,SAAQ;wCAAQ2C,OAAM;wCAAiBI,IAAI;4CAAE6C,YAAY;wCAAS;kDAC3E;;kDAEH,KAACpJ;wCAAU0C,OAAM;wCAASuE,OAAOnE,QAAQlC,KAAK;wCAAEsG,UAAU,CAACC,QAAUpE,WAAW,aAAKD;gDAASlC,OAAOuG,MAAME,MAAM,CAACJ,KAAK;;wCAAKV,IAAI;4CAAE6C,YAAY;wCAAS;;kDACvJ,KAACpJ;wCAAU0C,OAAM;wCAAOuE,OAAOnE,QAAQhC,IAAI;wCAAEoG,UAAU,CAACC,QAAUpE,WAAW,aAAKD;gDAAShC,MAAMqG,MAAME,MAAM,CAACJ,KAAK;;;kDACnH,KAACjH;wCAAU0C,OAAM;wCAAQuE,OAAOnE,QAAQ/B,KAAK;wCAAEmG,UAAU,CAACC,QAAUpE,WAAW,aAAKD;gDAAS/B,OAAOoG,MAAME,MAAM,CAACJ,KAAK;;;kDACtH,KAACjH;wCAAU0C,OAAM;wCAAcuE,OAAOnE,QAAQ9B,UAAU;wCAAEkG,UAAU,CAACC,QAAUpE,WAAW,aAAKD;gDAAS9B,YAAYmG,MAAME,MAAM,CAACJ,KAAK;;;kDACtI,KAACjH;wCACC0C,OAAM;wCACNuE,OAAOnE,QAAQ7B,OAAO;wCACtBiG,UAAU,CAACC,QAAUpE,WAAW,aAAKD;gDAAS7B,SAASkG,MAAME,MAAM,CAACJ,KAAK,CAACoC,WAAW,GAAGC,KAAK,CAAC,GAAG;;;;iCAGnG;;;kCAEN,MAAC3J;;0CACC,KAACH;gCAAOqG,SAAS,IAAMtD,UAAU;0CAAS;;0CAC1C,KAAC/C;gCAAOgE,SAAQ;gCAAYkF,UAAU1F,QAAQ,CAACR,KAAK0C,IAAI;gCAAIW,SAAS,IAAM,KAAKb;0CAC7E;;;;;;;;AAMb;AACA9D,eAAeqI,WAAW,GAAG;AAE7B,eAAerI,eAAc"}
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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"}
@@ -28,7 +28,7 @@ export declare function posProductPlan(options: {
28
28
  export declare const POS_GRID_QUERY: typeof PRODUCT_LIST_QUERY;
29
29
  /** The basket line an item-sheet choice makes: label and estimate from the product. */
30
30
  export declare function registerLineFor(product: any, choice: PosItemChoice): RegisterLine;
31
- export declare function PosConsolePage({ hostId }: ConsolePluginPageProps): import("react").JSX.Element;
31
+ export declare function PosConsolePage({ hostId, basePath }: ConsolePluginPageProps): import("react").JSX.Element;
32
32
  export declare namespace PosConsolePage {
33
33
  var displayName: string;
34
34
  }
@@ -24,7 +24,7 @@ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
24
24
  import { planListQuery } from "@aglyn/shared-ui-jsx/const/list-query-plan";
25
25
  import { useViewportFill } from "@aglyn/shared-ui-jsx/hooks/use-viewport-fill";
26
26
  import { nameSearchNormalizers } from "@aglyn/aglyn/app-utils/name-search";
27
- import { Badge, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Drawer, MenuItem, Stack, TextField, ToggleButton, ToggleButtonGroup, Typography, useMediaQuery, useTheme } from "@mui/material";
27
+ import { Badge, Box, Button, Drawer, MenuItem, Stack, TextField, Typography, useMediaQuery, useTheme } from "@mui/material";
28
28
  import { collection, getDocs, limit, query, where } from "firebase/firestore";
29
29
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
30
30
  import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
@@ -42,6 +42,7 @@ import { PosReceiptPanel } from "./pos/pos-receipt-panel.component.js";
42
42
  import { PosTenderPanel } from "./pos/pos-tender-panel.component.js";
43
43
  import { PosKioskQueue } from "./pos/pos-kiosk-queue.component.js";
44
44
  import { posDisplayTipCents, usePosDisplay } from "./pos/use-pos-display.js";
45
+ import { posRegistersSettingsHref, usePosPairing } from "./pos/pos-pairing.component.js";
45
46
  import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
46
47
  import { POS_ORDERS_ZONE } from "./pos-zones.js";
47
48
  import { PosOfflineBanner, PosOfflineCheckout, usePosOffline } from "./pos-offline/register-offline.js";
@@ -185,8 +186,8 @@ import { PosOfflineBanner, PosOfflineCheckout, usePosOffline } from "./pos-offli
185
186
  registerId: registerId
186
187
  });
187
188
  });
188
- export function PosConsolePage({ hostId }) {
189
- var _ref, _ref1, _ref2, _liveRegister_openShiftId, _ref3, _ref4, _ref5, _ref6;
189
+ export function PosConsolePage({ hostId, basePath }) {
190
+ var _ref, _ref1, _ref2, _liveRegister_openShiftId, _ref3, _ref4, _ref5;
190
191
  var _usableRegisters_find, _usableRegisters_, _offline_totalsFor;
191
192
  const WidgetSlot = useConsoleWidgetSlot();
192
193
  const firestore = useFirestore();
@@ -327,7 +328,6 @@ export function PosConsolePage({ hostId }) {
327
328
  const [saleLines, setSaleLines] = useState([]);
328
329
  const [opening, setOpening] = useState(false);
329
330
  const [sheetOpen, setSheetOpen] = useState(false);
330
- const [pairing, setPairing] = useState(null);
331
331
  const display = usePosDisplay(user, hostId, registerId);
332
332
  /**
333
333
  * One key per basket (AGL-1691): minted on the first Charge and retired
@@ -421,18 +421,17 @@ export function PosConsolePage({ hostId }) {
421
421
  }, [
422
422
  enqueueSnackbar
423
423
  ]);
424
- /** A pairing code for a customer display or a self-service kiosk (AGL-3623). */ const showPairingCode = useCallback(async (mode)=>{
425
- try {
426
- setPairing(_extends({}, await display.pairingCode(mode), {
427
- mode
428
- }));
429
- } catch (error) {
430
- notify(error instanceof PosRequestError ? error.message : 'Could not make a code', 'error');
431
- }
432
- }, [
433
- display,
424
+ /**
425
+ * A pairing code for a customer display or a self-service kiosk (AGL-3623),
426
+ * through the same flow the POS devices card offers (AGL-3712).
427
+ */ const onPairingError = useCallback((message)=>notify(message, 'error'), [
434
428
  notify
435
429
  ]);
430
+ const pairingFlow = usePosPairing({
431
+ user,
432
+ hostId,
433
+ onError: onPairingError
434
+ });
436
435
  /**
437
436
  * Adds a line, or more of an identical one: the same product, variant and
438
437
  * modifiers merge, so two oat lattes are one line of two and an oat latte
@@ -696,14 +695,34 @@ export function PosConsolePage({ hostId }) {
696
695
  estimateCents,
697
696
  pendingTipCents
698
697
  ]);
698
+ const registersHref = posRegistersSettingsHref(basePath);
699
699
  const registerPicker = !planReady ? /*#__PURE__*/ _jsx(Typography, {
700
700
  variant: "body2",
701
701
  color: "text.secondary",
702
702
  children: 'Checking your plan…'
703
- }) : usableRegisters.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
703
+ }) : usableRegisters.length === 0 ? registers.length > 0 ? /*#__PURE__*/ _jsx(Typography, {
704
704
  variant: "body2",
705
705
  color: "warning.main",
706
- children: registers.length > 0 ? 'Your registers exceed your plan — remove extras or upgrade in Billing to take payments.' : 'No POS register yet. Add one under Commerce → Settings → POS registers before taking payments.'
706
+ children: 'Your registers exceed your plan — remove extras or upgrade in Billing to take payments.'
707
+ }) : // A way there, not a path to follow by hand (AGL-3712).
708
+ /*#__PURE__*/ _jsxs(Stack, {
709
+ spacing: 1,
710
+ sx: {
711
+ alignItems: 'flex-start'
712
+ },
713
+ children: [
714
+ /*#__PURE__*/ _jsx(Typography, {
715
+ variant: "body2",
716
+ color: "warning.main",
717
+ children: 'No POS register yet. Add one before taking payments.'
718
+ }),
719
+ registersHref ? /*#__PURE__*/ _jsx(Button, {
720
+ size: "small",
721
+ variant: "outlined",
722
+ href: registersHref,
723
+ children: 'Add a register'
724
+ }) : null
725
+ ]
707
726
  }) : usableRegisters.length > 1 ? /*#__PURE__*/ _jsx(TextField, {
708
727
  label: "Register",
709
728
  value: registerId,
@@ -767,7 +786,7 @@ export function PosConsolePage({ hostId }) {
767
786
  }),
768
787
  registerId ? /*#__PURE__*/ _jsx(Button, {
769
788
  size: "small",
770
- onClick: ()=>void showPairingCode('display'),
789
+ onClick: ()=>void pairingFlow.open(registerId, 'display'),
771
790
  children: display.connected ? 'Display connected' : 'Pair display'
772
791
  }) : null
773
792
  ]
@@ -1129,75 +1148,7 @@ export function PosConsolePage({ hostId }) {
1129
1148
  setSheetOpen(false);
1130
1149
  }
1131
1150
  })),
1132
- /*#__PURE__*/ _jsxs(Dialog, {
1133
- open: Boolean(pairing),
1134
- onClose: ()=>setPairing(null),
1135
- maxWidth: "xs",
1136
- fullWidth: true,
1137
- children: [
1138
- /*#__PURE__*/ _jsx(DialogTitle, {
1139
- children: (pairing == null ? void 0 : pairing.mode) === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'
1140
- }),
1141
- /*#__PURE__*/ _jsxs(DialogContent, {
1142
- sx: {
1143
- display: 'flex',
1144
- flexDirection: 'column',
1145
- gap: 2
1146
- },
1147
- children: [
1148
- /*#__PURE__*/ _jsxs(ToggleButtonGroup, {
1149
- exclusive: true,
1150
- fullWidth: true,
1151
- size: "small",
1152
- value: (_ref6 = pairing == null ? void 0 : pairing.mode) != null ? _ref6 : 'display',
1153
- onChange: (_event, mode)=>{
1154
- if (mode && mode !== (pairing == null ? void 0 : pairing.mode)) void showPairingCode(mode);
1155
- },
1156
- sx: {
1157
- mt: 1
1158
- },
1159
- children: [
1160
- /*#__PURE__*/ _jsx(ToggleButton, {
1161
- value: "display",
1162
- children: 'Customer display'
1163
- }),
1164
- /*#__PURE__*/ _jsx(ToggleButton, {
1165
- value: "kiosk",
1166
- children: 'Self-service kiosk'
1167
- })
1168
- ]
1169
- }),
1170
- /*#__PURE__*/ _jsx(Typography, {
1171
- variant: "body2",
1172
- color: "text.secondary",
1173
- children: ((pairing == null ? void 0 : pairing.mode) === 'kiosk' ? 'On the tablet customers order from, open this address and enter the code. ' : 'On the tablet facing your customer, open this address and enter the code. ') + 'The code works once and expires in 10 minutes.'
1174
- }),
1175
- /*#__PURE__*/ _jsx(Typography, {
1176
- variant: "body1",
1177
- sx: {
1178
- wordBreak: 'break-all'
1179
- },
1180
- children: `${typeof window === 'undefined' ? '' : window.location.origin}/kiosk/commerce/${(pairing == null ? void 0 : pairing.mode) === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`
1181
- }),
1182
- /*#__PURE__*/ _jsx(Typography, {
1183
- variant: "h3",
1184
- component: "p",
1185
- sx: {
1186
- textAlign: 'center',
1187
- letterSpacing: 8
1188
- },
1189
- children: pairing == null ? void 0 : pairing.code
1190
- })
1191
- ]
1192
- }),
1193
- /*#__PURE__*/ _jsx(DialogActions, {
1194
- children: /*#__PURE__*/ _jsx(Button, {
1195
- onClick: ()=>setPairing(null),
1196
- children: 'Done'
1197
- })
1198
- })
1199
- ]
1200
- })
1151
+ pairingFlow.dialog
1201
1152
  ]
1202
1153
  });
1203
1154
  }