diff --git a/.changeset/calm-bananas-roll.md b/.changeset/calm-bananas-roll.md new file mode 100644 index 0000000000..1527b780df --- /dev/null +++ b/.changeset/calm-bananas-roll.md @@ -0,0 +1,5 @@ +--- +"@medusajs/admin-ui": patch +--- + +feat(admin-ui): added breadcrumbs for categories on create/edit modal diff --git a/packages/admin-ui/ui/src/domain/product-categories/components/tree-crumbs.tsx b/packages/admin-ui/ui/src/domain/product-categories/components/tree-crumbs.tsx new file mode 100644 index 0000000000..7813ef8272 --- /dev/null +++ b/packages/admin-ui/ui/src/domain/product-categories/components/tree-crumbs.tsx @@ -0,0 +1,54 @@ +import React from "react" +import { ProductCategory } from "@medusajs/medusa" +import { getAncestors } from "../utils" + +type TreeCrumbsProps = React.HtmlHTMLAttributes & { + nodes: ProductCategory[] + currentNode: ProductCategory + showPlaceholder: boolean + placeholderText: string +} + +const TreeCrumbs: React.FC = ({ + nodes, + currentNode, + showPlaceholder = false, + placeholderText = "", + ...props +}) => { + const ancestors = getAncestors(currentNode, nodes) + + return ( + + + {ancestors.map((ancestor, index) => { + const categoryName = ancestor.name + + return ( +
+ + {categoryName.length > 25 + ? categoryName.substring(0, 25) + "..." + : categoryName} + + + {(showPlaceholder || ancestors.length !== index + 1) && ( + / + )} +
+ ) + })} + + {showPlaceholder && ( + + + {placeholderText} + + + )} +
+
+ ) +} + +export default TreeCrumbs diff --git a/packages/admin-ui/ui/src/domain/product-categories/modals/add-product-category.tsx b/packages/admin-ui/ui/src/domain/product-categories/modals/add-product-category.tsx index 5e0fb658d9..73db73dfef 100644 --- a/packages/admin-ui/ui/src/domain/product-categories/modals/add-product-category.tsx +++ b/packages/admin-ui/ui/src/domain/product-categories/modals/add-product-category.tsx @@ -12,6 +12,7 @@ import Button from "../../../components/fundamentals/button" import CrossIcon from "../../../components/fundamentals/icons/cross-icon" import InputField from "../../../components/molecules/input" import Select from "../../../components/molecules/select" +import TreeCrumbs from "../components/tree-crumbs" import { useQueryClient } from "@tanstack/react-query" const visibilityOptions = [ @@ -36,7 +37,7 @@ type CreateProductCategoryProps = { * Focus modal container for creating Publishable Keys. */ function CreateProductCategory(props: CreateProductCategoryProps) { - const { closeModal, parentCategory } = props + const { closeModal, parentCategory, categories } = props const notification = useNotification() const queryClient = useQueryClient() @@ -90,9 +91,21 @@ function CreateProductCategory(props: CreateProductCategoryProps) {
-

+

Add category {parentCategory && `to ${parentCategory.name}`}

+ + {parentCategory && ( +
+ +
+ )} +

Details

diff --git a/packages/admin-ui/ui/src/domain/product-categories/modals/edit-product-category.tsx b/packages/admin-ui/ui/src/domain/product-categories/modals/edit-product-category.tsx index f45cafb71a..ff1a084ea4 100644 --- a/packages/admin-ui/ui/src/domain/product-categories/modals/edit-product-category.tsx +++ b/packages/admin-ui/ui/src/domain/product-categories/modals/edit-product-category.tsx @@ -9,6 +9,7 @@ import CrossIcon from "../../../components/fundamentals/icons/cross-icon" import InputField from "../../../components/molecules/input" import Select from "../../../components/molecules/select" import useNotification from "../../../hooks/use-notification" +import TreeCrumbs from "../components/tree-crumbs" const visibilityOptions = [ { @@ -35,7 +36,7 @@ type EditProductCategoriesSideModalProps = { function EditProductCategoriesSideModal( props: EditProductCategoriesSideModalProps ) { - const { isVisible, close, activeCategory } = props + const { isVisible, close, activeCategory, categories } = props const [name, setName] = useState("") const [handle, setHandle] = useState("") @@ -81,10 +82,9 @@ function EditProductCategoriesSideModal( return ( -
+
{/* === HEADER === */} - -
+

Edit product category

@@ -96,9 +96,17 @@ function EditProductCategoriesSideModal(
- {/* === DIVIDER === */} -
+ {/* === DIVIDER === */} +
+ + {activeCategory && ( +
+ +
+ )} + +
setIsPublic(o.value === "public")} />
+ {/* === DIVIDER === */} +
-
{/* === FOOTER === */} - -
+
diff --git a/packages/admin-ui/ui/src/domain/product-categories/pages/index.tsx b/packages/admin-ui/ui/src/domain/product-categories/pages/index.tsx index 2fa5fcc59d..7f8ee24fbc 100644 --- a/packages/admin-ui/ui/src/domain/product-categories/pages/index.tsx +++ b/packages/admin-ui/ui/src/domain/product-categories/pages/index.tsx @@ -8,6 +8,7 @@ import BodyCard from "../../../components/organisms/body-card" import CreateProductCategory from "../modals/add-product-category" import ProductCategoriesList from "../components/product-categories-list" import EditProductCategoriesSideModal from "../modals/edit-product-category" +import { flattenCategoryTree } from "../utils" /** * Product categories empty state placeholder. @@ -46,7 +47,7 @@ function ProductCategoryPage() { const [activeCategory, setActiveCategory] = useState() - const { product_categories: categories, isLoading } = + const { product_categories: categories = [], isLoading } = useAdminProductCategories({ parent_category_id: "null", include_descendants_tree: true, @@ -59,7 +60,7 @@ function ProductCategoryPage() { }, ] - const showPlaceholder = !isLoading && !categories?.length + const showPlaceholder = !isLoading && !categories.length const editCategory = (category: ProductCategory) => { setActiveCategory(category) @@ -67,10 +68,14 @@ function ProductCategoryPage() { } const createSubCategory = (category: ProductCategory) => { + if (isLoading) { + return + } setActiveCategory(category) showCreateModal() } + const flattenedCategories = flattenCategoryTree(categories) const context = { editCategory, createSubCategory, @@ -97,6 +102,7 @@ function ProductCategoryPage() { {isCreateModalVisible && ( { hideCreateModal() setActiveCategory(undefined) @@ -108,6 +114,7 @@ function ProductCategoryPage() { close={hideEditModal} activeCategory={activeCategory} isVisible={!!activeCategory && isEditModalVisible} + categories={flattenedCategories} />
diff --git a/packages/admin-ui/ui/src/domain/product-categories/utils/index.tsx b/packages/admin-ui/ui/src/domain/product-categories/utils/index.tsx new file mode 100644 index 0000000000..665ac3653b --- /dev/null +++ b/packages/admin-ui/ui/src/domain/product-categories/utils/index.tsx @@ -0,0 +1,31 @@ +export const flattenCategoryTree = (rootCategories) => { + return rootCategories.reduce((acc, category) => { + if (category?.category_children.length) { + acc = acc + .concat(flattenCategoryTree(category.category_children)) + .concat(category) + } else { + acc.push(category) + } + + return acc + }, []) +} + +export const getAncestors = (targetNode, nodes, acc = []) => { + let parentCategory = null + + acc.push(targetNode) + + if (targetNode.parent_category_id) { + parentCategory = nodes.find((n) => n.id === targetNode.parent_category_id) + + acc = getAncestors(parentCategory, nodes, acc) + } + + if (!parentCategory) { + return acc.reverse() + } + + return acc +}