Files
thrilltrack-explorer/src-old/components/moderation/ConflictResolutionModal.tsx

158 lines
5.6 KiB
TypeScript

import { useState } from 'react';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogFooter,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { AlertTriangle, User, Clock } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { format } from 'date-fns';
import type { ConflictCheckResult } from '@/lib/submissionItemsService';
interface ConflictResolutionModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
conflictData: ConflictCheckResult;
onResolve: (resolution: 'keep-mine' | 'keep-theirs' | 'reload') => Promise<void>;
}
export function ConflictResolutionModal({
open,
onOpenChange,
conflictData,
onResolve,
}: ConflictResolutionModalProps) {
const [selectedResolution, setSelectedResolution] = useState<string | null>(null);
const [isResolving, setIsResolving] = useState(false);
const { toast } = useToast();
const handleResolve = async () => {
if (!selectedResolution) return;
setIsResolving(true);
try {
await onResolve(selectedResolution as 'keep-mine' | 'keep-theirs' | 'reload');
toast({
title: 'Conflict Resolved',
description: 'Changes have been applied successfully',
});
onOpenChange(false);
} catch (error) {
toast({
title: 'Resolution Failed',
description: error instanceof Error ? error.message : 'Failed to resolve conflict',
variant: 'destructive',
});
} finally {
setIsResolving(false);
}
};
if (!conflictData.serverVersion) return null;
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-destructive" />
Edit Conflict Detected
</DialogTitle>
<DialogDescription>
Someone else modified this submission while you were editing.
Choose how to resolve the conflict.
</DialogDescription>
</DialogHeader>
<Alert className="border-destructive/50 bg-destructive/10">
<AlertDescription className="flex items-center gap-4">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<User className="h-4 w-4" />
<span className="font-medium">
Modified by: {conflictData.serverVersion.modified_by_profile?.display_name ||
conflictData.serverVersion.modified_by_profile?.username || 'Unknown'}
</span>
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Clock className="h-3 w-3" />
{format(new Date(conflictData.serverVersion.last_modified_at), 'PPpp')}
</div>
</div>
</AlertDescription>
</Alert>
<div className="space-y-3 py-4">
<h4 className="text-sm font-medium">Choose Resolution:</h4>
<Button
variant={selectedResolution === 'keep-mine' ? 'default' : 'outline'}
className="w-full justify-start text-left h-auto py-3"
onClick={() => setSelectedResolution('keep-mine')}
>
<div className="flex-1">
<div className="font-medium">Keep My Changes</div>
<div className="text-xs text-muted-foreground mt-1">
Overwrite their changes with your edits (use with caution)
</div>
</div>
{selectedResolution === 'keep-mine' && (
<Badge variant="default" className="ml-2">Selected</Badge>
)}
</Button>
<Button
variant={selectedResolution === 'keep-theirs' ? 'default' : 'outline'}
className="w-full justify-start text-left h-auto py-3"
onClick={() => setSelectedResolution('keep-theirs')}
>
<div className="flex-1">
<div className="font-medium">Keep Their Changes</div>
<div className="text-xs text-muted-foreground mt-1">
Discard your changes and accept the latest version
</div>
</div>
{selectedResolution === 'keep-theirs' && (
<Badge variant="default" className="ml-2">Selected</Badge>
)}
</Button>
<Button
variant={selectedResolution === 'reload' ? 'default' : 'outline'}
className="w-full justify-start text-left h-auto py-3"
onClick={() => setSelectedResolution('reload')}
>
<div className="flex-1">
<div className="font-medium">Reload and Review</div>
<div className="text-xs text-muted-foreground mt-1">
Load the latest version to review changes before deciding
</div>
</div>
{selectedResolution === 'reload' && (
<Badge variant="default" className="ml-2">Selected</Badge>
)}
</Button>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isResolving}>
Cancel
</Button>
<Button
onClick={handleResolve}
disabled={!selectedResolution || isResolving}
>
{isResolving ? 'Resolving...' : 'Apply Resolution'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}