Clean up product typings (#7663)

There are a lot of issues in the admin after applying the correct typings, but fixing those should be done gradually, it's better to keep it out of this PR
This commit is contained in:
Stevche Radevski
2024-06-11 09:28:40 +00:00
committed by GitHub
parent f3bf8c73a3
commit 8e2a42b786
88 changed files with 1205 additions and 990 deletions
@@ -864,7 +864,7 @@ medusaIntegrationTestRunner({
it("successfully creates, updates and deletes an inventory item link from a variant", async () => {
const res = await api.post(
`/admin/products/${baseProduct.id}/variants/inventory-items/batch`,
`/admin/products/${inventoryProduct.id}/variants/inventory-items/batch`,
{
create: [
{
@@ -894,7 +894,7 @@ medusaIntegrationTestRunner({
const createdLinkVariant = (
await api.get(
`/admin/products/${baseProduct.id}/variants/${inventoryVariant3.id}?fields=inventory_items.inventory.*,inventory_items.*`,
`/admin/products/${inventoryProduct.id}/variants/${inventoryVariant3.id}?fields=inventory_items.inventory.*,inventory_items.*`,
adminHeaders
)
).data.variant
@@ -911,7 +911,7 @@ medusaIntegrationTestRunner({
const updatedLinkVariant = (
await api.get(
`/admin/products/${baseProduct.id}/variants/${inventoryVariant1.id}?fields=inventory_items.inventory.*,inventory_items.*`,
`/admin/products/${inventoryProduct.id}/variants/${inventoryVariant1.id}?fields=inventory_items.inventory.*,inventory_items.*`,
adminHeaders
)
).data.variant
@@ -928,7 +928,7 @@ medusaIntegrationTestRunner({
const deletedLinkVariant = (
await api.get(
`/admin/products/${baseProduct.id}/variants/${inventoryVariant2.id}?fields=inventory_items.inventory.*,inventory_items.*`,
`/admin/products/${inventoryProduct.id}/variants/${inventoryVariant2.id}?fields=inventory_items.inventory.*,inventory_items.*`,
adminHeaders
)
).data.variant
@@ -1,10 +1,10 @@
import { useTranslation } from "react-i18next"
import { ProductCollectionDTO } from "@medusajs/types"
import { PlaceholderCell } from "../../common/placeholder-cell"
import { HttpTypes } from "@medusajs/types"
type CollectionCellProps = {
collection?: ProductCollectionDTO | null
collection?: HttpTypes.AdminCollection | null
}
export const CollectionCell = ({ collection }: CollectionCellProps) => {
@@ -1,10 +1,10 @@
import { useTranslation } from "react-i18next"
import { ProductDTO } from "@medusajs/types"
import { Thumbnail } from "../../../../common/thumbnail"
import { HttpTypes } from "@medusajs/types"
type ProductCellProps = {
product: ProductDTO
product: HttpTypes.AdminProduct
}
export const ProductCell = ({ product }: ProductCellProps) => {
@@ -1,10 +1,10 @@
import { ProductStatus } from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { StatusCell } from "../../common/status-cell"
import { HttpTypes } from "@medusajs/types"
type ProductStatusCellProps = {
status: ProductStatus
status: HttpTypes.AdminProductStatus
}
export const ProductStatusCell = ({ status }: ProductStatusCellProps) => {
@@ -1,10 +1,10 @@
import { useTranslation } from "react-i18next"
import { ProductVariantDTO } from "@medusajs/types"
import { PlaceholderCell } from "../../common/placeholder-cell"
import { HttpTypes } from "@medusajs/types"
type VariantCellProps = {
variants?: ProductVariantDTO[] | null
variants?: HttpTypes.AdminProductVariant[] | null
}
export const VariantCell = ({ variants }: VariantCellProps) => {
@@ -1,7 +1,7 @@
import { QueryKey, UseQueryOptions, useQuery } from "@tanstack/react-query"
import { client } from "../../lib/client"
import { queryKeysFactory } from "../../lib/query-key-factory"
import { ProductTypeListRes, ProductTypeRes } from "../../types/api-responses"
import { HttpTypes } from "@medusajs/types"
const PRODUCT_TYPES_QUERY_KEY = "product_types" as const
const productTypesQueryKeys = queryKeysFactory(PRODUCT_TYPES_QUERY_KEY)
@@ -10,7 +10,12 @@ export const useProductType = (
id: string,
query?: Record<string, any>,
options?: Omit<
UseQueryOptions<ProductTypeRes, Error, ProductTypeRes, QueryKey>,
UseQueryOptions<
{ product_type: HttpTypes.AdminProductType },
Error,
{ product_type: HttpTypes.AdminProductType },
QueryKey
>,
"queryKey" | "queryFn"
>
) => {
@@ -26,7 +31,12 @@ export const useProductType = (
export const useProductTypes = (
query?: Record<string, any>,
options?: Omit<
UseQueryOptions<ProductTypeListRes, Error, ProductTypeListRes, QueryKey>,
UseQueryOptions<
{ product_types: HttpTypes.AdminProductType[] },
Error,
{ product_types: HttpTypes.AdminProductType[] },
QueryKey
>,
"queryKey" | "queryFn"
>
) => {
@@ -5,14 +5,9 @@ import {
useQuery,
UseQueryOptions,
} from "@tanstack/react-query"
import { client, sdk } from "../../lib/client"
import { sdk } from "../../lib/client"
import { queryClient } from "../../lib/query-client"
import { queryKeysFactory } from "../../lib/query-key-factory"
import {
ProductDeleteRes,
ProductListRes,
ProductRes,
} from "../../types/api-responses"
import { HttpTypes } from "@medusajs/types"
const PRODUCTS_QUERY_KEY = "products" as const
@@ -29,8 +24,8 @@ export const useCreateProductOption = (
options?: UseMutationOptions<any, Error, any>
) => {
return useMutation({
mutationFn: (payload: any) =>
client.products.createOption(productId, payload),
mutationFn: (payload: HttpTypes.AdminCreateProductOption) =>
sdk.admin.product.createOption(productId, payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: optionsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -48,8 +43,8 @@ export const useUpdateProductOption = (
options?: UseMutationOptions<any, Error, any>
) => {
return useMutation({
mutationFn: (payload: any) =>
client.products.updateOption(productId, optionId, payload),
mutationFn: (payload: HttpTypes.AdminUpdateProductOption) =>
sdk.admin.product.updateOption(productId, optionId, payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: optionsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -71,7 +66,7 @@ export const useDeleteProductOption = (
options?: UseMutationOptions<any, Error, void>
) => {
return useMutation({
mutationFn: () => client.products.deleteOption(productId, optionId),
mutationFn: () => sdk.admin.product.deleteOption(productId, optionId),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: optionsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -97,7 +92,8 @@ export const useProductVariant = (
>
) => {
const { data, ...rest } = useQuery({
queryFn: () => client.products.retrieveVariant(productId, variantId, query),
queryFn: () =>
sdk.admin.product.retrieveVariant(productId, variantId, query),
queryKey: variantsQueryKeys.detail(variantId),
...options,
})
@@ -114,7 +110,7 @@ export const useProductVariants = (
>
) => {
const { data, ...rest } = useQuery({
queryFn: () => client.products.listVariants(productId, query),
queryFn: () => sdk.admin.product.listVariants(productId, query),
queryKey: variantsQueryKeys.list({ productId, ...query }),
...options,
})
@@ -127,8 +123,8 @@ export const useCreateProductVariant = (
options?: UseMutationOptions<any, Error, any>
) => {
return useMutation({
mutationFn: (payload: any) =>
client.products.createVariant(productId, payload),
mutationFn: (payload: HttpTypes.AdminCreateProductVariant) =>
sdk.admin.product.createVariant(productId, payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: variantsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -146,8 +142,8 @@ export const useUpdateProductVariant = (
options?: UseMutationOptions<any, Error, any>
) => {
return useMutation({
mutationFn: (payload: any) =>
client.products.updateVariant(productId, variantId, payload),
mutationFn: (payload: HttpTypes.AdminUpdateProductVariant) =>
sdk.admin.product.updateVariant(productId, variantId, payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: variantsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -168,8 +164,9 @@ export const useUpdateProductVariantsBatch = (
options?: UseMutationOptions<any, Error, any>
) => {
return useMutation({
mutationFn: (payload: any) =>
client.products.updateVariantsBatch(productId, payload),
mutationFn: (
payload: HttpTypes.AdminBatchProductVariantRequest["update"]
) => sdk.admin.product.batchVariants(productId, { update: payload }),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: variantsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -188,7 +185,7 @@ export const useDeleteVariant = (
options?: UseMutationOptions<any, Error, void>
) => {
return useMutation({
mutationFn: () => client.products.deleteVariant(productId, variantId),
mutationFn: () => sdk.admin.product.deleteVariant(productId, variantId),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: variantsQueryKeys.lists() })
queryClient.invalidateQueries({
@@ -213,7 +210,7 @@ export const useProduct = (
>
) => {
const { data, ...rest } = useQuery({
queryFn: () => client.products.retrieve(id, query),
queryFn: () => sdk.admin.product.retrieve(id, query),
queryKey: productsQueryKeys.detail(id),
...options,
})
@@ -224,12 +221,17 @@ export const useProduct = (
export const useProducts = (
query?: Record<string, any>,
options?: Omit<
UseQueryOptions<ProductListRes, Error, ProductListRes, QueryKey>,
UseQueryOptions<
HttpTypes.AdminProductListResponse,
Error,
HttpTypes.AdminProductListResponse,
QueryKey
>,
"queryFn" | "queryKey"
>
) => {
const { data, ...rest } = useQuery({
queryFn: () => client.products.list(query),
queryFn: () => sdk.admin.product.list(query),
queryKey: productsQueryKeys.list(query),
...options,
})
@@ -245,7 +247,8 @@ export const useCreateProduct = (
>
) => {
return useMutation({
mutationFn: (payload: any) => sdk.admin.product.create(payload),
mutationFn: (payload: HttpTypes.AdminCreateProduct) =>
sdk.admin.product.create(payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() })
options?.onSuccess?.(data, variables, context)
@@ -256,10 +259,11 @@ export const useCreateProduct = (
export const useUpdateProduct = (
id: string,
options?: UseMutationOptions<ProductRes, Error, any>
options?: UseMutationOptions<HttpTypes.AdminProductResponse, Error, any>
) => {
return useMutation({
mutationFn: (payload: any) => client.products.update(id, payload),
mutationFn: (payload: HttpTypes.AdminUpdateProduct) =>
sdk.admin.product.update(id, payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() })
queryClient.invalidateQueries({ queryKey: productsQueryKeys.detail(id) })
@@ -272,10 +276,14 @@ export const useUpdateProduct = (
export const useDeleteProduct = (
id: string,
options?: UseMutationOptions<ProductDeleteRes, Error, void>
options?: UseMutationOptions<
HttpTypes.AdminProductDeleteResponse,
Error,
void
>
) => {
return useMutation({
mutationFn: () => client.products.delete(id),
mutationFn: () => sdk.admin.product.delete(id),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() })
queryClient.invalidateQueries({ queryKey: productsQueryKeys.detail(id) })
@@ -21,9 +21,9 @@ import {
VariantCell,
VariantHeader,
} from "../../../components/table/table-cells/product/variant-cell"
import { ExtendedProductDTO } from "../../../types/api-responses"
import { HttpTypes } from "@medusajs/types"
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
export const useProductTableColumns = () => {
return useMemo(
@@ -1,6 +1,4 @@
import { AdminGetProductsParams } from "@medusajs/medusa"
import { ProductStatus } from "@medusajs/types"
import { HttpTypes } from "@medusajs/types"
import { useQueryParams } from "../../use-query-params"
type UseProductTableQueryProps = {
@@ -46,7 +44,7 @@ export const useProductTableQuery = ({
q,
} = queryObject
const searchParams: AdminGetProductsParams = {
const searchParams: HttpTypes.AdminProductParams = {
limit: pageSize,
offset: offset ? Number(offset) : 0,
sales_channel_id: sales_channel_id?.split(","),
@@ -56,9 +54,9 @@ export const useProductTableQuery = ({
collection_id: collection_id?.split(","),
is_giftcard: is_giftcard ? is_giftcard === "true" : undefined,
order: order,
tags: tags?.split(","),
tags: tags ? { value: tags.split(",") } : undefined,
type_id: type_id?.split(","),
status: status?.split(",") as ProductStatus[],
status: status?.split(",") as HttpTypes.AdminProductStatus[],
q,
}
@@ -10,7 +10,6 @@ import { invites } from "./invites"
import { payments } from "./payments"
import { priceLists } from "./price-lists"
import { productTypes } from "./product-types"
import { products } from "./products"
import { promotions } from "./promotions"
import { reservations } from "./reservations"
import { salesChannels } from "./sales-channels"
@@ -42,7 +41,6 @@ export const client = {
reservations: reservations,
fulfillments: fulfillments,
fulfillmentProviders: fulfillmentProviders,
products: products,
productTypes: productTypes,
priceLists: priceLists,
stockLocations: stockLocations,
@@ -1,12 +1,18 @@
import { ProductTypeListRes, ProductTypeRes } from "../../types/api-responses"
import { HttpTypes } from "@medusajs/types"
import { getRequest } from "./common"
async function listProductTypes(query?: Record<string, any>) {
return getRequest<ProductTypeListRes>(`/admin/product-types`, query)
return getRequest<{ product_types: HttpTypes.AdminProductType[] }>(
`/admin/product-types`,
query
)
}
async function retrieveProductType(id: string, query?: Record<string, any>) {
return getRequest<ProductTypeRes>(`/admin/product-types/${id}`, query)
return getRequest<{ product_type: HttpTypes.AdminProductType }>(
`/admin/product-types/${id}`,
query
)
}
export const productTypes = {
@@ -1,100 +0,0 @@
import { ProductDeleteRes, ProductListRes } from "../../types/api-responses"
import { deleteRequest, getRequest, postRequest } from "./common"
async function retrieveProduct(id: string, query?: Record<string, any>) {
return getRequest<any>(`/admin/products/${id}`, query)
}
async function createProduct(payload: any) {
return postRequest<any>(`/admin/products`, payload)
}
async function listProducts(query?: Record<string, any>) {
return getRequest<ProductListRes>(`/admin/products`, query)
}
async function updateProduct(id: string, payload: any) {
return postRequest<any>(`/admin/products/${id}`, payload)
}
async function deleteProduct(id: string) {
return deleteRequest<ProductDeleteRes>(`/admin/products/${id}`)
}
async function retrieveVariant(
productId: string,
variantId: string,
query?: Record<string, any>
) {
return getRequest<any>(
`/admin/products/${productId}/variants/${variantId}`,
query
)
}
async function listVariants(productId: string, query?: Record<string, any>) {
return getRequest<any>(`/admin/products/${productId}/variants`, query)
}
async function createVariant(productId: string, payload: any) {
return postRequest<any>(`/admin/products/${productId}/variants`, payload)
}
async function updateVariant(
productId: string,
variantId: string,
payload: any
) {
return postRequest<any>(
`/admin/products/${productId}/variants/${variantId}`,
payload
)
}
async function updateVariantsBatch(
productId: string,
payload: { add: any[]; update: any[]; remove: any[] }
) {
return postRequest<any>(
`/admin/products/${productId}/variants/batch`,
payload
)
}
async function deleteVariant(productId: string, variantId: string) {
return deleteRequest<any>(
`/admin/products/${productId}/variants/${variantId}`
)
}
async function createOption(productId: string, payload: any) {
return postRequest<any>(`/admin/products/${productId}/options`, payload)
}
async function updateOption(productId: string, optionId: string, payload: any) {
return postRequest<any>(
`/admin/products/${productId}/options/${optionId}`,
payload
)
}
async function deleteOption(productId: string, optionId: string) {
return deleteRequest<any>(`/admin/products/${productId}/options/${optionId}`)
}
export const products = {
retrieve: retrieveProduct,
list: listProducts,
create: createProduct,
update: updateProduct,
delete: deleteProduct,
retrieveVariant,
listVariants,
createVariant,
updateVariant,
updateVariantsBatch,
deleteVariant,
createOption,
updateOption,
deleteOption,
}
@@ -63,7 +63,7 @@ export const RouteMap: RouteObject[] = [
path: ":id",
lazy: () => import("../../routes/products/product-detail"),
handle: {
crumb: (data: { product: HttpTypes.AdminProduct }) =>
crumb: (data: HttpTypes.AdminProductResponse) =>
data.product.title,
},
children: [
@@ -22,7 +22,6 @@ import { useProductTableColumns } from "../../../../../hooks/table/columns/use-p
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters.tsx"
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query.tsx"
import { useDataTable } from "../../../../../hooks/use-data-table.tsx"
import { ExtendedProductDTO } from "../../../../../types/api-responses.ts"
type AddProductsToCollectionFormProps = {
collection: HttpTypes.AdminCollection
@@ -186,7 +185,7 @@ export const AddProductsToCollectionForm = ({
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const { t } = useTranslation()
@@ -13,7 +13,6 @@ import { useProductTableColumns } from "../../../../../hooks/table/columns/use-p
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
type CollectionProductSectionProps = {
collection: HttpTypes.AdminCollection
@@ -139,7 +138,7 @@ const ProductActions = ({
product,
collectionId,
}: {
product: ExtendedProductDTO
product: HttpTypes.AdminProduct
collectionId: string
}) => {
const { t } = useTranslation()
@@ -201,7 +200,7 @@ const ProductActions = ({
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const columns = useProductTableColumns()
@@ -4,16 +4,16 @@ import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import * as zod from "zod"
import { ProductCollectionDTO } from "@medusajs/types"
import { Form } from "../../../../../components/common/form"
import {
RouteDrawer,
useRouteModal,
} from "../../../../../components/route-modal"
import { useUpdateCollection } from "../../../../../hooks/api/collections"
import { HttpTypes } from "@medusajs/types"
type EditCollectionFormProps = {
collection: ProductCollectionDTO
collection: HttpTypes.AdminCollection
}
const EditCollectionSchema = zod.object({
@@ -1,15 +1,15 @@
import { Container, Heading } from "@medusajs/ui"
import { ProductVariantDTO } from "@medusajs/types"
import { ActionMenu } from "../../../../components/common/action-menu"
import { InventoryItemRes } from "../../../../types/api-responses"
import { PencilSquare } from "@medusajs/icons"
import { SectionRow } from "../../../../components/common/section"
import { useTranslation } from "react-i18next"
import { HttpTypes } from "@medusajs/types"
type InventoryItemGeneralSectionProps = {
inventoryItem: InventoryItemRes["inventory_item"] & {
variant: ProductVariantDTO | ProductVariantDTO[]
variant: HttpTypes.AdminProductVariant | HttpTypes.AdminProductVariant[]
}
}
export const InventoryItemGeneralSection = ({
@@ -74,7 +74,7 @@ export const InventoryItemGeneralSection = ({
)
}
const getQuantityFormat = (quantity: number, locations: number) => {
const getQuantityFormat = (quantity: number, locations?: number) => {
return `${quantity ?? "-"}
${quantity ? `across ${locations} locations` : ""}`
${quantity ? `across ${locations ?? "-"} locations` : ""}`
}
@@ -1,4 +1,4 @@
import { CurrencyDTO, HttpTypes, ProductVariantDTO } from "@medusajs/types"
import { CurrencyDTO, HttpTypes } from "@medusajs/types"
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
import { useEffect, useMemo, useState } from "react"
import { UseFormReturn } from "react-hook-form"
@@ -9,7 +9,6 @@ import { CurrencyCell } from "../../../../../components/grid/grid-cells/common/c
import { DataGrid } from "../../../../../components/grid/data-grid"
import { DataGridMeta } from "../../../../../components/grid/types"
import { useCurrencies } from "../../../../../hooks/api/currencies"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { useRegions } from "../../../../../hooks/api/regions"
import { useStore } from "../../../../../hooks/api/store"
@@ -95,7 +94,7 @@ export const CreateShippingOptionsPricesForm = ({
}
const columnHelper = createColumnHelper<
ExtendedProductDTO | ProductVariantDTO
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
>()
const useColumns = ({
@@ -107,45 +106,46 @@ const useColumns = ({
}) => {
const { t } = useTranslation()
const colDefs: ColumnDef<ExtendedProductDTO | ProductVariantDTO>[] =
useMemo(() => {
return [
...currencies.map((currency) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: currency.code.toUpperCase(),
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta<any>}
field={`currency_prices.${currency.code}`}
/>
)
},
})
}),
...regions.map((region) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: region.name,
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currencies.find(
(c) => c.code === region.currency_code
)}
meta={table.options.meta as DataGridMeta<any>}
field={`region_prices.${region.id}`}
/>
)
},
})
}),
]
}, [t, currencies, regions])
const colDefs: ColumnDef<
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
>[] = useMemo(() => {
return [
...currencies.map((currency) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: currency.code.toUpperCase(),
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta<any>}
field={`currency_prices.${currency.code}`}
/>
)
},
})
}),
...regions.map((region) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: region.name,
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currencies.find(
(c) => c.code === region.currency_code
)}
meta={table.options.meta as DataGridMeta<any>}
field={`region_prices.${region.id}`}
/>
)
},
})
}),
]
}, [t, currencies, regions])
return colDefs
}
@@ -6,13 +6,12 @@ import * as zod from "zod"
import {
CurrencyDTO,
PriceDTO,
ProductVariantDTO,
ShippingOptionDTO,
HttpTypes,
} from "@medusajs/types"
import { Button, toast } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { HttpTypes } from "@medusajs/types"
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
import { DataGrid } from "../../../../../components/grid/data-grid/index"
import { CurrencyCell } from "../../../../../components/grid/grid-cells/common/currency-cell/index"
@@ -25,7 +24,6 @@ import { useCurrencies } from "../../../../../hooks/api/currencies"
import { useRegions } from "../../../../../hooks/api/regions"
import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options"
import { useStore } from "../../../../../hooks/api/store"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { castNumber } from "../../../../../lib/cast-number.ts"
const getInitialCurrencyPrices = (prices: PriceDTO[]) => {
@@ -254,7 +252,7 @@ export function EditShippingOptionsPricingForm({
}
const columnHelper = createColumnHelper<
ExtendedProductDTO | ProductVariantDTO
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
>()
const useColumns = ({
@@ -266,45 +264,46 @@ const useColumns = ({
}) => {
const { t } = useTranslation()
const colDefs: ColumnDef<ExtendedProductDTO | ProductVariantDTO>[] =
useMemo(() => {
return [
...currencies.map((currency) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: currency.code.toUpperCase(),
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta<any>}
field={`currency_prices.${currency.code}`}
/>
)
},
})
}),
...regions.map((region) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: region.name,
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currencies.find(
(c) => c.code === region.currency_code
)}
meta={table.options.meta as DataGridMeta<any>}
field={`region_prices.${region.id}`}
/>
)
},
})
}),
]
}, [t, currencies, regions])
const colDefs: ColumnDef<
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
>[] = useMemo(() => {
return [
...currencies.map((currency) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: currency.code.toUpperCase(),
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta<any>}
field={`currency_prices.${currency.code}`}
/>
)
},
})
}),
...regions.map((region) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: region.name,
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currencies.find(
(c) => c.code === region.currency_code
)}
meta={table.options.meta as DataGridMeta<any>}
field={`region_prices.${region.id}`}
/>
)
},
})
}),
]
}, [t, currencies, regions])
return colDefs
}
@@ -1,10 +1,5 @@
import { Buildings, XCircle } from "@medusajs/icons"
import {
AdminOrder,
FulfillmentDTO,
OrderLineItemDTO,
ProductVariantDTO,
} from "@medusajs/types"
import { AdminOrder, FulfillmentDTO, OrderLineItemDTO } from "@medusajs/types"
import {
Container,
Copy,
@@ -49,7 +44,7 @@ const UnfulfilledItem = ({
item,
currencyCode,
}: {
item: OrderLineItemDTO & { variant: ProductVariantDTO }
item: OrderLineItemDTO & { variant: HttpTypes.AdminProductVariant }
currencyCode: string
}) => {
return (
@@ -1,4 +1,4 @@
import { CurrencyDTO, ProductVariantDTO } from "@medusajs/types"
import { CurrencyDTO, HttpTypes } from "@medusajs/types"
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
@@ -7,11 +7,10 @@ import { CurrencyCell } from "../../../../components/grid/grid-cells/common/curr
import { ReadonlyCell } from "../../../../components/grid/grid-cells/common/readonly-cell"
import { VoidCell } from "../../../../components/grid/grid-cells/common/void-cell"
import { DataGridMeta } from "../../../../components/grid/types"
import { ExtendedProductDTO } from "../../../../types/api-responses"
import { isProductRow } from "../utils"
const columnHelper = createColumnHelper<
ExtendedProductDTO | ProductVariantDTO
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
>()
export const usePriceListGridColumns = ({
@@ -21,57 +20,58 @@ export const usePriceListGridColumns = ({
}) => {
const { t } = useTranslation()
const colDefs: ColumnDef<ExtendedProductDTO | ProductVariantDTO>[] =
useMemo(() => {
return [
columnHelper.display({
id: t("fields.title"),
header: t("fields.title"),
cell: ({ row }) => {
const colDefs: ColumnDef<
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
>[] = useMemo(() => {
return [
columnHelper.display({
id: t("fields.title"),
header: t("fields.title"),
cell: ({ row }) => {
const entity = row.original
if (isProductRow(entity)) {
return (
<VoidCell>
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
<Thumbnail src={entity.thumbnail} />
<span className="truncate">{entity.title}</span>
</div>
</VoidCell>
)
}
return (
<ReadonlyCell>
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
<span className="truncate">{entity.title}</span>
</div>
</ReadonlyCell>
)
},
}),
...currencies.map((currency) => {
return columnHelper.display({
header: `Price ${currency.code.toUpperCase()}`,
cell: ({ row, table }) => {
const entity = row.original
if (isProductRow(entity)) {
return (
<VoidCell>
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
<Thumbnail src={entity.thumbnail} />
<span className="truncate">{entity.title}</span>
</div>
</VoidCell>
)
return <VoidCell />
}
return (
<ReadonlyCell>
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
<span className="truncate">{entity.title}</span>
</div>
</ReadonlyCell>
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta}
field={`products.${entity.product_id}.variants.${entity.id}.currency_prices.${currency.code}.amount`}
/>
)
},
}),
...currencies.map((currency) => {
return columnHelper.display({
header: `Price ${currency.code.toUpperCase()}`,
cell: ({ row, table }) => {
const entity = row.original
if (isProductRow(entity)) {
return <VoidCell />
}
return (
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta}
field={`products.${entity.product_id}.variants.${entity.id}.currency_prices.${currency.code}.amount`}
/>
)
},
})
}),
]
}, [t, currencies])
})
}),
]
}, [t, currencies])
return colDefs
}
@@ -1,6 +1,5 @@
import { PriceListDTO, ProductVariantDTO } from "@medusajs/types"
import { PriceListDTO, HttpTypes } from "@medusajs/types"
import { TFunction } from "i18next"
import { ExtendedProductDTO } from "../../../types/api-responses"
import { PriceListStatus } from "./constants"
const getValues = (priceList: PriceListDTO) => {
@@ -49,7 +48,7 @@ export const getPriceListStatus = (
}
export const isProductRow = (
row: ExtendedProductDTO | ProductVariantDTO
): row is ExtendedProductDTO => {
row: HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
): row is HttpTypes.AdminProduct => {
return "variants" in row
}
@@ -13,9 +13,9 @@ import { useProductTableColumns } from "../../../../../hooks/table/columns/use-p
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { PricingProductsRecordType } from "../../../common/schemas"
import { PricingCreateSchemaType } from "./schema"
import { HttpTypes } from "@medusajs/types"
type PricingProductsFormProps = {
form: UseFormReturn<PricingCreateSchemaType>
@@ -132,7 +132,7 @@ export const PricingProductsForm = ({ form }: PricingProductsFormProps) => {
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const base = useProductTableColumns()
@@ -1,5 +1,5 @@
import { PencilSquare, Plus, Trash } from "@medusajs/icons"
import { PriceListDTO } from "@medusajs/types"
import { PriceListDTO, HttpTypes } from "@medusajs/types"
import { Checkbox, Container, Heading, usePrompt } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { RowSelectionState, createColumnHelper } from "@tanstack/react-table"
@@ -13,7 +13,6 @@ import { useProductTableColumns } from "../../../../../hooks/table/columns/use-p
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
type PricingProductSectionProps = {
priceList: PriceListDTO
@@ -145,7 +144,7 @@ export const PricingProductSection = ({
)
}
const ProductRowAction = ({ product }: { product: ExtendedProductDTO }) => {
const ProductRowAction = ({ product }: { product: HttpTypes.AdminProduct }) => {
const { t } = useTranslation()
// TODO: The endpoint to remove prices by product id is not implemented in v2.
@@ -171,7 +170,7 @@ const ProductRowAction = ({ product }: { product: ExtendedProductDTO }) => {
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const base = useProductTableColumns()
@@ -2,6 +2,7 @@ import {
CreatePriceListPriceDTO,
PriceListDTO,
UpdatePriceListPriceDTO,
HttpTypes,
} from "@medusajs/types"
import { Button } from "@medusajs/ui"
import { UseFormReturn, useForm, useWatch } from "react-hook-form"
@@ -23,7 +24,6 @@ import {
} from "../../../../../hooks/api/price-lists"
import { useStore } from "../../../../../hooks/api/store"
import { castNumber } from "../../../../../lib/cast-number"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns"
import {
PricingProductsRecordSchema,
@@ -33,7 +33,7 @@ import { isProductRow } from "../../../common/utils"
type PricingProductPricesFormProps = {
priceList: PriceListDTO
products: ExtendedProductDTO[]
products: HttpTypes.AdminProduct[]
}
const PricingProductPricesSchema = z.object({
@@ -245,7 +245,7 @@ export const PricingProductPricesForm = ({
}
function initDefaultValues(
products: ExtendedProductDTO[],
products: HttpTypes.AdminProduct[],
existingProducts: any,
form: UseFormReturn<z.infer<typeof PricingProductPricesSchema>>,
record: VariantsPriceRecord
@@ -1,4 +1,4 @@
import { CurrencyDTO, ProductVariantDTO } from "@medusajs/types"
import { CurrencyDTO, HttpTypes } from "@medusajs/types"
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { UseFormReturn, useWatch } from "react-hook-form"
@@ -48,7 +48,7 @@ export const VariantPricingForm = ({ form }: VariantPricingFormProps) => {
)
}
const columnHelper = createColumnHelper<ProductVariantDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProductVariant>()
export const useVariantPriceGridColumns = ({
currencies = [],
@@ -57,7 +57,7 @@ export const useVariantPriceGridColumns = ({
}) => {
const { t } = useTranslation()
const colDefs: ColumnDef<ProductVariantDTO>[] = useMemo(() => {
const colDefs: ColumnDef<HttpTypes.AdminProductVariant>[] = useMemo(() => {
return [
columnHelper.display({
id: t("fields.title"),
@@ -1,5 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Product } from "@medusajs/medusa"
import { Button, Input } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -11,9 +10,10 @@ import {
useRouteModal,
} from "../../../../../components/route-modal"
import { useUpdateProduct } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type ProductAttributesFormProps = {
product: Product
product: HttpTypes.AdminProduct
}
const dimension = zod
@@ -1,5 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Product } from "@medusajs/medusa"
import { Button, Input, toast } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -12,9 +11,10 @@ import {
useRouteModal,
} from "../../../../../components/route-modal"
import { useCreateProductOption } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type EditProductOptionsFormProps = {
product: Product
product: HttpTypes.AdminProduct
}
const CreateProductOptionSchema = z.object({
@@ -1,5 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Product } from "@medusajs/medusa"
import { Button, Heading, Input, Switch } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -17,9 +16,10 @@ import {
import { useCreateProductVariant } from "../../../../../hooks/api/products"
import { castNumber } from "../../../../../lib/cast-number"
import { optionalInt } from "../../../../../lib/validation"
import { HttpTypes } from "@medusajs/types"
type CreateProductVariantFormProps = {
product: Product
product: HttpTypes.AdminProduct
isStockAndInventoryEnabled?: boolean
}
@@ -3,7 +3,7 @@ import { Button, ProgressStatus, ProgressTabs, toast } from "@medusajs/ui"
import { useEffect, useMemo, useState } from "react"
import { useForm, useWatch } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { SalesChannelDTO } from "@medusajs/types"
import { SalesChannelDTO, HttpTypes } from "@medusajs/types"
import {
RouteFocusModal,
useRouteModal,
@@ -21,7 +21,6 @@ import { ProductCreateInventoryKitForm } from "../product-create-inventory-kit-f
import { ProductCreateVariantsForm } from "../product-create-variants-form"
import { isFetchError } from "../../../../../lib/is-fetch-error"
import { sdk } from "../../../../../lib/client"
import { HttpTypes } from "@medusajs/types"
enum Tab {
DETAILS = "details",
@@ -1,4 +1,4 @@
import { ProductVariantDTO, RegionDTO } from "@medusajs/types"
import { HttpTypes, RegionDTO } from "@medusajs/types"
import { useMemo } from "react"
import { UseFormReturn, useWatch } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -10,7 +10,6 @@ import { DataGridTextCell } from "../../../../../components/data-grid/data-grid-
import { createDataGridHelper } from "../../../../../components/data-grid/utils"
import { DataGridCurrencyCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-currency-cell"
import { DataGridBooleanCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-boolean-cell"
import { DataGridCountrySelectCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-country-select-cell"
import { useRegions } from "../../../../../hooks/api/regions"
type ProductCreateVariantsFormProps = {
@@ -64,7 +63,7 @@ export const ProductCreateVariantsForm = ({
)
}
const columnHelper = createDataGridHelper<ProductVariantDTO>()
const columnHelper = createDataGridHelper<HttpTypes.AdminProductVariant>()
const useColumns = ({
options,
@@ -1,9 +1,9 @@
import { CreateProductDTO } from "@medusajs/types"
import { HttpTypes } from "@medusajs/types"
import { ProductCreateSchemaType } from "./types"
import { castNumber } from "../../../lib/cast-number"
export const normalizeProductFormValues = (
values: ProductCreateSchemaType & { status: CreateProductDTO["status"] }
values: ProductCreateSchemaType & { status: HttpTypes.AdminProductStatus }
) => {
const thumbnail = values.media?.find((media) => media.isThumbnail)?.url
const images = values.media
@@ -1,13 +1,13 @@
import { PencilSquare } from "@medusajs/icons"
import { Product } from "@medusajs/medusa"
import { Container, Heading, Text } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { SectionRow } from "../../../../../components/common/section"
import { getFormattedCountry } from "../../../../../lib/addresses"
import { HttpTypes } from "@medusajs/types"
type ProductAttributeSectionProps = {
product: Product
product: HttpTypes.AdminProduct
}
export const ProductAttributeSection = ({
@@ -1,11 +1,11 @@
import { PencilSquare, Trash } from "@medusajs/icons"
import { Product } from "@medusajs/medusa"
import { Container, Heading, StatusBadge, usePrompt } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { SectionRow } from "../../../../../components/common/section"
import { useDeleteProduct } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
const productStatusColor = (status: string) => {
switch (status) {
@@ -23,7 +23,7 @@ const productStatusColor = (status: string) => {
}
type ProductGeneralSectionProps = {
product: Product
product: HttpTypes.AdminProduct
}
export const ProductGeneralSection = ({
@@ -1,5 +1,4 @@
import { PencilSquare, ThumbnailBadge } from "@medusajs/icons"
import { Product } from "@medusajs/medusa"
import {
Button,
Checkbox,
@@ -16,9 +15,10 @@ import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { useUpdateProduct } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type ProductMedisaSectionProps = {
product: Product
product: HttpTypes.AdminProduct
}
export const ProductMediaSection = ({ product }: ProductMedisaSectionProps) => {
@@ -1,17 +1,17 @@
import { PencilSquare, Plus, Trash } from "@medusajs/icons"
import { Product } from "@medusajs/medusa"
import { Badge, Container, Heading, usePrompt } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { SectionRow } from "../../../../../components/common/section"
import { useDeleteProductOption } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
const OptionActions = ({
product,
option,
}: {
product: Product
option: any
product: HttpTypes.AdminProduct
option: HttpTypes.AdminProductOption
}) => {
const { t } = useTranslation()
const { mutateAsync } = useDeleteProductOption(product.id, option.id)
@@ -61,7 +61,7 @@ const OptionActions = ({
}
type ProductOptionSectionProps = {
product: Product
product: HttpTypes.AdminProduct
}
export const ProductOptionSection = ({
@@ -88,12 +88,12 @@ export const ProductOptionSection = ({
/>
</div>
{product.options.map((option) => {
{product.options?.map((option) => {
return (
<SectionRow
title={option.title}
key={option.id}
value={option.values.map((val) => {
value={option.values?.map((val) => {
return (
<Badge
key={val.value}
@@ -1,13 +1,13 @@
import { PencilSquare } from "@medusajs/icons"
import { Product } from "@medusajs/medusa"
import { Badge, Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { SectionRow } from "../../../../../components/common/section"
import { HttpTypes } from "@medusajs/types"
type ProductOrganizationSectionProps = {
product: Product
product: HttpTypes.AdminProduct
}
export const ProductOrganizationSection = ({
@@ -37,7 +37,7 @@ export const ProductOrganizationSection = ({
<SectionRow
title={t("fields.tags")}
value={
product.tags.length > 0
product.tags?.length
? product.tags.map((tag) => (
<Badge key={tag.id} className="w-fit" size="2xsmall" asChild>
<Link to={`/products?tags=${tag.id}`}>{tag.value}</Link>
@@ -75,7 +75,7 @@ export const ProductOrganizationSection = ({
<SectionRow
title={t("fields.categories")}
value={
product.categories?.length > 0
product.categories?.length
? product.categories.map((pcat) => (
<Badge
key={pcat.id}
@@ -1,12 +1,12 @@
import { Channels, PencilSquare } from "@medusajs/icons"
import { Product } from "@medusajs/medusa"
import { Container, Heading, Text, Tooltip } from "@medusajs/ui"
import { Trans, useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { HttpTypes } from "@medusajs/types"
type ProductSalesChannelSectionProps = {
product: Product
product: HttpTypes.AdminProduct
}
// TODO: The fetched sales channel doesn't contain all necessary info
@@ -1,7 +1,6 @@
import { PencilSquare, Plus } from "@medusajs/icons"
import { Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { ProductDTO } from "@medusajs/types"
import { keepPreviousData } from "@tanstack/react-query"
import { ActionMenu } from "../../../../../components/common/action-menu"
@@ -11,9 +10,10 @@ import { useProductVariantTableColumns } from "./use-variant-table-columns"
import { useProductVariantTableFilters } from "./use-variant-table-filters"
import { useProductVariantTableQuery } from "./use-variant-table-query"
import { useProductVariants } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type ProductVariantSectionProps = {
product: ProductDTO
product: HttpTypes.AdminProduct
}
const PAGE_SIZE = 10
@@ -1,5 +1,4 @@
import { PencilSquare, Trash } from "@medusajs/icons"
import { Product, ProductVariant } from "@medusajs/medusa"
import { Badge, usePrompt } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
@@ -8,13 +7,14 @@ import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell"
import { useDeleteVariant } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
const VariantActions = ({
variant,
product,
}: {
variant: ProductVariant
product: Product
variant: HttpTypes.AdminProductVariant
product: HttpTypes.AdminProduct
}) => {
const { mutateAsync } = useDeleteVariant(product.id, variant.id)
const { t } = useTranslation()
@@ -63,9 +63,11 @@ const VariantActions = ({
)
}
const columnHelper = createColumnHelper<ProductVariant>()
const columnHelper = createColumnHelper<HttpTypes.AdminProductVariant>()
export const useProductVariantTableColumns = (product?: Product) => {
export const useProductVariantTableColumns = (
product?: HttpTypes.AdminProduct
) => {
const { t } = useTranslation()
const optionColumns = useMemo(() => {
@@ -141,7 +143,9 @@ export const useProductVariantTableColumns = (product?: Product) => {
columnHelper.display({
id: "actions",
cell: ({ row, table }) => {
const { product } = table.options.meta as { product: Product }
const { product } = table.options.meta as {
product: HttpTypes.AdminProduct
}
return <VariantActions variant={row.original} product={product} />
},
@@ -1,4 +1,4 @@
import { AdminGetProductsVariantsParams } from "@medusajs/medusa"
import { HttpTypes } from "@medusajs/types"
import { useQueryParams } from "../../../../../hooks/use-query-params"
export const useProductVariantTableQuery = ({
@@ -31,7 +31,7 @@ export const useProductVariantTableQuery = ({
order,
} = queryObject
const searchParams: AdminGetProductsVariantsParams = {
const searchParams: HttpTypes.AdminProductVariantParams = {
limit: pageSize,
offset: offset ? Number(offset) : 0,
manage_inventory: manage_inventory
@@ -1,14 +1,14 @@
import { LoaderFunctionArgs } from "react-router-dom"
import { productsQueryKeys } from "../../../hooks/api/products"
import { client } from "../../../lib/client"
import { sdk } from "../../../lib/client"
import { queryClient } from "../../../lib/query-client"
import { PRODUCT_DETAIL_FIELDS } from "./constants"
const productDetailQuery = (id: string) => ({
queryKey: productsQueryKeys.detail(id),
queryFn: async () =>
client.products.retrieve(id, { fields: PRODUCT_DETAIL_FIELDS }),
sdk.admin.product.retrieve(id, { fields: PRODUCT_DETAIL_FIELDS }),
})
export const productLoader = async ({ params }: LoaderFunctionArgs) => {
@@ -1,5 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { ProductOption } from "@medusajs/medusa"
import { Button, Input } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -12,9 +11,10 @@ import {
useRouteModal,
} from "../../../../../components/route-modal"
import { useUpdateProductOption } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type EditProductOptionFormProps = {
option: ProductOption
option: HttpTypes.AdminProductOption
}
const CreateProductOptionSchema = z.object({
@@ -1,5 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Product, ProductVariant } from "@medusajs/medusa"
import { Button, Heading, Input, Switch } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -19,10 +18,11 @@ import {
parseOptionalFormNumber,
} from "../../../../../lib/form-helpers"
import { optionalInt } from "../../../../../lib/validation"
import { HttpTypes } from "@medusajs/types"
type ProductEditVariantFormProps = {
product: Product
variant: ProductVariant
product: HttpTypes.AdminProduct
variant: HttpTypes.AdminProductVariant
}
const ProductEditVariantSchema = z.object({
@@ -1,7 +1,7 @@
import { LoaderFunctionArgs } from "react-router-dom"
import { productsQueryKeys } from "../../../hooks/api/products"
import { client } from "../../../lib/client"
import { sdk } from "../../../lib/client"
import { queryClient } from "../../../lib/query-client"
const queryKey = (id: string) => {
@@ -9,7 +9,7 @@ const queryKey = (id: string) => {
}
const queryFn = async (id: string) => {
return await client.products.retrieve(id)
return await sdk.admin.product.retrieve(id)
}
const editProductVariantQuery = (id: string) => ({
@@ -1,4 +1,3 @@
import { ProductVariant } from "@medusajs/medusa"
import { Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { json, useLoaderData, useParams } from "react-router-dom"
@@ -6,6 +5,7 @@ import { RouteDrawer } from "../../../components/route-modal"
import { useProduct } from "../../../hooks/api/products"
import { ProductEditVariantForm } from "./components/product-edit-variant-form"
import { editProductVariantLoader } from "./loader"
import { HttpTypes } from "@medusajs/types"
export const ProductEditVariant = () => {
const initialData = useLoaderData() as Awaited<
@@ -20,7 +20,7 @@ export const ProductEditVariant = () => {
})
const variant = product?.variants.find(
(v: ProductVariant) => v.id === variant_id
(v: HttpTypes.AdminProductVariant) => v.id === variant_id
)
if (!isLoading && !variant) {
@@ -44,7 +44,7 @@ export const ProductEditVariant = () => {
{ready && (
<ProductEditVariantForm
product={product}
variant={variant as unknown as ProductVariant}
variant={variant as unknown as HttpTypes.AdminProductVariant}
/>
)}
</RouteDrawer>
@@ -1,6 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Product } from "@medusajs/medusa"
import { ProductStatus } from "@medusajs/types"
import { Button, Input, Select, Text, Textarea } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -14,9 +12,10 @@ import {
} from "../../../../../components/route-modal"
import { useUpdateProduct } from "../../../../../hooks/api/products"
import { parseOptionalFormData } from "../../../../../lib/form-helpers"
import { HttpTypes } from "@medusajs/types"
type EditProductFormProps = {
product: Product
product: HttpTypes.AdminProduct
}
const EditProductSchema = zod.object({
@@ -58,7 +57,7 @@ export const EditProductForm = ({ product }: EditProductFormProps) => {
title,
discountable,
handle,
status: status as ProductStatus,
status: status as HttpTypes.AdminProductStatus,
...nullableData,
},
{
@@ -16,8 +16,8 @@ import { useProductTableColumns } from "../../../../../hooks/table/columns/use-p
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { productsLoader } from "../../loader"
import { HttpTypes } from "@medusajs/types"
const PAGE_SIZE = 20
@@ -43,7 +43,7 @@ export const ProductListTable = () => {
const columns = useColumns()
const { table } = useDataTable({
data: (products ?? []) as ExtendedProductDTO[],
data: (products ?? []) as HttpTypes.AdminProduct[],
columns,
count,
enablePagination: true,
@@ -81,7 +81,7 @@ export const ProductListTable = () => {
)
}
const ProductActions = ({ product }: { product: ExtendedProductDTO }) => {
const ProductActions = ({ product }: { product: HttpTypes.AdminProduct }) => {
const { t } = useTranslation()
const prompt = usePrompt()
const { mutateAsync } = useDeleteProduct(product.id)
@@ -144,7 +144,7 @@ const ProductActions = ({ product }: { product: ExtendedProductDTO }) => {
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const base = useProductTableColumns()
@@ -1,13 +1,13 @@
import { QueryClient } from "@tanstack/react-query"
import { productsQueryKeys } from "../../../hooks/api/products"
import { client } from "../../../lib/client"
import { sdk } from "../../../lib/client"
import { queryClient } from "../../../lib/query-client"
import { ProductListRes } from "../../../types/api-responses"
const productsListQuery = () => ({
queryKey: productsQueryKeys.list({ limit: 20, offset: 0 }),
queryFn: async () => client.products.list({ limit: 20, offset: 0 }),
queryFn: async () => sdk.admin.product.list({ limit: 20, offset: 0 }),
})
export const productsLoader = (client: QueryClient) => {
@@ -5,7 +5,6 @@ import {
TriangleLeftMini,
TriangleRightMini,
} from "@medusajs/icons"
import { Image, Product } from "@medusajs/medusa"
import { Button, IconButton, Text, Tooltip, clx, usePrompt } from "@medusajs/ui"
import { useCallback, useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
@@ -13,9 +12,10 @@ import { Link, useLocation } from "react-router-dom"
import { RouteFocusModal } from "../../../../../components/route-modal"
import { useUpdateProduct } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type ProductMediaGalleryProps = {
product: Product
product: HttpTypes.AdminProduct
}
export const ProductMediaGallery = ({ product }: ProductMediaGalleryProps) => {
@@ -303,7 +303,10 @@ type Media = {
isThumbnail: boolean
}
const getMedia = (images: Image[] | null, thumbnail: string | null) => {
const getMedia = (
images: HttpTypes.AdminProductImage[] | null,
thumbnail: string | null
) => {
const media: Media[] =
images?.map((image) => ({
id: image.id,
@@ -1,5 +1,4 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Product } from "@medusajs/medusa"
import { Button, toast } from "@medusajs/ui"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
@@ -14,9 +13,10 @@ import { useUpdateProduct } from "../../../../../hooks/api/products"
import { useComboboxData } from "../../../../../hooks/use-combobox-data"
import { client, sdk } from "../../../../../lib/client"
import { CategoryCombobox } from "../../../common/components/category-combobox"
import { HttpTypes } from "@medusajs/types"
type ProductOrganizationFormProps = {
product: Product
product: HttpTypes.AdminProduct
}
const ProductOrganizationSchema = zod.object({
@@ -5,9 +5,9 @@ import { useTranslation } from "react-i18next"
import * as zod from "zod"
import { RouteFocusModal, useRouteModal } from "../../../components/route-modal"
import { useUpdateProductVariantsBatch } from "../../../hooks/api/products"
import { ExtendedProductDTO } from "../../../types/api-responses"
import { VariantPricingForm } from "../common/variant-pricing-form"
import { castNumber } from "../../../lib/cast-number"
import { HttpTypes } from "@medusajs/types"
export const UpdateVariantPricesSchema = zod.object({
variants: zod.array(
@@ -23,7 +23,11 @@ export type UpdateVariantPricesSchemaType = zod.infer<
typeof UpdateVariantPricesSchema
>
export const PricingEdit = ({ product }: { product: ExtendedProductDTO }) => {
export const PricingEdit = ({
product,
}: {
product: HttpTypes.AdminProduct
}) => {
const { t } = useTranslation()
const { handleSuccess } = useRouteModal()
const form = useForm<UpdateVariantPricesSchemaType>({
@@ -1,4 +1,4 @@
import { Product, SalesChannel } from "@medusajs/medusa"
import { SalesChannel } from "@medusajs/medusa"
import { Button, Checkbox } from "@medusajs/ui"
import { RowSelectionState, createColumnHelper } from "@tanstack/react-table"
import { useEffect, useMemo, useState } from "react"
@@ -18,9 +18,10 @@ import { useSalesChannelTableQuery } from "../../../../../hooks/table/query/use-
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useSalesChannels } from "../../../../../hooks/api/sales-channels"
import { useUpdateProduct } from "../../../../../hooks/api/products"
import { HttpTypes } from "@medusajs/types"
type EditSalesChannelsFormProps = {
product: Product
product: HttpTypes.AdminProduct
}
const EditSalesChannelsSchema = zod.object({
@@ -22,7 +22,7 @@ import { useProductTableColumns } from "../../../../hooks/table/columns/use-prod
import { useProductTableFilters } from "../../../../hooks/table/filters/use-product-table-filters"
import { useProductTableQuery } from "../../../../hooks/table/query/use-product-table-query"
import { useDataTable } from "../../../../hooks/use-data-table"
import { ExtendedProductDTO } from "../../../../types/api-responses"
import { HttpTypes } from "@medusajs/types"
type AddProductsToSalesChannelFormProps = {
salesChannel: SalesChannelDTO
@@ -176,7 +176,7 @@ export const AddProductsToSalesChannelForm = ({
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const base = useProductTableColumns()
@@ -22,7 +22,7 @@ import { useProductTableColumns } from "../../../../../hooks/table/columns/use-p
import { useProductTableFilters } from "../../../../../hooks/table/filters/use-product-table-filters"
import { useProductTableQuery } from "../../../../../hooks/table/query/use-product-table-query"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { HttpTypes } from "@medusajs/types"
const PAGE_SIZE = 10
@@ -154,7 +154,7 @@ export const SalesChannelProductSection = ({
)
}
const columnHelper = createColumnHelper<ExtendedProductDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
const useColumns = () => {
const base = useProductTableColumns()
@@ -1,4 +1,4 @@
import { CustomerGroup, Product, ProductType } from "@medusajs/medusa"
import { CustomerGroup } from "@medusajs/medusa"
import { Button } from "@medusajs/ui"
import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
@@ -25,7 +25,6 @@ import { useProductCollectionConditionsTableColumns } from "../../hooks/columns/
import { useProductCollectionConditionsTableFilters } from "../../hooks/filters/use-product-collection-conditions-table-filters"
import { useProductCollectionConditionsTableQuery } from "../../hooks/query/use-product-collection-conditions-table-query"
import { ProductCollectionDTO } from "@medusajs/types"
import { keepPreviousData } from "@tanstack/react-query"
import { useCollections } from "../../../../../hooks/api/collections"
import { useCustomerGroups } from "../../../../../hooks/api/customer-groups"
@@ -33,6 +32,7 @@ import { useProductTypes } from "../../../../../hooks/api/product-types"
import { useProducts } from "../../../../../hooks/api/products"
import { ConditionEntities } from "../../constants"
import { ConditionsOption } from "../../types"
import { HttpTypes } from "@medusajs/types"
const PAGE_SIZE = 50
@@ -102,7 +102,7 @@ const ProductConditionsTable = ({ selected = [], onSave }: ConditionsProps) => {
if (added.length) {
const addedProducts = (products?.filter((p) => added.includes(p.id!)) ??
[]) as Product[]
[]) as HttpTypes.AdminProduct[]
if (addedProducts.length > 0) {
const newConditions = addedProducts.map((p) => ({
@@ -322,7 +322,7 @@ const ProductTypeConditionsTable = ({
if (added.length) {
const addedTypes = (product_types?.filter((p) => added.includes(p.id!)) ??
[]) as ProductType[]
[]) as HttpTypes.AdminProductType[]
if (addedTypes.length > 0) {
const newConditions = addedTypes.map((p) => ({
@@ -431,7 +431,7 @@ const ProductCollectionConditionsTable = ({
if (added.length) {
const addedCollections = (collections?.filter((p) =>
added.includes(p.id!)
) ?? []) as ProductCollectionDTO[]
) ?? []) as HttpTypes.AdminCollection[]
if (addedCollections.length > 0) {
const newConditions = addedCollections.map((p) => ({
@@ -1,10 +1,10 @@
import { ProductCollectionDTO } from "@medusajs/types"
import { HttpTypes } from "@medusajs/types"
import { Checkbox } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
const columnHelper = createColumnHelper<ProductCollectionDTO>()
const columnHelper = createColumnHelper<HttpTypes.AdminCollection>()
export const useProductCollectionConditionsTableColumns = () => {
const { t } = useTranslation()
@@ -1,10 +1,10 @@
import { Product } from "@medusajs/medusa"
import { Checkbox } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useProductTableColumns } from "../../../../../hooks/table/columns/use-product-table-columns"
import { HttpTypes } from "@medusajs/types"
const columnHelper = createColumnHelper<Product>()
const columnHelper = createColumnHelper<HttpTypes.AdminProduct>()
export const useProductConditionsTableColumns = () => {
const base = useProductTableColumns()
@@ -1,10 +1,10 @@
import { ProductType } from "@medusajs/medusa"
import { HttpTypes } from "@medusajs/types"
import { Checkbox } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
const columnHelper = createColumnHelper<ProductType>()
const columnHelper = createColumnHelper<HttpTypes.AdminProductType>()
export const useProductTypeConditionsTableColumns = () => {
const { t } = useTranslation()
@@ -13,11 +13,6 @@ import {
OrderDTO,
PaymentProviderDTO,
PriceListDTO,
ProductCategoryDTO,
ProductCollectionDTO,
ProductDTO,
ProductTypeDTO,
ProductVariantDTO,
PromotionDTO,
PromotionRuleDTO,
SalesChannelDTO,
@@ -27,9 +22,9 @@ import {
StockLocationDTO,
StoreDTO,
UserDTO,
HttpTypes,
} from "@medusajs/types"
import { ProductTagDTO } from "@medusajs/types/dist/product"
import { WorkflowExecutionDTO } from "../routes/workflow-executions/types"
type ListRes = {
@@ -105,29 +100,6 @@ export type InviteDeleteRes = DeleteRes
export type OrderRes = { order: OrderDTO }
export type OrderListRes = { orders: OrderDTO[] } & ListRes
// Products
export type ExtendedProductDTO = ProductDTO & {
variants: ProductVariantDTO[] | null
sales_channels: SalesChannelDTO[] | null
collections: ProductCollectionDTO[] | null
categories: ProductCategoryDTO[] | null
}
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
// Payments
export type PaymentProvidersListRes = {
+217 -1
View File
@@ -8,12 +8,27 @@ export class Product {
this.client = client
}
async batch(
body: HttpTypes.AdminBatchProductRequest,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminBatchProductResponse>(
`/admin/products/batch`,
{
method: "POST",
headers,
body,
query,
}
)
}
async create(
body: HttpTypes.AdminCreateProduct,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<{ product: HttpTypes.AdminProduct }>(
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products`,
{
method: "POST",
@@ -23,4 +38,205 @@ export class Product {
}
)
}
async update(
id: string,
body: HttpTypes.AdminUpdateProduct,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products/${id}`,
{
method: "POST",
headers,
body,
query,
}
)
}
async list(
queryParams?: HttpTypes.AdminProductParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductListResponse>(
`/admin/products`,
{
headers,
query: queryParams,
}
)
}
async retrieve(id: string, query?: SelectParams, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products/${id}`,
{
query,
headers,
}
)
}
async delete(id: string, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminProductDeleteResponse>(
`/admin/products/${id}`,
{
method: "DELETE",
headers,
}
)
}
async batchVariants(
productId: string,
body: HttpTypes.AdminBatchProductVariantRequest,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminBatchProductVariantResponse>(
`/admin/products/${productId}/variants/batch`,
{
method: "POST",
headers,
body,
query,
}
)
}
async createVariant(
productId: string,
body: HttpTypes.AdminCreateProductVariant,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products/${productId}/variants`,
{
method: "POST",
headers,
body,
query,
}
)
}
async updateVariant(
productId: string,
id: string,
body: HttpTypes.AdminUpdateProductVariant,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products/${productId}/variants/${id}`,
{
method: "POST",
headers,
body,
query,
}
)
}
async listVariants(
productId: string,
queryParams?: HttpTypes.AdminProductVariantParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductVariantListResponse>(
`/admin/products/${productId}/variants`,
{
headers,
query: queryParams,
}
)
}
async retrieveVariant(
productId: string,
id: string,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductVariantResponse>(
`/admin/products/${productId}/variants/${id}`,
{
query,
headers,
}
)
}
async deleteVariant(productId: string, id: string, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminProductVariantDeleteResponse>(
`/admin/products/${productId}/variants/${id}`,
{
method: "DELETE",
headers,
}
)
}
async createOption(
productId: string,
body: HttpTypes.AdminCreateProductOption,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products/${productId}/options`,
{
method: "POST",
headers,
body,
query,
}
)
}
async updateOption(
productId: string,
id: string,
body: HttpTypes.AdminUpdateProductOption,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductResponse>(
`/admin/products/${productId}/variants/${id}`,
{
method: "POST",
headers,
body,
query,
}
)
}
async listOptions(
productId: string,
queryParams?: HttpTypes.AdminProductOptionParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductOptionListResponse>(
`/admin/products/${productId}/options`,
{
headers,
query: queryParams,
}
)
}
async retrieveOption(
productId: string,
id: string,
query?: SelectParams,
headers?: ClientHeaders
) {
return await this.client.fetch<HttpTypes.AdminProductOptionResponse>(
`/admin/products/${productId}/options/${id}`,
{
query,
headers,
}
)
}
async deleteOption(productId: string, id: string, headers?: ClientHeaders) {
return await this.client.fetch<HttpTypes.AdminProductOptionDeleteResponse>(
`/admin/products/${productId}/options/${id}`,
{
method: "DELETE",
headers,
}
)
}
}
+10 -9
View File
@@ -71,7 +71,7 @@ export class Store {
public category = {
list: async (
query?: FindParams & HttpTypes.StoreProductCategoryFilters,
query?: HttpTypes.StoreProductCategoryParams,
headers?: ClientHeaders
) => {
return this.client.fetch<
@@ -99,22 +99,23 @@ export class Store {
public product = {
list: async (
query?: FindParams & HttpTypes.StoreProductFilters,
query?: HttpTypes.StoreProductParams,
headers?: ClientHeaders
) => {
return this.client.fetch<
PaginatedResponse<{ products: HttpTypes.StoreProduct[] }>
>(`/store/products`, {
query,
headers,
})
return this.client.fetch<HttpTypes.StoreProductListResponse>(
`/store/products`,
{
query,
headers,
}
)
},
retrieve: async (
id: string,
query?: SelectParams,
headers?: ClientHeaders
) => {
return this.client.fetch<{ product: HttpTypes.StoreProduct }>(
return this.client.fetch<HttpTypes.StoreProductResponse>(
`/store/products/${id}`,
{
query,
@@ -1,96 +0,0 @@
import {
BaseProduct,
BaseProductCategory,
BaseProductCategoryFilters,
BaseProductFilters,
BaseProductImage,
BaseProductOption,
BaseProductOptionFilters,
BaseProductOptionValue,
BaseProductTag,
BaseProductTagFilters,
BaseProductType,
BaseProductTypeFilters,
BaseProductVariant,
BaseProductVariantFilters,
} from "./common"
export interface AdminProduct extends Omit<BaseProduct, "categories"> {
categories?: AdminProductCategory[]
}
export interface AdminProductCategory extends BaseProductCategory {
is_active?: boolean
is_internal?: boolean
}
export interface AdminProductVariant extends BaseProductVariant {}
export interface AdminProductTag extends BaseProductTag {}
export interface AdminProductType extends BaseProductType {}
export interface AdminProductOption extends BaseProductOption {}
export interface AdminProductImage extends BaseProductImage {}
export interface AdminProductOptionValue extends BaseProductOptionValue {}
export interface AdminProductFilters extends BaseProductFilters {}
export interface AdminProductTagFilters extends BaseProductTagFilters {}
export interface AdminProductTypeFilters extends BaseProductTypeFilters {}
export interface AdminProductOptionFilters extends BaseProductOptionFilters {}
export interface AdminProductVariantFilters extends BaseProductVariantFilters {}
export interface AdminProductCategoryFilters
extends BaseProductCategoryFilters {}
export interface AdminCreateProductVariantPrice {
currency_code: string
amount: number
min_quantity?: number
max_quantity?: number
}
export interface AdminCreateProductVariant {
title: string
sku?: string
ean?: string
upc?: string
barcode?: string
hs_code?: string
mid_code?: string
allow_backorder?: boolean
manage_inventory?: boolean
variant_rank?: number
weight?: number
length?: number
height?: number
width?: number
origin_country?: string
material?: string
metadata?: Record<string, any>
prices: AdminCreateProductVariantPrice[]
options?: Record<string, any>
}
export interface AdminCreateProduct {
title: string
subtitle?: string
description?: string
is_giftcard?: boolean
discountable?: boolean
images?: { url: string }[]
thumbnail?: string
handle?: string
status?: string
type_id?: string | null
collection_id?: string | null
categories?: { id: string }[]
tags?: { id?: string; value?: string }[]
options?: { title?: string; values?: string[] }[]
variants?: AdminCreateProductVariant[]
sales_channels?: { id: string }[]
weight?: number
length?: number
height?: number
width?: number
hs_code?: string
mid_code?: string
origin_country?: string
material?: string
metadata?: Record<string, any>
}
@@ -0,0 +1,33 @@
import { AdminPrice } from "../../pricing"
import { AdminSalesChannel } from "../../sales-channel"
import {
BaseProduct,
BaseProductCategory,
BaseProductImage,
BaseProductOption,
BaseProductOptionValue,
BaseProductTag,
BaseProductType,
BaseProductVariant,
ProductStatus,
} from "../common"
export interface AdminProductCategory extends BaseProductCategory {
is_active?: boolean
is_internal?: boolean
}
export interface AdminProductVariant extends BaseProductVariant {
prices: AdminPrice[] | null
}
export interface AdminProductTag extends BaseProductTag {}
export interface AdminProductType extends BaseProductType {}
export interface AdminProductOption extends BaseProductOption {}
export interface AdminProductImage extends BaseProductImage {}
export interface AdminProductOptionValue extends BaseProductOptionValue {}
export interface AdminProduct
extends Omit<BaseProduct, "categories" | "variants"> {
categories?: AdminProductCategory[] | null
sales_channels?: AdminSalesChannel[] | null
variants?: AdminProductVariant[] | null
}
export type AdminProductStatus = ProductStatus
@@ -0,0 +1,4 @@
export * from "./entitites"
export * from "./payloads"
export * from "./responses"
export * from "./queries"
@@ -0,0 +1,127 @@
import { BatchMethodRequest } from "../../../common"
import { ProductStatus } from "../common"
export interface AdminBatchProductRequest
extends BatchMethodRequest<AdminCreateProduct, AdminUpdateProduct> {}
export interface AdminBatchProductVariantRequest
extends BatchMethodRequest<
AdminCreateProductVariant,
AdminUpdateProductVariant
> {}
export interface AdminCreateProductVariantPrice {
currency_code: string
amount: number
min_quantity?: number | null
max_quantity?: number | null
}
export interface AdminCreateProductVariant {
title: string
sku?: string
ean?: string
upc?: string
barcode?: string
hs_code?: string
mid_code?: string
allow_backorder?: boolean
manage_inventory?: boolean
variant_rank?: number
weight?: number
length?: number
height?: number
width?: number
origin_country?: string
material?: string
metadata?: Record<string, unknown>
prices: AdminCreateProductVariantPrice[]
options?: Record<string, string>
}
export interface AdminCreateProduct {
title: string
subtitle?: string
description?: string
is_giftcard?: boolean
discountable?: boolean
images?: { url: string }[]
thumbnail?: string
handle?: string
status?: ProductStatus
type_id?: string
collection_id?: string
categories?: { id: string }[]
tags?: { id?: string; value?: string }[]
options?: AdminCreateProductOption[]
variants?: AdminCreateProductVariant[]
sales_channels?: { id: string }[]
weight?: number
length?: number
height?: number
width?: number
hs_code?: string
mid_code?: string
origin_country?: string
material?: string
metadata?: Record<string, unknown>
}
export interface AdminUpdateProductVariant {
title?: string
sku?: string | null
ean?: string | null
upc?: string | null
barcode?: string | null
hs_code?: string | null
mid_code?: string | null
allow_backorder?: boolean
manage_inventory?: boolean
variant_rank?: number
weight?: number | null
length?: number | null
height?: number | null
width?: number | null
origin_country?: string | null
material?: string | null
metadata?: Record<string, unknown> | null
prices?: AdminCreateProductVariantPrice[]
options?: Record<string, string>
}
export interface AdminUpdateProduct {
title?: string
subtitle?: string | null
description?: string | null
is_giftcard?: boolean
discountable?: boolean
images?: { url: string }[]
thumbnail?: string | null
handle?: string
status?: ProductStatus
type_id?: string | null
collection_id?: string | null
categories?: { id: string }[]
tags?: { id?: string; value?: string }[]
options?: AdminUpdateProductOption[]
variants?: AdminCreateProductVariant[]
sales_channels?: { id: string }[]
weight?: number | null
length?: number | null
height?: number | null
width?: number | null
hs_code?: string | null
mid_code?: string | null
origin_country?: string | null
material?: string | null
metadata?: Record<string, unknown> | null
}
export interface AdminCreateProductOption {
title: string
values: string[]
}
export interface AdminUpdateProductOption {
title?: string
values?: string[]
}
@@ -0,0 +1,18 @@
import {
BaseProductCategoryParams,
BaseProductOptionParams,
BaseProductParams,
BaseProductTagParams,
BaseProductTypeParams,
BaseProductVariantParams,
} from "../common"
export interface AdminProductTagParams extends BaseProductTagParams {}
export interface AdminProductTypeParams extends BaseProductTypeParams {}
export interface AdminProductOptionParams extends BaseProductOptionParams {}
export interface AdminProductVariantParams extends BaseProductVariantParams {}
export interface AdminProductParams extends BaseProductParams {
price_list_id?: string | string[]
variants?: AdminProductVariantParams
}
export interface AdminProductCategoryParams extends BaseProductCategoryParams {}
@@ -0,0 +1,45 @@
import { BatchMethodResponse } from "../../../common"
import { DeleteResponse, PaginatedResponse } from "../../common"
import {
AdminProduct,
AdminProductOption,
AdminProductVariant,
} from "./entitites"
export interface AdminProductResponse {
product: AdminProduct
}
export type AdminProductListResponse = PaginatedResponse<{
products: AdminProduct[]
}>
export interface AdminProductDeleteResponse extends DeleteResponse<"product"> {}
export interface AdminBatchProductResponse
extends BatchMethodResponse<AdminProduct> {}
export interface AdminProductVariantResponse {
variant: AdminProductVariant
}
export type AdminProductVariantListResponse = PaginatedResponse<{
variants: AdminProductVariant[]
}>
export interface AdminProductVariantDeleteResponse
extends DeleteResponse<"variant", AdminProduct> {}
export interface AdminBatchProductVariantResponse
extends BatchMethodResponse<AdminProductVariant> {}
export interface AdminProductOptionResponse {
product_option: AdminProductOption
}
export type AdminProductOptionListResponse = PaginatedResponse<{
product_options: AdminProductOption[]
}>
export interface AdminProductOptionDeleteResponse
extends DeleteResponse<"product_option", AdminProduct> {}
+94 -86
View File
@@ -1,94 +1,94 @@
import { BaseFilterable, OperatorMap } from "../../dal"
import { BaseCollection } from "../collection/common"
import { FindParams } from "../common"
export type ProductStatus = "draft" | "proposed" | "published" | "rejected"
export interface BaseProduct {
id?: string
title?: string
handle?: string
subtitle?: string | null
description?: string | null
is_giftcard?: boolean
status?: ProductStatus
thumbnail?: string
width?: number
weight?: number
length?: number
height?: number
origin_country?: string
hs_code?: string
mid_code?: string
material?: string
collection?: BaseCollection
collection_id?: string | null
categories?: BaseProductCategory[]
type?: BaseProductType | null
type_id?: string | null
tags?: BaseProductTag[]
variants?: BaseProductVariant[]
options?: BaseProductOption[]
images?: BaseProductImage[]
discountable?: boolean
external_id?: string | null
created_at?: string
updated_at?: string
deleted_at?: string | null
id: string
title: string
handle: string
subtitle: string | null
description: string | null
is_giftcard: boolean
status: ProductStatus
thumbnail: string | null
width: number | null
weight: number | null
length: number | null
height: number | null
origin_country: string | null
hs_code: string | null
mid_code: string | null
material: string | null
collection: BaseCollection | null
collection_id: string | null
categories?: BaseProductCategory[] | null
type: BaseProductType | null
type_id: string | null
tags: BaseProductTag[] | null
variants: BaseProductVariant[] | null
options: BaseProductOption[] | null
images: BaseProductImage[] | null
discountable: boolean
external_id: string | null
created_at: string | null
updated_at: string | null
deleted_at: string | null
metadata?: Record<string, unknown> | null
}
export interface BaseProductVariant {
id?: string
title?: string
sku?: string
barcode?: string
ean?: string
upc?: string
allow_backorder?: boolean
manage_inventory?: boolean
hs_code?: string
origin_country?: string
mid_code?: string
material?: string
weight?: number
length?: number
height?: number
width?: number
options?: BaseProductOptionValue[]
product?: BaseProduct
product_id?: string | null
variant_rank?: number
created_at?: string
updated_at?: string
deleted_at?: string | null
id: string
title: string | null
sku: string | null
barcode: string | null
ean: string | null
upc: string | null
allow_backorder: boolean | null
manage_inventory: boolean | null
hs_code: string | null
origin_country: string | null
mid_code: string | null
material: string | null
weight: number | null
length: number | null
height: number | null
width: number | null
options: BaseProductOptionValue[] | null
product?: BaseProduct | null
product_id?: string
variant_rank?: number | null
created_at: string
updated_at: string
deleted_at: string | null
metadata?: Record<string, unknown> | null
}
export interface BaseProductCategory {
id?: string
name?: string
description?: string | null
handle?: string
id: string
name: string
description: string | null
handle: string
rank?: number
parent_category?: BaseProductCategory
parent_category_id?: string
parent_category?: BaseProductCategory | null
parent_category_id?: string | null
category_children?: BaseProductCategory[]
products?: BaseProduct[]
created_at?: string
updated_at?: string
metadata?: Record<string, unknown>
metadata?: Record<string, unknown> | null
}
export interface BaseProductTag {
id?: string
value?: string
id: string
value: string
products?: BaseProduct[]
metadata?: Record<string, unknown> | null
}
export interface BaseProductType {
id?: string
value?: string
id: string
value: string
created_at?: string
updated_at?: string
deleted_at?: string | null
@@ -96,10 +96,10 @@ export interface BaseProductType {
}
export interface BaseProductOption {
id?: string
title?: string
product?: BaseProduct
product_id?: string
id: string
title: string
product?: BaseProduct | null
product_id?: string | null
values?: BaseProductOptionValue[]
metadata?: Record<string, unknown> | null
created_at?: string
@@ -108,8 +108,8 @@ export interface BaseProductOption {
}
export interface BaseProductImage {
id?: string
url?: string
id: string
url: string
created_at?: string
updated_at?: string
deleted_at?: string | null
@@ -117,19 +117,22 @@ export interface BaseProductImage {
}
export interface BaseProductOptionValue {
id?: string
value?: string
option?: BaseProductOption
option_id?: string
id: string
value: string
option?: BaseProductOption | null
option_id?: string | null
metadata?: Record<string, unknown> | null
created_at?: string
updated_at?: string
deleted_at?: string | null
}
export interface BaseProductFilters extends BaseFilterable<BaseProductFilters> {
export interface BaseProductParams
extends FindParams,
BaseFilterable<BaseProductParams> {
q?: string
status?: ProductStatus | ProductStatus[]
sales_channel_id?: string | string[]
title?: string | string[]
handle?: string | string[]
id?: string | string[]
@@ -146,30 +149,34 @@ export interface BaseProductFilters extends BaseFilterable<BaseProductFilters> {
deleted_at?: OperatorMap<string>
}
export interface BaseProductTagFilters
extends BaseFilterable<BaseProductTagFilters> {
export interface BaseProductTagParams
extends FindParams,
BaseFilterable<BaseProductTagParams> {
q?: string
id?: string | string[]
value?: string | string[]
}
export interface BaseProductTypeFilters
extends BaseFilterable<BaseProductTypeFilters> {
export interface BaseProductTypeParams
extends FindParams,
BaseFilterable<BaseProductTypeParams> {
q?: string
id?: string | string[]
value?: string
}
export interface BaseProductOptionFilters
extends BaseFilterable<BaseProductOptionFilters> {
export interface BaseProductOptionParams
extends FindParams,
BaseFilterable<BaseProductOptionParams> {
q?: string
id?: string | string[]
title?: string | string[]
product_id?: string | string[]
}
export interface BaseProductVariantFilters
extends BaseFilterable<BaseProductVariantFilters> {
export interface BaseProductVariantParams
extends FindParams,
BaseFilterable<BaseProductVariantParams> {
q?: string
id?: string | string[]
sku?: string | string[]
@@ -177,8 +184,9 @@ export interface BaseProductVariantFilters
options?: Record<string, string>
}
export interface BaseProductCategoryFilters
extends BaseFilterable<BaseProductCategoryFilters> {
export interface BaseProductCategoryParams
extends FindParams,
BaseFilterable<BaseProductCategoryParams> {
q?: string
id?: string | string[]
name?: string | string[]
@@ -1,38 +0,0 @@
import {
BaseProduct,
BaseProductCategory,
BaseProductCategoryFilters,
BaseProductFilters,
BaseProductImage,
BaseProductOption,
BaseProductOptionFilters,
BaseProductOptionValue,
BaseProductTag,
BaseProductTagFilters,
BaseProductType,
BaseProductTypeFilters,
BaseProductVariant,
BaseProductVariantFilters,
} from "./common"
export interface StoreProduct extends BaseProduct {}
export interface StoreProductCategory extends BaseProductCategory {}
export interface StoreProductVariant extends BaseProductVariant {}
export interface StoreProductTag extends BaseProductTag {}
export interface StoreProductType extends BaseProductType {}
export interface StoreProductOption extends BaseProductOption {}
export interface StoreProductImage extends BaseProductImage {}
export interface StoreProductOptionValue extends BaseProductOptionValue {}
export interface StoreProductTagFilters extends BaseProductTagFilters {}
export interface StoreProductTypeFilters extends BaseProductTypeFilters {}
export interface StoreProductOptionFilters extends BaseProductOptionFilters {}
export interface StoreProductVariantFilters extends BaseProductVariantFilters {}
export interface StoreProductFilters extends BaseProductFilters {
// The region ID and currency_code are not filters, but are used for the pricing context. Maybe move to separate type definition.
region_id?: string
currency_code?: string
variants?: StoreProductVariantFilters
}
export interface StoreProductCategoryFilters
extends BaseProductCategoryFilters {}
@@ -0,0 +1,21 @@
import {
BaseProduct,
BaseProductCategory,
BaseProductImage,
BaseProductOption,
BaseProductOptionValue,
BaseProductTag,
BaseProductType,
BaseProductVariant,
ProductStatus,
} from "../common"
export interface StoreProduct extends BaseProduct {}
export interface StoreProductCategory extends BaseProductCategory {}
export interface StoreProductVariant extends BaseProductVariant {}
export interface StoreProductTag extends BaseProductTag {}
export interface StoreProductType extends BaseProductType {}
export interface StoreProductOption extends BaseProductOption {}
export interface StoreProductImage extends BaseProductImage {}
export interface StoreProductOptionValue extends BaseProductOptionValue {}
export type StoreProductStatus = ProductStatus
@@ -0,0 +1,3 @@
export * from "./entitites"
export * from "./responses"
export * from "./queries"
@@ -0,0 +1,20 @@
import {
BaseProductCategoryParams,
BaseProductParams,
BaseProductOptionParams,
BaseProductTagParams,
BaseProductTypeParams,
BaseProductVariantParams,
} from "../common"
export interface StoreProductTagParams extends BaseProductTagParams {}
export interface StoreProductTypeParams extends BaseProductTypeParams {}
export interface StoreProductOptionParams extends BaseProductOptionParams {}
export interface StoreProductVariantParams extends BaseProductVariantParams {}
export interface StoreProductParams extends BaseProductParams {
// The region ID and currency_code are not params, but are used for the pricing context. Maybe move to separate type definition.
region_id?: string
currency_code?: string
variants?: StoreProductVariantParams
}
export interface StoreProductCategoryParams extends BaseProductCategoryParams {}
@@ -0,0 +1,10 @@
import { PaginatedResponse } from "../../common"
import { StoreProduct } from "../store"
export interface StoreProductResponse {
product: StoreProduct
}
export type StoreProductListResponse = PaginatedResponse<{
products: StoreProduct[]
}>
@@ -1,7 +1,9 @@
import { OperatorMap } from "../../../dal"
import { BaseFilterable, OperatorMap } from "../../../dal"
import { FindParams } from "../../common"
export interface AdminSalesChannelListParams extends FindParams {
export interface AdminSalesChannelListParams
extends FindParams,
BaseFilterable<AdminSalesChannelListParams> {
id?: string | string[]
q?: string
name?: string | string[]
@@ -12,6 +14,4 @@ export interface AdminSalesChannelListParams extends FindParams {
created_at?: OperatorMap<string>
updated_at?: OperatorMap<string>
deleted_at?: OperatorMap<string>
$and?: AdminSalesChannelListParams[]
$or?: AdminSalesChannelListParams[]
}
+61 -61
View File
@@ -23,15 +23,15 @@ export interface ProductDTO {
/**
* The handle of the product. The handle can be used to create slug URL paths.
*/
handle?: string | null
handle: string
/**
* The subttle of the product.
*/
subtitle?: string | null
subtitle: string | null
/**
* The description of the product.
*/
description?: string | null
description: string | null
/**
* Whether the product is a gift card.
*/
@@ -43,49 +43,49 @@ export interface ProductDTO {
/**
* The URL of the product's thumbnail.
*/
thumbnail?: string | null
thumbnail: string | null
/**
* The width of the product.
*/
width?: number | null
width: number | null
/**
* The weight of the product.
*/
weight?: number | null
weight: number | null
/**
* The length of the product.
*/
length?: number | null
length: number | null
/**
* The height of the product.
*/
height?: number | null
height: number | null
/**
* The origin country of the product.
*/
origin_country?: string | null
origin_country: string | null
/**
* The HS Code of the product.
*/
hs_code?: string | null
hs_code: string | null
/**
* The MID Code of the product.
*/
mid_code?: string | null
mid_code: string | null
/**
* The material of the product.
*/
material?: string | null
material: string | null
/**
* The associated product collection.
*
* @expandable
*/
collection?: ProductCollectionDTO | null
collection: ProductCollectionDTO | null
/**
* The associated product collection id.
*/
collection_id?: string | null
collection_id: string | null
/**
* The associated product categories.
*
@@ -97,11 +97,11 @@ export interface ProductDTO {
*
* @expandable
*/
type?: ProductTypeDTO | null
type: ProductTypeDTO | null
/**
* The associated product type id.
*/
type_id?: string | null
type_id: string | null
/**
* The associated product tags.
*
@@ -134,19 +134,19 @@ export interface ProductDTO {
* The ID of the product in an external system. This is useful if you're integrating the product with a third-party service and want to maintain
* a reference to the ID in the integrated service.
*/
external_id?: string | null
external_id: string | null
/**
* When the product was created.
*/
created_at?: string | Date
created_at: string | Date
/**
* When the product was updated.
*/
updated_at?: string | Date
updated_at: string | Date
/**
* When the product was deleted.
*/
deleted_at?: string | Date
deleted_at: string | Date
/**
* Holds custom data in key-value pairs.
*/
@@ -170,59 +170,59 @@ export interface ProductVariantDTO {
/**
* The SKU of the product variant.
*/
sku?: string | null
sku: string | null
/**
* The barcode of the product variant.
*/
barcode?: string | null
barcode: string | null
/**
* The EAN of the product variant.
*/
ean?: string | null
ean: string | null
/**
* The UPC of the product variant.
*/
upc?: string | null
upc: string | null
/**
* Whether the product variant can be ordered when it's out of stock.
*/
allow_backorder?: boolean
allow_backorder: boolean
/**
* Whether the product variant's inventory should be managed by the core system.
*/
manage_inventory?: boolean
manage_inventory: boolean
/**
* The HS Code of the product variant.
*/
hs_code?: string | null
hs_code: string | null
/**
* The origin country of the product variant.
*/
origin_country?: string | null
origin_country: string | null
/**
* The MID Code of the product variant.
*/
mid_code?: string | null
mid_code: string | null
/**
* The material of the product variant.
*/
material?: string | null
material: string | null
/**
* The weight of the product variant.
*/
weight?: number | null
weight: number | null
/**
* The length of the product variant.
*/
length?: number | null
length: number | null
/**
* The height of the product variant.
*/
height?: number | null
height: number | null
/**
* The width of the product variant.
*/
width?: number | null
width: number | null
/**
* The associated product options.
*
@@ -232,7 +232,7 @@ export interface ProductVariantDTO {
/**
* Holds custom data in key-value pairs.
*/
metadata?: Record<string, unknown> | null
metadata: Record<string, unknown> | null
/**
* The associated product.
*
@@ -242,7 +242,7 @@ export interface ProductVariantDTO {
/**
* The associated product id.
*/
product_id?: string | null
product_id: string | null
/**
* he ranking of the variant among other variants associated with the product.
*/
@@ -1183,19 +1183,19 @@ export interface CreateProductVariantDTO {
/**
* The SKU of the product variant.
*/
sku?: string | null
sku?: string
/**
* The barcode of the product variant.
*/
barcode?: string | null
barcode?: string
/**
* The EAN of the product variant.
*/
ean?: string | null
ean?: string
/**
* The UPC of the product variant.
*/
upc?: string | null
upc?: string
/**
* Whether the product variant can be ordered when it's out of stock.
*/
@@ -1207,35 +1207,35 @@ export interface CreateProductVariantDTO {
/**
* The HS Code of the product variant.
*/
hs_code?: string | null
hs_code?: string
/**
* The origin country of the product variant.
*/
origin_country?: string | null
origin_country?: string
/**
* The MID Code of the product variant.
*/
mid_code?: string | null
mid_code?: string
/**
* The material of the product variant.
*/
material?: string | null
material?: string
/**
* The weight of the product variant.
*/
weight?: number | null
weight?: number
/**
* The length of the product variant.
*/
length?: number | null
length?: number
/**
* The height of the product variant.
*/
height?: number | null
height?: number
/**
* The width of the product variant.
*/
width?: number | null
width?: number
/**
* The options of the variant. Each key is an option's title, and value
* is an option's value. If an option with the specified title doesn't exist,
@@ -1340,7 +1340,7 @@ export interface UpdateProductVariantDTO {
/**
* Holds custom data in key-value pairs.
*/
metadata?: Record<string, unknown>
metadata?: Record<string, unknown> | null
}
/**
@@ -1472,11 +1472,11 @@ export interface UpdateProductDTO {
/**
* The subttle of the product.
*/
subtitle?: string
subtitle?: string | null
/**
* The description of the product.
*/
description?: string
description?: string | null
/**
* Whether the product is a gift card.
*/
@@ -1488,7 +1488,7 @@ export interface UpdateProductDTO {
/**
* The URL of the product's thumbnail.
*/
thumbnail?: string
thumbnail?: string | null
/**
* The handle of the product. The handle can be used to create slug URL paths.
* If not supplied, the value of the `handle` attribute of the product is set to the slug version of the `title` attribute.
@@ -1530,37 +1530,37 @@ export interface UpdateProductDTO {
/**
* The width of the product.
*/
width?: number
width?: number | null
/**
* The height of the product.
*/
height?: number
height?: number | null
/**
* The length of the product.
*/
length?: number
length?: number | null
/**
* The weight of the product.
*/
weight?: number
weight?: number | null
/**
* The origin country of the product.
*/
origin_country?: string
origin_country?: string | null
/**
* The HS Code of the product.
*/
hs_code?: string
hs_code?: string | null
/**
* The material of the product.
*/
material?: string
material?: string | null
/**
* The MID Code of the product.
*/
mid_code?: string
mid_code?: string | null
/**
* Holds custom data in key-value pairs.
*/
metadata?: Record<string, unknown>
metadata?: Record<string, unknown> | null
}
@@ -6,9 +6,9 @@ export type CreateProductVariantWorkflowInputDTO =
}
export type UpdateProductVariantWorkflowInputDTO =
(ProductTypes.UpsertProductVariantDTO & {
ProductTypes.UpsertProductVariantDTO & {
prices?: PricingTypes.CreateMoneyAmountDTO[]
})[]
}
export type CreateProductWorkflowInputDTO = Omit<
ProductTypes.CreateProductDTO,
@@ -7,37 +7,29 @@ import {
updateProductOptionsWorkflow,
} from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchProduct, remapProductResponse } from "../../../helpers"
import { AdminUpdateProductOptionType } from "../../../validators"
import { remapKeysForProduct, remapProductResponse } from "../../../helpers"
import { HttpTypes } from "@medusajs/types"
import { refetchEntity } from "../../../../../utils/refetch-entity"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductOptionResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const productId = req.params.id
const optionId = req.params.option_id
const productOption = await refetchEntity(
"product_option",
{ id: optionId, product_id: productId },
req.scope,
req.remoteQueryConfig.fields
)
const variables = { id: optionId, product_id: productId }
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_option",
variables,
fields: req.remoteQueryConfig.fields,
})
const [product_option] = await remoteQuery(queryObject)
res.status(200).json({ product_option })
res.status(200).json({ product_option: productOption })
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateProductOptionType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminUpdateProductOption>,
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
const productId = req.params.id
const optionId = req.params.option_id
@@ -49,17 +41,19 @@ export const POST = async (
},
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
productId,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({ product: remapProductResponse(product) })
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductOptionDeleteResponse>
) => {
const productId = req.params.id
const optionId = req.params.option_id
@@ -69,10 +63,11 @@ export const DELETE = async (
input: { ids: [optionId] /* product_id: productId */ },
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
productId,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({
@@ -4,30 +4,25 @@ import {
} from "../../../../../types/routing"
import { createProductOptionsWorkflow } from "@medusajs/core-flows"
import { remapKeysForProduct, remapProductResponse } from "../../helpers"
import { HttpTypes } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchProduct, remapProductResponse } from "../../helpers"
import { AdminCreateProductOptionType } from "../../validators"
refetchEntities,
refetchEntity,
} from "../../../../utils/refetch-entity"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminProductOptionParams>,
res: MedusaResponse<HttpTypes.AdminProductOptionListResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const productId = req.params.id
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_option",
variables: {
filters: { ...req.filterableFields, product_id: productId },
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const { rows: product_options, metadata } = await remoteQuery(queryObject)
const { rows: product_options, metadata } = await refetchEntities(
"product_option",
{ ...req.filterableFields, product_id: productId },
req.scope,
req.remoteQueryConfig.fields,
req.remoteQueryConfig.pagination
)
res.json({
product_options,
@@ -38,25 +33,26 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreateProductOptionType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminCreateProductOption>,
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
const productId = req.params.id
const input = [
{
...req.validatedBody,
product_id: productId,
},
]
await createProductOptionsWorkflow(req.scope).run({
input: { product_options: input },
input: {
product_options: [
{
...req.validatedBody,
product_id: productId,
},
],
},
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
productId,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({ product: remapProductResponse(product) })
}
@@ -6,36 +6,23 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { UpdateProductDTO } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
refetchProduct,
remapKeysForProduct,
remapProductResponse,
} from "../helpers"
import { AdminUpdateProductType } from "../validators"
import { remapKeysForProduct, remapProductResponse } from "../helpers"
import { MedusaError } from "@medusajs/utils"
import { HttpTypes } from "@medusajs/types"
import { refetchEntity } from "../../../utils/refetch-entity"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const variables = { id: req.params.id }
const selectFields = remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
const queryObject = remoteQueryObjectFromString({
entryPoint: "product",
variables,
fields: selectFields,
})
const product = await refetchEntity(
"product",
req.params.id,
req.scope,
selectFields
)
const [product] = await remoteQuery(queryObject)
if (!product) {
throw new MedusaError(MedusaError.Types.NOT_FOUND, "Product not found")
}
@@ -44,27 +31,29 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateProductType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminUpdateProduct>,
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
const { result } = await updateProductsWorkflow(req.scope).run({
input: {
selector: { id: req.params.id },
update: req.validatedBody as UpdateProductDTO,
update: req.validatedBody,
},
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
result[0].id,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({ product: remapProductResponse(product) })
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductDeleteResponse>
) => {
const id = req.params.id
@@ -8,44 +8,36 @@ import {
} from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
refetchProduct,
remapKeysForProduct,
remapKeysForVariant,
remapProductResponse,
remapVariantResponse,
} from "../../../helpers"
import { AdminUpdateProductVariantType } from "../../../validators"
import { HttpTypes } from "@medusajs/types"
import { refetchEntity } from "../../../../../utils/refetch-entity"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductVariantResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
// TODO: Should we allow fetching a variant without knowing the product ID? In such case we'll need to change the route to /admin/products/variants/:id
const productId = req.params.id
const variantId = req.params.variant_id
const variables = { id: variantId, product_id: productId }
const queryObject = remoteQueryObjectFromString({
entryPoint: "variant",
const variant = await refetchEntity(
"variant",
variables,
fields: remapKeysForVariant(req.remoteQueryConfig.fields ?? []),
})
req.scope,
remapKeysForVariant(req.remoteQueryConfig.fields ?? [])
)
const [variant] = await remoteQuery(queryObject)
res.status(200).json({ variant: remapVariantResponse(variant) })
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateProductVariantType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminUpdateProductVariant>,
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
// TODO: Should we allow fetching a variant without knowing the product ID? In such case we'll need to change the route to /admin/products/variants/:id
const productId = req.params.id
const variantId = req.params.variant_id
@@ -56,19 +48,19 @@ export const POST = async (
},
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
productId,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({ product: remapProductResponse(product) })
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductVariantDeleteResponse>
) => {
// TODO: Should we allow fetching a variant without knowing the product ID? In such case we'll need to change the route to /admin/products/variants/:id
const productId = req.params.id
const variantId = req.params.variant_id
@@ -77,16 +69,17 @@ export const DELETE = async (
input: { ids: [variantId] /* product_id: productId */ },
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
productId,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({
id: variantId,
object: "variant",
deleted: true,
parent: product,
parent: remapProductResponse(product),
})
}
@@ -3,21 +3,12 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../types/routing"
import {
AdminBatchUpdateProductVariantType,
AdminCreateProductType,
} from "../../../validators"
import { BatchMethodRequest } from "@medusajs/types"
import { refetchBatchVariants, remapVariantResponse } from "../../../helpers"
import { HttpTypes } from "@medusajs/types"
export const POST = async (
req: AuthenticatedMedusaRequest<
BatchMethodRequest<
AdminCreateProductType,
AdminBatchUpdateProductVariantType
>
>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminBatchProductVariantRequest>,
res: MedusaResponse<HttpTypes.AdminBatchProductVariantResponse>
) => {
const productId = req.params.id
@@ -31,8 +22,7 @@ export const POST = async (
product_id: productId,
})),
delete: req.validatedBody.delete,
// TODO: Fix types
} as any
}
const { result } = await batchProductVariantsWorkflow(req.scope).run({
input: normalizedInput,
@@ -5,36 +5,29 @@ import {
import { createProductVariantsWorkflow } from "@medusajs/core-flows"
import {
remoteQueryObjectFromString,
ContainerRegistrationKeys,
} from "@medusajs/utils"
import {
refetchProduct,
remapKeysForProduct,
remapKeysForVariant,
remapProductResponse,
remapVariantResponse,
} from "../../helpers"
import { AdminCreateProductVariantType } from "../../validators"
import { HttpTypes } from "@medusajs/types"
import {
refetchEntities,
refetchEntity,
} from "../../../../utils/refetch-entity"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
res: MedusaResponse<HttpTypes.AdminProductVariantListResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const productId = req.params.id
const queryObject = remoteQueryObjectFromString({
entryPoint: "variant",
variables: {
filters: { ...req.filterableFields, product_id: productId },
order: req.listConfig.order,
skip: req.listConfig.skip,
take: req.listConfig.take,
},
fields: remapKeysForVariant(req.remoteQueryConfig.fields ?? []),
})
const { rows: variants, metadata } = await remoteQuery(queryObject)
const { rows: variants, metadata } = await refetchEntities(
"variant",
{ ...req.filterableFields, product_id: productId },
req.scope,
remapKeysForVariant(req.remoteQueryConfig.fields ?? []),
req.remoteQueryConfig.pagination
)
res.json({
variants: variants.map(remapVariantResponse),
@@ -45,8 +38,8 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreateProductVariantType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminCreateProductVariant>,
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
const productId = req.params.id
const input = [
@@ -60,10 +53,12 @@ export const POST = async (
input: { product_variants: input },
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
productId,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({ product: remapProductResponse(product) })
}
@@ -3,24 +3,15 @@ import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import {
AdminBatchUpdateProductType,
AdminCreateProductType,
} from "../validators"
import { BatchMethodRequest } from "@medusajs/types"
import { refetchBatchProducts, remapProductResponse } from "../helpers"
import { HttpTypes } from "@medusajs/types"
export const POST = async (
req: AuthenticatedMedusaRequest<
BatchMethodRequest<AdminCreateProductType, AdminBatchUpdateProductType>
>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminBatchProductRequest>,
res: MedusaResponse<HttpTypes.AdminBatchProductResponse>
) => {
// TODO: Fix types
const input = req.validatedBody as any
const { result } = await batchProductsWorkflow(req.scope).run({
input,
input: req.validatedBody,
})
const batchResults = await refetchBatchProducts(
@@ -4,6 +4,7 @@ import {
MedusaContainer,
ProductDTO,
ProductVariantDTO,
HttpTypes,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
@@ -46,19 +47,24 @@ export const remapKeysForVariant = (selectFields: string[]) => {
return [...variantFields, ...pricingFields]
}
export const remapProductResponse = (product: ProductDTO) => {
export const remapProductResponse = (
product: ProductDTO
): HttpTypes.AdminProduct => {
return {
...product,
variants: product.variants?.map(remapVariantResponse),
}
// TODO: Remove any once all typings are cleaned up
} as any
}
export const remapVariantResponse = (variant: ProductVariantDTO) => {
export const remapVariantResponse = (
variant: ProductVariantDTO
): HttpTypes.AdminProductVariant => {
if (!variant) {
return variant
}
return {
const resp = {
...variant,
prices: (variant as any).price_set?.prices?.map((price) => ({
id: price.id,
@@ -70,26 +76,11 @@ export const remapVariantResponse = (variant: ProductVariantDTO) => {
created_at: price.created_at,
updated_at: price.updated_at,
})),
price_set: undefined,
}
}
export const refetchProduct = async (
productId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "product",
variables: {
filters: { id: productId },
},
fields: remapKeysForProduct(fields ?? []),
})
const products = await remoteQuery(queryObject)
return products[0]
delete (resp as any).price_set
// TODO: Remove any once all typings are cleaned up
return resp as any
}
export const refetchVariant = async (
+18 -34
View File
@@ -1,40 +1,25 @@
import { createProductsWorkflow } from "@medusajs/core-flows"
import { CreateProductDTO } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { HttpTypes } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import {
refetchProduct,
remapKeysForProduct,
remapProductResponse,
} from "./helpers"
import {
AdminCreateProductType,
AdminGetProductsParamsType,
} from "./validators"
import { remapKeysForProduct, remapProductResponse } from "./helpers"
import { refetchEntities, refetchEntity } from "../../utils/refetch-entity"
export const GET = async (
req: AuthenticatedMedusaRequest<AdminGetProductsParamsType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminProductParams>,
res: MedusaResponse<HttpTypes.AdminProductListResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const selectFields = remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
const queryObject = remoteQueryObjectFromString({
entryPoint: "product",
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: selectFields,
})
const { rows: products, metadata } = await remoteQuery(queryObject)
const { rows: products, metadata } = await refetchEntities(
"product",
req.filterableFields,
req.scope,
selectFields,
req.remoteQueryConfig.pagination
)
res.json({
products: products.map(remapProductResponse),
@@ -45,19 +30,18 @@ export const GET = async (
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreateProductType>,
res: MedusaResponse
req: AuthenticatedMedusaRequest<HttpTypes.AdminCreateProduct>,
res: MedusaResponse<HttpTypes.AdminProductResponse>
) => {
const input = [req.validatedBody as CreateProductDTO]
const { result } = await createProductsWorkflow(req.scope).run({
input: { products: input },
input: { products: [req.validatedBody] },
})
const product = await refetchProduct(
const product = await refetchEntity(
"product",
result[0].id,
req.scope,
req.remoteQueryConfig.fields
remapKeysForProduct(req.remoteQueryConfig.fields ?? [])
)
res.status(200).json({ product: remapProductResponse(product) })
@@ -4,11 +4,11 @@ import {
} from "@medusajs/utils"
import { NextFunction } from "express"
import { MedusaRequest } from "../../../../types/routing"
import { AdminGetProductsParamsType } from "../validators"
import { HttpTypes } from "@medusajs/types"
export function maybeApplyPriceListsFilter() {
return async (req: MedusaRequest, _, next: NextFunction) => {
const filterableFields: AdminGetProductsParamsType = req.filterableFields
const filterableFields: HttpTypes.AdminProductParams = req.filterableFields
if (!filterableFields.price_list_id) {
return next()
@@ -163,13 +163,30 @@ export const AdminCreateProductVariant = z
export type AdminUpdateProductVariantType = z.infer<
typeof AdminUpdateProductVariant
>
export const AdminUpdateProductVariant = AdminCreateProductVariant.extend({
id: z.string().optional(),
title: z.string().optional(),
prices: z.array(AdminUpdateVariantPrice).optional(),
allow_backorder: z.boolean().optional(),
manage_inventory: z.boolean().optional(),
}).strict()
export const AdminUpdateProductVariant = z
.object({
id: z.string().optional(),
title: z.string().optional(),
prices: z.array(AdminUpdateVariantPrice).optional(),
sku: z.string().nullable().optional().nullable(),
ean: z.string().nullable().optional().nullable(),
upc: z.string().nullable().optional().nullable(),
barcode: z.string().nullable().optional().nullable(),
hs_code: z.string().nullable().optional().nullable(),
mid_code: z.string().nullable().optional().nullable(),
allow_backorder: z.boolean().optional(),
manage_inventory: z.boolean().optional(),
variant_rank: z.number().optional(),
weight: z.number().nullable().optional().nullable(),
length: z.number().nullable().optional().nullable(),
height: z.number().nullable().optional().nullable(),
width: z.number().nullable().optional().nullable(),
origin_country: z.string().nullable().optional().nullable(),
material: z.string().nullable().optional().nullable(),
metadata: z.record(z.unknown()).optional().nullable(),
options: z.record(z.string()).optional(),
})
.strict()
export type AdminBatchUpdateProductVariantType = z.infer<
typeof AdminBatchUpdateProductVariant
@@ -190,46 +207,57 @@ export const AdminCreateProduct = z
description: z.string().nullable().optional(),
is_giftcard: z.boolean().optional().default(false),
discountable: z.boolean().optional().default(true),
images: z
.array(z.object({ url: z.string() }))
.nullable()
.optional(),
thumbnail: z.string().nullable().optional(),
images: z.array(z.object({ url: z.string() })).optional(),
thumbnail: z.string().optional(),
handle: z.string().optional(),
status: statusEnum.optional().default(ProductStatus.DRAFT),
type_id: z.string().nullable().optional(),
collection_id: z.string().nullable().optional(),
categories: z
.array(AdminCreateProductProductCategory)
.nullable()
.optional(),
tags: z.array(AdminUpdateProductTag).nullable().optional(),
type_id: z.string().optional(),
collection_id: z.string().optional(),
categories: z.array(AdminCreateProductProductCategory).optional(),
tags: z.array(AdminUpdateProductTag).optional(),
options: z.array(AdminCreateProductOption).optional(),
variants: z.array(AdminCreateProductVariant).optional(),
sales_channels: z
.array(z.object({ id: z.string() }))
.nullable()
.optional(),
weight: z.number().nullable().optional(),
length: z.number().nullable().optional(),
height: z.number().nullable().optional(),
width: z.number().nullable().optional(),
hs_code: z.string().nullable().optional(),
mid_code: z.string().nullable().optional(),
origin_country: z.string().nullable().optional(),
material: z.string().nullable().optional(),
sales_channels: z.array(z.object({ id: z.string() })).optional(),
weight: z.number().optional(),
length: z.number().optional(),
height: z.number().optional(),
width: z.number().optional(),
hs_code: z.string().optional(),
mid_code: z.string().optional(),
origin_country: z.string().optional(),
material: z.string().optional(),
metadata: z.record(z.unknown()).optional(),
})
.strict()
export type AdminUpdateProductType = z.infer<typeof AdminUpdateProduct>
export const AdminUpdateProduct = AdminCreateProduct.omit({ is_giftcard: true })
.extend({
export const AdminUpdateProduct = z
.object({
title: z.string().optional(),
discountable: z.boolean().optional(),
is_giftcard: z.boolean().optional(),
options: z.array(AdminUpdateProductOption).optional(),
variants: z.array(AdminUpdateProductVariant).optional(),
status: statusEnum.optional(),
subtitle: z.string().optional().nullable(),
description: z.string().optional().nullable(),
images: z.array(z.object({ url: z.string() })).optional(),
thumbnail: z.string().optional().nullable(),
handle: z.string().optional(),
type_id: z.string().optional().nullable(),
collection_id: z.string().optional().nullable(),
categories: z.array(AdminCreateProductProductCategory).optional(),
tags: z.array(AdminUpdateProductTag).nullable().optional(),
sales_channels: z.array(z.object({ id: z.string() })).optional(),
weight: z.number().optional().nullable(),
length: z.number().optional().nullable(),
height: z.number().optional().nullable(),
width: z.number().optional().nullable(),
hs_code: z.string().optional().nullable(),
mid_code: z.string().optional().nullable(),
origin_country: z.string().optional().nullable(),
material: z.string().optional().nullable(),
metadata: z.record(z.unknown()).optional().nullable(),
})
.strict()