fix(dashboard): Cleanup unsafe accesses to possibly undefined values in order timeline (#10454)
**What** - Fixes an unsafe access to a field that may be undefined if an order was created through a custom workflow.
This commit is contained in:
+73
-68
@@ -1,10 +1,8 @@
|
||||
import { Button, IconButton, Text, Tooltip, clx, usePrompt } from "@medusajs/ui"
|
||||
import { Button, Text, Tooltip, clx, usePrompt } from "@medusajs/ui"
|
||||
import * as Collapsible from "@radix-ui/react-collapsible"
|
||||
|
||||
import { PropsWithChildren, ReactNode, useMemo, useState } from "react"
|
||||
import { Link } from "react-router-dom"
|
||||
|
||||
import { XMarkMini } from "@medusajs/icons"
|
||||
import {
|
||||
AdminClaim,
|
||||
AdminExchange,
|
||||
@@ -16,6 +14,7 @@ import {
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { AdminOrderLineItem } from "@medusajs/types"
|
||||
import { By } from "../../../../../components/common/user-link"
|
||||
import {
|
||||
useCancelOrderTransfer,
|
||||
useCustomer,
|
||||
@@ -29,12 +28,11 @@ import {
|
||||
} from "../../../../../hooks/api/exchanges"
|
||||
import { useCancelReturn, useReturns } from "../../../../../hooks/api/returns"
|
||||
import { useDate } from "../../../../../hooks/use-date"
|
||||
import { getFormattedAddress } from "../../../../../lib/addresses"
|
||||
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
|
||||
import { getPaymentsFromOrder } from "../order-payment-section"
|
||||
import ActivityItems from "./activity-items"
|
||||
import { By, UserLink } from "../../../../../components/common/user-link"
|
||||
import ChangeDetailsTooltip from "./change-details-tooltip"
|
||||
import { getFormattedAddress } from "../../../../../lib/addresses"
|
||||
|
||||
type OrderTimelineProps = {
|
||||
order: AdminOrder
|
||||
@@ -394,12 +392,12 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
||||
edit.status === "requested"
|
||||
? edit.requested_at
|
||||
: edit.status === "confirmed"
|
||||
? edit.confirmed_at
|
||||
: edit.status === "declined"
|
||||
? edit.declined_at
|
||||
: edit.status === "canceled"
|
||||
? edit.canceled_at
|
||||
: edit.created_at,
|
||||
? edit.confirmed_at
|
||||
: edit.status === "declined"
|
||||
? edit.declined_at
|
||||
: edit.status === "canceled"
|
||||
? edit.canceled_at
|
||||
: edit.created_at,
|
||||
children: isConfirmed ? <OrderEditBody edit={edit} /> : null,
|
||||
})
|
||||
}
|
||||
@@ -455,7 +453,7 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
||||
),
|
||||
timestamp: update.created_at,
|
||||
children: (
|
||||
<div className="mt-2 flex text-sm gap-x-2 text-ui-fg-subtle">
|
||||
<div className="text-ui-fg-subtle mt-2 flex gap-x-2 text-sm">
|
||||
{t("fields.by")} <By id={update.created_by} />
|
||||
</div>
|
||||
),
|
||||
@@ -477,7 +475,7 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
||||
),
|
||||
timestamp: update.created_at,
|
||||
children: (
|
||||
<div className="mt-2 flex text-sm gap-x-2 text-ui-fg-subtle">
|
||||
<div className="text-ui-fg-subtle mt-2 flex gap-x-2 text-sm">
|
||||
{t("fields.by")} <By id={update.created_by} />
|
||||
</div>
|
||||
),
|
||||
@@ -495,7 +493,7 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
||||
),
|
||||
timestamp: update.created_at,
|
||||
children: (
|
||||
<div className="mt-2 flex text-sm gap-x-2 text-ui-fg-subtle">
|
||||
<div className="text-ui-fg-subtle mt-2 flex gap-x-2 text-sm">
|
||||
{t("fields.by")} <By id={update.created_by} />
|
||||
</div>
|
||||
),
|
||||
@@ -674,64 +672,67 @@ const OrderActivityCollapsible = ({
|
||||
)
|
||||
}
|
||||
|
||||
const NoteBody = ({ note }: { note: Note }) => {
|
||||
const { t } = useTranslation()
|
||||
const prompt = usePrompt()
|
||||
/**
|
||||
* TODO: Add once notes are supported.
|
||||
*/
|
||||
// const NoteBody = ({ note }: { note: Note }) => {
|
||||
// const { t } = useTranslation()
|
||||
// const prompt = usePrompt()
|
||||
|
||||
const { first_name, last_name, email } = note.author || {}
|
||||
const name = [first_name, last_name].filter(Boolean).join(" ")
|
||||
// const { first_name, last_name, email } = note.author || {}
|
||||
// const name = [first_name, last_name].filter(Boolean).join(" ")
|
||||
|
||||
const byLine = t("orders.activity.events.note.byLine", {
|
||||
author: name || email,
|
||||
})
|
||||
// const byLine = t("orders.activity.events.note.byLine", {
|
||||
// author: name || email,
|
||||
// })
|
||||
|
||||
const { mutateAsync } = {} // useAdminDeleteNote(note.id)
|
||||
// const { mutateAsync } = {} // useAdminDeleteNote(note.id)
|
||||
|
||||
const handleDelete = async () => {
|
||||
const res = await prompt({
|
||||
title: t("general.areYouSure"),
|
||||
description: "This action cannot be undone",
|
||||
confirmText: t("actions.delete"),
|
||||
cancelText: t("actions.cancel"),
|
||||
})
|
||||
// const handleDelete = async () => {
|
||||
// const res = await prompt({
|
||||
// title: t("general.areYouSure"),
|
||||
// description: "This action cannot be undone",
|
||||
// confirmText: t("actions.delete"),
|
||||
// cancelText: t("actions.cancel"),
|
||||
// })
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
// if (!res) {
|
||||
// return
|
||||
// }
|
||||
|
||||
await mutateAsync()
|
||||
}
|
||||
// await mutateAsync()
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-2 pt-2">
|
||||
<div className="bg-ui-bg-component shadow-borders-base group grid grid-cols-[1fr_20px] items-start gap-x-2 text-pretty rounded-r-2xl rounded-bl-md rounded-tl-xl px-3 py-1.5">
|
||||
<div className="flex h-full min-h-7 items-center">
|
||||
<Text size="xsmall" className="text-ui-fg-subtle">
|
||||
{note.value}
|
||||
</Text>
|
||||
</div>
|
||||
<IconButton
|
||||
size="small"
|
||||
variant="transparent"
|
||||
className="transition-fg invisible opacity-0 group-hover:visible group-hover:opacity-100"
|
||||
type="button"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<span className="sr-only">
|
||||
{t("orders.activity.comment.deleteButtonText")}
|
||||
</span>
|
||||
<XMarkMini className="text-ui-fg-muted" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<Link
|
||||
to={`/settings/users/${note.author_id}`}
|
||||
className="text-ui-fg-subtle hover:text-ui-fg-base transition-fg w-fit"
|
||||
>
|
||||
<Text size="small">{byLine}</Text>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
// return (
|
||||
// <div className="flex flex-col gap-y-2 pt-2">
|
||||
// <div className="bg-ui-bg-component shadow-borders-base group grid grid-cols-[1fr_20px] items-start gap-x-2 text-pretty rounded-r-2xl rounded-bl-md rounded-tl-xl px-3 py-1.5">
|
||||
// <div className="flex h-full min-h-7 items-center">
|
||||
// <Text size="xsmall" className="text-ui-fg-subtle">
|
||||
// {note.value}
|
||||
// </Text>
|
||||
// </div>
|
||||
// <IconButton
|
||||
// size="small"
|
||||
// variant="transparent"
|
||||
// className="transition-fg invisible opacity-0 group-hover:visible group-hover:opacity-100"
|
||||
// type="button"
|
||||
// onClick={handleDelete}
|
||||
// >
|
||||
// <span className="sr-only">
|
||||
// {t("orders.activity.comment.deleteButtonText")}
|
||||
// </span>
|
||||
// <XMarkMini className="text-ui-fg-muted" />
|
||||
// </IconButton>
|
||||
// </div>
|
||||
// <Link
|
||||
// to={`/settings/users/${note.author_id}`}
|
||||
// className="text-ui-fg-subtle hover:text-ui-fg-base transition-fg w-fit"
|
||||
// >
|
||||
// <Text size="small">{byLine}</Text>
|
||||
// </Link>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
const FulfillmentCreatedBody = ({
|
||||
fulfillment,
|
||||
@@ -1085,11 +1086,15 @@ function getMissingLineItemIds(order: AdminOrder, changes: AdminOrderChange[]) {
|
||||
|
||||
changes.forEach((change) => {
|
||||
change.actions.forEach((action) => {
|
||||
if (!action.details?.reference_id) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
(action.details!.reference_id as string).startsWith("ordli_") &&
|
||||
!existingItemsMap.has(action.details!.reference_id as string)
|
||||
(action.details.reference_id as string).startsWith("ordli_") &&
|
||||
!existingItemsMap.has(action.details.reference_id as string)
|
||||
) {
|
||||
retIds.add(action.details!.reference_id as string)
|
||||
retIds.add(action.details.reference_id as string)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
|
||||
import { RouteFocusModal } from "../../../components/modals"
|
||||
import { CreateShippingProfileForm } from "./components/create-shipping-profile-form"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user