"use client"; import { useCallback, 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, Eye } from "lucide-react"; import Link from "next/link"; import { z } from "zod"; import { Button, Dialog, Drawer, Input, ConfirmDialog, Select, Textarea, FormField, } from "@/components/ds"; import { CommunicationTablePage, CommunicationStatGrid } from "@/modules/communication/design-system"; import { CommunicationPageLoader, CommunicationPageError, exportToCsv, } from "@/modules/communication/pages/shared"; import { useTenantId } from "@/hooks/useTenantId"; import { useCommunicationLookups } from "@/modules/communication/hooks/useCommunicationCapabilities"; import { useCommunicationPermissions } from "@/modules/communication/hooks/useCommunicationPermissions"; import { CommunicationStatusChip } from "@/modules/communication/design-system"; import { PermissionDeniedState } from "@/src/shared/ui"; export type CommunicationFieldConfig = { name: string; label: string; type?: "text" | "number" | "email" | "tel" | "textarea" | "select" | "organization" | "hub"; options?: { value: string; label: string }[]; required?: boolean; createOnly?: boolean; editOnly?: boolean; placeholder?: string; }; export type CommunicationColumnConfig = { key: string; header: string; type?: "status" | "datetime" | "text" | "link"; linkPrefix?: string; render?: (row: Record) => React.ReactNode; }; type ApiResource = { list: ( tenantId: string, params?: Record ) => Promise[] | { items: Record[]; total: number }>; 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; }; export type CommunicationListConfig = { title: string; description?: string; breadcrumb?: string; resourceKey: string; permission?: string; api: ApiResource; columns: CommunicationColumnConfig[]; createFields?: CommunicationFieldConfig[]; editFields?: CommunicationFieldConfig[]; readOnly?: boolean; detailHref?: (row: Record) => string | null; rowActions?: { label: string; onClick: (tenantId: string, row: Record) => Promise; variant?: "default" | "outline" | "danger"; }[]; }; function buildSchema(fields: CommunicationFieldConfig[]) { const shape: Record = {}; for (const f of fields) { shape[f.name] = f.required ? z.string().min(1, `${f.label} الزامی است`) : z.string().optional(); } return z.object(shape); } function normalizeList( data: Record[] | { items: Record[]; total: number } ): Record[] { if (Array.isArray(data)) return data; return data.items ?? []; } function renderCell(col: CommunicationColumnConfig, 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 === "link" && col.linkPrefix && row.id) { return ( {val != null ? String(val) : "—"} ); } return val != null ? String(val) : "—"; } function renderField( field: CommunicationFieldConfig, register: ReturnType["register"], mode: "create" | "edit", lookups: { organizations: Record[]; hubs: Record[] } ) { if (mode === "create" && field.editOnly) return null; if (mode === "edit" && field.createOnly) return null; if (field.type === "textarea") { return (