@aglyn/plugins-commerce 1.0.0-beta.269 → 1.0.0-beta.270
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/console/payment-methods-card.component.js +7 -0
- package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
- package/src/lib/components/console/payments-settings-card.component.d.ts +2 -0
- package/src/lib/components/console/payments-settings-card.component.js +2 -0
- package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-kiosk-readiness.component.d.ts +58 -0
- package/src/lib/components/console/pos/pos-kiosk-readiness.component.js +146 -0
- package/src/lib/components/console/pos/pos-kiosk-readiness.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-pairing.component.d.ts +12 -2
- package/src/lib/components/console/pos/pos-pairing.component.js +14 -1
- package/src/lib/components/console/pos/pos-pairing.component.js.map +1 -1
- package/src/lib/components/console/pos-devices-card.component.js +13 -0
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/contracts.d.ts +237 -0
- package/src/lib/contracts.js +46 -0
- package/src/lib/contracts.js.map +1 -0
- package/src/lib/declarations.console-server.js +8 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/model/commerce-record-routes.d.ts +7 -0
- package/src/lib/model/commerce-record-routes.js +7 -0
- package/src/lib/model/commerce-record-routes.js.map +1 -1
- package/src/lib/model/commerce-server-search-sources.d.ts +30 -0
- package/src/lib/model/commerce-server-search-sources.js +57 -0
- package/src/lib/model/commerce-server-search-sources.js.map +1 -0
- package/src/lib/model/order-events.d.ts +8 -127
- package/src/lib/model/order-events.js +11 -24
- package/src/lib/model/order-events.js.map +1 -1
- package/src/lib/model/order-history.d.ts +7 -40
- package/src/lib/model/order-history.js +7 -5
- package/src/lib/model/order-history.js.map +1 -1
- package/src/lib/native-nav.d.ts +30 -0
- package/src/lib/native-nav.js +127 -0
- package/src/lib/native-nav.js.map +1 -0
- package/src/lib/server/order-event-triggers.d.ts +2 -2
|
@@ -25,9 +25,19 @@ export interface PosPairingDialogProps {
|
|
|
25
25
|
pairing: PosPairingState | null;
|
|
26
26
|
onModeChange: (mode: CommerceModel.PosDeviceMode) => void;
|
|
27
27
|
onClose: () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Who is pairing, and for which site: a kiosk code then also says what the
|
|
30
|
+
* kiosk still needs to take payment (AGL-3982). Without them it shows the
|
|
31
|
+
* code alone.
|
|
32
|
+
*/
|
|
33
|
+
user?: User | null;
|
|
34
|
+
hostId?: string;
|
|
28
35
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The code, the address to enter it at, and the display-or-kiosk choice; for
|
|
38
|
+
* a kiosk, what it still needs to take payment.
|
|
39
|
+
*/
|
|
40
|
+
export declare function PosPairingDialog({ pairing, onModeChange, onClose, user, hostId }: PosPairingDialogProps): import("react").JSX.Element;
|
|
31
41
|
export declare namespace PosPairingDialog {
|
|
32
42
|
var displayName: string;
|
|
33
43
|
}
|
|
@@ -20,6 +20,7 @@ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, ToggleButton
|
|
|
20
20
|
import { useCallback, useState } from "react";
|
|
21
21
|
import * as CommerceModel from "../../../model/index.js";
|
|
22
22
|
import { posDisplayCall, PosRequestError } from "./pos-api.js";
|
|
23
|
+
import { PosKioskReadiness } from "./pos-kiosk-readiness.component.js";
|
|
23
24
|
/**
|
|
24
25
|
* A fresh six-digit code that pairs a tablet to one register, as a customer
|
|
25
26
|
* display or a self-service kiosk (AGL-3608, AGL-3623). The server holds the
|
|
@@ -37,7 +38,10 @@ import { posDisplayCall, PosRequestError } from "./pos-api.js";
|
|
|
37
38
|
return `${origin}/kiosk/commerce/${mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`;
|
|
38
39
|
}
|
|
39
40
|
const TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60000);
|
|
40
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* The code, the address to enter it at, and the display-or-kiosk choice; for
|
|
43
|
+
* a kiosk, what it still needs to take payment.
|
|
44
|
+
*/ export function PosPairingDialog({ pairing, onModeChange, onClose, user, hostId }) {
|
|
41
45
|
var _ref;
|
|
42
46
|
const mode = (_ref = pairing == null ? void 0 : pairing.mode) != null ? _ref : 'display';
|
|
43
47
|
return /*#__PURE__*/ _jsxs(Dialog, {
|
|
@@ -89,6 +93,13 @@ const TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60000)
|
|
|
89
93
|
})
|
|
90
94
|
]
|
|
91
95
|
}),
|
|
96
|
+
mode === 'kiosk' && pairing && hostId ? /*#__PURE__*/ _jsx(PosKioskReadiness, _extends({
|
|
97
|
+
user: user,
|
|
98
|
+
hostId: hostId,
|
|
99
|
+
registerId: pairing.registerId
|
|
100
|
+
}, pairing.registerName ? {
|
|
101
|
+
registerName: pairing.registerName
|
|
102
|
+
} : {})) : null,
|
|
92
103
|
/*#__PURE__*/ _jsx(Typography, {
|
|
93
104
|
variant: "body2",
|
|
94
105
|
color: "text.secondary",
|
|
@@ -149,6 +160,8 @@ PosPairingDialog.displayName = 'PosPairingDialog';
|
|
|
149
160
|
const close = useCallback(()=>setPairing(null), []);
|
|
150
161
|
const dialog = /*#__PURE__*/ _jsx(PosPairingDialog, {
|
|
151
162
|
pairing: pairing,
|
|
163
|
+
user: user,
|
|
164
|
+
hostId: hostId,
|
|
152
165
|
onClose: close,
|
|
153
166
|
onModeChange: (mode)=>{
|
|
154
167
|
if (pairing) void open(pairing.registerId, mode, pairing.registerName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos/pos-pairing.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport * as CommerceModel from '../../../model'\nimport { posDisplayCall, PosRequestError } from './pos-api'\n\ntype User = Parameters<typeof posDisplayCall>[0]\n\n/** A code on screen, and the register and kind of device it pairs. */\nexport interface PosPairingState {\n registerId: string\n registerName?: string\n mode: CommerceModel.PosDeviceMode\n code: string\n expiresAtMs: number\n}\n\n/**\n * A fresh six-digit code that pairs a tablet to one register, as a customer\n * display or a self-service kiosk (AGL-3608, AGL-3623). The server holds the\n * gate: the register's role and `managePos` checks and the plan's `pos`.\n */\nexport function requestPosPairingCode(\n user: User,\n hostId: string,\n registerId: string,\n mode: CommerceModel.PosDeviceMode = 'display',\n): Promise<{ code: string; expiresAtMs: number }> {\n return posDisplayCall<{ code: string; expiresAtMs: number }>(user, {\n action: 'pairing-code',\n hostId,\n registerId,\n mode,\n })\n}\n\n/** The address a tablet opens to enter the code. */\nexport function posPairingAddress(mode: CommerceModel.PosDeviceMode): string {\n const origin = typeof window === 'undefined' ? '' : window.location.origin\n return `${origin}/kiosk/commerce/${mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`\n}\n\nconst TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60_000)\n\nexport interface PosPairingDialogProps {\n pairing: PosPairingState | null\n onModeChange: (mode: CommerceModel.PosDeviceMode) => void\n onClose: () => void\n}\n\n/** The code, the address to enter it at, and the display-or-kiosk choice. */\nexport function PosPairingDialog({ pairing, onModeChange, onClose }: PosPairingDialogProps) {\n const mode = pairing?.mode ?? 'display'\n return (\n <Dialog open={Boolean(pairing)} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>\n {mode === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'}\n {pairing?.registerName ? (\n <Typography component=\"span\" variant=\"body2\" color=\"text.secondary\" sx={{ display: 'block' }}>\n {`For ${pairing.registerName}`}\n </Typography>\n ) : null}\n </DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <ToggleButtonGroup\n exclusive\n fullWidth\n size=\"small\"\n value={mode}\n onChange={(_event, next: CommerceModel.PosDeviceMode | null) => {\n if (next && next !== mode) onModeChange(next)\n }}\n sx={{ mt: 1 }}\n >\n <ToggleButton value=\"display\">{'Customer display'}</ToggleButton>\n <ToggleButton value=\"kiosk\">{'Self-service kiosk'}</ToggleButton>\n </ToggleButtonGroup>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(mode === 'kiosk'\n ? 'On the tablet customers order from, open this address and enter the code. '\n : 'On the tablet facing your customer, open this address and enter the code. ') +\n `The code works once and expires in ${TTL_MINUTES} minutes.`}\n </Typography>\n <Typography variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {posPairingAddress(mode)}\n </Typography>\n <Typography variant=\"h3\" component=\"p\" sx={{ textAlign: 'center', letterSpacing: 8 }}>\n {pairing?.code}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\nPosPairingDialog.displayName = 'PosPairingDialog'\n\nexport interface UsePosPairingOptions {\n user: User | null | undefined\n hostId: string\n /** Told the reason a code could not be made. */\n onError: (message: string) => void\n}\n\n/**\n * The one pairing flow, for the register and the POS devices card alike:\n * `open(registerId)` asks for a code and shows it; switching between\n * customer display and kiosk asks for a code of that kind. Render `dialog`.\n */\nexport function usePosPairing({ user, hostId, onError }: UsePosPairingOptions): {\n pairing: PosPairingState | null\n open: (registerId: string, mode?: CommerceModel.PosDeviceMode, registerName?: string) => Promise<void>\n close: () => void\n dialog: ReactNode\n} {\n const [pairing, setPairing] = useState<PosPairingState | null>(null)\n\n const open = useCallback(\n async (registerId: string, mode: CommerceModel.PosDeviceMode = 'display', registerName?: string) => {\n if (!user || !registerId) return\n try {\n const answer = await requestPosPairingCode(user, hostId, registerId, mode)\n setPairing({ ...answer, registerId, mode, ...(registerName ? { registerName } : {}) })\n } catch (error) {\n onError(error instanceof PosRequestError ? error.message : 'Could not make a code')\n }\n },\n [user, hostId, onError],\n )\n const close = useCallback(() => setPairing(null), [])\n\n const dialog = (\n <PosPairingDialog\n pairing={pairing}\n onClose={close}\n onModeChange={(mode) => {\n if (pairing) void open(pairing.registerId, mode, pairing.registerName)\n }}\n />\n )\n return { pairing, open, close, dialog }\n}\n\n/** The element id the POS registers card carries, for a link that lands on it. */\nexport const POS_REGISTERS_ANCHOR = 'pos-registers'\n\n/**\n * Where a register is created — the POS registers card, on Products → Point\n * of sale (`…/products/pos`, AGL-3713) — from the register page's own console\n * path (`…/pos`). Undefined when that path is not the register's.\n */\nexport function posRegistersSettingsHref(posBasePath: string | undefined): string | undefined {\n if (!posBasePath || !/\\/pos\\/?$/.test(posBasePath)) return undefined\n return `${posBasePath.replace(/\\/pos\\/?$/, '/products/pos')}#${POS_REGISTERS_ANCHOR}`\n}\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","ToggleButton","ToggleButtonGroup","Typography","useCallback","useState","CommerceModel","posDisplayCall","PosRequestError","requestPosPairingCode","user","hostId","registerId","mode","action","posPairingAddress","origin","window","location","TTL_MINUTES","Math","round","POS_DISPLAY_PAIRING_TTL_MS","PosPairingDialog","pairing","onModeChange","onClose","open","Boolean","maxWidth","fullWidth","registerName","component","variant","color","sx","display","flexDirection","gap","exclusive","size","value","onChange","_event","next","mt","wordBreak","textAlign","letterSpacing","code","onClick","displayName","usePosPairing","onError","setPairing","answer","error","message","close","dialog","POS_REGISTERS_ANCHOR","posRegistersSettingsHref","posBasePath","test","undefined","replace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos/pos-pairing.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport * as CommerceModel from '../../../model'\nimport { posDisplayCall, PosRequestError } from './pos-api'\nimport { PosKioskReadiness } from './pos-kiosk-readiness.component'\n\ntype User = Parameters<typeof posDisplayCall>[0]\n\n/** A code on screen, and the register and kind of device it pairs. */\nexport interface PosPairingState {\n registerId: string\n registerName?: string\n mode: CommerceModel.PosDeviceMode\n code: string\n expiresAtMs: number\n}\n\n/**\n * A fresh six-digit code that pairs a tablet to one register, as a customer\n * display or a self-service kiosk (AGL-3608, AGL-3623). The server holds the\n * gate: the register's role and `managePos` checks and the plan's `pos`.\n */\nexport function requestPosPairingCode(\n user: User,\n hostId: string,\n registerId: string,\n mode: CommerceModel.PosDeviceMode = 'display',\n): Promise<{ code: string; expiresAtMs: number }> {\n return posDisplayCall<{ code: string; expiresAtMs: number }>(user, {\n action: 'pairing-code',\n hostId,\n registerId,\n mode,\n })\n}\n\n/** The address a tablet opens to enter the code. */\nexport function posPairingAddress(mode: CommerceModel.PosDeviceMode): string {\n const origin = typeof window === 'undefined' ? '' : window.location.origin\n return `${origin}/kiosk/commerce/${mode === 'kiosk' ? 'pos-kiosk' : 'pos-display'}`\n}\n\nconst TTL_MINUTES = Math.round(CommerceModel.POS_DISPLAY_PAIRING_TTL_MS / 60_000)\n\nexport interface PosPairingDialogProps {\n pairing: PosPairingState | null\n onModeChange: (mode: CommerceModel.PosDeviceMode) => void\n onClose: () => void\n /**\n * Who is pairing, and for which site: a kiosk code then also says what the\n * kiosk still needs to take payment (AGL-3982). Without them it shows the\n * code alone.\n */\n user?: User | null\n hostId?: string\n}\n\n/**\n * The code, the address to enter it at, and the display-or-kiosk choice; for\n * a kiosk, what it still needs to take payment.\n */\nexport function PosPairingDialog({ pairing, onModeChange, onClose, user, hostId }: PosPairingDialogProps) {\n const mode = pairing?.mode ?? 'display'\n return (\n <Dialog open={Boolean(pairing)} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>\n {mode === 'kiosk' ? 'Pair a self-service kiosk' : 'Pair a customer display'}\n {pairing?.registerName ? (\n <Typography component=\"span\" variant=\"body2\" color=\"text.secondary\" sx={{ display: 'block' }}>\n {`For ${pairing.registerName}`}\n </Typography>\n ) : null}\n </DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <ToggleButtonGroup\n exclusive\n fullWidth\n size=\"small\"\n value={mode}\n onChange={(_event, next: CommerceModel.PosDeviceMode | null) => {\n if (next && next !== mode) onModeChange(next)\n }}\n sx={{ mt: 1 }}\n >\n <ToggleButton value=\"display\">{'Customer display'}</ToggleButton>\n <ToggleButton value=\"kiosk\">{'Self-service kiosk'}</ToggleButton>\n </ToggleButtonGroup>\n {mode === 'kiosk' && pairing && hostId ? (\n <PosKioskReadiness\n user={user}\n hostId={hostId}\n registerId={pairing.registerId}\n {...(pairing.registerName ? { registerName: pairing.registerName } : {})}\n />\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(mode === 'kiosk'\n ? 'On the tablet customers order from, open this address and enter the code. '\n : 'On the tablet facing your customer, open this address and enter the code. ') +\n `The code works once and expires in ${TTL_MINUTES} minutes.`}\n </Typography>\n <Typography variant=\"body1\" sx={{ wordBreak: 'break-all' }}>\n {posPairingAddress(mode)}\n </Typography>\n <Typography variant=\"h3\" component=\"p\" sx={{ textAlign: 'center', letterSpacing: 8 }}>\n {pairing?.code}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Done'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\nPosPairingDialog.displayName = 'PosPairingDialog'\n\nexport interface UsePosPairingOptions {\n user: User | null | undefined\n hostId: string\n /** Told the reason a code could not be made. */\n onError: (message: string) => void\n}\n\n/**\n * The one pairing flow, for the register and the POS devices card alike:\n * `open(registerId)` asks for a code and shows it; switching between\n * customer display and kiosk asks for a code of that kind. Render `dialog`.\n */\nexport function usePosPairing({ user, hostId, onError }: UsePosPairingOptions): {\n pairing: PosPairingState | null\n open: (registerId: string, mode?: CommerceModel.PosDeviceMode, registerName?: string) => Promise<void>\n close: () => void\n dialog: ReactNode\n} {\n const [pairing, setPairing] = useState<PosPairingState | null>(null)\n\n const open = useCallback(\n async (registerId: string, mode: CommerceModel.PosDeviceMode = 'display', registerName?: string) => {\n if (!user || !registerId) return\n try {\n const answer = await requestPosPairingCode(user, hostId, registerId, mode)\n setPairing({ ...answer, registerId, mode, ...(registerName ? { registerName } : {}) })\n } catch (error) {\n onError(error instanceof PosRequestError ? error.message : 'Could not make a code')\n }\n },\n [user, hostId, onError],\n )\n const close = useCallback(() => setPairing(null), [])\n\n const dialog = (\n <PosPairingDialog\n pairing={pairing}\n user={user}\n hostId={hostId}\n onClose={close}\n onModeChange={(mode) => {\n if (pairing) void open(pairing.registerId, mode, pairing.registerName)\n }}\n />\n )\n return { pairing, open, close, dialog }\n}\n\n/** The element id the POS registers card carries, for a link that lands on it. */\nexport const POS_REGISTERS_ANCHOR = 'pos-registers'\n\n/**\n * Where a register is created — the POS registers card, on Products → Point\n * of sale (`…/products/pos`, AGL-3713) — from the register page's own console\n * path (`…/pos`). Undefined when that path is not the register's.\n */\nexport function posRegistersSettingsHref(posBasePath: string | undefined): string | undefined {\n if (!posBasePath || !/\\/pos\\/?$/.test(posBasePath)) return undefined\n return `${posBasePath.replace(/\\/pos\\/?$/, '/products/pos')}#${POS_REGISTERS_ANCHOR}`\n}\n"],"names":["Button","Dialog","DialogActions","DialogContent","DialogTitle","ToggleButton","ToggleButtonGroup","Typography","useCallback","useState","CommerceModel","posDisplayCall","PosRequestError","PosKioskReadiness","requestPosPairingCode","user","hostId","registerId","mode","action","posPairingAddress","origin","window","location","TTL_MINUTES","Math","round","POS_DISPLAY_PAIRING_TTL_MS","PosPairingDialog","pairing","onModeChange","onClose","open","Boolean","maxWidth","fullWidth","registerName","component","variant","color","sx","display","flexDirection","gap","exclusive","size","value","onChange","_event","next","mt","wordBreak","textAlign","letterSpacing","code","onClick","displayName","usePosPairing","onError","setPairing","answer","error","message","close","dialog","POS_REGISTERS_ANCHOR","posRegistersSettingsHref","posBasePath","test","undefined","replace"],"mappings":"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"}
|
|
@@ -25,6 +25,7 @@ import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, M
|
|
|
25
25
|
import { collection, limit, query } from "firebase/firestore";
|
|
26
26
|
import { useCallback, useEffect, useState } from "react";
|
|
27
27
|
import { posDisplayCall, posReadersCall, PosRequestError } from "./pos/pos-api.js";
|
|
28
|
+
import { POS_DEVICES_ANCHOR } from "./pos/pos-kiosk-readiness.component.js";
|
|
28
29
|
import { POS_REGISTERS_ANCHOR, usePosPairing } from "./pos/pos-pairing.component.js";
|
|
29
30
|
const EMPTY_ADDRESS = {
|
|
30
31
|
line1: '',
|
|
@@ -176,7 +177,19 @@ const EMPTY_ADDRESS = {
|
|
|
176
177
|
};
|
|
177
178
|
const readers = (_ref1 = answer == null ? void 0 : answer.readers) != null ? _ref1 : [];
|
|
178
179
|
const showReaders = Boolean(answer == null ? void 0 : answer.available);
|
|
180
|
+
/*
|
|
181
|
+
* Arrived by a kiosk's "Open POS devices" (AGL-3982): this card renders
|
|
182
|
+
* after the page has already tried to scroll to the anchor, so it brings
|
|
183
|
+
* itself into view.
|
|
184
|
+
*/ useEffect(()=>{
|
|
185
|
+
var _document_getElementById_scrollIntoView, _document_getElementById;
|
|
186
|
+
if (typeof window === 'undefined' || window.location.hash !== `#${POS_DEVICES_ANCHOR}`) return;
|
|
187
|
+
(_document_getElementById = document.getElementById(POS_DEVICES_ANCHOR)) == null ? void 0 : (_document_getElementById_scrollIntoView = _document_getElementById.scrollIntoView) == null ? void 0 : _document_getElementById_scrollIntoView.call(_document_getElementById, {
|
|
188
|
+
block: 'start'
|
|
189
|
+
});
|
|
190
|
+
}, []);
|
|
179
191
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
192
|
+
id: POS_DEVICES_ANCHOR,
|
|
180
193
|
header: 'POS devices',
|
|
181
194
|
help: pluginDocsHelp('pos', {
|
|
182
195
|
anchor: '#card-readers'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-devices-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, useFirestoreCollection, useOrgPlan, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { posDisplayCall, posReadersCall, PosRequestError } from './pos/pos-api'\nimport { POS_REGISTERS_ANCHOR, usePosPairing } from './pos/pos-pairing.component'\n\ninterface ReaderRow {\n id: string\n label: string\n registerId: string | null\n deviceType: string | null\n serialNumber: string | null\n status: string\n livemode: boolean\n}\n\ninterface ReadersAnswer {\n available: boolean\n testMode: boolean\n locationReady: boolean\n readers: ReaderRow[]\n}\n\ninterface DisplayRow {\n id: string\n label: string\n createdAtMs: number\n lastSeenAtMs: number\n}\n\nexport interface PosDevicesCardProps {\n hostId: string\n /**\n * Whether the viewer may work this site's register: `managePos`, as the\n * shell resolved it, on a site role that may run it (admin or editor).\n * Pairing a tablet is refused by the server otherwise, so the card says why\n * rather than offering a button that fails. Absent reads as not allowed.\n */\n canManagePos?: boolean\n}\n\nconst EMPTY_ADDRESS = { line1: '', line2: '', city: '', state: '', postalCode: '', country: 'US' }\n\n/**\n * The register's hardware (AGL-3607, AGL-3608): Stripe Terminal card readers\n * — registered with the code a reader shows, bound to a register, renamed,\n * removed — and the customer displays paired to each register, which can be\n * signed out. Readers appear only where card readers are offered at all.\n *\n * Each register pairs a new customer display or self-service kiosk here,\n * through the same flow as the register's own Pair display (AGL-3712); a site\n * with no register is sent to the card that makes one.\n */\nexport function PosDevicesCard({ hostId, canManagePos = false }: PosDevicesCardProps) {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Not \"no registers\" until the listen has answered.\n const noRegisters = registerDocs !== undefined && registerDocs.length === 0\n const registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const [answer, setAnswer] = useState<ReadersAnswer | null>(null)\n const [displays, setDisplays] = useState<Record<string, DisplayRow[]>>({})\n const [adding, setAdding] = useState(false)\n const [code, setCode] = useState('')\n const [label, setLabel] = useState('')\n const [registerId, setRegisterId] = useState('')\n const [address, setAddress] = useState(EMPTY_ADDRESS)\n const [busy, setBusy] = useState(false)\n const [renaming, setRenaming] = useState<{ id: string; label: string } | null>(null)\n const { org, ready: planReady } = useOrgPlan(hostId)\n const posEntitled = planReady && Aglyn.checkEntitlement(org as any, 'pos')\n\n const notify = useCallback(\n (error: unknown, fallback: string) =>\n enqueueSnackbar(error instanceof PosRequestError ? error.message : fallback, {\n variant: 'warning',\n persist: false,\n }),\n [enqueueSnackbar],\n )\n\n const onPairingError = useCallback(\n (message: string) => enqueueSnackbar(message, { variant: 'warning', persist: false }),\n [enqueueSnackbar],\n )\n const pairingFlow = usePosPairing({ user, hostId, onError: onPairingError })\n /** Why pairing is not offered to this viewer, or null when it is. */\n const pairBlocked = !planReady\n ? 'Checking your plan…'\n : !posEntitled\n ? 'Point of Sale is not on your plan. Upgrade in Billing to pair displays and kiosks.'\n : !canManagePos\n ? 'Only an admin or editor with the \"Use the point of sale\" permission can pair displays and kiosks.'\n : null\n\n const load = useCallback(\n async (refresh = false) => {\n if (!user) return\n try {\n setAnswer(await posReadersCall<ReadersAnswer>(user, { action: 'list', hostId, refresh }))\n } catch {\n setAnswer(null)\n }\n },\n [user, hostId],\n )\n const uid = user?.uid ?? ''\n useEffect(() => {\n void load(true)\n // Keyed on the person, not the session object (see the register page).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId])\n\n const registerIds = registers.map((register: any) => register.$id).join(',')\n useEffect(() => {\n if (!user || !registerIds) return\n let active = true\n void Promise.all(\n registerIds.split(',').map(async (id) => {\n const result = await posDisplayCall<{ displays: DisplayRow[] }>(user, {\n action: 'displays',\n hostId,\n registerId: id,\n }).catch(() => ({ displays: [] as DisplayRow[] }))\n return [id, result.displays] as const\n }),\n ).then((entries) => {\n if (active) setDisplays(Object.fromEntries(entries))\n })\n return () => {\n active = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId, registerIds])\n\n const registerName = (id: string | null) =>\n registers.find((register: any) => register.$id === id)?.name ?? null\n\n const addReader = async () => {\n if (!user) return\n setBusy(true)\n try {\n await posReadersCall(user, {\n action: 'register',\n hostId,\n registrationCode: code.trim(),\n label: label.trim(),\n ...(registerId ? { registerId } : {}),\n ...(answer?.locationReady ? {} : { address }),\n })\n setAdding(false)\n setCode('')\n setLabel('')\n setRegisterId('')\n await load()\n } catch (error) {\n notify(error, 'The reader could not be added')\n } finally {\n setBusy(false)\n }\n }\n\n const readers = answer?.readers ?? []\n const showReaders = Boolean(answer?.available)\n\n return (\n <CardDisplay\n header={'POS devices'}\n help={pluginDocsHelp('pos', { anchor: '#card-readers' })}\n HeaderProps={\n showReaders || noRegisters\n ? {\n action: (\n <Stack direction=\"row\" spacing={1}>\n {noRegisters ? (\n <Button size=\"small\" href={`#${POS_REGISTERS_ANCHOR}`}>\n {'Add a register'}\n </Button>\n ) : null}\n {showReaders ? (\n <Button size=\"small\" onClick={() => setAdding(true)}>\n {'Add card reader'}\n </Button>\n ) : null}\n </Stack>\n ),\n }\n : undefined\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {showReaders ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Card readers'}</Typography>\n {readers.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {answer?.testMode\n ? 'No card readers yet. In test mode, add one with the code \"simulated-wpe\" to try a simulated reader.'\n : 'No card readers yet. Add a Stripe Reader S700 or BBPOS WisePOS E with the code it shows on screen.'}\n </Typography>\n ) : (\n readers.map((reader) => (\n <Stack\n key={reader.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1, minWidth: 160 }}>\n {reader.label}\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {` · ${registerName(reader.registerId) ?? 'Any register'}` +\n (reader.livemode ? '' : ' · test')}\n </Typography>\n </Typography>\n <Chip\n size=\"small\"\n label={reader.status === 'online' ? 'Online' : 'Offline'}\n color={reader.status === 'online' ? 'success' : 'default'}\n />\n <TextField\n select\n size=\"small\"\n label=\"Register\"\n value={reader.registerId ?? ''}\n onChange={async (event) => {\n if (!user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: reader.id,\n registerId: event.target.value,\n })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be updated')\n }\n }}\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n <Button\n size=\"small\"\n onClick={() => setRenaming({ id: reader.id, label: reader.label })}\n >\n {'Rename'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n const confirmed = await confirm({\n title: 'Remove this card reader?',\n description:\n `\"${reader.label}\" is removed from your account and stops taking payments. ` +\n 'To use it again, register it with a new code.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed || !user) return\n try {\n await posReadersCall(user, { action: 'remove', hostId, readerId: reader.id })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be removed')\n }\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n </Stack>\n ) : null}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Customer displays and kiosks'}</Typography>\n {noRegisters ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'A display or kiosk pairs to a register. Add a register first.'}\n </Typography>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {pairBlocked ??\n 'Pair display gives you a code. Open the address it shows on the tablet and enter the code.'}\n </Typography>\n {registers.map((register: any) => (\n <Stack key={register.$id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1, fontWeight: 'fontWeightMedium' }} noWrap>\n {register.name}\n </Typography>\n <Button\n size=\"small\"\n disabled={Boolean(pairBlocked)}\n onClick={() => void pairingFlow.open(register.$id, 'display', register.name)}\n aria-label={`Pair display to ${register.name}`}\n >\n {'Pair display'}\n </Button>\n </Stack>\n {(displays[register.$id] ?? []).map((display) => (\n <Stack key={`${register.$id}:${display.id}`} direction=\"row\" spacing={1} sx={{ alignItems: 'center', pl: 2 }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {display.label}\n </Typography>\n <Chip\n size=\"small\"\n label={Date.now() - display.lastSeenAtMs < 120_000 ? 'Connected' : 'Not connected'}\n color={Date.now() - display.lastSeenAtMs < 120_000 ? 'success' : 'default'}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n if (!user) return\n try {\n await posDisplayCall(user, {\n action: 'revoke',\n hostId,\n registerId: register.$id,\n displayId: display.id,\n })\n setDisplays((prev) => ({\n ...prev,\n [register.$id]: (prev[register.$id] ?? []).filter((entry) => entry.id !== display.id),\n }))\n } catch (error) {\n notify(error, 'The display could not be signed out')\n }\n }}\n >\n {'Sign out'}\n </Button>\n </Stack>\n ))}\n </Stack>\n ))}\n </>\n )}\n </Stack>\n </Stack>\n\n <Dialog open={Boolean(renaming)} onClose={() => setRenaming(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'Rename card reader'}</DialogTitle>\n <DialogContent>\n <TextField\n label=\"Name\"\n value={renaming?.label ?? ''}\n onChange={(event) => setRenaming((prev) => (prev ? { ...prev, label: event.target.value } : prev))}\n fullWidth\n autoFocus\n sx={{ mt: 1 }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setRenaming(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={!renaming?.label.trim()}\n onClick={async () => {\n if (!renaming || !user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: renaming.id,\n label: renaming.label.trim(),\n })\n setRenaming(null)\n await load()\n } catch (error) {\n notify(error, 'The reader could not be renamed')\n }\n }}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={adding} onClose={() => setAdding(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{'Add a card reader'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {'On the reader, open Settings and choose Generate pairing code, then enter the code it shows.'}\n </Typography>\n <TextField\n label=\"Registration code\"\n value={code}\n onChange={(event) => setCode(event.target.value)}\n autoFocus\n slotProps={{ htmlInput: { autoComplete: 'off' } }}\n />\n <TextField label=\"Name\" placeholder=\"Front counter reader\" value={label} onChange={(event) => setLabel(event.target.value)} />\n <TextField select label=\"Register\" value={registerId} onChange={(event) => setRegisterId(event.target.value)}>\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n {answer && !answer.locationReady ? (\n <Box sx={{ display: 'grid', gap: 1.5, gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' } }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ gridColumn: '1 / -1' }}>\n {'Where the reader is used. Stripe sets the reader up for this country.'}\n </Typography>\n <TextField label=\"Street\" value={address.line1} onChange={(event) => setAddress({ ...address, line1: event.target.value })} sx={{ gridColumn: '1 / -1' }} />\n <TextField label=\"City\" value={address.city} onChange={(event) => setAddress({ ...address, city: event.target.value })} />\n <TextField label=\"State\" value={address.state} onChange={(event) => setAddress({ ...address, state: event.target.value })} />\n <TextField label=\"Postal code\" value={address.postalCode} onChange={(event) => setAddress({ ...address, postalCode: event.target.value })} />\n <TextField\n label=\"Country\"\n value={address.country}\n onChange={(event) => setAddress({ ...address, country: event.target.value.toUpperCase().slice(0, 2) })}\n />\n </Box>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdding(false)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={busy || !code.trim()} onClick={() => void addReader()}>\n {'Add reader'}\n </Button>\n </DialogActions>\n </Dialog>\n {pairingFlow.dialog}\n </CardDisplay>\n )\n}\nPosDevicesCard.displayName = 'PosDevicesCard'\n\nexport default PosDevicesCard\n"],"names":["Aglyn","pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","useFirestore","useFirestoreCollection","useOrgPlan","useUser","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","query","useCallback","useEffect","useState","posDisplayCall","posReadersCall","PosRequestError","POS_REGISTERS_ANCHOR","usePosPairing","EMPTY_ADDRESS","line1","line2","city","state","postalCode","country","PosDevicesCard","hostId","canManagePos","firestore","data","user","enqueueSnackbar","confirm","registerDocs","idField","noRegisters","undefined","length","registers","sort","a","b","String","name","localeCompare","answer","setAnswer","displays","setDisplays","adding","setAdding","code","setCode","label","setLabel","registerId","setRegisterId","address","setAddress","busy","setBusy","renaming","setRenaming","org","ready","planReady","posEntitled","checkEntitlement","notify","error","fallback","message","variant","persist","onPairingError","pairingFlow","onError","pairBlocked","load","refresh","action","uid","registerIds","map","register","$id","join","active","Promise","all","split","id","result","catch","then","entries","Object","fromEntries","registerName","find","addReader","registrationCode","trim","locationReady","readers","showReaders","Boolean","available","header","help","anchor","HeaderProps","direction","spacing","size","href","onClick","contentGutterX","contentGutterY","color","testMode","reader","sx","alignItems","flexWrap","rowGap","flex","minWidth","component","livemode","status","select","value","onChange","event","readerId","target","confirmed","title","description","confirmationText","confirmationButtonProps","fontWeight","noWrap","disabled","open","aria-label","display","pl","Date","now","lastSeenAtMs","displayId","prev","filter","entry","onClose","maxWidth","fullWidth","autoFocus","mt","flexDirection","gap","slotProps","htmlInput","autoComplete","placeholder","gridTemplateColumns","xs","sm","gridColumn","toUpperCase","slice","dialog","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/pos-devices-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, useFirestoreCollection, useOrgPlan, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, limit, query } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { posDisplayCall, posReadersCall, PosRequestError } from './pos/pos-api'\nimport { POS_DEVICES_ANCHOR } from './pos/pos-kiosk-readiness.component'\nimport { POS_REGISTERS_ANCHOR, usePosPairing } from './pos/pos-pairing.component'\n\ninterface ReaderRow {\n id: string\n label: string\n registerId: string | null\n deviceType: string | null\n serialNumber: string | null\n status: string\n livemode: boolean\n}\n\ninterface ReadersAnswer {\n available: boolean\n testMode: boolean\n locationReady: boolean\n readers: ReaderRow[]\n}\n\ninterface DisplayRow {\n id: string\n label: string\n createdAtMs: number\n lastSeenAtMs: number\n}\n\nexport interface PosDevicesCardProps {\n hostId: string\n /**\n * Whether the viewer may work this site's register: `managePos`, as the\n * shell resolved it, on a site role that may run it (admin or editor).\n * Pairing a tablet is refused by the server otherwise, so the card says why\n * rather than offering a button that fails. Absent reads as not allowed.\n */\n canManagePos?: boolean\n}\n\nconst EMPTY_ADDRESS = { line1: '', line2: '', city: '', state: '', postalCode: '', country: 'US' }\n\n/**\n * The register's hardware (AGL-3607, AGL-3608): Stripe Terminal card readers\n * — registered with the code a reader shows, bound to a register, renamed,\n * removed — and the customer displays paired to each register, which can be\n * signed out. Readers appear only where card readers are offered at all.\n *\n * Each register pairs a new customer display or self-service kiosk here,\n * through the same flow as the register's own Pair display (AGL-3712); a site\n * with no register is sent to the card that makes one.\n */\nexport function PosDevicesCard({ hostId, canManagePos = false }: PosDevicesCardProps) {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const { data: registerDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'registers'), limit(25)),\n [firestore, hostId],\n { idField: '$id' },\n )\n // Not \"no registers\" until the listen has answered.\n const noRegisters = registerDocs !== undefined && registerDocs.length === 0\n const registers = [...(registerDocs ?? [])].sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n const [answer, setAnswer] = useState<ReadersAnswer | null>(null)\n const [displays, setDisplays] = useState<Record<string, DisplayRow[]>>({})\n const [adding, setAdding] = useState(false)\n const [code, setCode] = useState('')\n const [label, setLabel] = useState('')\n const [registerId, setRegisterId] = useState('')\n const [address, setAddress] = useState(EMPTY_ADDRESS)\n const [busy, setBusy] = useState(false)\n const [renaming, setRenaming] = useState<{ id: string; label: string } | null>(null)\n const { org, ready: planReady } = useOrgPlan(hostId)\n const posEntitled = planReady && Aglyn.checkEntitlement(org as any, 'pos')\n\n const notify = useCallback(\n (error: unknown, fallback: string) =>\n enqueueSnackbar(error instanceof PosRequestError ? error.message : fallback, {\n variant: 'warning',\n persist: false,\n }),\n [enqueueSnackbar],\n )\n\n const onPairingError = useCallback(\n (message: string) => enqueueSnackbar(message, { variant: 'warning', persist: false }),\n [enqueueSnackbar],\n )\n const pairingFlow = usePosPairing({ user, hostId, onError: onPairingError })\n /** Why pairing is not offered to this viewer, or null when it is. */\n const pairBlocked = !planReady\n ? 'Checking your plan…'\n : !posEntitled\n ? 'Point of Sale is not on your plan. Upgrade in Billing to pair displays and kiosks.'\n : !canManagePos\n ? 'Only an admin or editor with the \"Use the point of sale\" permission can pair displays and kiosks.'\n : null\n\n const load = useCallback(\n async (refresh = false) => {\n if (!user) return\n try {\n setAnswer(await posReadersCall<ReadersAnswer>(user, { action: 'list', hostId, refresh }))\n } catch {\n setAnswer(null)\n }\n },\n [user, hostId],\n )\n const uid = user?.uid ?? ''\n useEffect(() => {\n void load(true)\n // Keyed on the person, not the session object (see the register page).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId])\n\n const registerIds = registers.map((register: any) => register.$id).join(',')\n useEffect(() => {\n if (!user || !registerIds) return\n let active = true\n void Promise.all(\n registerIds.split(',').map(async (id) => {\n const result = await posDisplayCall<{ displays: DisplayRow[] }>(user, {\n action: 'displays',\n hostId,\n registerId: id,\n }).catch(() => ({ displays: [] as DisplayRow[] }))\n return [id, result.displays] as const\n }),\n ).then((entries) => {\n if (active) setDisplays(Object.fromEntries(entries))\n })\n return () => {\n active = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [uid, hostId, registerIds])\n\n const registerName = (id: string | null) =>\n registers.find((register: any) => register.$id === id)?.name ?? null\n\n const addReader = async () => {\n if (!user) return\n setBusy(true)\n try {\n await posReadersCall(user, {\n action: 'register',\n hostId,\n registrationCode: code.trim(),\n label: label.trim(),\n ...(registerId ? { registerId } : {}),\n ...(answer?.locationReady ? {} : { address }),\n })\n setAdding(false)\n setCode('')\n setLabel('')\n setRegisterId('')\n await load()\n } catch (error) {\n notify(error, 'The reader could not be added')\n } finally {\n setBusy(false)\n }\n }\n\n const readers = answer?.readers ?? []\n const showReaders = Boolean(answer?.available)\n\n /*\n * Arrived by a kiosk's \"Open POS devices\" (AGL-3982): this card renders\n * after the page has already tried to scroll to the anchor, so it brings\n * itself into view.\n */\n useEffect(() => {\n if (typeof window === 'undefined' || window.location.hash !== `#${POS_DEVICES_ANCHOR}`) return\n document.getElementById(POS_DEVICES_ANCHOR)?.scrollIntoView?.({ block: 'start' })\n }, [])\n\n return (\n <CardDisplay\n id={POS_DEVICES_ANCHOR}\n header={'POS devices'}\n help={pluginDocsHelp('pos', { anchor: '#card-readers' })}\n HeaderProps={\n showReaders || noRegisters\n ? {\n action: (\n <Stack direction=\"row\" spacing={1}>\n {noRegisters ? (\n <Button size=\"small\" href={`#${POS_REGISTERS_ANCHOR}`}>\n {'Add a register'}\n </Button>\n ) : null}\n {showReaders ? (\n <Button size=\"small\" onClick={() => setAdding(true)}>\n {'Add card reader'}\n </Button>\n ) : null}\n </Stack>\n ),\n }\n : undefined\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {showReaders ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Card readers'}</Typography>\n {readers.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {answer?.testMode\n ? 'No card readers yet. In test mode, add one with the code \"simulated-wpe\" to try a simulated reader.'\n : 'No card readers yet. Add a Stripe Reader S700 or BBPOS WisePOS E with the code it shows on screen.'}\n </Typography>\n ) : (\n readers.map((reader) => (\n <Stack\n key={reader.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1, minWidth: 160 }}>\n {reader.label}\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">\n {` · ${registerName(reader.registerId) ?? 'Any register'}` +\n (reader.livemode ? '' : ' · test')}\n </Typography>\n </Typography>\n <Chip\n size=\"small\"\n label={reader.status === 'online' ? 'Online' : 'Offline'}\n color={reader.status === 'online' ? 'success' : 'default'}\n />\n <TextField\n select\n size=\"small\"\n label=\"Register\"\n value={reader.registerId ?? ''}\n onChange={async (event) => {\n if (!user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: reader.id,\n registerId: event.target.value,\n })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be updated')\n }\n }}\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n <Button\n size=\"small\"\n onClick={() => setRenaming({ id: reader.id, label: reader.label })}\n >\n {'Rename'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n const confirmed = await confirm({\n title: 'Remove this card reader?',\n description:\n `\"${reader.label}\" is removed from your account and stops taking payments. ` +\n 'To use it again, register it with a new code.',\n confirmationText: 'Remove',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed || !user) return\n try {\n await posReadersCall(user, { action: 'remove', hostId, readerId: reader.id })\n await load()\n } catch (error) {\n notify(error, 'The reader could not be removed')\n }\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ))\n )}\n </Stack>\n ) : null}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Customer displays and kiosks'}</Typography>\n {noRegisters ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'A display or kiosk pairs to a register. Add a register first.'}\n </Typography>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {pairBlocked ??\n 'Pair display gives you a code. Open the address it shows on the tablet and enter the code.'}\n </Typography>\n {registers.map((register: any) => (\n <Stack key={register.$id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flex: 1, fontWeight: 'fontWeightMedium' }} noWrap>\n {register.name}\n </Typography>\n <Button\n size=\"small\"\n disabled={Boolean(pairBlocked)}\n onClick={() => void pairingFlow.open(register.$id, 'display', register.name)}\n aria-label={`Pair display to ${register.name}`}\n >\n {'Pair display'}\n </Button>\n </Stack>\n {(displays[register.$id] ?? []).map((display) => (\n <Stack key={`${register.$id}:${display.id}`} direction=\"row\" spacing={1} sx={{ alignItems: 'center', pl: 2 }}>\n <Typography variant=\"body2\" sx={{ flex: 1 }}>\n {display.label}\n </Typography>\n <Chip\n size=\"small\"\n label={Date.now() - display.lastSeenAtMs < 120_000 ? 'Connected' : 'Not connected'}\n color={Date.now() - display.lastSeenAtMs < 120_000 ? 'success' : 'default'}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={async () => {\n if (!user) return\n try {\n await posDisplayCall(user, {\n action: 'revoke',\n hostId,\n registerId: register.$id,\n displayId: display.id,\n })\n setDisplays((prev) => ({\n ...prev,\n [register.$id]: (prev[register.$id] ?? []).filter((entry) => entry.id !== display.id),\n }))\n } catch (error) {\n notify(error, 'The display could not be signed out')\n }\n }}\n >\n {'Sign out'}\n </Button>\n </Stack>\n ))}\n </Stack>\n ))}\n </>\n )}\n </Stack>\n </Stack>\n\n <Dialog open={Boolean(renaming)} onClose={() => setRenaming(null)} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'Rename card reader'}</DialogTitle>\n <DialogContent>\n <TextField\n label=\"Name\"\n value={renaming?.label ?? ''}\n onChange={(event) => setRenaming((prev) => (prev ? { ...prev, label: event.target.value } : prev))}\n fullWidth\n autoFocus\n sx={{ mt: 1 }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setRenaming(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={!renaming?.label.trim()}\n onClick={async () => {\n if (!renaming || !user) return\n try {\n await posReadersCall(user, {\n action: 'update',\n hostId,\n readerId: renaming.id,\n label: renaming.label.trim(),\n })\n setRenaming(null)\n await load()\n } catch (error) {\n notify(error, 'The reader could not be renamed')\n }\n }}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={adding} onClose={() => setAdding(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{'Add a card reader'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {'On the reader, open Settings and choose Generate pairing code, then enter the code it shows.'}\n </Typography>\n <TextField\n label=\"Registration code\"\n value={code}\n onChange={(event) => setCode(event.target.value)}\n autoFocus\n slotProps={{ htmlInput: { autoComplete: 'off' } }}\n />\n <TextField label=\"Name\" placeholder=\"Front counter reader\" value={label} onChange={(event) => setLabel(event.target.value)} />\n <TextField select label=\"Register\" value={registerId} onChange={(event) => setRegisterId(event.target.value)}>\n <MenuItem value=\"\">{'Any register'}</MenuItem>\n {registers.map((register: any) => (\n <MenuItem key={register.$id} value={register.$id}>\n {register.name}\n </MenuItem>\n ))}\n </TextField>\n {answer && !answer.locationReady ? (\n <Box sx={{ display: 'grid', gap: 1.5, gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' } }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ gridColumn: '1 / -1' }}>\n {'Where the reader is used. Stripe sets the reader up for this country.'}\n </Typography>\n <TextField label=\"Street\" value={address.line1} onChange={(event) => setAddress({ ...address, line1: event.target.value })} sx={{ gridColumn: '1 / -1' }} />\n <TextField label=\"City\" value={address.city} onChange={(event) => setAddress({ ...address, city: event.target.value })} />\n <TextField label=\"State\" value={address.state} onChange={(event) => setAddress({ ...address, state: event.target.value })} />\n <TextField label=\"Postal code\" value={address.postalCode} onChange={(event) => setAddress({ ...address, postalCode: event.target.value })} />\n <TextField\n label=\"Country\"\n value={address.country}\n onChange={(event) => setAddress({ ...address, country: event.target.value.toUpperCase().slice(0, 2) })}\n />\n </Box>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setAdding(false)}>{'Cancel'}</Button>\n <Button variant=\"contained\" disabled={busy || !code.trim()} onClick={() => void addReader()}>\n {'Add reader'}\n </Button>\n </DialogActions>\n </Dialog>\n {pairingFlow.dialog}\n </CardDisplay>\n )\n}\nPosDevicesCard.displayName = 'PosDevicesCard'\n\nexport default PosDevicesCard\n"],"names":["Aglyn","pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","useFirestore","useFirestoreCollection","useOrgPlan","useUser","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Typography","collection","limit","query","useCallback","useEffect","useState","posDisplayCall","posReadersCall","PosRequestError","POS_DEVICES_ANCHOR","POS_REGISTERS_ANCHOR","usePosPairing","EMPTY_ADDRESS","line1","line2","city","state","postalCode","country","PosDevicesCard","hostId","canManagePos","firestore","data","user","enqueueSnackbar","confirm","registerDocs","idField","noRegisters","undefined","length","registers","sort","a","b","String","name","localeCompare","answer","setAnswer","displays","setDisplays","adding","setAdding","code","setCode","label","setLabel","registerId","setRegisterId","address","setAddress","busy","setBusy","renaming","setRenaming","org","ready","planReady","posEntitled","checkEntitlement","notify","error","fallback","message","variant","persist","onPairingError","pairingFlow","onError","pairBlocked","load","refresh","action","uid","registerIds","map","register","$id","join","active","Promise","all","split","id","result","catch","then","entries","Object","fromEntries","registerName","find","addReader","registrationCode","trim","locationReady","readers","showReaders","Boolean","available","document","window","location","hash","getElementById","scrollIntoView","block","header","help","anchor","HeaderProps","direction","spacing","size","href","onClick","contentGutterX","contentGutterY","color","testMode","reader","sx","alignItems","flexWrap","rowGap","flex","minWidth","component","livemode","status","select","value","onChange","event","readerId","target","confirmed","title","description","confirmationText","confirmationButtonProps","fontWeight","noWrap","disabled","open","aria-label","display","pl","Date","now","lastSeenAtMs","displayId","prev","filter","entry","onClose","maxWidth","fullWidth","autoFocus","mt","flexDirection","gap","slotProps","htmlInput","autoComplete","placeholder","gridTemplateColumns","xs","sm","gridColumn","toUpperCase","slice","dialog","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* What commerce publishes for other plugins (AGL-3980): the order and return
|
|
19
|
+
* events it raises, the checkout it reports, and the order history service it
|
|
20
|
+
* registers — each by its id and its shape, and nothing that runs commerce.
|
|
21
|
+
*
|
|
22
|
+
* Another plugin imports this module as `@aglyn/plugins-commerce/contracts`,
|
|
23
|
+
* names `commerce` in its `uses` in plugins.config.json, and carries
|
|
24
|
+
* `@aglyn/plugins-commerce` as an optional peer. Importing it never loads
|
|
25
|
+
* commerce, so the subscriber still runs with commerce absent: an event it
|
|
26
|
+
* listens for is simply never raised, and the history service resolves to
|
|
27
|
+
* `null`. `check:lib-boundaries` refuses a runtime import here other than
|
|
28
|
+
* `@aglyn/aglyn`, and any other import of commerce from another plugin.
|
|
29
|
+
*/
|
|
30
|
+
/** A shipment as the order itself lists it. */
|
|
31
|
+
export interface OrderViewFulfillment {
|
|
32
|
+
id: string | null;
|
|
33
|
+
lineItemIds: unknown[];
|
|
34
|
+
/** Units per line; a fulfillment recorded before quantities shipped every unit of the lines it names. */
|
|
35
|
+
lines: Array<{
|
|
36
|
+
lineItemId: number;
|
|
37
|
+
quantity: number;
|
|
38
|
+
}>;
|
|
39
|
+
status: 'active' | 'cancelled';
|
|
40
|
+
carrier: string | null;
|
|
41
|
+
trackingNumber: string | null;
|
|
42
|
+
trackingUrl: string | null;
|
|
43
|
+
labelUrl: string | null;
|
|
44
|
+
/** ISO 8601. */
|
|
45
|
+
at: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** An order as the public API returns it. Money is integer cents. */
|
|
48
|
+
export interface OrderEventOrder {
|
|
49
|
+
id: string;
|
|
50
|
+
object: 'order';
|
|
51
|
+
number: number | null;
|
|
52
|
+
status: string | null;
|
|
53
|
+
channel: string;
|
|
54
|
+
currency: string;
|
|
55
|
+
customerEmail: string | null;
|
|
56
|
+
customerName: string | null;
|
|
57
|
+
/** The lines as stored, serialized; their fields are not a contract yet. */
|
|
58
|
+
lineItems: unknown;
|
|
59
|
+
totals: {
|
|
60
|
+
itemsCents: number;
|
|
61
|
+
shippingCents: number;
|
|
62
|
+
taxCents: number;
|
|
63
|
+
discountCents: number;
|
|
64
|
+
totalCents: number | null;
|
|
65
|
+
feeCents: number;
|
|
66
|
+
/** Optional lines the buyer added at checkout (AGL-3635), inside `totalCents` and outside `itemsCents`. */
|
|
67
|
+
extrasCents?: number;
|
|
68
|
+
};
|
|
69
|
+
refundedCents: number;
|
|
70
|
+
/** The tax regime: `stripe-automatic`, `manual`, `none`, or `null` when not recorded. */
|
|
71
|
+
taxMode: string | null;
|
|
72
|
+
/** Whether real money moved (AGL-3699): `false` for a Stripe test-mode order. */
|
|
73
|
+
livemode?: boolean;
|
|
74
|
+
/** The optional lines the buyer added at checkout and whose offer each was (AGL-3635). */
|
|
75
|
+
extras?: Array<{
|
|
76
|
+
id: string;
|
|
77
|
+
pluginId: string;
|
|
78
|
+
key: string;
|
|
79
|
+
label: string;
|
|
80
|
+
amountCents: number;
|
|
81
|
+
quoteRef: string | null;
|
|
82
|
+
}>;
|
|
83
|
+
/** Store credit another plugin kept that paid part of the sale (AGL-3640). */
|
|
84
|
+
credits?: Array<{
|
|
85
|
+
providerId: string;
|
|
86
|
+
pluginId: string;
|
|
87
|
+
reference: string;
|
|
88
|
+
label: string;
|
|
89
|
+
last4: string;
|
|
90
|
+
amountCents: number;
|
|
91
|
+
appliedAs: 'discount' | 'tender';
|
|
92
|
+
}>;
|
|
93
|
+
/** The merchant's own tax service that priced the sale (AGL-3631); `null` when none was asked. */
|
|
94
|
+
taxEngine?: {
|
|
95
|
+
provider: string;
|
|
96
|
+
providerLabel: string;
|
|
97
|
+
status: 'quoted' | 'fallback';
|
|
98
|
+
reason: string | null;
|
|
99
|
+
sandbox: boolean;
|
|
100
|
+
} | null;
|
|
101
|
+
disputed?: boolean;
|
|
102
|
+
/** The address as stored, serialized; `null` for an order that ships nowhere. */
|
|
103
|
+
shippingAddress?: Record<string, unknown> | null;
|
|
104
|
+
couponCode?: string | null;
|
|
105
|
+
fulfillments?: OrderViewFulfillment[];
|
|
106
|
+
/** ISO 8601. */
|
|
107
|
+
created?: string | null;
|
|
108
|
+
[key: string]: unknown;
|
|
109
|
+
}
|
|
110
|
+
/** A shipment as an event carries it. */
|
|
111
|
+
export interface OrderEventFulfillment {
|
|
112
|
+
id: string;
|
|
113
|
+
lines: Array<{
|
|
114
|
+
lineItemId: number;
|
|
115
|
+
quantity: number;
|
|
116
|
+
}>;
|
|
117
|
+
carrier: string | null;
|
|
118
|
+
trackingNumber: string | null;
|
|
119
|
+
trackingUrl: string | null;
|
|
120
|
+
labelUrl: string | null;
|
|
121
|
+
/** What the shipment cost to send — its label's cost, or the merchant's hand-entered one (AGL-3705) — in integer cents of the order's currency; `null` when unknown (AGL-3693). */
|
|
122
|
+
labelCostCents: number | null;
|
|
123
|
+
at: string;
|
|
124
|
+
}
|
|
125
|
+
/** A refund as an event carries it: what THIS refund moved. */
|
|
126
|
+
export interface OrderEventRefund {
|
|
127
|
+
/** Stripe's refund id, when there is one. */
|
|
128
|
+
id: string | null;
|
|
129
|
+
amountCents: number;
|
|
130
|
+
/** The line indexes refunded by name; empty for an amount-only refund. */
|
|
131
|
+
lineItemIds: number[];
|
|
132
|
+
/** Whether this refund left nothing more to refund. */
|
|
133
|
+
full: boolean;
|
|
134
|
+
}
|
|
135
|
+
/** A return as an event carries it. */
|
|
136
|
+
export interface OrderEventReturn {
|
|
137
|
+
id: string;
|
|
138
|
+
status: string;
|
|
139
|
+
lines: Array<{
|
|
140
|
+
lineItemId: number;
|
|
141
|
+
quantity: number;
|
|
142
|
+
reason: string;
|
|
143
|
+
}>;
|
|
144
|
+
refundCents: number | null;
|
|
145
|
+
/** What went back in stock when the parcel arrived; `null` before then. */
|
|
146
|
+
restock?: Array<{
|
|
147
|
+
lineItemId: number;
|
|
148
|
+
quantity: number;
|
|
149
|
+
}> | null;
|
|
150
|
+
}
|
|
151
|
+
export interface OrderEventPayload {
|
|
152
|
+
order: OrderEventOrder;
|
|
153
|
+
}
|
|
154
|
+
export interface OrderFulfilledEventPayload extends OrderEventPayload {
|
|
155
|
+
fulfillment: OrderEventFulfillment;
|
|
156
|
+
}
|
|
157
|
+
export interface OrderRefundedEventPayload extends OrderEventPayload {
|
|
158
|
+
refund: OrderEventRefund;
|
|
159
|
+
}
|
|
160
|
+
export interface ReturnEventPayload extends OrderEventPayload {
|
|
161
|
+
return: OrderEventReturn;
|
|
162
|
+
}
|
|
163
|
+
export declare const ORDER_PAID_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<OrderEventPayload>;
|
|
164
|
+
export declare const ORDER_FULFILLED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<OrderFulfilledEventPayload>;
|
|
165
|
+
export declare const ORDER_DELIVERED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<OrderEventPayload>;
|
|
166
|
+
export declare const ORDER_REFUNDED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<OrderRefundedEventPayload>;
|
|
167
|
+
export declare const ORDER_CANCELLED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<OrderEventPayload>;
|
|
168
|
+
export declare const RETURN_REQUESTED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<ReturnEventPayload>;
|
|
169
|
+
export declare const RETURN_APPROVED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<ReturnEventPayload>;
|
|
170
|
+
export declare const RETURN_DECLINED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<ReturnEventPayload>;
|
|
171
|
+
export declare const RETURN_RECEIVED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<ReturnEventPayload>;
|
|
172
|
+
export declare const RETURN_REFUNDED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<ReturnEventPayload>;
|
|
173
|
+
/**
|
|
174
|
+
* A checkout a shopper reached with an address (AGL-3639): the fact an
|
|
175
|
+
* abandoned-cart flow in a merchant's own email platform starts from. Not an
|
|
176
|
+
* order event — nothing was bought — so it is raised beside them rather than
|
|
177
|
+
* among them: it is not a workflow trigger and not offered to the merchant's
|
|
178
|
+
* outbound webhooks, whose events are about orders.
|
|
179
|
+
*/
|
|
180
|
+
export interface CheckoutStartedEventPayload {
|
|
181
|
+
checkout: {
|
|
182
|
+
/** The checkout session's id: one event per checkout. */
|
|
183
|
+
id: string;
|
|
184
|
+
email: string;
|
|
185
|
+
/** Whether the shopper ticked the store's marketing box at checkout. */
|
|
186
|
+
marketingOptIn: boolean;
|
|
187
|
+
currency: string;
|
|
188
|
+
/** The basket's list value, integer cents, before discounts and shipping. */
|
|
189
|
+
itemsCents: number;
|
|
190
|
+
/** Where the shopper can pick the checkout up again. */
|
|
191
|
+
resumeUrl: string | null;
|
|
192
|
+
items: Array<{
|
|
193
|
+
productId: string;
|
|
194
|
+
variantId: string | null;
|
|
195
|
+
name: string;
|
|
196
|
+
sku: string | null;
|
|
197
|
+
quantity: number;
|
|
198
|
+
unitCents: number;
|
|
199
|
+
}>;
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
export declare const CHECKOUT_STARTED_EVENT: import("@aglyn/aglyn/plugin-manager/plugin-domain-events").PluginDomainEvent<CheckoutStartedEventPayload>;
|
|
203
|
+
/** Every order and return event commerce raises, by name. */
|
|
204
|
+
export type CommerceEventName = 'order.paid' | 'order.fulfilled' | 'order.delivered' | 'order.refunded' | 'order.cancelled' | 'return.requested' | 'return.approved' | 'return.declined' | 'return.received' | 'return.refunded';
|
|
205
|
+
/** The order lifecycle's five events, by name. */
|
|
206
|
+
export type CommerceOrderEventName = Extract<CommerceEventName, `order.${string}`>;
|
|
207
|
+
/** The event names, for a webhook's event picker. */
|
|
208
|
+
export declare const COMMERCE_EVENT_NAMES: readonly CommerceEventName[];
|
|
209
|
+
/** One stored order, as the history hands it out. */
|
|
210
|
+
export interface OrderHistoryEntry {
|
|
211
|
+
id: string;
|
|
212
|
+
createdAtMs: number;
|
|
213
|
+
/** When it was paid; `null` for one never paid. */
|
|
214
|
+
paidAtMs: number | null;
|
|
215
|
+
/** The newest refund on its timeline, or `null`. */
|
|
216
|
+
lastRefundAtMs: number | null;
|
|
217
|
+
/** Whether its prices include the tax. */
|
|
218
|
+
taxInclusive: boolean;
|
|
219
|
+
/** The tax rate it was charged under, when one was named. */
|
|
220
|
+
taxRateId: string | null;
|
|
221
|
+
/** The order in the public API's shape. */
|
|
222
|
+
order: OrderEventOrder;
|
|
223
|
+
}
|
|
224
|
+
export interface OrderHistoryPageRequest {
|
|
225
|
+
hostId: string;
|
|
226
|
+
/** Orders created at or after this… */
|
|
227
|
+
fromMs: number;
|
|
228
|
+
/** …and before this. */
|
|
229
|
+
untilMs: number;
|
|
230
|
+
/** At most this many, oldest first; capped at 200. */
|
|
231
|
+
limit: number;
|
|
232
|
+
}
|
|
233
|
+
export interface OrderHistoryReader {
|
|
234
|
+
listOrders(request: OrderHistoryPageRequest): Promise<OrderHistoryEntry[]>;
|
|
235
|
+
}
|
|
236
|
+
export declare const ORDER_HISTORY_CONTRACT_ID = "commerce.orderHistory";
|
|
237
|
+
export declare const ORDER_HISTORY_CONTRACT: import("@aglyn/aglyn/src").PluginServiceContract<OrderHistoryReader>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { definePluginDomainEvent } from "@aglyn/aglyn/plugin-manager/plugin-domain-events";
|
|
17
|
+
import { definePluginServiceContract } from "@aglyn/aglyn/plugin-manager/plugin-services";
|
|
18
|
+
export const ORDER_PAID_EVENT = definePluginDomainEvent('order.paid');
|
|
19
|
+
export const ORDER_FULFILLED_EVENT = definePluginDomainEvent('order.fulfilled');
|
|
20
|
+
export const ORDER_DELIVERED_EVENT = definePluginDomainEvent('order.delivered');
|
|
21
|
+
export const ORDER_REFUNDED_EVENT = definePluginDomainEvent('order.refunded');
|
|
22
|
+
export const ORDER_CANCELLED_EVENT = definePluginDomainEvent('order.cancelled');
|
|
23
|
+
export const RETURN_REQUESTED_EVENT = definePluginDomainEvent('return.requested');
|
|
24
|
+
export const RETURN_APPROVED_EVENT = definePluginDomainEvent('return.approved');
|
|
25
|
+
export const RETURN_DECLINED_EVENT = definePluginDomainEvent('return.declined');
|
|
26
|
+
export const RETURN_RECEIVED_EVENT = definePluginDomainEvent('return.received');
|
|
27
|
+
export const RETURN_REFUNDED_EVENT = definePluginDomainEvent('return.refunded');
|
|
28
|
+
export const CHECKOUT_STARTED_EVENT = definePluginDomainEvent('checkout.started');
|
|
29
|
+
/** The event names, for a webhook's event picker. */ export const COMMERCE_EVENT_NAMES = [
|
|
30
|
+
'order.paid',
|
|
31
|
+
'order.fulfilled',
|
|
32
|
+
'order.delivered',
|
|
33
|
+
'order.refunded',
|
|
34
|
+
'order.cancelled',
|
|
35
|
+
'return.requested',
|
|
36
|
+
'return.approved',
|
|
37
|
+
'return.declined',
|
|
38
|
+
'return.received',
|
|
39
|
+
'return.refunded'
|
|
40
|
+
];
|
|
41
|
+
export const ORDER_HISTORY_CONTRACT_ID = 'commerce.orderHistory';
|
|
42
|
+
export const ORDER_HISTORY_CONTRACT = definePluginServiceContract(ORDER_HISTORY_CONTRACT_ID, {
|
|
43
|
+
multiple: false
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=contracts.js.map
|