diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index d150268574..9cd31cc4f1 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -236,6 +236,22 @@ }, "weight": { "label": "Weight" + }, + "options": { + "label": "Product options", + "hint": "Options are used to define the color, size, etc. of the product", + "optionTitle": "Option title", + "variations": "Variations (comma-separated)" + }, + "variants": { + "label": "Product variants", + "hint": "Variants left unchecked won't be created, This ranking will affect how the variants are ranked in your frontend." + }, + "mid_code": { + "label": "Mid code" + }, + "hs_code": { + "label": "HS code" } }, "variant": { diff --git a/packages/admin-next/dashboard/src/components/common/list/index.tsx b/packages/admin-next/dashboard/src/components/common/list/index.tsx new file mode 100644 index 0000000000..252d7e2efc --- /dev/null +++ b/packages/admin-next/dashboard/src/components/common/list/index.tsx @@ -0,0 +1 @@ +export * from "./list" diff --git a/packages/admin-next/dashboard/src/components/common/list/list.tsx b/packages/admin-next/dashboard/src/components/common/list/list.tsx new file mode 100644 index 0000000000..4817c4cb8c --- /dev/null +++ b/packages/admin-next/dashboard/src/components/common/list/list.tsx @@ -0,0 +1,54 @@ +import { Checkbox, Text } from "@medusajs/ui" + +export interface ListProps { + options: { title: string; value: T }[] + value?: T[] + onChange?: (value: T[]) => void + compare?: (a: T, b: T) => boolean + disabled?: boolean +} + +export const List = ({ + options, + onChange, + value, + compare, + disabled, +}: ListProps) => { + if (options.length === 0) { + return
No options
+ } + + return ( +
+ {options.map((option) => { + return ( +
+ {onChange && value !== undefined && ( + compare?.(v, option.value) ?? v === option.value + )} + onCheckedChange={(checked) => { + if (checked) { + onChange([...value, option.value]) + } else { + onChange( + value.filter( + (v) => + !(compare?.(v, option.value) ?? v === option.value) + ) + ) + } + }} + /> + )} + + {option.title} +
+ ) + })} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/categories.tsx b/packages/admin-next/dashboard/src/hooks/api/categories.tsx new file mode 100644 index 0000000000..f5cd9e88c3 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/categories.tsx @@ -0,0 +1,39 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { CategoriesListRes, CategoryRes } from "../../types/api-responses" + +const CATEGORIES_QUERY_KEY = "categories" as const +export const categoriesQueryKeys = queryKeysFactory(CATEGORIES_QUERY_KEY) + +export const useCategory = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: categoriesQueryKeys.detail(id), + queryFn: async () => client.categories.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useCategories = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: categoriesQueryKeys.list(query), + queryFn: async () => client.categories.list(query), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/hooks/api/products.tsx b/packages/admin-next/dashboard/src/hooks/api/products.tsx index 635fd5fc62..a84209c591 100644 --- a/packages/admin-next/dashboard/src/hooks/api/products.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/products.tsx @@ -1,7 +1,18 @@ -import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" import { client } from "../../lib/client" import { queryKeysFactory } from "../../lib/query-key-factory" -import { ProductListRes, ProductRes } from "../../types/api-responses" +import { + ProductDeleteRes, + ProductListRes, + ProductRes, +} from "../../types/api-responses" +import { queryClient } from "../../lib/medusa" const PRODUCTS_QUERY_KEY = "products" as const export const productsQueryKeys = queryKeysFactory(PRODUCTS_QUERY_KEY) @@ -38,3 +49,48 @@ export const useProducts = ( return { ...data, ...rest } } + +export const useCreateProduct = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: any) => client.products.create(payload), + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useUpdateProduct = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: any) => client.products.update(id, payload), + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: productsQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteProduct = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.products.delete(id), + onSuccess: (data: any, variables: any, context: any) => { + queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() }) + queryClient.invalidateQueries({ queryKey: productsQueryKeys.detail(id) }) + + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/hooks/api/tags.tsx b/packages/admin-next/dashboard/src/hooks/api/tags.tsx new file mode 100644 index 0000000000..988ff531f4 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/tags.tsx @@ -0,0 +1,39 @@ +import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query" +import { client } from "../../lib/client" +import { queryKeysFactory } from "../../lib/query-key-factory" +import { TagsListRes, TagRes } from "../../types/api-responses" + +const TAGS_QUERY_KEY = "tags" as const +export const tagsQueryKeys = queryKeysFactory(TAGS_QUERY_KEY) + +export const useTag = ( + id: string, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: tagsQueryKeys.detail(id), + queryFn: async () => client.tags.retrieve(id), + ...options, + }) + + return { ...data, ...rest } +} + +export const useTags = ( + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: tagsQueryKeys.list(query), + queryFn: async () => client.tags.list(query), + ...options, + }) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/lib/client/categories.ts b/packages/admin-next/dashboard/src/lib/client/categories.ts new file mode 100644 index 0000000000..cc5a3029fd --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/categories.ts @@ -0,0 +1,21 @@ +import { + ProductCollectionListRes, + ProductCollectionRes, +} from "../../types/api-responses" +import { getRequest } from "./common" + +async function listProductCategories(query?: Record) { + return getRequest(`/admin/categories`, query) +} + +async function retrieveProductCategory( + id: string, + query?: Record +) { + return getRequest(`/admin/categories/${id}`, query) +} + +export const categories = { + list: listProductCategories, + retrieve: retrieveProductCategory, +} diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index b659d89e2a..f721ded8be 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -1,5 +1,6 @@ import { apiKeys } from "./api-keys" import { auth } from "./auth" +import { categories } from "./categories" import { collections } from "./collections" import { currencies } from "./currencies" import { customers } from "./customers" @@ -11,18 +12,21 @@ import { regions } from "./regions" import { salesChannels } from "./sales-channels" import { stockLocations } from "./stock-locations" import { stores } from "./stores" +import { tags } from "./tags" import { users } from "./users" import { workflowExecutions } from "./workflow-executions" export const client = { auth: auth, apiKeys: apiKeys, + categories: categories, customers: customers, currencies: currencies, collections: collections, promotions: promotions, stores: stores, salesChannels: salesChannels, + tags: tags, users: users, regions: regions, invites: invites, diff --git a/packages/admin-next/dashboard/src/lib/client/products.ts b/packages/admin-next/dashboard/src/lib/client/products.ts index 43af2d9bd0..8871fffdd7 100644 --- a/packages/admin-next/dashboard/src/lib/client/products.ts +++ b/packages/admin-next/dashboard/src/lib/client/products.ts @@ -1,15 +1,34 @@ -import { ProductListRes, ProductRes } from "../../types/api-responses" -import { getRequest } from "./common" +import { + ProductDeleteRes, + ProductListRes, + ProductRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" async function retrieveProduct(id: string, query?: Record) { return getRequest(`/admin/products/${id}`, query) } +async function createProduct(payload: any) { + return postRequest(`/admin/products`, payload) +} + async function listProducts(query?: Record) { return getRequest(`/admin/products`, query) } +async function updateProduct(id: string, payload: any) { + return postRequest(`/admin/products/${id}`, payload) +} + +async function deleteProduct(id: string) { + return deleteRequest(`/admin/products/${id}`) +} + export const products = { retrieve: retrieveProduct, list: listProducts, + create: createProduct, + update: updateProduct, + delete: deleteProduct, } diff --git a/packages/admin-next/dashboard/src/lib/client/tags.ts b/packages/admin-next/dashboard/src/lib/client/tags.ts new file mode 100644 index 0000000000..b3aa9ff759 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/tags.ts @@ -0,0 +1,18 @@ +import { + ProductCollectionListRes, + ProductCollectionRes, +} from "../../types/api-responses" +import { getRequest } from "./common" + +async function listProductTags(query?: Record) { + return getRequest(`/admin/tags`, query) +} + +async function retrieveProductTag(id: string, query?: Record) { + return getRequest(`/admin/tags/${id}`, query) +} + +export const tags = { + list: listProductTags, + retrieve: retrieveProductTag, +} diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx index 179d36b416..ea6206e898 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v1.tsx @@ -5,7 +5,9 @@ import type { AdminDraftOrdersRes, AdminGiftCardsRes, AdminOrdersRes, - AdminProductsRes, + AdminRegionsRes, + AdminSalesChannelsRes, + AdminUserRes, } from "@medusajs/medusa" import { Outlet, RouteObject } from "react-router-dom" @@ -188,66 +190,6 @@ export const v1Routes: RouteObject[] = [ }, ], }, - { - path: "/products", - handle: { - crumb: () => "Products", - }, - children: [ - { - path: "", - lazy: () => import("../../routes/products/product-list"), - children: [ - { - path: "create", - lazy: () => import("../../routes/products/product-create"), - }, - ], - }, - { - path: ":id", - lazy: () => import("../../routes/products/product-detail"), - handle: { - crumb: (data: AdminProductsRes) => data.product.title, - }, - children: [ - { - path: "edit", - lazy: () => import("../../routes/products/product-edit"), - }, - { - path: "sales-channels", - lazy: () => - import("../../routes/products/product-sales-channels"), - }, - { - path: "attributes", - lazy: () => - import("../../routes/products/product-attributes"), - }, - { - path: "options/create", - lazy: () => - import("../../routes/products/product-create-option"), - }, - { - path: "options/:option_id/edit", - lazy: () => - import("../../routes/products/product-edit-option"), - }, - { - path: "media", - lazy: () => import("../../routes/products/product-media"), - }, - { - path: "variants/:variant_id/edit", - lazy: () => - import("../../routes/products/product-edit-variant"), - }, - ], - }, - ], - }, { path: "/categories", handle: { diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx index 8bdb4aa81c..d1d88e64c0 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx @@ -2,7 +2,7 @@ import { SalesChannelDTO, UserDTO } from "@medusajs/types" import { Navigate, Outlet, RouteObject, useLocation } from "react-router-dom" import { Spinner } from "@medusajs/icons" -import { AdminCollectionsRes } from "@medusajs/medusa" +import { AdminCollectionsRes, AdminProductsRes } from "@medusajs/medusa" import { ErrorBoundary } from "../../components/error/error-boundary" import { MainLayout } from "../../components/layout-v2/main-layout" import { SettingsLayout } from "../../components/layout/settings-layout" @@ -66,6 +66,68 @@ export const v2Routes: RouteObject[] = [ path: "/", element: , children: [ + { + path: "/products", + handle: { + crumb: () => "Products", + }, + children: [ + { + path: "", + lazy: () => import("../../v2-routes/products/product-list"), + children: [ + { + path: "create", + lazy: () => + import("../../v2-routes/products/product-create"), + }, + ], + }, + { + path: ":id", + lazy: () => import("../../v2-routes/products/product-detail"), + handle: { + crumb: (data: AdminProductsRes) => data.product.title, + }, + children: [ + { + path: "edit", + lazy: () => import("../../v2-routes/products/product-edit"), + }, + { + path: "sales-channels", + lazy: () => + import("../../v2-routes/products/product-sales-channels"), + }, + { + path: "attributes", + lazy: () => + import("../../v2-routes/products/product-attributes"), + }, + { + path: "options/create", + lazy: () => + import("../../v2-routes/products/product-create-option"), + }, + { + path: "options/:option_id/edit", + lazy: () => + import("../../v2-routes/products/product-edit-option"), + }, + { + path: "media", + lazy: () => + import("../../v2-routes/products/product-media"), + }, + { + path: "variants/:variant_id/edit", + lazy: () => + import("../../v2-routes/products/product-edit-variant"), + }, + ], + }, + ], + }, { path: "/orders", handle: { @@ -364,7 +426,7 @@ export const v2Routes: RouteObject[] = [ handle: { crumb: (data: ApiKeyRes) => { console.log("data", data) - return data.apiKey.title + return data.api_key.title }, }, children: [ diff --git a/packages/admin-next/dashboard/src/routes/draft-orders/draft-order-create/components/create-draft-order-form/create-draft-order-drawer/add-variant-drawer.tsx b/packages/admin-next/dashboard/src/routes/draft-orders/draft-order-create/components/create-draft-order-form/create-draft-order-drawer/add-variant-drawer.tsx index 6146beb89d..a4e126bbc2 100644 --- a/packages/admin-next/dashboard/src/routes/draft-orders/draft-order-create/components/create-draft-order-form/create-draft-order-drawer/add-variant-drawer.tsx +++ b/packages/admin-next/dashboard/src/routes/draft-orders/draft-order-create/components/create-draft-order-form/create-draft-order-drawer/add-variant-drawer.tsx @@ -15,10 +15,10 @@ import { MoneyAmountCell } from "../../../../../../components/table/table-cells/ import { PlaceholderCell } from "../../../../../../components/table/table-cells/common/placeholder-cell" import { ProductCell } from "../../../../../../components/table/table-cells/product/product-cell" import { useDataTable } from "../../../../../../hooks/use-data-table" -import { useProductVariantTableFilters } from "../../../../../products/product-detail/components/product-variant-section/use-variant-table-filters" -import { useProductVariantTableQuery } from "../../../../../products/product-detail/components/product-variant-section/use-variant-table-query" import { useCreateDraftOrder } from "../hooks" import { ExistingItem } from "../types" +import { useProductVariantTableQuery } from "../../../../../../v2-routes/products/product-detail/components/product-variant-section/use-variant-table-query" +import { useProductVariantTableFilters } from "../../../../../../v2-routes/products/product-detail/components/product-variant-section/use-variant-table-filters" const PAGE_SIZE = 50 const PREFIX = "av" @@ -193,7 +193,7 @@ const useVariantTableColumns = () => { cell: ({ getValue }) => { const options = getValue() - const displayValue = options?.map((o) => o.value).join(" · ") + const displayValue = options?.map((o: any) => o.value).join(" · ") return (
diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index 848ca71c62..b5bc86d0d9 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -21,6 +21,7 @@ import { UserDTO, } from "@medusajs/types" import { WorkflowExecutionDTO } from "../v2-routes/workflow-executions/types" +import { ProductTagDTO } from "@medusajs/types/dist/product" type ListRes = { count: number @@ -99,6 +100,14 @@ export type ProductRes = { product: ExtendedProductDTO } export type ProductListRes = { products: ExtendedProductDTO[] } & ListRes export type ProductDeleteRes = DeleteRes +// Categories +export type CategoryRes = { category: ProductCategoryDTO } +export type CategoriesListRes = { categories: ProductCategoryDTO[] } & ListRes + +// Tags +export type TagRes = { tag: ProductTagDTO } +export type TagsListRes = { tags: ProductTagDTO[] } & ListRes + // Product Types export type ProductTypeRes = { product_type: ProductTypeDTO } export type ProductTypeListRes = { product_types: ProductTypeDTO[] } & ListRes diff --git a/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-attributes/components/product-attributes-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-attributes/components/product-attributes-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx similarity index 98% rename from packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx index 5a8193b451..994ca18874 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx @@ -1,17 +1,16 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Product } from "@medusajs/medusa" import { Button, Input } from "@medusajs/ui" -import { useAdminUpdateProduct } from "medusa-react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" - import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" +import { useUpdateProduct } from "../../../../../hooks/api/products" type ProductAttributesFormProps = { product: Product @@ -57,7 +56,7 @@ export const ProductAttributesForm = ({ resolver: zodResolver(ProductAttributesSchema), }) - const { mutateAsync, isLoading } = useAdminUpdateProduct(product.id) + const { mutateAsync, isLoading } = useUpdateProduct(product.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync( diff --git a/packages/admin-next/dashboard/src/routes/products/product-attributes/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-attributes/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-attributes/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-attributes/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-attributes/product-attributes.tsx similarity index 84% rename from packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-attributes/product-attributes.tsx index d6eb19e27b..f937773cd0 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-attributes/product-attributes.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-attributes/product-attributes.tsx @@ -1,16 +1,16 @@ import { Heading } from "@medusajs/ui" -import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" import { ProductAttributesForm } from "./components/product-attributes-form" +import { useProduct } from "../../../hooks/api/products" export const ProductAttributes = () => { const { id } = useParams() const { t } = useTranslation() - const { product, isLoading, isError, error } = useAdminProduct(id!) + const { product, isLoading, isError, error } = useProduct(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-create-option/components/create-product-option-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-create-option/components/create-product-option-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-create-option/components/create-product-option-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-create-option/components/create-product-option-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-create-option/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-create-option/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-create-option/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-create-option/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-create-option/product-create-option.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-create-option/product-create-option.tsx similarity index 84% rename from packages/admin-next/dashboard/src/routes/products/product-create-option/product-create-option.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-create-option/product-create-option.tsx index 02c4f84973..043bfe826c 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-create-option/product-create-option.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-create-option/product-create-option.tsx @@ -1,15 +1,15 @@ import { Heading } from "@medusajs/ui" -import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" import { CreateProductOptionForm } from "./components/create-product-option-form" +import { useProduct } from "../../../hooks/api/products" export const ProductCreateOption = () => { const { id } = useParams() const { t } = useTranslation() - const { product, isLoading, isError, error } = useAdminProduct(id!) + const { product, isLoading, isError, error } = useProduct(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-details.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/create-product-details.tsx similarity index 77% rename from packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-details.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/create-product-details.tsx index d0f3163e11..b3b657677e 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-details.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/create-product-details.tsx @@ -13,13 +13,6 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels" import { SalesChannel } from "@medusajs/medusa" import { RowSelectionState, createColumnHelper } from "@tanstack/react-table" -import { - useAdminCollections, - useAdminProductCategories, - useAdminProductTags, - useAdminProductTypes, - useAdminSalesChannels, -} from "medusa-react" import { Fragment, useMemo, useState } from "react" import { CountrySelect } from "../../../../../components/common/country-select" import { Form } from "../../../../../components/common/form" @@ -32,6 +25,12 @@ import { useDataTable } from "../../../../../hooks/use-data-table" import { CreateProductFormReturn } from "./create-product-form" import { Combobox } from "../../../../../components/common/combobox" import { FileUpload } from "../../../../../components/common/file-upload" +import { List } from "../../../../../components/common/list" +import { useProductTypes } from "../../../../../hooks/api/product-types" +import { useCollections } from "../../../../../hooks/api/collections" +import { useSalesChannels } from "../../../../../hooks/api/sales-channels" +import { useCategories } from "../../../../../hooks/api/categories" +import { useTags } from "../../../../../hooks/api/tags" type CreateProductPropsProps = { form: CreateProductFormReturn @@ -46,16 +45,48 @@ const SUPPORTED_FORMATS = [ "image/svg+xml", ] +const permutations = ( + data: { title: string; values: string[] }[] +): { [key: string]: string }[] => { + if (data.length === 0) { + return [] + } + + if (data.length === 1) { + return data[0].values.map((value) => ({ [data[0].title]: value })) + } + + const toProcess = data[0] + const rest = data.slice(1) + + return toProcess.values.flatMap((value) => { + return permutations(rest).map((permutation) => { + return { + [toProcess.title]: value, + ...permutation, + } + }) + }) +} + +const generateNameFromPermutation = (permutation: { + [key: string]: string +}) => { + return Object.values(permutation).join(" / ") +} + export const CreateProductDetails = ({ form }: CreateProductPropsProps) => { const { t } = useTranslation() const [open, onOpenChange] = useState(false) - const { product_types, isLoading: isLoadingTypes } = useAdminProductTypes() - const { product_tags, isLoading: isLoadingTags } = useAdminProductTags() - const { collections, isLoading: isLoadingCollections } = useAdminCollections() + const { product_types, isLoading: isLoadingTypes } = useProductTypes() + const { product_tags, isLoading: isLoadingTags } = useTags() + const { collections, isLoading: isLoadingCollections } = useCollections() const { sales_channels, isLoading: isLoadingSalesChannels } = - useAdminSalesChannels() - const { product_categories, isLoading: isLoadingCategories } = - useAdminProductCategories() + useSalesChannels() + const { product_categories, isLoading: isLoadingCategories } = useCategories() + + const options = form.watch("options") + const optionPermutations = permutations(options) // const { append } = useFieldArray({ // name: "images", @@ -202,14 +233,18 @@ export const CreateProductDetails = ({ form }: CreateProductPropsProps) => { { + render={({ field: { onChange, ...field } }) => { return ( {t("products.fields.type.label")} - @@ -229,14 +264,18 @@ export const CreateProductDetails = ({ form }: CreateProductPropsProps) => { { + render={({ field: { onChange, ...field } }) => { return ( {t("products.fields.collection.label")} - @@ -351,6 +390,91 @@ export const CreateProductDetails = ({ form }: CreateProductPropsProps) => {
{t("products.variants")} +
+ { + return ( + + + {t("products.fields.options.label")} + + + {t("products.fields.options.hint")} + + + + + + ) + }} + /> +
+
+ { + const selectedOptions = value.map((v) => v.options) + return ( + + + {t("products.fields.variants.label")} + + + {t("products.fields.variants.hint")} + + + { + onChange( + v.map((options, i) => { + return { + title: + generateNameFromPermutation(options), + variant_rank: i, + options, + prices: [], + } + }) + ) + }} + compare={(a, b) => { + return ( + generateNameFromPermutation(a) === + generateNameFromPermutation(b) + ) + }} + options={optionPermutations.map((opt) => { + return { + title: generateNameFromPermutation(opt), + value: opt, + } + })} + /> + + + + ) + }} + /> +
@@ -454,8 +578,39 @@ export const CreateProductDetails = ({ form }: CreateProductPropsProps) => { ) }} /> + { + return ( + + + {t("products.fields.mid_code.label")} + + + + + + ) + }} + /> + { + return ( + + + {t("products.fields.hs_code.label")} + + + + + + ) + }} + />
- {/* TODO: Add missing attribute fields */}
{t("products.media.label")} diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/create-product-form.tsx similarity index 84% rename from packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/create-product-form.tsx index b4474d80a6..e04116b3e3 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/create-product-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/create-product-form.tsx @@ -1,6 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Button } from "@medusajs/ui" -import { useAdminCreateProduct } from "medusa-react" import { UseFormReturn, useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import * as zod from "zod" @@ -10,6 +9,7 @@ import { useRouteModal, } from "../../../../../components/route-modal" import { CreateProductDetails } from "./create-product-details" +import { useCreateProduct } from "../../../../../hooks/api/products" const CreateProductSchema = zod.object({ title: zod.string(), @@ -30,8 +30,16 @@ const CreateProductSchema = zod.object({ weight: zod.string().optional(), mid_code: zod.string().optional(), hs_code: zod.string().optional(), + options: zod.array( + zod.object({ + title: zod.string(), + values: zod.array(zod.string()), + }) + ), variants: zod.array( zod.object({ + title: zod.string(), + options: zod.record(zod.string(), zod.string()), variant_rank: zod.number(), }) ), @@ -55,17 +63,18 @@ export const CreateProductForm = () => { discountable: true, tags: [], sales_channels: [], + options: [], variants: [], images: [], }, resolver: zodResolver(CreateProductSchema), }) - const { mutateAsync, isLoading } = useAdminCreateProduct() + const { mutateAsync, isLoading } = useCreateProduct() - const handleSubmit = form.handleSubmit(async (values) => { - await mutateAsync( - { + const handleSubmit = form.handleSubmit( + async (values) => { + const reqData = { ...values, is_giftcard: false, tags: values.tags?.map((tag) => ({ value: tag })), @@ -74,15 +83,21 @@ export const CreateProductForm = () => { length: values.length ? parseFloat(values.length) : undefined, height: values.height ? parseFloat(values.height) : undefined, weight: values.weight ? parseFloat(values.weight) : undefined, - variants: values.variants.map((v) => ({ title: "", prices: [] })), - }, - { + variants: values.variants, + } as any + + delete reqData.sales_channels + + await mutateAsync(reqData, { onSuccess: ({ product }) => { handleSuccess(`../${product.id}`) }, - } - ) - }) + }) + }, + (err) => { + console.log(err) + } + ) return ( diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-create/components/create-product-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-create/components/create-product-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-create/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-create/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-create/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-create/product-create.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-create/product-create.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-create/product-create.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-create/product-create.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-attribute-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-attribute-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-attribute-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-attribute-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-general-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-general-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-general-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-general-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-general-section/product-general-section.tsx similarity index 96% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-general-section/product-general-section.tsx index 03e56a8566..be31377a06 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-general-section/product-general-section.tsx @@ -1,10 +1,10 @@ import { PencilSquare, Trash } from "@medusajs/icons" import { Product } from "@medusajs/medusa" import { Container, Heading, StatusBadge, Text, usePrompt } from "@medusajs/ui" -import { useAdminDeleteProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useDeleteProduct } from "../../../../../hooks/api/products" type ProductGeneralSectionProps = { product: Product @@ -17,7 +17,7 @@ export const ProductGeneralSection = ({ const prompt = usePrompt() const navigate = useNavigate() - const { mutateAsync } = useAdminDeleteProduct(product.id) + const { mutateAsync } = useDeleteProduct(product.id) const handleDelete = async () => { const res = await prompt({ diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-media-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-media-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-media-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-media-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-media-section/product-media-section.tsx similarity index 97% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-media-section/product-media-section.tsx index 330f16b278..efb31d39cd 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-media-section/product-media-section.tsx @@ -9,11 +9,11 @@ import { clx, usePrompt, } from "@medusajs/ui" -import { useAdminUpdateProduct } from "medusa-react" import { useState } from "react" import { useTranslation } from "react-i18next" import { Link } from "react-router-dom" import { ActionMenu } from "../../../../../components/common/action-menu" +import { useUpdateProduct } from "../../../../../hooks/api/products" type ProductMedisaSectionProps = { product: Product @@ -37,7 +37,7 @@ export const ProductMediaSection = ({ product }: ProductMedisaSectionProps) => { }) } - const { mutateAsync } = useAdminUpdateProduct(product.id) + const { mutateAsync } = useUpdateProduct(product.id) const handleDelete = async () => { const ids = Object.keys(selection) diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-option-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-option-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-option-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-option-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-option-section/product-option-section.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-option-section/product-option-section.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-organization-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-organization-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-organization-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-organization-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-organization-section/product-organization-section.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-organization-section/product-organization-section.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-sales-channel-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-sales-channel-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-sales-channel-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-sales-channel-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/product-variant-section.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/product-variant-section.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-filters.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/use-variant-table-filters.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-filters.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/use-variant-table-filters.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-query.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/use-variant-table-query.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-query.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/components/product-variant-section/use-variant-table-query.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/loader.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/loader.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-detail/loader.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/loader.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/product-detail.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/product-detail.tsx similarity index 94% rename from packages/admin-next/dashboard/src/routes/products/product-detail/product-detail.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-detail/product-detail.tsx index 817fb51a07..551e8f5324 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-detail/product-detail.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-detail/product-detail.tsx @@ -1,4 +1,3 @@ -import { useAdminProduct } from "medusa-react" import { Outlet, useLoaderData, useParams } from "react-router-dom" import { JsonViewSection } from "../../../components/common/json-view-section" @@ -15,6 +14,7 @@ import before from "medusa-admin:widgets/product/details/before" import sideAfter from "medusa-admin:widgets/product/details/side/after" import sideBefore from "medusa-admin:widgets/product/details/side/before" import { ProductOrganizationSection } from "./components/product-organization-section" +import { useProduct } from "../../../hooks/api/products" export const ProductDetail = () => { const initialData = useLoaderData() as Awaited< @@ -22,13 +22,9 @@ export const ProductDetail = () => { > const { id } = useParams() - const { product, isLoading, isError, error } = useAdminProduct( - id!, - undefined, - { - initialData: initialData, - } - ) + const { product, isLoading, isError, error } = useProduct(id!, undefined, { + initialData: initialData, + }) if (isLoading || !product) { return
Loading...
diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-option/components/edit-product-option-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/components/edit-product-option-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-option/components/edit-product-option-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/components/edit-product-option-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-option/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-option/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-option/product-edit-option.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/product-edit-option.tsx similarity index 87% rename from packages/admin-next/dashboard/src/routes/products/product-edit-option/product-edit-option.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/product-edit-option.tsx index ee30be18c6..d56c9e085d 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-edit-option/product-edit-option.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-option/product-edit-option.tsx @@ -1,15 +1,15 @@ import { Heading } from "@medusajs/ui" -import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { json, useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" import { CreateProductOptionForm } from "./components/edit-product-option-form" +import { useProduct } from "../../../hooks/api/products" export const ProductEditOption = () => { const { id, option_id } = useParams() const { t } = useTranslation() - const { product, isLoading, isError, error } = useAdminProduct(id!) + const { product, isLoading, isError, error } = useProduct(id!) const option = product?.options.find((o) => o.id === option_id) diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-variant/components/product-edit-variant-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/components/product-edit-variant-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-variant/components/product-edit-variant-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/components/product-edit-variant-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-variant/components/product-edit-variant-form/product-edit-variant-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/components/product-edit-variant-form/product-edit-variant-form.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-variant/components/product-edit-variant-form/product-edit-variant-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/components/product-edit-variant-form/product-edit-variant-form.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-variant/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-variant/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-variant/loader.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/loader.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit-variant/loader.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/loader.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit-variant/product-edit-variant.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/product-edit-variant.tsx similarity index 87% rename from packages/admin-next/dashboard/src/routes/products/product-edit-variant/product-edit-variant.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/product-edit-variant.tsx index f6e28b05bd..1746ad6f7e 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-edit-variant/product-edit-variant.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-edit-variant/product-edit-variant.tsx @@ -1,11 +1,11 @@ import { ProductVariant } from "@medusajs/medusa" import { Heading } from "@medusajs/ui" -import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { json, useLoaderData, useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" import { ProductEditVariantForm } from "./components/product-edit-variant-form" import { editProductVariantLoader } from "./loader" +import { useProduct } from "../../../hooks/api/products" export const ProductEditVariant = () => { const loaderData = useLoaderData() as Awaited< @@ -15,13 +15,9 @@ export const ProductEditVariant = () => { const { t } = useTranslation() const { id, variant_id } = useParams() - const { product, isLoading, isError, error } = useAdminProduct( - id!, - undefined, - { - initialData: loaderData?.initialData, - } - ) + const { product, isLoading, isError, error } = useProduct(id!, undefined, { + initialData: loaderData?.initialData, + }) const variant = product?.variants.find( (v: ProductVariant) => v.id === variant_id diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-edit/components/edit-product-form/edit-product-form.tsx similarity index 98% rename from packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit/components/edit-product-form/edit-product-form.tsx index d6b9ae6cbd..924326ad8e 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-edit/components/edit-product-form/edit-product-form.tsx @@ -2,7 +2,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { Product } from "@medusajs/medusa" import { ProductStatus } from "@medusajs/types" import { Button, Input, Select, Switch, Text, Textarea } from "@medusajs/ui" -import { useAdminUpdateProduct } from "medusa-react" import { useForm } from "react-hook-form" import { Trans, useTranslation } from "react-i18next" import * as zod from "zod" @@ -12,6 +11,7 @@ import { RouteDrawer, useRouteModal, } from "../../../../../components/route-modal" +import { useUpdateProduct } from "../../../../../hooks/api/products" type EditProductFormProps = { product: Product @@ -44,7 +44,7 @@ export const EditProductForm = ({ product }: EditProductFormProps) => { resolver: zodResolver(EditProductSchema), }) - const { mutateAsync, isLoading } = useAdminUpdateProduct(product.id) + const { mutateAsync, isLoading } = useUpdateProduct(product.id) const handleSubmit = form.handleSubmit(async (data) => { await mutateAsync( diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit/components/edit-product-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit/components/edit-product-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit/components/edit-product-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-edit/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-edit/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-edit/product-edit.tsx similarity index 83% rename from packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-edit/product-edit.tsx index d68df23081..09d0109b1d 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-edit/product-edit.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-edit/product-edit.tsx @@ -1,16 +1,16 @@ import { Heading } from "@medusajs/ui" -import { useAdminProduct } from "medusa-react" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" import { RouteDrawer } from "../../../components/route-modal" import { EditProductForm } from "./components/edit-product-form" +import { useProduct } from "../../../hooks/api/products" export const ProductEdit = () => { const { id } = useParams() const { t } = useTranslation() - const { product, isLoading, isError, error } = useAdminProduct(id!) + const { product, isLoading, isError, error } = useProduct(id!) if (isError) { throw error diff --git a/packages/admin-next/dashboard/src/routes/products/product-list/components/product-list-table/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-list/components/product-list-table/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-list/components/product-list-table/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-list/components/product-list-table/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-list/components/product-list-table/product-list-table.tsx similarity index 94% rename from packages/admin-next/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-list/components/product-list-table/product-list-table.tsx index 2ac39bbb89..1f80bba000 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-list/components/product-list-table/product-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-list/components/product-list-table/product-list-table.tsx @@ -2,7 +2,6 @@ import { PencilSquare, Trash } from "@medusajs/icons" import type { Product } from "@medusajs/medusa" import { Button, Container, Heading, usePrompt } from "@medusajs/ui" import { createColumnHelper } from "@tanstack/react-table" -import { useAdminDeleteProduct, useAdminProducts } from "medusa-react" import { useMemo } from "react" import { useTranslation } from "react-i18next" import { Link, Outlet, useLoaderData } from "react-router-dom" @@ -14,6 +13,10 @@ import { useProductTableFilters } from "../../../../../hooks/table/filters/use-p import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query" import { useDataTable } from "../../../../../hooks/use-data-table" import { productsLoader } from "../../loader" +import { + useDeleteProduct, + useProducts, +} from "../../../../../hooks/api/products" const PAGE_SIZE = 20 @@ -25,7 +28,7 @@ export const ProductListTable = () => { > const { searchParams, raw } = useProductTableQuery({ pageSize: PAGE_SIZE }) - const { products, count, isLoading, isError, error } = useAdminProducts( + const { products, count, isLoading, isError, error } = useProducts( { ...searchParams, }, @@ -80,7 +83,7 @@ export const ProductListTable = () => { const ProductActions = ({ product }: { product: Product }) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteProduct(product.id) + const { mutateAsync } = useDeleteProduct(product.id) const handleDelete = async () => { const res = await prompt({ diff --git a/packages/admin-next/dashboard/src/routes/products/product-list/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-list/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-list/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-list/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-list/loader.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-list/loader.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-list/loader.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-list/loader.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-list/product-list.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-list/product-list.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-list/product-list.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-list/product-list.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/edit-product-media-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/edit-product-media-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/edit-product-media-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/edit-product-media-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-gallery/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-gallery/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-gallery/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-gallery/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx similarity index 98% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx index f75caeb570..41e2d0ad9e 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx @@ -12,8 +12,8 @@ import { useCallback, useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { Link, useLocation } from "react-router-dom" -import { useAdminUpdateProduct } from "medusa-react" import { RouteFocusModal } from "../../../../../components/route-modal" +import { useUpdateProduct } from "../../../../../hooks/api/products" type ProductMediaGalleryProps = { product: Product @@ -25,7 +25,7 @@ export const ProductMediaGallery = ({ product }: ProductMediaGalleryProps) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync, isLoading } = useAdminUpdateProduct(product.id) + const { mutateAsync, isLoading } = useUpdateProduct(product.id) const media = getMedia(product.images, product.thumbnail) diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/product-media-view-context.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/product-media-view-context.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/product-media-view-context.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/product-media-view-context.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/product-media-view.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/product-media-view.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/product-media-view.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/product-media-view.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/use-product-media-view.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/use-product-media-view.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/components/product-media-view/use-product-media-view.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/components/product-media-view/use-product-media-view.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-media/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-media/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-media/product-media.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-media/product-media.tsx similarity index 78% rename from packages/admin-next/dashboard/src/routes/products/product-media/product-media.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-media/product-media.tsx index 71f47a2ebe..34a3b3cce0 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-media/product-media.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-media/product-media.tsx @@ -1,12 +1,12 @@ -import { useAdminProduct } from "medusa-react" import { useParams } from "react-router-dom" import { RouteFocusModal } from "../../../components/route-modal" import { ProductMediaView } from "./components/product-media-view" +import { useProduct } from "../../../hooks/api/products" export const ProductMedia = () => { const { id } = useParams() - const { product, isLoading, isError, error } = useAdminProduct(id!) + const { product, isLoading, isError, error } = useProduct(id!) const ready = !isLoading && product diff --git a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx diff --git a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/components/edit-sales-channels-form/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-sales-channels/components/edit-sales-channels-form/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/components/edit-sales-channels-form/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/index.ts b/packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/index.ts similarity index 100% rename from packages/admin-next/dashboard/src/routes/products/product-sales-channels/index.ts rename to packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/index.ts diff --git a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx b/packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/product-sales-channels.tsx similarity index 78% rename from packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx rename to packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/product-sales-channels.tsx index 89f42fcd8d..9c886e7491 100644 --- a/packages/admin-next/dashboard/src/routes/products/product-sales-channels/product-sales-channels.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/products/product-sales-channels/product-sales-channels.tsx @@ -1,12 +1,12 @@ -import { useAdminProduct } from "medusa-react" import { useParams } from "react-router-dom" import { RouteFocusModal } from "../../../components/route-modal" import { EditSalesChannelsForm } from "./components/edit-sales-channels-form" +import { useProduct } from "../../../hooks/api/products" export const ProductSalesChannels = () => { const { id } = useParams() - const { product, isLoading, isError, error } = useAdminProduct(id!) + const { product, isLoading, isError, error } = useProduct(id!) if (isError) { throw error diff --git a/packages/core-flows/src/product/workflows/create-products.ts b/packages/core-flows/src/product/workflows/create-products.ts index 8bf28884bb..60f4e97dcc 100644 --- a/packages/core-flows/src/product/workflows/create-products.ts +++ b/packages/core-flows/src/product/workflows/create-products.ts @@ -45,7 +45,7 @@ export const createProductsWorkflow = createWorkflow( const inputProduct = data.input.products[i] return p.variants?.map((v, j) => ({ ...v, - prices: inputProduct?.variants?.[j]?.prices, + prices: inputProduct?.variants?.[j]?.prices ?? [], })) }) .flat() @@ -54,7 +54,7 @@ export const createProductsWorkflow = createWorkflow( const pricesToCreate = transform({ variantsWithAssociatedPrices }, (data) => data.variantsWithAssociatedPrices.map((v) => ({ - prices: v.prices, + prices: v.prices ?? [], })) ) diff --git a/packages/medusa/src/api-v2/admin/products/validators.ts b/packages/medusa/src/api-v2/admin/products/validators.ts index cc28776e90..d50a79941d 100644 --- a/packages/medusa/src/api-v2/admin/products/validators.ts +++ b/packages/medusa/src/api-v2/admin/products/validators.ts @@ -509,6 +509,10 @@ export class AdminPostProductsProductVariantsReq { @IsOptional() manage_inventory?: boolean = true + @IsNumber() + @IsOptional() + variant_rank?: number + @IsNumber() @IsOptional() weight?: number @@ -538,9 +542,10 @@ export class AdminPostProductsProductVariantsReq { metadata?: Record @IsArray() + @IsOptional() @ValidateNested({ each: true }) @Type(() => ProductVariantPricesCreateReq) - prices: ProductVariantPricesCreateReq[] + prices?: ProductVariantPricesCreateReq[] @IsOptional() @IsObject() @@ -588,6 +593,10 @@ export class AdminPostProductsProductVariantsVariantReq { @IsOptional() manage_inventory?: boolean + @IsNumber() + @IsOptional() + variant_rank?: number + @IsNumber() @IsOptional() weight?: number