"use client"; import { useState, useEffect, useTransition } from "react"; import { Database, AlertTriangle, Link2, Plus, Loader2, ArrowRight } from "lucide-react"; import { toast } from "sonner"; import { createDestinationViaWorker, setGlobalDestination } from "../actions"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import type { GlobalDestination, ChannelRow } from "@/lib/telegram/admin-queries"; interface DestinationCardProps { destination: GlobalDestination; channels?: ChannelRow[]; } type CreateState = | { phase: "idle" } | { phase: "creating"; requestId?: string } | { phase: "done"; title: string; telegramId: string } | { phase: "error"; message: string }; export function DestinationCard({ destination, channels = [] }: DestinationCardProps) { const [isPending, startTransition] = useTransition(); const [createOpen, setCreateOpen] = useState(false); const [title, setTitle] = useState("dragonsstash db"); const [createState, setCreateState] = useState({ phase: "idle" }); const [selectedChannelId, setSelectedChannelId] = useState(""); // Channels that can be assigned as destination (SOURCE channels only, exclude current destination) const assignableChannels = channels.filter( (c) => c.type === "SOURCE" && c.id !== destination?.id ); // Poll for worker result when creating useEffect(() => { if (createState.phase !== "creating" || !createState.requestId) return; let mounted = true; const requestId = createState.requestId; const poll = async () => { for (let i = 0; i < 60; i++) { await new Promise((r) => setTimeout(r, 2000)); if (!mounted) return; try { const res = await fetch( `/api/telegram/worker-request?requestId=${requestId}` ); if (!res.ok) continue; const data = await res.json(); if (data.status === "COMPLETED" && data.result) { if (mounted) { setCreateState({ phase: "done", title: data.result.title, telegramId: data.result.telegramId, }); toast.success(`Telegram group "${data.result.title}" created and set as destination!`); setCreateOpen(false); // Refresh the page to show the new destination window.location.reload(); } return; } else if (data.status === "FAILED") { if (mounted) { setCreateState({ phase: "error", message: data.error || "Worker failed to create the group", }); } return; } } catch { // Network blip — keep polling } } if (mounted) { setCreateState({ phase: "error", message: "Timed out waiting for the worker" }); } }; poll(); return () => { mounted = false; }; }, [createState]); const handleCreate = () => { if (!title.trim()) return; startTransition(async () => { const result = await createDestinationViaWorker(title.trim()); if (result.success) { setCreateState({ phase: "creating", requestId: result.data.requestId }); } else { setCreateState({ phase: "error", message: result.error ?? "Unknown error" }); } }); }; const handleAssignExisting = () => { if (!selectedChannelId) return; startTransition(async () => { const result = await setGlobalDestination(selectedChannelId); if (result.success) { toast.success("Channel set as destination!"); setCreateOpen(false); setSelectedChannelId(""); } else { toast.error(result.error ?? "Failed to set destination"); } }); }; const handleOpenChange = (open: boolean) => { setCreateOpen(open); if (!open) { // Reset state when closing (unless actively creating) if (createState.phase !== "creating") { setCreateState({ phase: "idle" }); } setSelectedChannelId(""); } }; if (!destination) { return ( <>

No destination channel configured

Create a private Telegram group that all accounts will write archives to. Requires at least one authenticated account.

); } return ( <>

{destination.title}

DESTINATION
ID: {destination.telegramId} {destination.inviteLink && ( Invite link active )}
); } function DestinationDialog({ open, onOpenChange, title, setTitle, onSubmitCreate, createState, isPending, assignableChannels, selectedChannelId, setSelectedChannelId, onSubmitAssign, }: { open: boolean; onOpenChange: (open: boolean) => void; title: string; setTitle: (v: string) => void; onSubmitCreate: () => void; createState: CreateState; isPending: boolean; assignableChannels: ChannelRow[]; selectedChannelId: string; setSelectedChannelId: (v: string) => void; onSubmitAssign: () => void; }) { const isCreating = createState.phase === "creating"; const hasAssignable = assignableChannels.length > 0; return ( Set Destination Channel Choose an existing channel or create a new private group. All accounts will write archives to this destination. {isCreating ? (

Creating Telegram group...

This may take a few seconds

) : ( Use Existing Create New {createState.phase === "error" && (

{createState.message}

)}

The selected channel will become the destination. All accounts will be linked as writers automatically.

{createState.phase === "error" && (

{createState.message}

)}
setTitle(e.target.value)} />

A new private Telegram group will be created using one of your authenticated accounts. You can rename it later in Telegram.

)}
); }