vault-video-processor / src /features /moderation /components /admin-moderation-queue.tsx
dvijaykrishnan's picture
feat: Implement admin management for product requests and affiliate proposals, including new database migrations and expanded dashboard statistics.
7bb5991
Raw
History Blame Contribute Delete
5.96 kB
"use client";
import React, { useState } from "react";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { ShieldAlert, Filter, Search, RotateCcw } from "lucide-react";
import { AdminDetectionCard } from "./admin-detection-card";
import { AdminCorrectionModal } from "./admin-correction-modal";
import { AdminMarkIncorrectModal } from "./admin-mark-incorrect-modal";
interface AdminDetection {
detection: {
id: string;
objectName: string;
category: string;
confidenceScore: number;
status: string;
frameTimestamp: number;
createdAt: Date;
};
video: {
id: string;
title: string;
thumbnailUrl: string;
};
channel: {
creatorId: string;
channelName: string;
};
marketplaceMatches: any[];
pledgeCount?: number;
}
interface AdminModerationQueueProps {
initialDetections: AdminDetection[];
initialMinPledges?: number;
initialCategory?: string;
initialMinConfidence?: number;
}
export function AdminModerationQueue({
initialDetections,
initialMinPledges = 0,
initialCategory = "all",
initialMinConfidence = 0
}: AdminModerationQueueProps) {
const [detections, setDetections] = useState(initialDetections);
const [filterCategory, setFilterCategory] = useState<string>(initialCategory);
const [minConfidence, setMinConfidence] = useState(initialMinConfidence);
const [minPledges, setMinPledges] = useState(initialMinPledges);
const filteredDetections = detections.filter(d =>
(filterCategory === "all" || d.detection.category === filterCategory) &&
d.detection.confidenceScore >= minConfidence &&
(d.pledgeCount || 0) >= minPledges
);
const categories = Array.from(new Set(initialDetections.map(d => d.detection.category)));
const removeDetection = (id: string) => {
setDetections(prev => prev.filter(d => d.detection.id !== id));
};
return (
<div className="space-y-6">
{/* Filters Bar */}
<div className="flex flex-col md:flex-row gap-4 items-end justify-between bg-card/50 p-4 rounded-lg border border-border/50 backdrop-blur-sm">
<div className="flex flex-wrap gap-4 items-center">
<div className="space-y-1">
<Label>Category</Label>
<Select value={filterCategory} onValueChange={setFilterCategory}>
<SelectTrigger className="w-[160px]">
<SelectValue placeholder="All Categories" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Categories</SelectItem>
{categories.map(cat => (
<SelectItem key={cat} value={cat}>{cat}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-1">
<Label>Min Confidence ({Math.round(minConfidence * 100)}%)</Label>
<Input
type="range"
min="0"
max="1"
step="0.05"
value={minConfidence}
onChange={(e) => setMinConfidence(parseFloat(e.target.value))}
className="w-[150px]"
/>
</div>
<div className="space-y-1">
<Label>Min Pledges ({minPledges})</Label>
<Input
type="range"
min="0"
max="20"
step="1"
value={minPledges}
onChange={(e) => setMinPledges(parseInt(e.target.value))}
className="w-[150px] accent-emerald-500"
/>
</div>
</div>
<Button variant="outline" size="sm" onClick={() => { setFilterCategory("all"); setMinConfidence(0); setMinPledges(0); }}>
<RotateCcw className="w-4 h-4 mr-2" /> Reset
</Button>
</div>
{/* Grid */}
{filteredDetections.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{filteredDetections.map((d) => (
<AdminDetectionCard
key={d.detection.id}
detection={d}
onActionSuccess={() => removeDetection(d.detection.id)}
/>
))}
</div>
) : (
<div className="col-span-full py-20 text-center space-y-4 bg-muted/10 rounded-xl border-2 border-dashed border-border/40">
<div className="flex justify-center">
<ShieldAlert className="w-12 h-12 text-muted-foreground opacity-20" />
</div>
<div className="space-y-2">
<h3 className="text-xl font-semibold text-muted-foreground">Admin Queue Clear</h3>
<p className="text-muted-foreground max-w-xs mx-auto">No flagged items found with current filters. Good job on the quality control!</p>
</div>
</div>
)}
</div>
);
}