From 608c10383a1a86068f0648d5aa641cbf11ffd323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Tue, 27 Feb 2024 12:33:19 +0100 Subject: [PATCH] feat(admin-next) discounts list page (#6490) --- .changeset/polite-papayas-exercise.md | 5 + .../public/locales/en-US/translation.json | 15 +- .../discount/code-cell/code-cell.tsx | 28 ++++ .../table-cells/discount/code-cell/index.ts | 1 + .../description-cell/description-cell.tsx | 25 +++ .../discount/description-cell/index.ts | 1 + .../discount/redemption-cell/index.ts | 1 + .../redemption-cell/redemption-cell.tsx | 23 +++ .../table-cells/discount/status-cell/index.ts | 1 + .../discount/status-cell/status-cell.tsx | 72 +++++++++ .../table-cells/discount/value-cell/index.ts | 1 + .../discount/value-cell/value-cell.tsx | 35 +++++ .../columns/use-discount-table-columns.tsx | 65 ++++++++ .../filters/use-discount-table-filters.tsx | 51 ++++++ .../table/query/use-discount-table-query.tsx | 45 ++++++ .../discount-list-table.tsx | 145 ++++++++++++++++++ .../components/discount-list-table/index.ts | 1 + .../src/routes/discounts/list/index.ts | 3 +- .../src/routes/discounts/list/list.tsx | 21 ++- .../src/routes/discounts/list/loader.ts | 24 +++ .../src/lib/models/AdminGetDiscountsParams.ts | 46 ++++++ .../routes/admin/discounts/list-discounts.ts | 73 ++++++++- packages/medusa/src/types/discount.ts | 11 ++ 23 files changed, 683 insertions(+), 10 deletions(-) create mode 100644 .changeset/polite-papayas-exercise.md create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/code-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/description-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/redemption-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/status-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/value-cell.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/columns/use-discount-table-columns.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/filters/use-discount-table-filters.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/query/use-discount-table-query.tsx create mode 100644 packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/discount-list-table.tsx create mode 100644 packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/index.ts create mode 100644 packages/admin-next/dashboard/src/routes/discounts/list/loader.ts diff --git a/.changeset/polite-papayas-exercise.md b/.changeset/polite-papayas-exercise.md new file mode 100644 index 0000000000..571fc9ff48 --- /dev/null +++ b/.changeset/polite-papayas-exercise.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +fix(medusa): add `order` param to discounts list diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index 02892e6db7..f7875cbde2 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -187,7 +187,14 @@ "domain": "Draft Orders" }, "discounts": { - "domain": "Discounts" + "domain": "Discounts", + "deleteWarning": "You are about to delete the discount {{title}}. This action cannot be undone.", + "discountStatus": { + "scheduled": "Scheduled", + "expired": "Expired", + "active": "Active", + "disabled": "Disabled" + } }, "pricing": { "domain": "Pricing" @@ -355,6 +362,11 @@ "sales_channels": "Sales Channels", "status": "Status", "code": "Code", + "value": "Value", + "disabled": "Disabled", + "dynamic": "Dynamic", + "normal": "Normal", + "totalRedemptions": "Total Redemptions", "countries": "Countries", "paymentProviders": "Payment Providers", "fulfillmentProviders": "Fulfillment Providers", @@ -392,6 +404,7 @@ "items": "Items", "salesChannel": "Sales Channel", "region": "Region", + "discount": "Discount", "role": "Role", "sent": "Sent", "salesChannels": "Sales Channels", diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/code-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/code-cell.tsx new file mode 100644 index 0000000000..b6d0cf4994 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/code-cell.tsx @@ -0,0 +1,28 @@ +import type { Discount } from "@medusajs/medusa" + +import { useTranslation } from "react-i18next" + +type DiscountCellProps = { + discount: Discount +} + +export const CodeCell = ({ discount }: DiscountCellProps) => { + return ( +
+ {/*// TODO: border color inversion*/} + + {discount.code} + +
+ ) +} + +export const CodeHeader = () => { + const { t } = useTranslation() + + return ( +
+ {t("fields.description")} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/index.ts new file mode 100644 index 0000000000..a2eb7174de --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/code-cell/index.ts @@ -0,0 +1 @@ +export * from "./code-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/description-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/description-cell.tsx new file mode 100644 index 0000000000..b625626782 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/description-cell.tsx @@ -0,0 +1,25 @@ +import type { Discount } from "@medusajs/medusa" + +import { useTranslation } from "react-i18next" + +type DiscountCellProps = { + discount: Discount +} + +export const DescriptionCell = ({ discount }: DiscountCellProps) => { + return ( +
+ {discount.rule.description} +
+ ) +} + +export const DescriptionHeader = () => { + const { t } = useTranslation() + + return ( +
+ {t("fields.description")} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/index.ts new file mode 100644 index 0000000000..83760e2da6 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/description-cell/index.ts @@ -0,0 +1 @@ +export * from "./description-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/index.ts new file mode 100644 index 0000000000..faac33e975 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/index.ts @@ -0,0 +1 @@ +export * from "./redemption-cell.tsx" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/redemption-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/redemption-cell.tsx new file mode 100644 index 0000000000..00c76fd738 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/redemption-cell/redemption-cell.tsx @@ -0,0 +1,23 @@ +import { useTranslation } from "react-i18next" + +type DiscountCellProps = { + redemptions: number +} + +export const RedemptionCell = ({ redemptions }: DiscountCellProps) => { + return ( +
+ {redemptions} +
+ ) +} + +export const RedemptionHeader = () => { + const { t } = useTranslation() + + return ( +
+ {t("fields.totalRedemptions")} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/index.ts new file mode 100644 index 0000000000..8cc5d6026b --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/index.ts @@ -0,0 +1 @@ +export * from "./status-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/status-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/status-cell.tsx new file mode 100644 index 0000000000..61a0362090 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/status-cell/status-cell.tsx @@ -0,0 +1,72 @@ +import { Discount } from "@medusajs/medusa" +import { end, parse } from "iso8601-duration" + +import { StatusCell as StatusCell_ } from "../../common/status-cell" + +import { useTranslation } from "react-i18next" + +type DiscountCellProps = { + discount: Discount +} + +enum PromotionStatus { + SCHEDULED = "SCHEDULED", + EXPIRED = "EXPIRED", + ACTIVE = "ACTIVE", + DISABLED = "DISABLED", +} + +const getDiscountStatus = (discount: Discount) => { + if (discount.is_disabled) { + return PromotionStatus.DISABLED + } + + const date = new Date() + if (new Date(discount.starts_at) > date) { + return PromotionStatus.SCHEDULED + } + + if ( + (discount.ends_at && new Date(discount.ends_at) < date) || + (discount.valid_duration && + date > + end(parse(discount.valid_duration), new Date(discount.starts_at))) || + discount.usage_count === discount.usage_limit + ) { + return PromotionStatus.EXPIRED + } + + return PromotionStatus.ACTIVE +} + +export const StatusCell = ({ discount }: DiscountCellProps) => { + const { t } = useTranslation() + + const [color, text] = { + [PromotionStatus.DISABLED]: [ + "grey", + t("discounts.discountStatus.disabled"), + ], + [PromotionStatus.ACTIVE]: ["green", t("discounts.discountStatus.active")], + [PromotionStatus.SCHEDULED]: [ + "orange", + t("discounts.discountStatus.scheduled"), + ], + [PromotionStatus.EXPIRED]: ["red", t("discounts.discountStatus.expired")], + }[getDiscountStatus(discount)] as [ + "grey" | "orange" | "green" | "red", + string + ] + + return {text} +} + +export const StatusHeader = () => { + const { t } = useTranslation() + + return ( +
+ {t("fields.status")} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/index.ts new file mode 100644 index 0000000000..91c37e81b2 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/index.ts @@ -0,0 +1 @@ +export * from "./value-cell.tsx" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/value-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/value-cell.tsx new file mode 100644 index 0000000000..6ce4b0c86e --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/discount/value-cell/value-cell.tsx @@ -0,0 +1,35 @@ +import { DiscountRule } from "@medusajs/medusa" + +import { useTranslation } from "react-i18next" +import { MoneyAmountCell } from "../../common/money-amount-cell" + +type DiscountCellProps = { + rule: DiscountRule + currencyCode: string +} + +export const ValueCell = ({ currencyCode, rule }: DiscountCellProps) => { + const isFixed = rule.type === "fixed" + const isPercentage = rule.type === "percentage" + const isFreeShipping = rule.type === "free_shipping" + + return ( +
+ {isFreeShipping && Free shipping} + {isPercentage && {rule.value}%} + {isFixed && ( + + )} +
+ ) +} + +export const ValueHeader = () => { + const { t } = useTranslation() + + return ( +
+ {t("fields.value")} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/hooks/table/columns/use-discount-table-columns.tsx b/packages/admin-next/dashboard/src/hooks/table/columns/use-discount-table-columns.tsx new file mode 100644 index 0000000000..3c2ed34960 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/columns/use-discount-table-columns.tsx @@ -0,0 +1,65 @@ +import { useMemo } from "react" +import { ColumnDef, createColumnHelper } from "@tanstack/react-table" + +import type { Discount } from "@medusajs/medusa" + +import { + CodeCell, + CodeHeader, +} from "../../../components/table/table-cells/discount/code-cell" +import { + DescriptionHeader, + DescriptionCell, +} from "../../../components/table/table-cells/discount/description-cell" +import { + ValueCell, + ValueHeader, +} from "../../../components/table/table-cells/discount/value-cell" +import { + RedemptionCell, + RedemptionHeader, +} from "../../../components/table/table-cells/discount/redemption-cell" +import { + StatusHeader, + StatusCell, +} from "../../../components/table/table-cells/discount/status-cell" + +const columnHelper = createColumnHelper() + +export const useDiscountTableColumns = () => { + return useMemo( + () => [ + columnHelper.display({ + id: "discount", + header: () => , + cell: ({ row }) => , + }), + columnHelper.accessor("rule.description", { + header: () => , + cell: ({ row }) => , + }), + + columnHelper.accessor("rule.value", { + header: () => , + cell: ({ row }) => ( + + ), + }), + columnHelper.display({ + id: "status", + header: () => , + cell: ({ row }) => , + }), + columnHelper.accessor("usage_count", { + header: () => , + cell: ({ row }) => ( + + ), + }), + ], + [] + ) as ColumnDef[] +} diff --git a/packages/admin-next/dashboard/src/hooks/table/filters/use-discount-table-filters.tsx b/packages/admin-next/dashboard/src/hooks/table/filters/use-discount-table-filters.tsx new file mode 100644 index 0000000000..8158b0a3d8 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/filters/use-discount-table-filters.tsx @@ -0,0 +1,51 @@ +import { useTranslation } from "react-i18next" +import { Filter } from "../../../components/table/data-table" + +export const useDiscountTableFilters = () => { + const { t } = useTranslation() + + let filters: 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", + })) + + const isDisabledFilter: Filter = { + key: "is_disabled", + label: t("fields.disabled"), + type: "select", + options: [ + { + label: t("fields.true"), + value: "true", + }, + { + label: t("fields.false"), + value: "false", + }, + ], + } + + const isDynamicFilter: Filter = { + key: "is_dynamic", + label: t("fields.type"), + type: "select", + options: [ + { + label: t("fields.dynamic"), + value: "true", + }, + { + label: t("fields.normal"), + value: "false", + }, + ], + } + + filters = [...filters, isDisabledFilter, isDynamicFilter] + + return filters +} diff --git a/packages/admin-next/dashboard/src/hooks/table/query/use-discount-table-query.tsx b/packages/admin-next/dashboard/src/hooks/table/query/use-discount-table-query.tsx new file mode 100644 index 0000000000..a3f9f0e6fb --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/query/use-discount-table-query.tsx @@ -0,0 +1,45 @@ +import { AdminGetDiscountsParams } from "@medusajs/medusa" + +import { useQueryParams } from "../../use-query-params" + +type UseDiscountTableQueryProps = { + prefix?: string + pageSize?: number +} + +export const useDiscountTableQuery = ({ + prefix, + pageSize = 20, +}: UseDiscountTableQueryProps) => { + const queryObject = useQueryParams( + [ + "offset", + "order", + "q", + "is_dynamic", + "is_disabled", + "created_at", + "updated_at", + ], + prefix + ) + + const { offset, order, q, is_dynamic, is_disabled, created_at, updated_at } = + queryObject + + const searchParams: AdminGetDiscountsParams = { + limit: pageSize, + is_disabled: is_disabled ? is_disabled === "true" : undefined, + is_dynamic: is_dynamic ? is_dynamic === "true" : undefined, + created_at: created_at ? JSON.parse(created_at) : undefined, + updated_at: updated_at ? JSON.parse(updated_at) : undefined, + offset: offset ? Number(offset) : 0, + order, + q, + } + + return { + searchParams, + raw: queryObject, + } +} diff --git a/packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/discount-list-table.tsx b/packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/discount-list-table.tsx new file mode 100644 index 0000000000..71cd438564 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/discount-list-table.tsx @@ -0,0 +1,145 @@ +import { PencilSquare, Trash } from "@medusajs/icons" +import type { Discount } from "@medusajs/medusa" +import { Button, Container, Heading, usePrompt } from "@medusajs/ui" +import { createColumnHelper } from "@tanstack/react-table" +import { useAdminDeleteDiscount, useAdminDiscounts } from "medusa-react" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" +import { Link, Outlet, useLoaderData } from "react-router-dom" + +import { ActionMenu } from "../../../../../components/common/action-menu" +import { DataTable } from "../../../../../components/table/data-table" +import { useDiscountTableFilters } from "../../../../../hooks/table/filters/use-discount-table-filters" +import { useDiscountTableColumns } from "../../../../../hooks/table/columns/use-discount-table-columns" +import { useDiscountTableQuery } from "../../../../../hooks/table/query/use-discount-table-query" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { discountsLoader } from "../../loader" + +const PAGE_SIZE = 20 + +export const DiscountListTable = () => { + const { t } = useTranslation() + + const initialData = useLoaderData() as Awaited< + ReturnType> + > + + const { searchParams, raw } = useDiscountTableQuery({ pageSize: PAGE_SIZE }) + const { discounts, count, isLoading, isError, error } = useAdminDiscounts( + { + ...searchParams, + }, + { + initialData, + keepPreviousData: true, + } + ) + + const filters = useDiscountTableFilters() + const columns = useColumns() + + const { table } = useDataTable({ + data: (discounts ?? []) as Discount[], + columns, + count, + enablePagination: true, + pageSize: PAGE_SIZE, + getRowId: (row) => row.id, + }) + + if (isError) { + throw error + } + + return ( + +
+ {t("discounts.domain")} + +
+ `${row.original.id}`} + orderBy={["code", "created_at", "updated_at"]} + /> + +
+ ) +} + +const DiscountActions = ({ discount }: { discount: Discount }) => { + const { t } = useTranslation() + const prompt = usePrompt() + const { mutateAsync } = useAdminDeleteDiscount(discount.id) + + const handleDelete = async () => { + const res = await prompt({ + title: t("general.areYouSure"), + description: t("discounts.deleteWarning", { + title: discount.code, + }), + confirmText: t("actions.delete"), + cancelText: t("actions.cancel"), + }) + + if (!res) { + return + } + + await mutateAsync() + } + + return ( + , + label: t("actions.edit"), + to: `/discounts/${discount.id}/edit`, + }, + ], + }, + { + actions: [ + { + icon: , + label: t("actions.delete"), + onClick: handleDelete, + }, + ], + }, + ]} + /> + ) +} + +const columnHelper = createColumnHelper() + +const useColumns = () => { + const base = useDiscountTableColumns() + + return useMemo( + () => [ + ...base, + columnHelper.display({ + id: "actions", + cell: ({ row }) => { + return + }, + }), + ], + [base] + ) +} diff --git a/packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/index.ts b/packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/index.ts new file mode 100644 index 0000000000..caa2b8e5d3 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/discounts/list/components/discount-list-table/index.ts @@ -0,0 +1 @@ +export * from "./discount-list-table.tsx" diff --git a/packages/admin-next/dashboard/src/routes/discounts/list/index.ts b/packages/admin-next/dashboard/src/routes/discounts/list/index.ts index 9ede617802..4a47518db9 100644 --- a/packages/admin-next/dashboard/src/routes/discounts/list/index.ts +++ b/packages/admin-next/dashboard/src/routes/discounts/list/index.ts @@ -1 +1,2 @@ -export { DiscountsList as Component } from "./list"; +export { discountsLoader } from "./loader" +export { DiscountsList as Component } from "./list" diff --git a/packages/admin-next/dashboard/src/routes/discounts/list/list.tsx b/packages/admin-next/dashboard/src/routes/discounts/list/list.tsx index 5be301f0c6..d543729208 100644 --- a/packages/admin-next/dashboard/src/routes/discounts/list/list.tsx +++ b/packages/admin-next/dashboard/src/routes/discounts/list/list.tsx @@ -1,11 +1,18 @@ -import { Container, Heading } from "@medusajs/ui"; +import after from "medusa-admin:widgets/discount/list/after" +import before from "medusa-admin:widgets/discount/list/before" + +import { DiscountListTable } from "./components/discount-list-table" export const DiscountsList = () => { return ( -
- - Discounts - +
+ {before.widgets.map((w, i) => ( + + ))} + + {after.widgets.map((w, i) => ( + + ))}
- ); -}; + ) +} diff --git a/packages/admin-next/dashboard/src/routes/discounts/list/loader.ts b/packages/admin-next/dashboard/src/routes/discounts/list/loader.ts new file mode 100644 index 0000000000..026655de47 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/discounts/list/loader.ts @@ -0,0 +1,24 @@ +import { QueryClient } from "@tanstack/react-query" + +import { Response } from "@medusajs/medusa-js" +import { AdminDiscountsListRes } from "@medusajs/medusa" +import { adminDiscountKeys } from "medusa-react" + +import { medusa, queryClient } from "../../../lib/medusa" + +const discountsListQuery = () => ({ + queryKey: adminDiscountKeys.list({ limit: 20, offset: 0 }), + queryFn: async () => medusa.admin.discounts.list({ limit: 20, offset: 0 }), +}) + +export const discountsLoader = (client: QueryClient) => { + return async () => { + const query = discountsListQuery() + + return ( + queryClient.getQueryData>( + query.queryKey + ) ?? (await client.fetchQuery(query)) + ) + } +} diff --git a/packages/generated/client-types/src/lib/models/AdminGetDiscountsParams.ts b/packages/generated/client-types/src/lib/models/AdminGetDiscountsParams.ts index baf5329143..428a128704 100644 --- a/packages/generated/client-types/src/lib/models/AdminGetDiscountsParams.ts +++ b/packages/generated/client-types/src/lib/models/AdminGetDiscountsParams.ts @@ -41,4 +41,50 @@ export interface AdminGetDiscountsParams { * Comma-separated relations that should be expanded in each returned discount. */ expand?: string + /** + * A discount field to sort-order the retrieved discounts by. + */ + order?: string + /** + * Filter by a creation date range. + */ + created_at?: { + /** + * filter by dates less than this date + */ + lt?: string + /** + * filter by dates greater than this date + */ + gt?: string + /** + * filter by dates less than or equal to this date + */ + lte?: string + /** + * filter by dates greater than or equal to this date + */ + gte?: string + } + /** + * Filter by an update date range. + */ + updated_at?: { + /** + * filter by dates less than this date + */ + lt?: string + /** + * filter by dates greater than this date + */ + gt?: string + /** + * filter by dates less than or equal to this date + */ + lte?: string + /** + * filter by dates greater than or equal to this date + */ + gte?: string + } } diff --git a/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts b/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts index bce231e7c2..1a786920dd 100644 --- a/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts +++ b/packages/medusa/src/api/routes/admin/discounts/list-discounts.ts @@ -7,7 +7,10 @@ import { import { Transform, Type } from "class-transformer" import { AdminGetDiscountsDiscountRuleParams } from "../../../../types/discount" -import { extendedFindParamsMixin } from "../../../../types/common" +import { + DateComparisonOperator, + extendedFindParamsMixin, +} from "../../../../types/common" import { Request, Response } from "express" import { DiscountService } from "../../../../services" import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean" @@ -39,6 +42,51 @@ import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean" * - (query) limit=20 {number} The number of discounts to return * - (query) offset=0 {number} The number of discounts to skip when retrieving the discounts. * - (query) expand {string} Comma-separated relations that should be expanded in each returned discount. + * - (query) order {string} A discount field to sort-order the retrieved discounts by. + * - in: query + * name: created_at + * description: Filter by a creation date range. + * schema: + * type: object + * properties: + * lt: + * type: string + * description: filter by dates less than this date + * format: date + * gt: + * type: string + * description: filter by dates greater than this date + * format: date + * lte: + * type: string + * description: filter by dates less than or equal to this date + * format: date + * gte: + * type: string + * description: filter by dates greater than or equal to this date + * format: date + * - in: query + * name: updated_at + * description: Filter by an update date range. + * schema: + * type: object + * properties: + * lt: + * type: string + * description: filter by dates less than this date + * format: date + * gt: + * type: string + * description: filter by dates greater than this date + * format: date + * lte: + * type: string + * description: filter by dates less than or equal to this date + * format: date + * gte: + * type: string + * description: filter by dates greater than or equal to this date + * format: date * x-codegen: * method: list * queryParams: AdminGetDiscountsParams @@ -167,4 +215,27 @@ export class AdminGetDiscountsParams extends extendedFindParamsMixin({ @IsOptional() @Transform(({ value }) => optionalBooleanMapper.get(value)) is_disabled?: boolean + + /** + * Date filters to apply on the discounts' `created_at` date. + */ + @IsOptional() + @ValidateNested() + @Type(() => DateComparisonOperator) + created_at?: DateComparisonOperator + + /** + * Date filters to apply on the discounts' `updated_at` date. + */ + @IsOptional() + @ValidateNested() + @Type(() => DateComparisonOperator) + updated_at?: DateComparisonOperator + + /** + * The field to sort the data by. By default, the sort order is ascending. To change the order to descending, prefix the field name with `-`. + */ + @IsString() + @IsOptional() + order?: string } diff --git a/packages/medusa/src/types/discount.ts b/packages/medusa/src/types/discount.ts index a22f1bd89c..d54a7d07b4 100644 --- a/packages/medusa/src/types/discount.ts +++ b/packages/medusa/src/types/discount.ts @@ -18,6 +18,7 @@ import { import { optionalBooleanMapper } from "../utils/validators/is-boolean" import { IsType } from "../utils/validators/is-type" import { ExactlyOne } from "./validators/exactly-one" +import { DateComparisonOperator } from "./common" export type QuerySelector = { q?: string @@ -46,6 +47,16 @@ export class FilterableDiscountProps { @IsOptional() @Type(() => AdminGetDiscountsDiscountRuleParams) rule?: AdminGetDiscountsDiscountRuleParams + + @IsOptional() + @ValidateNested() + @Type(() => DateComparisonOperator) + created_at?: DateComparisonOperator + + @IsOptional() + @ValidateNested() + @Type(() => DateComparisonOperator) + updated_at?: DateComparisonOperator } /**