@burdenoff/microfe-devportal 2026.914.4 → 2026.914.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/hooks/useSellerOrganization.d.ts.map +1 -1
- package/dist/devportal/hooks/useSellerOrganization.js +2 -2
- package/dist/devportal/hooks/useSellerOrganization.js.map +1 -1
- package/dist/devportal/pages/CreatePhysicalItemPage.d.ts.map +1 -1
- package/dist/devportal/pages/CreatePhysicalItemPage.js +145 -125
- package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSellerOrganization.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/useSellerOrganization.ts"],"names":[],"mappings":"
|
|
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,7 +1,7 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { useEffect as t, useRef as n, useState as r } from "react";
|
|
3
3
|
//#region src/devportal/hooks/useSellerOrganization.ts
|
|
4
|
-
var i = "\n query SellerPhysicalOrganizations {\n
|
|
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
5
|
function o() {
|
|
6
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
7
|
return t(() => {
|
|
@@ -27,7 +27,7 @@ function o() {
|
|
|
27
27
|
try {
|
|
28
28
|
let n = await t(i);
|
|
29
29
|
if (e) return;
|
|
30
|
-
let r = n.
|
|
30
|
+
let r = n.myDeveloperOrganizations.filter((e) => e.kind === "PHYSICAL");
|
|
31
31
|
if (r.length === 0 && !g.current) {
|
|
32
32
|
g.current = !0;
|
|
33
33
|
let n = await t(a, { input: { name: "My Store" } });
|
|
@@ -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\
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreatePhysicalItemPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAA0B,MAAM,OAAO,CAAC;AAkLxD,eAAO,MAAM,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"CreatePhysicalItemPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAA0B,MAAM,OAAO,CAAC;AAkLxD,eAAO,MAAM,sBAAsB,EAAE,EAq0BpC,CAAC"}
|
|
@@ -3,12 +3,12 @@ import { cn as t } from "../utils/cn.js";
|
|
|
3
3
|
import { PageLayout as n, PageSection as r } from "../components/PageLayout.js";
|
|
4
4
|
import { CheckboxField as i, InputField as a, SelectField as o, TextareaField as s } from "../components/FormField.js";
|
|
5
5
|
import { usePackageUpload as c } from "../hooks/usePackageUpload.js";
|
|
6
|
-
import { useSellerOrganization as
|
|
7
|
-
import { useCallback as
|
|
8
|
-
import { AlertTriangle as
|
|
9
|
-
import { Fragment as
|
|
6
|
+
import { useSellerOrganization as ee } from "../hooks/useSellerOrganization.js";
|
|
7
|
+
import { useCallback as l, useRef as te, useState as u } from "react";
|
|
8
|
+
import { AlertTriangle as d, Info as f, Loader2 as p, RotateCw as m, Upload as ne, X as re } from "lucide-react";
|
|
9
|
+
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
10
10
|
//#region src/devportal/pages/CreatePhysicalItemPage.tsx
|
|
11
|
-
var
|
|
11
|
+
var v = [
|
|
12
12
|
{
|
|
13
13
|
value: "MERCHANDISE",
|
|
14
14
|
label: "Merchandise"
|
|
@@ -41,7 +41,7 @@ var b = [
|
|
|
41
41
|
value: "CONSUMABLE",
|
|
42
42
|
label: "Consumable"
|
|
43
43
|
}
|
|
44
|
-
],
|
|
44
|
+
], y = [
|
|
45
45
|
{
|
|
46
46
|
value: "agri-seeds",
|
|
47
47
|
label: "Agri-Inputs — Seeds"
|
|
@@ -62,7 +62,7 @@ var b = [
|
|
|
62
62
|
value: "agri-irrigation",
|
|
63
63
|
label: "Agri-Inputs — Irrigation"
|
|
64
64
|
}
|
|
65
|
-
],
|
|
65
|
+
], ie = [
|
|
66
66
|
{
|
|
67
67
|
value: "USD",
|
|
68
68
|
label: "USD — US Dollar"
|
|
@@ -87,7 +87,7 @@ var b = [
|
|
|
87
87
|
value: "AED",
|
|
88
88
|
label: "AED — UAE Dirham"
|
|
89
89
|
}
|
|
90
|
-
],
|
|
90
|
+
], ae = [
|
|
91
91
|
{
|
|
92
92
|
value: "GRAM",
|
|
93
93
|
label: "Grams (g)"
|
|
@@ -104,7 +104,7 @@ var b = [
|
|
|
104
104
|
value: "POUND",
|
|
105
105
|
label: "Pounds (lb)"
|
|
106
106
|
}
|
|
107
|
-
],
|
|
107
|
+
], b = [
|
|
108
108
|
{
|
|
109
109
|
value: "CENTIMETER",
|
|
110
110
|
label: "Centimeters (cm)"
|
|
@@ -117,7 +117,7 @@ var b = [
|
|
|
117
117
|
value: "METER",
|
|
118
118
|
label: "Meters (m)"
|
|
119
119
|
}
|
|
120
|
-
],
|
|
120
|
+
], x = [
|
|
121
121
|
"name",
|
|
122
122
|
"slug",
|
|
123
123
|
"type",
|
|
@@ -125,18 +125,18 @@ var b = [
|
|
|
125
125
|
"price",
|
|
126
126
|
"currency",
|
|
127
127
|
"stockQuantity"
|
|
128
|
-
],
|
|
128
|
+
], S = "\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n", C = "\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n", w = "\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n }\n }\n", T = "\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n";
|
|
129
129
|
function E(e) {
|
|
130
130
|
return e.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
131
131
|
}
|
|
132
132
|
function D(e) {
|
|
133
|
-
return
|
|
133
|
+
return x.every((t) => {
|
|
134
134
|
let n = e[t];
|
|
135
135
|
return typeof n == "boolean" ? !0 : n.trim() !== "";
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
var O = () => {
|
|
139
|
-
let { navigate:
|
|
139
|
+
let { navigate: x, basePath: O, apiGatewayUrl: k, authToken: A, workspacesGatewayUrl: oe, workspaceId: se, getWorkspaceToken: ce } = e(), { organization: j, organizations: M, loading: N, error: P, selectOrganization: F } = ee(), [I, L] = u({
|
|
140
140
|
name: "",
|
|
141
141
|
slug: "",
|
|
142
142
|
type: "",
|
|
@@ -163,12 +163,12 @@ var O = () => {
|
|
|
163
163
|
countryOfOrigin: "",
|
|
164
164
|
warrantyInfo: "",
|
|
165
165
|
returnPolicy: ""
|
|
166
|
-
}), [R, z] =
|
|
166
|
+
}), [R, z] = u([]), [B, V] = u(!1), H = te(null), { upload: U } = c(oe, A, se, ce, "/DevPortal/Assets"), [W, G] = u(!1), [K, q] = u(null), [le, ue] = u(!1), de = R.some((e) => e.uploading), J = D(I) && !!j && !de, fe = `${k || ""}/global/graphql`, Y = (e, t) => {
|
|
167
167
|
L((n) => ({
|
|
168
168
|
...n,
|
|
169
169
|
[e]: t
|
|
170
170
|
}));
|
|
171
|
-
},
|
|
171
|
+
}, pe = (e) => {
|
|
172
172
|
let t = e.target.value;
|
|
173
173
|
L((e) => ({
|
|
174
174
|
...e,
|
|
@@ -177,7 +177,7 @@ var O = () => {
|
|
|
177
177
|
}));
|
|
178
178
|
}, X = (e) => (t) => {
|
|
179
179
|
Y(e, t.target.checked);
|
|
180
|
-
}, Z =
|
|
180
|
+
}, Z = l((e, t) => {
|
|
181
181
|
U(t).then((t) => {
|
|
182
182
|
z((n) => n.map((n) => n.id === e ? {
|
|
183
183
|
...n,
|
|
@@ -192,7 +192,7 @@ var O = () => {
|
|
|
192
192
|
error: t instanceof Error ? t.message : "Upload failed"
|
|
193
193
|
} : n));
|
|
194
194
|
});
|
|
195
|
-
}, [U]), Q =
|
|
195
|
+
}, [U]), Q = l((e) => {
|
|
196
196
|
let t = Array.from(e).filter((e) => e.type.startsWith("image/")).map((e) => ({
|
|
197
197
|
id: crypto.randomUUID(),
|
|
198
198
|
file: e,
|
|
@@ -200,18 +200,18 @@ var O = () => {
|
|
|
200
200
|
uploading: !0
|
|
201
201
|
}));
|
|
202
202
|
z((e) => [...e, ...t]), t.forEach((e) => Z(e.id, e.file));
|
|
203
|
-
}, [Z]),
|
|
203
|
+
}, [Z]), me = (e) => {
|
|
204
204
|
e.target.files && Q(e.target.files), e.target.value = "";
|
|
205
|
-
},
|
|
205
|
+
}, he = (e) => {
|
|
206
206
|
e.preventDefault(), V(!1), e.dataTransfer.files && Q(e.dataTransfer.files);
|
|
207
|
-
},
|
|
207
|
+
}, ge = (e) => {
|
|
208
208
|
e.preventDefault(), V(!0);
|
|
209
|
-
},
|
|
209
|
+
}, _e = () => V(!1), ve = (e) => {
|
|
210
210
|
z((t) => {
|
|
211
211
|
let n = t.find((t) => t.id === e);
|
|
212
212
|
return n && URL.revokeObjectURL(n.url), t.filter((t) => t.id !== e);
|
|
213
213
|
});
|
|
214
|
-
},
|
|
214
|
+
}, ye = (e) => {
|
|
215
215
|
let t = R.find((t) => t.id === e);
|
|
216
216
|
t && (z((t) => t.map((t) => t.id === e ? {
|
|
217
217
|
...t,
|
|
@@ -220,7 +220,7 @@ var O = () => {
|
|
|
220
220
|
} : t)), Z(e, t.file));
|
|
221
221
|
};
|
|
222
222
|
async function $(e, t) {
|
|
223
|
-
let n = await (await fetch(
|
|
223
|
+
let n = await (await fetch(fe, {
|
|
224
224
|
method: "POST",
|
|
225
225
|
headers: {
|
|
226
226
|
"Content-Type": "application/json",
|
|
@@ -235,41 +235,61 @@ var O = () => {
|
|
|
235
235
|
if (n.errors?.length) throw Error(n.errors.map((e) => e.message).join("; "));
|
|
236
236
|
return n.data;
|
|
237
237
|
}
|
|
238
|
-
return /* @__PURE__ */
|
|
238
|
+
return /* @__PURE__ */ g(n, {
|
|
239
239
|
title: "List a Physical Item",
|
|
240
240
|
description: "Fill in all required details. Your listing will be reviewed before it appears in the store.",
|
|
241
241
|
backLink: "/seller/items",
|
|
242
242
|
backLabel: "Back to My Items",
|
|
243
|
-
children: /* @__PURE__ */
|
|
243
|
+
children: /* @__PURE__ */ _("div", {
|
|
244
244
|
className: "space-y-8 max-w-3xl",
|
|
245
245
|
children: [
|
|
246
|
-
|
|
246
|
+
le && /* @__PURE__ */ g("div", {
|
|
247
247
|
className: "rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text",
|
|
248
248
|
children: "Your listing has been submitted for review! Redirecting..."
|
|
249
249
|
}),
|
|
250
|
-
K && /* @__PURE__ */
|
|
250
|
+
K && /* @__PURE__ */ g("div", {
|
|
251
251
|
className: "rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
252
252
|
children: K
|
|
253
253
|
}),
|
|
254
|
-
|
|
254
|
+
P && /* @__PURE__ */ _("div", {
|
|
255
255
|
className: "rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
256
|
-
children: ["Couldn't set up your seller organization: ",
|
|
256
|
+
children: ["Couldn't set up your seller organization: ", P]
|
|
257
|
+
}),
|
|
258
|
+
/* @__PURE__ */ g(r, {
|
|
259
|
+
title: "Seller Organization",
|
|
260
|
+
description: "The seller organization this item will be listed under.",
|
|
261
|
+
children: /* @__PURE__ */ g("div", {
|
|
262
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-3",
|
|
263
|
+
children: /* @__PURE__ */ g(o, {
|
|
264
|
+
label: "Organization",
|
|
265
|
+
required: !0,
|
|
266
|
+
value: j?.id ?? "",
|
|
267
|
+
onChange: (e) => F(e.target.value),
|
|
268
|
+
options: M.map((e) => ({
|
|
269
|
+
value: e.id,
|
|
270
|
+
label: e.name
|
|
271
|
+
})),
|
|
272
|
+
placeholder: N ? "Resolving your seller organization…" : "Select an organization",
|
|
273
|
+
disabled: N || M.length <= 1,
|
|
274
|
+
hint: M.length > 1 ? "You have more than one seller organization — pick the one this item belongs to." : j ? `Automatically set to your only seller organization, "${j.name}".` : void 0
|
|
275
|
+
})
|
|
276
|
+
})
|
|
257
277
|
}),
|
|
258
|
-
/* @__PURE__ */
|
|
278
|
+
/* @__PURE__ */ g(r, {
|
|
259
279
|
title: "Basic Information",
|
|
260
280
|
description: "Core details shown in the store listing.",
|
|
261
|
-
children: /* @__PURE__ */
|
|
281
|
+
children: /* @__PURE__ */ _("div", {
|
|
262
282
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
263
283
|
children: [
|
|
264
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ g(a, {
|
|
265
285
|
label: "Name",
|
|
266
286
|
required: !0,
|
|
267
287
|
value: I.name,
|
|
268
|
-
onChange:
|
|
288
|
+
onChange: pe,
|
|
269
289
|
placeholder: "e.g. Wireless Ergonomic Keyboard",
|
|
270
290
|
maxLength: 120
|
|
271
291
|
}),
|
|
272
|
-
/* @__PURE__ */
|
|
292
|
+
/* @__PURE__ */ g(a, {
|
|
273
293
|
label: "Slug",
|
|
274
294
|
required: !0,
|
|
275
295
|
value: I.slug,
|
|
@@ -277,23 +297,23 @@ var O = () => {
|
|
|
277
297
|
placeholder: "auto-derived-from-name",
|
|
278
298
|
hint: "URL-friendly identifier. Auto-derived from name; you can edit it."
|
|
279
299
|
}),
|
|
280
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ g(o, {
|
|
281
301
|
label: "Product Type",
|
|
282
302
|
required: !0,
|
|
283
303
|
value: I.type,
|
|
284
304
|
onChange: (e) => Y("type", e.target.value),
|
|
285
|
-
options:
|
|
305
|
+
options: v,
|
|
286
306
|
placeholder: "Select a type"
|
|
287
307
|
}),
|
|
288
|
-
/* @__PURE__ */
|
|
308
|
+
/* @__PURE__ */ g(o, {
|
|
289
309
|
label: "Store Category",
|
|
290
310
|
value: I.category,
|
|
291
311
|
onChange: (e) => Y("category", e.target.value),
|
|
292
|
-
options:
|
|
312
|
+
options: y,
|
|
293
313
|
placeholder: "General / uncategorised",
|
|
294
314
|
hint: "Pick an Agri-Inputs category to list this item in the HealthyBowl Agri-Inputs Store. Leave blank for a general store listing."
|
|
295
315
|
}),
|
|
296
|
-
/* @__PURE__ */
|
|
316
|
+
/* @__PURE__ */ g(a, {
|
|
297
317
|
label: "Short Description",
|
|
298
318
|
required: !0,
|
|
299
319
|
value: I.shortDescription,
|
|
@@ -302,7 +322,7 @@ var O = () => {
|
|
|
302
322
|
maxLength: 160,
|
|
303
323
|
hint: `${I.shortDescription.length}/160`
|
|
304
324
|
}),
|
|
305
|
-
/* @__PURE__ */
|
|
325
|
+
/* @__PURE__ */ g(s, {
|
|
306
326
|
label: "Long Description",
|
|
307
327
|
value: I.longDescription,
|
|
308
328
|
onChange: (e) => Y("longDescription", e.target.value),
|
|
@@ -312,91 +332,91 @@ var O = () => {
|
|
|
312
332
|
]
|
|
313
333
|
})
|
|
314
334
|
}),
|
|
315
|
-
/* @__PURE__ */
|
|
335
|
+
/* @__PURE__ */ g(r, {
|
|
316
336
|
title: "Product Images",
|
|
317
337
|
description: "The first image is used as the main icon.",
|
|
318
|
-
children: /* @__PURE__ */
|
|
338
|
+
children: /* @__PURE__ */ _("div", {
|
|
319
339
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-4",
|
|
320
340
|
children: [
|
|
321
|
-
/* @__PURE__ */
|
|
322
|
-
onDrop:
|
|
323
|
-
onDragOver:
|
|
324
|
-
onDragLeave:
|
|
341
|
+
/* @__PURE__ */ _("div", {
|
|
342
|
+
onDrop: he,
|
|
343
|
+
onDragOver: ge,
|
|
344
|
+
onDragLeave: _e,
|
|
325
345
|
onClick: () => H.current?.click(),
|
|
326
346
|
className: t("flex flex-col items-center justify-center gap-3 rounded-input border-2 border-dashed px-6 py-10 cursor-pointer transition-colors", B ? "border-action-primary-bg bg-action-primary-bg/5" : "border-border-subtle hover:border-border-strong"),
|
|
327
347
|
children: [
|
|
328
|
-
/* @__PURE__ */
|
|
348
|
+
/* @__PURE__ */ g(ne, {
|
|
329
349
|
size: 28,
|
|
330
350
|
className: B ? "text-action-primary-bg" : "text-text-muted"
|
|
331
351
|
}),
|
|
332
|
-
/* @__PURE__ */
|
|
352
|
+
/* @__PURE__ */ _("div", {
|
|
333
353
|
className: "text-center space-y-1",
|
|
334
|
-
children: [/* @__PURE__ */
|
|
354
|
+
children: [/* @__PURE__ */ g("p", {
|
|
335
355
|
className: "text-sm font-medium text-text-primary",
|
|
336
356
|
children: "Drag and drop images here"
|
|
337
|
-
}), /* @__PURE__ */
|
|
357
|
+
}), /* @__PURE__ */ g("p", {
|
|
338
358
|
className: "text-xs text-text-muted",
|
|
339
359
|
children: "or click to browse — accepts any image format, multiple files"
|
|
340
360
|
})]
|
|
341
361
|
}),
|
|
342
|
-
/* @__PURE__ */
|
|
362
|
+
/* @__PURE__ */ g("input", {
|
|
343
363
|
ref: H,
|
|
344
364
|
type: "file",
|
|
345
365
|
accept: "image/*",
|
|
346
366
|
multiple: !0,
|
|
347
367
|
className: "sr-only",
|
|
348
|
-
onChange:
|
|
368
|
+
onChange: me
|
|
349
369
|
})
|
|
350
370
|
]
|
|
351
371
|
}),
|
|
352
|
-
R.length > 0 && /* @__PURE__ */
|
|
372
|
+
R.length > 0 && /* @__PURE__ */ g("div", {
|
|
353
373
|
className: "flex flex-wrap gap-3",
|
|
354
374
|
children: (() => {
|
|
355
375
|
let e = R.find((e) => e.downloadUrl)?.id;
|
|
356
|
-
return R.map((n, r) => /* @__PURE__ */
|
|
376
|
+
return R.map((n, r) => /* @__PURE__ */ _("div", {
|
|
357
377
|
className: "relative group",
|
|
358
378
|
children: [
|
|
359
|
-
/* @__PURE__ */
|
|
379
|
+
/* @__PURE__ */ g("img", {
|
|
360
380
|
src: n.url,
|
|
361
381
|
alt: `Product image ${r + 1}`,
|
|
362
382
|
className: t("h-20 w-20 rounded-input object-cover border", n.error ? "border-status-error-text/50" : "border-border-subtle")
|
|
363
383
|
}),
|
|
364
|
-
n.uploading && /* @__PURE__ */
|
|
384
|
+
n.uploading && /* @__PURE__ */ g("div", {
|
|
365
385
|
className: "absolute inset-0 flex items-center justify-center rounded-input bg-bg-overlay",
|
|
366
|
-
children: /* @__PURE__ */
|
|
386
|
+
children: /* @__PURE__ */ g(p, {
|
|
367
387
|
size: 16,
|
|
368
388
|
className: "animate-spin text-white"
|
|
369
389
|
})
|
|
370
390
|
}),
|
|
371
|
-
n.error && /* @__PURE__ */
|
|
391
|
+
n.error && /* @__PURE__ */ _("button", {
|
|
372
392
|
type: "button",
|
|
373
393
|
onClick: (e) => {
|
|
374
|
-
e.stopPropagation(),
|
|
394
|
+
e.stopPropagation(), ye(n.id);
|
|
375
395
|
},
|
|
376
396
|
title: `${n.error} — click to retry`,
|
|
377
397
|
className: "absolute inset-0 flex flex-col items-center justify-center gap-0.5 rounded-input bg-status-error-bg/80 text-status-error-text",
|
|
378
|
-
children: [/* @__PURE__ */
|
|
398
|
+
children: [/* @__PURE__ */ g(d, { size: 14 }), /* @__PURE__ */ g(m, { size: 11 })]
|
|
379
399
|
}),
|
|
380
|
-
n.id === e && /* @__PURE__ */
|
|
400
|
+
n.id === e && /* @__PURE__ */ g("span", {
|
|
381
401
|
className: "absolute bottom-0 left-0 right-0 rounded-b-input bg-bg-surface/80 text-center text-[10px] font-medium py-0.5 text-text-secondary",
|
|
382
402
|
children: "Main"
|
|
383
403
|
}),
|
|
384
|
-
/* @__PURE__ */
|
|
404
|
+
/* @__PURE__ */ g("button", {
|
|
385
405
|
type: "button",
|
|
386
406
|
onClick: (e) => {
|
|
387
|
-
e.stopPropagation(),
|
|
407
|
+
e.stopPropagation(), ve(n.id);
|
|
388
408
|
},
|
|
389
409
|
className: "absolute -top-1.5 -right-1.5 hidden group-hover:flex h-5 w-5 items-center justify-center rounded-full bg-bg-surface border border-border-subtle text-text-muted hover:text-status-error-text hover:border-status-error-text/40 transition-colors",
|
|
390
410
|
"aria-label": "Remove image",
|
|
391
|
-
children: /* @__PURE__ */
|
|
411
|
+
children: /* @__PURE__ */ g(re, { size: 11 })
|
|
392
412
|
})
|
|
393
413
|
]
|
|
394
414
|
}, n.id));
|
|
395
415
|
})()
|
|
396
416
|
}),
|
|
397
|
-
/* @__PURE__ */
|
|
417
|
+
/* @__PURE__ */ _("p", {
|
|
398
418
|
className: "flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary",
|
|
399
|
-
children: [/* @__PURE__ */
|
|
419
|
+
children: [/* @__PURE__ */ g(f, {
|
|
400
420
|
size: 13,
|
|
401
421
|
className: "mt-0.5 shrink-0 text-text-muted"
|
|
402
422
|
}), "At least 1 image helps buyers — high quality photos increase conversions. The first image becomes the main icon."]
|
|
@@ -404,13 +424,13 @@ var O = () => {
|
|
|
404
424
|
]
|
|
405
425
|
})
|
|
406
426
|
}),
|
|
407
|
-
/* @__PURE__ */
|
|
427
|
+
/* @__PURE__ */ g(r, {
|
|
408
428
|
title: "Pricing",
|
|
409
|
-
children: /* @__PURE__ */
|
|
429
|
+
children: /* @__PURE__ */ _("div", {
|
|
410
430
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
411
|
-
children: [/* @__PURE__ */
|
|
431
|
+
children: [/* @__PURE__ */ _("div", {
|
|
412
432
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
413
|
-
children: [/* @__PURE__ */
|
|
433
|
+
children: [/* @__PURE__ */ g(a, {
|
|
414
434
|
label: "Price",
|
|
415
435
|
required: !0,
|
|
416
436
|
type: "number",
|
|
@@ -419,15 +439,15 @@ var O = () => {
|
|
|
419
439
|
placeholder: "0.00",
|
|
420
440
|
min: .01,
|
|
421
441
|
step: "0.01"
|
|
422
|
-
}), /* @__PURE__ */
|
|
442
|
+
}), /* @__PURE__ */ g(o, {
|
|
423
443
|
label: "Currency",
|
|
424
444
|
required: !0,
|
|
425
445
|
value: I.currency,
|
|
426
446
|
onChange: (e) => Y("currency", e.target.value),
|
|
427
|
-
options:
|
|
447
|
+
options: ie,
|
|
428
448
|
placeholder: "Select currency"
|
|
429
449
|
})]
|
|
430
|
-
}), /* @__PURE__ */
|
|
450
|
+
}), /* @__PURE__ */ g(a, {
|
|
431
451
|
label: "Tags",
|
|
432
452
|
value: I.tags,
|
|
433
453
|
onChange: (e) => Y("tags", e.target.value),
|
|
@@ -436,14 +456,14 @@ var O = () => {
|
|
|
436
456
|
})]
|
|
437
457
|
})
|
|
438
458
|
}),
|
|
439
|
-
/* @__PURE__ */
|
|
459
|
+
/* @__PURE__ */ g(r, {
|
|
440
460
|
title: "Inventory",
|
|
441
|
-
children: /* @__PURE__ */
|
|
461
|
+
children: /* @__PURE__ */ _("div", {
|
|
442
462
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
443
463
|
children: [
|
|
444
|
-
/* @__PURE__ */
|
|
464
|
+
/* @__PURE__ */ _("div", {
|
|
445
465
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
446
|
-
children: [/* @__PURE__ */
|
|
466
|
+
children: [/* @__PURE__ */ g(a, {
|
|
447
467
|
label: "Stock Quantity",
|
|
448
468
|
required: !0,
|
|
449
469
|
type: "number",
|
|
@@ -451,7 +471,7 @@ var O = () => {
|
|
|
451
471
|
onChange: (e) => Y("stockQuantity", e.target.value),
|
|
452
472
|
min: 0,
|
|
453
473
|
step: 1
|
|
454
|
-
}), /* @__PURE__ */
|
|
474
|
+
}), /* @__PURE__ */ g(a, {
|
|
455
475
|
label: "SKU",
|
|
456
476
|
value: I.sku,
|
|
457
477
|
onChange: (e) => Y("sku", e.target.value),
|
|
@@ -459,7 +479,7 @@ var O = () => {
|
|
|
459
479
|
hint: "Your internal stock-keeping unit reference."
|
|
460
480
|
})]
|
|
461
481
|
}),
|
|
462
|
-
/* @__PURE__ */
|
|
482
|
+
/* @__PURE__ */ g(a, {
|
|
463
483
|
label: "Low Stock Threshold",
|
|
464
484
|
type: "number",
|
|
465
485
|
value: I.lowStockThreshold,
|
|
@@ -469,13 +489,13 @@ var O = () => {
|
|
|
469
489
|
step: 1,
|
|
470
490
|
hint: "Get flagged when stock falls to or below this number."
|
|
471
491
|
}),
|
|
472
|
-
/* @__PURE__ */
|
|
492
|
+
/* @__PURE__ */ g(i, {
|
|
473
493
|
label: "Track Inventory",
|
|
474
494
|
description: "Automatically reduce stock when orders are placed.",
|
|
475
495
|
checked: I.trackInventory,
|
|
476
496
|
onChange: X("trackInventory")
|
|
477
497
|
}),
|
|
478
|
-
/* @__PURE__ */
|
|
498
|
+
/* @__PURE__ */ g(i, {
|
|
479
499
|
label: "Allow Backorder",
|
|
480
500
|
description: "Let buyers order even when stock quantity is zero.",
|
|
481
501
|
checked: I.allowBackorder,
|
|
@@ -484,21 +504,21 @@ var O = () => {
|
|
|
484
504
|
]
|
|
485
505
|
})
|
|
486
506
|
}),
|
|
487
|
-
/* @__PURE__ */
|
|
507
|
+
/* @__PURE__ */ g(r, {
|
|
488
508
|
title: "Shipping",
|
|
489
|
-
children: /* @__PURE__ */
|
|
509
|
+
children: /* @__PURE__ */ _("div", {
|
|
490
510
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
491
511
|
children: [
|
|
492
|
-
/* @__PURE__ */
|
|
512
|
+
/* @__PURE__ */ g(i, {
|
|
493
513
|
label: "Requires Shipping",
|
|
494
514
|
description: "This item will be physically shipped to the buyer.",
|
|
495
515
|
checked: I.requiresShipping,
|
|
496
516
|
onChange: X("requiresShipping")
|
|
497
517
|
}),
|
|
498
|
-
I.requiresShipping && /* @__PURE__ */
|
|
499
|
-
/* @__PURE__ */
|
|
518
|
+
I.requiresShipping && /* @__PURE__ */ _(h, { children: [
|
|
519
|
+
/* @__PURE__ */ _("div", {
|
|
500
520
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
501
|
-
children: [/* @__PURE__ */
|
|
521
|
+
children: [/* @__PURE__ */ g(a, {
|
|
502
522
|
label: "Weight",
|
|
503
523
|
type: "number",
|
|
504
524
|
value: I.weightKg,
|
|
@@ -507,17 +527,17 @@ var O = () => {
|
|
|
507
527
|
min: 0,
|
|
508
528
|
step: "0.01",
|
|
509
529
|
hint: "Used to calculate shipping costs."
|
|
510
|
-
}), /* @__PURE__ */
|
|
530
|
+
}), /* @__PURE__ */ g(o, {
|
|
511
531
|
label: "Weight Unit",
|
|
512
532
|
value: I.weightUnit,
|
|
513
533
|
onChange: (e) => Y("weightUnit", e.target.value),
|
|
514
|
-
options:
|
|
534
|
+
options: ae
|
|
515
535
|
})]
|
|
516
536
|
}),
|
|
517
|
-
/* @__PURE__ */
|
|
537
|
+
/* @__PURE__ */ _("div", {
|
|
518
538
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-3",
|
|
519
539
|
children: [
|
|
520
|
-
/* @__PURE__ */
|
|
540
|
+
/* @__PURE__ */ g(a, {
|
|
521
541
|
label: "Length",
|
|
522
542
|
type: "number",
|
|
523
543
|
value: I.length,
|
|
@@ -526,7 +546,7 @@ var O = () => {
|
|
|
526
546
|
min: 0,
|
|
527
547
|
step: "0.01"
|
|
528
548
|
}),
|
|
529
|
-
/* @__PURE__ */
|
|
549
|
+
/* @__PURE__ */ g(a, {
|
|
530
550
|
label: "Width",
|
|
531
551
|
type: "number",
|
|
532
552
|
value: I.width,
|
|
@@ -535,7 +555,7 @@ var O = () => {
|
|
|
535
555
|
min: 0,
|
|
536
556
|
step: "0.01"
|
|
537
557
|
}),
|
|
538
|
-
/* @__PURE__ */
|
|
558
|
+
/* @__PURE__ */ g(a, {
|
|
539
559
|
label: "Height",
|
|
540
560
|
type: "number",
|
|
541
561
|
value: I.height,
|
|
@@ -546,17 +566,17 @@ var O = () => {
|
|
|
546
566
|
})
|
|
547
567
|
]
|
|
548
568
|
}),
|
|
549
|
-
/* @__PURE__ */
|
|
569
|
+
/* @__PURE__ */ g(o, {
|
|
550
570
|
label: "Dimension Unit",
|
|
551
571
|
value: I.dimensionUnit,
|
|
552
572
|
onChange: (e) => Y("dimensionUnit", e.target.value),
|
|
553
|
-
options:
|
|
573
|
+
options: b,
|
|
554
574
|
hint: "Applies to length, width, and height above."
|
|
555
575
|
})
|
|
556
576
|
] }),
|
|
557
|
-
/* @__PURE__ */
|
|
577
|
+
/* @__PURE__ */ _("p", {
|
|
558
578
|
className: "flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary",
|
|
559
|
-
children: [/* @__PURE__ */
|
|
579
|
+
children: [/* @__PURE__ */ g(f, {
|
|
560
580
|
size: 13,
|
|
561
581
|
className: "mt-0.5 shrink-0 text-text-muted"
|
|
562
582
|
}), "Delivery fees are configured at the organization level in store settings."]
|
|
@@ -564,39 +584,39 @@ var O = () => {
|
|
|
564
584
|
]
|
|
565
585
|
})
|
|
566
586
|
}),
|
|
567
|
-
/* @__PURE__ */
|
|
587
|
+
/* @__PURE__ */ g(r, {
|
|
568
588
|
title: "Product Details",
|
|
569
589
|
description: "Optional supplemental information for buyers.",
|
|
570
|
-
children: /* @__PURE__ */
|
|
590
|
+
children: /* @__PURE__ */ _("div", {
|
|
571
591
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
572
592
|
children: [
|
|
573
|
-
/* @__PURE__ */
|
|
593
|
+
/* @__PURE__ */ _("div", {
|
|
574
594
|
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
575
|
-
children: [/* @__PURE__ */
|
|
595
|
+
children: [/* @__PURE__ */ g(a, {
|
|
576
596
|
label: "Brand",
|
|
577
597
|
value: I.brand,
|
|
578
598
|
onChange: (e) => Y("brand", e.target.value),
|
|
579
599
|
placeholder: "e.g. Logitech"
|
|
580
|
-
}), /* @__PURE__ */
|
|
600
|
+
}), /* @__PURE__ */ g(a, {
|
|
581
601
|
label: "Manufacturer",
|
|
582
602
|
value: I.manufacturer,
|
|
583
603
|
onChange: (e) => Y("manufacturer", e.target.value),
|
|
584
604
|
placeholder: "e.g. Logitech Manufacturing Ltd."
|
|
585
605
|
})]
|
|
586
606
|
}),
|
|
587
|
-
/* @__PURE__ */
|
|
607
|
+
/* @__PURE__ */ g(a, {
|
|
588
608
|
label: "Country of Origin",
|
|
589
609
|
value: I.countryOfOrigin,
|
|
590
610
|
onChange: (e) => Y("countryOfOrigin", e.target.value),
|
|
591
611
|
placeholder: "e.g. China"
|
|
592
612
|
}),
|
|
593
|
-
/* @__PURE__ */
|
|
613
|
+
/* @__PURE__ */ g(a, {
|
|
594
614
|
label: "Warranty Info",
|
|
595
615
|
value: I.warrantyInfo,
|
|
596
616
|
onChange: (e) => Y("warrantyInfo", e.target.value),
|
|
597
617
|
placeholder: "e.g. 1-year limited manufacturer warranty"
|
|
598
618
|
}),
|
|
599
|
-
/* @__PURE__ */
|
|
619
|
+
/* @__PURE__ */ g(s, {
|
|
600
620
|
label: "Return Policy",
|
|
601
621
|
value: I.returnPolicy,
|
|
602
622
|
onChange: (e) => Y("returnPolicy", e.target.value),
|
|
@@ -606,29 +626,29 @@ var O = () => {
|
|
|
606
626
|
]
|
|
607
627
|
})
|
|
608
628
|
}),
|
|
609
|
-
/* @__PURE__ */
|
|
629
|
+
/* @__PURE__ */ _("div", {
|
|
610
630
|
className: "flex items-center gap-3 pb-8",
|
|
611
|
-
children: [/* @__PURE__ */
|
|
631
|
+
children: [/* @__PURE__ */ g("button", {
|
|
612
632
|
type: "button",
|
|
613
|
-
onClick: () =>
|
|
633
|
+
onClick: () => x?.(`${O}/seller/items`),
|
|
614
634
|
className: "rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors",
|
|
615
635
|
children: "Cancel"
|
|
616
|
-
}), /* @__PURE__ */
|
|
636
|
+
}), /* @__PURE__ */ g("div", {
|
|
617
637
|
className: "relative",
|
|
618
|
-
children: /* @__PURE__ */
|
|
638
|
+
children: /* @__PURE__ */ g("button", {
|
|
619
639
|
type: "button",
|
|
620
640
|
onClick: async () => {
|
|
621
|
-
if (!(!J || W || !
|
|
641
|
+
if (!(!J || W || !j)) {
|
|
622
642
|
G(!0), q(null);
|
|
623
643
|
try {
|
|
624
|
-
let e = (await $(
|
|
625
|
-
e ||= (await $(
|
|
626
|
-
name:
|
|
627
|
-
email:
|
|
628
|
-
websiteUrl:
|
|
629
|
-
legalName:
|
|
630
|
-
country:
|
|
631
|
-
contactEmail:
|
|
644
|
+
let e = (await $(S)).myPublisher?.id ?? null;
|
|
645
|
+
e ||= (await $(C, { input: {
|
|
646
|
+
name: j.legalEntityName ?? j.name,
|
|
647
|
+
email: j.contactEmail ?? "seller@burdenoff.com",
|
|
648
|
+
websiteUrl: j.websiteUrl ?? null,
|
|
649
|
+
legalName: j.legalEntityName ?? j.name,
|
|
650
|
+
country: j.taxCountry ?? null,
|
|
651
|
+
contactEmail: j.contactEmail ?? null
|
|
632
652
|
} })).registerPublisher.id;
|
|
633
653
|
let t = I.tags.split(",").map((e) => e.trim()).filter(Boolean), n = R.map((e) => e.downloadUrl).filter((e) => !!e), r = I.length.trim() || I.width.trim() || I.height.trim(), i = (await $(w, { input: {
|
|
634
654
|
name: I.name.trim(),
|
|
@@ -659,13 +679,13 @@ var O = () => {
|
|
|
659
679
|
returnPolicy: I.returnPolicy.trim() || null,
|
|
660
680
|
icon: n[0] ?? null,
|
|
661
681
|
screenshots: n.length > 1 ? n.slice(1) : null,
|
|
662
|
-
organizationId:
|
|
682
|
+
organizationId: j.id
|
|
663
683
|
} })).createPhysicalProduct.id;
|
|
664
684
|
await $(T, {
|
|
665
685
|
id: i,
|
|
666
686
|
notes: "Submitted from seller portal"
|
|
667
|
-
}),
|
|
668
|
-
|
|
687
|
+
}), ue(!0), setTimeout(() => {
|
|
688
|
+
x?.(`${O}/seller/items`);
|
|
669
689
|
}, 2e3);
|
|
670
690
|
} catch (e) {
|
|
671
691
|
q(e instanceof Error ? e.message : "Submission failed. Please try again.");
|
|
@@ -676,12 +696,12 @@ var O = () => {
|
|
|
676
696
|
},
|
|
677
697
|
disabled: !J || W,
|
|
678
698
|
"aria-disabled": !J || W,
|
|
679
|
-
title:
|
|
699
|
+
title: N ? "Setting up your seller organization..." : J ? void 0 : "Fill in all required fields to continue",
|
|
680
700
|
className: t("inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors", "bg-action-primary-bg text-action-primary-text", "hover:bg-action-primary-bg/90", "disabled:opacity-50 disabled:cursor-not-allowed", "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"),
|
|
681
|
-
children: W ? /* @__PURE__ */
|
|
701
|
+
children: W ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(p, {
|
|
682
702
|
size: 15,
|
|
683
703
|
className: "animate-spin"
|
|
684
|
-
}), "Submitting..."] }) :
|
|
704
|
+
}), "Submitting..."] }) : N ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(p, {
|
|
685
705
|
size: 15,
|
|
686
706
|
className: "animate-spin"
|
|
687
707
|
}), "Setting up your store..."] }) : "Create & Submit for Review"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreatePhysicalItemPage.js","names":[],"sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent, DragEvent } from \"react\";\nimport { useState, useCallback, useRef } from \"react\";\nimport {\n Loader2,\n Upload,\n X,\n Info,\n AlertTriangle,\n RotateCw,\n} from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useSellerOrganization } from \"../hooks/useSellerOrganization\";\nimport { usePackageUpload } from \"../hooks/usePackageUpload\";\nimport { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface ImagePreview {\n id: string;\n file: File;\n url: string;\n uploading: boolean;\n downloadUrl?: string;\n error?: string;\n}\n\ninterface FormState {\n name: string;\n slug: string;\n type: string;\n category: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n allowBackorder: boolean;\n lowStockThreshold: string;\n requiresShipping: boolean;\n weightKg: string;\n weightUnit: string;\n length: string;\n width: string;\n height: string;\n dimensionUnit: string;\n brand: string;\n manufacturer: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst PRODUCT_CATEGORIES = [\n { value: \"MERCHANDISE\", label: \"Merchandise\" },\n { value: \"HARDWARE\", label: \"Hardware\" },\n { value: \"ACCESSORY\", label: \"Accessory\" },\n { value: \"BOOK\", label: \"Book\" },\n { value: \"EQUIPMENT\", label: \"Equipment\" },\n { value: \"APPAREL\", label: \"Apparel\" },\n { value: \"COLLECTIBLE\", label: \"Collectible\" },\n { value: \"CONSUMABLE\", label: \"Consumable\" },\n];\n\n// Store category (the product `category` string, distinct from the ProductType above).\n// The five `agri-` slugs are the HealthyBowl Agri-Inputs Store discriminator: picking one lists\n// the item in that farmer-facing storefront (roadmap #26). Leave blank for general listings.\nconst STORE_CATEGORIES = [\n { value: \"agri-seeds\", label: \"Agri-Inputs — Seeds\" },\n { value: \"agri-fertilizer\", label: \"Agri-Inputs — Organic Fertilizer\" },\n { value: \"agri-biopesticide\", label: \"Agri-Inputs — Bio-Pesticides\" },\n { value: \"agri-tools\", label: \"Agri-Inputs — Tools\" },\n { value: \"agri-irrigation\", label: \"Agri-Inputs — Irrigation\" },\n];\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst WEIGHT_UNITS = [\n { value: \"GRAM\", label: \"Grams (g)\" },\n { value: \"KILOGRAM\", label: \"Kilograms (kg)\" },\n { value: \"OUNCE\", label: \"Ounces (oz)\" },\n { value: \"POUND\", label: \"Pounds (lb)\" },\n];\n\nconst DIMENSION_UNITS = [\n { value: \"CENTIMETER\", label: \"Centimeters (cm)\" },\n { value: \"INCH\", label: \"Inches (in)\" },\n { value: \"METER\", label: \"Meters (m)\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"slug\",\n \"type\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL strings ──────────────────────────────────────────────────────────\n\nconst MY_PUBLISHER_QUERY = /* GraphQL */ `\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n`;\n\nconst REGISTER_PUBLISHER_MUTATION = /* GraphQL */ `\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n`;\n\nconst CREATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction deriveSlug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return value.trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const CreatePhysicalItemPage: FC = () => {\n const {\n navigate,\n basePath,\n apiGatewayUrl,\n authToken,\n workspacesGatewayUrl,\n workspaceId,\n getWorkspaceToken,\n } = useDevportal();\n const {\n organization: sellerOrganization,\n loading: sellerOrgLoading,\n error: sellerOrgError,\n } = useSellerOrganization();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n category: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n allowBackorder: false,\n lowStockThreshold: \"\",\n requiresShipping: true,\n weightKg: \"\",\n weightUnit: \"KILOGRAM\",\n length: \"\",\n width: \"\",\n height: \"\",\n dimensionUnit: \"CENTIMETER\",\n brand: \"\",\n manufacturer: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [images, setImages] = useState<ImagePreview[]>([]);\n const [isDragOver, setIsDragOver] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const { upload: uploadImage } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n \"/DevPortal/Assets\",\n );\n\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const imagesUploading = images.some((img) => img.uploading);\n const isValid = isFormValid(form) && !!sellerOrganization && !imagesUploading;\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n slug: deriveSlug(name),\n }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\" | \"allowBackorder\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Image handling ───────────────────────────────────────────────────────\n // Each dropped/selected file uploads immediately (to /DevPortal/Assets via\n // usePackageUpload — the same pipeline app icons/screenshots use). The\n // first successfully-uploaded image becomes the product's `icon`; the rest\n // become `screenshots`. Per-image status is tracked locally rather than\n // via the hook's shared uploading/progress state, since multiple uploads\n // can be in flight at once.\n\n const uploadOne = useCallback(\n (id: string, file: File) => {\n uploadImage(file)\n .then((result) => {\n setImages((prev) =>\n prev.map((img) =>\n img.id === id\n ? {\n ...img,\n uploading: false,\n downloadUrl: result.downloadUrl,\n error: undefined,\n }\n : img,\n ),\n );\n })\n .catch((err) => {\n setImages((prev) =>\n prev.map((img) =>\n img.id === id\n ? {\n ...img,\n uploading: false,\n error: err instanceof Error ? err.message : \"Upload failed\",\n }\n : img,\n ),\n );\n });\n },\n [uploadImage],\n );\n\n const addFiles = useCallback(\n (files: FileList | File[]) => {\n const incoming = Array.from(files).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n const previews: ImagePreview[] = incoming.map((file) => ({\n id: crypto.randomUUID(),\n file,\n url: URL.createObjectURL(file),\n uploading: true,\n }));\n setImages((prev) => [...prev, ...previews]);\n previews.forEach((preview) => uploadOne(preview.id, preview.file));\n },\n [uploadOne],\n );\n\n const handleFileInput = (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files) addFiles(e.target.files);\n e.target.value = \"\";\n };\n\n const handleDrop = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(false);\n if (e.dataTransfer.files) addFiles(e.dataTransfer.files);\n };\n\n const handleDragOver = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(true);\n };\n\n const handleDragLeave = () => setIsDragOver(false);\n\n const removeImage = (id: string) => {\n setImages((prev) => {\n const target = prev.find((img) => img.id === id);\n if (target) URL.revokeObjectURL(target.url);\n return prev.filter((img) => img.id !== id);\n });\n };\n\n const retryUpload = (id: string) => {\n const target = images.find((img) => img.id === id);\n if (!target) return;\n setImages((prev) =>\n prev.map((img) =>\n img.id === id ? { ...img, uploading: true, error: undefined } : img,\n ),\n );\n uploadOne(id, target.file);\n };\n\n // ─── GraphQL fetch helper ─────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\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 // ─── Submit flow ──────────────────────────────────────────────────────────\n\n const handleSubmit = async () => {\n if (!isValid || submitting || !sellerOrganization) return;\n setSubmitting(true);\n setSubmitError(null);\n\n try {\n // Step 1: check if publisher exists\n const publisherData = await gqlFetch<{\n myPublisher: { id: string } | null;\n }>(MY_PUBLISHER_QUERY);\n\n let publisherId = publisherData.myPublisher?.id ?? null;\n\n if (!publisherId) {\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: sellerOrganization.legalEntityName ?? sellerOrganization.name,\n email: sellerOrganization.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: sellerOrganization.websiteUrl ?? null,\n legalName:\n sellerOrganization.legalEntityName ?? sellerOrganization.name,\n country: sellerOrganization.taxCountry ?? null,\n contactEmail: sellerOrganization.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 2: create the product, linked to the seller's PHYSICAL org —\n // validated server-side (createPhysicalProduct rejects any org that\n // isn't PHYSICAL-kind, see DeveloperOrgKind / BOFF-7285).\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n const uploadedImageUrls = images\n .map((img) => img.downloadUrl)\n .filter((url): url is string => !!url);\n const hasDimensions =\n form.length.trim() || form.width.trim() || form.height.trim();\n\n const productData = await gqlFetch<{\n createPhysicalProduct: { id: string };\n }>(CREATE_PHYSICAL_PRODUCT_MUTATION, {\n input: {\n name: form.name.trim(),\n slug: form.slug.trim(),\n type: form.type,\n category: form.category.trim() || null,\n description: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n allowBackorder: form.allowBackorder,\n lowStockThreshold: form.lowStockThreshold.trim()\n ? parseInt(form.lowStockThreshold, 10)\n : null,\n requiresShipping: form.requiresShipping,\n weight:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n weightUnit:\n form.requiresShipping && form.weightKg.trim()\n ? form.weightUnit\n : null,\n length:\n form.requiresShipping && form.length.trim()\n ? parseFloat(form.length)\n : null,\n width:\n form.requiresShipping && form.width.trim()\n ? parseFloat(form.width)\n : null,\n height:\n form.requiresShipping && form.height.trim()\n ? parseFloat(form.height)\n : null,\n dimensionUnit:\n form.requiresShipping && hasDimensions ? form.dimensionUnit : null,\n brand: form.brand.trim() || null,\n manufacturer: form.manufacturer.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n icon: uploadedImageUrls[0] ?? null,\n screenshots:\n uploadedImageUrls.length > 1 ? uploadedImageUrls.slice(1) : null,\n organizationId: sellerOrganization.id,\n },\n });\n\n const productId = productData.createPhysicalProduct.id;\n\n // Step 3: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: productId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 4: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error\n ? err.message\n : \"Submission failed. Please try again.\",\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <PageLayout\n title=\"List a Physical Item\"\n description=\"Fill in all required details. Your listing will be reviewed before it appears in the store.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Your listing has been submitted for review! Redirecting...\n </div>\n )}\n\n {/* Error banner */}\n {submitError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n\n {/* Seller org resolution error — the form can't be submitted without one */}\n {sellerOrgError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n Couldn't set up your seller organization: {sellerOrgError}\n </div>\n )}\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Slug\"\n required\n value={form.slug}\n onChange={(e) => setField(\"slug\", e.target.value)}\n placeholder=\"auto-derived-from-name\"\n hint=\"URL-friendly identifier. Auto-derived from name; you can edit it.\"\n />\n\n <SelectField\n label=\"Product Type\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a type\"\n />\n\n <SelectField\n label=\"Store Category\"\n value={form.category}\n onChange={(e) => setField(\"category\", e.target.value)}\n options={STORE_CATEGORIES}\n placeholder=\"General / uncategorised\"\n hint=\"Pick an Agri-Inputs category to list this item in the HealthyBowl Agri-Inputs Store. Leave blank for a general store listing.\"\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Product Images ── */}\n <PageSection\n title=\"Product Images\"\n description=\"The first image is used as the main icon.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-4\">\n {/* Drop zone */}\n <div\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onClick={() => fileInputRef.current?.click()}\n className={cn(\n \"flex flex-col items-center justify-center gap-3 rounded-input border-2 border-dashed px-6 py-10 cursor-pointer transition-colors\",\n isDragOver\n ? \"border-action-primary-bg bg-action-primary-bg/5\"\n : \"border-border-subtle hover:border-border-strong\",\n )}\n >\n <Upload\n size={28}\n className={\n isDragOver ? \"text-action-primary-bg\" : \"text-text-muted\"\n }\n />\n <div className=\"text-center space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n Drag and drop images here\n </p>\n <p className=\"text-xs text-text-muted\">\n or click to browse — accepts any image format, multiple files\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"sr-only\"\n onChange={handleFileInput}\n />\n </div>\n\n {/* Previews */}\n {images.length > 0 && (\n <div className=\"flex flex-wrap gap-3\">\n {(() => {\n const firstUploadedId = images.find(\n (im) => im.downloadUrl,\n )?.id;\n return images.map((img, i) => (\n <div key={img.id} className=\"relative group\">\n <img\n src={img.url}\n alt={`Product image ${i + 1}`}\n className={cn(\n \"h-20 w-20 rounded-input object-cover border\",\n img.error\n ? \"border-status-error-text/50\"\n : \"border-border-subtle\",\n )}\n />\n {img.uploading && (\n <div className=\"absolute inset-0 flex items-center justify-center rounded-input bg-bg-overlay\">\n <Loader2\n size={16}\n className=\"animate-spin text-white\"\n />\n </div>\n )}\n {img.error && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n retryUpload(img.id);\n }}\n title={`${img.error} — click to retry`}\n className=\"absolute inset-0 flex flex-col items-center justify-center gap-0.5 rounded-input bg-status-error-bg/80 text-status-error-text\"\n >\n <AlertTriangle size={14} />\n <RotateCw size={11} />\n </button>\n )}\n {img.id === firstUploadedId && (\n <span className=\"absolute bottom-0 left-0 right-0 rounded-b-input bg-bg-surface/80 text-center text-[10px] font-medium py-0.5 text-text-secondary\">\n Main\n </span>\n )}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeImage(img.id);\n }}\n className=\"absolute -top-1.5 -right-1.5 hidden group-hover:flex h-5 w-5 items-center justify-center rounded-full bg-bg-surface border border-border-subtle text-text-muted hover:text-status-error-text hover:border-status-error-text/40 transition-colors\"\n aria-label=\"Remove image\"\n >\n <X size={11} />\n </button>\n </div>\n ));\n })()}\n </div>\n )}\n\n {/* Tip */}\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n At least 1 image helps buyers — high quality photos increase\n conversions. The first image becomes the main icon.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 3: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords to help buyers find your item.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <InputField\n label=\"Low Stock Threshold\"\n type=\"number\"\n value={form.lowStockThreshold}\n onChange={(e) => setField(\"lowStockThreshold\", e.target.value)}\n placeholder=\"e.g. 5\"\n min={0}\n step={1}\n hint=\"Get flagged when stock falls to or below this number.\"\n />\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n\n <CheckboxField\n label=\"Allow Backorder\"\n description=\"Let buyers order even when stock quantity is zero.\"\n checked={form.allowBackorder}\n onChange={handleCheckboxChange(\"allowBackorder\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 5: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <>\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Weight\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n\n <SelectField\n label=\"Weight Unit\"\n value={form.weightUnit}\n onChange={(e) => setField(\"weightUnit\", e.target.value)}\n options={WEIGHT_UNITS}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <InputField\n label=\"Length\"\n type=\"number\"\n value={form.length}\n onChange={(e) => setField(\"length\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Width\"\n type=\"number\"\n value={form.width}\n onChange={(e) => setField(\"width\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Height\"\n type=\"number\"\n value={form.height}\n onChange={(e) => setField(\"height\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n </div>\n\n <SelectField\n label=\"Dimension Unit\"\n value={form.dimensionUnit}\n onChange={(e) => setField(\"dimensionUnit\", e.target.value)}\n options={DIMENSION_UNITS}\n hint=\"Applies to length, width, and height above.\"\n />\n </>\n )}\n\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Delivery fees are configured at the organization level in store\n settings.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 6: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Manufacturer\"\n value={form.manufacturer}\n onChange={(e) => setField(\"manufacturer\", e.target.value)}\n placeholder=\"e.g. Logitech Manufacturing Ltd.\"\n />\n </div>\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!isValid || submitting}\n aria-disabled={!isValid || submitting}\n title={\n sellerOrgLoading\n ? \"Setting up your seller organization...\"\n : !isValid\n ? \"Fill in all required fields to continue\"\n : undefined\n }\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\",\n )}\n >\n {submitting ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Submitting...\n </>\n ) : sellerOrgLoading ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Setting up your store...\n </>\n ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreatePhysicalItemPage.js","names":[],"sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent, DragEvent } from \"react\";\nimport { useState, useCallback, useRef } from \"react\";\nimport {\n Loader2,\n Upload,\n X,\n Info,\n AlertTriangle,\n RotateCw,\n} from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useSellerOrganization } from \"../hooks/useSellerOrganization\";\nimport { usePackageUpload } from \"../hooks/usePackageUpload\";\nimport { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface ImagePreview {\n id: string;\n file: File;\n url: string;\n uploading: boolean;\n downloadUrl?: string;\n error?: string;\n}\n\ninterface FormState {\n name: string;\n slug: string;\n type: string;\n category: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n allowBackorder: boolean;\n lowStockThreshold: string;\n requiresShipping: boolean;\n weightKg: string;\n weightUnit: string;\n length: string;\n width: string;\n height: string;\n dimensionUnit: string;\n brand: string;\n manufacturer: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst PRODUCT_CATEGORIES = [\n { value: \"MERCHANDISE\", label: \"Merchandise\" },\n { value: \"HARDWARE\", label: \"Hardware\" },\n { value: \"ACCESSORY\", label: \"Accessory\" },\n { value: \"BOOK\", label: \"Book\" },\n { value: \"EQUIPMENT\", label: \"Equipment\" },\n { value: \"APPAREL\", label: \"Apparel\" },\n { value: \"COLLECTIBLE\", label: \"Collectible\" },\n { value: \"CONSUMABLE\", label: \"Consumable\" },\n];\n\n// Store category (the product `category` string, distinct from the ProductType above).\n// The five `agri-` slugs are the HealthyBowl Agri-Inputs Store discriminator: picking one lists\n// the item in that farmer-facing storefront (roadmap #26). Leave blank for general listings.\nconst STORE_CATEGORIES = [\n { value: \"agri-seeds\", label: \"Agri-Inputs — Seeds\" },\n { value: \"agri-fertilizer\", label: \"Agri-Inputs — Organic Fertilizer\" },\n { value: \"agri-biopesticide\", label: \"Agri-Inputs — Bio-Pesticides\" },\n { value: \"agri-tools\", label: \"Agri-Inputs — Tools\" },\n { value: \"agri-irrigation\", label: \"Agri-Inputs — Irrigation\" },\n];\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst WEIGHT_UNITS = [\n { value: \"GRAM\", label: \"Grams (g)\" },\n { value: \"KILOGRAM\", label: \"Kilograms (kg)\" },\n { value: \"OUNCE\", label: \"Ounces (oz)\" },\n { value: \"POUND\", label: \"Pounds (lb)\" },\n];\n\nconst DIMENSION_UNITS = [\n { value: \"CENTIMETER\", label: \"Centimeters (cm)\" },\n { value: \"INCH\", label: \"Inches (in)\" },\n { value: \"METER\", label: \"Meters (m)\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"slug\",\n \"type\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL strings ──────────────────────────────────────────────────────────\n\nconst MY_PUBLISHER_QUERY = /* GraphQL */ `\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n`;\n\nconst REGISTER_PUBLISHER_MUTATION = /* GraphQL */ `\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n`;\n\nconst CREATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction deriveSlug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return value.trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const CreatePhysicalItemPage: FC = () => {\n const {\n navigate,\n basePath,\n apiGatewayUrl,\n authToken,\n workspacesGatewayUrl,\n workspaceId,\n getWorkspaceToken,\n } = useDevportal();\n const {\n organization: sellerOrganization,\n organizations: sellerOrganizations,\n loading: sellerOrgLoading,\n error: sellerOrgError,\n selectOrganization,\n } = useSellerOrganization();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n category: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n allowBackorder: false,\n lowStockThreshold: \"\",\n requiresShipping: true,\n weightKg: \"\",\n weightUnit: \"KILOGRAM\",\n length: \"\",\n width: \"\",\n height: \"\",\n dimensionUnit: \"CENTIMETER\",\n brand: \"\",\n manufacturer: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [images, setImages] = useState<ImagePreview[]>([]);\n const [isDragOver, setIsDragOver] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const { upload: uploadImage } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n \"/DevPortal/Assets\",\n );\n\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const imagesUploading = images.some((img) => img.uploading);\n const isValid = isFormValid(form) && !!sellerOrganization && !imagesUploading;\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n slug: deriveSlug(name),\n }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\" | \"allowBackorder\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Image handling ───────────────────────────────────────────────────────\n // Each dropped/selected file uploads immediately (to /DevPortal/Assets via\n // usePackageUpload — the same pipeline app icons/screenshots use). The\n // first successfully-uploaded image becomes the product's `icon`; the rest\n // become `screenshots`. Per-image status is tracked locally rather than\n // via the hook's shared uploading/progress state, since multiple uploads\n // can be in flight at once.\n\n const uploadOne = useCallback(\n (id: string, file: File) => {\n uploadImage(file)\n .then((result) => {\n setImages((prev) =>\n prev.map((img) =>\n img.id === id\n ? {\n ...img,\n uploading: false,\n downloadUrl: result.downloadUrl,\n error: undefined,\n }\n : img,\n ),\n );\n })\n .catch((err) => {\n setImages((prev) =>\n prev.map((img) =>\n img.id === id\n ? {\n ...img,\n uploading: false,\n error: err instanceof Error ? err.message : \"Upload failed\",\n }\n : img,\n ),\n );\n });\n },\n [uploadImage],\n );\n\n const addFiles = useCallback(\n (files: FileList | File[]) => {\n const incoming = Array.from(files).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n const previews: ImagePreview[] = incoming.map((file) => ({\n id: crypto.randomUUID(),\n file,\n url: URL.createObjectURL(file),\n uploading: true,\n }));\n setImages((prev) => [...prev, ...previews]);\n previews.forEach((preview) => uploadOne(preview.id, preview.file));\n },\n [uploadOne],\n );\n\n const handleFileInput = (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files) addFiles(e.target.files);\n e.target.value = \"\";\n };\n\n const handleDrop = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(false);\n if (e.dataTransfer.files) addFiles(e.dataTransfer.files);\n };\n\n const handleDragOver = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(true);\n };\n\n const handleDragLeave = () => setIsDragOver(false);\n\n const removeImage = (id: string) => {\n setImages((prev) => {\n const target = prev.find((img) => img.id === id);\n if (target) URL.revokeObjectURL(target.url);\n return prev.filter((img) => img.id !== id);\n });\n };\n\n const retryUpload = (id: string) => {\n const target = images.find((img) => img.id === id);\n if (!target) return;\n setImages((prev) =>\n prev.map((img) =>\n img.id === id ? { ...img, uploading: true, error: undefined } : img,\n ),\n );\n uploadOne(id, target.file);\n };\n\n // ─── GraphQL fetch helper ─────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\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 // ─── Submit flow ──────────────────────────────────────────────────────────\n\n const handleSubmit = async () => {\n if (!isValid || submitting || !sellerOrganization) return;\n setSubmitting(true);\n setSubmitError(null);\n\n try {\n // Step 1: check if publisher exists\n const publisherData = await gqlFetch<{\n myPublisher: { id: string } | null;\n }>(MY_PUBLISHER_QUERY);\n\n let publisherId = publisherData.myPublisher?.id ?? null;\n\n if (!publisherId) {\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: sellerOrganization.legalEntityName ?? sellerOrganization.name,\n email: sellerOrganization.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: sellerOrganization.websiteUrl ?? null,\n legalName:\n sellerOrganization.legalEntityName ?? sellerOrganization.name,\n country: sellerOrganization.taxCountry ?? null,\n contactEmail: sellerOrganization.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 2: create the product, linked to the seller's PHYSICAL org —\n // validated server-side (createPhysicalProduct rejects any org that\n // isn't PHYSICAL-kind, see DeveloperOrgKind / BOFF-7285).\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n const uploadedImageUrls = images\n .map((img) => img.downloadUrl)\n .filter((url): url is string => !!url);\n const hasDimensions =\n form.length.trim() || form.width.trim() || form.height.trim();\n\n const productData = await gqlFetch<{\n createPhysicalProduct: { id: string };\n }>(CREATE_PHYSICAL_PRODUCT_MUTATION, {\n input: {\n name: form.name.trim(),\n slug: form.slug.trim(),\n type: form.type,\n category: form.category.trim() || null,\n description: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n allowBackorder: form.allowBackorder,\n lowStockThreshold: form.lowStockThreshold.trim()\n ? parseInt(form.lowStockThreshold, 10)\n : null,\n requiresShipping: form.requiresShipping,\n weight:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n weightUnit:\n form.requiresShipping && form.weightKg.trim()\n ? form.weightUnit\n : null,\n length:\n form.requiresShipping && form.length.trim()\n ? parseFloat(form.length)\n : null,\n width:\n form.requiresShipping && form.width.trim()\n ? parseFloat(form.width)\n : null,\n height:\n form.requiresShipping && form.height.trim()\n ? parseFloat(form.height)\n : null,\n dimensionUnit:\n form.requiresShipping && hasDimensions ? form.dimensionUnit : null,\n brand: form.brand.trim() || null,\n manufacturer: form.manufacturer.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n icon: uploadedImageUrls[0] ?? null,\n screenshots:\n uploadedImageUrls.length > 1 ? uploadedImageUrls.slice(1) : null,\n organizationId: sellerOrganization.id,\n },\n });\n\n const productId = productData.createPhysicalProduct.id;\n\n // Step 3: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: productId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 4: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error\n ? err.message\n : \"Submission failed. Please try again.\",\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <PageLayout\n title=\"List a Physical Item\"\n description=\"Fill in all required details. Your listing will be reviewed before it appears in the store.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Your listing has been submitted for review! Redirecting...\n </div>\n )}\n\n {/* Error banner */}\n {submitError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n\n {/* Seller org resolution error — the form can't be submitted without one */}\n {sellerOrgError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n Couldn't set up your seller organization: {sellerOrgError}\n </div>\n )}\n\n {/* ── Section 0: Seller Organization ── */}\n <PageSection\n title=\"Seller Organization\"\n description=\"The seller organization this item will be listed under.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-3\">\n <SelectField\n label=\"Organization\"\n required\n value={sellerOrganization?.id ?? \"\"}\n onChange={(e) => selectOrganization(e.target.value)}\n options={sellerOrganizations.map((org) => ({\n value: org.id,\n label: org.name,\n }))}\n placeholder={\n sellerOrgLoading\n ? \"Resolving your seller organization…\"\n : \"Select an organization\"\n }\n disabled={sellerOrgLoading || sellerOrganizations.length <= 1}\n hint={\n sellerOrganizations.length > 1\n ? \"You have more than one seller organization — pick the one this item belongs to.\"\n : sellerOrganization\n ? `Automatically set to your only seller organization, \"${sellerOrganization.name}\".`\n : undefined\n }\n />\n </div>\n </PageSection>\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Slug\"\n required\n value={form.slug}\n onChange={(e) => setField(\"slug\", e.target.value)}\n placeholder=\"auto-derived-from-name\"\n hint=\"URL-friendly identifier. Auto-derived from name; you can edit it.\"\n />\n\n <SelectField\n label=\"Product Type\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a type\"\n />\n\n <SelectField\n label=\"Store Category\"\n value={form.category}\n onChange={(e) => setField(\"category\", e.target.value)}\n options={STORE_CATEGORIES}\n placeholder=\"General / uncategorised\"\n hint=\"Pick an Agri-Inputs category to list this item in the HealthyBowl Agri-Inputs Store. Leave blank for a general store listing.\"\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Product Images ── */}\n <PageSection\n title=\"Product Images\"\n description=\"The first image is used as the main icon.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-4\">\n {/* Drop zone */}\n <div\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onClick={() => fileInputRef.current?.click()}\n className={cn(\n \"flex flex-col items-center justify-center gap-3 rounded-input border-2 border-dashed px-6 py-10 cursor-pointer transition-colors\",\n isDragOver\n ? \"border-action-primary-bg bg-action-primary-bg/5\"\n : \"border-border-subtle hover:border-border-strong\",\n )}\n >\n <Upload\n size={28}\n className={\n isDragOver ? \"text-action-primary-bg\" : \"text-text-muted\"\n }\n />\n <div className=\"text-center space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n Drag and drop images here\n </p>\n <p className=\"text-xs text-text-muted\">\n or click to browse — accepts any image format, multiple files\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"sr-only\"\n onChange={handleFileInput}\n />\n </div>\n\n {/* Previews */}\n {images.length > 0 && (\n <div className=\"flex flex-wrap gap-3\">\n {(() => {\n const firstUploadedId = images.find(\n (im) => im.downloadUrl,\n )?.id;\n return images.map((img, i) => (\n <div key={img.id} className=\"relative group\">\n <img\n src={img.url}\n alt={`Product image ${i + 1}`}\n className={cn(\n \"h-20 w-20 rounded-input object-cover border\",\n img.error\n ? \"border-status-error-text/50\"\n : \"border-border-subtle\",\n )}\n />\n {img.uploading && (\n <div className=\"absolute inset-0 flex items-center justify-center rounded-input bg-bg-overlay\">\n <Loader2\n size={16}\n className=\"animate-spin text-white\"\n />\n </div>\n )}\n {img.error && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n retryUpload(img.id);\n }}\n title={`${img.error} — click to retry`}\n className=\"absolute inset-0 flex flex-col items-center justify-center gap-0.5 rounded-input bg-status-error-bg/80 text-status-error-text\"\n >\n <AlertTriangle size={14} />\n <RotateCw size={11} />\n </button>\n )}\n {img.id === firstUploadedId && (\n <span className=\"absolute bottom-0 left-0 right-0 rounded-b-input bg-bg-surface/80 text-center text-[10px] font-medium py-0.5 text-text-secondary\">\n Main\n </span>\n )}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeImage(img.id);\n }}\n className=\"absolute -top-1.5 -right-1.5 hidden group-hover:flex h-5 w-5 items-center justify-center rounded-full bg-bg-surface border border-border-subtle text-text-muted hover:text-status-error-text hover:border-status-error-text/40 transition-colors\"\n aria-label=\"Remove image\"\n >\n <X size={11} />\n </button>\n </div>\n ));\n })()}\n </div>\n )}\n\n {/* Tip */}\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n At least 1 image helps buyers — high quality photos increase\n conversions. The first image becomes the main icon.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 3: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords to help buyers find your item.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <InputField\n label=\"Low Stock Threshold\"\n type=\"number\"\n value={form.lowStockThreshold}\n onChange={(e) => setField(\"lowStockThreshold\", e.target.value)}\n placeholder=\"e.g. 5\"\n min={0}\n step={1}\n hint=\"Get flagged when stock falls to or below this number.\"\n />\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n\n <CheckboxField\n label=\"Allow Backorder\"\n description=\"Let buyers order even when stock quantity is zero.\"\n checked={form.allowBackorder}\n onChange={handleCheckboxChange(\"allowBackorder\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 5: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <>\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Weight\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n\n <SelectField\n label=\"Weight Unit\"\n value={form.weightUnit}\n onChange={(e) => setField(\"weightUnit\", e.target.value)}\n options={WEIGHT_UNITS}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <InputField\n label=\"Length\"\n type=\"number\"\n value={form.length}\n onChange={(e) => setField(\"length\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Width\"\n type=\"number\"\n value={form.width}\n onChange={(e) => setField(\"width\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Height\"\n type=\"number\"\n value={form.height}\n onChange={(e) => setField(\"height\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n </div>\n\n <SelectField\n label=\"Dimension Unit\"\n value={form.dimensionUnit}\n onChange={(e) => setField(\"dimensionUnit\", e.target.value)}\n options={DIMENSION_UNITS}\n hint=\"Applies to length, width, and height above.\"\n />\n </>\n )}\n\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Delivery fees are configured at the organization level in store\n settings.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 6: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Manufacturer\"\n value={form.manufacturer}\n onChange={(e) => setField(\"manufacturer\", e.target.value)}\n placeholder=\"e.g. Logitech Manufacturing Ltd.\"\n />\n </div>\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!isValid || submitting}\n aria-disabled={!isValid || submitting}\n title={\n sellerOrgLoading\n ? \"Setting up your seller organization...\"\n : !isValid\n ? \"Fill in all required fields to continue\"\n : undefined\n }\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\",\n )}\n >\n {submitting ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Submitting...\n </>\n ) : sellerOrgLoading ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Setting up your store...\n </>\n ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|