@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
|
@@ -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,
|
|
@@ -160,14 +168,16 @@ const en = {
|
|
|
160
168
|
};
|
|
161
169
|
const locales = { ru, en };
|
|
162
170
|
function detectLang() {
|
|
163
|
-
var _a;
|
|
171
|
+
var _a, _b, _c;
|
|
164
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();
|
|
168
176
|
if (locales[short]) return short;
|
|
169
177
|
}
|
|
170
|
-
const
|
|
178
|
+
const htmlLang = (_b = (_a = document.documentElement) == null ? void 0 : _a.lang) == null ? void 0 : _b.slice(0, 2).toLowerCase();
|
|
179
|
+
if (htmlLang && locales[htmlLang]) return htmlLang;
|
|
180
|
+
const nav = (((_c = window.navigator) == null ? void 0 : _c.language) || "en").slice(0, 2).toLowerCase();
|
|
171
181
|
return locales[nav] ? nav : "en";
|
|
172
182
|
}
|
|
173
183
|
function useT() {
|
|
@@ -177,7 +187,7 @@ function useT() {
|
|
|
177
187
|
}, []);
|
|
178
188
|
return (key, fallback) => dict[key] ?? fallback ?? key;
|
|
179
189
|
}
|
|
180
|
-
const emptyForm$
|
|
190
|
+
const emptyForm$1 = () => ({ label: "", type: "text", unit: "" });
|
|
181
191
|
const CategoryAttributeTemplatesWidget = ({
|
|
182
192
|
data
|
|
183
193
|
}) => {
|
|
@@ -188,7 +198,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
188
198
|
const queryKey = ["category-custom-attributes", categoryId];
|
|
189
199
|
const [showAddForm, setShowAddForm] = useState(false);
|
|
190
200
|
const [showTemplateList, setShowTemplateList] = useState(false);
|
|
191
|
-
const [addForm, setAddForm] = useState(emptyForm$
|
|
201
|
+
const [addForm, setAddForm] = useState(emptyForm$1());
|
|
192
202
|
const [confirmDeleteId, setConfirmDeleteId] = useState(null);
|
|
193
203
|
const [mutationError, setMutationError] = useState(null);
|
|
194
204
|
const templatesQuery = useQuery({
|
|
@@ -206,7 +216,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
206
216
|
setShowTemplateList(false);
|
|
207
217
|
},
|
|
208
218
|
onError: (err) => {
|
|
209
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
219
|
+
setMutationError((err == null ? void 0 : err.message) || t("applyTemplateError"));
|
|
210
220
|
}
|
|
211
221
|
});
|
|
212
222
|
const {
|
|
@@ -228,11 +238,11 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
228
238
|
onSuccess: () => {
|
|
229
239
|
qc.invalidateQueries({ queryKey });
|
|
230
240
|
setShowAddForm(false);
|
|
231
|
-
setAddForm(emptyForm$
|
|
241
|
+
setAddForm(emptyForm$1());
|
|
232
242
|
setMutationError(null);
|
|
233
243
|
},
|
|
234
244
|
onError: (err) => {
|
|
235
|
-
setMutationError((err == null ? void 0 : err.message) || "
|
|
245
|
+
setMutationError((err == null ? void 0 : err.message) || t("createError"));
|
|
236
246
|
}
|
|
237
247
|
});
|
|
238
248
|
const deleteMutation = useMutation({
|
|
@@ -253,7 +263,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
253
263
|
unit: addForm.type === "number" && addForm.unit.trim() ? addForm.unit.trim() : null
|
|
254
264
|
});
|
|
255
265
|
};
|
|
256
|
-
const
|
|
266
|
+
const typeLabel = (v) => t(`type.${v}`, v);
|
|
257
267
|
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
258
268
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
259
269
|
/* @__PURE__ */ jsx(Heading, { level: "h2", children: t("attributes") }),
|
|
@@ -267,35 +277,32 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
267
277
|
children: t("fromTemplate")
|
|
268
278
|
}
|
|
269
279
|
),
|
|
270
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ jsx(
|
|
271
281
|
Button,
|
|
272
282
|
{
|
|
273
283
|
variant: "secondary",
|
|
274
284
|
size: "small",
|
|
275
285
|
onClick: () => setShowAddForm(true),
|
|
276
|
-
children:
|
|
277
|
-
"+ ",
|
|
278
|
-
t("add")
|
|
279
|
-
]
|
|
286
|
+
children: t("add")
|
|
280
287
|
}
|
|
281
288
|
)
|
|
282
289
|
] })
|
|
283
290
|
] }),
|
|
284
291
|
showTemplateList && /* @__PURE__ */ jsxs("div", { className: "px-6 py-3", children: [
|
|
285
292
|
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
286
|
-
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "
|
|
293
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: t("chooseTemplate") }),
|
|
287
294
|
/* @__PURE__ */ jsx(
|
|
288
295
|
Button,
|
|
289
296
|
{
|
|
290
297
|
size: "small",
|
|
291
298
|
variant: "secondary",
|
|
292
299
|
onClick: () => setShowTemplateList(false),
|
|
293
|
-
children: "
|
|
300
|
+
children: t("close")
|
|
294
301
|
}
|
|
295
302
|
)
|
|
296
303
|
] }),
|
|
297
304
|
templatesQuery.isLoading && /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }),
|
|
298
|
-
((_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") }),
|
|
299
306
|
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: (_b = templatesQuery.data) == null ? void 0 : _b.attribute_templates.map((p) => /* @__PURE__ */ jsxs(
|
|
300
307
|
"button",
|
|
301
308
|
{
|
|
@@ -305,7 +312,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
305
312
|
children: [
|
|
306
313
|
/* @__PURE__ */ jsx("span", { children: p.label }),
|
|
307
314
|
/* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
|
|
308
|
-
|
|
315
|
+
typeLabel(p.type),
|
|
309
316
|
p.unit ? `, ${p.unit}` : ""
|
|
310
317
|
] })
|
|
311
318
|
]
|
|
@@ -314,9 +321,9 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
314
321
|
)) })
|
|
315
322
|
] }),
|
|
316
323
|
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
317
|
-
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") }) }),
|
|
318
325
|
inheritedAttributes.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
319
|
-
/* @__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") }) }),
|
|
320
327
|
/* @__PURE__ */ jsx("div", { className: "divide-y", children: inheritedAttributes.map((attr) => /* @__PURE__ */ jsxs(
|
|
321
328
|
"div",
|
|
322
329
|
{
|
|
@@ -324,28 +331,23 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
324
331
|
children: [
|
|
325
332
|
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm text-ui-fg-subtle", children: attr.label }),
|
|
326
333
|
/* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
|
|
327
|
-
|
|
334
|
+
typeLabel(attr.type),
|
|
328
335
|
attr.unit ? `, ${attr.unit}` : ""
|
|
329
|
-
] })
|
|
330
|
-
/* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "blue", children: "из родителя" })
|
|
336
|
+
] })
|
|
331
337
|
]
|
|
332
338
|
},
|
|
333
339
|
attr.id
|
|
334
340
|
)) })
|
|
335
341
|
] }),
|
|
336
342
|
ownAttributes.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
337
|
-
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") }) }),
|
|
338
344
|
/* @__PURE__ */ jsx("div", { className: "divide-y", children: ownAttributes.map(
|
|
339
345
|
(attr) => confirmDeleteId === attr.id ? /* @__PURE__ */ jsxs(
|
|
340
346
|
"div",
|
|
341
347
|
{
|
|
342
348
|
className: "flex items-center gap-3 px-6 py-3 text-sm",
|
|
343
349
|
children: [
|
|
344
|
-
/* @__PURE__ */
|
|
345
|
-
"Удалить «",
|
|
346
|
-
attr.label,
|
|
347
|
-
"»?"
|
|
348
|
-
] }),
|
|
350
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-ui-fg-base", children: t("confirmDelete").replace("{name}", attr.label) }),
|
|
349
351
|
/* @__PURE__ */ jsx(
|
|
350
352
|
Button,
|
|
351
353
|
{
|
|
@@ -353,7 +355,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
353
355
|
variant: "danger",
|
|
354
356
|
onClick: () => deleteMutation.mutate(attr.id),
|
|
355
357
|
isLoading: deleteMutation.isPending,
|
|
356
|
-
children: "
|
|
358
|
+
children: t("delete")
|
|
357
359
|
}
|
|
358
360
|
),
|
|
359
361
|
/* @__PURE__ */ jsx(
|
|
@@ -362,7 +364,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
362
364
|
size: "small",
|
|
363
365
|
variant: "secondary",
|
|
364
366
|
onClick: () => setConfirmDeleteId(null),
|
|
365
|
-
children: "
|
|
367
|
+
children: t("cancel")
|
|
366
368
|
}
|
|
367
369
|
)
|
|
368
370
|
]
|
|
@@ -375,7 +377,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
375
377
|
children: [
|
|
376
378
|
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm text-ui-fg-base", children: attr.label }),
|
|
377
379
|
/* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
|
|
378
|
-
|
|
380
|
+
typeLabel(attr.type),
|
|
379
381
|
attr.unit ? `, ${attr.unit}` : ""
|
|
380
382
|
] }),
|
|
381
383
|
/* @__PURE__ */ jsx(
|
|
@@ -383,7 +385,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
383
385
|
{
|
|
384
386
|
onClick: () => setConfirmDeleteId(attr.id),
|
|
385
387
|
className: "text-xs text-ui-fg-error hover:underline",
|
|
386
|
-
children: "
|
|
388
|
+
children: t("delete")
|
|
387
389
|
}
|
|
388
390
|
)
|
|
389
391
|
]
|
|
@@ -392,14 +394,14 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
392
394
|
)
|
|
393
395
|
) })
|
|
394
396
|
] }),
|
|
395
|
-
!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") }) }),
|
|
396
398
|
showAddForm && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-6 py-3", children: [
|
|
397
399
|
/* @__PURE__ */ jsx(
|
|
398
400
|
Input,
|
|
399
401
|
{
|
|
400
402
|
value: addForm.label,
|
|
401
403
|
onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })),
|
|
402
|
-
placeholder: "
|
|
404
|
+
placeholder: t("name"),
|
|
403
405
|
className: "flex-1 h-8 text-sm",
|
|
404
406
|
autoFocus: true
|
|
405
407
|
}
|
|
@@ -415,10 +417,10 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
415
417
|
})),
|
|
416
418
|
className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
417
419
|
children: [
|
|
418
|
-
/* @__PURE__ */ jsx("option", { value: "text", children: "
|
|
419
|
-
/* @__PURE__ */ jsx("option", { value: "number", children: "
|
|
420
|
-
/* @__PURE__ */ jsx("option", { value: "file", children: "
|
|
421
|
-
/* @__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") })
|
|
422
424
|
]
|
|
423
425
|
}
|
|
424
426
|
),
|
|
@@ -427,7 +429,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
427
429
|
{
|
|
428
430
|
value: addForm.unit,
|
|
429
431
|
onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
|
|
430
|
-
placeholder: "
|
|
432
|
+
placeholder: t("unit"),
|
|
431
433
|
className: "w-28 h-8 text-sm"
|
|
432
434
|
}
|
|
433
435
|
),
|
|
@@ -437,7 +439,7 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
437
439
|
size: "small",
|
|
438
440
|
onClick: handleAdd,
|
|
439
441
|
isLoading: createMutation.isPending,
|
|
440
|
-
children: "
|
|
442
|
+
children: t("add")
|
|
441
443
|
}
|
|
442
444
|
),
|
|
443
445
|
/* @__PURE__ */ jsx(
|
|
@@ -447,10 +449,10 @@ const CategoryAttributeTemplatesWidget = ({
|
|
|
447
449
|
size: "small",
|
|
448
450
|
onClick: () => {
|
|
449
451
|
setShowAddForm(false);
|
|
450
|
-
setAddForm(emptyForm$
|
|
452
|
+
setAddForm(emptyForm$1());
|
|
451
453
|
setMutationError(null);
|
|
452
454
|
},
|
|
453
|
-
children: "
|
|
455
|
+
children: t("cancel")
|
|
454
456
|
}
|
|
455
457
|
)
|
|
456
458
|
] }),
|
|
@@ -466,6 +468,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
466
468
|
var _a;
|
|
467
469
|
const productId = data.id;
|
|
468
470
|
const productHandle = data.handle || productId;
|
|
471
|
+
const t = useT();
|
|
469
472
|
const qc = useQueryClient();
|
|
470
473
|
const [formValues, setFormValues] = useState({});
|
|
471
474
|
const [saveSuccess, setSaveSuccess] = useState(false);
|
|
@@ -540,7 +543,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
540
543
|
if (!response.ok) {
|
|
541
544
|
const text = await response.text();
|
|
542
545
|
console.error("Upload failed", response.status, text);
|
|
543
|
-
alert(
|
|
546
|
+
alert(`${t("uploadError")}: ${response.status}`);
|
|
544
547
|
return;
|
|
545
548
|
}
|
|
546
549
|
const res = await response.json();
|
|
@@ -549,11 +552,11 @@ const ProductAttributeValuesWidget = ({
|
|
|
549
552
|
setFormValues((prev) => ({ ...prev, [attrId]: url }));
|
|
550
553
|
} else {
|
|
551
554
|
console.error("No URL in upload response", res);
|
|
552
|
-
alert("
|
|
555
|
+
alert(t("uploadError"));
|
|
553
556
|
}
|
|
554
557
|
} catch (err) {
|
|
555
558
|
console.error("Upload error", err);
|
|
556
|
-
alert("
|
|
559
|
+
alert(t("uploadError"));
|
|
557
560
|
} finally {
|
|
558
561
|
setUploadingId(null);
|
|
559
562
|
}
|
|
@@ -562,10 +565,10 @@ const ProductAttributeValuesWidget = ({
|
|
|
562
565
|
const isError = attributesQuery.isError || valuesQuery.isError;
|
|
563
566
|
const attributes2 = ((_a = attributesQuery.data) == null ? void 0 : _a.attributes) ?? [];
|
|
564
567
|
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
565
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "
|
|
566
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
567
|
-
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: "
|
|
568
|
-
!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") }) }),
|
|
569
572
|
!isLoading && !isError && attributes2.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
570
573
|
/* @__PURE__ */ jsx("div", { className: "divide-y", children: attributes2.map((attr) => /* @__PURE__ */ jsxs(
|
|
571
574
|
"div",
|
|
@@ -587,8 +590,8 @@ const ProductAttributeValuesWidget = ({
|
|
|
587
590
|
className: "h-8 flex-1 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
588
591
|
children: [
|
|
589
592
|
/* @__PURE__ */ jsx("option", { value: "", children: "—" }),
|
|
590
|
-
/* @__PURE__ */ jsx("option", { value: "true", children: "
|
|
591
|
-
/* @__PURE__ */ jsx("option", { value: "false", children: "
|
|
593
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: t("yes") }),
|
|
594
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: t("no") })
|
|
592
595
|
]
|
|
593
596
|
}
|
|
594
597
|
) : attr.type === "file" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -615,7 +618,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
615
618
|
...prev,
|
|
616
619
|
[attr.id]: e.target.value
|
|
617
620
|
})),
|
|
618
|
-
placeholder: "
|
|
621
|
+
placeholder: t("fileUrl"),
|
|
619
622
|
className: "h-8 text-sm flex-1"
|
|
620
623
|
}
|
|
621
624
|
),
|
|
@@ -630,7 +633,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
630
633
|
return (_a2 = fileInputs.current[attr.id]) == null ? void 0 : _a2.click();
|
|
631
634
|
},
|
|
632
635
|
isLoading: uploadingId === attr.id,
|
|
633
|
-
children: "
|
|
636
|
+
children: t("upload")
|
|
634
637
|
}
|
|
635
638
|
)
|
|
636
639
|
] }) : /* @__PURE__ */ jsx(
|
|
@@ -642,7 +645,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
642
645
|
...prev,
|
|
643
646
|
[attr.id]: e.target.value
|
|
644
647
|
})),
|
|
645
|
-
placeholder: attr.type === "number" ? "0" : "
|
|
648
|
+
placeholder: attr.type === "number" ? "0" : t("value"),
|
|
646
649
|
className: "h-8 text-sm flex-1"
|
|
647
650
|
}
|
|
648
651
|
) })
|
|
@@ -656,7 +659,7 @@ const ProductAttributeValuesWidget = ({
|
|
|
656
659
|
size: "small",
|
|
657
660
|
onClick: handleSave,
|
|
658
661
|
isLoading: saveMutation.isPending,
|
|
659
|
-
children: saveSuccess ? "
|
|
662
|
+
children: saveSuccess ? t("saved") : t("save")
|
|
660
663
|
}
|
|
661
664
|
) })
|
|
662
665
|
] })
|
|
@@ -665,71 +668,6 @@ const ProductAttributeValuesWidget = ({
|
|
|
665
668
|
defineWidgetConfig({
|
|
666
669
|
zone: "product.details.after"
|
|
667
670
|
});
|
|
668
|
-
var __defProp$1 = Object.defineProperty;
|
|
669
|
-
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
|
|
670
|
-
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
671
|
-
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
|
|
672
|
-
var __defNormalProp$1 = (obj, key, value2) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
|
|
673
|
-
var __spreadValues$1 = (a, b) => {
|
|
674
|
-
for (var prop in b || (b = {}))
|
|
675
|
-
if (__hasOwnProp$1.call(b, prop))
|
|
676
|
-
__defNormalProp$1(a, prop, b[prop]);
|
|
677
|
-
if (__getOwnPropSymbols$1)
|
|
678
|
-
for (var prop of __getOwnPropSymbols$1(b)) {
|
|
679
|
-
if (__propIsEnum$1.call(b, prop))
|
|
680
|
-
__defNormalProp$1(a, prop, b[prop]);
|
|
681
|
-
}
|
|
682
|
-
return a;
|
|
683
|
-
};
|
|
684
|
-
var __objRest$1 = (source, exclude) => {
|
|
685
|
-
var target = {};
|
|
686
|
-
for (var prop in source)
|
|
687
|
-
if (__hasOwnProp$1.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
688
|
-
target[prop] = source[prop];
|
|
689
|
-
if (source != null && __getOwnPropSymbols$1)
|
|
690
|
-
for (var prop of __getOwnPropSymbols$1(source)) {
|
|
691
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum$1.call(source, prop))
|
|
692
|
-
target[prop] = source[prop];
|
|
693
|
-
}
|
|
694
|
-
return target;
|
|
695
|
-
};
|
|
696
|
-
const Globe = React.forwardRef(
|
|
697
|
-
(_a, ref) => {
|
|
698
|
-
var _b = _a, { color = "currentColor" } = _b, props = __objRest$1(_b, ["color"]);
|
|
699
|
-
return /* @__PURE__ */ React.createElement(
|
|
700
|
-
"svg",
|
|
701
|
-
__spreadValues$1({
|
|
702
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
703
|
-
width: 15,
|
|
704
|
-
height: 15,
|
|
705
|
-
viewBox: "0 0 15 15",
|
|
706
|
-
fill: "none",
|
|
707
|
-
ref
|
|
708
|
-
}, props),
|
|
709
|
-
/* @__PURE__ */ React.createElement(
|
|
710
|
-
"path",
|
|
711
|
-
{
|
|
712
|
-
stroke: color,
|
|
713
|
-
strokeLinecap: "round",
|
|
714
|
-
strokeLinejoin: "round",
|
|
715
|
-
strokeWidth: 1.5,
|
|
716
|
-
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"
|
|
717
|
-
}
|
|
718
|
-
),
|
|
719
|
-
/* @__PURE__ */ React.createElement(
|
|
720
|
-
"path",
|
|
721
|
-
{
|
|
722
|
-
stroke: color,
|
|
723
|
-
strokeLinecap: "round",
|
|
724
|
-
strokeLinejoin: "round",
|
|
725
|
-
strokeWidth: 1.5,
|
|
726
|
-
d: "M7.5 13.945a6.444 6.444 0 1 0 0-12.89 6.444 6.444 0 0 0 0 12.89"
|
|
727
|
-
}
|
|
728
|
-
)
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
|
-
);
|
|
732
|
-
Globe.displayName = "Globe";
|
|
733
671
|
var __defProp = Object.defineProperty;
|
|
734
672
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
735
673
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
@@ -758,7 +696,7 @@ var __objRest = (source, exclude) => {
|
|
|
758
696
|
}
|
|
759
697
|
return target;
|
|
760
698
|
};
|
|
761
|
-
const
|
|
699
|
+
const Tag = React.forwardRef(
|
|
762
700
|
(_a, ref) => {
|
|
763
701
|
var _b = _a, { color = "currentColor" } = _b, props = __objRest(_b, ["color"]);
|
|
764
702
|
return /* @__PURE__ */ React.createElement(
|
|
@@ -771,56 +709,73 @@ const SquaresPlus = React.forwardRef(
|
|
|
771
709
|
fill: "none",
|
|
772
710
|
ref
|
|
773
711
|
}, props),
|
|
774
|
-
/* @__PURE__ */ React.createElement(
|
|
712
|
+
/* @__PURE__ */ React.createElement("g", { clipPath: "url(#a)" }, /* @__PURE__ */ React.createElement(
|
|
775
713
|
"path",
|
|
776
714
|
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
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"
|
|
715
|
+
fill: color,
|
|
716
|
+
fillRule: "evenodd",
|
|
717
|
+
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",
|
|
718
|
+
clipRule: "evenodd"
|
|
782
719
|
}
|
|
783
|
-
)
|
|
720
|
+
)),
|
|
721
|
+
/* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("clipPath", { id: "a" }, /* @__PURE__ */ React.createElement("path", { fill: "#fff", d: "M0 0h15v15H0z" })))
|
|
784
722
|
);
|
|
785
723
|
}
|
|
786
724
|
);
|
|
787
|
-
|
|
788
|
-
const emptyForm
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
725
|
+
Tag.displayName = "Tag";
|
|
726
|
+
const emptyForm = () => ({ label: "", type: "text", unit: "", description: "" });
|
|
727
|
+
const ProductAttributesSettingsPage = () => {
|
|
728
|
+
const t = useT();
|
|
729
|
+
const [tab, setTab] = useState("globals");
|
|
730
|
+
return /* @__PURE__ */ jsxs(Container, { className: "p-0", children: [
|
|
731
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-6 border-b border-ui-border-base px-6 pt-4", children: [
|
|
732
|
+
/* @__PURE__ */ jsx(TabButton, { active: tab === "globals", onClick: () => setTab("globals"), children: t("globals") }),
|
|
733
|
+
/* @__PURE__ */ jsx(TabButton, { active: tab === "templates", onClick: () => setTab("templates"), children: t("templates") })
|
|
734
|
+
] }),
|
|
735
|
+
tab === "globals" ? /* @__PURE__ */ jsx(GlobalsTab, {}) : /* @__PURE__ */ jsx(TemplatesTab, {})
|
|
736
|
+
] });
|
|
737
|
+
};
|
|
738
|
+
const TabButton = ({
|
|
739
|
+
active,
|
|
740
|
+
onClick,
|
|
741
|
+
children
|
|
742
|
+
}) => /* @__PURE__ */ jsx(
|
|
743
|
+
"button",
|
|
744
|
+
{
|
|
745
|
+
onClick,
|
|
746
|
+
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"}`,
|
|
747
|
+
children
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
const GlobalsTab = () => {
|
|
751
|
+
const t = useT();
|
|
796
752
|
const qc = useQueryClient();
|
|
797
|
-
const queryKey = ["
|
|
753
|
+
const queryKey = ["global-attributes"];
|
|
798
754
|
const [showAddForm, setShowAddForm] = useState(false);
|
|
799
|
-
const [addForm, setAddForm] = useState(emptyForm
|
|
755
|
+
const [addForm, setAddForm] = useState(emptyForm());
|
|
800
756
|
const [confirmDeleteId, setConfirmDeleteId] = useState(null);
|
|
801
757
|
const { data, isLoading, isError } = useQuery({
|
|
802
758
|
queryKey,
|
|
803
|
-
queryFn: () => sdk.client.fetch(`/admin/
|
|
759
|
+
queryFn: () => sdk.client.fetch(`/admin/global-attributes`)
|
|
804
760
|
});
|
|
805
|
-
const
|
|
761
|
+
const attrs = (data == null ? void 0 : data.global_attributes) ?? [];
|
|
806
762
|
const createMutation = useMutation({
|
|
807
|
-
mutationFn: (body) => sdk.client.fetch(`/admin/
|
|
763
|
+
mutationFn: (body) => sdk.client.fetch(`/admin/global-attributes`, {
|
|
808
764
|
method: "POST",
|
|
809
765
|
body: {
|
|
810
766
|
label: body.label,
|
|
811
767
|
type: body.type,
|
|
812
|
-
unit: body.type === "number" && body.unit ? body.unit : null
|
|
813
|
-
description: body.description || null
|
|
768
|
+
unit: body.type === "number" && body.unit ? body.unit : null
|
|
814
769
|
}
|
|
815
770
|
}),
|
|
816
771
|
onSuccess: () => {
|
|
817
772
|
qc.invalidateQueries({ queryKey });
|
|
818
773
|
setShowAddForm(false);
|
|
819
|
-
setAddForm(emptyForm
|
|
774
|
+
setAddForm(emptyForm());
|
|
820
775
|
}
|
|
821
776
|
});
|
|
822
777
|
const deleteMutation = useMutation({
|
|
823
|
-
mutationFn: (id) => sdk.client.fetch(`/admin/
|
|
778
|
+
mutationFn: (id) => sdk.client.fetch(`/admin/global-attributes`, {
|
|
824
779
|
method: "PATCH",
|
|
825
780
|
body: { id, deleted_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
826
781
|
}),
|
|
@@ -838,86 +793,57 @@ const AttributeTemplatesSettingsPage = () => {
|
|
|
838
793
|
description: addForm.description.trim()
|
|
839
794
|
});
|
|
840
795
|
};
|
|
841
|
-
return /* @__PURE__ */ jsxs(
|
|
842
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-
|
|
796
|
+
return /* @__PURE__ */ jsxs("div", { className: "divide-y", children: [
|
|
797
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
843
798
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
844
|
-
/* @__PURE__ */ jsx(Heading, { level: "
|
|
845
|
-
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "
|
|
799
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: t("globals") }),
|
|
800
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: t("globalsDesc") })
|
|
846
801
|
] }),
|
|
847
|
-
!showAddForm && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: "
|
|
802
|
+
!showAddForm && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: t("add") })
|
|
848
803
|
] }),
|
|
849
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
850
|
-
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: "
|
|
851
|
-
!isLoading && !isError &&
|
|
852
|
-
/* @__PURE__ */ jsx("div", { className: "divide-y", children:
|
|
853
|
-
(
|
|
854
|
-
/* @__PURE__ */
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
/* @__PURE__ */
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
863
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
864
|
-
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: p.label }),
|
|
865
|
-
/* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
|
|
866
|
-
typeLabel$1(p.type),
|
|
867
|
-
p.unit ? `, ${p.unit}` : ""
|
|
868
|
-
] })
|
|
869
|
-
] }),
|
|
870
|
-
p.description && /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: p.description })
|
|
804
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
805
|
+
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: t("loadFailed") }) }),
|
|
806
|
+
!isLoading && !isError && attrs.length === 0 && !showAddForm && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("noGlobals") }) }),
|
|
807
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y", children: attrs.map(
|
|
808
|
+
(a) => confirmDeleteId === a.id ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3 text-sm", children: [
|
|
809
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: t("confirmDelete").replace("{name}", a.label) }),
|
|
810
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "danger", onClick: () => deleteMutation.mutate(a.id), isLoading: deleteMutation.isPending, children: t("delete") }),
|
|
811
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", onClick: () => setConfirmDeleteId(null), children: t("cancel") })
|
|
812
|
+
] }, a.id) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
|
|
813
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm", children: a.label }),
|
|
814
|
+
/* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
|
|
815
|
+
t(`type.${a.type}`, a.type),
|
|
816
|
+
a.unit ? `, ${a.unit}` : ""
|
|
871
817
|
] }),
|
|
872
|
-
/* @__PURE__ */ jsx("button", { onClick: () => setConfirmDeleteId(
|
|
873
|
-
] },
|
|
818
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setConfirmDeleteId(a.id), className: "text-xs text-ui-fg-error hover:underline", children: t("delete") })
|
|
819
|
+
] }, a.id)
|
|
874
820
|
) }),
|
|
875
|
-
showAddForm && /* @__PURE__ */
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
/* @__PURE__ */ jsx("option", { value: "text", children: "Текст" }),
|
|
880
|
-
/* @__PURE__ */ jsx("option", { value: "number", children: "Число" }),
|
|
881
|
-
/* @__PURE__ */ jsx("option", { value: "file", children: "Файл" }),
|
|
882
|
-
/* @__PURE__ */ jsx("option", { value: "boolean", children: "Да/Нет" })
|
|
883
|
-
] }),
|
|
884
|
-
addForm.type === "number" && /* @__PURE__ */ jsx(Input, { value: addForm.unit, onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })), placeholder: "ед.", className: "w-28 h-8 text-sm" })
|
|
885
|
-
] }),
|
|
886
|
-
/* @__PURE__ */ jsx(Input, { value: addForm.description, onChange: (e) => setAddForm((f) => ({ ...f, description: e.target.value })), placeholder: "Описание (необязательно)", className: "h-8 text-sm" }),
|
|
887
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
888
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => {
|
|
889
|
-
setShowAddForm(false);
|
|
890
|
-
setAddForm(emptyForm$1());
|
|
891
|
-
}, children: "Отмена" }),
|
|
892
|
-
/* @__PURE__ */ jsx(Button, { size: "small", onClick: handleAdd, isLoading: createMutation.isPending, children: "Создать" })
|
|
893
|
-
] })
|
|
894
|
-
] })
|
|
821
|
+
showAddForm && /* @__PURE__ */ jsx(AddForm, { t, form: addForm, setForm: setAddForm, onCancel: () => {
|
|
822
|
+
setShowAddForm(false);
|
|
823
|
+
setAddForm(emptyForm());
|
|
824
|
+
}, onSubmit: handleAdd, isLoading: createMutation.isPending, withDescription: false })
|
|
895
825
|
] });
|
|
896
826
|
};
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
icon: SquaresPlus
|
|
900
|
-
});
|
|
901
|
-
const emptyForm = () => ({ label: "", type: "text", unit: "" });
|
|
902
|
-
const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
|
|
903
|
-
const GlobalAttributesSettingsPage = () => {
|
|
827
|
+
const TemplatesTab = () => {
|
|
828
|
+
const t = useT();
|
|
904
829
|
const qc = useQueryClient();
|
|
905
|
-
const queryKey = ["
|
|
830
|
+
const queryKey = ["attribute-templates"];
|
|
906
831
|
const [showAddForm, setShowAddForm] = useState(false);
|
|
907
832
|
const [addForm, setAddForm] = useState(emptyForm());
|
|
908
833
|
const [confirmDeleteId, setConfirmDeleteId] = useState(null);
|
|
909
834
|
const { data, isLoading, isError } = useQuery({
|
|
910
835
|
queryKey,
|
|
911
|
-
queryFn: () => sdk.client.fetch(`/admin/
|
|
836
|
+
queryFn: () => sdk.client.fetch(`/admin/attribute-templates`)
|
|
912
837
|
});
|
|
913
|
-
const
|
|
838
|
+
const templates2 = (data == null ? void 0 : data.attribute_templates) ?? [];
|
|
914
839
|
const createMutation = useMutation({
|
|
915
|
-
mutationFn: (body) => sdk.client.fetch(`/admin/
|
|
840
|
+
mutationFn: (body) => sdk.client.fetch(`/admin/attribute-templates`, {
|
|
916
841
|
method: "POST",
|
|
917
842
|
body: {
|
|
918
843
|
label: body.label,
|
|
919
844
|
type: body.type,
|
|
920
|
-
unit: body.type === "number" && body.unit ? body.unit : null
|
|
845
|
+
unit: body.type === "number" && body.unit ? body.unit : null,
|
|
846
|
+
description: body.description || null
|
|
921
847
|
}
|
|
922
848
|
}),
|
|
923
849
|
onSuccess: () => {
|
|
@@ -927,7 +853,7 @@ const GlobalAttributesSettingsPage = () => {
|
|
|
927
853
|
}
|
|
928
854
|
});
|
|
929
855
|
const deleteMutation = useMutation({
|
|
930
|
-
mutationFn: (id) => sdk.client.fetch(`/admin/
|
|
856
|
+
mutationFn: (id) => sdk.client.fetch(`/admin/attribute-templates`, {
|
|
931
857
|
method: "PATCH",
|
|
932
858
|
body: { id, deleted_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
933
859
|
}),
|
|
@@ -939,60 +865,113 @@ const GlobalAttributesSettingsPage = () => {
|
|
|
939
865
|
const handleAdd = () => {
|
|
940
866
|
if (!addForm.label.trim()) return;
|
|
941
867
|
createMutation.mutate({
|
|
868
|
+
...addForm,
|
|
942
869
|
label: addForm.label.trim(),
|
|
943
|
-
|
|
944
|
-
|
|
870
|
+
unit: addForm.unit.trim(),
|
|
871
|
+
description: addForm.description.trim()
|
|
945
872
|
});
|
|
946
873
|
};
|
|
947
|
-
return /* @__PURE__ */ jsxs(
|
|
948
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-
|
|
874
|
+
return /* @__PURE__ */ jsxs("div", { className: "divide-y", children: [
|
|
875
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
949
876
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
950
|
-
/* @__PURE__ */ jsx(Heading, { level: "
|
|
951
|
-
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "
|
|
877
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: t("templates") }),
|
|
878
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: t("templatesDesc") })
|
|
952
879
|
] }),
|
|
953
|
-
!showAddForm && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: "
|
|
880
|
+
!showAddForm && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => setShowAddForm(true), children: t("add") })
|
|
954
881
|
] }),
|
|
955
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "
|
|
956
|
-
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: "
|
|
957
|
-
!isLoading && !isError &&
|
|
958
|
-
/* @__PURE__ */ jsx("div", { className: "divide-y", children:
|
|
959
|
-
(
|
|
960
|
-
/* @__PURE__ */
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
882
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("loading") }) }),
|
|
883
|
+
isError && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error text-sm", children: t("loadFailed") }) }),
|
|
884
|
+
!isLoading && !isError && templates2.length === 0 && !showAddForm && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: t("noTemplates") }) }),
|
|
885
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y", children: templates2.map(
|
|
886
|
+
(p) => confirmDeleteId === p.id ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3 text-sm", children: [
|
|
887
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1", children: t("confirmDelete").replace("{name}", p.label) }),
|
|
888
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "danger", onClick: () => deleteMutation.mutate(p.id), isLoading: deleteMutation.isPending, children: t("delete") }),
|
|
889
|
+
/* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", onClick: () => setConfirmDeleteId(null), children: t("cancel") })
|
|
890
|
+
] }, p.id) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
|
|
891
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
892
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
893
|
+
/* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: p.label }),
|
|
894
|
+
/* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
|
|
895
|
+
t(`type.${p.type}`, p.type),
|
|
896
|
+
p.unit ? `, ${p.unit}` : ""
|
|
897
|
+
] })
|
|
898
|
+
] }),
|
|
899
|
+
p.description && /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: p.description })
|
|
972
900
|
] }),
|
|
973
|
-
/* @__PURE__ */ jsx("button", { onClick: () => setConfirmDeleteId(
|
|
974
|
-
] },
|
|
901
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setConfirmDeleteId(p.id), className: "text-xs text-ui-fg-error hover:underline", children: t("delete") })
|
|
902
|
+
] }, p.id)
|
|
975
903
|
) }),
|
|
976
|
-
showAddForm && /* @__PURE__ */
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
/* @__PURE__ */ jsx("option", { value: "number", children: "Число" }),
|
|
981
|
-
/* @__PURE__ */ jsx("option", { value: "file", children: "Файл" }),
|
|
982
|
-
/* @__PURE__ */ jsx("option", { value: "boolean", children: "Да/Нет" })
|
|
983
|
-
] }),
|
|
984
|
-
addForm.type === "number" && /* @__PURE__ */ jsx(Input, { value: addForm.unit, onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })), placeholder: "ед.", className: "w-28 h-8 text-sm" }),
|
|
985
|
-
/* @__PURE__ */ jsx(Button, { size: "small", onClick: handleAdd, isLoading: createMutation.isPending, children: "Создать" }),
|
|
986
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => {
|
|
987
|
-
setShowAddForm(false);
|
|
988
|
-
setAddForm(emptyForm());
|
|
989
|
-
}, children: "Отмена" })
|
|
990
|
-
] })
|
|
904
|
+
showAddForm && /* @__PURE__ */ jsx(AddForm, { t, form: addForm, setForm: setAddForm, onCancel: () => {
|
|
905
|
+
setShowAddForm(false);
|
|
906
|
+
setAddForm(emptyForm());
|
|
907
|
+
}, onSubmit: handleAdd, isLoading: createMutation.isPending, withDescription: true })
|
|
991
908
|
] });
|
|
992
909
|
};
|
|
910
|
+
const AddForm = ({
|
|
911
|
+
t,
|
|
912
|
+
form,
|
|
913
|
+
setForm,
|
|
914
|
+
onCancel,
|
|
915
|
+
onSubmit,
|
|
916
|
+
isLoading,
|
|
917
|
+
withDescription
|
|
918
|
+
}) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 px-6 py-4", children: [
|
|
919
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
920
|
+
/* @__PURE__ */ jsx(
|
|
921
|
+
Input,
|
|
922
|
+
{
|
|
923
|
+
value: form.label,
|
|
924
|
+
onChange: (e) => setForm((f) => ({ ...f, label: e.target.value })),
|
|
925
|
+
placeholder: t("name"),
|
|
926
|
+
className: "flex-1 h-8 text-sm",
|
|
927
|
+
autoFocus: true
|
|
928
|
+
}
|
|
929
|
+
),
|
|
930
|
+
/* @__PURE__ */ jsxs(
|
|
931
|
+
"select",
|
|
932
|
+
{
|
|
933
|
+
value: form.type,
|
|
934
|
+
onChange: (e) => setForm((f) => ({
|
|
935
|
+
...f,
|
|
936
|
+
type: e.target.value,
|
|
937
|
+
unit: e.target.value === "number" ? f.unit : ""
|
|
938
|
+
})),
|
|
939
|
+
className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
|
|
940
|
+
children: [
|
|
941
|
+
/* @__PURE__ */ jsx("option", { value: "text", children: t("type.text") }),
|
|
942
|
+
/* @__PURE__ */ jsx("option", { value: "number", children: t("type.number") }),
|
|
943
|
+
/* @__PURE__ */ jsx("option", { value: "file", children: t("type.file") }),
|
|
944
|
+
/* @__PURE__ */ jsx("option", { value: "boolean", children: t("type.boolean") })
|
|
945
|
+
]
|
|
946
|
+
}
|
|
947
|
+
),
|
|
948
|
+
form.type === "number" && /* @__PURE__ */ jsx(
|
|
949
|
+
Input,
|
|
950
|
+
{
|
|
951
|
+
value: form.unit,
|
|
952
|
+
onChange: (e) => setForm((f) => ({ ...f, unit: e.target.value })),
|
|
953
|
+
placeholder: t("unit"),
|
|
954
|
+
className: "w-28 h-8 text-sm"
|
|
955
|
+
}
|
|
956
|
+
)
|
|
957
|
+
] }),
|
|
958
|
+
withDescription && /* @__PURE__ */ jsx(
|
|
959
|
+
Input,
|
|
960
|
+
{
|
|
961
|
+
value: form.description,
|
|
962
|
+
onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })),
|
|
963
|
+
placeholder: t("description"),
|
|
964
|
+
className: "h-8 text-sm"
|
|
965
|
+
}
|
|
966
|
+
),
|
|
967
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
968
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: onCancel, children: t("cancel") }),
|
|
969
|
+
/* @__PURE__ */ jsx(Button, { size: "small", onClick: onSubmit, isLoading, children: t("create") })
|
|
970
|
+
] })
|
|
971
|
+
] });
|
|
993
972
|
const config = defineRouteConfig({
|
|
994
|
-
label: "
|
|
995
|
-
icon:
|
|
973
|
+
label: "Product Attributes",
|
|
974
|
+
icon: Tag
|
|
996
975
|
});
|
|
997
976
|
const widgetModule = { widgets: [
|
|
998
977
|
{
|
|
@@ -1007,29 +986,17 @@ const widgetModule = { widgets: [
|
|
|
1007
986
|
const routeModule = {
|
|
1008
987
|
routes: [
|
|
1009
988
|
{
|
|
1010
|
-
Component:
|
|
1011
|
-
path: "/settings/
|
|
1012
|
-
},
|
|
1013
|
-
{
|
|
1014
|
-
Component: GlobalAttributesSettingsPage,
|
|
1015
|
-
path: "/settings/global-attributes"
|
|
989
|
+
Component: ProductAttributesSettingsPage,
|
|
990
|
+
path: "/settings/product-attributes"
|
|
1016
991
|
}
|
|
1017
992
|
]
|
|
1018
993
|
};
|
|
1019
994
|
const menuItemModule = {
|
|
1020
995
|
menuItems: [
|
|
1021
|
-
{
|
|
1022
|
-
label: config$1.label,
|
|
1023
|
-
icon: config$1.icon,
|
|
1024
|
-
path: "/settings/attribute-templates",
|
|
1025
|
-
nested: void 0,
|
|
1026
|
-
rank: void 0,
|
|
1027
|
-
translationNs: void 0
|
|
1028
|
-
},
|
|
1029
996
|
{
|
|
1030
997
|
label: config.label,
|
|
1031
998
|
icon: config.icon,
|
|
1032
|
-
path: "/settings/
|
|
999
|
+
path: "/settings/product-attributes",
|
|
1033
1000
|
nested: void 0,
|
|
1034
1001
|
rank: void 0,
|
|
1035
1002
|
translationNs: void 0
|