"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 { 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 { Placement } 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, { status: purchase.status === "active" ? "archived" : "active", }); setPurchase({ ...purchase, status: purchase.status === "active" ? "archived" : "active", }); } 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 || "Закуп не найден"}
); } // Remove message text generation as we don't have creative.text in Placement const messageText = `Текст сообщения с ссылкой: ${purchase.invite_link}`; return ( <>

{purchase.external_channel_title}

{purchase.status === "archived" ? ( Архив ) : ( Активно )}
Целевой канал:{" "} {purchase.target_channel_title} Креатив:{" "} {purchase.creative_name} Дата размещения: {formatDate(purchase.placement_date)}
Стоимость
{formatCurrency(purchase.cost)}
Подписчики
{formatNumber(purchase.subscriptions_count)}
Просмотры
{purchase.views_count ? formatNumber(purchase.views_count) : "—"}
Статус просмотров
{purchase.views_availability === "available" ? "Доступны" : purchase.views_availability === "manual" ? "Вручную" : purchase.views_availability === "unavailable" ? "Недоступны" : "Неизвестно"}

{purchase.last_views_fetch_at ? `Обновлено: ${formatDate(purchase.last_views_fetch_at)}` : "Еще не обновлялись"}

Тип ссылки
{purchase.invite_link_type === "approval" ? "С одобрением" : "Публичная"}

{purchase.invite_link_type === "approval" ? "Отслеживание подписчиков" : "Без отслеживания"}

Пригласительная ссылка Используется для отслеживания подписок
{purchase.invite_link}
{purchase.ad_post_url && (
{purchase.ad_post_url}
)}
Рекламное сообщение Текст для размещения

{messageText}

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

{purchase.comment}

)} Заметка Детальная информация о размещении доступна через раздел "Подписки" в главном меню

Для просмотра списка подписчиков и истории просмотров используйте соответствующие разделы в главном меню.

); } function Label({ children, className, }: { children: React.ReactNode; className?: string; }) { return
{children}
; }