@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.
@@ -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 nav = (((_a = window.navigator) == null ? void 0 : _a.language) || "en").slice(0, 2).toLowerCase();
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$2 = () => ({ label: "", type: "text", unit: "" });
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$2());
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$2());
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 typeLabel2 = (v) => t(`type.${v}`, v);
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__ */ jsxs(
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: "Пресетов нет. Создайте в настройках Product Attributes." }),
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
- typeLabel2(p.type),
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
- typeLabel2(attr.type),
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__ */ jsxs("span", { className: "flex-1 text-ui-fg-base", children: [
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
- typeLabel2(attr.type),
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$2());
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(`Ошибка загрузки файла: ${response.status}`);
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("Файл загружен, но URL не получен");
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: "URL файла",
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 SquaresPlus = React.forwardRef(
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
- stroke: color,
778
- strokeLinecap: "round",
779
- strokeLinejoin: "round",
780
- strokeWidth: 1.5,
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
- SquaresPlus.displayName = "SquaresPlus";
788
- const emptyForm$1 = () => ({
789
- label: "",
790
- type: "text",
791
- unit: "",
792
- description: ""
793
- });
794
- const typeLabel$1 = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
795
- const AttributeTemplatesSettingsPage = () => {
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 = ["attribute-templates"];
753
+ const queryKey = ["global-attributes"];
798
754
  const [showAddForm, setShowAddForm] = useState(false);
799
- const [addForm, setAddForm] = useState(emptyForm$1());
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/attribute-templates`)
759
+ queryFn: () => sdk.client.fetch(`/admin/global-attributes`)
804
760
  });
805
- const templates2 = (data == null ? void 0 : data.attribute_templates) ?? [];
761
+ const attrs = (data == null ? void 0 : data.global_attributes) ?? [];
806
762
  const createMutation = useMutation({
807
- mutationFn: (body) => sdk.client.fetch(`/admin/attribute-templates`, {
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$1());
774
+ setAddForm(emptyForm());
820
775
  }
821
776
  });
822
777
  const deleteMutation = useMutation({
823
- mutationFn: (id) => sdk.client.fetch(`/admin/attribute-templates`, {
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(Container, { className: "divide-y p-0", children: [
842
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
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: "h1", children: "Шаблоны атрибутов" }),
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 && templates2.length === 0 && !showAddForm && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Шаблонов пока нет." }) }),
852
- /* @__PURE__ */ jsx("div", { className: "divide-y", children: templates2.map(
853
- (p) => confirmDeleteId === p.id ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3 text-sm", children: [
854
- /* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
855
- "Удалить «",
856
- p.label,
857
- "»?"
858
- ] }),
859
- /* @__PURE__ */ jsx(Button, { size: "small", variant: "danger", onClick: () => deleteMutation.mutate(p.id), isLoading: deleteMutation.isPending, children: "Удалить" }),
860
- /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", onClick: () => setConfirmDeleteId(null), children: "Отмена" })
861
- ] }, p.id) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
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(p.id), className: "text-xs text-ui-fg-error hover:underline", children: "Удалить" })
873
- ] }, p.id)
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__ */ jsxs("div", { className: "flex flex-col gap-2 px-6 py-4", children: [
876
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
877
- /* @__PURE__ */ jsx(Input, { value: addForm.label, onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })), placeholder: "Название", className: "flex-1 h-8 text-sm", autoFocus: true }),
878
- /* @__PURE__ */ jsxs("select", { value: addForm.type, onChange: (e) => setAddForm((f) => ({ ...f, type: e.target.value, unit: e.target.value === "number" ? f.unit : "" })), className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm", children: [
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 config$1 = defineRouteConfig({
898
- label: "Attribute Templates",
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 = ["global-attributes"];
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/global-attributes`)
836
+ queryFn: () => sdk.client.fetch(`/admin/attribute-templates`)
912
837
  });
913
- const attrs = (data == null ? void 0 : data.global_attributes) ?? [];
838
+ const templates2 = (data == null ? void 0 : data.attribute_templates) ?? [];
914
839
  const createMutation = useMutation({
915
- mutationFn: (body) => sdk.client.fetch(`/admin/global-attributes`, {
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/global-attributes`, {
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
- type: addForm.type,
944
- unit: addForm.unit.trim()
870
+ unit: addForm.unit.trim(),
871
+ description: addForm.description.trim()
945
872
  });
946
873
  };
947
- return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
948
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
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: "h1", children: "Глобальные атрибуты" }),
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 && attrs.length === 0 && !showAddForm && /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Глобальных атрибутов нет." }) }),
958
- /* @__PURE__ */ jsx("div", { className: "divide-y", children: attrs.map(
959
- (a) => confirmDeleteId === a.id ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3 text-sm", children: [
960
- /* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
961
- "Удалить «",
962
- a.label,
963
- "»?"
964
- ] }),
965
- /* @__PURE__ */ jsx(Button, { size: "small", variant: "danger", onClick: () => deleteMutation.mutate(a.id), isLoading: deleteMutation.isPending, children: "Удалить" }),
966
- /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", onClick: () => setConfirmDeleteId(null), children: "Отмена" })
967
- ] }, a.id) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
968
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm", children: a.label }),
969
- /* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
970
- typeLabel(a.type),
971
- a.unit ? `, ${a.unit}` : ""
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(a.id), className: "text-xs text-ui-fg-error hover:underline", children: "Удалить" })
974
- ] }, a.id)
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__ */ jsxs("div", { className: "flex items-center gap-2 px-6 py-4", children: [
977
- /* @__PURE__ */ jsx(Input, { value: addForm.label, onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })), placeholder: "Название", className: "flex-1 h-8 text-sm", autoFocus: true }),
978
- /* @__PURE__ */ jsxs("select", { value: addForm.type, onChange: (e) => setAddForm((f) => ({ ...f, type: e.target.value, unit: e.target.value === "number" ? f.unit : "" })), className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm", children: [
979
- /* @__PURE__ */ jsx("option", { value: "text", children: "Текст" }),
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: "Global Attributes",
995
- icon: Globe
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: AttributeTemplatesSettingsPage,
1011
- path: "/settings/attribute-templates"
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/global-attributes",
999
+ path: "/settings/product-attributes",
1033
1000
  nested: void 0,
1034
1001
  rank: void 0,
1035
1002
  translationNs: void 0