import * as zod from "zod" import { Button, Heading, Text, Textarea, toast } from "@medusajs/ui" import { RouteFocusModal, useRouteModal, } from "../../../../../components/modals" import { zodResolver } from "@hookform/resolvers/zod" import { HttpTypes } from "@medusajs/types" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import { Form } from "../../../../../components/common/form" import { Combobox } from "../../../../../components/inputs/combobox" import { QuantityInput } from "../../../../../components/inputs/quantity-input" import { KeyboundForm } from "../../../../../components/utilities/keybound-form" import { useComboboxData } from "../../../../../hooks/use-combobox-data" import { sdk } from "../../../../../lib/client" import { formatQuantity } from "../../../../../lib/format-quantity" import { useCreateReservationItem } from "../../../../../hooks/api/reservations" import { useStockLocations } from "../../../../../hooks/api/stock-locations" import { inventoryItemsQueryKeys } from "../../../../../hooks/api/inventory" export const CreateReservationSchema = zod.object({ inventory_item_id: zod.string().min(1), location_id: zod.string().min(1), quantity: zod.number().positive(), description: zod.string().optional(), }) const AttributeGridRow = ({ title, value, }: { title: string value: string | number }) => { return (
{title} {value}
) } export const ReservationCreateForm = (props: { inventoryItemId?: string }) => { const { t } = useTranslation() const { handleSuccess } = useRouteModal() const form = useForm>({ defaultValues: { inventory_item_id: props.inventoryItemId || "", location_id: "", quantity: 0, description: "", }, resolver: zodResolver(CreateReservationSchema), }) const inventoryItemId = form.watch("inventory_item_id") const inventoryItems = useComboboxData({ queryKey: inventoryItemsQueryKeys.list(), queryFn: (params) => sdk.admin.inventoryItem.list(params), getOptions: (data) => data.inventory_items.map((inventoryItem) => ({ label: inventoryItem.title ?? inventoryItem.sku!, value: inventoryItem.id, inventoryItem, })), defaultValueKey: "id", defaultValue: props.inventoryItemId, selectedValue: inventoryItemId, }) const selectedInventoryItem = inventoryItems.options.find( (option) => option.value === inventoryItemId )?.inventoryItem as | HttpTypes.AdminInventoryItemResponse["inventory_item"] | undefined const locationId = form.watch("location_id") const selectedLocationLevel = selectedInventoryItem?.location_levels?.find( (it) => it.location_id === locationId ) const quantity = form.watch("quantity") const { stock_locations } = useStockLocations( { id: selectedInventoryItem?.location_levels?.map( (level: HttpTypes.AdminInventoryLevel) => level.location_id ) ?? [], }, { enabled: !!selectedInventoryItem, } ) const { mutateAsync, isPending } = useCreateReservationItem() const handleSubmit = form.handleSubmit(async (data) => { const max = selectedLocationLevel?.available_quantity ? selectedLocationLevel.available_quantity : 0 if (!selectedLocationLevel?.available_quantity) { form.setError("quantity", { type: "manual", message: t("inventory.reservation.errors.noAvaliableQuantity"), }) return } if (data.quantity > max) { form.setError("quantity", { type: "manual", message: t("inventory.reservation.errors.quantityOutOfRange", { max: max, }), }) return } await mutateAsync(data, { onSuccess: ({ reservation }) => { toast.success(t("inventory.reservation.successToast")) handleSuccess( props.inventoryItemId ? `/inventory/${props.inventoryItemId}` : `/reservations/${reservation.id}` ) }, onError: (e) => { toast.error(e.message) }, }) }) return (
{t("inventory.reservation.create")}
{ return ( {t("inventory.reservation.itemToReserve")} { onChange(v) }} {...field} disabled={!!props.inventoryItemId} options={inventoryItems.options} searchValue={inventoryItems.searchValue} onSearchValueChange={ inventoryItems.onSearchValueChange } fetchNextPage={inventoryItems.fetchNextPage} /> ) }} /> { return ( {t("fields.location")} { onChange(v) }} {...field} disabled={!inventoryItemId} options={(stock_locations ?? []).map( (stockLocation) => ({ label: stockLocation.name, value: stockLocation.id, }) )} /> ) }} />
{ return ( {t("fields.quantity")} { const value = e.target.value if (value === "") { onChange(null) } else { onChange(parseFloat(value)) } }} {...field} disabled={!inventoryItemId || !locationId} /> ) }} />
{ return ( {t("fields.description")}