"use client"; import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { AlertTriangle, Bell, BookOpen, Calendar, Eye, Gamepad2, Loader2, Megaphone, Route, Settings2, Trash2, TrendingUp, Users, } from "lucide-react"; import { http, toUserError } from "@/lib/api"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { toast } from "@/components/providers"; import { SafeMarkdown } from "@/components/safe-markdown"; const CATEGORIES = [ { id: "event", label: "رویداد", icon: Calendar, hint: "جلسه زنده، کارگاه، ددلاین" }, { id: "progress", label: "پیشرفت", icon: TrendingUp, hint: "درس جدید، فصل بازشده" }, { id: "gamification", label: "بازی‌سازی", icon: Gamepad2, hint: "جایزه، استریک، چالش" }, { id: "mentor", label: "منتور", icon: Users, hint: "پیام مستقیم تیم منتور" }, { id: "urgent", label: "فوری", icon: AlertTriangle, hint: "نیاز به توجه همین حالا" }, { id: "curriculum", label: "درس", icon: BookOpen, hint: "محتوای آموزشی" }, { id: "community", label: "جامعه", icon: Users, hint: "فضای مشترک دوره" }, { id: "system", label: "سیستم", icon: Settings2, hint: "وضعیت خود پرگار" }, ] as const; type Announcement = { id: number; title: string; body: string; category: string; route: string; pinned: boolean; }; const ROUTES = [ { path: "/unwrap", label: "نقشه مسیر" }, { path: "/notifications", label: "اعلان‌ها" }, { path: "/leaderboard", label: "لیدربورد" }, { path: "/cap", label: "داشبورد" }, ]; function announcementExcerpt(body: string) { const plain = body .replace(/```[\s\S]*?```/g, " ") .replace(/`([^`]*)`/g, "$1") .replace(/!\[[^\]]*]\([^)]*\)/g, " ") .replace(/\[([^\]]*)]\([^)]*\)/g, "$1") .replace(/^#{1,6}\s+/gm, "") .replace(/[*_~>]+/g, "") .replace(/\s+/g, " ") .trim(); if (plain.length <= 180) return plain; return `${plain.slice(0, 180).trimEnd()}…`; } function PreviewCard({ title, body, category, route, }: { title: string; body: string; category: string; route: string; }) { const cat = CATEGORIES.find((c) => c.id === category); return (
{cat?.label ?? category}

{title.trim() || "عنوان اطلاعیه"}

→ {route || "/"}

); } export function AnnouncePanel() { const [title, setTitle] = useState(""); const [body, setBody] = useState(""); const [category, setCategory] = useState("event"); const [route, setRoute] = useState(""); const [pinned, setPinned] = useState(false); const [editingId, setEditingId] = useState(null); const [busy, setBusy] = useState(false); const qc = useQueryClient(); const listQ = useQuery({ queryKey: ["admin-announcements"], queryFn: () => http.get<{ announcements: Announcement[] }>("/api/admin/announcements"), }); const [confirmOpen, setConfirmOpen] = useState(false); const [lastSent, setLastSent] = useState<{ title: string; recipients: number } | null>(null); const titleOk = title.trim().length >= 3; const bodyOk = body.trim().length >= 8; const valid = titleOk && bodyOk; const send = async () => { if (!valid) return; setBusy(true); try { const payload = { title: title.trim(), body: body.trim(), category, route: route.trim(), pinned }; if (editingId) { await http.put(`/api/admin/announcements/${editingId}`, payload); toast.success("اطلاعیه به‌روز شد"); } else { const res = await http.post<{ recipients: number }>("/api/admin/announce", payload); toast.success(`اطلاعیه برای ${res.recipients} نفر ارسال شد`); setLastSent({ title: title.trim(), recipients: res.recipients }); } setTitle(""); setBody(""); setPinned(false); setEditingId(null); setConfirmOpen(false); void qc.invalidateQueries({ queryKey: ["admin-announcements"] }); } catch (e) { toast.error(toUserError(e, "ارسال اطلاعیه ممکن نشد")); } finally { setBusy(false); } }; return ( <>
نگارش اعلان

به همه هنرجویان فعال می‌رسد و در مرکز اعلان‌ها دیده می‌شود.

setTitle(e.target.value)} placeholder="مثلاً جلسه زنده هفته ۴ · پنجشنبه ۲۰:۰۰" className={cn("h-11 rounded-2xl", !titleOk && title.length > 0 && "border-destructive")} autoFocus />
{body.length}/۴۰۰۰