'use client'; import React, { useState, useTransition } from 'react'; import { useRouter } from 'next/navigation'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Check, X, Link2, ExternalLink, User, Clock, Tv, Search, Filter } from 'lucide-react'; import { approveProposal, rejectProposal } from '@/features/moderation/actions/handle-proposal'; import { toast } from 'sonner'; import { formatDistanceToNow } from 'date-fns'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Input } from '@/components/ui/input'; interface Proposal { id: string; productName: string; productUrl: string; affiliateUrl: string; submitterName: string | null; submitterEmail: string | null; note: string | null; status: 'PENDING' | 'APPROVED' | 'REJECTED'; createdAt: Date; video: { title: string; channel: { channelName: string; }; }; } interface AdminProposalListProps { proposals: Proposal[]; forcedStatus?: string; hideFilters?: boolean; } export function AdminProposalList({ proposals: initialProposals, forcedStatus, hideFilters }: AdminProposalListProps) { const router = useRouter(); const [proposals, setProposals] = useState(initialProposals); const [isPending, startTransition] = useTransition(); const [filterStatus, setFilterStatus] = useState(forcedStatus || 'PENDING'); const [searchQuery, setSearchQuery] = useState(''); const handleApprove = (id: string) => { startTransition(async () => { const result = await approveProposal(id); if (result.success) { setProposals(prev => prev.map(p => p.id === id ? { ...p, status: 'APPROVED' } : p)); toast.success('Proposal approved and added to Vault!'); router.refresh(); } else { toast.error(result.error || 'Failed to approve'); } }); }; const handleReject = (id: string) => { startTransition(async () => { const result = await rejectProposal(id); if (result.success) { setProposals(prev => prev.map(p => p.id === id ? { ...p, status: 'REJECTED' } : p)); toast.success('Proposal rejected'); router.refresh(); } else { toast.error(result.error || 'Failed to reject'); } }); }; // Update internal status if forcedStatus changes React.useEffect(() => { if (forcedStatus) { setFilterStatus(forcedStatus); } }, [forcedStatus]); const filteredProposals = proposals.filter(p => { if (p.status !== filterStatus) return false; if (searchQuery) { const query = searchQuery.toLowerCase(); return ( p.productName.toLowerCase().includes(query) || (p.submitterName?.toLowerCase().includes(query) ?? false) || (p.submitterEmail?.toLowerCase().includes(query) ?? false) || (p.video.title.toLowerCase().includes(query)) || (p.video.channel.channelName.toLowerCase().includes(query)) ); } return true; }); const stats = { pending: proposals.filter(p => p.status === 'PENDING').length, approved: proposals.filter(p => p.status === 'APPROVED').length, rejected: proposals.filter(p => p.status === 'REJECTED').length, }; return (
{/* Filter Bar */} {!hideFilters && (
setSearchQuery(e.target.value)} className="pl-9 h-9 bg-background/50" />
)}
{filteredProposals.length > 0 ? ( filteredProposals.map((proposal) => (
{proposal.productName} {proposal.status}
{proposal.video.channel.channelName}

Submitted {formatDistanceToNow(new Date(proposal.createdAt))} ago • {proposal.video.title}

{proposal.status === 'PENDING' && (
)}
Original Link View Product Page
Affiliate Link
{proposal.affiliateUrl}
Submitter Details
{proposal.submitterName || 'Anonymous'} {proposal.submitterEmail && ( ({proposal.submitterEmail}) )}
{proposal.note && (
Note

"{proposal.note}"

)}
)) ) : (

No proposals found

Try adjusting your filters or search query.

)}
); }