263 lines
10 KiB
PHP
263 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\ClassProgress;
|
|
|
|
use App\Http\Controllers\Api\Core\BaseApiController;
|
|
use App\Http\Requests\ClassProgress\ClassProgressIndexRequest;
|
|
use App\Http\Requests\ClassProgress\ClassProgressMetaRequest;
|
|
use App\Http\Requests\ClassProgress\ClassProgressStoreRequest;
|
|
use App\Http\Requests\ClassProgress\ClassProgressUpdateRequest;
|
|
use App\Http\Resources\ClassProgress\ClassProgressGroupCollection;
|
|
use App\Http\Resources\ClassProgress\ClassProgressReportResource;
|
|
use App\Http\Resources\ClassProgress\ClassProgressShowResource;
|
|
use App\Models\ClassProgressAttachment;
|
|
use App\Models\ClassProgressReport;
|
|
use App\Models\Configuration;
|
|
use App\Models\User;
|
|
use App\Services\ClassProgress\ClassProgressAttachmentService;
|
|
use App\Services\ClassProgress\ClassProgressMetaService;
|
|
use App\Services\ClassProgress\ClassProgressMutationService;
|
|
use App\Services\ClassProgress\ClassProgressQueryService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class ClassProgressController extends BaseApiController
|
|
{
|
|
public function __construct(
|
|
private ClassProgressQueryService $queryService,
|
|
private ClassProgressMutationService $mutationService,
|
|
private ClassProgressMetaService $metaService,
|
|
private ClassProgressAttachmentService $attachmentService
|
|
) {
|
|
parent::__construct();
|
|
$this->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),
|
|
$request->query('school_year'),
|
|
$request->query('semester')
|
|
),
|
|
'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, $schoolYear, $semester);
|
|
$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();
|
|
$meta = $this->queryService->meta($filters['school_year'] ?? null, $filters['semester'] ?? null);
|
|
$filters['school_year'] = $filters['school_year'] ?? ($meta['school_year'] ?? null);
|
|
$filters['semester'] = $filters['semester'] ?? ($meta['semester'] ?? null);
|
|
$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()),
|
|
'meta' => $meta,
|
|
'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, ?string $schoolYear = null, ?string $semester = null): array
|
|
{
|
|
$meta = $this->queryService->meta($schoolYear, $semester);
|
|
|
|
return [
|
|
'subject_sections' => $this->metaService->subjectSections(),
|
|
'status_options' => $this->metaService->statusOptions(),
|
|
'sunday_options' => $this->metaService->sundayOptionsAlignedWithCi($sundayCount),
|
|
'curriculum' => $this->metaService->curriculumOptions($classId, $meta['school_year'] ?? null),
|
|
'meta' => $meta,
|
|
];
|
|
}
|
|
}
|