@withpotter/pages-admin 0.13.0-next.102 → 0.13.0-next.103

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.
@@ -0,0 +1,73 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { TableRow, Th } from "@withpotter/ui-kit";
5
+ import { ListCard } from "../_ui/list-card";
6
+ import list from "../_ui/list-card.module.css";
7
+ import { posErrorText } from "./pos-rules";
8
+ import { accessLine } from "./pos-words";
9
+ import { useTenantOperators } from "./use-pos";
10
+ import pos from "./pos.module.css";
11
+ const sentence = (s) => s ? s.charAt(0).toUpperCase() + s.slice(1).replace(/_/g, " ") : s;
12
+ function TenantOperators({
13
+ terminals,
14
+ period
15
+ }) {
16
+ const live = useMemo(
17
+ () => terminals.filter((t) => t.status !== "revoked"),
18
+ [terminals]
19
+ );
20
+ const results = useTenantOperators(
21
+ live.map((t) => t.id),
22
+ period
23
+ );
24
+ const isLoading = results.some((r) => r.isLoading);
25
+ const failed = results.find((r) => r.isError);
26
+ const people = useMemo(() => {
27
+ const byId = /* @__PURE__ */ new Map();
28
+ for (const r of results) {
29
+ const t = r.data;
30
+ if (!t) continue;
31
+ for (const o of t.operators) {
32
+ const row = byId.get(o.id) ?? { ...o, at: [] };
33
+ row.at.push(t.label);
34
+ byId.set(o.id, row);
35
+ }
36
+ }
37
+ return [...byId.values()].sort((a, b) => a.name.localeCompare(b.name));
38
+ }, [results.map((r) => r.dataUpdatedAt).join(",")]);
39
+ const open = live.some((t) => t.operatorAccess === "any_member");
40
+ return /* @__PURE__ */ jsx(
41
+ ListCard,
42
+ {
43
+ title: "Operators",
44
+ count: people.length,
45
+ columns: 3,
46
+ minWidth: 560,
47
+ isLoading,
48
+ isError: !!failed && people.length === 0,
49
+ onRetry: () => results.forEach((r) => void r.refetch()),
50
+ errorTitle: posErrorText(failed?.error, "Operators did not load."),
51
+ isEmpty: people.length === 0,
52
+ emptyTitle: live.length === 0 ? "This business has no terminals, so no operators." : "Nobody can sign in at its terminals yet.",
53
+ controls: live[0] ? /* @__PURE__ */ jsx("span", { className: "adm-caption", children: open ? accessLine({ operatorAccess: "any_member" }) : accessLine(live[0]) }) : void 0,
54
+ head: /* @__PURE__ */ jsxs("tr", { children: [
55
+ /* @__PURE__ */ jsx(Th, { children: "Person" }),
56
+ /* @__PURE__ */ jsx(Th, { children: "Role" }),
57
+ /* @__PURE__ */ jsx(Th, { children: "Terminals" })
58
+ ] }),
59
+ children: people.map((o) => /* @__PURE__ */ jsxs(TableRow, { children: [
60
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: pos.cellStack, children: [
61
+ /* @__PURE__ */ jsx("span", { className: pos.clip, children: o.name }),
62
+ !o.active && /* @__PURE__ */ jsx("span", { className: pos.subtle, children: "Removed" })
63
+ ] }) }),
64
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: sentence(o.role) }),
65
+ /* @__PURE__ */ jsx("td", { className: list.muted, children: o.at.join(", ") })
66
+ ] }, o.id))
67
+ }
68
+ );
69
+ }
70
+ export {
71
+ TenantOperators
72
+ };
73
+ //# sourceMappingURL=tenant-operators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/_pos/tenant-operators.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { TableRow, Th } from '@withpotter/ui-kit';\nimport type { PosTerminal, PosWindow } from '@withpotter/lib/admin-pos-types';\nimport { ListCard } from '../_ui/list-card';\nimport list from '../_ui/list-card.module.css';\nimport { posErrorText } from './pos-rules';\nimport { accessLine } from './pos-words';\nimport { useTenantOperators } from './use-pos';\nimport pos from './pos.module.css';\n\nconst sentence = (s: string) =>\n s ? s.charAt(0).toUpperCase() + s.slice(1).replace(/_/g, ' ') : s;\n\n/**\n * Everyone who can sign in at the business's terminals, once each, with the\n * terminals they can use. Read from each terminal's own record.\n */\nexport function TenantOperators({\n terminals,\n period,\n}: {\n terminals: PosTerminal[];\n period: PosWindow;\n}) {\n const live = useMemo(\n () => terminals.filter((t) => t.status !== 'revoked'),\n [terminals],\n );\n const results = useTenantOperators(\n live.map((t) => t.id),\n period,\n );\n const isLoading = results.some((r) => r.isLoading);\n const failed = results.find((r) => r.isError);\n\n const people = useMemo(() => {\n const byId = new Map<\n string,\n { id: string; name: string; role: string; active: boolean; at: string[] }\n >();\n\n for (const r of results) {\n const t = r.data;\n\n if (!t) continue;\n\n for (const o of t.operators) {\n const row = byId.get(o.id) ?? { ...o, at: [] };\n\n row.at.push(t.label);\n byId.set(o.id, row);\n }\n }\n\n return [...byId.values()].sort((a, b) => a.name.localeCompare(b.name));\n // `results` is a new array each render; its data changes are what matter.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [results.map((r) => r.dataUpdatedAt).join(',')]);\n\n const open = live.some((t) => t.operatorAccess === 'any_member');\n\n return (\n <ListCard\n title=\"Operators\"\n count={people.length}\n columns={3}\n minWidth={560}\n isLoading={isLoading}\n isError={!!failed && people.length === 0}\n onRetry={() => results.forEach((r) => void r.refetch())}\n errorTitle={posErrorText(failed?.error, 'Operators did not load.')}\n isEmpty={people.length === 0}\n emptyTitle={\n live.length === 0\n ? 'This business has no terminals, so no operators.'\n : 'Nobody can sign in at its terminals yet.'\n }\n controls={\n live[0] ? (\n <span className=\"adm-caption\">\n {open\n ? accessLine({ operatorAccess: 'any_member' })\n : accessLine(live[0])}\n </span>\n ) : undefined\n }\n head={\n <tr>\n <Th>Person</Th>\n <Th>Role</Th>\n <Th>Terminals</Th>\n </tr>\n }\n >\n {people.map((o) => (\n <TableRow key={o.id}>\n <td>\n <span className={pos.cellStack}>\n <span className={pos.clip}>{o.name}</span>\n {!o.active && <span className={pos.subtle}>Removed</span>}\n </span>\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>{sentence(o.role)}</td>\n <td className={list.muted}>{o.at.join(', ')}</td>\n </TableRow>\n ))}\n </ListCard>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,82 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { StatCard } from "../_ui";
4
+ import { amountLines } from "./pos-money";
5
+ import { isLongOpen } from "./pos-rules";
6
+ import { inPeriod, onlineTone } from "./pos-words";
7
+ import styles from "./pos.module.css";
8
+ function TenantPosFigures({
9
+ data,
10
+ phrase,
11
+ currency = ""
12
+ }) {
13
+ const live = data.terminals.filter((t) => t.status !== "revoked");
14
+ const online = live.filter((t) => t.online).length;
15
+ const offlineOverDay = live.filter((t) => {
16
+ const tone = onlineTone(t);
17
+ return tone === "stale" || tone === "never";
18
+ }).length;
19
+ const suspended = live.filter((t) => t.status === "suspended").length;
20
+ const longOpen = data.openShifts.filter(isLongOpen).length;
21
+ const money = amountLines(data.takings.amounts, currency);
22
+ return /* @__PURE__ */ jsxs(
23
+ "section",
24
+ {
25
+ className: styles.figures,
26
+ "aria-label": "This business's POS at a glance",
27
+ children: [
28
+ /* @__PURE__ */ jsx(
29
+ StatCard,
30
+ {
31
+ compact: true,
32
+ label: "Terminals",
33
+ value: String(live.length),
34
+ sub: `${data.terminals.length - live.length} revoked`
35
+ }
36
+ ),
37
+ /* @__PURE__ */ jsx(
38
+ StatCard,
39
+ {
40
+ compact: true,
41
+ label: "Online now",
42
+ value: String(online),
43
+ sub: `Of ${live.length}`
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsx(
47
+ StatCard,
48
+ {
49
+ compact: true,
50
+ label: "Offline over a day",
51
+ value: String(offlineOverDay),
52
+ alert: offlineOverDay > 0
53
+ }
54
+ ),
55
+ /* @__PURE__ */ jsx(StatCard, { compact: true, label: "Suspended", value: String(suspended) }),
56
+ /* @__PURE__ */ jsx(
57
+ StatCard,
58
+ {
59
+ compact: true,
60
+ label: "Open shifts",
61
+ value: String(data.openShifts.length),
62
+ sub: longOpen ? `${longOpen} open over 12 h` : void 0,
63
+ alert: longOpen > 0
64
+ }
65
+ ),
66
+ /* @__PURE__ */ jsx(
67
+ StatCard,
68
+ {
69
+ compact: true,
70
+ label: "Card payments",
71
+ value: data.takings.count.toLocaleString("en-US"),
72
+ sub: money.length ? `${money.join(" \xB7 ")}, ${phrase}` : `None ${inPeriod(phrase)}`
73
+ }
74
+ )
75
+ ]
76
+ }
77
+ );
78
+ }
79
+ export {
80
+ TenantPosFigures
81
+ };
82
+ //# sourceMappingURL=tenant-pos-figures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/_pos/tenant-pos-figures.tsx"],"sourcesContent":["'use client';\n\nimport type { TenantPos } from '@withpotter/lib/admin-pos-types';\nimport { StatCard } from '../_ui';\nimport { amountLines } from './pos-money';\nimport { isLongOpen } from './pos-rules';\nimport { inPeriod, onlineTone } from './pos-words';\nimport styles from './pos.module.css';\n\n/**\n * One business's POS at a glance, worked out from its own terminals and\n * shifts, so it matches what the merchant's POS page counts.\n */\nexport function TenantPosFigures({\n data,\n phrase,\n currency = '',\n}: {\n data: TenantPos;\n phrase: string;\n currency?: string;\n}) {\n const live = data.terminals.filter((t) => t.status !== 'revoked');\n const online = live.filter((t) => t.online).length;\n const offlineOverDay = live.filter((t) => {\n const tone = onlineTone(t);\n\n return tone === 'stale' || tone === 'never';\n }).length;\n const suspended = live.filter((t) => t.status === 'suspended').length;\n const longOpen = data.openShifts.filter(isLongOpen).length;\n const money = amountLines(data.takings.amounts, currency);\n\n return (\n <section\n className={styles.figures}\n aria-label=\"This business's POS at a glance\"\n >\n <StatCard\n compact\n label=\"Terminals\"\n value={String(live.length)}\n sub={`${data.terminals.length - live.length} revoked`}\n />\n <StatCard\n compact\n label=\"Online now\"\n value={String(online)}\n sub={`Of ${live.length}`}\n />\n <StatCard\n compact\n label=\"Offline over a day\"\n value={String(offlineOverDay)}\n alert={offlineOverDay > 0}\n />\n <StatCard compact label=\"Suspended\" value={String(suspended)} />\n <StatCard\n compact\n label=\"Open shifts\"\n value={String(data.openShifts.length)}\n sub={longOpen ? `${longOpen} open over 12 h` : undefined}\n alert={longOpen > 0}\n />\n <StatCard\n compact\n label=\"Card payments\"\n value={data.takings.count.toLocaleString('en-US')}\n sub={\n money.length\n ? `${money.join(' · ')}, ${phrase}`\n : `None ${inPeriod(phrase)}`\n }\n />\n </section>\n );\n}\n"],"mappings":";AAkCI,SAIE,KAJF;AA/BJ,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,UAAU,kBAAkB;AACrC,OAAO,YAAY;AAMZ,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAIG;AACD,QAAM,OAAO,KAAK,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS;AAChE,QAAM,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE;AAC5C,QAAM,iBAAiB,KAAK,OAAO,CAAC,MAAM;AACxC,UAAM,OAAO,WAAW,CAAC;AAEzB,WAAO,SAAS,WAAW,SAAS;AAAA,EACtC,CAAC,EAAE;AACH,QAAM,YAAY,KAAK,OAAO,CAAC,MAAM,EAAE,WAAW,WAAW,EAAE;AAC/D,QAAM,WAAW,KAAK,WAAW,OAAO,UAAU,EAAE;AACpD,QAAM,QAAQ,YAAY,KAAK,QAAQ,SAAS,QAAQ;AAExD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,OAAO;AAAA,MAClB,cAAW;AAAA,MAEX;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAO;AAAA,YACP,OAAM;AAAA,YACN,OAAO,OAAO,KAAK,MAAM;AAAA,YACzB,KAAK,GAAG,KAAK,UAAU,SAAS,KAAK,MAAM;AAAA;AAAA,QAC7C;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAO;AAAA,YACP,OAAM;AAAA,YACN,OAAO,OAAO,MAAM;AAAA,YACpB,KAAK,MAAM,KAAK,MAAM;AAAA;AAAA,QACxB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAO;AAAA,YACP,OAAM;AAAA,YACN,OAAO,OAAO,cAAc;AAAA,YAC5B,OAAO,iBAAiB;AAAA;AAAA,QAC1B;AAAA,QACA,oBAAC,YAAS,SAAO,MAAC,OAAM,aAAY,OAAO,OAAO,SAAS,GAAG;AAAA,QAC9D;AAAA,UAAC;AAAA;AAAA,YACC,SAAO;AAAA,YACP,OAAM;AAAA,YACN,OAAO,OAAO,KAAK,WAAW,MAAM;AAAA,YACpC,KAAK,WAAW,GAAG,QAAQ,oBAAoB;AAAA,YAC/C,OAAO,WAAW;AAAA;AAAA,QACpB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAO;AAAA,YACP,OAAM;AAAA,YACN,OAAO,KAAK,QAAQ,MAAM,eAAe,OAAO;AAAA,YAChD,KACE,MAAM,SACF,GAAG,MAAM,KAAK,QAAK,CAAC,KAAK,MAAM,KAC/B,QAAQ,SAAS,MAAM,CAAC;AAAA;AAAA,QAEhC;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  keepPreviousData,
4
4
  useMutation,
5
+ useQueries,
5
6
  useQuery,
6
7
  useQueryClient
7
8
  } from "@tanstack/react-query";
@@ -52,6 +53,16 @@ function usePosShifts(filters, enabled = true) {
52
53
  retry
53
54
  });
54
55
  }
56
+ function useTenantOperators(terminalIds, w, enabled = true) {
57
+ return useQueries({
58
+ queries: terminalIds.map((id) => ({
59
+ queryKey: key("terminal", id, w),
60
+ queryFn: () => adminPosApi.terminal(id, w),
61
+ enabled,
62
+ retry
63
+ }))
64
+ });
65
+ }
55
66
  function useTenantPos(tenantId, w, enabled = true) {
56
67
  return useQuery({
57
68
  queryKey: key("tenant", tenantId, w),
@@ -83,6 +94,7 @@ export {
83
94
  usePosSummary,
84
95
  usePosTerminal,
85
96
  usePosTerminals,
97
+ useTenantOperators,
86
98
  useTenantPos,
87
99
  useTerminalStatus
88
100
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/_pos/use-pos.ts"],"sourcesContent":["'use client';\n\nimport {\n keepPreviousData,\n useMutation,\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 } 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 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":";AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAqC;AAM9C,SAAS,aAAa,kBAAkB;AAGjC,MAAM,UAAU,CAAC,kBAAkB,KAAK;AAE/C,MAAM,MAAM,IAAI,UAAqB,CAAC,GAAG,SAAS,GAAG,KAAK;AAG1D,MAAM,QAAQ,CAAC,OAAe,UAC5B,CAAC,YAAY,KAAK,KAAK,CAAC,WAAW,KAAK,KAAK,QAAQ;AAEhD,SAAS,cAAc,GAAc,UAAU,MAAM;AAC1D,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,WAAW,CAAC;AAAA,IAC1B,SAAS,MAAM,YAAY,QAAQ,CAAC;AAAA,IACpC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,gBAAgB,SAA0B,UAAU,MAAM;AACxE,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,aAAa,OAAO;AAAA,IAClC,SAAS,MAAM,YAAY,UAAU,OAAO;AAAA,IAC5C,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,eAAe,IAAY,GAAc,UAAU,MAAM;AACvE,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,YAAY,IAAI,CAAC;AAAA,IAC/B,SAAS,MAAM,YAAY,SAAS,IAAI,CAAC;AAAA,IACzC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,cAAc,IAAY,UAAU,MAAM;AACxD,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,WAAW,EAAE;AAAA,IAC3B,SAAS,MAAM,YAAY,QAAQ,EAAE;AAAA,IACrC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,aAAa,SAAuB,UAAU,MAAM;AAClE,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,UAAU,OAAO;AAAA,IAC/B,SAAS,MAAM,YAAY,OAAO,OAAO;AAAA,IACzC,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,aAAa,UAAkB,GAAc,UAAU,MAAM;AAC3E,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,UAAU,UAAU,CAAC;AAAA,IACnC,SAAS,MAAM,YAAY,OAAO,UAAU,CAAC;AAAA,IAC7C;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAQA,SAAS,YAAkB,IAA0B;AACnD,QAAM,SAAS,eAAe;AAE9B,SAAO,YAAY;AAAA,IACjB,YAAY;AAAA,IACZ,WAAW,MAAM;AACf,WAAK,OAAO,kBAAkB,EAAE,UAAU,QAAQ,CAAC;AAAA,IACrD;AAAA,EACF,CAAC;AACH;AAEO,MAAM,oBAAoB,MAC/B;AAAA,EAAY,CAAC,MACX,YAAY,kBAAkB,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM;AACrD;AAEK,MAAM,gBAAgB,MAC3B;AAAA,EAAY,CAAC,MACX,YAAY,WAAW,EAAE,IAAI,EAAE,MAAM;AACvC;","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/_pos/use-pos.ts"],"sourcesContent":["'use client';\n\nimport {\n keepPreviousData,\n useMutation,\n useQueries,\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 } 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\n/**\n * A business's operators, gathered from its terminals: the admin API lists\n * operators per terminal, not per business, so each live terminal is asked\n * once (the same query its own page uses, so the cache is shared).\n */\nexport function useTenantOperators(\n terminalIds: string[],\n w: PosWindow,\n enabled = true,\n) {\n return useQueries({\n queries: terminalIds.map((id) => ({\n queryKey: key('terminal', id, w),\n queryFn: () => adminPosApi.terminal(id, w),\n enabled,\n retry,\n })),\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":";AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAqC;AAM9C,SAAS,aAAa,kBAAkB;AAGjC,MAAM,UAAU,CAAC,kBAAkB,KAAK;AAE/C,MAAM,MAAM,IAAI,UAAqB,CAAC,GAAG,SAAS,GAAG,KAAK;AAG1D,MAAM,QAAQ,CAAC,OAAe,UAC5B,CAAC,YAAY,KAAK,KAAK,CAAC,WAAW,KAAK,KAAK,QAAQ;AAEhD,SAAS,cAAc,GAAc,UAAU,MAAM;AAC1D,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,WAAW,CAAC;AAAA,IAC1B,SAAS,MAAM,YAAY,QAAQ,CAAC;AAAA,IACpC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,gBAAgB,SAA0B,UAAU,MAAM;AACxE,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,aAAa,OAAO;AAAA,IAClC,SAAS,MAAM,YAAY,UAAU,OAAO;AAAA,IAC5C,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,eAAe,IAAY,GAAc,UAAU,MAAM;AACvE,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,YAAY,IAAI,CAAC;AAAA,IAC/B,SAAS,MAAM,YAAY,SAAS,IAAI,CAAC;AAAA,IACzC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,cAAc,IAAY,UAAU,MAAM;AACxD,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,WAAW,EAAE;AAAA,IAC3B,SAAS,MAAM,YAAY,QAAQ,EAAE;AAAA,IACrC;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,aAAa,SAAuB,UAAU,MAAM;AAClE,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,UAAU,OAAO;AAAA,IAC/B,SAAS,MAAM,YAAY,OAAO,OAAO;AAAA,IACzC,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAOO,SAAS,mBACd,aACA,GACA,UAAU,MACV;AACA,SAAO,WAAW;AAAA,IAChB,SAAS,YAAY,IAAI,CAAC,QAAQ;AAAA,MAChC,UAAU,IAAI,YAAY,IAAI,CAAC;AAAA,MAC/B,SAAS,MAAM,YAAY,SAAS,IAAI,CAAC;AAAA,MACzC;AAAA,MACA;AAAA,IACF,EAAE;AAAA,EACJ,CAAC;AACH;AAEO,SAAS,aAAa,UAAkB,GAAc,UAAU,MAAM;AAC3E,SAAO,SAAS;AAAA,IACd,UAAU,IAAI,UAAU,UAAU,CAAC;AAAA,IACnC,SAAS,MAAM,YAAY,OAAO,UAAU,CAAC;AAAA,IAC7C;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAQA,SAAS,YAAkB,IAA0B;AACnD,QAAM,SAAS,eAAe;AAE9B,SAAO,YAAY;AAAA,IACjB,YAAY;AAAA,IACZ,WAAW,MAAM;AACf,WAAK,OAAO,kBAAkB,EAAE,UAAU,QAAQ,CAAC;AAAA,IACrD;AAAA,EACF,CAAC;AACH;AAEO,MAAM,oBAAoB,MAC/B;AAAA,EAAY,CAAC,MACX,YAAY,kBAAkB,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM;AACrD;AAEK,MAAM,gBAAgB,MAC3B;AAAA,EAAY,CAAC,MACX,YAAY,WAAW,EAAE,IAAI,EAAE,MAAM;AACvC;","names":[]}
@@ -2,7 +2,9 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Button, RemovableChip } from "@withpotter/ui-kit";
5
- import { POS_PAGE_SIZE } from "@withpotter/lib/admin-pos-query";
5
+ import {
6
+ POS_PAGE_SIZE
7
+ } from "@withpotter/lib/admin-pos-query";
6
8
  import { FilterPress, FilterSelect, Filters } from "../_ui";
7
9
  import { ListPager } from "../_ui/list-card";
8
10
  import { LONG_OPEN_HOURS } from "../_pos/pos-rules";
@@ -17,8 +19,13 @@ const STATUS_OPTIONS = [
17
19
  label
18
20
  }))
19
21
  ];
20
- function ShiftsTab({ currency = "" }) {
21
- const [tenantId, setTenantId] = useTenantParam();
22
+ function ShiftsTab({
23
+ currency = "",
24
+ tenantId: fixedTenantId,
25
+ title = "Shifts"
26
+ }) {
27
+ const [paramTenantId, setTenantId] = useTenantParam();
28
+ const tenantId = fixedTenantId ?? paramTenantId;
22
29
  const [status, setStatus] = useState("");
23
30
  const [longOpen, setLongOpen] = useState(false);
24
31
  const [page, setPage] = useState(1);
@@ -30,7 +37,7 @@ function ShiftsTab({ currency = "" }) {
30
37
  limit: POS_PAGE_SIZE
31
38
  };
32
39
  const { data, isLoading, isError, error, refetch } = usePosShifts(filters);
33
- const narrowed = !!(status || longOpen || tenantId);
40
+ const narrowed = !!(status || longOpen || tenantId && !fixedTenantId);
34
41
  const reset = (fn) => {
35
42
  fn();
36
43
  setPage(1);
@@ -38,10 +45,10 @@ function ShiftsTab({ currency = "" }) {
38
45
  return /* @__PURE__ */ jsx(
39
46
  ShiftsTable,
40
47
  {
41
- title: "Shifts",
48
+ title,
42
49
  rows: data?.data,
43
50
  total: data?.total,
44
- withBusiness: true,
51
+ withBusiness: !fixedTenantId,
45
52
  currency,
46
53
  isLoading,
47
54
  isError,
@@ -49,7 +56,7 @@ function ShiftsTab({ currency = "" }) {
49
56
  onRetry: () => void refetch(),
50
57
  emptyTitle: narrowed ? "No shifts match." : "No shifts yet.",
51
58
  controls: /* @__PURE__ */ jsxs(Filters, { label: "Filter shifts", children: [
52
- tenantId && /* @__PURE__ */ jsx(
59
+ tenantId && !fixedTenantId && /* @__PURE__ */ jsx(
53
60
  RemovableChip,
54
61
  {
55
62
  onRemove: () => reset(() => setTenantId("")),
@@ -86,7 +93,7 @@ function ShiftsTab({ currency = "" }) {
86
93
  onClick: () => reset(() => {
87
94
  setStatus("");
88
95
  setLongOpen(false);
89
- setTenantId("");
96
+ if (!fixedTenantId) setTenantId("");
90
97
  }),
91
98
  children: "Clear filters"
92
99
  }
@@ -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 { POS_PAGE_SIZE, type ShiftFilters } 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({ currency = '' }: { currency?: string }) {\n const [tenantId, setTenantId] = useTenantParam();\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);\n const reset = (fn: () => void) => {\n fn();\n setPage(1);\n };\n\n return (\n <ShiftsTable\n title=\"Shifts\"\n rows={data?.data}\n total={data?.total}\n withBusiness\n currency={currency}\n isLoading={isLoading}\n isError={isError}\n error={error}\n onRetry={() => void refetch()}\n emptyTitle={narrowed ? 'No shifts match.' : 'No shifts yet.'}\n controls={\n <Filters label=\"Filter shifts\">\n {tenantId && (\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 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":";AAyDY,cAOF,YAPE;AAvDZ,SAAS,gBAAgB;AACzB,SAAS,QAAQ,qBAAqB;AACtC,SAAS,qBAAwC;AAEjD,SAAS,aAAa,cAAc,eAAe;AACnD,SAAS,iBAAiB;AAC1B,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAE/B,MAAM,iBAAiB;AAAA,EACrB,EAAE,OAAO,IAAI,OAAO,aAAa;AAAA,EACjC,GAAG,OAAO,QAAQ,kBAAkB,EAAE,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO;AAAA,IAC7D;AAAA,IACA;AAAA,EACF,EAAE;AACJ;AAGO,SAAS,UAAU,EAAE,WAAW,GAAG,GAA0B;AAClE,QAAM,CAAC,UAAU,WAAW,IAAI,eAAe;AAC/C,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA8B,EAAE;AAC5D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,CAAC;AAClC,QAAM,UAAwB;AAAA,IAC5B,QAAQ,WAAW,SAAS;AAAA,IAC5B,eAAe,WAAW,kBAAkB;AAAA,IAC5C,UAAU,YAAY;AAAA,IACtB;AAAA,IACA,OAAO;AAAA,EACT;AACA,QAAM,EAAE,MAAM,WAAW,SAAS,OAAO,QAAQ,IAAI,aAAa,OAAO;AACzE,QAAM,WAAW,CAAC,EAAE,UAAU,YAAY;AAC1C,QAAM,QAAQ,CAAC,OAAmB;AAChC,OAAG;AACH,YAAQ,CAAC;AAAA,EACX;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC5B,YAAY,WAAW,qBAAqB;AAAA,MAC5C,UACE,qBAAC,WAAQ,OAAM,iBACZ;AAAA,oBACC;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,MAAM,MAAM,MAAM,YAAY,EAAE,CAAC;AAAA,YAC3C,aAAY;AAAA,YACb;AAAA;AAAA,QAED;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,IAAI;AAAA,YACJ,UAAU,MAAM,MAAM,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,YACnD;AAAA;AAAA,cACY;AAAA,cAAgB;AAAA;AAAA;AAAA,QAC7B;AAAA,QACC,CAAC,YACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,MAAM,MAAM,UAAU,CAAwB,CAAC;AAAA,YAChE,SAAS;AAAA;AAAA,QACX;AAAA,QAED,YACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,SAAS,MACP,MAAM,MAAM;AACV,wBAAU,EAAE;AACZ,0BAAY,KAAK;AACjB,0BAAY,EAAE;AAAA,YAChB,CAAC;AAAA,YAEJ;AAAA;AAAA,QAED;AAAA,SAEJ;AAAA,MAEF,QACE,QAAQ,KAAK,QAAQ,IACnB;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,YAAY,KAAK,IAAI,GAAG,KAAK,UAAU;AAAA,UACvC,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK,UAAU,IAAI,UAAU;AAAA,UACnC,QAAQ;AAAA;AAAA,MACV,IACE;AAAA;AAAA,EAER;AAEJ;","names":[]}
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 = 'Shifts',\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 shifts match.' : 'No shifts 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":[]}
@@ -3,7 +3,14 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import Link from "next/link";
4
4
  import { useRouter } from "next/navigation";
5
5
  import { EmptyNote } from "@withpotter/ui-kit";
6
- import { AdmLoading, CurrencyFilter, currenciesIn, useCurrencyParam } from "../../_ui";
6
+ import {
7
+ AdmLoading,
8
+ CurrencyFilter,
9
+ PageTabs,
10
+ currenciesIn,
11
+ useCurrencyParam,
12
+ useTabParam
13
+ } from "../../_ui";
7
14
  import { ListCard } from "../../_ui/list-card";
8
15
  import { LoadError, MoneyLines } from "../../_pos/pos-parts";
9
16
  import { count } from "../../_pos/pos-words";
@@ -13,10 +20,25 @@ import { usePosAccess } from "../../_pos/use-pos-access";
13
20
  import { usePosPeriod } from "../../_pos/use-pos-period";
14
21
  import { useTenantPos } from "../../_pos/use-pos";
15
22
  import pos from "../../_pos/pos.module.css";
16
- import { TerminalHead, TerminalRow, terminalColumns } from "../../pos/terminal-row";
23
+ import { TenantOperators } from "../../_pos/tenant-operators";
24
+ import { TenantPosFigures } from "../../_pos/tenant-pos-figures";
25
+ import { ShiftsTab } from "../../pos/shifts-tab";
26
+ import {
27
+ TerminalHead,
28
+ TerminalRow,
29
+ terminalColumns
30
+ } from "../../pos/terminal-row";
31
+ const VIEWS = [
32
+ { id: "overview", label: "Overview" },
33
+ { id: "terminals", label: "Terminals" },
34
+ { id: "sessions", label: "Sessions" },
35
+ { id: "operators", label: "Operators" }
36
+ ];
37
+ const VIEW_KEYS = VIEWS.map((v) => v.id);
17
38
  function TenantPos({ tenantId }) {
18
39
  const router = useRouter();
19
40
  const { isLoading: accessLoading, canRead } = usePosAccess();
41
+ const [view, setView] = useTabParam(VIEW_KEYS, "view");
20
42
  const { period, phrase, control } = usePosPeriod();
21
43
  const [currency, setCurrency] = useCurrencyParam();
22
44
  const query = useTenantPos(tenantId, period, canRead);
@@ -46,7 +68,14 @@ function TenantPos({ tenantId }) {
46
68
  /* @__PURE__ */ jsx(MoneyLines, { amounts: data.takings.amounts, currency }),
47
69
  ` taken, ${phrase} \xB7 `
48
70
  ] }) : null,
49
- /* @__PURE__ */ jsx(Link, { href: `/admin/pos?tenant=${tenantId}`, className: "text-brand-500 hover:underline", children: "Open in POS" })
71
+ /* @__PURE__ */ jsx(
72
+ Link,
73
+ {
74
+ href: `/admin/pos?tenant=${tenantId}`,
75
+ className: "text-brand-500 hover:underline",
76
+ children: "Open in POS"
77
+ }
78
+ )
50
79
  ] }),
51
80
  /* @__PURE__ */ jsxs("div", { className: "adm-toolbar", children: [
52
81
  /* @__PURE__ */ jsx(
@@ -54,13 +83,30 @@ function TenantPos({ tenantId }) {
54
83
  {
55
84
  value: currency,
56
85
  onChange: setCurrency,
57
- currencies: currenciesIn(data.takings.amounts.map((a) => a.currency))
86
+ currencies: currenciesIn(
87
+ data.takings.amounts.map((a) => a.currency)
88
+ )
58
89
  }
59
90
  ),
60
91
  control
61
92
  ] })
62
93
  ] }),
63
- /* @__PURE__ */ jsx(
94
+ /* @__PURE__ */ jsx(PageTabs, { tabs: VIEWS, active: view, onChange: setView }),
95
+ view === "overview" && /* @__PURE__ */ jsxs(Fragment, { children: [
96
+ /* @__PURE__ */ jsx(TenantPosFigures, { data, phrase, currency }),
97
+ /* @__PURE__ */ jsx(
98
+ ShiftsTable,
99
+ {
100
+ title: "Open shifts",
101
+ rows: data.openShifts,
102
+ total: data.openShifts.length,
103
+ withBusiness: false,
104
+ currency,
105
+ emptyTitle: "No till is open right now."
106
+ }
107
+ )
108
+ ] }),
109
+ view === "terminals" && /* @__PURE__ */ jsx(
64
110
  ListCard,
65
111
  {
66
112
  title: "Terminals",
@@ -82,17 +128,8 @@ function TenantPos({ tenantId }) {
82
128
  ))
83
129
  }
84
130
  ),
85
- /* @__PURE__ */ jsx(
86
- ShiftsTable,
87
- {
88
- title: "Open shifts",
89
- rows: data.openShifts,
90
- total: data.openShifts.length,
91
- withBusiness: false,
92
- currency,
93
- emptyTitle: "No till is open right now."
94
- }
95
- )
131
+ view === "sessions" && /* @__PURE__ */ jsx(ShiftsTab, { tenantId, currency, title: "Sessions" }),
132
+ view === "operators" && /* @__PURE__ */ jsx(TenantOperators, { terminals: data.terminals, period })
96
133
  ] });
97
134
  }
98
135
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/tenant-pos.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { useRouter } from 'next/navigation';\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport { AdmLoading, CurrencyFilter, currenciesIn, useCurrencyParam } from '../../_ui';\nimport { ListCard } from '../../_ui/list-card';\nimport { LoadError, MoneyLines } from '../../_pos/pos-parts';\nimport { count } from '../../_pos/pos-words';\nimport { terminalHref } from '../../_pos/shift-row';\nimport { ShiftsTable } from '../../_pos/shifts-table';\nimport { usePosAccess } from '../../_pos/use-pos-access';\nimport { usePosPeriod } from '../../_pos/use-pos-period';\nimport { useTenantPos } from '../../_pos/use-pos';\nimport pos from '../../_pos/pos.module.css';\nimport { TerminalHead, TerminalRow, terminalColumns } from '../../pos/terminal-row';\n\n/**\n * Merchant 360 → POS: the business's terminals, the shifts open now, and\n * what its terminals took in the period, per currency.\n */\nexport function TenantPos({ tenantId }: { tenantId: string }) {\n const router = useRouter();\n const { isLoading: accessLoading, canRead } = usePosAccess();\n const { period, phrase, control } = usePosPeriod();\n const [currency, setCurrency] = useCurrencyParam();\n const query = useTenantPos(tenantId, period, canRead);\n const data = query.data;\n\n if (accessLoading || (canRead && query.isLoading))\n return <AdmLoading label=\"Loading POS…\" />;\n\n if (!canRead)\n return (\n <EmptyNote well>{\"You don't have permission to see this business's POS.\"}</EmptyNote>\n );\n\n if (query.isError || !data)\n return (\n <LoadError\n error={query.error}\n what=\"this business's POS\"\n onRetry={() => void query.refetch()}\n />\n );\n\n const live = data.terminals.filter((t) => t.status !== 'revoked').length;\n const revoked = data.terminals.length - live;\n\n return (\n <div className={pos.stack}>\n <div className={pos.toolbarRow}>\n <span className=\"adm-caption\">\n {`${count(live, 'terminal')} in use`}\n {revoked ? `, ${revoked} revoked` : ''}\n {' · '}\n {data.takings.count > 0 ? (\n <>\n <MoneyLines amounts={data.takings.amounts} currency={currency} />\n {` taken, ${phrase} · `}\n </>\n ) : null}\n <Link href={`/admin/pos?tenant=${tenantId}`} className=\"text-brand-500 hover:underline\">\n Open in POS\n </Link>\n </span>\n <div className=\"adm-toolbar\">\n <CurrencyFilter\n value={currency}\n onChange={setCurrency}\n currencies={currenciesIn(data.takings.amounts.map((a) => a.currency))}\n />\n {control}\n </div>\n </div>\n\n <ListCard\n title=\"Terminals\"\n count={data.terminals.length}\n columns={terminalColumns(false)}\n minWidth={860}\n errorTitle=\"\"\n isEmpty={data.terminals.length === 0}\n emptyTitle=\"This business has no terminals.\"\n head={<TerminalHead withBusiness={false} />}\n >\n {data.terminals.map((t) => (\n <TerminalRow\n key={t.id}\n t={t}\n currency={currency}\n onOpen={() => router.push(terminalHref(t.id))}\n />\n ))}\n </ListCard>\n\n <ShiftsTable\n title=\"Open shifts\"\n rows={data.openShifts}\n total={data.openShifts.length}\n withBusiness={false}\n currency={currency}\n emptyTitle=\"No till is open right now.\"\n />\n </div>\n );\n}\n"],"mappings":";AA8BW,SA2BC,UA3BD,KA2BC,YA3BD;AA5BX,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAC1B,SAAS,YAAY,gBAAgB,cAAc,wBAAwB;AAC3E,SAAS,gBAAgB;AACzB,SAAS,WAAW,kBAAkB;AACtC,SAAS,aAAa;AACtB,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,OAAO,SAAS;AAChB,SAAS,cAAc,aAAa,uBAAuB;AAMpD,SAAS,UAAU,EAAE,SAAS,GAAyB;AAC5D,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,WAAW,eAAe,QAAQ,IAAI,aAAa;AAC3D,QAAM,EAAE,QAAQ,QAAQ,QAAQ,IAAI,aAAa;AACjD,QAAM,CAAC,UAAU,WAAW,IAAI,iBAAiB;AACjD,QAAM,QAAQ,aAAa,UAAU,QAAQ,OAAO;AACpD,QAAM,OAAO,MAAM;AAEnB,MAAI,iBAAkB,WAAW,MAAM;AACrC,WAAO,oBAAC,cAAW,OAAM,qBAAe;AAE1C,MAAI,CAAC;AACH,WACE,oBAAC,aAAU,MAAI,MAAE,mEAAwD;AAG7E,MAAI,MAAM,WAAW,CAAC;AACpB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,MAAM;AAAA,QACb,MAAK;AAAA,QACL,SAAS,MAAM,KAAK,MAAM,QAAQ;AAAA;AAAA,IACpC;AAGJ,QAAM,OAAO,KAAK,UAAU,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE;AAClE,QAAM,UAAU,KAAK,UAAU,SAAS;AAExC,SACE,qBAAC,SAAI,WAAW,IAAI,OAClB;AAAA,yBAAC,SAAI,WAAW,IAAI,YAClB;AAAA,2BAAC,UAAK,WAAU,eACb;AAAA,WAAG,MAAM,MAAM,UAAU,CAAC;AAAA,QAC1B,UAAU,KAAK,OAAO,aAAa;AAAA,QACnC;AAAA,QACA,KAAK,QAAQ,QAAQ,IACpB,iCACE;AAAA,8BAAC,cAAW,SAAS,KAAK,QAAQ,SAAS,UAAoB;AAAA,UAC9D,WAAW,MAAM;AAAA,WACpB,IACE;AAAA,QACJ,oBAAC,QAAK,MAAM,qBAAqB,QAAQ,IAAI,WAAU,kCAAiC,yBAExF;AAAA,SACF;AAAA,MACA,qBAAC,SAAI,WAAU,eACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU;AAAA,YACV,YAAY,aAAa,KAAK,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;AAAA;AAAA,QACtE;AAAA,QACC;AAAA,SACH;AAAA,OACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK,UAAU;AAAA,QACtB,SAAS,gBAAgB,KAAK;AAAA,QAC9B,UAAU;AAAA,QACV,YAAW;AAAA,QACX,SAAS,KAAK,UAAU,WAAW;AAAA,QACnC,YAAW;AAAA,QACX,MAAM,oBAAC,gBAAa,cAAc,OAAO;AAAA,QAExC,eAAK,UAAU,IAAI,CAAC,MACnB;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA;AAAA,YACA,QAAQ,MAAM,OAAO,KAAK,aAAa,EAAE,EAAE,CAAC;AAAA;AAAA,UAHvC,EAAE;AAAA,QAIT,CACD;AAAA;AAAA,IACH;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,MAAM,KAAK;AAAA,QACX,OAAO,KAAK,WAAW;AAAA,QACvB,cAAc;AAAA,QACd;AAAA,QACA,YAAW;AAAA;AAAA,IACb;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/admin/tenants/%5Bid%5D/tenant-pos.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { useRouter } from 'next/navigation';\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport {\n AdmLoading,\n CurrencyFilter,\n PageTabs,\n currenciesIn,\n useCurrencyParam,\n useTabParam,\n} from '../../_ui';\nimport { ListCard } from '../../_ui/list-card';\nimport { LoadError, MoneyLines } from '../../_pos/pos-parts';\nimport { count } from '../../_pos/pos-words';\nimport { terminalHref } from '../../_pos/shift-row';\nimport { ShiftsTable } from '../../_pos/shifts-table';\nimport { usePosAccess } from '../../_pos/use-pos-access';\nimport { usePosPeriod } from '../../_pos/use-pos-period';\nimport { useTenantPos } from '../../_pos/use-pos';\nimport pos from '../../_pos/pos.module.css';\nimport { TenantOperators } from '../../_pos/tenant-operators';\nimport { TenantPosFigures } from '../../_pos/tenant-pos-figures';\nimport { ShiftsTab } from '../../pos/shifts-tab';\nimport {\n TerminalHead,\n TerminalRow,\n terminalColumns,\n} from '../../pos/terminal-row';\n\nconst VIEWS = [\n { id: 'overview', label: 'Overview' },\n { id: 'terminals', label: 'Terminals' },\n { id: 'sessions', label: 'Sessions' },\n { id: 'operators', label: 'Operators' },\n] as const;\n\nconst VIEW_KEYS = VIEWS.map((v) => v.id);\n\n/**\n * Merchant 360 → POS: the business's terminals, the shifts open now, and\n * what its terminals took in the period, per currency.\n */\nexport function TenantPos({ tenantId }: { tenantId: string }) {\n const router = useRouter();\n const { isLoading: accessLoading, canRead } = usePosAccess();\n const [view, setView] = useTabParam(VIEW_KEYS, 'view');\n const { period, phrase, control } = usePosPeriod();\n const [currency, setCurrency] = useCurrencyParam();\n const query = useTenantPos(tenantId, period, canRead);\n const data = query.data;\n\n if (accessLoading || (canRead && query.isLoading))\n return <AdmLoading label=\"Loading POS…\" />;\n\n if (!canRead)\n return (\n <EmptyNote well>\n {\"You don't have permission to see this business's POS.\"}\n </EmptyNote>\n );\n\n if (query.isError || !data)\n return (\n <LoadError\n error={query.error}\n what=\"this business's POS\"\n onRetry={() => void query.refetch()}\n />\n );\n\n const live = data.terminals.filter((t) => t.status !== 'revoked').length;\n const revoked = data.terminals.length - live;\n\n return (\n <div className={pos.stack}>\n <div className={pos.toolbarRow}>\n <span className=\"adm-caption\">\n {`${count(live, 'terminal')} in use`}\n {revoked ? `, ${revoked} revoked` : ''}\n {' · '}\n {data.takings.count > 0 ? (\n <>\n <MoneyLines amounts={data.takings.amounts} currency={currency} />\n {` taken, ${phrase} · `}\n </>\n ) : null}\n <Link\n href={`/admin/pos?tenant=${tenantId}`}\n className=\"text-brand-500 hover:underline\"\n >\n Open in POS\n </Link>\n </span>\n <div className=\"adm-toolbar\">\n <CurrencyFilter\n value={currency}\n onChange={setCurrency}\n currencies={currenciesIn(\n data.takings.amounts.map((a) => a.currency),\n )}\n />\n {control}\n </div>\n </div>\n\n <PageTabs tabs={VIEWS} active={view} onChange={setView} />\n\n {view === 'overview' && (\n <>\n <TenantPosFigures data={data} phrase={phrase} currency={currency} />\n <ShiftsTable\n title=\"Open shifts\"\n rows={data.openShifts}\n total={data.openShifts.length}\n withBusiness={false}\n currency={currency}\n emptyTitle=\"No till is open right now.\"\n />\n </>\n )}\n\n {view === 'terminals' && (\n <ListCard\n title=\"Terminals\"\n count={data.terminals.length}\n columns={terminalColumns(false)}\n minWidth={860}\n errorTitle=\"\"\n isEmpty={data.terminals.length === 0}\n emptyTitle=\"This business has no terminals.\"\n head={<TerminalHead withBusiness={false} />}\n >\n {data.terminals.map((t) => (\n <TerminalRow\n key={t.id}\n t={t}\n currency={currency}\n onOpen={() => router.push(terminalHref(t.id))}\n />\n ))}\n </ListCard>\n )}\n\n {view === 'sessions' && (\n <ShiftsTab tenantId={tenantId} currency={currency} title=\"Sessions\" />\n )}\n\n {view === 'operators' && (\n <TenantOperators terminals={data.terminals} period={period} />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withpotter/pages-admin",
3
- "version": "0.13.0-next.102",
3
+ "version": "0.13.0-next.103",
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.102",
35
- "@withpotter/platform-admin": "^0.13.0-next.102",
36
- "@withpotter/ui-kit": "^0.13.0-next.102",
34
+ "@withpotter/lib": "^0.13.0-next.103",
35
+ "@withpotter/platform-admin": "^0.13.0-next.103",
36
+ "@withpotter/ui-kit": "^0.13.0-next.103",
37
37
  "lucide-react": "^0.562.0",
38
38
  "motion": "^12.42.2"
39
39
  },