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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/admin/_overview/activation-panel.js +6 -6
  2. package/dist/admin/_overview/activation-panel.js.map +1 -1
  3. package/dist/admin/_overview/attention-groups.js +8 -8
  4. package/dist/admin/_overview/attention-groups.js.map +1 -1
  5. package/dist/admin/_overview/attention-panel.js +3 -3
  6. package/dist/admin/_overview/attention-panel.js.map +1 -1
  7. package/dist/admin/_overview/control-room.js +9 -9
  8. package/dist/admin/_overview/control-room.js.map +1 -1
  9. package/dist/admin/_overview/kpi-row.js +7 -7
  10. package/dist/admin/_overview/kpi-row.js.map +1 -1
  11. package/dist/admin/_overview/leaders-panel.js +1 -1
  12. package/dist/admin/_overview/leaders-panel.js.map +1 -1
  13. package/dist/admin/_overview/portfolio-panel.js +5 -5
  14. package/dist/admin/_overview/portfolio-panel.js.map +1 -1
  15. package/dist/admin/_overview/position-panel.js +6 -6
  16. package/dist/admin/_overview/position-panel.js.map +1 -1
  17. package/dist/admin/_pos/olive-balance.js +40 -0
  18. package/dist/admin/_pos/olive-balance.js.map +1 -0
  19. package/dist/admin/_pos/olive-state.js +23 -0
  20. package/dist/admin/_pos/olive-state.js.map +1 -0
  21. package/dist/admin/_pos/pos-rules.js +2 -0
  22. package/dist/admin/_pos/pos-rules.js.map +1 -1
  23. package/dist/admin/_pos/tenant-operators.js +36 -46
  24. package/dist/admin/_pos/tenant-operators.js.map +1 -1
  25. package/dist/admin/_pos/tenant-outlets.js +77 -0
  26. package/dist/admin/_pos/tenant-outlets.js.map +1 -0
  27. package/dist/admin/_pos/tenant-requests.js +64 -0
  28. package/dist/admin/_pos/tenant-requests.js.map +1 -0
  29. package/dist/admin/_pos/tenant-transactions.js +145 -0
  30. package/dist/admin/_pos/tenant-transactions.js.map +1 -0
  31. package/dist/admin/_pos/use-pos.js +60 -11
  32. package/dist/admin/_pos/use-pos.js.map +1 -1
  33. package/dist/admin/_ui/data-table.js +1 -1
  34. package/dist/admin/_ui/data-table.js.map +1 -1
  35. package/dist/admin/_ui/list-card.js +1 -1
  36. package/dist/admin/_ui/list-card.js.map +1 -1
  37. package/dist/admin/_ui/trend.js +2 -2
  38. package/dist/admin/_ui/trend.js.map +1 -1
  39. package/dist/admin/page.js +6 -6
  40. package/dist/admin/page.js.map +1 -1
  41. package/dist/admin/payout-tiers/_tier-dialog.js +31 -21
  42. package/dist/admin/payout-tiers/_tier-dialog.js.map +1 -1
  43. package/dist/admin/payout-tiers/requirement-labels.js +70 -0
  44. package/dist/admin/payout-tiers/requirement-labels.js.map +1 -0
  45. package/dist/admin/payout-tiers/tier-preview-result.js +10 -9
  46. package/dist/admin/payout-tiers/tier-preview-result.js.map +1 -1
  47. package/dist/admin/payout-tiers/tiers-table.js +6 -7
  48. package/dist/admin/payout-tiers/tiers-table.js.map +1 -1
  49. package/dist/admin/payout-tiers/tiers-workspace.js +2 -2
  50. package/dist/admin/payout-tiers/tiers-workspace.js.map +1 -1
  51. package/dist/admin/revenue/cohort-panel.js +1 -1
  52. package/dist/admin/revenue/cohort-panel.js.map +1 -1
  53. package/dist/admin/revenue/plan-mix-panel.js +2 -2
  54. package/dist/admin/revenue/plan-mix-panel.js.map +1 -1
  55. package/dist/admin/revenue/revenue-kpis.js +3 -3
  56. package/dist/admin/revenue/revenue-kpis.js.map +1 -1
  57. package/dist/admin/revenue/revenue-overview.js +1 -1
  58. package/dist/admin/revenue/revenue-overview.js.map +1 -1
  59. package/dist/admin/revenue/subscriptions-view.js +3 -3
  60. package/dist/admin/revenue/subscriptions-view.js.map +1 -1
  61. package/dist/admin/revenue/top-tenants-panel.js +3 -3
  62. package/dist/admin/revenue/top-tenants-panel.js.map +1 -1
  63. package/dist/admin/tenants/[id]/tenant-pos.js +13 -2
  64. package/dist/admin/tenants/[id]/tenant-pos.js.map +1 -1
  65. package/package.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/_pos/olive-state.tsx"],"sourcesContent":["'use client';\n\nimport { EmptyNote } from '@withpotter/ui-kit';\nimport { formatMinor } from '@withpotter/lib/money-minor';\nimport { LoadError } from './pos-parts';\nimport { isNotLinked } from './pos-rules';\n\n/** Olive's amounts are kobo; the merchant's own POS page shows them as naira. */\nexport const naira = (minor: number | null) =>\n minor === null ? '–' : formatMinor(String(Math.trunc(minor)), 'NGN');\n\n/**\n * Why Olive's data is not on screen. A business that has not linked Olive is a state, not a fault, and\n * says so; anything else is a retry.\n */\nexport function OliveProblem({\n error,\n what,\n onRetry,\n}: {\n error: unknown;\n what: string;\n onRetry: () => void;\n}) {\n if (isNotLinked(error))\n return (\n <EmptyNote well>\n {`This business has not linked its Olive account, so its ${what} cannot be read yet.`}\n </EmptyNote>\n );\n\n return <LoadError error={error} what={`its ${what}`} onRetry={onRetry} />;\n}\n\nexport const OLIVE_NOTE =\n 'Read live from Olive under the business’s own login. Each view is recorded in the audit log.';\n"],"mappings":";AA0BM;AAxBN,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAGrB,MAAM,QAAQ,CAAC,UACpB,UAAU,OAAO,WAAM,YAAY,OAAO,KAAK,MAAM,KAAK,CAAC,GAAG,KAAK;AAM9D,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,YAAY,KAAK;AACnB,WACE,oBAAC,aAAU,MAAI,MACZ,oEAA0D,IAAI,wBACjE;AAGJ,SAAO,oBAAC,aAAU,OAAc,MAAM,OAAO,IAAI,IAAI,SAAkB;AACzE;AAEO,MAAM,aACX;","names":[]}
@@ -17,6 +17,7 @@ const statusOf = (e) => e?.status;
17
17
  const isStale = (e) => statusOf(e) === 409;
18
18
  const isForbidden = (e) => statusOf(e) === 403;
19
19
  const isNotFound = (e) => statusOf(e) === 404;
20
+ const isNotLinked = (e) => statusOf(e) === 400;
20
21
  const STALE_TEXT = "This changed since you opened it. Refresh to see where it stands now.";
21
22
  function posErrorText(error, fallback) {
22
23
  const status = statusOf(error);
@@ -35,6 +36,7 @@ export {
35
36
  isForbidden,
36
37
  isLongOpen,
37
38
  isNotFound,
39
+ isNotLinked,
38
40
  isStale,
39
41
  posErrorText,
40
42
  reasonError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/_pos/pos-rules.ts"],"sourcesContent":["import type {\n PosShift,\n PosTerminalStatus,\n} from '@withpotter/lib/admin-pos-types';\n\n/**\n * What an admin may do to a terminal or a shift, the forms' checks and what\n * an error says. Pure, so the rules the API enforces are tested here rather\n * than met as a 409.\n */\n\nexport const REASON_MAX = 300;\n\n/** A shift open this long is flagged; the API's own threshold. */\nexport const LONG_OPEN_HOURS = 12;\n\nexport type TerminalAction = 'suspend' | 'resume' | 'revoke';\n\n/**\n * Suspend needs active, resume needs suspended, revoke works from either\n * and is one-way. A revoked terminal has nothing left to do.\n */\nexport function terminalActions(status: PosTerminalStatus): TerminalAction[] {\n if (status === 'revoked') return [];\n\n return [status === 'active' ? 'suspend' : 'resume', 'revoke'];\n}\n\nexport const canCloseShift = (s: Pick<PosShift, 'status'>) =>\n s.status === 'open';\n\nexport const isLongOpen = (s: Pick<PosShift, 'status' | 'openHours'>) =>\n s.status === 'open' && (s.openHours ?? 0) >= LONG_OPEN_HOURS;\n\n/** An error for a reason, or undefined when it can be sent. */\nexport function reasonError(reason: string): string | undefined {\n const clean = reason.trim();\n\n if (!clean) return 'Say why, for the audit log.';\n\n if (clean.length > REASON_MAX)\n return `Keep the reason to ${REASON_MAX} characters.`;\n\n return undefined;\n}\n\nconst statusOf = (e: unknown) => (e as { status?: unknown } | null)?.status;\n\n/** The record changed under the admin: someone else acted first. */\nexport const isStale = (e: unknown) => statusOf(e) === 409;\n\nexport const isForbidden = (e: unknown) => statusOf(e) === 403;\n\nexport const isNotFound = (e: unknown) => statusOf(e) === 404;\n\nexport const STALE_TEXT =\n 'This changed since you opened it. Refresh to see where it stands now.';\n\n/**\n * An error in plain words. A 409 means the terminal or shift moved on, so\n * it says so and offers a refresh. A 400 keeps the server's line.\n */\nexport function posErrorText(error: unknown, fallback: string): string {\n const status = statusOf(error);\n const message = error instanceof Error ? error.message.trim() : '';\n\n if (status === 409) return STALE_TEXT;\n\n if (status === 403) return \"You don't have permission to do this.\";\n\n if (status === 404) return 'This no longer exists. Refresh the list.';\n\n if (status === 400 && message) return message;\n\n return fallback;\n}\n"],"mappings":"AAWO,MAAM,aAAa;AAGnB,MAAM,kBAAkB;AAQxB,SAAS,gBAAgB,QAA6C;AAC3E,MAAI,WAAW,UAAW,QAAO,CAAC;AAElC,SAAO,CAAC,WAAW,WAAW,YAAY,UAAU,QAAQ;AAC9D;AAEO,MAAM,gBAAgB,CAAC,MAC5B,EAAE,WAAW;AAER,MAAM,aAAa,CAAC,MACzB,EAAE,WAAW,WAAW,EAAE,aAAa,MAAM;AAGxC,SAAS,YAAY,QAAoC;AAC9D,QAAM,QAAQ,OAAO,KAAK;AAE1B,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,MAAM,SAAS;AACjB,WAAO,sBAAsB,UAAU;AAEzC,SAAO;AACT;AAEA,MAAM,WAAW,CAAC,MAAgB,GAAmC;AAG9D,MAAM,UAAU,CAAC,MAAe,SAAS,CAAC,MAAM;AAEhD,MAAM,cAAc,CAAC,MAAe,SAAS,CAAC,MAAM;AAEpD,MAAM,aAAa,CAAC,MAAe,SAAS,CAAC,MAAM;AAEnD,MAAM,aACX;AAMK,SAAS,aAAa,OAAgB,UAA0B;AACrE,QAAM,SAAS,SAAS,KAAK;AAC7B,QAAM,UAAU,iBAAiB,QAAQ,MAAM,QAAQ,KAAK,IAAI;AAEhE,MAAI,WAAW,IAAK,QAAO;AAE3B,MAAI,WAAW,IAAK,QAAO;AAE3B,MAAI,WAAW,IAAK,QAAO;AAE3B,MAAI,WAAW,OAAO,QAAS,QAAO;AAEtC,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/_pos/pos-rules.ts"],"sourcesContent":["import type {\n PosShift,\n PosTerminalStatus,\n} from '@withpotter/lib/admin-pos-types';\n\n/**\n * What an admin may do to a terminal or a shift, the forms' checks and what\n * an error says. Pure, so the rules the API enforces are tested here rather\n * than met as a 409.\n */\n\nexport const REASON_MAX = 300;\n\n/** A shift open this long is flagged; the API's own threshold. */\nexport const LONG_OPEN_HOURS = 12;\n\nexport type TerminalAction = 'suspend' | 'resume' | 'revoke';\n\n/**\n * Suspend needs active, resume needs suspended, revoke works from either\n * and is one-way. A revoked terminal has nothing left to do.\n */\nexport function terminalActions(status: PosTerminalStatus): TerminalAction[] {\n if (status === 'revoked') return [];\n\n return [status === 'active' ? 'suspend' : 'resume', 'revoke'];\n}\n\nexport const canCloseShift = (s: Pick<PosShift, 'status'>) =>\n s.status === 'open';\n\nexport const isLongOpen = (s: Pick<PosShift, 'status' | 'openHours'>) =>\n s.status === 'open' && (s.openHours ?? 0) >= LONG_OPEN_HOURS;\n\n/** An error for a reason, or undefined when it can be sent. */\nexport function reasonError(reason: string): string | undefined {\n const clean = reason.trim();\n\n if (!clean) return 'Say why, for the audit log.';\n\n if (clean.length > REASON_MAX)\n return `Keep the reason to ${REASON_MAX} characters.`;\n\n return undefined;\n}\n\nconst statusOf = (e: unknown) => (e as { status?: unknown } | null)?.status;\n\n/** The record changed under the admin: someone else acted first. */\nexport const isStale = (e: unknown) => statusOf(e) === 409;\n\nexport const isForbidden = (e: unknown) => statusOf(e) === 403;\n\nexport const isNotFound = (e: unknown) => statusOf(e) === 404;\n\n/** The business has not linked Olive (or must reconnect it): Olive's data cannot be read yet. */\nexport const isNotLinked = (e: unknown) => statusOf(e) === 400;\n\nexport const STALE_TEXT =\n 'This changed since you opened it. Refresh to see where it stands now.';\n\n/**\n * An error in plain words. A 409 means the terminal or shift moved on, so\n * it says so and offers a refresh. A 400 keeps the server's line.\n */\nexport function posErrorText(error: unknown, fallback: string): string {\n const status = statusOf(error);\n const message = error instanceof Error ? error.message.trim() : '';\n\n if (status === 409) return STALE_TEXT;\n\n if (status === 403) return \"You don't have permission to do this.\";\n\n if (status === 404) return 'This no longer exists. Refresh the list.';\n\n if (status === 400 && message) return message;\n\n return fallback;\n}\n"],"mappings":"AAWO,MAAM,aAAa;AAGnB,MAAM,kBAAkB;AAQxB,SAAS,gBAAgB,QAA6C;AAC3E,MAAI,WAAW,UAAW,QAAO,CAAC;AAElC,SAAO,CAAC,WAAW,WAAW,YAAY,UAAU,QAAQ;AAC9D;AAEO,MAAM,gBAAgB,CAAC,MAC5B,EAAE,WAAW;AAER,MAAM,aAAa,CAAC,MACzB,EAAE,WAAW,WAAW,EAAE,aAAa,MAAM;AAGxC,SAAS,YAAY,QAAoC;AAC9D,QAAM,QAAQ,OAAO,KAAK;AAE1B,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,MAAM,SAAS;AACjB,WAAO,sBAAsB,UAAU;AAEzC,SAAO;AACT;AAEA,MAAM,WAAW,CAAC,MAAgB,GAAmC;AAG9D,MAAM,UAAU,CAAC,MAAe,SAAS,CAAC,MAAM;AAEhD,MAAM,cAAc,CAAC,MAAe,SAAS,CAAC,MAAM;AAEpD,MAAM,aAAa,CAAC,MAAe,SAAS,CAAC,MAAM;AAGnD,MAAM,cAAc,CAAC,MAAe,SAAS,CAAC,MAAM;AAEpD,MAAM,aACX;AAMK,SAAS,aAAa,OAAgB,UAA0B;AACrE,QAAM,SAAS,SAAS,KAAK;AAC7B,QAAM,UAAU,iBAAiB,QAAQ,MAAM,QAAQ,KAAK,IAAI;AAEhE,MAAI,WAAW,IAAK,QAAO;AAE3B,MAAI,WAAW,IAAK,QAAO;AAE3B,MAAI,WAAW,IAAK,QAAO;AAE3B,MAAI,WAAW,OAAO,QAAS,QAAO;AAEtC,SAAO;AACT;","names":[]}
@@ -1,73 +1,63 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useMemo } from "react";
4
3
  import { TableRow, Th } from "@withpotter/ui-kit";
5
4
  import { ListCard } from "../_ui/list-card";
6
5
  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";
6
+ import { LoadError } from "./pos-parts";
7
+ import { isForbidden, posErrorText } from "./pos-rules";
8
+ import { atTime } from "./pos-words";
9
+ import { useTenantRoster } from "./use-pos";
10
10
  import pos from "./pos.module.css";
11
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);
12
+ function shiftLimit(minutes) {
13
+ if (!Number.isFinite(minutes) || minutes <= 0) return "\u2013";
14
+ const h = Math.floor(minutes / 60);
15
+ const m = minutes % 60;
16
+ if (!h) return `${m} min`;
17
+ return m ? `${h} h ${m} min` : `${h} h`;
18
+ }
19
+ function TenantOperators({ tenantId }) {
20
+ const { data, isLoading, isError, error, refetch } = useTenantRoster(tenantId);
21
+ if (isError && !data)
22
+ return /* @__PURE__ */ jsx(
23
+ LoadError,
24
+ {
25
+ error,
26
+ what: "this business's operators",
27
+ onRetry: () => void refetch()
35
28
  }
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");
29
+ );
40
30
  return /* @__PURE__ */ jsx(
41
31
  ListCard,
42
32
  {
43
33
  title: "Operators",
44
- count: people.length,
45
- columns: 3,
46
- minWidth: 560,
34
+ count: data?.length,
35
+ columns: 4,
36
+ minWidth: 640,
47
37
  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,
38
+ errorTitle: isForbidden(error) ? "" : posErrorText(error, "Operators did not load."),
39
+ isEmpty: data?.length === 0,
40
+ emptyTitle: "Nobody can sign in at this business's terminals yet.",
54
41
  head: /* @__PURE__ */ jsxs("tr", { children: [
55
- /* @__PURE__ */ jsx(Th, { children: "Person" }),
42
+ /* @__PURE__ */ jsx(Th, { children: "Operator" }),
56
43
  /* @__PURE__ */ jsx(Th, { children: "Role" }),
57
- /* @__PURE__ */ jsx(Th, { children: "Terminals" })
44
+ /* @__PURE__ */ jsx(Th, { children: "Shift limit" }),
45
+ /* @__PURE__ */ jsx(Th, { children: "Updated" })
58
46
  ] }),
59
- children: people.map((o) => /* @__PURE__ */ jsxs(TableRow, { children: [
47
+ children: data?.map((o) => /* @__PURE__ */ jsxs(TableRow, { children: [
60
48
  /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: pos.cellStack, children: [
61
49
  /* @__PURE__ */ jsx("span", { className: pos.clip, children: o.name }),
62
- !o.active && /* @__PURE__ */ jsx("span", { className: pos.subtle, children: "Removed" })
50
+ /* @__PURE__ */ jsx("span", { className: pos.subtle, children: o.active ? o.signIn !== o.name ? `Signs in as ${o.signIn}` : "" : "Removed" })
63
51
  ] }) }),
64
52
  /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: sentence(o.role) }),
65
- /* @__PURE__ */ jsx("td", { className: list.muted, children: o.at.join(", ") })
53
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: shiftLimit(o.shiftLimitMinutes) }),
54
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: atTime(o.updatedAt) })
66
55
  ] }, o.id))
67
56
  }
68
57
  );
69
58
  }
70
59
  export {
71
- TenantOperators
60
+ TenantOperators,
61
+ shiftLimit
72
62
  };
73
63
  //# sourceMappingURL=tenant-operators.js.map
@@ -1 +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":[]}
1
+ {"version":3,"sources":["../../../src/admin/_pos/tenant-operators.tsx"],"sourcesContent":["'use client';\n\nimport { TableRow, Th } from '@withpotter/ui-kit';\nimport { ListCard } from '../_ui/list-card';\nimport list from '../_ui/list-card.module.css';\nimport { LoadError } from './pos-parts';\nimport { isForbidden, posErrorText } from './pos-rules';\nimport { atTime } from './pos-words';\nimport { useTenantRoster } 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/** \"168 h\", \"30 min\", \"1 h 30 min\". */\nexport function shiftLimit(minutes: number): string {\n if (!Number.isFinite(minutes) || minutes <= 0) return '–';\n\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n\n if (!h) return `${m} min`;\n\n return m ? `${h} h ${m} min` : `${h} h`;\n}\n\n/** The business's whole till roster: who can sign in, as what, and for how long a shift may run. */\nexport function TenantOperators({ tenantId }: { tenantId: string }) {\n const { data, isLoading, isError, error, refetch } =\n useTenantRoster(tenantId);\n\n if (isError && !data)\n return (\n <LoadError\n error={error}\n what=\"this business's operators\"\n onRetry={() => void refetch()}\n />\n );\n\n return (\n <ListCard\n title=\"Operators\"\n count={data?.length}\n columns={4}\n minWidth={640}\n isLoading={isLoading}\n errorTitle={\n isForbidden(error) ? '' : posErrorText(error, 'Operators did not load.')\n }\n isEmpty={data?.length === 0}\n emptyTitle=\"Nobody can sign in at this business's terminals yet.\"\n head={\n <tr>\n <Th>Operator</Th>\n <Th>Role</Th>\n <Th>Shift limit</Th>\n <Th>Updated</Th>\n </tr>\n }\n >\n {data?.map((o) => (\n <TableRow key={o.id}>\n <td>\n <span className={pos.cellStack}>\n <span className={pos.clip}>{o.name}</span>\n <span className={pos.subtle}>\n {o.active\n ? o.signIn !== o.name\n ? `Signs in as ${o.signIn}`\n : ''\n : 'Removed'}\n </span>\n </span>\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>{sentence(o.role)}</td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {shiftLimit(o.shiftLimitMinutes)}\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {atTime(o.updatedAt)}\n </td>\n </TableRow>\n ))}\n </ListCard>\n );\n}\n"],"mappings":";AAiCM,cAoBE,YApBF;AA/BN,SAAS,UAAU,UAAU;AAC7B,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,aAAa,oBAAoB;AAC1C,SAAS,cAAc;AACvB,SAAS,uBAAuB;AAChC,OAAO,SAAS;AAEhB,MAAM,WAAW,CAAC,MAChB,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,IAAI;AAG3D,SAAS,WAAW,SAAyB;AAClD,MAAI,CAAC,OAAO,SAAS,OAAO,KAAK,WAAW,EAAG,QAAO;AAEtD,QAAM,IAAI,KAAK,MAAM,UAAU,EAAE;AACjC,QAAM,IAAI,UAAU;AAEpB,MAAI,CAAC,EAAG,QAAO,GAAG,CAAC;AAEnB,SAAO,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC;AACrC;AAGO,SAAS,gBAAgB,EAAE,SAAS,GAAyB;AAClE,QAAM,EAAE,MAAM,WAAW,SAAS,OAAO,QAAQ,IAC/C,gBAAgB,QAAQ;AAE1B,MAAI,WAAW,CAAC;AACd,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,SAAS,MAAM,KAAK,QAAQ;AAAA;AAAA,IAC9B;AAGJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,MACA,YACE,YAAY,KAAK,IAAI,KAAK,aAAa,OAAO,yBAAyB;AAAA,MAEzE,SAAS,MAAM,WAAW;AAAA,MAC1B,YAAW;AAAA,MACX,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,sBAAQ;AAAA,QACZ,oBAAC,MAAG,kBAAI;AAAA,QACR,oBAAC,MAAG,yBAAW;AAAA,QACf,oBAAC,MAAG,qBAAO;AAAA,SACb;AAAA,MAGD,gBAAM,IAAI,CAAC,MACV,qBAAC,YACC;AAAA,4BAAC,QACC,+BAAC,UAAK,WAAW,IAAI,WACnB;AAAA,8BAAC,UAAK,WAAW,IAAI,MAAO,YAAE,MAAK;AAAA,UACnC,oBAAC,UAAK,WAAW,IAAI,QAClB,YAAE,SACC,EAAE,WAAW,EAAE,OACb,eAAe,EAAE,MAAM,KACvB,KACF,WACN;AAAA,WACF,GACF;AAAA,QACA,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IAAK,mBAAS,EAAE,IAAI,GAAE;AAAA,QACjE,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IACxC,qBAAW,EAAE,iBAAiB,GACjC;AAAA,QACA,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IACxC,iBAAO,EAAE,SAAS,GACrB;AAAA,WAnBa,EAAE,EAoBjB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import { Badge, TableRow, Th } from "@withpotter/ui-kit";
5
+ import { oliveOutlets } from "@withpotter/lib/admin-pos-olive";
6
+ import { FilterSearch, Filters } from "../_ui";
7
+ import { ListCard } from "../_ui/list-card";
8
+ import list from "../_ui/list-card.module.css";
9
+ import { OLIVE_NOTE, OliveProblem } from "./olive-state";
10
+ import { atTime } from "./pos-words";
11
+ import { useOliveOutlets } from "./use-pos";
12
+ function TenantOutlets({ tenantId }) {
13
+ const { data, isLoading, isError, error, refetch } = useOliveOutlets(tenantId);
14
+ const [search, setSearch] = useState("");
15
+ const outlets = useMemo(() => oliveOutlets(data), [data]);
16
+ const shown = useMemo(() => {
17
+ const q = search.trim().toLowerCase();
18
+ if (!q) return outlets;
19
+ return outlets.filter(
20
+ (o) => [o.name, o.address, o.contact].some((v) => v?.toLowerCase().includes(q))
21
+ );
22
+ }, [outlets, search]);
23
+ if (isError)
24
+ return /* @__PURE__ */ jsx(
25
+ OliveProblem,
26
+ {
27
+ error,
28
+ what: "outlets",
29
+ onRetry: () => void refetch()
30
+ }
31
+ );
32
+ return /* @__PURE__ */ jsx(
33
+ ListCard,
34
+ {
35
+ title: "Outlets",
36
+ count: outlets.length,
37
+ columns: 6,
38
+ minWidth: 860,
39
+ isLoading,
40
+ errorTitle: "",
41
+ isEmpty: shown.length === 0,
42
+ emptyTitle: search ? "No outlets match." : "No outlets yet.",
43
+ controls: /* @__PURE__ */ jsxs(Filters, { label: "Filter outlets", children: [
44
+ /* @__PURE__ */ jsx(
45
+ FilterSearch,
46
+ {
47
+ value: search,
48
+ onChange: setSearch,
49
+ placeholder: "Search outlets\u2026",
50
+ label: "Search outlets"
51
+ }
52
+ ),
53
+ /* @__PURE__ */ jsx("span", { className: "adm-caption", children: OLIVE_NOTE })
54
+ ] }),
55
+ head: /* @__PURE__ */ jsxs("tr", { children: [
56
+ /* @__PURE__ */ jsx(Th, { children: "Outlet" }),
57
+ /* @__PURE__ */ jsx(Th, { children: "Address" }),
58
+ /* @__PURE__ */ jsx(Th, { children: "Contact" }),
59
+ /* @__PURE__ */ jsx(Th, { children: "Terminals" }),
60
+ /* @__PURE__ */ jsx(Th, { children: "Status" }),
61
+ /* @__PURE__ */ jsx(Th, { children: "Created" })
62
+ ] }),
63
+ children: shown.map((o) => /* @__PURE__ */ jsxs(TableRow, { children: [
64
+ /* @__PURE__ */ jsx("td", { children: o.name }),
65
+ /* @__PURE__ */ jsx("td", { className: list.muted, children: o.address ?? "\u2013" }),
66
+ /* @__PURE__ */ jsx("td", { className: list.muted, children: o.contact ?? "\u2013" }),
67
+ /* @__PURE__ */ jsx("td", { className: list.nowrap, children: o.terminalCount ?? "\u2013" }),
68
+ /* @__PURE__ */ jsx("td", { className: list.nowrap, children: /* @__PURE__ */ jsx(Badge, { variant: o.active ? "success" : "secondary", children: o.active ? "Active" : "Inactive" }) }),
69
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: o.createdAt ? atTime(o.createdAt) : "\u2013" })
70
+ ] }, o.id))
71
+ }
72
+ );
73
+ }
74
+ export {
75
+ TenantOutlets
76
+ };
77
+ //# sourceMappingURL=tenant-outlets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/_pos/tenant-outlets.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { Badge, TableRow, Th } from '@withpotter/ui-kit';\nimport { oliveOutlets } from '@withpotter/lib/admin-pos-olive';\nimport { FilterSearch, Filters } from '../_ui';\nimport { ListCard } from '../_ui/list-card';\nimport list from '../_ui/list-card.module.css';\nimport { OLIVE_NOTE, OliveProblem } from './olive-state';\nimport { atTime } from './pos-words';\nimport { useOliveOutlets } from './use-pos';\n\n/** The business's outlets as Olive keeps them: where it sells, who to call, how many terminals. */\nexport function TenantOutlets({ tenantId }: { tenantId: string }) {\n const { data, isLoading, isError, error, refetch } =\n useOliveOutlets(tenantId);\n const [search, setSearch] = useState('');\n const outlets = useMemo(() => oliveOutlets(data), [data]);\n const shown = useMemo(() => {\n const q = search.trim().toLowerCase();\n\n if (!q) return outlets;\n\n return outlets.filter((o) =>\n [o.name, o.address, o.contact].some((v) => v?.toLowerCase().includes(q)),\n );\n }, [outlets, search]);\n\n if (isError)\n return (\n <OliveProblem\n error={error}\n what=\"outlets\"\n onRetry={() => void refetch()}\n />\n );\n\n return (\n <ListCard\n title=\"Outlets\"\n count={outlets.length}\n columns={6}\n minWidth={860}\n isLoading={isLoading}\n errorTitle=\"\"\n isEmpty={shown.length === 0}\n emptyTitle={search ? 'No outlets match.' : 'No outlets yet.'}\n controls={\n <Filters label=\"Filter outlets\">\n <FilterSearch\n value={search}\n onChange={setSearch}\n placeholder=\"Search outlets…\"\n label=\"Search outlets\"\n />\n <span className=\"adm-caption\">{OLIVE_NOTE}</span>\n </Filters>\n }\n head={\n <tr>\n <Th>Outlet</Th>\n <Th>Address</Th>\n <Th>Contact</Th>\n <Th>Terminals</Th>\n <Th>Status</Th>\n <Th>Created</Th>\n </tr>\n }\n >\n {shown.map((o) => (\n <TableRow key={o.id}>\n <td>{o.name}</td>\n <td className={list.muted}>{o.address ?? '–'}</td>\n <td className={list.muted}>{o.contact ?? '–'}</td>\n <td className={list.nowrap}>{o.terminalCount ?? '–'}</td>\n <td className={list.nowrap}>\n <Badge variant={o.active ? 'success' : 'secondary'}>\n {o.active ? 'Active' : 'Inactive'}\n </Badge>\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {o.createdAt ? atTime(o.createdAt) : '–'}\n </td>\n </TableRow>\n ))}\n </ListCard>\n );\n}\n"],"mappings":";AA8BM,cAkBE,YAlBF;AA5BN,SAAS,SAAS,gBAAgB;AAClC,SAAS,OAAO,UAAU,UAAU;AACpC,SAAS,oBAAoB;AAC7B,SAAS,cAAc,eAAe;AACtC,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,YAAY,oBAAoB;AACzC,SAAS,cAAc;AACvB,SAAS,uBAAuB;AAGzB,SAAS,cAAc,EAAE,SAAS,GAAyB;AAChE,QAAM,EAAE,MAAM,WAAW,SAAS,OAAO,QAAQ,IAC/C,gBAAgB,QAAQ;AAC1B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,EAAE;AACvC,QAAM,UAAU,QAAQ,MAAM,aAAa,IAAI,GAAG,CAAC,IAAI,CAAC;AACxD,QAAM,QAAQ,QAAQ,MAAM;AAC1B,UAAM,IAAI,OAAO,KAAK,EAAE,YAAY;AAEpC,QAAI,CAAC,EAAG,QAAO;AAEf,WAAO,QAAQ;AAAA,MAAO,CAAC,MACrB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,YAAY,EAAE,SAAS,CAAC,CAAC;AAAA,IACzE;AAAA,EACF,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,MAAI;AACF,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,SAAS,MAAM,KAAK,QAAQ;AAAA;AAAA,IAC9B;AAGJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO,QAAQ;AAAA,MACf,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,MACA,YAAW;AAAA,MACX,SAAS,MAAM,WAAW;AAAA,MAC1B,YAAY,SAAS,sBAAsB;AAAA,MAC3C,UACE,qBAAC,WAAQ,OAAM,kBACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU;AAAA,YACV,aAAY;AAAA,YACZ,OAAM;AAAA;AAAA,QACR;AAAA,QACA,oBAAC,UAAK,WAAU,eAAe,sBAAW;AAAA,SAC5C;AAAA,MAEF,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,qBAAO;AAAA,QACX,oBAAC,MAAG,qBAAO;AAAA,QACX,oBAAC,MAAG,uBAAS;AAAA,QACb,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,qBAAO;AAAA,SACb;AAAA,MAGD,gBAAM,IAAI,CAAC,MACV,qBAAC,YACC;AAAA,4BAAC,QAAI,YAAE,MAAK;AAAA,QACZ,oBAAC,QAAG,WAAW,KAAK,OAAQ,YAAE,WAAW,UAAI;AAAA,QAC7C,oBAAC,QAAG,WAAW,KAAK,OAAQ,YAAE,WAAW,UAAI;AAAA,QAC7C,oBAAC,QAAG,WAAW,KAAK,QAAS,YAAE,iBAAiB,UAAI;AAAA,QACpD,oBAAC,QAAG,WAAW,KAAK,QAClB,8BAAC,SAAM,SAAS,EAAE,SAAS,YAAY,aACpC,YAAE,SAAS,WAAW,YACzB,GACF;AAAA,QACA,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IACxC,YAAE,YAAY,OAAO,EAAE,SAAS,IAAI,UACvC;AAAA,WAZa,EAAE,EAajB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Badge, TableRow, Th } from "@withpotter/ui-kit";
4
+ import { ListCard } from "../_ui/list-card";
5
+ import list from "../_ui/list-card.module.css";
6
+ import { LoadError } from "./pos-parts";
7
+ import { isForbidden, posErrorText } from "./pos-rules";
8
+ import { atTime, count } from "./pos-words";
9
+ import { useTenantTillRequests } from "./use-pos";
10
+ import pos from "./pos.module.css";
11
+ const LABEL = {
12
+ pending: "Pending",
13
+ approved: "Approved",
14
+ rejected: "Rejected",
15
+ fulfilled: "Provisioned"
16
+ };
17
+ const TONE = {
18
+ pending: "warning",
19
+ approved: "success",
20
+ rejected: "secondary",
21
+ fulfilled: "success"
22
+ };
23
+ function TenantRequests({ tenantId }) {
24
+ const { data, isLoading, isError, error, refetch } = useTenantTillRequests(tenantId);
25
+ if (isError && !data)
26
+ return /* @__PURE__ */ jsx(
27
+ LoadError,
28
+ {
29
+ error,
30
+ what: "this business's terminal requests",
31
+ onRetry: () => void refetch()
32
+ }
33
+ );
34
+ return /* @__PURE__ */ jsx(
35
+ ListCard,
36
+ {
37
+ title: "Terminal requests",
38
+ count: data?.length,
39
+ columns: 3,
40
+ minWidth: 560,
41
+ isLoading,
42
+ errorTitle: isForbidden(error) ? "" : posErrorText(error, "Requests did not load."),
43
+ isEmpty: data?.length === 0,
44
+ emptyTitle: "This business has not asked for any terminals.",
45
+ head: /* @__PURE__ */ jsxs("tr", { children: [
46
+ /* @__PURE__ */ jsx(Th, { children: "Request" }),
47
+ /* @__PURE__ */ jsx(Th, { children: "Status" }),
48
+ /* @__PURE__ */ jsx(Th, { children: "Requested" })
49
+ ] }),
50
+ children: data?.map((r) => /* @__PURE__ */ jsxs(TableRow, { children: [
51
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: pos.cellStack, children: [
52
+ /* @__PURE__ */ jsx("span", { className: pos.clip, children: `${count(r.quantity, "terminal")} requested` }),
53
+ (r.decisionNote || r.note) && /* @__PURE__ */ jsx("span", { className: pos.subtle, children: r.decisionNote ?? r.note })
54
+ ] }) }),
55
+ /* @__PURE__ */ jsx("td", { className: list.nowrap, children: /* @__PURE__ */ jsx(Badge, { variant: TONE[r.status] ?? "secondary", children: LABEL[r.status] ?? r.status }) }),
56
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: atTime(r.createdAt) })
57
+ ] }, r.id))
58
+ }
59
+ );
60
+ }
61
+ export {
62
+ TenantRequests
63
+ };
64
+ //# sourceMappingURL=tenant-requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/_pos/tenant-requests.tsx"],"sourcesContent":["'use client';\n\nimport { Badge, TableRow, Th } from '@withpotter/ui-kit';\nimport type { TillRequestStatus } from '@withpotter/lib/admin-pos-types';\nimport { ListCard } from '../_ui/list-card';\nimport list from '../_ui/list-card.module.css';\nimport { LoadError } from './pos-parts';\nimport { isForbidden, posErrorText } from './pos-rules';\nimport { atTime, count } from './pos-words';\nimport { useTenantTillRequests } from './use-pos';\nimport pos from './pos.module.css';\n\nconst LABEL: Record<TillRequestStatus, string> = {\n pending: 'Pending',\n approved: 'Approved',\n rejected: 'Rejected',\n fulfilled: 'Provisioned',\n};\n\nconst TONE = {\n pending: 'warning',\n approved: 'success',\n rejected: 'secondary',\n fulfilled: 'success',\n} as const;\n\n/** The terminals the business has asked for, newest first, and where each request stands. */\nexport function TenantRequests({ tenantId }: { tenantId: string }) {\n const { data, isLoading, isError, error, refetch } =\n useTenantTillRequests(tenantId);\n\n if (isError && !data)\n return (\n <LoadError\n error={error}\n what=\"this business's terminal requests\"\n onRetry={() => void refetch()}\n />\n );\n\n return (\n <ListCard\n title=\"Terminal requests\"\n count={data?.length}\n columns={3}\n minWidth={560}\n isLoading={isLoading}\n errorTitle={\n isForbidden(error) ? '' : posErrorText(error, 'Requests did not load.')\n }\n isEmpty={data?.length === 0}\n emptyTitle=\"This business has not asked for any terminals.\"\n head={\n <tr>\n <Th>Request</Th>\n <Th>Status</Th>\n <Th>Requested</Th>\n </tr>\n }\n >\n {data?.map((r) => (\n <TableRow key={r.id}>\n <td>\n <span className={pos.cellStack}>\n <span className={pos.clip}>\n {`${count(r.quantity, 'terminal')} requested`}\n </span>\n {(r.decisionNote || r.note) && (\n <span className={pos.subtle}>{r.decisionNote ?? r.note}</span>\n )}\n </span>\n </td>\n <td className={list.nowrap}>\n <Badge variant={TONE[r.status] ?? 'secondary'}>\n {LABEL[r.status] ?? r.status}\n </Badge>\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {atTime(r.createdAt)}\n </td>\n </TableRow>\n ))}\n </ListCard>\n );\n}\n"],"mappings":";AAiCM,cAoBE,YApBF;AA/BN,SAAS,OAAO,UAAU,UAAU;AAEpC,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,aAAa,oBAAoB;AAC1C,SAAS,QAAQ,aAAa;AAC9B,SAAS,6BAA6B;AACtC,OAAO,SAAS;AAEhB,MAAM,QAA2C;AAAA,EAC/C,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,WAAW;AACb;AAEA,MAAM,OAAO;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,WAAW;AACb;AAGO,SAAS,eAAe,EAAE,SAAS,GAAyB;AACjE,QAAM,EAAE,MAAM,WAAW,SAAS,OAAO,QAAQ,IAC/C,sBAAsB,QAAQ;AAEhC,MAAI,WAAW,CAAC;AACd,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,SAAS,MAAM,KAAK,QAAQ;AAAA;AAAA,IAC9B;AAGJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,MACA,YACE,YAAY,KAAK,IAAI,KAAK,aAAa,OAAO,wBAAwB;AAAA,MAExE,SAAS,MAAM,WAAW;AAAA,MAC1B,YAAW;AAAA,MACX,MACE,qBAAC,QACC;AAAA,4BAAC,MAAG,qBAAO;AAAA,QACX,oBAAC,MAAG,oBAAM;AAAA,QACV,oBAAC,MAAG,uBAAS;AAAA,SACf;AAAA,MAGD,gBAAM,IAAI,CAAC,MACV,qBAAC,YACC;AAAA,4BAAC,QACC,+BAAC,UAAK,WAAW,IAAI,WACnB;AAAA,8BAAC,UAAK,WAAW,IAAI,MAClB,aAAG,MAAM,EAAE,UAAU,UAAU,CAAC,cACnC;AAAA,WACE,EAAE,gBAAgB,EAAE,SACpB,oBAAC,UAAK,WAAW,IAAI,QAAS,YAAE,gBAAgB,EAAE,MAAK;AAAA,WAE3D,GACF;AAAA,QACA,oBAAC,QAAG,WAAW,KAAK,QAClB,8BAAC,SAAM,SAAS,KAAK,EAAE,MAAM,KAAK,aAC/B,gBAAM,EAAE,MAAM,KAAK,EAAE,QACxB,GACF;AAAA,QACA,oBAAC,QAAG,WAAW,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IACxC,iBAAO,EAAE,SAAS,GACrB;AAAA,WAlBa,EAAE,EAmBjB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,145 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import { Badge, Button, TableRow, Th } from "@withpotter/ui-kit";
5
+ import { oliveTransactions } from "@withpotter/lib/admin-pos-olive";
6
+ import { formatMinor } from "@withpotter/lib/money-minor";
7
+ import { FilterPress, Filters } from "../_ui";
8
+ import { ListCard } from "../_ui/list-card";
9
+ import list from "../_ui/list-card.module.css";
10
+ import { OLIVE_NOTE, OliveProblem, naira } from "./olive-state";
11
+ import { LoadError } from "./pos-parts";
12
+ import { isForbidden, posErrorText } from "./pos-rules";
13
+ import { atTime } from "./pos-words";
14
+ import { useOliveAccountTransactions, useTenantPayments } from "./use-pos";
15
+ import pos from "./pos.module.css";
16
+ function TenantTransactions({ tenantId }) {
17
+ const [source, setSource] = useState("card");
18
+ const switcher = /* @__PURE__ */ jsxs(Filters, { label: "Transactions source", children: [
19
+ /* @__PURE__ */ jsx(FilterPress, { on: source === "card", onToggle: () => setSource("card"), children: "Card payments" }),
20
+ /* @__PURE__ */ jsx(
21
+ FilterPress,
22
+ {
23
+ on: source === "account",
24
+ onToggle: () => setSource("account"),
25
+ children: "POS account"
26
+ }
27
+ )
28
+ ] });
29
+ return source === "card" ? /* @__PURE__ */ jsx(CardPayments, { tenantId, controls: switcher }) : /* @__PURE__ */ jsx(AccountLines, { tenantId, controls: switcher });
30
+ }
31
+ function CardPayments({
32
+ tenantId,
33
+ controls
34
+ }) {
35
+ const q = useTenantPayments(tenantId);
36
+ const rows = useMemo(
37
+ () => q.data?.pages.flatMap((p) => p.rows) ?? [],
38
+ [q.data]
39
+ );
40
+ if (q.isError && !q.data)
41
+ return /* @__PURE__ */ jsx(
42
+ LoadError,
43
+ {
44
+ error: q.error,
45
+ what: "this business's card payments",
46
+ onRetry: () => void q.refetch()
47
+ }
48
+ );
49
+ return /* @__PURE__ */ jsx(
50
+ ListCard,
51
+ {
52
+ title: "Card payments",
53
+ count: q.hasNextPage ? void 0 : rows.length,
54
+ columns: 5,
55
+ minWidth: 760,
56
+ isLoading: q.isLoading,
57
+ errorTitle: isForbidden(q.error) ? "" : posErrorText(q.error, "Payments did not load."),
58
+ isEmpty: rows.length === 0,
59
+ emptyTitle: "No card payments yet.",
60
+ controls,
61
+ head: /* @__PURE__ */ jsxs("tr", { children: [
62
+ /* @__PURE__ */ jsx(Th, { children: "Terminal" }),
63
+ /* @__PURE__ */ jsx(Th, { children: "Reference" }),
64
+ /* @__PURE__ */ jsx(Th, { children: "Status" }),
65
+ /* @__PURE__ */ jsx(Th, { children: "Amount" }),
66
+ /* @__PURE__ */ jsx(Th, { children: "When" })
67
+ ] }),
68
+ footer: q.hasNextPage ? /* @__PURE__ */ jsx("div", { className: pos.stack, children: /* @__PURE__ */ jsx(
69
+ Button,
70
+ {
71
+ size: "sm",
72
+ variant: "secondary",
73
+ disabled: q.isFetchingNextPage,
74
+ onClick: () => void q.fetchNextPage(),
75
+ children: q.isFetchingNextPage ? "Loading\u2026" : "Show more"
76
+ }
77
+ ) }) : void 0,
78
+ children: rows.map((p) => /* @__PURE__ */ jsxs(TableRow, { children: [
79
+ /* @__PURE__ */ jsx("td", { children: p.deviceLabel ?? "\u2013" }),
80
+ /* @__PURE__ */ jsx("td", { className: list.muted, children: p.reference ?? "\u2013" }),
81
+ /* @__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() }) }),
82
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${pos.num}`, children: `${p.direction === "out" ? "-" : ""}${formatMinor(
83
+ p.amountMinor,
84
+ p.currency
85
+ )}` }),
86
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: atTime(p.at) })
87
+ ] }, p.id))
88
+ }
89
+ );
90
+ }
91
+ function AccountLines({
92
+ tenantId,
93
+ controls
94
+ }) {
95
+ const q = useOliveAccountTransactions(tenantId);
96
+ const rows = useMemo(() => oliveTransactions(q.data), [q.data]);
97
+ if (q.isError)
98
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
99
+ controls,
100
+ /* @__PURE__ */ jsx(
101
+ OliveProblem,
102
+ {
103
+ error: q.error,
104
+ what: "account transactions",
105
+ onRetry: () => void q.refetch()
106
+ }
107
+ )
108
+ ] });
109
+ return /* @__PURE__ */ jsx(
110
+ ListCard,
111
+ {
112
+ title: "POS account",
113
+ count: rows.length,
114
+ columns: 4,
115
+ minWidth: 680,
116
+ isLoading: q.isLoading,
117
+ errorTitle: "",
118
+ isEmpty: rows.length === 0,
119
+ emptyTitle: "No account transactions yet.",
120
+ controls: /* @__PURE__ */ jsxs(Fragment, { children: [
121
+ controls,
122
+ /* @__PURE__ */ jsx("span", { className: "adm-caption", children: OLIVE_NOTE })
123
+ ] }),
124
+ head: /* @__PURE__ */ jsxs("tr", { children: [
125
+ /* @__PURE__ */ jsx(Th, { children: "Transaction" }),
126
+ /* @__PURE__ */ jsx(Th, { children: "Status" }),
127
+ /* @__PURE__ */ jsx(Th, { children: "Amount" }),
128
+ /* @__PURE__ */ jsx(Th, { children: "When" })
129
+ ] }),
130
+ children: rows.map((t) => /* @__PURE__ */ jsxs(TableRow, { children: [
131
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", { className: pos.cellStack, children: [
132
+ /* @__PURE__ */ jsx("span", { className: pos.clip, children: t.summary }),
133
+ t.reference && /* @__PURE__ */ jsx("span", { className: pos.subtle, children: t.reference })
134
+ ] }) }),
135
+ /* @__PURE__ */ jsx("td", { className: list.nowrap, children: t.status ?? "\u2013" }),
136
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${pos.num}`, children: `${t.direction === "in" ? "+" : t.direction === "out" ? "-" : ""}${naira(t.amountMinor)}` }),
137
+ /* @__PURE__ */ jsx("td", { className: `${list.nowrap} ${list.muted}`, children: t.at ? atTime(t.at) : "\u2013" })
138
+ ] }, t.id))
139
+ }
140
+ );
141
+ }
142
+ export {
143
+ TenantTransactions
144
+ };
145
+ //# sourceMappingURL=tenant-transactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/_pos/tenant-transactions.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { Badge, Button, TableRow, Th } from '@withpotter/ui-kit';\nimport { oliveTransactions } from '@withpotter/lib/admin-pos-olive';\nimport { formatMinor } from '@withpotter/lib/money-minor';\nimport { FilterPress, Filters } from '../_ui';\nimport { ListCard } from '../_ui/list-card';\nimport list from '../_ui/list-card.module.css';\nimport { OLIVE_NOTE, OliveProblem, naira } from './olive-state';\nimport { LoadError } from './pos-parts';\nimport { isForbidden, posErrorText } from './pos-rules';\nimport { atTime } from './pos-words';\nimport { useOliveAccountTransactions, useTenantPayments } from './use-pos';\nimport pos from './pos.module.css';\n\ntype Source = 'card' | 'account';\n\n/**\n * What moved: the card payments its terminals took (Box's own record), or the lines on its POS account\n * (Olive's). One switch; the other source is not fetched until it is chosen.\n */\nexport function TenantTransactions({ tenantId }: { tenantId: string }) {\n const [source, setSource] = useState<Source>('card');\n\n const switcher = (\n <Filters label=\"Transactions source\">\n <FilterPress on={source === 'card'} onToggle={() => setSource('card')}>\n Card payments\n </FilterPress>\n <FilterPress\n on={source === 'account'}\n onToggle={() => setSource('account')}\n >\n POS account\n </FilterPress>\n </Filters>\n );\n\n return source === 'card' ? (\n <CardPayments tenantId={tenantId} controls={switcher} />\n ) : (\n <AccountLines tenantId={tenantId} controls={switcher} />\n );\n}\n\nfunction CardPayments({\n tenantId,\n controls,\n}: {\n tenantId: string;\n controls: React.ReactNode;\n}) {\n const q = useTenantPayments(tenantId);\n const rows = useMemo(\n () => q.data?.pages.flatMap((p) => p.rows) ?? [],\n [q.data],\n );\n\n if (q.isError && !q.data)\n return (\n <LoadError\n error={q.error}\n what=\"this business's card payments\"\n onRetry={() => void q.refetch()}\n />\n );\n\n return (\n <ListCard\n title=\"Card payments\"\n count={q.hasNextPage ? undefined : rows.length}\n columns={5}\n minWidth={760}\n isLoading={q.isLoading}\n errorTitle={\n isForbidden(q.error)\n ? ''\n : posErrorText(q.error, 'Payments did not load.')\n }\n isEmpty={rows.length === 0}\n emptyTitle=\"No card payments yet.\"\n controls={controls}\n head={\n <tr>\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>{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\nfunction AccountLines({\n tenantId,\n controls,\n}: {\n tenantId: string;\n controls: React.ReactNode;\n}) {\n const q = useOliveAccountTransactions(tenantId);\n const rows = useMemo(() => oliveTransactions(q.data), [q.data]);\n\n if (q.isError)\n return (\n <>\n {controls}\n <OliveProblem\n error={q.error}\n what=\"account transactions\"\n onRetry={() => void q.refetch()}\n />\n </>\n );\n\n return (\n <ListCard\n title=\"POS account\"\n count={rows.length}\n columns={4}\n minWidth={680}\n isLoading={q.isLoading}\n errorTitle=\"\"\n isEmpty={rows.length === 0}\n emptyTitle=\"No account transactions yet.\"\n controls={\n <>\n {controls}\n <span className=\"adm-caption\">{OLIVE_NOTE}</span>\n </>\n }\n head={\n <tr>\n <Th>Transaction</Th>\n <Th>Status</Th>\n <Th>Amount</Th>\n <Th>When</Th>\n </tr>\n }\n >\n {rows.map((t) => (\n <TableRow key={t.id}>\n <td>\n <span className={pos.cellStack}>\n <span className={pos.clip}>{t.summary}</span>\n {t.reference && <span className={pos.subtle}>{t.reference}</span>}\n </span>\n </td>\n <td className={list.nowrap}>{t.status ?? '–'}</td>\n <td className={`${list.nowrap} ${pos.num}`}>\n {`${\n t.direction === 'in' ? '+' : t.direction === 'out' ? '-' : ''\n }${naira(t.amountMinor)}`}\n </td>\n <td className={`${list.nowrap} ${list.muted}`}>\n {t.at ? atTime(t.at) : '–'}\n </td>\n </TableRow>\n ))}\n </ListCard>\n );\n}\n"],"mappings":"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","names":[]}
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  keepPreviousData,
4
4
  useMutation,
5
- useQueries,
5
+ useInfiniteQuery,
6
6
  useQuery,
7
7
  useQueryClient
8
8
  } from "@tanstack/react-query";
9
9
  import { adminPosApi } from "@withpotter/lib/admin-pos-api";
10
- import { isForbidden, isNotFound } from "./pos-rules";
10
+ import { isForbidden, isNotFound, isNotLinked } from "./pos-rules";
11
11
  const POS_KEY = ["platform-admin", "pos"];
12
12
  const key = (...parts) => [...POS_KEY, ...parts];
13
13
  const retry = (count, error) => !isForbidden(error) && !isNotFound(error) && count < 1;
@@ -53,14 +53,58 @@ function usePosShifts(filters, enabled = true) {
53
53
  retry
54
54
  });
55
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
- }))
56
+ function useTenantRoster(tenantId, enabled = true) {
57
+ return useQuery({
58
+ queryKey: key("roster", tenantId),
59
+ queryFn: () => adminPosApi.roster(tenantId),
60
+ enabled,
61
+ retry
62
+ });
63
+ }
64
+ function useTenantTillRequests(tenantId, enabled = true) {
65
+ return useQuery({
66
+ queryKey: key("till-requests", tenantId),
67
+ queryFn: () => adminPosApi.tillRequests(tenantId),
68
+ enabled,
69
+ retry
70
+ });
71
+ }
72
+ function useTenantPayments(tenantId, enabled = true) {
73
+ return useInfiniteQuery({
74
+ queryKey: key("payments", tenantId),
75
+ queryFn: ({ pageParam }) => adminPosApi.payments({ tenantId, cursor: pageParam, limit: 25 }),
76
+ initialPageParam: void 0,
77
+ getNextPageParam: (last) => last.nextCursor ?? void 0,
78
+ enabled,
79
+ retry
80
+ });
81
+ }
82
+ const oliveRetry = (count, error) => !isForbidden(error) && !isNotFound(error) && !isNotLinked(error) && count < 1;
83
+ function useOliveAccount(tenantId, enabled = true) {
84
+ return useQuery({
85
+ queryKey: key("olive", tenantId, "account"),
86
+ queryFn: () => adminPosApi.olive.account(tenantId),
87
+ enabled,
88
+ retry: oliveRetry,
89
+ staleTime: 6e4
90
+ });
91
+ }
92
+ function useOliveAccountTransactions(tenantId, enabled = true) {
93
+ return useQuery({
94
+ queryKey: key("olive", tenantId, "account-transactions"),
95
+ queryFn: () => adminPosApi.olive.accountTransactions(tenantId),
96
+ enabled,
97
+ retry: oliveRetry,
98
+ staleTime: 6e4
99
+ });
100
+ }
101
+ function useOliveOutlets(tenantId, enabled = true) {
102
+ return useQuery({
103
+ queryKey: key("olive", tenantId, "outlets"),
104
+ queryFn: () => adminPosApi.olive.outlets(tenantId),
105
+ enabled,
106
+ retry: oliveRetry,
107
+ staleTime: 6e4
64
108
  });
65
109
  }
66
110
  function useTenantPos(tenantId, w, enabled = true) {
@@ -89,13 +133,18 @@ const useCloseShift = () => usePosWrite(
89
133
  export {
90
134
  POS_KEY,
91
135
  useCloseShift,
136
+ useOliveAccount,
137
+ useOliveAccountTransactions,
138
+ useOliveOutlets,
92
139
  usePosHistory,
93
140
  usePosShifts,
94
141
  usePosSummary,
95
142
  usePosTerminal,
96
143
  usePosTerminals,
97
- useTenantOperators,
144
+ useTenantPayments,
98
145
  useTenantPos,
146
+ useTenantRoster,
147
+ useTenantTillRequests,
99
148
  useTerminalStatus
100
149
  };
101
150
  //# sourceMappingURL=use-pos.js.map