Skip to content

Commit

Permalink
feat(admin-ui): added breadcrumbs for categories on create/edit modal (
Browse files Browse the repository at this point in the history
…#3420)

What:
- Adds breadcrumbs to create modal
- Adds breadcrumbs to edit modal

<img width="581" alt="2" src="https://user-images.githubusercontent.com/5105988/223782603-f168d554-65bd-4cfc-bdcd-eabdd9f06b20.png">
<img width="1115" alt="1" src="https://user-images.githubusercontent.com/5105988/223782607-1ae441c9-c9eb-4cb0-9015-2038db55dd64.png">


RESOLVES CORE-1210
  • Loading branch information
riqwan authored Mar 9, 2023
1 parent 4042beb commit cdbc5ff
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/calm-bananas-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/admin-ui": patch
---

feat(admin-ui): added breadcrumbs for categories on create/edit modal
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react"
import { ProductCategory } from "@medusajs/medusa"
import { getAncestors } from "../utils"

type TreeCrumbsProps = React.HtmlHTMLAttributes<HTMLDivElement> & {
nodes: ProductCategory[]
currentNode: ProductCategory
showPlaceholder: boolean
placeholderText: string
}

const TreeCrumbs: React.FC<TreeCrumbsProps> = ({
nodes,
currentNode,
showPlaceholder = false,
placeholderText = "",
...props
}) => {
const ancestors = getAncestors(currentNode, nodes)

return (
<span {...props}>
<span className="text-grey-40">
{ancestors.map((ancestor, index) => {
const categoryName = ancestor.name

return (
<div key={ancestor.id} className="inline-block">
<span>
{categoryName.length > 25
? categoryName.substring(0, 25) + "..."
: categoryName}
</span>

{(showPlaceholder || ancestors.length !== index + 1) && (
<span className="mx-2">/</span>
)}
</div>
)
})}

{showPlaceholder && (
<span>
<span className="border-grey-40 rounded-[10px] border-[1px] border-dashed px-[8px] py-[4px]">
{placeholderText}
</span>
</span>
)}
</span>
</span>
)
}

export default TreeCrumbs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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()

Expand Down Expand Up @@ -90,9 +91,21 @@ function CreateProductCategory(props: CreateProductCategoryProps) {

<FocusModal.Main className="no-scrollbar flex w-full justify-center">
<div className="small:w-4/5 medium:w-7/12 large:w-6/12 my-16 max-w-[700px]">
<h1 className="inter-xlarge-semibold text-grey-90 pb-8">
<h1 className="inter-xlarge-semibold text-grey-90 pb-6">
Add category {parentCategory && `to ${parentCategory.name}`}
</h1>

{parentCategory && (
<div className="mb-6">
<TreeCrumbs
nodes={categories}
currentNode={parentCategory}
showPlaceholder={true}
placeholderText={name || "New"}
/>
</div>
)}

<h4 className="inter-large-semibold text-grey-90 pb-1">Details</h4>

<div className="mb-8 flex justify-between gap-6">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand All @@ -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("")
Expand Down Expand Up @@ -81,10 +82,9 @@ function EditProductCategoriesSideModal(

return (
<SideModal close={onClose} isVisible={!!isVisible}>
<div className="flex h-full flex-col justify-between p-6">
<div className="flex h-full flex-col justify-between">
{/* === HEADER === */}

<div className="flex items-center justify-between">
<div className="flex items-center justify-between p-6">
<h3 className="inter-large-semibold flex items-center gap-2 text-xl text-gray-900">
Edit product category
</h3>
Expand All @@ -96,9 +96,17 @@ function EditProductCategoriesSideModal(
<CrossIcon size={20} className="text-grey-50" />
</Button>
</div>

{/* === DIVIDER === */}
<div className="block h-[1px] bg-gray-200" />

{activeCategory && (
<div className="mt-[25px] px-6">
<TreeCrumbs nodes={categories} currentNode={activeCategory} />
</div>
)}

<div className="flex-grow">
<div className="flex-grow px-6">
<InputField
required
label="Name"
Expand Down Expand Up @@ -138,15 +146,12 @@ function EditProductCategoriesSideModal(
onChange={(o) => setIsPublic(o.value === "public")}
/>
</div>

{/* === DIVIDER === */}
<div className="block h-[1px] bg-gray-200" />

<div
className="block h-[1px] bg-gray-200"
style={{ margin: "24px -24px" }}
/>
{/* === FOOTER === */}

<div className="flex justify-end gap-2">
<div className="flex justify-end gap-2 p-3">
<Button size="small" variant="ghost" onClick={onClose}>
Cancel
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -46,7 +47,7 @@ function ProductCategoryPage() {

const [activeCategory, setActiveCategory] = useState<ProductCategory>()

const { product_categories: categories, isLoading } =
const { product_categories: categories = [], isLoading } =
useAdminProductCategories({
parent_category_id: "null",
include_descendants_tree: true,
Expand All @@ -59,18 +60,22 @@ function ProductCategoryPage() {
},
]

const showPlaceholder = !isLoading && !categories?.length
const showPlaceholder = !isLoading && !categories.length

const editCategory = (category: ProductCategory) => {
setActiveCategory(category)
showEditModal()
}

const createSubCategory = (category: ProductCategory) => {
if (isLoading) {
return
}
setActiveCategory(category)
showCreateModal()
}

const flattenedCategories = flattenCategoryTree(categories)
const context = {
editCategory,
createSubCategory,
Expand All @@ -97,6 +102,7 @@ function ProductCategoryPage() {
{isCreateModalVisible && (
<CreateProductCategory
parentCategory={activeCategory}
categories={flattenedCategories}
closeModal={() => {
hideCreateModal()
setActiveCategory(undefined)
Expand All @@ -108,6 +114,7 @@ function ProductCategoryPage() {
close={hideEditModal}
activeCategory={activeCategory}
isVisible={!!activeCategory && isEditModalVisible}
categories={flattenedCategories}
/>
</div>
</div>
Expand Down
31 changes: 31 additions & 0 deletions packages/admin-ui/ui/src/domain/product-categories/utils/index.tsx
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit cdbc5ff

Please sign in to comment.