'use client'; import React, { useState, useEffect } from 'react'; import { CareerApplication } from '@/app/types'; import { apiClient } from '@/app/utils/apiClient'; import { Box, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Button, Chip, TextField, MenuItem, Pagination, Dialog, DialogTitle, DialogContent, DialogActions, IconButton, CircularProgress, Alert, Tooltip, FormControlLabel, Switch, Tabs, Tab } from '@mui/material'; import VisibilityIcon from '@mui/icons-material/Visibility'; import EmailIcon from '@mui/icons-material/Email'; import CloudDownloadIcon from '@mui/icons-material/CloudDownload'; import CheckIcon from '@mui/icons-material/Check'; import CloseIcon from '@mui/icons-material/Close'; const statusColors: Record = { pending: 'warning', approved: 'success', rejected: 'error' }; interface ApplicationDetailsProps { application: CareerApplication; onClose: () => void; onStatusChange: (id: number, status: string, feedbackNote?: string) => Promise; } const ApplicationDetails: React.FC = ({ application, onClose, onStatusChange }) => { const [loading, setLoading] = useState(false); const [feedbackNote, setFeedbackNote] = useState(''); const [activeTab, setActiveTab] = useState(0); const handleStatusChange = async (status: string) => { setLoading(true); try { await onStatusChange(application.id, status, feedbackNote); onClose(); } catch (error) { console.error('Error changing status:', error); } finally { setLoading(false); } }; const handleDownload = async (fileType: 'resume' | 'coverLetter') => { try { const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080'; const token = localStorage.getItem('adminToken'); const url = `${baseUrl}/careers/applications/${application.id}/download?fileType=${fileType}&token=${token}`; window.open(url, '_blank'); } catch (error: any) { console.error(`Error downloading ${fileType}:`, error); alert(`Failed to download ${fileType === 'resume' ? 'resume' : 'cover letter'}`); } }; return ( Application Details setActiveTab(newValue)} sx={{ mb: 2, borderBottom: 1, borderColor: 'divider' }} > {activeTab === 0 && ( {application.name} Applied for: {application.career?.title} Applied on: {new Date(application.createdAt).toLocaleDateString()} Contact Information Email: {application.email} Phone: {application.phone} )} {activeTab === 1 && ( {application.career?.title} {application.career?.location} Job Description: {application.career?.description} )} {activeTab === 2 && ( Application Documents )} {application.status === 'pending' && ( Application Feedback setFeedbackNote(e.target.value)} placeholder="This feedback will be sent to the applicant when you approve or reject" margin="normal" /> )} {application.status === 'pending' && ( )} ); }; const ApplicationList: React.FC = () => { const [applications, setApplications] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [selectedStatus, setSelectedStatus] = useState('all'); const [selectedCareerId, setSelectedCareerId] = useState(''); const [careers, setCareers] = useState<{ id: number, title: string }[]>([]); const [detailsOpen, setDetailsOpen] = useState(false); const [selectedApplication, setSelectedApplication] = useState(null); const [bulkActionEnabled, setBulkActionEnabled] = useState(false); const [selectedIds, setSelectedIds] = useState([]); const [bulkStatus, setBulkStatus] = useState<'pending' | 'approved' | 'rejected'>('approved'); const fetchApplications = async () => { setLoading(true); setError(null); try { const params = new URLSearchParams(); params.append('page', page.toString()); params.append('limit', '10'); if (selectedStatus !== 'all') { params.append('status', selectedStatus); } if (selectedCareerId) { params.append('careerId', selectedCareerId); } const response = await apiClient(`/careers/applications?${params.toString()}`); if (response.success) { setApplications(response.data); setTotalPages(response.totalPages || 1); } else { setError('Failed to load applications'); } } catch (err) { console.error('Error fetching applications:', err); setError('Failed to load applications. Please try again.'); } finally { setLoading(false); } }; const fetchCareers = async () => { try { const response = await apiClient('/careers/listings?status=active&limit=100'); if (response.success) { setCareers(response.data.map((career: any) => ({ id: career.id, title: career.title }))); } } catch (err) { console.error('Error fetching careers for dropdown:', err); } }; useEffect(() => { fetchCareers(); }, []); useEffect(() => { fetchApplications(); }, [page, selectedStatus, selectedCareerId]); const handlePageChange = (event: React.ChangeEvent, value: number) => { setPage(value); }; const handleStatusChange = (event: React.ChangeEvent) => { setSelectedStatus(event.target.value); setPage(1); // Reset to first page }; const handleCareerChange = (event: React.ChangeEvent) => { setSelectedCareerId(event.target.value); setPage(1); // Reset to first page }; const handleViewDetails = (application: CareerApplication) => { setSelectedApplication(application); setDetailsOpen(true); }; const handleStatusUpdate = async (id: number, status: string, feedbackNote?: string): Promise => { try { const response = await apiClient(`/careers/applications/${id}/status`, { method: 'PUT', body: JSON.stringify({ status, feedbackNote }) }); if (response.success) { // Update application in the list with proper typing setApplications(prev => prev.map(app => app.id === id ? { ...app, status: status as CareerApplication['status'] } : app ) ); // Update selected application if it's open if (selectedApplication && selectedApplication.id === id) { setSelectedApplication(prev => prev ? { ...prev, status: status as CareerApplication['status'] } : null ); } // Return void instead of boolean } else { throw new Error('Failed to update status'); } } catch (err) { console.error('Error updating application status:', err); setError('Failed to update application status. Please try again.'); throw err; } }; const handleBulkSelection = (appId: number, checked: boolean) => { if (checked) { setSelectedIds(prev => [...prev, appId]); } else { setSelectedIds(prev => prev.filter(id => id !== appId)); } }; const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedIds(applications.map(app => app.id)); } else { setSelectedIds([]); } }; const handleBulkStatusUpdate = async () => { if (selectedIds.length === 0) return; try { const response = await apiClient('/careers/applications/bulk-update-status', { method: 'PUT', body: JSON.stringify({ applicationIds: selectedIds, status: bulkStatus }) }); if (response.success) { fetchApplications(); setSelectedIds([]); setBulkActionEnabled(false); } else { setError('Failed to update application statuses'); } } catch (err) { console.error('Error updating application statuses:', err); setError('Failed to update application statuses. Please try again.'); } }; return ( {error && ( {error} )} Job Applications All Status Pending Approved Rejected All Positions {careers.map((career) => ( {career.title} ))} {bulkActionEnabled && selectedIds.length > 0 && ( {selectedIds.length} application(s) selected setBulkStatus(e.target.value as 'pending' | 'approved' | 'rejected')} size="small" sx={{ minWidth: 150 }} > Pending Approved Rejected )} { setBulkActionEnabled(e.target.checked); if (!e.target.checked) { setSelectedIds([]); } }} /> } label="Enable Bulk Actions" /> {bulkActionEnabled && ( 0} onChange={(e) => handleSelectAll(e.target.checked)} size="small" /> } label="" /> )} Applicant Position Applied Date Status Actions {loading ? ( ) : applications.length === 0 ? ( No applications found ) : ( applications.map((application) => ( {bulkActionEnabled && ( handleBulkSelection(application.id, e.target.checked)} size="small" /> } label="" /> )} {application.name} {application.email} {application.career?.title || `Job ID: ${application.career_id}`} {new Date(application.createdAt).toLocaleDateString()} handleViewDetails(application)}> {application.status === 'pending' && ( <> handleStatusUpdate(application.id, 'approved')} color="success" > handleStatusUpdate(application.id, 'rejected')} color="error" > )} )) )}
{/* Application Details Dialog */} {detailsOpen && selectedApplication && ( { setDetailsOpen(false); setSelectedApplication(null); }} onStatusChange={handleStatusUpdate} /> )}
); }; export default ApplicationList;