export button
This commit is contained in:
70
apps/admin/app/(main)/jobs/[jobId]/ExportButton.tsx
Normal file
70
apps/admin/app/(main)/jobs/[jobId]/ExportButton.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@workspace/ui/components/button';
|
||||||
|
import { Download } from 'lucide-react';
|
||||||
|
|
||||||
|
type Applicant = {
|
||||||
|
applicationId: number;
|
||||||
|
status: string;
|
||||||
|
firstName: string | null;
|
||||||
|
lastName: string | null;
|
||||||
|
email: string | null;
|
||||||
|
studentId: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ExportButtonProps {
|
||||||
|
applicants: Applicant[];
|
||||||
|
jobId: number;
|
||||||
|
jobTitle: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ExportButton({ applicants, jobId, jobTitle }: ExportButtonProps) {
|
||||||
|
const exportToCSV = () => {
|
||||||
|
if (applicants.length === 0) {
|
||||||
|
alert('No applications to export.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = ['Application ID', 'Student Name', 'Email', 'Status'];
|
||||||
|
const csvData = applicants.map(applicant => [
|
||||||
|
applicant.applicationId.toString(),
|
||||||
|
`${applicant.firstName ?? ''} ${applicant.lastName ?? ''}`.trim() || 'Unknown',
|
||||||
|
applicant.email || '',
|
||||||
|
applicant.status
|
||||||
|
]);
|
||||||
|
|
||||||
|
const csvContent = [
|
||||||
|
headers.join(','),
|
||||||
|
...csvData.map(row => row.map(field => `"${field.replace(/"/g, '""')}"`).join(','))
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||||
|
const link = document.createElement('a');
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Clean job title for filename
|
||||||
|
const cleanJobTitle = jobTitle.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
|
||||||
|
const filename = `${cleanJobTitle}-job-${jobId}-applications-${new Date().toISOString().split('T')[0]}.csv`;
|
||||||
|
|
||||||
|
link.setAttribute('href', url);
|
||||||
|
link.setAttribute('download', filename);
|
||||||
|
link.style.visibility = 'hidden';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={exportToCSV}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="flex items-center gap-2 text-gray-700 hover:text-gray-900 border-gray-300 hover:border-gray-400"
|
||||||
|
disabled={applicants.length === 0}
|
||||||
|
>
|
||||||
|
<Download className="w-4 h-4" />
|
||||||
|
Export to CSV
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,11 +15,13 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
Users,
|
Users,
|
||||||
FileText,
|
FileText,
|
||||||
Clock
|
Clock,
|
||||||
|
Download
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import StatusSelect from './StatusSelect';
|
import StatusSelect from './StatusSelect';
|
||||||
import ApplicationsTable from './ApplicationsTable';
|
import ApplicationsTable from './ApplicationsTable';
|
||||||
|
import ExportButton from './ExportButton';
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
@@ -303,13 +305,22 @@ export default async function JobDetailPage({ params }: { params: Promise<{ jobI
|
|||||||
<div className="mt-12">
|
<div className="mt-12">
|
||||||
<Card className="border border-gray-200 shadow-sm">
|
<Card className="border border-gray-200 shadow-sm">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<div className="flex items-center justify-between">
|
||||||
<Users className="w-5 h-5 text-gray-600" />
|
<div>
|
||||||
Student Applications ({applicants.length})
|
<CardTitle className="flex items-center gap-2">
|
||||||
</CardTitle>
|
<Users className="w-5 h-5 text-gray-600" />
|
||||||
<CardDescription>
|
Student Applications ({applicants.length})
|
||||||
View all students who have applied for this position
|
</CardTitle>
|
||||||
</CardDescription>
|
<CardDescription>
|
||||||
|
View all students who have applied for this position
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<ExportButton
|
||||||
|
applicants={applicants}
|
||||||
|
jobId={jobId}
|
||||||
|
jobTitle={job.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{applicants.length === 0 ? (
|
{applicants.length === 0 ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user