@burdenoff/microfe-devportal 2026.707.2 → 2026.713.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/AdminDevportalRoot.js +33 -27
- package/dist/devportal/AdminDevportalRoot.js.map +1 -1
- package/dist/devportal/AdminDevportalRoutes.js +9 -5
- package/dist/devportal/AdminDevportalRoutes.js.map +1 -1
- package/dist/devportal/DevportalRoutes.js +125 -105
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/pages/AdminOrganizationVerificationPage.js +169 -0
- package/dist/devportal/pages/AdminOrganizationVerificationPage.js.map +1 -0
- package/dist/devportal/pages/ApplicationVersionsPage.js +551 -320
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/CreatePhysicalItemPage.js +494 -0
- package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -0
- package/dist/devportal/pages/EditPhysicalItemPage.js +419 -0
- package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -0
- package/dist/devportal/pages/OrganizationDetailPage.js +441 -295
- package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/devportal/pages/RevenueDashboardPage.js +72 -54
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js +364 -0
- package/dist/devportal/pages/SellerDashboardPage.js.map +1 -0
- package/dist/devportal/pages/SellerItemsPage.js +533 -0
- package/dist/devportal/pages/SellerItemsPage.js.map +1 -0
- package/dist/devportal/pages/SellerOrdersPage.js +411 -0
- package/dist/devportal/pages/SellerOrdersPage.js.map +1 -0
- package/dist/devportal/types/index.js.map +1 -1
- package/dist/generated/global-operations.js +149 -149
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { PageLayout as n, PageSection as r } from "../components/PageLayout.js";
|
|
4
|
+
import { CheckboxField as i, InputField as a, SelectField as o, TextareaField as s } from "../components/FormField.js";
|
|
5
|
+
import { useCallback as c, useRef as l, useState as u } from "react";
|
|
6
|
+
import { Info as d, Loader2 as f, Upload as ee, X as p } from "lucide-react";
|
|
7
|
+
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
8
|
+
//#region src/devportal/pages/CreatePhysicalItemPage.tsx
|
|
9
|
+
var _ = [
|
|
10
|
+
{
|
|
11
|
+
value: "MERCHANDISE",
|
|
12
|
+
label: "Merchandise"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
value: "HARDWARE",
|
|
16
|
+
label: "Hardware"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: "ACCESSORY",
|
|
20
|
+
label: "Accessory"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
value: "BOOK",
|
|
24
|
+
label: "Book"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
value: "EQUIPMENT",
|
|
28
|
+
label: "Equipment"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
value: "APPAREL",
|
|
32
|
+
label: "Apparel"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
value: "COLLECTIBLE",
|
|
36
|
+
label: "Collectible"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
value: "CONSUMABLE",
|
|
40
|
+
label: "Consumable"
|
|
41
|
+
}
|
|
42
|
+
], v = [
|
|
43
|
+
{
|
|
44
|
+
value: "USD",
|
|
45
|
+
label: "USD — US Dollar"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: "INR",
|
|
49
|
+
label: "INR — Indian Rupee"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
value: "EUR",
|
|
53
|
+
label: "EUR — Euro"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
value: "GBP",
|
|
57
|
+
label: "GBP — British Pound"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
value: "SGD",
|
|
61
|
+
label: "SGD — Singapore Dollar"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
value: "AED",
|
|
65
|
+
label: "AED — UAE Dirham"
|
|
66
|
+
}
|
|
67
|
+
], y = [
|
|
68
|
+
"name",
|
|
69
|
+
"slug",
|
|
70
|
+
"type",
|
|
71
|
+
"shortDescription",
|
|
72
|
+
"price",
|
|
73
|
+
"currency",
|
|
74
|
+
"stockQuantity"
|
|
75
|
+
], b = "\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n", x = "\n query SellerOrgForPublisher {\n developerOrganizations(limit: 1, offset: 0) {\n edges {\n node {\n name\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n }\n }\n", S = "\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n", C = "\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", w = "\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n";
|
|
76
|
+
function T(e) {
|
|
77
|
+
return e.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
78
|
+
}
|
|
79
|
+
function E(e) {
|
|
80
|
+
return y.every((t) => {
|
|
81
|
+
let n = e[t];
|
|
82
|
+
return typeof n == "boolean" ? !0 : n.trim() !== "";
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
var D = () => {
|
|
86
|
+
let { navigate: y, basePath: D, apiGatewayUrl: O, authToken: k } = e(), [A, j] = u({
|
|
87
|
+
name: "",
|
|
88
|
+
slug: "",
|
|
89
|
+
type: "",
|
|
90
|
+
shortDescription: "",
|
|
91
|
+
longDescription: "",
|
|
92
|
+
price: "",
|
|
93
|
+
currency: "",
|
|
94
|
+
tags: "",
|
|
95
|
+
stockQuantity: "1",
|
|
96
|
+
sku: "",
|
|
97
|
+
trackInventory: !0,
|
|
98
|
+
requiresShipping: !0,
|
|
99
|
+
weightKg: "",
|
|
100
|
+
brand: "",
|
|
101
|
+
countryOfOrigin: "",
|
|
102
|
+
warrantyInfo: "",
|
|
103
|
+
returnPolicy: ""
|
|
104
|
+
}), [M, N] = u([]), [P, F] = u(!1), I = l(null), [L, R] = u(!1), [z, B] = u(null), [V, H] = u(!1), U = E(A), W = `${O || ""}/global/graphql`, G = (e, t) => {
|
|
105
|
+
j((n) => ({
|
|
106
|
+
...n,
|
|
107
|
+
[e]: t
|
|
108
|
+
}));
|
|
109
|
+
}, K = (e) => {
|
|
110
|
+
let t = e.target.value;
|
|
111
|
+
j((e) => ({
|
|
112
|
+
...e,
|
|
113
|
+
name: t,
|
|
114
|
+
slug: T(t)
|
|
115
|
+
}));
|
|
116
|
+
}, q = (e) => (t) => {
|
|
117
|
+
G(e, t.target.checked);
|
|
118
|
+
}, J = c((e) => {
|
|
119
|
+
let t = Array.from(e).filter((e) => e.type.startsWith("image/")).map((e) => ({
|
|
120
|
+
file: e,
|
|
121
|
+
url: URL.createObjectURL(e)
|
|
122
|
+
}));
|
|
123
|
+
N((e) => [...e, ...t]);
|
|
124
|
+
}, []), Y = (e) => {
|
|
125
|
+
e.target.files && J(e.target.files), e.target.value = "";
|
|
126
|
+
}, X = (e) => {
|
|
127
|
+
e.preventDefault(), F(!1), e.dataTransfer.files && J(e.dataTransfer.files);
|
|
128
|
+
}, Z = (e) => {
|
|
129
|
+
e.preventDefault(), F(!0);
|
|
130
|
+
}, Q = () => F(!1), te = (e) => {
|
|
131
|
+
N((t) => {
|
|
132
|
+
let n = [...t];
|
|
133
|
+
return URL.revokeObjectURL(n[e].url), n.splice(e, 1), n;
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
async function $(e, t) {
|
|
137
|
+
let n = await (await fetch(W, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: {
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
...k ? { Authorization: `Bearer ${k}` } : {}
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
query: e,
|
|
145
|
+
variables: t
|
|
146
|
+
})
|
|
147
|
+
})).json();
|
|
148
|
+
if (n.errors?.length) throw Error(n.errors.map((e) => e.message).join("; "));
|
|
149
|
+
return n.data;
|
|
150
|
+
}
|
|
151
|
+
return /* @__PURE__ */ h(n, {
|
|
152
|
+
title: "List a Physical Item",
|
|
153
|
+
description: "Fill in all required details. Your listing will be reviewed before it appears in the store.",
|
|
154
|
+
backLink: "/seller/items",
|
|
155
|
+
backLabel: "Back to My Items",
|
|
156
|
+
children: /* @__PURE__ */ g("div", {
|
|
157
|
+
className: "space-y-8 max-w-3xl",
|
|
158
|
+
children: [
|
|
159
|
+
V && /* @__PURE__ */ h("div", {
|
|
160
|
+
className: "rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text",
|
|
161
|
+
children: "Your listing has been submitted for review! Redirecting..."
|
|
162
|
+
}),
|
|
163
|
+
z && /* @__PURE__ */ h("div", {
|
|
164
|
+
className: "rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
165
|
+
children: z
|
|
166
|
+
}),
|
|
167
|
+
/* @__PURE__ */ h(r, {
|
|
168
|
+
title: "Basic Information",
|
|
169
|
+
description: "Core details shown in the store listing.",
|
|
170
|
+
children: /* @__PURE__ */ g("div", {
|
|
171
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
172
|
+
children: [
|
|
173
|
+
/* @__PURE__ */ h(a, {
|
|
174
|
+
label: "Name",
|
|
175
|
+
required: !0,
|
|
176
|
+
value: A.name,
|
|
177
|
+
onChange: K,
|
|
178
|
+
placeholder: "e.g. Wireless Ergonomic Keyboard",
|
|
179
|
+
maxLength: 120
|
|
180
|
+
}),
|
|
181
|
+
/* @__PURE__ */ h(a, {
|
|
182
|
+
label: "Slug",
|
|
183
|
+
required: !0,
|
|
184
|
+
value: A.slug,
|
|
185
|
+
onChange: (e) => G("slug", e.target.value),
|
|
186
|
+
placeholder: "auto-derived-from-name",
|
|
187
|
+
hint: "URL-friendly identifier. Auto-derived from name; you can edit it."
|
|
188
|
+
}),
|
|
189
|
+
/* @__PURE__ */ h(o, {
|
|
190
|
+
label: "Product Category",
|
|
191
|
+
required: !0,
|
|
192
|
+
value: A.type,
|
|
193
|
+
onChange: (e) => G("type", e.target.value),
|
|
194
|
+
options: _,
|
|
195
|
+
placeholder: "Select a category"
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ h(a, {
|
|
198
|
+
label: "Short Description",
|
|
199
|
+
required: !0,
|
|
200
|
+
value: A.shortDescription,
|
|
201
|
+
onChange: (e) => G("shortDescription", e.target.value),
|
|
202
|
+
placeholder: "One-line summary shown on the store card",
|
|
203
|
+
maxLength: 160,
|
|
204
|
+
hint: `${A.shortDescription.length}/160`
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ h(s, {
|
|
207
|
+
label: "Long Description",
|
|
208
|
+
value: A.longDescription,
|
|
209
|
+
onChange: (e) => G("longDescription", e.target.value),
|
|
210
|
+
rows: 5,
|
|
211
|
+
placeholder: "Detailed product description, features, specs…"
|
|
212
|
+
})
|
|
213
|
+
]
|
|
214
|
+
})
|
|
215
|
+
}),
|
|
216
|
+
/* @__PURE__ */ h(r, {
|
|
217
|
+
title: "Product Images",
|
|
218
|
+
description: "The first image is used as the main icon.",
|
|
219
|
+
children: /* @__PURE__ */ g("div", {
|
|
220
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-4",
|
|
221
|
+
children: [
|
|
222
|
+
/* @__PURE__ */ g("div", {
|
|
223
|
+
onDrop: X,
|
|
224
|
+
onDragOver: Z,
|
|
225
|
+
onDragLeave: Q,
|
|
226
|
+
onClick: () => I.current?.click(),
|
|
227
|
+
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", P ? "border-action-primary-bg bg-action-primary-bg/5" : "border-border-subtle hover:border-border-strong"),
|
|
228
|
+
children: [
|
|
229
|
+
/* @__PURE__ */ h(ee, {
|
|
230
|
+
size: 28,
|
|
231
|
+
className: P ? "text-action-primary-bg" : "text-text-muted"
|
|
232
|
+
}),
|
|
233
|
+
/* @__PURE__ */ g("div", {
|
|
234
|
+
className: "text-center space-y-1",
|
|
235
|
+
children: [/* @__PURE__ */ h("p", {
|
|
236
|
+
className: "text-sm font-medium text-text-primary",
|
|
237
|
+
children: "Drag and drop images here"
|
|
238
|
+
}), /* @__PURE__ */ h("p", {
|
|
239
|
+
className: "text-xs text-text-muted",
|
|
240
|
+
children: "or click to browse — accepts any image format, multiple files"
|
|
241
|
+
})]
|
|
242
|
+
}),
|
|
243
|
+
/* @__PURE__ */ h("input", {
|
|
244
|
+
ref: I,
|
|
245
|
+
type: "file",
|
|
246
|
+
accept: "image/*",
|
|
247
|
+
multiple: !0,
|
|
248
|
+
className: "sr-only",
|
|
249
|
+
onChange: Y
|
|
250
|
+
})
|
|
251
|
+
]
|
|
252
|
+
}),
|
|
253
|
+
M.length > 0 && /* @__PURE__ */ h("div", {
|
|
254
|
+
className: "flex flex-wrap gap-3",
|
|
255
|
+
children: M.map((e, t) => /* @__PURE__ */ g("div", {
|
|
256
|
+
className: "relative group",
|
|
257
|
+
children: [
|
|
258
|
+
/* @__PURE__ */ h("img", {
|
|
259
|
+
src: e.url,
|
|
260
|
+
alt: `Product image ${t + 1}`,
|
|
261
|
+
className: "h-20 w-20 rounded-input object-cover border border-border-subtle"
|
|
262
|
+
}),
|
|
263
|
+
t === 0 && /* @__PURE__ */ h("span", {
|
|
264
|
+
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",
|
|
265
|
+
children: "Main"
|
|
266
|
+
}),
|
|
267
|
+
/* @__PURE__ */ h("button", {
|
|
268
|
+
type: "button",
|
|
269
|
+
onClick: (e) => {
|
|
270
|
+
e.stopPropagation(), te(t);
|
|
271
|
+
},
|
|
272
|
+
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",
|
|
273
|
+
"aria-label": "Remove image",
|
|
274
|
+
children: /* @__PURE__ */ h(p, { size: 11 })
|
|
275
|
+
})
|
|
276
|
+
]
|
|
277
|
+
}, e.url))
|
|
278
|
+
}),
|
|
279
|
+
/* @__PURE__ */ g("p", {
|
|
280
|
+
className: "flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary",
|
|
281
|
+
children: [/* @__PURE__ */ h(d, {
|
|
282
|
+
size: 13,
|
|
283
|
+
className: "mt-0.5 shrink-0 text-text-muted"
|
|
284
|
+
}), "At least 1 image helps buyers — high quality photos increase conversions."]
|
|
285
|
+
})
|
|
286
|
+
]
|
|
287
|
+
})
|
|
288
|
+
}),
|
|
289
|
+
/* @__PURE__ */ h(r, {
|
|
290
|
+
title: "Pricing",
|
|
291
|
+
children: /* @__PURE__ */ g("div", {
|
|
292
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
293
|
+
children: [/* @__PURE__ */ g("div", {
|
|
294
|
+
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
295
|
+
children: [/* @__PURE__ */ h(a, {
|
|
296
|
+
label: "Price",
|
|
297
|
+
required: !0,
|
|
298
|
+
type: "number",
|
|
299
|
+
value: A.price,
|
|
300
|
+
onChange: (e) => G("price", e.target.value),
|
|
301
|
+
placeholder: "0.00",
|
|
302
|
+
min: .01,
|
|
303
|
+
step: "0.01"
|
|
304
|
+
}), /* @__PURE__ */ h(o, {
|
|
305
|
+
label: "Currency",
|
|
306
|
+
required: !0,
|
|
307
|
+
value: A.currency,
|
|
308
|
+
onChange: (e) => G("currency", e.target.value),
|
|
309
|
+
options: v,
|
|
310
|
+
placeholder: "Select currency"
|
|
311
|
+
})]
|
|
312
|
+
}), /* @__PURE__ */ h(a, {
|
|
313
|
+
label: "Tags",
|
|
314
|
+
value: A.tags,
|
|
315
|
+
onChange: (e) => G("tags", e.target.value),
|
|
316
|
+
placeholder: "mechanical, wireless, ergonomic",
|
|
317
|
+
hint: "Comma-separated keywords to help buyers find your item."
|
|
318
|
+
})]
|
|
319
|
+
})
|
|
320
|
+
}),
|
|
321
|
+
/* @__PURE__ */ h(r, {
|
|
322
|
+
title: "Inventory",
|
|
323
|
+
children: /* @__PURE__ */ g("div", {
|
|
324
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
325
|
+
children: [/* @__PURE__ */ g("div", {
|
|
326
|
+
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
327
|
+
children: [/* @__PURE__ */ h(a, {
|
|
328
|
+
label: "Stock Quantity",
|
|
329
|
+
required: !0,
|
|
330
|
+
type: "number",
|
|
331
|
+
value: A.stockQuantity,
|
|
332
|
+
onChange: (e) => G("stockQuantity", e.target.value),
|
|
333
|
+
min: 0,
|
|
334
|
+
step: 1
|
|
335
|
+
}), /* @__PURE__ */ h(a, {
|
|
336
|
+
label: "SKU",
|
|
337
|
+
value: A.sku,
|
|
338
|
+
onChange: (e) => G("sku", e.target.value),
|
|
339
|
+
placeholder: "e.g. KB-WL-001",
|
|
340
|
+
hint: "Your internal stock-keeping unit reference."
|
|
341
|
+
})]
|
|
342
|
+
}), /* @__PURE__ */ h(i, {
|
|
343
|
+
label: "Track Inventory",
|
|
344
|
+
description: "Automatically reduce stock when orders are placed.",
|
|
345
|
+
checked: A.trackInventory,
|
|
346
|
+
onChange: q("trackInventory")
|
|
347
|
+
})]
|
|
348
|
+
})
|
|
349
|
+
}),
|
|
350
|
+
/* @__PURE__ */ h(r, {
|
|
351
|
+
title: "Shipping",
|
|
352
|
+
children: /* @__PURE__ */ g("div", {
|
|
353
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
354
|
+
children: [
|
|
355
|
+
/* @__PURE__ */ h(i, {
|
|
356
|
+
label: "Requires Shipping",
|
|
357
|
+
description: "This item will be physically shipped to the buyer.",
|
|
358
|
+
checked: A.requiresShipping,
|
|
359
|
+
onChange: q("requiresShipping")
|
|
360
|
+
}),
|
|
361
|
+
A.requiresShipping && /* @__PURE__ */ h(a, {
|
|
362
|
+
label: "Weight (kg)",
|
|
363
|
+
type: "number",
|
|
364
|
+
value: A.weightKg,
|
|
365
|
+
onChange: (e) => G("weightKg", e.target.value),
|
|
366
|
+
placeholder: "0.0",
|
|
367
|
+
min: 0,
|
|
368
|
+
step: "0.01",
|
|
369
|
+
hint: "Used to calculate shipping costs."
|
|
370
|
+
}),
|
|
371
|
+
/* @__PURE__ */ g("p", {
|
|
372
|
+
className: "flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary",
|
|
373
|
+
children: [/* @__PURE__ */ h(d, {
|
|
374
|
+
size: 13,
|
|
375
|
+
className: "mt-0.5 shrink-0 text-text-muted"
|
|
376
|
+
}), "Delivery fees are configured at the organization level in store settings."]
|
|
377
|
+
})
|
|
378
|
+
]
|
|
379
|
+
})
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ h(r, {
|
|
382
|
+
title: "Product Details",
|
|
383
|
+
description: "Optional supplemental information for buyers.",
|
|
384
|
+
children: /* @__PURE__ */ g("div", {
|
|
385
|
+
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-5",
|
|
386
|
+
children: [
|
|
387
|
+
/* @__PURE__ */ g("div", {
|
|
388
|
+
className: "grid grid-cols-1 gap-5 sm:grid-cols-2",
|
|
389
|
+
children: [/* @__PURE__ */ h(a, {
|
|
390
|
+
label: "Brand",
|
|
391
|
+
value: A.brand,
|
|
392
|
+
onChange: (e) => G("brand", e.target.value),
|
|
393
|
+
placeholder: "e.g. Logitech"
|
|
394
|
+
}), /* @__PURE__ */ h(a, {
|
|
395
|
+
label: "Country of Origin",
|
|
396
|
+
value: A.countryOfOrigin,
|
|
397
|
+
onChange: (e) => G("countryOfOrigin", e.target.value),
|
|
398
|
+
placeholder: "e.g. China"
|
|
399
|
+
})]
|
|
400
|
+
}),
|
|
401
|
+
/* @__PURE__ */ h(a, {
|
|
402
|
+
label: "Warranty Info",
|
|
403
|
+
value: A.warrantyInfo,
|
|
404
|
+
onChange: (e) => G("warrantyInfo", e.target.value),
|
|
405
|
+
placeholder: "e.g. 1-year limited manufacturer warranty"
|
|
406
|
+
}),
|
|
407
|
+
/* @__PURE__ */ h(s, {
|
|
408
|
+
label: "Return Policy",
|
|
409
|
+
value: A.returnPolicy,
|
|
410
|
+
onChange: (e) => G("returnPolicy", e.target.value),
|
|
411
|
+
rows: 3,
|
|
412
|
+
placeholder: "e.g. 30-day returns accepted"
|
|
413
|
+
})
|
|
414
|
+
]
|
|
415
|
+
})
|
|
416
|
+
}),
|
|
417
|
+
/* @__PURE__ */ g("div", {
|
|
418
|
+
className: "flex items-center gap-3 pb-8",
|
|
419
|
+
children: [/* @__PURE__ */ h("button", {
|
|
420
|
+
type: "button",
|
|
421
|
+
onClick: () => y?.(`${D}/seller/items`),
|
|
422
|
+
className: "rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors",
|
|
423
|
+
children: "Cancel"
|
|
424
|
+
}), /* @__PURE__ */ h("div", {
|
|
425
|
+
className: "relative",
|
|
426
|
+
children: /* @__PURE__ */ h("button", {
|
|
427
|
+
type: "button",
|
|
428
|
+
onClick: async () => {
|
|
429
|
+
if (!(!U || L)) {
|
|
430
|
+
R(!0), B(null);
|
|
431
|
+
try {
|
|
432
|
+
let e = (await $(b)).myPublisher?.id ?? null;
|
|
433
|
+
if (!e) {
|
|
434
|
+
let t = (await $(x)).developerOrganizations.edges[0]?.node ?? null;
|
|
435
|
+
e = (await $(S, { input: {
|
|
436
|
+
name: t?.legalEntityName ?? t?.name ?? "My Store",
|
|
437
|
+
email: t?.contactEmail ?? "seller@burdenoff.com",
|
|
438
|
+
websiteUrl: t?.websiteUrl ?? null,
|
|
439
|
+
legalName: t?.legalEntityName ?? t?.name ?? "My Store",
|
|
440
|
+
country: t?.taxCountry ?? null,
|
|
441
|
+
contactEmail: t?.contactEmail ?? null
|
|
442
|
+
} })).registerPublisher.id;
|
|
443
|
+
}
|
|
444
|
+
let t = A.tags.split(",").map((e) => e.trim()).filter(Boolean), n = (await $(C, { input: {
|
|
445
|
+
name: A.name.trim(),
|
|
446
|
+
slug: A.slug.trim(),
|
|
447
|
+
type: A.type,
|
|
448
|
+
shortDescription: A.shortDescription.trim(),
|
|
449
|
+
longDescription: A.longDescription.trim() || null,
|
|
450
|
+
price: parseFloat(A.price),
|
|
451
|
+
currency: A.currency,
|
|
452
|
+
tags: t.length > 0 ? t : null,
|
|
453
|
+
stockQuantity: parseInt(A.stockQuantity, 10),
|
|
454
|
+
sku: A.sku.trim() || null,
|
|
455
|
+
trackInventory: A.trackInventory,
|
|
456
|
+
requiresShipping: A.requiresShipping,
|
|
457
|
+
weightKg: A.requiresShipping && A.weightKg.trim() ? parseFloat(A.weightKg) : null,
|
|
458
|
+
brand: A.brand.trim() || null,
|
|
459
|
+
countryOfOrigin: A.countryOfOrigin.trim() || null,
|
|
460
|
+
warrantyInfo: A.warrantyInfo.trim() || null,
|
|
461
|
+
returnPolicy: A.returnPolicy.trim() || null
|
|
462
|
+
} })).createPhysicalProduct.id;
|
|
463
|
+
await $(w, {
|
|
464
|
+
id: n,
|
|
465
|
+
notes: "Submitted from seller portal"
|
|
466
|
+
}), H(!0), setTimeout(() => {
|
|
467
|
+
y?.(`${D}/seller/items`);
|
|
468
|
+
}, 2e3);
|
|
469
|
+
} catch (e) {
|
|
470
|
+
B(e instanceof Error ? e.message : "Submission failed. Please try again.");
|
|
471
|
+
} finally {
|
|
472
|
+
R(!1);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
disabled: !U || L,
|
|
477
|
+
"aria-disabled": !U || L,
|
|
478
|
+
title: U ? void 0 : "Fill in all required fields to continue",
|
|
479
|
+
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"),
|
|
480
|
+
children: L ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(f, {
|
|
481
|
+
size: 15,
|
|
482
|
+
className: "animate-spin"
|
|
483
|
+
}), "Submitting..."] }) : "Create & Submit for Review"
|
|
484
|
+
})
|
|
485
|
+
})]
|
|
486
|
+
})
|
|
487
|
+
]
|
|
488
|
+
})
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
//#endregion
|
|
492
|
+
export { D as CreatePhysicalItemPage };
|
|
493
|
+
|
|
494
|
+
//# sourceMappingURL=CreatePhysicalItemPage.js.map
|
|
@@ -0,0 +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 { Loader2, Upload, X, Info } 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 { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface ImagePreview {\n file: File;\n url: string;\n}\n\ninterface FormState {\n name: string;\n slug: string;\n type: 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 requiresShipping: boolean;\n weightKg: string;\n brand: 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\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 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 ORG_QUERY = /* GraphQL */ `\n query SellerOrgForPublisher {\n developerOrganizations(limit: 1, offset: 0) {\n edges {\n node {\n name\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\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 { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n requiresShipping: true,\n weightKg: \"\",\n brand: \"\",\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\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const isValid = isFormValid(form);\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\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Image handling ───────────────────────────────────────────────────────\n\n const addFiles = useCallback((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 file,\n url: URL.createObjectURL(file),\n }));\n setImages((prev) => [...prev, ...previews]);\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 = (index: number) => {\n setImages((prev) => {\n const next = [...prev];\n URL.revokeObjectURL(next[index]!.url);\n next.splice(index, 1);\n return next;\n });\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 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) 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 // Step 2: auto-register publisher if needed\n if (!publisherId) {\n const orgData = await gqlFetch<{\n developerOrganizations: {\n edges: {\n node: {\n name: string;\n contactEmail: string | null;\n websiteUrl: string | null;\n legalEntityName: string | null;\n taxCountry: string | null;\n };\n }[];\n };\n }>(ORG_QUERY);\n\n const org =\n orgData.developerOrganizations.edges[0]?.node ?? null;\n\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: org?.legalEntityName ?? org?.name ?? \"My Store\",\n email: org?.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: org?.websiteUrl ?? null,\n legalName: org?.legalEntityName ?? org?.name ?? \"My Store\",\n country: org?.taxCountry ?? null,\n contactEmail: org?.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 3: create the product\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\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 shortDescription: 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 requiresShipping: form.requiresShipping,\n weightKg:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n brand: form.brand.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n },\n });\n\n const productId = productData.createPhysicalProduct.id;\n\n // Step 4: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: productId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 5: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error ? err.message : \"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 {/* ── 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 Category\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a category\"\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={isDragOver ? \"text-action-primary-bg\" : \"text-text-muted\"}\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 {images.map((img, i) => (\n <div key={img.url} className=\"relative group\">\n <img\n src={img.url}\n alt={`Product image ${i + 1}`}\n className=\"h-20 w-20 rounded-input object-cover border border-border-subtle\"\n />\n {i === 0 && (\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(i);\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 </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.\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 <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\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 <InputField\n label=\"Weight (kg)\"\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\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=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n </div>\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 !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 ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|