diff --git a/be/.env.production b/be/.env.production index 69139e2..3a55ebd 100644 --- a/be/.env.production +++ b/be/.env.production @@ -4,6 +4,7 @@ APP_KEY=base64:XdMAy5IbQMHsTrp1N7Tn/LIJC4SKenN8CfhYLt1s3tk= APP_DEBUG=false APP_TIMEZONE=Asia/Kuala_Lumpur APP_URL=https://api.koppkb.com +FRONTEND_URL=https://mykopkb.koppkb.com APP_LOCALE=en APP_FALLBACK_LOCALE=en @@ -91,11 +92,15 @@ ENABLE_API_KEY_AUTH=true API_VALID_KEYS=86f58825e5c1e0872d8786092d47e3bd,6461fc5390660c55dc47bdfcd85ed9ae API_LOG_KEY_USAGE=true -PUBLIC_PROFILE_TOKEN_TTL_DAYS=7 -FRONTEND_URL=https://mykopkb.koppkb.com +AUTH_COOKIE_NAME=auth_token +AUTH_COOKIE_LIFETIME=360 +AUTH_COOKIE_SECURE=true +AUTH_COOKIE_SAME_SITE=lax +AUTH_COOKIE_EXPOSE_TOKEN=false + BROWSERSHOT_NO_SANDBOX=true -PUBLIC_PROFILE_TOKEN_TTL_DAYS=7 +PUBLIC_PROFILE_TOKEN_TTL_DAYS=3 EMAIL_VERIFICATION_EXPIRE_MINUTES=60 EMAIL_VERIFICATION_REDIRECT_PATH=/profile-overview-2 diff --git a/be/Modules/MembershipApplication/Entities/MembershipApplicationReview.php b/be/Modules/MembershipApplication/Entities/MembershipApplicationReview.php index 2fb89f2..e2b6d36 100644 --- a/be/Modules/MembershipApplication/Entities/MembershipApplicationReview.php +++ b/be/Modules/MembershipApplication/Entities/MembershipApplicationReview.php @@ -69,4 +69,13 @@ class MembershipApplicationReview extends Model return BoardDecision::tryFrom($this->decision); } + + public function completionDecision(): ?BoardDecision + { + if ($this->stage !== ReviewStage::Completion) { + return null; + } + + return BoardDecision::tryFrom($this->decision); + } } diff --git a/be/Modules/MembershipApplication/Enums/ReviewStage.php b/be/Modules/MembershipApplication/Enums/ReviewStage.php index ef4d9cb..5e72f30 100644 --- a/be/Modules/MembershipApplication/Enums/ReviewStage.php +++ b/be/Modules/MembershipApplication/Enums/ReviewStage.php @@ -6,4 +6,5 @@ enum ReviewStage: string { case Management = 'MANAGEMENT'; case Board = 'BOARD'; + case Completion = 'COMPLETION'; } diff --git a/be/Modules/MembershipApplication/Services/MembershipApplicationService.php b/be/Modules/MembershipApplication/Services/MembershipApplicationService.php index b44c3d8..2c24e16 100644 --- a/be/Modules/MembershipApplication/Services/MembershipApplicationService.php +++ b/be/Modules/MembershipApplication/Services/MembershipApplicationService.php @@ -254,12 +254,8 @@ class MembershipApplicationService }); } - public function complete( - MembershipApplication $application, - User $processedBy, - string $boardMeetingReference, - string $boardMeetingDate, - ): MembershipApplication { + public function complete(MembershipApplication $application, User $processedBy, string $boardMeetingReference, string $boardMeetingDate,): MembershipApplication + { if ($application->status !== ApplicationStatus::PendingNotification) { throw ValidationException::withMessages([ 'status' => ['Permohonan ini tidak boleh diselesaikan pada masa ini.'], @@ -272,7 +268,7 @@ class MembershipApplicationService ]); } - return DB::transaction(function () use ($application, $boardMeetingReference, $boardMeetingDate) { + return DB::transaction(function () use ($application, $processedBy, $boardMeetingReference, $boardMeetingDate) { $plainPassword = null; if ($application->board_result === BoardResult::Pass->value && ! $application->user_id) { @@ -286,6 +282,18 @@ class MembershipApplicationService $this->sendResultNotification($application, $resultLetter, $plainPassword); + $application->reviews()->create([ + 'stage' => ReviewStage::Completion, + 'decision' => $application->board_result, + 'remarks' => sprintf( + 'Rujukan mesyuarat lembaga: %s, Tarikh mesyuarat: %s', + $boardMeetingReference, + $boardMeetingDate, + ), + 'reviewer_id' => $processedBy->id, + 'reviewed_at' => now(), + ]); + $application->update([ 'status' => ApplicationStatus::Completed, 'completed_at' => now()->toDateTimeString(), @@ -302,12 +310,8 @@ class MembershipApplicationService * failed: list * } */ - public function completeBatch( - array $applicationIds, - User $processedBy, - string $boardMeetingReference, - string $boardMeetingDate, - ): array { + public function completeBatch(array $applicationIds, User $processedBy, string $boardMeetingReference, string $boardMeetingDate): array + { $succeeded = []; $failed = []; diff --git a/fe/src/constants/employers.ts b/fe/src/constants/employers.ts index 490b223..a170f44 100644 --- a/fe/src/constants/employers.ts +++ b/fe/src/constants/employers.ts @@ -1,7 +1,7 @@ export const EMPLOYERS = [ { - name: 'Infra Quest Sdn. Bhd. (IQSB)', - address: 'Lot 1045, Jalan Dato’ Lundang, 15200 Kota Bharu, Kelantan', + name: 'Infra Quest Sdn Bhd (IQSB)', + address: "Lot 1045, Jalan Dato' Lundang, 15200 Kota Bharu, Kelantan", }, { name: 'Permodalan Kelantan Berhad (PKB)', @@ -14,11 +14,19 @@ export const EMPLOYERS = [ 'Lot Pt 448, Tingkat 1,Jalan Kuala Krai, Batu 3, Wakaf Che Yeh, 15150 Kota Bharu, Kelantan.', }, { - name: "An-Nisa'", + name: "Pusat Perubatan An-Nisa'", address: 'Jln Sultan Ibrahim, Bandar Kota Bharu, 15050 Kota Bharu, Kelantan.', }, { - name: 'Kel Infra Sdn. Bhd.', + name: 'Kel Infra Sdn Bhd (KISB)', address: 'Tingkat 2 Menara Perbadanan, Jalan Tengku Petra Semerak, 15000 Kota Bharu, Kelantan.', }, + { + name: 'Xtra Biz Sdn Bhd (XBSB)', + address: "Lot 241, Jalan Dato' Cik Arif, Taman Mutiara 1, 15000 Kota Bharu, Kelantan.", + }, + { + name: 'Pesara', + address: '-' + } ] as const diff --git a/fe/src/modules/membership-application/pages/MembershipApplication.vue b/fe/src/modules/membership-application/pages/MembershipApplication.vue index aefe61b..b26b912 100644 --- a/fe/src/modules/membership-application/pages/MembershipApplication.vue +++ b/fe/src/modules/membership-application/pages/MembershipApplication.vue @@ -30,6 +30,7 @@ import type { MembershipApplicationReferenceForm, } from '../types/membership-application.types' import illustrationUrl from '@/assets/images/logo.svg' +import { EMPLOYERS } from '@/constants/employers' const MAX_FILE_SIZE_MB = 10 const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024 @@ -78,26 +79,6 @@ const RELATIONSHIP_OPTIONS: SelectOption[] = [ { label: 'Lain-lain', value: 'Lain-lain' }, ] -// TODO: replace with API lookup -const EMPLOYERS = [ - { - name: 'Infra Quest Sdn. Bhd. (IQSB)', - address: 'Lot 1045, Jalan Dato’ Lundang, 15200 Kota Bharu, Kelantan', - }, - { - name: 'Permodalan Kelantan Berhad (PKB)', - address: 'Permodalan Kelantan Berhad, Tingkat 4, Wisma Permodalan Kelantan Berhad, Jalan Maju, 15000 Kota Bharu Kelantan', - }, - { - name: 'Koperasi Permodalan Kelantan Berhad (KOPKB)', - address: 'Lot Pt 448, Tingkat 1,Jalan Kuala Krai, Batu 3, Wakaf Che Yeh, 15150 Kota Bharu, Kelantan.', - }, - { - name: "An-Nisa'", - address: 'Jln Sultan Ibrahim, Bandar Kota Bharu, 15050 Kota Bharu, Kelantan.', - }, -] as const - const EMPLOYER_OPTIONS: SelectOption[] = EMPLOYERS.map((employer) => ({ label: employer.name, value: employer.name, @@ -319,7 +300,7 @@ const stepTitle = computed(() => { case 1: return 'Maklumat Peribadi' case 2: - return 'Hubungan & Alamat' + return 'Hubungan & Alamat Rumah Semasa' case 3: return 'Maklumat Pekerjaan & Caruman' case 4: @@ -940,7 +921,7 @@ function stepLabelClass(stepId: number) { {{ fieldErrors['documents.employer_letter'] - }} + }}
diff --git a/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue b/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue index af07050..100acb1 100644 --- a/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue +++ b/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue @@ -168,14 +168,14 @@ const confirmDialogTitle = computed(() => { if (pendingAction.value.type === 'management') { return pendingAction.value.decision === 'APPROVED' - ? 'Luluskan Permohonan?' + ? 'Semak Permohonan?' : 'Tolak Permohonan?' } if (pendingAction.value.type === 'board') { return pendingAction.value.decision === 'PASS' - ? 'Luluskan?' - : 'Gagalkan?' + ? 'Keputusan ALK?' + : 'Keputusan ALK?' } return 'Selesaikan Permohonan?' @@ -186,7 +186,7 @@ const confirmDialogDescription = computed(() => { if (pendingAction.value.type === 'management') { return pendingAction.value.decision === 'APPROVED' - ? 'Permohonan akan dihantar ke mesyuarat ALK.' + ? 'Permohonan akan dihantar ke peringkat keputusan ALK.' : 'Permohonan akan ditolak pada peringkat pentadbiran.' } @@ -325,6 +325,7 @@ function getReference(type: 'PROPOSER' | 'SUPPORTER'): MembershipApplicationRefe function reviewStageLabel(stage: string): string { if (stage === 'MANAGEMENT') return 'Semakan Pentadbiran' if (stage === 'BOARD') return 'Keputusan Ahli Lembaga Koperasi (ALK)' + if (stage === 'COMPLETION') return 'Penyelesaian & Makluman Keputusan' return stage } @@ -332,11 +333,11 @@ function reviewDecisionLabel(decision: string | null, stage: string): string { if (!decision) return '-' if (stage === 'MANAGEMENT') { - if (decision === 'APPROVED') return 'Diluluskan' + if (decision === 'APPROVED') return 'Disemak' if (decision === 'REJECTED') return 'Ditolak' } - if (stage === 'BOARD') { + if (stage === 'BOARD' || stage === 'COMPLETION') { if (decision === 'PASS') return 'Lulus' if (decision === 'FAIL') return 'Gagal' } @@ -647,7 +648,7 @@ onUnmounted(() => {
Semakan Pentadbiran

- Luluskan atau tolak permohonan pada peringkat pentadbiran. + Semak atau tolak permohonan pada peringkat pentadbiran.

Catatan @@ -657,7 +658,7 @@ onUnmounted(() => {
diff --git a/fe/src/modules/membership-application/pages/MembershipApplicationEdit.vue b/fe/src/modules/membership-application/pages/MembershipApplicationEdit.vue index 7adba2f..9949187 100644 --- a/fe/src/modules/membership-application/pages/MembershipApplicationEdit.vue +++ b/fe/src/modules/membership-application/pages/MembershipApplicationEdit.vue @@ -260,6 +260,7 @@ const isPreviewPdf = computed(() => (previewDocument.value ? isPdfDocument(previ function reviewStageLabel(stage: string): string { if (stage === 'MANAGEMENT') return 'Semakan Pentadbiran' if (stage === 'BOARD') return 'Keputusan Ahli Lembaga Koperasi (ALK)' + if (stage === 'COMPLETION') return 'Penyelesaian & Makluman Keputusan' return stage } @@ -271,7 +272,7 @@ function reviewDecisionLabel(decision: string | null, stage: string): string { if (decision === 'REJECTED') return 'Ditolak' } - if (stage === 'BOARD') { + if (stage === 'BOARD' || stage === 'COMPLETION') { if (decision === 'PASS') return 'Lulus' if (decision === 'FAIL') return 'Gagal' } @@ -867,13 +868,13 @@ onUnmounted(() => { Jawatan Semasa {{ fieldErrors['applicant.current_position'] - }} + }}
Alamat Majikan