configService->context(); $drafts = DB::table('exam_drafts as ed') ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'ed.class_section_id') ->leftJoin('users as u', 'u.id', '=', 'ed.teacher_id') ->leftJoin('users as a', 'a.id', '=', 'ed.admin_id') ->select( 'ed.*', 'cs.class_section_name', 'u.firstname as teacher_first', 'u.lastname as teacher_last', 'a.firstname as admin_first', 'a.lastname as admin_last' ) ->orderByDesc('ed.created_at') ->get() ->map(fn ($r) => (array) $r) ->all(); $classSections = ClassSection::query() ->select('class_section_id', 'class_section_name') ->orderBy('class_section_name') ->get() ->toArray(); $legacyByClass = []; if ($context['has_legacy'] ?? false) { foreach ($drafts as $row) { $isLegacy = !empty($row['is_legacy']); if (!$isLegacy) { continue; } $cid = (int) ($row['class_section_id'] ?? 0); if (!isset($legacyByClass[$cid])) { $legacyByClass[$cid] = [ 'class_section_id' => $cid, 'class_section_name' => $row['class_section_name'] ?? 'Class ' . $cid, 'items' => [], ]; } $legacyByClass[$cid]['items'][] = $row; } } return [ 'drafts' => $drafts, 'classSections' => $classSections, 'legacyByClass' => $legacyByClass, 'examTypes' => ExamDraftConfigService::EXAM_TYPES, 'statusOptions' => ExamDraftConfigService::STATUS_OPTIONS, 'schoolYear' => (string) ($context['school_year'] ?? ''), 'semester' => (string) ($context['semester'] ?? ''), 'allowedExtensions' => ExamDraftConfigService::ADMIN_ALLOWED_EXTENSIONS, 'maxUploadBytes' => ExamDraftConfigService::MAX_UPLOAD_BYTES, ]; } public function uploadLegacy(int $adminId, array $payload, \Illuminate\Http\UploadedFile $file): array { $context = $this->configService->context(); $classSectionId = (int) ($payload['class_section_id'] ?? 0); $schoolYear = trim((string) ($payload['school_year'] ?? $context['school_year'] ?? '')); $semester = trim((string) ($payload['semester'] ?? $context['semester'] ?? '')); $examType = trim((string) ($payload['exam_type'] ?? '')); if ($classSectionId <= 0 || $schoolYear === '' || $semester === '') { return ['ok' => false, 'message' => 'Select class section, school year, and semester.']; } $stored = $this->fileService->storeUploadedFile( $file, ExamDraftConfigService::FINAL_UPLOAD_DIR, ExamDraftConfigService::ADMIN_ALLOWED_EXTENSIONS, ExamDraftConfigService::MAX_UPLOAD_BYTES ); if ($stored === null) { return ['ok' => false, 'message' => 'File type not allowed or upload failed.']; } $payload = [ 'teacher_id' => $adminId, 'class_section_id' => $classSectionId, 'semester' => ucfirst(strtolower($semester)), 'school_year' => $schoolYear, 'exam_type' => $examType !== '' ? $examType : null, 'draft_title' => $examType !== '' ? $examType : 'Legacy Exam', 'final_file' => $stored['stored'], 'final_filename' => $stored['original'], 'status' => 'finalized', 'admin_id' => $adminId, 'reviewed_at' => now(), 'version' => 1, ]; if ($context['has_legacy'] ?? false) { $payload['is_legacy'] = 1; } $pdfName = null; if ($stored['extension'] === 'pdf') { $pdfName = $stored['stored']; } else { $pdfName = $this->fileService->ensurePdfExists( $stored['stored'], $stored['extension'], ExamDraftConfigService::FINAL_UPLOAD_DIR ); } if ($pdfName !== null && ($context['has_final_pdf'] ?? false)) { $payload['final_pdf_file'] = $pdfName; } $draft = ExamDraft::query()->create($payload); return ['ok' => true, 'draft' => $draft->toArray()]; } public function review(int $adminId, array $payload, ?\Illuminate\Http\UploadedFile $file): array { $draftId = (int) ($payload['draft_id'] ?? 0); if ($draftId <= 0) { return ['ok' => false, 'message' => 'Invalid submission selected.']; } $draft = ExamDraft::query()->find($draftId); if (!$draft) { return ['ok' => false, 'message' => 'Submission not found.']; } $comments = trim((string) ($payload['admin_comments'] ?? '')); $statusInput = trim((string) ($payload['review_status'] ?? '')); $currentStatus = (string) ($draft->status ?? 'draft'); $status = $this->normalizeStatus($statusInput !== '' ? $statusInput : $currentStatus, $currentStatus); if (!in_array($status, ExamDraftConfigService::STATUS_OPTIONS, true)) { $status = 'reviewed'; } $update = [ 'status' => $status, 'admin_comments' => $comments !== '' ? $comments : null, 'admin_id' => $adminId, 'reviewed_at' => now(), ]; if ($file) { $stored = $this->fileService->storeUploadedFile( $file, ExamDraftConfigService::FINAL_UPLOAD_DIR, ExamDraftConfigService::ADMIN_ALLOWED_EXTENSIONS, ExamDraftConfigService::MAX_UPLOAD_BYTES ); if ($stored === null) { return ['ok' => false, 'message' => 'Unable to store the final draft.']; } $update['final_file'] = $stored['stored']; $update['final_filename'] = $stored['original']; $pdfName = $this->fileService->ensurePdfExists( $stored['stored'], $stored['extension'], ExamDraftConfigService::FINAL_UPLOAD_DIR ); if ($pdfName !== null && ($this->configService->context()['has_final_pdf'] ?? false)) { $update['final_pdf_file'] = $pdfName; } } elseif ($status === 'finalized' && !empty($draft->teacher_file)) { $copied = $this->fileService->copyDraftToFinal( (string) $draft->teacher_file, ExamDraftConfigService::TEACHER_UPLOAD_DIR, ExamDraftConfigService::FINAL_UPLOAD_DIR ); if ($copied !== null) { $update['final_file'] = $copied; $update['final_filename'] = $draft->teacher_filename ?? $draft->teacher_file; $pdfName = $this->fileService->ensurePdfExists( $copied, pathinfo($copied, PATHINFO_EXTENSION), ExamDraftConfigService::FINAL_UPLOAD_DIR ); if ($pdfName !== null && ($this->configService->context()['has_final_pdf'] ?? false)) { $update['final_pdf_file'] = $pdfName; } } } if ($status === 'finalized' && ($this->configService->context()['has_legacy'] ?? false)) { $update['is_legacy'] = 1; } $draft->update($update); return ['ok' => true, 'draft' => $draft->fresh()->toArray()]; } private function normalizeStatus(string $input, string $default): string { $input = strtolower(trim($input)); $aliases = [ 'pending' => 'submitted', 'final' => 'finalized', 'approved' => 'finalized', ]; if (isset($aliases[$input])) { $input = $aliases[$input]; } return in_array($input, ExamDraftConfigService::STATUS_OPTIONS, true) ? $input : $default; } }