117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { Trash2, Plus } from "lucide-react";
|
|
import Shell from "@/components/Shell";
|
|
import EditableTable, { Col } from "@/components/EditableTable";
|
|
import { api } from "@/lib/api";
|
|
|
|
type Row = Record<string, unknown>;
|
|
|
|
const PACK_COLS: Col[] = [
|
|
{ key: "id", label: "شناسه" },
|
|
{ key: "title", label: "عنوان" },
|
|
{ key: "kind", label: "نوع (text/emoji)" },
|
|
{ key: "price_coins", label: "قیمت (سکه)", type: "number" },
|
|
{ key: "vip", label: "VIP رایگان", type: "bool" },
|
|
{ key: "sort", label: "ترتیب", type: "number" },
|
|
];
|
|
|
|
export default function ChatPage() {
|
|
const [packs, setPacks] = useState<Row[]>([]);
|
|
const [messages, setMessages] = useState<Row[]>([]);
|
|
const [sel, setSel] = useState<string>("");
|
|
const [newMsg, setNewMsg] = useState("");
|
|
|
|
async function load() {
|
|
const r = await api.get<{ packs: Row[]; messages: Row[] }>("/chat");
|
|
setPacks(r.packs || []);
|
|
setMessages(r.messages || []);
|
|
if (!sel && r.packs?.length) setSel(String(r.packs[0].id));
|
|
}
|
|
useEffect(() => {
|
|
load();
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const packMsgs = messages.filter((m) => String(m.pack_id) === sel);
|
|
|
|
async function addMsg() {
|
|
if (!sel || !newMsg.trim()) return;
|
|
await api.post("/chat/message", { pack_id: sel, body: newMsg.trim(), sort: packMsgs.length });
|
|
setNewMsg("");
|
|
await load();
|
|
}
|
|
|
|
return (
|
|
<Shell title="چت">
|
|
<h2 className="font-bold mb-3">بستهها</h2>
|
|
<EditableTable
|
|
cols={PACK_COLS}
|
|
rows={packs}
|
|
newTemplate={{ id: "", title: "", kind: "text", price_coins: 0, vip: false, sort: 0 }}
|
|
onSave={async (row) => {
|
|
await api.post("/chat/pack", row);
|
|
await load();
|
|
}}
|
|
onDelete={async (id) => {
|
|
if (!confirm("حذف بسته و پیامهایش؟")) return;
|
|
await api.del(`/chat/pack?id=${encodeURIComponent(id)}`);
|
|
await load();
|
|
}}
|
|
/>
|
|
|
|
<div className="flex items-center gap-2 mt-6 mb-3">
|
|
<h2 className="font-bold">پیامها</h2>
|
|
<select
|
|
className="inp w-auto"
|
|
value={sel}
|
|
onChange={(e) => setSel(e.target.value)}
|
|
>
|
|
{packs.map((p) => (
|
|
<option key={String(p.id)} value={String(p.id)}>
|
|
{String(p.title)}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="card p-4">
|
|
<div className="flex gap-2 mb-4">
|
|
<input
|
|
className="inp"
|
|
placeholder="پیام یا شکلکِ جدید…"
|
|
value={newMsg}
|
|
onChange={(e) => setNewMsg(e.target.value)}
|
|
onKeyDown={(e) => e.key === "Enter" && addMsg()}
|
|
/>
|
|
<button className="btn btn-gold flex items-center gap-1" onClick={addMsg}>
|
|
<Plus size={16} /> افزودن
|
|
</button>
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{packMsgs.map((m) => (
|
|
<span
|
|
key={String(m.id)}
|
|
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-[var(--panel-2)] border border-[#16305a]"
|
|
>
|
|
{String(m.body)}
|
|
<button
|
|
onClick={async () => {
|
|
await api.del(`/chat/message?id=${m.id}`);
|
|
await load();
|
|
}}
|
|
className="text-red-400"
|
|
>
|
|
<Trash2 size={13} />
|
|
</button>
|
|
</span>
|
|
))}
|
|
{packMsgs.length === 0 && (
|
|
<span className="text-[#8aa] text-sm">پیامی نیست</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Shell>
|
|
);
|
|
}
|