diff --git a/src/components/versioning/FieldHistoryDialog.tsx b/src/components/versioning/FieldHistoryDialog.tsx index 8710c640..bfe0364e 100644 --- a/src/components/versioning/FieldHistoryDialog.tsx +++ b/src/components/versioning/FieldHistoryDialog.tsx @@ -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([]); - 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 ; - case 'removed': - return ; - case 'modified': - return ; - 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 ( - + Field History: {formatFieldName(fieldName)} - - {loading ? ( -
-
-
- ) : changes.length > 0 ? ( -
- {changes.map((change) => ( -
- {/* Header */} -
-
- {getChangeIcon(change.change_type)} - - {change.change_type} - - - Version {change.version?.version_number} - -
- -
- {change.version?.changer_profile?.username} - - - {formatDistanceToNow(new Date(change.created_at), { addSuffix: true })} - -
-
- - {/* Values */} -
- {change.old_value !== null && ( -
-
Previous Value
-
- {formatValue(change.old_value)} -
-
- )} - - {change.new_value !== null && ( -
-
New Value
-
- {formatValue(change.new_value)} -
-
- )} -
-
- ))} -
- ) : ( -
-

No history found for this field

-
- )} - +
+ + + +

Field-level history is not available in the current versioning system.

+

+ To see changes to the "{formatFieldName(fieldName)}" field, please use the Version Comparison feature + in the Version History tab. This will show you all field changes between any two versions. +

+
+
+
); diff --git a/src/hooks/useEntityVersions.ts b/src/hooks/useEntityVersions.ts index fdd350cb..078e28bd 100644 --- a/src/hooks/useEntityVersions.ts +++ b/src/hooks/useEntityVersions.ts @@ -88,32 +88,14 @@ export function useEntityVersions(entityType: EntityType, entityId: string) { } }, [entityType, entityId]); + /** + * Field history has been removed - use version comparison instead + * This function is kept for backward compatibility but does nothing + * @deprecated Use compareVersions() to see field-level changes + */ const fetchFieldHistory = async (versionId: string) => { - if (!isMountedRef.current) return; - - const currentRequestId = ++fieldHistoryRequestCounterRef.current; - - try { - const { data, error } = await supabase - .from('entity_field_history') - .select('*') - .eq('version_id', versionId) - .order('created_at', { ascending: false }); - - if (error) throw error; - - if (isMountedRef.current && currentRequestId === fieldHistoryRequestCounterRef.current) { - const fieldChanges = Array.isArray(data) ? data as FieldChange[] : []; - setFieldHistory(fieldChanges); - } - } catch (error: any) { - console.error('Error fetching field history:', error); - - if (isMountedRef.current && currentRequestId === fieldHistoryRequestCounterRef.current) { - const errorMessage = error?.message || 'Failed to load field history'; - toast.error(errorMessage); - } - } + console.warn('fetchFieldHistory is deprecated. Use compareVersions() instead for field-level changes.'); + setFieldHistory([]); }; const compareVersions = async (fromVersionId: string, toVersionId: string) => { diff --git a/src/lib/systemActivityService.ts b/src/lib/systemActivityService.ts index b5e8fbff..e56a6c43 100644 --- a/src/lib/systemActivityService.ts +++ b/src/lib/systemActivityService.ts @@ -103,28 +103,113 @@ export async function fetchSystemActivities( ): Promise { const activities: SystemActivity[] = []; - // Fetch entity versions (entity changes) - // Use simplified query without foreign key join - we'll fetch profiles separately - const { data: versions, error: versionsError } = await supabase - .from('entity_versions') - .select('id, entity_type, entity_id, version_number, version_data, changed_by, changed_at, change_type, change_reason') - .eq('is_current', true) - .order('changed_at', { ascending: false }) - .limit(limit * 2); // Fetch more to account for filtering + // Fetch entity versions from relational version tables + // Query all four version tables in parallel for better performance + const versionQueries = [ + supabase + .from('park_versions') + .select('version_id, park_id, version_number, name, created_by, created_at, change_type, change_reason, is_current') + .eq('is_current', true) + .order('created_at', { ascending: false }) + .limit(Math.ceil(limit / 2)), + supabase + .from('ride_versions') + .select('version_id, ride_id, version_number, name, created_by, created_at, change_type, change_reason, is_current') + .eq('is_current', true) + .order('created_at', { ascending: false }) + .limit(Math.ceil(limit / 2)), + supabase + .from('company_versions') + .select('version_id, company_id, version_number, name, created_by, created_at, change_type, change_reason, is_current') + .eq('is_current', true) + .order('created_at', { ascending: false }) + .limit(Math.ceil(limit / 4)), + supabase + .from('ride_model_versions') + .select('version_id, ride_model_id, version_number, name, created_by, created_at, change_type, change_reason, is_current') + .eq('is_current', true) + .order('created_at', { ascending: false }) + .limit(Math.ceil(limit / 4)), + ]; - if (!versionsError && versions) { - for (const version of versions) { - const versionData = version.version_data as any; + const [parkVersions, rideVersions, companyVersions, modelVersions] = await Promise.all(versionQueries); + + // Process park versions + if (!parkVersions.error && parkVersions.data) { + for (const version of parkVersions.data) { activities.push({ - id: version.id, + id: version.version_id, type: 'entity_change', - timestamp: version.changed_at, - actor_id: version.changed_by || null, - action: `${version.change_type} ${version.entity_type}`, + timestamp: version.created_at, + actor_id: version.created_by || null, + action: `${version.change_type} park`, details: { - entity_type: version.entity_type, - entity_id: version.entity_id, - entity_name: versionData?.name || versionData?.title, + entity_type: 'park', + entity_id: version.park_id, + entity_name: version.name, + change_type: version.change_type, + change_reason: version.change_reason, + version_number: version.version_number, + } as EntityChangeDetails, + }); + } + } + + // Process ride versions + if (!rideVersions.error && rideVersions.data) { + for (const version of rideVersions.data) { + activities.push({ + id: version.version_id, + type: 'entity_change', + timestamp: version.created_at, + actor_id: version.created_by || null, + action: `${version.change_type} ride`, + details: { + entity_type: 'ride', + entity_id: version.ride_id, + entity_name: version.name, + change_type: version.change_type, + change_reason: version.change_reason, + version_number: version.version_number, + } as EntityChangeDetails, + }); + } + } + + // Process company versions + if (!companyVersions.error && companyVersions.data) { + for (const version of companyVersions.data) { + activities.push({ + id: version.version_id, + type: 'entity_change', + timestamp: version.created_at, + actor_id: version.created_by || null, + action: `${version.change_type} company`, + details: { + entity_type: 'company', + entity_id: version.company_id, + entity_name: version.name, + change_type: version.change_type, + change_reason: version.change_reason, + version_number: version.version_number, + } as EntityChangeDetails, + }); + } + } + + // Process ride model versions + if (!modelVersions.error && modelVersions.data) { + for (const version of modelVersions.data) { + activities.push({ + id: version.version_id, + type: 'entity_change', + timestamp: version.created_at, + actor_id: version.created_by || null, + action: `${version.change_type} ride_model`, + details: { + entity_type: 'ride_model', + entity_id: version.ride_model_id, + entity_name: version.name, change_type: version.change_type, change_reason: version.change_reason, version_number: version.version_number,