@aglyn/plugins-commerce 1.0.0-beta.250 → 1.0.0-beta.253
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/lib/components/commerce-console-page.d.ts +1 -1
- package/src/lib/components/commerce-console-page.js +139 -175
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/commerce-console-sections.d.ts +30 -2
- package/src/lib/components/commerce-console-sections.js +82 -7
- package/src/lib/components/commerce-console-sections.js.map +1 -1
- package/src/lib/components/console/discounts-card.component.js +36 -3
- package/src/lib/components/console/discounts-card.component.js.map +1 -1
- package/src/lib/components/console/host-coupons-card.component.js +1 -1
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-pairing.component.d.ts +59 -0
- package/src/lib/components/console/pos/pos-pairing.component.js +174 -0
- package/src/lib/components/console/pos/pos-pairing.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.d.ts +0 -8
- package/src/lib/components/console/pos/use-pos-display.js +2 -16
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
- package/src/lib/components/console/pos-devices-card.component.d.ts +12 -1
- package/src/lib/components/console/pos-devices-card.component.js +132 -59
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.d.ts +1 -1
- package/src/lib/components/console/pos-page.component.js +37 -86
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/registers-card.component.js +13 -0
- package/src/lib/components/console/registers-card.component.js.map +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js +6 -11
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/store-zones.d.ts +15 -0
- package/src/lib/components/console/store-zones.js +36 -4
- package/src/lib/components/console/store-zones.js.map +1 -1
- package/src/lib/components/console/suppliers-card.component.js +2 -2
- package/src/lib/components/console/suppliers-card.component.js.map +1 -1
- package/src/lib/model/commerce-record-routes.js +10 -3
- package/src/lib/model/commerce-record-routes.js.map +1 -1
- package/src/lib/model/commerce-shipping.d.ts +16 -0
- package/src/lib/model/commerce-shipping.js +21 -0
- package/src/lib/model/commerce-shipping.js.map +1 -1
- package/src/lib/model/commerce-tax-decision.js +2 -2
- package/src/lib/model/commerce-tax-decision.js.map +1 -1
- package/src/lib/plugin.js +4 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +13 -0
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/order-webhooks.js +1 -1
- package/src/lib/server/order-webhooks.js.map +1 -1
- package/src/lib/server/shippingeasy.js +1 -1
- package/src/lib/server/shippingeasy.js.map +1 -1
- package/src/lib/server/store-setup-ai-capability.js +10 -2
- package/src/lib/server/store-setup-ai-capability.js.map +1 -1
- package/src/lib/server/supplier-routing.d.ts +86 -0
- package/src/lib/server/supplier-routing.js +175 -0
- 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":"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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_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,
|
|
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
|
|
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
|
-
/**
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
}
|