migrants-nt-web/src/pages/MigrantProfilePage.tsx

176 lines
6.7 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import apiService from "../services/apiService";
import type { MigrantProfile } from "../types/migrant";
import MigrantProfileComponent from "../components/MigrantProfileComponent";
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
import { AlertCircle, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
// Helper function to calculate age at migration
const calculateAgeAtMigration = (birthDate: string | null | undefined, migrationDate: string | null | undefined): number => {
if (!birthDate || !migrationDate) return 0;
try {
const normalizedBirthDate = birthDate.includes('.') ? birthDate.split('.')[0] + 'Z' : birthDate;
const normalizedMigrationDate = migrationDate.includes('.') ? migrationDate.split('.')[0] + 'Z' : migrationDate;
const birthYear = new Date(normalizedBirthDate).getFullYear();
const migrationYear = new Date(normalizedMigrationDate).getFullYear();
// Simple year difference calculation
const age = migrationYear - birthYear;
return age >= 0 ? age : 0;
} catch (error) {
console.error(`Error calculating age: birth=${birthDate}, migration=${migrationDate}`, error);
return 0;
}
};
const MigrantProfilePage = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const [migrant, setMigrant] = useState<MigrantProfile | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [retryCount, setRetryCount] = useState(0);
// Fetch migrant data when component mounts or ID changes
useEffect(() => {
const fetchMigrantData = async () => {
// Reset state when ID changes
setLoading(true);
setMigrant(null);
setError(null);
if (!id) {
setError('Missing migrant ID');
setLoading(false);
return;
}
try {
// Fetch migrant data from the backend using apiService
const data = await apiService.getRecordById(id);
if (data) {
// Data successfully retrieved - convert API data to MigrantProfile
const migrantProfile: MigrantProfile = {
id: data.person_id || id,
firstName: data.christian_name || '',
lastName: data.surname || '',
middleName: '',
birthDate: data.date_of_birth || '',
birthPlace: data.place_of_birth || '',
ageAtMigration: calculateAgeAtMigration(data.date_of_birth, data.migration?.date_of_arrival_nt),
yearOfArrival: data.migration?.date_of_arrival_nt ?
new Date(data.migration.date_of_arrival_nt).getFullYear() : 0,
regionOfOrigin: data.place_of_birth || 'Unknown',
settlementLocation: data.residence?.town_or_city || 'Unknown',
occupation: data.occupation || 'Unknown',
deathDate: data.date_of_death || '',
deathPlace: data.residence?.home_at_death || '',
mainPhoto: '', // No photo URL in the API response
biography: data.additional_notes || '',
photos: [],
relatedMigrants: []
};
setMigrant(migrantProfile);
setError(null);
} else {
// No data found for this ID
setMigrant(null);
setError(`No migrant found with ID: ${id}`);
}
} catch (error: any) {
// Handle API errors
const errorMessage =
error.response?.data?.message ||
error.message ||
'An unexpected error occurred';
console.error('Error fetching migrant:', errorMessage);
setError(`Failed to load migrant data: ${errorMessage}`);
} finally {
setLoading(false);
}
};
fetchMigrantData();
}, [id, retryCount]); // retryCount allows manual retries
// Handle retry button click
const handleRetry = () => {
setRetryCount(prev => prev + 1);
};
// Loading state
if (loading) {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh] p-8">
<Loader2 className="h-12 w-12 animate-spin text-primary mb-4" />
<h2 className="text-xl font-medium mb-2">Loading migrant profile...</h2>
<p className="text-gray-500">Retrieving data from the database</p>
</div>
);
}
// Error state
if (error) {
// Check if the error is specifically about not finding a migrant
const isNotFoundError = error.includes('No migrant found');
return (
<div className="max-w-3xl mx-auto p-8">
<Alert variant={isNotFoundError ? "default" : "destructive"} className={`mb-6 ${isNotFoundError ? 'border-amber-500' : ''}`}>
<AlertCircle className={`h-5 w-5 ${isNotFoundError ? 'text-amber-500' : ''}`} />
<AlertTitle>{isNotFoundError ? "Migrant Not Found" : "Error"}</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
<div className="flex flex-col items-center mt-8 mb-4">
{isNotFoundError && (
<div className="text-center mb-6">
<p className="text-lg mb-4">The migrant profile you're looking for could not be found in our database.</p>
<p className="text-gray-600">This might be because:</p>
<ul className="list-disc list-inside text-gray-600 mt-2 mb-4">
<li>The ID provided is incorrect</li>
<li>The record has been removed from the database</li>
<li>The record hasn't been added to the database yet</li>
</ul>
</div>
)}
<div className="flex gap-4 justify-center">
<Button onClick={() => navigate(-1)}>Go Back</Button>
{!isNotFoundError && (
<Button variant="outline" onClick={handleRetry}>Retry</Button>
)}
<Button variant="outline" onClick={() => window.location.href = '/search-test'}>Search Again</Button>
</div>
</div>
</div>
);
}
// No data state (should be caught by error state, but just in case)
if (!migrant) {
return (
<div className="max-w-3xl mx-auto p-8 text-center">
<h2 className="text-2xl font-bold mb-4">Migrant Profile Not Found</h2>
<p className="mb-6">The migrant profile you're looking for could not be found.</p>
<Button onClick={() => navigate(-1)}>Return to Previous Page</Button>
</div>
);
}
// Render migrant profile when data is loaded successfully
return <MigrantProfileComponent migrant={migrant} />;
};
export default MigrantProfilePage;