"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, Trash2, Copy, Check, ExternalLink, } 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 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); // Edit form state const [name, setName] = useState(""); const [text, setText] = useState(""); const [error, setError] = useState(null); const canWrite = hasPermission("placements_write"); useEffect(() => { loadCreative(); }, [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: any) { setError(err?.detail || "Не удалось сохранить"); } finally { setIsSaving(false); } }; const handleArchive = async () => { if (!creative) return; try { // Toggle archive status by updating via delete endpoint (soft delete) await creativesApi.delete(workspaceId, creativeId); 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 ( <>

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

); } return ( <>

{creative.name}

{creative.status === "active" ? "Активен" : "Архив"}

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

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