diff --git a/package-lock.json b/package-lock.json index 8f6c1559..861f955b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "zod": "3.25.76" }, "devDependencies": { + "@ianvs/prettier-plugin-sort-imports": "^4.7.0", "@medusajs/admin-shared": "2.10.3", "@medusajs/admin-vite-plugin": "2.10.3", "@medusajs/types": "2.10.3", @@ -260,14 +261,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -277,13 +278,13 @@ } }, "node_modules/@babel/generator/node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -293,14 +294,14 @@ } }, "node_modules/@babel/generator/node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -494,9 +495,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -1480,6 +1481,90 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@ianvs/prettier-plugin-sort-imports": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@ianvs/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.7.0.tgz", + "integrity": "sha512-soa2bPUJAFruLL4z/CnMfSEKGznm5ebz29fIa9PxYtu8HHyLKNE1NXAs6dylfw1jn/ilEIfO2oLLN6uAafb7DA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/generator": "^7.26.2", + "@babel/parser": "^7.26.2", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "semver": "^7.5.2" + }, + "peerDependencies": { + "@prettier/plugin-oxc": "^0.0.4", + "@vue/compiler-sfc": "2.7.x || 3.x", + "content-tag": "^4.0.0", + "prettier": "2 || 3 || ^4.0.0-0", + "prettier-plugin-ember-template-tag": "^2.1.0" + }, + "peerDependenciesMeta": { + "@prettier/plugin-oxc": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "content-tag": { + "optional": true + }, + "prettier-plugin-ember-template-tag": { + "optional": true + } + } + }, + "node_modules/@ianvs/prettier-plugin-sort-imports/node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ianvs/prettier-plugin-sort-imports/node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ianvs/prettier-plugin-sort-imports/node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@internationalized/date": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz", diff --git a/src/components/data-grid/components/data-grid-root.tsx b/src/components/data-grid/components/data-grid-root.tsx index 64ff426e..dc52b86f 100644 --- a/src/components/data-grid/components/data-grid-root.tsx +++ b/src/components/data-grid/components/data-grid-root.tsx @@ -444,6 +444,7 @@ export const DataGridRoot = < const specialFocusHandler = (e: KeyboardEvent) => { if (isSpecialFocusKey(e)) { handleSpecialFocusKeys(e) + return } } @@ -715,6 +716,7 @@ const DataGridHeader = ({ onHeaderInteractionChange(value) setColumnsOpen(value) } + return (
@@ -861,10 +863,10 @@ const DataGridCell = ({ type DataGridRowProps = { row: Row rowIndex: number - virtualRow: VirtualItem + virtualRow: VirtualItem virtualPaddingLeft?: number virtualPaddingRight?: number - virtualColumns: VirtualItem[] + virtualColumns: VirtualItem[] flatColumns: Column[] anchor: DataGridCoordinates | null onDragToFillStart: (e: React.MouseEvent) => void diff --git a/src/components/search/use-search-results.tsx b/src/components/search/use-search-results.tsx index 41149d9b..bc1d77e7 100644 --- a/src/components/search/use-search-results.tsx +++ b/src/components/search/use-search-results.tsx @@ -29,6 +29,7 @@ import { useReturnReasons } from "../../hooks/api/return-reasons" import { Shortcut, ShortcutType } from "../../providers/keybind-provider" import { useGlobalShortcuts } from "../../providers/keybind-provider/hooks" import { DynamicSearchResult, SearchArea } from "./types" +import { ExtendedAdminProduct, ExtendedAdminProductVariant } from "@custom-types/product" type UseSearchProps = { q?: string @@ -426,6 +427,7 @@ const useDynamicSearchResults = ( if (isAreaEnabled(currentArea, area) || currentArea === "all") { return transformDynamicSearchResults(area, limit, t, response) } + return null }) .filter(Boolean) // Remove null values @@ -488,6 +490,7 @@ function isAreaEnabled(area: SearchArea, currentArea: SearchArea) { if (area === currentArea) { return true } + return false } @@ -518,7 +521,7 @@ const transformMap: TransformMap = { }, product: { dataKey: "products", - transform: (product: HttpTypes.AdminProduct) => ({ + transform: (product: ExtendedAdminProduct) => ({ id: product.id, title: product.title, to: `/products/${product.id}`, @@ -528,7 +531,7 @@ const transformMap: TransformMap = { }, productVariant: { dataKey: "variants", - transform: (variant: HttpTypes.AdminProductVariant) => ({ + transform: (variant: ExtendedAdminProductVariant) => ({ id: variant.id, title: variant.title!, subtitle: variant.sku ?? undefined, @@ -561,6 +564,7 @@ const transformMap: TransformMap = { const name = [customer.first_name, customer.last_name] .filter(Boolean) .join(" ") + return { id: customer.id, title: name || customer.email, diff --git a/src/components/table/table-cells/product/variant-cell/variant-cell.tsx b/src/components/table/table-cells/product/variant-cell/variant-cell.tsx index bdaa33d0..294fba63 100644 --- a/src/components/table/table-cells/product/variant-cell/variant-cell.tsx +++ b/src/components/table/table-cells/product/variant-cell/variant-cell.tsx @@ -1,10 +1,10 @@ import { useTranslation } from "react-i18next" import { PlaceholderCell } from "../../common/placeholder-cell" -import { HttpTypes } from "@medusajs/types" +import type { ExtendedAdminProductVariant } from "@custom-types/product" type VariantCellProps = { - variants?: HttpTypes.AdminProductVariant[] | null + variants?: ExtendedAdminProductVariant[] | null "data-testid"?: string } diff --git a/src/dashboard-app/routes/get-route.map.tsx b/src/dashboard-app/routes/get-route.map.tsx index f0f02cff..745d0c07 100644 --- a/src/dashboard-app/routes/get-route.map.tsx +++ b/src/dashboard-app/routes/get-route.map.tsx @@ -12,6 +12,9 @@ import { ErrorBoundary } from "@components/utilities/error-boundary"; import { TaxRegionDetailBreadcrumb } from "@routes/tax-regions/tax-region-detail/breadcrumb"; import { taxRegionLoader } from "@routes/tax-regions/tax-region-detail/loader"; +import type { ExtendedAdminProductResponse, ExtendedAdminProductVariantResponse } from "@custom-types/product"; +import type { ExtendedAdminInventoryItemResponse } from "@custom-types/inventory"; + export function getRouteMap({ settingsRoutes, coreRoutes, @@ -73,7 +76,7 @@ export function getRouteMap({ loader, handle: { breadcrumb: ( - match: UIMatch, + match: UIMatch, ) => , }, }; @@ -175,8 +178,7 @@ export function getRouteMap({ loader, handle: { breadcrumb: ( - // eslint-disable-next-line max-len - match: UIMatch, + match: UIMatch, ) => , }, }; @@ -974,7 +976,7 @@ export function getRouteMap({ loader, handle: { breadcrumb: ( - match: UIMatch, + match: UIMatch, ) => , }, }; diff --git a/src/hooks/api/claims.tsx b/src/hooks/api/claims.tsx index 4fb5bbeb..fd5b4f87 100644 --- a/src/hooks/api/claims.tsx +++ b/src/hooks/api/claims.tsx @@ -1,18 +1,19 @@ -import { FetchError } from "@medusajs/js-sdk" -import { HttpTypes } from "@medusajs/types" +import type { FetchError } from "@medusajs/js-sdk" +import type { HttpTypes } from "@medusajs/types" import { - QueryKey, + type QueryKey, useMutation, - UseMutationOptions, + type UseMutationOptions, useQuery, - UseQueryOptions, + type UseQueryOptions, } from "@tanstack/react-query" -import { sdk } from "../../lib/client" -import { queryClient } from "../../lib/query-client" -import { queryKeysFactory } from "../../lib/query-key-factory" +import { sdk } from "@lib/client" +import { queryClient } from "@lib/query-client" +import { queryKeysFactory } from "@lib/query-key-factory" import { ordersQueryKeys } from "./orders" import { returnsQueryKeys } from "./returns" +import type { AdminAddClaimOutboundItemsPayload } from "@custom-types/claims" const CLAIMS_QUERY_KEY = "claims" as const export const claimsQueryKeys = queryKeysFactory(CLAIMS_QUERY_KEY) @@ -71,7 +72,7 @@ export const useCreateClaim = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminCreateClaim) => sdk.admin.claim.create(payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: HttpTypes.AdminCreateClaim, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -93,11 +94,11 @@ export const useCreateClaim = ( export const useCancelClaim = ( id: string, orderId: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: () => sdk.admin.claim.cancel(id), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: void, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -131,7 +132,7 @@ export const useAddClaimItems = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminAddClaimItems) => sdk.admin.claim.addItems(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: HttpTypes.AdminAddClaimItems, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -162,7 +163,7 @@ export const useUpdateClaimItems = ( }: HttpTypes.AdminUpdateClaimItem & { actionId: string }) => { return sdk.admin.claim.updateItem(id, actionId, payload) }, - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: HttpTypes.AdminUpdateClaimItem & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -189,7 +190,7 @@ export const useRemoveClaimItem = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.return.removeReturnItem(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminReturnResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -214,8 +215,8 @@ export const useAddClaimInboundItems = ( > ) => { return useMutation({ - mutationFn: (payload) => sdk.admin.claim.addInboundItems(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + mutationFn: (payload: HttpTypes.AdminAddClaimInboundItems) => sdk.admin.claim.addInboundItems(id, payload), + onSuccess: (data: HttpTypes.AdminClaimReturnPreviewResponse, variables: HttpTypes.AdminAddClaimInboundItems, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -234,7 +235,7 @@ export const useUpdateClaimInboundItem = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminClaimResponse, + HttpTypes.AdminClaimReturnPreviewResponse, FetchError, HttpTypes.AdminUpdateClaimInboundItem & { actionId: string } > @@ -246,7 +247,7 @@ export const useUpdateClaimInboundItem = ( }: HttpTypes.AdminUpdateClaimInboundItem & { actionId: string }) => { return sdk.admin.claim.updateInboundItem(id, actionId, payload) }, - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimReturnPreviewResponse, variables: HttpTypes.AdminUpdateClaimInboundItem & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -264,12 +265,12 @@ export const useUpdateClaimInboundItem = ( export const useRemoveClaimInboundItem = ( id: string, orderId: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: (actionId: string) => sdk.admin.claim.removeInboundItem(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimReturnPreviewResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -292,7 +293,7 @@ export const useAddClaimInboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminClaimResponse, + HttpTypes.AdminClaimReturnPreviewResponse, FetchError, HttpTypes.AdminClaimAddInboundShipping > @@ -300,7 +301,7 @@ export const useAddClaimInboundShipping = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminClaimAddInboundShipping) => sdk.admin.claim.addInboundShipping(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimReturnPreviewResponse, variables: HttpTypes.AdminClaimAddInboundShipping, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -319,9 +320,9 @@ export const useUpdateClaimInboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminClaimResponse, + HttpTypes.AdminClaimPreviewResponse, FetchError, - HttpTypes.AdminClaimUpdateInboundShipping + HttpTypes.AdminClaimUpdateInboundShipping & { actionId: string } > ) => { return useMutation({ @@ -330,7 +331,7 @@ export const useUpdateClaimInboundShipping = ( ...payload }: HttpTypes.AdminClaimUpdateInboundShipping & { actionId: string }) => sdk.admin.claim.updateInboundShipping(id, actionId, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimPreviewResponse, variables: HttpTypes.AdminClaimUpdateInboundShipping & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -348,12 +349,12 @@ export const useUpdateClaimInboundShipping = ( export const useDeleteClaimInboundShipping = ( id: string, orderId: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: (actionId: string) => sdk.admin.claim.deleteInboundShipping(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimReturnPreviewResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -374,13 +375,13 @@ export const useAddClaimOutboundItems = ( options?: UseMutationOptions< HttpTypes.AdminClaimResponse, FetchError, - HttpTypes.AdminAddClaimOutboundItems + AdminAddClaimOutboundItemsPayload > ) => { return useMutation({ - mutationFn: (payload: HttpTypes.AdminAddClaimOutboundItems) => - sdk.admin.claim.addOutboundItems(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + mutationFn: (payload: AdminAddClaimOutboundItemsPayload) => + sdk.admin.claim.addOutboundItems(id, payload as unknown as HttpTypes.AdminAddClaimOutboundItems), + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: AdminAddClaimOutboundItemsPayload, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -411,7 +412,7 @@ export const useUpdateClaimOutboundItems = ( }: HttpTypes.AdminUpdateClaimOutboundItem & { actionId: string }) => { return sdk.admin.claim.updateOutboundItem(id, actionId, payload) }, - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: HttpTypes.AdminUpdateClaimOutboundItem & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -434,7 +435,7 @@ export const useRemoveClaimOutboundItem = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.claim.removeOutboundItem(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -461,7 +462,7 @@ export const useAddClaimOutboundShipping = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminClaimAddOutboundShipping) => sdk.admin.claim.addOutboundShipping(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: HttpTypes.AdminClaimAddOutboundShipping, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -480,9 +481,9 @@ export const useUpdateClaimOutboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminClaimResponse, + HttpTypes.AdminClaimPreviewResponse, FetchError, - HttpTypes.AdminClaimUpdateOutboundShipping + HttpTypes.AdminClaimUpdateOutboundShipping & { actionId: string } > ) => { return useMutation({ @@ -491,7 +492,7 @@ export const useUpdateClaimOutboundShipping = ( ...payload }: HttpTypes.AdminClaimUpdateOutboundShipping & { actionId: string }) => sdk.admin.claim.updateOutboundShipping(id, actionId, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimPreviewResponse, variables: HttpTypes.AdminClaimUpdateOutboundShipping & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -514,7 +515,7 @@ export const useDeleteClaimOutboundShipping = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.claim.deleteOutboundShipping(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -541,7 +542,7 @@ export const useClaimConfirmRequest = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminRequestClaim) => sdk.admin.claim.request(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimResponse, variables: HttpTypes.AdminRequestClaim, context: unknown) => { queryClient.invalidateQueries({ queryKey: returnsQueryKeys.all, }) @@ -567,11 +568,11 @@ export const useClaimConfirmRequest = ( export const useCancelClaimRequest = ( id: string, orderId: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: () => sdk.admin.claim.cancelRequest(id), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminClaimDeleteResponse, variables: void, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) diff --git a/src/hooks/api/exchanges.tsx b/src/hooks/api/exchanges.tsx index f28600f5..ed7a54fe 100644 --- a/src/hooks/api/exchanges.tsx +++ b/src/hooks/api/exchanges.tsx @@ -1,17 +1,22 @@ -import { FetchError } from "@medusajs/js-sdk" -import { HttpTypes } from "@medusajs/types" +import type { FetchError } from "@medusajs/js-sdk" +import type { HttpTypes } from "@medusajs/types" import { - QueryKey, + type QueryKey, useMutation, - UseMutationOptions, + type UseMutationOptions, useQuery, - UseQueryOptions, + type UseQueryOptions, } from "@tanstack/react-query" -import { sdk } from "../../lib/client" -import { queryClient } from "../../lib/query-client" -import { queryKeysFactory } from "../../lib/query-key-factory" +import { sdk } from "@lib/client" +import { queryClient } from "@lib/query-client" +import { queryKeysFactory } from "@lib/query-key-factory" import { ordersQueryKeys } from "./orders" import { returnsQueryKeys } from "./returns" +import type { + ExtendedAdminExchangeListResponse, + ExtendedAdminExchangeResponse, + AdminAddExchangeOutboundItemsPayload +} from "@custom-types/exchanges/common" const EXCHANGES_QUERY_KEY = "exchanges" as const export const exchangesQueryKeys = queryKeysFactory(EXCHANGES_QUERY_KEY) @@ -21,16 +26,16 @@ export const useExchange = ( query?: HttpTypes.AdminExchangeListParams, options?: Omit< UseQueryOptions< - HttpTypes.AdminExchangeResponse, + ExtendedAdminExchangeResponse, FetchError, - HttpTypes.AdminExchangeResponse, + ExtendedAdminExchangeResponse, QueryKey >, "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ - queryFn: async () => sdk.admin.exchange.retrieve(id, query), + queryFn: async () => sdk.admin.exchange.retrieve(id, query) as unknown as Promise, queryKey: exchangesQueryKeys.detail(id, query), ...options, }) @@ -42,16 +47,16 @@ export const useExchanges = ( query?: HttpTypes.AdminExchangeListParams, options?: Omit< UseQueryOptions< - HttpTypes.AdminExchangeListParams, + ExtendedAdminExchangeListResponse, FetchError, - HttpTypes.AdminExchangeListResponse, + ExtendedAdminExchangeListResponse, QueryKey >, "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ - queryFn: async () => sdk.admin.exchange.list(query), + queryFn: async () => sdk.admin.exchange.list(query) as unknown as Promise, queryKey: exchangesQueryKeys.list(query), ...options, }) @@ -62,7 +67,7 @@ export const useExchanges = ( export const useCreateExchange = ( orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + ExtendedAdminExchangeResponse, FetchError, HttpTypes.AdminCreateExchange > @@ -70,7 +75,7 @@ export const useCreateExchange = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminCreateExchange) => sdk.admin.exchange.create(payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: ExtendedAdminExchangeResponse, variables: HttpTypes.AdminCreateExchange, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -92,11 +97,11 @@ export const useCreateExchange = ( export const useCancelExchange = ( id: string, orderId: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: () => sdk.admin.exchange.cancel(id), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: ExtendedAdminExchangeResponse, variables: void, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -122,7 +127,7 @@ export const useAddExchangeInboundItems = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeReturnResponse, FetchError, HttpTypes.AdminAddExchangeInboundItems > @@ -130,7 +135,7 @@ export const useAddExchangeInboundItems = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminAddExchangeInboundItems) => sdk.admin.exchange.addInboundItems(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeReturnResponse, variables: HttpTypes.AdminAddExchangeInboundItems, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -144,7 +149,7 @@ export const useUpdateExchangeInboundItem = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeReturnResponse, FetchError, HttpTypes.AdminUpdateExchangeInboundItem & { actionId: string } > @@ -156,7 +161,7 @@ export const useUpdateExchangeInboundItem = ( }: HttpTypes.AdminUpdateExchangeInboundItem & { actionId: string }) => { return sdk.admin.exchange.updateInboundItem(id, actionId, payload) }, - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeReturnResponse, variables: HttpTypes.AdminUpdateExchangeInboundItem & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -170,7 +175,7 @@ export const useRemoveExchangeInboundItem = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeReturnResponse, FetchError, string > @@ -178,7 +183,7 @@ export const useRemoveExchangeInboundItem = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.exchange.removeInboundItem(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeReturnResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -201,7 +206,7 @@ export const useAddExchangeInboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeReturnResponse, FetchError, HttpTypes.AdminExchangeAddInboundShipping > @@ -209,7 +214,7 @@ export const useAddExchangeInboundShipping = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminExchangeAddInboundShipping) => sdk.admin.exchange.addInboundShipping(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeReturnResponse, variables: HttpTypes.AdminExchangeAddInboundShipping, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -223,9 +228,9 @@ export const useUpdateExchangeInboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeReturnResponse, FetchError, - HttpTypes.AdminExchangeUpdateInboundShipping + HttpTypes.AdminExchangeUpdateInboundShipping & { actionId: string } > ) => { return useMutation({ @@ -234,7 +239,7 @@ export const useUpdateExchangeInboundShipping = ( ...payload }: HttpTypes.AdminExchangeUpdateInboundShipping & { actionId: string }) => sdk.admin.exchange.updateInboundShipping(id, actionId, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeReturnResponse, variables: HttpTypes.AdminExchangeUpdateInboundShipping & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -248,7 +253,7 @@ export const useDeleteExchangeInboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeReturnResponse, FetchError, string > @@ -256,7 +261,7 @@ export const useDeleteExchangeInboundShipping = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.exchange.deleteInboundShipping(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeReturnResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -271,15 +276,15 @@ export const useAddExchangeOutboundItems = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangePreviewResponse, FetchError, - HttpTypes.AdminAddExchangeOutboundItems + AdminAddExchangeOutboundItemsPayload > ) => { return useMutation({ - mutationFn: (payload: HttpTypes.AdminAddExchangeOutboundItems) => - sdk.admin.exchange.addOutboundItems(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + mutationFn: (payload: AdminAddExchangeOutboundItemsPayload) => + sdk.admin.exchange.addOutboundItems(id, payload as unknown as HttpTypes.AdminAddExchangeOutboundItems), + onSuccess: (data: HttpTypes.AdminExchangePreviewResponse, variables: AdminAddExchangeOutboundItemsPayload, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -293,7 +298,7 @@ export const useUpdateExchangeOutboundItems = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangePreviewResponse, FetchError, HttpTypes.AdminUpdateExchangeOutboundItem & { actionId: string } > @@ -305,7 +310,7 @@ export const useUpdateExchangeOutboundItems = ( }: HttpTypes.AdminUpdateExchangeOutboundItem & { actionId: string }) => { return sdk.admin.exchange.updateOutboundItem(id, actionId, payload) }, - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangePreviewResponse, variables: HttpTypes.AdminUpdateExchangeOutboundItem & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -319,7 +324,7 @@ export const useRemoveExchangeOutboundItem = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangePreviewResponse, FetchError, string > @@ -327,7 +332,7 @@ export const useRemoveExchangeOutboundItem = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.exchange.removeOutboundItem(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangePreviewResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) @@ -346,7 +351,7 @@ export const useAddExchangeOutboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangePreviewResponse, FetchError, HttpTypes.AdminExchangeAddOutboundShipping > @@ -354,7 +359,7 @@ export const useAddExchangeOutboundShipping = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminExchangeAddOutboundShipping) => sdk.admin.exchange.addOutboundShipping(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangePreviewResponse, variables: HttpTypes.AdminExchangeAddOutboundShipping, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -368,9 +373,9 @@ export const useUpdateExchangeOutboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangePreviewResponse, FetchError, - HttpTypes.AdminExchangeUpdateOutboundShipping + HttpTypes.AdminExchangeUpdateOutboundShipping & { actionId: string } > ) => { return useMutation({ @@ -379,7 +384,7 @@ export const useUpdateExchangeOutboundShipping = ( ...payload }: HttpTypes.AdminExchangeUpdateOutboundShipping & { actionId: string }) => sdk.admin.exchange.updateOutboundShipping(id, actionId, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangePreviewResponse, variables: HttpTypes.AdminExchangeUpdateOutboundShipping & { actionId: string }, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -393,7 +398,7 @@ export const useDeleteExchangeOutboundShipping = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangePreviewResponse, FetchError, string > @@ -401,7 +406,7 @@ export const useDeleteExchangeOutboundShipping = ( return useMutation({ mutationFn: (actionId: string) => sdk.admin.exchange.deleteOutboundShipping(id, actionId), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangePreviewResponse, variables: string, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) @@ -415,7 +420,7 @@ export const useExchangeConfirmRequest = ( id: string, orderId: string, options?: UseMutationOptions< - HttpTypes.AdminExchangeResponse, + HttpTypes.AdminExchangeRequestResponse, FetchError, HttpTypes.AdminRequestExchange > @@ -423,7 +428,7 @@ export const useExchangeConfirmRequest = ( return useMutation({ mutationFn: (payload: HttpTypes.AdminRequestExchange) => sdk.admin.exchange.request(id, payload), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeRequestResponse, variables: HttpTypes.AdminRequestExchange, context: unknown) => { queryClient.invalidateQueries({ queryKey: returnsQueryKeys.all, }) @@ -449,11 +454,11 @@ export const useExchangeConfirmRequest = ( export const useCancelExchangeRequest = ( id: string, orderId: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: () => sdk.admin.exchange.cancelRequest(id), - onSuccess: (data: any, variables: any, context: any) => { + onSuccess: (data: HttpTypes.AdminExchangeDeleteResponse, variables: void, context: unknown) => { queryClient.invalidateQueries({ queryKey: ordersQueryKeys.details(), }) diff --git a/src/hooks/api/products.tsx b/src/hooks/api/products.tsx index 2b9c136a..f4daf408 100644 --- a/src/hooks/api/products.tsx +++ b/src/hooks/api/products.tsx @@ -13,9 +13,12 @@ import { queryKeysFactory } from "../../lib/query-key-factory"; import { inventoryItemsQueryKeys } from "./inventory.tsx"; import { AttributeDTO } from "../../types/index.ts"; import { - AdminProductResponse, - AdminProductUpdate, + ExtendedAdminProductResponse, + ExtendedAdminProductUpdate, ExtendedAdminProductListParams, + ExtendedAdminProductListResponse, + ExtendedAdminProductVariantResponse, + ExtendedAdminProductVariantListResponse, } from "../../types/product/common.ts"; const PRODUCTS_QUERY_KEY = "products" as const; @@ -96,17 +99,16 @@ export const useProductVariant = ( query?: HttpTypes.AdminProductVariantParams, options?: Omit< UseQueryOptions< - HttpTypes.AdminProductVariantResponse, - FetchError, - HttpTypes.AdminProductVariantResponse, - QueryKey + ExtendedAdminProductVariantResponse, + FetchError, + ExtendedAdminProductVariantResponse, + QueryKey >, "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ - queryFn: () => - sdk.admin.product.retrieveVariant(productId, variantId, query), + queryFn: async () => sdk.admin.product.retrieveVariant(productId, variantId, query) as Promise, queryKey: variantsQueryKeys.detail(variantId, query), ...options, }); @@ -117,18 +119,20 @@ export const useProductVariant = ( export const useProductVariants = ( productId: string, query?: HttpTypes.AdminProductVariantParams, + options?: Omit< UseQueryOptions< - HttpTypes.AdminProductVariantListResponse, + ExtendedAdminProductVariantListResponse, FetchError, - HttpTypes.AdminProductVariantListResponse, + ExtendedAdminProductVariantListResponse, QueryKey >, "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ - queryFn: () => sdk.admin.product.listVariants(productId, query), + queryFn: async () => + sdk.admin.product.listVariants(productId, query) as Promise, queryKey: variantsQueryKeys.list({ productId, ...query }), ...options, }); @@ -275,19 +279,19 @@ export const useDeleteVariantLazy = ( export const useProduct = ( id: string, - query?: Record, + query?: Record, options?: Omit< UseQueryOptions< - AdminProductResponse, + ExtendedAdminProductResponse, FetchError, - AdminProductResponse, + ExtendedAdminProductResponse, QueryKey >, "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ - queryFn: () => sdk.admin.product.retrieve(id, query), + queryFn: async () => sdk.admin.product.retrieve(id, query) as Promise, queryKey: productsQueryKeys.detail(id, query), ...options, }); @@ -299,16 +303,20 @@ export const useProducts = ( query?: ExtendedAdminProductListParams, options?: Omit< UseQueryOptions< - HttpTypes.AdminProductListResponse, + ExtendedAdminProductListResponse, FetchError, - HttpTypes.AdminProductListResponse, + ExtendedAdminProductListResponse, QueryKey >, "queryFn" | "queryKey" > ) => { const { data, ...rest } = useQuery({ - queryFn: () => sdk.admin.product.list(query), + queryFn: async () => { + const response = await sdk.admin.product.list(query) + + return response as unknown as ExtendedAdminProductListResponse + }, queryKey: productsQueryKeys.list(query), ...options, }); @@ -340,13 +348,17 @@ export const useCreateProduct = ( export const useUpdateProduct = ( id: string, options?: UseMutationOptions< - AdminProductResponse, + ExtendedAdminProductResponse, FetchError, - AdminProductUpdate + ExtendedAdminProductUpdate > ) => { return useMutation({ - mutationFn: (payload) => sdk.admin.product.update(id, payload), + mutationFn: async (payload) => { + const response = await sdk.admin.product.update(id, payload) + + return response as unknown as ExtendedAdminProductResponse + }, onSuccess: async (data, variables, context) => { await queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists(), diff --git a/src/hooks/api/sellers.tsx b/src/hooks/api/sellers.tsx index a195ae40..1d8cc250 100644 --- a/src/hooks/api/sellers.tsx +++ b/src/hooks/api/sellers.tsx @@ -8,8 +8,8 @@ import { import { sdk } from "../../lib/client"; import { queryKeysFactory } from "../../lib/query-key-factory"; -import { VendorSeller } from "../../types"; -import { AdminCustomerGroup, AdminOrder, AdminProduct } from "@medusajs/types"; +import { ExtendedAdminProductListResponse, ExtendedAdminProduct, VendorSeller } from "../../types"; +import type { AdminCustomerGroup, AdminOrder } from "@medusajs/types"; import { OrderSet } from "../../types/order/common"; export const sellerQueryKeys = queryKeysFactory("seller"); @@ -25,8 +25,8 @@ const sortOrders = (orders: any[], order: string) => { const isDesc = order.startsWith("-"); return [...orders].sort((a, b) => { - let aValue: string | number | null | undefined = a[field]; - let bValue: string | number | null | undefined = b[field]; + const aValue: string | number | null | undefined = a[field]; + const bValue: string | number | null | undefined = b[field]; // Handle null/undefined values if (!aValue && aValue !== "") return isDesc ? -1 : 1; @@ -36,6 +36,7 @@ const sortOrders = (orders: any[], order: string) => { if (field === "created_at" || field === "updated_at") { const aDate = new Date(String(aValue)).getTime(); const bDate = new Date(String(bValue)).getTime(); + return isDesc ? bDate - aDate : aDate - bDate; } @@ -43,6 +44,7 @@ const sortOrders = (orders: any[], order: string) => { if (field === "display_id") { const aNum = Number(aValue); const bNum = Number(bValue); + return isDesc ? bNum - aNum : aNum - bNum; } @@ -52,6 +54,7 @@ const sortOrders = (orders: any[], order: string) => { if (aString < bString) return isDesc ? 1 : -1; if (aString > bString) return isDesc ? -1 : 1; + return 0; }); }; @@ -63,8 +66,8 @@ const sortProducts = (products: any[], order: string) => { const isDesc = order.startsWith("-"); return [...products].sort((a, b) => { - let aValue: string | number | null | undefined = a[field]; - let bValue: string | number | null | undefined = b[field]; + const aValue: string | number | null | undefined = a[field]; + const bValue: string | number | null | undefined = b[field]; // Handle null/undefined values if (!aValue && aValue !== "") return isDesc ? -1 : 1; @@ -74,6 +77,7 @@ const sortProducts = (products: any[], order: string) => { if (field === "created_at" || field === "updated_at") { const aDate = new Date(String(aValue)).getTime(); const bDate = new Date(String(bValue)).getTime(); + return isDesc ? bDate - aDate : aDate - bDate; } @@ -83,6 +87,7 @@ const sortProducts = (products: any[], order: string) => { if (aString < bString) return isDesc ? 1 : -1; if (aString > bString) return isDesc ? -1 : 1; + return 0; }); }; @@ -94,8 +99,8 @@ const sortCustomerGroups = (customerGroups: any[], order: string) => { const isDesc = order.startsWith("-"); return [...customerGroups].sort((a, b) => { - let aValue: string | number | null | undefined = a[field]; - let bValue: string | number | null | undefined = b[field]; + const aValue: string | number | null | undefined = a[field]; + const bValue: string | number | null | undefined = b[field]; // Handle null/undefined values if (!aValue && aValue !== "") return isDesc ? -1 : 1; @@ -105,6 +110,7 @@ const sortCustomerGroups = (customerGroups: any[], order: string) => { if (field === "created_at" || field === "updated_at") { const aDate = new Date(String(aValue)).getTime(); const bDate = new Date(String(bValue)).getTime(); + return isDesc ? bDate - aDate : aDate - bDate; } @@ -114,6 +120,7 @@ const sortCustomerGroups = (customerGroups: any[], order: string) => { if (aString < bString) return isDesc ? 1 : -1; if (aString > bString) return isDesc ? -1 : 1; + return 0; }); }; @@ -222,6 +229,7 @@ export const useSellerOrders = ( const filterDate = new Date(dateFilter.$gte); processedOrders = processedOrders.filter((order) => { const orderCreatedAt = new Date(order.created_at || ""); + return orderCreatedAt >= filterDate; }); } @@ -229,6 +237,7 @@ export const useSellerOrders = ( const filterDate = new Date(dateFilter.$lte); processedOrders = processedOrders.filter((order) => { const orderCreatedAt = new Date(order.created_at || ""); + return orderCreatedAt <= filterDate; }); } @@ -236,12 +245,13 @@ export const useSellerOrders = ( // Filter by updated_at date ranges if (filters?.updated_at) { - const dateFilter = filters.updated_at as any; + const dateFilter = filters.updated_at; if (dateFilter.$gte) { const filterDate = new Date(dateFilter.$gte); processedOrders = processedOrders.filter((order) => { const orderUpdatedAt = new Date(order.updated_at || ""); + return orderUpdatedAt >= filterDate; }); } @@ -249,6 +259,7 @@ export const useSellerOrders = ( const filterDate = new Date(dateFilter.$lte); processedOrders = processedOrders.filter((order) => { const orderUpdatedAt = new Date(order.updated_at || ""); + return orderUpdatedAt <= filterDate; }); } @@ -303,23 +314,23 @@ export const useSellerProducts = ( filters?: any ) => { const { data, isLoading, refetch } = useQuery< - { products: AdminProduct[] }, + ExtendedAdminProductListResponse, Error, - { products: AdminProduct[] } + ExtendedAdminProductListResponse >({ queryKey: ["seller-products", id, query], queryFn: () => sdk.client.fetch(`/admin/sellers/${id}/products`, { method: "GET", query, - }), + }) as Promise, }); if (!data?.products) { return { data, isLoading, refetch }; } - let processedProducts = [...data.products]; + let processedProducts: ExtendedAdminProduct[] = [...data.products]; // Apply search filter if present if (filters?.q) { @@ -332,7 +343,7 @@ export const useSellerProducts = ( // Filter by tag_id if (filters?.tag_id && Array.isArray(filters.tag_id)) { processedProducts = processedProducts.filter((product) => - product.tags?.some((tag: any) => filters.tag_id.includes(tag.id)) + product.tags?.some((tag) => filters.tag_id.includes(tag.id)) ); } @@ -346,7 +357,7 @@ export const useSellerProducts = ( // Filter by sales_channel_id if (filters?.sales_channel_id && Array.isArray(filters.sales_channel_id)) { processedProducts = processedProducts.filter((product) => - product.sales_channels?.some((channel: any) => + product.sales_channels?.some((channel) => filters.sales_channel_id.includes(channel.id) ) ); @@ -361,11 +372,12 @@ export const useSellerProducts = ( // Filter by created_at date ranges if (filters?.created_at) { - const dateFilter = filters.created_at as any; + const dateFilter = filters.created_at; if (dateFilter.$gte) { const filterDate = new Date(dateFilter.$gte); processedProducts = processedProducts.filter((product) => { const productCreatedAt = new Date(product.created_at || ""); + return productCreatedAt >= filterDate; }); } @@ -373,6 +385,7 @@ export const useSellerProducts = ( const filterDate = new Date(dateFilter.$lte); processedProducts = processedProducts.filter((product) => { const productCreatedAt = new Date(product.created_at || ""); + return productCreatedAt <= filterDate; }); } @@ -380,11 +393,12 @@ export const useSellerProducts = ( // Filter by updated_at date ranges if (filters?.updated_at) { - const dateFilter = filters.updated_at as any; + const dateFilter = filters.updated_at; if (dateFilter.$gte) { const filterDate = new Date(dateFilter.$gte); processedProducts = processedProducts.filter((product) => { const productUpdatedAt = new Date(product.updated_at || ""); + return productUpdatedAt >= filterDate; }); } @@ -392,6 +406,7 @@ export const useSellerProducts = ( const filterDate = new Date(dateFilter.$lte); processedProducts = processedProducts.filter((product) => { const productUpdatedAt = new Date(product.updated_at || ""); + return productUpdatedAt <= filterDate; }); } @@ -474,6 +489,7 @@ export const useSellerCustomerGroups = ( const filterDate = new Date(dateFilter.$gte); processedCustomerGroups = processedCustomerGroups.filter((group) => { const groupCreatedAt = new Date(group.created_at || ""); + return groupCreatedAt >= filterDate; }); } @@ -481,6 +497,7 @@ export const useSellerCustomerGroups = ( const filterDate = new Date(dateFilter.$lte); processedCustomerGroups = processedCustomerGroups.filter((group) => { const groupCreatedAt = new Date(group.created_at || ""); + return groupCreatedAt <= filterDate; }); } @@ -493,6 +510,7 @@ export const useSellerCustomerGroups = ( const filterDate = new Date(dateFilter.$gte); processedCustomerGroups = processedCustomerGroups.filter((group) => { const groupUpdatedAt = new Date(group.updated_at || ""); + return groupUpdatedAt >= filterDate; }); } @@ -500,6 +518,7 @@ export const useSellerCustomerGroups = ( const filterDate = new Date(dateFilter.$lte); processedCustomerGroups = processedCustomerGroups.filter((group) => { const groupUpdatedAt = new Date(group.updated_at || ""); + return groupUpdatedAt <= filterDate; }); } diff --git a/src/hooks/table/columns/use-product-table-columns.tsx b/src/hooks/table/columns/use-product-table-columns.tsx index d0c69f7a..066d0e64 100644 --- a/src/hooks/table/columns/use-product-table-columns.tsx +++ b/src/hooks/table/columns/use-product-table-columns.tsx @@ -21,9 +21,9 @@ import { VariantCell, VariantHeader, } from "../../../components/table/table-cells/product/variant-cell" -import { HttpTypes } from "@medusajs/types" +import type { ExtendedAdminProduct } from "@custom-types/product" -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() export const useProductTableColumns = () => { return useMemo( diff --git a/src/hooks/table/columns/use-refund-reason-table-columns.tsx b/src/hooks/table/columns/use-refund-reason-table-columns.tsx index 9595d474..805420a2 100644 --- a/src/hooks/table/columns/use-refund-reason-table-columns.tsx +++ b/src/hooks/table/columns/use-refund-reason-table-columns.tsx @@ -5,7 +5,7 @@ import { createDataTableColumnHelper } from "@medusajs/ui"; import { useTranslation } from "react-i18next"; import { DescriptionCell } from "../../../components/table/table-cells/sales-channel/description-cell"; -import { AdminRefundReason } from "../../../types/refund-reasons/common"; +import type { AdminRefundReason } from "@custom-types/refund-reasons"; const columnHelper = createDataTableColumnHelper(); diff --git a/src/hooks/table/query/use-product-table-query.tsx b/src/hooks/table/query/use-product-table-query.tsx index c213e89e..29a276ff 100644 --- a/src/hooks/table/query/use-product-table-query.tsx +++ b/src/hooks/table/query/use-product-table-query.tsx @@ -7,10 +7,6 @@ type UseProductTableQueryProps = { pageSize?: number } -type ExtendedAdminProductListParams = HttpTypes.AdminProductListParams & { - tag_id?: string[] -} - const DEFAULT_FIELDS = "id,title,handle,status,*collection,*sales_channels,variants.id,thumbnail" diff --git a/src/hooks/table/query/use-refund-reason-table-query.tsx b/src/hooks/table/query/use-refund-reason-table-query.tsx index 578a88b8..0ca35848 100644 --- a/src/hooks/table/query/use-refund-reason-table-query.tsx +++ b/src/hooks/table/query/use-refund-reason-table-query.tsx @@ -1,5 +1,5 @@ -import { HttpTypes } from "@medusajs/types" import { useQueryParams } from "../../use-query-params" +import type { AdminRefundReasonListParams } from "@custom-types/refund-reasons" type UseRefundReasonTableQueryProps = { prefix?: string @@ -16,7 +16,7 @@ export const useRefundReasonTableQuery = ({ ) const { offset, q, order, created_at, updated_at } = queryObject - const searchParams: HttpTypes.AdminRefundReasonListParams = { + const searchParams: AdminRefundReasonListParams = { limit: pageSize, offset: offset ? Number(offset) : 0, order, diff --git a/src/i18n/translations/$schema.json b/src/i18n/translations/$schema.json index 25f4daa2..3612a6d1 100644 --- a/src/i18n/translations/$schema.json +++ b/src/i18n/translations/$schema.json @@ -4216,6 +4216,9 @@ }, "selectPaymentToRefund": { "type": "string" + }, + "refundAmountWarning": { + "type": "string" } }, "required": [ @@ -4239,7 +4242,8 @@ "createRefundWrongQuantity", "refundAmount", "paymentLink", - "selectPaymentToRefund" + "selectPaymentToRefund", + "refundAmountWarning" ], "additionalProperties": false }, diff --git a/src/i18n/translations/en.json b/src/i18n/translations/en.json index 805e4044..68e90134 100644 --- a/src/i18n/translations/en.json +++ b/src/i18n/translations/en.json @@ -1125,7 +1125,8 @@ "createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}", "refundAmount": "Refund {{ amount }}", "paymentLink": "Copy payment link for {{ amount }}", - "selectPaymentToRefund": "Select payment to refund" + "selectPaymentToRefund": "Select payment to refund", + "refundAmountWarning": "Refund amount must be greater than 0" }, "edits": { "title": "Edit order", diff --git a/src/routes/categories/category-detail/components/category-product-section/category-product-section.tsx b/src/routes/categories/category-detail/components/category-product-section/category-product-section.tsx index db990a77..e8d933b5 100644 --- a/src/routes/categories/category-detail/components/category-product-section/category-product-section.tsx +++ b/src/routes/categories/category-detail/components/category-product-section/category-product-section.tsx @@ -21,6 +21,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 { ExtendedAdminProduct } from "@custom-types/product" type CategoryProductSectionProps = { category: HttpTypes.AdminProductCategory @@ -162,7 +163,7 @@ export const CategoryProductSection = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const base = useProductTableColumns() diff --git a/src/routes/categories/category-products/components/edit-category-products-form/edit-category-products-form.tsx b/src/routes/categories/category-products/components/edit-category-products-form/edit-category-products-form.tsx index 163358a9..91a49d85 100644 --- a/src/routes/categories/category-products/components/edit-category-products-form/edit-category-products-form.tsx +++ b/src/routes/categories/category-products/components/edit-category-products-form/edit-category-products-form.tsx @@ -2,7 +2,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { z } from "zod" -import { HttpTypes } from "@medusajs/types" import { Button, Checkbox, Hint, Tooltip, toast } from "@medusajs/ui" import { OnChangeFn, @@ -23,10 +22,11 @@ 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 { ExtendedAdminProduct } from "@custom-types/product" type EditCategoryProductsFormProps = { categoryId: string - products?: Pick[] + products?: Pick[] } const EditCategoryProductsSchema = z.object({ @@ -46,6 +46,7 @@ export const EditCategoryProductsForm = ({ const [selection, setSelection] = useState( products.reduce((acc, p) => { acc[p.id!] = true + return acc }, {} as RowSelectionState) ) @@ -185,7 +186,7 @@ export const EditCategoryProductsForm = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const { t } = useTranslation() diff --git a/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx b/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx index 50c21c54..f25ce725 100644 --- a/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx +++ b/src/routes/collections/collection-add-products/components/add-products-to-collection-form/add-products-to-collection-form.tsx @@ -1,5 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { HttpTypes } from "@medusajs/types" +import type { HttpTypes } from "@medusajs/types" import { Button, Checkbox, Hint, Tooltip, toast } from "@medusajs/ui" import { keepPreviousData } from "@tanstack/react-query" import { @@ -23,6 +23,7 @@ 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 { ExtendedAdminProduct } from "@custom-types/index.ts" type AddProductsToCollectionFormProps = { collection: HttpTypes.AdminCollection @@ -194,7 +195,7 @@ export const AddProductsToCollectionForm = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const { t } = useTranslation() diff --git a/src/routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx b/src/routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx index 0cc72319..353f6566 100644 --- a/src/routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx +++ b/src/routes/collections/collection-detail/components/collection-product-section/collection-product-section.tsx @@ -13,6 +13,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 { ExtendedAdminProduct } from "@custom-types/product" type CollectionProductSectionProps = { collection: HttpTypes.AdminCollection @@ -215,7 +216,7 @@ const ProductActions = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const columns = useProductTableColumns() diff --git a/src/routes/inventory/inventory-detail/breadcrumb.tsx b/src/routes/inventory/inventory-detail/breadcrumb.tsx index 9e9f7a30..7788cf3a 100644 --- a/src/routes/inventory/inventory-detail/breadcrumb.tsx +++ b/src/routes/inventory/inventory-detail/breadcrumb.tsx @@ -1,11 +1,11 @@ -import { HttpTypes } from "@medusajs/types" import { UIMatch } from "react-router-dom" import { useInventoryItem } from "../../../hooks/api" import { INVENTORY_DETAIL_FIELDS } from "./constants" +import type { ExtendedAdminInventoryItemResponse } from "@custom-types/inventory" type InventoryDetailBreadcrumbProps = - UIMatch + UIMatch export const InventoryDetailBreadcrumb = ( props: InventoryDetailBreadcrumbProps diff --git a/src/routes/inventory/inventory-detail/components/location-levels-table/location-list-table.tsx b/src/routes/inventory/inventory-detail/components/location-levels-table/location-list-table.tsx index 30b2e509..33c0b81a 100644 --- a/src/routes/inventory/inventory-detail/components/location-levels-table/location-list-table.tsx +++ b/src/routes/inventory/inventory-detail/components/location-levels-table/location-list-table.tsx @@ -1,13 +1,12 @@ +import { useInventoryItemLevels } from "@hooks/api" +import { useLocationListTableColumns } from "./use-location-list-table-columns" +import { useLocationLevelTableQuery } from "./use-location-list-table-query" import type { ExtendedInventoryItemLevel } from "@custom-types/inventory"; import { _DataTable } from "@components/table/data-table"; -import { useInventoryItemLevels } from "@hooks/api"; import { useDataTable } from "@hooks/use-data-table"; -import { useLocationListTableColumns } from "./use-location-list-table-columns"; -import { useLocationLevelTableQuery } from "./use-location-list-table-query"; - const PAGE_SIZE = 20; const PREFIX = "invlvl"; diff --git a/src/routes/inventory/inventory-detail/components/location-levels-table/use-location-list-table-columns.tsx b/src/routes/inventory/inventory-detail/components/location-levels-table/use-location-list-table-columns.tsx index 6323decc..4f5ac8ad 100644 --- a/src/routes/inventory/inventory-detail/components/location-levels-table/use-location-list-table-columns.tsx +++ b/src/routes/inventory/inventory-detail/components/location-levels-table/use-location-list-table-columns.tsx @@ -68,6 +68,7 @@ export const useLocationListTableColumns = () => { return useMemo( () => [ columnHelper.display({ + id: "location", header: t("fields.location"), cell: ({ row }) => { @@ -139,6 +140,7 @@ export const useLocationListTableColumns = () => { columnHelper.action({ actions: (ctx) => { const level = ctx.row.original; + return [ [ { diff --git a/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx b/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx index 19bcf093..6d86c156 100644 --- a/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx +++ b/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx @@ -21,6 +21,7 @@ import { AllocateItemsSchema } from "./constants" import { OrderAllocateItemsItem } from "./order-allocate-items-item" import { checkInventoryKit } from "./utils" import { useDocumentDirection } from "../../../../../hooks/use-document-direction" +import { getErrorMessage } from "@utils/error-helper" type OrderAllocateItemsFormProps = { order: AdminOrder @@ -116,13 +117,11 @@ export function OrderAllocateItemsForm({ order }: OrderAllocateItemsFormProps) { description: t("orders.allocateItems.toast.error", { items: failedItems, }), - dismissLabel: t("actions.close"), }) } } catch (e) { toast.error(t("general.error"), { - description: e.message, - dismissLabel: t("actions.close"), + description: getErrorMessage(e), }) } }) diff --git a/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/utils.ts b/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/utils.ts index 8423e151..5bcad142 100644 --- a/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/utils.ts +++ b/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/utils.ts @@ -1,17 +1,9 @@ -import { - AdminProductVariant, - AdminProductVariantInventoryItemLink, - OrderLineItemDTO, -} from "@medusajs/types" +import type { ExtendedAdminProductVariant } from "@custom-types/product" +import type { OrderLineItemDTO } from "@medusajs/types" -/** - * Check if the line item has inventory kit. - */ export function checkInventoryKit( item: OrderLineItemDTO & { - variant?: AdminProductVariant & { - inventory_items: AdminProductVariantInventoryItemLink[] - } + variant?: ExtendedAdminProductVariant } ) { const variant = item.variant @@ -21,8 +13,8 @@ export function checkInventoryKit( } return ( - (!!variant.inventory_items.length && variant.inventory_items.length > 1) || - (variant.inventory_items.length === 1 && + (!!variant.inventory_items?.length && variant.inventory_items?.length > 1) || + (variant.inventory_items?.length === 1 && variant.inventory_items[0].required_quantity! > 1) ) } diff --git a/src/routes/orders/order-create-claim/claim-create.tsx b/src/routes/orders/order-create-claim/claim-create.tsx index 16a66942..6e9d4ff5 100644 --- a/src/routes/orders/order-create-claim/claim-create.tsx +++ b/src/routes/orders/order-create-claim/claim-create.tsx @@ -9,6 +9,7 @@ import { useOrder, useOrderPreview } from "../../../hooks/api/orders" import { useReturn } from "../../../hooks/api/returns" import { DEFAULT_FIELDS } from "../order-detail/constants" import { ClaimCreateForm } from "./components/claim-create-form" +import { getErrorMessage } from "@utils/error-helper" let IS_REQUEST_RUNNING = false @@ -22,13 +23,13 @@ export const ClaimCreate = () => { }) const { order: preview } = useOrderPreview(id!) - const [activeClaimId, setActiveClaimId] = useState() + const [activeClaimId, setActiveClaimId] = useState() const { mutateAsync: createClaim } = useCreateClaim(order.id) - const { claim } = useClaim(activeClaimId!, undefined, { + const { claim } = useClaim(activeClaimId ?? "", undefined, { enabled: !!activeClaimId, }) - const { return: orderReturn } = useReturn(claim?.return_id!, undefined, { + const { return: orderReturn } = useReturn(claim?.return_id ?? "", undefined, { enabled: !!claim?.return_id, }) @@ -59,7 +60,7 @@ export const ClaimCreate = () => { setActiveClaimId(createdClaim.id) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) navigate(`/orders/${preview.id}`, { replace: true }) } finally { IS_REQUEST_RUNNING = false diff --git a/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx b/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx index 6f603c75..c0aef131 100644 --- a/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx +++ b/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx @@ -1,11 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { PencilSquare } from "@medusajs/icons" -import { +import type { AdminClaim, AdminOrder, AdminOrderPreview, InventoryLevelDTO, } from "@medusajs/types" +import type { ExtendedAdminProductVariantListResponse } from "@custom-types/product" import { Alert, Button, @@ -206,13 +207,14 @@ export const ClaimCreateForm = ({ const inboundAction = i.actions?.find( (a) => a.action === "RETURN_ITEM" ) + const reasonId = inboundAction?.details?.reason_id return { item_id: i.id, variant_id: i.variant_id, quantity: i.detail.return_requested_quantity, note: inboundAction?.internal_note, - reason_id: inboundAction?.details?.reason_id as string | undefined, + reason_id: typeof reasonId === "string" ? reasonId : undefined, } }), outbound_items: outboundPreviewItems.map((i) => ({ @@ -317,12 +319,13 @@ export const ClaimCreateForm = ({ const returnItemAction = i.actions?.find( (a) => a.action === "RETURN_ITEM" ) + const reasonId = returnItemAction?.details?.reason_id update(ind, { ...inboundItems[ind], quantity: i.detail.return_requested_quantity, note: returnItemAction?.internal_note, - reason_id: returnItemAction?.details?.reason_id as string, + reason_id: typeof reasonId === "string" ? reasonId : undefined, }) } } else { @@ -405,7 +408,7 @@ export const ClaimCreateForm = ({ }) const onItemsSelected = async () => { - itemsToAdd.length && + if (itemsToAdd.length) { (await addInboundItem( { items: itemsToAdd.map((id) => ({ @@ -419,6 +422,7 @@ export const ClaimCreateForm = ({ }, } )) + } for (const itemToRemove of itemsToRemove) { const actionId = previewItems @@ -525,14 +529,12 @@ export const ClaimCreateForm = ({ const variantIds = inboundItems .map((item) => item?.variant_id) - .filter(Boolean) + .filter((id): id is string => Boolean(id)) - const variants = ( - await sdk.admin.productVariant.list({ - id: variantIds, - fields: "*inventory.location_levels", - }) - ).variants + const { variants } = (await sdk.admin.productVariant.list({ + id: variantIds, + fields: "*inventory.location_levels", + })) as ExtendedAdminProductVariantListResponse variants.forEach((variant) => { // TODO: fix this for inventory kits diff --git a/src/routes/orders/order-create-claim/components/claim-create-form/claim-inbound-item.tsx b/src/routes/orders/order-create-claim/components/claim-create-form/claim-inbound-item.tsx index 75f31f20..a801c0f1 100644 --- a/src/routes/orders/order-create-claim/components/claim-create-form/claim-inbound-item.tsx +++ b/src/routes/orders/order-create-claim/components/claim-create-form/claim-inbound-item.tsx @@ -107,18 +107,18 @@ function ClaimInboundItem({ groups={[ { actions: [ - !showReturnReason && { + ...(!showReturnReason ? [{ label: t("actions.addReason"), onClick: () => form.setValue(`inbound_items.${index}.reason_id`, ""), icon: , - }, - !showNote && { + }] : []), + ...(!showNote? [{ label: t("actions.addNote"), onClick: () => form.setValue(`inbound_items.${index}.note`, ""), icon: , - }, + }] : []), { label: t("actions.remove"), onClick: onRemove, @@ -146,7 +146,7 @@ function ClaimInboundItem({ { + render={({ field: { value, onChange, ...field } }) => { return ( @@ -201,7 +201,7 @@ function ClaimInboundItem({ { + render={({ field: { ...field } }) => { return ( diff --git a/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx b/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx index 38187c39..0ae9c358 100644 --- a/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx +++ b/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx @@ -32,6 +32,7 @@ import { ItemPlaceholder } from "./item-placeholder" import { CreateClaimSchemaType } from "./schema" import { useOrderShippingOptions } from "../../../../../hooks/api/orders" import { getFormattedShippingOptionLocationName } from "../../../../../lib/shipping-options" +import { ExtendedAdminProductVariantListResponse } from "@custom-types/product" type ClaimOutboundSectionProps = { order: AdminOrder @@ -162,7 +163,7 @@ export const ClaimOutboundSection = ({ const showOutboundItemsPlaceholder = !outboundItems.length const onItemsSelected = async () => { - itemsToAdd.length && + if (itemsToAdd.length) { (await addOutboundItem( { items: itemsToAdd.map((variantId) => ({ @@ -176,6 +177,7 @@ export const ClaimOutboundSection = ({ }, } )) + } for (const itemToRemove of itemsToRemove) { const action = previewOutboundItems @@ -202,7 +204,7 @@ export const ClaimOutboundSection = ({ (a) => a.action === "SHIPPING_ADD" && !a.return_id ) - return action && !!!action?.return_id + return action && !!action?.return_id }) const promises = outboundShippingMethods @@ -238,6 +240,10 @@ export const ClaimOutboundSection = ({ const allItemsHaveLocation = outboundItems .map((i) => { + if (!i.variant_id) { + return true + } + const item = variantItemMap.get(i.variant_id) if (!item?.variant_id || !item?.variant) { return true @@ -267,14 +273,13 @@ export const ClaimOutboundSection = ({ const variantIds = outboundItems .map((item) => item?.variant_id) - .filter(Boolean) + .filter((id): id is string => Boolean(id)) - const variants = ( - await sdk.admin.productVariant.list({ - id: variantIds, - fields: "*inventory.location_levels", - }) - ).variants + + const { variants } = (await sdk.admin.productVariant.list({ + id: variantIds, + fields: "*inventory.location_levels", + })) as ExtendedAdminProductVariantListResponse variants.forEach((variant) => { ret[variant.id] = variant.inventory?.[0]?.location_levels || [] @@ -303,10 +308,10 @@ export const ClaimOutboundSection = ({ i.variant_id)} + selectedItems={outboundItems.map((i) => i.variant_id).filter((id): id is string => Boolean(id))} currencyCode={order.currency_code} onSelectionChange={(finalSelection) => { - const alreadySelected = outboundItems.map((i) => i.variant_id) + const alreadySelected = outboundItems.map((i) => i.variant_id).filter((id): id is string => Boolean(id)) itemsToAdd = finalSelection.filter( (selection) => !alreadySelected.includes(selection) @@ -345,11 +350,20 @@ export const ClaimOutboundSection = ({ {showOutboundItemsPlaceholder && } {outboundItems.map( - (item, index) => - variantOutboundMap.get(item.variant_id) && ( + (item, index) => { + if (!item.variant_id) { + return null + } + + const previewItem = variantOutboundMap.get(item.variant_id) + if (!previewItem) { + return null + } + + return ( { @@ -384,6 +398,7 @@ export const ClaimOutboundSection = ({ index={index} /> ) + } )} {!showOutboundItemsPlaceholder && (
diff --git a/src/routes/orders/order-create-claim/components/claim-create-form/schema.ts b/src/routes/orders/order-create-claim/components/claim-create-form/schema.ts index 0cc035bf..0cc64350 100644 --- a/src/routes/orders/order-create-claim/components/claim-create-form/schema.ts +++ b/src/routes/orders/order-create-claim/components/claim-create-form/schema.ts @@ -4,6 +4,7 @@ export const ClaimCreateSchema = z.object({ inbound_items: z.array( z.object({ item_id: z.string(), + variant_id: z.string().nullish(), quantity: z.number(), reason_id: z.string().nullish(), note: z.string().nullish(), @@ -11,7 +12,8 @@ export const ClaimCreateSchema = z.object({ ), outbound_items: z.array( z.object({ - item_id: z.string(), // TODO: variant id? + item_id: z.string(), + variant_id: z.string().nullish(), quantity: z.number(), }) ), diff --git a/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-create-form.tsx b/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-create-form.tsx index cc2602c5..067e6a5d 100644 --- a/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-create-form.tsx +++ b/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-create-form.tsx @@ -18,6 +18,7 @@ import { import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" import { OrderEditItemsSection } from "./order-edit-items-section" import { CreateOrderEditSchemaType, OrderEditCreateSchema } from "./schema" +import { getErrorMessage } from "@utils/error-helper" type ReturnCreateFormProps = { order: AdminOrder @@ -78,7 +79,7 @@ export const OrderEditCreateForm = ({ handleSuccess() } catch (e) { toast.error(t("general.error"), { - description: e.message, + description: getErrorMessage(e), }) } }) diff --git a/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-item.tsx b/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-item.tsx index de1a957a..0f6c70f1 100644 --- a/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-item.tsx +++ b/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-item.tsx @@ -13,6 +13,7 @@ import { useUpdateOrderEditAddedItem, useUpdateOrderEditOriginalItem, } from "../../../../../hooks/api/order-edits" +import { getErrorMessage } from "@utils/error-helper" type OrderEditItemProps = { item: AdminOrderLineItem @@ -68,7 +69,7 @@ function OrderEditItem({ item, currencyCode, orderId }: OrderEditItemProps) { await updateOriginalItem({ quantity, itemId: item.id }) } } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } @@ -85,7 +86,7 @@ function OrderEditItem({ item, currencyCode, orderId }: OrderEditItemProps) { }) } } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } @@ -99,7 +100,7 @@ function OrderEditItem({ item, currencyCode, orderId }: OrderEditItemProps) { await undoAction(updateItemAction.id) // Remove action that updated items quantity to fulfilled quantity which makes it "removed" } } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } diff --git a/src/routes/orders/order-create-edit/order-edit-create.tsx b/src/routes/orders/order-create-edit/order-edit-create.tsx index 9a973db1..ffbfee9a 100644 --- a/src/routes/orders/order-create-edit/order-edit-create.tsx +++ b/src/routes/orders/order-create-edit/order-edit-create.tsx @@ -8,6 +8,7 @@ import { useOrder, useOrderPreview } from "../../../hooks/api/orders" import { DEFAULT_FIELDS } from "../order-detail/constants" import { OrderEditCreateForm } from "./components/order-edit-create-form" import { useCreateOrderEdit } from "../../../hooks/api/order-edits" +import { getErrorMessage } from "@utils/error-helper" let IS_REQUEST_RUNNING = false @@ -45,7 +46,7 @@ export const OrderEditCreate = () => { order_id: preview.id, }) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) navigate(`/orders/${preview.id}`, { replace: true }) } finally { IS_REQUEST_RUNNING = false diff --git a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-create-form.tsx b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-create-form.tsx index 2ff97fd6..b4d1a73b 100644 --- a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-create-form.tsx +++ b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-create-form.tsx @@ -1,6 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { PencilSquare } from "@medusajs/icons" -import { AdminExchange, AdminOrder, AdminOrderPreview } from "@medusajs/types" +import type { AdminOrder, AdminOrderPreview } from "@medusajs/types" import { Button, CurrencyInput, @@ -25,6 +25,7 @@ import { CreateExchangeSchemaType, ExchangeCreateSchema } from "./schema" import { AdminReturn } from "@medusajs/types" import { KeyboundForm } from "../../../../../components/utilities/keybound-form/keybound-form.tsx" +import { getErrorMessage } from "@utils/error-helper" import { useCancelExchangeRequest, useExchangeConfirmRequest, @@ -34,10 +35,11 @@ import { import { currencies } from "../../../../../lib/data/currencies" import { ExchangeInboundSection } from "./exchange-inbound-section.tsx" import { ExchangeOutboundSection } from "./exchange-outbound-section" +import type { ExtendedAdminExchange } from "@custom-types/exchanges/common.ts" type ReturnCreateFormProps = { order: AdminOrder - exchange: AdminExchange + exchange: ExtendedAdminExchange preview: AdminOrderPreview orderReturn?: AdminReturn } @@ -139,16 +141,18 @@ export const ExchangeCreateForm = ({ const inboundAction = i.actions?.find( (a) => a.action === "RETURN_ITEM" ) + const reasonId = inboundAction?.details?.reason_id return { item_id: i.id, - variant_id: i.variant_id, quantity: i.detail.return_requested_quantity, note: inboundAction?.internal_note, - reason_id: inboundAction?.details?.reason_id as string | undefined, + reason_id: typeof reasonId === "string" ? reasonId : undefined, } }), - outbound_items: outboundPreviewItems.map((i) => ({ + outbound_items: outboundPreviewItems + .filter((i): i is typeof i & { variant_id: string } => !!i.variant_id) + .map((i) => ({ item_id: i.id, variant_id: i.variant_id, quantity: i.detail.quantity, @@ -177,14 +181,23 @@ export const ExchangeCreateForm = ({ }) useEffect(() => { + console.log(customInboundShippingAmount, 'custom inbound shipping amount') + console.log(customOutboundShippingAmount, 'custom OUTBOUND shipping amount') + if (inboundShipping) { - setCustomInboundShippingAmount(inboundShipping.total) + setCustomInboundShippingAmount({ + value: inboundShipping.total.toString(), + float: inboundShipping.total, + }) } }, [inboundShipping]) useEffect(() => { if (outboundShipping) { - setCustomOutboundShippingAmount(outboundShipping.total) + setCustomOutboundShippingAmount({ + value: outboundShipping.total.toString(), + float: outboundShipping.total, + }) } }, [outboundShipping]) @@ -212,7 +225,7 @@ export const ExchangeCreateForm = ({ handleSuccess() } catch (e) { toast.error(t("general.error"), { - description: e.message, + description: getErrorMessage(e), }) } }) diff --git a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-item.tsx b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-item.tsx index e9ae7f44..364dec0c 100644 --- a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-item.tsx +++ b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-item.tsx @@ -107,24 +107,24 @@ function ExchangeInboundItem({ groups={[ { actions: [ - !showReturnReason && { + ...(!showReturnReason ? [{ label: t("actions.addReason"), onClick: () => form.setValue(`inbound_items.${index}.reason_id`, ""), icon: , - }, - !showNote && { + }] : []), + ...(!showNote ? [{ label: t("actions.addNote"), onClick: () => form.setValue(`inbound_items.${index}.note`, ""), icon: , - }, + }] : []), { label: t("actions.remove"), onClick: onRemove, icon: , }, - ].filter(Boolean), + ], }, ]} /> @@ -146,7 +146,7 @@ function ExchangeInboundItem({ { + render={({ field: { value, onChange, ...field } }) => { return ( @@ -201,7 +201,7 @@ function ExchangeInboundItem({ { + render={({ field: { ...field } }) => { return ( diff --git a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-section.tsx b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-section.tsx index ad834598..c7ab94f9 100644 --- a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-section.tsx +++ b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-inbound-section.tsx @@ -33,6 +33,7 @@ import { ItemPlaceholder } from "../../../order-create-claim/components/claim-cr import { AddExchangeInboundItemsTable } from "../add-exchange-inbound-items-table" import { ExchangeInboundItem } from "./exchange-inbound-item" import { CreateExchangeSchemaType } from "./schema" +import { ExtendedAdminProductVariantListResponse } from "@custom-types/product" type ExchangeInboundSectionProps = { order: AdminOrder @@ -67,7 +68,7 @@ export const ExchangeInboundSection = ({ */ const { mutateAsync: updateReturn } = useUpdateReturn( preview?.order_change?.return_id!, - order.id + order.id, ) const { mutateAsync: addInboundShipping } = useAddExchangeInboundShipping( @@ -165,12 +166,13 @@ export const ExchangeInboundSection = ({ const returnItemAction = i.actions?.find( (a) => a.action === "RETURN_ITEM" ) + const reasonId = returnItemAction?.details?.reason_id update(ind, { ...inboundItems[ind], quantity: i.detail.return_requested_quantity, note: returnItemAction?.internal_note, - reason_id: returnItemAction?.details?.reason_id as string, + reason_id: typeof reasonId === "string" ? reasonId : undefined, }) } } else { @@ -210,8 +212,8 @@ export const ExchangeInboundSection = ({ const showInboundItemsPlaceholder = !inboundItems.length const onItemsSelected = async () => { - itemsToAdd.length && - (await addInboundItem( + if (itemsToAdd.length) { + await addInboundItem( { items: itemsToAdd.map((id) => ({ id, @@ -223,7 +225,8 @@ export const ExchangeInboundSection = ({ toast.error(error.message) }, } - )) + ) + } for (const itemToRemove of itemsToRemove) { const actionId = previewInboundItems @@ -314,14 +317,12 @@ export const ExchangeInboundSection = ({ const variantIds = inboundItems .map((item) => item?.variant_id) - .filter(Boolean) + .filter((id): id is string => Boolean(id)) - const variants = ( - await sdk.admin.productVariant.list({ - id: variantIds, - fields: "*inventory.location_levels", - }) - ).variants + const { variants } = (await sdk.admin.productVariant.list({ + id: variantIds, + fields: "*inventory.location_levels", + })) as ExtendedAdminProductVariantListResponse variants.forEach((variant) => { ret[variant.id] = variant.inventory?.[0]?.location_levels || [] diff --git a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-section.tsx b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-section.tsx index 30bce488..df84f9c4 100644 --- a/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-section.tsx +++ b/src/routes/orders/order-create-exchange/components/exchange-create-form/exchange-outbound-section.tsx @@ -31,6 +31,7 @@ import { ExchangeOutboundItem } from "./exchange-outbound-item" import { useOrderShippingOptions } from "../../../../../hooks/api/orders" import { CreateExchangeSchemaType } from "./schema" import { getFormattedShippingOptionLocationName } from "../../../../../lib/shipping-options" +import type { ExtendedAdminProductVariantListResponse } from "@custom-types/product" type ExchangeOutboundSectionProps = { order: AdminOrder @@ -127,6 +128,10 @@ export const ExchangeOutboundSection = ({ const existingItemsMap: Record = {} previewOutboundItems.forEach((i) => { + if (!i.variant_id) { + return + } + const ind = outboundItems.findIndex((field) => field.item_id === i.id) existingItemsMap[i.id] = true @@ -161,8 +166,8 @@ export const ExchangeOutboundSection = ({ const showOutboundItemsPlaceholder = !outboundItems.length const onItemsSelected = async () => { - itemsToAdd.length && - (await addOutboundItem( + if (itemsToAdd.length) { + await addOutboundItem( { items: itemsToAdd.map((variantId) => ({ variant_id: variantId, @@ -174,7 +179,8 @@ export const ExchangeOutboundSection = ({ toast.error(error.message) }, } - )) + ) + } for (const itemToRemove of itemsToRemove) { const action = previewOutboundItems @@ -277,12 +283,10 @@ export const ExchangeOutboundSection = ({ .map((item) => item?.variant_id) .filter(Boolean) - const variants = ( - await sdk.admin.productVariant.list({ - id: variantIds, - fields: "*inventory.location_levels", - }) - ).variants + const { variants } = (await sdk.admin.productVariant.list({ + id: variantIds, + fields: "*inventory.location_levels", + })) as ExtendedAdminProductVariantListResponse variants.forEach((variant) => { ret[variant.id] = variant.inventory?.[0]?.location_levels || [] diff --git a/src/routes/orders/order-create-exchange/components/exchange-create-form/schema.ts b/src/routes/orders/order-create-exchange/components/exchange-create-form/schema.ts index 0b0ec703..1dc60906 100644 --- a/src/routes/orders/order-create-exchange/components/exchange-create-form/schema.ts +++ b/src/routes/orders/order-create-exchange/components/exchange-create-form/schema.ts @@ -4,6 +4,7 @@ export const ExchangeCreateSchema = z.object({ inbound_items: z.array( z.object({ item_id: z.string(), + variant_id: z.string().nullish(), quantity: z.number(), reason_id: z.string().nullish(), note: z.string().nullish(), @@ -13,6 +14,7 @@ export const ExchangeCreateSchema = z.object({ z.object({ item_id: z.string(), quantity: z.number(), + variant_id: z.string(), }) ), location_id: z.string().optional(), diff --git a/src/routes/orders/order-create-exchange/exchange-create.tsx b/src/routes/orders/order-create-exchange/exchange-create.tsx index 0ff0e637..fe019977 100644 --- a/src/routes/orders/order-create-exchange/exchange-create.tsx +++ b/src/routes/orders/order-create-exchange/exchange-create.tsx @@ -3,12 +3,13 @@ import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { useNavigate, useParams } from "react-router-dom" -import { RouteFocusModal } from "../../../components/modals" -import { useCreateExchange, useExchange } from "../../../hooks/api/exchanges" -import { useOrder, useOrderPreview } from "../../../hooks/api/orders" -import { useReturn } from "../../../hooks/api/returns" +import { RouteFocusModal } from "@components/modals" +import { useCreateExchange, useExchange } from "@hooks/api/exchanges" +import { useOrder, useOrderPreview } from "@hooks/api/orders" +import { useReturn } from "@hooks/api/returns" import { DEFAULT_FIELDS } from "../order-detail/constants" import { ExchangeCreateForm } from "./components/exchange-create-form" +import { getErrorMessage } from "@utils/error-helper" let IS_REQUEST_RUNNING = false @@ -22,14 +23,14 @@ export const ExchangeCreate = () => { }) const { order: preview } = useOrderPreview(id!) - const [activeExchangeId, setActiveExchangeId] = useState() + const [activeExchangeId, setActiveExchangeId] = useState() const { mutateAsync: createExchange } = useCreateExchange(order.id) - const { exchange } = useExchange(activeExchangeId!, undefined, { + const { exchange } = useExchange(activeExchangeId || "", undefined, { enabled: !!activeExchangeId, }) - const { return: orderReturn } = useReturn(exchange?.return_id!, undefined, { + const { return: orderReturn } = useReturn(exchange?.return_id || "", undefined, { enabled: !!exchange?.return_id, }) @@ -56,10 +57,9 @@ export const ExchangeCreate = () => { const { exchange: createdExchange } = await createExchange({ order_id: preview.id, }) - setActiveExchangeId(createdExchange.id) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) navigate(`/orders/${preview.id}`, { replace: true }) } finally { IS_REQUEST_RUNNING = false diff --git a/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx b/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx index ff75690a..ed508345 100644 --- a/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx +++ b/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx @@ -23,6 +23,7 @@ import { useShippingOptions, } from "../../../../../hooks/api" import { getReservationsLimitCount } from "../../../../../lib/orders" +import { getErrorMessage } from "@utils/error-helper" import { sdk } from "../../../../../lib/client" import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { Combobox } from "../../../../../components/inputs/combobox" @@ -155,7 +156,7 @@ export function OrderCreateFulfillmentForm({ toast.success(t("orders.fulfillment.toast.created")) handleSuccess(`/orders/${order.id}`) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } }) diff --git a/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx b/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx index f7b0ebad..8cfdf798 100644 --- a/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx +++ b/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx @@ -32,13 +32,13 @@ export function OrderCreateFulfillmentItem({ const { t } = useTranslation() const { variant } = useProductVariant( - item.product_id, - item.variant_id, + item.product_id ?? "", + item.variant_id ?? "", { fields: "*inventory,*inventory.location_levels,*inventory_items", }, { - enabled: !!item.variant, + enabled: !!item.variant_id && !!item.product_id, } ) diff --git a/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx b/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx index 7262fb5e..98b48ae0 100644 --- a/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx +++ b/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx @@ -1,5 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { HttpTypes } from "@medusajs/types" +import type { HttpTypes } from "@medusajs/types" import { Button, CurrencyInput, @@ -12,6 +12,7 @@ import { useEffect, useMemo, useState } from "react" import { formatValue } from "react-currency-input-field" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" +import i18n from "i18next" import { useSearchParams } from "react-router-dom" import * as zod from "zod" import { Form } from "../../../../../components/common/form" @@ -23,17 +24,27 @@ import { formatCurrency } from "../../../../../lib/format-currency" import { getLocaleAmount } from "../../../../../lib/money-amount-helpers" import { getPaymentsFromOrder } from "../../../../../lib/orders" import { useDocumentDirection } from "../../../../../hooks/use-document-direction" -import { formatProvider } from "../../../../../lib/format-provider.ts" +import { formatProvider } from "../../../../../lib/format-provider" type CreateRefundFormProps = { order: HttpTypes.AdminOrder } +// If amount.float is sent as 0 or undefined, then full refund is made. Making the amount field non nullable to avoid user confusion. const CreateRefundSchema = zod.object({ - amount: zod.object({ - value: zod.string().or(zod.number()), - float: zod.number().or(zod.null()), - }), + amount: zod + .object({ + value: zod.string(), + float: zod.number(), + }) + .refine( + (data) => { + return data.value && data.value.trim() !== "" && data.float > 0 + }, + { + message: i18n.t('orders.payment.refundAmountWarning'), + } + ), note: zod.string().optional(), refund_reason_id: zod.string().optional(), }) @@ -51,7 +62,6 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => { const payments = getPaymentsFromOrder(order) const payment = payments.find((p) => p.id === paymentId) const paymentAmount = payment?.amount || 0 - const currency = useMemo( () => currencies[order.currency_code.toUpperCase()], [order.currency_code] @@ -85,22 +95,43 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => { }) }, [payment?.id || ""]) - const { mutateAsync, isPending } = useRefundPayment(order.id, payment?.id!) + const { mutateAsync, isPending } = useRefundPayment( + order.id, + payment?.id ?? "", + ) const handleSubmit = form.handleSubmit(async (data) => { + if (!payment || !paymentId) { + toast.error(t("orders.payment.selectPaymentToRefund")) + + return + } + + await mutateAsync( { - amount: data.amount.float!, + amount: data.amount.float, note: data.note, refund_reason_id: data.refund_reason_id, }, { onSuccess: () => { + if (!payment?.currency_code) { + toast.success( + t("orders.payment.refundPaymentSuccess", { + amount: data.amount.float + }) + ) + handleSuccess() + + return + } + toast.success( t("orders.payment.refundPaymentSuccess", { amount: formatCurrency( - data.amount.float!, - payment?.currency_code! + data.amount.float, + payment.currency_code ), }) ) @@ -181,7 +212,7 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => { payment!.currency_code )} -  -  + - (#{payment!.id.substring(23)})
)} @@ -191,7 +222,7 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => { name="amount" rules={{ required: true, - min: 0, + min: 0.01, max: paymentAmount, }} render={({ field: { onChange, ...field } }) => { @@ -202,22 +233,27 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => { - onChange({ + onValueChange={(_value, _name, values) => { + const newValue = { value: values?.value ?? "", - float: values?.float ?? null, - }) - } - autoFocus + float: values?.float ?? 0, + } + onChange(newValue) + }} + onBlur={() => { + field.onBlur() + form.trigger("amount") + }} /> @@ -263,7 +299,7 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => { { return ( diff --git a/src/routes/orders/order-create-return/components/add-return-items-table/add-return-items-table.tsx b/src/routes/orders/order-create-return/components/add-return-items-table/add-return-items-table.tsx index c081f397..9b8b9a3d 100644 --- a/src/routes/orders/order-create-return/components/add-return-items-table/add-return-items-table.tsx +++ b/src/routes/orders/order-create-return/components/add-return-items-table/add-return-items-table.tsx @@ -37,6 +37,7 @@ export const AddReturnItemsTable = ({ const [rowSelection, setRowSelection] = useState( selectedItems.reduce((acc, id) => { acc[id] = true + return acc }, {} as RowSelectionState) ) diff --git a/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx b/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx index 1457660b..b7c39650 100644 --- a/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx +++ b/src/routes/orders/order-create-return/components/return-create-form/return-create-form.tsx @@ -1,11 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { PencilSquare } from "@medusajs/icons" -import { +import type { AdminOrder, AdminOrderPreview, AdminReturn, InventoryLevelDTO, } from "@medusajs/types" +import type { ExtendedAdminProductVariantListResponse } from "@custom-types/product" import { Alert, Button, @@ -27,6 +28,7 @@ import { useRouteModal, useStackedModal, } from "../../../../../components/modals" +import { getErrorMessage } from "@utils/error-helper" import { Form } from "../../../../../components/common/form" import { Combobox } from "../../../../../components/inputs/combobox" @@ -175,15 +177,19 @@ export const ReturnCreateForm = ({ ) return Promise.resolve({ - items: previewItems.map((i) => ({ - item_id: i.id, - quantity: i.detail.return_requested_quantity, - note: i.actions?.find((a) => a.action === "RETURN_ITEM") - ?.internal_note, - reason_id: i.actions?.find((a) => a.action === "RETURN_ITEM")?.details - ?.reason_id, - })), - option_id: method ? method.shipping_option_id : "", + items: previewItems.map((i) => { + const returnAction = i.actions?.find((a) => a.action === "RETURN_ITEM") + const reasonId = returnAction?.details?.reason_id + + return { + item_id: i.id, + variant_id: i.variant_id, + quantity: i.detail.return_requested_quantity, + note: returnAction?.internal_note, + reason_id: typeof reasonId === "string" ? reasonId : undefined, + } + }), + option_id: method ? method.shipping_option_id : null, location_id: activeReturn?.location_id, send_notification: false, }) @@ -221,12 +227,13 @@ export const ReturnCreateForm = ({ const returnItemAction = i.actions?.find( (a) => a.action === "RETURN_ITEM" ) + const reasonId = returnItemAction?.details?.reason_id update(ind, { ...items[ind], quantity: i.detail.return_requested_quantity, note: returnItemAction?.internal_note, - reason_id: returnItemAction?.details?.reason_id, + reason_id: typeof reasonId === "string" ? reasonId : undefined, }) } } else { @@ -249,7 +256,7 @@ export const ReturnCreateForm = ({ if (method) { form.setValue("option_id", method.shipping_option_id!) } else { - form.setValue("option_id", "") + form.setValue("option_id", null) } }, [preview.shipping_methods]) @@ -277,8 +284,7 @@ export const ReturnCreateForm = ({ handleSuccess() } catch (e) { toast.error(t("general.error"), { - description: e.message, - dismissLabel: t("actions.close"), + description: getErrorMessage(e), }) } }) @@ -294,17 +300,18 @@ export const ReturnCreateForm = ({ setIsOpen("items", false) } - const onLocationChange = async (selectedLocationId: string) => { + const onLocationChange = async (selectedLocationId?: string | null) => { await updateReturnRequest({ location_id: selectedLocationId }) } const onShippingOptionChange = async ( selectedOptionId: string | undefined ) => { - const promises = preview.shipping_methods + const actionIds = preview.shipping_methods .map((s) => s.actions?.find((a) => a.action === "SHIPPING_ADD")?.id) - .filter(Boolean) - .map(deleteReturnShipping) + .filter((id): id is string => Boolean(id)) + + const promises = actionIds.map((id) => deleteReturnShipping(id)) await Promise.all(promises) @@ -315,7 +322,7 @@ export const ReturnCreateForm = ({ useEffect(() => { if (isShippingPriceEdit) { - document.getElementById("js-shipping-input").focus() + document?.getElementById("js-shipping-input")?.focus() } }, [isShippingPriceEdit]) @@ -356,33 +363,22 @@ export const ReturnCreateForm = ({ return ret } - ( - await Promise.all( - items.map(async (_i) => { - const item = itemsMap.get(_i.item_id) - - if (!item.variant_id) { - return undefined - } - return await sdk.admin.product.retrieveVariant( - item.product_id, - item.variant_id, - { fields: "*inventory,*inventory.location_levels" } - ) - }) - ) - ) - .filter((it) => it?.variant) - .forEach((item) => { - const { variant } = item - const levels = variant.inventory[0]?.location_levels + const variantIds = items + .map((item) => item?.variant_id) + .filter((id): id is string => Boolean(id)) - if (!levels) { - return - } + if (!variantIds.length) { + return ret + } - ret[variant.id] = levels - }) + const { variants } = (await sdk.admin.productVariant.list({ + id: variantIds, + fields: "*inventory.location_levels", + })) as ExtendedAdminProductVariantListResponse + + variants.forEach((variant) => { + ret[variant.id] = variant.inventory?.[0]?.location_levels || [] + }) return ret } @@ -472,11 +468,19 @@ export const ReturnCreateForm = ({ )} {items .filter((item) => !!previewItemsMap.get(item.item_id)) - .map((item, index) => ( + .map((item, index) => { + const orderItem = itemsMap.get(item.item_id) + const previewItem = previewItemsMap.get(item.item_id) + + if (!orderItem || !previewItem) { + return null + } + + return ( { @@ -513,7 +517,8 @@ export const ReturnCreateForm = ({ }} index={index} /> - ))} + ) + })} {!showPlaceholder && (
{/* LOCATION*/} @@ -581,7 +586,7 @@ export const ReturnCreateForm = ({ { onChange(v) onShippingOptionChange(v) @@ -663,21 +668,24 @@ export const ReturnCreateForm = ({ id="js-shipping-input" onBlur={() => { let actionId + let shippingOptionId preview.shipping_methods.forEach((s) => { if (s.actions) { for (const a of s.actions) { if (a.action === "SHIPPING_ADD") { actionId = a.id + shippingOptionId = s.shipping_option_id } } } }) - if (actionId) { + if (actionId && shippingOptionId) { updateReturnShipping({ actionId, - custom_amount: customShippingAmount.float, + shipping_option_id: shippingOptionId, + custom_amount: customShippingAmount.float ?? undefined, }) } setIsShippingPriceEdit(false) diff --git a/src/routes/orders/order-create-return/components/return-create-form/return-item.tsx b/src/routes/orders/order-create-return/components/return-create-form/return-item.tsx index 420cbda5..1957bfde 100644 --- a/src/routes/orders/order-create-return/components/return-create-form/return-item.tsx +++ b/src/routes/orders/order-create-return/components/return-create-form/return-item.tsx @@ -109,17 +109,17 @@ function ReturnItem({ groups={[ { actions: [ - !showReturnReason && { + ...(!showReturnReason ? [{ label: t("actions.addReason"), onClick: () => form.setValue(`items.${index}.reason_id`, ""), icon: , - }, - !showNote && { + }] : []), + ...(!showNote ? [{ label: t("actions.addNote"), onClick: () => form.setValue(`items.${index}.note`, ""), icon: , - }, + }] : []), { label: t("actions.remove"), onClick: onRemove, diff --git a/src/routes/orders/order-create-return/components/return-create-form/schema.ts b/src/routes/orders/order-create-return/components/return-create-form/schema.ts index 709495ec..8d9c96c7 100644 --- a/src/routes/orders/order-create-return/components/return-create-form/schema.ts +++ b/src/routes/orders/order-create-return/components/return-create-form/schema.ts @@ -4,13 +4,14 @@ export const ReturnCreateSchema = z.object({ items: z.array( z.object({ item_id: z.string(), + variant_id: z.string().nullish(), quantity: z.number(), - reason_id: z.string().optional().nullable(), - note: z.string().optional().nullable(), + reason_id: z.string().nullish(), + note: z.string().nullish(), }) ), location_id: z.string().optional(), - option_id: z.string(), + option_id: z.string().nullish(), send_notification: z.boolean().optional(), // TODO: implement this receive_now: z.boolean().optional(), diff --git a/src/routes/orders/order-create-return/return-create.tsx b/src/routes/orders/order-create-return/return-create.tsx index 4b20e866..f47aa615 100644 --- a/src/routes/orders/order-create-return/return-create.tsx +++ b/src/routes/orders/order-create-return/return-create.tsx @@ -10,6 +10,7 @@ import { ReturnCreateForm } from "./components/return-create-form" import { useOrder, useOrderPreview } from "../../../hooks/api/orders" import { useInitiateReturn, useReturn } from "../../../hooks/api/returns" import { DEFAULT_FIELDS } from "../order-detail/constants" +import { getErrorMessage } from "@utils/error-helper" let IS_REQUEST_RUNNING = false @@ -25,11 +26,11 @@ export const ReturnCreate = () => { const { order: preview } = useOrderPreview(id!, undefined, {}) - const [activeReturnId, setActiveReturnId] = useState() + const [activeReturnId, setActiveReturnId] = useState() const { mutateAsync: initiateReturn } = useInitiateReturn(order.id) - const { return: activeReturn } = useReturn(activeReturnId, undefined, { + const { return: activeReturn } = useReturn(activeReturnId ?? "", undefined, { enabled: !!activeReturnId, }) @@ -57,7 +58,7 @@ export const ReturnCreate = () => { setActiveReturnId(orderReturn.id) } catch (e) { navigate(`/orders/${order.id}`, { replace: true }) - toast.error(e.message) + toast.error(getErrorMessage(e)) } finally { IS_REQUEST_RUNNING = false } diff --git a/src/routes/orders/order-detail/components/order-active-edit-section/order-active-edit-section.tsx b/src/routes/orders/order-detail/components/order-active-edit-section/order-active-edit-section.tsx index ac56d189..43a4119b 100644 --- a/src/routes/orders/order-detail/components/order-active-edit-section/order-active-edit-section.tsx +++ b/src/routes/orders/order-detail/components/order-active-edit-section/order-active-edit-section.tsx @@ -8,9 +8,10 @@ import { useConfirmOrderEdit, } from "../../../../../hooks/api/order-edits" import { useMemo } from "react" -import { HttpTypes } from "@medusajs/types" +import type { HttpTypes } from "@medusajs/types" import { Thumbnail } from "../../../../../components/common/thumbnail" import { useNavigate } from "react-router-dom" +import { getErrorMessage } from "@utils/error-helper" type OrderActiveEditSectionProps = { order: HttpTypes.AdminOrder @@ -101,7 +102,7 @@ export const OrderActiveEditSection = ({ toast.success(t("orders.edits.toast.confirmedSuccessfully")) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } @@ -111,7 +112,7 @@ export const OrderActiveEditSection = ({ toast.success(t("orders.edits.toast.canceledSuccessfully")) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } diff --git a/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx b/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx index 97bea76d..bbbfe855 100644 --- a/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx +++ b/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx @@ -1,6 +1,5 @@ import { AdminClaim, - AdminExchange, AdminOrderLineItem, AdminReturn, } from "@medusajs/types" @@ -8,11 +7,12 @@ import { Popover, Text } from "@medusajs/ui" import { useState } from "react" import { useTranslation } from "react-i18next" import { Thumbnail } from "../../../../../components/common/thumbnail" +import type { ExtendedAdminExchange } from "../../../../../types/exchanges" type ActivityItemsProps = { itemsToSend?: | AdminClaim["additional_items"] - | AdminExchange["additional_items"] + | ExtendedAdminExchange["additional_items"] itemsToReturn?: AdminReturn["items"] itemsMap?: Map title: string diff --git a/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx b/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx index 70427df3..b2c26b4e 100644 --- a/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx +++ b/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx @@ -1,11 +1,10 @@ import { Button, Text, Tooltip, clx, toast, usePrompt } from "@medusajs/ui" import { Collapsible as RadixCollapsible } from "radix-ui" -import { PropsWithChildren, ReactNode, useMemo, useState } from "react" +import { type PropsWithChildren, type ReactNode, useMemo, useState } from "react" -import { +import type { AdminClaim, - AdminExchange, AdminFulfillment, AdminOrder, AdminOrderChange, @@ -13,26 +12,27 @@ import { } from "@medusajs/types" import { useTranslation } from "react-i18next" -import { AdminOrderLineItem } from "@medusajs/types" -import { By } from "../../../../../components/common/user-link" +import type { AdminOrderLineItem } from "@medusajs/types" import { useCancelOrderTransfer, useCustomer, useOrderChanges, useOrderLineItems, -} from "../../../../../hooks/api" -import { useCancelClaim, useClaims } from "../../../../../hooks/api/claims" +} from "@hooks/api" +import { useCancelClaim, useClaims } from "@hooks/api/claims" import { useCancelExchange, useExchanges, -} from "../../../../../hooks/api/exchanges" -import { useCancelReturn, useReturns } from "../../../../../hooks/api/returns" -import { useDate } from "../../../../../hooks/use-date" -import { getFormattedAddress } from "../../../../../lib/addresses" -import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" -import { getPaymentsFromOrder } from "../../../../../lib/orders" +} from "@hooks/api/exchanges" +import { useCancelReturn, useReturns } from "@hooks/api/returns" +import { useDate } from "@hooks/use-date" +import { getFormattedAddress } from "@lib/addresses" +import { getStylizedAmount } from "@lib/money-amount-helpers" +import { getPaymentsFromOrder } from "@lib/orders" import ActivityItems from "./activity-items" import ChangeDetailsTooltip from "./change-details-tooltip" +import type { ExtendedAdminExchange } from "@custom-types/exchanges" +import { By } from "@components/common/user-link" type OrderTimelineProps = { order: AdminOrder @@ -107,10 +107,9 @@ type Activity = { title: string timestamp: string | Date children?: ReactNode - itemsToSend?: ( + itemsToSend?: | AdminClaim["additional_items"] - | AdminExchange["additional_items"] - )[] + | ExtendedAdminExchange["additional_items"] itemsToReturn?: AdminReturn["items"] itemsMap?: Map } @@ -174,7 +173,7 @@ const useActivityItems = (order: AdminOrder): Activity[] => { order_id: order.id, fields: "*additional_items", }) - + const payments = getPaymentsFromOrder(order) const notes = [] @@ -544,7 +543,7 @@ type OrderActivityItemProps = PropsWithChildren<{ isFirst?: boolean itemsToSend?: | AdminClaim["additional_items"] - | AdminExchange["additional_items"] + | ExtendedAdminExchange["additional_items"] itemsToReturn?: AdminReturn["items"] itemsMap?: Map }> @@ -890,7 +889,7 @@ const ExchangeBody = ({ exchange, exchangeReturn, }: { - exchange: AdminExchange + exchange: ExtendedAdminExchange exchangeReturn?: AdminReturn }) => { const prompt = usePrompt() diff --git a/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx b/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx index 82ebdddb..fac9a387 100644 --- a/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx +++ b/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx @@ -14,7 +14,6 @@ import { } from "@medusajs/icons" import { AdminClaim, - AdminExchange, AdminOrder, AdminOrderLineItem, AdminOrderPreview, @@ -43,6 +42,7 @@ import { ActionMenu } from "../../../../../components/common/action-menu" import DisplayId from "../../../../../components/common/display-id/display-id" import { Thumbnail } from "../../../../../components/common/thumbnail" import { useClaims } from "../../../../../hooks/api/claims" +import type { ExtendedAdminExchange } from "../../../../../types/exchanges" import { useExchanges } from "../../../../../hooks/api/exchanges" import { useOrderPreview } from "../../../../../hooks/api/orders" import { useMarkPaymentCollectionAsPaid } from "../../../../../hooks/api/payment-collections" @@ -392,7 +392,7 @@ const Item = ({ reservation?: AdminReservation returns: AdminReturn[] claims: AdminClaim[] - exchanges: AdminExchange[] + exchanges: ExtendedAdminExchange[] }) => { const { t } = useTranslation() @@ -1179,7 +1179,7 @@ const ExchangeBreakdown = ({ exchange, itemId, }: { - exchange: AdminExchange + exchange: ExtendedAdminExchange itemId: string }) => { const { t } = useTranslation() diff --git a/src/routes/orders/order-receive-return/components/order-receive-return-form/dismissed-quantity.tsx b/src/routes/orders/order-receive-return/components/order-receive-return-form/dismissed-quantity.tsx index 4151ac4e..aec709e6 100644 --- a/src/routes/orders/order-receive-return/components/order-receive-return-form/dismissed-quantity.tsx +++ b/src/routes/orders/order-receive-return/components/order-receive-return-form/dismissed-quantity.tsx @@ -1,25 +1,27 @@ import { useMemo, useState } from "react" import { HeartBroken } from "@medusajs/icons" -import { UseFormReturn } from "react-hook-form" +import type { UseFormReturn } from "react-hook-form" import { useTranslation } from "react-i18next" +import type { z } from "zod" -import { AdminOrderLineItem } from "@medusajs/types" import { Button, Input, Popover, toast } from "@medusajs/ui" -import { ReceiveReturnSchema } from "./constants" +import type { ReceiveReturnSchema } from "./constants" import { Form } from "../../../../../components/common/form" import { useAddDismissItems, useRemoveDismissItem, useUpdateDismissItem, } from "../../../../../hooks/api/returns" +import { getErrorMessage } from "@utils/error-helper" +import type { OrderLineItemWithActions } from "./order-receive-return-form" type DismissedQuantityProps = { returnId: string orderId: string index: number - item: AdminOrderLineItem - form: UseFormReturn + item: OrderLineItemWithActions + form: UseFormReturn> } function DismissedQuantity({ @@ -56,10 +58,16 @@ function DismissedQuantity({ (a) => a.action === "RECEIVE_DAMAGED_RETURN_ITEM" ) - return [receivedAction?.details.quantity, dismissedAction?.details.quantity] + const receivedQty = receivedAction?.details?.quantity + const dismissedQty = dismissedAction?.details?.quantity + + return [ + typeof receivedQty === "number" ? receivedQty : undefined, + typeof dismissedQty === "number" ? dismissedQty : undefined, + ] }, [item]) - const onDismissedQuantityChanged = async (value: number | null) => { + const onDismissedQuantityChanged = async (value: number | null | undefined) => { // TODO: if out of bounds prevent sending and notify user const action = item.actions?.find( @@ -107,7 +115,7 @@ function DismissedQuantity({ } } } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } @@ -137,7 +145,7 @@ function DismissedQuantity({ min={0} max={item.quantity} type="number" - value={value} + value={value ?? ""} className="bg-ui-bg-field-component text-right [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" onChange={(e) => { const value = diff --git a/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx b/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx index 4042b188..9bbb026f 100644 --- a/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx +++ b/src/routes/orders/order-receive-return/components/order-receive-return-form/order-receive-return-form.tsx @@ -1,11 +1,11 @@ import { zodResolver } from "@hookform/resolvers/zod" import { ArrowRight } from "@medusajs/icons" -import { AdminOrder, AdminReturn } from "@medusajs/types" +import type { AdminOrder, AdminReturn, AdminOrderLineItem, AdminOrderChangeAction } from "@medusajs/types" import { Alert, Button, Input, Switch, Text, toast } from "@medusajs/ui" import { useEffect, useMemo } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import * as zod from "zod" +import type * as zod from "zod" import { Form } from "../../../../../components/common/form" import { Thumbnail } from "../../../../../components/common/thumbnail" @@ -22,6 +22,11 @@ import { import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" import { ReceiveReturnSchema } from "./constants" import DismissedQuantity from "./dismissed-quantity" +import { getErrorMessage } from "@utils/error-helper" + +export type OrderLineItemWithActions = AdminOrderLineItem & { + actions?: AdminOrderChangeAction[] +} type OrderAllocateItemsFormProps = { order: AdminOrder @@ -40,8 +45,8 @@ export function OrderReceiveReturnForm({ /** * Items on the preview order that are part of the return we are receiving currently. */ - const previewItems = useMemo(() => { - const idsMap = {} + const previewItems: OrderLineItemWithActions[] = useMemo(() => { + const idsMap: Record = {} orderReturn.items.forEach((i) => (idsMap[i.item_id] = true)) @@ -72,7 +77,7 @@ export function OrderReceiveReturnForm({ ) const { stock_location } = useStockLocation( - orderReturn.location_id, + orderReturn.location_id ?? "", undefined, { enabled: !!orderReturn.location_id, @@ -80,8 +85,10 @@ export function OrderReceiveReturnForm({ ) const itemsMap = useMemo(() => { - const ret = {} + const ret: Record = {} + order.items.forEach((i) => (ret[i.id] = i)) + return ret }, [order.items]) @@ -108,14 +115,17 @@ export function OrderReceiveReturnForm({ (a) => a.action === "RECEIVE_DAMAGED_RETURN_ITEM" ) + const receivedQuantity = receivedAction?.details?.quantity + const dismissedQuantity = dismissedAction?.details?.quantity + form.setValue( `items.${index}.quantity`, - receivedAction?.details.quantity, + typeof receivedQuantity === "number" ? receivedQuantity : undefined, { shouldTouch: true, shouldDirty: true } ) form.setValue( `items.${index}.dismissed_quantity`, - dismissedAction?.details.quantity, + typeof dismissedQuantity === "number" ? dismissedQuantity : undefined, { shouldTouch: true, shouldDirty: true } ) }) @@ -133,23 +143,26 @@ export function OrderReceiveReturnForm({ toast.success(t("general.success"), { description: t("orders.returns.receive.toast.success"), - dismissLabel: t("actions.close"), }) } catch (e) { toast.error(t("general.error"), { - description: e.message, - dismissLabel: t("actions.close"), + description: getErrorMessage(e), }) } }) const handleQuantityChange = async ( itemId: string, - value: number | null, + value: number | null | undefined, index: number ) => { const item = previewItems?.find((i) => i.id === itemId) - const action = item?.actions?.find( + + if (!item) { + return + } + + const action = item.actions?.find( (a) => a.action === "RECEIVE_RETURN_ITEM" ) @@ -181,7 +194,7 @@ export function OrderReceiveReturnForm({ try { if (action) { - if (value === null || value === 0) { + if (value === null || value === 0 || value === undefined) { await removeReceiveItem(action.id) return @@ -194,7 +207,7 @@ export function OrderReceiveReturnForm({ } } } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } @@ -204,7 +217,7 @@ export function OrderReceiveReturnForm({ await cancelReceiveReturn() } } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } } @@ -279,7 +292,7 @@ export function OrderReceiveReturnForm({ min={0} max={item.quantity} type="number" - value={value} + value={value ?? ""} className="bg-ui-bg-field-component text-right [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" onChange={(e) => { const value = diff --git a/src/routes/orders/order-receive-return/order-receive-return.tsx b/src/routes/orders/order-receive-return/order-receive-return.tsx index c9240a49..97a1f629 100644 --- a/src/routes/orders/order-receive-return/order-receive-return.tsx +++ b/src/routes/orders/order-receive-return/order-receive-return.tsx @@ -11,37 +11,36 @@ import { useInitiateReceiveReturn, useReturn, } from "../../../hooks/api/returns" +import { getErrorMessage } from "@utils/error-helper" let IS_REQUEST_RUNNING = false export function OrderReceiveReturn() { - const { id, return_id } = useParams() + const { id: order_id, return_id } = useParams() const { t } = useTranslation() const navigate = useNavigate() - /** - * HOOKS - */ + const id = order_id ?? "" + const returnId = return_id ?? "" - const { order } = useOrder(id!, { fields: "+currency_code,*items" }) - const { order: preview } = useOrderPreview(id!) - const { return: orderReturn } = useReturn(return_id, { + const { order } = useOrder(id, { fields: "+currency_code,*items" }, { enabled: !!id }) + const { order: preview } = useOrderPreview(id, {}, { enabled: !!id }) + const { return: orderReturn } = useReturn(returnId, { fields: "*items.item,*items.item.variant,*items.item.variant.product", + }, + { + enabled: !!returnId, }) // TODO: fix API needs to return 404 if return not exists and not an empty object - /** - * MUTATIONS - */ - const { mutateAsync: initiateReceiveReturn } = useInitiateReceiveReturn( - return_id, - id + returnId, + id, ) - const { mutateAsync: addReceiveItems } = useAddReceiveItems(return_id, id) + const { mutateAsync: addReceiveItems } = useAddReceiveItems(returnId, id) useEffect(() => { - ;(async function () { + (async function () { if (IS_REQUEST_RUNNING || !preview) { return } @@ -51,6 +50,7 @@ export function OrderReceiveReturn() { navigate(`/orders/${id}`, { replace: true }) toast.error(t("orders.returns.activeChangeError")) } + return } @@ -66,7 +66,7 @@ export function OrderReceiveReturn() { })), }) } catch (e) { - toast.error(e.message) + toast.error(getErrorMessage(e)) } finally { IS_REQUEST_RUNNING = false } diff --git a/src/routes/price-lists/common/hooks/use-price-list-grid-columns.tsx b/src/routes/price-lists/common/hooks/use-price-list-grid-columns.tsx index bd9ff849..7866af32 100644 --- a/src/routes/price-lists/common/hooks/use-price-list-grid-columns.tsx +++ b/src/routes/price-lists/common/hooks/use-price-list-grid-columns.tsx @@ -11,9 +11,10 @@ import { import { createDataGridPriceColumns } from "../../../../components/data-grid/helpers/create-data-grid-price-columns" import { PricingCreateSchemaType } from "../../price-list-create/components/price-list-create-form/schema" import { isProductRow } from "../utils" +import { ExtendedAdminProduct, ExtendedAdminProductVariant } from "@custom-types/product" const columnHelper = createDataGridHelper< - HttpTypes.AdminProduct | HttpTypes.AdminProductVariant, + ExtendedAdminProduct | ExtendedAdminProductVariant, PricingCreateSchemaType >() @@ -29,7 +30,7 @@ export const usePriceListGridColumns = ({ const { t } = useTranslation() const colDefs: ColumnDef< - HttpTypes.AdminProduct | HttpTypes.AdminProductVariant + ExtendedAdminProduct | ExtendedAdminProductVariant >[] = useMemo(() => { return [ columnHelper.column({ @@ -59,7 +60,7 @@ export const usePriceListGridColumns = ({ disableHiding: true, }), ...createDataGridPriceColumns< - HttpTypes.AdminProduct | HttpTypes.AdminProductVariant, + ExtendedAdminProduct | ExtendedAdminProductVariant, PricingCreateSchemaType >({ currencies: currencies.map((c) => c.currency_code), @@ -67,6 +68,7 @@ export const usePriceListGridColumns = ({ pricePreferences, isReadyOnly: (context) => { const entity = context.row.original + return isProductRow(entity) }, getFieldName: (context, value) => { diff --git a/src/routes/price-lists/common/utils.ts b/src/routes/price-lists/common/utils.ts index 4660da83..383f74ab 100644 --- a/src/routes/price-lists/common/utils.ts +++ b/src/routes/price-lists/common/utils.ts @@ -8,6 +8,7 @@ import { PriceListCreateProductVariantSchema, PriceListCreateProductsSchema, } from "./schemas" +import type { ExtendedAdminProduct, ExtendedAdminProductVariant } from "@custom-types/product" const getValues = (priceList: HttpTypes.AdminPriceList) => { const startsAt = priceList.starts_at @@ -60,8 +61,8 @@ export const getPriceListStatus = ( } export const isProductRow = ( - row: HttpTypes.AdminProduct | HttpTypes.AdminProductVariant -): row is HttpTypes.AdminProduct => { + row: ExtendedAdminProduct | ExtendedAdminProductVariant +): row is ExtendedAdminProduct => { return "variants" in row } diff --git a/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-prices-form.tsx b/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-prices-form.tsx index 932222d3..65f34bec 100644 --- a/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-prices-form.tsx +++ b/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-prices-form.tsx @@ -58,6 +58,7 @@ export const PriceListPricesForm = ({ currency_prices: {}, region_prices: {}, } + return variants }, {} as PriceListCreateProductVariantsSchema), }) diff --git a/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-products-form.tsx b/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-products-form.tsx index 0017b85f..bb5a8b05 100644 --- a/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-products-form.tsx +++ b/src/routes/price-lists/price-list-create/components/price-list-create-form/price-list-products-form.tsx @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { Checkbox } from "@medusajs/ui" import { keepPreviousData } from "@tanstack/react-query" import { @@ -18,6 +17,7 @@ import { useProductTableQuery } from "../../../../../hooks/table/query/use-produ import { useDataTable } from "../../../../../hooks/use-data-table" import { PriceListCreateProductsSchema } from "../../../common/schemas" import { PricingCreateSchemaType } from "./schema" +import type { ExtendedAdminProduct } from "@custom-types/product" type PriceListProductsFormProps = { form: UseFormReturn @@ -145,7 +145,7 @@ export const PriceListProductsForm = ({ form }: PriceListProductsFormProps) => { ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const base = useProductTableColumns() diff --git a/src/routes/price-lists/price-list-detail/components/price-list-product-section/price-list-product-section.tsx b/src/routes/price-lists/price-list-detail/components/price-list-product-section/price-list-product-section.tsx index 0d6c44a7..bd30b986 100644 --- a/src/routes/price-lists/price-list-detail/components/price-list-product-section/price-list-product-section.tsx +++ b/src/routes/price-lists/price-list-detail/components/price-list-product-section/price-list-product-section.tsx @@ -16,6 +16,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 type { ExtendedAdminProduct } from "@custom-types/product" type PriceListProductSectionProps = { priceList: HttpTypes.AdminPriceList @@ -174,7 +175,7 @@ const ProductRowAction = ({ product, priceList, }: { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct priceList: HttpTypes.AdminPriceList }) => { const { t } = useTranslation() @@ -240,7 +241,7 @@ const ProductRowAction = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = (priceList: HttpTypes.AdminPriceList) => { const base = useProductTableColumns() diff --git a/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-prices-form.tsx b/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-prices-form.tsx index 513fdf83..0c332b54 100644 --- a/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-prices-form.tsx +++ b/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-prices-form.tsx @@ -59,6 +59,7 @@ export const PriceListPricesAddPricesForm = ({ currency_prices: {}, region_prices: {}, } + return variants }, {} as PriceListCreateProductVariantsSchema), }) diff --git a/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-product-ids-form.tsx b/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-product-ids-form.tsx index 5237a381..cea56782 100644 --- a/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-product-ids-form.tsx +++ b/src/routes/price-lists/price-list-prices-add/components/price-list-prices-add-form/price-list-prices-add-product-ids-form.tsx @@ -17,6 +17,7 @@ import { useProductTableQuery } from "../../../../../hooks/table/query/use-produ import { useDataTable } from "../../../../../hooks/use-data-table" import { PriceListCreateProductsSchema } from "../../../common/schemas" import { PriceListPricesAddSchema } from "./schema" +import type { ExtendedAdminProduct } from "@custom-types/product" type PriceListPricesAddProductIdsFormProps = { form: UseFormReturn @@ -162,7 +163,7 @@ export const PriceListPricesAddProductIdsForm = ({ ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const base = useProductTableColumns() diff --git a/src/routes/price-lists/price-list-prices-edit/components/price-list-prices-edit-form/price-list-prices-edit-form.tsx b/src/routes/price-lists/price-list-prices-edit/components/price-list-prices-edit-form/price-list-prices-edit-form.tsx index 56a6d686..ec1c6150 100644 --- a/src/routes/price-lists/price-list-prices-edit/components/price-list-prices-edit-form/price-list-prices-edit-form.tsx +++ b/src/routes/price-lists/price-list-prices-edit/components/price-list-prices-edit-form/price-list-prices-edit-form.tsx @@ -20,10 +20,11 @@ import { PriceListUpdateProductsSchema, } from "../../../common/schemas" import { isProductRow } from "../../../common/utils" +import type { ExtendedAdminProduct, ExtendedAdminProductVariant } from "@custom-types/product" type PriceListPricesEditFormProps = { priceList: HttpTypes.AdminPriceList - products: HttpTypes.AdminProduct[] + products: ExtendedAdminProduct[] regions: HttpTypes.AdminRegion[] currencies: HttpTypes.AdminStoreCurrency[] pricePreferences: HttpTypes.AdminPricePreference[] @@ -93,7 +94,7 @@ export const PriceListPricesEditForm = ({ - > columns={columns} data={products} getSubRows={(row) => { @@ -125,7 +126,7 @@ export const PriceListPricesEditForm = ({ function initRecord( priceList: HttpTypes.AdminPriceList, - products: HttpTypes.AdminProduct[] + products: ExtendedAdminProduct[] ): PriceListUpdateProductsSchema { const record: PriceListUpdateProductsSchema = {} @@ -155,6 +156,7 @@ function initRecord( } variants[price.variant_id] = variantObject + return variants }, {} as PriceListUpdateProductVariantsSchema) @@ -189,10 +191,11 @@ function convertToPriceArray( const regionCurrencyMap = regions.reduce((map, region) => { map[region.id] = region.currency_code + return map }, {} as Record) - for (const [_productId, product] of Object.entries(data || {})) { + for (const product of Object.values(data || {})) { const { variants } = product || {} for (const [variantId, variant] of Object.entries(variants || {})) { @@ -250,11 +253,13 @@ function comparePrices(initialPrices: PriceObject[], newPrices: PriceObject[]) { const initialPriceMap = initialPrices.reduce((map, price) => { map[createMapKey(price)] = price + return map }, {} as Record) const newPriceMap = newPrices.reduce((map, price) => { map[createMapKey(price)] = price + return map }, {} as Record) diff --git a/src/routes/product-variants/product-variant-detail/breadcrumb.tsx b/src/routes/product-variants/product-variant-detail/breadcrumb.tsx index 730ee435..e389b74b 100644 --- a/src/routes/product-variants/product-variant-detail/breadcrumb.tsx +++ b/src/routes/product-variants/product-variant-detail/breadcrumb.tsx @@ -1,10 +1,10 @@ -import { HttpTypes } from "@medusajs/types" -import { UIMatch } from "react-router-dom" -import { useProductVariant } from "../../../hooks/api" +import type { UIMatch } from "react-router-dom" +import { useProductVariant } from "@hooks/api" import { VARIANT_DETAIL_FIELDS } from "./constants" +import type { ExtendedAdminProductVariantResponse } from "@custom-types/product" type ProductVariantDetailBreadcrumbProps = - UIMatch + UIMatch export const ProductVariantDetailBreadcrumb = ( props: ProductVariantDetailBreadcrumbProps diff --git a/src/routes/product-variants/product-variant-detail/components/variant-general-section/variant-general-section.tsx b/src/routes/product-variants/product-variant-detail/components/variant-general-section/variant-general-section.tsx index b2000ddc..3cbd538f 100644 --- a/src/routes/product-variants/product-variant-detail/components/variant-general-section/variant-general-section.tsx +++ b/src/routes/product-variants/product-variant-detail/components/variant-general-section/variant-general-section.tsx @@ -1,15 +1,15 @@ import { Component, PencilSquare, Trash } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Badge, Container, Heading, 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 { useDeleteVariant } from "../../../../../hooks/api/products" +import { ActionMenu } from "@components/common/action-menu" +import { SectionRow } from "@components/common/section" +import { useDeleteVariant } from "@hooks/api/products" +import type { ExtendedAdminProductVariant } from "@custom-types/product" type VariantGeneralSectionProps = { - variant: HttpTypes.AdminProductVariant + variant: ExtendedAdminProductVariant } export function VariantGeneralSection({ variant }: VariantGeneralSectionProps) { @@ -17,7 +17,7 @@ export function VariantGeneralSection({ variant }: VariantGeneralSectionProps) { const prompt = usePrompt() const navigate = useNavigate() - const hasInventoryKit = variant.inventory?.length > 1 + const hasInventoryKit = (variant.inventory?.length ?? 0) > 1 const { mutateAsync } = useDeleteVariant(variant.product_id!, variant.id) @@ -88,7 +88,7 @@ export function VariantGeneralSection({ variant }: VariantGeneralSectionProps) { {variant.options?.map((o) => ( {o.value}} /> ))} diff --git a/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/inventory-actions.tsx b/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/inventory-actions.tsx index f3caba5c..052a404d 100644 --- a/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/inventory-actions.tsx +++ b/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/inventory-actions.tsx @@ -1,11 +1,11 @@ import { useTranslation } from "react-i18next" import { Buildings } from "@medusajs/icons" -import { InventoryItemDTO } from "@medusajs/types" import { ActionMenu } from "../../../../../components/common/action-menu" +import type { ExtendedAdminProductVariantInventoryItemWithQuantity } from "../../../../../types/product" -export const InventoryActions = ({ item }: { item: InventoryItemDTO }) => { +export const InventoryActions = ({ item }: { item: ExtendedAdminProductVariantInventoryItemWithQuantity }) => { const { t } = useTranslation() return ( diff --git a/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/use-inventory-table-columns.tsx b/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/use-inventory-table-columns.tsx index 0767b09d..a4e72b9d 100644 --- a/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/use-inventory-table-columns.tsx +++ b/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/use-inventory-table-columns.tsx @@ -1,26 +1,24 @@ -import { InventoryNext, ProductVariantDTO } from "@medusajs/types" +import type { InventoryLevelDTO } from "@medusajs/types" import { InventoryActions } from "./inventory-actions" import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell" import { createColumnHelper } from "@tanstack/react-table" import { useMemo } from "react" import { useTranslation } from "react-i18next" +import type { ExtendedAdminProductVariantInventoryItemWithQuantity } from "../../../../../types/product" -interface ExtendedInventoryItem extends InventoryNext.InventoryItemDTO { - variants: ProductVariantDTO[] -} - -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() export const useInventoryTableColumns = () => { const { t } = useTranslation() return useMemo( () => [ - columnHelper.accessor("title", { + columnHelper.display({ + id: "title", header: t("fields.title"), - cell: ({ getValue }) => { - const title = getValue() + cell: ({ row }) => { + const title = row.original.title if (!title) { return @@ -33,10 +31,11 @@ export const useInventoryTableColumns = () => { ) }, }), - columnHelper.accessor("sku", { + columnHelper.display({ + id: "sku", header: t("fields.sku"), - cell: ({ getValue }) => { - const sku = getValue() as string + cell: ({ row }) => { + const sku = row.original.sku if (!sku) { return @@ -49,12 +48,13 @@ export const useInventoryTableColumns = () => { ) }, }), - columnHelper.accessor("required_quantity", { + columnHelper.display({ + id: "required_quantity", header: t("fields.requiredQuantity"), - cell: ({ getValue }) => { - const quantity = getValue() + cell: ({ row }) => { + const quantity = row.original.required_quantity - if (Number.isNaN(quantity)) { + if (quantity === undefined || Number.isNaN(quantity)) { return } @@ -76,7 +76,7 @@ export const useInventoryTableColumns = () => { let quantity = 0 let locations = 0 - inventory.location_levels.forEach((level) => { + inventory.location_levels.forEach((level: InventoryLevelDTO) => { quantity += level.available_quantity locations += 1 }) diff --git a/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/variant-inventory-section.tsx b/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/variant-inventory-section.tsx index 24924267..a9aae607 100644 --- a/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/variant-inventory-section.tsx +++ b/src/routes/product-variants/product-variant-detail/components/variant-inventory-section/variant-inventory-section.tsx @@ -1,7 +1,6 @@ import { useTranslation } from "react-i18next" import { Buildings, Component } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Container, Heading } from "@medusajs/ui" import { ActionMenu } from "../../../../../components/common/action-menu" @@ -10,11 +9,12 @@ import { _DataTable } from "../../../../../components/table/data-table" import { LinkButton } from "../../../../../components/common/link-button" import { useDataTable } from "../../../../../hooks/use-data-table" import { useInventoryTableColumns } from "./use-inventory-table-columns" +import type { ExtendedAdminProductVariantInventoryItemWithQuantity } from "../../../../../types/product" const PAGE_SIZE = 20 type VariantInventorySectionProps = { - inventoryItems: HttpTypes.AdminInventoryItem[] + inventoryItems: ExtendedAdminProductVariantInventoryItemWithQuantity[] } export function VariantInventorySection({ diff --git a/src/routes/product-variants/product-variant-detail/components/variant-prices-section/variant-prices-section.tsx b/src/routes/product-variants/product-variant-detail/components/variant-prices-section/variant-prices-section.tsx index 1c2c85af..d2bcb912 100644 --- a/src/routes/product-variants/product-variant-detail/components/variant-prices-section/variant-prices-section.tsx +++ b/src/routes/product-variants/product-variant-detail/components/variant-prices-section/variant-prices-section.tsx @@ -2,15 +2,15 @@ import { useState } from "react" import { useTranslation } from "react-i18next" import { CurrencyDollar } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Button, Container, Heading } from "@medusajs/ui" import { ActionMenu } from "../../../../../components/common/action-menu" import { NoRecords } from "../../../../../components/common/empty-table-content" import { getLocaleAmount } from "../../../../../lib/money-amount-helpers" +import type { ExtendedAdminProductVariant } from "../../../../../types/product" type VariantPricesSectionProps = { - variant: HttpTypes.AdminProductVariant + variant: ExtendedAdminProductVariant } export function VariantPricesSection({ variant }: VariantPricesSectionProps) { diff --git a/src/routes/product-variants/product-variant-detail/loader.ts b/src/routes/product-variants/product-variant-detail/loader.ts index 973bcb3a..57582bdf 100644 --- a/src/routes/product-variants/product-variant-detail/loader.ts +++ b/src/routes/product-variants/product-variant-detail/loader.ts @@ -1,9 +1,10 @@ -import { LoaderFunctionArgs } from "react-router-dom" +import type { LoaderFunctionArgs } from "react-router-dom" -import { variantsQueryKeys } from "../../../hooks/api/products" -import { sdk } from "../../../lib/client" -import { queryClient } from "../../../lib/query-client" +import { variantsQueryKeys } from "@hooks/api/products" +import { sdk } from "@lib/client" +import { queryClient } from "@lib/query-client" import { VARIANT_DETAIL_FIELDS } from "./constants" +import type { ExtendedAdminProductVariantResponse } from "@custom-types/product" const variantDetailQuery = (productId: string, variantId: string) => ({ queryKey: variantsQueryKeys.detail(variantId, { @@ -12,7 +13,7 @@ const variantDetailQuery = (productId: string, variantId: string) => ({ queryFn: async () => sdk.admin.product.retrieveVariant(productId, variantId, { fields: VARIANT_DETAIL_FIELDS, - }), + }) as Promise, }) export const variantLoader = async ({ params }: LoaderFunctionArgs) => { @@ -22,4 +23,4 @@ export const variantLoader = async ({ params }: LoaderFunctionArgs) => { const query = variantDetailQuery(productId!, variantId!) return queryClient.ensureQueryData(query) -} +} \ No newline at end of file diff --git a/src/routes/product-variants/product-variant-detail/product-variant-detail.tsx b/src/routes/product-variants/product-variant-detail/product-variant-detail.tsx index f5f27925..0586ec2e 100644 --- a/src/routes/product-variants/product-variant-detail/product-variant-detail.tsx +++ b/src/routes/product-variants/product-variant-detail/product-variant-detail.tsx @@ -2,9 +2,9 @@ import { useLoaderData, useParams } from "react-router-dom" import { useProductVariant } from "../../../hooks/api/products" -import { TwoColumnPageSkeleton } from "../../../components/common/skeleton" -import { TwoColumnPage } from "../../../components/layout/pages" -import { useExtension } from "../../../providers/extension-provider" +import { TwoColumnPageSkeleton } from "@components/common/skeleton" +import { TwoColumnPage } from "@components/layout/pages" +import { useExtension } from "@providers/extension-provider" import { VariantGeneralSection } from "./components/variant-general-section" import { InventorySectionPlaceholder, @@ -12,7 +12,7 @@ import { } from "./components/variant-inventory-section" import { VariantPricesSection } from "./components/variant-prices-section" import { VARIANT_DETAIL_FIELDS } from "./constants" -import { variantLoader } from "./loader" +import type { variantLoader } from "./loader" export const ProductVariantDetail = () => { const initialData = useLoaderData() as Awaited< @@ -28,7 +28,7 @@ export const ProductVariantDetail = () => { initialData, } ) - +console.log(variant, 'VARIANT') const { getWidgets } = useExtension() if (isLoading || !variant) { @@ -65,13 +65,12 @@ export const ProductVariantDetail = () => { ) : ( { - return { + inventoryItems={ + variant.inventory_items?.map((i) => ({ ...i.inventory, required_quantity: i.required_quantity, - variant, - } - }) ?? []} + })) ?? [] + } /> )} diff --git a/src/routes/product-variants/product-variant-edit/components/product-edit-variant-form/product-edit-variant-form.tsx b/src/routes/product-variants/product-variant-edit/components/product-edit-variant-form/product-edit-variant-form.tsx index 4b54216d..03742514 100644 --- a/src/routes/product-variants/product-variant-edit/components/product-edit-variant-form/product-edit-variant-form.tsx +++ b/src/routes/product-variants/product-variant-edit/components/product-edit-variant-form/product-edit-variant-form.tsx @@ -4,22 +4,22 @@ import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import { z } from "zod" -import { HttpTypes } from "@medusajs/types" -import { Form } from "../../../../../components/common/form" -import { Combobox } from "../../../../../components/inputs/combobox" -import { CountrySelect } from "../../../../../components/inputs/country-select" -import { RouteDrawer, useRouteModal } from "../../../../../components/modals" -import { KeyboundForm } from "../../../../../components/utilities/keybound-form" -import { useUpdateProductVariant } from "../../../../../hooks/api/products" +import { Form } from "@components/common/form" +import { Combobox } from "@components/inputs/combobox" +import { CountrySelect } from "@components/inputs/country-select" +import { RouteDrawer, useRouteModal } from "@components/modals" +import { KeyboundForm } from "@components/utilities/keybound-form" +import { useUpdateProductVariant } from "@hooks/api/products" import { transformNullableFormData, transformNullableFormNumber, -} from "../../../../../lib/form-helpers" -import { optionalInt } from "../../../../../lib/validation" +} from "@lib/form-helpers" +import { optionalInt } from "@lib/validation" +import type { ExtendedAdminProduct, ExtendedAdminProductVariant } from "@custom-types/product" type ProductEditVariantFormProps = { - product: HttpTypes.AdminProduct - variant: HttpTypes.AdminProductVariant + product: ExtendedAdminProduct + variant: ExtendedAdminProductVariant } const ProductEditVariantSchema = z.object({ @@ -48,11 +48,12 @@ export const ProductEditVariantForm = ({ }: ProductEditVariantFormProps) => { const { t } = useTranslation() const { handleSuccess } = useRouteModal() - const defaultOptions = product.options?.reduce((acc: any, option: any) => { - const varOpt = variant.options?.find((o: any) => o.option_id === option.id) + const defaultOptions = product.options?.reduce((acc, option) => { + const varOpt = variant.options?.find((o) => o.option_id === option.id) acc[option.title] = varOpt?.value + return acc - }, {}) + }, {} as Record) const form = useForm>({ defaultValues: { @@ -164,8 +165,9 @@ export const ProductEditVariantForm = ({ ) }} /> - {product.options?.map((option: any) => { + {product.options?.map((option) => { const optionKey = option.title.toLowerCase().replace(/\s+/g, "-") + return ( ({ + options={option.values?.map((v) => ({ label: v.value, value: v.value, - }))} + })) ?? []} data-testid={`product-variant-edit-form-option-${optionKey}-combobox`} />
diff --git a/src/routes/product-variants/product-variant-edit/loader.ts b/src/routes/product-variants/product-variant-edit/loader.ts index c42c7642..1ac8e1b9 100644 --- a/src/routes/product-variants/product-variant-edit/loader.ts +++ b/src/routes/product-variants/product-variant-edit/loader.ts @@ -1,12 +1,11 @@ -import { LoaderFunctionArgs } from "react-router-dom" +import type { LoaderFunctionArgs } from "react-router-dom" -import { productVariantQueryKeys } from "../../../hooks/api" -import { sdk } from "../../../lib/client" -import { queryClient } from "../../../lib/query-client" +import { productVariantQueryKeys } from "@hooks/api" +import { sdk } from "@lib/client" +import { queryClient } from "@lib/query-client" +import type { ExtendedAdminProductVariantResponse } from "@custom-types/product" -const queryFn = async (id: string, variantId: string) => { - return await sdk.admin.product.retrieveVariant(id, variantId) -} +const queryFn = async (id: string, variantId: string) => sdk.admin.product.retrieveVariant(id, variantId) as Promise const editProductVariantQuery = (id: string, variantId: string) => ({ queryKey: productVariantQueryKeys.detail(variantId), diff --git a/src/routes/product-variants/product-variant-edit/product-variant-edit.tsx b/src/routes/product-variants/product-variant-edit/product-variant-edit.tsx index 6d490bc2..6c7d68cc 100644 --- a/src/routes/product-variants/product-variant-edit/product-variant-edit.tsx +++ b/src/routes/product-variants/product-variant-edit/product-variant-edit.tsx @@ -1,10 +1,10 @@ import { Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { useLoaderData, useParams, useSearchParams } from "react-router-dom" -import { RouteDrawer } from "../../../components/modals" -import { useProduct, useProductVariant } from "../../../hooks/api/products" +import { RouteDrawer } from "@components/modals" +import { useProduct, useProductVariant } from "@hooks/api/products" import { ProductEditVariantForm } from "./components/product-edit-variant-form" -import { editProductVariantLoader } from "./loader" +import type { editProductVariantLoader } from "./loader" export const ProductVariantEdit = () => { const initialData = useLoaderData() as Awaited< @@ -31,7 +31,7 @@ export const ProductVariantEdit = () => { isError: isProductError, error: productError, } = useProduct( - variant?.product_id!, + variant?.product_id ?? "", { fields: "-variants", }, diff --git a/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx b/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx index 80000c15..d20699d3 100644 --- a/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx +++ b/src/routes/product-variants/product-variant-manage-inventory-items/components/manage-variant-inventory-items-form/manage-variant-inventory-items-form.tsx @@ -1,6 +1,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { XMarkMini } from "@medusajs/icons" -import { AdminProductVariant, HttpTypes } from "@medusajs/types" +import { HttpTypes } from "@medusajs/types" import { Button, Heading, IconButton, Input, Label, toast } from "@medusajs/ui" import i18next from "i18next" import { @@ -23,15 +23,17 @@ import { useProductVariantsInventoryItemsBatch } from "../../../../../hooks/api/ import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { castNumber } from "../../../../../lib/cast-number" import { sdk } from "../../../../../lib/client" +import { ExtendedAdminProductVariant } from "@custom-types/product" type ManageVariantInventoryItemsFormProps = { - variant: AdminProductVariant & { - inventory_items: { - inventory: HttpTypes.AdminInventoryItem - inventory_item_id: string - required_quantity: number - }[] - } + // variant: AdminProductVariant & { + // inventory_items: { + // inventory: HttpTypes.AdminInventoryItem + // inventory_item_id: string + // required_quantity: number + // }[] + // }\ + variant: ExtendedAdminProductVariant } const ManageVariantInventoryItemsSchema = zod.object({ @@ -61,18 +63,18 @@ const ManageVariantInventoryItemsSchema = zod.object({ ), }) -type InventoryItemFormData = zod.infer< - typeof ManageVariantInventoryItemsSchema ->["inventory"] +type ManageVariantInventoryItemsFormData = zod.infer + +type InventoryItemField = { + id: string + inventory_item_id: string + required_quantity: string | number +} type VariantInventoryItemRowProps = { - form: UseFormReturn + form: UseFormReturn inventoryIndex: number - inventoryItem: { - id: string - inventory_item_id: string - required_quantity: number - } + inventoryItem: InventoryItemField isItemOptionDisabled: ( option: { value: string }, inventoryIndex: number @@ -207,7 +209,7 @@ export function ManageVariantInventoryItemsForm({ const form = useForm>({ defaultValues: { - inventory: variant.inventory_items.length + inventory: variant.inventory_items?.length ? variant.inventory_items!.map((i) => ({ required_quantity: i.required_quantity, inventory_item_id: i.inventory.id, @@ -255,7 +257,7 @@ export function ManageVariantInventoryItemsForm({ const existingItems: Record = {} const selectedItems: Record = {} - variant.inventory_items.forEach( + variant.inventory_items?.forEach( (i) => (existingItems[i.inventory.id] = i.required_quantity) ) @@ -285,7 +287,7 @@ export function ManageVariantInventoryItemsForm({ } }) - variant.inventory_items.forEach((i) => { + variant.inventory_items?.forEach((i) => { if (!(i.inventory.id in selectedItems)) { payload.delete = payload.delete || [] @@ -356,7 +358,7 @@ export function ManageVariantInventoryItemsForm({ isItemOptionDisabled={isItemOptionDisabled} onRemove={() => inventory.remove(inventoryIndex)} /> - ))} + ))}
diff --git a/src/routes/product-variants/product-variant-manage-inventory-items/product-variant-manage-inventory-items.tsx b/src/routes/product-variants/product-variant-manage-inventory-items/product-variant-manage-inventory-items.tsx index 59c7f0d6..bf62d927 100644 --- a/src/routes/product-variants/product-variant-manage-inventory-items/product-variant-manage-inventory-items.tsx +++ b/src/routes/product-variants/product-variant-manage-inventory-items/product-variant-manage-inventory-items.tsx @@ -13,8 +13,10 @@ export function ProductVariantManageInventoryItems() { isPending: isLoading, isError, error, - } = useProductVariant(id!, variant_id!, { + } = useProductVariant(id ?? "", variant_id ?? "", { fields: VARIANT_DETAIL_FIELDS, + }, { + enabled: !!id && !!variant_id, }) if (isError) { @@ -23,7 +25,7 @@ export function ProductVariantManageInventoryItems() { return ( - {!isLoading && variant && ( + {!isLoading && variant && variant.inventory_items && ( )} diff --git a/src/routes/products/common/variant-pricing-form.tsx b/src/routes/products/common/variant-pricing-form.tsx index 51eb995c..a88406d3 100644 --- a/src/routes/products/common/variant-pricing-form.tsx +++ b/src/routes/products/common/variant-pricing-form.tsx @@ -69,6 +69,7 @@ const useVariantPriceGridColumns = ({ header: t("fields.title"), cell: (context) => { const entity = context.row.original + return (
@@ -90,6 +91,7 @@ const useVariantPriceGridColumns = ({ if (context.column.id?.startsWith("currency_prices")) { return `variants.${context.row.index}.prices.${value}` } + return `variants.${context.row.index}.prices.${value}` }, t, diff --git a/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx b/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx index 1ddca9e6..e915b18f 100644 --- a/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx +++ b/src/routes/products/product-attributes/components/product-attributes-form/product-attributes-form.tsx @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { Button, Input } from "@medusajs/ui" import { useTranslation } from "react-i18next" import * as zod from "zod" @@ -12,9 +11,10 @@ import { } from "../../../../../dashboard-app" import { useUpdateProduct } from "../../../../../hooks/api/products" import { useExtension } from "../../../../../providers/extension-provider" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductAttributesFormProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } const dimension = zod @@ -23,6 +23,7 @@ const dimension = zod if (value === "") { return null } + return Number(value) }) .optional() diff --git a/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx b/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx index 05c8b85c..53345939 100644 --- a/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx +++ b/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx @@ -4,15 +4,15 @@ import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import { z } from "zod" -import { HttpTypes } from "@medusajs/types" import { Form } from "../../../../../components/common/form" import { ChipInput } from "../../../../../components/inputs/chip-input" import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { KeyboundForm } from "../../../../../components/utilities/keybound-form" import { useCreateProductOption } from "../../../../../hooks/api/products" +import type { ExtendedAdminProduct } from "@custom-types/product" type EditProductOptionsFormProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } const CreateProductOptionSchema = z.object({ diff --git a/src/routes/products/product-create-variant/components/create-product-variant-form/create-product-variant-form.tsx b/src/routes/products/product-create-variant/components/create-product-variant-form/create-product-variant-form.tsx index e5aaa404..2ad4071d 100644 --- a/src/routes/products/product-create-variant/components/create-product-variant-form/create-product-variant-form.tsx +++ b/src/routes/products/product-create-variant/components/create-product-variant-form/create-product-variant-form.tsx @@ -5,7 +5,7 @@ import { useFieldArray, useForm, useWatch } from "react-hook-form" import { useTranslation } from "react-i18next" import { z } from "zod" -import { AdminCreateProductVariantPrice, HttpTypes } from "@medusajs/types" +import { AdminCreateProductVariantPrice } from "@medusajs/types" import { RouteDrawer, RouteFocusModal, @@ -27,6 +27,7 @@ import DetailsTab from "./details-tab" import InventoryKitTab from "./inventory-kit-tab" import PricingTab from "./pricing-tab" import { useDocumentDirection } from "../../../../../hooks/use-document-direction" +import { ExtendedAdminProduct } from "@custom-types/product" enum Tab { DETAIL = "detail", @@ -43,7 +44,7 @@ const initialTabState: TabState = { } type CreateProductVariantFormProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } export const CreateProductVariantForm = ({ @@ -79,6 +80,7 @@ export const CreateProductVariantForm = ({ return regions.reduce( (acc, reg) => { acc[reg.id] = reg.currency_code + return acc }, {} as Record @@ -104,10 +106,10 @@ export const CreateProductVariantForm = ({ const tabOrder = useMemo(() => { if (inventoryTabEnabled) { - return [Tab.DETAIL, Tab.PRICE, Tab.INVENTORY] as const + return [Tab.DETAIL, Tab.PRICE, Tab.INVENTORY] } - return [Tab.DETAIL, Tab.PRICE] as const + return [Tab.DETAIL, Tab.PRICE] }, [inventoryTabEnabled]) useEffect(() => { @@ -134,6 +136,7 @@ export const CreateProductVariantForm = ({ })) setTab(update) + return } @@ -155,6 +158,7 @@ export const CreateProductVariantForm = ({ [tab]: "in-progress", })) setTab(tab) + return } @@ -219,17 +223,22 @@ export const CreateProductVariantForm = ({ return undefined } - const ret: AdminCreateProductVariantPrice = {} const amount = castNumber(value) + let currency_code: string + let rules: AdminCreateProductVariantPrice["rules"] | undefined if (currencyOrRegion.startsWith("reg_")) { - ret.rules = { region_id: currencyOrRegion } - ret.currency_code = regionsCurrencyMap[currencyOrRegion] + rules = { region_id: currencyOrRegion } + currency_code = regionsCurrencyMap[currencyOrRegion] } else { - ret.currency_code = currencyOrRegion + currency_code = currencyOrRegion } - ret.amount = amount + const ret: AdminCreateProductVariantPrice = { + currency_code, + amount, + ...(rules && { rules }), + } return ret }) diff --git a/src/routes/products/product-create-variant/components/create-product-variant-form/details-tab.tsx b/src/routes/products/product-create-variant/components/create-product-variant-form/details-tab.tsx index dcdf2ced..d6b7994e 100644 --- a/src/routes/products/product-create-variant/components/create-product-variant-form/details-tab.tsx +++ b/src/routes/products/product-create-variant/components/create-product-variant-form/details-tab.tsx @@ -3,14 +3,13 @@ import { UseFormReturn, useWatch } from "react-hook-form" import { useTranslation } from "react-i18next" import { z } from "zod" -import { HttpTypes } from "@medusajs/types" - import { Form } from "../../../../../components/common/form" import { Combobox } from "../../../../../components/inputs/combobox" import { CreateProductVariantSchema } from "./constants" +import { ExtendedAdminProduct } from "@custom-types/product" type DetailsTabProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct form: UseFormReturn> } @@ -60,7 +59,7 @@ function DetailsTab({ form, product }: DetailsTabProps) { }} /> - {product.options?.map((option: any) => ( + {product.options?.map((option) => ( ({ + options={option.values?.map((v) => ({ label: v.value, value: v.value, - }))} + })) ?? []} /> diff --git a/src/routes/products/product-create-variant/components/create-product-variant-form/inventory-kit-tab.tsx b/src/routes/products/product-create-variant/components/create-product-variant-form/inventory-kit-tab.tsx index 9d04cfcb..925e4fb2 100644 --- a/src/routes/products/product-create-variant/components/create-product-variant-form/inventory-kit-tab.tsx +++ b/src/routes/products/product-create-variant/components/create-product-variant-form/inventory-kit-tab.tsx @@ -1,15 +1,15 @@ -import { z } from "zod" -import { useFieldArray, UseFormReturn } from "react-hook-form" +import type { z } from "zod" +import { useFieldArray, type UseFormReturn } from "react-hook-form" import { Button, Heading, IconButton, Input, Label } from "@medusajs/ui" -import { CreateProductVariantSchema } from "./constants" +import type { CreateProductVariantSchema } from "./constants" import { XMarkMini } from "@medusajs/icons" import { useTranslation } from "react-i18next" -import { useComboboxData } from "../../../../../hooks/use-combobox-data" -import { sdk } from "../../../../../lib/client" -import { Form } from "../../../../../components/common/form" -import { Combobox } from "../../../../../components/inputs/combobox" +import { useComboboxData } from "@hooks/use-combobox-data" +import { sdk } from "@lib/client" +import { Form } from "@components/common/form" +import { Combobox } from "@components/inputs/combobox" type InventoryKitTabProps = { form: UseFormReturn> @@ -30,7 +30,7 @@ function InventoryKitTab({ form }: InventoryKitTabProps) { queryFn: (params) => sdk.admin.inventoryItem.list(params), getOptions: (data) => data.inventory_items.map((item) => ({ - label: item.title, + label: item.title || item.sku || item.id, value: item.id, })), }) diff --git a/src/routes/products/product-create-variant/components/create-product-variant-form/pricing-tab.tsx b/src/routes/products/product-create-variant/components/create-product-variant-form/pricing-tab.tsx index a10850c2..4fd1d11e 100644 --- a/src/routes/products/product-create-variant/components/create-product-variant-form/pricing-tab.tsx +++ b/src/routes/products/product-create-variant/components/create-product-variant-form/pricing-tab.tsx @@ -1,18 +1,18 @@ import { useMemo } from "react" -import { UseFormReturn, useWatch } from "react-hook-form" -import { HttpTypes } from "@medusajs/types" +import { type UseFormReturn, useWatch } from "react-hook-form" +import type { HttpTypes } from "@medusajs/types" import { useTranslation } from "react-i18next" -import { z } from "zod" +import type { z } from "zod" -import { CreateProductVariantSchema } from "./constants" -import { useRegions, useStore } from "../../../../../hooks/api" -import { usePricePreferences } from "../../../../../hooks/api/price-preferences" -import { useRouteModal } from "../../../../../components/modals" +import type { CreateProductVariantSchema } from "./constants" +import { useRegions, useStore } from "@hooks/api" +import { usePricePreferences } from "@hooks/api/price-preferences" +import { useRouteModal } from "@components/modals" import { createDataGridHelper, createDataGridPriceColumns, DataGrid, -} from "../../../../../components/data-grid" +} from "@components/data-grid" type PricingTabProps = { form: UseFormReturn> @@ -33,10 +33,10 @@ function PricingTab({ form }: PricingTabProps) { const variant = useWatch({ control: form.control, - }) as any + }) as z.infer return ( - , z.infer> columns={columns} data={[variant]} state={form} @@ -46,7 +46,7 @@ function PricingTab({ form }: PricingTabProps) { } const columnHelper = createDataGridHelper< - HttpTypes.AdminProductVariant, + z.infer, z.infer >() @@ -68,6 +68,7 @@ const useVariantPriceGridColumns = ({ header: t("fields.title"), cell: (context) => { const entity = context.row.original + return (
@@ -79,7 +80,7 @@ const useVariantPriceGridColumns = ({ disableHiding: true, }), ...createDataGridPriceColumns< - HttpTypes.AdminProductVariant, + z.infer, z.infer >({ currencies: currencies.map((c) => c.currency_code), @@ -89,6 +90,7 @@ const useVariantPriceGridColumns = ({ if (context.column.id?.startsWith("currency_prices")) { return `prices.${value}` } + return `prices.${value}` }, t, diff --git a/src/routes/products/product-create-variant/product-create-variant.tsx b/src/routes/products/product-create-variant/product-create-variant.tsx index d37b6106..3b8a3369 100644 --- a/src/routes/products/product-create-variant/product-create-variant.tsx +++ b/src/routes/products/product-create-variant/product-create-variant.tsx @@ -1,6 +1,6 @@ import { useParams } from "react-router-dom" -import { RouteFocusModal } from "../../../components/modals" -import { useProduct } from "../../../hooks/api/products" +import { RouteFocusModal } from "@components/modals" +import { useProduct } from "@hooks/api/products" import { CreateProductVariantForm } from "./components/create-product-variant-form" export const ProductCreateVariant = () => { diff --git a/src/routes/products/product-create/components/product-create-form/product-create-form.tsx b/src/routes/products/product-create/components/product-create-form/product-create-form.tsx index 60497838..91aa0ac5 100644 --- a/src/routes/products/product-create/components/product-create-form/product-create-form.tsx +++ b/src/routes/products/product-create/components/product-create-form/product-create-form.tsx @@ -81,6 +81,7 @@ export const ProductCreateForm = ({ return regions.reduce( (acc, reg) => { acc[reg.id] = reg.currency_code + return acc }, {} as Record @@ -148,7 +149,7 @@ export const ProductCreateForm = ({ normalizeProductFormValues({ ...payload, media: uploadedMedia, - status: (isDraftSubmission ? "draft" : "published") as any, + status: (isDraftSubmission ? "draft" : "published"), regionsCurrencyMap, }), { @@ -210,7 +211,6 @@ export const ProductCreateForm = ({ } setTabState({ ...currentState }) - // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to run when the tab changes }, [tab]) return ( diff --git a/src/routes/products/product-create/components/product-create-variants-form/product-create-variants-form.tsx b/src/routes/products/product-create/components/product-create-variants-form/product-create-variants-form.tsx index 9b4661a2..83a98bdc 100644 --- a/src/routes/products/product-create/components/product-create-variants-form/product-create-variants-form.tsx +++ b/src/routes/products/product-create/components/product-create-variants-form/product-create-variants-form.tsx @@ -22,6 +22,8 @@ type ProductCreateVariantsFormProps = { pricePreferences: HttpTypes.AdminPricePreference[] } +type VariantWithIndex = ProductCreateVariantSchema & { originalIndex: number } + export const ProductCreateVariantsForm = ({ form, regions, @@ -57,8 +59,9 @@ export const ProductCreateVariantsForm = ({ pricePreferences, }) + const variantData = useMemo(() => { - const ret = [] + const ret: VariantWithIndex[] = [] variants.forEach((v, i) => { if (v.should_create) { @@ -82,7 +85,7 @@ export const ProductCreateVariantsForm = ({ } const columnHelper = createDataGridHelper< - ProductCreateVariantSchema, + VariantWithIndex, ProductCreateSchemaType >() @@ -184,7 +187,7 @@ const useColumns = ({ }), ...createDataGridPriceColumns< - ProductCreateVariantSchema, + VariantWithIndex, ProductCreateSchemaType >({ currencies, @@ -194,6 +197,7 @@ const useColumns = ({ if (context.column.id?.startsWith("currency_prices")) { return `variants.${context.row.original.originalIndex}.prices.${value}` } + return `variants.${context.row.original.originalIndex}.prices.${value}` }, t, diff --git a/src/routes/products/product-create/utils.ts b/src/routes/products/product-create/utils.ts index 31c74fd9..2f1ac747 100644 --- a/src/routes/products/product-create/utils.ts +++ b/src/routes/products/product-create/utils.ts @@ -49,6 +49,57 @@ export const normalizeProductFormValues = ( } } +type InventoryItem = { + inventory_item_id: string + required_quantity: number +} + +type PriceItem = { + currency_code: string + amount: number + rules?: { region_id: string } +} + +const normalizeInventoryItem = ( + item: { inventory_item_id: string; required_quantity?: number | string | null } +): InventoryItem | null => { + const quantity = item.required_quantity + ? castNumber(item.required_quantity) + : null + + if (!item.inventory_item_id || !quantity) { + return null + } + + return { + ...item, + required_quantity: quantity, + } +} + +const normalizePriceItem = ( + key: string, + value: number | string | undefined, + regionsCurrencyMap: Record +): PriceItem | null => { + if (value === "" || value === undefined) { + return null + } + + if (key.startsWith("reg_")) { + return { + currency_code: regionsCurrencyMap[key], + amount: castNumber(value), + rules: { region_id: key }, + } + } + + return { + currency_code: key, + amount: castNumber(value), + } +} + export const normalizeVariants = ( variants: ProductCreateSchemaType["variants"], regionsCurrencyMap: Record @@ -60,47 +111,12 @@ export const normalizeVariants = ( manage_inventory: !!variant.manage_inventory, allow_backorder: !!variant.allow_backorder, variant_rank: variant.variant_rank, - inventory_items: variant - .inventory!.map((i) => { - const quantity = i.required_quantity - ? castNumber(i.required_quantity) - : null - - if (!i.inventory_item_id || !quantity) { - return false - } - - return { - ...i, - required_quantity: quantity, - } - }) - .filter( - ( - item - ): item is { required_quantity: number; inventory_item_id: string } => - item !== false - ), + inventory_items: (variant.inventory || []) + .map(normalizeInventoryItem) + .filter((item): item is InventoryItem => item !== null), prices: Object.entries(variant.prices || {}) - .map(([key, value]: any) => { - if (value === "" || value === undefined) { - return undefined - } - - if (key.startsWith("reg_")) { - return { - currency_code: regionsCurrencyMap[key], - amount: castNumber(value), - rules: { region_id: key }, - } - } else { - return { - currency_code: key, - amount: castNumber(value), - } - } - }) - .filter((v) => !!v), + .map(([key, value]) => normalizePriceItem(key, value, regionsCurrencyMap)) + .filter((price): price is PriceItem => price !== null), })) } diff --git a/src/routes/products/product-detail/breadcrumb.tsx b/src/routes/products/product-detail/breadcrumb.tsx index 1abc8805..b1e70d7a 100644 --- a/src/routes/products/product-detail/breadcrumb.tsx +++ b/src/routes/products/product-detail/breadcrumb.tsx @@ -1,9 +1,9 @@ -import { HttpTypes } from "@medusajs/types" -import { UIMatch } from "react-router-dom" -import { useProduct } from "../../../hooks/api" +import type { UIMatch } from "react-router-dom" +import { useProduct } from "@hooks/api" import { PRODUCT_DETAIL_FIELDS } from "./constants" +import type { ExtendedAdminProductResponse } from "@custom-types/product" -type ProductDetailBreadcrumbProps = UIMatch +type ProductDetailBreadcrumbProps = UIMatch export const ProductDetailBreadcrumb = ( props: ProductDetailBreadcrumbProps diff --git a/src/routes/products/product-detail/components/product-additional-attribute-section/product-additional-attribute-section.tsx b/src/routes/products/product-detail/components/product-additional-attribute-section/product-additional-attribute-section.tsx index f7ee1817..753bf327 100644 --- a/src/routes/products/product-detail/components/product-additional-attribute-section/product-additional-attribute-section.tsx +++ b/src/routes/products/product-detail/components/product-additional-attribute-section/product-additional-attribute-section.tsx @@ -14,13 +14,13 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useParams } from "react-router-dom"; -import { ActionMenu } from "../../../../../components/common/action-menu"; -import { RouteDrawer } from "../../../../../components/modals"; +import { ActionMenu } from "@components/common/action-menu"; +import { RouteDrawer } from "@components/modals"; import { useProduct, useProductAttributes, useUpdateProduct, -} from "../../../../../hooks/api"; +} from "@hooks/api"; import { FormComponents } from "./components/form-components"; export const ProductAdditionalAttributeSection = () => { @@ -39,7 +39,7 @@ export const ProductAdditionalAttributeSection = () => { const form = useForm({ defaultValues: {}, }); - +console.log(product?.attribute_values, 'PRODUCT ATTRIBUTE VALUES') // Reset form when product data is loaded useEffect(() => { if (product?.attribute_values) { diff --git a/src/routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx b/src/routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx index 6813ed58..ccc2578d 100644 --- a/src/routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx +++ b/src/routes/products/product-detail/components/product-attribute-section/product-attribute-section.tsx @@ -1,14 +1,14 @@ import { PencilSquare } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Container, Heading } 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 { useExtension } from "../../../../../providers/extension-provider" +import { ActionMenu } from "@components/common/action-menu" +import { SectionRow } from "@components/common/section" +import { getFormattedCountry } from "@lib/addresses" +import { useExtension } from "@providers/extension-provider" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductAttributeSectionProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } export const ProductAttributeSection = ({ diff --git a/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx b/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx index 6475b5d4..660d860e 100644 --- a/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx +++ b/src/routes/products/product-detail/components/product-general-section/product-general-section.tsx @@ -1,5 +1,4 @@ import { PencilSquare, Trash } from "@medusajs/icons"; -import { HttpTypes } from "@medusajs/types"; import { Container, Heading, StatusBadge, usePrompt } from "@medusajs/ui"; import { useTranslation } from "react-i18next"; @@ -9,6 +8,7 @@ import { ActionMenu } from "../../../../../components/common/action-menu"; import { SectionRow } from "../../../../../components/common/section"; import { useDeleteProduct } from "../../../../../hooks/api/products"; import { useExtension } from "../../../../../providers/extension-provider"; +import type { ExtendedAdminProduct } from "@custom-types/product"; const productStatusColor = (status: string) => { switch (status) { @@ -26,7 +26,7 @@ const productStatusColor = (status: string) => { }; type ProductGeneralSectionProps = { - product: HttpTypes.AdminProduct; + product: ExtendedAdminProduct; }; export const ProductGeneralSection = ({ diff --git a/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx b/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx index ce3b2591..934c8856 100644 --- a/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx +++ b/src/routes/products/product-detail/components/product-media-section/product-media-section.tsx @@ -13,12 +13,12 @@ import { import { useState } from "react" import { useTranslation } from "react-i18next" import { Link } from "react-router-dom" -import { ActionMenu } from "../../../../../components/common/action-menu" -import { useUpdateProduct } from "../../../../../hooks/api/products" -import { HttpTypes } from "@medusajs/types" +import { ActionMenu } from "@components/common/action-menu" +import { useUpdateProduct } from "@hooks/api/products" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductMedisaSectionProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } export const ProductMediaSection = ({ product }: ProductMedisaSectionProps) => { @@ -31,7 +31,9 @@ export const ProductMediaSection = ({ product }: ProductMedisaSectionProps) => { const handleCheckedChange = (id: string) => { setSelection((prev) => { if (prev[id]) { - const { [id]: _, ...rest } = prev + const rest = { ...prev } + delete rest[id] + return rest } else { return { ...prev, [id]: true } @@ -133,10 +135,10 @@ export const ProductMediaSection = ({ product }: ProductMedisaSectionProps) => {
)} - + {`${product.title} @@ -194,10 +196,10 @@ type Media = { isThumbnail: boolean } -const getMedia = (product: Product) => { +const getMedia = (product: ExtendedAdminProduct) => { const { images = [], thumbnail } = product - const media: Media[] = images.map((image) => ({ + const media: Media[] = (images || []).map((image) => ({ id: image.id, url: image.url, isThumbnail: image.url === thumbnail, diff --git a/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx b/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx index a8ce121f..25d3bb10 100644 --- a/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx +++ b/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx @@ -4,13 +4,14 @@ 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" +import type { HttpTypes } from "@medusajs/types" +import type { ExtendedAdminProduct } from "@custom-types/product"; const OptionActions = ({ product, option, }: { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct option: HttpTypes.AdminProductOption }) => { const { t } = useTranslation() @@ -62,7 +63,7 @@ const OptionActions = ({ } type ProductOptionSectionProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } export const ProductOptionSection = ({ diff --git a/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx b/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx index 12bdf963..8ae36459 100644 --- a/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx +++ b/src/routes/products/product-detail/components/product-organization-section/product-organization-section.tsx @@ -1,14 +1,14 @@ import { PencilSquare } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Badge, Container, Heading, Tooltip } 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 { useExtension } from "../../../../../providers/extension-provider" +import { ActionMenu } from "@components/common/action-menu" +import { SectionRow } from "@components/common/section" +import { useExtension } from "@providers/extension-provider" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductOrganizationSectionProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } export const ProductOrganizationSection = ({ diff --git a/src/routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx b/src/routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx index ef046d0f..1d3d4368 100644 --- a/src/routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx +++ b/src/routes/products/product-detail/components/product-sales-channel-section/product-sales-channel-section.tsx @@ -3,10 +3,10 @@ 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" +import { ExtendedAdminProduct } from "@custom-types/product" type ProductSalesChannelSectionProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } // TODO: The fetched sales channel doesn't contain all necessary info diff --git a/src/routes/products/product-detail/components/product-shipping-profile-section/product-shipping-profile-section.tsx b/src/routes/products/product-detail/components/product-shipping-profile-section/product-shipping-profile-section.tsx index 1cfab543..dbae3280 100644 --- a/src/routes/products/product-detail/components/product-shipping-profile-section/product-shipping-profile-section.tsx +++ b/src/routes/products/product-detail/components/product-shipping-profile-section/product-shipping-profile-section.tsx @@ -1,15 +1,13 @@ import { PencilSquare, ShoppingBag } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Container, Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" -import { SidebarLink } from "../../../../../components/common/sidebar-link/sidebar-link" -import { ActionMenu } from "../../../../../components/common/action-menu" +import { SidebarLink } from "@components/common/sidebar-link/sidebar-link" +import { ActionMenu } from "@components/common/action-menu" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductShippingProfileSectionProps = { - product: HttpTypes.AdminProduct & { - shipping_profile: HttpTypes.AdminShippingProfile - } + product: ExtendedAdminProduct } export const ProductShippingProfileSection = ({ diff --git a/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx b/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx index 4452c530..3b18d491 100644 --- a/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx +++ b/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx @@ -1,5 +1,4 @@ import { Buildings, Component, PencilSquare, Trash } from "@medusajs/icons" -import { HttpTypes } from "@medusajs/types" import { Badge, clx, @@ -26,9 +25,10 @@ import { } from "../../../../../hooks/api/products" import { useQueryParams } from "../../../../../hooks/use-query-params" import { PRODUCT_VARIANT_IDS_KEY } from "../../../common/constants" +import type { ExtendedAdminProduct, ExtendedAdminProductVariant } from "@custom-types/product" type ProductVariantSectionProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } const PAGE_SIZE = 10 @@ -145,9 +145,9 @@ export const ProductVariantSection = ({ } const columnHelper = - createDataTableColumnHelper() + createDataTableColumnHelper() -const useColumns = (product: HttpTypes.AdminProduct) => { +const useColumns = (product: ExtendedAdminProduct) => { const { t } = useTranslation() const navigate = useNavigate() const { mutateAsync } = useDeleteVariantLazy(product.id) @@ -161,10 +161,11 @@ const useColumns = (product: HttpTypes.AdminProduct) => { filtered.append(key, value) } } + return filtered }, [searchParams]) - const dateColumns = useDataTableDateColumns() + const dateColumns = useDataTableDateColumns() const handleDelete = useCallback( async (id: string, title: string) => { @@ -224,12 +225,10 @@ const useColumns = (product: HttpTypes.AdminProduct) => { }, [product]) const getActions = useCallback( - (ctx: CellContext) => { - const variant = ctx.row.original as HttpTypes.AdminProductVariant & { - inventory_items: { inventory: HttpTypes.AdminInventoryItem }[] - } + (ctx: CellContext) => { + const variant = ctx.row.original - const mainActions: DataTableAction[] = [ + const mainActions: DataTableAction[] = [ { icon: , label: t("actions.edit"), @@ -248,7 +247,7 @@ const useColumns = (product: HttpTypes.AdminProduct) => { }, ] - const secondaryActions: DataTableAction[] = + const secondaryActions: DataTableAction[] = [ { icon: , @@ -303,11 +302,7 @@ const useColumns = (product: HttpTypes.AdminProduct) => { ) const getInventory = useCallback( - (variant: HttpTypes.AdminProductVariant) => { - const castVariant = variant as HttpTypes.AdminProductVariant & { - inventory_items: { inventory: HttpTypes.AdminInventoryItem }[] - } - + (variant: ExtendedAdminProductVariant) => { if (!variant.manage_inventory) { return { text: t("products.variant.inventory.notManaged"), @@ -318,9 +313,9 @@ const useColumns = (product: HttpTypes.AdminProduct) => { const quantity = variant.inventory_quantity - const inventoryItems = castVariant.inventory_items + const inventoryItems = variant.inventory_items ?.map((i) => i.inventory) - .filter(Boolean) as HttpTypes.AdminInventoryItem[] + .filter(Boolean) ?? [] const hasInventoryKit = inventoryItems.length > 1 @@ -399,7 +394,7 @@ const useColumns = (product: HttpTypes.AdminProduct) => { } const filterHelper = - createDataTableFilterHelper() + createDataTableFilterHelper() const useFilters = () => { const { t } = useTranslation() diff --git a/src/routes/products/product-detail/constants.ts b/src/routes/products/product-detail/constants.ts index e5f98171..1da24443 100644 --- a/src/routes/products/product-detail/constants.ts +++ b/src/routes/products/product-detail/constants.ts @@ -2,5 +2,5 @@ import { getLinkedFields } from "../../../dashboard-app" export const PRODUCT_DETAIL_FIELDS = getLinkedFields( "product", - "*categories,*shipping_profile,-variants" + "*categories,*shipping_profile,*variants" ) diff --git a/src/routes/products/product-detail/loader.ts b/src/routes/products/product-detail/loader.ts index fa1375a7..19482aec 100644 --- a/src/routes/products/product-detail/loader.ts +++ b/src/routes/products/product-detail/loader.ts @@ -4,11 +4,12 @@ import { productsQueryKeys } from "../../../hooks/api/products" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" import { PRODUCT_DETAIL_FIELDS } from "./constants" +import type { ExtendedAdminProductResponse } from "@custom-types/product" const productDetailQuery = (id: string) => ({ queryKey: productsQueryKeys.detail(id, { fields: PRODUCT_DETAIL_FIELDS }), queryFn: async () => - sdk.admin.product.retrieve(id, { fields: PRODUCT_DETAIL_FIELDS }), + sdk.admin.product.retrieve(id, { fields: PRODUCT_DETAIL_FIELDS }) as Promise, }) export const productLoader = async ({ params }: LoaderFunctionArgs) => { diff --git a/src/routes/products/product-detail/product-detail.tsx b/src/routes/products/product-detail/product-detail.tsx index 9702aadc..40f7539c 100644 --- a/src/routes/products/product-detail/product-detail.tsx +++ b/src/routes/products/product-detail/product-detail.tsx @@ -29,7 +29,7 @@ export const ProductDetail = () => { initialData: initialData, }, ); - +console.log(product, 'PRODUCT') const { getWidgets } = useExtension(); const after = getWidgets("product.details.after"); diff --git a/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx b/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx index 0a816040..52074270 100644 --- a/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx +++ b/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx @@ -4,12 +4,12 @@ import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import { z } from "zod" -import { HttpTypes } from "@medusajs/types" -import { Form } from "../../../../../components/common/form" -import { ChipInput } from "../../../../../components/inputs/chip-input" -import { RouteDrawer, useRouteModal } from "../../../../../components/modals" -import { KeyboundForm } from "../../../../../components/utilities/keybound-form" -import { useUpdateProductOption } from "../../../../../hooks/api/products" +import type { HttpTypes } from "@medusajs/types" +import { Form } from "@components/common/form" +import { ChipInput } from "@components/inputs/chip-input" +import { RouteDrawer, useRouteModal } from "@components/modals" +import { KeyboundForm } from "@components/utilities/keybound-form" +import { useUpdateProductOption } from "@hooks/api/products" type EditProductOptionFormProps = { option: HttpTypes.AdminProductOption @@ -29,7 +29,7 @@ export const CreateProductOptionForm = ({ const form = useForm>({ defaultValues: { title: option.title, - values: option.values.map((v: any) => v.value), + values: option.values?.map((v) => v.value) ?? [], }, resolver: zodResolver(CreateProductOptionSchema), }) diff --git a/src/routes/products/product-edit-option/product-edit-option.tsx b/src/routes/products/product-edit-option/product-edit-option.tsx index 84f10ed9..155fe20c 100644 --- a/src/routes/products/product-edit-option/product-edit-option.tsx +++ b/src/routes/products/product-edit-option/product-edit-option.tsx @@ -1,8 +1,8 @@ import { Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { json, useParams } from "react-router-dom" -import { RouteDrawer } from "../../../components/modals" -import { useProduct } from "../../../hooks/api/products" +import { RouteDrawer } from "@components/modals" +import { useProduct } from "@hooks/api/products" import { CreateProductOptionForm } from "./components/edit-product-option-form" export const ProductEditOption = () => { diff --git a/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx b/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx index 1aeec666..1d7b8b98 100644 --- a/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx +++ b/src/routes/products/product-edit/components/edit-product-form/edit-product-form.tsx @@ -1,22 +1,21 @@ -import { HttpTypes } from "@medusajs/types"; import { Button, Input, Select, Text, Textarea, toast } from "@medusajs/ui"; import { useTranslation } from "react-i18next"; import * as zod from "zod"; -import { Form } from "../../../../../components/common/form"; -import { SwitchBox } from "../../../../../components/common/switch-box"; -import { RouteDrawer, useRouteModal } from "../../../../../components/modals"; -import { KeyboundForm } from "../../../../../components/utilities/keybound-form"; -import { FormExtensionZone } from "../../../../../dashboard-app"; -import { useExtendableForm } from "../../../../../dashboard-app/forms/hooks"; -import { useUpdateProduct } from "../../../../../hooks/api/products"; -import { useDocumentDirection } from "../../../../../hooks/use-document-direction"; -import { transformNullableFormData } from "../../../../../lib/form-helpers"; -import { useExtension } from "../../../../../providers/extension-provider"; +import { Form } from "@components/common/form"; +import { SwitchBox } from "@components/common/switch-box"; +import { RouteDrawer, useRouteModal } from "@components/modals"; +import { KeyboundForm } from "@components/utilities/keybound-form"; +import { useUpdateProduct } from "@hooks/api/products"; +import { useDocumentDirection } from "@hooks/use-document-direction"; +import { transformNullableFormData } from "@lib/form-helpers"; +import { useExtension } from "@providers/extension-provider"; +import { FormExtensionZone, useExtendableForm } from "@/dashboard-app"; +import type { ExtendedAdminProduct } from "@custom-types/product"; type EditProductFormProps = { - product: HttpTypes.AdminProduct; + product: ExtendedAdminProduct; }; const EditProductSchema = zod.object({ @@ -64,7 +63,7 @@ export const EditProductForm = ({ product }: EditProductFormProps) => { title, discountable, handle, - status: status as HttpTypes.AdminProductStatus, + status: status, ...nullableData, }, { diff --git a/src/routes/products/product-edit/product-edit.tsx b/src/routes/products/product-edit/product-edit.tsx index 3648035c..c9d2dead 100644 --- a/src/routes/products/product-edit/product-edit.tsx +++ b/src/routes/products/product-edit/product-edit.tsx @@ -2,8 +2,8 @@ import { Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { RouteDrawer } from "../../../components/modals" -import { useProduct } from "../../../hooks/api/products" +import { RouteDrawer } from "@components/modals" +import { useProduct } from "@hooks/api/products" import { PRODUCT_DETAIL_FIELDS } from "../product-detail/constants" import { EditProductForm } from "./components/edit-product-form" diff --git a/src/routes/products/product-list/components/product-list-table/product-list-table.tsx b/src/routes/products/product-list/components/product-list-table/product-list-table.tsx index 05af2fab..8b2a07dc 100644 --- a/src/routes/products/product-list/components/product-list-table/product-list-table.tsx +++ b/src/routes/products/product-list/components/product-list-table/product-list-table.tsx @@ -6,7 +6,6 @@ import { useMemo } from "react" import { useTranslation } from "react-i18next" import { Link, Outlet, useLoaderData, useLocation } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" import { ActionMenu } from "../../../../../components/common/action-menu" import { _DataTable } from "../../../../../components/table/data-table" import { @@ -20,6 +19,7 @@ import { useDataTable } from "../../../../../hooks/use-data-table" import { productsLoader } from "../../loader" import { useFeatureFlag } from "../../../../../providers/feature-flag-provider" import { ConfigurableProductListTable } from "./configurable-product-list-table" +import { ExtendedAdminProduct } from "@custom-types/product" const PAGE_SIZE = 20 @@ -53,7 +53,7 @@ export const ProductListTable = () => { const columns = useColumns() const { table } = useDataTable({ - data: (products ?? []) as HttpTypes.AdminProduct[], + data: (products ?? []), columns, count, enablePagination: true, @@ -108,7 +108,7 @@ export const ProductListTable = () => { ) } -const ProductActions = ({ product }: { product: HttpTypes.AdminProduct }) => { +const ProductActions = ({ product }: { product: ExtendedAdminProduct }) => { const { t } = useTranslation() const prompt = usePrompt() const { mutateAsync } = useDeleteProduct(product.id) @@ -170,7 +170,7 @@ const ProductActions = ({ product }: { product: HttpTypes.AdminProduct }) => { ) } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() const useColumns = () => { const base = useProductTableColumns() diff --git a/src/routes/products/product-list/components/product-list-table/product-table-adapter.tsx b/src/routes/products/product-list/components/product-list-table/product-table-adapter.tsx index 3d6f5cfb..5d60ffd1 100644 --- a/src/routes/products/product-list/components/product-list-table/product-table-adapter.tsx +++ b/src/routes/products/product-list/components/product-list-table/product-table-adapter.tsx @@ -1,11 +1,11 @@ -import { HttpTypes } from "@medusajs/types" import { useProducts } from "../../../../../hooks/api/products" import { productColumnAdapter } from "../../../../../lib/table/entity-adapters" import { createTableAdapter, TableAdapter } from "../../../../../lib/table/table-adapters" import { useProductTableFilters } from "./use-product-table-filters" +import type { ExtendedAdminProduct } from "@custom-types/product" -export function createProductTableAdapter(): TableAdapter { - return createTableAdapter({ +export function createProductTableAdapter(): TableAdapter { + return createTableAdapter({ entity: "products", queryPrefix: "p", pageSize: 20, @@ -20,16 +20,22 @@ export function createProductTableAdapter(): TableAdapter { // Only keep placeholder data if the fields haven't changed - const prevFields = previousQuery?.[previousQuery.length - 1]?.query?.fields + const queryKey = previousQuery?.queryKey + const prevFields = Array.isArray(queryKey) + ? (queryKey[queryKey.length - 1] as { query?: { fields?: string } } | undefined)?.query?.fields + : undefined + if (prevFields && prevFields !== fields) { // Fields changed, don't use placeholder data return undefined } + // Fields are the same, keep previous data for smooth transitions return previousData }, } ) + return { data: products, count, isLoading, isError, error } }, getRowHref: (row) => `/products/${row.id}`, @@ -39,7 +45,7 @@ export function createProductTableAdapter(): TableAdapter { +export function useProductTableAdapter(): TableAdapter { const filters = useProductTableFilters() const adapter = createProductTableAdapter() diff --git a/src/routes/products/product-list/components/product-list-table/use-product-table-filters.tsx b/src/routes/products/product-list/components/product-list-table/use-product-table-filters.tsx index 009eeafa..9ba5c007 100644 --- a/src/routes/products/product-list/components/product-list-table/use-product-table-filters.tsx +++ b/src/routes/products/product-list/components/product-list-table/use-product-table-filters.tsx @@ -1,13 +1,15 @@ import { useMemo } from "react" import { useTranslation } from "react-i18next" import { createDataTableFilterHelper } from "@medusajs/ui" -import { HttpTypes } from "@medusajs/types" import { useDataTableDateFilters } from "../../../../../components/data-table/helpers/general/use-data-table-date-filters" import { useProductTypes } from "../../../../../hooks/api/product-types" import { useProductTags } from "../../../../../hooks/api" import { useSalesChannels } from "../../../../../hooks/api/sales-channels" +import type { ExtendedAdminProductListParams } from "@custom-types/product" -const filterHelper = createDataTableFilterHelper() +const filterHelper = createDataTableFilterHelper() + +// const filterHelper = createDataTableFilterHelper() /** * Hook to create filters in the format expected by @medusajs/ui DataTable diff --git a/src/routes/products/product-list/loader.ts b/src/routes/products/product-list/loader.ts index 60eb724f..1f07eca3 100644 --- a/src/routes/products/product-list/loader.ts +++ b/src/routes/products/product-list/loader.ts @@ -1,9 +1,9 @@ import { QueryClient } from "@tanstack/react-query" -import { HttpTypes } from "@medusajs/types" import { productsQueryKeys } from "../../../hooks/api/products" import { sdk } from "../../../lib/client" import { queryClient } from "../../../lib/query-client" +import { ExtendedAdminProductListResponse } from "@custom-types/product" const productsListQuery = () => ({ queryKey: productsQueryKeys.list({ @@ -12,7 +12,7 @@ const productsListQuery = () => ({ is_giftcard: false, }), queryFn: async () => - sdk.admin.product.list({ limit: 20, offset: 0, is_giftcard: false }), + sdk.admin.product.list({ limit: 20, offset: 0, is_giftcard: false }) as Promise, }) export const productsLoader = (client: QueryClient) => { @@ -20,7 +20,7 @@ export const productsLoader = (client: QueryClient) => { const query = productsListQuery() return ( - queryClient.getQueryData( + queryClient.getQueryData( query.queryKey ) ?? (await client.fetchQuery(query)) ) diff --git a/src/routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx b/src/routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx index 74cc32a9..725c4725 100644 --- a/src/routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx +++ b/src/routes/products/product-media/components/edit-product-media-form/edit-product-media-form.tsx @@ -1,7 +1,7 @@ import { Fragment, useCallback, useState } from "react"; import { ThumbnailBadge } from "@medusajs/icons"; -import { HttpTypes } from "@medusajs/types"; +import type { HttpTypes } from "@medusajs/types"; import { Button, Checkbox, @@ -51,9 +51,10 @@ import { MediaSchema, } from "../../../product-create/constants"; import { EditProductMediaSchemaType } from "../../../product-create/types"; +import type { ExtendedAdminProduct } from "@custom-types/product"; type ProductMediaViewProps = { - product: HttpTypes.AdminProduct; + product: ExtendedAdminProduct; }; type Media = z.infer; @@ -125,6 +126,7 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => { type: "invalid_file", message: t("products.media.failedToUpload"), }); + return { files: [] }; }); uploaded = uploads; @@ -135,6 +137,7 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => { if (toUploadIndex > -1) { return { ...entry, url: uploaded[toUploadIndex]?.url }; } + return entry; }); const thumbnail = withUpdatedUrls.find((m) => m.isThumbnail)?.url; @@ -160,8 +163,12 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => { (id: string) => { return (val: boolean) => { if (!val) { - const { [id]: _, ...rest } = selection; - setSelection(rest); + setSelection(prev => { + const next = { ...prev }; + delete next[id]; + + return next; + }); } else { setSelection((prev) => ({ ...prev, [id]: true })); } diff --git a/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx b/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx index 5147dc04..223f6b42 100644 --- a/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx +++ b/src/routes/products/product-media/components/product-media-gallery/product-media-gallery.tsx @@ -10,12 +10,12 @@ import { useCallback, useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { Link, useLocation } from "react-router-dom" -import { HttpTypes } from "@medusajs/types" -import { RouteFocusModal } from "../../../../../components/modals" -import { useUpdateProduct } from "../../../../../hooks/api/products" +import { RouteFocusModal } from "@components/modals" +import { useUpdateProduct } from "@hooks/api/products" +import type { ExtendedAdminProduct, ExtendedAdminProductImage } from "@custom-types/product" type ProductMediaGalleryProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } export const ProductMediaGallery = ({ product }: ProductMediaGalleryProps) => { @@ -305,7 +305,7 @@ type Media = { } const getMedia = ( - images: HttpTypes.AdminProductImage[] | null, + images: ExtendedAdminProductImage[] | null, thumbnail: string | null ) => { const media: Media[] = diff --git a/src/routes/products/product-media/components/product-media-view/product-media-view.tsx b/src/routes/products/product-media/components/product-media-view/product-media-view.tsx index d21b71d8..91fc525e 100644 --- a/src/routes/products/product-media/components/product-media-view/product-media-view.tsx +++ b/src/routes/products/product-media/components/product-media-view/product-media-view.tsx @@ -2,10 +2,10 @@ import { useSearchParams } from "react-router-dom" import { EditProductMediaForm } from "../edit-product-media-form" import { ProductMediaGallery } from "../product-media-gallery" import { ProductMediaViewContext } from "./product-media-view-context" -import { HttpTypes } from "@medusajs/types" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductMediaViewProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } enum View { @@ -44,7 +44,7 @@ export const ProductMediaView = ({ product }: ProductMediaViewProps) => { ) } -const renderView = (view: View, product: HttpTypes.AdminProduct) => { +const renderView = (view: View, product: ExtendedAdminProduct) => { switch (view) { case View.GALLERY: return diff --git a/src/routes/products/product-media/product-media.tsx b/src/routes/products/product-media/product-media.tsx index 5ae2b49e..6614a1c2 100644 --- a/src/routes/products/product-media/product-media.tsx +++ b/src/routes/products/product-media/product-media.tsx @@ -1,7 +1,7 @@ import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { RouteFocusModal } from "../../../components/modals" -import { useProduct } from "../../../hooks/api/products" +import { RouteFocusModal } from "@components/modals" +import { useProduct } from "@hooks/api/products" import { ProductMediaView } from "./components/product-media-view" export const ProductMedia = () => { diff --git a/src/routes/products/product-organization/components/product-organization-form/product-organization-form.tsx b/src/routes/products/product-organization/components/product-organization-form/product-organization-form.tsx index 78f1b8c7..52898d62 100644 --- a/src/routes/products/product-organization/components/product-organization-form/product-organization-form.tsx +++ b/src/routes/products/product-organization/components/product-organization-form/product-organization-form.tsx @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { Button, toast } from "@medusajs/ui" import { useTranslation } from "react-i18next" import * as zod from "zod" @@ -16,9 +15,10 @@ import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { sdk } from "../../../../../lib/client" import { useExtension } from "../../../../../providers/extension-provider" import { CategoryCombobox } from "../../../common/components/category-combobox" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductOrganizationFormProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } const ProductOrganizationSchema = zod.object({ diff --git a/src/routes/products/product-organization/product-organization.tsx b/src/routes/products/product-organization/product-organization.tsx index ca8edeae..571c6878 100644 --- a/src/routes/products/product-organization/product-organization.tsx +++ b/src/routes/products/product-organization/product-organization.tsx @@ -2,8 +2,8 @@ import { Heading } from "@medusajs/ui" import { useTranslation } from "react-i18next" import { useParams } from "react-router-dom" -import { RouteDrawer } from "../../../components/modals" -import { useProduct } from "../../../hooks/api/products" +import { RouteDrawer } from "@components/modals" +import { useProduct } from "@hooks/api/products" import { PRODUCT_DETAIL_FIELDS } from "../product-detail/constants" import { ProductOrganizationForm } from "./components/product-organization-form" diff --git a/src/routes/products/product-prices/pricing-edit.tsx b/src/routes/products/product-prices/pricing-edit.tsx index 7f3f5239..f964d35d 100644 --- a/src/routes/products/product-prices/pricing-edit.tsx +++ b/src/routes/products/product-prices/pricing-edit.tsx @@ -1,5 +1,4 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { HttpTypes } from "@medusajs/types" import { Button } from "@medusajs/ui" import { useMemo } from "react" import { useForm } from "react-hook-form" @@ -12,6 +11,7 @@ import { useUpdateProductVariantsBatch } from "../../../hooks/api/products" import { useRegions } from "../../../hooks/api/regions" import { castNumber } from "../../../lib/cast-number" import { VariantPricingForm } from "../common/variant-pricing-form" +import type { ExtendedAdminProduct } from "@custom-types/product" export const UpdateVariantPricesSchema = zod.object({ variants: zod.array( @@ -31,7 +31,7 @@ export const PricingEdit = ({ product, variantId, }: { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct variantId?: string }) => { const { t } = useTranslation() @@ -41,11 +41,12 @@ export const PricingEdit = ({ const { regions } = useRegions({ limit: 9999 }) const regionsCurrencyMap = useMemo(() => { if (!regions?.length) { - return {} + return {} as Record } - return regions.reduce((acc, reg) => { + return regions.reduce>((acc, reg) => { acc[reg.id] = reg.currency_code + return acc }, {}) }, [regions]) @@ -56,45 +57,54 @@ export const PricingEdit = ({ const form = useForm({ defaultValues: { - variants: variants?.map((variant: any) => ({ + variants: variants?.map((variant) => ({ title: variant.title, - prices: variant.prices.reduce((acc: any, price: any) => { + prices: variant.prices?.reduce>((acc, price) => { if (price.rules?.region_id) { acc[price.rules.region_id] = price.amount } else { acc[price.currency_code] = price.amount } + return acc - }, {}), - })) as any, + }, {}) ?? {}, + })), }, resolver: zodResolver(UpdateVariantPricesSchema, {}), }) const handleSubmit = form.handleSubmit(async (values) => { + if (!variants) { + return + } + const reqData = values.variants.map((variant, ind) => ({ id: variants[ind].id, prices: Object.entries(variant.prices || {}) .filter( - ([_, value]) => value !== "" && typeof value !== "undefined" // deleted cells + (entry): entry is [string, string | number] => { + const [, value] = entry + + return value !== "" && typeof value !== "undefined" + } ) - .map(([currencyCodeOrRegionId, value]: any) => { + .map(([currencyCodeOrRegionId, value]) => { const regionId = currencyCodeOrRegionId.startsWith("reg_") ? currencyCodeOrRegionId : undefined const currencyCode = currencyCodeOrRegionId.startsWith("reg_") - ? regionsCurrencyMap[regionId] + ? (regionId ? regionsCurrencyMap[regionId] : currencyCodeOrRegionId) : currencyCodeOrRegionId - let existingId = undefined + let existingId: string | undefined = undefined if (regionId) { - existingId = variants?.[ind]?.prices?.find( - (p) => p.rules["region_id"] === regionId + existingId = variants[ind]?.prices?.find( + (p) => p.rules?.["region_id"] === regionId )?.id } else { - existingId = variants?.[ind]?.prices?.find( + existingId = variants[ind]?.prices?.find( (p) => p.currency_code === currencyCode && Object.keys(p.rules ?? {}).length === 0 diff --git a/src/routes/products/product-prices/product-prices.tsx b/src/routes/products/product-prices/product-prices.tsx index b6f2fe77..474e5786 100644 --- a/src/routes/products/product-prices/product-prices.tsx +++ b/src/routes/products/product-prices/product-prices.tsx @@ -1,7 +1,7 @@ import { useParams } from "react-router-dom" -import { RouteFocusModal } from "../../../components/modals" -import { useProduct } from "../../../hooks/api/products" +import { RouteFocusModal } from "@components/modals" +import { useProduct } from "@hooks/api/products" import { PricingEdit } from "./pricing-edit" export const ProductPrices = () => { diff --git a/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx b/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx index 9744d043..a49b9533 100644 --- a/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx +++ b/src/routes/products/product-sales-channels/components/edit-sales-channels-form/edit-sales-channels-form.tsx @@ -16,9 +16,10 @@ import { } from "../../../../../components/modals" import { useUpdateProduct } from "../../../../../hooks/api/products" import { useSalesChannels } from "../../../../../hooks/api/sales-channels" +import { ExtendedAdminProduct } from "@custom-types/product" type EditSalesChannelsFormProps = { - product: HttpTypes.AdminProduct + product: ExtendedAdminProduct } const EditSalesChannelsSchema = zod.object({ @@ -46,6 +47,7 @@ export const EditSalesChannelsForm = ({ const initialState = product.sales_channels?.reduce((acc, curr) => { acc[curr.id] = true + return acc }, {} as RowSelectionState) ?? {} diff --git a/src/routes/products/product-shipping-profile/components/product-organization-form/product-shipping-profile-form.tsx b/src/routes/products/product-shipping-profile/components/product-organization-form/product-shipping-profile-form.tsx index 8625cab6..82ebaf49 100644 --- a/src/routes/products/product-shipping-profile/components/product-organization-form/product-shipping-profile-form.tsx +++ b/src/routes/products/product-shipping-profile/components/product-organization-form/product-shipping-profile-form.tsx @@ -1,4 +1,3 @@ -import { HttpTypes } from "@medusajs/types" import { Button, toast } from "@medusajs/ui" import { useTranslation } from "react-i18next" import * as zod from "zod" @@ -13,11 +12,10 @@ import { sdk } from "../../../../../lib/client" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { useEffect } from "react" +import type { ExtendedAdminProduct } from "@custom-types/product" type ProductShippingProfileFormProps = { - product: HttpTypes.AdminProduct & { - shipping_profile?: HttpTypes.AdminShippingProfile - } + product: ExtendedAdminProduct } const ProductShippingProfileSchema = zod.object({ diff --git a/src/routes/promotions/common/constants.ts b/src/routes/promotions/common/constants.ts new file mode 100644 index 00000000..6fed1a01 --- /dev/null +++ b/src/routes/promotions/common/constants.ts @@ -0,0 +1,12 @@ +import type { PromotionRuleOperatorValues } from "@medusajs/types" + +export const PROMOTION_RULE_OPERATORS = [ + "gt", + "lt", + "eq", + "ne", + "in", + "lte", + "gte", +] as const satisfies readonly PromotionRuleOperatorValues[] + diff --git a/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx b/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx index 3942d870..a33a51da 100644 --- a/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx +++ b/src/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form.tsx @@ -1,20 +1,21 @@ import { zodResolver } from "@hookform/resolvers/zod" -import { PromotionDTO, PromotionRuleDTO } from "@medusajs/types" +import type { HttpTypes, PromotionRuleDTO } from "@medusajs/types" import { Button } from "@medusajs/ui" import { useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { RouteDrawer } from "../../../../../../components/modals" -import { KeyboundForm } from "../../../../../../components/utilities/keybound-form" -import { RuleTypeValues } from "../../edit-rules" +import type { RuleToRemove } from "@custom-types/promotion/common" +import { RouteDrawer } from "@/components/modals" +import { KeyboundForm } from "@/components/utilities/keybound-form" +import type { RuleTypeValues } from "../../edit-rules" import { RulesFormField } from "../rules-form-field" -import { EditRules, EditRulesType } from "./form-schema" +import { EditRules, type EditRulesType } from "./form-schema" type EditPromotionFormProps = { - promotion: PromotionDTO + promotion: HttpTypes.AdminPromotion rules: PromotionRuleDTO[] ruleType: RuleTypeValues - handleSubmit: any + handleSubmit: (rulesToRemove?: RuleToRemove[]) => (data: EditRulesType) => Promise isSubmitting: boolean } @@ -25,20 +26,22 @@ export const EditRulesForm = ({ isSubmitting, }: EditPromotionFormProps) => { const { t } = useTranslation() - const [rulesToRemove, setRulesToRemove] = useState([]) + const [rulesToRemove, setRulesToRemove] = useState([]) const form = useForm({ defaultValues: { rules: [], type: promotion.type, application_method: { - target_type: promotion.application_method?.target_type, + target_type: promotion.application_method?.target_type || "items", }, }, resolver: zodResolver(EditRules), }) - const handleFormSubmit = form.handleSubmit(handleSubmit(rulesToRemove)) + const handleFormSubmit = form.handleSubmit((data) => { + return handleSubmit(rulesToRemove)(data) + }) return ( @@ -48,7 +51,7 @@ export const EditRulesForm = ({ > - (rules || []).map((rule) => ({ - id: rule.id, - required: rule.required, - field_type: rule.field_type, - disguised: rule.disguised, - attribute: rule.attribute!, - operator: rule.operator!, - values: - rule.field_type === "number" || rule.operator === "eq" - ? typeof rule.values === "object" - ? rule.values[0]?.value - : rule.values - : rule?.values?.map((v: { value: string }) => v.value!), - })) + +export const generateRuleAttributes = (rules?: HttpTypes.AdminPromotionRule[]) => + (rules || []).map((rule) => { + const extendedRule = rule as ExtendedAdminPromotionRule + + const values = + extendedRule.field_type === "number" || extendedRule.operator === "eq" + ? Array.isArray(extendedRule.values) && extendedRule.values.length > 0 + ? extendedRule.values[0]?.value + : typeof extendedRule.values === "string" || typeof extendedRule.values === "number" + ? extendedRule.values + : "" + : Array.isArray(extendedRule.values) + ? extendedRule.values.map((v) => v.value ?? "").filter(Boolean) + : [] + + return { + id: extendedRule.id, + required: extendedRule.required, + field_type: extendedRule.field_type, + disguised: extendedRule.disguised, + attribute: extendedRule.attribute ?? "", + operator: extendedRule.operator ?? "", + values: values as string | number | string[], + } + }) diff --git a/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/edit-rules-wrapper.tsx b/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/edit-rules-wrapper.tsx index 0e2328a7..625962e7 100644 --- a/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/edit-rules-wrapper.tsx +++ b/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/edit-rules-wrapper.tsx @@ -1,23 +1,22 @@ -import { +import type { CreatePromotionRuleDTO, - PromotionDTO, + HttpTypes, PromotionRuleDTO, - PromotionRuleOperatorValues, - PromotionRuleResponse, } from "@medusajs/types" -import { useRouteModal } from "../../../../../../components/modals" +import type { RuleToRemove } from "@custom-types/promotion/common" +import { useRouteModal } from "@/components/modals" import { usePromotionAddRules, usePromotionRemoveRules, usePromotionUpdateRules, useUpdatePromotion, -} from "../../../../../../hooks/api/promotions" -import { RuleTypeValues } from "../../edit-rules" -import { EditRulesForm } from "../edit-rules-form" -import { getRuleValue } from "./utils" +} from "@/hooks/api/promotions" +import type { RuleTypeValues } from "@/routes/promotions/common/edit-rules/edit-rules" +import { EditRulesForm } from "@/routes/promotions/common/edit-rules/components/edit-rules-form/edit-rules-form" +import type { EditRulesType } from "@/routes/promotions/common/edit-rules/components/edit-rules-form/form-schema" type EditPromotionFormProps = { - promotion: PromotionDTO + promotion: HttpTypes.AdminPromotion rules: PromotionRuleDTO[] ruleType: RuleTypeValues } @@ -42,11 +41,9 @@ export const EditRulesWrapper = ({ const { mutateAsync: updatePromotionRules, isPending } = usePromotionUpdateRules(promotion.id, ruleType) - const handleSubmit = ( - rulesToRemove?: { id: string; disguised: boolean; attribute: string }[] - ) => { - return async function (data: { rules: PromotionRuleResponse[] }) { - const applicationMethodData: Record = {} + const handleSubmit = (rulesToRemove?: RuleToRemove[]) => { + return async function (data: EditRulesType) { + const applicationMethodData: Record = {} const { rules: allRules = [] } = data const disguisedRules = allRules.filter((rule) => rule.disguised) const disguisedRulesToRemove = @@ -56,7 +53,12 @@ export const EditRulesWrapper = ({ // database, they are currently all under application_method. If more of these are coming // up, abstract this away. for (const rule of disguisedRules) { - applicationMethodData[rule.attribute] = getRuleValue(rule) + const value = Array.isArray(rule.values) + ? rule.values[0] || null + : rule.values || null + + applicationMethodData[rule.attribute] = + rule.field_type === "number" && value ? Number(value) : value } for (const rule of disguisedRulesToRemove) { @@ -66,46 +68,85 @@ export const EditRulesWrapper = ({ // This variable will contain the rules that are actual rule objects, without the disguised // objects const rulesData = allRules.filter((rule) => !rule.disguised) - const rulesToCreate: CreatePromotionRuleDTO[] = rulesData.filter( - (rule) => !("id" in rule) + + const existingRuleIds = new Set(rules.map((r) => r.id).filter(Boolean)) + + const currentFormRuleIds = new Set( + rulesData + .map((r) => r.id) + .filter((id): id is string => typeof id === "string" && id !== "") + ) + + const explicitlyMarkedForRemoval = new Set( + (rulesToRemove || []).map((r) => r.id).filter(Boolean) ) + + const rulesToRemoveIds = new Set([ + ...explicitlyMarkedForRemoval, + ...rules + .map((r) => r.id) + .filter((id): id is string => Boolean(id) && !currentFormRuleIds.has(id)), + ]) + + // Rules to create: no ID or ID doesn't exist in current promotion + const rulesToCreate = rulesData.filter( + (rule) => !rule.id || rule.id === "" || !existingRuleIds.has(rule.id) + ) + + // Rules to update: have ID and ID exists in current promotion const rulesToUpdate = rulesData.filter( - (rule: { id: string }) => typeof rule.id === "string" + (rule): rule is EditRulesType["rules"][number] & { id: string } => + typeof rule.id === "string" && rule.id !== "" && existingRuleIds.has(rule.id) ) if (Object.keys(applicationMethodData).length) { await updatePromotion({ application_method: applicationMethodData, - } as any) + }) } - rulesToCreate.length && - (await addPromotionRules({ - rules: rulesToCreate.map((rule) => { + if (rulesToCreate.length) { + await addPromotionRules({ + rules: rulesToCreate.map((rule): CreatePromotionRuleDTO => { + const values: string | string[] = Array.isArray(rule.values) + ? rule.values.map((v) => String(v)) + : typeof rule.values === "number" + ? String(rule.values) + : rule.values + return { attribute: rule.attribute, operator: rule.operator, - values: rule.values, - } as any + values, + } }), - })) + }) + } + + if (rulesToRemoveIds.size > 0) { + await removePromotionRules({ + rule_ids: Array.from(rulesToRemoveIds) as string[], + }) + } - rulesToRemove?.length && - (await removePromotionRules({ - rule_ids: rulesToRemove.map((r) => r.id).filter(Boolean), - })) + if (rulesToUpdate.length) { + await updatePromotionRules({ + rules: rulesToUpdate.map((rule) => { + const values: string | string[] = Array.isArray(rule.values) + ? rule.values.map((v) => String(v)) + : typeof rule.values === "number" + ? String(rule.values) + : rule.values - rulesToUpdate.length && - (await updatePromotionRules({ - rules: rulesToUpdate.map((rule: PromotionRuleResponse) => { return { - id: rule.id!, + id: rule.id, attribute: rule.attribute, - operator: rule.operator as PromotionRuleOperatorValues, - values: rule.values as unknown as string | string[], + operator: rule.operator, + values, } }), - })) + }) + } handleSuccess() } diff --git a/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/utils.ts b/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/utils.ts index b88d48b9..d2540ec3 100644 --- a/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/utils.ts +++ b/src/routes/promotions/common/edit-rules/components/edit-rules-wrapper/utils.ts @@ -1,9 +1,19 @@ -import { PromotionRuleResponse } from "@medusajs/types" +import type { ExtendedAdminPromotionRule } from "@custom-types/promotion/common" -export const getRuleValue = (rule: PromotionRuleResponse) => { +export const getRuleValue = (rule: ExtendedAdminPromotionRule): string | number | null => { if (rule.field_type === "number") { - return parseInt(rule.values as unknown as string) + const value = Array.isArray(rule.values) && rule.values.length > 0 + ? rule.values[0]?.value + : typeof rule.values === "string" + ? rule.values + : null + + return value ? parseInt(value, 10) : null } - return rule.values + if (Array.isArray(rule.values)) { + return rule.values.length > 0 ? rule.values[0]?.value ?? null : null + } + + return typeof rule.values === "string" ? rule.values : null } diff --git a/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx b/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx index baf4223f..bc0a3d6e 100644 --- a/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx +++ b/src/routes/promotions/common/edit-rules/components/rule-value-form-field/rule-value-form-field.tsx @@ -177,7 +177,7 @@ export const RuleValueFormField = ({ ? t("labels.selectValue") : t("labels.selectValues") } - disabled={!watchOperator} + disabled={!fieldRule.attribute || !watchOperator} onChange={onChange} data-testid={`${testIdBase}-combobox`} /> diff --git a/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx b/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx index 850f185b..cbcea1cd 100644 --- a/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx +++ b/src/routes/promotions/common/edit-rules/components/rules-form-field/rules-form-field.tsx @@ -1,31 +1,33 @@ import { XMarkMini } from "@medusajs/icons" -import { PromotionDTO } from "@medusajs/types" +import type { HttpTypes, PromotionRuleOperatorValues } from "@medusajs/types" import { Badge, Button, Heading, IconButton, Select, Text } from "@medusajs/ui" import { forwardRef, Fragment, useEffect, useRef } from "react" -import { +import type { ControllerRenderProps, - useFieldArray, + FieldValues, UseFormReturn, - useWatch, } from "react-hook-form" +import { useFieldArray, useWatch } from "react-hook-form" import { useTranslation } from "react-i18next" -import { Form } from "../../../../../../components/common/form" +import type { FormRule, RuleToRemove } from "@custom-types/promotion/common" +import { Form } from "@/components/common/form" import { usePromotionRuleAttributes, usePromotionRules, -} from "../../../../../../hooks/api/promotions" -import { useDocumentDirection } from "../../../../../../hooks/use-document-direction" -import { CreatePromotionSchemaType } from "../../../../promotion-create/components/create-promotion-form/form-schema" -import { generateRuleAttributes } from "../edit-rules-form/utils" -import { RuleValueFormField } from "../rule-value-form-field" +} from "@/hooks/api/promotions" +import { useDocumentDirection } from "@/hooks/use-document-direction" +import type { CreatePromotionSchemaType } from "@/routes/promotions/promotion-create/components/create-promotion-form/form-schema" +import type { EditRulesType } from "@/routes/promotions/common/edit-rules/components/edit-rules-form/form-schema" +import { generateRuleAttributes } from "@/routes/promotions/common/edit-rules/components/edit-rules-form/utils" +import { RuleValueFormField } from "@/routes/promotions/common/edit-rules/components/rule-value-form-field" import { requiredProductRule } from "./constants" type RulesFormFieldType = { - promotion?: PromotionDTO - form: UseFormReturn + promotion?: HttpTypes.AdminPromotion + form: UseFormReturn | UseFormReturn ruleType: "rules" | "target-rules" | "buy-rules" - setRulesToRemove?: any - rulesToRemove?: any + setRulesToRemove?: (rules: RuleToRemove[]) => void + rulesToRemove?: RuleToRemove[] scope?: | "application_method.buy_rules" | "rules" @@ -46,46 +48,92 @@ export const RulesFormField = ({ const { t } = useTranslation() const direction = useDocumentDirection() + + // Type-safe form data access based on formType const formData = form.getValues() + const formTypeValue = formType === "create" + ? (formData as CreatePromotionSchemaType).type + : (formData as EditRulesType).type + + const applicationMethodTargetTypeValue = formType === "create" + ? (formData as CreatePromotionSchemaType).application_method?.target_type + : (formData as EditRulesType).application_method?.target_type + const { attributes } = usePromotionRuleAttributes( ruleType, - formData.type, - formData.application_method?.target_type + formTypeValue, + applicationMethodTargetTypeValue ) - const { fields, append, remove, update, replace } = useFieldArray({ - control: form.control, - name: scope, - keyName: scope, - }) - - const promotionType = useWatch({ - control: form.control, - name: "type", - defaultValue: promotion?.type, - }) - - const applicationMethodType = useWatch({ - control: form.control, - name: "application_method.type", - defaultValue: promotion?.application_method?.type, - }) - - const applicationMethodTargetType = useWatch({ - control: form.control, - name: "application_method.target_type", - defaultValue: promotion?.application_method?.target_type, - }) + // Type-safe field array - use conditional logic based on formType + // Helper to get the correct field name based on scope + const getFieldName = (): "rules" | "application_method.buy_rules" | "application_method.target_rules" => { + if (scope === "rules") { + return "rules" + } + if (scope === "application_method.buy_rules") { + return "application_method.buy_rules" + } + + return "application_method.target_rules" + } + + const fieldName = getFieldName() + + const createFormFieldArray = formType === "create" + ? useFieldArray({ + control: (form as UseFormReturn).control, + name: fieldName as "rules" | "application_method.buy_rules" | "application_method.target_rules", + }) + : useFieldArray({ + control: (form as UseFormReturn).control, + name: "rules" as const, // EditRulesType only supports "rules" + }) + + const { fields, append, remove, update, replace } = createFormFieldArray + + // Type-safe watch - handle union type properly + const promotionType = formType === "create" + ? useWatch({ + control: (form as UseFormReturn).control, + name: "type", + defaultValue: promotion?.type, + }) + : useWatch({ + control: (form as UseFormReturn).control, + name: "type", + defaultValue: promotion?.type, + }) + + const applicationMethodType = formType === "create" + ? useWatch({ + control: (form as UseFormReturn).control, + name: "application_method.type", + defaultValue: promotion?.application_method?.type, + }) + : undefined + + const applicationMethodTargetType = formType === "create" + ? useWatch({ + control: (form as UseFormReturn).control, + name: "application_method.target_type", + defaultValue: promotion?.application_method?.target_type, + }) + : useWatch({ + control: (form as UseFormReturn).control, + name: "application_method.target_type", + defaultValue: promotion?.application_method?.target_type, + }) const query: Record = promotionType ? { promotion_type: promotionType, - application_method_type: applicationMethodType, - application_method_target_type: applicationMethodTargetType, + ...(applicationMethodType && { application_method_type: applicationMethodType }), + ...(applicationMethodTargetType && { application_method_target_type: applicationMethodTargetType }), } : {} - const { rules, isLoading } = usePromotionRules( + const { rules: apiRules, isLoading } = usePromotionRules( promotion?.id || null, ruleType, query, @@ -94,6 +142,9 @@ export const RulesFormField = ({ } ) + // Type-safe conversion: API returns HttpTypes.AdminPromotionRule[] but we need ExtendedAdminPromotionRule[] + const rules = apiRules as HttpTypes.AdminPromotionRule[] | undefined + useEffect(() => { if (isLoading) { return @@ -108,29 +159,37 @@ export const RulesFormField = ({ } if (ruleType === "rules" && !fields.length) { - form.resetField("rules") - - replace(generateRuleAttributes(rules) as any) + if (formType === "create") { + (form as UseFormReturn).resetField("rules") + } else { + (form as UseFormReturn).resetField("rules") + } + replace(generateRuleAttributes(rules) as CreatePromotionSchemaType["rules"] | EditRulesType["rules"]) } if (ruleType === "buy-rules" && !fields.length) { - form.resetField("application_method.buy_rules") + // EditRulesType doesn't have buy_rules, only CreatePromotionSchemaType does + if (formType === "create") { + (form as UseFormReturn).resetField("application_method.buy_rules") + } const rulesToAppend = promotion?.id || promotionType === "standard" ? rules - : [...rules, requiredProductRule] + : [...(rules || []), requiredProductRule as HttpTypes.AdminPromotionRule] - replace(generateRuleAttributes(rulesToAppend) as any) + replace(generateRuleAttributes(rulesToAppend) as CreatePromotionSchemaType["application_method"]["buy_rules"]) } if (ruleType === "target-rules" && !fields.length) { - form.resetField("application_method.target_rules") + if (formType === "create") { + (form as UseFormReturn).resetField("application_method.target_rules") + } const rulesToAppend = promotion?.id || promotionType === "standard" ? rules - : [...rules, requiredProductRule] + : [...(rules || []), requiredProductRule as HttpTypes.AdminPromotionRule] - replace(generateRuleAttributes(rulesToAppend) as any) + replace(generateRuleAttributes(rulesToAppend) as CreatePromotionSchemaType["application_method"]["target_rules"]) } initialRulesSet.current = true @@ -149,26 +208,35 @@ export const RulesFormField = ({ return (
- {t( - ruleType === "target-rules" - ? `promotions.fields.conditions.${ruleType}.${applicationMethodTargetType}.title` - : `promotions.fields.conditions.${ruleType}.title` - )} + {ruleType === "target-rules" && applicationMethodTargetType + ? applicationMethodTargetType === "order" + ? t("promotions.fields.conditions.target-rules.order.title") + : applicationMethodTargetType === "shipping_methods" + ? t("promotions.fields.conditions.target-rules.shipping_methods.title") + : t("promotions.fields.conditions.target-rules.items.title") + : ruleType === "rules" + ? t("promotions.fields.conditions.rules.title") + : t("promotions.fields.conditions.buy-rules.title")} - {t( - ruleType === "target-rules" - ? `promotions.fields.conditions.${ruleType}.${applicationMethodTargetType}.description` - : `promotions.fields.conditions.${ruleType}.description` - )} + {ruleType === "target-rules" && applicationMethodTargetType + ? applicationMethodTargetType === "order" + ? t("promotions.fields.conditions.target-rules.order.description") + : applicationMethodTargetType === "shipping_methods" + ? t("promotions.fields.conditions.target-rules.shipping_methods.description") + : t("promotions.fields.conditions.target-rules.items.description") + : ruleType === "rules" + ? t("promotions.fields.conditions.rules.description") + : t("promotions.fields.conditions.buy-rules.description")} {fields.map((fieldRule, index) => { - const identifier = fieldRule.id + const typedFieldRule = fieldRule as FormRule + const identifier = typedFieldRule.id ?? `field-${index}` return ( - +
field.attribute) || [] + fields?.map((field) => (field as FormRule).attribute).filter(Boolean) as string[] || [] + const attributeOptions = attributes?.filter((attr) => { - if (attr.value === fieldRule.attribute) { + if (attr.value === typedFieldRule.attribute) { return true } return !existingAttributes.includes(attr.value) }) || [] - const disabled = !!fieldRule.required + const disabled = !!typedFieldRule.required const onValueChange = (e: string) => { const currentAttributeOption = attributeOptions.find( (ao) => ao.id === e ) - const fieldRuleOverrides: typeof fieldRule = { - ...fieldRule, + const fieldRuleOverrides: FormRule = { + ...typedFieldRule, disguised: currentAttributeOption?.disguised || false, } if (currentAttributeOption?.operators?.length === 1) { fieldRuleOverrides.operator = - currentAttributeOption.operators[0].value + currentAttributeOption.operators[0].value as PromotionRuleOperatorValues } if (fieldRuleOverrides.operator === "eq") { @@ -215,7 +284,7 @@ export const RulesFormField = ({ return ( - {fieldRule.required && ( + {typedFieldRule.required && (

{t("promotions.form.required")} @@ -229,7 +298,7 @@ export const RulesFormField = ({ dir={direction} {...fieldProps} onValueChange={onValueChange} - disabled={fieldRule.required} + disabled={typedFieldRule.required} data-testid={`rules-form-field-rule-${ruleType}-${index}-attribute-select`} > ao.value === fieldRule.attribute - )?.label || "" + (ao) => ao.value === typedFieldRule.attribute + )?.label ?? "" } field={field} /> @@ -282,7 +351,7 @@ export const RulesFormField = ({ const { onChange, ref, ...fieldProps } = field const currentAttributeOption = attributes?.find( - (attr) => attr.value === fieldRule.attribute + (attr) => attr.value === typedFieldRule.attribute ) const options = @@ -293,16 +362,17 @@ export const RulesFormField = ({ })) || [] const disabled = - !!fieldRule.attribute && options?.length <= 1 + !!typedFieldRule.attribute && options?.length <= 1 return ( {!disabled ? (