diff --git a/next.config.ts b/next.config.ts index b9be33d..101586f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -11,7 +11,7 @@ const nextConfig: NextConfig = { return [ { source: "/api/backend/:path*", - destination: "http://localhost:3001/api/:path*", + destination: "http://localhost:3000/api/:path*", }, ]; }, diff --git a/src/components/content/ContentTable.tsx b/src/components/content/ContentTable.tsx index 313710c..41fd275 100644 --- a/src/components/content/ContentTable.tsx +++ b/src/components/content/ContentTable.tsx @@ -1,18 +1,14 @@ "use client"; import { Box, Table, Badge, HStack, IconButton } from "@chakra-ui/react"; -import { LuEye, LuPencil, LuTrash2 } from "react-icons/lu"; +import { useState, useEffect } from "react"; +import { useSession } from "next-auth/react"; +import { LuEye, LuPencil, LuTrash2, LuRefreshCw } from "react-icons/lu"; import { toaster } from "@/components/ui/feedback/toaster"; -import { useState } from "react"; import { ContentPreviewDialog } from "./ContentPreviewDialog"; -const MOCK_CONTENT = [ - { id: 1, title: "The Future of AI in Marketing", platform: "LinkedIn", status: "published", date: "2024-03-10" }, - { id: 2, title: "5 Tips for Better Sleep", platform: "Twitter", status: "draft", date: "2024-03-12" }, - { id: 3, title: "Product Launch Announcement", platform: "Instagram", status: "scheduled", date: "2024-03-15" }, - { id: 4, title: "Weekly Tech Roundup", platform: "LinkedIn", status: "review", date: "2024-03-18" }, - { id: 5, title: "Customer Success Story", platform: "Blog", status: "draft", date: "2024-03-20" }, -]; + + const getStatusColor = (status: string) => { switch (status) { @@ -25,8 +21,36 @@ const getStatusColor = (status: string) => { }; export function ContentTable() { + const { data: session } = useSession(); const [selectedItem, setSelectedItem] = useState(null); const [isPreviewOpen, setIsPreviewOpen] = useState(false); + const [contentList, setContentList] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const fetchContent = async () => { + if (!session?.accessToken) return; + setIsLoading(true); + try { + const res = await fetch('/api/backend/content', { + headers: { + 'Authorization': `Bearer ${session.accessToken}` + } + }); + if (res.ok) { + const data = await res.json(); + setContentList(Array.isArray(data) ? data : []); + } + } catch (error) { + console.error("Failed to fetch content:", error); + } finally { + setIsLoading(false); + } + }; + + fetchContent(); + }, [session]); + const handleAction = (action: string, item: any) => { if (action === 'View') { @@ -56,16 +80,31 @@ export function ContentTable() { - {MOCK_CONTENT.map((item) => ( + {isLoading ? ( + + + + + Loading content... + + + + ) : contentList.length === 0 ? ( + + + No content found. Start by generating some! + + + ) : contentList.map((item) => ( {item.title} - {item.platform} + {item.type || item.platform} {item.status} - {item.date} + {new Date(item.createdAt || item.date).toLocaleDateString()} + ([]); const [isGenerating, setIsGenerating] = useState(false); + const [generationStage, setGenerationStage] = useState(""); + const [generationProgress, setGenerationProgress] = useState(0); + const router = useRouter(); + const [niches, setNiches] = useState([]); const [isLoadingNiches, setIsLoadingNiches] = useState(false); @@ -133,19 +140,36 @@ export function GenerateWizard() { const handleGenerate = async () => { setIsGenerating(true); + setGeneratedBundle(null); + setGenerationProgress(10); + setGenerationStage("Researching topic and niche..."); + try { const payload = { topic, description: trendDescription || undefined, keywords: trendKeywords.length > 0 ? trendKeywords : undefined, - niche: selectedNiche, // The service expects 'niche' as string ID + niche: selectedNiche, platforms: selectedPlatforms, includeResearch: true, includeHashtags: true, - brandVoice: "friendly-expert", // Default for now, can be added to UI + brandVoice: "friendly-expert", count: 1 }; + // Simulated progress stages since the backend is a single call + const progressInterval = setInterval(() => { + setGenerationProgress(prev => { + if (prev >= 90) { + clearInterval(progressInterval); + return 90; + } + if (prev >= 60) setGenerationStage("Finalizing content and SEO..."); + else if (prev >= 30) setGenerationStage("Generating platform-specific posts..."); + return prev + 5; + }); + }, 800); + const headers: HeadersInit = { 'Content-Type': 'application/json' }; if (session?.accessToken) { headers['Authorization'] = `Bearer ${session.accessToken}`; @@ -157,29 +181,47 @@ export function GenerateWizard() { body: JSON.stringify(payload), }); + clearInterval(progressInterval); + if (!response.ok) throw new Error("Generation failed"); - const data = await response.json(); - setGeneratedBundle(data); + setGenerationProgress(95); + setGenerationStage("Saving to library..."); - toaster.create({ - title: "Content Generated", - description: "Your content is ready!", - type: "success" - }); + const data = await response.json(); + + setGenerationProgress(100); + setGenerationStage("Success!"); + + // Give a small delay to show 100% + setTimeout(() => { + setGeneratedBundle(data); + setIsGenerating(false); + + toaster.create({ + title: "Content Generated", + description: "Your content is ready and saved to library!", + type: "success" + }); + + // Auto-redirect after success + if (data.masterContentId) { + router.push(`/[locale]/content?id=${data.masterContentId}`); + } + }, 500); } catch (error) { console.error(error); + setIsGenerating(false); toaster.create({ title: "Error", description: "Failed to generate content. Please try again.", type: "error" }); - } finally { - setIsGenerating(false); } }; + if (generatedBundle) { return { setGeneratedBundle(null); @@ -204,7 +246,26 @@ export function GenerateWizard() { - {activeStep === 0 && ( + {isGenerating ? ( + + + + {generationStage} + + + + + + + {generationProgress}% Complete + + + Our AI is analyzing the topic, researching facts, and crafting perfect posts for your platforms. + + + ) : activeStep === 0 && ( + + 1. Start with a Topic