mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-16 03:39:15 +02:00
feat(reels): add real-time generation progress tracking on dashboard (desktop & mobile)
This commit is contained in:
1 parent
11b5ef0ad2
commit
e1c6b51e0a
7 files changed
+209
-6
No files matched your search
@@ -0,0 +1,109 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Clapperboard, Loader2, CheckCircle2, XCircle } from "lucide-react";
|
||||
import type { Post } from "@shared/schema";
|
||||
|
||||
/** Labels for each progress stage */
|
||||
function getStageLabel(progress: number): string {
|
||||
if (progress <= 5) return "Démarrage du traitement…";
|
||||
if (progress <= 15) return "Traitement vidéo (FFmpeg)…";
|
||||
if (progress <= 65) return "Encodage en cours…";
|
||||
if (progress <= 85) return "Upload vers le cloud…";
|
||||
if (progress <= 90) return "Liaison du média…";
|
||||
if (progress <= 95) return "Publication en cours…";
|
||||
return "Finalisation…";
|
||||
}
|
||||
|
||||
interface OngoingReelsProps {
|
||||
/** Compact layout for mobile */
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export default function OngoingReels({ compact = false }: OngoingReelsProps) {
|
||||
const { data: ongoingPosts = [] } = useQuery<Post[]>({
|
||||
queryKey: ["/api/reels/ongoing"],
|
||||
refetchInterval: 3000, // Poll every 3 seconds
|
||||
});
|
||||
|
||||
// Nothing to show — render nothing (not even a card)
|
||||
if (ongoingPosts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={`rounded-2xl border-border/50 shadow-lg ${compact ? "" : ""
|
||||
}`}
|
||||
data-testid="card-ongoing-reels"
|
||||
>
|
||||
<CardHeader className={compact ? "pb-2 px-4 pt-4" : ""}>
|
||||
<CardTitle className={`flex items-center gap-2 ${compact ? "text-base" : ""}`}>
|
||||
<Loader2 className="w-5 h-5 animate-spin text-primary" />
|
||||
Génération en cours
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className={compact ? "px-4 pb-4 pt-0" : ""}>
|
||||
<div className={`space-y-${compact ? "3" : "4"}`}>
|
||||
{ongoingPosts.map((post) => {
|
||||
const progress = post.generationProgress ?? 0;
|
||||
const isFailed = post.generationStatus === "failed";
|
||||
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
className={`flex items-start gap-3 p-${compact ? "3" : "4"} rounded-lg border border-border/50 bg-muted/30`}
|
||||
data-testid={`ongoing-reel-${post.id}`}
|
||||
>
|
||||
{/* Icon */}
|
||||
<div className="w-9 h-9 rounded-lg bg-gradient-to-br from-violet-500 to-fuchsia-500 flex items-center justify-center flex-shrink-0">
|
||||
<Clapperboard className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className={`font-medium truncate ${compact ? "text-sm" : "text-sm"}`}>
|
||||
{post.content || "Reel sans texte"}
|
||||
</p>
|
||||
|
||||
{isFailed ? (
|
||||
<div className="flex items-center gap-1.5 mt-2 text-destructive">
|
||||
<XCircle className="w-4 h-4" />
|
||||
<span className="text-xs">
|
||||
{post.generationError || "Erreur lors du traitement"}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="mt-2">
|
||||
<Progress
|
||||
value={progress}
|
||||
className="h-2"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-1.5">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{getStageLabel(progress)}
|
||||
</span>
|
||||
<span className="text-xs font-medium text-primary">
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{progress >= 100 && !isFailed && (
|
||||
<div className="flex items-center gap-1.5 mt-2 text-green-500">
|
||||
<CheckCircle2 className="w-4 h-4" />
|
||||
<span className="text-xs font-medium">Terminé</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import Sidebar from "@/components/sidebar";
|
||||
import TopBar from "@/components/topbar";
|
||||
import StatsCards from "@/components/stats-cards";
|
||||
import OngoingReels from "@/components/ongoing-reels";
|
||||
import RecentPublications from "@/components/recent-publications";
|
||||
import ManagedPages from "@/components/managed-pages";
|
||||
import { useState } from "react";
|
||||
@@ -11,12 +12,12 @@ export default function Dashboard() {
|
||||
return (
|
||||
<div className="flex h-screen overflow-hidden bg-background">
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
<div className={`
|
||||
fixed lg:static inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
@@ -26,12 +27,14 @@ export default function Dashboard() {
|
||||
|
||||
<main className="flex-1 overflow-y-auto">
|
||||
<TopBar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
|
||||
|
||||
|
||||
<div className="p-4 sm:p-6 lg:p-8 space-y-6 sm:space-y-8 max-w-[1600px] mx-auto">
|
||||
<StatsCards />
|
||||
|
||||
|
||||
<OngoingReels />
|
||||
|
||||
<RecentPublications />
|
||||
|
||||
|
||||
<ManagedPages />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Sidebar from "@/components/sidebar";
|
||||
import TopBar from "@/components/topbar";
|
||||
import StatsCards from "@/components/stats-cards";
|
||||
import OngoingReels from "@/components/ongoing-reels";
|
||||
import RecentPublications from "@/components/recent-publications";
|
||||
import ManagedPages from "@/components/managed-pages";
|
||||
import { useState } from "react";
|
||||
@@ -31,6 +32,8 @@ export default function DashboardMobile() {
|
||||
<div className="p-4 space-y-6">
|
||||
<StatsCards />
|
||||
|
||||
<OngoingReels compact />
|
||||
|
||||
<RecentPublications />
|
||||
|
||||
<ManagedPages />
|
||||
|
||||
Reference in new issue
Block a user