@burdenoff/microfe-coolandlovely 2026.1031.1 → 2026.1031.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,417 @@
1
+ import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
2
+ import { cn as t } from "../../utils/cn.js";
3
+ import { PageLayout as n } from "../../components/PageLayout.js";
4
+ import { useTr as r } from "../../hooks/useTr.js";
5
+ import "../../hooks/index.js";
6
+ import { formatPrice as i, titleCase as a } from "../../utils/formatters.js";
7
+ import { SelectField as o, TextField as s, ToggleField as c } from "../../components/Field.js";
8
+ import { useImportExternalCatalog as l, usePreviewExternalCatalog as u } from "../../hooks/useCatalogImport.js";
9
+ import { useMemo as d, useState as f } from "react";
10
+ import { ArrowLeft as p, CheckCircle2 as m, Download as h, ImageOff as g, Loader2 as _, PackageOpen as v, Sparkles as y } from "lucide-react";
11
+ import { jsx as b, jsxs as x } from "react/jsx-runtime";
12
+ import { GlassCard as S } from "@burdenoff/fe-libs/ui";
13
+ //#region src/coolandlovely/pages/brands/ImportCatalogPage.tsx
14
+ var C = "mock.shop", w = "100", T = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
+ function E() {
16
+ let e = r();
17
+ return d(() => ({
18
+ SHOPIFY: {
19
+ label: "Shopify",
20
+ storeLabel: e("coolandlovely.import.shopify.store", "Shop domain"),
21
+ storePlaceholder: "your-brand.myshopify.com",
22
+ storeHint: e("coolandlovely.import.shopify.storeHint", "Your permanent myshopify.com domain. Use mock.shop to try the Shopify sample store."),
23
+ tokenLabel: e("coolandlovely.import.shopify.token", "Storefront API access token"),
24
+ tokenHint: e("coolandlovely.import.shopify.tokenHint", "Shopify admin → Settings → Apps → Develop apps → Storefront API. Not needed for mock.shop.")
25
+ },
26
+ WOOCOMMERCE: {
27
+ label: "WooCommerce",
28
+ storeLabel: e("coolandlovely.import.woo.store", "Store address"),
29
+ storePlaceholder: "https://shop.your-brand.com",
30
+ storeHint: e("coolandlovely.import.woo.storeHint", "The HTTPS address of your WordPress store."),
31
+ tokenLabel: e("coolandlovely.import.woo.token", "Consumer key"),
32
+ tokenHint: e("coolandlovely.import.woo.tokenHint", "WooCommerce → Settings → Advanced → REST API → Add key, with Read permission."),
33
+ secretLabel: e("coolandlovely.import.woo.secret", "Consumer secret")
34
+ },
35
+ BIGCOMMERCE: {
36
+ label: "BigCommerce",
37
+ storeLabel: e("coolandlovely.import.bc.store", "Store hash"),
38
+ storePlaceholder: "abc123def",
39
+ storeHint: e("coolandlovely.import.bc.storeHint", "The code after /stores/ in your API path (or paste the API path itself)."),
40
+ tokenLabel: e("coolandlovely.import.bc.token", "API account access token"),
41
+ tokenHint: e("coolandlovely.import.bc.tokenHint", "Settings → Store-level API accounts → Create, with read-only Products and Information scopes.")
42
+ }
43
+ }), [e]);
44
+ }
45
+ var D = {
46
+ provider: "SHOPIFY",
47
+ storeIdentifier: "",
48
+ accessToken: "",
49
+ accessSecret: "",
50
+ maxItems: w
51
+ };
52
+ function O() {
53
+ let d = r(), { navigateTo: w } = e(), O = E(), k = u(), A = l(), [j, M] = f(D), [N, P] = f(null), [F, I] = f(""), [L, R] = f(""), [z, B] = f(/* @__PURE__ */ new Set()), [V, H] = f(!0), [U, W] = f(null), [G, K] = f(null), q = O[j.provider], J = j.provider === "SHOPIFY" && j.storeIdentifier.trim().toLowerCase() === C, Y = L && !T.test(L) ? d("coolandlovely.import.handleInvalid", "Use lowercase letters, numbers and single hyphens") : void 0, X = (e, t) => M((n) => ({
54
+ ...n,
55
+ [e]: t
56
+ })), Z = (e) => {
57
+ let t = Number.parseInt(e.maxItems, 10);
58
+ k.mutate({
59
+ provider: e.provider,
60
+ storeIdentifier: e.storeIdentifier.trim(),
61
+ ...e.accessToken.trim() ? { accessToken: e.accessToken.trim() } : {},
62
+ ...e.accessSecret.trim() ? { accessSecret: e.accessSecret.trim() } : {},
63
+ ...Number.isFinite(t) ? { maxItems: t } : {}
64
+ }, { onSuccess: (e) => {
65
+ P(e), I(e.brand.name), R(e.brand.handle), B(new Set(e.items.map((e) => e.externalId))), M((e) => ({
66
+ ...e,
67
+ accessToken: "",
68
+ accessSecret: ""
69
+ }));
70
+ } });
71
+ }, Q = (e) => {
72
+ e.preventDefault(), Z(j);
73
+ }, ee = () => {
74
+ let e = {
75
+ ...D,
76
+ storeIdentifier: C,
77
+ maxItems: "20"
78
+ };
79
+ M(e), Z(e);
80
+ }, te = (e) => B((t) => {
81
+ let n = new Set(t);
82
+ return n.has(e) ? n.delete(e) : n.add(e), n;
83
+ }), ne = () => {
84
+ !N || z.size === 0 || !F.trim() || !L || Y || A.mutate({
85
+ preview: N,
86
+ brandName: F.trim(),
87
+ brandHandle: L,
88
+ selectedIds: z,
89
+ copyImages: V,
90
+ onProgress: W
91
+ }, {
92
+ onSuccess: K,
93
+ onSettled: () => W(null)
94
+ });
95
+ }, $ = () => {
96
+ P(null), K(null), B(/* @__PURE__ */ new Set()), k.reset(), A.reset();
97
+ };
98
+ return /* @__PURE__ */ b(n, {
99
+ title: d("coolandlovely.import.title", "Import catalog"),
100
+ description: d("coolandlovely.import.subtitle", "Bring your existing storefront over from Shopify, WooCommerce or BigCommerce"),
101
+ actions: /* @__PURE__ */ x("button", {
102
+ type: "button",
103
+ onClick: () => w("/brands"),
104
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
105
+ children: [
106
+ /* @__PURE__ */ b(p, { className: "h-4 w-4" }),
107
+ " ",
108
+ d("coolandlovely.actions.backToBrands", "Brands")
109
+ ]
110
+ }),
111
+ children: G ? /* @__PURE__ */ b("div", {
112
+ className: "mx-auto max-w-2xl",
113
+ "data-testid": "catalog-import-result",
114
+ children: /* @__PURE__ */ b(S, {
115
+ treatment: "plain",
116
+ className: "p-6",
117
+ children: /* @__PURE__ */ x("div", {
118
+ className: "flex items-start gap-3",
119
+ children: [/* @__PURE__ */ b(m, { className: "mt-0.5 h-6 w-6 text-status-success-text" }), /* @__PURE__ */ x("div", {
120
+ className: "flex-1",
121
+ children: [
122
+ /* @__PURE__ */ b("h2", {
123
+ className: "text-base font-semibold text-text-primary",
124
+ children: d("coolandlovely.import.doneTitle", "Imported {{name}}", { name: G.brand.name })
125
+ }),
126
+ /* @__PURE__ */ b("p", {
127
+ className: "mt-1 text-sm text-text-secondary",
128
+ children: d("coolandlovely.import.doneSummary", "{{items}} style items in {{collections}} collections. The storefront starts as a draft.", {
129
+ items: G.itemsCreated,
130
+ collections: G.collectionsCreated
131
+ })
132
+ }),
133
+ V && /* @__PURE__ */ b("p", {
134
+ className: "mt-1 text-sm text-text-secondary",
135
+ children: d("coolandlovely.import.doneImages", "{{copied}} images copied to Files, {{failed}} could not be copied.", {
136
+ copied: G.imagesCopied,
137
+ failed: G.imagesFailed
138
+ })
139
+ }),
140
+ /* @__PURE__ */ x("div", {
141
+ className: "mt-4 flex flex-wrap gap-2",
142
+ children: [/* @__PURE__ */ b("button", {
143
+ type: "button",
144
+ onClick: () => w(`/brands/${G.brand.id}`),
145
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
146
+ children: d("coolandlovely.import.openBrand", "Open storefront")
147
+ }), /* @__PURE__ */ b("button", {
148
+ type: "button",
149
+ onClick: $,
150
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
151
+ children: d("coolandlovely.import.another", "Import another catalog")
152
+ })]
153
+ })
154
+ ]
155
+ })]
156
+ })
157
+ })
158
+ }) : N ? /* @__PURE__ */ x("div", {
159
+ className: "mx-auto max-w-5xl space-y-4",
160
+ "data-testid": "catalog-import-review",
161
+ children: [
162
+ /* @__PURE__ */ x(S, {
163
+ treatment: "plain",
164
+ className: "p-6",
165
+ children: [
166
+ /* @__PURE__ */ x("div", {
167
+ className: "flex flex-wrap items-start justify-between gap-3",
168
+ children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
169
+ className: "text-base font-semibold text-text-primary",
170
+ children: d("coolandlovely.import.reviewTitle", "Review the {{provider}} catalog", { provider: O[N.provider].label })
171
+ }), /* @__PURE__ */ b("p", {
172
+ className: "text-xs text-text-secondary",
173
+ children: d("coolandlovely.import.reviewSummary", "{{items}} products in {{collections}} collections from {{source}}", {
174
+ items: N.items.length,
175
+ collections: N.collections.length,
176
+ source: N.sourceUrl ?? O[N.provider].label
177
+ })
178
+ })] }), /* @__PURE__ */ b("button", {
179
+ type: "button",
180
+ onClick: $,
181
+ disabled: A.isPending,
182
+ className: "text-sm font-medium text-text-secondary hover:text-text-primary disabled:opacity-50",
183
+ children: d("coolandlovely.import.changeSource", "Change source")
184
+ })]
185
+ }),
186
+ (N.truncated || N.warnings.length > 0) && /* @__PURE__ */ x("ul", {
187
+ className: "mt-4 space-y-1 rounded-lg bg-status-warning-bg-subtle px-3 py-2 text-sm text-status-warning-text",
188
+ children: [N.truncated && /* @__PURE__ */ b("li", { children: d("coolandlovely.import.truncated", "The store has more products than were fetched. Import these, then run another import for the rest.") }), N.warnings.map((e) => /* @__PURE__ */ b("li", { children: e }, e))]
189
+ }),
190
+ /* @__PURE__ */ x("div", {
191
+ className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
192
+ children: [/* @__PURE__ */ b(s, {
193
+ id: "brandName",
194
+ label: d("coolandlovely.import.brandName", "Brand name"),
195
+ value: F,
196
+ onChange: I,
197
+ required: !0
198
+ }), /* @__PURE__ */ b(s, {
199
+ id: "brandHandle",
200
+ label: d("coolandlovely.import.brandHandle", "Handle"),
201
+ value: L,
202
+ onChange: (e) => R(e.toLowerCase()),
203
+ hint: d("coolandlovely.import.brandHandleHint", "Unique per workspace"),
204
+ error: Y,
205
+ required: !0
206
+ })]
207
+ }),
208
+ /* @__PURE__ */ b(c, {
209
+ id: "copyImages",
210
+ label: d("coolandlovely.import.copyImages", "Copy product images into Files"),
211
+ checked: V,
212
+ onChange: H,
213
+ hint: d("coolandlovely.import.copyImagesHint", "Images are stored in your workspace Files. Images the platform will not share are skipped."),
214
+ className: "mt-4"
215
+ })
216
+ ]
217
+ }),
218
+ /* @__PURE__ */ x(S, {
219
+ treatment: "plain",
220
+ className: "p-0",
221
+ children: [/* @__PURE__ */ x("div", {
222
+ className: "flex items-center justify-between border-b border-border-seam px-4 py-3 text-sm",
223
+ children: [/* @__PURE__ */ b("span", {
224
+ className: "text-text-secondary",
225
+ children: d("coolandlovely.import.selected", "{{count}} of {{total}} selected", {
226
+ count: z.size,
227
+ total: N.items.length
228
+ })
229
+ }), /* @__PURE__ */ x("div", {
230
+ className: "flex gap-3",
231
+ children: [/* @__PURE__ */ b("button", {
232
+ type: "button",
233
+ onClick: () => B(new Set(N.items.map((e) => e.externalId))),
234
+ className: "font-medium text-text-secondary hover:text-text-primary",
235
+ children: d("coolandlovely.import.selectAll", "Select all")
236
+ }), /* @__PURE__ */ b("button", {
237
+ type: "button",
238
+ onClick: () => B(/* @__PURE__ */ new Set()),
239
+ className: "font-medium text-text-secondary hover:text-text-primary",
240
+ children: d("coolandlovely.import.selectNone", "Select none")
241
+ })]
242
+ })]
243
+ }), /* @__PURE__ */ b("ul", {
244
+ className: "max-h-[28rem] divide-y divide-border-seam overflow-y-auto",
245
+ children: N.items.map((e) => {
246
+ let n = z.has(e.externalId);
247
+ return /* @__PURE__ */ b("li", { children: /* @__PURE__ */ x("label", {
248
+ className: t("flex cursor-pointer items-center gap-3 px-4 py-3 transition-colors hover:bg-bg-sunken", !n && "opacity-60"),
249
+ children: [
250
+ /* @__PURE__ */ b("input", {
251
+ type: "checkbox",
252
+ checked: n,
253
+ onChange: () => te(e.externalId),
254
+ className: "h-4 w-4 accent-[var(--color-action-primary-bg)]",
255
+ "aria-label": e.name
256
+ }),
257
+ e.imageUrl ? /* @__PURE__ */ b("img", {
258
+ src: e.imageUrl,
259
+ alt: "",
260
+ loading: "lazy",
261
+ referrerPolicy: "no-referrer",
262
+ className: "h-12 w-12 flex-none rounded-md bg-bg-sunken object-cover"
263
+ }) : /* @__PURE__ */ b("span", {
264
+ className: "flex h-12 w-12 flex-none items-center justify-center rounded-md bg-bg-sunken text-text-secondary",
265
+ children: /* @__PURE__ */ b(g, { className: "h-4 w-4" })
266
+ }),
267
+ /* @__PURE__ */ x("span", {
268
+ className: "min-w-0 flex-1",
269
+ children: [/* @__PURE__ */ b("span", {
270
+ className: "block truncate text-sm font-medium text-text-primary",
271
+ children: e.name
272
+ }), /* @__PURE__ */ b("span", {
273
+ className: "block truncate text-xs text-text-secondary",
274
+ children: [
275
+ a(e.category),
276
+ e.collectionName,
277
+ e.sizes.length ? e.sizes.join(" / ") : null
278
+ ].filter(Boolean).join(" · ")
279
+ })]
280
+ }),
281
+ /* @__PURE__ */ b("span", {
282
+ className: "flex-none text-sm font-medium text-text-primary",
283
+ children: i(e.price, e.currency)
284
+ })
285
+ ]
286
+ }) }, e.externalId);
287
+ })
288
+ })]
289
+ }),
290
+ A.isError && /* @__PURE__ */ b("p", {
291
+ role: "alert",
292
+ className: "rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
293
+ children: A.error instanceof Error ? A.error.message : d("coolandlovely.errors.generic", "Something went wrong")
294
+ }),
295
+ /* @__PURE__ */ x("div", {
296
+ className: "flex flex-wrap items-center justify-end gap-3",
297
+ children: [U && /* @__PURE__ */ b("span", {
298
+ className: "text-sm text-text-secondary",
299
+ "aria-live": "polite",
300
+ children: U.phase === "images" ? d("coolandlovely.import.progressImages", "Copying images {{done}} / {{total}}…", {
301
+ done: U.done,
302
+ total: U.total
303
+ }) : d("coolandlovely.import.progressSaving", "Creating your storefront…")
304
+ }), /* @__PURE__ */ x("button", {
305
+ type: "button",
306
+ onClick: ne,
307
+ disabled: A.isPending || z.size === 0 || !F.trim() || !L || !!Y,
308
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50",
309
+ children: [A.isPending ? /* @__PURE__ */ b(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ b(h, { className: "h-4 w-4" }), d("coolandlovely.import.importCount", "Import {{count}} items", { count: z.size })]
310
+ })]
311
+ })
312
+ ]
313
+ }) : /* @__PURE__ */ b("form", {
314
+ onSubmit: Q,
315
+ className: "mx-auto max-w-2xl",
316
+ "data-testid": "catalog-import-connect",
317
+ children: /* @__PURE__ */ x(S, {
318
+ treatment: "plain",
319
+ className: "p-6",
320
+ children: [
321
+ /* @__PURE__ */ x("div", {
322
+ className: "mb-6 flex items-center gap-3",
323
+ children: [/* @__PURE__ */ b("div", {
324
+ className: "flex h-11 w-11 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary",
325
+ children: /* @__PURE__ */ b(v, { className: "h-5 w-5" })
326
+ }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
327
+ className: "text-base font-semibold text-text-primary",
328
+ children: d("coolandlovely.import.connectTitle", "Connect your store")
329
+ }), /* @__PURE__ */ b("p", {
330
+ className: "text-xs text-text-secondary",
331
+ children: d("coolandlovely.import.connectHint", "Credentials are used once to read your catalog and are never stored.")
332
+ })] })]
333
+ }),
334
+ /* @__PURE__ */ x("div", {
335
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
336
+ children: [/* @__PURE__ */ b(o, {
337
+ id: "provider",
338
+ label: d("coolandlovely.import.platform", "Platform"),
339
+ value: j.provider,
340
+ onChange: (e) => X("provider", e),
341
+ options: Object.keys(O).map((e) => ({
342
+ value: e,
343
+ label: O[e].label
344
+ }))
345
+ }), /* @__PURE__ */ b(s, {
346
+ id: "maxItems",
347
+ label: d("coolandlovely.import.maxItems", "Most products to fetch"),
348
+ type: "number",
349
+ value: j.maxItems,
350
+ onChange: (e) => X("maxItems", e),
351
+ hint: d("coolandlovely.import.maxItemsHint", "Up to 500 per import")
352
+ })]
353
+ }),
354
+ /* @__PURE__ */ b(s, {
355
+ id: "storeIdentifier",
356
+ label: q.storeLabel,
357
+ value: j.storeIdentifier,
358
+ onChange: (e) => X("storeIdentifier", e),
359
+ placeholder: q.storePlaceholder,
360
+ hint: q.storeHint,
361
+ required: !0,
362
+ className: "mt-4"
363
+ }),
364
+ /* @__PURE__ */ b(s, {
365
+ id: "accessToken",
366
+ label: q.tokenLabel,
367
+ type: "password",
368
+ autoComplete: "off",
369
+ value: j.accessToken,
370
+ onChange: (e) => X("accessToken", e),
371
+ hint: q.tokenHint,
372
+ required: !J,
373
+ className: "mt-4"
374
+ }),
375
+ q.secretLabel && /* @__PURE__ */ b(s, {
376
+ id: "accessSecret",
377
+ label: q.secretLabel,
378
+ type: "password",
379
+ autoComplete: "off",
380
+ value: j.accessSecret,
381
+ onChange: (e) => X("accessSecret", e),
382
+ required: !0,
383
+ className: "mt-4"
384
+ }),
385
+ k.isError && /* @__PURE__ */ b("p", {
386
+ role: "alert",
387
+ className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
388
+ children: k.error instanceof Error ? k.error.message : d("coolandlovely.errors.generic", "Something went wrong")
389
+ }),
390
+ /* @__PURE__ */ x("div", {
391
+ className: "mt-6 flex flex-wrap justify-between gap-2",
392
+ children: [/* @__PURE__ */ x("button", {
393
+ type: "button",
394
+ onClick: ee,
395
+ disabled: k.isPending,
396
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50",
397
+ children: [
398
+ /* @__PURE__ */ b(y, { className: "h-4 w-4" }),
399
+ " ",
400
+ d("coolandlovely.import.trySandbox", "Try the Shopify sample store")
401
+ ]
402
+ }), /* @__PURE__ */ x("button", {
403
+ type: "submit",
404
+ disabled: k.isPending || !j.storeIdentifier.trim(),
405
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50",
406
+ children: [k.isPending ? /* @__PURE__ */ b(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ b(h, { className: "h-4 w-4" }), d("coolandlovely.import.fetch", "Fetch catalog")]
407
+ })]
408
+ })
409
+ ]
410
+ })
411
+ })
412
+ });
413
+ }
414
+ //#endregion
415
+ export { O as ImportCatalogPage };
416
+
417
+ //# sourceMappingURL=ImportCatalogPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImportCatalogPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/ImportCatalogPage.tsx"],"sourcesContent":["/**\n * Import Catalog Page (BOFF-3384)\n * @module coolandlovely/pages/brands\n *\n * Lets a brand bring its existing catalog over from Shopify, WooCommerce or\n * BigCommerce in three steps:\n *\n * 1. Connect — pick the platform and paste read-only API credentials. They\n * are sent once with `cooPreviewExternalCatalog` and never stored.\n * 2. Review — the catalog comes back mapped onto a brand, collections and\n * style items. The merchant confirms the brand name/handle and unticks\n * anything they do not want.\n * 3. Import — product images are copied into wspace-files and everything is\n * created in one transaction with `cooBulkImportCatalog`.\n */\n\nimport { type FormEvent, useMemo, useState } from 'react';\nimport {\n ArrowLeft,\n CheckCircle2,\n Download,\n ImageOff,\n Loader2,\n PackageOpen,\n Sparkles,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { SelectField, TextField, ToggleField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useTr } from '../../hooks';\nimport {\n type CatalogImportProgress,\n type ImportExternalCatalogResult,\n useImportExternalCatalog,\n usePreviewExternalCatalog,\n} from '../../hooks/useCatalogImport';\nimport type { CooCatalogProvider, CooExternalCatalogPreview } from '../../types';\nimport { cn } from '../../utils/cn';\nimport { formatPrice, titleCase } from '../../utils/formatters';\n\nconst SHOPIFY_SANDBOX = 'mock.shop';\nconst DEFAULT_MAX_ITEMS = '100';\nconst HANDLE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\ninterface ProviderCopy {\n label: string;\n storeLabel: string;\n storePlaceholder: string;\n storeHint: string;\n tokenLabel: string;\n tokenHint: string;\n secretLabel?: string;\n}\n\nfunction useProviderCopy(): Record<CooCatalogProvider, ProviderCopy> {\n const tr = useTr();\n return useMemo(\n () => ({\n SHOPIFY: {\n label: 'Shopify',\n storeLabel: tr('coolandlovely.import.shopify.store', 'Shop domain'),\n storePlaceholder: 'your-brand.myshopify.com',\n storeHint: tr(\n 'coolandlovely.import.shopify.storeHint',\n 'Your permanent myshopify.com domain. Use mock.shop to try the Shopify sample store.'\n ),\n tokenLabel: tr('coolandlovely.import.shopify.token', 'Storefront API access token'),\n tokenHint: tr(\n 'coolandlovely.import.shopify.tokenHint',\n 'Shopify admin → Settings → Apps → Develop apps → Storefront API. Not needed for mock.shop.'\n ),\n },\n WOOCOMMERCE: {\n label: 'WooCommerce',\n storeLabel: tr('coolandlovely.import.woo.store', 'Store address'),\n storePlaceholder: 'https://shop.your-brand.com',\n storeHint: tr(\n 'coolandlovely.import.woo.storeHint',\n 'The HTTPS address of your WordPress store.'\n ),\n tokenLabel: tr('coolandlovely.import.woo.token', 'Consumer key'),\n tokenHint: tr(\n 'coolandlovely.import.woo.tokenHint',\n 'WooCommerce → Settings → Advanced → REST API → Add key, with Read permission.'\n ),\n secretLabel: tr('coolandlovely.import.woo.secret', 'Consumer secret'),\n },\n BIGCOMMERCE: {\n label: 'BigCommerce',\n storeLabel: tr('coolandlovely.import.bc.store', 'Store hash'),\n storePlaceholder: 'abc123def',\n storeHint: tr(\n 'coolandlovely.import.bc.storeHint',\n 'The code after /stores/ in your API path (or paste the API path itself).'\n ),\n tokenLabel: tr('coolandlovely.import.bc.token', 'API account access token'),\n tokenHint: tr(\n 'coolandlovely.import.bc.tokenHint',\n 'Settings → Store-level API accounts → Create, with read-only Products and Information scopes.'\n ),\n },\n }),\n [tr]\n );\n}\n\ninterface SourceForm {\n provider: CooCatalogProvider;\n storeIdentifier: string;\n accessToken: string;\n accessSecret: string;\n maxItems: string;\n}\n\nconst EMPTY_SOURCE: SourceForm = {\n provider: 'SHOPIFY',\n storeIdentifier: '',\n accessToken: '',\n accessSecret: '',\n maxItems: DEFAULT_MAX_ITEMS,\n};\n\nexport function ImportCatalogPage() {\n const tr = useTr();\n const { navigateTo } = useCoolAndLovely();\n const copy = useProviderCopy();\n const previewMutation = usePreviewExternalCatalog();\n const importMutation = useImportExternalCatalog();\n\n const [source, setSource] = useState<SourceForm>(EMPTY_SOURCE);\n const [preview, setPreview] = useState<CooExternalCatalogPreview | null>(null);\n const [brandName, setBrandName] = useState('');\n const [brandHandle, setBrandHandle] = useState('');\n const [selected, setSelected] = useState<Set<string>>(new Set());\n const [copyImages, setCopyImages] = useState(true);\n const [progress, setProgress] = useState<CatalogImportProgress | null>(null);\n const [result, setResult] = useState<ImportExternalCatalogResult | null>(null);\n\n const providerCopy = copy[source.provider];\n const isSandbox =\n source.provider === 'SHOPIFY' &&\n source.storeIdentifier.trim().toLowerCase() === SHOPIFY_SANDBOX;\n const handleError =\n brandHandle && !HANDLE_PATTERN.test(brandHandle)\n ? tr(\n 'coolandlovely.import.handleInvalid',\n 'Use lowercase letters, numbers and single hyphens'\n )\n : undefined;\n\n const setSourceField = <K extends keyof SourceForm>(key: K, value: SourceForm[K]) =>\n setSource((current) => ({ ...current, [key]: value }));\n\n const fetchPreview = (form: SourceForm) => {\n const maxItems = Number.parseInt(form.maxItems, 10);\n previewMutation.mutate(\n {\n provider: form.provider,\n storeIdentifier: form.storeIdentifier.trim(),\n ...(form.accessToken.trim() ? { accessToken: form.accessToken.trim() } : {}),\n ...(form.accessSecret.trim() ? { accessSecret: form.accessSecret.trim() } : {}),\n ...(Number.isFinite(maxItems) ? { maxItems } : {}),\n },\n {\n onSuccess: (data) => {\n setPreview(data);\n setBrandName(data.brand.name);\n setBrandHandle(data.brand.handle);\n setSelected(new Set(data.items.map((item) => item.externalId)));\n // Credentials are only needed for the preview: drop them from memory.\n setSource((current) => ({ ...current, accessToken: '', accessSecret: '' }));\n },\n }\n );\n };\n\n const handleConnect = (event: FormEvent) => {\n event.preventDefault();\n fetchPreview(source);\n };\n\n const trySandbox = () => {\n const sandbox: SourceForm = {\n ...EMPTY_SOURCE,\n storeIdentifier: SHOPIFY_SANDBOX,\n maxItems: '20',\n };\n setSource(sandbox);\n fetchPreview(sandbox);\n };\n\n const toggleItem = (id: string) =>\n setSelected((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n\n const startImport = () => {\n if (!preview || selected.size === 0 || !brandName.trim() || !brandHandle || handleError) return;\n importMutation.mutate(\n {\n preview,\n brandName: brandName.trim(),\n brandHandle,\n selectedIds: selected,\n copyImages,\n onProgress: setProgress,\n },\n { onSuccess: setResult, onSettled: () => setProgress(null) }\n );\n };\n\n const reset = () => {\n setPreview(null);\n setResult(null);\n setSelected(new Set());\n previewMutation.reset();\n importMutation.reset();\n };\n\n return (\n <PageLayout\n title={tr('coolandlovely.import.title', 'Import catalog')}\n description={tr(\n 'coolandlovely.import.subtitle',\n 'Bring your existing storefront over from Shopify, WooCommerce or BigCommerce'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> {tr('coolandlovely.actions.backToBrands', 'Brands')}\n </button>\n }\n >\n {result ? (\n <div className=\"mx-auto max-w-2xl\" data-testid=\"catalog-import-result\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"flex items-start gap-3\">\n <CheckCircle2 className=\"mt-0.5 h-6 w-6 text-status-success-text\" />\n <div className=\"flex-1\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('coolandlovely.import.doneTitle', 'Imported {{name}}', {\n name: result.brand.name,\n })}\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n 'coolandlovely.import.doneSummary',\n '{{items}} style items in {{collections}} collections. The storefront starts as a draft.',\n { items: result.itemsCreated, collections: result.collectionsCreated }\n )}\n </p>\n {copyImages && (\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n 'coolandlovely.import.doneImages',\n '{{copied}} images copied to Files, {{failed}} could not be copied.',\n {\n copied: result.imagesCopied,\n failed: result.imagesFailed,\n }\n )}\n </p>\n )}\n <div className=\"mt-4 flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/brands/${result.brand.id}`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n {tr('coolandlovely.import.openBrand', 'Open storefront')}\n </button>\n <button\n type=\"button\"\n onClick={reset}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n {tr('coolandlovely.import.another', 'Import another catalog')}\n </button>\n </div>\n </div>\n </div>\n </GlassCard>\n </div>\n ) : !preview ? (\n <form\n onSubmit={handleConnect}\n className=\"mx-auto max-w-2xl\"\n data-testid=\"catalog-import-connect\"\n >\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <PackageOpen className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('coolandlovely.import.connectTitle', 'Connect your store')}\n </h2>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'coolandlovely.import.connectHint',\n 'Credentials are used once to read your catalog and are never stored.'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <SelectField\n id=\"provider\"\n label={tr('coolandlovely.import.platform', 'Platform')}\n value={source.provider}\n onChange={(value) => setSourceField('provider', value as CooCatalogProvider)}\n options={(Object.keys(copy) as CooCatalogProvider[]).map((value) => ({\n value,\n label: copy[value].label,\n }))}\n />\n <TextField\n id=\"maxItems\"\n label={tr('coolandlovely.import.maxItems', 'Most products to fetch')}\n type=\"number\"\n value={source.maxItems}\n onChange={(value) => setSourceField('maxItems', value)}\n hint={tr('coolandlovely.import.maxItemsHint', 'Up to 500 per import')}\n />\n </div>\n\n <TextField\n id=\"storeIdentifier\"\n label={providerCopy.storeLabel}\n value={source.storeIdentifier}\n onChange={(value) => setSourceField('storeIdentifier', value)}\n placeholder={providerCopy.storePlaceholder}\n hint={providerCopy.storeHint}\n required\n className=\"mt-4\"\n />\n <TextField\n id=\"accessToken\"\n label={providerCopy.tokenLabel}\n type=\"password\"\n autoComplete=\"off\"\n value={source.accessToken}\n onChange={(value) => setSourceField('accessToken', value)}\n hint={providerCopy.tokenHint}\n required={!isSandbox}\n className=\"mt-4\"\n />\n {providerCopy.secretLabel && (\n <TextField\n id=\"accessSecret\"\n label={providerCopy.secretLabel}\n type=\"password\"\n autoComplete=\"off\"\n value={source.accessSecret}\n onChange={(value) => setSourceField('accessSecret', value)}\n required\n className=\"mt-4\"\n />\n )}\n\n {previewMutation.isError && (\n <p\n role=\"alert\"\n className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {previewMutation.error instanceof Error\n ? previewMutation.error.message\n : tr('coolandlovely.errors.generic', 'Something went wrong')}\n </p>\n )}\n\n <div className=\"mt-6 flex flex-wrap justify-between gap-2\">\n <button\n type=\"button\"\n onClick={trySandbox}\n disabled={previewMutation.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Sparkles className=\"h-4 w-4\" />{' '}\n {tr('coolandlovely.import.trySandbox', 'Try the Shopify sample store')}\n </button>\n <button\n type=\"submit\"\n disabled={previewMutation.isPending || !source.storeIdentifier.trim()}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {previewMutation.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Download className=\"h-4 w-4\" />\n )}\n {tr('coolandlovely.import.fetch', 'Fetch catalog')}\n </button>\n </div>\n </GlassCard>\n </form>\n ) : (\n <div className=\"mx-auto max-w-5xl space-y-4\" data-testid=\"catalog-import-review\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n {tr('coolandlovely.import.reviewTitle', 'Review the {{provider}} catalog', {\n provider: copy[preview.provider].label,\n })}\n </h2>\n <p className=\"text-xs text-text-secondary\">\n {tr(\n 'coolandlovely.import.reviewSummary',\n '{{items}} products in {{collections}} collections from {{source}}',\n {\n items: preview.items.length,\n collections: preview.collections.length,\n source: preview.sourceUrl ?? copy[preview.provider].label,\n }\n )}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={reset}\n disabled={importMutation.isPending}\n className=\"text-sm font-medium text-text-secondary hover:text-text-primary disabled:opacity-50\"\n >\n {tr('coolandlovely.import.changeSource', 'Change source')}\n </button>\n </div>\n\n {(preview.truncated || preview.warnings.length > 0) && (\n <ul className=\"mt-4 space-y-1 rounded-lg bg-status-warning-bg-subtle px-3 py-2 text-sm text-status-warning-text\">\n {preview.truncated && (\n <li>\n {tr(\n 'coolandlovely.import.truncated',\n 'The store has more products than were fetched. Import these, then run another import for the rest.'\n )}\n </li>\n )}\n {preview.warnings.map((warning) => (\n <li key={warning}>{warning}</li>\n ))}\n </ul>\n )}\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"brandName\"\n label={tr('coolandlovely.import.brandName', 'Brand name')}\n value={brandName}\n onChange={setBrandName}\n required\n />\n <TextField\n id=\"brandHandle\"\n label={tr('coolandlovely.import.brandHandle', 'Handle')}\n value={brandHandle}\n onChange={(value) => setBrandHandle(value.toLowerCase())}\n hint={tr('coolandlovely.import.brandHandleHint', 'Unique per workspace')}\n error={handleError}\n required\n />\n </div>\n <ToggleField\n id=\"copyImages\"\n label={tr('coolandlovely.import.copyImages', 'Copy product images into Files')}\n checked={copyImages}\n onChange={setCopyImages}\n hint={tr(\n 'coolandlovely.import.copyImagesHint',\n 'Images are stored in your workspace Files. Images the platform will not share are skipped.'\n )}\n className=\"mt-4\"\n />\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-0\">\n <div className=\"flex items-center justify-between border-b border-border-seam px-4 py-3 text-sm\">\n <span className=\"text-text-secondary\">\n {tr('coolandlovely.import.selected', '{{count}} of {{total}} selected', {\n count: selected.size,\n total: preview.items.length,\n })}\n </span>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={() => setSelected(new Set(preview.items.map((item) => item.externalId)))}\n className=\"font-medium text-text-secondary hover:text-text-primary\"\n >\n {tr('coolandlovely.import.selectAll', 'Select all')}\n </button>\n <button\n type=\"button\"\n onClick={() => setSelected(new Set())}\n className=\"font-medium text-text-secondary hover:text-text-primary\"\n >\n {tr('coolandlovely.import.selectNone', 'Select none')}\n </button>\n </div>\n </div>\n <ul className=\"max-h-[28rem] divide-y divide-border-seam overflow-y-auto\">\n {preview.items.map((item) => {\n const checked = selected.has(item.externalId);\n return (\n <li key={item.externalId}>\n <label\n className={cn(\n 'flex cursor-pointer items-center gap-3 px-4 py-3 transition-colors hover:bg-bg-sunken',\n !checked && 'opacity-60'\n )}\n >\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={() => toggleItem(item.externalId)}\n className=\"h-4 w-4 accent-[var(--color-action-primary-bg)]\"\n aria-label={item.name}\n />\n {item.imageUrl ? (\n <img\n src={item.imageUrl}\n alt=\"\"\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n className=\"h-12 w-12 flex-none rounded-md bg-bg-sunken object-cover\"\n />\n ) : (\n <span className=\"flex h-12 w-12 flex-none items-center justify-center rounded-md bg-bg-sunken text-text-secondary\">\n <ImageOff className=\"h-4 w-4\" />\n </span>\n )}\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-sm font-medium text-text-primary\">\n {item.name}\n </span>\n <span className=\"block truncate text-xs text-text-secondary\">\n {[\n titleCase(item.category),\n item.collectionName,\n item.sizes.length ? item.sizes.join(' / ') : null,\n ]\n .filter(Boolean)\n .join(' · ')}\n </span>\n </span>\n <span className=\"flex-none text-sm font-medium text-text-primary\">\n {formatPrice(item.price, item.currency)}\n </span>\n </label>\n </li>\n );\n })}\n </ul>\n </GlassCard>\n\n {importMutation.isError && (\n <p\n role=\"alert\"\n className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {importMutation.error instanceof Error\n ? importMutation.error.message\n : tr('coolandlovely.errors.generic', 'Something went wrong')}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center justify-end gap-3\">\n {progress && (\n <span className=\"text-sm text-text-secondary\" aria-live=\"polite\">\n {progress.phase === 'images'\n ? tr(\n 'coolandlovely.import.progressImages',\n 'Copying images {{done}} / {{total}}…',\n {\n done: progress.done,\n total: progress.total,\n }\n )\n : tr('coolandlovely.import.progressSaving', 'Creating your storefront…')}\n </span>\n )}\n <button\n type=\"button\"\n onClick={startImport}\n disabled={\n importMutation.isPending ||\n selected.size === 0 ||\n !brandName.trim() ||\n !brandHandle ||\n Boolean(handleError)\n }\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {importMutation.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Download className=\"h-4 w-4\" />\n )}\n {tr('coolandlovely.import.importCount', 'Import {{count}} items', {\n count: selected.size,\n })}\n </button>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -537,5 +537,85 @@ export interface DashboardStats {
537
537
  activeCollaborations: number;
538
538
  wardrobeItems: number;
539
539
  }
540
+ export type CooCatalogProvider = 'SHOPIFY' | 'WOOCOMMERCE' | 'BIGCOMMERCE';
541
+ export interface CooExternalCatalogSourceInput {
542
+ provider: CooCatalogProvider;
543
+ storeIdentifier: string;
544
+ accessToken?: string;
545
+ accessSecret?: string;
546
+ maxItems?: number;
547
+ }
548
+ export interface CooExternalBrandPreview {
549
+ name: string;
550
+ handle: string;
551
+ tagline: string | null;
552
+ bio: string | null;
553
+ websiteUrl: string | null;
554
+ logoUrl: string | null;
555
+ tags: string[];
556
+ }
557
+ export interface CooExternalCollectionPreview {
558
+ name: string;
559
+ description: string | null;
560
+ tags: string[];
561
+ }
562
+ export interface CooExternalCatalogItemPreview {
563
+ externalId: string;
564
+ collectionName: string | null;
565
+ name: string;
566
+ description: string | null;
567
+ category: CooStyleCategory;
568
+ sku: string | null;
569
+ price: number;
570
+ currency: string;
571
+ colours: string[];
572
+ sizes: string[];
573
+ materials: string[];
574
+ tags: string[];
575
+ imageUrl: string | null;
576
+ sourceUrl: string | null;
577
+ }
578
+ export interface CooExternalCatalogPreview {
579
+ provider: CooCatalogProvider;
580
+ sourceUrl: string | null;
581
+ brand: CooExternalBrandPreview;
582
+ collections: CooExternalCollectionPreview[];
583
+ items: CooExternalCatalogItemPreview[];
584
+ truncated: boolean;
585
+ warnings: string[];
586
+ }
587
+ export interface CooBulkImportCatalogItemInput {
588
+ collectionName: string;
589
+ name: string;
590
+ description?: string;
591
+ category?: CooStyleCategory;
592
+ sku?: string;
593
+ imageFileId?: string;
594
+ price?: number;
595
+ currency?: string;
596
+ colours?: string[];
597
+ sizes?: string[];
598
+ materials?: string[];
599
+ tags?: string[];
600
+ }
601
+ export interface CooBulkImportCatalogCollectionInput {
602
+ name: string;
603
+ description?: string;
604
+ tags?: string[];
605
+ }
606
+ export interface CooBulkImportCatalogInput {
607
+ brand: CooCreateBrandInput;
608
+ collections?: CooBulkImportCatalogCollectionInput[];
609
+ items: CooBulkImportCatalogItemInput[];
610
+ }
611
+ export interface CooBulkCatalogImportResult {
612
+ brand: {
613
+ id: string;
614
+ name: string;
615
+ handle: string;
616
+ };
617
+ collectionsCreated: number;
618
+ itemsCreated: number;
619
+ }
540
620
  export * from './commerce';
541
621
  //# sourceMappingURL=index.d.ts.map