"use client"; // ============================================================================ // Purchase Detail Page // ============================================================================ import React, { useEffect, useState } from "react"; import { use } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { DashboardHeader } from "@/components/layout/dashboard-header"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { purchasesApi } from "@/lib/api"; import { formatNumber, formatMetric, formatCurrency, formatDate, formatPercent, formatUsername, } from "@/lib/utils/format"; import { ShoppingCart, ArrowLeft, Loader2, AlertCircle, Archive, Trash2, BarChart3, Users, TrendingUp, Eye, Copy, ExternalLink as ExternalLinkIcon, Check, Clock, CheckCircle, } from "lucide-react"; import type { PurchaseDetail } from "@/lib/types/api"; interface PageProps { params: Promise<{ id: string }>; } export default function PurchaseDetailPage({ params }: PageProps) { const resolvedParams = use(params); const router = useRouter(); const [purchase, setPurchase] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); useEffect(() => { const loadPurchase = async () => { try { setLoading(true); const data = await purchasesApi.get(resolvedParams.id); setPurchase(data); } catch (err: any) { setError(err?.error?.message || "Ошибка загрузки закупа"); } finally { setLoading(false); } }; loadPurchase(); }, [resolvedParams.id]); const handleArchive = async () => { if (!purchase) return; try { await purchasesApi.update(purchase.id, { is_archived: !purchase.is_archived, }); setPurchase({ ...purchase, is_archived: !purchase.is_archived }); } catch (err: any) { alert(err?.error?.message || "Ошибка обновления закупа"); } }; const handleDelete = async () => { if (!purchase) return; if (!confirm("Удалить закуп?")) return; try { await purchasesApi.delete(purchase.id); router.push("/purchases"); } catch (err: any) { alert(err?.error?.message || "Ошибка удаления закупа"); } }; const handleCopyLink = () => { if (!purchase) return; navigator.clipboard.writeText(purchase.invite_link); setCopied(true); setTimeout(() => setCopied(false), 2000); }; if (loading) { return ( <>
); } if (error || !purchase) { return ( <>
{error || "Закуп не найден"}
); } const messageText = purchase.creative.text.replace( "{link}", purchase.invite_link ); return ( <>

{purchase.external_channel.title}

{purchase.is_archived ? ( Архив ) : purchase.actual_date ? ( Завершено ) : ( Запланировано )}
Целевой канал:{" "} {purchase.target_channel.title} Креатив:{" "} {purchase.creative.name} Дата размещения:{" "} {formatDate(purchase.actual_date || purchase.scheduled_date)}
Стоимость
{formatCurrency(purchase.cost)}
Подписчики
{formatNumber(purchase.subscriptions_count)}
Просмотры
{purchase.views_count ? formatNumber(purchase.views_count) : "—"}
CPF
{purchase.cpf ? `₽${formatMetric(purchase.cpf)}` : "—"}
Конверсия
{purchase.conversion_rate ? formatPercent(purchase.conversion_rate) : "—"}
Пригласительная ссылка Используется для отслеживания подписок
{purchase.invite_link}
{purchase.post_link && (
{purchase.post_link}
)}
Рекламное сообщение Текст для размещения

{messageText}

{purchase.comment && ( Комментарий

{purchase.comment}

)} Подписки ({purchase.subscriptions.length}) {purchase.views_history && purchase.views_history.length > 0 && ( История просмотров ({purchase.views_history.length}) )} Подписки Пользователи, перешедшие по ссылке {purchase.subscriptions.length === 0 ? (

Подписок пока нет

) : ( Пользователь Username Дата подписки Статус {purchase.subscriptions.map((subscription) => ( {subscription.user_first_name}{" "} {subscription.user_last_name} {subscription.user_username ? formatUsername(subscription.user_username) : "—"} {formatDate(subscription.subscribed_at)} {subscription.is_active ? ( Активен ) : ( Отписался )} ))}
)}
{purchase.views_history && purchase.views_history.length > 0 && ( История просмотров Динамика просмотров рекламного поста Дата Просмотры Прирост {purchase.views_history.map((item, index) => { const prevViews = index > 0 ? purchase.views_history![index - 1].views : 0; const growth = item.views - prevViews; return ( {formatDate(item.fetched_at)} {formatNumber(item.views)} {index > 0 && ( 0 ? "text-green-600" : "text-muted-foreground" } > +{formatNumber(growth)} )} ); })}
)}
); } function Label({ children, className, }: { children: React.ReactNode; className?: string; }) { return
{children}
; }