authorizeResource(ClassProgressReport::class, 'class_progress'); } public function meta(ClassProgressMetaRequest $request): JsonResponse { return $this->respondSuccess( $this->buildMetaPayload( $request->validated('class_id'), (int) ($request->validated('sunday_count') ?? 12) ), 'Progress metadata loaded.' ); } /** * Legacy teacher portal URL for the weekly class progress submit page. */ public function legacySubmitForm(ClassProgressMetaRequest $request): JsonResponse { $auth = $this->requireAuthenticatedUser($request->user()); if ($auth instanceof JsonResponse) { return $auth; } $schoolYear = (string) (Configuration::getConfig('school_year') ?? ''); $semester = (string) (Configuration::getConfig('semester') ?? ''); $assignments = $this->queryService->teacherAssignments($auth, $schoolYear, $semester); $requestedClassId = (int) ($request->validated('class_id') ?? 0); $activeClassId = $requestedClassId > 0 ? $requestedClassId : (int) ($assignments[0]['class_id'] ?? 0); $activeAssignment = collect($assignments)->first( fn (array $assignment) => (int) ($assignment['class_id'] ?? 0) === $activeClassId ) ?? ($assignments[0] ?? []); $sundayCount = (int) ($request->validated('sunday_count') ?? 12); $payload = $this->buildMetaPayload($activeClassId > 0 ? $activeClassId : null, $sundayCount); $payload['classes'] = $assignments; $payload['defaults'] = [ 'class_id' => $activeClassId > 0 ? $activeClassId : null, 'class_section_id' => (int) ($activeAssignment['class_section_id'] ?? 0) ?: null, 'school_year' => $schoolYear !== '' ? $schoolYear : null, 'semester' => $semester !== '' ? $semester : null, 'week_start' => $this->metaService->pickDefaultWeekStart($payload['sunday_options']), ]; return $this->respondSuccess($payload, 'Progress metadata loaded.'); } public function index(ClassProgressIndexRequest $request): JsonResponse { $auth = $this->requireAuthenticatedUser($request->user()); if ($auth instanceof JsonResponse) { return $auth; } $filters = $request->validated(); $paginator = $this->queryService->listReports($auth, $filters); if (!empty($filters['group_by_week'])) { $grouped = ClassProgressGroupCollection::fromPaginator($paginator); return $this->respondSuccess($grouped, 'Progress reports retrieved.'); } return $this->respondSuccess([ 'items' => ClassProgressReportResource::collection($paginator->items()), 'pagination' => [ 'current_page' => $paginator->currentPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), 'last_page' => $paginator->lastPage(), ], ], 'Progress reports retrieved.'); } public function store(ClassProgressStoreRequest $request): JsonResponse { $auth = $this->requireAuthenticatedUser($request->user()); if ($auth instanceof JsonResponse) { return $auth; } try { $filesBySubject = $request->filesBySubject(); $reports = $this->mutationService->createReports($auth, $request->validated(), $filesBySubject); } catch (\InvalidArgumentException $e) { if ($e->getMessage() === 'CONFIRM_OVERWRITE') { return response()->json([ 'status' => false, 'message' => 'A progress report already exists for this week. Resubmit with confirm_overwrite=true to replace it.', 'data' => ['requires_confirmation' => true], ], 409); } return $this->respondError($e->getMessage(), 422); } catch (\Throwable $e) { Log::error('Failed to create class progress reports.', [ 'error' => $e->getMessage(), ]); return $this->respondError('Unable to save progress reports.', 500); } return $this->respondCreated(ClassProgressReportResource::collection($reports), 'Progress reports saved.'); } public function show(ClassProgressReport $class_progress): JsonResponse { $auth = $this->requireAuthenticatedUser($this->laravelRequest->user()); if ($auth instanceof JsonResponse) { return $auth; } $weeklyReports = $this->queryService->weeklyReports($auth, $class_progress); return $this->respondSuccess( new ClassProgressShowResource([ 'report' => $class_progress, 'weekly_reports' => $weeklyReports, 'status_options' => $this->metaService->statusOptions(), ]), 'Progress report retrieved.' ); } public function update(ClassProgressUpdateRequest $request, ClassProgressReport $class_progress): JsonResponse { try { $updated = $this->mutationService->updateReport($class_progress, $request->validated(), $request->attachments()); } catch (\InvalidArgumentException $e) { return $this->respondError($e->getMessage(), 422); } catch (\Throwable $e) { Log::error('Failed to update class progress report.', [ 'error' => $e->getMessage(), ]); return $this->respondError('Unable to update progress report.', 500); } return $this->respondSuccess(new ClassProgressReportResource($updated), 'Progress report updated.'); } public function destroy(ClassProgressReport $class_progress): JsonResponse { try { $this->mutationService->deleteReport($class_progress); } catch (\Throwable $e) { Log::error('Failed to delete class progress report.', [ 'error' => $e->getMessage(), ]); return $this->respondError('Unable to delete progress report.', 500); } return $this->respondDeleted(null, 'Progress report deleted.'); } public function downloadAttachment(int $attachment): BinaryFileResponse|JsonResponse { $record = ClassProgressAttachment::query()->find($attachment); if (!$record || !$record->file_path) { return $this->respondError('Attachment not found.', 404); } if ($record->report) { $this->authorize('view', $record->report); } $path = $this->attachmentService->resolvePath($record->file_path); if (!$path) { return $this->respondError('Attachment missing.', 404); } $downloadName = $record->original_name ?: basename($path); return response()->download($path, $downloadName); } public function downloadLegacyAttachment(ClassProgressReport $class_progress): BinaryFileResponse|JsonResponse { $this->authorize('view', $class_progress); if (!$class_progress->attachment_path) { return $this->respondError('Attachment not found.', 404); } $path = $this->attachmentService->resolvePath($class_progress->attachment_path); if (!$path) { return $this->respondError('Attachment missing.', 404); } return response()->download($path, basename($path)); } /** * @param mixed $user * @return User|JsonResponse */ private function requireAuthenticatedUser($user): User|JsonResponse { if (!$user instanceof User) { $user = auth()->user(); } if (!$user instanceof User) { $user = $this->laravelRequest->user(); } if (!$user instanceof User) { return $this->respondError('Unauthorized.', Response::HTTP_UNAUTHORIZED); } return $user; } private function buildMetaPayload(?int $classId, int $sundayCount): array { return [ 'subject_sections' => $this->metaService->subjectSections(), 'status_options' => $this->metaService->statusOptions(), 'sunday_options' => $this->metaService->sundayOptionsAlignedWithCi($sundayCount), 'curriculum' => $this->metaService->curriculumOptions($classId), ]; } }