From 0c0b425de7b154b80b712ab17b16215cf62d1e83 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Fri, 29 Mar 2024 12:22:42 +0100 Subject: [PATCH] feat(medusa-react,medusa,types,dashboard): added empty state + table for promotions list page (#6827) what: - adds empty state for promotions list page - lists all promotions with pagination Screenshot 2024-03-26 at 14 19 27 Screenshot 2024-03-27 at 20 46 17 --- .changeset/angry-fans-collect.md | 7 + .../admin-next/admin-shared/src/constants.ts | 5 + .../dashboard/public/locales/$schema.json | 26 ++++ .../public/locales/en-US/translation.json | 16 ++ .../layout-v2/main-layout/main-layout.tsx | 4 +- .../common/code-cell/code-cell.tsx | 26 ++++ .../table-cells/common/code-cell/index.ts | 1 + .../table-cells/common/text-cell/index.ts | 1 + .../common/text-cell/text-cell.tsx | 23 +++ .../promotion/status-cell/index.ts | 1 + .../promotion/status-cell/status-cell.tsx | 28 ++++ .../use-promotion-table-columns.tsx | 55 +++++++ .../use-promotion-table-filters.tsx | 13 ++ .../query-v2/use-promotion-table-query.tsx | 32 ++++ .../dashboard/src/lib/api-v2/index.ts | 1 + .../dashboard/src/lib/api-v2/promotion.ts | 23 +++ .../dashboard/src/lib/promotions.ts | 31 ++++ .../src/providers/router-provider/v2.tsx | 12 ++ .../components/promotion-list-table/index.ts | 1 + .../promotion-list-table.tsx | 147 ++++++++++++++++++ .../promotions/promotion-list/index.ts | 2 + .../promotions/promotion-list/loader.ts | 22 +++ .../promotion-list/promotions-list.tsx | 20 +++ packages/medusa-react/src/hooks/index.ts | 5 +- packages/medusa-react/src/index.ts | 3 +- packages/medusa/src/api-v2/admin/index.ts | 1 + .../src/api-v2/admin/promotions/index.ts | 2 + .../src/api-v2/admin/promotions/route.ts | 5 +- .../src/api-v2/admin/promotions/types.ts | 5 + .../src/api-v2/admin/promotions/validators.ts | 29 +++- packages/medusa/src/api-v2/index.ts | 1 + packages/medusa/src/index.js | 1 + packages/types/src/common/common.ts | 4 +- 33 files changed, 542 insertions(+), 11 deletions(-) create mode 100644 .changeset/angry-fans-collect.md create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/code-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/text-cell.tsx create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/index.ts create mode 100644 packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/status-cell.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/columns-v2/use-promotion-table-columns.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/filters-v2/use-promotion-table-filters.tsx create mode 100644 packages/admin-next/dashboard/src/hooks/table/query-v2/use-promotion-table-query.tsx create mode 100644 packages/admin-next/dashboard/src/lib/api-v2/promotion.ts create mode 100644 packages/admin-next/dashboard/src/lib/promotions.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/index.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx create mode 100644 packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/index.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/loader.ts create mode 100644 packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/promotions-list.tsx create mode 100644 packages/medusa/src/api-v2/admin/index.ts create mode 100644 packages/medusa/src/api-v2/admin/promotions/index.ts create mode 100644 packages/medusa/src/api-v2/admin/promotions/types.ts create mode 100644 packages/medusa/src/api-v2/index.ts diff --git a/.changeset/angry-fans-collect.md b/.changeset/angry-fans-collect.md new file mode 100644 index 0000000000..be0c220686 --- /dev/null +++ b/.changeset/angry-fans-collect.md @@ -0,0 +1,7 @@ +--- +"medusa-react": patch +"@medusajs/medusa": patch +"@medusajs/types": patch +--- + +feat(medusa-react,medusa,types,dashboard): added empty state for promotions list page diff --git a/packages/admin-next/admin-shared/src/constants.ts b/packages/admin-next/admin-shared/src/constants.ts index e934069d67..740f68551d 100644 --- a/packages/admin-next/admin-shared/src/constants.ts +++ b/packages/admin-next/admin-shared/src/constants.ts @@ -46,6 +46,11 @@ export const injectionZones = [ "discount.details.after", "discount.list.before", "discount.list.after", + // Promotion injection zones + "promotion.details.before", + "promotion.details.after", + "promotion.list.before", + "promotion.list.after", // Gift card injection zones "gift_card.details.before", "gift_card.details.after", diff --git a/packages/admin-next/dashboard/public/locales/$schema.json b/packages/admin-next/dashboard/public/locales/$schema.json index 1e210b1de2..d391cfc9bd 100644 --- a/packages/admin-next/dashboard/public/locales/$schema.json +++ b/packages/admin-next/dashboard/public/locales/$schema.json @@ -131,6 +131,15 @@ }, "required": ["domain"] }, + "promotions": { + "type": "object", + "properties": { + "domain": { + "type": "string" + } + }, + "required": ["domain"] + }, "giftCards": { "type": "object", "properties": { @@ -176,6 +185,23 @@ }, "required": ["domain", "role", "roles"] }, + "statuses": { + "type": "object", + "properties": { + "scheduled": { + "type": "string" + }, + "expired": { + "type": "string" + }, + "active": { + "type": "string" + }, + "disabled": { + "type": "string" + } + } + }, "fields": { "type": "object", "properties": { 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 f58e569832..457885be4e 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -502,6 +502,16 @@ "disabled": "Disabled" } }, + "promotions": { + "domain": "Promotions", + "fields": { + "method": "Method", + "campaign": "Campaign" + }, + "deleteWarning": "You are about to delete the promotion {{code}}. This action cannot be undone.", + "createPromotionTitle": "Create Promotion", + "type": "Promotion type" + }, "pricing": { "domain": "Pricing", "deletePriceListWarning": "You are about to delete the price list {{name}}. This action cannot be undone.", @@ -801,6 +811,12 @@ "serverError": "Server error - Try again later.", "invalidCredentials": "Wrong email or password" }, + "statuses": { + "scheduled": "Scheduled", + "expired": "Expired", + "active": "Active", + "disabled": "Disabled" + }, "fields": { "amount": "Amount", "name": "Name", diff --git a/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx b/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx index 40df2fbf46..9bf63f4eb1 100644 --- a/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx +++ b/packages/admin-next/dashboard/src/components/layout-v2/main-layout/main-layout.tsx @@ -135,8 +135,8 @@ const useCoreRoutes = (): Omit[] => { }, { icon: , - label: t("discounts.domain"), - to: "/discounts", + label: t("promotions.domain"), + to: "/promotions", }, { icon: , diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/code-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/code-cell.tsx new file mode 100644 index 0000000000..62ab371009 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/code-cell.tsx @@ -0,0 +1,26 @@ +type CellProps = { + code: string +} + +type HeaderProps = { + text: string +} + +export const CodeCell = ({ code }: CellProps) => { + return ( +
+ {/* // TODO: border color inversion*/} + + {code} + +
+ ) +} + +export const CodeHeader = ({ text }: HeaderProps) => { + return ( +
+ {text} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/index.ts new file mode 100644 index 0000000000..a2eb7174de --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/code-cell/index.ts @@ -0,0 +1 @@ +export * from "./code-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/index.ts new file mode 100644 index 0000000000..22f7bca960 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/index.ts @@ -0,0 +1 @@ +export * from "./text-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/text-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/text-cell.tsx new file mode 100644 index 0000000000..dba1cf5d68 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/common/text-cell/text-cell.tsx @@ -0,0 +1,23 @@ +type CellProps = { + text?: string | number +} + +type HeaderProps = { + text: string +} + +export const TextCell = ({ text }: CellProps) => { + return ( +
+ {text} +
+ ) +} + +export const TextHeader = ({ text }: HeaderProps) => { + return ( +
+ {text} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/index.ts b/packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/index.ts new file mode 100644 index 0000000000..8cc5d6026b --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/index.ts @@ -0,0 +1 @@ +export * from "./status-cell" diff --git a/packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/status-cell.tsx b/packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/status-cell.tsx new file mode 100644 index 0000000000..62b8d96b84 --- /dev/null +++ b/packages/admin-next/dashboard/src/components/table/table-cells/promotion/status-cell/status-cell.tsx @@ -0,0 +1,28 @@ +import { PromotionDTO } from "@medusajs/types" +import { useTranslation } from "react-i18next" +import { + getPromotionStatus, + PromotionStatus, +} from "../../../../../lib/promotions" +import { StatusCell as StatusCell_ } from "../../common/status-cell" + +type PromotionCellProps = { + promotion: PromotionDTO +} +type StatusColors = "grey" | "orange" | "green" | "red" +type StatusMap = Record + +export const StatusCell = ({ promotion }: PromotionCellProps) => { + const { t } = useTranslation() + + const statusMap: StatusMap = { + [PromotionStatus.DISABLED]: ["grey", t("statuses.disabled")], + [PromotionStatus.ACTIVE]: ["green", t("statuses.active")], + [PromotionStatus.SCHEDULED]: ["orange", t("statuses.scheduled")], + [PromotionStatus.EXPIRED]: ["red", t("statuses.expired")], + } + + const [color, text] = statusMap[getPromotionStatus(promotion)] + + return {text} +} diff --git a/packages/admin-next/dashboard/src/hooks/table/columns-v2/use-promotion-table-columns.tsx b/packages/admin-next/dashboard/src/hooks/table/columns-v2/use-promotion-table-columns.tsx new file mode 100644 index 0000000000..94cbef0390 --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/columns-v2/use-promotion-table-columns.tsx @@ -0,0 +1,55 @@ +import { PromotionDTO } from "@medusajs/types" +import { ColumnDef, createColumnHelper } from "@tanstack/react-table" +import { useMemo } from "react" +import { useTranslation } from "react-i18next" + +import { + CodeCell, + CodeHeader, +} from "../../../components/table/table-cells/common/code-cell" +import { + TextCell, + TextHeader, +} from "../../../components/table/table-cells/common/text-cell" +import { StatusCell } from "../../../components/table/table-cells/promotion/status-cell" + +const columnHelper = createColumnHelper() + +export const usePromotionTableColumns = () => { + const { t } = useTranslation() + + return useMemo( + () => [ + columnHelper.display({ + id: "code", + header: () => , + cell: ({ row }) => , + }), + + columnHelper.display({ + id: "campaign", + header: () => , + cell: ({ row }) => , + }), + + columnHelper.display({ + id: "method", + header: () => , + cell: ({ row }) => { + const text = row.original.is_automatic + ? "Automatic" + : "Promotion Code" + + return + }, + }), + + columnHelper.display({ + id: "status", + header: () => , + cell: ({ row }) => , + }), + ], + [] + ) as ColumnDef[] +} diff --git a/packages/admin-next/dashboard/src/hooks/table/filters-v2/use-promotion-table-filters.tsx b/packages/admin-next/dashboard/src/hooks/table/filters-v2/use-promotion-table-filters.tsx new file mode 100644 index 0000000000..246191a7cc --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/filters-v2/use-promotion-table-filters.tsx @@ -0,0 +1,13 @@ +import { useTranslation } from "react-i18next" +import { Filter } from "../../../components/table/data-table" + +export const usePromotionTableFilters = () => { + const { t } = useTranslation() + + let filters: Filter[] = [ + { label: t("fields.createdAt"), key: "created_at", type: "date" }, + { label: t("fields.updatedAt"), key: "updated_at", type: "date" }, + ] + + return filters +} diff --git a/packages/admin-next/dashboard/src/hooks/table/query-v2/use-promotion-table-query.tsx b/packages/admin-next/dashboard/src/hooks/table/query-v2/use-promotion-table-query.tsx new file mode 100644 index 0000000000..ae2b68b01f --- /dev/null +++ b/packages/admin-next/dashboard/src/hooks/table/query-v2/use-promotion-table-query.tsx @@ -0,0 +1,32 @@ +import { AdminGetPromotionsParams } from "@medusajs/medusa" +import { useQueryParams } from "../../use-query-params" + +type UsePromotionTableQueryProps = { + prefix?: string + pageSize?: number +} + +export const usePromotionTableQuery = ({ + prefix, + pageSize = 20, +}: UsePromotionTableQueryProps) => { + const queryObject = useQueryParams( + ["offset", "q", "created_at", "updated_at"], + prefix + ) + + const { offset, q, created_at, updated_at } = queryObject + + const searchParams: AdminGetPromotionsParams = { + limit: pageSize, + created_at: created_at ? JSON.parse(created_at) : undefined, + updated_at: updated_at ? JSON.parse(updated_at) : undefined, + offset: offset ? Number(offset) : 0, + q, + } + + return { + searchParams, + raw: queryObject, + } +} diff --git a/packages/admin-next/dashboard/src/lib/api-v2/index.ts b/packages/admin-next/dashboard/src/lib/api-v2/index.ts index 4b23d2e698..1bb9826de0 100644 --- a/packages/admin-next/dashboard/src/lib/api-v2/index.ts +++ b/packages/admin-next/dashboard/src/lib/api-v2/index.ts @@ -1,2 +1,3 @@ export * from "./auth" +export * from "./promotion" export * from "./store" diff --git a/packages/admin-next/dashboard/src/lib/api-v2/promotion.ts b/packages/admin-next/dashboard/src/lib/api-v2/promotion.ts new file mode 100644 index 0000000000..ff28055712 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/api-v2/promotion.ts @@ -0,0 +1,23 @@ +import { + AdminGetPromotionsParams, + AdminPromotionsListRes, +} from "@medusajs/medusa" +import { queryKeysFactory, useAdminCustomQuery } from "medusa-react" + +const QUERY_KEY = "admin_promotions" +export const adminPromotionKeys = queryKeysFactory< + typeof QUERY_KEY, + AdminGetPromotionsParams +>(QUERY_KEY) + +export const useV2Promotions = ( + query?: AdminGetPromotionsParams, + options?: object +) => { + const { data, ...rest } = useAdminCustomQuery< + AdminGetPromotionsParams, + AdminPromotionsListRes + >("/admin/promotions", adminPromotionKeys.list(query), query, options) + + return { ...data, ...rest } +} diff --git a/packages/admin-next/dashboard/src/lib/promotions.ts b/packages/admin-next/dashboard/src/lib/promotions.ts new file mode 100644 index 0000000000..0627e711d5 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/promotions.ts @@ -0,0 +1,31 @@ +import { PromotionDTO } from "@medusajs/types" + +export enum PromotionStatus { + SCHEDULED = "SCHEDULED", + EXPIRED = "EXPIRED", + ACTIVE = "ACTIVE", + DISABLED = "DISABLED", +} + +export const getPromotionStatus = (promotion: PromotionDTO) => { + const date = new Date() + const campaign = promotion.campaign + + if (!campaign) { + return PromotionStatus.ACTIVE + } + + if (new Date(campaign.starts_at!) > date) { + return PromotionStatus.SCHEDULED + } + + const campaignBudget = campaign.budget + const overBudget = + campaignBudget && campaignBudget.used! > campaignBudget.limit! + + if ((campaign.ends_at && new Date(campaign.ends_at) < date) || overBudget) { + return PromotionStatus.EXPIRED + } + + return PromotionStatus.ACTIVE +} diff --git a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx index 394bc5a733..2e8ecac565 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/v2.tsx @@ -77,6 +77,18 @@ export const v2Routes: RouteObject[] = [ }, ], }, + { + path: "/promotions", + handle: { + crumb: () => "Promotions", + }, + children: [ + { + path: "", + lazy: () => import("../../v2-routes/promotions/promotion-list"), + }, + ], + }, ], }, ], diff --git a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/index.ts b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/index.ts new file mode 100644 index 0000000000..aae0291b39 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/index.ts @@ -0,0 +1 @@ +export * from "./promotion-list-table" diff --git a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx new file mode 100644 index 0000000000..62d07a9210 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/components/promotion-list-table/promotion-list-table.tsx @@ -0,0 +1,147 @@ +import { PencilSquare, Trash } from "@medusajs/icons" +import { PromotionDTO } from "@medusajs/types" +import { Button, Container, Heading, usePrompt } from "@medusajs/ui" +import { createColumnHelper } from "@tanstack/react-table" +import { useAdminDeleteDiscount } 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 { usePromotionTableColumns } from "../../../../../hooks/table/columns-v2/use-promotion-table-columns" +import { usePromotionTableFilters } from "../../../../../hooks/table/filters-v2/use-promotion-table-filters" +import { usePromotionTableQuery } from "../../../../../hooks/table/query-v2/use-promotion-table-query" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { useV2Promotions } from "../../../../../lib/api-v2" +import { promotionsLoader } from "../../loader" + +const PAGE_SIZE = 20 + +export const PromotionListTable = () => { + const { t } = useTranslation() + const initialData = useLoaderData() as Awaited< + ReturnType> + > + + const { searchParams, raw } = usePromotionTableQuery({ pageSize: PAGE_SIZE }) + const { promotions, count, isLoading, isError, error } = useV2Promotions( + { ...searchParams }, + { + initialData, + keepPreviousData: true, + } + ) + + const filters = usePromotionTableFilters() + const columns = useColumns() + + const { table } = useDataTable({ + data: (promotions ?? []) as PromotionDTO[], + columns, + count, + enablePagination: true, + pageSize: PAGE_SIZE, + getRowId: (row) => row.id, + }) + + if (isError) { + throw error + } + + return ( + +
+ {t("promotions.domain")} + + +
+ + `${row.original.id}`} + orderBy={["created_at", "updated_at"]} + /> + +
+ ) +} + +const PromotionActions = ({ promotion }: { promotion: PromotionDTO }) => { + const { t } = useTranslation() + const prompt = usePrompt() + // TODO: change to promotions delete endpoint + const { mutateAsync } = useAdminDeleteDiscount(promotion.id) + + const handleDelete = async () => { + const res = await prompt({ + title: t("general.areYouSure"), + description: t("promotions.deleteWarning", { + code: promotion.code, + }), + confirmText: t("actions.delete"), + cancelText: t("actions.cancel"), + }) + + if (!res) { + return + } + + // TODO: handle error scenario here + await mutateAsync() + } + + return ( + , + label: t("actions.edit"), + to: `/promotions/${promotion.id}/edit`, + }, + ], + }, + { + actions: [ + { + icon: , + label: t("actions.delete"), + onClick: handleDelete, + }, + ], + }, + ]} + /> + ) +} + +const columnHelper = createColumnHelper() + +const useColumns = () => { + const base = usePromotionTableColumns() + + return useMemo( + () => [ + ...base, + columnHelper.display({ + id: "actions", + cell: ({ row }) => { + return + }, + }), + ], + [base] + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/index.ts b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/index.ts new file mode 100644 index 0000000000..2dec1dc272 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/index.ts @@ -0,0 +1,2 @@ +export { promotionsLoader } from "./loader" +export { PromotionsList as Component } from "./promotions-list.tsx" diff --git a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/loader.ts b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/loader.ts new file mode 100644 index 0000000000..f66e516581 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/loader.ts @@ -0,0 +1,22 @@ +import { AdminPromotionsListRes } from "@medusajs/medusa" +import { Response } from "@medusajs/medusa-js" +import { QueryClient } from "@tanstack/react-query" +import { adminPromotionKeys, useV2Promotions } from "../../../lib/api-v2" +import { queryClient } from "../../../lib/medusa" + +const promotionsListQuery = () => ({ + queryKey: adminPromotionKeys.list(), + queryFn: async () => useV2Promotions({ limit: 20, offset: 0 }), +}) + +export const promotionsLoader = (client: QueryClient) => { + return async () => { + const query = promotionsListQuery() + + return ( + queryClient.getQueryData>( + query.queryKey + ) ?? (await client.fetchQuery(query)) + ) + } +} diff --git a/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/promotions-list.tsx b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/promotions-list.tsx new file mode 100644 index 0000000000..3ac5e93ffd --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/promotions/promotion-list/promotions-list.tsx @@ -0,0 +1,20 @@ +import after from "medusa-admin:widgets/promotion/list/after" +import before from "medusa-admin:widgets/promotion/list/before" + +import { PromotionListTable } from "./components/promotion-list-table" + +export const PromotionsList = () => { + return ( +
+ {before.widgets.map((w, i) => ( + + ))} + + + + {after.widgets.map((w, i) => ( + + ))} +
+ ) +} diff --git a/packages/medusa-react/src/hooks/index.ts b/packages/medusa-react/src/hooks/index.ts index b9e637c3d9..2fc1cf4bb5 100644 --- a/packages/medusa-react/src/hooks/index.ts +++ b/packages/medusa-react/src/hooks/index.ts @@ -1,2 +1,3 @@ -export * from "./store/" -export * from "./admin/" +export * from "./admin" +export * from "./store" +export * from "./utils" diff --git a/packages/medusa-react/src/index.ts b/packages/medusa-react/src/index.ts index f0a8990e49..3745e361d9 100644 --- a/packages/medusa-react/src/index.ts +++ b/packages/medusa-react/src/index.ts @@ -1,3 +1,4 @@ export * from "./contexts" -export * from "./hooks/" export * from "./helpers" +export * from "./hooks" +export * from "./types" diff --git a/packages/medusa/src/api-v2/admin/index.ts b/packages/medusa/src/api-v2/admin/index.ts new file mode 100644 index 0000000000..b579f7910f --- /dev/null +++ b/packages/medusa/src/api-v2/admin/index.ts @@ -0,0 +1 @@ +export * from "./promotions" diff --git a/packages/medusa/src/api-v2/admin/promotions/index.ts b/packages/medusa/src/api-v2/admin/promotions/index.ts new file mode 100644 index 0000000000..1bb71ae474 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/promotions/index.ts @@ -0,0 +1,2 @@ +export * from "./types" +export * from "./validators" diff --git a/packages/medusa/src/api-v2/admin/promotions/route.ts b/packages/medusa/src/api-v2/admin/promotions/route.ts index 701d553f85..8fb9335f5d 100644 --- a/packages/medusa/src/api-v2/admin/promotions/route.ts +++ b/packages/medusa/src/api-v2/admin/promotions/route.ts @@ -1,12 +1,11 @@ +import { createPromotionsWorkflow } from "@medusajs/core-flows" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { CreatePromotionDTO, IPromotionModuleService } from "@medusajs/types" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../types/routing" -import { createPromotionsWorkflow } from "@medusajs/core-flows" -import { ModuleRegistrationName } from "@medusajs/modules-sdk" - export const GET = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse diff --git a/packages/medusa/src/api-v2/admin/promotions/types.ts b/packages/medusa/src/api-v2/admin/promotions/types.ts new file mode 100644 index 0000000000..df356c69b9 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/promotions/types.ts @@ -0,0 +1,5 @@ +import { PaginatedResponse, PromotionDTO } from "@medusajs/types" + +export type AdminPromotionsListRes = PaginatedResponse<{ + promotions: PromotionDTO[] +}> diff --git a/packages/medusa/src/api-v2/admin/promotions/validators.ts b/packages/medusa/src/api-v2/admin/promotions/validators.ts index dd5a134889..f41d40b3a5 100644 --- a/packages/medusa/src/api-v2/admin/promotions/validators.ts +++ b/packages/medusa/src/api-v2/admin/promotions/validators.ts @@ -20,7 +20,11 @@ import { ValidateIf, ValidateNested, } from "class-validator" -import { FindParams, extendedFindParamsMixin } from "../../../types/common" +import { + DateComparisonOperator, + FindParams, + extendedFindParamsMixin, +} from "../../../types/common" import { XorConstraint } from "../../../types/validators/xor" import { AdminPostCampaignsReq } from "../campaigns/validators" @@ -33,6 +37,29 @@ export class AdminGetPromotionsParams extends extendedFindParamsMixin({ @IsString() @IsOptional() code?: string + + /** + * Search terms to search promotions' code fields. + */ + @IsString() + @IsOptional() + q?: string + + /** + * Date filters to apply on the promotions' `created_at` date. + */ + @IsOptional() + @ValidateNested() + @Type(() => DateComparisonOperator) + created_at?: DateComparisonOperator + + /** + * Date filters to apply on the promotions' `updated_at` date. + */ + @IsOptional() + @ValidateNested() + @Type(() => DateComparisonOperator) + updated_at?: DateComparisonOperator } export class AdminPostPromotionsReq { diff --git a/packages/medusa/src/api-v2/index.ts b/packages/medusa/src/api-v2/index.ts new file mode 100644 index 0000000000..26b8eb9dad --- /dev/null +++ b/packages/medusa/src/api-v2/index.ts @@ -0,0 +1 @@ +export * from "./admin" diff --git a/packages/medusa/src/index.js b/packages/medusa/src/index.js index 84748367af..4b8f350e7f 100644 --- a/packages/medusa/src/index.js +++ b/packages/medusa/src/index.js @@ -1,4 +1,5 @@ export * from "./api" +export * from "./api-v2" export * from "./api/middlewares" export * from "./interfaces" export * from "./joiner-config" diff --git a/packages/types/src/common/common.ts b/packages/types/src/common/common.ts index d77d5d8ac4..e6a14b71ae 100644 --- a/packages/types/src/common/common.ts +++ b/packages/types/src/common/common.ts @@ -230,7 +230,7 @@ export type RequestQueryFields = { * * Fields included in the response if it's paginated. */ -export type PaginatedResponse = { +export type PaginatedResponse = { /** * The limit applied on the retrieved items. */ @@ -245,7 +245,7 @@ export type PaginatedResponse = { * The total count of items. */ count: number -} +} & T /** * The fields returned in the response of a DELETE request.