Spaces:
Runtime error
Runtime error
feat: Implement admin management for product requests and affiliate proposals, including new database migrations and expanded dashboard statistics.
7bb5991 | "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> | |
| ); | |
| } | |