@empty-complete-org/medusa-product-attributes 1.0.1 → 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.
- package/.medusa/server/src/admin/index.js +233 -266
- package/.medusa/server/src/admin/index.mjs +233 -266
- package/package.json +1 -1
|
@@ -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,
|
|
@@ -180,14 +188,16 @@ const en = {
|
|
|
180
188
|
};
|
|
181
189
|
const locales = { ru, en };
|
|
182
190
|
function detectLang() {
|
|
183
|
-
var _a;
|
|
191
|
+
var _a, _b, _c;
|
|
184
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();
|
|
188
196
|
if (locales[short]) return short;
|
|
189
197
|
}
|
|
190
|
-
const
|
|
198
|
+
const htmlLang = (_b = (_a = document.documentElement) == null ? void 0 : _a.lang) == null ? void 0 : _b.slice(0, 2).toLowerCase();
|
|
199
|
+
if (htmlLang && locales[htmlLang]) return htmlLang;
|
|
200
|
+
const nav = (((_c = window.navigator) == null ? void 0 : _c.language) || "en").slice(0, 2).toLowerCase();
|
|
191
201
|
return locales[nav] ? nav : "en";
|
|
192
202
|
}
|
|
193
203
|
function useT() {
|
|
@@ -197,7 +207,7 @@ function useT() {
|
|
|
197
207
|
}, []);
|
|
198
208
|
return (key, fallback) => dict[key] ?? fallback ?? key;
|
|
199
209
|
}
|
|
200
|
-
const emptyForm$
|
|
210
|
+
const emptyForm$1 = () => ({ label: "", type: "text", unit: "" });
|
|
201
211
|
const CategoryAttributeTemplatesWidget = ({
|
|
202
212
|
data
|
|
203
213
|
}) => {
|
|
@@ -208,7 +218,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
208
218
|
const queryKey = ["category-custom-attributes", categoryId];
|
|
209
219
|
const [showAddForm, setShowAddForm] = React.useState(false);
|
|
210
220
|
const [showTemplateList, setShowTemplateList] = React.useState(false);
|
|
211
|
-
const [addForm, setAddForm] = React.useState(emptyForm$
|
|
221
|
+
const [addForm, setAddForm] = React.useState(emptyForm$1());
|
|
212
222
|
const [confirmDeleteId, setConfirmDeleteId] = React.useState(null);
|
|
213
223
|
const [mutationError, setMutationError] = React.useState(null);
|
|
214
224
|
const templatesQuery = reactQuery.useQuery({
|
|
@@ -226,7 +236,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
226
236
|
setShowTemplateList(false);
|
|
227
237
|
},
|
|
228
238
|
onError: (err) => {
|
|
229
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
239
|
+
setMutationError((err == null ? void 0 : err.message) || t("applyTemplateError"));
|
|
230
240
|
}
|
|
231
241
|
});
|
|
232
242
|
const {
|
|
@@ -248,11 +258,11 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
248
258
|
onSuccess: () => {
|
|
249
259
|
qc.invalidateQueries({ queryKey });
|
|
250
260
|
setShowAddForm(false);
|
|
251
|
-
setAddForm(emptyForm$
|
|
261
|
+
setAddForm(emptyForm$1());
|
|
252
262
|
setMutationError(null);
|
|
253
263
|
},
|
|
254
264
|
onError: (err) => {
|
|
255
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
265
|
+
setMutationError((err == null ? void 0 : err.message) || t("createError"));
|
|
256
266
|
}
|
|
257
267
|
});
|
|
258
268
|
const deleteMutation = reactQuery.useMutation({
|
|
@@ -273,7 +283,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
273
283
|
unit: addForm.type === "number" && addForm.unit.trim() ? addForm.unit.trim() : null
|
|
274
284
|
});
|
|
275
285
|
};
|
|
276
|
-
const
|
|
286
|
+
const typeLabel = (v) => t(`type.${v}`, v);
|
|
277
287
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
278
288
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
279
289
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("attributes") }),
|
|
@@ -287,35 +297,32 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
287
297
|
children: t("fromTemplate")
|
|
288
298
|
}
|
|
289
299
|
),
|
|
290
|
-
/* @__PURE__ */ jsxRuntime.
|
|
300
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
291
301
|
ui.Button,
|
|
292
302
|
{
|
|
293
303
|
variant: "secondary",
|
|
294
304
|
size: "small",
|
|
295
305
|
onClick: () => setShowAddForm(true),
|
|
296
|
-
children:
|
|
297
|
-
"+ ",
|
|
298
|
-
t("add")
|
|
299
|
-
]
|
|
306
|
+
children: t("add")
|
|
300
307
|
}
|
|
301
308
|
)
|
|
302
309
|
] })
|
|
303
310
|
] }),
|
|
304
311
|
showTemplateList && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-3", children: [
|
|
305
312
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
306
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "
|
|
313
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: t("chooseTemplate") }),
|
|
307
314
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
308
315
|
ui.Button,
|
|
309
316
|
{
|
|
310
317
|
size: "small",
|
|
311
318
|
variant: "secondary",
|
|
312
319
|
onClick: () => setShowTemplateList(false),
|
|
313
|
-
children: "
|
|
320
|
+
children: t("close")
|
|
314
321
|
}
|
|
315
322
|
)
|
|
316
323
|
] }),
|
|
317
324
|
templatesQuery.isLoading && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }),
|
|
318
|
-
((_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") }),
|
|
319
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(
|
|
320
327
|
"button",
|
|
321
328
|
{
|
|
@@ -325,7 +332,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
325
332
|
children: [
|
|
326
333
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: p.label }),
|
|
327
334
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
|
|
328
|
-
|
|
335
|
+
typeLabel(p.type),
|
|
329
336
|
p.unit ? `, ${p.unit}` : ""
|
|
330
337
|
] })
|
|
331
338
|
]
|
|
@@ -334,9 +341,9 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
334
341
|
)) })
|
|
335
342
|
] }),
|
|
336
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") }) }),
|
|
337
|
-
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") }) }),
|
|
338
345
|
inheritedAttributes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
339
|
-
/* @__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") }) }),
|
|
340
347
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: inheritedAttributes.map((attr) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
341
348
|
"div",
|
|
342
349
|
{
|
|
@@ -344,28 +351,23 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
344
351
|
children: [
|
|
345
352
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-sm text-ui-fg-subtle", children: attr.label }),
|
|
346
353
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
|
|
347
|
-
|
|
354
|
+
typeLabel(attr.type),
|
|
348
355
|
attr.unit ? `, ${attr.unit}` : ""
|
|
349
|
-
] })
|
|
350
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "blue", children: "из родителя" })
|
|
356
|
+
] })
|
|
351
357
|
]
|
|
352
358
|
},
|
|
353
359
|
attr.id
|
|
354
360
|
)) })
|
|
355
361
|
] }),
|
|
356
362
|
ownAttributes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
357
|
-
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") }) }),
|
|
358
364
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: ownAttributes.map(
|
|
359
365
|
(attr) => confirmDeleteId === attr.id ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
360
366
|
"div",
|
|
361
367
|
{
|
|
362
368
|
className: "flex items-center gap-3 px-6 py-3 text-sm",
|
|
363
369
|
children: [
|
|
364
|
-
/* @__PURE__ */ jsxRuntime.
|
|
365
|
-
"Удалить «",
|
|
366
|
-
attr.label,
|
|
367
|
-
"»?"
|
|
368
|
-
] }),
|
|
370
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-ui-fg-base", children: t("confirmDelete").replace("{name}", attr.label) }),
|
|
369
371
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
370
372
|
ui.Button,
|
|
371
373
|
{
|
|
@@ -373,7 +375,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
373
375
|
variant: "danger",
|
|
374
376
|
onClick: () => deleteMutation.mutate(attr.id),
|
|
375
377
|
isLoading: deleteMutation.isPending,
|
|
376
|
-
children: "
|
|
378
|
+
children: t("delete")
|
|
377
379
|
}
|
|
378
380
|
),
|
|
379
381
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -382,7 +384,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
382
384
|
size: "small",
|
|
383
385
|
variant: "secondary",
|
|
384
386
|
onClick: () => setConfirmDeleteId(null),
|
|
385
|
-
children: "
|
|
387
|
+
children: t("cancel")
|
|
386
388
|
}
|
|
387
389
|
)
|
|
388
390
|
]
|
|
@@ -395,7 +397,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
395
397
|
children: [
|
|
396
398
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-sm text-ui-fg-base", children: attr.label }),
|
|
397
399
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
|
|
398
|
-
|
|
400
|
+
typeLabel(attr.type),
|
|
399
401
|
attr.unit ? `, ${attr.unit}` : ""
|
|
400
402
|
] }),
|
|
401
403
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -403,7 +405,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
403
405
|
{
|
|
404
406
|
onClick: () => setConfirmDeleteId(attr.id),
|
|
405
407
|
className: "text-xs text-ui-fg-error hover:underline",
|
|
406
|
-
children: "
|
|
408
|
+
children: t("delete")
|
|
407
409
|
}
|
|
408
410
|
)
|
|
409
411
|
]
|
|
@@ -412,14 +414,14 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
412
414
|
)
|
|
413
415
|
) })
|
|
414
416
|
] }),
|
|
415
|
-
!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") }) }),
|
|
416
418
|
showAddForm && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-6 py-3", children: [
|
|
417
419
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
418
420
|
ui.Input,
|
|
419
421
|
{
|
|
420
422
|
value: addForm.label,
|
|
421
423
|
onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })),
|
|
422
|
-
placeholder: "
|
|
424
|
+
placeholder: t("name"),
|
|
423
425
|
className: "flex-1 h-8 text-sm",
|
|
424
426
|
autoFocus: true
|
|
425
427
|
}
|
|
@@ -435,10 +437,10 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
435
437
|
})),
|
|
436
438
|
className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
437
439
|
children: [
|
|
438
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: "
|
|
439
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "
|
|
440
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: "
|
|
441
|
-
/* @__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") })
|
|
442
444
|
]
|
|
443
445
|
}
|
|
444
446
|
),
|
|
@@ -447,7 +449,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
447
449
|
{
|
|
448
450
|
value: addForm.unit,
|
|
449
451
|
onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
|
|
450
|
-
placeholder: "
|
|
452
|
+
placeholder: t("unit"),
|
|
451
453
|
className: "w-28 h-8 text-sm"
|
|
452
454
|
}
|
|
453
455
|
),
|
|
@@ -457,7 +459,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
457
459
|
size: "small",
|
|
458
460
|
onClick: handleAdd,
|
|
459
461
|
isLoading: createMutation.isPending,
|
|
460
|
-
children: "
|
|
462
|
+
children: t("add")
|
|
461
463
|
}
|
|
462
464
|
),
|
|
463
465
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -467,10 +469,10 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
467
469
|
size: "small",
|
|
468
470
|
onClick: () => {
|
|
469
471
|
setShowAddForm(false);
|
|
470
|
-
setAddForm(emptyForm$
|
|
472
|
+
setAddForm(emptyForm$1());
|
|
471
473
|
setMutationError(null);
|
|
472
474
|
},
|
|
473
|
-
children: "
|
|
475
|
+
children: t("cancel")
|
|
474
476
|
}
|
|
475
477
|
)
|
|
476
478
|
] }),
|
|
@@ -486,6 +488,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
486
488
|
var _a;
|
|
487
489
|
const productId = data.id;
|
|
488
490
|
const productHandle = data.handle || productId;
|
|
491
|
+
const t = useT();
|
|
489
492
|
const qc = reactQuery.useQueryClient();
|
|
490
493
|
const [formValues, setFormValues] = React.useState({});
|
|
491
494
|
const [saveSuccess, setSaveSuccess] = React.useState(false);
|
|
@@ -560,7 +563,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
560
563
|
if (!response.ok) {
|
|
561
564
|
const text = await response.text();
|
|
562
565
|
console.error("Upload failed", response.status, text);
|
|
563
|
-
alert(
|
|
566
|
+
alert(`${t("uploadError")}: ${response.status}`);
|
|
564
567
|
return;
|
|
565
568
|
}
|
|
566
569
|
const res = await response.json();
|
|
@@ -569,11 +572,11 @@ const ProductAttributeValuesWidget = ({
|
|
|
569
572
|
setFormValues((prev) => ({ ...prev, [attrId]: url }));
|
|
570
573
|
} else {
|
|
571
574
|
console.error("No URL in upload response", res);
|
|
572
|
-
alert("
|
|
575
|
+
alert(t("uploadError"));
|
|
573
576
|
}
|
|
574
577
|
} catch (err) {
|
|
575
578
|
console.error("Upload error", err);
|
|
576
|
-
alert("
|
|
579
|
+
alert(t("uploadError"));
|
|
577
580
|
} finally {
|
|
578
581
|
setUploadingId(null);
|
|
579
582
|
}
|
|
@@ -582,10 +585,10 @@ const ProductAttributeValuesWidget = ({
|
|
|
582
585
|
const isError = attributesQuery.isError || valuesQuery.isError;
|
|
583
586
|
const attributes2 = ((_a = attributesQuery.data) == null ? void 0 : _a.attributes) ?? [];
|
|
584
587
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
585
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "
|
|
586
|
-
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
587
|
-
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "
|
|
588
|
-
!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") }) }),
|
|
589
592
|
!isLoading && !isError && attributes2.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
590
593
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: attributes2.map((attr) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
591
594
|
"div",
|
|
@@ -607,8 +610,8 @@ const ProductAttributeValuesWidget = ({
|
|
|
607
610
|
className: "h-8 flex-1 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
608
611
|
children: [
|
|
609
612
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "—" }),
|
|
610
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "true", children: "
|
|
611
|
-
/* @__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") })
|
|
612
615
|
]
|
|
613
616
|
}
|
|
614
617
|
) : attr.type === "file" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -635,7 +638,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
635
638
|
...prev,
|
|
636
639
|
[attr.id]: e.target.value
|
|
637
640
|
})),
|
|
638
|
-
placeholder: "
|
|
641
|
+
placeholder: t("fileUrl"),
|
|
639
642
|
className: "h-8 text-sm flex-1"
|
|
640
643
|
}
|
|
641
644
|
),
|
|
@@ -650,7 +653,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
650
653
|
return (_a2 = fileInputs.current[attr.id]) == null ? void 0 : _a2.click();
|
|
651
654
|
},
|
|
652
655
|
isLoading: uploadingId === attr.id,
|
|
653
|
-
children: "
|
|
656
|
+
children: t("upload")
|
|
654
657
|
}
|
|
655
658
|
)
|
|
656
659
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -662,7 +665,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
662
665
|
...prev,
|
|
663
666
|
[attr.id]: e.target.value
|
|
664
667
|
})),
|
|
665
|
-
placeholder: attr.type === "number" ? "0" : "
|
|
668
|
+
placeholder: attr.type === "number" ? "0" : t("value"),
|
|
666
669
|
className: "h-8 text-sm flex-1"
|
|
667
670
|
}
|
|
668
671
|
) })
|
|
@@ -676,7 +679,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
676
679
|
size: "small",
|
|
677
680
|
onClick: handleSave,
|
|
678
681
|
isLoading: saveMutation.isPending,
|
|
679
|
-
children: saveSuccess ? "
|
|
682
|
+
children: saveSuccess ? t("saved") : t("save")
|
|
680
683
|
}
|
|
681
684
|
) })
|
|
682
685
|
] })
|
|
@@ -685,71 +688,6 @@ const ProductAttributeValuesWidget = ({
|
|
|
685
688
|
adminSdk.defineWidgetConfig({
|
|
686
689
|
zone: "product.details.after"
|
|
687
690
|
});
|
|
688
|
-
var __defProp$1 = Object.defineProperty;
|
|
689
|
-
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
|
|
690
|
-
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
691
|
-
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
|
|
692
|
-
var __defNormalProp$1 = (obj, key, value2) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
|
|
693
|
-
var __spreadValues$1 = (a, b) => {
|
|
694
|
-
for (var prop in b || (b = {}))
|
|
695
|
-
if (__hasOwnProp$1.call(b, prop))
|
|
696
|
-
__defNormalProp$1(a, prop, b[prop]);
|
|
697
|
-
if (__getOwnPropSymbols$1)
|
|
698
|
-
for (var prop of __getOwnPropSymbols$1(b)) {
|
|
699
|
-
if (__propIsEnum$1.call(b, prop))
|
|
700
|
-
__defNormalProp$1(a, prop, b[prop]);
|
|
701
|
-
}
|
|
702
|
-
return a;
|
|
703
|
-
};
|
|
704
|
-
var __objRest$1 = (source, exclude) => {
|
|
705
|
-
var target = {};
|
|
706
|
-
for (var prop in source)
|
|
707
|
-
if (__hasOwnProp$1.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
708
|
-
target[prop] = source[prop];
|
|
709
|
-
if (source != null && __getOwnPropSymbols$1)
|
|
710
|
-
for (var prop of __getOwnPropSymbols$1(source)) {
|
|
711
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum$1.call(source, prop))
|
|
712
|
-
target[prop] = source[prop];
|
|
713
|
-
}
|
|
714
|
-
return target;
|
|
715
|
-
};
|
|
716
|
-
const Globe = React__namespace.forwardRef(
|
|
717
|
-
(_a, ref) => {
|
|
718
|
-
var _b = _a, { color = "currentColor" } = _b, props = __objRest$1(_b, ["color"]);
|
|
719
|
-
return /* @__PURE__ */ React__namespace.createElement(
|
|
720
|
-
"svg",
|
|
721
|
-
__spreadValues$1({
|
|
722
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
723
|
-
width: 15,
|
|
724
|
-
height: 15,
|
|
725
|
-
viewBox: "0 0 15 15",
|
|
726
|
-
fill: "none",
|
|
727
|
-
ref
|
|
728
|
-
}, props),
|
|
729
|
-
/* @__PURE__ */ React__namespace.createElement(
|
|
730
|
-
"path",
|
|
731
|
-
{
|
|
732
|
-
stroke: color,
|
|
733
|
-
strokeLinecap: "round",
|
|
734
|
-
strokeLinejoin: "round",
|
|
735
|
-
strokeWidth: 1.5,
|
|
736
|
-
d: "M7.5 13.945c1.473 0 2.667-2.886 2.667-6.445S8.973 1.056 7.5 1.056 4.833 3.94 4.833 7.5s1.194 6.445 2.667 6.445M1.056 7.5h12.888"
|
|
737
|
-
}
|
|
738
|
-
),
|
|
739
|
-
/* @__PURE__ */ React__namespace.createElement(
|
|
740
|
-
"path",
|
|
741
|
-
{
|
|
742
|
-
stroke: color,
|
|
743
|
-
strokeLinecap: "round",
|
|
744
|
-
strokeLinejoin: "round",
|
|
745
|
-
strokeWidth: 1.5,
|
|
746
|
-
d: "M7.5 13.945a6.444 6.444 0 1 0 0-12.89 6.444 6.444 0 0 0 0 12.89"
|
|
747
|
-
}
|
|
748
|
-
)
|
|
749
|
-
);
|
|
750
|
-
}
|
|
751
|
-
);
|
|
752
|
-
Globe.displayName = "Globe";
|
|
753
691
|
var __defProp = Object.defineProperty;
|
|
754
692
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
755
693
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
@@ -778,7 +716,7 @@ var __objRest = (source, exclude) => {
|
|
|
778
716
|
}
|
|
779
717
|
return target;
|
|
780
718
|
};
|
|
781
|
-
const
|
|
719
|
+
const Tag = React__namespace.forwardRef(
|
|
782
720
|
(_a, ref) => {
|
|
783
721
|
var _b = _a, { color = "currentColor" } = _b, props = __objRest(_b, ["color"]);
|
|
784
722
|
return /* @__PURE__ */ React__namespace.createElement(
|
|
@@ -791,56 +729,73 @@ const SquaresPlus = React__namespace.forwardRef(
|
|
|
791
729
|
fill: "none",
|
|
792
730
|
ref
|
|
793
731
|
}, props),
|
|
794
|
-
/* @__PURE__ */ React__namespace.createElement(
|
|
732
|
+
/* @__PURE__ */ React__namespace.createElement("g", { clipPath: "url(#a)" }, /* @__PURE__ */ React__namespace.createElement(
|
|
795
733
|
"path",
|
|
796
734
|
{
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
d: "M5.056 1.944H2.833a.89.89 0 0 0-.889.89v2.221c0 .491.398.89.89.89h2.222c.49 0 .888-.399.888-.89V2.833a.89.89 0 0 0-.888-.889M12.167 1.944H9.944a.89.89 0 0 0-.888.89v2.221c0 .491.398.89.888.89h2.223c.49 0 .889-.399.889-.89V2.833a.89.89 0 0 0-.89-.889M5.056 9.056H2.833a.89.89 0 0 0-.889.889v2.222c0 .49.398.889.89.889h2.222c.49 0 .888-.398.888-.89V9.946a.89.89 0 0 0-.888-.89M11.056 8.611v4.445M13.278 10.833H8.833"
|
|
735
|
+
fill: color,
|
|
736
|
+
fillRule: "evenodd",
|
|
737
|
+
d: "M2.25 2.389a.14.14 0 0 1 .139-.139h4.375c.272 0 .534.108.727.301l5.11 5.111a1.027 1.027 0 0 1 0 1.453l-3.486 3.487a1.027 1.027 0 0 1-1.453 0L2.552 7.49a1.03 1.03 0 0 1-.302-.727zM2.389.75A1.64 1.64 0 0 0 .75 2.389v4.375c0 .67.267 1.313.74 1.787l5.112 5.111a2.527 2.527 0 0 0 3.574 0l3.486-3.486a2.527 2.527 0 0 0 0-3.574L8.552 1.49A2.53 2.53 0 0 0 6.763.75zm3.778 4.305a1.111 1.111 0 1 1-2.223 0 1.111 1.111 0 0 1 2.223 0",
|
|
738
|
+
clipRule: "evenodd"
|
|
802
739
|
}
|
|
803
|
-
)
|
|
740
|
+
)),
|
|
741
|
+
/* @__PURE__ */ React__namespace.createElement("defs", null, /* @__PURE__ */ React__namespace.createElement("clipPath", { id: "a" }, /* @__PURE__ */ React__namespace.createElement("path", { fill: "#fff", d: "M0 0h15v15H0z" })))
|
|
804
742
|
);
|
|
805
743
|
}
|
|
806
744
|
);
|
|
807
|
-
|
|
808
|
-
const emptyForm
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
745
|
+
Tag.displayName = "Tag";
|
|
746
|
+
const emptyForm = () => ({ label: "", type: "text", unit: "", description: "" });
|
|
747
|
+
const ProductAttributesSettingsPage = () => {
|
|
748
|
+
const t = useT();
|
|
749
|
+
const [tab, setTab] = React.useState("globals");
|
|
750
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
|
|
751
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-6 border-b border-ui-border-base px-6 pt-4", children: [
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { active: tab === "globals", onClick: () => setTab("globals"), children: t("globals") }),
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabButton, { active: tab === "templates", onClick: () => setTab("templates"), children: t("templates") })
|
|
754
|
+
] }),
|
|
755
|
+
tab === "globals" ? /* @__PURE__ */ jsxRuntime.jsx(GlobalsTab, {}) : /* @__PURE__ */ jsxRuntime.jsx(TemplatesTab, {})
|
|
756
|
+
] });
|
|
757
|
+
};
|
|
758
|
+
const TabButton = ({
|
|
759
|
+
active,
|
|
760
|
+
onClick,
|
|
761
|
+
children
|
|
762
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
763
|
+
"button",
|
|
764
|
+
{
|
|
765
|
+
onClick,
|
|
766
|
+
className: `-mb-px border-b-2 px-1 pb-3 text-sm font-medium transition-colors ${active ? "border-ui-border-interactive text-ui-fg-base" : "border-transparent text-ui-fg-subtle hover:text-ui-fg-base"}`,
|
|
767
|
+
children
|
|
768
|
+
}
|
|
769
|
+
);
|
|
770
|
+
const GlobalsTab = () => {
|
|
771
|
+
const t = useT();
|
|
816
772
|
const qc = reactQuery.useQueryClient();
|
|
817
|
-
const queryKey = ["
|
|
773
|
+
const queryKey = ["global-attributes"];
|
|
818
774
|
const [showAddForm, setShowAddForm] = React.useState(false);
|
|
819
|
-
const [addForm, setAddForm] = React.useState(emptyForm
|
|
775
|
+
const [addForm, setAddForm] = React.useState(emptyForm());
|
|
820
776
|
const [confirmDeleteId, setConfirmDeleteId] = React.useState(null);
|
|
821
777
|
const { data, isLoading, isError } = reactQuery.useQuery({
|
|
822
778
|
queryKey,
|
|
823
|
-
queryFn: () => sdk.client.fetch(`/admin/
|
|
779
|
+
queryFn: () => sdk.client.fetch(`/admin/global-attributes`)
|
|
824
780
|
});
|
|
825
|
-
const
|
|
781
|
+
const attrs = (data == null ? void 0 : data.global_attributes) ?? [];
|
|
826
782
|
const createMutation = reactQuery.useMutation({
|
|
827
|
-
mutationFn: (body) => sdk.client.fetch(`/admin/
|
|
783
|
+
mutationFn: (body) => sdk.client.fetch(`/admin/global-attributes`, {
|
|
828
784
|
method: "POST",
|
|
829
785
|
body: {
|
|
830
786
|
label: body.label,
|
|
831
787
|
type: body.type,
|
|
832
|
-
unit: body.type === "number" && body.unit ? body.unit : null
|
|
833
|
-
description: body.description || null
|
|
788
|
+
unit: body.type === "number" && body.unit ? body.unit : null
|
|
834
789
|
}
|
|
835
790
|
}),
|
|
836
791
|
onSuccess: () => {
|
|
837
792
|
qc.invalidateQueries({ queryKey });
|
|
838
793
|
setShowAddForm(false);
|
|
839
|
-
setAddForm(emptyForm
|
|
794
|
+
setAddForm(emptyForm());
|
|
840
795
|
}
|
|
841
796
|
});
|
|
842
797
|
const deleteMutation = reactQuery.useMutation({
|
|
843
|
-
mutationFn: (id) => sdk.client.fetch(`/admin/
|
|
798
|
+
mutationFn: (id) => sdk.client.fetch(`/admin/global-attributes`, {
|
|
844
799
|
method: "PATCH",
|
|
845
800
|
body: { id, deleted_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
846
801
|
}),
|
|
@@ -858,86 +813,57 @@ const AttributeTemplatesSettingsPage = () => {
|
|
|
858
813
|
description: addForm.description.trim()
|
|
859
814
|
});
|
|
860
815
|
};
|
|
861
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
862
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-
|
|
816
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y", children: [
|
|
817
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
863
818
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
864
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "
|
|
865
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "
|
|
819
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("globals") }),
|
|
820
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("globalsDesc") })
|
|
866
821
|
] }),
|
|
867
|
-
!showAddForm && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: "
|
|
822
|
+
!showAddForm && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: t("add") })
|
|
868
823
|
] }),
|
|
869
|
-
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
870
|
-
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "
|
|
871
|
-
!isLoading && !isError &&
|
|
872
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children:
|
|
873
|
-
(
|
|
874
|
-
/* @__PURE__ */ jsxRuntime.
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
/* @__PURE__ */ jsxRuntime.
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
883
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
884
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: p.label }),
|
|
885
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
|
|
886
|
-
typeLabel$1(p.type),
|
|
887
|
-
p.unit ? `, ${p.unit}` : ""
|
|
888
|
-
] })
|
|
889
|
-
] }),
|
|
890
|
-
p.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: p.description })
|
|
824
|
+
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") }) }),
|
|
825
|
+
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") }) }),
|
|
826
|
+
!isLoading && !isError && attrs.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("noGlobals") }) }),
|
|
827
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: attrs.map(
|
|
828
|
+
(a) => confirmDeleteId === a.id ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-6 py-3 text-sm", children: [
|
|
829
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: t("confirmDelete").replace("{name}", a.label) }),
|
|
830
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "danger", onClick: () => deleteMutation.mutate(a.id), isLoading: deleteMutation.isPending, children: t("delete") }),
|
|
831
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => setConfirmDeleteId(null), children: t("cancel") })
|
|
832
|
+
] }, a.id) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
|
|
833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-sm", children: a.label }),
|
|
834
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
|
|
835
|
+
t(`type.${a.type}`, a.type),
|
|
836
|
+
a.unit ? `, ${a.unit}` : ""
|
|
891
837
|
] }),
|
|
892
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setConfirmDeleteId(
|
|
893
|
-
] },
|
|
838
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setConfirmDeleteId(a.id), className: "text-xs text-ui-fg-error hover:underline", children: t("delete") })
|
|
839
|
+
] }, a.id)
|
|
894
840
|
) }),
|
|
895
|
-
showAddForm && /* @__PURE__ */ jsxRuntime.
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: "Текст" }),
|
|
900
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "Число" }),
|
|
901
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: "Файл" }),
|
|
902
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "Да/Нет" })
|
|
903
|
-
] }),
|
|
904
|
-
addForm.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: addForm.unit, onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })), placeholder: "ед.", className: "w-28 h-8 text-sm" })
|
|
905
|
-
] }),
|
|
906
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: addForm.description, onChange: (e) => setAddForm((f) => ({ ...f, description: e.target.value })), placeholder: "Описание (необязательно)", className: "h-8 text-sm" }),
|
|
907
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
908
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: () => {
|
|
909
|
-
setShowAddForm(false);
|
|
910
|
-
setAddForm(emptyForm$1());
|
|
911
|
-
}, children: "Отмена" }),
|
|
912
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: handleAdd, isLoading: createMutation.isPending, children: "Создать" })
|
|
913
|
-
] })
|
|
914
|
-
] })
|
|
841
|
+
showAddForm && /* @__PURE__ */ jsxRuntime.jsx(AddForm, { t, form: addForm, setForm: setAddForm, onCancel: () => {
|
|
842
|
+
setShowAddForm(false);
|
|
843
|
+
setAddForm(emptyForm());
|
|
844
|
+
}, onSubmit: handleAdd, isLoading: createMutation.isPending, withDescription: false })
|
|
915
845
|
] });
|
|
916
846
|
};
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
icon: SquaresPlus
|
|
920
|
-
});
|
|
921
|
-
const emptyForm = () => ({ label: "", type: "text", unit: "" });
|
|
922
|
-
const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
|
|
923
|
-
const GlobalAttributesSettingsPage = () => {
|
|
847
|
+
const TemplatesTab = () => {
|
|
848
|
+
const t = useT();
|
|
924
849
|
const qc = reactQuery.useQueryClient();
|
|
925
|
-
const queryKey = ["
|
|
850
|
+
const queryKey = ["attribute-templates"];
|
|
926
851
|
const [showAddForm, setShowAddForm] = React.useState(false);
|
|
927
852
|
const [addForm, setAddForm] = React.useState(emptyForm());
|
|
928
853
|
const [confirmDeleteId, setConfirmDeleteId] = React.useState(null);
|
|
929
854
|
const { data, isLoading, isError } = reactQuery.useQuery({
|
|
930
855
|
queryKey,
|
|
931
|
-
queryFn: () => sdk.client.fetch(`/admin/
|
|
856
|
+
queryFn: () => sdk.client.fetch(`/admin/attribute-templates`)
|
|
932
857
|
});
|
|
933
|
-
const
|
|
858
|
+
const templates2 = (data == null ? void 0 : data.attribute_templates) ?? [];
|
|
934
859
|
const createMutation = reactQuery.useMutation({
|
|
935
|
-
mutationFn: (body) => sdk.client.fetch(`/admin/
|
|
860
|
+
mutationFn: (body) => sdk.client.fetch(`/admin/attribute-templates`, {
|
|
936
861
|
method: "POST",
|
|
937
862
|
body: {
|
|
938
863
|
label: body.label,
|
|
939
864
|
type: body.type,
|
|
940
|
-
unit: body.type === "number" && body.unit ? body.unit : null
|
|
865
|
+
unit: body.type === "number" && body.unit ? body.unit : null,
|
|
866
|
+
description: body.description || null
|
|
941
867
|
}
|
|
942
868
|
}),
|
|
943
869
|
onSuccess: () => {
|
|
@@ -947,7 +873,7 @@ const GlobalAttributesSettingsPage = () => {
|
|
|
947
873
|
}
|
|
948
874
|
});
|
|
949
875
|
const deleteMutation = reactQuery.useMutation({
|
|
950
|
-
mutationFn: (id) => sdk.client.fetch(`/admin/
|
|
876
|
+
mutationFn: (id) => sdk.client.fetch(`/admin/attribute-templates`, {
|
|
951
877
|
method: "PATCH",
|
|
952
878
|
body: { id, deleted_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
953
879
|
}),
|
|
@@ -959,60 +885,113 @@ const GlobalAttributesSettingsPage = () => {
|
|
|
959
885
|
const handleAdd = () => {
|
|
960
886
|
if (!addForm.label.trim()) return;
|
|
961
887
|
createMutation.mutate({
|
|
888
|
+
...addForm,
|
|
962
889
|
label: addForm.label.trim(),
|
|
963
|
-
|
|
964
|
-
|
|
890
|
+
unit: addForm.unit.trim(),
|
|
891
|
+
description: addForm.description.trim()
|
|
965
892
|
});
|
|
966
893
|
};
|
|
967
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
968
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-
|
|
894
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y", children: [
|
|
895
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
969
896
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
970
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "
|
|
971
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "
|
|
897
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("templates") }),
|
|
898
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("templatesDesc") })
|
|
972
899
|
] }),
|
|
973
|
-
!showAddForm && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: "
|
|
900
|
+
!showAddForm && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: t("add") })
|
|
974
901
|
] }),
|
|
975
|
-
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
976
|
-
isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "
|
|
977
|
-
!isLoading && !isError &&
|
|
978
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children:
|
|
979
|
-
(
|
|
980
|
-
/* @__PURE__ */ jsxRuntime.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
902
|
+
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") }) }),
|
|
903
|
+
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") }) }),
|
|
904
|
+
!isLoading && !isError && templates2.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("noTemplates") }) }),
|
|
905
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: templates2.map(
|
|
906
|
+
(p) => confirmDeleteId === p.id ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-6 py-3 text-sm", children: [
|
|
907
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: t("confirmDelete").replace("{name}", p.label) }),
|
|
908
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "danger", onClick: () => deleteMutation.mutate(p.id), isLoading: deleteMutation.isPending, children: t("delete") }),
|
|
909
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => setConfirmDeleteId(null), children: t("cancel") })
|
|
910
|
+
] }, p.id) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
|
|
911
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
912
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
913
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: p.label }),
|
|
914
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
|
|
915
|
+
t(`type.${p.type}`, p.type),
|
|
916
|
+
p.unit ? `, ${p.unit}` : ""
|
|
917
|
+
] })
|
|
918
|
+
] }),
|
|
919
|
+
p.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: p.description })
|
|
992
920
|
] }),
|
|
993
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setConfirmDeleteId(
|
|
994
|
-
] },
|
|
921
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setConfirmDeleteId(p.id), className: "text-xs text-ui-fg-error hover:underline", children: t("delete") })
|
|
922
|
+
] }, p.id)
|
|
995
923
|
) }),
|
|
996
|
-
showAddForm && /* @__PURE__ */ jsxRuntime.
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "Число" }),
|
|
1001
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: "Файл" }),
|
|
1002
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "Да/Нет" })
|
|
1003
|
-
] }),
|
|
1004
|
-
addForm.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: addForm.unit, onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })), placeholder: "ед.", className: "w-28 h-8 text-sm" }),
|
|
1005
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: handleAdd, isLoading: createMutation.isPending, children: "Создать" }),
|
|
1006
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: () => {
|
|
1007
|
-
setShowAddForm(false);
|
|
1008
|
-
setAddForm(emptyForm());
|
|
1009
|
-
}, children: "Отмена" })
|
|
1010
|
-
] })
|
|
924
|
+
showAddForm && /* @__PURE__ */ jsxRuntime.jsx(AddForm, { t, form: addForm, setForm: setAddForm, onCancel: () => {
|
|
925
|
+
setShowAddForm(false);
|
|
926
|
+
setAddForm(emptyForm());
|
|
927
|
+
}, onSubmit: handleAdd, isLoading: createMutation.isPending, withDescription: true })
|
|
1011
928
|
] });
|
|
1012
929
|
};
|
|
930
|
+
const AddForm = ({
|
|
931
|
+
t,
|
|
932
|
+
form,
|
|
933
|
+
setForm,
|
|
934
|
+
onCancel,
|
|
935
|
+
onSubmit,
|
|
936
|
+
isLoading,
|
|
937
|
+
withDescription
|
|
938
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 px-6 py-4", children: [
|
|
939
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
940
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
941
|
+
ui.Input,
|
|
942
|
+
{
|
|
943
|
+
value: form.label,
|
|
944
|
+
onChange: (e) => setForm((f) => ({ ...f, label: e.target.value })),
|
|
945
|
+
placeholder: t("name"),
|
|
946
|
+
className: "flex-1 h-8 text-sm",
|
|
947
|
+
autoFocus: true
|
|
948
|
+
}
|
|
949
|
+
),
|
|
950
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
951
|
+
"select",
|
|
952
|
+
{
|
|
953
|
+
value: form.type,
|
|
954
|
+
onChange: (e) => setForm((f) => ({
|
|
955
|
+
...f,
|
|
956
|
+
type: e.target.value,
|
|
957
|
+
unit: e.target.value === "number" ? f.unit : ""
|
|
958
|
+
})),
|
|
959
|
+
className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
960
|
+
children: [
|
|
961
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: t("type.text") }),
|
|
962
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: t("type.number") }),
|
|
963
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: t("type.file") }),
|
|
964
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: t("type.boolean") })
|
|
965
|
+
]
|
|
966
|
+
}
|
|
967
|
+
),
|
|
968
|
+
form.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
969
|
+
ui.Input,
|
|
970
|
+
{
|
|
971
|
+
value: form.unit,
|
|
972
|
+
onChange: (e) => setForm((f) => ({ ...f, unit: e.target.value })),
|
|
973
|
+
placeholder: t("unit"),
|
|
974
|
+
className: "w-28 h-8 text-sm"
|
|
975
|
+
}
|
|
976
|
+
)
|
|
977
|
+
] }),
|
|
978
|
+
withDescription && /* @__PURE__ */ jsxRuntime.jsx(
|
|
979
|
+
ui.Input,
|
|
980
|
+
{
|
|
981
|
+
value: form.description,
|
|
982
|
+
onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })),
|
|
983
|
+
placeholder: t("description"),
|
|
984
|
+
className: "h-8 text-sm"
|
|
985
|
+
}
|
|
986
|
+
),
|
|
987
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
988
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: onCancel, children: t("cancel") }),
|
|
989
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: onSubmit, isLoading, children: t("create") })
|
|
990
|
+
] })
|
|
991
|
+
] });
|
|
1013
992
|
const config = adminSdk.defineRouteConfig({
|
|
1014
|
-
label: "
|
|
1015
|
-
icon:
|
|
993
|
+
label: "Product Attributes",
|
|
994
|
+
icon: Tag
|
|
1016
995
|
});
|
|
1017
996
|
const widgetModule = { widgets: [
|
|
1018
997
|
{
|
|
@@ -1027,29 +1006,17 @@ const widgetModule = { widgets: [
|
|
|
1027
1006
|
const routeModule = {
|
|
1028
1007
|
routes: [
|
|
1029
1008
|
{
|
|
1030
|
-
Component:
|
|
1031
|
-
path: "/settings/
|
|
1032
|
-
},
|
|
1033
|
-
{
|
|
1034
|
-
Component: GlobalAttributesSettingsPage,
|
|
1035
|
-
path: "/settings/global-attributes"
|
|
1009
|
+
Component: ProductAttributesSettingsPage,
|
|
1010
|
+
path: "/settings/product-attributes"
|
|
1036
1011
|
}
|
|
1037
1012
|
]
|
|
1038
1013
|
};
|
|
1039
1014
|
const menuItemModule = {
|
|
1040
1015
|
menuItems: [
|
|
1041
|
-
{
|
|
1042
|
-
label: config$1.label,
|
|
1043
|
-
icon: config$1.icon,
|
|
1044
|
-
path: "/settings/attribute-templates",
|
|
1045
|
-
nested: void 0,
|
|
1046
|
-
rank: void 0,
|
|
1047
|
-
translationNs: void 0
|
|
1048
|
-
},
|
|
1049
1016
|
{
|
|
1050
1017
|
label: config.label,
|
|
1051
1018
|
icon: config.icon,
|
|
1052
|
-
path: "/settings/
|
|
1019
|
+
path: "/settings/product-attributes",
|
|
1053
1020
|
nested: void 0,
|
|
1054
1021
|
rank: void 0,
|
|
1055
1022
|
translationNs: void 0
|