mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:51:14 -05:00
feat: Implement complete queue system
This commit is contained in:
170
src/components/moderation/ReassignDialog.tsx
Normal file
170
src/components/moderation/ReassignDialog.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { UserCog } from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
interface Moderator {
|
||||
user_id: string;
|
||||
username: string;
|
||||
display_name?: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
interface ReassignDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onReassign: (moderatorId: string) => Promise<void>;
|
||||
submissionType: string;
|
||||
}
|
||||
|
||||
export function ReassignDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onReassign,
|
||||
submissionType,
|
||||
}: ReassignDialogProps) {
|
||||
const [selectedModerator, setSelectedModerator] = useState<string>('');
|
||||
const [moderators, setModerators] = useState<Moderator[]>([]);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { toast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
fetchModerators();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const fetchModerators = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data: roles, error: rolesError } = await supabase
|
||||
.from('user_roles')
|
||||
.select('user_id, role')
|
||||
.in('role', ['moderator', 'admin', 'superuser']);
|
||||
|
||||
if (rolesError) throw rolesError;
|
||||
|
||||
if (!roles || roles.length === 0) {
|
||||
setModerators([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const userIds = roles.map((r) => r.user_id);
|
||||
|
||||
const { data: profiles, error: profilesError } = await supabase
|
||||
.from('profiles')
|
||||
.select('user_id, username, display_name')
|
||||
.in('user_id', userIds);
|
||||
|
||||
if (profilesError) throw profilesError;
|
||||
|
||||
const moderatorsList = roles.map((role) => {
|
||||
const profile = profiles?.find((p) => p.user_id === role.user_id);
|
||||
return {
|
||||
user_id: role.user_id,
|
||||
username: profile?.username || 'Unknown',
|
||||
display_name: profile?.display_name,
|
||||
role: role.role,
|
||||
};
|
||||
});
|
||||
|
||||
setModerators(moderatorsList);
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching moderators:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to load moderators list',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReassign = async () => {
|
||||
if (!selectedModerator) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await onReassign(selectedModerator);
|
||||
setSelectedModerator('');
|
||||
onOpenChange(false);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<UserCog className="h-5 w-5" />
|
||||
Reassign Submission
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Assign this {submissionType} to another moderator. They will receive a lock for 15 minutes.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="moderator">Select Moderator</Label>
|
||||
{loading ? (
|
||||
<div className="text-sm text-muted-foreground">Loading moderators...</div>
|
||||
) : moderators.length === 0 ? (
|
||||
<div className="text-sm text-muted-foreground">No moderators available</div>
|
||||
) : (
|
||||
<Select value={selectedModerator} onValueChange={setSelectedModerator}>
|
||||
<SelectTrigger id="moderator">
|
||||
<SelectValue placeholder="Choose a moderator" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{moderators.map((mod) => (
|
||||
<SelectItem key={mod.user_id} value={mod.user_id}>
|
||||
{mod.display_name || mod.username} ({mod.role})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleReassign}
|
||||
disabled={!selectedModerator || isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Reassigning...' : 'Reassign'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user