Add frontend/modules/loyalty with types, API client, design system, feature pages and thin App Router routes under app/loyalty/. BFF proxy at app/api/loyalty/. Include loyalty frontend docs. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
3.3 KiB
TypeScript
93 lines
3.3 KiB
TypeScript
import Link from "next/link";
|
||
import { z } from "zod";
|
||
import { Button } from "@/components/ds";
|
||
import { loyaltyApi } from "@/modules/loyalty/services/loyalty-api";
|
||
import { createLoyaltyListPage } from "@/modules/loyalty/components/LoyaltyListCrudPage";
|
||
import { LoyaltyStatusChip } from "@/modules/loyalty/design-system";
|
||
import type { Member } from "@/modules/loyalty/types";
|
||
|
||
const createSchema = z.object({
|
||
program_id: z.string().uuid(),
|
||
display_name: z.string().min(1),
|
||
membership_number: z.string().optional(),
|
||
email: z.string().email().optional().or(z.literal("")),
|
||
mobile: z.string().optional(),
|
||
status: z
|
||
.enum(["pending", "active", "frozen", "cancelled", "expired", "transferred"])
|
||
.default("pending"),
|
||
enroll: z.coerce.boolean().default(false),
|
||
});
|
||
|
||
const editSchema = z.object({
|
||
display_name: z.string().min(1).optional(),
|
||
email: z.string().optional(),
|
||
mobile: z.string().optional(),
|
||
status: z
|
||
.enum(["pending", "active", "frozen", "cancelled", "expired", "transferred"])
|
||
.optional(),
|
||
version: z.coerce.number().optional(),
|
||
});
|
||
|
||
export default createLoyaltyListPage<Member>({
|
||
title: "اعضا",
|
||
description: "فهرست اعضای برنامه وفاداری — ایجاد، ویرایش و حذف نرم.",
|
||
breadcrumb: "اعضا",
|
||
resourceKey: "members",
|
||
createSchema,
|
||
editSchema,
|
||
createDefaults: {
|
||
program_id: "",
|
||
display_name: "",
|
||
membership_number: "",
|
||
email: "",
|
||
mobile: "",
|
||
status: "pending",
|
||
enroll: false,
|
||
},
|
||
fields: [
|
||
{ name: "program_id", label: "شناسه برنامه", required: true, createOnly: true },
|
||
{ name: "display_name", label: "نام نمایشی", required: true },
|
||
{ name: "membership_number", label: "شماره عضویت", createOnly: true },
|
||
{ name: "email", label: "ایمیل", type: "email" },
|
||
{ name: "mobile", label: "موبایل", type: "tel" },
|
||
{
|
||
name: "status",
|
||
label: "وضعیت",
|
||
type: "select",
|
||
editOnly: true,
|
||
options: [
|
||
{ value: "pending", label: "در انتظار" },
|
||
{ value: "active", label: "فعال" },
|
||
{ value: "frozen", label: "مسدود" },
|
||
{ value: "cancelled", label: "لغو" },
|
||
{ value: "expired", label: "منقضی" },
|
||
{ value: "transferred", label: "منتقل" },
|
||
],
|
||
},
|
||
],
|
||
columns: [
|
||
{ key: "membership_number", header: "شماره" },
|
||
{ key: "display_name", header: "نام" },
|
||
{ key: "email", header: "ایمیل" },
|
||
{ key: "mobile", header: "موبایل" },
|
||
{
|
||
key: "status",
|
||
header: "وضعیت",
|
||
render: (r) => <LoyaltyStatusChip status={r.status} domain="member" />,
|
||
},
|
||
],
|
||
list: (tid) => loyaltyApi.members.list(tid, { page: 1, page_size: 500 }),
|
||
create: (tid, d) => loyaltyApi.members.create(tid, d),
|
||
update: (tid, id, d) => loyaltyApi.members.update(tid, id, d),
|
||
remove: (tid, id) => loyaltyApi.members.delete(tid, id),
|
||
filterDeleted: true,
|
||
exportColumns: ["membership_number", "display_name", "email", "mobile", "status", "program_id"],
|
||
extraActions: (row) => (
|
||
<Link href={`/loyalty/engage/customer-profile?memberId=${row.id}`}>
|
||
<Button variant="ghost" size="sm">
|
||
پروفایل
|
||
</Button>
|
||
</Link>
|
||
),
|
||
});
|