File size: 7,669 Bytes
304bdf5
 
 
 
 
 
 
 
 
5480d48
304bdf5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5480d48
 
 
304bdf5
 
 
 
 
 
 
 
 
 
5480d48
304bdf5
 
 
5480d48
304bdf5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { getAnalysisQueue } from "@/features/admin/actions/analysis";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Video, Clock, CheckCircle2, AlertCircle, Play, ArrowRight } from "lucide-react";
import Link from "next/link";
import { formatDistanceToNow } from "date-fns";

export const metadata = {
    title: "Video Hub | Vault Admin",
};

export default async function AnalysisQueuePage(props: {
    searchParams: Promise<{ status?: string }>;
}) {
    const searchParams = await props.searchParams;
    const status = searchParams.status;
    const queue = await getAnalysisQueue(status);

    const getStatusColor = (status: string) => {
        switch (status) {
            case 'pending_analysis': return 'bg-amber-500/10 text-amber-500 border-amber-500/20';
            case 'in_progress': return 'bg-blue-500/10 text-blue-500 border-blue-500/20';
            case 'awaiting_approval': return 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20';
            default: return 'bg-slate-500/10 text-slate-500 border-slate-500/20';
        }
    };

    const getStatusLabel = (status: string) => {
        switch (status) {
            case 'pending_analysis': return 'In Intake';
            case 'in_progress': return 'In Pipeline';
            case 'awaiting_approval': return 'Ready for Review';
            default: return status;
        }
    };

    return (
        <div className="space-y-6">
            <div className="flex flex-col gap-2">
                <div className="flex items-center gap-2 text-xs text-muted-foreground mb-1">
                    <Link href="/admin/dashboard" className="hover:text-primary transition-colors">Dashboard</Link>
                    <span>/</span>
                    <span className="text-foreground font-medium">Video Hub</span>
                </div>
                <div className="flex items-center justify-between">
                    <div>
                        <h1 className="text-3xl font-bold tracking-tight">Video Hub</h1>
                        <p className="text-muted-foreground">Manage video content submitted for AI product discovery.</p>
                    </div>
                    {status && (
                        <Badge variant="outline" className="h-8 gap-2">
                            <span className="w-2 h-2 rounded-full bg-primary animate-pulse" />
                            Filtering by: {getStatusLabel(status)}
                            <Button variant="ghost" size="icon" className="h-4 w-4 ml-2 p-0" asChild>
                                <Link href="/admin/analysis-queue">×</Link>
                            </Button>
                        </Badge>
                    )}
                </div>
            </div>

            <div className="grid gap-4">
                {queue.length === 0 ? (
                    <Card className="flex flex-col items-center justify-center py-12 text-center">
                        <CheckCircle2 className="h-12 w-12 text-muted-foreground mb-4 opacity-20" />
                        <CardTitle>Queue is empty</CardTitle>
                        <CardDescription>No videos currently require analysis review.</CardDescription>
                    </Card>
                ) : (
                    queue.map((video) => (
                        <Card key={video.id} className="overflow-hidden bg-card/40 backdrop-blur-md border-border/40 hover:border-primary/50 transition-colors">
                            <div className="flex flex-col md:flex-row">
                                <div className="relative w-full md:w-48 aspect-video md:aspect-auto">
                                    <img
                                        src={video.thumbnailUrl || ''}
                                        alt={video.title}
                                        className="absolute inset-0 w-full h-full object-cover"
                                    />
                                    {video.scanStatus === 'in_progress' && (
                                        <div className="absolute inset-0 bg-background/60 backdrop-blur-sm flex items-center justify-center">
                                            <div className="flex flex-col items-center gap-2">
                                                <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-primary"></div>
                                                <span className="text-xs font-medium">Analyzing...</span>
                                            </div>
                                        </div>
                                    )}
                                </div>
                                <div className="flex-1 p-6 flex flex-col gap-4">
                                    <div className="flex flex-col gap-1">
                                        <div className="flex items-center justify-between gap-4">
                                            <h3 className="font-semibold text-lg line-clamp-1">{video.title}</h3>
                                            <Badge variant="outline" className={getStatusColor(video.scanStatus)}>
                                                {getStatusLabel(video.scanStatus)}
                                            </Badge>
                                        </div>
                                        <div className="flex items-center gap-2 text-sm text-muted-foreground">
                                            <span>{video.channel?.channelName}</span>
                                            <span></span>
                                            <span>{formatDistanceToNow(new Date(video.createdAt))} ago</span>
                                        </div>
                                    </div>

                                    <div className="mt-auto flex items-center justify-between gap-4">
                                        <div className="flex items-center gap-4 text-xs">
                                            <div className="flex items-center gap-1.5">
                                                <Video className="h-3.5 w-3.5 opacity-50" />
                                                <span>{video.platform}</span>
                                            </div>
                                            {video.scanStatus === 'awaiting_approval' && (
                                                <div className="flex items-center gap-1.5 text-emerald-500 font-medium">
                                                    <AlertCircle className="h-3.5 w-3.5" />
                                                    <span>Ready for Approval</span>
                                                </div>
                                            )}
                                        </div>

                                        <Button size="sm" asChild>
                                            <Link href={`/admin/analysis/${video.id}`}>
                                                {video.scanStatus === 'awaiting_approval' ? 'Review Results' : 'Start Review'}
                                                <ArrowRight className="ml-2 h-4 w-4" />
                                            </Link>
                                        </Button>
                                    </div>
                                </div>
                            </div>
                        </Card>
                    ))
                )}
            </div>
        </div>
    );
}