@empty-complete-org/medusa-product-attributes 1.1.0 → 1.1.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.
|
@@ -61,6 +61,8 @@ const no$1 = "Нет";
|
|
|
61
61
|
const upload$1 = "Загрузить";
|
|
62
62
|
const uploadError$1 = "Ошибка загрузки файла";
|
|
63
63
|
const fileUrl$1 = "URL файла";
|
|
64
|
+
const applyTemplateError$1 = "Ошибка при применении шаблона";
|
|
65
|
+
const createError$1 = "Ошибка при создании атрибута";
|
|
64
66
|
const templates$1 = "Шаблоны атрибутов";
|
|
65
67
|
const templatesDesc$1 = "Заготовки атрибутов, которые можно применить к любой категории.";
|
|
66
68
|
const globals$1 = "Глобальные атрибуты";
|
|
@@ -100,6 +102,8 @@ const ru = {
|
|
|
100
102
|
upload: upload$1,
|
|
101
103
|
uploadError: uploadError$1,
|
|
102
104
|
fileUrl: fileUrl$1,
|
|
105
|
+
applyTemplateError: applyTemplateError$1,
|
|
106
|
+
createError: createError$1,
|
|
103
107
|
templates: templates$1,
|
|
104
108
|
templatesDesc: templatesDesc$1,
|
|
105
109
|
globals: globals$1,
|
|
@@ -134,6 +138,8 @@ const no = "No";
|
|
|
134
138
|
const upload = "Upload";
|
|
135
139
|
const uploadError = "Upload failed";
|
|
136
140
|
const fileUrl = "File URL";
|
|
141
|
+
const applyTemplateError = "Failed to apply template";
|
|
142
|
+
const createError = "Failed to create attribute";
|
|
137
143
|
const templates = "Attribute Templates";
|
|
138
144
|
const templatesDesc = "Reusable attribute blueprints. Apply to any category in one click.";
|
|
139
145
|
const globals = "Global Attributes";
|
|
@@ -173,6 +179,8 @@ const en = {
|
|
|
173
179
|
upload,
|
|
174
180
|
uploadError,
|
|
175
181
|
fileUrl,
|
|
182
|
+
applyTemplateError,
|
|
183
|
+
createError,
|
|
176
184
|
templates,
|
|
177
185
|
templatesDesc,
|
|
178
186
|
globals,
|
|
@@ -181,7 +189,7 @@ const en = {
|
|
|
181
189
|
const locales = { ru, en };
|
|
182
190
|
function detectLang() {
|
|
183
191
|
var _a, _b, _c;
|
|
184
|
-
if (typeof window === "undefined") return "
|
|
192
|
+
if (typeof window === "undefined") return "en";
|
|
185
193
|
const stored = window.localStorage.getItem("i18nextLng");
|
|
186
194
|
if (stored) {
|
|
187
195
|
const short = stored.slice(0, 2).toLowerCase();
|
|
@@ -189,8 +197,8 @@ function detectLang() {
|
|
|
189
197
|
}
|
|
190
198
|
const htmlLang = (_b = (_a = document.documentElement) == null ? void 0 : _a.lang) == null ? void 0 : _b.slice(0, 2).toLowerCase();
|
|
191
199
|
if (htmlLang && locales[htmlLang]) return htmlLang;
|
|
192
|
-
const nav = (((_c = window.navigator) == null ? void 0 : _c.language) || "
|
|
193
|
-
return locales[nav] ? nav : "
|
|
200
|
+
const nav = (((_c = window.navigator) == null ? void 0 : _c.language) || "en").slice(0, 2).toLowerCase();
|
|
201
|
+
return locales[nav] ? nav : "en";
|
|
194
202
|
}
|
|
195
203
|
function useT() {
|
|
196
204
|
const dict = React.useMemo(() => {
|
|
@@ -228,7 +236,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
228
236
|
setShowTemplateList(false);
|
|
229
237
|
},
|
|
230
238
|
onError: (err) => {
|
|
231
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
239
|
+
setMutationError((err == null ? void 0 : err.message) || t("applyTemplateError"));
|
|
232
240
|
}
|
|
233
241
|
});
|
|
234
242
|
const {
|
|
@@ -254,7 +262,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
254
262
|
setMutationError(null);
|
|
255
263
|
},
|
|
256
264
|
onError: (err) => {
|
|
257
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
265
|
+
setMutationError((err == null ? void 0 : err.message) || t("createError"));
|
|
258
266
|
}
|
|
259
267
|
});
|
|
260
268
|
const deleteMutation = reactQuery.useMutation({
|
|
@@ -302,19 +310,19 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
302
310
|
] }),
|
|
303
311
|
showTemplateList && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-3", children: [
|
|
304
312
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
305
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "
|
|
313
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: t("chooseTemplate") }),
|
|
306
314
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
307
315
|
ui.Button,
|
|
308
316
|
{
|
|
309
317
|
size: "small",
|
|
310
318
|
variant: "secondary",
|
|
311
319
|
onClick: () => setShowTemplateList(false),
|
|
312
|
-
children: "
|
|
320
|
+
children: t("close")
|
|
313
321
|
}
|
|
314
322
|
)
|
|
315
323
|
] }),
|
|
316
324
|
templatesQuery.isLoading && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }),
|
|
317
|
-
((_a = templatesQuery.data) == null ? void 0 : _a.attribute_templates.length) === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
325
|
+
((_a = templatesQuery.data) == null ? void 0 : _a.attribute_templates.length) === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("noTemplates") }),
|
|
318
326
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: (_b = templatesQuery.data) == null ? void 0 : _b.attribute_templates.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
319
327
|
"button",
|
|
320
328
|
{
|
|
@@ -333,9 +341,9 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
333
341
|
)) })
|
|
334
342
|
] }),
|
|
335
343
|
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
336
|
-
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "
|
|
344
|
+
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: t("loadFailed") }) }),
|
|
337
345
|
inheritedAttributes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
338
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: "
|
|
346
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: t("inherited") }) }),
|
|
339
347
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: inheritedAttributes.map((attr) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
340
348
|
"div",
|
|
341
349
|
{
|
|
@@ -352,18 +360,14 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
352
360
|
)) })
|
|
353
361
|
] }),
|
|
354
362
|
ownAttributes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
355
|
-
inheritedAttributes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: "
|
|
363
|
+
inheritedAttributes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: t("own") }) }),
|
|
356
364
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: ownAttributes.map(
|
|
357
365
|
(attr) => confirmDeleteId === attr.id ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
358
366
|
"div",
|
|
359
367
|
{
|
|
360
368
|
className: "flex items-center gap-3 px-6 py-3 text-sm",
|
|
361
369
|
children: [
|
|
362
|
-
/* @__PURE__ */ jsxRuntime.
|
|
363
|
-
"Удалить «",
|
|
364
|
-
attr.label,
|
|
365
|
-
"»?"
|
|
366
|
-
] }),
|
|
370
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-ui-fg-base", children: t("confirmDelete").replace("{name}", attr.label) }),
|
|
367
371
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
368
372
|
ui.Button,
|
|
369
373
|
{
|
|
@@ -371,7 +375,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
371
375
|
variant: "danger",
|
|
372
376
|
onClick: () => deleteMutation.mutate(attr.id),
|
|
373
377
|
isLoading: deleteMutation.isPending,
|
|
374
|
-
children: "
|
|
378
|
+
children: t("delete")
|
|
375
379
|
}
|
|
376
380
|
),
|
|
377
381
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -380,7 +384,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
380
384
|
size: "small",
|
|
381
385
|
variant: "secondary",
|
|
382
386
|
onClick: () => setConfirmDeleteId(null),
|
|
383
|
-
children: "
|
|
387
|
+
children: t("cancel")
|
|
384
388
|
}
|
|
385
389
|
)
|
|
386
390
|
]
|
|
@@ -401,7 +405,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
401
405
|
{
|
|
402
406
|
onClick: () => setConfirmDeleteId(attr.id),
|
|
403
407
|
className: "text-xs text-ui-fg-error hover:underline",
|
|
404
|
-
children: "
|
|
408
|
+
children: t("delete")
|
|
405
409
|
}
|
|
406
410
|
)
|
|
407
411
|
]
|
|
@@ -410,14 +414,14 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
410
414
|
)
|
|
411
415
|
) })
|
|
412
416
|
] }),
|
|
413
|
-
!isLoading && !isError && attributes2.length === 0 && !showAddForm && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
417
|
+
!isLoading && !isError && attributes2.length === 0 && !showAddForm && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("noAttributes") }) }),
|
|
414
418
|
showAddForm && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-6 py-3", children: [
|
|
415
419
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
416
420
|
ui.Input,
|
|
417
421
|
{
|
|
418
422
|
value: addForm.label,
|
|
419
423
|
onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })),
|
|
420
|
-
placeholder: "
|
|
424
|
+
placeholder: t("name"),
|
|
421
425
|
className: "flex-1 h-8 text-sm",
|
|
422
426
|
autoFocus: true
|
|
423
427
|
}
|
|
@@ -433,10 +437,10 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
433
437
|
})),
|
|
434
438
|
className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
435
439
|
children: [
|
|
436
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: "
|
|
437
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "
|
|
438
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: "
|
|
439
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "
|
|
440
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: t("type.text") }),
|
|
441
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: t("type.number") }),
|
|
442
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: t("type.file") }),
|
|
443
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: t("type.boolean") })
|
|
440
444
|
]
|
|
441
445
|
}
|
|
442
446
|
),
|
|
@@ -445,7 +449,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
445
449
|
{
|
|
446
450
|
value: addForm.unit,
|
|
447
451
|
onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
|
|
448
|
-
placeholder: "
|
|
452
|
+
placeholder: t("unit"),
|
|
449
453
|
className: "w-28 h-8 text-sm"
|
|
450
454
|
}
|
|
451
455
|
),
|
|
@@ -455,7 +459,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
455
459
|
size: "small",
|
|
456
460
|
onClick: handleAdd,
|
|
457
461
|
isLoading: createMutation.isPending,
|
|
458
|
-
children: "
|
|
462
|
+
children: t("add")
|
|
459
463
|
}
|
|
460
464
|
),
|
|
461
465
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -468,7 +472,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
468
472
|
setAddForm(emptyForm$1());
|
|
469
473
|
setMutationError(null);
|
|
470
474
|
},
|
|
471
|
-
children: "
|
|
475
|
+
children: t("cancel")
|
|
472
476
|
}
|
|
473
477
|
)
|
|
474
478
|
] }),
|
|
@@ -484,6 +488,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
484
488
|
var _a;
|
|
485
489
|
const productId = data.id;
|
|
486
490
|
const productHandle = data.handle || productId;
|
|
491
|
+
const t = useT();
|
|
487
492
|
const qc = reactQuery.useQueryClient();
|
|
488
493
|
const [formValues, setFormValues] = React.useState({});
|
|
489
494
|
const [saveSuccess, setSaveSuccess] = React.useState(false);
|
|
@@ -558,7 +563,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
558
563
|
if (!response.ok) {
|
|
559
564
|
const text = await response.text();
|
|
560
565
|
console.error("Upload failed", response.status, text);
|
|
561
|
-
alert(
|
|
566
|
+
alert(`${t("uploadError")}: ${response.status}`);
|
|
562
567
|
return;
|
|
563
568
|
}
|
|
564
569
|
const res = await response.json();
|
|
@@ -567,11 +572,11 @@ const ProductAttributeValuesWidget = ({
|
|
|
567
572
|
setFormValues((prev) => ({ ...prev, [attrId]: url }));
|
|
568
573
|
} else {
|
|
569
574
|
console.error("No URL in upload response", res);
|
|
570
|
-
alert("
|
|
575
|
+
alert(t("uploadError"));
|
|
571
576
|
}
|
|
572
577
|
} catch (err) {
|
|
573
578
|
console.error("Upload error", err);
|
|
574
|
-
alert("
|
|
579
|
+
alert(t("uploadError"));
|
|
575
580
|
} finally {
|
|
576
581
|
setUploadingId(null);
|
|
577
582
|
}
|
|
@@ -580,10 +585,10 @@ const ProductAttributeValuesWidget = ({
|
|
|
580
585
|
const isError = attributesQuery.isError || valuesQuery.isError;
|
|
581
586
|
const attributes2 = ((_a = attributesQuery.data) == null ? void 0 : _a.attributes) ?? [];
|
|
582
587
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
583
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "
|
|
584
|
-
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
585
|
-
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "
|
|
586
|
-
!isLoading && !isError && attributes2.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
588
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("characteristics") }) }),
|
|
589
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
590
|
+
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: t("loadFailed") }) }),
|
|
591
|
+
!isLoading && !isError && attributes2.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("noAttributes") }) }),
|
|
587
592
|
!isLoading && !isError && attributes2.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
588
593
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: attributes2.map((attr) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
589
594
|
"div",
|
|
@@ -605,8 +610,8 @@ const ProductAttributeValuesWidget = ({
|
|
|
605
610
|
className: "h-8 flex-1 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
606
611
|
children: [
|
|
607
612
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "—" }),
|
|
608
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "true", children: "
|
|
609
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "false", children: "
|
|
613
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "true", children: t("yes") }),
|
|
614
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "false", children: t("no") })
|
|
610
615
|
]
|
|
611
616
|
}
|
|
612
617
|
) : attr.type === "file" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -633,7 +638,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
633
638
|
...prev,
|
|
634
639
|
[attr.id]: e.target.value
|
|
635
640
|
})),
|
|
636
|
-
placeholder: "
|
|
641
|
+
placeholder: t("fileUrl"),
|
|
637
642
|
className: "h-8 text-sm flex-1"
|
|
638
643
|
}
|
|
639
644
|
),
|
|
@@ -648,7 +653,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
648
653
|
return (_a2 = fileInputs.current[attr.id]) == null ? void 0 : _a2.click();
|
|
649
654
|
},
|
|
650
655
|
isLoading: uploadingId === attr.id,
|
|
651
|
-
children: "
|
|
656
|
+
children: t("upload")
|
|
652
657
|
}
|
|
653
658
|
)
|
|
654
659
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -660,7 +665,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
660
665
|
...prev,
|
|
661
666
|
[attr.id]: e.target.value
|
|
662
667
|
})),
|
|
663
|
-
placeholder: attr.type === "number" ? "0" : "
|
|
668
|
+
placeholder: attr.type === "number" ? "0" : t("value"),
|
|
664
669
|
className: "h-8 text-sm flex-1"
|
|
665
670
|
}
|
|
666
671
|
) })
|
|
@@ -674,7 +679,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
674
679
|
size: "small",
|
|
675
680
|
onClick: handleSave,
|
|
676
681
|
isLoading: saveMutation.isPending,
|
|
677
|
-
children: saveSuccess ? "
|
|
682
|
+
children: saveSuccess ? t("saved") : t("save")
|
|
678
683
|
}
|
|
679
684
|
) })
|
|
680
685
|
] })
|
|
@@ -41,6 +41,8 @@ const no$1 = "Нет";
|
|
|
41
41
|
const upload$1 = "Загрузить";
|
|
42
42
|
const uploadError$1 = "Ошибка загрузки файла";
|
|
43
43
|
const fileUrl$1 = "URL файла";
|
|
44
|
+
const applyTemplateError$1 = "Ошибка при применении шаблона";
|
|
45
|
+
const createError$1 = "Ошибка при создании атрибута";
|
|
44
46
|
const templates$1 = "Шаблоны атрибутов";
|
|
45
47
|
const templatesDesc$1 = "Заготовки атрибутов, которые можно применить к любой категории.";
|
|
46
48
|
const globals$1 = "Глобальные атрибуты";
|
|
@@ -80,6 +82,8 @@ const ru = {
|
|
|
80
82
|
upload: upload$1,
|
|
81
83
|
uploadError: uploadError$1,
|
|
82
84
|
fileUrl: fileUrl$1,
|
|
85
|
+
applyTemplateError: applyTemplateError$1,
|
|
86
|
+
createError: createError$1,
|
|
83
87
|
templates: templates$1,
|
|
84
88
|
templatesDesc: templatesDesc$1,
|
|
85
89
|
globals: globals$1,
|
|
@@ -114,6 +118,8 @@ const no = "No";
|
|
|
114
118
|
const upload = "Upload";
|
|
115
119
|
const uploadError = "Upload failed";
|
|
116
120
|
const fileUrl = "File URL";
|
|
121
|
+
const applyTemplateError = "Failed to apply template";
|
|
122
|
+
const createError = "Failed to create attribute";
|
|
117
123
|
const templates = "Attribute Templates";
|
|
118
124
|
const templatesDesc = "Reusable attribute blueprints. Apply to any category in one click.";
|
|
119
125
|
const globals = "Global Attributes";
|
|
@@ -153,6 +159,8 @@ const en = {
|
|
|
153
159
|
upload,
|
|
154
160
|
uploadError,
|
|
155
161
|
fileUrl,
|
|
162
|
+
applyTemplateError,
|
|
163
|
+
createError,
|
|
156
164
|
templates,
|
|
157
165
|
templatesDesc,
|
|
158
166
|
globals,
|
|
@@ -161,7 +169,7 @@ const en = {
|
|
|
161
169
|
const locales = { ru, en };
|
|
162
170
|
function detectLang() {
|
|
163
171
|
var _a, _b, _c;
|
|
164
|
-
if (typeof window === "undefined") return "
|
|
172
|
+
if (typeof window === "undefined") return "en";
|
|
165
173
|
const stored = window.localStorage.getItem("i18nextLng");
|
|
166
174
|
if (stored) {
|
|
167
175
|
const short = stored.slice(0, 2).toLowerCase();
|
|
@@ -169,8 +177,8 @@ function detectLang() {
|
|
|
169
177
|
}
|
|
170
178
|
const htmlLang = (_b = (_a = document.documentElement) == null ? void 0 : _a.lang) == null ? void 0 : _b.slice(0, 2).toLowerCase();
|
|
171
179
|
if (htmlLang && locales[htmlLang]) return htmlLang;
|
|
172
|
-
const nav = (((_c = window.navigator) == null ? void 0 : _c.language) || "
|
|
173
|
-
return locales[nav] ? nav : "
|
|
180
|
+
const nav = (((_c = window.navigator) == null ? void 0 : _c.language) || "en").slice(0, 2).toLowerCase();
|
|
181
|
+
return locales[nav] ? nav : "en";
|
|
174
182
|
}
|
|
175
183
|
function useT() {
|
|
176
184
|
const dict = useMemo(() => {
|
|
@@ -208,7 +216,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
208
216
|
setShowTemplateList(false);
|
|
209
217
|
},
|
|
210
218
|
onError: (err) => {
|
|
211
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
219
|
+
setMutationError((err == null ? void 0 : err.message) || t("applyTemplateError"));
|
|
212
220
|
}
|
|
213
221
|
});
|
|
214
222
|
const {
|
|
@@ -234,7 +242,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
234
242
|
setMutationError(null);
|
|
235
243
|
},
|
|
236
244
|
onError: (err) => {
|
|
237
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
245
|
+
setMutationError((err == null ? void 0 : err.message) || t("createError"));
|
|
238
246
|
}
|
|
239
247
|
});
|
|
240
248
|
const deleteMutation = useMutation({
|
|
@@ -282,19 +290,19 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
282
290
|
] }),
|
|
283
291
|
showTemplateList && /* @__PURE__ */ jsxs("div", { className: "px-6 py-3", children: [
|
|
284
292
|
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
285
|
-
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "
|
|
293
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: t("chooseTemplate") }),
|
|
286
294
|
/* @__PURE__ */ jsx(
|
|
287
295
|
Button,
|
|
288
296
|
{
|
|
289
297
|
size: "small",
|
|
290
298
|
variant: "secondary",
|
|
291
299
|
onClick: () => setShowTemplateList(false),
|
|
292
|
-
children: "
|
|
300
|
+
children: t("close")
|
|
293
301
|
}
|
|
294
302
|
)
|
|
295
303
|
] }),
|
|
296
304
|
templatesQuery.isLoading && /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }),
|
|
297
|
-
((_a = templatesQuery.data) == null ? void 0 : _a.attribute_templates.length) === 0 && /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
305
|
+
((_a = templatesQuery.data) == null ? void 0 : _a.attribute_templates.length) === 0 && /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("noTemplates") }),
|
|
298
306
|
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: (_b = templatesQuery.data) == null ? void 0 : _b.attribute_templates.map((p) => /* @__PURE__ */ jsxs(
|
|
299
307
|
"button",
|
|
300
308
|
{
|
|
@@ -313,9 +321,9 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
313
321
|
)) })
|
|
314
322
|
] }),
|
|
315
323
|
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
316
|
-
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: "
|
|
324
|
+
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: t("loadFailed") }) }),
|
|
317
325
|
inheritedAttributes.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
318
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsx(Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: "
|
|
326
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsx(Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: t("inherited") }) }),
|
|
319
327
|
/* @__PURE__ */ jsx("div", { className: "divide-y", children: inheritedAttributes.map((attr) => /* @__PURE__ */ jsxs(
|
|
320
328
|
"div",
|
|
321
329
|
{
|
|
@@ -332,18 +340,14 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
332
340
|
)) })
|
|
333
341
|
] }),
|
|
334
342
|
ownAttributes.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
335
|
-
inheritedAttributes.length > 0 && /* @__PURE__ */ jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsx(Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: "
|
|
343
|
+
inheritedAttributes.length > 0 && /* @__PURE__ */ jsx("div", { className: "px-6 py-2 bg-ui-bg-subtle", children: /* @__PURE__ */ jsx(Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-muted uppercase", children: t("own") }) }),
|
|
336
344
|
/* @__PURE__ */ jsx("div", { className: "divide-y", children: ownAttributes.map(
|
|
337
345
|
(attr) => confirmDeleteId === attr.id ? /* @__PURE__ */ jsxs(
|
|
338
346
|
"div",
|
|
339
347
|
{
|
|
340
348
|
className: "flex items-center gap-3 px-6 py-3 text-sm",
|
|
341
349
|
children: [
|
|
342
|
-
/* @__PURE__ */
|
|
343
|
-
"Удалить «",
|
|
344
|
-
attr.label,
|
|
345
|
-
"»?"
|
|
346
|
-
] }),
|
|
350
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-ui-fg-base", children: t("confirmDelete").replace("{name}", attr.label) }),
|
|
347
351
|
/* @__PURE__ */ jsx(
|
|
348
352
|
Button,
|
|
349
353
|
{
|
|
@@ -351,7 +355,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
351
355
|
variant: "danger",
|
|
352
356
|
onClick: () => deleteMutation.mutate(attr.id),
|
|
353
357
|
isLoading: deleteMutation.isPending,
|
|
354
|
-
children: "
|
|
358
|
+
children: t("delete")
|
|
355
359
|
}
|
|
356
360
|
),
|
|
357
361
|
/* @__PURE__ */ jsx(
|
|
@@ -360,7 +364,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
360
364
|
size: "small",
|
|
361
365
|
variant: "secondary",
|
|
362
366
|
onClick: () => setConfirmDeleteId(null),
|
|
363
|
-
children: "
|
|
367
|
+
children: t("cancel")
|
|
364
368
|
}
|
|
365
369
|
)
|
|
366
370
|
]
|
|
@@ -381,7 +385,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
381
385
|
{
|
|
382
386
|
onClick: () => setConfirmDeleteId(attr.id),
|
|
383
387
|
className: "text-xs text-ui-fg-error hover:underline",
|
|
384
|
-
children: "
|
|
388
|
+
children: t("delete")
|
|
385
389
|
}
|
|
386
390
|
)
|
|
387
391
|
]
|
|
@@ -390,14 +394,14 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
390
394
|
)
|
|
391
395
|
) })
|
|
392
396
|
] }),
|
|
393
|
-
!isLoading && !isError && attributes2.length === 0 && !showAddForm && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
397
|
+
!isLoading && !isError && attributes2.length === 0 && !showAddForm && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("noAttributes") }) }),
|
|
394
398
|
showAddForm && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-6 py-3", children: [
|
|
395
399
|
/* @__PURE__ */ jsx(
|
|
396
400
|
Input,
|
|
397
401
|
{
|
|
398
402
|
value: addForm.label,
|
|
399
403
|
onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })),
|
|
400
|
-
placeholder: "
|
|
404
|
+
placeholder: t("name"),
|
|
401
405
|
className: "flex-1 h-8 text-sm",
|
|
402
406
|
autoFocus: true
|
|
403
407
|
}
|
|
@@ -413,10 +417,10 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
413
417
|
})),
|
|
414
418
|
className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
415
419
|
children: [
|
|
416
|
-
/* @__PURE__ */ jsx("option", { value: "text", children: "
|
|
417
|
-
/* @__PURE__ */ jsx("option", { value: "number", children: "
|
|
418
|
-
/* @__PURE__ */ jsx("option", { value: "file", children: "
|
|
419
|
-
/* @__PURE__ */ jsx("option", { value: "boolean", children: "
|
|
420
|
+
/* @__PURE__ */ jsx("option", { value: "text", children: t("type.text") }),
|
|
421
|
+
/* @__PURE__ */ jsx("option", { value: "number", children: t("type.number") }),
|
|
422
|
+
/* @__PURE__ */ jsx("option", { value: "file", children: t("type.file") }),
|
|
423
|
+
/* @__PURE__ */ jsx("option", { value: "boolean", children: t("type.boolean") })
|
|
420
424
|
]
|
|
421
425
|
}
|
|
422
426
|
),
|
|
@@ -425,7 +429,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
425
429
|
{
|
|
426
430
|
value: addForm.unit,
|
|
427
431
|
onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
|
|
428
|
-
placeholder: "
|
|
432
|
+
placeholder: t("unit"),
|
|
429
433
|
className: "w-28 h-8 text-sm"
|
|
430
434
|
}
|
|
431
435
|
),
|
|
@@ -435,7 +439,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
435
439
|
size: "small",
|
|
436
440
|
onClick: handleAdd,
|
|
437
441
|
isLoading: createMutation.isPending,
|
|
438
|
-
children: "
|
|
442
|
+
children: t("add")
|
|
439
443
|
}
|
|
440
444
|
),
|
|
441
445
|
/* @__PURE__ */ jsx(
|
|
@@ -448,7 +452,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
448
452
|
setAddForm(emptyForm$1());
|
|
449
453
|
setMutationError(null);
|
|
450
454
|
},
|
|
451
|
-
children: "
|
|
455
|
+
children: t("cancel")
|
|
452
456
|
}
|
|
453
457
|
)
|
|
454
458
|
] }),
|
|
@@ -464,6 +468,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
464
468
|
var _a;
|
|
465
469
|
const productId = data.id;
|
|
466
470
|
const productHandle = data.handle || productId;
|
|
471
|
+
const t = useT();
|
|
467
472
|
const qc = useQueryClient();
|
|
468
473
|
const [formValues, setFormValues] = useState({});
|
|
469
474
|
const [saveSuccess, setSaveSuccess] = useState(false);
|
|
@@ -538,7 +543,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
538
543
|
if (!response.ok) {
|
|
539
544
|
const text = await response.text();
|
|
540
545
|
console.error("Upload failed", response.status, text);
|
|
541
|
-
alert(
|
|
546
|
+
alert(`${t("uploadError")}: ${response.status}`);
|
|
542
547
|
return;
|
|
543
548
|
}
|
|
544
549
|
const res = await response.json();
|
|
@@ -547,11 +552,11 @@ const ProductAttributeValuesWidget = ({
|
|
|
547
552
|
setFormValues((prev) => ({ ...prev, [attrId]: url }));
|
|
548
553
|
} else {
|
|
549
554
|
console.error("No URL in upload response", res);
|
|
550
|
-
alert("
|
|
555
|
+
alert(t("uploadError"));
|
|
551
556
|
}
|
|
552
557
|
} catch (err) {
|
|
553
558
|
console.error("Upload error", err);
|
|
554
|
-
alert("
|
|
559
|
+
alert(t("uploadError"));
|
|
555
560
|
} finally {
|
|
556
561
|
setUploadingId(null);
|
|
557
562
|
}
|
|
@@ -560,10 +565,10 @@ const ProductAttributeValuesWidget = ({
|
|
|
560
565
|
const isError = attributesQuery.isError || valuesQuery.isError;
|
|
561
566
|
const attributes2 = ((_a = attributesQuery.data) == null ? void 0 : _a.attributes) ?? [];
|
|
562
567
|
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
563
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "
|
|
564
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
565
|
-
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: "
|
|
566
|
-
!isLoading && !isError && attributes2.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
568
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: t("characteristics") }) }),
|
|
569
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
570
|
+
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: t("loadFailed") }) }),
|
|
571
|
+
!isLoading && !isError && attributes2.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("noAttributes") }) }),
|
|
567
572
|
!isLoading && !isError && attributes2.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
568
573
|
/* @__PURE__ */ jsx("div", { className: "divide-y", children: attributes2.map((attr) => /* @__PURE__ */ jsxs(
|
|
569
574
|
"div",
|
|
@@ -585,8 +590,8 @@ const ProductAttributeValuesWidget = ({
|
|
|
585
590
|
className: "h-8 flex-1 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
586
591
|
children: [
|
|
587
592
|
/* @__PURE__ */ jsx("option", { value: "", children: "—" }),
|
|
588
|
-
/* @__PURE__ */ jsx("option", { value: "true", children: "
|
|
589
|
-
/* @__PURE__ */ jsx("option", { value: "false", children: "
|
|
593
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: t("yes") }),
|
|
594
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: t("no") })
|
|
590
595
|
]
|
|
591
596
|
}
|
|
592
597
|
) : attr.type === "file" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -613,7 +618,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
613
618
|
...prev,
|
|
614
619
|
[attr.id]: e.target.value
|
|
615
620
|
})),
|
|
616
|
-
placeholder: "
|
|
621
|
+
placeholder: t("fileUrl"),
|
|
617
622
|
className: "h-8 text-sm flex-1"
|
|
618
623
|
}
|
|
619
624
|
),
|
|
@@ -628,7 +633,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
628
633
|
return (_a2 = fileInputs.current[attr.id]) == null ? void 0 : _a2.click();
|
|
629
634
|
},
|
|
630
635
|
isLoading: uploadingId === attr.id,
|
|
631
|
-
children: "
|
|
636
|
+
children: t("upload")
|
|
632
637
|
}
|
|
633
638
|
)
|
|
634
639
|
] }) : /* @__PURE__ */ jsx(
|
|
@@ -640,7 +645,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
640
645
|
...prev,
|
|
641
646
|
[attr.id]: e.target.value
|
|
642
647
|
})),
|
|
643
|
-
placeholder: attr.type === "number" ? "0" : "
|
|
648
|
+
placeholder: attr.type === "number" ? "0" : t("value"),
|
|
644
649
|
className: "h-8 text-sm flex-1"
|
|
645
650
|
}
|
|
646
651
|
) })
|
|
@@ -654,7 +659,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
654
659
|
size: "small",
|
|
655
660
|
onClick: handleSave,
|
|
656
661
|
isLoading: saveMutation.isPending,
|
|
657
|
-
children: saveSuccess ? "
|
|
662
|
+
children: saveSuccess ? t("saved") : t("save")
|
|
658
663
|
}
|
|
659
664
|
) })
|
|
660
665
|
] })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empty-complete-org/medusa-product-attributes",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Custom attributes module for Medusa v2 with support for text, number, file types and units of measurement",
|
|
5
5
|
"author": "empty-complete",
|
|
6
6
|
"license": "MIT",
|