@empty-complete-org/medusa-product-attributes 0.11.1 → 0.12.0

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.
@@ -14,7 +14,7 @@ const sdk = new Medusa__default.default({
14
14
  type: "session"
15
15
  }
16
16
  });
17
- const emptyForm = () => ({ label: "", type: "text" });
17
+ const emptyForm = () => ({ label: "", type: "text", unit: "" });
18
18
  const CategoryAttributeTemplatesWidget = ({
19
19
  data
20
20
  }) => {
@@ -65,10 +65,11 @@ const CategoryAttributeTemplatesWidget = ({
65
65
  if (!addForm.label.trim()) return;
66
66
  createMutation.mutate({
67
67
  label: addForm.label.trim(),
68
- type: addForm.type
68
+ type: addForm.type,
69
+ unit: addForm.type === "number" && addForm.unit.trim() ? addForm.unit.trim() : null
69
70
  });
70
71
  };
71
- const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t;
72
+ const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
72
73
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
73
74
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
74
75
  /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Атрибуты" }),
@@ -92,7 +93,10 @@ const CategoryAttributeTemplatesWidget = ({
92
93
  className: "flex items-center gap-3 px-6 py-3",
93
94
  children: [
94
95
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-sm text-ui-fg-subtle", children: attr.label }),
95
- /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: typeLabel(attr.type) }),
96
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "grey", children: [
97
+ typeLabel(attr.type),
98
+ attr.unit ? `, ${attr.unit}` : ""
99
+ ] }),
96
100
  /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "blue", children: "из родителя" })
97
101
  ]
98
102
  },
@@ -173,15 +177,27 @@ const CategoryAttributeTemplatesWidget = ({
173
177
  value: addForm.type,
174
178
  onChange: (e) => setAddForm((f) => ({
175
179
  ...f,
176
- type: e.target.value
180
+ type: e.target.value,
181
+ unit: e.target.value === "number" ? f.unit : ""
177
182
  })),
178
183
  className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
179
184
  children: [
180
185
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: "Текст" }),
181
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "Число" })
186
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "Число" }),
187
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "file", children: "Файл" }),
188
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "Да/Нет" })
182
189
  ]
183
190
  }
184
191
  ),
192
+ addForm.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(
193
+ ui.Input,
194
+ {
195
+ value: addForm.unit,
196
+ onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
197
+ placeholder: "ед. (кг, м, шт...)",
198
+ className: "w-28 h-8 text-sm"
199
+ }
200
+ ),
185
201
  /* @__PURE__ */ jsxRuntime.jsx(
186
202
  ui.Button,
187
203
  {
@@ -11,7 +11,7 @@ const sdk = new Medusa({
11
11
  type: "session"
12
12
  }
13
13
  });
14
- const emptyForm = () => ({ label: "", type: "text" });
14
+ const emptyForm = () => ({ label: "", type: "text", unit: "" });
15
15
  const CategoryAttributeTemplatesWidget = ({
16
16
  data
17
17
  }) => {
@@ -62,10 +62,11 @@ const CategoryAttributeTemplatesWidget = ({
62
62
  if (!addForm.label.trim()) return;
63
63
  createMutation.mutate({
64
64
  label: addForm.label.trim(),
65
- type: addForm.type
65
+ type: addForm.type,
66
+ unit: addForm.type === "number" && addForm.unit.trim() ? addForm.unit.trim() : null
66
67
  });
67
68
  };
68
- const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t;
69
+ const typeLabel = (t) => t === "text" ? "Текст" : t === "number" ? "Число" : t === "file" ? "Файл" : t === "boolean" ? "Да/Нет" : t;
69
70
  return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
70
71
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
71
72
  /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Атрибуты" }),
@@ -89,7 +90,10 @@ const CategoryAttributeTemplatesWidget = ({
89
90
  className: "flex items-center gap-3 px-6 py-3",
90
91
  children: [
91
92
  /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm text-ui-fg-subtle", children: attr.label }),
92
- /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "grey", children: typeLabel(attr.type) }),
93
+ /* @__PURE__ */ jsxs(Badge, { size: "2xsmall", color: "grey", children: [
94
+ typeLabel(attr.type),
95
+ attr.unit ? `, ${attr.unit}` : ""
96
+ ] }),
93
97
  /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "blue", children: "из родителя" })
94
98
  ]
95
99
  },
@@ -170,15 +174,27 @@ const CategoryAttributeTemplatesWidget = ({
170
174
  value: addForm.type,
171
175
  onChange: (e) => setAddForm((f) => ({
172
176
  ...f,
173
- type: e.target.value
177
+ type: e.target.value,
178
+ unit: e.target.value === "number" ? f.unit : ""
174
179
  })),
175
180
  className: "h-8 rounded border border-ui-border-base bg-ui-bg-base px-2 text-sm",
176
181
  children: [
177
182
  /* @__PURE__ */ jsx("option", { value: "text", children: "Текст" }),
178
- /* @__PURE__ */ jsx("option", { value: "number", children: "Число" })
183
+ /* @__PURE__ */ jsx("option", { value: "number", children: "Число" }),
184
+ /* @__PURE__ */ jsx("option", { value: "file", children: "Файл" }),
185
+ /* @__PURE__ */ jsx("option", { value: "boolean", children: "Да/Нет" })
179
186
  ]
180
187
  }
181
188
  ),
189
+ addForm.type === "number" && /* @__PURE__ */ jsx(
190
+ Input,
191
+ {
192
+ value: addForm.unit,
193
+ onChange: (e) => setAddForm((f) => ({ ...f, unit: e.target.value })),
194
+ placeholder: "ед. (кг, м, шт...)",
195
+ className: "w-28 h-8 text-sm"
196
+ }
197
+ ),
182
198
  /* @__PURE__ */ jsx(
183
199
  Button,
184
200
  {
@@ -0,0 +1,5 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ export declare class Migration20260405120000 extends Migration {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20260405120000 = void 0;
4
+ const migrations_1 = require("@mikro-orm/migrations");
5
+ class Migration20260405120000 extends migrations_1.Migration {
6
+ async up() {
7
+ this.addSql(`
8
+ CREATE TABLE IF NOT EXISTS "category_custom_attribute" (
9
+ "id" text NOT NULL,
10
+ "key" text NOT NULL,
11
+ "type" text NOT NULL DEFAULT 'text',
12
+ "label" text NOT NULL DEFAULT '',
13
+ "unit" text NULL,
14
+ "sort_order" integer NOT NULL DEFAULT 0,
15
+ "category_id" text NOT NULL,
16
+ "is_standard" boolean NOT NULL DEFAULT false,
17
+ "created_at" timestamptz NOT NULL DEFAULT now(),
18
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
19
+ "deleted_at" timestamptz NULL,
20
+ CONSTRAINT "category_custom_attribute_pkey" PRIMARY KEY ("id")
21
+ );
22
+ `);
23
+ this.addSql(`
24
+ CREATE INDEX IF NOT EXISTS "IDX_category_custom_attribute_category_id"
25
+ ON "category_custom_attribute" ("category_id")
26
+ WHERE "deleted_at" IS NULL;
27
+ `);
28
+ this.addSql(`
29
+ CREATE INDEX IF NOT EXISTS "IDX_category_custom_attribute_deleted_at"
30
+ ON "category_custom_attribute" ("deleted_at")
31
+ WHERE "deleted_at" IS NOT NULL;
32
+ `);
33
+ this.addSql(`
34
+ CREATE TABLE IF NOT EXISTS "product_custom_attribute" (
35
+ "id" text NOT NULL,
36
+ "product_id" text NOT NULL,
37
+ "value" text NOT NULL,
38
+ "value_numeric" integer NULL,
39
+ "value_file" text NULL,
40
+ "category_custom_attribute_id" text NOT NULL,
41
+ "created_at" timestamptz NOT NULL DEFAULT now(),
42
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
43
+ "deleted_at" timestamptz NULL,
44
+ CONSTRAINT "product_custom_attribute_pkey" PRIMARY KEY ("id"),
45
+ CONSTRAINT "product_custom_attribute_category_custom_attribute_id_foreign"
46
+ FOREIGN KEY ("category_custom_attribute_id")
47
+ REFERENCES "category_custom_attribute" ("id")
48
+ ON UPDATE CASCADE ON DELETE CASCADE
49
+ );
50
+ `);
51
+ this.addSql(`
52
+ CREATE INDEX IF NOT EXISTS "IDX_product_custom_attribute_product_id"
53
+ ON "product_custom_attribute" ("product_id")
54
+ WHERE "deleted_at" IS NULL;
55
+ `);
56
+ this.addSql(`
57
+ CREATE INDEX IF NOT EXISTS "IDX_product_custom_attribute_category_custom_attribute_id"
58
+ ON "product_custom_attribute" ("category_custom_attribute_id")
59
+ WHERE "deleted_at" IS NULL;
60
+ `);
61
+ this.addSql(`
62
+ CREATE INDEX IF NOT EXISTS "IDX_product_custom_attribute_deleted_at"
63
+ ON "product_custom_attribute" ("deleted_at")
64
+ WHERE "deleted_at" IS NOT NULL;
65
+ `);
66
+ }
67
+ async down() {
68
+ this.addSql(`DROP TABLE IF EXISTS "product_custom_attribute" CASCADE;`);
69
+ this.addSql(`DROP TABLE IF EXISTS "category_custom_attribute" CASCADE;`);
70
+ }
71
+ }
72
+ exports.Migration20260405120000 = Migration20260405120000;
73
+ //# sourceMappingURL=Migration20260405120000.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Migration20260405120000.js","sourceRoot":"","sources":["../../../../../../src/modules/product-attributes/migrations/Migration20260405120000.ts"],"names":[],"mappings":";;;AAAA,sDAAiD;AAEjD,MAAa,uBAAwB,SAAQ,sBAAS;IAC3C,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;KAeX,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;KAiBX,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC;;;;KAIX,CAAC,CAAA;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,0DAA0D,CAAC,CAAA;QACvE,IAAI,CAAC,MAAM,CAAC,2DAA2D,CAAC,CAAA;IAC1E,CAAC;CACF;AAzED,0DAyEC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empty-complete-org/medusa-product-attributes",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "Custom attributes module for Medusa v2 with support for text, number, file types and units of measurement",
5
5
  "author": "empty-complete",
6
6
  "license": "MIT",
@@ -63,6 +63,8 @@
63
63
  "@medusajs/js-sdk": "^2.13.1",
64
64
  "@medusajs/medusa": "^2.13.1",
65
65
  "@medusajs/ui": "^4.0.27",
66
+ "@mikro-orm/core": "6.4.16",
67
+ "@mikro-orm/migrations": "6.4.16",
66
68
  "@tanstack/react-query": "^5.64.2",
67
69
  "@types/jest": "^30.0.0",
68
70
  "@types/node": "^20.0.0",
@@ -71,6 +73,7 @@
71
73
  "jest": "^30.3.0",
72
74
  "react": "^19.0.0",
73
75
  "ts-jest": "^29.4.6",
76
+ "ts-node": "^10.9.2",
74
77
  "typescript": "^5.0.0",
75
78
  "yalc": "^1.0.0-pre.53"
76
79
  },