176 lines
6.7 KiB
TypeScript
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;
|