diff --git a/integration-tests/api/__tests__/admin/reservations/index.spec.ts b/integration-tests/api/__tests__/admin/reservations/index.spec.ts index 4d50a4a331..57a73239dd 100644 --- a/integration-tests/api/__tests__/admin/reservations/index.spec.ts +++ b/integration-tests/api/__tests__/admin/reservations/index.spec.ts @@ -190,6 +190,25 @@ medusaIntegrationTestRunner({ }) }) + it("should update a reservation item description", async () => { + await breaking(null, async () => { + const reservationResponse = await api.post( + `/admin/reservations/${reservationId}`, + { + description: "test description 1", + }, + adminHeaders + ) + + expect(reservationResponse.status).toEqual(200) + expect(reservationResponse.data.reservation).toEqual( + expect.objectContaining({ + description: "test description 1", + }) + ) + }) + }) + it("should update a reservation item", async () => { await breaking(null, async () => { const reservationResponse = await api.post( diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index c4eb3d41b8..c78a3b87fc 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -343,7 +343,25 @@ "locationLevels": "Location levels", "associatedVariants": "Associated variants", "manageLocations": "Manage locations", - "deleteWarning": "You are about to delete an inventory item. This action cannot be undone." + "deleteWarning": "You are about to delete an inventory item. This action cannot be undone.", + "reservation": { + "header": "Reservation of {{itemName}}", + "editItemDetails": "Edit item details", + "orderID": "Order ID", + "description": "Description", + "location": "Location", + "inStockAtLocation": "In stock at this location", + "availableAtLocation": "Available at this location", + "reservedAtLocation": "Reserved at this location", + "reservedAmount": "Reserve amount", + "create": "Create reservation", + "itemToReserve": "Item to reserve", + "quantityPlaceholder": "How many do you want to reserve?", + "descriptionPlaceholder": "What type of reservation is this?", + "successToast": "Reservation was successfully created.", + "updateSuccessToast": "Reservation was successfully updated.", + "deleteSuccessToast": "Reservation was successfully deleted." + } }, "giftCards": { "domain": "Gift Cards", diff --git a/packages/admin-next/dashboard/src/hooks/api/inventory.tsx b/packages/admin-next/dashboard/src/hooks/api/inventory.tsx index 3dbf02ea67..52edc66f19 100644 --- a/packages/admin-next/dashboard/src/hooks/api/inventory.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/inventory.tsx @@ -1,11 +1,9 @@ +import { AdminInventoryItemResponse, InventoryNext } from "@medusajs/types" import { InventoryItemDeleteRes, InventoryItemListRes, InventoryItemLocationLevelsRes, InventoryItemRes, - ReservationItemDeleteRes, - ReservationItemListRes, - ReservationItemRes, } from "../../types/api-responses" import { InventoryItemLocationBatch, @@ -20,7 +18,6 @@ import { useQuery, } from "@tanstack/react-query" -import { InventoryNext } from "@medusajs/types" import { client } from "../../lib/client" import { queryClient } from "../../lib/medusa" import { queryKeysFactory } from "../../lib/query-key-factory" @@ -35,11 +32,6 @@ export const inventoryItemLevelsQueryKeys = queryKeysFactory( INVENTORY_ITEM_LEVELS_QUERY_KEY ) -const RESERVATION_ITEMS_QUERY_KEY = "reservation_items" as const -export const reservationItemsQueryKeys = queryKeysFactory( - RESERVATION_ITEMS_QUERY_KEY -) - export const useInventoryItems = ( query?: Record, options?: Omit< @@ -171,7 +163,7 @@ export const useUpdateInventoryItemLevel = ( inventoryItemId: string, locationId: string, options?: UseMutationOptions< - AdminInventoryLevelResponse, + AdminInventoryItemResponse, Error, UpdateInventoryLevelReq > @@ -225,67 +217,3 @@ export const useBatchInventoryItemLevels = ( ...options, }) } - -export const useReservationItems = ( - query?: Record, - options?: Omit< - UseQueryOptions< - ReservationItemListRes, - Error, - ReservationItemListRes, - QueryKey - >, - "queryKey" | "queryFn" - > -) => { - const { data, ...rest } = useQuery({ - queryFn: () => client.inventoryItems.listReservationItems(query), - queryKey: reservationItemsQueryKeys.list(query), - ...options, - }) - - return { ...data, ...rest } -} - -export const useUpdateReservationItem = ( - id: string, - payload: InventoryNext.UpdateInventoryItemInput, - options?: UseMutationOptions< - ReservationItemRes, - Error, - UpdateInventoryItemReq - > -) => { - return useMutation({ - mutationFn: () => client.inventoryItems.updateReservationItem(id, payload), - onSuccess: (data, variables, context) => { - queryClient.invalidateQueries({ - queryKey: inventoryItemsQueryKeys.lists(), - }) - queryClient.invalidateQueries({ - queryKey: inventoryItemsQueryKeys.detail(id), - }) - options?.onSuccess?.(data, variables, context) - }, - ...options, - }) -} - -export const useDeleteReservationItem = ( - id: string, - options?: UseMutationOptions -) => { - return useMutation({ - mutationFn: () => client.inventoryItems.deleteReservationItem(id), - onSuccess: (data, variables, context) => { - queryClient.invalidateQueries({ - queryKey: inventoryItemsQueryKeys.lists(), - }) - queryClient.invalidateQueries({ - queryKey: inventoryItemsQueryKeys.detail(id), - }) - options?.onSuccess?.(data, variables, context) - }, - ...options, - }) -} diff --git a/packages/admin-next/dashboard/src/hooks/api/reservations.tsx b/packages/admin-next/dashboard/src/hooks/api/reservations.tsx new file mode 100644 index 0000000000..e46f27dc3f --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/api/reservations.tsx @@ -0,0 +1,119 @@ +import { + CreateReservationReq, + UpdateReservationReq, +} from "../../types/api-payloads" +import { + QueryKey, + UseMutationOptions, + UseQueryOptions, + useMutation, + useQuery, +} from "@tanstack/react-query" +import { + ReservationItemDeleteRes, + ReservationItemListRes, + ReservationItemRes, +} from "../../types/api-responses" + +import { InventoryNext } from "@medusajs/types" +import { client } from "../../lib/client" +import { queryClient } from "../../lib/medusa" +import { queryKeysFactory } from "../../lib/query-key-factory" + +const RESERVATION_ITEMS_QUERY_KEY = "reservation_items" as const +export const reservationItemsQueryKeys = queryKeysFactory( + RESERVATION_ITEMS_QUERY_KEY +) + +export const useReservationItem = ( + id: string, + query?: Record, + options?: Omit< + UseQueryOptions, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryKey: reservationItemsQueryKeys.detail(id), + queryFn: async () => client.reservations.retrieve(id, query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useReservationItems = ( + query?: Record, + options?: Omit< + UseQueryOptions< + ReservationItemListRes, + Error, + ReservationItemListRes, + QueryKey + >, + "queryKey" | "queryFn" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: () => client.reservations.list(query), + queryKey: reservationItemsQueryKeys.list(query), + ...options, + }) + + return { ...data, ...rest } +} + +export const useUpdateReservationItem = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: InventoryNext.UpdateReservationItemInput) => + client.reservations.update(id, payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: reservationItemsQueryKeys.detail(id), + }) + queryClient.invalidateQueries({ + queryKey: reservationItemsQueryKeys.lists(), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useCreateReservationItem = ( + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: (payload: CreateReservationReq) => + client.reservations.create(payload), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: reservationItemsQueryKeys.lists(), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} + +export const useDeleteReservationItem = ( + id: string, + options?: UseMutationOptions +) => { + return useMutation({ + mutationFn: () => client.reservations.delete(id), + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries({ + queryKey: reservationItemsQueryKeys.lists(), + }) + queryClient.invalidateQueries({ + queryKey: reservationItemsQueryKeys.detail(id), + }) + options?.onSuccess?.(data, variables, context) + }, + ...options, + }) +} diff --git a/packages/admin-next/dashboard/src/lib/client/client.ts b/packages/admin-next/dashboard/src/lib/client/client.ts index e0907a5e46..ed6cbda3d6 100644 --- a/packages/admin-next/dashboard/src/lib/client/client.ts +++ b/packages/admin-next/dashboard/src/lib/client/client.ts @@ -14,6 +14,7 @@ import { productTypes } from "./product-types" import { products } from "./products" import { promotions } from "./promotions" import { regions } from "./regions" +import { reservations } from "./reservations" import { salesChannels } from "./sales-channels" import { shippingOptions } from "./shipping-options" import { stockLocations } from "./stock-locations" @@ -45,6 +46,7 @@ export const client = { taxes: taxes, invites: invites, inventoryItems: inventoryItems, + reservations: reservations, products: products, productTypes: productTypes, priceLists: priceLists, diff --git a/packages/admin-next/dashboard/src/lib/client/inventory.ts b/packages/admin-next/dashboard/src/lib/client/inventory.ts index cae30c6eb1..ffaa1342b4 100644 --- a/packages/admin-next/dashboard/src/lib/client/inventory.ts +++ b/packages/admin-next/dashboard/src/lib/client/inventory.ts @@ -1,7 +1,6 @@ import { AdminInventoryItemListResponse, AdminInventoryItemResponse, - AdminInventoryLevelListResponse, AdminInventoryLevelResponse, } from "@medusajs/types" import { @@ -12,9 +11,7 @@ import { } from "../../types/api-payloads" import { InventoryItemLevelDeleteRes, - ReservationItemDeleteRes, - ReservationItemListRes, - ReservationItemRes, + InventoryItemLocationLevelsRes, } from "../../types/api-responses" import { deleteRequest, getRequest, postRequest } from "./common" @@ -59,7 +56,7 @@ async function listInventoryItemLevels( id: string, query?: Record ) { - return getRequest( + return getRequest( `/admin/inventory-items/${id}/location-levels`, query ) @@ -79,32 +76,12 @@ async function updateInventoryLevel( locationId: string, payload: UpdateInventoryLevelReq ) { - return postRequest( + return postRequest( `/admin/inventory-items/${inventoryItemId}/location-levels/${locationId}`, payload ) } -async function listReservationItems(query?: Record) { - return getRequest(`/admin/reservations`, query) -} - -async function deleteReservationItem(reservationId: string) { - return deleteRequest( - `/admin/reservations/${reservationId}` - ) -} - -async function updateReservationItem( - reservationId: string, - payload: UpdateInventoryItemReq -) { - return postRequest( - `/admin/reservatinos/${reservationId}`, - payload - ) -} - async function batchPostLocationLevels( inventoryItemId: string, payload: InventoryItemLocationBatch @@ -127,8 +104,5 @@ export const inventoryItems = { listLocationLevels: listInventoryItemLevels, updateInventoryLevel, deleteInventoryItemLevel, - listReservationItems, - deleteReservationItem, - updateReservationItem, batchPostLocationLevels, } diff --git a/packages/admin-next/dashboard/src/lib/client/reservations.ts b/packages/admin-next/dashboard/src/lib/client/reservations.ts new file mode 100644 index 0000000000..68ac62e426 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/client/reservations.ts @@ -0,0 +1,41 @@ +import { + ReservationDeleteRes, + ReservationListRes, + ReservationRes, +} from "../../types/api-responses" +import { deleteRequest, getRequest, postRequest } from "./common" + +import { InventoryNext } from "@medusajs/types" + +async function retrieveReservation(id: string, query?: Record) { + return getRequest(`/admin/reservations/${id}`, query) +} + +async function listReservations(query?: Record) { + return getRequest(`/admin/reservations`, query) +} + +async function createReservation( + payload: InventoryNext.CreateReservationItemInput +) { + return postRequest(`/admin/reservations`, payload) +} + +async function updateReservation( + id: string, + payload: InventoryNext.UpdateReservationItemInput +) { + return postRequest(`/admin/reservations/${id}`, payload) +} + +async function deleteReservation(id: string) { + return deleteRequest(`/admin/reservations/${id}`) +} + +export const reservations = { + retrieve: retrieveReservation, + list: listReservations, + create: createReservation, + update: updateReservation, + delete: deleteReservation, +} diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx index 138a27e61c..b3d85d358f 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx @@ -1,10 +1,3 @@ -import { AdminCustomersRes } from "@medusajs/client-types" -import { - AdminCollectionsRes, - AdminProductsRes, - AdminPromotionRes, - AdminRegionsRes, -} from "@medusajs/medusa" import { AdminApiKeyResponse, AdminCustomerGroupResponse, @@ -14,13 +7,20 @@ import { SalesChannelDTO, UserDTO, } from "@medusajs/types" +import { + AdminCollectionsRes, + AdminProductsRes, + AdminPromotionRes, + AdminRegionsRes, +} from "@medusajs/medusa" +import { InventoryItemRes, PriceListRes } from "../../types/api-responses" import { Outlet, RouteObject } from "react-router-dom" -import { ProtectedRoute } from "../../components/authentication/protected-route" +import { AdminCustomersRes } from "@medusajs/client-types" import { ErrorBoundary } from "../../components/error/error-boundary" import { MainLayout } from "../../components/layout/main-layout" +import { ProtectedRoute } from "../../components/authentication/protected-route" import { SettingsLayout } from "../../components/layout/settings-layout" -import { InventoryItemRes, PriceListRes } from "../../types/api-responses" /** * Experimental V2 routes. @@ -383,7 +383,7 @@ export const v2Routes: RouteObject[] = [ path: "create", lazy: () => import( - "../../v2-routes/customer-groups/customer-group-create" + "../../v2-routes/reservations/reservation-list/create-reservation" ), }, ], @@ -417,6 +417,51 @@ export const v2Routes: RouteObject[] = [ }, ], }, + { + path: "/reservations", + handle: { + crumb: () => "Reservations", + }, + children: [ + { + path: "", + lazy: () => + import("../../v2-routes/reservations/reservation-list"), + children: [ + { + path: "create", + lazy: () => + import( + "../../v2-routes/reservations/reservation-list/create-reservation" + ), + }, + ], + }, + { + path: ":id", + lazy: () => + import("../../v2-routes/reservations/reservation-detail"), + handle: { + crumb: ({ reservation }: any) => { + return ( + reservation?.inventory_item?.title ?? + reservation?.inventory_item?.sku ?? + reservation?.id + ) + }, + }, + children: [ + { + path: "edit", + lazy: () => + import( + "../../v2-routes/reservations/reservation-detail/components/edit-reservation" + ), + }, + ], + }, + ], + }, { path: "/inventory", handle: { @@ -437,7 +482,6 @@ export const v2Routes: RouteObject[] = [ }, children: [ { - // TODO: edit item path: "edit", lazy: () => import( @@ -445,7 +489,6 @@ export const v2Routes: RouteObject[] = [ ), }, { - // TODO: edit item attributes path: "attributes", lazy: () => import( @@ -453,7 +496,6 @@ export const v2Routes: RouteObject[] = [ ), }, { - // TODO: manage locations path: "locations", lazy: () => import( @@ -461,7 +503,6 @@ export const v2Routes: RouteObject[] = [ ), }, { - // TODO: adjust item level path: "locations/:location_id", lazy: () => import( diff --git a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx index e0fc97daf3..ec1d8f80b2 100644 --- a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx +++ b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx @@ -1,6 +1,7 @@ +import { Container, Heading } from "@medusajs/ui" + import { ExtendedReservationItem } from "@medusajs/medusa" -import { Container } from "@medusajs/ui" -import { useAdminInventoryItem } from "medusa-react" +import { useInventoryItem } from "../../../../../hooks/api/inventory" type ReservationGeneralSectionProps = { reservation: ExtendedReservationItem @@ -11,11 +12,11 @@ type ReservationGeneralSectionProps = { export const ReservationGeneralSection = ({ reservation, }: ReservationGeneralSectionProps) => { - const { inventory_item, isLoading, isError, error } = useAdminInventoryItem( + const { inventory_item, isPending, isError, error } = useInventoryItem( reservation.inventory_item_id ) - if (isLoading || !inventory_item) { + if (isPending || !inventory_item) { return
Loading...
} diff --git a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/index.ts b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/index.ts index 233de04c8c..0c31eb061c 100644 --- a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/index.ts +++ b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/index.ts @@ -1 +1 @@ -export { ReservationDetail as Component } from "./reservation-edit" +export { ReservationDetail as Component } from "./reservation-detail" diff --git a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/reservation-edit.tsx b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/reservation-detail.tsx similarity index 100% rename from packages/admin-next/dashboard/src/routes/reservations/reservation-detail/reservation-edit.tsx rename to packages/admin-next/dashboard/src/routes/reservations/reservation-detail/reservation-detail.tsx diff --git a/packages/admin-next/dashboard/src/routes/reservations/reservation-list/components/reservation-list-table/reservation-actions.tsx b/packages/admin-next/dashboard/src/routes/reservations/reservation-list/components/reservation-list-table/reservation-actions.tsx index edf50937c6..002ed7cefb 100644 --- a/packages/admin-next/dashboard/src/routes/reservations/reservation-list/components/reservation-list-table/reservation-actions.tsx +++ b/packages/admin-next/dashboard/src/routes/reservations/reservation-list/components/reservation-list-table/reservation-actions.tsx @@ -1,9 +1,10 @@ import { PencilSquare, Trash } from "@medusajs/icons" -import { ExtendedReservationItem } from "@medusajs/medusa" -import { usePrompt } from "@medusajs/ui" -import { useAdminDeleteReservation } from "medusa-react" -import { useTranslation } from "react-i18next" +import { toast, usePrompt } from "@medusajs/ui" + import { ActionMenu } from "../../../../../components/common/action-menu" +import { ExtendedReservationItem } from "@medusajs/medusa" +import { useDeleteReservationItem } from "../../../../../hooks/api/reservations" +import { useTranslation } from "react-i18next" export const ReservationActions = ({ reservation, @@ -12,7 +13,7 @@ export const ReservationActions = ({ }) => { const { t } = useTranslation() const prompt = usePrompt() - const { mutateAsync } = useAdminDeleteReservation(reservation.id) + const { mutateAsync } = useDeleteReservationItem(reservation.id) const handleDelete = async () => { const res = await prompt({ @@ -26,7 +27,14 @@ export const ReservationActions = ({ return } - await mutateAsync() + await mutateAsync(undefined, { + onSuccess: () => { + toast.success(t("general.success"), { + dismissLabel: t("actions.close"), + description: t("inventory.reservation.deleteSuccessToast"), + }) + }, + }) } return ( diff --git a/packages/admin-next/dashboard/src/types/api-payloads.ts b/packages/admin-next/dashboard/src/types/api-payloads.ts index 9b06cc0c60..590374a26e 100644 --- a/packages/admin-next/dashboard/src/types/api-payloads.ts +++ b/packages/admin-next/dashboard/src/types/api-payloads.ts @@ -109,6 +109,13 @@ export type UpdateInventoryItemReq = Omit< "id" > +// Reservations +export type UpdateReservationReq = Omit< + InventoryNext.UpdateReservationItemInput, + "id" +> +export type CreateReservationReq = InventoryNext.CreateReservationItemInput + // Inventory Item Levels export type InventoryItemLocationBatch = { creates: { location_id: string; stocked_quantity?: number }[] diff --git a/packages/admin-next/dashboard/src/types/api-responses.ts b/packages/admin-next/dashboard/src/types/api-responses.ts index 280ba3b8d3..e7237c3892 100644 --- a/packages/admin-next/dashboard/src/types/api-responses.ts +++ b/packages/admin-next/dashboard/src/types/api-responses.ts @@ -71,6 +71,13 @@ export type RegionRes = { region: RegionDTO } export type RegionListRes = { regions: RegionDTO[] } & ListRes export type RegionDeleteRes = DeleteRes +// Reservations +export type ReservationRes = { reservation: InventoryNext.ReservationItemDTO } +export type ReservationListRes = { + reservations: InventoryNext.ReservationItemDTO[] +} & ListRes +export type ReservationDeleteRes = DeleteRes + // Campaigns export type CampaignRes = { campaign: CampaignDTO } export type CampaignListRes = { campaigns: CampaignDTO[] } & ListRes diff --git a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/adjust-inventory/components/adjust-inventory-form.tsx b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/adjust-inventory/components/adjust-inventory-form.tsx index a55010ffa6..ae3aefc0f2 100644 --- a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/adjust-inventory/components/adjust-inventory-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/adjust-inventory/components/adjust-inventory-form.tsx @@ -109,11 +109,11 @@ export const AdjustInventoryForm = ({ /> {t("reservations.domain")} diff --git a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-actions.tsx b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-actions.tsx index 05d945ff17..8051a4b94f 100644 --- a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-actions.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-actions.tsx @@ -1,9 +1,9 @@ import { PencilSquare, Trash } from "@medusajs/icons" +import { toast, usePrompt } from "@medusajs/ui" import { ActionMenu } from "../../../../../components/common/action-menu" import { InventoryNext } from "@medusajs/types" -import { useDeleteReservationItem } from "../../../../../hooks/api/inventory" -import { usePrompt } from "@medusajs/ui" +import { useDeleteReservationItem } from "../../../../../hooks/api/reservations" import { useTranslation } from "react-i18next" export const ReservationActions = ({ @@ -27,7 +27,14 @@ export const ReservationActions = ({ return } - await mutateAsync() + await mutateAsync(undefined, { + onSuccess: () => { + toast.success(t("general.success"), { + dismissLabel: t("actions.close"), + description: t("inventory.reservation.deleteSuccessToast"), + }) + }, + }) } return ( @@ -38,7 +45,7 @@ export const ReservationActions = ({ { icon: , label: t("actions.edit"), - to: `/reservation/${reservation.id}/edit`, + to: `/reservations/${reservation.id}/edit`, }, ], }, diff --git a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-list-table.tsx index f5d086148d..3d64f87523 100644 --- a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-list-table.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/reservation-list-table.tsx @@ -1,9 +1,9 @@ import { DataTable } from "../../../../../components/table/data-table" import { InventoryNext } from "@medusajs/types" import { useDataTable } from "../../../../../hooks/use-data-table" -import { useInventoryTableColumns } from "./use-reservation-list-table-columns" -import { useInventoryTableQuery } from "./use-reservation-list-table-query" -import { useReservationItems } from "../../../../../hooks/api/inventory" +import { useReservationItems } from "../../../../../hooks/api/reservations" +import { useReservationTableColumn } from "./use-reservation-list-table-columns" +import { useReservationsTableQuery } from "./use-reservation-list-table-query" const PAGE_SIZE = 20 @@ -12,29 +12,24 @@ export const ReservationItemTable = ({ }: { inventoryItem: InventoryNext.InventoryItemDTO }) => { - const { searchParams, raw } = useInventoryTableQuery({ + const { searchParams, raw } = useReservationsTableQuery({ pageSize: PAGE_SIZE, }) - const { - reservations, - count, - isPending: isLoading, - isError, - error, - } = useReservationItems({ - ...searchParams, - inventory_item_id: [inventoryItem.id], - }) + const { reservations, count, isPending, isError, error } = + useReservationItems({ + ...searchParams, + inventory_item_id: [inventoryItem.id], + }) - const columns = useInventoryTableColumns({ sku: inventoryItem.sku! }) + const columns = useReservationTableColumn({ sku: inventoryItem.sku! }) const { table } = useDataTable({ data: reservations ?? [], columns, count, enablePagination: true, - getRowId: (row) => row.id, + getRowId: (row: InventoryNext.ReservationItemDTO) => row.id, pageSize: PAGE_SIZE, }) @@ -48,7 +43,7 @@ export const ReservationItemTable = ({ columns={columns} pageSize={PAGE_SIZE} count={count} - isLoading={isLoading} + isLoading={isPending} pagination queryObject={raw} /> diff --git a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-columns.tsx b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-columns.tsx index 5222adc999..38106fb939 100644 --- a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-columns.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-columns.tsx @@ -9,14 +9,14 @@ import { useTranslation } from "react-i18next" /** * Adds missing properties to the InventoryItemDTO type. */ -interface ExtendedInventoryItem extends InventoryNext.ReservationItemDTO { +interface ExtendedReservationItem extends InventoryNext.ReservationItemDTO { line_item: { order_id: string } location: StockLocationDTO } -const columnHelper = createColumnHelper() +const columnHelper = createColumnHelper() -export const useInventoryTableColumns = ({ sku }: { sku: string }) => { +export const useReservationTableColumn = ({ sku }: { sku: string }) => { const { t } = useTranslation() return useMemo( @@ -32,17 +32,17 @@ export const useInventoryTableColumns = ({ sku }: { sku: string }) => { }, }), columnHelper.accessor("line_item.order_id", { - header: t("inventory.reserved"), + header: t("inventory.reservation.orderID"), cell: ({ getValue }) => { - const quantity = getValue() + const orderId = getValue() - if (Number.isNaN(quantity)) { + if (!orderId) { return } return (
- {quantity} + {orderId}
) }, @@ -64,7 +64,7 @@ export const useInventoryTableColumns = ({ sku }: { sku: string }) => { }, }), columnHelper.accessor("location.name", { - header: t("inventory.location"), + header: t("inventory.reservation.location"), cell: ({ getValue }) => { const location = getValue() @@ -99,7 +99,7 @@ export const useInventoryTableColumns = ({ sku }: { sku: string }) => { }), columnHelper.display({ id: "actions", - cell: ({ row }) => , + cell: ({ row }) => , }), ], [t] diff --git a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-query.tsx b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-query.tsx index 2866bde16b..0f3b2b318c 100644 --- a/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-query.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/inventory/inventory-detail/components/reservations-table/use-reservation-list-table-query.tsx @@ -1,6 +1,6 @@ import { useQueryParams } from "../../../../../hooks/use-query-params" -export const useInventoryTableQuery = ({ +export const useReservationsTableQuery = ({ pageSize = 20, prefix, }: { diff --git a/packages/admin-next/dashboard/src/v2-routes/reservations/reservation-detail/components/edit-reservation/components/edit-reservation-form.tsx b/packages/admin-next/dashboard/src/v2-routes/reservations/reservation-detail/components/edit-reservation/components/edit-reservation-form.tsx new file mode 100644 index 0000000000..3dcdbbc2e4 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/reservations/reservation-detail/components/edit-reservation/components/edit-reservation-form.tsx @@ -0,0 +1,208 @@ +import * as zod from "zod" + +import { Button, Input, Select, Text, Textarea, toast } from "@medusajs/ui" +import { InventoryNext, StockLocationDTO } from "@medusajs/types" +import { + RouteDrawer, + useRouteModal, +} from "../../../../../../components/route-modal" + +import { Form } from "../../../../../../components/common/form" +import { InventoryItemRes } from "../../../../../../types/api-responses" +import { useForm } from "react-hook-form" +import { useTranslation } from "react-i18next" +import { useUpdateReservationItem } from "../../../../../../hooks/api/reservations" +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" + +type EditReservationFormProps = { + reservation: InventoryNext.ReservationItemDTO + locations: StockLocationDTO[] + item: InventoryItemRes["inventory_item"] +} + +const EditReservationSchema = z.object({ + location_id: z.string(), + description: z.string().optional(), + quantity: z.number().min(1), +}) + +const AttributeGridRow = ({ + title, + value, +}: { + title: string + value: string | number +}) => { + return ( +
+ + {title} + + + {value} + +
+ ) +} + +const getDefaultValues = (reservation: InventoryNext.ReservationItemDTO) => { + return { + quantity: reservation.quantity, + location_id: reservation.location_id, + description: reservation.description ?? undefined, + } +} + +export const EditReservationForm = ({ + reservation, + item, + locations, +}: EditReservationFormProps) => { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + + const form = useForm>({ + defaultValues: getDefaultValues(reservation), + resolver: zodResolver(EditReservationSchema), + }) + + const { mutateAsync } = useUpdateReservationItem(reservation.id) + + const handleSubmit = form.handleSubmit(async (values) => { + mutateAsync(values as any, { + onSuccess: () => { + handleSuccess() + toast.success(t("general.success"), { + dismissLabel: t("actions.close"), + description: t("inventory.reservation.updateSuccessToast"), + }) + }, + }) + }) + + const reservedQuantity = form.watch("quantity") + const locationId = form.watch("location_id") + + const level = item.location_levels!.find( + (level: InventoryNext.InventoryLevelDTO) => level.location_id === locationId + ) + + return ( + +
+ + { + return ( + + {t("inventory.reservation.location")} + + + + + + ) + }} + /> +
+ + + + +
+ { + return ( + + + {t("inventory.reservation.reservedAmount")} + + + { + const value = e.target.value + + if (value === "") { + onChange(null) + } else { + onChange(parseFloat(value)) + } + }} + {...field} + /> + + + + ) + }} + /> + { + return ( + + {t("fields.description")} + +