authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $payload = $this->service->indexData(); return response()->json([ 'ok' => true, 'competitions' => array_values(array_map( fn ($row) => $this->normalizeCompetitionRow($this->rowToArray($row)), $this->iterableToArray($payload['competitions'] ?? []) )), 'sectionMap' => $payload['sectionMap'] ?? [], ]); } public function createForm(): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } return response()->json([ 'ok' => true, ...$this->service->createFormData(), ]); } public function settingsForm(int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $payload = $this->service->settingsFormData($id); if ($payload === null) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, ...$payload, ]); } public function store(StoreCompetitionWinnerRequest $request): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $validated = $request->validated(); $id = $this->service->storeCompetition( $validated, (array) ($validated['winner_overrides'] ?? []), (array) ($validated['question_counts'] ?? []), (array) ($validated['prizes'] ?? []), $guard ); return response()->json([ 'ok' => true, 'message' => 'Competition created.', 'id' => $id, ], Response::HTTP_CREATED); } public function update(UpdateCompetitionWinnerRequest $request, int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $validated = $request->validated(); $updated = $this->service->updateCompetition( $id, $validated, (array) ($validated['winner_overrides'] ?? []), (array) ($validated['question_counts'] ?? []), (array) ($validated['prizes'] ?? []) ); if (! $updated) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, 'message' => 'Competition updated.', 'id' => $id, ]); } public function scores(Request $request, int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $payload = $this->service->editScoresData($id, $this->intOrNull($request->query('class_section_id'))); if ($payload === null) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, ...$payload, ]); } public function saveScores(SaveCompetitionScoresRequest $request, int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $competition = $this->service->editScoresData($id, $this->intOrNull($request->input('class_section_id'))); if ($competition === null) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } $payload = $request->validated(); $saved = $this->service->saveScores( $id, (array) ($payload['scores'] ?? []), $this->intOrNull($payload['class_section_id'] ?? null), (array) ($competition['competition'] ?? []) ); if (! $saved) { return response()->json([ 'ok' => false, 'message' => 'Select a class section before saving scores.', ], Response::HTTP_UNPROCESSABLE_ENTITY); } return response()->json([ 'ok' => true, 'message' => 'Scores saved.', 'savedCount' => count((array) ($payload['scores'] ?? [])), ]); } public function preview(int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $payload = $this->service->previewData($id); if ($payload === null) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, ...$payload, ]); } public function winners(int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $payload = $this->service->settingsFormData($id); if ($payload === null || ! isset($payload['competition'])) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, 'competition' => $payload['competition'], 'rows' => $this->service->winnersListingRows($id), 'sectionMap' => $this->service->getClassSectionMap(), ]); } public function publish(int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } if (! $this->service->publishWinners($id)) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, 'message' => 'Competition winners published.', ]); } public function lock(int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } if (! $this->service->lockCompetition($id, $guard)) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, 'message' => 'Competition locked.', ]); } public function unlock(int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } if (! $this->service->unlockCompetition($id)) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', ], Response::HTTP_NOT_FOUND); } return response()->json([ 'ok' => true, 'message' => 'Competition unlocked.', ]); } public function exportQuiz(Request $request, int $id): JsonResponse { $guard = $this->authenticatedAdminOrForbidden(); if ($guard instanceof JsonResponse) { return $guard; } $validated = $request->validate([ 'class_section_id' => ['nullable', 'integer'], 'semester' => ['nullable', 'string'], 'school_year' => ['nullable', 'string'], ]); $schoolId = $this->intOrNull(optional(auth()->user())->school_id); $result = $this->service->exportCompetitionToQuiz( $id, $this->intOrNull($validated['class_section_id'] ?? null) ?? 0, $schoolId, $guard, $validated['semester'] ?? null, $validated['school_year'] ?? null ); return response()->json($result, $result['ok'] ? Response::HTTP_OK : Response::HTTP_UNPROCESSABLE_ENTITY); } /** * @return int|JsonResponse */ private function authenticatedAdminOrForbidden(): int|JsonResponse { $user = $this->getCurrentUser(); if (! $user || (int) ($user->id ?? 0) <= 0) { return response()->json(['ok' => false, 'message' => 'Unauthorized.'], Response::HTTP_UNAUTHORIZED); } $roles = array_map('strtolower', (array) ($user->roles ?? [])); $allowed = ['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal']; if (empty(array_intersect($roles, $allowed))) { return response()->json(['ok' => false, 'message' => 'Forbidden.'], Response::HTTP_FORBIDDEN); } return (int) $user->id; } /** * @param iterable $rows * @return array */ private function iterableToArray(iterable $rows): array { return is_array($rows) ? $rows : iterator_to_array($rows, false); } /** * @param array $row * @return array */ private function normalizeCompetitionRow(array $row): array { $id = $this->intOrNull($row['id'] ?? $row['competition_id'] ?? null); if ($id !== null) { $row['id'] = $id; } $classSectionId = $this->intOrNull($row['class_section_id'] ?? null); $row['class_section_id'] = $classSectionId; $row['is_locked'] = (bool) ($row['is_locked'] ?? false); $row['is_published'] = (bool) ($row['is_published'] ?? false); return $row; } /** * @param mixed $row * @return array */ private function rowToArray(mixed $row): array { if (is_array($row)) { return $row; } if ($row instanceof \JsonSerializable) { $serialized = $row->jsonSerialize(); if (is_array($serialized)) { return $serialized; } } if (is_object($row) && method_exists($row, 'toArray')) { $array = $row->toArray(); if (is_array($array)) { return $array; } } return (array) $row; } private function intOrNull(mixed $value): ?int { if ($value === null || $value === '') { return null; } if (is_numeric($value)) { return (int) $value; } return null; } }