@froze-labs/medusa-plugin-brands 0.0.3 → 0.0.5

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.
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  const jsxRuntime = require("react/jsx-runtime");
3
3
  const adminSdk = require("@medusajs/admin-sdk");
4
- const icons = require("@medusajs/icons");
5
4
  const ui = require("@medusajs/ui");
6
5
  const reactQuery = require("@tanstack/react-query");
7
6
  const react = require("react");
8
- const reactRouterDom = require("react-router-dom");
9
7
  const Medusa = require("@medusajs/js-sdk");
8
+ const icons = require("@medusajs/icons");
9
+ const reactRouterDom = require("react-router-dom");
10
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
11
  const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
12
12
  const sdk = new Medusa__default.default({
@@ -15,6 +15,127 @@ const sdk = new Medusa__default.default({
15
15
  type: "session"
16
16
  }
17
17
  });
18
+ const ProductBrandsWidget = ({ data }) => {
19
+ var _a;
20
+ const queryClient = reactQuery.useQueryClient();
21
+ const [isEditing, setIsEditing] = react.useState(false);
22
+ const [selectedBrands, setSelectedBrands] = react.useState([]);
23
+ const { data: productBrands, isLoading: loadingProductBrands } = reactQuery.useQuery({
24
+ queryKey: ["product-brands", data.id],
25
+ queryFn: () => sdk.client.fetch(`/admin/products/${data.id}/brands`)
26
+ });
27
+ const { data: allBrands, isLoading: loadingAllBrands } = reactQuery.useQuery({
28
+ queryKey: ["brands-all"],
29
+ queryFn: () => sdk.client.fetch("/admin/brands", {
30
+ query: { limit: 100 }
31
+ }),
32
+ enabled: isEditing
33
+ });
34
+ const updateBrandsMutation = reactQuery.useMutation({
35
+ mutationFn: async (brandIds) => {
36
+ await sdk.client.fetch(`/admin/products/${data.id}/brands`, {
37
+ method: "DELETE"
38
+ });
39
+ if (brandIds.length > 0) {
40
+ await sdk.client.fetch(`/admin/products/${data.id}/brands`, {
41
+ method: "POST",
42
+ body: { brand_ids: brandIds }
43
+ });
44
+ }
45
+ },
46
+ onSuccess: () => {
47
+ queryClient.invalidateQueries({ queryKey: ["product-brands", data.id] });
48
+ ui.toast.success("Success", { description: "Brands updated successfully" });
49
+ setIsEditing(false);
50
+ },
51
+ onError: () => {
52
+ ui.toast.error("Error", { description: "Failed to update brands" });
53
+ }
54
+ });
55
+ const handleStartEdit = () => {
56
+ var _a2;
57
+ setSelectedBrands(((_a2 = productBrands == null ? void 0 : productBrands.brands) == null ? void 0 : _a2.map((b) => b.id)) || []);
58
+ setIsEditing(true);
59
+ };
60
+ const handleSave = () => {
61
+ updateBrandsMutation.mutate(selectedBrands);
62
+ };
63
+ const handleCancel = () => {
64
+ setIsEditing(false);
65
+ setSelectedBrands([]);
66
+ };
67
+ const toggleBrand = (brandId) => {
68
+ setSelectedBrands(
69
+ (prev) => prev.includes(brandId) ? prev.filter((id) => id !== brandId) : [...prev, brandId]
70
+ );
71
+ };
72
+ const currentBrands = (productBrands == null ? void 0 : productBrands.brands) || [];
73
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
74
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
75
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
76
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Brands" }),
77
+ !isEditing && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { children: currentBrands.length })
78
+ ] }),
79
+ !isEditing ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: handleStartEdit, children: "Edit" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
80
+ /* @__PURE__ */ jsxRuntime.jsx(
81
+ ui.Button,
82
+ {
83
+ size: "small",
84
+ onClick: handleSave,
85
+ isLoading: updateBrandsMutation.isPending,
86
+ children: "Save"
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", size: "small", onClick: handleCancel, children: "Cancel" })
90
+ ] })
91
+ ] }),
92
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: loadingProductBrands ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-muted", children: "Loading..." }) : isEditing ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: loadingAllBrands ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-muted", children: "Loading brands..." }) : ((_a = allBrands == null ? void 0 : allBrands.brands) == null ? void 0 : _a.length) ? allBrands.brands.map((brand) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
93
+ /* @__PURE__ */ jsxRuntime.jsx(
94
+ ui.Checkbox,
95
+ {
96
+ id: brand.id,
97
+ checked: selectedBrands.includes(brand.id),
98
+ onCheckedChange: () => toggleBrand(brand.id)
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { htmlFor: brand.id, className: "flex items-center gap-2", children: [
102
+ brand.logo_image && /* @__PURE__ */ jsxRuntime.jsx(
103
+ "img",
104
+ {
105
+ src: brand.logo_image,
106
+ alt: brand.name,
107
+ className: "h-5 w-5 rounded object-cover"
108
+ }
109
+ ),
110
+ brand.name
111
+ ] })
112
+ ] }, brand.id)) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-ui-fg-muted", children: [
113
+ "No brands available.",
114
+ " ",
115
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/brands/create", className: "text-ui-fg-interactive", children: "Create one" })
116
+ ] }) }) : currentBrands.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: currentBrands.map((brand) => /* @__PURE__ */ jsxRuntime.jsxs(
117
+ "div",
118
+ {
119
+ className: "flex items-center gap-2 rounded-md border px-2 py-1",
120
+ children: [
121
+ brand.logo_image && /* @__PURE__ */ jsxRuntime.jsx(
122
+ "img",
123
+ {
124
+ src: brand.logo_image,
125
+ alt: brand.name,
126
+ className: "h-5 w-5 rounded object-cover"
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: brand.name })
130
+ ]
131
+ },
132
+ brand.id
133
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-muted", children: "No brands assigned to this product" }) })
134
+ ] });
135
+ };
136
+ adminSdk.defineWidgetConfig({
137
+ zone: "product.details.side.after"
138
+ });
18
139
  const BrandsPage = () => {
19
140
  var _a;
20
141
  const navigate = reactRouterDom.useNavigate();
@@ -433,7 +554,12 @@ const EditBrandPage = () => {
433
554
  ] })
434
555
  ] });
435
556
  };
436
- const widgetModule = { widgets: [] };
557
+ const widgetModule = { widgets: [
558
+ {
559
+ Component: ProductBrandsWidget,
560
+ zone: ["product.details.side.after"]
561
+ }
562
+ ] };
437
563
  const routeModule = {
438
564
  routes: [
439
565
  {
@@ -1,17 +1,138 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { defineRouteConfig } from "@medusajs/admin-sdk";
3
- import { TagSolid } from "@medusajs/icons";
4
- import { Container, Heading, Button, Text, Table, toast, Badge, Label, Input, Textarea } from "@medusajs/ui";
5
- import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
2
+ import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
+ import { toast, Container, Heading, Badge, Button, Checkbox, Label, Text, Table, Input, Textarea } from "@medusajs/ui";
4
+ import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query";
6
5
  import { useState, useMemo, useEffect } from "react";
7
- import { useNavigate, Link, useParams } from "react-router-dom";
8
6
  import Medusa from "@medusajs/js-sdk";
7
+ import { TagSolid } from "@medusajs/icons";
8
+ import { useNavigate, Link, useParams } from "react-router-dom";
9
9
  const sdk = new Medusa({
10
10
  baseUrl: "",
11
11
  auth: {
12
12
  type: "session"
13
13
  }
14
14
  });
15
+ const ProductBrandsWidget = ({ data }) => {
16
+ var _a;
17
+ const queryClient = useQueryClient();
18
+ const [isEditing, setIsEditing] = useState(false);
19
+ const [selectedBrands, setSelectedBrands] = useState([]);
20
+ const { data: productBrands, isLoading: loadingProductBrands } = useQuery({
21
+ queryKey: ["product-brands", data.id],
22
+ queryFn: () => sdk.client.fetch(`/admin/products/${data.id}/brands`)
23
+ });
24
+ const { data: allBrands, isLoading: loadingAllBrands } = useQuery({
25
+ queryKey: ["brands-all"],
26
+ queryFn: () => sdk.client.fetch("/admin/brands", {
27
+ query: { limit: 100 }
28
+ }),
29
+ enabled: isEditing
30
+ });
31
+ const updateBrandsMutation = useMutation({
32
+ mutationFn: async (brandIds) => {
33
+ await sdk.client.fetch(`/admin/products/${data.id}/brands`, {
34
+ method: "DELETE"
35
+ });
36
+ if (brandIds.length > 0) {
37
+ await sdk.client.fetch(`/admin/products/${data.id}/brands`, {
38
+ method: "POST",
39
+ body: { brand_ids: brandIds }
40
+ });
41
+ }
42
+ },
43
+ onSuccess: () => {
44
+ queryClient.invalidateQueries({ queryKey: ["product-brands", data.id] });
45
+ toast.success("Success", { description: "Brands updated successfully" });
46
+ setIsEditing(false);
47
+ },
48
+ onError: () => {
49
+ toast.error("Error", { description: "Failed to update brands" });
50
+ }
51
+ });
52
+ const handleStartEdit = () => {
53
+ var _a2;
54
+ setSelectedBrands(((_a2 = productBrands == null ? void 0 : productBrands.brands) == null ? void 0 : _a2.map((b) => b.id)) || []);
55
+ setIsEditing(true);
56
+ };
57
+ const handleSave = () => {
58
+ updateBrandsMutation.mutate(selectedBrands);
59
+ };
60
+ const handleCancel = () => {
61
+ setIsEditing(false);
62
+ setSelectedBrands([]);
63
+ };
64
+ const toggleBrand = (brandId) => {
65
+ setSelectedBrands(
66
+ (prev) => prev.includes(brandId) ? prev.filter((id) => id !== brandId) : [...prev, brandId]
67
+ );
68
+ };
69
+ const currentBrands = (productBrands == null ? void 0 : productBrands.brands) || [];
70
+ return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
71
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
72
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
73
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Brands" }),
74
+ !isEditing && /* @__PURE__ */ jsx(Badge, { children: currentBrands.length })
75
+ ] }),
76
+ !isEditing ? /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: handleStartEdit, children: "Edit" }) : /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
77
+ /* @__PURE__ */ jsx(
78
+ Button,
79
+ {
80
+ size: "small",
81
+ onClick: handleSave,
82
+ isLoading: updateBrandsMutation.isPending,
83
+ children: "Save"
84
+ }
85
+ ),
86
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: handleCancel, children: "Cancel" })
87
+ ] })
88
+ ] }),
89
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: loadingProductBrands ? /* @__PURE__ */ jsx("p", { className: "text-ui-fg-muted", children: "Loading..." }) : isEditing ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: loadingAllBrands ? /* @__PURE__ */ jsx("p", { className: "text-ui-fg-muted", children: "Loading brands..." }) : ((_a = allBrands == null ? void 0 : allBrands.brands) == null ? void 0 : _a.length) ? allBrands.brands.map((brand) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
90
+ /* @__PURE__ */ jsx(
91
+ Checkbox,
92
+ {
93
+ id: brand.id,
94
+ checked: selectedBrands.includes(brand.id),
95
+ onCheckedChange: () => toggleBrand(brand.id)
96
+ }
97
+ ),
98
+ /* @__PURE__ */ jsxs(Label, { htmlFor: brand.id, className: "flex items-center gap-2", children: [
99
+ brand.logo_image && /* @__PURE__ */ jsx(
100
+ "img",
101
+ {
102
+ src: brand.logo_image,
103
+ alt: brand.name,
104
+ className: "h-5 w-5 rounded object-cover"
105
+ }
106
+ ),
107
+ brand.name
108
+ ] })
109
+ ] }, brand.id)) : /* @__PURE__ */ jsxs("p", { className: "text-ui-fg-muted", children: [
110
+ "No brands available.",
111
+ " ",
112
+ /* @__PURE__ */ jsx("a", { href: "/brands/create", className: "text-ui-fg-interactive", children: "Create one" })
113
+ ] }) }) : currentBrands.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: currentBrands.map((brand) => /* @__PURE__ */ jsxs(
114
+ "div",
115
+ {
116
+ className: "flex items-center gap-2 rounded-md border px-2 py-1",
117
+ children: [
118
+ brand.logo_image && /* @__PURE__ */ jsx(
119
+ "img",
120
+ {
121
+ src: brand.logo_image,
122
+ alt: brand.name,
123
+ className: "h-5 w-5 rounded object-cover"
124
+ }
125
+ ),
126
+ /* @__PURE__ */ jsx("span", { children: brand.name })
127
+ ]
128
+ },
129
+ brand.id
130
+ )) }) : /* @__PURE__ */ jsx("p", { className: "text-ui-fg-muted", children: "No brands assigned to this product" }) })
131
+ ] });
132
+ };
133
+ defineWidgetConfig({
134
+ zone: "product.details.side.after"
135
+ });
15
136
  const BrandsPage = () => {
16
137
  var _a;
17
138
  const navigate = useNavigate();
@@ -430,7 +551,12 @@ const EditBrandPage = () => {
430
551
  ] })
431
552
  ] });
432
553
  };
433
- const widgetModule = { widgets: [] };
554
+ const widgetModule = { widgets: [
555
+ {
556
+ Component: ProductBrandsWidget,
557
+ zone: ["product.details.side.after"]
558
+ }
559
+ ] };
434
560
  const routeModule = {
435
561
  routes: [
436
562
  {
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DELETE = exports.POST = exports.GET = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const brand_1 = require("../../../../../modules/brand");
6
+ // GET /admin/products/:id/brands - Get brands for a product
7
+ const GET = async (req, res) => {
8
+ const { id } = req.params;
9
+ const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
10
+ const { data } = await query.graph({
11
+ entity: "product",
12
+ fields: ["id", "brands.*"],
13
+ filters: { id },
14
+ });
15
+ if (!data.length) {
16
+ return res.status(404).json({ message: "Product not found" });
17
+ }
18
+ res.json({ brands: data[0].brands || [] });
19
+ };
20
+ exports.GET = GET;
21
+ // POST /admin/products/:id/brands - Link brands to a product
22
+ const POST = async (req, res) => {
23
+ const { id } = req.params;
24
+ const { brand_ids } = req.body;
25
+ if (!brand_ids || !Array.isArray(brand_ids)) {
26
+ return res.status(400).json({ message: "brand_ids array is required" });
27
+ }
28
+ const remoteLink = req.scope.resolve(utils_1.ContainerRegistrationKeys.REMOTE_LINK);
29
+ const links = brand_ids.map((brandId) => ({
30
+ [utils_1.Modules.PRODUCT]: { product_id: id },
31
+ [brand_1.BRAND_MODULE]: { brand_id: brandId },
32
+ }));
33
+ await remoteLink.create(links);
34
+ res.json({ success: true });
35
+ };
36
+ exports.POST = POST;
37
+ // DELETE /admin/products/:id/brands - Remove brands from a product
38
+ const DELETE = async (req, res) => {
39
+ const { id } = req.params;
40
+ const { brand_ids } = req.body;
41
+ const remoteLink = req.scope.resolve(utils_1.ContainerRegistrationKeys.REMOTE_LINK);
42
+ const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
43
+ let brandIdsToRemove = [];
44
+ if (brand_ids && Array.isArray(brand_ids) && brand_ids.length > 0) {
45
+ // Remove specific brands
46
+ brandIdsToRemove = brand_ids;
47
+ }
48
+ else {
49
+ // Remove all brands - first fetch current brands
50
+ const { data } = await query.graph({
51
+ entity: "product",
52
+ fields: ["brands.id"],
53
+ filters: { id },
54
+ });
55
+ brandIdsToRemove = data[0]?.brands?.map((b) => b.id) || [];
56
+ }
57
+ if (brandIdsToRemove.length > 0) {
58
+ const links = brandIdsToRemove.map((brandId) => ({
59
+ [utils_1.Modules.PRODUCT]: { product_id: id },
60
+ [brand_1.BRAND_MODULE]: { brand_id: brandId },
61
+ }));
62
+ await remoteLink.dismiss(links);
63
+ }
64
+ res.json({ success: true });
65
+ };
66
+ exports.DELETE = DELETE;
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3Byb2R1Y3RzL1tpZF0vYnJhbmRzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHFEQUE4RTtBQUM5RSx3REFBMkQ7QUFFM0QsNERBQTREO0FBQ3JELE1BQU0sR0FBRyxHQUFHLEtBQUssRUFBRSxHQUFrQixFQUFFLEdBQW1CLEVBQUUsRUFBRTtJQUNuRSxNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQTtJQUN6QixNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUVoRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQ2pDLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxVQUFVLENBQUM7UUFDMUIsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFO0tBQ2hCLENBQUMsQ0FBQTtJQUVGLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDakIsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxDQUFDLENBQUE7SUFDL0QsQ0FBQztJQUVELEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFBO0FBQzVDLENBQUMsQ0FBQTtBQWZZLFFBQUEsR0FBRyxPQWVmO0FBRUQsNkRBQTZEO0FBQ3RELE1BQU0sSUFBSSxHQUFHLEtBQUssRUFBRSxHQUFrQixFQUFFLEdBQW1CLEVBQUUsRUFBRTtJQUNwRSxNQUFN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
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Migration20250127000002 = void 0;
4
+ const migrations_1 = require("@mikro-orm/migrations");
5
+ class Migration20250127000002 extends migrations_1.Migration {
6
+ async up() {
7
+ // Link table for product <-> brand many-to-many relationship
8
+ this.addSql(`
9
+ CREATE TABLE IF NOT EXISTS "product_brand" (
10
+ "id" TEXT NOT NULL,
11
+ "product_id" TEXT NOT NULL,
12
+ "brand_id" TEXT NOT NULL,
13
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT now(),
14
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT now(),
15
+ "deleted_at" TIMESTAMPTZ NULL,
16
+ CONSTRAINT "product_brand_pkey" PRIMARY KEY ("id")
17
+ );
18
+ `);
19
+ this.addSql(`
20
+ CREATE INDEX IF NOT EXISTS "IDX_product_brand_product_id" ON "product_brand" ("product_id");
21
+ `);
22
+ this.addSql(`
23
+ CREATE INDEX IF NOT EXISTS "IDX_product_brand_brand_id" ON "product_brand" ("brand_id");
24
+ `);
25
+ this.addSql(`
26
+ CREATE UNIQUE INDEX IF NOT EXISTS "IDX_product_brand_unique" ON "product_brand" ("product_id", "brand_id") WHERE "deleted_at" IS NULL;
27
+ `);
28
+ this.addSql(`
29
+ CREATE INDEX IF NOT EXISTS "IDX_product_brand_deleted_at" ON "product_brand" ("deleted_at");
30
+ `);
31
+ }
32
+ async down() {
33
+ this.addSql(`DROP INDEX IF EXISTS "IDX_product_brand_deleted_at";`);
34
+ this.addSql(`DROP INDEX IF EXISTS "IDX_product_brand_unique";`);
35
+ this.addSql(`DROP INDEX IF EXISTS "IDX_product_brand_brand_id";`);
36
+ this.addSql(`DROP INDEX IF EXISTS "IDX_product_brand_product_id";`);
37
+ this.addSql(`DROP TABLE IF EXISTS "product_brand";`);
38
+ }
39
+ }
40
+ exports.Migration20250127000002 = Migration20250127000002;
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uMjAyNTAxMjcwMDAwMDIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmFuZC9taWdyYXRpb25zL01pZ3JhdGlvbjIwMjUwMTI3MDAwMDAyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHNEQUFpRDtBQUVqRCxNQUFhLHVCQUF3QixTQUFRLHNCQUFTO0lBQ3BELEtBQUssQ0FBQyxFQUFFO1FBQ04sNkRBQTZEO1FBQzdELElBQUksQ0FBQyxNQUFNLENBQUM7Ozs7Ozs7Ozs7S0FVWCxDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsTUFBTSxDQUFDOztLQUVYLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxNQUFNLENBQUM7O0tBRVgsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQzs7S0FFWCxDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsTUFBTSxDQUFDOztLQUVYLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsSUFBSTtRQUNSLElBQUksQ0FBQyxNQUFNLENBQUMsc0RBQXNELENBQUMsQ0FBQTtRQUNuRSxJQUFJLENBQUMsTUFBTSxDQUFDLGtEQUFrRCxDQUFDLENBQUE7UUFDL0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxvREFBb0QsQ0FBQyxDQUFBO1FBQ2pFLElBQUksQ0FBQyxNQUFNLENBQUMsc0RBQXNELENBQUMsQ0FBQTtRQUNuRSxJQUFJLENBQUMsTUFBTSxDQUFDLHVDQUF1QyxDQUFDLENBQUE7SUFDdEQsQ0FBQztDQUNGO0FBdkNELDBEQXVDQyJ9
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Migration20250127000001 = void 0;
3
+ exports.Migration20250127000002 = exports.Migration20250127000001 = void 0;
4
4
  var Migration20250127000001_1 = require("./Migration20250127000001");
5
5
  Object.defineProperty(exports, "Migration20250127000001", { enumerable: true, get: function () { return Migration20250127000001_1.Migration20250127000001; } });
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmFuZC9taWdyYXRpb25zL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFFQUFtRTtBQUExRCxrSUFBQSx1QkFBdUIsT0FBQSJ9
6
+ var Migration20250127000002_1 = require("./Migration20250127000002");
7
+ Object.defineProperty(exports, "Migration20250127000002", { enumerable: true, get: function () { return Migration20250127000002_1.Migration20250127000002; } });
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9icmFuZC9taWdyYXRpb25zL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFFQUFtRTtBQUExRCxrSUFBQSx1QkFBdUIsT0FBQTtBQUNoQyxxRUFBbUU7QUFBMUQsa0lBQUEsdUJBQXVCLE9BQUEifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@froze-labs/medusa-plugin-brands",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Medusa plugin to add brands",
5
5
  "author": "Froze Oy <emil.selroos@froze.fi> (https://froze.fi/)",
6
6
  "license": "MIT",