"use client"; // ============================================================================ // Creative Detail Page // ============================================================================ import { useEffect, useState } from "react"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import Link from "next/link"; import { Loader2, ArrowLeft, Pencil, Archive, Copy, Check, Eye, LayoutList, Plus, BarChart3, FileText, Image, Video, } from "lucide-react"; import { DashboardHeader } from "@/components/layout/dashboard-header"; import { useWorkspace } from "@/components/providers/workspace-provider"; import { creativesApi } from "@/lib/api"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/components/ui/tabs"; import { TelegramPostPreview } from "@/components/telegram-post-preview"; import type { Creative } from "@/lib/types/api"; // ============================================================================ // Component // ============================================================================ export default function CreativeDetailPage() { const params = useParams(); const router = useRouter(); const searchParams = useSearchParams(); const workspaceId = params?.workspaceId as string; const creativeId = params?.creativeId as string; const { hasPermission } = useWorkspace(); const [creative, setCreative] = useState(null); const [loading, setLoading] = useState(true); const [isEditing, setIsEditing] = useState(searchParams.get("edit") === "true"); const [isSaving, setIsSaving] = useState(false); const [copied, setCopied] = useState(false); const [viewMode, setViewMode] = useState<"preview" | "data">("preview"); const [name, setName] = useState(""); const [text, setText] = useState(""); const [error, setError] = useState(null); const canWrite = hasPermission("placements_write"); useEffect(() => { loadCreative(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [workspaceId, creativeId]); const loadCreative = async () => { try { setLoading(true); const data = await creativesApi.get(workspaceId, creativeId); setCreative(data); setName(data.name); setText(data.text); } catch (err) { console.error("Failed to load creative:", err); } finally { setLoading(false); } }; const handleSave = async () => { if (!name.trim() || !text.trim()) { setError("Заполните все поля"); return; } if (!text.includes("{invite_link}")) { setError("Текст должен содержать {invite_link}"); return; } try { setIsSaving(true); setError(null); const updated = await creativesApi.update(workspaceId, creativeId, { name: name.trim(), text: text.trim(), }); setCreative(updated); setIsEditing(false); } catch (err) { const errorMessage = err && typeof err === 'object' && 'detail' in err ? (err as { detail?: string }).detail ?? "Не удалось сохранить" : "Не удалось сохранить"; setError(errorMessage); } finally { setIsSaving(false); } }; const handleArchive = async () => { if (!creative) return; try { await creativesApi.update(workspaceId, creativeId, { status: "archived" }); router.push(`/dashboard/${workspaceId}/creatives`); } catch (err) { console.error("Failed to archive:", err); } }; const handleCopy = () => { if (creative) { navigator.clipboard.writeText(creative.text); setCopied(true); setTimeout(() => setCopied(false), 2000); } }; const handleCancel = () => { if (creative) { setName(creative.name); setText(creative.text); } setIsEditing(false); setError(null); }; if (loading) { return ( <>
); } if (!creative) { return ( <>

Креатив не найден

); } const isActive = creative.status === "active"; return ( <>

{creative.name}

{isActive ? "Активен" : "Архив"}

{creative.placements_count} размещений

{canWrite && !isEditing && (
{isActive && ( )} Все размещения Аналитика Архивировать креатив? Креатив будет перемещён в архив. Существующие размещения останутся без изменений. Отмена Архивировать
)}
{isEditing ? ( Редактирование Изменения не повлияют на существующие размещения
setName(e.target.value)} disabled={isSaving} />