TorbatYar/frontend/modules/loyalty/features/membership/members.tsx
Mortezakoohjani d579d0b142 feat(loyalty): add Loyalty Platform Frontend module
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>
2026-07-27 10:50:55 +03:30

93 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
),
});