// Fetch selections data for the file
const fetchSelections = useCallback(async (fileId: string) => {
setState(prev => ({ ...prev, selectionsLoading: true, selectionsError: null }));
try {
const accessToken = await getAccessToken();
const response = await fetch(`/api/files/${fileId}/selections`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch selections: ${response.status}`);
}
const data = await response.json();
setState(prev => ({
...prev,
selections: data.data || [],
selectionsLoading: false
}));
} catch (error) {
console.error('Error fetching selections:', error);
setState(prev => ({
...prev,
selectionsError: 'Failed to load selections',
selectionsLoading: false
}));
}
}, [getAccessToken]);
// Generate consistent color for a species based on species ID
const getSpeciesColor = useCallback((speciesId: string): string => {
const colors = [
'#4F46E5', '#7C3AED', '#DC2626', '#059669', '#D97706',
'#2563EB', '#9333EA', '#EF4444', '#10B981', '#F59E0B'
];
// Simple hash function to get consistent color for each species
let hash = 0;
for (let i = 0; i < speciesId.length; i++) {
hash = ((hash << 5) - hash + speciesId.charCodeAt(i)) & 0xffffffff;
}
return colors[Math.abs(hash) % colors.length];
}, []);
// Get all available filters from selections
const getAvailableFilters = useCallback(() => {
const filtersMap = new Map<string, string>();
state.selections.forEach(selection => {
selection.filters.forEach(filter => {
if (filter.id && filter.name) {
filtersMap.set(filter.id, filter.name);
}
});
});
return Array.from(filtersMap.entries()).map(([id, name]) => ({ id, name }));
}, [state.selections]);
// Create regions from selections data
const createRegions = useCallback(() => {
if (!state.regionsPlugin || state.selections.length === 0) return;
try {
// Clear existing regions
if (state.regionsPlugin.clearRegions) {
state.regionsPlugin.clearRegions();
}
// Create a region for each selection, filtered by selected filter
state.selections.forEach((selection) => {
selection.filters.forEach(filter => {
// Skip if we have a filter selected and this isn't it
if (state.selectedFilterId && filter.id !== state.selectedFilterId) {
return;
}
// Create label text from species and call types
const speciesLabels = filter.species.map(species => {
const callTypeText = species.callTypes.length > 0
? ` (${species.callTypes.map(ct => ct.name).join(', ')})`
: '';
return `${species.name}${callTypeText}`;
}).join('; ');
// Get consistent color based on the primary species
const primarySpecies = filter.species[0];
const color = primarySpecies ? getSpeciesColor(primarySpecies.id) : '#4F46E5';
// Create the region
state.regionsPlugin.addRegion({
start: selection.startTime,
end: selection.endTime,
color: color + '40', // Add transparency
content: speciesLabels,
resize: false,
drag: false,
contentEditable: false,
style: {
fontSize: '10px',
padding: '2px 4px',
position: 'absolute',
top: '2px',
left: '4px',
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: '2px',
}
});
});
});
} catch (error) {
console.error('Error creating regions:', error);
}
}, [state.regionsPlugin, state.selections, state.selectedFilterId, getSpeciesColor]);