Improve error handling and environment configuration across the application

Enhance input validation, update environment variable usage for Supabase and Turnstile, and refine image upload and auth logic for better robustness and developer experience.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cb061c75-702e-4b89-a8d1-77a96cdcdfbb
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/cb061c75-702e-4b89-a8d1-77a96cdcdfbb/ANdRXVZ
This commit is contained in:
pac7
2025-10-07 14:42:22 +00:00
parent 01c2df62a8
commit 1addcbc0dd
8 changed files with 412 additions and 116 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { toast } from 'sonner';
@@ -36,9 +36,17 @@ export function useEntityVersions(entityType: string, entityId: string) {
const [currentVersion, setCurrentVersion] = useState<EntityVersion | null>(null);
const [loading, setLoading] = useState(true);
const [fieldHistory, setFieldHistory] = useState<FieldChange[]>([]);
// Track if component is mounted to prevent state updates after unmount
const isMountedRef = useRef(true);
// Track the current channel to prevent duplicate subscriptions
const channelRef = useRef<ReturnType<typeof supabase.channel> | null>(null);
const fetchVersions = async () => {
try {
if (!isMountedRef.current) return;
setLoading(true);
const { data, error } = await supabase
@@ -62,13 +70,20 @@ export function useEntityVersions(entityType: string, entityId: string) {
changer_profile: profiles?.find(p => p.user_id === v.changed_by)
})) as EntityVersion[];
setVersions(versionsWithProfiles || []);
setCurrentVersion(versionsWithProfiles?.find(v => v.is_current) || null);
// Only update state if component is still mounted
if (isMountedRef.current) {
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');
if (isMountedRef.current) {
toast.error('Failed to load version history');
}
} finally {
setLoading(false);
if (isMountedRef.current) {
setLoading(false);
}
}
};
@@ -82,10 +97,14 @@ export function useEntityVersions(entityType: string, entityId: string) {
if (error) throw error;
setFieldHistory(data as FieldChange[] || []);
if (isMountedRef.current) {
setFieldHistory(data as FieldChange[] || []);
}
} catch (error: any) {
console.error('Error fetching field history:', error);
toast.error('Failed to load field history');
if (isMountedRef.current) {
toast.error('Failed to load field history');
}
}
};
@@ -101,7 +120,9 @@ export function useEntityVersions(entityType: string, entityId: string) {
return data;
} catch (error: any) {
console.error('Error comparing versions:', error);
toast.error('Failed to compare versions');
if (isMountedRef.current) {
toast.error('Failed to compare versions');
}
return null;
}
};
@@ -121,12 +142,16 @@ export function useEntityVersions(entityType: string, entityId: string) {
if (error) throw error;
toast.success('Successfully rolled back to previous version');
await fetchVersions();
if (isMountedRef.current) {
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');
if (isMountedRef.current) {
toast.error('Failed to rollback version');
}
return null;
}
};
@@ -147,11 +172,15 @@ export function useEntityVersions(entityType: string, entityId: string) {
if (error) throw error;
await fetchVersions();
if (isMountedRef.current) {
await fetchVersions();
}
return data;
} catch (error: any) {
console.error('Error creating version:', error);
toast.error('Failed to create version');
if (isMountedRef.current) {
toast.error('Failed to create version');
}
return null;
}
};
@@ -164,6 +193,15 @@ export function useEntityVersions(entityType: string, entityId: string) {
// Set up realtime subscription for version changes
useEffect(() => {
if (!entityType || !entityId) return;
// Clean up existing channel if any
if (channelRef.current) {
supabase.removeChannel(channelRef.current);
channelRef.current = null;
}
// Create new channel
const channel = supabase
.channel('entity_versions_changes')
.on(
@@ -175,16 +213,35 @@ export function useEntityVersions(entityType: string, entityId: string) {
filter: `entity_type=eq.${entityType},entity_id=eq.${entityId}`
},
() => {
fetchVersions();
if (isMountedRef.current) {
fetchVersions();
}
}
)
.subscribe();
channelRef.current = channel;
return () => {
supabase.removeChannel(channel);
// Ensure cleanup happens in all scenarios
if (channelRef.current) {
supabase.removeChannel(channelRef.current).catch((error) => {
console.error('Error removing channel:', error);
});
channelRef.current = null;
}
};
}, [entityType, entityId]);
// Set mounted ref on mount and cleanup on unmount
useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
};
}, []);
return {
versions,
currentVersion,