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:
@@ -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 = (
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user