@burdenoff/microfe-devportal 2026.917.2 → 2026.918.1

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 (29) hide show
  1. package/dist/devportal/components/MultiSelectCombobox.d.ts +38 -0
  2. package/dist/devportal/components/MultiSelectCombobox.d.ts.map +1 -0
  3. package/dist/devportal/components/MultiSelectCombobox.js +80 -0
  4. package/dist/devportal/components/MultiSelectCombobox.js.map +1 -0
  5. package/dist/devportal/components/ReportOrderIssueModal.d.ts.map +1 -1
  6. package/dist/devportal/components/ReportOrderIssueModal.js.map +1 -1
  7. package/dist/devportal/hooks/usePackageUpload.d.ts.map +1 -1
  8. package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
  9. package/dist/devportal/pages/ApplicationDetailPage.d.ts.map +1 -1
  10. package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
  11. package/dist/devportal/pages/CreateApplicationPage.d.ts.map +1 -1
  12. package/dist/devportal/pages/CreateApplicationPage.js +204 -217
  13. package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
  14. package/dist/devportal/pages/CreatePhysicalItemPage.d.ts.map +1 -1
  15. package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
  16. package/dist/devportal/pages/EditApplicationPage.d.ts.map +1 -1
  17. package/dist/devportal/pages/EditApplicationPage.js +142 -154
  18. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  19. package/dist/devportal/pages/EditPhysicalItemPage.d.ts.map +1 -1
  20. package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
  21. package/dist/devportal/pages/PublishPage.d.ts.map +1 -1
  22. package/dist/devportal/pages/PublishPage.js.map +1 -1
  23. package/dist/devportal/pages/SellerDashboardPage.d.ts.map +1 -1
  24. package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
  25. package/dist/devportal/pages/SellerItemsPage.d.ts.map +1 -1
  26. package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
  27. package/dist/devportal/types/index.d.ts.map +1 -1
  28. package/dist/devportal/types/index.js.map +1 -1
  29. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ /**
2
+ * MultiSelectCombobox — a searchable, checkbox multi-select.
3
+ * @module devportal/components
4
+ *
5
+ * Drop-in replacement for a flat checkbox-pill grid wherever the option
6
+ * list can grow past a handful of items (e.g. Compatible Products, which
7
+ * lists every active platform product). A closed-by-default trigger opens
8
+ * a searchable popover list with a checkmark per selected item; picking an
9
+ * item toggles it without closing the popover, and every selected item
10
+ * renders as a removable chip below the trigger.
11
+ *
12
+ * Built entirely on the existing fe-libs `ui` primitives (Command + Popover)
13
+ * — no new dependency. Mirrors the proven Popover+Command composition used
14
+ * by intelwatchtower's `Combobox` (single-select) and manufacturedops'
15
+ * `TagPicker` (chips-below-trigger), adapted for a fixed, fully-client-side
16
+ * option list (no server search/create needed here).
17
+ */
18
+ export interface MultiSelectOption {
19
+ value: string;
20
+ label: string;
21
+ /** Optional secondary text rendered muted next to the label. */
22
+ hint?: string;
23
+ }
24
+ interface MultiSelectComboboxProps {
25
+ options: MultiSelectOption[];
26
+ selected: string[];
27
+ onChange: (selected: string[]) => void;
28
+ placeholder?: string;
29
+ searchPlaceholder?: string;
30
+ emptyText?: string;
31
+ id?: string;
32
+ disabled?: boolean;
33
+ className?: string;
34
+ error?: boolean;
35
+ }
36
+ export declare function MultiSelectCombobox({ options, selected, onChange, placeholder, searchPlaceholder, emptyText, id, disabled, className, error, }: MultiSelectComboboxProps): import("react").JSX.Element;
37
+ export {};
38
+ //# sourceMappingURL=MultiSelectCombobox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiSelectCombobox.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/MultiSelectCombobox.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAgBH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,wBAAwB;IAChC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,iBAA6B,EAC7B,SAAyB,EACzB,EAAE,EACF,QAAgB,EAChB,SAAS,EACT,KAAa,GACd,EAAE,wBAAwB,+BAyG1B"}
@@ -0,0 +1,80 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useMemo as t, useState as n } from "react";
3
+ import { Check as r, ChevronDown as i, X as a } from "lucide-react";
4
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
5
+ import { Command as c, CommandEmpty as l, CommandInput as u, CommandItem as d, CommandList as f, Popover as p, PopoverContent as m, PopoverTrigger as h } from "@burdenoff/fe-libs/ui";
6
+ //#region src/devportal/components/MultiSelectCombobox.tsx
7
+ function g({ options: g, selected: _, onChange: v, placeholder: y = "Select…", searchPlaceholder: b = "Search…", emptyText: x = "No matches.", id: S, disabled: C = !1, className: w, error: T = !1 }) {
8
+ let [E, D] = n(!1), O = t(() => new Set(_), [_]), k = t(() => g.filter((e) => O.has(e.value)), [g, O]), A = (e) => {
9
+ v(O.has(e) ? _.filter((t) => t !== e) : [..._, e]);
10
+ };
11
+ return /* @__PURE__ */ s("div", {
12
+ className: w,
13
+ children: [/* @__PURE__ */ s(p, {
14
+ open: E,
15
+ onOpenChange: D,
16
+ children: [/* @__PURE__ */ o(h, {
17
+ asChild: !0,
18
+ children: /* @__PURE__ */ s("button", {
19
+ type: "button",
20
+ id: S,
21
+ role: "combobox",
22
+ "aria-expanded": E,
23
+ disabled: C,
24
+ className: e("flex h-10 w-full items-center justify-between gap-2 rounded-input border bg-bg-surface px-3 py-2 text-sm transition-colors duration-200 focus:border-border-focus focus:outline-none focus:shadow-[var(--shadow-focus)] focus:ring-1 focus:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50", T ? "border-status-error-bg/50" : "border-border-default", k.length > 0 ? "text-text-primary" : "text-text-placeholder"),
25
+ children: [/* @__PURE__ */ o("span", {
26
+ className: "truncate",
27
+ children: k.length > 0 ? `${k.length} selected` : y
28
+ }), /* @__PURE__ */ o(i, {
29
+ className: "size-4 shrink-0 text-text-muted",
30
+ "aria-hidden": "true"
31
+ })]
32
+ })
33
+ }), /* @__PURE__ */ o(m, {
34
+ align: "start",
35
+ className: "w-[var(--radix-popover-trigger-width)] p-0 border-border-subtle bg-bg-elevated",
36
+ children: /* @__PURE__ */ s(c, {
37
+ className: "bg-bg-elevated text-text-primary",
38
+ children: [/* @__PURE__ */ o(u, { placeholder: b }), /* @__PURE__ */ s(f, { children: [/* @__PURE__ */ o(l, { children: x }), g.map((t) => {
39
+ let n = O.has(t.value);
40
+ return /* @__PURE__ */ s(d, {
41
+ value: t.label,
42
+ onSelect: () => A(t.value),
43
+ children: [
44
+ /* @__PURE__ */ o(r, {
45
+ className: e("size-4 shrink-0", n ? "opacity-100" : "opacity-0"),
46
+ "aria-hidden": "true"
47
+ }),
48
+ /* @__PURE__ */ o("span", {
49
+ className: "truncate",
50
+ children: t.label
51
+ }),
52
+ t.hint && /* @__PURE__ */ o("span", {
53
+ className: "ml-auto truncate text-xs text-text-tertiary",
54
+ children: t.hint
55
+ })
56
+ ]
57
+ }, t.value);
58
+ })] })]
59
+ })
60
+ })]
61
+ }), k.length > 0 && /* @__PURE__ */ o("div", {
62
+ className: "mt-2 flex flex-wrap gap-1.5",
63
+ children: k.map((e) => /* @__PURE__ */ s("span", {
64
+ className: "inline-flex items-center gap-1 rounded-full border border-border-subtle bg-bg-sunken px-2 py-1 text-xs text-text-primary",
65
+ children: [e.label, /* @__PURE__ */ o("button", {
66
+ type: "button",
67
+ onClick: () => A(e.value),
68
+ disabled: C,
69
+ className: "text-text-secondary hover:text-text-primary disabled:cursor-not-allowed",
70
+ "aria-label": `Remove ${e.label}`,
71
+ children: /* @__PURE__ */ o(a, { className: "size-3" })
72
+ })]
73
+ }, e.value))
74
+ })]
75
+ });
76
+ }
77
+ //#endregion
78
+ export { g as MultiSelectCombobox };
79
+
80
+ //# sourceMappingURL=MultiSelectCombobox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiSelectCombobox.js","names":[],"sources":["../../../src/devportal/components/MultiSelectCombobox.tsx"],"sourcesContent":["/**\n * MultiSelectCombobox — a searchable, checkbox multi-select.\n * @module devportal/components\n *\n * Drop-in replacement for a flat checkbox-pill grid wherever the option\n * list can grow past a handful of items (e.g. Compatible Products, which\n * lists every active platform product). A closed-by-default trigger opens\n * a searchable popover list with a checkmark per selected item; picking an\n * item toggles it without closing the popover, and every selected item\n * renders as a removable chip below the trigger.\n *\n * Built entirely on the existing fe-libs `ui` primitives (Command + Popover)\n * — no new dependency. Mirrors the proven Popover+Command composition used\n * by intelwatchtower's `Combobox` (single-select) and manufacturedops'\n * `TagPicker` (chips-below-trigger), adapted for a fixed, fully-client-side\n * option list (no server search/create needed here).\n */\n\nimport { useMemo, useState } from \"react\";\nimport { Check, ChevronDown, X } from \"lucide-react\";\nimport {\n Command,\n CommandEmpty,\n CommandInput,\n CommandItem,\n CommandList,\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@burdenoff/fe-libs/ui\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n /** Optional secondary text rendered muted next to the label. */\n hint?: string;\n}\n\ninterface MultiSelectComboboxProps {\n options: MultiSelectOption[];\n selected: string[];\n onChange: (selected: string[]) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyText?: string;\n id?: string;\n disabled?: boolean;\n className?: string;\n error?: boolean;\n}\n\nexport function MultiSelectCombobox({\n options,\n selected,\n onChange,\n placeholder = \"Select…\",\n searchPlaceholder = \"Search…\",\n emptyText = \"No matches.\",\n id,\n disabled = false,\n className,\n error = false,\n}: MultiSelectComboboxProps) {\n const [open, setOpen] = useState(false);\n const selectedSet = useMemo(() => new Set(selected), [selected]);\n const selectedOptions = useMemo(\n () => options.filter((o) => selectedSet.has(o.value)),\n [options, selectedSet],\n );\n\n const toggle = (value: string) => {\n onChange(\n selectedSet.has(value)\n ? selected.filter((v) => v !== value)\n : [...selected, value],\n );\n };\n\n return (\n <div className={className}>\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={id}\n role=\"combobox\"\n aria-expanded={open}\n disabled={disabled}\n className={cn(\n \"flex h-10 w-full items-center justify-between gap-2 rounded-input border bg-bg-surface px-3 py-2 text-sm transition-colors duration-200 focus:border-border-focus focus:outline-none focus:shadow-[var(--shadow-focus)] focus:ring-1 focus:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50\",\n error ? \"border-status-error-bg/50\" : \"border-border-default\",\n selectedOptions.length > 0\n ? \"text-text-primary\"\n : \"text-text-placeholder\",\n )}\n >\n <span className=\"truncate\">\n {selectedOptions.length > 0\n ? `${selectedOptions.length} selected`\n : placeholder}\n </span>\n <ChevronDown\n className=\"size-4 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n </button>\n </PopoverTrigger>\n <PopoverContent\n align=\"start\"\n className=\"w-[var(--radix-popover-trigger-width)] p-0 border-border-subtle bg-bg-elevated\"\n >\n <Command className=\"bg-bg-elevated text-text-primary\">\n <CommandInput placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{emptyText}</CommandEmpty>\n {options.map((o) => {\n const checked = selectedSet.has(o.value);\n return (\n <CommandItem\n key={o.value}\n value={o.label}\n onSelect={() => toggle(o.value)}\n >\n <Check\n className={cn(\n \"size-4 shrink-0\",\n checked ? \"opacity-100\" : \"opacity-0\",\n )}\n aria-hidden=\"true\"\n />\n <span className=\"truncate\">{o.label}</span>\n {o.hint && (\n <span className=\"ml-auto truncate text-xs text-text-tertiary\">\n {o.hint}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n\n {selectedOptions.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {selectedOptions.map((o) => (\n <span\n key={o.value}\n className=\"inline-flex items-center gap-1 rounded-full border border-border-subtle bg-bg-sunken px-2 py-1 text-xs text-text-primary\"\n >\n {o.label}\n <button\n type=\"button\"\n onClick={() => toggle(o.value)}\n disabled={disabled}\n className=\"text-text-secondary hover:text-text-primary disabled:cursor-not-allowed\"\n aria-label={`Remove ${o.label}`}\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;AAoDA,SAAgB,EAAoB,EAClC,YACA,aACA,aACA,iBAAc,WACd,uBAAoB,WACpB,eAAY,eACZ,OACA,cAAW,IACX,cACA,WAAQ,MACmB;CAC3B,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAc,QAAc,IAAI,IAAI,EAAS,EAAE,CAAC,EAAS,CAAC,EAC1D,IAAkB,QAChB,EAAQ,QAAQ,MAAM,EAAY,IAAI,EAAE,MAAM,CAAC,EACrD,CAAC,GAAS,EAAY,CACvB,EAEK,KAAU,MAAkB;AAChC,IACE,EAAY,IAAI,EAAM,GAClB,EAAS,QAAQ,MAAM,MAAM,EAAM,GACnC,CAAC,GAAG,GAAU,EAAM,CACzB;;AAGH,QACE,kBAAC,OAAD;EAAgB;YAAhB,CACE,kBAAC,GAAD;GAAe;GAAM,cAAc;aAAnC,CACE,kBAAC,GAAD;IAAgB,SAAA;cACd,kBAAC,UAAD;KACE,MAAK;KACD;KACJ,MAAK;KACL,iBAAe;KACL;KACV,WAAW,EACT,6TACA,IAAQ,8BAA8B,yBACtC,EAAgB,SAAS,IACrB,sBACA,wBACL;eAZH,CAcE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAgB,SAAS,IACtB,GAAG,EAAgB,OAAO,aAC1B;MACC,CAAA,EACP,kBAAC,GAAD;MACE,WAAU;MACV,eAAY;MACZ,CAAA,CACK;;IACM,CAAA,EACjB,kBAAC,GAAD;IACE,OAAM;IACN,WAAU;cAEV,kBAAC,GAAD;KAAS,WAAU;eAAnB,CACE,kBAAC,GAAD,EAAc,aAAa,GAAqB,CAAA,EAChD,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAe,GAAyB,CAAA,EACvC,EAAQ,KAAK,MAAM;MAClB,IAAM,IAAU,EAAY,IAAI,EAAE,MAAM;AACxC,aACE,kBAAC,GAAD;OAEE,OAAO,EAAE;OACT,gBAAgB,EAAO,EAAE,MAAM;iBAHjC;QAKE,kBAAC,GAAD;SACE,WAAW,EACT,mBACA,IAAU,gBAAgB,YAC3B;SACD,eAAY;SACZ,CAAA;QACF,kBAAC,QAAD;SAAM,WAAU;mBAAY,EAAE;SAAa,CAAA;QAC1C,EAAE,QACD,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAE;SACE,CAAA;QAEG;SAjBP,EAAE,MAiBK;OAEhB,CACU,EAAA,CAAA,CACN;;IACK,CAAA,CACT;MAET,EAAgB,SAAS,KACxB,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAgB,KAAK,MACpB,kBAAC,QAAD;IAEE,WAAU;cAFZ,CAIG,EAAE,OACH,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAO,EAAE,MAAM;KACpB;KACV,WAAU;KACV,cAAY,UAAU,EAAE;eAExB,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;KACjB,CAAA,CACJ;MAbA,EAAE,MAaF,CACP;GACE,CAAA,CAEJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReportOrderIssueModal.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/ReportOrderIssueModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAIzD,MAAM,MAAM,kBAAkB,GAC5B,cAAc,GAAG,gBAAgB,GAAG,eAAe,GAAG,OAAO,CAAC;AAqBhE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CA0KhE,CAAC"}
1
+ {"version":3,"file":"ReportOrderIssueModal.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/ReportOrderIssueModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAIzD,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,OAAO,CAAC;AAqBZ,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CA0KhE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ReportOrderIssueModal.js","names":[],"sources":["../../../src/devportal/components/ReportOrderIssueModal.tsx"],"sourcesContent":["import { useId, useRef, useState, type FC } from \"react\";\nimport { AlertTriangle, X, Loader2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport type OrderIssueCategory =\n \"OUT_OF_STOCK\" | \"CANNOT_FULFILL\" | \"WRONG_ADDRESS\" | \"OTHER\";\n\nconst CATEGORY_OPTIONS: { value: OrderIssueCategory; label: string }[] = [\n { value: \"OUT_OF_STOCK\", label: \"Out of stock\" },\n { value: \"CANNOT_FULFILL\", label: \"Cannot fulfill\" },\n { value: \"WRONG_ADDRESS\", label: \"Wrong address / pincode\" },\n { value: \"OTHER\", label: \"Other\" },\n];\n\nconst FLAG_ORDER_ISSUE_MUTATION = `\n mutation FlagOrderIssue($orderId: ID!, $organizationId: ID!, $category: OrderIssueCategory!, $note: String) {\n flagOrderIssue(orderId: $orderId, organizationId: $organizationId, category: $category, note: $note) {\n id\n category\n note\n status\n createdAt\n }\n }\n`;\n\nexport interface ReportOrderIssueModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n orderId: string;\n organizationId: string;\n gqlUrl: string;\n headers: Record<string, string>;\n onReported: () => void;\n}\n\n/**\n * Small modal for a seller to report a problem fulfilling a physical order\n * (out of stock, cannot fulfill, wrong address, etc). Purely informational —\n * visible to admin on the fulfillment console; does not cancel or refund\n * anything (that stays manual for now, see BOFF-7192 follow-up).\n */\nexport const ReportOrderIssueModal: FC<ReportOrderIssueModalProps> = ({\n open,\n onOpenChange,\n orderId,\n organizationId,\n gqlUrl,\n headers,\n onReported,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const titleId = useId();\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const [category, setCategory] = useState<OrderIssueCategory>(\"OUT_OF_STOCK\");\n const [note, setNote] = useState(\"\");\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n if (!open) return null;\n\n const handleClose = () => {\n if (submitting) return;\n setNote(\"\");\n setCategory(\"OUT_OF_STOCK\");\n setError(null);\n onOpenChange(false);\n };\n\n const handleSubmit = async () => {\n setSubmitting(true);\n setError(null);\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query: FLAG_ORDER_ISSUE_MUTATION,\n variables: {\n orderId,\n organizationId,\n category,\n note: note.trim() || null,\n },\n }),\n });\n const json = (await res.json()) as { errors?: { message: string }[] };\n if (json.errors?.length) {\n throw new Error(json.errors[0]?.message ?? \"Failed to report issue\");\n }\n onReported();\n handleClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to report issue\");\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <button\n type=\"button\"\n aria-label=\"Close report issue dialog backdrop\"\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={handleClose}\n />\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n className=\"relative z-10 w-full max-w-md rounded-lg bg-bg-surface shadow-xl\"\n >\n <div className=\"flex items-start gap-4 border-b p-6\">\n <div className=\"rounded-full bg-status-warning-bg-subtle p-2\">\n <AlertTriangle className=\"h-6 w-6 text-status-warning-text\" />\n </div>\n <div className=\"flex-1\">\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {tr(\"devportal.reportOrderIssue\", \"Report an Issue\")}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n \"devportal.reportOrderIssueDesc\",\n \"Let admin know about a problem fulfilling this order.\",\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={submitting}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n\n <div className=\"space-y-4 p-6\">\n <div>\n <label className=\"mb-2 block text-sm font-medium\">\n {tr(\"devportal.issueCategory\", \"What's the problem?\")}\n </label>\n <div className=\"space-y-2\">\n {CATEGORY_OPTIONS.map((opt) => (\n <label\n key={opt.value}\n className=\"flex items-center gap-2 rounded-md border border-border-seam px-3 py-2 text-sm cursor-pointer hover:bg-bg-sunken transition-colors has-[:checked]:border-primary has-[:checked]:bg-primary/5\"\n >\n <input\n type=\"radio\"\n name=\"issue-category\"\n value={opt.value}\n checked={category === opt.value}\n onChange={() => setCategory(opt.value)}\n className=\"text-primary\"\n />\n {opt.label}\n </label>\n ))}\n </div>\n </div>\n\n <div>\n <label className=\"mb-2 block text-sm font-medium\">\n {tr(\"devportal.issueNote\", \"Additional details (optional)\")}\n </label>\n <textarea\n value={note}\n onChange={(e) => setNote(e.target.value)}\n rows={3}\n maxLength={500}\n placeholder={tr(\n \"devportal.issueNotePlaceholder\",\n \"Anything admin should know…\",\n )}\n className=\"w-full resize-none rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <div className=\"flex items-center justify-end gap-3 border-t p-6\">\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={submitting}\n className=\"rounded-md px-4 py-2 text-sm font-medium text-text-secondary hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={submitting}\n className=\"flex items-center gap-2 rounded-md bg-status-warning-bg px-4 py-2 text-sm font-medium text-status-warning-text hover:opacity-90 transition-opacity disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {tr(\"devportal.submitReport\", \"Report Issue\")}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ReportOrderIssueModal.js","names":[],"sources":["../../../src/devportal/components/ReportOrderIssueModal.tsx"],"sourcesContent":["import { useId, useRef, useState, type FC } from \"react\";\nimport { AlertTriangle, X, Loader2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport type OrderIssueCategory =\n | \"OUT_OF_STOCK\"\n | \"CANNOT_FULFILL\"\n | \"WRONG_ADDRESS\"\n | \"OTHER\";\n\nconst CATEGORY_OPTIONS: { value: OrderIssueCategory; label: string }[] = [\n { value: \"OUT_OF_STOCK\", label: \"Out of stock\" },\n { value: \"CANNOT_FULFILL\", label: \"Cannot fulfill\" },\n { value: \"WRONG_ADDRESS\", label: \"Wrong address / pincode\" },\n { value: \"OTHER\", label: \"Other\" },\n];\n\nconst FLAG_ORDER_ISSUE_MUTATION = `\n mutation FlagOrderIssue($orderId: ID!, $organizationId: ID!, $category: OrderIssueCategory!, $note: String) {\n flagOrderIssue(orderId: $orderId, organizationId: $organizationId, category: $category, note: $note) {\n id\n category\n note\n status\n createdAt\n }\n }\n`;\n\nexport interface ReportOrderIssueModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n orderId: string;\n organizationId: string;\n gqlUrl: string;\n headers: Record<string, string>;\n onReported: () => void;\n}\n\n/**\n * Small modal for a seller to report a problem fulfilling a physical order\n * (out of stock, cannot fulfill, wrong address, etc). Purely informational —\n * visible to admin on the fulfillment console; does not cancel or refund\n * anything (that stays manual for now, see BOFF-7192 follow-up).\n */\nexport const ReportOrderIssueModal: FC<ReportOrderIssueModalProps> = ({\n open,\n onOpenChange,\n orderId,\n organizationId,\n gqlUrl,\n headers,\n onReported,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const titleId = useId();\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const [category, setCategory] = useState<OrderIssueCategory>(\"OUT_OF_STOCK\");\n const [note, setNote] = useState(\"\");\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n if (!open) return null;\n\n const handleClose = () => {\n if (submitting) return;\n setNote(\"\");\n setCategory(\"OUT_OF_STOCK\");\n setError(null);\n onOpenChange(false);\n };\n\n const handleSubmit = async () => {\n setSubmitting(true);\n setError(null);\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query: FLAG_ORDER_ISSUE_MUTATION,\n variables: {\n orderId,\n organizationId,\n category,\n note: note.trim() || null,\n },\n }),\n });\n const json = (await res.json()) as { errors?: { message: string }[] };\n if (json.errors?.length) {\n throw new Error(json.errors[0]?.message ?? \"Failed to report issue\");\n }\n onReported();\n handleClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to report issue\");\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <button\n type=\"button\"\n aria-label=\"Close report issue dialog backdrop\"\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={handleClose}\n />\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n className=\"relative z-10 w-full max-w-md rounded-lg bg-bg-surface shadow-xl\"\n >\n <div className=\"flex items-start gap-4 border-b p-6\">\n <div className=\"rounded-full bg-status-warning-bg-subtle p-2\">\n <AlertTriangle className=\"h-6 w-6 text-status-warning-text\" />\n </div>\n <div className=\"flex-1\">\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {tr(\"devportal.reportOrderIssue\", \"Report an Issue\")}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n \"devportal.reportOrderIssueDesc\",\n \"Let admin know about a problem fulfilling this order.\",\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={submitting}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n\n <div className=\"space-y-4 p-6\">\n <div>\n <label className=\"mb-2 block text-sm font-medium\">\n {tr(\"devportal.issueCategory\", \"What's the problem?\")}\n </label>\n <div className=\"space-y-2\">\n {CATEGORY_OPTIONS.map((opt) => (\n <label\n key={opt.value}\n className=\"flex items-center gap-2 rounded-md border border-border-seam px-3 py-2 text-sm cursor-pointer hover:bg-bg-sunken transition-colors has-[:checked]:border-primary has-[:checked]:bg-primary/5\"\n >\n <input\n type=\"radio\"\n name=\"issue-category\"\n value={opt.value}\n checked={category === opt.value}\n onChange={() => setCategory(opt.value)}\n className=\"text-primary\"\n />\n {opt.label}\n </label>\n ))}\n </div>\n </div>\n\n <div>\n <label className=\"mb-2 block text-sm font-medium\">\n {tr(\"devportal.issueNote\", \"Additional details (optional)\")}\n </label>\n <textarea\n value={note}\n onChange={(e) => setNote(e.target.value)}\n rows={3}\n maxLength={500}\n placeholder={tr(\n \"devportal.issueNotePlaceholder\",\n \"Anything admin should know…\",\n )}\n className=\"w-full resize-none rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n\n <div className=\"flex items-center justify-end gap-3 border-t p-6\">\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={submitting}\n className=\"rounded-md px-4 py-2 text-sm font-medium text-text-secondary hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={submitting}\n className=\"flex items-center gap-2 rounded-md bg-status-warning-bg px-4 py-2 text-sm font-medium text-status-warning-text hover:opacity-90 transition-opacity disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {tr(\"devportal.submitReport\", \"Report Issue\")}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAmKD,wBAAgB,gBAAgB,CAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,EACxC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,iBAAiB,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,UAAU,SAAwB;eA3KvB,OAAO;cACR,MAAM;WACT,MAAM,GAAG,IAAI;mBAkLL,IAAI,KAAG,OAAO,CAAC,mBAAmB,CAAC;EAoDnD"}
1
+ {"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf;;qDAEiD;IACjD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAoKD,wBAAgB,gBAAgB,CAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,EACxC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,iBAAiB,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,UAAU,SAAwB;eA5KvB,OAAO;cACR,MAAM;WACT,MAAM,GAAG,IAAI;mBAmLL,IAAI,KAAG,OAAO,CAAC,mBAAmB,CAAC;EAoDnD"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a file to wspace-files-svc and returns a URL to\n * persist (ApplicationVersion.packageUrl, a physical item's image, an app\n * icon — this hook is generic despite its name; folderPath picks the\n * destination). Every upload here is visibility: PUBLIC (see\n * initiateUpload below), so the returned URL prefers the permanent CDN\n * publicUrl over the presigned downloadUrl (~1 hour TTL, wrong for anything\n * persisted and displayed later — see wspace-files-svc's FieldResolvers.ts\n * doc comment on `downloadUrl`). Falls back to downloadUrl when publicUrl\n * is null (the public-assets CDN infra isn't provisioned in every\n * environment yet), so this keeps working exactly as before until it is.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(folderPath) → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.publicUrl ?? file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n /** The permanent publicUrl when available, else the presigned downloadUrl\n * (field name kept as `downloadUrl` so existing callers don't need to\n * change — see the module doc comment above). */\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n { uploadUrl?: string; session?: { id?: string } } | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl publicUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | {\n file?: {\n id?: string;\n downloadUrl?: string;\n publicUrl?: string | null;\n };\n }\n | undefined\n )?.file;\n const url = file?.publicUrl || file?.downloadUrl;\n if (!file?.id || !url) {\n throw new Error(\"completeUpload did not return file id or a usable URL\");\n }\n return { fileId: file.id, downloadUrl: url };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a file to wspace-files-svc and returns a URL to\n * persist (ApplicationVersion.packageUrl, a physical item's image, an app\n * icon — this hook is generic despite its name; folderPath picks the\n * destination). Every upload here is visibility: PUBLIC (see\n * initiateUpload below), so the returned URL prefers the permanent CDN\n * publicUrl over the presigned downloadUrl (~1 hour TTL, wrong for anything\n * persisted and displayed later — see wspace-files-svc's FieldResolvers.ts\n * doc comment on `downloadUrl`). Falls back to downloadUrl when publicUrl\n * is null (the public-assets CDN infra isn't provisioned in every\n * environment yet), so this keeps working exactly as before until it is.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(folderPath) → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.publicUrl ?? file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n /** The permanent publicUrl when available, else the presigned downloadUrl\n * (field name kept as `downloadUrl` so existing callers don't need to\n * change — see the module doc comment above). */\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl publicUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | {\n file?: {\n id?: string;\n downloadUrl?: string;\n publicUrl?: string | null;\n };\n }\n | undefined\n )?.file;\n const url = file?.publicUrl || file?.downloadUrl;\n if (!file?.id || !url) {\n throw new Error(\"completeUpload did not return file id or a usable URL\");\n }\n return { fileId: file.id, downloadUrl: url };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/ApplicationDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA8ThC;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAigCnC,CAAC"}
1
+ {"version":3,"file":"ApplicationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/ApplicationDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAiUhC;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAigCnC,CAAC"}