fix parent pages and teachers and admin
API CI/CD / Validate (composer + pint) (push) Successful in 3m8s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Test (PHPUnit) (push) Failing after 6m5s
API CI/CD / Security audit (push) Failing after 52s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped

This commit is contained in:
root
2026-07-09 13:56:22 -04:00
parent 21c9322127
commit 02ba2fe6b6
34 changed files with 2306 additions and 99 deletions
@@ -97,13 +97,22 @@ class ClassProgressController extends BaseApiController
return $auth;
}
if ($this->isLegacyTeacherProgressListRoute($request) && ! $this->hasAnyRole($auth, ['teacher', 'administrator', 'admin'])) {
if ($this->isLegacyTeacherProgressListRoute($request) && ! $this->hasAnyRole($auth, ['teacher', 'teacher_assistant', 'ta', 'administrator', 'admin'])) {
return $this->respondError('Forbidden.', Response::HTTP_FORBIDDEN);
}
$filters = $request->validated();
$meta = $this->queryService->meta($filters['school_year'] ?? null, $filters['semester'] ?? null);
$filters['school_year'] = $filters['school_year'] ?? ($meta['school_year'] ?? null);
if ($this->isLegacyTeacherProgressListRoute($request) && empty($filters['class_section_id'])) {
$resolvedClassSectionId = $this->resolveTeacherHistoryClassSectionId($auth, $filters);
if ($resolvedClassSectionId instanceof JsonResponse) {
return $resolvedClassSectionId;
}
if ($resolvedClassSectionId > 0) {
$filters['class_section_id'] = $resolvedClassSectionId;
}
}
if ($this->isAdminProgressAliasRoute($request)) {
$filters['group_by_week'] = true;
}
@@ -136,7 +145,12 @@ class ClassProgressController extends BaseApiController
try {
$filesBySubject = $request->filesBySubject();
$reports = $this->mutationService->createReports($auth, $request->validated(), $filesBySubject);
$payload = $this->resolveTeacherStorePayload($auth, $request->validated());
if ($payload instanceof JsonResponse) {
return $payload;
}
$reports = $this->mutationService->createReports($auth, $payload, $filesBySubject);
} catch (\InvalidArgumentException $e) {
if ($e->getMessage() === 'CONFIRM_OVERWRITE') {
return response()->json([
@@ -278,6 +292,100 @@ class ClassProgressController extends BaseApiController
|| $request->is('api/v1/admin/progress');
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>|JsonResponse
*/
private function resolveTeacherStorePayload(User $user, array $payload): array|JsonResponse
{
if ($this->hasAnyRole($user, ['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'])) {
return $payload;
}
if (! $this->hasAnyRole($user, ['teacher', 'teacher_assistant', 'ta'])) {
return $payload;
}
$assignments = $this->queryService->teacherAssignments(
$user,
$payload['school_year'] ?? null,
$payload['semester'] ?? null
);
if ($assignments === []) {
return $this->respondError('No class section assigned to this teacher.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
$postedSectionId = (int) ($payload['class_section_id'] ?? 0);
$assignment = null;
if ($postedSectionId > 0) {
$assignment = collect($assignments)->first(function (array $assignment) use ($postedSectionId) {
return (int) ($assignment['class_section_id'] ?? 0) === $postedSectionId
|| (int) ($assignment['class_section_pk'] ?? 0) === $postedSectionId;
});
}
if (! $assignment) {
$sessionClassSectionId = (int) session('class_section_id', 0);
if ($sessionClassSectionId > 0) {
$assignment = collect($assignments)->first(function (array $assignment) use ($sessionClassSectionId) {
return (int) ($assignment['class_section_id'] ?? 0) === $sessionClassSectionId
|| (int) ($assignment['class_section_pk'] ?? 0) === $sessionClassSectionId;
});
}
}
$assignment ??= $assignments[0];
$payload['class_section_id'] = (int) ($assignment['class_section_id'] ?? 0);
if (empty($payload['school_year']) && ! empty($assignment['school_year'])) {
$payload['school_year'] = $assignment['school_year'];
}
if (empty($payload['semester']) && ! empty($assignment['semester'])) {
$payload['semester'] = $assignment['semester'];
}
return $payload;
}
/**
* @param array<string, mixed> $filters
*/
private function resolveTeacherHistoryClassSectionId(User $user, array $filters): int|JsonResponse
{
if ($this->hasAnyRole($user, ['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'])) {
return 0;
}
$assignments = $this->queryService->teacherAssignments(
$user,
$filters['school_year'] ?? null,
$filters['semester'] ?? null
);
$assignedSectionIds = collect($assignments)
->pluck('class_section_id')
->map(fn ($id) => (int) $id)
->filter(fn ($id) => $id > 0)
->unique()
->values()
->all();
if ($assignedSectionIds === []) {
return $this->respondError('No class section assigned to this teacher.', Response::HTTP_UNPROCESSABLE_ENTITY);
}
if (count($assignedSectionIds) === 1) {
return (int) $assignedSectionIds[0];
}
$sessionClassSectionId = (int) session('class_section_id', 0);
if ($sessionClassSectionId > 0 && in_array($sessionClassSectionId, $assignedSectionIds, true)) {
return $sessionClassSectionId;
}
return (int) $assignedSectionIds[0];
}
/**
* @param list<string> $roles
*/
@@ -80,6 +80,21 @@ class ParentController extends BaseApiController
]);
}
public function statement(ParentInvoiceRequest $request): JsonResponse
{
$guard = $this->parentIdOrUnauthorized();
if ($guard instanceof JsonResponse) {
return $guard;
}
$schoolYear = $request->validated()['school_year'] ?? null;
return response()->json([
'ok' => true,
'data' => $this->invoiceService->statement($guard, $schoolYear),
]);
}
public function enrollments(ParentEnrollmentRequest $request): JsonResponse
{
$guard = $this->parentIdOrUnauthorized();
@@ -9,6 +9,8 @@ use App\Services\Files\ExamDraftDownloadNameService;
use App\Services\Files\FileServeService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class FilesController extends BaseApiController
{
@@ -71,6 +73,7 @@ class FilesController extends BaseApiController
public function examDraftFinal(FileNameRequest $request, string $name): Response|JsonResponse
{
$name = $this->resolveFinalExamFileName($name);
$downloadName = $this->draftNameService->build($name, 'finals');
return $this->serveFile(
@@ -102,4 +105,37 @@ class FilesController extends BaseApiController
return $this->fileService->serveInline($baseDir, $name, $allowedExtensions, $request, $downloadName, $nosniff);
}
private function resolveFinalExamFileName(string $name): string
{
if (! ctype_digit($name) || ! Schema::hasTable('exam_drafts')) {
return $name;
}
$query = DB::table('exam_drafts')->where('id', (int) $name);
if (Schema::hasColumn('exam_drafts', 'final_pdf_file')) {
$query->select('final_file', 'final_pdf_file');
} else {
$query->select('final_file');
}
$row = $query->first();
if (! $row) {
return $name;
}
$pdfFile = trim((string) ($row->final_pdf_file ?? ''));
if ($this->looksLikeExamFileName($pdfFile)) {
return $pdfFile;
}
$finalFile = trim((string) ($row->final_file ?? ''));
return $this->looksLikeExamFileName($finalFile) ? $finalFile : $name;
}
private function looksLikeExamFileName(string $name): bool
{
return preg_match('/\.(doc|docx|pdf)\z/i', $name) === 1;
}
}
@@ -47,6 +47,8 @@ class TeacherController extends BaseApiController
$studentsBySection[(string) $sectionId] = TeacherStudentResource::collection($students);
}
$this->storeActiveClassInSession($data);
return response()->json([
'ok' => true,
'teacher' => $teacher,
@@ -91,6 +93,8 @@ class TeacherController extends BaseApiController
], 422);
}
$this->storeActiveClassInSession($data);
return response()->json([
'ok' => true,
'active_class_section_id' => $requested,
@@ -162,4 +166,18 @@ class TeacherController extends BaseApiController
return $userId;
}
/**
* @param array<string, mixed> $data
*/
private function storeActiveClassInSession(array $data): void
{
$classSectionId = (int) ($data['active_class_section_id'] ?? 0);
if ($classSectionId <= 0) {
return;
}
session()->put('class_section_id', $classSectionId);
session()->put('class_section_name', $data['class_section_name'] ?? null);
}
}