feat: implementation des dossiers candidats PDF, gestion des entretiens et optimisation de l'analyse IA

This commit is contained in:
jeremy bayse
2026-04-19 15:35:16 +02:00
parent 4017e3d9c5
commit f3d630d741
27 changed files with 2550 additions and 741 deletions

View File

@@ -0,0 +1,131 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Candidate;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
class CandidateExportController extends Controller
{
public function exportDossier(Candidate $candidate)
{
$candidate->load([
'user',
'jobPosition',
'tenant',
'attempts.quiz.questions',
'attempts.answers.option',
'attempts.answers.question',
'documents'
]);
$filename = 'Dossier_' . str_replace(' ', '_', $candidate->user->name) . '_' . date('Ymd') . '.pdf';
// 1. Generate Main Report with DomPDF
$pdfReport = Pdf::loadView('pdfs.candidate-dossier', [
'candidate' => $candidate
]);
$reportBinary = $pdfReport->output();
// 2. Setup FPDI for merging
$mergedPdf = new \setasign\Fpdi\Fpdi();
// Add Main Report Pages
$reportTmp = tempnam(sys_get_temp_dir(), 'pdf_report_');
file_put_contents($reportTmp, $reportBinary);
try {
$pageCount = $mergedPdf->setSourceFile($reportTmp);
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
$templateId = $mergedPdf->importPage($pageNo);
$size = $mergedPdf->getTemplateSize($templateId);
$mergedPdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
$mergedPdf->useTemplate($templateId);
}
} catch (\Exception $e) {
\Log::error('FPDI Error on report: ' . $e->getMessage());
}
@unlink($reportTmp);
// 3. Append Candidate Documents (CV, Letter)
foreach ($candidate->documents as $doc) {
if (\Storage::disk('local')->exists($doc->file_path)) {
$filePath = \Storage::disk('local')->path($doc->file_path);
try {
$pageCount = $mergedPdf->setSourceFile($filePath);
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
$templateId = $mergedPdf->importPage($pageNo);
$size = $mergedPdf->getTemplateSize($templateId);
$mergedPdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
$mergedPdf->useTemplate($templateId);
}
} catch (\Exception $e) {
\Log::warning('Could not merge document ID ' . $doc->id . ': ' . $e->getMessage());
// Add a professional placeholder page for unmergable documents
$mergedPdf->AddPage('P', 'A4');
$mergedPdf->SetFont('Arial', 'B', 16);
$mergedPdf->SetTextColor(0, 79, 130); // Primary color
$mergedPdf->Ln(50);
$mergedPdf->Cell(0, 20, utf8_decode("DOCUMENT JOINT : " . strtoupper($doc->type)), 0, 1, 'C');
$mergedPdf->SetFont('Arial', 'I', 12);
$mergedPdf->Cell(0, 10, utf8_decode($doc->original_name), 0, 1, 'C');
$mergedPdf->Ln(30);
$mergedPdf->SetFont('Arial', '', 11);
$mergedPdf->SetTextColor(100, 100, 100);
$mergedPdf->MultiCell(0, 8, utf8_decode("Ce document n'a pas pu être fusionné automatiquement au dossier car son format est trop récent (PDF 1.5+).\n\nPour garantir l'intégrité de la mise en page, veuillez consulter ce document séparément via l'interface du tableau de bord candidat."), 0, 'C');
$mergedPdf->Ln(20);
$mergedPdf->SetDrawColor(224, 176, 76); // Highlight color
$mergedPdf->Line(60, $mergedPdf->GetY(), 150, $mergedPdf->GetY());
}
}
}
return response($mergedPdf->Output('S'), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
'Cache-Control' => 'no-cache, no-store, must-revalidate',
'Pragma' => 'no-cache',
'Expires' => '0',
]);
}
public function exportZip(Candidate $candidate)
{
$candidate->load(['user', 'jobPosition', 'tenant', 'attempts.quiz.questions', 'attempts.answers.option', 'attempts.answers.question', 'documents']);
$baseName = 'Dossier_' . str_replace(' ', '_', $candidate->user->name) . '_' . date('Ymd');
$zipPath = tempnam(sys_get_temp_dir(), 'candidate_zip_');
$zip = new \ZipArchive();
if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
return back()->with('error', 'Impossible de créer le fichier ZIP.');
}
// 1. Add the main report (Rapport CABM)
$pdfReport = Pdf::loadView('pdfs.candidate-dossier', [
'candidate' => $candidate
]);
$zip->addFromString($baseName . '/Rapport_Synthese_CABM.pdf', $pdfReport->output());
// 2. Add original documents
foreach ($candidate->documents as $doc) {
if (\Storage::disk('local')->exists($doc->file_path)) {
$content = \Storage::disk('local')->get($doc->file_path);
// Sanitize original name or use type
$ext = pathinfo($doc->original_name, PATHINFO_EXTENSION);
$fileName = strtoupper($doc->type) . '_' . $doc->original_name;
$zip->addFromString($baseName . '/Documents_Originaux/' . $fileName, $content);
}
}
$zip->close();
return response()->download($zipPath, $baseName . '.zip')->deleteFileAfterSend(true);
}
}