fix(draft-order, medusa, types): ui polish (#13376)

**What**
- ensure unique promotion changes are displayed in the activity section
- check linked promotions by id and not code
- list promotions - add id param
- make address payload fields nullish
- Add items view UI tweaks

---

CLOSES SUP-2400
This commit is contained in:
Frane Polić
2025-09-05 08:55:07 +02:00
committed by GitHub
parent e67974ffe5
commit 0000ed6998
10 changed files with 114 additions and 77 deletions

View File

@@ -26,7 +26,7 @@ export const ActivitySection = ({ order, changes }: ActivitySectionProps) => {
)
return (
<Container className="p-0 overflow-hidden">
<Container className="overflow-hidden p-0">
<div className="px-6 py-4">
<Heading>Activity</Heading>
</div>
@@ -83,9 +83,9 @@ const CollapsibleActivityItemList = ({
{!open && (
<div className="grid grid-cols-[20px_1fr] items-start gap-2">
<div className="flex size-full flex-col items-center">
<div className="border-ui-border-strong w-px flex-1 bg-[linear-gradient(var(--border-strong)_33%,rgba(255,255,255,0)_0%)] bg-[length:1px_3px] bg-right bg-repeat-y bg-clip-content" />
<div className="border-ui-border-strong w-px flex-1 bg-[linear-gradient(var(--border-strong)_33%,rgba(255,255,255,0)_0%)] bg-[length:1px_3px] bg-clip-content bg-right bg-repeat-y" />
</div>
<Collapsible.Trigger className="text-left p-0 m-0 pb-4 text-ui-fg-muted hover:text-ui-fg-base focus:text-ui-fg-base outline-none transition-colors">
<Collapsible.Trigger className="text-ui-fg-muted hover:text-ui-fg-base focus:text-ui-fg-base m-0 p-0 pb-4 text-left outline-none transition-colors">
<Text size="small" leading="compact" weight="plus">
{`Show ${items.length} more ${
items.length === 1 ? "activity" : "activities"
@@ -130,22 +130,22 @@ const ActivityItem = ({ item, isFirst = false }: ActivityItemProps) => {
return (
<div
className={clx("grid grid-cols-[20px_1fr] items-start gap-x-2 w-full")}
className={clx("grid w-full grid-cols-[20px_1fr] items-start gap-x-2")}
>
<div className="flex flex-col items-center gap-0.5 h-full">
<div className="size-5 flex items-center justify-center">
<div className="size-2.5 rounded-full shadow-borders-base flex items-center justify-center">
<div className="size-1.5 rounded-full bg-ui-tag-neutral-icon" />
<div className="flex h-full flex-col items-center gap-0.5">
<div className="flex size-5 items-center justify-center">
<div className="shadow-borders-base flex size-2.5 items-center justify-center rounded-full">
<div className="bg-ui-tag-neutral-icon size-1.5 rounded-full" />
</div>
</div>
{!isFirst && (
<div className="flex flex-1 items-center justify-center">
<div className="h-full w-px bg-ui-border-base" />
<div className="bg-ui-border-base h-full w-px" />
</div>
)}
</div>
<div className={clx("flex flex-col", !isFirst && "pb-4")}>
<div className="flex items-center gap-x-2 justify-between">
<div className="flex items-center justify-between gap-x-2">
<Text size="small" weight="plus" leading="compact">
{item.label}
</Text>
@@ -159,10 +159,10 @@ const ActivityItem = ({ item, isFirst = false }: ActivityItemProps) => {
</div>
{item.content && renderContent(item.content)}
{item.userId && (
<div className="pt-2 text-ui-fg-muted">
<div className="text-ui-fg-muted pt-2">
{isUserLoaded ? (
<Link to={`/settings/users/${user.id}`} className="w-fit">
<div className="flex items-center gap-x-1.5 w-fit">
<div className="flex w-fit items-center gap-x-1.5">
<Text size="small">By</Text>
<Avatar
size="2xsmall"
@@ -179,8 +179,8 @@ const ActivityItem = ({ item, isFirst = false }: ActivityItemProps) => {
) : (
<div className="flex items-center gap-x-1.5">
<Text size="small">By</Text>
<Skeleton className="rounded-full w-5 h-5" />
<Skeleton className="w-[75px] h-4" />
<Skeleton className="h-5 w-5 rounded-full" />
<Skeleton className="h-4 w-[75px]" />
</div>
)}
</div>
@@ -215,7 +215,14 @@ function getEditActivityItems(
promotionsRemoved: 0,
}
for (const action of change.actions) {
const orderedActions = change.actions.sort((a, b) => {
return a.ordering - b.ordering
})
const addedPromotionMap = new Map<string, true>()
const removedPromotionMap = new Map<string, true>()
for (const action of orderedActions) {
if (!action.details) {
continue
}
@@ -234,13 +241,22 @@ function getEditActivityItems(
case "SHIPPING_REMOVE":
counts.shippingMethodsRemoved += 1
break
case "PROMOTION_ADD":
counts.promotionsAdded += 1
case "PROMOTION_ADD": {
addedPromotionMap.set(action.reference_id!, true)
break
case "PROMOTION_REMOVE":
counts.promotionsRemoved += 1
}
case "PROMOTION_REMOVE": {
if (addedPromotionMap.has(action.reference_id!)) {
addedPromotionMap.delete(action.reference_id!)
} else {
removedPromotionMap.set(action.reference_id!, true)
}
break
}
}
counts.promotionsAdded = addedPromotionMap.size
counts.promotionsRemoved = removedPromotionMap.size
}
const createActivityItem = (

View File

@@ -5,10 +5,10 @@ export const addressSchema = z.object({
first_name: z.string().min(1),
last_name: z.string().min(1),
address_1: z.string().min(1),
address_2: z.string().optional(),
company: z.string().optional(),
address_2: z.string().nullish(),
company: z.string().nullish(),
city: z.string().min(1),
province: z.string().optional(),
province: z.string().nullish(),
postal_code: z.string().min(1),
phone: z.string().optional(),
phone: z.string().nullish(),
})

View File

@@ -216,7 +216,7 @@ const ItemsForm = ({ preview, currencyCode }: ItemsFormProps) => {
</RouteFocusModal.Title>
<RouteFocusModal.Description asChild>
<Text size="small" className="text-ui-fg-subtle">
Edit the items in the draft order.
Edit the items in the draft order
</Text>
</RouteFocusModal.Description>
</div>
@@ -261,7 +261,7 @@ const ItemsForm = ({ preview, currencyCode }: ItemsFormProps) => {
</div>
<div className="bg-ui-bg-subtle shadow-elevation-card-rest rounded-xl">
<div className="px-[5px]">
<div className="grid grid-cols-[1fr_1fr_1fr_28px] gap-3 px-4 py-2 text-ui-fg-muted">
<div className="text-ui-fg-muted grid grid-cols-[2fr_1fr_2fr_28px] gap-3 px-4 py-2">
<div>
<Text size="small" weight="plus">
Item
@@ -282,7 +282,7 @@ const ItemsForm = ({ preview, currencyCode }: ItemsFormProps) => {
</div>
<div className="flex flex-col gap-y-1.5 px-[5px] pb-[5px]">
{itemCount <= 0 ? (
<div className="flex items-center justify-center gap-x-3 bg-ui-bg-base rounded-lg p-4 shadow-elevation-card-rest flex-col gap-1">
<div className="bg-ui-bg-base shadow-elevation-card-rest flex flex-col items-center justify-center gap-1 gap-x-3 rounded-lg p-4">
<Text size="small" weight="plus" leading="compact">
There are no items in this order
</Text>
@@ -300,7 +300,7 @@ const ItemsForm = ({ preview, currencyCode }: ItemsFormProps) => {
/>
))
) : (
<div className="flex items-center justify-center gap-x-3 bg-ui-bg-base rounded-lg p-4 shadow-elevation-card-rest flex-col gap-1">
<div className="bg-ui-bg-base shadow-elevation-card-rest flex flex-col items-center justify-center gap-1 gap-x-3 rounded-lg p-4">
<Text size="small" weight="plus" leading="compact">
No items found
</Text>
@@ -348,7 +348,7 @@ const ItemsForm = ({ preview, currencyCode }: ItemsFormProps) => {
</StackedFocusModal>
</RouteFocusModal.Body>
<RouteFocusModal.Footer>
<div className="flex items-center gap-x-2 justify-end">
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button size="small" variant="secondary" type="button">
Cancel
@@ -461,8 +461,8 @@ const VariantItem = ({ item, preview, currencyCode }: ItemProps) => {
return (
<Form {...form}>
<form onSubmit={onSubmit}>
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_28px] gap-3 px-4 py-2 bg-ui-bg-base shadow-elevation-card-rest rounded-lg items-center">
<div className="flex items-center gap-x-3 w-full">
<div className="bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,2fr)_28px] items-center gap-3 rounded-lg px-4 py-2">
<div className="flex w-full items-center gap-x-3">
<Thumbnail
thumbnail={item.thumbnail}
alt={item.product_title ?? undefined}
@@ -490,7 +490,7 @@ const VariantItem = ({ item, preview, currencyCode }: ItemProps) => {
</div>
</div>
{editing ? (
<div className="flex-1 w-full">
<div className="w-full flex-1">
<Form.Field
control={form.control}
name="quantity"
@@ -506,14 +506,14 @@ const VariantItem = ({ item, preview, currencyCode }: ItemProps) => {
/>
</div>
) : (
<div className="flex-1 w-full">
<div className="w-full flex-1">
<Text size="small" weight="plus">
{item.quantity}
</Text>
</div>
)}
{editing ? (
<div className="flex-1 w-full">
<div className="w-full flex-1">
<Form.Field
control={form.control}
name="unit_price"
@@ -536,7 +536,7 @@ const VariantItem = ({ item, preview, currencyCode }: ItemProps) => {
/>
</div>
) : (
<div className="flex-1 flex items-center justify-end w-full">
<div className="flex w-full flex-1 items-center justify-end">
<Text size="small" weight="plus">
{getLocaleAmount(item.unit_price, currencyCode)}
</Text>
@@ -670,7 +670,7 @@ const CustomItem = ({ item, preview, currencyCode }: ItemProps) => {
return (
<Form {...form}>
<form onSubmit={onSubmit}>
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_28px] gap-3 px-4 py-2 bg-ui-bg-base shadow-elevation-card-rest rounded-lg items-center">
<div className="bg-ui-bg-base shadow-elevation-card-rest grid grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,2fr)_28px] items-center gap-3 rounded-lg px-4 py-2">
<div className="flex items-center gap-x-3">
<Thumbnail
thumbnail={item.thumbnail}
@@ -737,7 +737,7 @@ const CustomItem = ({ item, preview, currencyCode }: ItemProps) => {
}}
/>
) : (
<div className="flex-1 flex items-center justify-end">
<div className="flex flex-1 items-center justify-end">
<Text size="small" weight="plus">
{getLocaleAmount(item.unit_price, currencyCode)}
</Text>
@@ -917,7 +917,7 @@ const ExistingItemsForm = ({ orderId, items }: ExistingItemsFormProps) => {
/>
</StackedFocusModal.Body>
<StackedFocusModal.Footer>
<div className="flex items-center gap-x-2 justify-end">
<div className="flex items-center justify-end gap-x-2">
<StackedFocusModal.Close asChild>
<Button size="small" variant="secondary" type="button">
Cancel
@@ -1120,9 +1120,9 @@ const CustomItemForm = ({ orderId, currencyCode }: CustomItemFormProps) => {
<Form.Label>Quantity</Form.Label>
<Form.Hint>Enter the quantity of the item</Form.Hint>
</div>
<div className="flex-1 w-full">
<div className="w-full flex-1">
<Form.Control>
<div className="flex-1 w-full">
<div className="w-full flex-1">
<NumberInput {...field} className="w-full" />
</div>
</Form.Control>
@@ -1136,7 +1136,7 @@ const CustomItemForm = ({ orderId, currencyCode }: CustomItemFormProps) => {
</div>
</StackedFocusModal.Body>
<StackedFocusModal.Footer>
<div className="flex items-center gap-x-2 justify-end">
<div className="flex items-center justify-end gap-x-2">
<StackedFocusModal.Close asChild>
<Button size="small" variant="secondary" type="button">
Cancel

View File

@@ -78,24 +78,24 @@ const PromotionForm = ({ preview }: PromotionFormProps) => {
const { mutateAsync: addPromotions, isPending: isAddingPromotions } =
useDraftOrderAddPromotions(preview.id)
const promoCodes = getPromotionCodes(items, shipping_methods)
const promoIds = getPromotionIds(items, shipping_methods)
const { promotions, isPending, isError, error } = usePromotions(
{
code: promoCodes,
id: promoIds,
},
{
enabled: !!promoCodes.length,
enabled: !!promoIds.length,
}
)
const comboboxData = useComboboxData({
queryKey: ["promotions", "combobox", promoCodes],
queryKey: ["promotions", "combobox", promoIds],
queryFn: async (params) => {
return await sdk.admin.promotion.list({
...params,
code: {
$nin: promoCodes,
id: {
$nin: promoIds,
},
})
},
@@ -261,7 +261,7 @@ const PromotionItem = ({
<div
key={promotion.id}
className={clx(
"px-3 py-2 rounded-lg bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between",
"bg-ui-bg-component shadow-elevation-card-rest flex items-center justify-between rounded-lg px-3 py-2",
{
"animate-pulse": isLoading,
}
@@ -271,7 +271,7 @@ const PromotionItem = ({
<Text size="small" weight="plus" leading="compact">
{promotion.code}
</Text>
<div className="flex items-center gap-1.5 text-ui-fg-subtle">
<div className="text-ui-fg-subtle flex items-center gap-1.5">
{displayValue && (
<div className="flex items-center gap-1.5">
<Text size="small" leading="compact">
@@ -337,17 +337,17 @@ const formatPercentage = (value?: number | null, isPercentageValue = false) => {
return formatter.format(val)
}
function getPromotionCodes(
function getPromotionIds(
items: HttpTypes.AdminOrderPreview["items"],
shippingMethods: HttpTypes.AdminOrderPreview["shipping_methods"]
) {
const codes = new Set<string>()
const promotionIds = new Set<string>()
for (const item of items) {
if (item.adjustments) {
for (const adjustment of item.adjustments) {
if (adjustment.code) {
codes.add(adjustment.code)
if (adjustment.promotion_id) {
promotionIds.add(adjustment.promotion_id)
}
}
}
@@ -356,14 +356,14 @@ function getPromotionCodes(
for (const shippingMethod of shippingMethods) {
if (shippingMethod.adjustments) {
for (const adjustment of shippingMethod.adjustments) {
if (adjustment.code) {
codes.add(adjustment.code)
if (adjustment.promotion_id) {
promotionIds.add(adjustment.promotion_id)
}
}
}
}
return Array.from(codes)
return Array.from(promotionIds)
}
export default Promotions