@withpotter/pages-admin 0.13.0-next.108 → 0.13.0-next.110
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/dist/admin/_pos/pos-figures.js +18 -5
- package/dist/admin/_pos/pos-figures.js.map +1 -1
- package/dist/admin/_pos/pos-words.js +6 -1
- package/dist/admin/_pos/pos-words.js.map +1 -1
- package/dist/admin/_pos/use-pos.js +4 -2
- package/dist/admin/_pos/use-pos.js.map +1 -1
- package/dist/admin/pos/pos-workspace.js +6 -2
- package/dist/admin/pos/pos-workspace.js.map +1 -1
- package/dist/admin/pos/shifts-tab.js +2 -2
- package/dist/admin/pos/shifts-tab.js.map +1 -1
- package/dist/admin/pos/transactions-tab.js +71 -0
- package/dist/admin/pos/transactions-tab.js.map +1 -0
- package/package.json +4 -4
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { StatCard } from "../_ui";
|
|
4
4
|
import { amountLines } from "./pos-money";
|
|
5
|
-
import { inPeriod } from "./pos-words";
|
|
5
|
+
import { ALL_TIME, givenOut, inPeriod } from "./pos-words";
|
|
6
6
|
import { usePosSummary } from "./use-pos";
|
|
7
7
|
import styles from "./pos.module.css";
|
|
8
8
|
function PosFigures({
|
|
@@ -11,6 +11,7 @@ function PosFigures({
|
|
|
11
11
|
currency = ""
|
|
12
12
|
}) {
|
|
13
13
|
const { data, isLoading, isError } = usePosSummary(period);
|
|
14
|
+
const total = usePosSummary(ALL_TIME);
|
|
14
15
|
const value = (n) => isError || n === void 0 ? "\u2013" : n.toLocaleString("en-US");
|
|
15
16
|
const money = data ? amountLines(data.paymentsAmount, currency) : [];
|
|
16
17
|
const countNote = currency ? ". The count is every currency" : "";
|
|
@@ -19,10 +20,10 @@ function PosFigures({
|
|
|
19
20
|
StatCard,
|
|
20
21
|
{
|
|
21
22
|
compact: true,
|
|
22
|
-
label: "
|
|
23
|
+
label: "POS given out",
|
|
23
24
|
isLoading,
|
|
24
|
-
value: value(data
|
|
25
|
-
sub: data ? `${data.revoked} revoked` : void 0
|
|
25
|
+
value: value(data ? givenOut(data) : void 0),
|
|
26
|
+
sub: data ? `${data.terminals} in use \xB7 ${data.revoked} revoked` : void 0
|
|
26
27
|
}
|
|
27
28
|
),
|
|
28
29
|
/* @__PURE__ */ jsx(
|
|
@@ -71,7 +72,19 @@ function PosFigures({
|
|
|
71
72
|
label: "Card payments",
|
|
72
73
|
isLoading,
|
|
73
74
|
value: value(data?.paymentsCount),
|
|
74
|
-
sub: data ? money.length ? `${money.join(" \xB7 ")}, ${phrase}${countNote}` : `None ${currency ? `in ${currency} ` : ""}${inPeriod(
|
|
75
|
+
sub: data ? money.length ? `${money.join(" \xB7 ")}, ${phrase}${countNote}` : `None ${currency ? `in ${currency} ` : ""}${inPeriod(
|
|
76
|
+
phrase
|
|
77
|
+
)}${countNote}` : void 0
|
|
78
|
+
}
|
|
79
|
+
),
|
|
80
|
+
/* @__PURE__ */ jsx(
|
|
81
|
+
StatCard,
|
|
82
|
+
{
|
|
83
|
+
compact: true,
|
|
84
|
+
label: "All-time transactions",
|
|
85
|
+
isLoading: total.isLoading,
|
|
86
|
+
value: value(total.data?.paymentsCount),
|
|
87
|
+
sub: total.data ? amountLines(total.data.paymentsAmount, currency).join(" \xB7 ") || "None yet" : void 0
|
|
75
88
|
}
|
|
76
89
|
)
|
|
77
90
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_pos/pos-figures.tsx"],"sourcesContent":["'use client';\n\nimport type { PosWindow } from '@withpotter/lib/admin-pos-types';\nimport { StatCard } from '../_ui';\nimport { amountLines } from './pos-money';\nimport { inPeriod } from './pos-words';\nimport { usePosSummary } from './use-pos';\nimport styles from './pos.module.css';\n\n/**\n * The fleet at a glance: one number per card. Payments are counted in the\n * period and listed per currency, never added across currencies.\n */\nexport function PosFigures({\n period,\n phrase,\n currency = '',\n}: {\n period: PosWindow;\n phrase: string;\n /** The currency filter's choice; '' lists every currency. */\n currency?: string;\n}) {\n const { data, isLoading, isError } = usePosSummary(period);\n const value = (n: number | undefined) =>\n isError || n === undefined ? '–' : n.toLocaleString('en-US');\n const money = data ? amountLines(data.paymentsAmount, currency) : [];\n // The API counts payments across every currency, not per currency.\n const countNote = currency ? '. The count is every currency' : '';\n\n return (\n <section className={styles.figures} aria-label=\"POS at a glance\">\n <StatCard\n compact\n label=\"
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_pos/pos-figures.tsx"],"sourcesContent":["'use client';\n\nimport type { PosWindow } from '@withpotter/lib/admin-pos-types';\nimport { StatCard } from '../_ui';\nimport { amountLines } from './pos-money';\nimport { ALL_TIME, givenOut, inPeriod } from './pos-words';\nimport { usePosSummary } from './use-pos';\nimport styles from './pos.module.css';\n\n/**\n * The fleet at a glance: one number per card. Payments are counted in the\n * period and listed per currency, never added across currencies.\n */\nexport function PosFigures({\n period,\n phrase,\n currency = '',\n}: {\n period: PosWindow;\n phrase: string;\n /** The currency filter's choice; '' lists every currency. */\n currency?: string;\n}) {\n const { data, isLoading, isError } = usePosSummary(period);\n // Not the period's: the running total since the first terminal, whatever the control says.\n const total = usePosSummary(ALL_TIME);\n const value = (n: number | undefined) =>\n isError || n === undefined ? '–' : n.toLocaleString('en-US');\n const money = data ? amountLines(data.paymentsAmount, currency) : [];\n // The API counts payments across every currency, not per currency.\n const countNote = currency ? '. The count is every currency' : '';\n\n return (\n <section className={styles.figures} aria-label=\"POS at a glance\">\n <StatCard\n compact\n label=\"POS given out\"\n isLoading={isLoading}\n value={value(data ? givenOut(data) : undefined)}\n sub={\n data\n ? `${data.terminals} in use · ${data.revoked} revoked`\n : undefined\n }\n />\n <StatCard\n compact\n label=\"Online now\"\n isLoading={isLoading}\n value={value(data?.online)}\n sub={data ? `Of ${data.terminals}` : undefined}\n />\n <StatCard\n compact\n label=\"Offline over a day\"\n isLoading={isLoading}\n value={value(data?.offlineOverDay)}\n alert={!!data?.offlineOverDay}\n />\n <StatCard\n compact\n label=\"Suspended\"\n isLoading={isLoading}\n value={value(data?.suspended)}\n />\n <StatCard\n compact\n label=\"Shifts open over 12 h\"\n isLoading={isLoading}\n value={value(data?.openShiftsOverTwelveHours)}\n alert={!!data?.openShiftsOverTwelveHours}\n />\n <StatCard\n compact\n label=\"Card payments\"\n isLoading={isLoading}\n value={value(data?.paymentsCount)}\n sub={\n data\n ? money.length\n ? `${money.join(' · ')}, ${phrase}${countNote}`\n : `None ${currency ? `in ${currency} ` : ''}${inPeriod(\n phrase,\n )}${countNote}`\n : undefined\n }\n />\n <StatCard\n compact\n label=\"All-time transactions\"\n isLoading={total.isLoading}\n value={value(total.data?.paymentsCount)}\n sub={\n total.data\n ? amountLines(total.data.paymentsAmount, currency).join(' · ') ||\n 'None yet'\n : undefined\n }\n />\n </section>\n );\n}\n"],"mappings":";AAiCI,SACE,KADF;AA9BJ,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,UAAU,UAAU,gBAAgB;AAC7C,SAAS,qBAAqB;AAC9B,OAAO,YAAY;AAMZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAKG;AACD,QAAM,EAAE,MAAM,WAAW,QAAQ,IAAI,cAAc,MAAM;AAEzD,QAAM,QAAQ,cAAc,QAAQ;AACpC,QAAM,QAAQ,CAAC,MACb,WAAW,MAAM,SAAY,WAAM,EAAE,eAAe,OAAO;AAC7D,QAAM,QAAQ,OAAO,YAAY,KAAK,gBAAgB,QAAQ,IAAI,CAAC;AAEnE,QAAM,YAAY,WAAW,kCAAkC;AAE/D,SACE,qBAAC,aAAQ,WAAW,OAAO,SAAS,cAAW,mBAC7C;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN;AAAA,QACA,OAAO,MAAM,OAAO,SAAS,IAAI,IAAI,MAAS;AAAA,QAC9C,KACE,OACI,GAAG,KAAK,SAAS,gBAAa,KAAK,OAAO,aAC1C;AAAA;AAAA,IAER;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN;AAAA,QACA,OAAO,MAAM,MAAM,MAAM;AAAA,QACzB,KAAK,OAAO,MAAM,KAAK,SAAS,KAAK;AAAA;AAAA,IACvC;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN;AAAA,QACA,OAAO,MAAM,MAAM,cAAc;AAAA,QACjC,OAAO,CAAC,CAAC,MAAM;AAAA;AAAA,IACjB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN;AAAA,QACA,OAAO,MAAM,MAAM,SAAS;AAAA;AAAA,IAC9B;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN;AAAA,QACA,OAAO,MAAM,MAAM,yBAAyB;AAAA,QAC5C,OAAO,CAAC,CAAC,MAAM;AAAA;AAAA,IACjB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN;AAAA,QACA,OAAO,MAAM,MAAM,aAAa;AAAA,QAChC,KACE,OACI,MAAM,SACJ,GAAG,MAAM,KAAK,QAAK,CAAC,KAAK,MAAM,GAAG,SAAS,KAC3C,QAAQ,WAAW,MAAM,QAAQ,MAAM,EAAE,GAAG;AAAA,UAC1C;AAAA,QACF,CAAC,GAAG,SAAS,KACf;AAAA;AAAA,IAER;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAO;AAAA,QACP,OAAM;AAAA,QACN,WAAW,MAAM;AAAA,QACjB,OAAO,MAAM,MAAM,MAAM,aAAa;AAAA,QACtC,KACE,MAAM,OACF,YAAY,MAAM,KAAK,gBAAgB,QAAQ,EAAE,KAAK,QAAK,KAC3D,aACA;AAAA;AAAA,IAER;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -69,7 +69,8 @@ const ADMIN_EVENT = {
|
|
|
69
69
|
"pos_terminal.revoke": "Revoked"
|
|
70
70
|
};
|
|
71
71
|
function historyLine(e) {
|
|
72
|
-
if (e.source === "admin_action")
|
|
72
|
+
if (e.source === "admin_action")
|
|
73
|
+
return ADMIN_EVENT[e.event] ?? words(e.event);
|
|
73
74
|
return e.summary?.trim() || words(e.event);
|
|
74
75
|
}
|
|
75
76
|
function historyBy(e) {
|
|
@@ -82,7 +83,10 @@ function inPeriod(phrase) {
|
|
|
82
83
|
return `in ${phrase.startsWith("last") ? "the " : ""}${phrase}`;
|
|
83
84
|
}
|
|
84
85
|
const accessLine = (t) => t.operatorAccess === "any_member" ? "Anyone on the till roster can sign in" : "Only the people listed can sign in";
|
|
86
|
+
const givenOut = (s) => s.terminals + s.revoked;
|
|
87
|
+
const ALL_TIME = { from: "2020-01-01T00:00:00.000Z" };
|
|
85
88
|
export {
|
|
89
|
+
ALL_TIME,
|
|
86
90
|
SHIFT_STATUS_LABEL,
|
|
87
91
|
TERMINAL_STATUS_LABEL,
|
|
88
92
|
accessLine,
|
|
@@ -91,6 +95,7 @@ export {
|
|
|
91
95
|
cardLine,
|
|
92
96
|
closedLine,
|
|
93
97
|
count,
|
|
98
|
+
givenOut,
|
|
94
99
|
historyBy,
|
|
95
100
|
historyLine,
|
|
96
101
|
inPeriod,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_pos/pos-words.ts"],"sourcesContent":["import type {\n PosHistoryEntry,\n PosShift,\n PosShiftStatus,\n PosTerminal,\n PosTerminalStatus,\n} from '@withpotter/lib/admin-pos-types';\n\n/** The words the POS screens use for what the API sends. Pure and tested. */\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\nexport const TERMINAL_STATUS_LABEL: Record<PosTerminalStatus, string> = {\n active: 'Active',\n suspended: 'Suspended',\n revoked: 'Revoked',\n};\n\nexport const SHIFT_STATUS_LABEL: Record<PosShiftStatus, string> = {\n open: 'Open',\n closed: 'Closed',\n};\n\nconst CLOSED_REASON: Record<string, string> = {\n operator_sign_in_elsewhere: 'Signed in at another till',\n shift_expired: 'Ran out of time',\n manual_revoke: 'Closed from a dashboard',\n};\n\n/** Why a shift ended, in words; null while it is open. */\nexport function closedLine(s: Pick<PosShift, 'status' | 'closedReason'>) {\n if (s.status === 'open') return null;\n\n return (s.closedReason && CLOSED_REASON[s.closedReason]) ?? 'Closed';\n}\n\n/** \"15 Oct 2026\". */\nexport function onDay(iso: string): string {\n return new Date(iso).toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n });\n}\n\n/** \"15 Oct 2026, 14:03\". */\nexport function atTime(iso: string): string {\n const d = new Date(iso);\n\n return `${onDay(iso)}, ${d.toLocaleTimeString('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n })}`;\n}\n\n/** \"just now\", \"4 min ago\", \"3 h ago\", \"2 days ago\". */\nexport function ago(iso: string, now = Date.now()): string {\n const ms = Math.max(0, now - new Date(iso).getTime());\n\n if (ms < MINUTE) return 'just now';\n\n if (ms < HOUR) return `${Math.floor(ms / MINUTE)} min ago`;\n\n if (ms < DAY) return `${Math.floor(ms / HOUR)} h ago`;\n\n const days = Math.floor(ms / DAY);\n\n return days === 1 ? '1 day ago' : `${days} days ago`;\n}\n\n/**\n * How a terminal's connection reads. Online is green; offline for over a\n * day is amber, because someone should look; a stopped terminal (revoked\n * or suspended) and one never heard from are grey.\n */\nexport type OnlineTone = 'online' | 'offline' | 'stale' | 'never' | 'stopped';\n\ntype Seen = Pick<PosTerminal, 'online' | 'lastSeenAt' | 'status'>;\n\nexport function onlineTone(t: Seen, now = Date.now()): OnlineTone {\n if (t.status !== 'active') return 'stopped';\n\n if (t.online) return 'online';\n\n if (!t.lastSeenAt) return 'never';\n\n return now - new Date(t.lastSeenAt).getTime() > DAY ? 'stale' : 'offline';\n}\n\n/** \"Seen 4 min ago\", \"Offline · seen 3 days ago\", \"Never seen\". */\nexport function seenLine(t: Seen, now = Date.now()): string {\n if (!t.lastSeenAt) return 'Never seen';\n\n const when = ago(t.lastSeenAt, now);\n\n if (t.online) return `Seen ${when}`;\n\n return t.status === 'active' ? `Offline · seen ${when}` : `Last seen ${when}`;\n}\n\n/** \"Urovo · TID T100\", \"Needs a TID\", or nothing for a revoked terminal. */\nexport function cardLine(\n t: Pick<PosTerminal, 'provider' | 'tid' | 'status'>,\n): string | null {\n if (t.status === 'revoked') return null;\n\n if (!t.tid) return 'Needs a TID';\n\n const vendor = t.provider\n ? `${t.provider.charAt(0).toUpperCase()}${t.provider.slice(1)} · `\n : '';\n\n return `${vendor}TID ${t.tid}`;\n}\n\nconst words = (s: string) => {\n const w = s.replace(/[._-]+/g, ' ').trim();\n\n return w.charAt(0).toUpperCase() + w.slice(1);\n};\n\nconst ADMIN_EVENT: Record<string, string> = {\n 'pos_terminal.suspend': 'Suspended',\n 'pos_terminal.resume': 'Resumed',\n 'pos_terminal.revoke': 'Revoked',\n};\n\n/** One history entry as a sentence: what happened. */\nexport function historyLine(e: PosHistoryEntry): string {\n if (e.source === 'admin_action')
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_pos/pos-words.ts"],"sourcesContent":["import type {\n PosHistoryEntry,\n PosShift,\n PosShiftStatus,\n PosTerminal,\n PosTerminalStatus,\n} from '@withpotter/lib/admin-pos-types';\n\n/** The words the POS screens use for what the API sends. Pure and tested. */\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\nexport const TERMINAL_STATUS_LABEL: Record<PosTerminalStatus, string> = {\n active: 'Active',\n suspended: 'Suspended',\n revoked: 'Revoked',\n};\n\nexport const SHIFT_STATUS_LABEL: Record<PosShiftStatus, string> = {\n open: 'Open',\n closed: 'Closed',\n};\n\nconst CLOSED_REASON: Record<string, string> = {\n operator_sign_in_elsewhere: 'Signed in at another till',\n shift_expired: 'Ran out of time',\n manual_revoke: 'Closed from a dashboard',\n};\n\n/** Why a shift ended, in words; null while it is open. */\nexport function closedLine(s: Pick<PosShift, 'status' | 'closedReason'>) {\n if (s.status === 'open') return null;\n\n return (s.closedReason && CLOSED_REASON[s.closedReason]) ?? 'Closed';\n}\n\n/** \"15 Oct 2026\". */\nexport function onDay(iso: string): string {\n return new Date(iso).toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n });\n}\n\n/** \"15 Oct 2026, 14:03\". */\nexport function atTime(iso: string): string {\n const d = new Date(iso);\n\n return `${onDay(iso)}, ${d.toLocaleTimeString('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n })}`;\n}\n\n/** \"just now\", \"4 min ago\", \"3 h ago\", \"2 days ago\". */\nexport function ago(iso: string, now = Date.now()): string {\n const ms = Math.max(0, now - new Date(iso).getTime());\n\n if (ms < MINUTE) return 'just now';\n\n if (ms < HOUR) return `${Math.floor(ms / MINUTE)} min ago`;\n\n if (ms < DAY) return `${Math.floor(ms / HOUR)} h ago`;\n\n const days = Math.floor(ms / DAY);\n\n return days === 1 ? '1 day ago' : `${days} days ago`;\n}\n\n/**\n * How a terminal's connection reads. Online is green; offline for over a\n * day is amber, because someone should look; a stopped terminal (revoked\n * or suspended) and one never heard from are grey.\n */\nexport type OnlineTone = 'online' | 'offline' | 'stale' | 'never' | 'stopped';\n\ntype Seen = Pick<PosTerminal, 'online' | 'lastSeenAt' | 'status'>;\n\nexport function onlineTone(t: Seen, now = Date.now()): OnlineTone {\n if (t.status !== 'active') return 'stopped';\n\n if (t.online) return 'online';\n\n if (!t.lastSeenAt) return 'never';\n\n return now - new Date(t.lastSeenAt).getTime() > DAY ? 'stale' : 'offline';\n}\n\n/** \"Seen 4 min ago\", \"Offline · seen 3 days ago\", \"Never seen\". */\nexport function seenLine(t: Seen, now = Date.now()): string {\n if (!t.lastSeenAt) return 'Never seen';\n\n const when = ago(t.lastSeenAt, now);\n\n if (t.online) return `Seen ${when}`;\n\n return t.status === 'active' ? `Offline · seen ${when}` : `Last seen ${when}`;\n}\n\n/** \"Urovo · TID T100\", \"Needs a TID\", or nothing for a revoked terminal. */\nexport function cardLine(\n t: Pick<PosTerminal, 'provider' | 'tid' | 'status'>,\n): string | null {\n if (t.status === 'revoked') return null;\n\n if (!t.tid) return 'Needs a TID';\n\n const vendor = t.provider\n ? `${t.provider.charAt(0).toUpperCase()}${t.provider.slice(1)} · `\n : '';\n\n return `${vendor}TID ${t.tid}`;\n}\n\nconst words = (s: string) => {\n const w = s.replace(/[._-]+/g, ' ').trim();\n\n return w.charAt(0).toUpperCase() + w.slice(1);\n};\n\nconst ADMIN_EVENT: Record<string, string> = {\n 'pos_terminal.suspend': 'Suspended',\n 'pos_terminal.resume': 'Resumed',\n 'pos_terminal.revoke': 'Revoked',\n};\n\n/** One history entry as a sentence: what happened. */\nexport function historyLine(e: PosHistoryEntry): string {\n if (e.source === 'admin_action')\n return ADMIN_EVENT[e.event] ?? words(e.event);\n\n return e.summary?.trim() || words(e.event);\n}\n\n/** Who did it: an admin by name, or the business itself. */\nexport function historyBy(e: PosHistoryEntry): string {\n if (e.by.type === 'admin') return `${e.by.name ?? 'An admin'} · Admin`;\n\n return 'The business';\n}\n\n/** \"2 terminals\", \"1 terminal\". */\nexport const count = (n: number, one: string, many = `${one}s`) =>\n `${n} ${n === 1 ? one : many}`;\n\n/**\n * A period phrase after a noun: \"in the last 30 days\", \"in Sep 30 – Oct 1\",\n * and \"yet\" for all time (\"No payments yet\").\n */\nexport function inPeriod(phrase: string): string {\n if (phrase === 'all time') return 'yet';\n\n return `in ${phrase.startsWith('last') ? 'the ' : ''}${phrase}`;\n}\n\n/** Who may sign in at a terminal, in a line. */\nexport const accessLine = (t: Pick<PosTerminal, 'operatorAccess'>) =>\n t.operatorAccess === 'any_member'\n ? 'Anyone on the till roster can sign in'\n : 'Only the people listed can sign in';\n\n/** Every terminal ever handed out: the ones in use (not revoked) and the ones taken back. */\nexport const givenOut = (s: { terminals: number; revoked: number }) =>\n s.terminals + s.revoked;\n\n/** The window that reaches back past the first terminal: for figures that must not follow the period control. */\nexport const ALL_TIME = { from: '2020-01-01T00:00:00.000Z' } as const;\n"],"mappings":"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","names":[]}
|
|
@@ -69,9 +69,9 @@ function useTenantTillRequests(tenantId, enabled = true) {
|
|
|
69
69
|
retry
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
function
|
|
72
|
+
function usePosPayments(tenantId, enabled = true) {
|
|
73
73
|
return useInfiniteQuery({
|
|
74
|
-
queryKey: key("payments", tenantId),
|
|
74
|
+
queryKey: key("payments", tenantId ?? "all"),
|
|
75
75
|
queryFn: ({ pageParam }) => adminPosApi.payments({ tenantId, cursor: pageParam, limit: 25 }),
|
|
76
76
|
initialPageParam: void 0,
|
|
77
77
|
getNextPageParam: (last) => last.nextCursor ?? void 0,
|
|
@@ -79,6 +79,7 @@ function useTenantPayments(tenantId, enabled = true) {
|
|
|
79
79
|
retry
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
|
+
const useTenantPayments = (tenantId, enabled = true) => usePosPayments(tenantId, enabled);
|
|
82
83
|
const oliveRetry = (count, error) => !isForbidden(error) && !isNotFound(error) && !isNotLinked(error) && count < 1;
|
|
83
84
|
function useOliveAccount(tenantId, enabled = true) {
|
|
84
85
|
return useQuery({
|
|
@@ -137,6 +138,7 @@ export {
|
|
|
137
138
|
useOliveAccountTransactions,
|
|
138
139
|
useOliveOutlets,
|
|
139
140
|
usePosHistory,
|
|
141
|
+
usePosPayments,
|
|
140
142
|
usePosShifts,
|
|
141
143
|
usePosSummary,
|
|
142
144
|
usePosTerminal,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/_pos/use-pos.ts"],"sourcesContent":["'use client';\n\nimport {\n keepPreviousData,\n useMutation,\n useInfiniteQuery,\n useQuery,\n useQueryClient,\n} from '@tanstack/react-query';\nimport { adminPosApi, type TerminalAct } from '@withpotter/lib/admin-pos-api';\nimport type {\n ShiftFilters,\n TerminalFilters,\n} from '@withpotter/lib/admin-pos-query';\nimport type { PosWindow } from '@withpotter/lib/admin-pos-types';\nimport { isForbidden, isNotFound, isNotLinked } from './pos-rules';\n\n/** Every POS query sits under this, so one action refreshes them all. */\nexport const POS_KEY = ['platform-admin', 'pos'] as const;\n\nconst key = (...parts: unknown[]) => [...POS_KEY, ...parts];\n\n/** A 403 or 404 will not change on a retry; anything else gets one more go. */\nconst retry = (count: number, error: unknown) =>\n !isForbidden(error) && !isNotFound(error) && count < 1;\n\nexport function usePosSummary(w: PosWindow, enabled = true) {\n return useQuery({\n queryKey: key('summary', w),\n queryFn: () => adminPosApi.summary(w),\n enabled,\n retry,\n });\n}\n\nexport function usePosTerminals(filters: TerminalFilters, enabled = true) {\n return useQuery({\n queryKey: key('terminals', filters),\n queryFn: () => adminPosApi.terminals(filters),\n placeholderData: keepPreviousData,\n enabled,\n retry,\n });\n}\n\nexport function usePosTerminal(id: string, w: PosWindow, enabled = true) {\n return useQuery({\n queryKey: key('terminal', id, w),\n queryFn: () => adminPosApi.terminal(id, w),\n enabled,\n retry,\n });\n}\n\nexport function usePosHistory(id: string, enabled = true) {\n return useQuery({\n queryKey: key('history', id),\n queryFn: () => adminPosApi.history(id),\n enabled,\n retry,\n });\n}\n\nexport function usePosShifts(filters: ShiftFilters, enabled = true) {\n return useQuery({\n queryKey: key('shifts', filters),\n queryFn: () => adminPosApi.shifts(filters),\n placeholderData: keepPreviousData,\n enabled,\n retry,\n });\n}\n\nexport function useTenantRoster(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('roster', tenantId),\n queryFn: () => adminPosApi.roster(tenantId),\n enabled,\n retry,\n });\n}\n\nexport function useTenantTillRequests(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('till-requests', tenantId),\n queryFn: () => adminPosApi.tillRequests(tenantId),\n enabled,\n retry,\n });\n}\n\n/** Card payments, newest first, a page at a time. */\nexport function
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/_pos/use-pos.ts"],"sourcesContent":["'use client';\n\nimport {\n keepPreviousData,\n useMutation,\n useInfiniteQuery,\n useQuery,\n useQueryClient,\n} from '@tanstack/react-query';\nimport { adminPosApi, type TerminalAct } from '@withpotter/lib/admin-pos-api';\nimport type {\n ShiftFilters,\n TerminalFilters,\n} from '@withpotter/lib/admin-pos-query';\nimport type { PosWindow } from '@withpotter/lib/admin-pos-types';\nimport { isForbidden, isNotFound, isNotLinked } from './pos-rules';\n\n/** Every POS query sits under this, so one action refreshes them all. */\nexport const POS_KEY = ['platform-admin', 'pos'] as const;\n\nconst key = (...parts: unknown[]) => [...POS_KEY, ...parts];\n\n/** A 403 or 404 will not change on a retry; anything else gets one more go. */\nconst retry = (count: number, error: unknown) =>\n !isForbidden(error) && !isNotFound(error) && count < 1;\n\nexport function usePosSummary(w: PosWindow, enabled = true) {\n return useQuery({\n queryKey: key('summary', w),\n queryFn: () => adminPosApi.summary(w),\n enabled,\n retry,\n });\n}\n\nexport function usePosTerminals(filters: TerminalFilters, enabled = true) {\n return useQuery({\n queryKey: key('terminals', filters),\n queryFn: () => adminPosApi.terminals(filters),\n placeholderData: keepPreviousData,\n enabled,\n retry,\n });\n}\n\nexport function usePosTerminal(id: string, w: PosWindow, enabled = true) {\n return useQuery({\n queryKey: key('terminal', id, w),\n queryFn: () => adminPosApi.terminal(id, w),\n enabled,\n retry,\n });\n}\n\nexport function usePosHistory(id: string, enabled = true) {\n return useQuery({\n queryKey: key('history', id),\n queryFn: () => adminPosApi.history(id),\n enabled,\n retry,\n });\n}\n\nexport function usePosShifts(filters: ShiftFilters, enabled = true) {\n return useQuery({\n queryKey: key('shifts', filters),\n queryFn: () => adminPosApi.shifts(filters),\n placeholderData: keepPreviousData,\n enabled,\n retry,\n });\n}\n\nexport function useTenantRoster(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('roster', tenantId),\n queryFn: () => adminPosApi.roster(tenantId),\n enabled,\n retry,\n });\n}\n\nexport function useTenantTillRequests(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('till-requests', tenantId),\n queryFn: () => adminPosApi.tillRequests(tenantId),\n enabled,\n retry,\n });\n}\n\n/** Card payments, newest first, a page at a time: one business's, or every business's when none is given. */\nexport function usePosPayments(tenantId?: string, enabled = true) {\n return useInfiniteQuery({\n queryKey: key('payments', tenantId ?? 'all'),\n queryFn: ({ pageParam }) =>\n adminPosApi.payments({ tenantId, cursor: pageParam, limit: 25 }),\n initialPageParam: undefined as string | undefined,\n getNextPageParam: (last) => last.nextCursor ?? undefined,\n enabled,\n retry,\n });\n}\n\nexport const useTenantPayments = (tenantId: string, enabled = true) =>\n usePosPayments(tenantId, enabled);\n\n/**\n * What the business sees from Olive. Read live and audited on the server, so these are fetched only\n * when their tab or card is on screen, never in the background, and a 400 (Olive not linked) is not retried.\n */\nconst oliveRetry = (count: number, error: unknown) =>\n !isForbidden(error) && !isNotFound(error) && !isNotLinked(error) && count < 1;\n\nexport function useOliveAccount(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('olive', tenantId, 'account'),\n queryFn: () => adminPosApi.olive.account(tenantId),\n enabled,\n retry: oliveRetry,\n staleTime: 60_000,\n });\n}\n\nexport function useOliveAccountTransactions(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('olive', tenantId, 'account-transactions'),\n queryFn: () => adminPosApi.olive.accountTransactions(tenantId),\n enabled,\n retry: oliveRetry,\n staleTime: 60_000,\n });\n}\n\nexport function useOliveOutlets(tenantId: string, enabled = true) {\n return useQuery({\n queryKey: key('olive', tenantId, 'outlets'),\n queryFn: () => adminPosApi.olive.outlets(tenantId),\n enabled,\n retry: oliveRetry,\n staleTime: 60_000,\n });\n}\n\nexport function useTenantPos(tenantId: string, w: PosWindow, enabled = true) {\n return useQuery({\n queryKey: key('tenant', tenantId, w),\n queryFn: () => adminPosApi.tenant(tenantId, w),\n enabled,\n retry,\n });\n}\n\n/**\n * Every action can move the figures, the lists and a business's tab, so\n * all of POS is refetched after it, a 409 included: that means someone\n * else acted first. The refetch is not awaited, so the caller's own\n * `onSuccess` (the toast, closing the form) is not held back by it.\n */\nfunction usePosWrite<V, R>(fn: (v: V) => Promise<R>) {\n const client = useQueryClient();\n\n return useMutation({\n mutationFn: fn,\n onSettled: () => {\n void client.invalidateQueries({ queryKey: POS_KEY });\n },\n });\n}\n\nexport const useTerminalStatus = () =>\n usePosWrite((v: { id: string; act: TerminalAct; reason: string }) =>\n adminPosApi.setTerminalStatus(v.id, v.act, v.reason),\n );\n\nexport const useCloseShift = () =>\n usePosWrite((v: { id: string; reason: string }) =>\n adminPosApi.closeShift(v.id, v.reason),\n );\n"],"mappings":"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","names":[]}
|
|
@@ -16,9 +16,12 @@ import { usePosPeriod } from "../_pos/use-pos-period";
|
|
|
16
16
|
import { usePosSummary } from "../_pos/use-pos";
|
|
17
17
|
import { ShiftsTab } from "./shifts-tab";
|
|
18
18
|
import { TerminalsTab } from "./terminals-tab";
|
|
19
|
+
import { TransactionsTab } from "./transactions-tab";
|
|
19
20
|
const TABS = [
|
|
20
21
|
{ id: "terminals", label: "Terminals" },
|
|
21
|
-
|
|
22
|
+
// The merchant's POS page calls these sessions, here POS sessions; the address keeps `shifts` so old links still open.
|
|
23
|
+
{ id: "shifts", label: "POS sessions" },
|
|
24
|
+
{ id: "transactions", label: "Transactions" }
|
|
22
25
|
];
|
|
23
26
|
const KEYS = TABS.map((t) => t.id);
|
|
24
27
|
function PosWorkspace() {
|
|
@@ -57,7 +60,8 @@ function PosWorkspace() {
|
|
|
57
60
|
/* @__PURE__ */ jsx(PosFigures, { period, phrase, currency }),
|
|
58
61
|
/* @__PURE__ */ jsx(PageTabs, { tabs: TABS, active: tab, onChange: setTab }),
|
|
59
62
|
tab === "terminals" && /* @__PURE__ */ jsx(TerminalsTab, { period, currency }),
|
|
60
|
-
tab === "shifts" && /* @__PURE__ */ jsx(ShiftsTab, { currency })
|
|
63
|
+
tab === "shifts" && /* @__PURE__ */ jsx(ShiftsTab, { currency }),
|
|
64
|
+
tab === "transactions" && /* @__PURE__ */ jsx(TransactionsTab, {})
|
|
61
65
|
] });
|
|
62
66
|
}
|
|
63
67
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/pos/pos-workspace.tsx"],"sourcesContent":["'use client';\n\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport {\n AdmLoading,\n CurrencyFilter,\n PageHeader,\n PageTabs,\n currenciesIn,\n useCurrencyParam,\n useTabParam,\n} from '../_ui';\nimport { PosFigures } from '../_pos/pos-figures';\nimport { usePosAccess } from '../_pos/use-pos-access';\nimport { usePosPeriod } from '../_pos/use-pos-period';\nimport { usePosSummary } from '../_pos/use-pos';\nimport { ShiftsTab } from './shifts-tab';\nimport { TerminalsTab } from './terminals-tab';\n\n/**\n * Box's POS page has an Orders tab for hardware orders. Olive sells no\n * terminals through the admin, so it has no such tab.\n */\nconst TABS = [\n { id: 'terminals', label: 'Terminals' },\n { id: 'shifts', label: '
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/pos/pos-workspace.tsx"],"sourcesContent":["'use client';\n\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport {\n AdmLoading,\n CurrencyFilter,\n PageHeader,\n PageTabs,\n currenciesIn,\n useCurrencyParam,\n useTabParam,\n} from '../_ui';\nimport { PosFigures } from '../_pos/pos-figures';\nimport { usePosAccess } from '../_pos/use-pos-access';\nimport { usePosPeriod } from '../_pos/use-pos-period';\nimport { usePosSummary } from '../_pos/use-pos';\nimport { ShiftsTab } from './shifts-tab';\nimport { TerminalsTab } from './terminals-tab';\nimport { TransactionsTab } from './transactions-tab';\n\n/**\n * Box's POS page has an Orders tab for hardware orders. Olive sells no\n * terminals through the admin, so it has no such tab.\n */\nconst TABS = [\n { id: 'terminals', label: 'Terminals' },\n // The merchant's POS page calls these sessions, here POS sessions; the address keeps `shifts` so old links still open.\n { id: 'shifts', label: 'POS sessions' },\n { id: 'transactions', label: 'Transactions' },\n] as const;\n\nconst KEYS = TABS.map((t) => t.id);\n\n/**\n * POS across every business: the fleet's figures, then every terminal and\n * every till shift. A terminal opens on its own page.\n */\nexport function PosWorkspace() {\n const [tab, setTab] = useTabParam(KEYS);\n const { isLoading, canRead } = usePosAccess();\n const { period, phrase, control } = usePosPeriod();\n const [currency, setCurrency] = useCurrencyParam();\n // The figures' own request (one shared query): its currencies are the\n // filter's choices.\n const summary = usePosSummary(period, canRead);\n const currencies = currenciesIn(\n summary.data?.paymentsAmount.map((a) => a.currency),\n );\n\n if (isLoading) return <AdmLoading label=\"Loading POS…\" />;\n\n if (!canRead)\n return (\n <div>\n <PageHeader title=\"POS\" />\n <EmptyNote well>{\"You don't have permission to see POS.\"}</EmptyNote>\n </div>\n );\n\n return (\n <div>\n <PageHeader\n title=\"POS\"\n actions={\n <div className=\"adm-toolbar\">\n <CurrencyFilter\n value={currency}\n onChange={setCurrency}\n currencies={currencies}\n />\n {control}\n </div>\n }\n />\n <PosFigures period={period} phrase={phrase} currency={currency} />\n <PageTabs tabs={TABS} active={tab} onChange={setTab} />\n {tab === 'terminals' && (\n <TerminalsTab period={period} currency={currency} />\n )}\n {tab === 'shifts' && <ShiftsTab currency={currency} />}\n {tab === 'transactions' && <TransactionsTab />}\n </div>\n );\n}\n"],"mappings":";AAiDwB,cAIlB,YAJkB;AA/CxB,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAMhC,MAAM,OAAO;AAAA,EACX,EAAE,IAAI,aAAa,OAAO,YAAY;AAAA;AAAA,EAEtC,EAAE,IAAI,UAAU,OAAO,eAAe;AAAA,EACtC,EAAE,IAAI,gBAAgB,OAAO,eAAe;AAC9C;AAEA,MAAM,OAAO,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE;AAM1B,SAAS,eAAe;AAC7B,QAAM,CAAC,KAAK,MAAM,IAAI,YAAY,IAAI;AACtC,QAAM,EAAE,WAAW,QAAQ,IAAI,aAAa;AAC5C,QAAM,EAAE,QAAQ,QAAQ,QAAQ,IAAI,aAAa;AACjD,QAAM,CAAC,UAAU,WAAW,IAAI,iBAAiB;AAGjD,QAAM,UAAU,cAAc,QAAQ,OAAO;AAC7C,QAAM,aAAa;AAAA,IACjB,QAAQ,MAAM,eAAe,IAAI,CAAC,MAAM,EAAE,QAAQ;AAAA,EACpD;AAEA,MAAI,UAAW,QAAO,oBAAC,cAAW,OAAM,qBAAe;AAEvD,MAAI,CAAC;AACH,WACE,qBAAC,SACC;AAAA,0BAAC,cAAW,OAAM,OAAM;AAAA,MACxB,oBAAC,aAAU,MAAI,MAAE,mDAAwC;AAAA,OAC3D;AAGJ,SACE,qBAAC,SACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,SACE,qBAAC,SAAI,WAAU,eACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU;AAAA,cACV;AAAA;AAAA,UACF;AAAA,UACC;AAAA,WACH;AAAA;AAAA,IAEJ;AAAA,IACA,oBAAC,cAAW,QAAgB,QAAgB,UAAoB;AAAA,IAChE,oBAAC,YAAS,MAAM,MAAM,QAAQ,KAAK,UAAU,QAAQ;AAAA,IACpD,QAAQ,eACP,oBAAC,gBAAa,QAAgB,UAAoB;AAAA,IAEnD,QAAQ,YAAY,oBAAC,aAAU,UAAoB;AAAA,IACnD,QAAQ,kBAAkB,oBAAC,mBAAgB;AAAA,KAC9C;AAEJ;","names":[]}
|
|
@@ -22,7 +22,7 @@ const STATUS_OPTIONS = [
|
|
|
22
22
|
function ShiftsTab({
|
|
23
23
|
currency = "",
|
|
24
24
|
tenantId: fixedTenantId,
|
|
25
|
-
title = "
|
|
25
|
+
title = "POS sessions"
|
|
26
26
|
}) {
|
|
27
27
|
const [paramTenantId, setTenantId] = useTenantParam();
|
|
28
28
|
const tenantId = fixedTenantId ?? paramTenantId;
|
|
@@ -54,7 +54,7 @@ function ShiftsTab({
|
|
|
54
54
|
isError,
|
|
55
55
|
error,
|
|
56
56
|
onRetry: () => void refetch(),
|
|
57
|
-
emptyTitle: narrowed ? "No
|
|
57
|
+
emptyTitle: narrowed ? "No POS sessions match." : "No POS sessions yet.",
|
|
58
58
|
controls: /* @__PURE__ */ jsxs(Filters, { label: "Filter shifts", children: [
|
|
59
59
|
tenantId && !fixedTenantId && /* @__PURE__ */ jsx(
|
|
60
60
|
RemovableChip,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/pos/shifts-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button, RemovableChip } from '@withpotter/ui-kit';\nimport {\n POS_PAGE_SIZE,\n type ShiftFilters,\n} from '@withpotter/lib/admin-pos-query';\nimport type { PosShiftStatus } from '@withpotter/lib/admin-pos-types';\nimport { FilterPress, FilterSelect, Filters } from '../_ui';\nimport { ListPager } from '../_ui/list-card';\nimport { LONG_OPEN_HOURS } from '../_pos/pos-rules';\nimport { SHIFT_STATUS_LABEL } from '../_pos/pos-words';\nimport { ShiftsTable } from '../_pos/shifts-table';\nimport { usePosShifts } from '../_pos/use-pos';\nimport { useTenantParam } from '../_pos/use-tenant-param';\n\nconst STATUS_OPTIONS = [\n { value: '', label: 'Any status' },\n ...Object.entries(SHIFT_STATUS_LABEL).map(([value, label]) => ({\n value,\n label,\n })),\n];\n\n/** Open and recent shifts across every business, newest first. */\nexport function ShiftsTab({\n currency = '',\n tenantId: fixedTenantId,\n title = '
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/pos/shifts-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button, RemovableChip } from '@withpotter/ui-kit';\nimport {\n POS_PAGE_SIZE,\n type ShiftFilters,\n} from '@withpotter/lib/admin-pos-query';\nimport type { PosShiftStatus } from '@withpotter/lib/admin-pos-types';\nimport { FilterPress, FilterSelect, Filters } from '../_ui';\nimport { ListPager } from '../_ui/list-card';\nimport { LONG_OPEN_HOURS } from '../_pos/pos-rules';\nimport { SHIFT_STATUS_LABEL } from '../_pos/pos-words';\nimport { ShiftsTable } from '../_pos/shifts-table';\nimport { usePosShifts } from '../_pos/use-pos';\nimport { useTenantParam } from '../_pos/use-tenant-param';\n\nconst STATUS_OPTIONS = [\n { value: '', label: 'Any status' },\n ...Object.entries(SHIFT_STATUS_LABEL).map(([value, label]) => ({\n value,\n label,\n })),\n];\n\n/** Open and recent shifts across every business, newest first. */\nexport function ShiftsTab({\n currency = '',\n tenantId: fixedTenantId,\n title = 'POS sessions',\n}: {\n currency?: string;\n /** Fixes the list to one business (its own page); otherwise `?tenant=` narrows it. */\n tenantId?: string;\n title?: string;\n}) {\n const [paramTenantId, setTenantId] = useTenantParam();\n const tenantId = fixedTenantId ?? paramTenantId;\n const [status, setStatus] = useState<PosShiftStatus | ''>('');\n const [longOpen, setLongOpen] = useState(false);\n const [page, setPage] = useState(1);\n const filters: ShiftFilters = {\n status: longOpen ? 'open' : status,\n openOverHours: longOpen ? LONG_OPEN_HOURS : undefined,\n tenantId: tenantId || undefined,\n page,\n limit: POS_PAGE_SIZE,\n };\n const { data, isLoading, isError, error, refetch } = usePosShifts(filters);\n const narrowed = !!(status || longOpen || (tenantId && !fixedTenantId));\n const reset = (fn: () => void) => {\n fn();\n setPage(1);\n };\n\n return (\n <ShiftsTable\n title={title}\n rows={data?.data}\n total={data?.total}\n withBusiness={!fixedTenantId}\n currency={currency}\n isLoading={isLoading}\n isError={isError}\n error={error}\n onRetry={() => void refetch()}\n emptyTitle={narrowed ? 'No POS sessions match.' : 'No POS sessions yet.'}\n controls={\n <Filters label=\"Filter shifts\">\n {tenantId && !fixedTenantId && (\n <RemovableChip\n onRemove={() => reset(() => setTenantId(''))}\n removeLabel=\"Show every business\"\n >\n One business\n </RemovableChip>\n )}\n <FilterPress\n on={longOpen}\n onToggle={() => reset(() => setLongOpen((v) => !v))}\n >\n Open over {LONG_OPEN_HOURS} hours\n </FilterPress>\n {!longOpen && (\n <FilterSelect\n label=\"Status\"\n value={status}\n onChange={(v) => reset(() => setStatus(v as PosShiftStatus | ''))}\n options={STATUS_OPTIONS}\n />\n )}\n {narrowed && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onClick={() =>\n reset(() => {\n setStatus('');\n setLongOpen(false);\n if (!fixedTenantId) setTenantId('');\n })\n }\n >\n Clear filters\n </Button>\n )}\n </Filters>\n }\n footer={\n data && data.total > 0 ? (\n <ListPager\n page={page}\n totalPages={Math.max(1, data.totalPages)}\n total={data.total}\n noun={data.total === 1 ? 'shift' : 'shifts'}\n onPage={setPage}\n />\n ) : undefined\n }\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { Badge, Button, TableRow, Th } from "@withpotter/ui-kit";
|
|
5
|
+
import { formatMinor } from "@withpotter/lib/money-minor";
|
|
6
|
+
import { ListCard } from "../_ui/list-card";
|
|
7
|
+
import list from "../_ui/list-card.module.css";
|
|
8
|
+
import { isForbidden, posErrorText } from "../_pos/pos-rules";
|
|
9
|
+
import { atTime } from "../_pos/pos-words";
|
|
10
|
+
import { usePosPayments, usePosTerminals } from "../_pos/use-pos";
|
|
11
|
+
import pos from "../_pos/pos.module.css";
|
|
12
|
+
function TransactionsTab() {
|
|
13
|
+
const q = usePosPayments();
|
|
14
|
+
const terminals = usePosTerminals({ limit: 100 });
|
|
15
|
+
const rows = useMemo(
|
|
16
|
+
() => q.data?.pages.flatMap((p) => p.rows) ?? [],
|
|
17
|
+
[q.data]
|
|
18
|
+
);
|
|
19
|
+
const names = useMemo(
|
|
20
|
+
() => new Map(terminals.data?.data.map((t) => [t.tenant.id, t.tenant.name])),
|
|
21
|
+
[terminals.data]
|
|
22
|
+
);
|
|
23
|
+
return /* @__PURE__ */ jsx(
|
|
24
|
+
ListCard,
|
|
25
|
+
{
|
|
26
|
+
title: "Transactions",
|
|
27
|
+
count: q.hasNextPage ? void 0 : rows.length,
|
|
28
|
+
columns: 6,
|
|
29
|
+
minWidth: 900,
|
|
30
|
+
isLoading: q.isLoading,
|
|
31
|
+
isError: q.isError && !q.data,
|
|
32
|
+
onRetry: () => void q.refetch(),
|
|
33
|
+
errorTitle: isForbidden(q.error) ? "You don't have permission to see transactions." : posErrorText(q.error, "Transactions did not load."),
|
|
34
|
+
isEmpty: rows.length === 0,
|
|
35
|
+
emptyTitle: "No card payments yet.",
|
|
36
|
+
head: /* @__PURE__ */ jsxs("tr", { children: [
|
|
37
|
+
/* @__PURE__ */ jsx(Th, { children: "Business" }),
|
|
38
|
+
/* @__PURE__ */ jsx(Th, { children: "Terminal" }),
|
|
39
|
+
/* @__PURE__ */ jsx(Th, { children: "Reference" }),
|
|
40
|
+
/* @__PURE__ */ jsx(Th, { children: "Status" }),
|
|
41
|
+
/* @__PURE__ */ jsx(Th, { children: "Amount" }),
|
|
42
|
+
/* @__PURE__ */ jsx(Th, { children: "When" })
|
|
43
|
+
] }),
|
|
44
|
+
footer: q.hasNextPage ? /* @__PURE__ */ jsx("div", { className: pos.stack, children: /* @__PURE__ */ jsx(
|
|
45
|
+
Button,
|
|
46
|
+
{
|
|
47
|
+
size: "sm",
|
|
48
|
+
variant: "secondary",
|
|
49
|
+
disabled: q.isFetchingNextPage,
|
|
50
|
+
onClick: () => void q.fetchNextPage(),
|
|
51
|
+
children: q.isFetchingNextPage ? "Loading\u2026" : "Show more"
|
|
52
|
+
}
|
|
53
|
+
) }) : void 0,
|
|
54
|
+
children: rows.map((p) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
55
|
+
/* @__PURE__ */ jsx("td", { children: names.get(p.tenantId) ?? "\u2013" }),
|
|
56
|
+
/* @__PURE__ */ jsx("td", { children: p.deviceLabel ?? "\u2013" }),
|
|
57
|
+
/* @__PURE__ */ jsx("td", { className: list.muted, children: p.reference ?? "\u2013" }),
|
|
58
|
+
/* @__PURE__ */ jsx("td", { className: list.nowrap, children: /* @__PURE__ */ jsx(Badge, { variant: p.status === "SUCCEEDED" ? "success" : "secondary", children: p.status.charAt(0) + p.status.slice(1).toLowerCase() }) }),
|
|
59
|
+
/* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${pos.num}`, children: `${p.direction === "out" ? "-" : ""}${formatMinor(
|
|
60
|
+
p.amountMinor,
|
|
61
|
+
p.currency
|
|
62
|
+
)}` }),
|
|
63
|
+
/* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: atTime(p.at) })
|
|
64
|
+
] }, p.id))
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
TransactionsTab
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=transactions-tab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/pos/transactions-tab.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { Badge, Button, TableRow, Th } from '@withpotter/ui-kit';\nimport { formatMinor } from '@withpotter/lib/money-minor';\nimport { ListCard } from '../_ui/list-card';\nimport list from '../_ui/list-card.module.css';\nimport { isForbidden, posErrorText } from '../_pos/pos-rules';\nimport { atTime } from '../_pos/pos-words';\nimport { usePosPayments, usePosTerminals } from '../_pos/use-pos';\nimport pos from '../_pos/pos.module.css';\n\n/**\n * Card payments taken on every business's terminals, newest first. The payments carry the business's\n * id, not its name, so the names come from the terminals (the API's page is as large as it allows).\n */\nexport function TransactionsTab() {\n const q = usePosPayments();\n const terminals = usePosTerminals({ limit: 100 });\n const rows = useMemo(\n () => q.data?.pages.flatMap((p) => p.rows) ?? [],\n [q.data],\n );\n const names = useMemo(\n () =>\n new Map(terminals.data?.data.map((t) => [t.tenant.id, t.tenant.name])),\n [terminals.data],\n );\n\n return (\n <ListCard\n title=\"Transactions\"\n count={q.hasNextPage ? undefined : rows.length}\n columns={6}\n minWidth={900}\n isLoading={q.isLoading}\n isError={q.isError && !q.data}\n onRetry={() => void q.refetch()}\n errorTitle={\n isForbidden(q.error)\n ? \"You don't have permission to see transactions.\"\n : posErrorText(q.error, 'Transactions did not load.')\n }\n isEmpty={rows.length === 0}\n emptyTitle=\"No card payments yet.\"\n head={\n <tr>\n <Th>Business</Th>\n <Th>Terminal</Th>\n <Th>Reference</Th>\n <Th>Status</Th>\n <Th>Amount</Th>\n <Th>When</Th>\n </tr>\n }\n footer={\n q.hasNextPage ? (\n <div className={pos.stack}>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n disabled={q.isFetchingNextPage}\n onClick={() => void q.fetchNextPage()}\n >\n {q.isFetchingNextPage ? 'Loading…' : 'Show more'}\n </Button>\n </div>\n ) : undefined\n }\n >\n {rows.map((p) => (\n <TableRow key={p.id}>\n <td>{names.get(p.tenantId) ?? '–'}</td>\n <td>{p.deviceLabel ?? '–'}</td>\n <td className={list.muted}>{p.reference ?? '–'}</td>\n <td className={list.nowrap}>\n <Badge variant={p.status === 'SUCCEEDED' ? 'success' : 'secondary'}>\n {p.status.charAt(0) + p.status.slice(1).toLowerCase()}\n </Badge>\n </td>\n <td className={`${list.nowrap} ${pos.num}`}>\n {`${p.direction === 'out' ? '-' : ''}${formatMinor(\n p.amountMinor,\n p.currency,\n )}`}\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>{atTime(p.at)}</td>\n </TableRow>\n ))}\n </ListCard>\n );\n}\n"],"mappings":";AA8CQ,SACE,KADF;AA5CR,SAAS,eAAe;AACxB,SAAS,OAAO,QAAQ,UAAU,UAAU;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,aAAa,oBAAoB;AAC1C,SAAS,cAAc;AACvB,SAAS,gBAAgB,uBAAuB;AAChD,OAAO,SAAS;AAMT,SAAS,kBAAkB;AAChC,QAAM,IAAI,eAAe;AACzB,QAAM,YAAY,gBAAgB,EAAE,OAAO,IAAI,CAAC;AAChD,QAAM,OAAO;AAAA,IACX,MAAM,EAAE,MAAM,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC;AAAA,IAC/C,CAAC,EAAE,IAAI;AAAA,EACT;AACA,QAAM,QAAQ;AAAA,IACZ,MACE,IAAI,IAAI,UAAU,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC;AAAA,IACvE,CAAC,UAAU,IAAI;AAAA,EACjB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO,EAAE,cAAc,SAAY,KAAK;AAAA,MACxC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW,EAAE;AAAA,MACb,SAAS,EAAE,WAAW,CAAC,EAAE;AAAA,MACzB,SAAS,MAAM,KAAK,EAAE,QAAQ;AAAA,MAC9B,YACE,YAAY,EAAE,KAAK,IACf,mDACA,aAAa,EAAE,OAAO,4BAA4B;AAAA,MAExD,SAAS,KAAK,WAAW;AAAA,MACzB,YAAW;AAAA,MACX,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,uBAAS;AAAA,QACb,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,kBAAI;AAAA,SACV;AAAA,MAEF,QACE,EAAE,cACA,oBAAC,SAAI,WAAW,IAAI,OAClB;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,UAAU,EAAE;AAAA,UACZ,SAAS,MAAM,KAAK,EAAE,cAAc;AAAA,UAEnC,YAAE,qBAAqB,kBAAa;AAAA;AAAA,MACvC,GACF,IACE;AAAA,MAGL,eAAK,IAAI,CAAC,MACT,qBAAC,YACC;AAAA,4BAAC,QAAI,gBAAM,IAAI,EAAE,QAAQ,KAAK,UAAI;AAAA,QAClC,oBAAC,QAAI,YAAE,eAAe,UAAI;AAAA,QAC1B,oBAAC,QAAG,WAAW,KAAK,OAAQ,YAAE,aAAa,UAAI;AAAA,QAC/C,oBAAC,QAAG,WAAW,KAAK,QAClB,8BAAC,SAAM,SAAS,EAAE,WAAW,cAAc,YAAY,aACpD,YAAE,OAAO,OAAO,CAAC,IAAI,EAAE,OAAO,MAAM,CAAC,EAAE,YAAY,GACtD,GACF;AAAA,QACA,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,IAAI,GAAG,IACrC,aAAG,EAAE,cAAc,QAAQ,MAAM,EAAE,GAAG;AAAA,UACrC,EAAE;AAAA,UACF,EAAE;AAAA,QACJ,CAAC,IACH;AAAA,QACA,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IAAK,iBAAO,EAAE,EAAE,GAAE;AAAA,WAfhD,EAAE,EAgBjB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withpotter/pages-admin",
|
|
3
|
-
"version": "0.13.0-next.
|
|
3
|
+
"version": "0.13.0-next.110",
|
|
4
4
|
"description": "Box console platform-admin routes: overview, revenue, accounts, tenants, admins, roles, audit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@radix-ui/react-popover": "^1.1.23",
|
|
33
33
|
"@tanstack/react-query": "^5.90.20",
|
|
34
|
-
"@withpotter/lib": "^0.13.0-next.
|
|
35
|
-
"@withpotter/platform-admin": "^0.13.0-next.
|
|
36
|
-
"@withpotter/ui-kit": "^0.13.0-next.
|
|
34
|
+
"@withpotter/lib": "^0.13.0-next.110",
|
|
35
|
+
"@withpotter/platform-admin": "^0.13.0-next.110",
|
|
36
|
+
"@withpotter/ui-kit": "^0.13.0-next.110",
|
|
37
37
|
"lucide-react": "^0.562.0",
|
|
38
38
|
"motion": "^12.42.2"
|
|
39
39
|
},
|