@empty-complete-org/medusa-product-attributes 0.13.0 → 0.14.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.
@@ -3,9 +3,27 @@ const jsxRuntime = require("react/jsx-runtime");
3
3
  const adminSdk = require("@medusajs/admin-sdk");
4
4
  const ui = require("@medusajs/ui");
5
5
  const reactQuery = require("@tanstack/react-query");
6
- const react = require("react");
6
+ const React = require("react");
7
7
  const Medusa = require("@medusajs/js-sdk");
8
8
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
12
+ if (e) {
13
+ for (const k in e) {
14
+ if (k !== "default") {
15
+ const d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: () => e[k]
19
+ });
20
+ }
21
+ }
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+ const React__namespace = /* @__PURE__ */ _interopNamespace(React);
9
27
  const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
10
28
  const sdk = new Medusa__default.default({
11
29
  baseUrl: "/",
@@ -14,17 +32,37 @@ const sdk = new Medusa__default.default({
14
32
  type: "session"
15
33
  }
16
34
  });
17
- const emptyForm = () => ({ label: "", type: "text", unit: "" });
35
+ const emptyForm$1 = () => ({ label: "", type: "text", unit: "" });
18
36
  const CategoryAttributeTemplatesWidget = ({
19
37
  data
20
38
  }) => {
39
+ var _a, _b;
21
40
  const categoryId = data.id;
22
41
  const qc = reactQuery.useQueryClient();
23
42
  const queryKey = ["category-custom-attributes", categoryId];
24
- const [showAddForm, setShowAddForm] = react.useState(false);
25
- const [addForm, setAddForm] = react.useState(emptyForm());
26
- const [confirmDeleteId, setConfirmDeleteId] = react.useState(null);
27
- const [mutationError, setMutationError] = react.useState(null);
43
+ const [showAddForm, setShowAddForm] = React.useState(false);
44
+ const [showPresetList, setShowPresetList] = React.useState(false);
45
+ const [addForm, setAddForm] = React.useState(emptyForm$1());
46
+ const [confirmDeleteId, setConfirmDeleteId] = React.useState(null);
47
+ const [mutationError, setMutationError] = React.useState(null);
48
+ const presetsQuery = reactQuery.useQuery({
49
+ queryKey: ["attribute-presets"],
50
+ queryFn: () => sdk.client.fetch(`/admin/attribute-presets`),
51
+ enabled: showPresetList
52
+ });
53
+ const applyPresetMutation = reactQuery.useMutation({
54
+ mutationFn: (presetId) => sdk.client.fetch(`/admin/attribute-presets/${presetId}/apply`, {
55
+ method: "POST",
56
+ body: { category_id: categoryId }
57
+ }),
58
+ onSuccess: () => {
59
+ qc.invalidateQueries({ queryKey });
60
+ setShowPresetList(false);
61
+ },
62
+ onError: (err) => {
63
+ setMutationError((err == null ? void 0 : err.message) || "Ошибка при применении пресета");
64
+ }
65
+ });
28
66
  const {
29
67
  data: result,
30
68
  isLoading,
@@ -44,7 +82,7 @@ const CategoryAttributeTemplatesWidget = ({
44
82
  onSuccess: () => {
45
83
  qc.invalidateQueries({ queryKey });
46
84
  setShowAddForm(false);
47
- setAddForm(emptyForm());
85
+ setAddForm(emptyForm$1());
48
86
  setMutationError(null);
49
87
  },
50
88
  onError: (err) => {
@@ -69,19 +107,62 @@ const CategoryAttributeTemplatesWidget = ({
69
107
  unit: addForm.type === "number" && addForm.unit.trim() ? addForm.unit.trim() : null
70
108
  });
71
109
  };
72
- const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
110
+ const typeLabel2 = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
73
111
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
74
112
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
75
113
  /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Атрибуты" }),
76
- !showAddForm && /* @__PURE__ */ jsxRuntime.jsx(
77
- ui.Button,
114
+ !showAddForm && !showPresetList && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
115
+ /* @__PURE__ */ jsxRuntime.jsx(
116
+ ui.Button,
117
+ {
118
+ variant: "secondary",
119
+ size: "small",
120
+ onClick: () => setShowPresetList(true),
121
+ children: "Из пресета"
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsxRuntime.jsx(
125
+ ui.Button,
126
+ {
127
+ variant: "secondary",
128
+ size: "small",
129
+ onClick: () => setShowAddForm(true),
130
+ children: "+ Добавить"
131
+ }
132
+ )
133
+ ] })
134
+ ] }),
135
+ showPresetList && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-3", children: [
136
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
137
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Выберите пресет" }),
138
+ /* @__PURE__ */ jsxRuntime.jsx(
139
+ ui.Button,
140
+ {
141
+ size: "small",
142
+ variant: "secondary",
143
+ onClick: () => setShowPresetList(false),
144
+ children: "Закрыть"
145
+ }
146
+ )
147
+ ] }),
148
+ presetsQuery.isLoading && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Загрузка…" }),
149
+ ((_a = presetsQuery.data) == null ? void 0 : _a.attribute_presets.length) === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Пресетов нет. Создайте в настройках Product Attributes." }),
150
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: (_b = presetsQuery.data) == null ? void 0 : _b.attribute_presets.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
151
+ "button",
78
152
  {
79
- variant: "secondary",
80
- size: "small",
81
- onClick: () => setShowAddForm(true),
82
- children: "+ Добавить"
83
- }
84
- )
153
+ onClick: () => applyPresetMutation.mutate(p.id),
154
+ disabled: applyPresetMutation.isPending,
155
+ className: "flex items-center justify-between rounded border border-ui-border-base px-3 py-2 text-left text-sm hover:bg-ui-bg-subtle disabled:opacity-50",
156
+ children: [
157
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: p.label }),
158
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
159
+ typeLabel2(p.type),
160
+ p.unit ? `, ${p.unit}` : ""
161
+ ] })
162
+ ]
163
+ },
164
+ p.id
165
+ )) })
85
166
  ] }),
86
167
  isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Загрузка…" }) }),
87
168
  isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "Не удалось загрузить атрибуты." }) }),
@@ -94,7 +175,7 @@ const CategoryAttributeTemplatesWidget = ({
94
175
  children: [
95
176
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-sm text-ui-fg-subtle", children: attr.label }),
96
177
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
97
- typeLabel(attr.type),
178
+ typeLabel2(attr.type),
98
179
  attr.unit ? `, ${attr.unit}` : ""
99
180
  ] }),
100
181
  /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "blue", children: "из родителя" })
@@ -145,7 +226,7 @@ const CategoryAttributeTemplatesWidget = ({
145
226
  children: [
146
227
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-sm text-ui-fg-base", children: attr.label }),
147
228
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
148
- typeLabel(attr.type),
229
+ typeLabel2(attr.type),
149
230
  attr.unit ? `, ${attr.unit}` : ""
150
231
  ] }),
151
232
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -217,7 +298,7 @@ const CategoryAttributeTemplatesWidget = ({
217
298
  size: "small",
218
299
  onClick: () => {
219
300
  setShowAddForm(false);
220
- setAddForm(emptyForm());
301
+ setAddForm(emptyForm$1());
221
302
  setMutationError(null);
222
303
  },
223
304
  children: "Отмена"
@@ -238,10 +319,10 @@ const ProductAttributeValuesWidget = ({
238
319
  const productHandle = data.handle || productId;
239
320
  const categoryId = ((_b = (_a = data.categories) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? null;
240
321
  const qc = reactQuery.useQueryClient();
241
- const [formValues, setFormValues] = react.useState({});
242
- const [saveSuccess, setSaveSuccess] = react.useState(false);
243
- const [uploadingId, setUploadingId] = react.useState(null);
244
- const fileInputs = react.useRef({});
322
+ const [formValues, setFormValues] = React.useState({});
323
+ const [saveSuccess, setSaveSuccess] = React.useState(false);
324
+ const [uploadingId, setUploadingId] = React.useState(null);
325
+ const fileInputs = React.useRef({});
245
326
  const attributesQuery = reactQuery.useQuery({
246
327
  queryKey: ["category-custom-attributes", categoryId],
247
328
  queryFn: () => sdk.client.fetch(`/admin/category/${categoryId}/custom-attributes`),
@@ -251,7 +332,7 @@ const ProductAttributeValuesWidget = ({
251
332
  queryKey: ["product-custom-attributes", productId],
252
333
  queryFn: () => sdk.client.fetch(`/admin/product/${productId}/custom-attributes`)
253
334
  });
254
- react.useEffect(() => {
335
+ React.useEffect(() => {
255
336
  if (!attributesQuery.data || !valuesQuery.data) return;
256
337
  const attributes2 = attributesQuery.data.category_custom_attributes;
257
338
  const values = valuesQuery.data.product_custom_attributes;
@@ -291,15 +372,19 @@ const ProductAttributeValuesWidget = ({
291
372
  const attrId = attr.id;
292
373
  setUploadingId(attrId);
293
374
  try {
294
- const dot = file.name.lastIndexOf(".");
295
- const ext = dot > -1 ? file.name.slice(dot) : "";
296
- const renamed = new File(
297
- [file],
298
- `${productHandle}_${attr.key}${ext}`,
299
- { type: file.type }
300
- );
375
+ const isImage = file.type.startsWith("image/");
376
+ let toUpload = file;
377
+ if (isImage) {
378
+ const dot = file.name.lastIndexOf(".");
379
+ const ext = dot > -1 ? file.name.slice(dot) : "";
380
+ toUpload = new File(
381
+ [file],
382
+ `${productHandle}_${attr.key}${ext}`,
383
+ { type: file.type }
384
+ );
385
+ }
301
386
  const formData = new FormData();
302
- formData.append("files", renamed);
387
+ formData.append("files", toUpload);
303
388
  const response = await fetch(`/admin/uploads`, {
304
389
  method: "POST",
305
390
  credentials: "include",
@@ -439,6 +524,269 @@ const ProductAttributeValuesWidget = ({
439
524
  adminSdk.defineWidgetConfig({
440
525
  zone: "product.details.after"
441
526
  });
527
+ var __defProp = Object.defineProperty;
528
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
529
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
530
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
531
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
532
+ var __spreadValues = (a, b) => {
533
+ for (var prop in b || (b = {}))
534
+ if (__hasOwnProp.call(b, prop))
535
+ __defNormalProp(a, prop, b[prop]);
536
+ if (__getOwnPropSymbols)
537
+ for (var prop of __getOwnPropSymbols(b)) {
538
+ if (__propIsEnum.call(b, prop))
539
+ __defNormalProp(a, prop, b[prop]);
540
+ }
541
+ return a;
542
+ };
543
+ var __objRest = (source, exclude) => {
544
+ var target = {};
545
+ for (var prop in source)
546
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
547
+ target[prop] = source[prop];
548
+ if (source != null && __getOwnPropSymbols)
549
+ for (var prop of __getOwnPropSymbols(source)) {
550
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
551
+ target[prop] = source[prop];
552
+ }
553
+ return target;
554
+ };
555
+ const CogSixTooth = React__namespace.forwardRef(
556
+ (_a, ref) => {
557
+ var _b = _a, { color = "currentColor" } = _b, props = __objRest(_b, ["color"]);
558
+ return /* @__PURE__ */ React__namespace.createElement(
559
+ "svg",
560
+ __spreadValues({
561
+ xmlns: "http://www.w3.org/2000/svg",
562
+ width: 15,
563
+ height: 15,
564
+ viewBox: "0 0 15 15",
565
+ fill: "none",
566
+ ref
567
+ }, props),
568
+ /* @__PURE__ */ React__namespace.createElement(
569
+ "g",
570
+ {
571
+ stroke: color,
572
+ strokeLinecap: "round",
573
+ strokeLinejoin: "round",
574
+ strokeWidth: 1.5,
575
+ clipPath: "url(#a)"
576
+ },
577
+ /* @__PURE__ */ React__namespace.createElement("path", { d: "M7.5 9.5a2 2 0 1 0 0-4 2 2 0 0 0 0 4" }),
578
+ /* @__PURE__ */ React__namespace.createElement("path", { d: "m12.989 5.97-.826-.292a5 5 0 0 0-.323-.685 5 5 0 0 0-.43-.621l.16-.86a1.43 1.43 0 0 0-.692-1.503l-.312-.18a1.43 1.43 0 0 0-1.647.152l-.663.566a5 5 0 0 0-1.513 0L6.08 1.98a1.43 1.43 0 0 0-1.647-.152l-.312.18a1.43 1.43 0 0 0-.691 1.503l.16.857c-.32.4-.574.841-.758 1.31l-.82.29a1.43 1.43 0 0 0-.956 1.35v.36c0 .608.383 1.15.955 1.35l.826.292c.09.232.194.462.323.684.128.222.275.427.43.622l-.16.86c-.111.597.166 1.2.691 1.503l.312.18a1.43 1.43 0 0 0 1.647-.152l.663-.567a5 5 0 0 0 1.512 0l.663.568a1.43 1.43 0 0 0 1.647.152l.312-.18c.526-.304.803-.906.691-1.502l-.16-.86c.32-.398.575-.84.757-1.308l.822-.29c.572-.202.956-.743.956-1.35v-.36c0-.608-.383-1.149-.956-1.35z" })
579
+ ),
580
+ /* @__PURE__ */ React__namespace.createElement("defs", null, /* @__PURE__ */ React__namespace.createElement("clipPath", { id: "a" }, /* @__PURE__ */ React__namespace.createElement("path", { fill: "#fff", d: "M0 0h15v15H0z" })))
581
+ );
582
+ }
583
+ );
584
+ CogSixTooth.displayName = "CogSixTooth";
585
+ const emptyForm = () => ({
586
+ label: "",
587
+ type: "text",
588
+ unit: "",
589
+ description: ""
590
+ });
591
+ const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
592
+ const ProductAttributesSettingsPage = () => {
593
+ const qc = reactQuery.useQueryClient();
594
+ const queryKey = ["attribute-presets"];
595
+ const [showAddForm, setShowAddForm] = React.useState(false);
596
+ const [addForm, setAddForm] = React.useState(emptyForm());
597
+ const [confirmDeleteId, setConfirmDeleteId] = React.useState(null);
598
+ const { data, isLoading, isError } = reactQuery.useQuery({
599
+ queryKey,
600
+ queryFn: () => sdk.client.fetch(`/admin/attribute-presets`)
601
+ });
602
+ const presets = (data == null ? void 0 : data.attribute_presets) ?? [];
603
+ const createMutation = reactQuery.useMutation({
604
+ mutationFn: (body) => sdk.client.fetch(`/admin/attribute-presets`, {
605
+ method: "POST",
606
+ body: {
607
+ label: body.label,
608
+ type: body.type,
609
+ unit: body.type === "number" && body.unit ? body.unit : null,
610
+ description: body.description || null
611
+ }
612
+ }),
613
+ onSuccess: () => {
614
+ qc.invalidateQueries({ queryKey });
615
+ setShowAddForm(false);
616
+ setAddForm(emptyForm());
617
+ }
618
+ });
619
+ const deleteMutation = reactQuery.useMutation({
620
+ mutationFn: (id) => sdk.client.fetch(`/admin/attribute-presets`, {
621
+ method: "PATCH",
622
+ body: { id, deleted_at: (/* @__PURE__ */ new Date()).toISOString() }
623
+ }),
624
+ onSuccess: () => {
625
+ qc.invalidateQueries({ queryKey });
626
+ setConfirmDeleteId(null);
627
+ }
628
+ });
629
+ const handleAdd = () => {
630
+ if (!addForm.label.trim()) return;
631
+ createMutation.mutate({
632
+ ...addForm,
633
+ label: addForm.label.trim(),
634
+ unit: addForm.unit.trim(),
635
+ description: addForm.description.trim()
636
+ });
637
+ };
638
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
639
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
640
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
641
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Пресеты атрибутов" }),
642
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Глобальные шаблоны атрибутов, которые можно применить к любой категории." })
643
+ ] }),
644
+ !showAddForm && /* @__PURE__ */ jsxRuntime.jsx(
645
+ ui.Button,
646
+ {
647
+ variant: "secondary",
648
+ size: "small",
649
+ onClick: () => setShowAddForm(true),
650
+ children: "+ Добавить пресет"
651
+ }
652
+ )
653
+ ] }),
654
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Загрузка…" }) }),
655
+ isError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error text-sm", children: "Не удалось загрузить пресеты." }) }),
656
+ !isLoading && !isError && presets.length === 0 && !showAddForm && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Пресетов пока нет. Добавьте первый." }) }),
657
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: presets.map(
658
+ (p) => confirmDeleteId === p.id ? /* @__PURE__ */ jsxRuntime.jsxs(
659
+ "div",
660
+ {
661
+ className: "flex items-center gap-3 px-6 py-3 text-sm",
662
+ children: [
663
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1", children: [
664
+ "Удалить «",
665
+ p.label,
666
+ "»?"
667
+ ] }),
668
+ /* @__PURE__ */ jsxRuntime.jsx(
669
+ ui.Button,
670
+ {
671
+ size: "small",
672
+ variant: "danger",
673
+ onClick: () => deleteMutation.mutate(p.id),
674
+ isLoading: deleteMutation.isPending,
675
+ children: "Удалить"
676
+ }
677
+ ),
678
+ /* @__PURE__ */ jsxRuntime.jsx(
679
+ ui.Button,
680
+ {
681
+ size: "small",
682
+ variant: "secondary",
683
+ onClick: () => setConfirmDeleteId(null),
684
+ children: "Отмена"
685
+ }
686
+ )
687
+ ]
688
+ },
689
+ p.id
690
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-6 py-3", children: [
691
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
692
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
693
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: p.label }),
694
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
695
+ typeLabel(p.type),
696
+ p.unit ? `, ${p.unit}` : ""
697
+ ] })
698
+ ] }),
699
+ p.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: p.description })
700
+ ] }),
701
+ /* @__PURE__ */ jsxRuntime.jsx(
702
+ "button",
703
+ {
704
+ onClick: () => setConfirmDeleteId(p.id),
705
+ className: "text-xs text-ui-fg-error hover:underline",
706
+ children: "Удалить"
707
+ }
708
+ )
709
+ ] }, p.id)
710
+ ) }),
711
+ showAddForm && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 px-6 py-4", children: [
712
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
713
+ /* @__PURE__ */ jsxRuntime.jsx(
714
+ ui.Input,
715
+ {
716
+ value: addForm.label,
717
+ onChange: (e) => setAddForm((f) => ({ ...f, label: e.target.value })),
718
+ placeholder: "Название (например, Сертификат)",
719
+ className: "flex-1 h-8 text-sm",
720
+ autoFocus: true
721
+ }
722
+ ),
723
+ /* @__PURE__ */ jsxRuntime.jsxs(
724
+ "select",
725
+ {
726
+ value: addForm.type,
727
+ onChange: (e) => setAddForm((f) => ({
728
+ ...f,
729
+ type: e.target.value,
730
+ unit: e.target.value === "number" ? f.unit : ""
731
+ })),
732
+ className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
733
+ children: [
734
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: "Текст" }),
735
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "Число" }),
736
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: "Файл" }),
737
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "Да/Нет" })
738
+ ]
739
+ }
740
+ ),
741
+ addForm.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(
742
+ ui.Input,
743
+ {
744
+ value: addForm.unit,
745
+ onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
746
+ placeholder: "ед. (кг, м...)",
747
+ className: "w-28 h-8 text-sm"
748
+ }
749
+ )
750
+ ] }),
751
+ /* @__PURE__ */ jsxRuntime.jsx(
752
+ ui.Input,
753
+ {
754
+ value: addForm.description,
755
+ onChange: (e) => setAddForm((f) => ({ ...f, description: e.target.value })),
756
+ placeholder: "Описание (необязательно)",
757
+ className: "h-8 text-sm"
758
+ }
759
+ ),
760
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
761
+ /* @__PURE__ */ jsxRuntime.jsx(
762
+ ui.Button,
763
+ {
764
+ variant: "secondary",
765
+ size: "small",
766
+ onClick: () => {
767
+ setShowAddForm(false);
768
+ setAddForm(emptyForm());
769
+ },
770
+ children: "Отмена"
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsxRuntime.jsx(
774
+ ui.Button,
775
+ {
776
+ size: "small",
777
+ onClick: handleAdd,
778
+ isLoading: createMutation.isPending,
779
+ children: "Создать"
780
+ }
781
+ )
782
+ ] })
783
+ ] })
784
+ ] });
785
+ };
786
+ const config = adminSdk.defineRouteConfig({
787
+ label: "Product Attributes",
788
+ icon: CogSixTooth
789
+ });
442
790
  const widgetModule = { widgets: [
443
791
  {
444
792
  Component: CategoryAttributeTemplatesWidget,
@@ -450,10 +798,24 @@ const widgetModule = { widgets: [
450
798
  }
451
799
  ] };
452
800
  const routeModule = {
453
- routes: []
801
+ routes: [
802
+ {
803
+ Component: ProductAttributesSettingsPage,
804
+ path: "/settings/product-attributes"
805
+ }
806
+ ]
454
807
  };
455
808
  const menuItemModule = {
456
- menuItems: []
809
+ menuItems: [
810
+ {
811
+ label: config.label,
812
+ icon: config.icon,
813
+ path: "/settings/product-attributes",
814
+ nested: void 0,
815
+ rank: void 0,
816
+ translationNs: void 0
817
+ }
818
+ ]
457
819
  };
458
820
  const formModule = { customFields: {} };
459
821
  const displayModule = {