first commit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user