Skip to content

Commit

Permalink
feat(dashboard): Product create from - details (#7121)
Browse files Browse the repository at this point in the history
**What**
- First part of the product creation form.
- New components:
  - ChipInput - Allows users to input chips into a input field. Chips are created by hitting the `,` or `Enter / Return` keys. Deleting a chip is done by hitting `Backspace` when the cursor is next to chip, or clicking the `X` button in the chip. Used for inputting option values.
  - SortableList - A sortable drag-n-drop list that allows the user to re-arrange the order of items. Used for re-arranging the ranking of variants.
  - ChipGroup - New re-usable component that is used to render a group of values as Chips. This should be used for SplitView form items.
  - CategoryCombobox - (WIP) Nested Combobox component for selecting multiple categories a product should be associated with.
- New hooks:
  - useComboboxData - Hook for easily managing the state of comboboxes.
  - useDebouncedSearch - Hook for managing debounced search queries.
  • Loading branch information
kasperkristensen committed May 3, 2024
1 parent e423085 commit fdee748
Show file tree
Hide file tree
Showing 84 changed files with 2,832 additions and 1,583 deletions.
10 changes: 6 additions & 4 deletions packages/admin-next/dashboard/package.json
Expand Up @@ -18,6 +18,8 @@
],
"dependencies": {
"@ariakit/react": "^0.4.1",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@hookform/resolvers": "3.3.2",
"@medusajs/icons": "workspace:^",
"@medusajs/ui": "workspace:^",
Expand All @@ -36,10 +38,10 @@
"match-sorter": "^6.3.4",
"medusa-react": "workspace:^",
"qs": "^6.12.0",
"react": "18.2.0",
"react": "^18.2.0",
"react-country-flag": "^3.1.0",
"react-currency-input-field": "^3.6.11",
"react-dom": "18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "7.49.1",
"react-i18next": "13.5.0",
"react-jwt": "^1.2.0",
Expand All @@ -53,8 +55,8 @@
"@medusajs/ui-preset": "workspace:^",
"@medusajs/vite-plugin-extension": "workspace:^",
"@types/node": "^20.11.15",
"@types/react": "18.2.43",
"@types/react-dom": "18.2.17",
"@types/react": "^18.2.79",
"@types/react-dom": "^18.2.25",
"@vitejs/plugin-react": "4.2.1",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.33",
Expand Down
43 changes: 32 additions & 11 deletions packages/admin-next/dashboard/public/locales/en-US/translation.json
Expand Up @@ -41,14 +41,13 @@
"typeToConfirm": "Please type {val} to confirm:",
"noResultsTitle": "No results",
"noResultsMessage": "Try changing the filters or search query",
"noSearchResults": "No search results",
"noSearchResultsFor": "No search results for <0>'{{query}}'</0>",
"noRecordsTitle": "No records",
"noRecordsMessage": "There are no records to show",
"unsavedChangesTitle": "Are you sure you want to leave this page?",
"unsavedChangesDescription": "You have unsaved changes that will be lost if you leave this page.",
"includesTaxTooltip": "Enter the total amount including tax. The net amount excluding tax will be automatically calculated and saved.",
"timeline": "Timeline",
"success": "Success",
"error": "Error"
"includesTaxTooltip": "Enter the total amount including tax. The net amount excluding tax will be automatically calculated and saved."
},
"validation": {
"mustBeInt": "The value must be a whole number.",
Expand Down Expand Up @@ -162,8 +161,27 @@
},
"products": {
"domain": "Products",
"createProductTitle": "Create Product",
"createProductHint": "Create a new product to sell in your store.",
"create": {
"header": "Create Product",
"hint": "Create a new product to sell in your store.",
"tabs": {
"details": "Details",
"variants": "Variants"
},
"variants": {
"header": "Variants",
"productVariants": {
"label": "Product variants",
"hint": "Variants left unchecked won't be created. This ranking will affect how the variants are ranked in your frontend.",
"alert": "Add options to create variants."
},
"productOptions": {
"label": "Product options",
"hint": "Define the options for the product, e.g. color, size, etc."
}
},
"successToast": "Product {{title}} was successfully created."
},
"deleteWarning": "You are about to delete the product {{title}}. This action cannot be undone.",
"variants": "Variants",
"attributes": "Attributes",
Expand Down Expand Up @@ -296,10 +314,12 @@
"options": {
"header": "Options",
"edit": {
"header": "Edit Option"
"header": "Edit Option",
"successToast": "Option {{title}} was successfully updated."
},
"create": {
"header": "Create Option"
"header": "Create Option",
"successToast": "Option {{title}} was successfully created."
}
},
"toasts": {
Expand Down Expand Up @@ -349,11 +369,11 @@
"associatedVariants": "Associated variants",
"manageLocations": "Manage locations",
"deleteWarning": "You are about to delete an inventory item. This action cannot be undone.",
"reservation": {
"reservation": {
"header": "Reservation of {{itemName}}",
"editItemDetails": "Edit item details",
"orderID": "Order ID",
"description": "Description",
"description": "Description",
"location": "Location",
"inStockAtLocation": "In stock at this location",
"availableAtLocation": "Available at this location",
Expand Down Expand Up @@ -1579,7 +1599,8 @@
"unitPrice": "Unit price",
"startDate": "Start date",
"endDate": "End date",
"draft": "Draft"
"draft": "Draft",
"values": "Values"
},
"metadata": {
"warnings": {
Expand Down
@@ -0,0 +1,111 @@
import { XMarkMini } from "@medusajs/icons"
import { Button, clx } from "@medusajs/ui"
import { Children, PropsWithChildren, createContext, useContext } from "react"
import { useTranslation } from "react-i18next"

type ChipGroupVariant = "base" | "component"

type ChipGroupProps = PropsWithChildren<{
onClearAll?: () => void
onRemove?: (index: number) => void
variant?: ChipGroupVariant
className?: string
}>

type GroupContextValue = {
onRemove?: (index: number) => void
variant: ChipGroupVariant
}

const GroupContext = createContext<GroupContextValue | null>(null)

const useGroupContext = () => {
const context = useContext(GroupContext)

if (!context) {
throw new Error("useGroupContext must be used within a ChipGroup component")
}

return context
}

const Group = ({
onClearAll,
onRemove,
variant = "component",
className,
children,
}: ChipGroupProps) => {
const { t } = useTranslation()

const showClearAll = !!onClearAll && Children.count(children) > 0

return (
<GroupContext.Provider value={{ onRemove, variant }}>
<ul
role="application"
className={clx("flex flex-wrap items-center gap-2", className)}
>
{children}
{showClearAll && (
<li>
<Button
size="small"
variant="transparent"
type="button"
onClick={onClearAll}
className="text-ui-fg-muted active:text-ui-fg-subtle"
>
{t("actions.clearAll")}
</Button>
</li>
)}
</ul>
</GroupContext.Provider>
)
}

type ChipProps = PropsWithChildren<{
index: number
className?: string
}>

const Chip = ({ index, className, children }: ChipProps) => {
const { onRemove, variant } = useGroupContext()

return (
<li
className={clx(
"bg-ui-bg-component shadow-borders-base flex items-center divide-x overflow-hidden rounded-md",
{
"bg-ui-bg-component": variant === "component",
"bg-ui-bg-base-": variant === "base",
},
className
)}
>
<span className="txt-compact-small-plus flex items-center justify-center px-2 py-1">
{children}
</span>
{!!onRemove && (
<button
onClick={() => onRemove(index)}
type="button"
className={clx(
"text-ui-fg-muted active:text-ui-fg-subtle transition-fg flex items-center justify-center p-1",
{
"hover:bg-ui-bg-component-hover active:bg-ui-bg-component-pressed":
variant === "component",
"hover:bg-ui-bg-base-hover active:bg-ui-bg-base-pressed":
variant === "base",
}
)}
>
<XMarkMini />
</button>
)}
</li>
)
}

export const ChipGroup = Object.assign(Group, { Chip })
@@ -0,0 +1 @@
export * from "./chip-group"

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit fdee748

Please sign in to comment.