"use client"; // ============================================================================ // External Channels List Page // ============================================================================ import React, { useEffect, useState } from "react"; import Link from "next/link"; import { DashboardHeader } from "@/components/layout/dashboard-header"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { externalChannelsApi, channelsApi } from "@/lib/api"; import { formatNumber, formatMetric, formatUsername, formatCompactNumber, } from "@/lib/utils/format"; import { ExternalLink as ExternalLinkIcon, Loader2, AlertCircle, Plus, Search, FileUp, Eye, Pencil, Trash2, Users, ArrowUpDown, ArrowUp, ArrowDown, LayoutGrid, Table2, } from "lucide-react"; import type { ExternalChannel } from "@/lib/types/api"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { useTargetChannel } from "@/components/providers/target-channel-provider"; type ViewMode = "table" | "grid"; type SortField = | "title" | "username" | "subscribers_count" | "telegram_id" | null; type SortDirection = "asc" | "desc" | null; export default function ExternalChannelsPage() { const { selectedChannel } = useTargetChannel(); const [channels, setChannels] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [searchQuery, setSearchQuery] = useState(""); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [isCreating, setIsCreating] = useState(false); const [viewMode, setViewMode] = useState("table"); const [sortField, setSortField] = useState(null); const [sortDirection, setSortDirection] = useState(null); // Form state const [formData, setFormData] = useState({ title: "", username: "", link: "", subscribers_count: "", description: "", }); useEffect(() => { if (selectedChannel) { loadChannels(); } else { setLoading(false); } }, [selectedChannel]); const loadChannels = async () => { if (!selectedChannel) return; try { setLoading(true); setError(null); const response = await externalChannelsApi.list(selectedChannel.id); setChannels(response.external_channels); } catch (err: any) { const errorMsg = typeof err?.detail === "string" ? err.detail : err?.error?.message || "Ошибка загрузки каналов"; setError(errorMsg); } finally { setLoading(false); } }; const handleCreate = async () => { try { setIsCreating(true); // Получаем первый доступный target channel const targetChannelsRes = await channelsApi.list(); if (targetChannelsRes.target_channels.length === 0) { alert("Добавьте сначала целевой канал"); setIsCreating(false); return; } await externalChannelsApi.create({ telegram_id: 0, // Временно, пока не получим реальный ID title: formData.title, username: formData.username || null, subscribers_count: formData.subscribers_count ? parseInt(formData.subscribers_count) : null, description: formData.description || null, target_channel_ids: [targetChannelsRes.target_channels[0].id], }); setIsCreateDialogOpen(false); setFormData({ title: "", username: "", link: "", subscribers_count: "", description: "", }); loadChannels(); } catch (err: any) { const errorMsg = typeof err?.detail === "string" ? err.detail : err?.error?.message || "Ошибка создания канала"; alert(errorMsg); } finally { setIsCreating(false); } }; const handleDelete = async (id: string, title: string) => { if (!confirm(`Удалить канал "${title}"?`)) return; try { await externalChannelsApi.delete(id); loadChannels(); } catch (err: any) { alert(err?.error?.message || "Ошибка удаления канала"); } }; // Обработка сортировки const handleSort = (field: SortField) => { if (sortField === field) { // Переключение: asc -> desc -> null -> asc if (sortDirection === "asc") { setSortDirection("desc"); } else if (sortDirection === "desc") { setSortField(null); setSortDirection(null); } } else { setSortField(field); setSortDirection("asc"); } }; const getSortIcon = (field: SortField) => { if (sortField !== field) { return ; } if (sortDirection === "asc") { return ; } return ; }; const filteredChannels = channels .filter( (channel) => channel.title.toLowerCase().includes(searchQuery.toLowerCase()) || channel.username?.toLowerCase().includes(searchQuery.toLowerCase()) ) .sort((a, b) => { if (!sortField || !sortDirection) return 0; let aValue: string | number | null = ""; let bValue: string | number | null = ""; switch (sortField) { case "title": aValue = a.title; bValue = b.title; break; case "username": aValue = a.username || ""; bValue = b.username || ""; break; case "subscribers_count": aValue = a.subscribers_count || 0; bValue = b.subscribers_count || 0; break; case "telegram_id": aValue = a.telegram_id; bValue = b.telegram_id; break; } // Сравнение строк if (typeof aValue === "string" && typeof bValue === "string") { return sortDirection === "asc" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); } // Сравнение чисел if (typeof aValue === "number" && typeof bValue === "number") { return sortDirection === "asc" ? aValue - bValue : bValue - aValue; } return 0; }); return ( <>

Внешние каналы

Каналы, в которых покупаете рекламу

Добавить внешний канал Добавьте канал, в котором планируете покупать рекламу
setFormData({ ...formData, title: e.target.value }) } placeholder="Название канала" />
setFormData({ ...formData, link: e.target.value }) } placeholder="https://t.me/channel_name" />
setFormData({ ...formData, username: e.target.value }) } placeholder="@channel_name" />
setFormData({ ...formData, subscribers_count: e.target.value, }) } placeholder="50000" />