@burdenoff/microfe-devportal 2026.1024.1 → 2026.1026.2

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 (33) hide show
  1. package/dist/devportal/components/NodeSmokePanel.d.ts +34 -0
  2. package/dist/devportal/components/NodeSmokePanel.d.ts.map +1 -0
  3. package/dist/devportal/components/NodeSmokePanel.js +131 -0
  4. package/dist/devportal/components/NodeSmokePanel.js.map +1 -0
  5. package/dist/devportal/components/PackageValidationPanel.d.ts.map +1 -1
  6. package/dist/devportal/components/PackageValidationPanel.js +46 -36
  7. package/dist/devportal/components/PackageValidationPanel.js.map +1 -1
  8. package/dist/devportal/components/index.d.ts +1 -0
  9. package/dist/devportal/components/index.d.ts.map +1 -1
  10. package/dist/devportal/components/index.js +1 -0
  11. package/dist/devportal/hooks/useSellerOrganization.d.ts.map +1 -1
  12. package/dist/devportal/hooks/useSellerOrganization.js +23 -22
  13. package/dist/devportal/hooks/useSellerOrganization.js.map +1 -1
  14. package/dist/devportal/pages/CreatePhysicalItemPage.d.ts.map +1 -1
  15. package/dist/devportal/pages/CreatePhysicalItemPage.js +119 -118
  16. package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
  17. package/dist/devportal/pages/EditPhysicalItemPage.d.ts.map +1 -1
  18. package/dist/devportal/pages/EditPhysicalItemPage.js +61 -60
  19. package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
  20. package/dist/devportal/pages/SellerDashboardPage.d.ts.map +1 -1
  21. package/dist/devportal/pages/SellerDashboardPage.js +142 -141
  22. package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
  23. package/dist/devportal/pages/SellerItemsPage.d.ts.map +1 -1
  24. package/dist/devportal/pages/SellerItemsPage.js +190 -189
  25. package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
  26. package/dist/devportal/pages/SellerOrdersPage.d.ts.map +1 -1
  27. package/dist/devportal/pages/SellerOrdersPage.js +43 -42
  28. package/dist/devportal/pages/SellerOrdersPage.js.map +1 -1
  29. package/dist/devportal/utils/graphqlEndpoint.d.ts +6 -0
  30. package/dist/devportal/utils/graphqlEndpoint.d.ts.map +1 -0
  31. package/dist/devportal/utils/graphqlEndpoint.js +9 -0
  32. package/dist/devportal/utils/graphqlEndpoint.js.map +1 -0
  33. package/package.json +1 -1
@@ -0,0 +1,34 @@
1
+ import { FC } from 'react';
2
+
3
+ export type SmokeOperationStatus = "PASS" | "FAIL" | "SKIP";
4
+ export interface SmokeOperation {
5
+ name: string;
6
+ status: SmokeOperationStatus;
7
+ detail: string | null;
8
+ error: string | null;
9
+ durationMs: number;
10
+ }
11
+ export type NodeSmokeState = {
12
+ kind: "none";
13
+ } | {
14
+ kind: "running";
15
+ } | {
16
+ kind: "passed" | "failed" | "skipped";
17
+ operations: SmokeOperation[];
18
+ passed: number;
19
+ failed: number;
20
+ skipped: number;
21
+ error: string | null;
22
+ finishedAt: string | null;
23
+ };
24
+ /** Parse the stored record defensively — metadata is untyped JSON. */
25
+ export declare function readNodeSmokeState(metadata: unknown): NodeSmokeState;
26
+ interface NodeSmokePanelProps {
27
+ metadata: unknown;
28
+ /** Start expanded (the admin review card does; the versions table does not). */
29
+ defaultExpanded?: boolean;
30
+ className?: string;
31
+ }
32
+ export declare const NodeSmokePanel: FC<NodeSmokePanelProps>;
33
+ export {};
34
+ //# sourceMappingURL=NodeSmokePanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeSmokePanel.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/NodeSmokePanel.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAahC,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IACE,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACtC,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AA6BN,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,cAAc,CAmBpE;AA+BD,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,gFAAgF;IAChF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CAwGlD,CAAC"}
@@ -0,0 +1,131 @@
1
+ import { useTr as e } from "../utils/i18n.js";
2
+ import { cn as t } from "../utils/cn.js";
3
+ import { useState as n } from "react";
4
+ import { CheckCircle2 as r, ChevronDown as i, ChevronUp as a, Loader2 as o, MinusCircle as s, XCircle as c } from "lucide-react";
5
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
6
+ //#region src/devportal/components/NodeSmokePanel.tsx
7
+ function d(e) {
8
+ return typeof e == "object" && !!e && !Array.isArray(e);
9
+ }
10
+ function f(e) {
11
+ return Array.isArray(e) ? e.filter(d).map((e) => ({
12
+ name: typeof e.name == "string" ? e.name : "",
13
+ status: e.status === "PASS" || e.status === "SKIP" ? e.status : "FAIL",
14
+ detail: typeof e.detail == "string" ? e.detail : null,
15
+ error: typeof e.error == "string" ? e.error : null,
16
+ durationMs: typeof e.durationMs == "number" ? e.durationMs : 0
17
+ })) : [];
18
+ }
19
+ var p = (e) => typeof e == "number" ? e : 0, m = {
20
+ PASSED: "passed",
21
+ FAILED: "failed",
22
+ SKIPPED: "skipped"
23
+ };
24
+ function h(e) {
25
+ if (!d(e)) return { kind: "none" };
26
+ let t = e.packageSmoke;
27
+ if (!d(t) || typeof t.status != "string") return { kind: "none" };
28
+ if (t.status === "RUNNING") return { kind: "running" };
29
+ let n = m[t.status];
30
+ return n ? {
31
+ kind: n,
32
+ operations: f(t.operations),
33
+ passed: p(t.passed),
34
+ failed: p(t.failed),
35
+ skipped: p(t.skipped),
36
+ error: typeof t.error == "string" ? t.error : null,
37
+ finishedAt: typeof t.finishedAt == "string" ? t.finishedAt : null
38
+ } : { kind: "none" };
39
+ }
40
+ var g = {
41
+ PASS: "text-status-success-text",
42
+ FAIL: "text-status-error-text",
43
+ SKIP: "text-text-secondary"
44
+ }, _ = ({ operations: e }) => /* @__PURE__ */ l("ul", {
45
+ className: "space-y-1",
46
+ children: e.map((e, n) => /* @__PURE__ */ u("li", {
47
+ className: "text-xs leading-snug",
48
+ children: [
49
+ /* @__PURE__ */ l("code", {
50
+ className: "mr-1 rounded bg-bg-sunken px-1 py-0.5 font-mono text-text-primary",
51
+ children: e.name
52
+ }),
53
+ /* @__PURE__ */ l("span", {
54
+ className: t("font-medium", g[e.status]),
55
+ children: e.status
56
+ }),
57
+ (e.error ?? e.detail) && /* @__PURE__ */ l("span", {
58
+ className: "ml-1 text-text-secondary",
59
+ children: e.error ?? e.detail
60
+ }),
61
+ /* @__PURE__ */ u("span", {
62
+ className: "ml-1 text-text-tertiary",
63
+ children: [e.durationMs, " ms"]
64
+ })
65
+ ]
66
+ }, `${e.name}-${n}`))
67
+ }), v = ({ metadata: d, defaultExpanded: f = !1, className: p }) => {
68
+ let m = e(), g = h(d), [v, y] = n(f);
69
+ if (g.kind === "none") return null;
70
+ if (g.kind === "running") return /* @__PURE__ */ u("p", {
71
+ className: t("inline-flex items-center gap-1 text-xs text-status-info-text", p),
72
+ "data-testid": "node-smoke",
73
+ "data-state": "running",
74
+ children: [/* @__PURE__ */ l(o, {
75
+ size: 12,
76
+ className: "animate-spin"
77
+ }), m("devportal.nodeSmoke.running", "Testing operations…")]
78
+ });
79
+ let b = {
80
+ passed: m("devportal.nodeSmoke.passed", "Operations tested: {{passed}} passed, {{skipped}} skipped", {
81
+ passed: g.passed,
82
+ skipped: g.skipped
83
+ }),
84
+ failed: m("devportal.nodeSmoke.failed", "Operation tests failed: {{failed}} of {{total}}", {
85
+ failed: g.failed,
86
+ total: g.operations.length
87
+ }),
88
+ skipped: m("devportal.nodeSmoke.skipped", "Operation tests not run in this environment")
89
+ }, x = {
90
+ passed: /* @__PURE__ */ l(r, { size: 12 }),
91
+ failed: /* @__PURE__ */ l(c, { size: 12 }),
92
+ skipped: /* @__PURE__ */ l(s, { size: 12 })
93
+ }, S = {
94
+ passed: "text-status-success-text",
95
+ failed: "text-status-error-text",
96
+ skipped: "text-text-secondary"
97
+ }, C = g.operations.length > 0 || g.error !== null;
98
+ return /* @__PURE__ */ u("div", {
99
+ className: t("space-y-1", p),
100
+ "data-testid": "node-smoke",
101
+ "data-state": g.kind,
102
+ children: [/* @__PURE__ */ u("button", {
103
+ type: "button",
104
+ onClick: () => C && y((e) => !e),
105
+ className: t("inline-flex items-center gap-1 text-xs font-medium", S[g.kind], C ? "hover:underline" : "cursor-default"),
106
+ "aria-expanded": C ? v : void 0,
107
+ children: [
108
+ x[g.kind],
109
+ b[g.kind],
110
+ C && l(v ? a : i, { size: 12 })
111
+ ]
112
+ }), v && C && /* @__PURE__ */ u("div", {
113
+ className: "max-w-md space-y-2 rounded-md border border-border-seam bg-bg-surface p-2",
114
+ children: [
115
+ g.error && /* @__PURE__ */ l("p", {
116
+ className: t("text-xs", g.kind === "skipped" ? "text-text-secondary" : "text-status-error-text"),
117
+ children: g.error
118
+ }),
119
+ g.operations.length > 0 && /* @__PURE__ */ l(_, { operations: g.operations }),
120
+ g.finishedAt && /* @__PURE__ */ l("p", {
121
+ className: "text-[11px] text-text-secondary",
122
+ children: new Date(g.finishedAt).toLocaleString()
123
+ })
124
+ ]
125
+ })]
126
+ });
127
+ };
128
+ //#endregion
129
+ export { v as NodeSmokePanel, h as readNodeSmokeState };
130
+
131
+ //# sourceMappingURL=NodeSmokePanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeSmokePanel.js","names":[],"sources":["../../../src/devportal/components/NodeSmokePanel.tsx"],"sourcesContent":["/**\n * NodeSmokePanel — shows the operation smoke run (BOFF-9656) that\n * global-devportal-svc records on `ApplicationVersion.metadata.packageSmoke`.\n *\n * Once a custom node's sandbox image is built, the service starts it in a\n * sandbox and calls every manifest operation with an empty input, the same\n * check `fluidgrids-node smoke` runs locally. A version whose run failed, or\n * is still running, cannot be submitted or approved; this panel is how the\n * developer sees which operation broke and how the reviewer sees the result.\n */\nimport { useState } from \"react\";\nimport type { FC } from \"react\";\nimport {\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Loader2,\n MinusCircle,\n XCircle,\n} from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\n\nexport type SmokeOperationStatus = \"PASS\" | \"FAIL\" | \"SKIP\";\n\nexport interface SmokeOperation {\n name: string;\n status: SmokeOperationStatus;\n detail: string | null;\n error: string | null;\n durationMs: number;\n}\n\nexport type NodeSmokeState =\n | { kind: \"none\" }\n | { kind: \"running\" }\n | {\n kind: \"passed\" | \"failed\" | \"skipped\";\n operations: SmokeOperation[];\n passed: number;\n failed: number;\n skipped: number;\n error: string | null;\n finishedAt: string | null;\n };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readOperations(value: unknown): SmokeOperation[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).map((entry) => ({\n name: typeof entry.name === \"string\" ? entry.name : \"\",\n status:\n entry.status === \"PASS\" || entry.status === \"SKIP\"\n ? entry.status\n : \"FAIL\",\n detail: typeof entry.detail === \"string\" ? entry.detail : null,\n error: typeof entry.error === \"string\" ? entry.error : null,\n durationMs: typeof entry.durationMs === \"number\" ? entry.durationMs : 0,\n }));\n}\n\nconst count = (value: unknown): number =>\n typeof value === \"number\" ? value : 0;\n\nconst SETTLED_KINDS: Record<string, \"passed\" | \"failed\" | \"skipped\"> = {\n PASSED: \"passed\",\n FAILED: \"failed\",\n SKIPPED: \"skipped\",\n};\n\n/** Parse the stored record defensively — metadata is untyped JSON. */\nexport function readNodeSmokeState(metadata: unknown): NodeSmokeState {\n if (!isRecord(metadata)) return { kind: \"none\" };\n const record = metadata.packageSmoke;\n if (!isRecord(record) || typeof record.status !== \"string\") {\n return { kind: \"none\" };\n }\n if (record.status === \"RUNNING\") return { kind: \"running\" };\n const kind = SETTLED_KINDS[record.status];\n if (!kind) return { kind: \"none\" };\n return {\n kind,\n operations: readOperations(record.operations),\n passed: count(record.passed),\n failed: count(record.failed),\n skipped: count(record.skipped),\n error: typeof record.error === \"string\" ? record.error : null,\n finishedAt:\n typeof record.finishedAt === \"string\" ? record.finishedAt : null,\n };\n}\n\nconst STATUS_CLASS: Record<SmokeOperationStatus, string> = {\n PASS: \"text-status-success-text\",\n FAIL: \"text-status-error-text\",\n SKIP: \"text-text-secondary\",\n};\n\nconst OperationList: FC<{ operations: SmokeOperation[] }> = ({\n operations,\n}) => (\n <ul className=\"space-y-1\">\n {operations.map((op, index) => (\n <li key={`${op.name}-${index}`} className=\"text-xs leading-snug\">\n <code className=\"mr-1 rounded bg-bg-sunken px-1 py-0.5 font-mono text-text-primary\">\n {op.name}\n </code>\n <span className={cn(\"font-medium\", STATUS_CLASS[op.status])}>\n {op.status}\n </span>\n {(op.error ?? op.detail) && (\n <span className=\"ml-1 text-text-secondary\">\n {op.error ?? op.detail}\n </span>\n )}\n <span className=\"ml-1 text-text-tertiary\">{op.durationMs} ms</span>\n </li>\n ))}\n </ul>\n);\n\ninterface NodeSmokePanelProps {\n metadata: unknown;\n /** Start expanded (the admin review card does; the versions table does not). */\n defaultExpanded?: boolean;\n className?: string;\n}\n\nexport const NodeSmokePanel: FC<NodeSmokePanelProps> = ({\n metadata,\n defaultExpanded = false,\n className,\n}) => {\n const tr = useTr();\n const state = readNodeSmokeState(metadata);\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n // Nothing recorded: the build has not finished, or the version predates\n // operation smoke tests. The build status already says which.\n if (state.kind === \"none\") return null;\n\n if (state.kind === \"running\") {\n return (\n <p\n className={cn(\n \"inline-flex items-center gap-1 text-xs text-status-info-text\",\n className,\n )}\n data-testid=\"node-smoke\"\n data-state=\"running\"\n >\n <Loader2 size={12} className=\"animate-spin\" />\n {tr(\"devportal.nodeSmoke.running\", \"Testing operations…\")}\n </p>\n );\n }\n\n const summaries = {\n passed: tr(\n \"devportal.nodeSmoke.passed\",\n \"Operations tested: {{passed}} passed, {{skipped}} skipped\",\n { passed: state.passed, skipped: state.skipped },\n ),\n failed: tr(\n \"devportal.nodeSmoke.failed\",\n \"Operation tests failed: {{failed}} of {{total}}\",\n { failed: state.failed, total: state.operations.length },\n ),\n skipped: tr(\n \"devportal.nodeSmoke.skipped\",\n \"Operation tests not run in this environment\",\n ),\n };\n const icons = {\n passed: <CheckCircle2 size={12} />,\n failed: <XCircle size={12} />,\n skipped: <MinusCircle size={12} />,\n };\n const tone = {\n passed: \"text-status-success-text\",\n failed: \"text-status-error-text\",\n skipped: \"text-text-secondary\",\n };\n const hasDetails = state.operations.length > 0 || state.error !== null;\n\n return (\n <div\n className={cn(\"space-y-1\", className)}\n data-testid=\"node-smoke\"\n data-state={state.kind}\n >\n <button\n type=\"button\"\n onClick={() => hasDetails && setExpanded((value) => !value)}\n className={cn(\n \"inline-flex items-center gap-1 text-xs font-medium\",\n tone[state.kind],\n hasDetails ? \"hover:underline\" : \"cursor-default\",\n )}\n aria-expanded={hasDetails ? expanded : undefined}\n >\n {icons[state.kind]}\n {summaries[state.kind]}\n {hasDetails &&\n (expanded ? <ChevronUp size={12} /> : <ChevronDown size={12} />)}\n </button>\n {expanded && hasDetails && (\n <div className=\"max-w-md space-y-2 rounded-md border border-border-seam bg-bg-surface p-2\">\n {state.error && (\n <p\n className={cn(\n \"text-xs\",\n state.kind === \"skipped\"\n ? \"text-text-secondary\"\n : \"text-status-error-text\",\n )}\n >\n {state.error}\n </p>\n )}\n {state.operations.length > 0 && (\n <OperationList operations={state.operations} />\n )}\n {state.finishedAt && (\n <p className=\"text-[11px] text-text-secondary\">\n {new Date(state.finishedAt).toLocaleString()}\n </p>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AA+CA,SAAS,EAAS,GAAkD;AAClE,QAAO,OAAO,KAAU,cAAY,KAAkB,CAAC,MAAM,QAAQ,EAAM;;AAG7E,SAAS,EAAe,GAAkC;AAExD,QADK,MAAM,QAAQ,EAAM,GAClB,EAAM,OAAO,EAAS,CAAC,KAAK,OAAW;EAC5C,MAAM,OAAO,EAAM,QAAS,WAAW,EAAM,OAAO;EACpD,QACE,EAAM,WAAW,UAAU,EAAM,WAAW,SACxC,EAAM,SACN;EACN,QAAQ,OAAO,EAAM,UAAW,WAAW,EAAM,SAAS;EAC1D,OAAO,OAAO,EAAM,SAAU,WAAW,EAAM,QAAQ;EACvD,YAAY,OAAO,EAAM,cAAe,WAAW,EAAM,aAAa;EACvE,EAAE,GAV+B,EAAE;;AAatC,IAAM,KAAS,MACb,OAAO,KAAU,WAAW,IAAQ,GAEhC,IAAiE;CACrE,QAAQ;CACR,QAAQ;CACR,SAAS;CACV;AAGD,SAAgB,EAAmB,GAAmC;AACpE,KAAI,CAAC,EAAS,EAAS,CAAE,QAAO,EAAE,MAAM,QAAQ;CAChD,IAAM,IAAS,EAAS;AACxB,KAAI,CAAC,EAAS,EAAO,IAAI,OAAO,EAAO,UAAW,SAChD,QAAO,EAAE,MAAM,QAAQ;AAEzB,KAAI,EAAO,WAAW,UAAW,QAAO,EAAE,MAAM,WAAW;CAC3D,IAAM,IAAO,EAAc,EAAO;AAElC,QADK,IACE;EACL;EACA,YAAY,EAAe,EAAO,WAAW;EAC7C,QAAQ,EAAM,EAAO,OAAO;EAC5B,QAAQ,EAAM,EAAO,OAAO;EAC5B,SAAS,EAAM,EAAO,QAAQ;EAC9B,OAAO,OAAO,EAAO,SAAU,WAAW,EAAO,QAAQ;EACzD,YACE,OAAO,EAAO,cAAe,WAAW,EAAO,aAAa;EAC/D,GAViB,EAAE,MAAM,QAAQ;;AAapC,IAAM,IAAqD;CACzD,MAAM;CACN,MAAM;CACN,MAAM;CACP,EAEK,KAAuD,EAC3D,oBAEA,kBAAC,MAAD;CAAI,WAAU;WACX,EAAW,KAAK,GAAI,MACnB,kBAAC,MAAD;EAAgC,WAAU;YAA1C;GACE,kBAAC,QAAD;IAAM,WAAU;cACb,EAAG;IACC,CAAA;GACP,kBAAC,QAAD;IAAM,WAAW,EAAG,eAAe,EAAa,EAAG,QAAQ;cACxD,EAAG;IACC,CAAA;IACL,EAAG,SAAS,EAAG,WACf,kBAAC,QAAD;IAAM,WAAU;cACb,EAAG,SAAS,EAAG;IACX,CAAA;GAET,kBAAC,QAAD;IAAM,WAAU;cAAhB,CAA2C,EAAG,YAAW,MAAU;;GAChE;IAbI,GAAG,EAAG,KAAK,GAAG,IAalB,CACL;CACC,CAAA,EAUM,KAA2C,EACtD,aACA,qBAAkB,IAClB,mBACI;CACJ,IAAM,IAAK,GAAO,EACZ,IAAQ,EAAmB,EAAS,EACpC,CAAC,GAAU,KAAe,EAAS,EAAgB;AAIzD,KAAI,EAAM,SAAS,OAAQ,QAAO;AAElC,KAAI,EAAM,SAAS,UACjB,QACE,kBAAC,KAAD;EACE,WAAW,EACT,gEACA,EACD;EACD,eAAY;EACZ,cAAW;YANb,CAQE,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAiB,CAAA,EAC7C,EAAG,+BAA+B,sBAAsB,CACvD;;CAIR,IAAM,IAAY;EAChB,QAAQ,EACN,8BACA,6DACA;GAAE,QAAQ,EAAM;GAAQ,SAAS,EAAM;GAAS,CACjD;EACD,QAAQ,EACN,8BACA,mDACA;GAAE,QAAQ,EAAM;GAAQ,OAAO,EAAM,WAAW;GAAQ,CACzD;EACD,SAAS,EACP,+BACA,8CACD;EACF,EACK,IAAQ;EACZ,QAAQ,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;EAClC,QAAQ,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;EAC7B,SAAS,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA;EACnC,EACK,IAAO;EACX,QAAQ;EACR,QAAQ;EACR,SAAS;EACV,EACK,IAAa,EAAM,WAAW,SAAS,KAAK,EAAM,UAAU;AAElE,QACE,kBAAC,OAAD;EACE,WAAW,EAAG,aAAa,EAAU;EACrC,eAAY;EACZ,cAAY,EAAM;YAHpB,CAKE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,KAAc,GAAa,MAAU,CAAC,EAAM;GAC3D,WAAW,EACT,sDACA,EAAK,EAAM,OACX,IAAa,oBAAoB,iBAClC;GACD,iBAAe,IAAa,IAAW,KAAA;aARzC;IAUG,EAAM,EAAM;IACZ,EAAU,EAAM;IAChB,KACa,EAAX,IAAY,IAA0B,GAA3B,EAAW,MAAM,IAAM,CAA4B;IAC1D;MACR,KAAY,KACX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,EAAM,SACL,kBAAC,KAAD;KACE,WAAW,EACT,WACA,EAAM,SAAS,YACX,wBACA,yBACL;eAEA,EAAM;KACL,CAAA;IAEL,EAAM,WAAW,SAAS,KACzB,kBAAC,GAAD,EAAe,YAAY,EAAM,YAAc,CAAA;IAEhD,EAAM,cACL,kBAAC,KAAD;KAAG,WAAU;eACV,IAAI,KAAK,EAAM,WAAW,CAAC,gBAAgB;KAC1C,CAAA;IAEF;KAEJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"PackageValidationPanel.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/PackageValidationPanel.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAahC,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IACE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAgBN,sEAAsE;AACtE,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,GAChB,sBAAsB,CAgBxB;AA4BD,UAAU,2BAA2B;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,gFAAgF;IAChF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,EAAE,EAAE,CAAC,2BAA2B,CA8HlE,CAAC"}
1
+ {"version":3,"file":"PackageValidationPanel.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/PackageValidationPanel.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAchC,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IACE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAgBN,sEAAsE;AACtE,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,GAChB,sBAAsB,CAgBxB;AA4BD,UAAU,2BAA2B;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,gFAAgF;IAChF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,EAAE,EAAE,CAAC,2BAA2B,CASlE,CAAC"}
@@ -1,92 +1,102 @@
1
1
  import { useTr as e } from "../utils/i18n.js";
2
2
  import { cn as t } from "../utils/cn.js";
3
- import { useState as n } from "react";
4
- import { AlertTriangle as r, CheckCircle2 as i, ChevronDown as a, ChevronUp as o, Loader2 as s, XCircle as c } from "lucide-react";
5
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
3
+ import { NodeSmokePanel as n } from "./NodeSmokePanel.js";
4
+ import { useState as r } from "react";
5
+ import { AlertTriangle as i, CheckCircle2 as a, ChevronDown as o, ChevronUp as s, Loader2 as c, XCircle as l } from "lucide-react";
6
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
6
7
  //#region src/devportal/components/PackageValidationPanel.tsx
7
- function d(e) {
8
+ function f(e) {
8
9
  return typeof e == "object" && !!e && !Array.isArray(e);
9
10
  }
10
- function f(e) {
11
- return Array.isArray(e) ? e.filter(d).map((e) => ({
11
+ function p(e) {
12
+ return Array.isArray(e) ? e.filter(f).map((e) => ({
12
13
  severity: e.severity === "warning" ? "warning" : "error",
13
14
  code: typeof e.code == "string" ? e.code : "",
14
15
  path: typeof e.path == "string" ? e.path : "",
15
16
  message: typeof e.message == "string" ? e.message : ""
16
17
  })) : [];
17
18
  }
18
- function p(e) {
19
- if (!d(e)) return { kind: "none" };
19
+ function m(e) {
20
+ if (!f(e)) return { kind: "none" };
20
21
  let t = e.packageValidation;
21
- return d(t) ? t.pending === !0 ? { kind: "pending" } : typeof t.valid == "boolean" ? {
22
+ return f(t) ? t.pending === !0 ? { kind: "pending" } : typeof t.valid == "boolean" ? {
22
23
  kind: t.valid ? "passed" : "failed",
23
- errors: f(t.errors),
24
- warnings: f(t.warnings),
24
+ errors: p(t.errors),
25
+ warnings: p(t.warnings),
25
26
  validatedAt: typeof t.validatedAt == "string" ? t.validatedAt : null,
26
27
  runtime: typeof t.runtime == "string" ? t.runtime : null,
27
28
  operationCount: typeof t.operationCount == "number" ? t.operationCount : 0
28
29
  } : { kind: "none" } : { kind: "none" };
29
30
  }
30
- var m = ({ issues: e }) => /* @__PURE__ */ l("ul", {
31
+ var h = ({ issues: e }) => /* @__PURE__ */ u("ul", {
31
32
  className: "space-y-1",
32
- children: e.map((e, t) => /* @__PURE__ */ u("li", {
33
+ children: e.map((e, t) => /* @__PURE__ */ d("li", {
33
34
  className: "text-xs leading-snug",
34
- children: [e.path && /* @__PURE__ */ l("code", {
35
+ children: [e.path && /* @__PURE__ */ u("code", {
35
36
  className: "mr-1 rounded bg-bg-sunken px-1 py-0.5 font-mono text-text-primary",
36
37
  children: e.path
37
- }), /* @__PURE__ */ l("span", {
38
+ }), /* @__PURE__ */ u("span", {
38
39
  className: e.severity === "error" ? "text-status-error-text" : "text-status-warning-text",
39
40
  children: e.message
40
41
  })]
41
42
  }, `${e.code}-${e.path}-${t}`))
42
- }), h = ({ metadata: d, defaultExpanded: f = !1, className: h }) => {
43
- let g = e(), _ = p(d), [v, y] = n(f);
44
- if (_.kind === "none") return /* @__PURE__ */ l("p", {
45
- className: t("text-xs text-text-secondary", h),
43
+ }), g = ({ metadata: e, defaultExpanded: r = !1, className: i }) => /* @__PURE__ */ d("div", {
44
+ className: t("space-y-1", i),
45
+ children: [/* @__PURE__ */ u(_, {
46
+ metadata: e,
47
+ defaultExpanded: r
48
+ }), /* @__PURE__ */ u(n, {
49
+ metadata: e,
50
+ defaultExpanded: r
51
+ })]
52
+ }), _ = ({ metadata: n, defaultExpanded: f = !1, className: p }) => {
53
+ let g = e(), _ = m(n), [v, y] = r(f);
54
+ if (_.kind === "none") return /* @__PURE__ */ u("p", {
55
+ className: t("text-xs text-text-secondary", p),
46
56
  "data-testid": "package-validation",
47
57
  "data-state": "none",
48
58
  children: g("devportal.packageValidation.none", "Package not validated (uploaded before validation existed)")
49
59
  });
50
- if (_.kind === "pending") return /* @__PURE__ */ u("p", {
51
- className: t("inline-flex items-center gap-1 text-xs text-status-info-text", h),
60
+ if (_.kind === "pending") return /* @__PURE__ */ d("p", {
61
+ className: t("inline-flex items-center gap-1 text-xs text-status-info-text", p),
52
62
  "data-testid": "package-validation",
53
63
  "data-state": "pending",
54
- children: [/* @__PURE__ */ l(s, {
64
+ children: [/* @__PURE__ */ u(c, {
55
65
  size: 12,
56
66
  className: "animate-spin"
57
67
  }), g("devportal.packageValidation.pending", "Validating package…")]
58
68
  });
59
69
  let b = _.kind === "failed", x = _.errors.length > 0 || _.warnings.length > 0, S = b ? g("devportal.packageValidation.failed", "Validation failed: {{count}} error(s)", { count: _.errors.length }) : g("devportal.packageValidation.passed", "Package validated");
60
- return /* @__PURE__ */ u("div", {
61
- className: t("space-y-1", h),
70
+ return /* @__PURE__ */ d("div", {
71
+ className: t("space-y-1", p),
62
72
  "data-testid": "package-validation",
63
73
  "data-state": _.kind,
64
- children: [/* @__PURE__ */ u("button", {
74
+ children: [/* @__PURE__ */ d("button", {
65
75
  type: "button",
66
76
  onClick: () => x && y((e) => !e),
67
77
  className: t("inline-flex items-center gap-1 text-xs font-medium", b ? "text-status-error-text" : "text-status-success-text", x ? "hover:underline" : "cursor-default"),
68
78
  "aria-expanded": x ? v : void 0,
69
79
  children: [
70
- l(b ? c : i, { size: 12 }),
80
+ u(b ? l : a, { size: 12 }),
71
81
  S,
72
- _.warnings.length > 0 && /* @__PURE__ */ u("span", {
82
+ _.warnings.length > 0 && /* @__PURE__ */ d("span", {
73
83
  className: "inline-flex items-center gap-0.5 text-status-warning-text",
74
- children: [/* @__PURE__ */ l(r, { size: 12 }), g("devportal.packageValidation.warnings", "{{count}} warning(s)", { count: _.warnings.length })]
84
+ children: [/* @__PURE__ */ u(i, { size: 12 }), g("devportal.packageValidation.warnings", "{{count}} warning(s)", { count: _.warnings.length })]
75
85
  }),
76
- x && l(v ? o : a, { size: 12 })
86
+ x && u(v ? s : o, { size: 12 })
77
87
  ]
78
- }), v && x && /* @__PURE__ */ u("div", {
88
+ }), v && x && /* @__PURE__ */ d("div", {
79
89
  className: "max-w-md space-y-2 rounded-md border border-border-seam bg-bg-surface p-2",
80
90
  children: [
81
- _.errors.length > 0 && /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
91
+ _.errors.length > 0 && /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
82
92
  className: "mb-1 text-xs font-semibold text-status-error-text",
83
93
  children: g("devportal.packageValidation.errorsTitle", "Errors")
84
- }), /* @__PURE__ */ l(m, { issues: _.errors })] }),
85
- _.warnings.length > 0 && /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
94
+ }), /* @__PURE__ */ u(h, { issues: _.errors })] }),
95
+ _.warnings.length > 0 && /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
86
96
  className: "mb-1 text-xs font-semibold text-status-warning-text",
87
97
  children: g("devportal.packageValidation.warningsTitle", "Needs reviewer attention")
88
- }), /* @__PURE__ */ l(m, { issues: _.warnings })] }),
89
- /* @__PURE__ */ l("p", {
98
+ }), /* @__PURE__ */ u(h, { issues: _.warnings })] }),
99
+ /* @__PURE__ */ u("p", {
90
100
  className: "text-[11px] text-text-secondary",
91
101
  children: [
92
102
  _.runtime && g("devportal.packageValidation.runtime", "Runtime: {{runtime}}", { runtime: _.runtime }),
@@ -99,6 +109,6 @@ var m = ({ issues: e }) => /* @__PURE__ */ l("ul", {
99
109
  });
100
110
  };
101
111
  //#endregion
102
- export { h as PackageValidationPanel, p as readPackageValidationState };
112
+ export { g as PackageValidationPanel, m as readPackageValidationState };
103
113
 
104
114
  //# sourceMappingURL=PackageValidationPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageValidationPanel.js","names":[],"sources":["../../../src/devportal/components/PackageValidationPanel.tsx"],"sourcesContent":["/**\n * PackageValidationPanel — shows the custom-node package validation report\n * (BOFF-4315) stored on `ApplicationVersion.metadata.packageValidation` by\n * global-devportal-svc.\n *\n * The service validates every uploaded NODE package before building it, and\n * refuses to submit or approve a version whose package failed. This panel is\n * how the developer learns why, and how the reviewing admin sees warnings\n * (dynamic code, unusual field types) that need a human decision.\n */\nimport { useState } from \"react\";\nimport type { FC } from \"react\";\nimport {\n AlertTriangle,\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Loader2,\n XCircle,\n} from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\n\nexport interface PackageValidationIssue {\n severity: \"error\" | \"warning\";\n code: string;\n path: string;\n message: string;\n}\n\nexport type PackageValidationState =\n | { kind: \"none\" }\n | { kind: \"pending\" }\n | {\n kind: \"passed\" | \"failed\";\n errors: PackageValidationIssue[];\n warnings: PackageValidationIssue[];\n validatedAt: string | null;\n runtime: string | null;\n operationCount: number;\n };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readIssues(value: unknown): PackageValidationIssue[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).map((entry) => ({\n severity: entry.severity === \"warning\" ? \"warning\" : \"error\",\n code: typeof entry.code === \"string\" ? entry.code : \"\",\n path: typeof entry.path === \"string\" ? entry.path : \"\",\n message: typeof entry.message === \"string\" ? entry.message : \"\",\n }));\n}\n\n/** Parse the stored report defensively — metadata is untyped JSON. */\nexport function readPackageValidationState(\n metadata: unknown,\n): PackageValidationState {\n if (!isRecord(metadata)) return { kind: \"none\" };\n const report = metadata.packageValidation;\n if (!isRecord(report)) return { kind: \"none\" };\n if (report.pending === true) return { kind: \"pending\" };\n if (typeof report.valid !== \"boolean\") return { kind: \"none\" };\n return {\n kind: report.valid ? \"passed\" : \"failed\",\n errors: readIssues(report.errors),\n warnings: readIssues(report.warnings),\n validatedAt:\n typeof report.validatedAt === \"string\" ? report.validatedAt : null,\n runtime: typeof report.runtime === \"string\" ? report.runtime : null,\n operationCount:\n typeof report.operationCount === \"number\" ? report.operationCount : 0,\n };\n}\n\nconst IssueList: FC<{ issues: PackageValidationIssue[] }> = ({ issues }) => (\n <ul className=\"space-y-1\">\n {issues.map((issue, index) => (\n <li\n key={`${issue.code}-${issue.path}-${index}`}\n className=\"text-xs leading-snug\"\n >\n {issue.path && (\n <code className=\"mr-1 rounded bg-bg-sunken px-1 py-0.5 font-mono text-text-primary\">\n {issue.path}\n </code>\n )}\n <span\n className={\n issue.severity === \"error\"\n ? \"text-status-error-text\"\n : \"text-status-warning-text\"\n }\n >\n {issue.message}\n </span>\n </li>\n ))}\n </ul>\n);\n\ninterface PackageValidationPanelProps {\n metadata: unknown;\n /** Start expanded (the admin review card does; the versions table does not). */\n defaultExpanded?: boolean;\n className?: string;\n}\n\nexport const PackageValidationPanel: FC<PackageValidationPanelProps> = ({\n metadata,\n defaultExpanded = false,\n className,\n}) => {\n const tr = useTr();\n const state = readPackageValidationState(metadata);\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n if (state.kind === \"none\") {\n return (\n <p\n className={cn(\"text-xs text-text-secondary\", className)}\n data-testid=\"package-validation\"\n data-state=\"none\"\n >\n {tr(\n \"devportal.packageValidation.none\",\n \"Package not validated (uploaded before validation existed)\",\n )}\n </p>\n );\n }\n\n if (state.kind === \"pending\") {\n return (\n <p\n className={cn(\n \"inline-flex items-center gap-1 text-xs text-status-info-text\",\n className,\n )}\n data-testid=\"package-validation\"\n data-state=\"pending\"\n >\n <Loader2 size={12} className=\"animate-spin\" />\n {tr(\"devportal.packageValidation.pending\", \"Validating package…\")}\n </p>\n );\n }\n\n const failed = state.kind === \"failed\";\n const hasDetails = state.errors.length > 0 || state.warnings.length > 0;\n const summary = failed\n ? tr(\n \"devportal.packageValidation.failed\",\n \"Validation failed: {{count}} error(s)\",\n { count: state.errors.length },\n )\n : tr(\"devportal.packageValidation.passed\", \"Package validated\");\n\n return (\n <div\n className={cn(\"space-y-1\", className)}\n data-testid=\"package-validation\"\n data-state={state.kind}\n >\n <button\n type=\"button\"\n onClick={() => hasDetails && setExpanded((value) => !value)}\n className={cn(\n \"inline-flex items-center gap-1 text-xs font-medium\",\n failed ? \"text-status-error-text\" : \"text-status-success-text\",\n hasDetails ? \"hover:underline\" : \"cursor-default\",\n )}\n aria-expanded={hasDetails ? expanded : undefined}\n >\n {failed ? <XCircle size={12} /> : <CheckCircle2 size={12} />}\n {summary}\n {state.warnings.length > 0 && (\n <span className=\"inline-flex items-center gap-0.5 text-status-warning-text\">\n <AlertTriangle size={12} />\n {tr(\n \"devportal.packageValidation.warnings\",\n \"{{count}} warning(s)\",\n { count: state.warnings.length },\n )}\n </span>\n )}\n {hasDetails &&\n (expanded ? <ChevronUp size={12} /> : <ChevronDown size={12} />)}\n </button>\n {expanded && hasDetails && (\n <div className=\"max-w-md space-y-2 rounded-md border border-border-seam bg-bg-surface p-2\">\n {state.errors.length > 0 && (\n <div>\n <p className=\"mb-1 text-xs font-semibold text-status-error-text\">\n {tr(\"devportal.packageValidation.errorsTitle\", \"Errors\")}\n </p>\n <IssueList issues={state.errors} />\n </div>\n )}\n {state.warnings.length > 0 && (\n <div>\n <p className=\"mb-1 text-xs font-semibold text-status-warning-text\">\n {tr(\n \"devportal.packageValidation.warningsTitle\",\n \"Needs reviewer attention\",\n )}\n </p>\n <IssueList issues={state.warnings} />\n </div>\n )}\n <p className=\"text-[11px] text-text-secondary\">\n {[\n state.runtime &&\n tr(\n \"devportal.packageValidation.runtime\",\n \"Runtime: {{runtime}}\",\n {\n runtime: state.runtime,\n },\n ),\n tr(\n \"devportal.packageValidation.operations\",\n \"{{count}} operation(s)\",\n { count: state.operationCount },\n ),\n state.validatedAt && new Date(state.validatedAt).toLocaleString(),\n ]\n .filter(Boolean)\n .join(\" · \")}\n </p>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PackageValidationPanel.js","names":[],"sources":["../../../src/devportal/components/PackageValidationPanel.tsx"],"sourcesContent":["/**\n * PackageValidationPanel — shows the custom-node package validation report\n * (BOFF-4315) stored on `ApplicationVersion.metadata.packageValidation` by\n * global-devportal-svc.\n *\n * The service validates every uploaded NODE package before building it, and\n * refuses to submit or approve a version whose package failed. This panel is\n * how the developer learns why, and how the reviewing admin sees warnings\n * (dynamic code, unusual field types) that need a human decision.\n *\n * Below the report it shows the operation smoke run on the built node\n * (BOFF-9656, `NodeSmokePanel`), so the versions page and the admin review\n * card both show every pre-review check in one place.\n */\nimport { useState } from \"react\";\nimport type { FC } from \"react\";\nimport {\n AlertTriangle,\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Loader2,\n XCircle,\n} from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport { NodeSmokePanel } from \"./NodeSmokePanel\";\n\nexport interface PackageValidationIssue {\n severity: \"error\" | \"warning\";\n code: string;\n path: string;\n message: string;\n}\n\nexport type PackageValidationState =\n | { kind: \"none\" }\n | { kind: \"pending\" }\n | {\n kind: \"passed\" | \"failed\";\n errors: PackageValidationIssue[];\n warnings: PackageValidationIssue[];\n validatedAt: string | null;\n runtime: string | null;\n operationCount: number;\n };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readIssues(value: unknown): PackageValidationIssue[] {\n if (!Array.isArray(value)) return [];\n return value.filter(isRecord).map((entry) => ({\n severity: entry.severity === \"warning\" ? \"warning\" : \"error\",\n code: typeof entry.code === \"string\" ? entry.code : \"\",\n path: typeof entry.path === \"string\" ? entry.path : \"\",\n message: typeof entry.message === \"string\" ? entry.message : \"\",\n }));\n}\n\n/** Parse the stored report defensively — metadata is untyped JSON. */\nexport function readPackageValidationState(\n metadata: unknown,\n): PackageValidationState {\n if (!isRecord(metadata)) return { kind: \"none\" };\n const report = metadata.packageValidation;\n if (!isRecord(report)) return { kind: \"none\" };\n if (report.pending === true) return { kind: \"pending\" };\n if (typeof report.valid !== \"boolean\") return { kind: \"none\" };\n return {\n kind: report.valid ? \"passed\" : \"failed\",\n errors: readIssues(report.errors),\n warnings: readIssues(report.warnings),\n validatedAt:\n typeof report.validatedAt === \"string\" ? report.validatedAt : null,\n runtime: typeof report.runtime === \"string\" ? report.runtime : null,\n operationCount:\n typeof report.operationCount === \"number\" ? report.operationCount : 0,\n };\n}\n\nconst IssueList: FC<{ issues: PackageValidationIssue[] }> = ({ issues }) => (\n <ul className=\"space-y-1\">\n {issues.map((issue, index) => (\n <li\n key={`${issue.code}-${issue.path}-${index}`}\n className=\"text-xs leading-snug\"\n >\n {issue.path && (\n <code className=\"mr-1 rounded bg-bg-sunken px-1 py-0.5 font-mono text-text-primary\">\n {issue.path}\n </code>\n )}\n <span\n className={\n issue.severity === \"error\"\n ? \"text-status-error-text\"\n : \"text-status-warning-text\"\n }\n >\n {issue.message}\n </span>\n </li>\n ))}\n </ul>\n);\n\ninterface PackageValidationPanelProps {\n metadata: unknown;\n /** Start expanded (the admin review card does; the versions table does not). */\n defaultExpanded?: boolean;\n className?: string;\n}\n\nexport const PackageValidationPanel: FC<PackageValidationPanelProps> = ({\n metadata,\n defaultExpanded = false,\n className,\n}) => (\n <div className={cn(\"space-y-1\", className)}>\n <ValidationReport metadata={metadata} defaultExpanded={defaultExpanded} />\n <NodeSmokePanel metadata={metadata} defaultExpanded={defaultExpanded} />\n </div>\n);\n\nconst ValidationReport: FC<PackageValidationPanelProps> = ({\n metadata,\n defaultExpanded = false,\n className,\n}) => {\n const tr = useTr();\n const state = readPackageValidationState(metadata);\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n if (state.kind === \"none\") {\n return (\n <p\n className={cn(\"text-xs text-text-secondary\", className)}\n data-testid=\"package-validation\"\n data-state=\"none\"\n >\n {tr(\n \"devportal.packageValidation.none\",\n \"Package not validated (uploaded before validation existed)\",\n )}\n </p>\n );\n }\n\n if (state.kind === \"pending\") {\n return (\n <p\n className={cn(\n \"inline-flex items-center gap-1 text-xs text-status-info-text\",\n className,\n )}\n data-testid=\"package-validation\"\n data-state=\"pending\"\n >\n <Loader2 size={12} className=\"animate-spin\" />\n {tr(\"devportal.packageValidation.pending\", \"Validating package…\")}\n </p>\n );\n }\n\n const failed = state.kind === \"failed\";\n const hasDetails = state.errors.length > 0 || state.warnings.length > 0;\n const summary = failed\n ? tr(\n \"devportal.packageValidation.failed\",\n \"Validation failed: {{count}} error(s)\",\n { count: state.errors.length },\n )\n : tr(\"devportal.packageValidation.passed\", \"Package validated\");\n\n return (\n <div\n className={cn(\"space-y-1\", className)}\n data-testid=\"package-validation\"\n data-state={state.kind}\n >\n <button\n type=\"button\"\n onClick={() => hasDetails && setExpanded((value) => !value)}\n className={cn(\n \"inline-flex items-center gap-1 text-xs font-medium\",\n failed ? \"text-status-error-text\" : \"text-status-success-text\",\n hasDetails ? \"hover:underline\" : \"cursor-default\",\n )}\n aria-expanded={hasDetails ? expanded : undefined}\n >\n {failed ? <XCircle size={12} /> : <CheckCircle2 size={12} />}\n {summary}\n {state.warnings.length > 0 && (\n <span className=\"inline-flex items-center gap-0.5 text-status-warning-text\">\n <AlertTriangle size={12} />\n {tr(\n \"devportal.packageValidation.warnings\",\n \"{{count}} warning(s)\",\n { count: state.warnings.length },\n )}\n </span>\n )}\n {hasDetails &&\n (expanded ? <ChevronUp size={12} /> : <ChevronDown size={12} />)}\n </button>\n {expanded && hasDetails && (\n <div className=\"max-w-md space-y-2 rounded-md border border-border-seam bg-bg-surface p-2\">\n {state.errors.length > 0 && (\n <div>\n <p className=\"mb-1 text-xs font-semibold text-status-error-text\">\n {tr(\"devportal.packageValidation.errorsTitle\", \"Errors\")}\n </p>\n <IssueList issues={state.errors} />\n </div>\n )}\n {state.warnings.length > 0 && (\n <div>\n <p className=\"mb-1 text-xs font-semibold text-status-warning-text\">\n {tr(\n \"devportal.packageValidation.warningsTitle\",\n \"Needs reviewer attention\",\n )}\n </p>\n <IssueList issues={state.warnings} />\n </div>\n )}\n <p className=\"text-[11px] text-text-secondary\">\n {[\n state.runtime &&\n tr(\n \"devportal.packageValidation.runtime\",\n \"Runtime: {{runtime}}\",\n {\n runtime: state.runtime,\n },\n ),\n tr(\n \"devportal.packageValidation.operations\",\n \"{{count}} operation(s)\",\n { count: state.operationCount },\n ),\n state.validatedAt && new Date(state.validatedAt).toLocaleString(),\n ]\n .filter(Boolean)\n .join(\" · \")}\n </p>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -23,4 +23,5 @@ export * from './SectionNav';
23
23
  export * from './MarkdownEditor';
24
24
  export * from './ManifestViewer';
25
25
  export * from './PackageValidationPanel';
26
+ export * from './NodeSmokePanel';
26
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC"}
@@ -17,4 +17,5 @@ import "./RegisterPickupLocationModal.js";
17
17
  import "./PermissionGuard.js";
18
18
  import "./MarkdownEditor.js";
19
19
  import "./ManifestViewer.js";
20
+ import "./NodeSmokePanel.js";
20
21
  import "./PackageValidationPanel.js";
@@ -1 +1 @@
1
- {"version":3,"file":"useSellerOrganization.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/useSellerOrganization.ts"],"names":[],"mappings":"AAmDA,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC/C,aAAa,EAAE,yBAAyB,EAAE,CAAC;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,qBAAqB,IAAI,2BAA2B,CA0GnE"}
1
+ {"version":3,"file":"useSellerOrganization.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/useSellerOrganization.ts"],"names":[],"mappings":"AAoDA,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC/C,aAAa,EAAE,yBAAyB,EAAE,CAAC;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,qBAAqB,IAAI,2BAA2B,CA0GnE"}
@@ -1,17 +1,18 @@
1
1
  import { useDevportal as e } from "../providers/DevportalProvider.js";
2
- import { useEffect as t, useRef as n, useState as r } from "react";
2
+ import { resolveGlobalGraphqlEndpoint as t } from "../utils/graphqlEndpoint.js";
3
+ import { useEffect as n, useRef as r, useState as i } from "react";
3
4
  //#region src/devportal/hooks/useSellerOrganization.ts
4
- var i = "\n query SellerPhysicalOrganizations {\n myDeveloperOrganizations {\n id\n name\n slug\n kind\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n", a = "\n mutation CreateSellerOrganizationForSellerPortal($input: CreateDeveloperOrganizationInput!) {\n createSellerOrganization(input: $input) {\n id\n name\n slug\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n";
5
- function o() {
6
- let { apiGatewayUrl: o, authToken: s, activePhysicalOrgId: c, setActivePhysicalOrgId: l } = e(), [u, d] = r([]), [f, p] = r(!0), [m, h] = r(null), g = n(!1), _ = `${o || ""}/global/graphql`;
7
- return t(() => {
5
+ var a = "\n query SellerPhysicalOrganizations {\n myDeveloperOrganizations {\n id\n name\n slug\n kind\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n", o = "\n mutation CreateSellerOrganizationForSellerPortal($input: CreateDeveloperOrganizationInput!) {\n createSellerOrganization(input: $input) {\n id\n name\n slug\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n";
6
+ function s() {
7
+ let { apiGatewayUrl: s, authToken: c, activePhysicalOrgId: l, setActivePhysicalOrgId: u } = e(), [d, f] = i([]), [p, m] = i(!0), [h, g] = i(null), _ = r(!1), v = t(s);
8
+ return n(() => {
8
9
  let e = !1;
9
10
  async function t(e, t) {
10
- let n = await (await fetch(_, {
11
+ let n = await (await fetch(v, {
11
12
  method: "POST",
12
13
  headers: {
13
14
  "Content-Type": "application/json",
14
- ...s ? { Authorization: `Bearer ${s}` } : {}
15
+ ...c ? { Authorization: `Bearer ${c}` } : {}
15
16
  },
16
17
  credentials: "include",
17
18
  body: JSON.stringify({
@@ -23,36 +24,36 @@ function o() {
23
24
  return n.data;
24
25
  }
25
26
  async function n() {
26
- p(!0), h(null);
27
+ m(!0), g(null);
27
28
  try {
28
- let n = await t(i);
29
+ let n = await t(a);
29
30
  if (e) return;
30
31
  let r = n.myDeveloperOrganizations.filter((e) => e.kind === "PHYSICAL");
31
- if (r.length === 0 && !g.current) {
32
- g.current = !0;
33
- let n = await t(a, { input: { name: "My Store" } });
32
+ if (r.length === 0 && !_.current) {
33
+ _.current = !0;
34
+ let n = await t(o, { input: { name: "My Store" } });
34
35
  if (e) return;
35
36
  r = [n.createSellerOrganization];
36
37
  }
37
- d(r), r.some((e) => e.id === c) || l(r[0]?.id ?? null);
38
+ f(r), r.some((e) => e.id === l) || u(r[0]?.id ?? null);
38
39
  } catch (t) {
39
- e || h(t instanceof Error ? t.message : String(t));
40
+ e || g(t instanceof Error ? t.message : String(t));
40
41
  } finally {
41
- e || p(!1);
42
+ e || m(!1);
42
43
  }
43
44
  }
44
45
  return n(), () => {
45
46
  e = !0;
46
47
  };
47
- }, [_, s]), {
48
- organization: u.find((e) => e.id === c) ?? u[0] ?? null,
49
- organizations: u,
50
- loading: f,
51
- error: m,
52
- selectOrganization: l
48
+ }, [v, c]), {
49
+ organization: d.find((e) => e.id === l) ?? d[0] ?? null,
50
+ organizations: d,
51
+ loading: p,
52
+ error: h,
53
+ selectOrganization: u
53
54
  };
54
55
  }
55
56
  //#endregion
56
- export { o as useSellerOrganization };
57
+ export { s as useSellerOrganization };
57
58
 
58
59
  //# sourceMappingURL=useSellerOrganization.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSellerOrganization.js","names":[],"sources":["../../../src/devportal/hooks/useSellerOrganization.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\n\n/**\n * Resolves the seller portal's current PHYSICAL-kind devportal org.\n *\n * Every seller-portal page (Dashboard, My Items, Orders, Create/Edit\n * Physical Item) must operate on the same PHYSICAL org, not independently\n * grab \"whichever org happens to be first\" — that let a multi-org seller's\n * physical listings silently attach to the wrong org. This hook is the\n * single source of truth: it prefers the persisted activePhysicalOrgId (if\n * still one of the caller's PHYSICAL orgs), falls back to the first PHYSICAL\n * org the actor belongs to, and — only if none exists yet — auto-provisions\n * one via createSellerOrganization (mirrors the previous auto-provision-on-\n * first-listing UX, now correctly scoped to PHYSICAL). See DeveloperOrgKind\n * (BOFF-7285).\n */\n\n// myDeveloperOrganizations (not developerOrganizations) — the latter is\n// admin-aware and returns every organization on the platform for a\n// SUPER_ADMIN actor, which would leak other sellers' orgs into this list.\n// See DeveloperOrgKind (BOFF-7285).\nconst PHYSICAL_ORGS_QUERY = `\n query SellerPhysicalOrganizations {\n myDeveloperOrganizations {\n id\n name\n slug\n kind\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n`;\n\nconst CREATE_SELLER_ORGANIZATION_MUTATION = `\n mutation CreateSellerOrganizationForSellerPortal($input: CreateDeveloperOrganizationInput!) {\n createSellerOrganization(input: $input) {\n id\n name\n slug\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n`;\n\nexport interface SellerOrganizationSummary {\n id: string;\n name: string;\n slug: string;\n contactEmail: string | null;\n websiteUrl: string | null;\n legalEntityName: string | null;\n taxCountry: string | null;\n}\n\nexport interface UseSellerOrganizationResult {\n organization: SellerOrganizationSummary | null;\n organizations: SellerOrganizationSummary[];\n loading: boolean;\n error: string | null;\n selectOrganization: (id: string) => void;\n}\n\nexport function useSellerOrganization(): UseSellerOrganizationResult {\n const {\n apiGatewayUrl,\n authToken,\n activePhysicalOrgId,\n setActivePhysicalOrgId,\n } = useDevportal();\n\n const [organizations, setOrganizations] = useState<\n SellerOrganizationSummary[]\n >([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const hasAttemptedAutoCreate = useRef(false);\n\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n useEffect(() => {\n let cancelled = false;\n\n async function gqlFetch<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n async function run(): Promise<void> {\n setLoading(true);\n setError(null);\n try {\n const data = await gqlFetch<{\n myDeveloperOrganizations: (SellerOrganizationSummary & {\n kind: \"DIGITAL\" | \"PHYSICAL\";\n })[];\n }>(PHYSICAL_ORGS_QUERY);\n if (cancelled) return;\n\n let physicalOrgs: SellerOrganizationSummary[] =\n data.myDeveloperOrganizations.filter(\n (org) => org.kind === \"PHYSICAL\",\n );\n\n if (physicalOrgs.length === 0 && !hasAttemptedAutoCreate.current) {\n hasAttemptedAutoCreate.current = true;\n const created = await gqlFetch<{\n createSellerOrganization: SellerOrganizationSummary;\n }>(CREATE_SELLER_ORGANIZATION_MUTATION, {\n input: { name: \"My Store\" },\n });\n if (cancelled) return;\n physicalOrgs = [created.createSellerOrganization];\n }\n\n setOrganizations(physicalOrgs);\n\n const stillValid = physicalOrgs.some(\n (org) => org.id === activePhysicalOrgId,\n );\n if (!stillValid) {\n setActivePhysicalOrgId(physicalOrgs[0]?.id ?? null);\n }\n } catch (err) {\n if (!cancelled) {\n setError(err instanceof Error ? err.message : String(err));\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n void run();\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- activePhysicalOrgId/setActivePhysicalOrgId intentionally excluded: they're read/written inside this effect, re-running on every write would loop.\n }, [endpoint, authToken]);\n\n const organization =\n organizations.find((org) => org.id === activePhysicalOrgId) ??\n organizations[0] ??\n null;\n\n return {\n organization,\n organizations,\n loading,\n error,\n selectOrganization: setActivePhysicalOrgId,\n };\n}\n"],"mappings":";;;AAsBA,IAAM,IAAsB,sNAetB,IAAsC;AAgC5C,SAAgB,IAAqD;CACnE,IAAM,EACJ,kBACA,cACA,wBACA,8BACE,GAAc,EAEZ,CAAC,GAAe,KAAoB,EAExC,EAAE,CAAC,EACC,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAyB,EAAO,GAAM,EAEtC,IAAW,GAAG,KAAiB,GAAG;AAoFxC,QAlFA,QAAgB;EACd,IAAI,IAAY;EAEhB,eAAe,EACb,GACA,GACY;GAUZ,IAAM,IAAQ,OATF,MAAM,MAAM,GAAU;IAChC,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,GAAI,IAAY,EAAE,eAAe,UAAU,KAAa,GAAG,EAAE;KAC9D;IACD,aAAa;IACb,MAAM,KAAK,UAAU;KAAE;KAAO;KAAW,CAAC;IAC3C,CAAC,EACsB,MAAM;AAI9B,OAAI,EAAK,QAAQ,OACf,OAAU,MAAM,EAAK,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;AAE/D,UAAO,EAAK;;EAGd,eAAe,IAAqB;AAElC,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAO,MAAM,EAIhB,EAAoB;AACvB,QAAI,EAAW;IAEf,IAAI,IACF,EAAK,yBAAyB,QAC3B,MAAQ,EAAI,SAAS,WACvB;AAEH,QAAI,EAAa,WAAW,KAAK,CAAC,EAAuB,SAAS;AAChE,OAAuB,UAAU;KACjC,IAAM,IAAU,MAAM,EAEnB,GAAqC,EACtC,OAAO,EAAE,MAAM,YAAY,EAC5B,CAAC;AACF,SAAI,EAAW;AACf,SAAe,CAAC,EAAQ,yBAAyB;;AAQnD,IALA,EAAiB,EAAa,EAEX,EAAa,MAC7B,MAAQ,EAAI,OAAO,EACrB,IAEC,EAAuB,EAAa,IAAI,MAAM,KAAK;YAE9C,GAAK;AACZ,IAAK,KACH,EAAS,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;aAEpD;AACR,IAAK,KAAW,EAAW,GAAM;;;AAKrC,SADK,GAAK,QACG;AACX,OAAY;;IAGb,CAAC,GAAU,EAAU,CAAC,EAOlB;EACL,cALA,EAAc,MAAM,MAAQ,EAAI,OAAO,EAAoB,IAC3D,EAAc,MACd;EAIA;EACA;EACA;EACA,oBAAoB;EACrB"}
1
+ {"version":3,"file":"useSellerOrganization.js","names":[],"sources":["../../../src/devportal/hooks/useSellerOrganization.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { resolveGlobalGraphqlEndpoint } from \"../utils/graphqlEndpoint\";\n\n/**\n * Resolves the seller portal's current PHYSICAL-kind devportal org.\n *\n * Every seller-portal page (Dashboard, My Items, Orders, Create/Edit\n * Physical Item) must operate on the same PHYSICAL org, not independently\n * grab \"whichever org happens to be first\" — that let a multi-org seller's\n * physical listings silently attach to the wrong org. This hook is the\n * single source of truth: it prefers the persisted activePhysicalOrgId (if\n * still one of the caller's PHYSICAL orgs), falls back to the first PHYSICAL\n * org the actor belongs to, and — only if none exists yet — auto-provisions\n * one via createSellerOrganization (mirrors the previous auto-provision-on-\n * first-listing UX, now correctly scoped to PHYSICAL). See DeveloperOrgKind\n * (BOFF-7285).\n */\n\n// myDeveloperOrganizations (not developerOrganizations) — the latter is\n// admin-aware and returns every organization on the platform for a\n// SUPER_ADMIN actor, which would leak other sellers' orgs into this list.\n// See DeveloperOrgKind (BOFF-7285).\nconst PHYSICAL_ORGS_QUERY = `\n query SellerPhysicalOrganizations {\n myDeveloperOrganizations {\n id\n name\n slug\n kind\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n`;\n\nconst CREATE_SELLER_ORGANIZATION_MUTATION = `\n mutation CreateSellerOrganizationForSellerPortal($input: CreateDeveloperOrganizationInput!) {\n createSellerOrganization(input: $input) {\n id\n name\n slug\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n`;\n\nexport interface SellerOrganizationSummary {\n id: string;\n name: string;\n slug: string;\n contactEmail: string | null;\n websiteUrl: string | null;\n legalEntityName: string | null;\n taxCountry: string | null;\n}\n\nexport interface UseSellerOrganizationResult {\n organization: SellerOrganizationSummary | null;\n organizations: SellerOrganizationSummary[];\n loading: boolean;\n error: string | null;\n selectOrganization: (id: string) => void;\n}\n\nexport function useSellerOrganization(): UseSellerOrganizationResult {\n const {\n apiGatewayUrl,\n authToken,\n activePhysicalOrgId,\n setActivePhysicalOrgId,\n } = useDevportal();\n\n const [organizations, setOrganizations] = useState<\n SellerOrganizationSummary[]\n >([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const hasAttemptedAutoCreate = useRef(false);\n\n const endpoint = resolveGlobalGraphqlEndpoint(apiGatewayUrl);\n\n useEffect(() => {\n let cancelled = false;\n\n async function gqlFetch<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n async function run(): Promise<void> {\n setLoading(true);\n setError(null);\n try {\n const data = await gqlFetch<{\n myDeveloperOrganizations: (SellerOrganizationSummary & {\n kind: \"DIGITAL\" | \"PHYSICAL\";\n })[];\n }>(PHYSICAL_ORGS_QUERY);\n if (cancelled) return;\n\n let physicalOrgs: SellerOrganizationSummary[] =\n data.myDeveloperOrganizations.filter(\n (org) => org.kind === \"PHYSICAL\",\n );\n\n if (physicalOrgs.length === 0 && !hasAttemptedAutoCreate.current) {\n hasAttemptedAutoCreate.current = true;\n const created = await gqlFetch<{\n createSellerOrganization: SellerOrganizationSummary;\n }>(CREATE_SELLER_ORGANIZATION_MUTATION, {\n input: { name: \"My Store\" },\n });\n if (cancelled) return;\n physicalOrgs = [created.createSellerOrganization];\n }\n\n setOrganizations(physicalOrgs);\n\n const stillValid = physicalOrgs.some(\n (org) => org.id === activePhysicalOrgId,\n );\n if (!stillValid) {\n setActivePhysicalOrgId(physicalOrgs[0]?.id ?? null);\n }\n } catch (err) {\n if (!cancelled) {\n setError(err instanceof Error ? err.message : String(err));\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n void run();\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- activePhysicalOrgId/setActivePhysicalOrgId intentionally excluded: they're read/written inside this effect, re-running on every write would loop.\n }, [endpoint, authToken]);\n\n const organization =\n organizations.find((org) => org.id === activePhysicalOrgId) ??\n organizations[0] ??\n null;\n\n return {\n organization,\n organizations,\n loading,\n error,\n selectOrganization: setActivePhysicalOrgId,\n };\n}\n"],"mappings":";;;;AAuBA,IAAM,IAAsB,sNAetB,IAAsC;AAgC5C,SAAgB,IAAqD;CACnE,IAAM,EACJ,kBACA,cACA,wBACA,8BACE,GAAc,EAEZ,CAAC,GAAe,KAAoB,EAExC,EAAE,CAAC,EACC,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAyB,EAAO,GAAM,EAEtC,IAAW,EAA6B,EAAc;AAoF5D,QAlFA,QAAgB;EACd,IAAI,IAAY;EAEhB,eAAe,EACb,GACA,GACY;GAUZ,IAAM,IAAQ,OATF,MAAM,MAAM,GAAU;IAChC,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,GAAI,IAAY,EAAE,eAAe,UAAU,KAAa,GAAG,EAAE;KAC9D;IACD,aAAa;IACb,MAAM,KAAK,UAAU;KAAE;KAAO;KAAW,CAAC;IAC3C,CAAC,EACsB,MAAM;AAI9B,OAAI,EAAK,QAAQ,OACf,OAAU,MAAM,EAAK,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;AAE/D,UAAO,EAAK;;EAGd,eAAe,IAAqB;AAElC,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAO,MAAM,EAIhB,EAAoB;AACvB,QAAI,EAAW;IAEf,IAAI,IACF,EAAK,yBAAyB,QAC3B,MAAQ,EAAI,SAAS,WACvB;AAEH,QAAI,EAAa,WAAW,KAAK,CAAC,EAAuB,SAAS;AAChE,OAAuB,UAAU;KACjC,IAAM,IAAU,MAAM,EAEnB,GAAqC,EACtC,OAAO,EAAE,MAAM,YAAY,EAC5B,CAAC;AACF,SAAI,EAAW;AACf,SAAe,CAAC,EAAQ,yBAAyB;;AAQnD,IALA,EAAiB,EAAa,EAEX,EAAa,MAC7B,MAAQ,EAAI,OAAO,EACrB,IAEC,EAAuB,EAAa,IAAI,MAAM,KAAK;YAE9C,GAAK;AACZ,IAAK,KACH,EAAS,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;aAEpD;AACR,IAAK,KAAW,EAAW,GAAM;;;AAKrC,SADK,GAAK,QACG;AACX,OAAY;;IAGb,CAAC,GAAU,EAAU,CAAC,EAOlB;EACL,cALA,EAAc,MAAM,MAAQ,EAAI,OAAO,EAAoB,IAC3D,EAAc,MACd;EAIA;EACA;EACA;EACA,oBAAoB;EACrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"CreatePhysicalItemPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAe,MAAM,OAAO,CAAC;AAyK7C,eAAO,MAAM,sBAAsB,EAAE,EA4rBpC,CAAC"}
1
+ {"version":3,"file":"CreatePhysicalItemPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAe,MAAM,OAAO,CAAC;AA0K7C,eAAO,MAAM,sBAAsB,EAAE,EA4rBpC,CAAC"}