"use client"; // ============================================================================ // Create Placement Page // ============================================================================ import { useEffect, useState } from "react"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import { Loader2, ArrowLeft, Info, Calendar as CalendarIcon } from "lucide-react"; import { format } from "date-fns"; import { ru } from "date-fns/locale"; import { DashboardHeader } from "@/components/layout/dashboard-header"; import { useWorkspace } from "@/components/providers/workspace-provider"; import { placementsApi, creativesApi, channelsApi } 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Calendar } from "@/components/ui/calendar"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; import type { Creative, Channel, InviteLinkType } from "@/lib/types/api"; // ============================================================================ // Component // ============================================================================ export default function NewPlacementPage() { const params = useParams(); const router = useRouter(); const searchParams = useSearchParams(); const workspaceId = params?.workspaceId as string; const { currentProject, setSelectedProjects, projects } = useWorkspace(); // Pre-filled from URL params const prefilledChannelId = searchParams.get("channel_id"); const prefilledCreativeId = searchParams.get("creative_id"); // Form state const [channelId, setChannelId] = useState(prefilledChannelId || ""); const [creativeId, setCreativeId] = useState(prefilledCreativeId || ""); const [placementDate, setPlacementDate] = useState(new Date()); const [cost, setCost] = useState(""); const [comment, setComment] = useState(""); const [adPostUrl, setAdPostUrl] = useState(""); const [inviteLinkType, setInviteLinkType] = useState("approval"); // Data const [channels, setChannels] = useState([]); const [creatives, setCreatives] = useState([]); const [loadingData, setLoadingData] = useState(true); // Submit state const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); // Auto-select project when creative_id is provided useEffect(() => { if (!prefilledCreativeId || projects.length === 0) return; const loadCreativeAndSelectProject = async () => { try { const creative = await creativesApi.get(workspaceId, prefilledCreativeId); const project = projects.find(p => p.id === creative.project_id); if (project) { setSelectedProjects([project]); } } catch (err) { console.error("Failed to load creative:", err); } }; loadCreativeAndSelectProject(); }, [prefilledCreativeId, projects, workspaceId, setSelectedProjects]); // Load channels and creatives const loadData = async () => { try { setLoadingData(true); const [channelsRes, creativesRes] = await Promise.all([ channelsApi.list({ size: 100 }), currentProject ? creativesApi.list(workspaceId, { project_id: currentProject.id, include_archived: false, size: 100, }) : Promise.resolve({ items: [] }), ]); setChannels(channelsRes.items); setCreatives(creativesRes.items); } catch (err) { console.error("Failed to load data:", err); } finally { setLoadingData(false); } }; useEffect(() => { loadData(); }, [workspaceId, currentProject?.id]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!currentProject) { setError("Выберите проект в верхнем меню"); return; } if (!channelId || !creativeId) { setError("Выберите канал и креатив"); return; } try { setIsSubmitting(true); setError(null); const placement = await placementsApi.create(workspaceId, { project_id: currentProject.id, placement_channel_id: channelId, creative_id: creativeId, placement_date: placementDate.toISOString(), cost: cost ? parseFloat(cost) : undefined, comment: comment || undefined, ad_post_url: adPostUrl || undefined, invite_link_type: inviteLinkType, }); // Redirect to placements list with project filter router.push(`/dashboard/${workspaceId}/placements?project_id=${currentProject.id}`); } catch (err: any) { setError(err?.detail || "Не удалось создать размещение"); } finally { setIsSubmitting(false); } }; return ( <>

Новое размещение

Создайте размещение рекламы в Telegram канале

{!currentProject && ( Выберите проект в верхнем меню для создания размещения )} {loadingData ? (
) : (
Параметры размещения Укажите канал, креатив и условия размещения {/* Channel */}
{/* Creative */}
{creatives.length === 0 && currentProject && (

Сначала{" "} создайте креатив

)}
{/* Placement Date */}
date && setPlacementDate(date)} locale={ru} />
{/* Cost */}
setCost(e.target.value)} disabled={isSubmitting} />
{/* Invite Link Type */}
{/* Ad Post URL */}
setAdPostUrl(e.target.value)} disabled={isSubmitting} />

Ссылка на пост после публикации (можно добавить позже)

{/* Comment */}