"use client"; import { useState } from "react"; import { Save, Trash2, Plus, Loader2 } from "lucide-react"; import NumberInput from "./NumberInput"; import { toastError, toastSuccess } from "@/lib/toast"; export type Col = { key: string; label: string; type?: "text" | "number" | "bool"; readonly?: boolean; }; type Row = Record; // سلولِ ورودی — در سطحِ ماژول تعریف شده تا با هر کلید دوباره mount نشود (وگرنه // اینپوت پس از یک حرف فوکوس را از دست می‌دهد). function Cell({ row, col, onChange, }: { row: Row; col: Col; onChange: (v: unknown) => void; }) { const v = row[col.key]; if (col.type === "bool") { return ( onChange(e.target.checked)} /> ); } if (col.type === "number") { // ورودیِ عددیِ گروه‌بندی‌شده (جداکننده‌ی هزارگان) و قابلِ تایپ با کیبورد. return ( ); } return ( onChange(e.target.value)} /> ); } export default function EditableTable({ cols, rows, onSave, onDelete, newTemplate, extra, preview, }: { cols: Col[]; rows: Row[]; onSave: (row: Row) => Promise; onDelete: (id: string) => Promise; newTemplate: Row; /** ستون/دکمه‌ی اضافی برای هر ردیف (مثلاً آپلود تصویر). */ extra?: (row: Row) => React.ReactNode; /** پیش‌نمایشِ تصویرِ ابتدای ردیف (مثلاً فرش یا کارت). */ preview?: (row: Row) => React.ReactNode; }) { const [draft, setDraft] = useState({ ...newTemplate }); const [busy, setBusy] = useState(null); // نسخه‌ی محلیِ قابلِ‌ویرایش هر ردیف. const [edit, setEdit] = useState>({}); const rowState = (r: Row) => edit[String(r.id)] ?? r; const patch = (id: string, key: string, val: unknown) => setEdit((e) => ({ ...e, [id]: { ...(e[id] ?? rows.find((r) => String(r.id) === id)!), [key]: val } })); // پیش از ذخیره، هر ستون را به نوعِ اعلام‌شده تبدیل کن تا سرور عددِ رشته‌ای نگیرد // (وگرنه 400: bad request). اعداد → number، بولین‌ها → boolean. const coerce = (row: Row): Row => { const out: Row = { ...row }; for (const c of cols) { if (c.type === "number") out[c.key] = Number(out[c.key] ?? 0) || 0; else if (c.type === "bool") out[c.key] = !!out[c.key] && out[c.key] !== 0; } return out; }; return (
{preview && } {cols.map((c) => ( ))} {rows.map((r) => { const id = String(r.id); const rs = rowState(r); return ( {preview && } {cols.map((c) => ( ))} ); })} {/* ردیفِ افزودن */} {preview && ))}
پیش‌نمایش{c.label}
{preview(r)} patch(id, c.key, v)} /> {extra?.(r)}
} {cols.map((c) => ( setDraft((d) => ({ ...d, [c.key]: v }))} />
); }