@csbeker/medusa-product-attributes 2.2.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.
- package/.medusa/server/src/admin/index.js +9012 -0
- package/.medusa/server/src/admin/index.mjs +9010 -0
- package/.medusa/server/src/api/admin/middlewares.js +10 -0
- package/.medusa/server/src/api/admin/plugin/attribute-set/[id]/attributes/route.js +17 -0
- package/.medusa/server/src/api/admin/plugin/attribute-set/[id]/route.js +33 -0
- package/.medusa/server/src/api/admin/plugin/attribute-set/middlewares.js +44 -0
- package/.medusa/server/src/api/admin/plugin/attribute-set/query-config.js +22 -0
- package/.medusa/server/src/api/admin/plugin/attribute-set/route.js +25 -0
- package/.medusa/server/src/api/admin/plugin/attribute-set/validators.js +37 -0
- package/.medusa/server/src/api/admin/plugin/attributes/[id]/route.js +68 -0
- package/.medusa/server/src/api/admin/plugin/attributes/[id]/values/[valueId]/route.js +37 -0
- package/.medusa/server/src/api/admin/plugin/attributes/[id]/values/route.js +31 -0
- package/.medusa/server/src/api/admin/plugin/attributes/middlewares.js +103 -0
- package/.medusa/server/src/api/admin/plugin/attributes/query-config.js +41 -0
- package/.medusa/server/src/api/admin/plugin/attributes/route.js +28 -0
- package/.medusa/server/src/api/admin/plugin/attributes/validators.js +69 -0
- package/.medusa/server/src/api/admin/plugin/route.js +7 -0
- package/.medusa/server/src/api/middlewares.js +12 -0
- package/.medusa/server/src/api/store/middlewares.js +8 -0
- package/.medusa/server/src/api/store/plugin/attributes/middlewares.js +64 -0
- package/.medusa/server/src/api/store/plugin/attributes/products/middlewares.js +100 -0
- package/.medusa/server/src/api/store/plugin/attributes/products/query-config.js +20 -0
- package/.medusa/server/src/api/store/plugin/attributes/products/route.js +48 -0
- package/.medusa/server/src/api/store/plugin/attributes/products/validators.js +39 -0
- package/.medusa/server/src/api/store/plugin/attributes/query-config.js +21 -0
- package/.medusa/server/src/api/store/plugin/attributes/route.js +15 -0
- package/.medusa/server/src/api/store/plugin/attributes/validators.js +14 -0
- package/.medusa/server/src/api/store/plugin/route.js +7 -0
- package/.medusa/server/src/api/utils/common-validators.js +23 -0
- package/.medusa/server/src/api/utils/constants.js +6 -0
- package/.medusa/server/src/api/utils/middlewares.js +34 -0
- package/.medusa/server/src/links/attribute-product-category.js +16 -0
- package/.medusa/server/src/links/attribute-value-product.js +16 -0
- package/.medusa/server/src/modules/attribute/events/index.js +8 -0
- package/.medusa/server/src/modules/attribute/index.js +13 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250319161229.js +24 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250320182643.js +16 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250321162638.js +14 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250505144933.js +23 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250505201747.js +21 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250506162300.js +14 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250611160552.js +14 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250611173345.js +16 -0
- package/.medusa/server/src/modules/attribute/migrations/Migration20250612192857.js +16 -0
- package/.medusa/server/src/modules/attribute/models/attribute-possible-value.js +24 -0
- package/.medusa/server/src/modules/attribute/models/attribute-set.js +22 -0
- package/.medusa/server/src/modules/attribute/models/attribute-value.js +17 -0
- package/.medusa/server/src/modules/attribute/models/attribute.js +27 -0
- package/.medusa/server/src/modules/attribute/service.js +84 -0
- package/.medusa/server/src/modules/attribute/types/attribute/common.js +13 -0
- package/.medusa/server/src/modules/attribute/types/attribute/index.js +18 -0
- package/.medusa/server/src/modules/attribute/types/attribute-set/index.js +18 -0
- package/.medusa/server/src/modules/attribute/types/attribute-set/mutations.js +3 -0
- package/.medusa/server/src/modules/attribute/types/attribute-value/index.js +18 -0
- package/.medusa/server/src/modules/attribute/types/attribute-value/mutations.js +3 -0
- package/.medusa/server/src/modules/attribute/types/index.js +20 -0
- package/.medusa/server/src/types/attribute/common.js +3 -0
- package/.medusa/server/src/types/attribute/http/attribute/admin/index.js +3 -0
- package/.medusa/server/src/types/attribute/http/attribute/index.js +18 -0
- package/.medusa/server/src/types/attribute/http/attribute-set/index.js +3 -0
- package/.medusa/server/src/types/attribute/http/index.js +19 -0
- package/.medusa/server/src/types/attribute/index.js +19 -0
- package/.medusa/server/src/utils/index.js +18 -0
- package/.medusa/server/src/utils/products-created-handler.js +22 -0
- package/.medusa/server/src/utils/products-updated-handler.js +58 -0
- package/.medusa/server/src/utils/validate-attribute-values-to-link.js +43 -0
- package/.medusa/server/src/workflows/attribute/index.js +19 -0
- package/.medusa/server/src/workflows/attribute/steps/create-attribute-possible-values.js +18 -0
- package/.medusa/server/src/workflows/attribute/steps/create-attributes.js +27 -0
- package/.medusa/server/src/workflows/attribute/steps/delete-attribute.js +31 -0
- package/.medusa/server/src/workflows/attribute/steps/index.js +21 -0
- package/.medusa/server/src/workflows/attribute/steps/update-attributes.js +34 -0
- package/.medusa/server/src/workflows/attribute/workflows/create-attribute-possible-values.js +11 -0
- package/.medusa/server/src/workflows/attribute/workflows/create-attributes.js +46 -0
- package/.medusa/server/src/workflows/attribute/workflows/delete-attribute.js +10 -0
- package/.medusa/server/src/workflows/attribute/workflows/index.js +20 -0
- package/.medusa/server/src/workflows/attribute/workflows/update-attributes.js +73 -0
- package/.medusa/server/src/workflows/attribute-set/steps/batch-link-attribute-set-attributes.js +66 -0
- package/.medusa/server/src/workflows/attribute-set/steps/create-attribute-set.js +24 -0
- package/.medusa/server/src/workflows/attribute-set/steps/index.js +20 -0
- package/.medusa/server/src/workflows/attribute-set/steps/update-attribute-set.js +22 -0
- package/.medusa/server/src/workflows/attribute-set/workflows/batch-link-attribute-set-attributes.js +10 -0
- package/.medusa/server/src/workflows/attribute-set/workflows/create-attribute-set.js +11 -0
- package/.medusa/server/src/workflows/attribute-set/workflows/index.js +20 -0
- package/.medusa/server/src/workflows/attribute-set/workflows/update-attribute-set.js +10 -0
- package/.medusa/server/src/workflows/attribute-value/steps/create-attribute-value.js +18 -0
- package/.medusa/server/src/workflows/attribute-value/steps/delete-attribute-value.js +18 -0
- package/.medusa/server/src/workflows/attribute-value/steps/index.js +20 -0
- package/.medusa/server/src/workflows/attribute-value/steps/validate-attribute-value.js +53 -0
- package/.medusa/server/src/workflows/attribute-value/workflow/create-attribute-value.js +28 -0
- package/.medusa/server/src/workflows/attribute-value/workflow/delete-attribute-value.js +38 -0
- package/.medusa/server/src/workflows/attribute-value/workflow/index.js +19 -0
- package/.medusa/server/src/workflows/attribute_possible_value/index.js +19 -0
- package/.medusa/server/src/workflows/attribute_possible_value/steps/index.js +18 -0
- package/.medusa/server/src/workflows/attribute_possible_value/steps/update-attribute-possible-value.js +18 -0
- package/.medusa/server/src/workflows/attribute_possible_value/workflows/index.js +18 -0
- package/.medusa/server/src/workflows/attribute_possible_value/workflows/update-attribute-possible-value.js +11 -0
- package/.medusa/server/src/workflows/index.js +18 -0
- package/CHANGELOG.md +104 -0
- package/README.md +86 -0
- package/package.json +90 -0
- package/src/admin/README.md +31 -0
- package/src/admin/components/metadata-editor/index.tsx +101 -0
- package/src/admin/components/section-row.tsx +41 -0
- package/src/admin/hooks/api/attribute-set.ts +122 -0
- package/src/admin/hooks/api/attributes.ts +126 -0
- package/src/admin/hooks/table/columns/index.ts +1 -0
- package/src/admin/hooks/table/columns/use-attribute-table-columns.tsx +280 -0
- package/src/admin/layouts/single-column.tsx +11 -0
- package/src/admin/lib/config.ts +8 -0
- package/src/admin/lib/query-key-factory.ts +53 -0
- package/src/admin/routes/attributes/[id]/edit/page.tsx +133 -0
- package/src/admin/routes/attributes/[id]/edit-possible-value/page.tsx +174 -0
- package/src/admin/routes/attributes/[id]/page.tsx +127 -0
- package/src/admin/routes/attributes/components/AttributeForm.tsx +301 -0
- package/src/admin/routes/attributes/components/AttributeSetTable.tsx +108 -0
- package/src/admin/routes/attributes/components/category-selection-modal.tsx +82 -0
- package/src/admin/routes/attributes/components/possible-values-table.tsx +119 -0
- package/src/admin/routes/attributes/create/components/MultiSelectCategory.tsx +148 -0
- package/src/admin/routes/attributes/create/components/PossibleValuesList.tsx +151 -0
- package/src/admin/routes/attributes/create/page.tsx +123 -0
- package/src/admin/routes/attributes/create-set/page.tsx +110 -0
- package/src/admin/routes/attributes/page.tsx +346 -0
- package/src/admin/routes/attributes/set/[id]/attributes/page.tsx +35 -0
- package/src/admin/routes/attributes/set/[id]/components/AttributeSetAttributesSection.tsx +114 -0
- package/src/admin/routes/attributes/set/[id]/components/AttributeSetGeneralSection.tsx +42 -0
- package/src/admin/routes/attributes/set/[id]/components/attribute-set-attributes-form.tsx +143 -0
- package/src/admin/routes/attributes/set/[id]/components/index.ts +2 -0
- package/src/admin/routes/attributes/set/[id]/edit/page.tsx +119 -0
- package/src/admin/routes/attributes/set/[id]/page.tsx +45 -0
- package/src/admin/tsconfig.json +27 -0
- package/src/admin/types/global.d.ts +3 -0
- package/src/admin/vite-env.d.ts +1 -0
- package/src/api/README.md +133 -0
- package/src/api/admin/middlewares.ts +8 -0
- package/src/api/admin/plugin/attribute-set/[id]/attributes/route.ts +17 -0
- package/src/api/admin/plugin/attribute-set/[id]/route.ts +41 -0
- package/src/api/admin/plugin/attribute-set/middlewares.ts +42 -0
- package/src/api/admin/plugin/attribute-set/query-config.ts +20 -0
- package/src/api/admin/plugin/attribute-set/route.ts +34 -0
- package/src/api/admin/plugin/attribute-set/validators.ts +45 -0
- package/src/api/admin/plugin/attributes/[id]/route.ts +85 -0
- package/src/api/admin/plugin/attributes/[id]/values/[valueId]/route.ts +41 -0
- package/src/api/admin/plugin/attributes/[id]/values/route.ts +39 -0
- package/src/api/admin/plugin/attributes/middlewares.ts +91 -0
- package/src/api/admin/plugin/attributes/query-config.ts +42 -0
- package/src/api/admin/plugin/attributes/route.ts +33 -0
- package/src/api/admin/plugin/attributes/validators.ts +91 -0
- package/src/api/admin/plugin/route.ts +8 -0
- package/src/api/middlewares.ts +10 -0
- package/src/api/store/middlewares.ts +6 -0
- package/src/api/store/plugin/attributes/middlewares.ts +33 -0
- package/src/api/store/plugin/attributes/products/middlewares.ts +73 -0
- package/src/api/store/plugin/attributes/products/query-config.ts +19 -0
- package/src/api/store/plugin/attributes/products/route.ts +68 -0
- package/src/api/store/plugin/attributes/products/validators.ts +55 -0
- package/src/api/store/plugin/attributes/query-config.ts +19 -0
- package/src/api/store/plugin/attributes/route.ts +13 -0
- package/src/api/store/plugin/attributes/validators.ts +14 -0
- package/src/api/store/plugin/route.ts +8 -0
- package/src/api/utils/common-validators.ts +24 -0
- package/src/api/utils/constants.ts +2 -0
- package/src/api/utils/middlewares.ts +31 -0
- package/src/jobs/README.md +36 -0
- package/src/links/README.md +26 -0
- package/src/links/attribute-product-category.ts +14 -0
- package/src/links/attribute-value-product.ts +14 -0
- package/src/modules/README.md +116 -0
- package/src/modules/attribute/events/index.ts +4 -0
- package/src/modules/attribute/index.ts +8 -0
- package/src/modules/attribute/migrations/.snapshot-medusa-attribute.json +624 -0
- package/src/modules/attribute/migrations/Migration20250319161229.ts +27 -0
- package/src/modules/attribute/migrations/Migration20250320182643.ts +15 -0
- package/src/modules/attribute/migrations/Migration20250321162638.ts +13 -0
- package/src/modules/attribute/migrations/Migration20250505144933.ts +26 -0
- package/src/modules/attribute/migrations/Migration20250505201747.ts +23 -0
- package/src/modules/attribute/migrations/Migration20250506162300.ts +13 -0
- package/src/modules/attribute/migrations/Migration20250611160552.ts +13 -0
- package/src/modules/attribute/migrations/Migration20250611173345.ts +17 -0
- package/src/modules/attribute/migrations/Migration20250612192857.ts +17 -0
- package/src/modules/attribute/models/attribute-possible-value.ts +20 -0
- package/src/modules/attribute/models/attribute-set.ts +18 -0
- package/src/modules/attribute/models/attribute-value.ts +13 -0
- package/src/modules/attribute/models/attribute.ts +23 -0
- package/src/modules/attribute/service.ts +102 -0
- package/src/modules/attribute/types/attribute/common.ts +94 -0
- package/src/modules/attribute/types/attribute/index.ts +1 -0
- package/src/modules/attribute/types/attribute-set/index.ts +1 -0
- package/src/modules/attribute/types/attribute-set/mutations.ts +7 -0
- package/src/modules/attribute/types/attribute-value/index.ts +1 -0
- package/src/modules/attribute/types/attribute-value/mutations.ts +5 -0
- package/src/modules/attribute/types/index.ts +3 -0
- package/src/providers/README.md +30 -0
- package/src/subscribers/README.md +59 -0
- package/src/types/attribute/common.ts +173 -0
- package/src/types/attribute/http/attribute/admin/index.ts +0 -0
- package/src/types/attribute/http/attribute/index.ts +42 -0
- package/src/types/attribute/http/attribute-set/index.ts +10 -0
- package/src/types/attribute/http/index.ts +2 -0
- package/src/types/attribute/index.ts +2 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/products-created-handler.ts +35 -0
- package/src/utils/products-updated-handler.ts +74 -0
- package/src/utils/validate-attribute-values-to-link.ts +67 -0
- package/src/workflows/README.md +79 -0
- package/src/workflows/attribute/index.ts +2 -0
- package/src/workflows/attribute/steps/create-attribute-possible-values.ts +29 -0
- package/src/workflows/attribute/steps/create-attributes.ts +35 -0
- package/src/workflows/attribute/steps/delete-attribute.ts +41 -0
- package/src/workflows/attribute/steps/index.ts +4 -0
- package/src/workflows/attribute/steps/update-attributes.ts +45 -0
- package/src/workflows/attribute/workflows/create-attribute-possible-values.ts +17 -0
- package/src/workflows/attribute/workflows/create-attributes.ts +56 -0
- package/src/workflows/attribute/workflows/delete-attribute.ts +15 -0
- package/src/workflows/attribute/workflows/index.ts +3 -0
- package/src/workflows/attribute/workflows/update-attributes.ts +103 -0
- package/src/workflows/attribute-set/steps/batch-link-attribute-set-attributes.ts +82 -0
- package/src/workflows/attribute-set/steps/create-attribute-set.ts +34 -0
- package/src/workflows/attribute-set/steps/index.ts +3 -0
- package/src/workflows/attribute-set/steps/update-attribute-set.ts +32 -0
- package/src/workflows/attribute-set/workflows/batch-link-attribute-set-attributes.ts +12 -0
- package/src/workflows/attribute-set/workflows/create-attribute-set.ts +17 -0
- package/src/workflows/attribute-set/workflows/index.ts +3 -0
- package/src/workflows/attribute-set/workflows/update-attribute-set.ts +14 -0
- package/src/workflows/attribute-value/steps/create-attribute-value.ts +26 -0
- package/src/workflows/attribute-value/steps/delete-attribute-value.ts +26 -0
- package/src/workflows/attribute-value/steps/index.ts +3 -0
- package/src/workflows/attribute-value/steps/validate-attribute-value.ts +95 -0
- package/src/workflows/attribute-value/workflow/create-attribute-value.ts +36 -0
- package/src/workflows/attribute-value/workflow/delete-attribute-value.ts +46 -0
- package/src/workflows/attribute-value/workflow/index.ts +2 -0
- package/src/workflows/attribute_possible_value/index.ts +2 -0
- package/src/workflows/attribute_possible_value/steps/index.ts +1 -0
- package/src/workflows/attribute_possible_value/steps/update-attribute-possible-value.ts +25 -0
- package/src/workflows/attribute_possible_value/workflows/index.ts +1 -0
- package/src/workflows/attribute_possible_value/workflows/update-attribute-possible-value.ts +15 -0
- package/src/workflows/index.ts +1 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { QueryKey, UseQueryOptions } from "@tanstack/react-query"
|
|
2
|
+
|
|
3
|
+
export type TQueryKey<TKey, TListQuery = any, TDetailQuery = string> = {
|
|
4
|
+
all: readonly [TKey]
|
|
5
|
+
lists: () => readonly [...TQueryKey<TKey>["all"], "list"]
|
|
6
|
+
list: (
|
|
7
|
+
query?: TListQuery
|
|
8
|
+
) => readonly [...ReturnType<TQueryKey<TKey>["lists"]>, { query: TListQuery }]
|
|
9
|
+
details: () => readonly [...TQueryKey<TKey>["all"], "detail"]
|
|
10
|
+
detail: (
|
|
11
|
+
id: TDetailQuery,
|
|
12
|
+
query?: TListQuery
|
|
13
|
+
) => readonly [
|
|
14
|
+
...ReturnType<TQueryKey<TKey>["details"]>,
|
|
15
|
+
TDetailQuery,
|
|
16
|
+
{ query: TListQuery },
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type UseQueryOptionsWrapper<
|
|
21
|
+
// Return type of queryFn
|
|
22
|
+
TQueryFn = unknown,
|
|
23
|
+
// Type thrown in case the queryFn rejects
|
|
24
|
+
E = Error,
|
|
25
|
+
// Query key type
|
|
26
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
27
|
+
> = Omit<
|
|
28
|
+
UseQueryOptions<TQueryFn, E, TQueryFn, TQueryKey>,
|
|
29
|
+
"queryKey" | "queryFn"
|
|
30
|
+
>
|
|
31
|
+
|
|
32
|
+
export const queryKeysFactory = <
|
|
33
|
+
T,
|
|
34
|
+
TListQueryType = any,
|
|
35
|
+
TDetailQueryType = string,
|
|
36
|
+
>(
|
|
37
|
+
globalKey: T
|
|
38
|
+
) => {
|
|
39
|
+
const queryKeyFactory: TQueryKey<T, TListQueryType, TDetailQueryType> = {
|
|
40
|
+
all: [globalKey],
|
|
41
|
+
lists: () => [...queryKeyFactory.all, "list"],
|
|
42
|
+
list: (query?: TListQueryType) =>
|
|
43
|
+
[...queryKeyFactory.lists(), query ? { query } : undefined].filter(
|
|
44
|
+
(k) => !!k
|
|
45
|
+
),
|
|
46
|
+
details: () => [...queryKeyFactory.all, "detail"],
|
|
47
|
+
detail: (id: TDetailQueryType, query?: TListQueryType) =>
|
|
48
|
+
[...queryKeyFactory.details(), id, query ? { query } : undefined].filter(
|
|
49
|
+
(k) => !!k
|
|
50
|
+
),
|
|
51
|
+
}
|
|
52
|
+
return queryKeyFactory
|
|
53
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
2
|
+
import { FocusModal, Heading, Button, toast, ProgressTabs } from "@medusajs/ui";
|
|
3
|
+
import { useNavigate, useParams } from "react-router-dom";
|
|
4
|
+
import { medusaClient } from "../../../../lib/config";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { AdminProductCategory } from "@medusajs/types";
|
|
7
|
+
import {
|
|
8
|
+
AttributeForm,
|
|
9
|
+
CreateAttributeFormSchema,
|
|
10
|
+
} from "../../components/AttributeForm";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import {
|
|
13
|
+
attributeQueryKeys,
|
|
14
|
+
useAttribute,
|
|
15
|
+
useUpdateAttribute,
|
|
16
|
+
} from "../../../../hooks/api/attributes";
|
|
17
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
18
|
+
|
|
19
|
+
const EditAttributePage = () => {
|
|
20
|
+
const navigate = useNavigate();
|
|
21
|
+
const { id } = useParams();
|
|
22
|
+
const [categories, setCategories] = useState<AdminProductCategory[]>([]);
|
|
23
|
+
const [activeTab, setActiveTab] = useState<"details" | "type">("details");
|
|
24
|
+
const [tabStatuses, setTabStatuses] = useState<{
|
|
25
|
+
detailsStatus: "not-started" | "in-progress" | "completed";
|
|
26
|
+
typeStatus: "not-started" | "in-progress" | "completed";
|
|
27
|
+
}>({
|
|
28
|
+
detailsStatus: "completed", // Edit mode starts with completed status
|
|
29
|
+
typeStatus: "completed", // Edit mode starts with completed status
|
|
30
|
+
});
|
|
31
|
+
const queryClient = useQueryClient();
|
|
32
|
+
|
|
33
|
+
const { attribute, isLoading } = useAttribute(
|
|
34
|
+
id ?? "",
|
|
35
|
+
{
|
|
36
|
+
fields:
|
|
37
|
+
"name,description,handle,is_variant_defining,is_filterable,ui_component,product_categories.name,possible_values.*",
|
|
38
|
+
},
|
|
39
|
+
{ enabled: !!id }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const { mutateAsync } = useUpdateAttribute(id!);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const fetchCategories = async () => {
|
|
46
|
+
try {
|
|
47
|
+
const response = await medusaClient.admin.productCategory.list();
|
|
48
|
+
setCategories(response.product_categories);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("Failed to fetch categories:", error);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
fetchCategories();
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const handleSave = async (
|
|
57
|
+
data: z.infer<typeof CreateAttributeFormSchema>
|
|
58
|
+
) => {
|
|
59
|
+
const { ...payload } = data;
|
|
60
|
+
await mutateAsync(payload, {
|
|
61
|
+
onSuccess: () => {
|
|
62
|
+
queryClient.invalidateQueries({ queryKey: attributeQueryKeys.lists() });
|
|
63
|
+
toast.success("Attribute updated!");
|
|
64
|
+
navigate(-1);
|
|
65
|
+
},
|
|
66
|
+
onError: (error) => {
|
|
67
|
+
toast.error((error as Error).message);
|
|
68
|
+
console.error(error);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handleClose = () => {
|
|
74
|
+
navigate(-1);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (isLoading || !attribute) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<FocusModal
|
|
83
|
+
open={true}
|
|
84
|
+
onOpenChange={(open) => {
|
|
85
|
+
if (!open) handleClose();
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<FocusModal.Content>
|
|
89
|
+
<ProgressTabs value={activeTab} onValueChange={(value) => setActiveTab(value as "details" | "type")} className="w-full h-full">
|
|
90
|
+
<FocusModal.Header className="flex items-center justify-between w-full py-0 h-fit">
|
|
91
|
+
<div className="w-full border-l h-full">
|
|
92
|
+
<ProgressTabs.List className="justify-start flex w-full items-center">
|
|
93
|
+
<ProgressTabs.Trigger value="details" status={tabStatuses.detailsStatus}>
|
|
94
|
+
Details
|
|
95
|
+
</ProgressTabs.Trigger>
|
|
96
|
+
<ProgressTabs.Trigger value="type" status={tabStatuses.typeStatus}>
|
|
97
|
+
Type
|
|
98
|
+
</ProgressTabs.Trigger>
|
|
99
|
+
</ProgressTabs.List>
|
|
100
|
+
</div>
|
|
101
|
+
</FocusModal.Header>
|
|
102
|
+
<FocusModal.Body className="flex flex-col items-center py-16">
|
|
103
|
+
<div>
|
|
104
|
+
<AttributeForm
|
|
105
|
+
initialData={attribute}
|
|
106
|
+
//@ts-expect-error correct data type will be received here
|
|
107
|
+
onSubmit={handleSave}
|
|
108
|
+
onCancel={handleClose}
|
|
109
|
+
categories={categories}
|
|
110
|
+
activeTab={activeTab}
|
|
111
|
+
onFormStateChange={setTabStatuses}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
</FocusModal.Body>
|
|
115
|
+
</ProgressTabs>
|
|
116
|
+
<FocusModal.Footer>
|
|
117
|
+
<Button variant="secondary" onClick={handleClose}>
|
|
118
|
+
Cancel
|
|
119
|
+
</Button>
|
|
120
|
+
<Button type="submit" form="attribute-form">
|
|
121
|
+
Save
|
|
122
|
+
</Button>
|
|
123
|
+
</FocusModal.Footer>
|
|
124
|
+
</FocusModal.Content>
|
|
125
|
+
</FocusModal>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const config = defineRouteConfig({
|
|
130
|
+
label: "Edit Attribute",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export default EditAttributePage;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { defineRouteConfig } from "@medusajs/admin-sdk"
|
|
2
|
+
import { Drawer, Heading, Text, Button, Input, toast, Label } from "@medusajs/ui"
|
|
3
|
+
import { useQuery } from "@tanstack/react-query"
|
|
4
|
+
import { useParams, useSearchParams, useNavigate } from "react-router-dom"
|
|
5
|
+
import { medusaClient } from "../../../../lib/config"
|
|
6
|
+
import { Attribute } from "../../../../../types/attribute/http/attribute"
|
|
7
|
+
import { useEffect } from "react"
|
|
8
|
+
import { useForm } from "react-hook-form"
|
|
9
|
+
import { zodResolver } from "@hookform/resolvers/zod"
|
|
10
|
+
import { z } from "zod"
|
|
11
|
+
import { MetadataEditor } from "../../../../components/metadata-editor"
|
|
12
|
+
import { useAttribute, useUpdateAttributePossibleValue } from "../../../../hooks/api/attributes"
|
|
13
|
+
|
|
14
|
+
const formSchema = z.object({
|
|
15
|
+
value: z.string().min(1, "Value is required"),
|
|
16
|
+
rank: z.preprocess(
|
|
17
|
+
(val) => (val === "" ? undefined : Number(val)),
|
|
18
|
+
z.number().min(0, "Rank must be non-negative").optional()
|
|
19
|
+
),
|
|
20
|
+
metadata: z.array(z.object({
|
|
21
|
+
key: z.string(),
|
|
22
|
+
value: z.string(),
|
|
23
|
+
})).default([]),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
type FormValues = z.infer<typeof formSchema>
|
|
27
|
+
|
|
28
|
+
const EditPossibleValuePage = () => {
|
|
29
|
+
const { id: attributeId } = useParams()
|
|
30
|
+
const [searchParams] = useSearchParams()
|
|
31
|
+
const navigate = useNavigate()
|
|
32
|
+
const possibleValueId = searchParams.get("possible_value_id")
|
|
33
|
+
|
|
34
|
+
const { attribute, isLoading: isAttributeLoading } = useAttribute(attributeId!, {
|
|
35
|
+
fields: 'possible_values.*'
|
|
36
|
+
}, {
|
|
37
|
+
enabled: !!attributeId,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const { mutateAsync, isPending } = useUpdateAttributePossibleValue(attributeId!, possibleValueId!)
|
|
41
|
+
|
|
42
|
+
const possibleValue = attribute?.possible_values?.find((pv: { id: string }) => pv.id === possibleValueId)
|
|
43
|
+
|
|
44
|
+
const form = useForm<FormValues>({
|
|
45
|
+
resolver: zodResolver(formSchema),
|
|
46
|
+
defaultValues: {
|
|
47
|
+
value: "",
|
|
48
|
+
rank: undefined,
|
|
49
|
+
metadata: [],
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (possibleValue) {
|
|
55
|
+
const metadataArray = Object.entries(possibleValue.metadata || {}).map(([key, value]) => ({ key, value: String(value) }))
|
|
56
|
+
form.reset({
|
|
57
|
+
value: possibleValue.value,
|
|
58
|
+
rank: possibleValue.rank,
|
|
59
|
+
metadata: metadataArray.length > 0 ? metadataArray : [{ key: "", value: "" }],
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}, [possibleValue, form])
|
|
63
|
+
|
|
64
|
+
const handleSave = form.handleSubmit(async (data) => {
|
|
65
|
+
const transformedMetadata = data.metadata.reduce((acc, item) => {
|
|
66
|
+
// Only include valid key-value pairs where both key and value are non-empty
|
|
67
|
+
if (item.key.trim() !== "" && item.value.trim() !== "") {
|
|
68
|
+
acc[item.key] = item.value;
|
|
69
|
+
}
|
|
70
|
+
return acc;
|
|
71
|
+
}, {} as Record<string, unknown>);
|
|
72
|
+
|
|
73
|
+
await mutateAsync(
|
|
74
|
+
{
|
|
75
|
+
value: data.value,
|
|
76
|
+
rank: data.rank,
|
|
77
|
+
metadata:
|
|
78
|
+
Object.keys(transformedMetadata).length > 0
|
|
79
|
+
? transformedMetadata
|
|
80
|
+
: null,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
onSuccess: () => {
|
|
84
|
+
toast.success("Possible value updated!");
|
|
85
|
+
navigate(-1);
|
|
86
|
+
},
|
|
87
|
+
onError: (error) => {
|
|
88
|
+
toast.error("Failed to update possible value");
|
|
89
|
+
console.error(error);
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const handleClose = () => {
|
|
96
|
+
navigate(`/attributes/${attributeId}`)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Drawer open={true} onOpenChange={(open) => { if (!open) handleClose() }}>
|
|
101
|
+
<Drawer.Content>
|
|
102
|
+
{isAttributeLoading ? (
|
|
103
|
+
<>
|
|
104
|
+
<Drawer.Header>
|
|
105
|
+
<Heading>Loading...</Heading>
|
|
106
|
+
</Drawer.Header>
|
|
107
|
+
<Drawer.Body>
|
|
108
|
+
<Text>Fetching possible value details...</Text>
|
|
109
|
+
</Drawer.Body>
|
|
110
|
+
</>
|
|
111
|
+
) : !possibleValue ? (
|
|
112
|
+
<>
|
|
113
|
+
<Drawer.Header>
|
|
114
|
+
<Heading>Possible Value Not Found</Heading>
|
|
115
|
+
</Drawer.Header>
|
|
116
|
+
<Drawer.Body>
|
|
117
|
+
<Text>The requested possible value could not be found.</Text>
|
|
118
|
+
<Button onClick={handleClose}>Close</Button>
|
|
119
|
+
</Drawer.Body>
|
|
120
|
+
</>
|
|
121
|
+
) : (
|
|
122
|
+
<>
|
|
123
|
+
<Drawer.Header>
|
|
124
|
+
<Drawer.Title>Edit Possible Value</Drawer.Title>
|
|
125
|
+
</Drawer.Header>
|
|
126
|
+
<Drawer.Body>
|
|
127
|
+
<form id="edit-possible-value-form" onSubmit={handleSave}>
|
|
128
|
+
<div className="grid gap-4">
|
|
129
|
+
<div>
|
|
130
|
+
<Label htmlFor="value">Value</Label>
|
|
131
|
+
<Input
|
|
132
|
+
id="value"
|
|
133
|
+
{...form.register("value")}
|
|
134
|
+
/>
|
|
135
|
+
{form.formState.errors.value && (
|
|
136
|
+
<Text className="text-red-500 text-sm mt-1">
|
|
137
|
+
{form.formState.errors.value.message}
|
|
138
|
+
</Text>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
<div>
|
|
142
|
+
<Label htmlFor="rank">Rank</Label>
|
|
143
|
+
<Input
|
|
144
|
+
id="rank"
|
|
145
|
+
type="number"
|
|
146
|
+
{...form.register("rank", { valueAsNumber: true })}
|
|
147
|
+
/>
|
|
148
|
+
{form.formState.errors.rank && (
|
|
149
|
+
<Text className="text-red-500 text-sm mt-1">
|
|
150
|
+
{form.formState.errors.rank.message}
|
|
151
|
+
</Text>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<MetadataEditor form={form} />
|
|
156
|
+
</div>
|
|
157
|
+
</form>
|
|
158
|
+
</Drawer.Body>
|
|
159
|
+
<Drawer.Footer>
|
|
160
|
+
<Button variant="secondary" onClick={handleClose} disabled={!!isPending}>Cancel</Button>
|
|
161
|
+
<Button type="submit" form="edit-possible-value-form" disabled={!!isPending}>Save</Button>
|
|
162
|
+
</Drawer.Footer>
|
|
163
|
+
</>
|
|
164
|
+
)}
|
|
165
|
+
</Drawer.Content>
|
|
166
|
+
</Drawer>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const config = defineRouteConfig({
|
|
171
|
+
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
export default EditPossibleValuePage
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
2
|
+
import {
|
|
3
|
+
Container,
|
|
4
|
+
Heading,
|
|
5
|
+
Text,
|
|
6
|
+
toast,
|
|
7
|
+
DropdownMenu,
|
|
8
|
+
Button,
|
|
9
|
+
Badge,
|
|
10
|
+
} from "@medusajs/ui";
|
|
11
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
12
|
+
import { useParams, useNavigate } from "react-router-dom";
|
|
13
|
+
import { medusaClient } from "../../../lib/config";
|
|
14
|
+
import { Attribute } from "../../../../types/attribute/http/attribute";
|
|
15
|
+
import { EllipsisHorizontal } from "@medusajs/icons";
|
|
16
|
+
import { SectionRow } from "../../../components/section-row";
|
|
17
|
+
import { PossibleValuesTable } from "../components/possible-values-table";
|
|
18
|
+
import { SingleColumnLayout } from "../../../layouts/single-column";
|
|
19
|
+
import { attributeQueryKeys, useAttribute } from "../../../hooks/api/attributes";
|
|
20
|
+
|
|
21
|
+
const AttributeDetailPage = () => {
|
|
22
|
+
const { id } = useParams();
|
|
23
|
+
const navigate = useNavigate();
|
|
24
|
+
|
|
25
|
+
const queryClient = useQueryClient()
|
|
26
|
+
|
|
27
|
+
const { attribute, isLoading } = useAttribute(id ?? "", {
|
|
28
|
+
fields: 'name, description, handle, product_categories.name, possible_values.*, is_filterable, ui_component'
|
|
29
|
+
}, { enabled: !!id })
|
|
30
|
+
|
|
31
|
+
if (isLoading) {
|
|
32
|
+
return (
|
|
33
|
+
<Container>
|
|
34
|
+
<div className="flex items-center justify-center h-[200px]">
|
|
35
|
+
<Text>Loading...</Text>
|
|
36
|
+
</div>
|
|
37
|
+
</Container>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!attribute) {
|
|
42
|
+
return (
|
|
43
|
+
<Container>
|
|
44
|
+
<div className="flex items-center justify-center h-[200px]">
|
|
45
|
+
<Text>Attribute not found</Text>
|
|
46
|
+
</div>
|
|
47
|
+
</Container>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const handleEdit = () => {
|
|
52
|
+
navigate(`/attributes/${id}/edit`);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleDelete = async () => {
|
|
56
|
+
try {
|
|
57
|
+
await medusaClient.client.fetch(`/admin/plugin/attributes/${id}`, {
|
|
58
|
+
method: "DELETE",
|
|
59
|
+
});
|
|
60
|
+
toast.success("Attribute deleted!");
|
|
61
|
+
queryClient.invalidateQueries({ queryKey: attributeQueryKeys.list() });
|
|
62
|
+
navigate("/attributes");
|
|
63
|
+
} catch (error) {
|
|
64
|
+
toast.error((error as Error).message);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<SingleColumnLayout>
|
|
70
|
+
<Container className="divide-y p-0">
|
|
71
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
72
|
+
<Heading level="h2">{attribute.name}</Heading>
|
|
73
|
+
<div className="flex items-center gap-2">
|
|
74
|
+
<DropdownMenu>
|
|
75
|
+
<DropdownMenu.Trigger asChild>
|
|
76
|
+
<Button variant="transparent" size="small">
|
|
77
|
+
<EllipsisHorizontal />
|
|
78
|
+
</Button>
|
|
79
|
+
</DropdownMenu.Trigger>
|
|
80
|
+
<DropdownMenu.Content align="end">
|
|
81
|
+
<DropdownMenu.Item onClick={handleEdit}>
|
|
82
|
+
Edit
|
|
83
|
+
</DropdownMenu.Item>
|
|
84
|
+
<DropdownMenu.Item onClick={handleDelete}>
|
|
85
|
+
Delete
|
|
86
|
+
</DropdownMenu.Item>
|
|
87
|
+
</DropdownMenu.Content>
|
|
88
|
+
</DropdownMenu>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<SectionRow title="Description" value={attribute.description} />
|
|
93
|
+
<SectionRow title="Handle" value={attribute.handle} />
|
|
94
|
+
<SectionRow title="UI Component" value={attribute.ui_component} />
|
|
95
|
+
<SectionRow title="Filterable" value={attribute.is_filterable ? "True" : "False"} />
|
|
96
|
+
<SectionRow
|
|
97
|
+
title="Global"
|
|
98
|
+
value={!attribute.product_categories?.length ? "True" : "False"}
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
{attribute.product_categories &&
|
|
102
|
+
attribute.product_categories.length > 0 && (
|
|
103
|
+
<SectionRow
|
|
104
|
+
title="Product Categories"
|
|
105
|
+
value={
|
|
106
|
+
<>
|
|
107
|
+
{attribute.product_categories.map((category) => (
|
|
108
|
+
<Badge size="xsmall" key={category.id}>
|
|
109
|
+
{category.name}
|
|
110
|
+
</Badge>
|
|
111
|
+
))}
|
|
112
|
+
</>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
)}
|
|
116
|
+
</Container>
|
|
117
|
+
|
|
118
|
+
<PossibleValuesTable attribute={attribute} isLoading={isLoading} />
|
|
119
|
+
</SingleColumnLayout>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const config = defineRouteConfig({
|
|
124
|
+
label: "Attribute Details",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export default AttributeDetailPage;
|