authorize('viewAny', MembershipApplication::class); $perPage = min((int) $request->get('per_page', 10), 100); $search = (string) ($request->get('search', '') ?? ''); $status = $request->get('status'); $sortBy = (string) ($request->get('sort_by', 'submitted_at') ?? 'submitted_at'); $sortOrder = (string) ($request->get('sort_order', 'desc') ?? 'desc'); $applications = $this->membershipApplicationService->getPaginatedList( $perPage, $search, $status, $sortBy, $sortOrder ); return response()->json([ 'success' => true, 'data' => MembershipApplicationListResource::collection($applications->items()), 'pagination' => [ 'current_page' => $applications->currentPage(), 'per_page' => $applications->perPage(), 'total' => $applications->total(), 'last_page' => $applications->lastPage(), 'from' => $applications->firstItem(), 'to' => $applications->lastItem(), 'has_more_pages' => $applications->hasMorePages(), ], ]); } public function show(MembershipApplication $membershipApplication): JsonResponse { $this->authorize('view', $membershipApplication); $application = $this->membershipApplicationService->getByIdWithRelations($membershipApplication->id); if (! $application) { return response()->json([ 'success' => false, 'message' => 'Permohonan tidak dijumpai.', ], 404); } return response()->json([ 'success' => true, 'data' => new MembershipApplicationResource($application), ]); } public function update(UpdateMembershipApplicationRequest $request, MembershipApplication $membershipApplication): JsonResponse { $this->authorize('update', $membershipApplication); $application = $this->membershipApplicationService->update( $membershipApplication, $request->validated(), ); return response()->json([ 'success' => true, 'message' => 'Permohonan keahlian berjaya dikemaskini.', 'data' => new MembershipApplicationResource($application), ]); } public function uploadDocument(UploadMembershipApplicationDocumentRequest $request, MembershipApplication $membershipApplication): JsonResponse { $this->authorize('update', $membershipApplication); $application = $this->membershipApplicationService->uploadDocument( $membershipApplication, $request->validated('type'), $request->file('file'), ); return response()->json([ 'success' => true, 'message' => 'Dokumen berjaya dimuat naik.', 'data' => new MembershipApplicationResource($application), ]); } public function managementReview(ManagementReviewRequest $request, MembershipApplication $membershipApplication): JsonResponse { $this->authorize('managementReview', $membershipApplication); $application = $this->membershipApplicationService->managementReview( $membershipApplication, ManagementDecision::from($request->validated('decision')), $request->validated('remarks'), $request->user() ); $message = $application->status === ApplicationStatus::PendingBoard ? 'Permohonan diluluskan dan dihantar ke lembaga.' : 'Permohonan ditolak.'; return response()->json([ 'success' => true, 'message' => $message, 'data' => new MembershipApplicationResource($application), ]); } public function boardReview(BoardReviewRequest $request, MembershipApplication $membershipApplication): JsonResponse { $this->authorize('boardReview', $membershipApplication); $application = $this->membershipApplicationService->boardReview( $membershipApplication, BoardDecision::from($request->validated('decision')), $request->validated('remarks'), $request->user() ); $message = $application->board_result === BoardResult::Pass->value ? 'Permohonan lulus semakan lembaga.' : 'Permohonan gagal semakan lembaga.'; return response()->json([ 'success' => true, 'message' => $message, 'data' => new MembershipApplicationResource($application), ]); } public function assignReferences(AssignReferencesRequest $request, MembershipApplication $membershipApplication): JsonResponse { $this->authorize('assignReferences', $membershipApplication); $application = $this->membershipApplicationService->assignReferences( $membershipApplication, $request->validated(), $request->user() ); return response()->json([ 'success' => true, 'message' => 'Pencadang dan penyokong berjaya dikemaskini.', 'data' => new MembershipApplicationResource($application), ]); } public function complete(Request $request, MembershipApplication $membershipApplication): JsonResponse { $this->authorize('complete', $membershipApplication); $application = $this->membershipApplicationService->complete( $membershipApplication, $request->user() ); $message = $application->board_result === BoardResult::Pass->value ? 'Permohonan selesai. Akaun ahli telah dicipta dan e-mel dihantar.' : 'Permohonan selesai. E-mel keputusan dihantar.'; return response()->json([ 'success' => true, 'message' => $message, 'data' => new MembershipApplicationResource($application), ]); } public function batchComplete(BatchCompleteRequest $request): JsonResponse { $this->authorize('completeBatch', MembershipApplication::class); $result = $this->membershipApplicationService->completeBatch( $request->validated('application_ids'), $request->user() ); $succeededCount = count($result['succeeded']); $failedCount = count($result['failed']); if ($succeededCount === 0) { return response()->json([ 'success' => false, 'message' => 'Tiada permohonan berjaya diselesaikan.', 'data' => [ 'succeeded' => [], 'failed' => $result['failed'], ], ], 422); } $message = $failedCount > 0 ? "{$succeededCount} permohonan berjaya, {$failedCount} gagal." : "{$succeededCount} permohonan berjaya diselesaikan."; return response()->json([ 'success' => true, 'message' => $message, 'data' => [ 'succeeded' => MembershipApplicationResource::collection($result['succeeded']), 'failed' => $result['failed'], ], ]); } public function downloadDocument(MembershipApplication $membershipApplication, string $documentId): StreamedResponse { $this->authorize('view', $membershipApplication); $document = $membershipApplication->documents()->findOrFail($documentId); return $this->documentService->downloadDocument($document->id); } public function generateResultLetter(GenerateResultLetterRequest $request, MembershipApplication $membershipApplication,): JsonResponse { $this->authorize('generateResultLetter', $membershipApplication); $application = $this->membershipApplicationService->generateResultLetter( $membershipApplication, $request->validated('board_meeting_reference'), ); $resultLetter = $application->documents ->firstWhere('type', MembershipApplication::RESULT_LETTER_DOCUMENT_TYPE); return response()->json([ 'success' => true, 'message' => 'Surat keputusan berjaya dijana.', 'data' => [ 'application' => new MembershipApplicationResource($application), 'document' => $resultLetter ? new MembershipApplicationDocumentResource($resultLetter) : null, ], ]); } public function approvedLetter(Request $request, MembershipApplication $membershipApplication): Response { $this->authorize('view', $membershipApplication); $application = $this->membershipApplicationService->getByIdWithRelations($membershipApplication->id); if (! $application) { abort(404, 'Permohonan tidak dijumpai.'); } $boardMeetingReference = (string) $request->query( 'board_meeting_reference', 'Mesyuarat Lembaga', ); $view = 'membershipapplication::pdf.approved-letter'; $data = $this->membershipApplicationService->getResultLetterViewData( $application, $boardMeetingReference, ); $format = $request->query('format', 'html'); $filename = 'surat-keputusan-'.$application->application_number.'.pdf'; if ($format === 'pdf') { return $this->letterService->pdfResponse($view, $data, $filename); } return $this->letterService->htmlResponse($view, $data); } }