diff --git a/src/components/versioning/RollbackDialog.tsx b/src/components/versioning/RollbackDialog.tsx index 26c19719..bb87dada 100644 --- a/src/components/versioning/RollbackDialog.tsx +++ b/src/components/versioning/RollbackDialog.tsx @@ -1,30 +1,70 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; -import { AlertTriangle } from 'lucide-react'; +import { AlertTriangle, Plus, Minus, Edit } from 'lucide-react'; import { Alert, AlertDescription } from '@/components/ui/alert'; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Badge } from '@/components/ui/badge'; +import { Separator } from '@/components/ui/separator'; +import { useEntityVersions } from '@/hooks/useEntityVersions'; +import type { EntityType } from '@/types/versioning'; interface RollbackDialogProps { open: boolean; onOpenChange: (open: boolean) => void; versionId: string; - entityType: string; + entityType: EntityType; entityId: string; entityName: string; onRollback: (reason: string) => Promise; } +interface VersionDiff { + [fieldName: string]: { + from: unknown; + to: unknown; + }; +} + export function RollbackDialog({ open, onOpenChange, versionId, + entityType, + entityId, entityName, onRollback, }: RollbackDialogProps) { const [reason, setReason] = useState(''); const [loading, setLoading] = useState(false); + const [diff, setDiff] = useState(null); + const [diffLoading, setDiffLoading] = useState(false); + + const { versions, compareVersions } = useEntityVersions(entityType, entityId); + const currentVersion = versions[0]; // Most recent version + + // Fetch diff when dialog opens + useEffect(() => { + const loadDiff = async () => { + if (open && versionId && currentVersion?.version_id) { + setDiffLoading(true); + try { + const result = await compareVersions(versionId, currentVersion.version_id); + setDiff(result as VersionDiff); + } catch (error) { + console.error('Failed to load diff:', error); + setDiff(null); + } finally { + setDiffLoading(false); + } + } + }; + + loadDiff(); + }, [open, versionId, currentVersion?.version_id, compareVersions]); const handleRollback = async () => { if (!reason.trim()) return; @@ -33,15 +73,32 @@ export function RollbackDialog({ try { await onRollback(reason); setReason(''); + setDiff(null); onOpenChange(false); } finally { setLoading(false); } }; + const formatValue = (value: any): string => { + if (value === null || value === undefined) return 'null'; + if (typeof value === 'boolean') return value ? 'true' : 'false'; + if (typeof value === 'object') return JSON.stringify(value, null, 2); + return String(value); + }; + + const formatFieldName = (fieldName: string): string => { + return fieldName + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + }; + + const changedFieldCount = diff ? Object.keys(diff).length : 0; + return ( - + Restore Previous Version (Moderator Action) @@ -56,6 +113,100 @@ export function RollbackDialog({ + {/* Preview Changes Section */} + + + +
+ Preview Changes + {changedFieldCount > 0 && ( + + {changedFieldCount} field{changedFieldCount !== 1 ? 's' : ''} will change + + )} +
+
+ + + {diffLoading ? ( +
+
+
+ ) : diff && Object.keys(diff).length > 0 ? ( +
+ {Object.entries(diff).map(([fieldName, changes]: [string, any]) => { + const isAdded = changes.from === null; + const isRemoved = changes.to === null; + const isModified = changes.from !== null && changes.to !== null; + + return ( +
+
+ {isAdded && } + {isRemoved && } + {isModified && } + {formatFieldName(fieldName)} + + {isAdded ? 'Added' : isRemoved ? 'Removed' : 'Modified'} + +
+ + + +
+ {/* Current value (will be replaced) */} +
+
Current
+
+ {formatValue(changes.to)} +
+
+ + {/* Restored value */} +
+
After Restore
+
+ {formatValue(changes.from)} +
+
+
+
+ ); + })} +
+ ) : ( +
+

No differences found

+
+ )} + + + + +