import { Alert, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { Badge } from "@workspace/ui/components/badge"; import { Button } from "@workspace/ui/components/button"; import { Switch } from "@workspace/ui/components/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@workspace/ui/components/tabs"; import { ConfirmButton } from "@workspace/ui/composed/confirm-button"; import Empty from "@workspace/ui/composed/empty"; import { ProTable, type ProTableActions, } from "@workspace/ui/composed/pro-table/pro-table"; import { createPromoPrice, createPromoRule, deletePromoPrice, deletePromoRule, getPromoPriceList, getPromoRuleList, getPromoUsageList, updatePromoRule, } from "@workspace/ui/services/admin/promo"; import { CircleAlert } from "lucide-react"; import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { useSubscribe } from "@/stores/subscribe"; import { formatDate } from "@/utils/common"; import PriceForm from "./price-form"; import RuleDetail from "./rule-detail"; import RuleForm from "./rule-form"; import { formatCurrency, parseOptionalNumber, promoRuleTypes } from "./utils"; type RuleFilters = { search?: string; type?: API.PromoRuleType; enabled?: string; }; type PriceFilters = { promo_rule_id?: string; subscribe_id?: string; }; type UsageFilters = PriceFilters & { user_id?: string; order_no?: string; }; function ErrorState({ title, description, }: { title: string; description: string; }) { return ( ); } export default function PromoPage() { const { t } = useTranslation("promo"); const { subscribes, getSubscribeName } = useSubscribe(); const [loading, setLoading] = useState(false); const [rules, setRules] = useState([]); const [detailRule, setDetailRule] = useState(); const [detailOpen, setDetailOpen] = useState(false); const ruleRef = useRef(null); const priceRef = useRef(null); const usageRef = useRef(null); return ( <> {t("rules", "Rules")} {t("prices", "Prices")} {t("usage", "Usage")} action={ruleRef} actions={{ render: (row) => [ , { setLoading(true); try { await updatePromoRule({ id: row.id }, values); toast.success(t("updateSuccess", "Update Success")); ruleRef.current?.refresh(); setLoading(false); return true; } catch (_error) { setLoading(false); return false; } }} title={t("editRule", "Edit Rule")} trigger={t("edit", "Edit")} />, { await deletePromoRule({ id: row.id }); toast.success(t("deleteSuccess", "Delete Success")); ruleRef.current?.refresh(); }} title={t("confirmDelete", "Are you sure you want to delete?")} trigger={ } />, ], }} columns={[ { accessorKey: "name", header: t("name", "Name"), }, { accessorKey: "type", header: t("type", "Type"), cell: ({ row }) => ( {t(`types.${row.original.type}`, row.original.type)} ), }, { accessorKey: "priority", header: t("priority", "Priority"), }, { accessorKey: "enabled", header: t("enabled", "Enabled"), cell: ({ row }) => ( { await updatePromoRule( { id: row.original.id }, { ...row.original, enabled: checked } ); toast.success(t("updateSuccess", "Update Success")); ruleRef.current?.refresh(); }} /> ), }, { accessorKey: "start_time", header: t("startTime", "Start Time"), cell: ({ row }) => row.original.start_time ? formatDate(row.original.start_time) : "--", }, { accessorKey: "end_time", header: t("endTime", "End Time"), cell: ({ row }) => row.original.end_time ? formatDate(row.original.end_time) : "--", }, ]} empty={} error={ } header={{ toolbar: ( { setLoading(true); try { await createPromoRule(values); toast.success(t("createSuccess", "Create Success")); ruleRef.current?.refresh(); setLoading(false); return true; } catch (_error) { setLoading(false); return false; } }} title={t("createRule", "Create Rule")} trigger={t("createRule", "Create Rule")} /> ), }} params={[ { key: "search", placeholder: t("searchRule", "Rule name") }, { key: "type", placeholder: t("type", "Type"), options: promoRuleTypes.map((type) => ({ label: t(`types.${type}`, type), value: type, })), }, { key: "enabled", placeholder: t("enabled", "Enabled"), options: [ { label: t("yes", "Yes"), value: "true" }, { label: t("no", "No"), value: "false" }, ], }, ]} request={async (pagination, filters) => { const { data } = await getPromoRuleList({ ...pagination, search: filters.search?.trim() || undefined, type: filters.type, enabled: filters.enabled === undefined || filters.enabled === "" ? undefined : filters.enabled === "true", }); const list = data.data?.list || []; setRules(list); return { list, total: data.data?.total || 0, }; }} /> action={priceRef} actions={{ render: (row) => [ { await deletePromoPrice({ id: row.id }); toast.success(t("deleteSuccess", "Delete Success")); priceRef.current?.refresh(); }} title={t("confirmDelete", "Are you sure you want to delete?")} trigger={ } />, ], }} columns={[ { accessorKey: "promo_rule_id", header: t("rule", "Rule"), cell: ({ row }) => row.original.promo_rule_id ? rules.find( (rule) => rule.id === row.original.promo_rule_id )?.name || `#${row.original.promo_rule_id}` : "--", }, { accessorKey: "subscribe_id", header: t("subscribe", "Subscribe"), cell: ({ row }) => row.original.subscribe_name || getSubscribeName(row.original.subscribe_id), }, { accessorKey: "quantity", header: t("quantity", "Quantity"), cell: ({ row }) => row.original.quantity || "--", }, { accessorKey: "unit_price", header: t("unitPrice", "Unit Price"), cell: ({ row }) => formatCurrency(row.original.unit_price), }, { accessorKey: "promo_price", header: t("promoPrice", "Promo Price"), cell: ({ row }) => formatCurrency(row.original.promo_price), }, ]} empty={} error={ } header={{ toolbar: ( { setLoading(true); try { await createPromoPrice(values); toast.success(t("createSuccess", "Create Success")); priceRef.current?.refresh(); setLoading(false); return true; } catch (_error) { setLoading(false); return false; } }} rules={rules} subscribes={subscribes} /> ), }} params={[ { key: "promo_rule_id", placeholder: t("rule", "Rule"), options: rules.map((rule) => ({ label: rule.name, value: String(rule.id), })), }, { key: "subscribe_id", placeholder: t("subscribe", "Subscribe"), options: subscribes .filter((item) => typeof item.id === "number") .map((item) => ({ label: item.name || `#${item.id}`, value: String(item.id), })), }, ]} request={async (pagination, filters) => { const { data } = await getPromoPriceList({ ...pagination, promo_rule_id: parseOptionalNumber(filters.promo_rule_id), subscribe_id: parseOptionalNumber(filters.subscribe_id), }); return { list: data.data?.list || [], total: data.data?.total || 0, }; }} /> action={usageRef} columns={[ { accessorKey: "promo_rule_id", header: t("rule", "Rule"), cell: ({ row }) => row.original.rule_name || rules.find((rule) => rule.id === row.original.promo_rule_id) ?.name || `#${row.original.promo_rule_id}`, }, { accessorKey: "user_id", header: t("userId", "User ID"), cell: ({ row }) => `#${row.original.user_id}`, }, { accessorKey: "subscribe_id", header: t("subscribe", "Subscribe"), cell: ({ row }) => row.original.subscribe_name || getSubscribeName(row.original.subscribe_id), }, { accessorKey: "quantity", header: t("quantity", "Quantity"), cell: ({ row }) => row.original.quantity || "--", }, { accessorKey: "order_no", header: t("orderNo", "Order No."), }, { accessorKey: "promo_price", header: t("promoPrice", "Promo Price"), cell: ({ row }) => formatCurrency(row.original.promo_price), }, { accessorKey: "used_at", header: t("usedAt", "Used At"), cell: ({ row }) => formatDate(row.original.used_at), }, ]} empty={ } error={ } params={[ { key: "promo_rule_id", placeholder: t("rule", "Rule"), options: rules.map((rule) => ({ label: rule.name, value: String(rule.id), })), }, { key: "subscribe_id", placeholder: t("subscribe", "Subscribe"), options: subscribes .filter((item) => typeof item.id === "number") .map((item) => ({ label: item.name || `#${item.id}`, value: String(item.id), })), }, { key: "user_id", placeholder: t("userId", "User ID") }, { key: "order_no", placeholder: t("orderNo", "Order No.") }, ]} request={async (pagination, filters) => { const { data } = await getPromoUsageList({ ...pagination, promo_rule_id: parseOptionalNumber(filters.promo_rule_id), subscribe_id: parseOptionalNumber(filters.subscribe_id), user_id: parseOptionalNumber(filters.user_id), order_no: filters.order_no?.trim() || undefined, }); return { list: data.data?.list || [], total: data.data?.total || 0, }; }} /> ); }