Add the Healthcare platform backend (phases 13.0–13.7) with Alembic migration revision fix, production deploy script, validation reports, shared UI exports, and loyalty list page client directives so Next.js build succeeds. Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
"use client";
|
||
|
||
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>
|
||
),
|
||
});
|