![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.ca/private_html/src/pages/admin/applications/ |
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/react';
import LayoutWithSidebar from '@/components/LayoutWithSidebar';
import RegistrationForm from '@/components/RegistrationForm';
import { motion } from 'framer-motion';
import { getFacilityName } from '@/utils/facilities';
interface RegistrationWithDetails {
id: string;
firstName: string;
lastName: string;
email: string;
phone: string;
birthDate: string;
relationship: string;
preferredLanguage: string;
preferredContactMethod: string;
message?: string;
status: 'PENDING' | 'APPROVED' | 'REJECTED';
createdAt: string;
updatedAt: string;
userId: string;
address?: {
street: string;
city: string;
state: string;
postalCode: string;
country: string;
};
detaineeInfo?: {
name: string;
facility: string;
inmateId: string;
incarcerationDate: string;
expectedReleaseDate?: string;
};
file?: {
name: string;
type: string;
url: string;
};
}
export default function AdminApplicationView() {
const { data: session, status } = useSession();
const router = useRouter();
const { id } = router.query;
const [registration, setRegistration] = useState<RegistrationWithDetails | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [isEditing, setIsEditing] = useState(false);
const [isFrench, setIsFrench] = useState(false);
// Debug logging for state changes
useEffect(() => {
console.log('🔄 State update:', {
loading,
error,
hasRegistration: !!registration,
status,
userRole: session?.user?.role
});
}, [loading, error, registration, status, session?.user?.role]);
useEffect(() => {
if (status === 'unauthenticated') {
router.push('/auth/login');
return;
}
// ...rest of your logic
}, [status, router]);
useEffect(() => {
const fetchRegistration = async () => {
if (!id || typeof id !== 'string') return;
try {
console.log('🔍 Fetching registration:', id);
const response = await fetch(`/api/admin/registrations/${id}`);
console.log('📡 Response status:', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
console.error('❌ API Error:', errorData);
throw new Error(`Failed to fetch registration: ${response.status} ${errorData.error || ''}`);
}
const data = await response.json();
console.log('✅ Registration data:', data);
setRegistration(data);
} catch (err) {
console.error('❌ Fetch error:', err);
setError(err instanceof Error ? err.message : 'Error loading registration');
} finally {
setLoading(false);
}
};
if (status === 'authenticated' && ['ADMIN', 'SUPERADMIN'].includes(session?.user?.role)) {
console.log('🔐 User authenticated with role:', session.user.role);
fetchRegistration();
} else {
console.log('❌ User not authenticated or wrong role:', {
status,
role: session?.user?.role
});
}
}, [id, status, session]);
const handleStatusChange = async (newStatus: 'PENDING' | 'APPROVED' | 'REJECTED') => {
if (!registration) return;
try {
const response = await fetch(`/api/admin/registrations/${registration.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: newStatus }),
});
if (!response.ok) {
throw new Error('Failed to update status');
}
const updatedRegistration = await response.json();
setRegistration(updatedRegistration);
} catch (err) {
setError('Error updating status');
console.error(err);
}
};
const handleSave = async (formData: any) => {
if (!registration) return;
try {
// First update the registration data
const response = await fetch(`/api/admin/registrations/${registration.id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
if (!response.ok) {
throw new Error('Failed to update registration');
}
const updatedRegistration = await response.json();
// If there's a file, upload it separately
if (formData.file) {
const fileFormData = new FormData();
fileFormData.append('file', formData.file);
fileFormData.append('registrationId', registration.id);
const fileResponse = await fetch('/api/upload', {
method: 'POST',
body: fileFormData,
});
if (!fileResponse.ok) {
throw new Error('Failed to upload file');
}
}
setRegistration(updatedRegistration);
setIsEditing(false);
} catch (err) {
setError('Error updating registration');
console.error(err);
}
};
if (status === 'loading') {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading session...</p>
</div>
</div>
);
}
if (!session || !['ADMIN', 'SUPERADMIN'].includes(session.user.role)) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h1 className="text-2xl font-bold text-red-600 mb-4">Access Denied</h1>
<p className="text-gray-600">You don't have permission to view this page.</p>
</div>
</div>
);
}
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading application...</p>
</div>
</div>
);
}
if (error) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h1 className="text-2xl font-bold text-red-600 mb-4">Error</h1>
<p className="text-gray-600">{error}</p>
<button
onClick={() => router.push('/admin')}
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
>
Back to Admin
</button>
</div>
</div>
);
}
if (!registration) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<h1 className="text-2xl font-bold text-red-600 mb-4">Application Not Found</h1>
<p className="text-gray-600">The application you're looking for doesn't exist.</p>
<button
onClick={() => router.push('/admin')}
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
>
Back to Admin
</button>
</div>
</div>
);
}
return (
<LayoutWithSidebar>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-8 flex justify-between items-center">
<div className="flex items-center space-x-4">
<button
onClick={() => router.push('/admin')}
className="flex items-center text-gray-600 hover:text-gray-900"
>
<svg
className="w-6 h-6 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M10 19l-7-7m0 0l7-7m-7 7h18"
/>
</svg>
{isFrench ? 'Retour' : 'Back'}
</button>
<h1 className="text-2xl font-bold text-gray-900">
{isFrench ? 'Détails de la demande' : 'Application Details'}
</h1>
</div>
<div className="flex space-x-4">
<select
value={registration.status}
onChange={(e) => handleStatusChange(e.target.value as 'PENDING' | 'APPROVED' | 'REJECTED')}
className={`px-4 py-2 rounded-md ${
registration.status === 'APPROVED' ? 'bg-green-100 text-green-800' :
registration.status === 'REJECTED' ? 'bg-red-100 text-red-800' :
'bg-yellow-100 text-yellow-800'
}`}
>
<option value="PENDING">{isFrench ? 'En attente' : 'Pending'}</option>
<option value="APPROVED">{isFrench ? 'Approuvé' : 'Approved'}</option>
<option value="REJECTED">{isFrench ? 'Rejeté' : 'Rejected'}</option>
</select>
<button
onClick={() => setIsEditing(!isEditing)}
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
>
{isEditing
? (isFrench ? 'Annuler' : 'Cancel')
: (isFrench ? 'Modifier' : 'Edit')
}
</button>
</div>
</div>
{error && (
<div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-md text-red-600">
{error}
</div>
)}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<RegistrationForm
isFrench={isFrench}
initialLocale={registration.preferredLanguage}
initialData={registration}
isEditing={isEditing}
onSave={handleSave}
isAdmin={true}
/>
</motion.div>
<div className="mt-4">
<div className="mb-2"><b>Facility:</b> {registration.detaineeInfo?.facility ? getFacilityName(registration.detaineeInfo.facility) : '-'}</div>
</div>
</div>
</LayoutWithSidebar>
);
}