Fix build errors and complete transformation

This commit is contained in:
gpt-engineer-app[bot]
2025-10-15 18:20:47 +00:00
parent f58d7d6014
commit fc752bcd54
3 changed files with 132 additions and 225 deletions

View File

@@ -1,36 +1,23 @@
import { useEffect, useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Clock, Plus, Minus, Edit } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
import { supabase } from '@/integrations/supabase/client';
import { AlertCircle } from 'lucide-react';
import { Alert, AlertDescription } from '@/components/ui/alert';
import type { EntityType } from '@/types/versioning';
interface FieldHistoryDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
entityType: string;
entityType: EntityType;
entityId: string;
fieldName: string;
}
interface FieldChange {
id: string;
field_name: string;
old_value: any;
new_value: any;
change_type: 'added' | 'modified' | 'removed';
created_at: string;
version: {
version_number: number;
changed_by: string;
changed_at: string;
changer_profile: {
username: string;
};
};
}
/**
* Field-level history has been removed in the relational versioning system.
* Use version comparison instead to see field changes between versions.
* This component now shows a helpful message directing users to use version comparison.
*/
export function FieldHistoryDialog({
open,
onOpenChange,
@@ -38,72 +25,6 @@ export function FieldHistoryDialog({
entityId,
fieldName,
}: FieldHistoryDialogProps) {
const [changes, setChanges] = useState<FieldChange[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchFieldHistory = async () => {
if (!open) return;
setLoading(true);
try {
// Get all versions for this entity
const { data: versions, error: versionsError } = await supabase
.from('entity_versions')
.select('id, version_number, changed_by, changed_at')
.eq('entity_type', entityType)
.eq('entity_id', entityId)
.order('version_number', { ascending: false });
if (versionsError) throw versionsError;
// Get profiles
const userIds = [...new Set(versions?.map(v => v.changed_by).filter(Boolean) || [])];
const { data: profiles } = await supabase
.from('profiles')
.select('user_id, username')
.in('user_id', userIds);
// Get field history for all these versions
const versionIds = versions?.map(v => v.id) || [];
const { data: fieldHistory, error: historyError } = await supabase
.from('entity_field_history')
.select('*')
.eq('field_name', fieldName)
.in('version_id', versionIds)
.order('created_at', { ascending: false });
if (historyError) throw historyError;
// Merge the data
const mergedData = fieldHistory?.map(change => ({
...change,
version: {
...versions?.find(v => v.id === change.version_id),
changer_profile: profiles?.find(p => p.user_id === versions?.find(v => v.id === change.version_id)?.changed_by)
},
})) || [];
setChanges(mergedData as FieldChange[]);
} catch (error) {
console.error('Error fetching field history:', error);
setChanges([]);
} finally {
setLoading(false);
}
};
fetchFieldHistory();
}, [open, entityType, entityId, fieldName]);
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 = (name: string): string => {
return name
.split('_')
@@ -111,106 +32,25 @@ export function FieldHistoryDialog({
.join(' ');
};
const getChangeIcon = (type: string) => {
switch (type) {
case 'added':
return <Plus className="h-4 w-4 text-green-500" />;
case 'removed':
return <Minus className="h-4 w-4 text-red-500" />;
case 'modified':
return <Edit className="h-4 w-4 text-blue-500" />;
default:
return null;
}
};
const getChangeBadgeColor = (type: string) => {
switch (type) {
case 'added':
return 'bg-green-500/10 text-green-700 dark:text-green-400';
case 'removed':
return 'bg-red-500/10 text-red-700 dark:text-red-400';
case 'modified':
return 'bg-blue-500/10 text-blue-700 dark:text-blue-400';
default:
return '';
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Field History: {formatFieldName(fieldName)}</DialogTitle>
</DialogHeader>
<ScrollArea className="h-[500px] pr-4">
{loading ? (
<div className="flex items-center justify-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary" />
</div>
) : changes.length > 0 ? (
<div className="space-y-4">
{changes.map((change) => (
<div key={change.id} className="border rounded-lg p-4 space-y-3">
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{getChangeIcon(change.change_type)}
<Badge variant="outline" className={getChangeBadgeColor(change.change_type)}>
{change.change_type}
</Badge>
<span className="text-sm text-muted-foreground">
Version {change.version?.version_number}
</span>
</div>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span>{change.version?.changer_profile?.username}</span>
<Clock className="h-3 w-3" />
<span>
{formatDistanceToNow(new Date(change.created_at), { addSuffix: true })}
</span>
</div>
</div>
{/* Values */}
<div className="grid grid-cols-2 gap-4">
{change.old_value !== null && (
<div className="space-y-1">
<div className="text-xs text-muted-foreground font-medium">Previous Value</div>
<div className="p-3 rounded-md bg-muted font-mono text-xs whitespace-pre-wrap">
{formatValue(change.old_value)}
</div>
</div>
)}
{change.new_value !== null && (
<div className="space-y-1">
<div className="text-xs text-muted-foreground font-medium">New Value</div>
<div
className={`p-3 rounded-md font-mono text-xs whitespace-pre-wrap ${
change.change_type === 'added'
? 'bg-green-500/10 text-green-700 dark:text-green-400 font-semibold'
: change.change_type === 'modified'
? 'bg-blue-500/10 text-blue-700 dark:text-blue-400 font-semibold'
: 'bg-muted'
}`}
>
{formatValue(change.new_value)}
</div>
</div>
)}
</div>
</div>
))}
</div>
) : (
<div className="text-center py-8 text-muted-foreground">
<p>No history found for this field</p>
</div>
)}
</ScrollArea>
<div className="py-6">
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertDescription>
<p className="font-medium mb-2">Field-level history is not available in the current versioning system.</p>
<p className="text-sm text-muted-foreground">
To see changes to the "{formatFieldName(fieldName)}" field, please use the <strong>Version Comparison</strong> feature
in the Version History tab. This will show you all field changes between any two versions.
</p>
</AlertDescription>
</Alert>
</div>
</DialogContent>
</Dialog>
);