feat: order export and upload stream (#14243)
* feat: order export * Merge branch 'develop' of https://github.com/medusajs/medusa into feat/order-export * normalize status * rm util * serialize totals * test * lock * comments * configurable order list
This commit is contained in:
committed by
GitHub
parent
e199f1eb01
commit
9366c6d468
@@ -300,6 +300,12 @@ export function getRouteMap({
|
||||
{
|
||||
path: "",
|
||||
lazy: () => import("../../routes/orders/order-list"),
|
||||
children: [
|
||||
{
|
||||
path: "export",
|
||||
lazy: () => import("../../routes/orders/order-export"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: ":id",
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
import { sdk } from "../../lib/client"
|
||||
import { queryClient } from "../../lib/query-client"
|
||||
import { queryKeysFactory, TQueryKey } from "../../lib/query-key-factory"
|
||||
import { reservationItemsQueryKeys } from "./reservations"
|
||||
import { inventoryItemsQueryKeys } from "./inventory"
|
||||
import { reservationItemsQueryKeys } from "./reservations"
|
||||
|
||||
const ORDERS_QUERY_KEY = "orders" as const
|
||||
const _orderKeys = queryKeysFactory(ORDERS_QUERY_KEY) as TQueryKey<"orders"> & {
|
||||
@@ -438,3 +438,20 @@ export const useUpdateOrderChange = (
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
export const useExportOrders = (
|
||||
query?: HttpTypes.AdminOrderFilters,
|
||||
options?: UseMutationOptions<
|
||||
{ transaction_id: string },
|
||||
FetchError,
|
||||
HttpTypes.AdminOrderFilters
|
||||
>
|
||||
) => {
|
||||
return useMutation({
|
||||
mutationFn: () => sdk.admin.order.export(query),
|
||||
onSuccess: (data, variables, context) => {
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4049,6 +4049,45 @@
|
||||
"required": ["noRecordsMessage"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"export": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"header": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["title", "description"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["title", "description"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["header", "description", "success", "filters"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5740,6 +5779,7 @@
|
||||
"orderCanceled",
|
||||
"onDateFromSalesChannel",
|
||||
"list",
|
||||
"export",
|
||||
"status",
|
||||
"summary",
|
||||
"transfer",
|
||||
|
||||
@@ -1080,6 +1080,18 @@
|
||||
"list": {
|
||||
"noRecordsMessage": "Your orders will show up here."
|
||||
},
|
||||
"export": {
|
||||
"header": "Export Order List",
|
||||
"description": "Export the order list to a CSV file.",
|
||||
"success": {
|
||||
"title": "Export started",
|
||||
"description": "You will be notified when the export is ready."
|
||||
},
|
||||
"filters": {
|
||||
"title": "Filters",
|
||||
"description": "The following filters will be applied to the export."
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"not_paid": "Not paid",
|
||||
"pending": "Pending",
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Heading, Text } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { DataTableFilter } from "../../../../components/table/data-table/data-table-filter"
|
||||
import { useOrderTableFilters } from "../../order-list/components/order-list-table/use-order-table-filters"
|
||||
|
||||
export const ExportFilters = () => {
|
||||
const { t } = useTranslation()
|
||||
const filters = useOrderTableFilters()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Heading level="h2">{t("orders.export.filters.title")}</Heading>
|
||||
<Text size="small" className="text-ui-fg-subtle">
|
||||
{t("orders.export.filters.description")}
|
||||
</Text>
|
||||
|
||||
<div className="mt-4">
|
||||
<DataTableFilter filters={filters} readonly />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { OrderExport as Component } from "./order-export"
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Button, Heading, toast } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { RouteDrawer, useRouteModal } from "../../../components/modals"
|
||||
import { useExportOrders } from "../../../hooks/api"
|
||||
import { useOrderTableQuery } from "../../../hooks/table/query"
|
||||
import { ExportFilters } from "./components/export-filters"
|
||||
|
||||
export const OrderExport = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<RouteDrawer>
|
||||
<RouteDrawer.Header>
|
||||
<RouteDrawer.Title asChild>
|
||||
<Heading>{t("orders.export.header")}</Heading>
|
||||
</RouteDrawer.Title>
|
||||
<RouteDrawer.Description className="sr-only">
|
||||
{t("orders.export.description")}
|
||||
</RouteDrawer.Description>
|
||||
</RouteDrawer.Header>
|
||||
<OrderExportContent />
|
||||
</RouteDrawer>
|
||||
)
|
||||
}
|
||||
|
||||
const OrderExportContent = () => {
|
||||
const { t } = useTranslation()
|
||||
const { searchParams } = useOrderTableQuery({})
|
||||
|
||||
const { mutateAsync } = useExportOrders(searchParams)
|
||||
const { handleSuccess } = useRouteModal()
|
||||
|
||||
const handleExportRequest = async () => {
|
||||
await mutateAsync(searchParams, {
|
||||
onSuccess: () => {
|
||||
toast.info(t("orders.export.success.title"), {
|
||||
description: t("orders.export.success.description"),
|
||||
})
|
||||
handleSuccess()
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(err.message)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<RouteDrawer.Body>
|
||||
<ExportFilters />
|
||||
</RouteDrawer.Body>
|
||||
<RouteDrawer.Footer>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<RouteDrawer.Close asChild>
|
||||
<Button size="small" variant="secondary">
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</RouteDrawer.Close>
|
||||
<Button onClick={handleExportRequest} size="small">
|
||||
{t("actions.export")}
|
||||
</Button>
|
||||
</div>
|
||||
</RouteDrawer.Footer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
+14
-6
@@ -1,16 +1,24 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Outlet, useLocation } from "react-router-dom"
|
||||
|
||||
import { ConfigurableDataTable } from "../../../../../components/table/configurable-data-table"
|
||||
import { useOrderTableAdapter } from "./order-table-adapter"
|
||||
|
||||
export const ConfigurableOrderListTable = () => {
|
||||
const { t } = useTranslation()
|
||||
const orderAdapter = useOrderTableAdapter()
|
||||
const location = useLocation()
|
||||
const adapter = useOrderTableAdapter()
|
||||
|
||||
return (
|
||||
<ConfigurableDataTable
|
||||
adapter={orderAdapter}
|
||||
heading={t("orders.domain")}
|
||||
layout="fill"
|
||||
/>
|
||||
<>
|
||||
<ConfigurableDataTable
|
||||
adapter={adapter}
|
||||
heading={t("orders.domain")}
|
||||
actions={[
|
||||
{ label: t("actions.export"), to: `export${location.search}` },
|
||||
]}
|
||||
/>
|
||||
<Outlet />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,15 +1,16 @@
|
||||
import { Container, Heading } from "@medusajs/ui"
|
||||
import { Button, Container, Heading } from "@medusajs/ui"
|
||||
import { keepPreviousData } from "@tanstack/react-query"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link, Outlet, useLocation } from "react-router-dom"
|
||||
|
||||
import { _DataTable } from "../../../../../components/table/data-table/data-table"
|
||||
import { useOrders } from "../../../../../hooks/api/orders"
|
||||
import { useOrderTableColumns } from "../../../../../hooks/table/columns/use-order-table-columns"
|
||||
import { useOrderTableFilters } from "./use-order-table-filters"
|
||||
import { useOrderTableQuery } from "../../../../../hooks/table/query/use-order-table-query"
|
||||
import { useDataTable } from "../../../../../hooks/use-data-table"
|
||||
import { useFeatureFlag } from "../../../../../providers/feature-flag-provider"
|
||||
import { ConfigurableOrderListTable } from "./configurable-order-list-table"
|
||||
import { useOrderTableFilters } from "./use-order-table-filters"
|
||||
|
||||
import { DEFAULT_FIELDS } from "../../const"
|
||||
|
||||
@@ -17,6 +18,7 @@ const PAGE_SIZE = 20
|
||||
|
||||
export const OrderListTable = () => {
|
||||
const { t } = useTranslation()
|
||||
const location = useLocation()
|
||||
const isViewConfigEnabled = useFeatureFlag("view_configurations")
|
||||
|
||||
// If feature flag is enabled, use the new configurable table
|
||||
@@ -57,6 +59,9 @@ export const OrderListTable = () => {
|
||||
<Container className="divide-y p-0">
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Heading>{t("orders.domain")}</Heading>
|
||||
<Button size="small" variant="secondary" asChild>
|
||||
<Link to={`export${location.search}`}>{t("actions.export")}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<_DataTable
|
||||
columns={columns}
|
||||
@@ -78,6 +83,7 @@ export const OrderListTable = () => {
|
||||
message: t("orders.list.noRecordsMessage"),
|
||||
}}
|
||||
/>
|
||||
<Outlet />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user