feat(dashboard,medusa): Draft order list page (#6658)
**What** - (dashboard) Adds list page for Draft Order domain - (medusa) Adds the ability to filter draft orders
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
export const DraftOrderDetails = () => {
|
||||
return <div>Draft Order Details</div>;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { DraftOrderDetails as Component } from "./details";
|
||||
@@ -0,0 +1,3 @@
|
||||
export const DraftOrderDetails = () => {
|
||||
return <div>Draft Order Details</div>
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { DraftOrderDetails as Component } from "./details"
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { Button, Container, Heading } from "@medusajs/ui"
|
||||
import { useAdminDraftOrders } from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link } from "react-router-dom"
|
||||
import { DataTable } from "../../../../../components/table/data-table"
|
||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||
import { useDraftOrderTableColumns } from "./use-draft-order-table-columns"
|
||||
import { useDraftOrderTableFilters } from "./use-draft-order-table-filters"
|
||||
import { useDraftOrderTableQuery } from "./use-draft-order-table-query"
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export const DraftOrderListTable = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { searchParams, raw } = useDraftOrderTableQuery({
|
||||
pageSize: PAGE_SIZE,
|
||||
})
|
||||
const { draft_orders, count, isLoading, isError, error } =
|
||||
useAdminDraftOrders(
|
||||
{
|
||||
...searchParams,
|
||||
expand: "cart,cart.customer",
|
||||
},
|
||||
{
|
||||
keepPreviousData: true,
|
||||
}
|
||||
)
|
||||
|
||||
const columns = useDraftOrderTableColumns()
|
||||
const filters = useDraftOrderTableFilters()
|
||||
|
||||
const { table } = useDataTable({
|
||||
data: draft_orders || [],
|
||||
columns,
|
||||
count,
|
||||
enablePagination: true,
|
||||
getRowId: (row) => row.id,
|
||||
pageSize: PAGE_SIZE,
|
||||
})
|
||||
|
||||
if (isError) {
|
||||
throw error
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="divide-y p-0">
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Heading>{t("draftOrders.domain")}</Heading>
|
||||
<Button variant="secondary" size="small" asChild>
|
||||
<Link to="create">{t("actions.create")}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<DataTable
|
||||
table={table}
|
||||
isLoading={isLoading}
|
||||
columns={columns}
|
||||
filters={filters}
|
||||
pageSize={PAGE_SIZE}
|
||||
count={count}
|
||||
search
|
||||
pagination
|
||||
navigateTo={(row) => row.original.id}
|
||||
orderBy={["status", "created_at", "updated_at"]}
|
||||
queryObject={raw}
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { PencilSquare, Trash } from "@medusajs/icons"
|
||||
import { DraftOrder } from "@medusajs/medusa"
|
||||
import { usePrompt } from "@medusajs/ui"
|
||||
import { useAdminDeleteDraftOrder } from "medusa-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||
|
||||
export const DraftOrderTableActions = ({
|
||||
draftOrder,
|
||||
}: {
|
||||
draftOrder: DraftOrder
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const prompt = usePrompt()
|
||||
|
||||
const { mutateAsync } = useAdminDeleteDraftOrder(draftOrder.id)
|
||||
|
||||
const handleDelete = async () => {
|
||||
const res = await prompt({
|
||||
title: t("general.areYouSure"),
|
||||
description: t("draftOrders.deleteWarning", {
|
||||
id: `#${draftOrder.display_id}`,
|
||||
}),
|
||||
confirmText: t("actions.delete"),
|
||||
cancelText: t("actions.cancel"),
|
||||
})
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
|
||||
await mutateAsync()
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionMenu
|
||||
groups={[
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: t("actions.edit"),
|
||||
to: `${draftOrder.id}/edit`,
|
||||
icon: <PencilSquare />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: t("actions.delete"),
|
||||
onClick: handleDelete,
|
||||
icon: <Trash />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./draft-order-list-table"
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import { DraftOrder } from "@medusajs/medusa"
|
||||
import { createColumnHelper } from "@tanstack/react-table"
|
||||
import { useMemo } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { DateCell } from "../../../../../components/table/table-cells/common/date-cell"
|
||||
import { StatusCell } from "../../../../../components/table/table-cells/common/status-cell"
|
||||
import {
|
||||
CustomerCell,
|
||||
CustomerHeader,
|
||||
} from "../../../../../components/table/table-cells/order/customer-cell"
|
||||
import { DraftOrderTableActions } from "./draft-order-table-actions"
|
||||
|
||||
const columnHelper = createColumnHelper<DraftOrder>()
|
||||
|
||||
export const useDraftOrderTableColumns = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
columnHelper.accessor("display_id", {
|
||||
header: t("fields.id"),
|
||||
cell: ({ getValue }) => (
|
||||
<div className="flex size-full items-center overflow-hidden">
|
||||
<span className="truncate">#{getValue()}</span>
|
||||
</div>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("status", {
|
||||
header: t("fields.status"),
|
||||
cell: ({ getValue }) => {
|
||||
const status = getValue()
|
||||
|
||||
const { color, label } = {
|
||||
open: { color: "orange", label: t("draftOrders.status.open") },
|
||||
completed: {
|
||||
color: "green",
|
||||
label: t("draftOrders.status.completed"),
|
||||
},
|
||||
}[status] as { color: "green" | "orange"; label: string }
|
||||
|
||||
return <StatusCell color={color}>{label}</StatusCell>
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("order", {
|
||||
header: t("fields.order"),
|
||||
cell: ({ getValue }) => {
|
||||
const displayId = getValue()?.display_id
|
||||
|
||||
return (
|
||||
<div className="flex size-full items-center overflow-hidden">
|
||||
<span className="truncate">
|
||||
{displayId ? `#${displayId}` : "-"}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("cart.customer", {
|
||||
header: () => <CustomerHeader />,
|
||||
cell: ({ getValue }) => {
|
||||
const customer = getValue()
|
||||
|
||||
return <CustomerCell customer={customer} />
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("created_at", {
|
||||
header: t("fields.createdAt"),
|
||||
cell: ({ getValue }) => {
|
||||
const date = getValue()
|
||||
|
||||
return <DateCell date={date} />
|
||||
},
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: "actions",
|
||||
cell: ({ row }) => <DraftOrderTableActions draftOrder={row.original} />,
|
||||
}),
|
||||
],
|
||||
[t]
|
||||
)
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Filter } from "../../../../../components/table/data-table"
|
||||
|
||||
export const useDraftOrderTableFilters = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const filters: Filter[] = [
|
||||
{
|
||||
type: "select",
|
||||
multiple: true,
|
||||
key: "status",
|
||||
label: t("fields.status"),
|
||||
options: [
|
||||
{
|
||||
label: t("draftOrders.status.open"),
|
||||
value: "open",
|
||||
},
|
||||
{
|
||||
label: t("draftOrders.status.completed"),
|
||||
value: "completed",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const dateFilters: Filter[] = [
|
||||
{ label: t("fields.createdAt"), key: "created_at" },
|
||||
{ label: t("fields.updatedAt"), key: "updated_at" },
|
||||
].map((f) => ({
|
||||
key: f.key,
|
||||
label: f.label,
|
||||
type: "date",
|
||||
}))
|
||||
|
||||
return [...filters, ...dateFilters]
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { AdminGetDraftOrdersParams } from "@medusajs/medusa"
|
||||
import { useQueryParams } from "../../../../../hooks/use-query-params"
|
||||
|
||||
export const useDraftOrderTableQuery = ({
|
||||
pageSize = 20,
|
||||
prefix,
|
||||
}: {
|
||||
pageSize?: number
|
||||
prefix?: string
|
||||
}) => {
|
||||
const raw = useQueryParams(
|
||||
["offset", "q", "order", "status", "created_at", "updated_at"],
|
||||
prefix
|
||||
)
|
||||
|
||||
const { status, offset, created_at, updated_at, ...rest } = raw
|
||||
|
||||
const searchParams: AdminGetDraftOrdersParams = {
|
||||
limit: pageSize,
|
||||
offset: offset ? Number(offset) : 0,
|
||||
status: status ? (status.split(",") as ["open" | "completed"]) : undefined,
|
||||
created_at: created_at ? JSON.parse(created_at) : undefined,
|
||||
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
|
||||
...rest,
|
||||
}
|
||||
|
||||
return {
|
||||
searchParams,
|
||||
raw,
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { DraftOrderListTable } from "./components/draft-order-list-table"
|
||||
|
||||
export const DraftOrderList = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<DraftOrderListTable />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { DraftOrderList as Component } from "./draft-order-list"
|
||||
@@ -1 +0,0 @@
|
||||
export { DraftOrderList as Component } from "./list";
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Container, Heading } from "@medusajs/ui";
|
||||
|
||||
export const DraftOrderList = () => {
|
||||
return (
|
||||
<Container>
|
||||
<Heading>Draft Orders</Heading>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user