"use client"; import { useCallback, useEffect, useState } from "react"; import { useForm, type FieldValues, type DefaultValues } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { Plus, Pencil, Trash2, Download, RefreshCw, Printer, Copy, Eye, } from "lucide-react"; import Link from "next/link"; import { z } from "zod"; import { Button, Dialog, Drawer, Input, EmptyState, FormField, ConfirmDialog, Select, Textarea, Badge, } from "@/components/ds"; import { HospitalityTablePage, HospitalityStatGrid, } from "@/modules/hospitality/design-system"; import { HospitalityPageLoader, HospitalityPageError, exportToCsv, } from "@/modules/hospitality/pages/shared"; import { useTenantId } from "@/hooks/useTenantId"; import { useHospitalityCapabilities } from "@/modules/hospitality/hooks/useHospitalityCapabilities"; import { useHospitalityVenues } from "@/modules/hospitality/hooks/useHospitalityVenues"; import { HospitalityStatusChip } from "@/modules/hospitality/design-system"; import { HospitalityBundleGate } from "@/modules/hospitality/components/HospitalityBundleGate"; import { countByStatus } from "@/modules/hospitality/constants/fieldHelpers"; export type HospitalityFieldConfig = { name: string; label: string; type?: "text" | "number" | "email" | "tel" | "textarea" | "select" | "venue" | "datetime-local"; options?: { value: string; label: string }[]; required?: boolean; createOnly?: boolean; editOnly?: boolean; placeholder?: string; }; export type HospitalityColumnConfig = { key: string; header: string; type?: "status" | "datetime" | "text" | "money"; render?: (row: Record) => React.ReactNode; searchable?: boolean; sortable?: boolean; defaultVisible?: boolean; }; export type HospitalityRowAction = { label: string; onClick: (tenantId: string, row: Record) => Promise; variant?: "default" | "outline" | "danger"; }; type ApiResource = { list: (tenantId: string, params?: { page?: number; page_size?: number }) => Promise[]>; get?: (tenantId: string, id: string) => Promise>; create?: (tenantId: string, body: Record) => Promise; update?: (tenantId: string, id: string, body: Record) => Promise; remove?: (tenantId: string, id: string) => Promise; upsert?: (tenantId: string, body: Record) => Promise; }; export type HospitalityListConfig = { title: string; description?: string; breadcrumb?: string; resourceKey: string; bundleFeature?: string | null; permission?: string; api: ApiResource; columns: HospitalityColumnConfig[]; createFields?: HospitalityFieldConfig[]; editFields?: HospitalityFieldConfig[]; filterDeleted?: boolean; listFilter?: (row: Record) => boolean; rowActions?: HospitalityRowAction[]; refreshIntervalMs?: number; statsKeys?: { total?: string; active?: string }; quickActions?: { label: string; href: string }[]; exportColumns?: string[]; readOnly?: boolean; useUpsert?: boolean; emptyHint?: string; }; function buildSchema(fields: HospitalityFieldConfig[]) { const shape: Record = {}; for (const f of fields) { if (f.type === "number") { shape[f.name] = f.required ? z.coerce.number() : z.coerce.number().optional(); } else { shape[f.name] = f.required ? z.string().min(1, `${f.label} الزامی است`) : z.string().optional(); } } return z.object(shape); } function renderCell(col: HospitalityColumnConfig, row: Record) { if (col.render) return col.render(row); const val = row[col.key]; if (col.type === "status" && val) return ; if (col.type === "datetime" && val) { try { return new Date(String(val)).toLocaleString("fa-IR"); } catch { return String(val); } } if (col.type === "money" && val != null) { return `${Number(val).toLocaleString("fa-IR")} ${row.currency_code ?? "IRR"}`; } return val != null ? String(val) : "—"; } function renderField( field: HospitalityFieldConfig, register: ReturnType["register"], mode: "create" | "edit", venues: Record[] ) { if (mode === "create" && field.editOnly) return null; if (mode === "edit" && field.createOnly) return null; if (field.type === "textarea") { return (