Spaces:
Running
Running
fix build error
Browse files- frontend/src/components/ExportModal.tsx +2 -2
- frontend/src/components/FilterBar.tsx +1 -2
- frontend/src/components/HeaderNav.tsx +1 -2
- frontend/src/contexts/AdminContext.tsx +4 -10
- frontend/src/contexts/FilterContext.tsx +4 -10
- frontend/src/hooks/useAdmin.ts +10 -0
- frontend/src/hooks/useFilterContext.ts +10 -0
- frontend/src/pages/AdminPage/AdminPage.tsx +74 -63
- frontend/src/pages/AnalyticsPage/AnalyticsPage.tsx +4 -4
- frontend/src/pages/ExplorePage/ExplorePage.tsx +1 -1
- frontend/src/pages/HelpPage.tsx +1 -1
- frontend/src/pages/MapDetailsPage/MapDetailPage.tsx +2 -2
- frontend/src/pages/UploadPage/UploadPage.tsx +11 -11
frontend/src/components/ExportModal.tsx
CHANGED
|
@@ -214,7 +214,7 @@ export default function ExportModal({
|
|
| 214 |
name="crisis-maps"
|
| 215 |
label={`Crisis Maps (${crisisMapsCount} images)`}
|
| 216 |
value={crisisMapsSelected}
|
| 217 |
-
onChange={(value
|
| 218 |
disabled={isLoading}
|
| 219 |
/>
|
| 220 |
</div>
|
|
@@ -224,7 +224,7 @@ export default function ExportModal({
|
|
| 224 |
name="drone-images"
|
| 225 |
label={`Drone Images (${droneImagesCount} images)`}
|
| 226 |
value={droneImagesSelected}
|
| 227 |
-
onChange={(value
|
| 228 |
disabled={isLoading}
|
| 229 |
/>
|
| 230 |
</div>
|
|
|
|
| 214 |
name="crisis-maps"
|
| 215 |
label={`Crisis Maps (${crisisMapsCount} images)`}
|
| 216 |
value={crisisMapsSelected}
|
| 217 |
+
onChange={(value) => setCrisisMapsSelected(value)}
|
| 218 |
disabled={isLoading}
|
| 219 |
/>
|
| 220 |
</div>
|
|
|
|
| 224 |
name="drone-images"
|
| 225 |
label={`Drone Images (${droneImagesCount} images)`}
|
| 226 |
value={droneImagesSelected}
|
| 227 |
+
onChange={(value) => setDroneImagesSelected(value)}
|
| 228 |
disabled={isLoading}
|
| 229 |
/>
|
| 230 |
</div>
|
frontend/src/components/FilterBar.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import React from 'react';
|
| 2 |
import { Container, TextInput, SelectInput, MultiSelectInput, Button } from '@ifrc-go/ui';
|
| 3 |
-
import { useFilterContext } from '../
|
| 4 |
-
import styles from './FilterBar.module.css';
|
| 5 |
|
| 6 |
interface FilterBarProps {
|
| 7 |
sources: {s_code: string, label: string}[];
|
|
|
|
| 1 |
import React from 'react';
|
| 2 |
import { Container, TextInput, SelectInput, MultiSelectInput, Button } from '@ifrc-go/ui';
|
| 3 |
+
import { useFilterContext } from '../hooks/useFilterContext';
|
|
|
|
| 4 |
|
| 5 |
interface FilterBarProps {
|
| 6 |
sources: {s_code: string, label: string}[];
|
frontend/src/components/HeaderNav.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
| 8 |
GoMainIcon,
|
| 9 |
SettingsIcon,
|
| 10 |
} from "@ifrc-go/icons";
|
| 11 |
-
|
| 12 |
|
| 13 |
declare global {
|
| 14 |
interface Window {
|
|
@@ -25,7 +25,6 @@ const navItems = [
|
|
| 25 |
export default function HeaderNav() {
|
| 26 |
const location = useLocation();
|
| 27 |
const navigate = useNavigate();
|
| 28 |
-
const { } = useAdmin();
|
| 29 |
|
| 30 |
return (
|
| 31 |
<nav className="border-b border-gray-200 bg-white shadow-sm sticky top-0 z-50 backdrop-blur-sm bg-white/95">
|
|
|
|
| 8 |
GoMainIcon,
|
| 9 |
SettingsIcon,
|
| 10 |
} from "@ifrc-go/icons";
|
| 11 |
+
|
| 12 |
|
| 13 |
declare global {
|
| 14 |
interface Window {
|
|
|
|
| 25 |
export default function HeaderNav() {
|
| 26 |
const location = useLocation();
|
| 27 |
const navigate = useNavigate();
|
|
|
|
| 28 |
|
| 29 |
return (
|
| 30 |
<nav className="border-b border-gray-200 bg-white shadow-sm sticky top-0 z-50 backdrop-blur-sm bg-white/95">
|
frontend/src/contexts/AdminContext.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import React, { createContext,
|
| 2 |
import type { ReactNode } from 'react';
|
| 3 |
|
| 4 |
interface AdminContextType {
|
|
@@ -9,15 +9,7 @@ interface AdminContextType {
|
|
| 9 |
verifyToken: () => Promise<void>;
|
| 10 |
}
|
| 11 |
|
| 12 |
-
const AdminContext = createContext<AdminContextType | undefined>(undefined);
|
| 13 |
-
|
| 14 |
-
export const useAdmin = () => {
|
| 15 |
-
const context = useContext(AdminContext);
|
| 16 |
-
if (context === undefined) {
|
| 17 |
-
throw new Error('useAdmin must be used within an AdminProvider');
|
| 18 |
-
}
|
| 19 |
-
return context;
|
| 20 |
-
};
|
| 21 |
|
| 22 |
interface AdminProviderProps {
|
| 23 |
children: ReactNode;
|
|
@@ -108,3 +100,5 @@ export const AdminProvider: React.FC<AdminProviderProps> = ({ children }) => {
|
|
| 108 |
</AdminContext.Provider>
|
| 109 |
);
|
| 110 |
};
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { createContext, useState, useEffect } from 'react';
|
| 2 |
import type { ReactNode } from 'react';
|
| 3 |
|
| 4 |
interface AdminContextType {
|
|
|
|
| 9 |
verifyToken: () => Promise<void>;
|
| 10 |
}
|
| 11 |
|
| 12 |
+
export const AdminContext = createContext<AdminContextType | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
interface AdminProviderProps {
|
| 15 |
children: ReactNode;
|
|
|
|
| 100 |
</AdminContext.Provider>
|
| 101 |
);
|
| 102 |
};
|
| 103 |
+
|
| 104 |
+
|
frontend/src/contexts/FilterContext.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import React, { createContext,
|
| 2 |
import type { ReactNode } from 'react';
|
| 3 |
|
| 4 |
interface FilterContextType {
|
|
@@ -24,15 +24,7 @@ interface FilterContextType {
|
|
| 24 |
clearAllFilters: () => void;
|
| 25 |
}
|
| 26 |
|
| 27 |
-
const FilterContext = createContext<FilterContextType | undefined>(undefined);
|
| 28 |
-
|
| 29 |
-
export const useFilterContext = () => {
|
| 30 |
-
const context = useContext(FilterContext);
|
| 31 |
-
if (context === undefined) {
|
| 32 |
-
throw new Error('useFilterContext must be used within a FilterProvider');
|
| 33 |
-
}
|
| 34 |
-
return context;
|
| 35 |
-
};
|
| 36 |
|
| 37 |
interface FilterProviderProps {
|
| 38 |
children: ReactNode;
|
|
@@ -81,3 +73,5 @@ export const FilterProvider: React.FC<FilterProviderProps> = ({ children }) => {
|
|
| 81 |
</FilterContext.Provider>
|
| 82 |
);
|
| 83 |
};
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { createContext, useState } from 'react';
|
| 2 |
import type { ReactNode } from 'react';
|
| 3 |
|
| 4 |
interface FilterContextType {
|
|
|
|
| 24 |
clearAllFilters: () => void;
|
| 25 |
}
|
| 26 |
|
| 27 |
+
export const FilterContext = createContext<FilterContextType | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
interface FilterProviderProps {
|
| 30 |
children: ReactNode;
|
|
|
|
| 73 |
</FilterContext.Provider>
|
| 74 |
);
|
| 75 |
};
|
| 76 |
+
|
| 77 |
+
|
frontend/src/hooks/useAdmin.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useContext } from 'react';
|
| 2 |
+
import { AdminContext } from '../contexts/AdminContext';
|
| 3 |
+
|
| 4 |
+
export const useAdmin = () => {
|
| 5 |
+
const context = useContext(AdminContext);
|
| 6 |
+
if (context === undefined) {
|
| 7 |
+
throw new Error('useAdmin must be used within an AdminProvider');
|
| 8 |
+
}
|
| 9 |
+
return context;
|
| 10 |
+
};
|
frontend/src/hooks/useFilterContext.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useContext } from 'react';
|
| 2 |
+
import { FilterContext } from '../contexts/FilterContext';
|
| 3 |
+
|
| 4 |
+
export const useFilterContext = () => {
|
| 5 |
+
const context = useContext(FilterContext);
|
| 6 |
+
if (context === undefined) {
|
| 7 |
+
throw new Error('useFilterContext must be used within a FilterProvider');
|
| 8 |
+
}
|
| 9 |
+
return context;
|
| 10 |
+
};
|
frontend/src/pages/AdminPage/AdminPage.tsx
CHANGED
|
@@ -1,53 +1,59 @@
|
|
| 1 |
// Force rebuild - Frontend updated with edit prompt functionality
|
| 2 |
-
import React, { useState, useEffect } from 'react';
|
| 3 |
-
import { useAdmin } from '../../
|
| 4 |
import { PageContainer, Heading, Button, Container, TextInput, SelectInput } from '@ifrc-go/ui';
|
| 5 |
import styles from './AdminPage.module.css';
|
| 6 |
|
| 7 |
const SELECTED_MODEL_KEY = 'selectedVlmModel';
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
export default function AdminPage() {
|
| 10 |
const { isAuthenticated, isLoading, login, logout } = useAdmin();
|
| 11 |
const [password, setPassword] = useState('');
|
| 12 |
const [error, setError] = useState('');
|
| 13 |
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
| 14 |
|
| 15 |
-
const [availableModels, setAvailableModels] = useState<
|
| 16 |
-
m_code: string;
|
| 17 |
-
label: string;
|
| 18 |
-
model_type: string;
|
| 19 |
-
is_available: boolean;
|
| 20 |
-
provider?: string;
|
| 21 |
-
model_id?: string;
|
| 22 |
-
config?: {
|
| 23 |
-
provider?: string;
|
| 24 |
-
model_id?: string;
|
| 25 |
-
model?: string;
|
| 26 |
-
stub?: boolean;
|
| 27 |
-
};
|
| 28 |
-
}>>([]);
|
| 29 |
const [selectedModel, setSelectedModel] = useState<string>('');
|
| 30 |
|
| 31 |
// Prompts state
|
| 32 |
-
const [availablePrompts, setAvailablePrompts] = useState<
|
| 33 |
-
p_code: string;
|
| 34 |
-
label: string;
|
| 35 |
-
metadata_instructions?: string;
|
| 36 |
-
image_type: string;
|
| 37 |
-
is_active: boolean;
|
| 38 |
-
}>>([]);
|
| 39 |
|
| 40 |
-
const [imageTypes, setImageTypes] = useState<
|
| 41 |
-
image_type: string;
|
| 42 |
-
label: string;
|
| 43 |
-
}>>([]);
|
| 44 |
|
| 45 |
// Prompt management state
|
| 46 |
const [showEditPromptForm, setShowEditPromptForm] = useState(false);
|
| 47 |
const [showAddPromptForm, setShowAddPromptForm] = useState(false);
|
| 48 |
const [addingPromptType, setAddingPromptType] = useState<'crisis_map' | 'drone_image' | null>(null);
|
| 49 |
-
const [editingPrompt, setEditingPrompt] = useState<
|
| 50 |
-
const [newPromptData, setNewPromptData] = useState({
|
| 51 |
p_code: '',
|
| 52 |
label: '',
|
| 53 |
metadata_instructions: '',
|
|
@@ -58,8 +64,8 @@ export default function AdminPage() {
|
|
| 58 |
// Model management state
|
| 59 |
const [showAddModelForm, setShowAddModelForm] = useState(false);
|
| 60 |
const [showEditModelForm, setShowEditModelForm] = useState(false);
|
| 61 |
-
const [editingModel, setEditingModel] = useState<
|
| 62 |
-
const [newModelData, setNewModelData] = useState({
|
| 63 |
m_code: '',
|
| 64 |
label: '',
|
| 65 |
model_type: 'custom',
|
|
@@ -79,21 +85,7 @@ export default function AdminPage() {
|
|
| 79 |
const [testResults, setTestResults] = useState<string>('');
|
| 80 |
const [testResultsTitle, setTestResultsTitle] = useState<string>('');
|
| 81 |
|
| 82 |
-
|
| 83 |
-
if (isAuthenticated) {
|
| 84 |
-
fetchModels();
|
| 85 |
-
fetchPrompts();
|
| 86 |
-
fetchImageTypes();
|
| 87 |
-
}
|
| 88 |
-
}, [isAuthenticated]);
|
| 89 |
-
|
| 90 |
-
// Debug effect to see when prompts state changes
|
| 91 |
-
useEffect(() => {
|
| 92 |
-
console.log('=== availablePrompts state changed ===');
|
| 93 |
-
console.log('New prompts state:', availablePrompts);
|
| 94 |
-
}, [availablePrompts]);
|
| 95 |
-
|
| 96 |
-
const fetchModels = () => {
|
| 97 |
fetch('/api/models')
|
| 98 |
.then(r => r.json())
|
| 99 |
.then(modelsData => {
|
|
@@ -117,15 +109,14 @@ export default function AdminPage() {
|
|
| 117 |
.catch(() => {
|
| 118 |
// Handle error silently
|
| 119 |
});
|
| 120 |
-
};
|
| 121 |
|
| 122 |
-
const fetchPrompts = () => {
|
| 123 |
console.log('=== fetchPrompts called ===');
|
| 124 |
fetch('/api/prompts')
|
| 125 |
.then(r => r.json())
|
| 126 |
.then(promptsData => {
|
| 127 |
console.log('Prompts data received:', promptsData);
|
| 128 |
-
console.log('Current availablePrompts state:', availablePrompts);
|
| 129 |
setAvailablePrompts(promptsData || []);
|
| 130 |
console.log('State update triggered with:', promptsData || []);
|
| 131 |
})
|
|
@@ -133,9 +124,9 @@ export default function AdminPage() {
|
|
| 133 |
console.error('Error fetching prompts:', error);
|
| 134 |
// Handle error silently
|
| 135 |
});
|
| 136 |
-
};
|
| 137 |
|
| 138 |
-
const fetchImageTypes = () => {
|
| 139 |
fetch('/api/image-types')
|
| 140 |
.then(r => r.json())
|
| 141 |
.then(imageTypesData => {
|
|
@@ -145,9 +136,19 @@ export default function AdminPage() {
|
|
| 145 |
.catch(() => {
|
| 146 |
// Handle error silently
|
| 147 |
});
|
| 148 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
-
|
|
|
|
| 151 |
setEditingPrompt(prompt);
|
| 152 |
setNewPromptData({
|
| 153 |
p_code: prompt.p_code,
|
|
@@ -161,7 +162,12 @@ export default function AdminPage() {
|
|
| 161 |
|
| 162 |
const handleSavePrompt = async () => {
|
| 163 |
try {
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
method: 'PUT',
|
| 166 |
headers: {
|
| 167 |
'Content-Type': 'application/json',
|
|
@@ -184,8 +190,8 @@ export default function AdminPage() {
|
|
| 184 |
const errorData = await response.json();
|
| 185 |
alert(`Failed to update prompt: ${errorData.error || 'Unknown error'}`);
|
| 186 |
}
|
| 187 |
-
} catch
|
| 188 |
-
alert(
|
| 189 |
}
|
| 190 |
};
|
| 191 |
|
|
@@ -205,7 +211,7 @@ export default function AdminPage() {
|
|
| 205 |
const errorData = await response.json();
|
| 206 |
alert(`Failed to toggle prompt active status: ${errorData.detail || 'Unknown error'}`);
|
| 207 |
}
|
| 208 |
-
} catch
|
| 209 |
alert('Error toggling prompt active status');
|
| 210 |
}
|
| 211 |
};
|
|
@@ -248,8 +254,8 @@ export default function AdminPage() {
|
|
| 248 |
const errorData = await response.json();
|
| 249 |
alert(`Failed to create prompt: ${errorData.detail || 'Unknown error'}`);
|
| 250 |
}
|
| 251 |
-
} catch
|
| 252 |
-
alert(
|
| 253 |
}
|
| 254 |
};
|
| 255 |
|
|
@@ -277,7 +283,7 @@ export default function AdminPage() {
|
|
| 277 |
const errorData = await response.json();
|
| 278 |
alert(`Failed to toggle model availability: ${errorData.error || 'Unknown error'}`);
|
| 279 |
}
|
| 280 |
-
} catch {
|
| 281 |
alert('Error toggling model availability');
|
| 282 |
}
|
| 283 |
};
|
|
@@ -340,7 +346,7 @@ Model "${newModelData.label}" added successfully!
|
|
| 340 |
}
|
| 341 |
};
|
| 342 |
|
| 343 |
-
const handleEditModel = (model:
|
| 344 |
setEditingModel(model);
|
| 345 |
setNewModelData({
|
| 346 |
m_code: model.m_code,
|
|
@@ -368,6 +374,11 @@ Model "${newModelData.label}" added successfully!
|
|
| 368 |
|
| 369 |
console.log('Update payload:', updatePayload);
|
| 370 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
const response = await fetch(`/api/admin/models/${editingModel.m_code}`, {
|
| 372 |
method: 'PUT',
|
| 373 |
headers: {
|
|
@@ -429,7 +440,7 @@ Model "${newModelData.label}" added successfully!
|
|
| 429 |
const errorData = await response.json();
|
| 430 |
alert(`Failed to delete model: ${errorData.detail || 'Unknown error'}`);
|
| 431 |
}
|
| 432 |
-
} catch
|
| 433 |
alert('Error deleting model');
|
| 434 |
}
|
| 435 |
};
|
|
@@ -449,7 +460,7 @@ Model "${newModelData.label}" added successfully!
|
|
| 449 |
if (!success) {
|
| 450 |
setError('Invalid password');
|
| 451 |
}
|
| 452 |
-
} catch
|
| 453 |
setError('Login failed. Please try again.');
|
| 454 |
} finally {
|
| 455 |
setIsLoggingIn(false);
|
|
|
|
| 1 |
// Force rebuild - Frontend updated with edit prompt functionality
|
| 2 |
+
import React, { useState, useEffect, useCallback } from 'react';
|
| 3 |
+
import { useAdmin } from '../../hooks/useAdmin';
|
| 4 |
import { PageContainer, Heading, Button, Container, TextInput, SelectInput } from '@ifrc-go/ui';
|
| 5 |
import styles from './AdminPage.module.css';
|
| 6 |
|
| 7 |
const SELECTED_MODEL_KEY = 'selectedVlmModel';
|
| 8 |
|
| 9 |
+
interface PromptData {
|
| 10 |
+
p_code: string;
|
| 11 |
+
label: string;
|
| 12 |
+
metadata_instructions?: string;
|
| 13 |
+
image_type: string;
|
| 14 |
+
is_active: boolean;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
interface ModelData {
|
| 18 |
+
m_code: string;
|
| 19 |
+
label: string;
|
| 20 |
+
model_type: string;
|
| 21 |
+
provider?: string;
|
| 22 |
+
model_id?: string;
|
| 23 |
+
config?: {
|
| 24 |
+
provider?: string;
|
| 25 |
+
model_id?: string;
|
| 26 |
+
model?: string;
|
| 27 |
+
stub?: boolean;
|
| 28 |
+
};
|
| 29 |
+
is_available: boolean;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
interface ImageTypeData {
|
| 33 |
+
image_type: string;
|
| 34 |
+
label: string;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
export default function AdminPage() {
|
| 38 |
const { isAuthenticated, isLoading, login, logout } = useAdmin();
|
| 39 |
const [password, setPassword] = useState('');
|
| 40 |
const [error, setError] = useState('');
|
| 41 |
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
| 42 |
|
| 43 |
+
const [availableModels, setAvailableModels] = useState<ModelData[]>([]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
const [selectedModel, setSelectedModel] = useState<string>('');
|
| 45 |
|
| 46 |
// Prompts state
|
| 47 |
+
const [availablePrompts, setAvailablePrompts] = useState<PromptData[]>([]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
const [imageTypes, setImageTypes] = useState<ImageTypeData[]>([]);
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
// Prompt management state
|
| 52 |
const [showEditPromptForm, setShowEditPromptForm] = useState(false);
|
| 53 |
const [showAddPromptForm, setShowAddPromptForm] = useState(false);
|
| 54 |
const [addingPromptType, setAddingPromptType] = useState<'crisis_map' | 'drone_image' | null>(null);
|
| 55 |
+
const [editingPrompt, setEditingPrompt] = useState<PromptData | null>(null);
|
| 56 |
+
const [newPromptData, setNewPromptData] = useState<PromptData>({
|
| 57 |
p_code: '',
|
| 58 |
label: '',
|
| 59 |
metadata_instructions: '',
|
|
|
|
| 64 |
// Model management state
|
| 65 |
const [showAddModelForm, setShowAddModelForm] = useState(false);
|
| 66 |
const [showEditModelForm, setShowEditModelForm] = useState(false);
|
| 67 |
+
const [editingModel, setEditingModel] = useState<ModelData | null>(null);
|
| 68 |
+
const [newModelData, setNewModelData] = useState<ModelData>({
|
| 69 |
m_code: '',
|
| 70 |
label: '',
|
| 71 |
model_type: 'custom',
|
|
|
|
| 85 |
const [testResults, setTestResults] = useState<string>('');
|
| 86 |
const [testResultsTitle, setTestResultsTitle] = useState<string>('');
|
| 87 |
|
| 88 |
+
const fetchModels = useCallback(() => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
fetch('/api/models')
|
| 90 |
.then(r => r.json())
|
| 91 |
.then(modelsData => {
|
|
|
|
| 109 |
.catch(() => {
|
| 110 |
// Handle error silently
|
| 111 |
});
|
| 112 |
+
}, []);
|
| 113 |
|
| 114 |
+
const fetchPrompts = useCallback(() => {
|
| 115 |
console.log('=== fetchPrompts called ===');
|
| 116 |
fetch('/api/prompts')
|
| 117 |
.then(r => r.json())
|
| 118 |
.then(promptsData => {
|
| 119 |
console.log('Prompts data received:', promptsData);
|
|
|
|
| 120 |
setAvailablePrompts(promptsData || []);
|
| 121 |
console.log('State update triggered with:', promptsData || []);
|
| 122 |
})
|
|
|
|
| 124 |
console.error('Error fetching prompts:', error);
|
| 125 |
// Handle error silently
|
| 126 |
});
|
| 127 |
+
}, []);
|
| 128 |
|
| 129 |
+
const fetchImageTypes = useCallback(() => {
|
| 130 |
fetch('/api/image-types')
|
| 131 |
.then(r => r.json())
|
| 132 |
.then(imageTypesData => {
|
|
|
|
| 136 |
.catch(() => {
|
| 137 |
// Handle error silently
|
| 138 |
});
|
| 139 |
+
}, []);
|
| 140 |
+
|
| 141 |
+
useEffect(() => {
|
| 142 |
+
if (isAuthenticated) {
|
| 143 |
+
fetchModels();
|
| 144 |
+
fetchPrompts();
|
| 145 |
+
fetchImageTypes();
|
| 146 |
+
}
|
| 147 |
+
}, [isAuthenticated, fetchModels, fetchPrompts, fetchImageTypes]);
|
| 148 |
+
|
| 149 |
|
| 150 |
+
|
| 151 |
+
const handleEditPrompt = (prompt: PromptData) => {
|
| 152 |
setEditingPrompt(prompt);
|
| 153 |
setNewPromptData({
|
| 154 |
p_code: prompt.p_code,
|
|
|
|
| 162 |
|
| 163 |
const handleSavePrompt = async () => {
|
| 164 |
try {
|
| 165 |
+
if (!editingPrompt) {
|
| 166 |
+
alert('No prompt selected for editing');
|
| 167 |
+
return;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
const response = await fetch(`/api/prompts/${editingPrompt.p_code}`, {
|
| 171 |
method: 'PUT',
|
| 172 |
headers: {
|
| 173 |
'Content-Type': 'application/json',
|
|
|
|
| 190 |
const errorData = await response.json();
|
| 191 |
alert(`Failed to update prompt: ${errorData.error || 'Unknown error'}`);
|
| 192 |
}
|
| 193 |
+
} catch {
|
| 194 |
+
alert('Error updating prompt');
|
| 195 |
}
|
| 196 |
};
|
| 197 |
|
|
|
|
| 211 |
const errorData = await response.json();
|
| 212 |
alert(`Failed to toggle prompt active status: ${errorData.detail || 'Unknown error'}`);
|
| 213 |
}
|
| 214 |
+
} catch {
|
| 215 |
alert('Error toggling prompt active status');
|
| 216 |
}
|
| 217 |
};
|
|
|
|
| 254 |
const errorData = await response.json();
|
| 255 |
alert(`Failed to create prompt: ${errorData.detail || 'Unknown error'}`);
|
| 256 |
}
|
| 257 |
+
} catch {
|
| 258 |
+
alert('Error creating prompt');
|
| 259 |
}
|
| 260 |
};
|
| 261 |
|
|
|
|
| 283 |
const errorData = await response.json();
|
| 284 |
alert(`Failed to toggle model availability: ${errorData.error || 'Unknown error'}`);
|
| 285 |
}
|
| 286 |
+
} catch (_error) {
|
| 287 |
alert('Error toggling model availability');
|
| 288 |
}
|
| 289 |
};
|
|
|
|
| 346 |
}
|
| 347 |
};
|
| 348 |
|
| 349 |
+
const handleEditModel = (model: ModelData) => {
|
| 350 |
setEditingModel(model);
|
| 351 |
setNewModelData({
|
| 352 |
m_code: model.m_code,
|
|
|
|
| 374 |
|
| 375 |
console.log('Update payload:', updatePayload);
|
| 376 |
|
| 377 |
+
if (!editingModel) {
|
| 378 |
+
alert('No model selected for editing');
|
| 379 |
+
return;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
const response = await fetch(`/api/admin/models/${editingModel.m_code}`, {
|
| 383 |
method: 'PUT',
|
| 384 |
headers: {
|
|
|
|
| 440 |
const errorData = await response.json();
|
| 441 |
alert(`Failed to delete model: ${errorData.detail || 'Unknown error'}`);
|
| 442 |
}
|
| 443 |
+
} catch {
|
| 444 |
alert('Error deleting model');
|
| 445 |
}
|
| 446 |
};
|
|
|
|
| 460 |
if (!success) {
|
| 461 |
setError('Invalid password');
|
| 462 |
}
|
| 463 |
+
} catch {
|
| 464 |
setError('Login failed. Please try again.');
|
| 465 |
} finally {
|
| 466 |
setIsLoggingIn(false);
|
frontend/src/pages/AnalyticsPage/AnalyticsPage.tsx
CHANGED
|
@@ -353,7 +353,7 @@ export default function AnalyticsPage() {
|
|
| 353 |
|
| 354 |
useEffect(() => {
|
| 355 |
fetchLookupData();
|
| 356 |
-
}, []);
|
| 357 |
|
| 358 |
useEffect(() => {
|
| 359 |
if (sourcesLookup.length > 0 && typesLookup.length > 0 && regionsLookup.length > 0 && modelsLookup.length > 0) {
|
|
@@ -590,7 +590,7 @@ export default function AnalyticsPage() {
|
|
| 590 |
},
|
| 591 |
),
|
| 592 |
|
| 593 |
-
], [
|
| 594 |
|
| 595 |
const editTimeColumns = useMemo(() => [
|
| 596 |
createStringColumn<EditTimeData, number>(
|
|
@@ -618,7 +618,7 @@ export default function AnalyticsPage() {
|
|
| 618 |
'Max Edit Time',
|
| 619 |
(item) => formatEditTime(item.maxEditTime),
|
| 620 |
),
|
| 621 |
-
], [
|
| 622 |
|
| 623 |
const percentageModifiedColumns = useMemo(() => [
|
| 624 |
createStringColumn<PercentageModifiedData, number>(
|
|
@@ -1053,7 +1053,7 @@ export default function AnalyticsPage() {
|
|
| 1053 |
};
|
| 1054 |
})
|
| 1055 |
.sort((a, b) => b.count - a.count);
|
| 1056 |
-
}, [data]);
|
| 1057 |
|
| 1058 |
const getImageTypeModelsTableData = useCallback((imageType: string) => {
|
| 1059 |
if (!data) return [];
|
|
|
|
| 353 |
|
| 354 |
useEffect(() => {
|
| 355 |
fetchLookupData();
|
| 356 |
+
}, [fetchLookupData]);
|
| 357 |
|
| 358 |
useEffect(() => {
|
| 359 |
if (sourcesLookup.length > 0 && typesLookup.length > 0 && regionsLookup.length > 0 && modelsLookup.length > 0) {
|
|
|
|
| 590 |
},
|
| 591 |
),
|
| 592 |
|
| 593 |
+
], []);
|
| 594 |
|
| 595 |
const editTimeColumns = useMemo(() => [
|
| 596 |
createStringColumn<EditTimeData, number>(
|
|
|
|
| 618 |
'Max Edit Time',
|
| 619 |
(item) => formatEditTime(item.maxEditTime),
|
| 620 |
),
|
| 621 |
+
], []);
|
| 622 |
|
| 623 |
const percentageModifiedColumns = useMemo(() => [
|
| 624 |
createStringColumn<PercentageModifiedData, number>(
|
|
|
|
| 1053 |
};
|
| 1054 |
})
|
| 1055 |
.sort((a, b) => b.count - a.count);
|
| 1056 |
+
}, [data, getModelLabel]);
|
| 1057 |
|
| 1058 |
const getImageTypeModelsTableData = useCallback((imageType: string) => {
|
| 1059 |
if (!data) return [];
|
frontend/src/pages/ExplorePage/ExplorePage.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import { useState, useEffect, useMemo } from 'react';
|
| 2 |
import { useNavigate, useLocation } from 'react-router-dom';
|
| 3 |
import { PageContainer, Container, SegmentInput, Spinner, Button } from '@ifrc-go/ui';
|
| 4 |
-
import { useFilterContext } from '../../
|
| 5 |
import FilterBar from '../../components/FilterBar';
|
| 6 |
import styles from './ExplorePage.module.css';
|
| 7 |
import ExportModal from '../../components/ExportModal';
|
|
|
|
| 1 |
import { useState, useEffect, useMemo } from 'react';
|
| 2 |
import { useNavigate, useLocation } from 'react-router-dom';
|
| 3 |
import { PageContainer, Container, SegmentInput, Spinner, Button } from '@ifrc-go/ui';
|
| 4 |
+
import { useFilterContext } from '../../hooks/useFilterContext';
|
| 5 |
import FilterBar from '../../components/FilterBar';
|
| 6 |
import styles from './ExplorePage.module.css';
|
| 7 |
import ExportModal from '../../components/ExportModal';
|
frontend/src/pages/HelpPage.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import { PageContainer, Heading, Button } from '@ifrc-go/ui';
|
| 2 |
import { useNavigate } from 'react-router-dom';
|
| 3 |
-
import { useFilterContext } from '../
|
| 4 |
import styles from './HelpPage.module.css';
|
| 5 |
|
| 6 |
export default function HelpPage() {
|
|
|
|
| 1 |
import { PageContainer, Heading, Button } from '@ifrc-go/ui';
|
| 2 |
import { useNavigate } from 'react-router-dom';
|
| 3 |
+
import { useFilterContext } from '../hooks/useFilterContext';
|
| 4 |
import styles from './HelpPage.module.css';
|
| 5 |
|
| 6 |
export default function HelpPage() {
|
frontend/src/pages/MapDetailsPage/MapDetailPage.tsx
CHANGED
|
@@ -3,8 +3,8 @@ import { useParams, useNavigate } from 'react-router-dom';
|
|
| 3 |
import { useState, useEffect, useMemo, useCallback } from 'react';
|
| 4 |
import { ChevronLeftLineIcon, ChevronRightLineIcon, DeleteBinLineIcon } from '@ifrc-go/icons';
|
| 5 |
import styles from './MapDetailPage.module.css';
|
| 6 |
-
import { useFilterContext } from '../../
|
| 7 |
-
import { useAdmin } from '../../
|
| 8 |
import ExportModal from '../../components/ExportModal';
|
| 9 |
|
| 10 |
interface MapOut {
|
|
|
|
| 3 |
import { useState, useEffect, useMemo, useCallback } from 'react';
|
| 4 |
import { ChevronLeftLineIcon, ChevronRightLineIcon, DeleteBinLineIcon } from '@ifrc-go/icons';
|
| 5 |
import styles from './MapDetailPage.module.css';
|
| 6 |
+
import { useFilterContext } from '../../hooks/useFilterContext';
|
| 7 |
+
import { useAdmin } from '../../hooks/useAdmin';
|
| 8 |
import ExportModal from '../../components/ExportModal';
|
| 9 |
|
| 10 |
interface MapOut {
|
frontend/src/pages/UploadPage/UploadPage.tsx
CHANGED
|
@@ -102,7 +102,7 @@ export default function UploadPage() {
|
|
| 102 |
setImageType(imageTypesData[0].image_type);
|
| 103 |
}
|
| 104 |
});
|
| 105 |
-
}, [searchParams]);
|
| 106 |
|
| 107 |
const handleNavigation = useCallback((to: string) => {
|
| 108 |
if (to === '/upload' || to === '/') {
|
|
@@ -201,8 +201,8 @@ export default function UploadPage() {
|
|
| 201 |
}
|
| 202 |
|
| 203 |
if (data.generated) {
|
| 204 |
-
// Extract the three parts from raw_json.
|
| 205 |
-
const extractedMetadataForParts = data.raw_json?.
|
| 206 |
if (extractedMetadataForParts) {
|
| 207 |
if (extractedMetadataForParts.description) {
|
| 208 |
setDescription(extractedMetadataForParts.description);
|
|
@@ -219,8 +219,8 @@ export default function UploadPage() {
|
|
| 219 |
setDraft(data.generated);
|
| 220 |
}
|
| 221 |
|
| 222 |
-
let extractedMetadata = data.raw_json?.
|
| 223 |
-
console.log('Raw
|
| 224 |
|
| 225 |
if (!extractedMetadata && data.generated) {
|
| 226 |
try {
|
|
@@ -642,7 +642,7 @@ export default function UploadPage() {
|
|
| 642 |
setShowFallbackNotification(true);
|
| 643 |
}
|
| 644 |
|
| 645 |
-
const extractedMetadata = (capJson.raw_json as Record<string, unknown>)?.
|
| 646 |
if (extractedMetadata) {
|
| 647 |
const metadata = (extractedMetadata as Record<string, unknown>).metadata || extractedMetadata;
|
| 648 |
if ((metadata as Record<string, unknown>).title) setTitle((metadata as Record<string, unknown>).title as string);
|
|
@@ -668,8 +668,8 @@ export default function UploadPage() {
|
|
| 668 |
}
|
| 669 |
}
|
| 670 |
|
| 671 |
-
// Extract the three parts from raw_json.
|
| 672 |
-
const extractedMetadataForParts = (capJson.raw_json as Record<string, unknown>)?.
|
| 673 |
if (extractedMetadataForParts) {
|
| 674 |
if ((extractedMetadataForParts as Record<string, unknown>).description) {
|
| 675 |
setDescription((extractedMetadataForParts as Record<string, unknown>).description as string);
|
|
@@ -761,7 +761,7 @@ export default function UploadPage() {
|
|
| 761 |
setShowFallbackNotification(true);
|
| 762 |
}
|
| 763 |
|
| 764 |
-
const extractedMetadata = (capJson.raw_json as Record<string, unknown>)?.
|
| 765 |
if (extractedMetadata) {
|
| 766 |
const metadata = (extractedMetadata as Record<string, unknown>).metadata || extractedMetadata;
|
| 767 |
if ((metadata as Record<string, unknown>).title) setTitle((metadata as Record<string, unknown>).title as string);
|
|
@@ -786,8 +786,8 @@ export default function UploadPage() {
|
|
| 786 |
}
|
| 787 |
}
|
| 788 |
|
| 789 |
-
// Extract the three parts from raw_json.
|
| 790 |
-
const extractedMetadataForParts = (capJson.raw_json as Record<string, unknown>)?.
|
| 791 |
if (extractedMetadataForParts) {
|
| 792 |
if ((extractedMetadataForParts as Record<string, unknown>).description) {
|
| 793 |
setDescription((extractedMetadataForParts as Record<string, unknown>).description as string);
|
|
|
|
| 102 |
setImageType(imageTypesData[0].image_type);
|
| 103 |
}
|
| 104 |
});
|
| 105 |
+
}, [searchParams, imageType]);
|
| 106 |
|
| 107 |
const handleNavigation = useCallback((to: string) => {
|
| 108 |
if (to === '/upload' || to === '/') {
|
|
|
|
| 201 |
}
|
| 202 |
|
| 203 |
if (data.generated) {
|
| 204 |
+
// Extract the three parts from raw_json.metadata (same as regular upload flow)
|
| 205 |
+
const extractedMetadataForParts = data.raw_json?.metadata;
|
| 206 |
if (extractedMetadataForParts) {
|
| 207 |
if (extractedMetadataForParts.description) {
|
| 208 |
setDescription(extractedMetadataForParts.description);
|
|
|
|
| 219 |
setDraft(data.generated);
|
| 220 |
}
|
| 221 |
|
| 222 |
+
let extractedMetadata = data.raw_json?.metadata;
|
| 223 |
+
console.log('Raw metadata:', extractedMetadata);
|
| 224 |
|
| 225 |
if (!extractedMetadata && data.generated) {
|
| 226 |
try {
|
|
|
|
| 642 |
setShowFallbackNotification(true);
|
| 643 |
}
|
| 644 |
|
| 645 |
+
const extractedMetadata = (capJson.raw_json as Record<string, unknown>)?.metadata;
|
| 646 |
if (extractedMetadata) {
|
| 647 |
const metadata = (extractedMetadata as Record<string, unknown>).metadata || extractedMetadata;
|
| 648 |
if ((metadata as Record<string, unknown>).title) setTitle((metadata as Record<string, unknown>).title as string);
|
|
|
|
| 668 |
}
|
| 669 |
}
|
| 670 |
|
| 671 |
+
// Extract the three parts from raw_json.metadata
|
| 672 |
+
const extractedMetadataForParts = (capJson.raw_json as Record<string, unknown>)?.metadata;
|
| 673 |
if (extractedMetadataForParts) {
|
| 674 |
if ((extractedMetadataForParts as Record<string, unknown>).description) {
|
| 675 |
setDescription((extractedMetadataForParts as Record<string, unknown>).description as string);
|
|
|
|
| 761 |
setShowFallbackNotification(true);
|
| 762 |
}
|
| 763 |
|
| 764 |
+
const extractedMetadata = (capJson.raw_json as Record<string, unknown>)?.metadata;
|
| 765 |
if (extractedMetadata) {
|
| 766 |
const metadata = (extractedMetadata as Record<string, unknown>).metadata || extractedMetadata;
|
| 767 |
if ((metadata as Record<string, unknown>).title) setTitle((metadata as Record<string, unknown>).title as string);
|
|
|
|
| 786 |
}
|
| 787 |
}
|
| 788 |
|
| 789 |
+
// Extract the three parts from raw_json.metadata
|
| 790 |
+
const extractedMetadataForParts = (capJson.raw_json as Record<string, unknown>)?.metadata;
|
| 791 |
if (extractedMetadataForParts) {
|
| 792 |
if ((extractedMetadataForParts as Record<string, unknown>).description) {
|
| 793 |
setDescription((extractedMetadataForParts as Record<string, unknown>).description as string);
|