From 2a6be52236f56b0ff148e2aa5c769b19399dca07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Wed, 4 Sep 2024 08:17:38 +0200 Subject: [PATCH] feat(dashboard): update create fulfillment UI part 1 (#8972) **What** - update Create fulfillment modal according to the design **Note** - in a followup I will add support for inventory kits **Question** - should we support overriding shipping method as per design? --- Before: ![Screenshot 2024-09-03 at 17 57 18](https://github.com/user-attachments/assets/733799b6-4ba2-4841-9626-982e0c398694) After: ![Screenshot 2024-09-03 at 17 51 26](https://github.com/user-attachments/assets/5caf4e3b-312a-40dd-b0ad-3eb4bbba0044) --- .../dashboard/src/i18n/translations/en.json | 2 + .../constants.ts | 1 + .../order-create-fulfillment-form.tsx | 150 +++++++++------- .../order-create-fulfillment-item.tsx | 169 +++++++++--------- 4 files changed, 170 insertions(+), 152 deletions(-) diff --git a/packages/admin-next/dashboard/src/i18n/translations/en.json b/packages/admin-next/dashboard/src/i18n/translations/en.json index 0e268a5589..aba558393b 100644 --- a/packages/admin-next/dashboard/src/i18n/translations/en.json +++ b/packages/admin-next/dashboard/src/i18n/translations/en.json @@ -1076,6 +1076,7 @@ "itemsToFulfillDesc": "Choose items and quantities to fulfill", "locationDescription": "Choose which location you want to fulfill items from.", "sendNotificationHint": "Notify customers about the created fulfillment.", + "methodDescription": "Choose a different shipping method from the one customer selected", "error": { "wrongQuantity": "Only one item is available for fulfillment", "wrongQuantity_other": "Quantity should be a number between 1 and {{number}}", @@ -2464,6 +2465,7 @@ "newPassword": "New Password", "repeatNewPassword": "Repeat New Password", "categories": "Categories", + "shippingMethod": "Shipping method", "configurations": "Configurations", "conditions": "Conditions", "category": "Category", diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/constants.ts b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/constants.ts index 67c6c4cfc6..ff4d473703 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/constants.ts +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/constants.ts @@ -4,5 +4,6 @@ export const CreateFulfillmentSchema = z.object({ quantity: z.record(z.string(), z.number()), location_id: z.string(), + shipping_option_id: z.string().optional(), send_notification: z.boolean().optional(), }) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx index 6bd6eba336..213a5308f2 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-form.tsx @@ -16,8 +16,9 @@ import { import { useCreateOrderFulfillment } from "../../../../../hooks/api/orders" import { useStockLocations } from "../../../../../hooks/api/stock-locations" import { getFulfillableQuantity } from "../../../../../lib/order-item" -import { CreateFulfillmentSchema } from "./constants" import { OrderCreateFulfillmentItem } from "./order-create-fulfillment-item" +import { CreateFulfillmentSchema } from "./constants" +import { useShippingOptions } from "../../../../../hooks/api" type OrderCreateFulfillmentFormProps = { order: AdminOrder @@ -38,24 +39,23 @@ export function OrderCreateFulfillmentForm({ const form = useForm>({ defaultValues: { - quantity: fulfillableItems.reduce( - (acc, item) => { - acc[item.id] = getFulfillableQuantity(item) - return acc - }, - {} as Record - ), + quantity: fulfillableItems.reduce((acc, item) => { + acc[item.id] = getFulfillableQuantity(item) + return acc + }, {} as Record), send_notification: !order.no_notification, }, resolver: zodResolver(CreateFulfillmentSchema), }) const { stock_locations = [] } = useStockLocations() + const { shipping_options = [] } = useShippingOptions() const handleSubmit = form.handleSubmit(async (data) => { try { await createOrderFulfillment({ location_id: data.location_id, + // shipping_option_id: data.shipping_option_id, no_notification: !data.send_notification, items: Object.entries(data.quantity) .filter(([, value]) => !!value) @@ -78,23 +78,6 @@ export function OrderCreateFulfillmentForm({ } }, [stock_locations?.length]) - const onItemRemove = (itemId: string) => { - setFulfillableItems((state) => state.filter((i) => i.id !== itemId)) - form.unregister(`quantity.${itemId}`) - } - - const resetItems = () => { - const items = (order.items || []).filter( - (item) => getFulfillableQuantity(item) > 0 - ) - setFulfillableItems(items) - - items.forEach((i) => - form.register(`quantity.${i.id}`, { value: getFulfillableQuantity(i) }) - ) - form.clearErrors("root") - } - const selectedLocationId = useWatch({ name: "location_id", control: form.control, @@ -119,13 +102,10 @@ export function OrderCreateFulfillmentForm({ }) } - const quantityMap = itemsToFulfill.reduce( - (acc, item) => { - acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO) - return acc - }, - {} as Record - ) + const quantityMap = itemsToFulfill.reduce((acc, item) => { + acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO) + return acc + }, {} as Record) form.setValue("quantity", quantityMap) }, [...fulfilledQuantityArray]) @@ -152,41 +132,91 @@ export function OrderCreateFulfillmentForm({
-
-
+
+
{ return ( - {t("fields.location")} - - {t("orders.fulfillment.locationDescription")} - - - - +
+
+ {t("fields.location")} + + {t("orders.fulfillment.locationDescription")} + +
+
+ + + +
+
) }} /> +
+ {/*
*/} + {/* {*/} + {/* return (*/} + {/* */} + {/*
*/} + {/*
*/} + {/* */} + {/* {t("fields.shippingMethod")}*/} + {/* */} + {/* */} + {/* {t("orders.fulfillment.methodDescription")}*/} + {/* */} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/*
*/} + {/* */} + {/*
*/} + {/* )*/} + {/* }}*/} + {/* />*/} + {/*
*/} +
{t("orders.fulfillment.itemsToFulfill")} @@ -202,9 +232,7 @@ export function OrderCreateFulfillmentForm({ key={item.id} form={form} item={item} - onItemRemove={onItemRemove} locationId={selectedLocationId} - currencyCode={order.currency_code} /> ) })} @@ -218,14 +246,6 @@ export function OrderCreateFulfillmentForm({ classNameInner="flex justify-between flex-1 items-center" > {form.formState.errors.root.message} - )}
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx index 70b731c8e2..5a88293ff0 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-fulfillment/components/order-create-fulfillment-form/order-create-fulfillment-item.tsx @@ -1,19 +1,16 @@ -import { Trash } from "@medusajs/icons" import { useMemo } from "react" import { useTranslation } from "react-i18next" import * as zod from "zod" import { Input, Text } from "@medusajs/ui" import { UseFormReturn } from "react-hook-form" - -import { ActionMenu } from "../../../../../components/common/action-menu/index.ts" -import { Form } from "../../../../../components/common/form/index.ts" -import { Thumbnail } from "../../../../../components/common/thumbnail/index.ts" -import { MoneyAmountCell } from "../../../../../components/table/table-cells/common/money-amount-cell/index.ts" -import { useProductVariant } from "../../../../../hooks/api/products.tsx" -import { getFulfillableQuantity } from "../../../../../lib/order-item.ts" -import { CreateFulfillmentSchema } from "./constants.ts" import { HttpTypes } from "@medusajs/types" +import { Form } from "../../../../../components/common/form/index" +import { Thumbnail } from "../../../../../components/common/thumbnail/index" +import { useProductVariant } from "../../../../../hooks/api/products" +import { getFulfillableQuantity } from "../../../../../lib/order-item" +import { CreateFulfillmentSchema } from "./constants" + type OrderEditItemProps = { item: HttpTypes.AdminOrderLineItem currencyCode: string @@ -24,10 +21,8 @@ type OrderEditItemProps = { export function OrderCreateFulfillmentItem({ item, - currencyCode, form, locationId, - onItemRemove, }: OrderEditItemProps) { const { t } = useTranslation() @@ -39,8 +34,6 @@ export function OrderCreateFulfillmentItem({ } ) - const hasInventoryItem = !!variant?.inventory.length - const { availableQuantity, inStockQuantity } = useMemo(() => { if (!variant || !locationId) { return {} @@ -70,8 +63,8 @@ export function OrderCreateFulfillmentItem({ return (
-
-
+
+
@@ -86,84 +79,86 @@ export function OrderCreateFulfillmentItem({
-
- - {hasInventoryItem && ( - - {t("orders.fulfillment.available")}: {availableQuantity || "N/A"}{" "} - ยท {t("orders.fulfillment.inStock")}: {inStockQuantity || "N/A"} +
+
+ +
+ + {t("orders.fulfillment.available")} - )} -
+ + {availableQuantity || "N/A"} + +
-
- , - onClick: () => onItemRemove(item.id), - }, - ], - }, - ]} - /> -
-
+
+
-
-
- - {t("fields.quantity")} - - { - return ( - - - { - const val = - e.target.value === "" ? null : Number(e.target.value) +
+ + {t("orders.fulfillment.inStock")} + + + {inStockQuantity || "N/A"}{" "} + {inStockQuantity && ( + + -{form.getValues(`quantity.${item.id}`)} + + )} + +
+
- field.onChange(val) +
+ { + return ( + + + { + const val = + e.target.value === "" + ? null + : Number(e.target.value) - if (!isNaN(val)) { - if (val < minValue || val > maxValue) { - form.setError(`quantity.${item.id}`, { - type: "manual", - message: t( - "orders.fulfillment.error.wrongQuantity", - { - count: maxValue, - number: maxValue, - } - ), - }) - } else { - form.clearErrors(`quantity.${item.id}`) + field.onChange(val) + + if (!isNaN(val)) { + if (val < minValue || val > maxValue) { + form.setError(`quantity.${item.id}`, { + type: "manual", + message: t( + "orders.fulfillment.error.wrongQuantity", + { + count: maxValue, + number: maxValue, + } + ), + }) + } else { + form.clearErrors(`quantity.${item.id}`) + } } - } - }} - /> - - - - ) - }} - /> + }} + /> + + + + ) + }} + /> + + + / {item.quantity} {t("fields.qty")} + +