import { useState, useEffect } from 'react'; import { supabase } from '@/integrations/supabase/client'; import { toast } from 'sonner'; interface EntityVersion { id: string; entity_type: string; entity_id: string; version_number: number; version_data: any; changed_by: string; changed_at: string; change_reason: string | null; change_type: 'created' | 'updated' | 'deleted' | 'restored' | 'archived'; submission_id: string | null; is_current: boolean; ip_address_hash: string | null; metadata: any; changer_profile?: { username: string; avatar_url: string | null; }; } interface FieldChange { id: string; field_name: string; old_value: any; new_value: any; change_type: 'added' | 'modified' | 'removed'; created_at: string; } export function useEntityVersions(entityType: string, entityId: string) { const [versions, setVersions] = useState([]); const [currentVersion, setCurrentVersion] = useState(null); const [loading, setLoading] = useState(true); const [fieldHistory, setFieldHistory] = useState([]); const fetchVersions = async () => { try { setLoading(true); const { data, error } = await supabase .from('entity_versions') .select('*') .eq('entity_type', entityType) .eq('entity_id', entityId) .order('version_number', { ascending: false }); if (error) throw error; // Fetch profiles separately const userIds = [...new Set(data?.map(v => v.changed_by).filter(Boolean) || [])]; const { data: profiles } = await supabase .from('profiles') .select('user_id, username, avatar_url') .in('user_id', userIds); const versionsWithProfiles = data?.map(v => ({ ...v, changer_profile: profiles?.find(p => p.user_id === v.changed_by) })) as EntityVersion[]; setVersions(versionsWithProfiles || []); setCurrentVersion(versionsWithProfiles?.find(v => v.is_current) || null); } catch (error: any) { console.error('Error fetching versions:', error); toast.error('Failed to load version history'); } finally { setLoading(false); } }; const fetchFieldHistory = async (versionId: string) => { try { const { data, error } = await supabase .from('entity_field_history') .select('*') .eq('version_id', versionId) .order('created_at', { ascending: false }); if (error) throw error; setFieldHistory(data as FieldChange[] || []); } catch (error: any) { console.error('Error fetching field history:', error); toast.error('Failed to load field history'); } }; const compareVersions = async (fromVersionId: string, toVersionId: string) => { try { const { data, error } = await supabase.rpc('compare_versions', { p_from_version_id: fromVersionId, p_to_version_id: toVersionId }); if (error) throw error; return data; } catch (error: any) { console.error('Error comparing versions:', error); toast.error('Failed to compare versions'); return null; } }; const rollbackToVersion = async (targetVersionId: string, reason: string) => { try { const { data: userData } = await supabase.auth.getUser(); if (!userData.user) throw new Error('Not authenticated'); const { data, error } = await supabase.rpc('rollback_to_version', { p_entity_type: entityType, p_entity_id: entityId, p_target_version_id: targetVersionId, p_changed_by: userData.user.id, p_reason: reason }); if (error) throw error; toast.success('Successfully rolled back to previous version'); await fetchVersions(); return data; } catch (error: any) { console.error('Error rolling back version:', error); toast.error('Failed to rollback version'); return null; } }; const createVersion = async (versionData: any, changeReason?: string, submissionId?: string) => { try { const { data: userData } = await supabase.auth.getUser(); if (!userData.user) throw new Error('Not authenticated'); const { data, error } = await supabase.rpc('create_entity_version', { p_entity_type: entityType, p_entity_id: entityId, p_version_data: versionData, p_changed_by: userData.user.id, p_change_reason: changeReason || null, p_submission_id: submissionId || null }); if (error) throw error; await fetchVersions(); return data; } catch (error: any) { console.error('Error creating version:', error); toast.error('Failed to create version'); return null; } }; useEffect(() => { if (entityType && entityId) { fetchVersions(); } }, [entityType, entityId]); // Set up realtime subscription for version changes useEffect(() => { const channel = supabase .channel('entity_versions_changes') .on( 'postgres_changes', { event: '*', schema: 'public', table: 'entity_versions', filter: `entity_type=eq.${entityType},entity_id=eq.${entityId}` }, () => { fetchVersions(); } ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [entityType, entityId]); return { versions, currentVersion, loading, fieldHistory, fetchVersions, fetchFieldHistory, compareVersions, rollbackToVersion, createVersion }; }