fix(admin): Add skus to claim menus (#3368)
**What** - Adds SKU to all tables in claim flow, allowing admins to easily copy them to their clipboard.
This commit is contained in:
@@ -32,14 +32,15 @@ const CopyToClipboard: React.FC<CopyToClipboardProps> = ({
|
|||||||
if (isCopied) {
|
if (isCopied) {
|
||||||
notification("Success", "Copied!", "success")
|
notification("Success", "Copied!", "success")
|
||||||
}
|
}
|
||||||
}, [isCopied])
|
}, [isCopied, notification])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center inter-small-regular text-grey-50 gap-x-xsmall">
|
<div className="inter-small-regular text-grey-50 gap-x-xsmall flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="small"
|
size="small"
|
||||||
className={clsx("p-0 text-grey-50", {
|
type="button"
|
||||||
|
className={clsx("text-grey-50 p-0", {
|
||||||
["text-violet-60"]: isCopied,
|
["text-violet-60"]: isCopied,
|
||||||
})}
|
})}
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
|
|||||||
+12
-12
@@ -104,14 +104,14 @@ const Claim = ({ event }: Props) => {
|
|||||||
topNode: Actions,
|
topNode: Actions,
|
||||||
children: [
|
children: [
|
||||||
<Fragment key={event.id}>
|
<Fragment key={event.id}>
|
||||||
<div className="flex flex-col gap-y-base">
|
<div className="gap-y-base flex flex-col">
|
||||||
<ClaimStatus event={event} />
|
<ClaimStatus event={event} />
|
||||||
{renderClaimItems(event)}
|
{renderClaimItems(event)}
|
||||||
{event.claim?.additional_items?.length > 0 &&
|
{event.claim?.additional_items?.length > 0 &&
|
||||||
renderReplacementItems(event)}
|
renderReplacementItems(event)}
|
||||||
{shouldHaveButtonActions && (
|
{shouldHaveButtonActions && (
|
||||||
<div className="flex items-center gap-x-xsmall">
|
<div className="gap-x-xsmall flex items-center">
|
||||||
{event.claim.return_order.status === "requested" && (
|
{event.claim.return_order?.status === "requested" && (
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -157,7 +157,7 @@ const Claim = ({ event }: Props) => {
|
|||||||
export default Claim
|
export default Claim
|
||||||
|
|
||||||
const ClaimStatus = ({ event }: Props) => {
|
const ClaimStatus = ({ event }: Props) => {
|
||||||
const divider = <div className="h-11 w-px bg-grey-20" />
|
const divider = <div className="bg-grey-20 h-11 w-px" />
|
||||||
|
|
||||||
const shouldHaveFulfillment =
|
const shouldHaveFulfillment =
|
||||||
!!event.claim?.fulfillment_status &&
|
!!event.claim?.fulfillment_status &&
|
||||||
@@ -168,7 +168,7 @@ const ClaimStatus = ({ event }: Props) => {
|
|||||||
|
|
||||||
if (event.claim?.type === "replace") {
|
if (event.claim?.type === "replace") {
|
||||||
refundStatus =
|
refundStatus =
|
||||||
event.claim?.return_order.status === "received"
|
event.claim?.return_order?.status === "received"
|
||||||
? "refunded"
|
? "refunded"
|
||||||
: event.claim?.payment_status
|
: event.claim?.payment_status
|
||||||
}
|
}
|
||||||
@@ -178,15 +178,15 @@ const ClaimStatus = ({ event }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="inter-small-regular flex items-center gap-x-base">
|
<div className="inter-small-regular gap-x-base flex items-center">
|
||||||
<div className="flex flex-col gap-y-2xsmall">
|
<div className="gap-y-2xsmall flex flex-col">
|
||||||
<span className="text-grey-50">Refund:</span>
|
<span className="text-grey-50">Refund:</span>
|
||||||
<RefundStatus refundStatus={refundStatus} />
|
<RefundStatus refundStatus={refundStatus} />
|
||||||
</div>
|
</div>
|
||||||
{shouldHaveReturnStatus && (
|
{shouldHaveReturnStatus && (
|
||||||
<>
|
<>
|
||||||
{divider}
|
{divider}
|
||||||
<div className="flex flex-col gap-y-2xsmall">
|
<div className="gap-y-2xsmall flex flex-col">
|
||||||
<span className="text-grey-50">Return:</span>
|
<span className="text-grey-50">Return:</span>
|
||||||
<ReturnStatus returnStatus={event.returnStatus} />
|
<ReturnStatus returnStatus={event.returnStatus} />
|
||||||
</div>
|
</div>
|
||||||
@@ -195,7 +195,7 @@ const ClaimStatus = ({ event }: Props) => {
|
|||||||
{shouldHaveFulfillment && (
|
{shouldHaveFulfillment && (
|
||||||
<>
|
<>
|
||||||
{divider}
|
{divider}
|
||||||
<div className="flex flex-col gap-y-2xsmall">
|
<div className="gap-y-2xsmall flex flex-col">
|
||||||
<span className="text-grey-50">Fulfillment:</span>
|
<span className="text-grey-50">Fulfillment:</span>
|
||||||
<FulfillmentStatus
|
<FulfillmentStatus
|
||||||
fulfillmentStatus={event.claim?.fulfillment_status}
|
fulfillmentStatus={event.claim?.fulfillment_status}
|
||||||
@@ -209,7 +209,7 @@ const ClaimStatus = ({ event }: Props) => {
|
|||||||
|
|
||||||
const renderClaimItems = (event: ClaimEvent) => {
|
const renderClaimItems = (event: ClaimEvent) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-small">
|
<div className="gap-y-small flex flex-col">
|
||||||
<span className="inter-small-regular text-grey-50">Claim Items</span>
|
<span className="inter-small-regular text-grey-50">Claim Items</span>
|
||||||
<div>
|
<div>
|
||||||
{event.claimItems.map((i, index) => (
|
{event.claimItems.map((i, index) => (
|
||||||
@@ -222,7 +222,7 @@ const renderClaimItems = (event: ClaimEvent) => {
|
|||||||
|
|
||||||
const renderReplacementItems = (event: ClaimEvent) => {
|
const renderReplacementItems = (event: ClaimEvent) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-small">
|
<div className="gap-y-small flex flex-col">
|
||||||
<span className="inter-small-regular text-grey-50">
|
<span className="inter-small-regular text-grey-50">
|
||||||
Replacement Items
|
Replacement Items
|
||||||
</span>
|
</span>
|
||||||
@@ -245,7 +245,7 @@ const renderClaimActions = (
|
|||||||
if (!event.canceledAt && !event.isCanceled) {
|
if (!event.canceledAt && !event.isCanceled) {
|
||||||
if (
|
if (
|
||||||
event.claim.return_order &&
|
event.claim.return_order &&
|
||||||
event.claim.return_order.status === "requested"
|
event.claim.return_order?.status === "requested"
|
||||||
) {
|
) {
|
||||||
actions.push({
|
actions.push({
|
||||||
icon: <TrashIcon size={20} />,
|
icon: <TrashIcon size={20} />,
|
||||||
|
|||||||
+17
-3
@@ -1,6 +1,7 @@
|
|||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import React, { ChangeEvent, useCallback, useMemo } from "react"
|
import React, { ChangeEvent, useCallback, useMemo } from "react"
|
||||||
import { Controller, useWatch } from "react-hook-form"
|
import { Controller, useWatch } from "react-hook-form"
|
||||||
|
import CopyToClipboard from "../../../../components/atoms/copy-to-clipboard"
|
||||||
import Thumbnail from "../../../../components/atoms/thumbnail"
|
import Thumbnail from "../../../../components/atoms/thumbnail"
|
||||||
import IndeterminateCheckbox from "../../../../components/molecules/indeterminate-checkbox"
|
import IndeterminateCheckbox from "../../../../components/molecules/indeterminate-checkbox"
|
||||||
import { NestedForm } from "../../../../utils/nested-form"
|
import { NestedForm } from "../../../../utils/nested-form"
|
||||||
@@ -91,14 +92,27 @@ export const useItemsToReceiveColumns = ({ form, orderCurrency }: Props) => {
|
|||||||
columnHelper.accessor("variant_title", {
|
columnHelper.accessor("variant_title", {
|
||||||
header: "Product",
|
header: "Product",
|
||||||
cell: ({ getValue, row: { original } }) => {
|
cell: ({ getValue, row: { original } }) => {
|
||||||
|
const value = getValue()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-x-base py-xsmall">
|
<div className="gap-x-base py-xsmall flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<Thumbnail src={original.thumbnail} />
|
<Thumbnail src={original.thumbnail} />
|
||||||
</div>
|
</div>
|
||||||
<div className="inter-small-regular">
|
<div className="inter-small-regular">
|
||||||
<p>{original.product_title}</p>
|
<div className="gap-x-2xsmall flex items-center">
|
||||||
<p className="text-grey-50">{getValue()}</p>
|
<p>{original.product_title}</p>
|
||||||
|
{value && <p className="text-grey-50">({value})</p>}
|
||||||
|
</div>
|
||||||
|
{original.sku && (
|
||||||
|
<span>
|
||||||
|
<CopyToClipboard
|
||||||
|
value={original.sku}
|
||||||
|
displayValue={original.sku}
|
||||||
|
iconSize={14}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export type ReturnItem = {
|
|||||||
thumbnail?: string | null
|
thumbnail?: string | null
|
||||||
product_title: string
|
product_title: string
|
||||||
variant_title: string
|
variant_title: string
|
||||||
|
sku?: string | null
|
||||||
quantity: number
|
quantity: number
|
||||||
original_quantity: number
|
original_quantity: number
|
||||||
total: number
|
total: number
|
||||||
@@ -75,7 +76,7 @@ const ItemsToReturnForm = ({ form, order, isClaim = false }: Props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-base">
|
<div className="gap-y-base flex flex-col">
|
||||||
<h2 className="inter-base-semibold">
|
<h2 className="inter-base-semibold">
|
||||||
Items to {isClaim ? "claim" : "return"}
|
Items to {isClaim ? "claim" : "return"}
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
+17
-3
@@ -2,6 +2,7 @@ import { createColumnHelper } from "@tanstack/react-table"
|
|||||||
import React, { useCallback, useMemo } from "react"
|
import React, { useCallback, useMemo } from "react"
|
||||||
import { Controller, useWatch } from "react-hook-form"
|
import { Controller, useWatch } from "react-hook-form"
|
||||||
import { ItemsToReturnFormType, ReturnItemObject } from "."
|
import { ItemsToReturnFormType, ReturnItemObject } from "."
|
||||||
|
import CopyToClipboard from "../../../../components/atoms/copy-to-clipboard"
|
||||||
import { Thumbnail } from "../../../../components/atoms/thumbnail/thumbnail"
|
import { Thumbnail } from "../../../../components/atoms/thumbnail/thumbnail"
|
||||||
import IndeterminateCheckbox from "../../../../components/molecules/indeterminate-checkbox"
|
import IndeterminateCheckbox from "../../../../components/molecules/indeterminate-checkbox"
|
||||||
import { NestedForm } from "../../../../utils/nested-form"
|
import { NestedForm } from "../../../../utils/nested-form"
|
||||||
@@ -98,14 +99,27 @@ export const useItemsToReturnColumns = ({ form, orderCurrency }: Props) => {
|
|||||||
columnHelper.accessor("variant_title", {
|
columnHelper.accessor("variant_title", {
|
||||||
header: "Product",
|
header: "Product",
|
||||||
cell: ({ getValue, row: { original } }) => {
|
cell: ({ getValue, row: { original } }) => {
|
||||||
|
const value = getValue()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-x-base py-xsmall">
|
<div className="gap-x-base py-xsmall flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<Thumbnail src={original.thumbnail} />
|
<Thumbnail src={original.thumbnail} />
|
||||||
</div>
|
</div>
|
||||||
<div className="inter-small-regular">
|
<div className="inter-small-regular">
|
||||||
<p>{original.product_title}</p>
|
<div className="gap-x-2xsmall flex items-center">
|
||||||
<p className="text-grey-50">{getValue()}</p>
|
<p>{original.product_title}</p>
|
||||||
|
{value && <p className="text-grey-50">({value})</p>}
|
||||||
|
</div>
|
||||||
|
{original.sku && (
|
||||||
|
<span>
|
||||||
|
<CopyToClipboard
|
||||||
|
value={original.sku}
|
||||||
|
displayValue={original.sku}
|
||||||
|
iconSize={14}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
+19
-8
@@ -1,5 +1,6 @@
|
|||||||
import { createColumnHelper } from "@tanstack/react-table"
|
import { createColumnHelper } from "@tanstack/react-table"
|
||||||
import { useCallback, useMemo } from "react"
|
import { useCallback, useMemo } from "react"
|
||||||
|
import CopyToClipboard from "../../../../components/atoms/copy-to-clipboard"
|
||||||
import Thumbnail from "../../../../components/atoms/thumbnail"
|
import Thumbnail from "../../../../components/atoms/thumbnail"
|
||||||
import Tooltip from "../../../../components/atoms/tooltip"
|
import Tooltip from "../../../../components/atoms/tooltip"
|
||||||
import Button from "../../../../components/fundamentals/button"
|
import Button from "../../../../components/fundamentals/button"
|
||||||
@@ -45,14 +46,24 @@ export const useAdditionalItemsColumns = ({
|
|||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-base">
|
<div className="gap-base flex items-center">
|
||||||
<Thumbnail src={thumbnail} />
|
<Thumbnail src={thumbnail} />
|
||||||
<div>
|
<div className="inter-small-regular">
|
||||||
<p>
|
<div className="gap-x-2xsmall flex items-center">
|
||||||
{product_title}{" "}
|
<p>{product_title}</p>
|
||||||
<span className="text-grey-50">({variant_title})</span>
|
{variant_title && (
|
||||||
</p>
|
<p className="text-grey-50">({variant_title})</p>
|
||||||
{sku && <p className="text-grey-50">{sku}</p>}
|
)}
|
||||||
|
</div>
|
||||||
|
{sku && (
|
||||||
|
<span>
|
||||||
|
<CopyToClipboard
|
||||||
|
value={sku}
|
||||||
|
displayValue={sku}
|
||||||
|
iconSize={14}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -101,7 +112,7 @@ export const useAdditionalItemsColumns = ({
|
|||||||
content="The price has been overridden in a price list, that is applicable to this order."
|
content="The price has been overridden in a price list, that is applicable to this order."
|
||||||
side="top"
|
side="top"
|
||||||
>
|
>
|
||||||
<p className="cursor-default text-grey-40 line-through">
|
<p className="text-grey-40 cursor-default line-through">
|
||||||
{formatAmountWithSymbol({
|
{formatAmountWithSymbol({
|
||||||
amount: original_price,
|
amount: original_price,
|
||||||
currency: orderCurrency,
|
currency: orderCurrency,
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ClaimSummary = ({ form, order }: Props) => {
|
export const ClaimSummary = ({ form, order }: Props) => {
|
||||||
const { control, getValues } = form
|
const { control } = form
|
||||||
|
|
||||||
const claimItems = useWatch({
|
const claimItems = useWatch({
|
||||||
control: control,
|
control: control,
|
||||||
name: "return_items.items",
|
name: "return_items.items",
|
||||||
|
defaultValue: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedClaimItems = useMemo(() => {
|
const selectedClaimItems = useMemo(() => {
|
||||||
@@ -34,6 +35,7 @@ export const ClaimSummary = ({ form, order }: Props) => {
|
|||||||
const replacementItems = useWatch({
|
const replacementItems = useWatch({
|
||||||
control: control,
|
control: control,
|
||||||
name: "additional_items.items",
|
name: "additional_items.items",
|
||||||
|
defaultValue: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
const replacementItemShipping = useWatch({
|
const replacementItemShipping = useWatch({
|
||||||
@@ -84,6 +86,7 @@ export const ClaimSummary = ({ form, order }: Props) => {
|
|||||||
currencyCode={order.currency_code}
|
currencyCode={order.currency_code}
|
||||||
productTitle={item.product_title}
|
productTitle={item.product_title}
|
||||||
quantity={item.quantity}
|
quantity={item.quantity}
|
||||||
|
sku={item.sku}
|
||||||
price={item.total / item.original_quantity}
|
price={item.total / item.original_quantity}
|
||||||
total={
|
total={
|
||||||
(item.total / item.original_quantity) * item.quantity
|
(item.total / item.original_quantity) * item.quantity
|
||||||
@@ -123,6 +126,7 @@ export const ClaimSummary = ({ form, order }: Props) => {
|
|||||||
currencyCode={order.currency_code}
|
currencyCode={order.currency_code}
|
||||||
productTitle={item.product_title}
|
productTitle={item.product_title}
|
||||||
quantity={item.quantity}
|
quantity={item.quantity}
|
||||||
|
sku={item.sku}
|
||||||
price={item.price}
|
price={item.price}
|
||||||
total={item.price * item.quantity}
|
total={item.price * item.quantity}
|
||||||
variantTitle={item.variant_title}
|
variantTitle={item.variant_title}
|
||||||
|
|||||||
+14
-4
@@ -1,4 +1,5 @@
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
import CopyToClipboard from "../../../../components/atoms/copy-to-clipboard"
|
||||||
import Thumbnail from "../../../../components/atoms/thumbnail"
|
import Thumbnail from "../../../../components/atoms/thumbnail"
|
||||||
import { formatAmountWithSymbol } from "../../../../utils/prices"
|
import { formatAmountWithSymbol } from "../../../../utils/prices"
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ type SummaryLineProps = {
|
|||||||
quantity: number
|
quantity: number
|
||||||
price: number
|
price: number
|
||||||
total: number
|
total: number
|
||||||
|
sku?: string | null
|
||||||
currencyCode: string
|
currencyCode: string
|
||||||
isFree?: boolean
|
isFree?: boolean
|
||||||
}
|
}
|
||||||
@@ -17,6 +19,7 @@ export const SummaryLineItem = ({
|
|||||||
thumbnail,
|
thumbnail,
|
||||||
productTitle,
|
productTitle,
|
||||||
variantTitle,
|
variantTitle,
|
||||||
|
sku,
|
||||||
quantity,
|
quantity,
|
||||||
price,
|
price,
|
||||||
total,
|
total,
|
||||||
@@ -25,16 +28,23 @@ export const SummaryLineItem = ({
|
|||||||
}: SummaryLineProps) => {
|
}: SummaryLineProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-x-base">
|
<div className="gap-x-base flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<Thumbnail src={thumbnail} />
|
<Thumbnail src={thumbnail} />
|
||||||
</div>
|
</div>
|
||||||
<div className="inter-small-regular">
|
<div className="inter-small-regular">
|
||||||
<p>{productTitle}</p>
|
<div className="gap-x-2xsmall flex items-center">
|
||||||
<p className="text-grey-50">{variantTitle}</p>
|
<p>{productTitle}</p>
|
||||||
|
{variantTitle && <p className="text-grey-50">({variantTitle})</p>}
|
||||||
|
</div>
|
||||||
|
{sku && (
|
||||||
|
<span>
|
||||||
|
<CopyToClipboard value={sku} displayValue={sku} iconSize={14} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="inter-small-regular flex items-center gap-x-base">
|
<div className="inter-small-regular gap-x-base flex items-center">
|
||||||
<p className="text-grey-40">
|
<p className="text-grey-40">
|
||||||
{formatAmountWithSymbol({
|
{formatAmountWithSymbol({
|
||||||
amount: price,
|
amount: price,
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ const getReturnableItemsValues = (order: Order) => {
|
|||||||
thumbnail: item.thumbnail,
|
thumbnail: item.thumbnail,
|
||||||
refundable: item.refundable || 0,
|
refundable: item.refundable || 0,
|
||||||
product_title: item.variant.product.title,
|
product_title: item.variant.product.title,
|
||||||
|
sku: item.variant.sku,
|
||||||
variant_title: item.variant.title,
|
variant_title: item.variant.title,
|
||||||
quantity: returnableQuantity,
|
quantity: returnableQuantity,
|
||||||
original_quantity: item.quantity,
|
original_quantity: item.quantity,
|
||||||
|
|||||||
Reference in New Issue
Block a user