diff --git a/src/assets/icons/BannerIcon.tsx b/src/assets/icons/BannerIcon.tsx new file mode 100644 index 00000000..4b038291 --- /dev/null +++ b/src/assets/icons/BannerIcon.tsx @@ -0,0 +1,61 @@ +export const BannerIcon = () => { + return ( + + + + + + + + + + + + + + ); +}; diff --git a/src/assets/icons/ThumbnailIcon.tsx b/src/assets/icons/ThumbnailIcon.tsx new file mode 100644 index 00000000..598e1ef9 --- /dev/null +++ b/src/assets/icons/ThumbnailIcon.tsx @@ -0,0 +1,33 @@ +export const ThumbnailIcon = () => { + return ( + + + + + + ); +}; diff --git a/src/dashboard-app/routes/get-route.map.tsx b/src/dashboard-app/routes/get-route.map.tsx index fae05526..547cee88 100644 --- a/src/dashboard-app/routes/get-route.map.tsx +++ b/src/dashboard-app/routes/get-route.map.tsx @@ -260,6 +260,11 @@ export function getRouteMap({ }; }, children: [ + { + path: 'media', + lazy: () => + import("../../routes/categories/category-media"), + }, { path: "edit", lazy: () => diff --git a/src/hooks/api/categories.tsx b/src/hooks/api/categories.tsx index 1d34c479..f60e67e9 100644 --- a/src/hooks/api/categories.tsx +++ b/src/hooks/api/categories.tsx @@ -11,6 +11,7 @@ import { sdk } from "../../lib/client" import { queryClient } from "../../lib/query-client" import { queryKeysFactory } from "../../lib/query-key-factory" import { productsQueryKeys } from "./products" +import { CategoryDetail } from "@routes/categories/category-detail/types" const CATEGORIES_QUERY_KEY = "categories" as const export const categoriesQueryKeys = queryKeysFactory(CATEGORIES_QUERY_KEY) @@ -98,6 +99,33 @@ export const useUpdateProductCategory = ( }) } +export const useUpdateProductCategoryDetails = () => { + return useMutation({ + mutationFn: ({ + id, + payload + }: { + id: string; + payload: { + media: { delete?: string[]; create?: { url: string; alt_text?: string }[] }; + thumbnail?: { url: string} | string | null; + icon?: { url: string} | string |null; + banner?: { url: string} | string| null; + rank?: number | null; + }; + }) => + sdk.client.fetch<{ + category_detail: CategoryDetail; + }>(`/admin/product-categories/${id}/details`, { + method: 'POST', + body: payload + }), + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ queryKey: categoriesQueryKeys.detail(variables.id) }); + } + }); +}; + export const useDeleteProductCategory = ( id: string, options?: UseMutationOptions< diff --git a/src/routes/categories/category-create/components/create-category-form/create-category-details.tsx b/src/routes/categories/category-create/components/create-category-form/create-category-details.tsx index 1cb902ae..6500e7a7 100644 --- a/src/routes/categories/category-create/components/create-category-form/create-category-details.tsx +++ b/src/routes/categories/category-create/components/create-category-form/create-category-details.tsx @@ -1,26 +1,88 @@ -import { Heading, Input, Select, Text, Textarea } from "@medusajs/ui" -import { UseFormReturn } from "react-hook-form" -import { useTranslation } from "react-i18next" +import { + Avatar, + Button, + Container, + DropdownMenu, + Heading, + InlineTip, + Input, + Select, + Text, + Textarea, + Tooltip +} from '@medusajs/ui'; +import { UseFormReturn } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; -import { Form } from "../../../../../components/common/form" -import { HandleInput } from "../../../../../components/inputs/handle-input" -import { useDocumentDirection } from "../../../../../hooks/use-document-direction" -import { CreateCategorySchema } from "./schema" +import { Form } from '../../../../../components/common/form'; +import { HandleInput } from '../../../../../components/inputs/handle-input'; +import { useDocumentDirection } from '../../../../../hooks/use-document-direction'; +import { CreateCategorySchema } from './schema'; +import { FileType, FileUpload } from '@components/common/file-upload'; +import { + EllipsisHorizontal, + InformationCircleSolid, + QueueList, + ThumbnailBadge, + Trash, + XMarkMini +} from '@medusajs/icons'; +import { BannerIcon } from '@assets/icons/BannerIcon'; +import { ThumbnailIcon } from '@assets/icons/ThumbnailIcon'; type CreateCategoryDetailsProps = { - form: UseFormReturn -} + form: UseFormReturn; +}; + +const SUPPORTED_FORMATS = [ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/webp', + 'image/heic', + 'image/svg+xml' +]; export const CreateCategoryDetails = ({ form }: CreateCategoryDetailsProps) => { - const { t } = useTranslation() - const direction = useDocumentDirection() + const { t } = useTranslation(); + const direction = useDocumentDirection(); + + const handleMediaChange = (onChange: (value: any) => void, next: FileType[]) => { + onChange(next.map(m => ({ ...m, isThumbnail: false, isBanner: false }))); + }; + + const promoteExclusive = < + T extends { id: string; url: string; isThumbnail: boolean; isBanner: boolean } + >( + list: T[], + targetId: string, + flag: 'isThumbnail' | 'isBanner' + ): T[] => { + const current = list.find(m => m.id === targetId); + const nextValue = !current?.[flag]; + + return list.map(m => ({ + ...m, + [flag]: m.id === targetId ? nextValue : false + })); + }; + return (
-
+
- {t("categories.create.header")} - - {t("categories.create.hint")} + + {t('categories.create.header')} + + + {t('categories.create.hint')}
@@ -30,13 +92,19 @@ export const CreateCategoryDetails = ({ form }: CreateCategoryDetailsProps) => { render={({ field }) => { return ( - {t("fields.title")} + + {t('fields.title')} + - + - ) + ); }} /> { render={({ field }) => { return ( - - {t("fields.handle")} + + {t('fields.handle')} - + - ) + ); }} />
@@ -63,13 +138,21 @@ export const CreateCategoryDetails = ({ form }: CreateCategoryDetailsProps) => { render={({ field }) => { return ( - {t("fields.description")} + + {t('fields.description')} + -