diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 31d929a..ed7c4ee 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,12 +1,11 @@ name: Build Docker Image -# Builds images on every push to main or tag, but does NOT auto-deploy on: push: - branches: - - main + # branches: + # - main tags: - - "v*" # v1.0.0, v1.0.1, etc. + - "v*" jobs: # test-backend: diff --git a/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php b/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php index 7da1e09..5398193 100644 --- a/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php +++ b/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php @@ -189,6 +189,7 @@ class MembershipApplicationController extends Controller $membershipApplication, $request->user(), $request->validated('board_meeting_reference'), + $request->validated('board_meeting_date'), ); $message = $application->board_result === BoardResult::Pass->value @@ -210,6 +211,7 @@ class MembershipApplicationController extends Controller $request->validated('application_ids'), $request->user(), $request->validated('board_meeting_reference'), + $request->validated('board_meeting_date'), ); $succeededCount = count($result['succeeded']); @@ -270,6 +272,7 @@ class MembershipApplicationController extends Controller $application = $this->membershipApplicationService->generateResultLetter( $membershipApplication, $request->validated('board_meeting_reference'), + $request->validated('board_meeting_date'), ); $resultLetter = $application->documents @@ -301,11 +304,16 @@ class MembershipApplicationController extends Controller 'board_meeting_reference', 'Mesyuarat Lembaga', ); + $boardMeetingDate = (string) $request->query( + 'board_meeting_date', + now()->toDateString(), + ); $view = 'membershipapplication::pdf.approved-letter'; $data = $this->membershipApplicationService->getResultLetterViewData( $application, $boardMeetingReference, + $boardMeetingDate, ); $format = $request->query('format', 'html'); $filename = 'surat-keputusan-'.$application->application_number.'.pdf'; diff --git a/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php b/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php index 0a4c3ae..5b54787 100644 --- a/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php +++ b/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php @@ -20,6 +20,7 @@ class BatchCompleteRequest extends FormRequest 'application_ids' => 'required|array|min:1|max:100', 'application_ids.*' => 'required|uuid|distinct|exists:membership_applications,id', 'board_meeting_reference' => 'required|string|max:255', + 'board_meeting_date' => 'required|date', ]; } @@ -36,6 +37,8 @@ class BatchCompleteRequest extends FormRequest 'application_ids.*.distinct' => 'Permohonan duplikat tidak dibenarkan.', 'board_meeting_reference.required' => 'Rujukan mesyuarat lembaga diperlukan.', 'board_meeting_reference.max' => 'Rujukan mesyuarat lembaga tidak boleh melebihi 255 aksara.', + 'board_meeting_date.required' => 'Tarikh mesyuarat lembaga diperlukan.', + 'board_meeting_date.date' => 'Tarikh mesyuarat lembaga tidak sah.', ]; } } diff --git a/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.php b/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.php index 971bfde..d0d3759 100644 --- a/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.php +++ b/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.php @@ -18,6 +18,7 @@ class CompleteMembershipApplicationRequest extends FormRequest { return [ 'board_meeting_reference' => 'required|string|max:255', + 'board_meeting_date' => 'required|date', ]; } @@ -29,6 +30,8 @@ class CompleteMembershipApplicationRequest extends FormRequest return [ 'board_meeting_reference.required' => 'Rujukan mesyuarat lembaga diperlukan.', 'board_meeting_reference.max' => 'Rujukan mesyuarat lembaga tidak boleh melebihi 255 aksara.', + 'board_meeting_date.required' => 'Tarikh mesyuarat lembaga diperlukan.', + 'board_meeting_date.date' => 'Tarikh mesyuarat lembaga tidak sah.', ]; } } diff --git a/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php b/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php index 72aa39c..8cd9605 100644 --- a/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php +++ b/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php @@ -18,6 +18,7 @@ class GenerateResultLetterRequest extends FormRequest { return [ 'board_meeting_reference' => 'required|string|max:255', + 'board_meeting_date' => 'required|date', ]; } @@ -29,6 +30,8 @@ class GenerateResultLetterRequest extends FormRequest return [ 'board_meeting_reference.required' => 'Rujukan mesyuarat lembaga diperlukan.', 'board_meeting_reference.max' => 'Rujukan mesyuarat lembaga tidak boleh melebihi 255 aksara.', + 'board_meeting_date.required' => 'Tarikh mesyuarat lembaga diperlukan.', + 'board_meeting_date.date' => 'Tarikh mesyuarat lembaga tidak sah.', ]; } } diff --git a/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php b/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php index 21c1fea..6d62dc7 100644 --- a/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php +++ b/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php @@ -12,49 +12,77 @@ {{ $applicant->postcode }} @endsection +@section('letter-honorific') + Tuan/Puan, +@endsection + @section('letter-subject') {{ $letterSubject }} @endsection @section('letter-body')

- Dengan segala hormatnya saya merujuk kepada perkara di atas. + Dengan segala hormatnya perkara di atas adalah dirujuk.

- 2. {{ $openingTone }} dimaklumkan bahawa, permohonan tuan untuk menjadi anggota Koperasi - Permodalan Kelantan Berhad (KoPKB) telah {{ $boardResultLabel }} dalam {{ $boardMeetingReference }}@if ($isPassed && $memberNumber). Nombor keanggotaan tuan adalah {{ $memberNumber }}@endif. + 2. + @if ($isPassed) + Sukacita dimaklumkan bahawa, permohonan tuan/puan untuk menjadi Anggota Koperasi Permodalan Kelantan Berhad (KoPKB) telah dipertimbangkan dan DILULUSKAN oleh Lembaga Koperasi dalam {{ $boardMeetingReference }} yang diadakan pada {{ $boardMeetingDate }}. + @else + Dukacita dimaklumkan bahawa, permohonan tuan/puan untuk menjadi Anggota Koperasi Permodalan Kelantan Berhad (KoPKB) telah dipertimbangkan dan TIDAK DILULUSKAN oleh Lembaga Koperasi dalam {{ $boardMeetingReference }} yang diadakan pada {{ $boardMeetingDate }}. + @endif

@if ($isPassed)

- 3. Pihak Koperasi akan membuat potongan gaji melalui majikan tuan sebagaimana ketetapan berikut:- -

+ 3. Maklumat keanggotaan tuan/puan adalah seperti berikut:- +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PerkaraMaklumat
Nama Anggota{{ $applicant->name }}
No. Kad Pengenalan{{ $applicant->ic_number }}
No. Anggota{{ $memberNumber }}
Tarikh Kuat Kuasa Keanggotaan{{ $boardMeetingDate }}
Modal Syer PermulaanRM500.00 (perlu dijelaskan dalam tempoh 6 bulan)
Fi MasukRM10.00
Yuran TahunanRM30.00 sebulan
+

+ 4. Sebagai anggota KoPKB, tuan/puan adalah tertakluk kepada Undang-Undang Kecil (UUK), dasar, polisi dan peraturan-peraturan KoPKB yang berkuat kuasa dari semasa ke semasa.

- 4. Untuk makluman tuan, kelulusan keanggotaan ini adalah bersyarat di mana tuan dikehendaki - menjelaskan modal syer minimum sebanyak RM{{ $minimumShareCapital }}. - Bayaran tersebut perlu dibuat secara ansuran melalui potongan gaji dalam tempoh enam (6) bulan. - Pemenuhan syarat ini adalah bagi melayakkan tuan memperoleh hak, kewajipan dan liabiliti sebagai anggota KoPKB - sebagaimana terkandung di dalam Undang-Undang Kecil (UUK), Perkara 59 yang telah didaftarkan pada 11 September 2024. - Manakala had maksima modal syer adalah sebanyak RM5,000.00. + 5. Sebagai anggota, tuan/puan adalah layak menikmati kemudahan, produk dan manfaat yang ditawarkan oleh koperasi dari semasa ke semasa, tertakluk kepada syarat-syarat yang ditetapkan. +

+

+ 6. Pihak KoPKB mengucapkan tahniah dan mengalu-alukan penyertaan tuan/puan sebagai anggota. Semoga keanggotaan ini akan memberi manfaat kepada tuan/puan serta menyumbang kepada kemajuan dan kecemerlangan KoPKB.

@else

- 3. Oleh yang demikian, permohonan tuan untuk menjadi anggota KoPKB tidak dapat diluluskan pada masa ini. + 3. Oleh yang demikian, permohonan tuan/puan untuk menjadi anggota KoPKB tidak dapat diluluskan pada masa ini.

@endif @endsection @@ -64,14 +92,5 @@ @endsection @section('letter-signature') -
Dengan hormatnya,
KOPERASI PERMODALAN KELANTAN BERHAD
-
- @if (! empty($signatureUrl)) - - @endif -
-
MUHAMMAD NAFIS BIN ZAINUDIN
-
Pengurus Besar
-
b/p Setiausaha
@endsection diff --git a/be/Modules/MembershipApplication/Services/MembershipApplicationService.php b/be/Modules/MembershipApplication/Services/MembershipApplicationService.php index c55e38b..5c20f4b 100644 --- a/be/Modules/MembershipApplication/Services/MembershipApplicationService.php +++ b/be/Modules/MembershipApplication/Services/MembershipApplicationService.php @@ -7,6 +7,7 @@ use App\Services\DocumentService; use App\Services\LetterService; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Notification; @@ -258,6 +259,7 @@ class MembershipApplicationService MembershipApplication $application, User $processedBy, string $boardMeetingReference, + string $boardMeetingDate, ): MembershipApplication { if ($application->status !== ApplicationStatus::PendingNotification) { throw ValidationException::withMessages([ @@ -271,14 +273,14 @@ class MembershipApplicationService ]); } - return DB::transaction(function () use ($application, $boardMeetingReference) { + return DB::transaction(function () use ($application, $boardMeetingReference, $boardMeetingDate) { if ($application->board_result === BoardResult::Pass->value && ! $application->user_id) { $user = $this->createMemberFromApplication($application); $application->update(['user_id' => $user->id]); $application->refresh(); } - $resultLetter = $this->storeResultLetter($application, $boardMeetingReference); + $resultLetter = $this->storeResultLetter($application, $boardMeetingReference, $boardMeetingDate); $this->sendResultNotification($application, $resultLetter); @@ -298,8 +300,12 @@ class MembershipApplicationService * failed: list * } */ - public function completeBatch(array $applicationIds, User $processedBy, string $boardMeetingReference): array - { + public function completeBatch( + array $applicationIds, + User $processedBy, + string $boardMeetingReference, + string $boardMeetingDate, + ): array { $succeeded = []; $failed = []; @@ -317,7 +323,12 @@ class MembershipApplicationService } try { - $succeeded[] = $this->complete($application, $processedBy, $boardMeetingReference); + $succeeded[] = $this->complete( + $application, + $processedBy, + $boardMeetingReference, + $boardMeetingDate, + ); } catch (ValidationException $e) { $failed[] = [ 'id' => $id, @@ -333,7 +344,11 @@ class MembershipApplicationService ]; } - public function generateResultLetter(MembershipApplication $application, string $boardMeetingReference,): MembershipApplication { + public function generateResultLetter( + MembershipApplication $application, + string $boardMeetingReference, + string $boardMeetingDate, + ): MembershipApplication { if ($application->status !== ApplicationStatus::Completed) { throw ValidationException::withMessages([ 'status' => ['Surat keputusan hanya boleh dijana untuk permohonan yang telah selesai.'], @@ -346,8 +361,8 @@ class MembershipApplicationService ]); } - return DB::transaction(function () use ($application, $boardMeetingReference) { - $this->storeResultLetter($application, $boardMeetingReference); + return DB::transaction(function () use ($application, $boardMeetingReference, $boardMeetingDate) { + $this->storeResultLetter($application, $boardMeetingReference, $boardMeetingDate); return $this->repository->findByIdWithRelations($application->id); }); @@ -356,6 +371,7 @@ class MembershipApplicationService protected function storeResultLetter( MembershipApplication $application, string $boardMeetingReference, + string $boardMeetingDate, ): Document { $lockedApplication = MembershipApplication::query() ->whereKey($application->id) @@ -376,7 +392,7 @@ class MembershipApplicationService ]); } - $viewData = $this->buildResultLetterViewData($application, $boardMeetingReference); + $viewData = $this->buildResultLetterViewData($application, $boardMeetingReference, $boardMeetingDate); $pdf = $this->letterService->renderPdf('membershipapplication::pdf.approved-letter', $viewData); $fileName = 'surat-keputusan-'.$application->application_number.'.pdf'; @@ -394,6 +410,7 @@ class MembershipApplicationService public function getResultLetterViewData( MembershipApplication $application, string $boardMeetingReference, + string $boardMeetingDate, ): array { $application = $this->repository->findByIdWithRelations($application->id); @@ -403,7 +420,7 @@ class MembershipApplicationService ]); } - return $this->buildResultLetterViewData($application, $boardMeetingReference); + return $this->buildResultLetterViewData($application, $boardMeetingReference, $boardMeetingDate); } /** @@ -412,32 +429,20 @@ class MembershipApplicationService protected function buildResultLetterViewData( MembershipApplication $application, string $boardMeetingReference, + string $boardMeetingDate, ): array { $applicant = $application->applicant; $isPassed = $application->board_result === BoardResult::Pass->value; - $stockMonthlyContribution = (float) $applicant->stock_monthly_contribution; - $monthlyShareInstallment = number_format( - $stockMonthlyContribution < 84 ? 84 : $stockMonthlyContribution, - 2, - '.', - '', - ); return [ 'application' => $application, 'applicant' => $applicant, 'boardMeetingReference' => $boardMeetingReference, + 'boardMeetingDate' => Carbon::parse($boardMeetingDate)->translatedFormat('d M Y'), 'isPassed' => $isPassed, - 'boardResultLabel' => $isPassed ? 'lulus' : 'gagal', - 'openingTone' => $isPassed ? 'Sukacita' : 'Dukacita', 'letterSubject' => $isPassed - ? 'KELULUSAN KEANGGOTAAN KOPERASI PERMODALAN KELANTAN BERHAD' - : 'KEPUTUSAN PERMOHONAN KEANGGOTAAN KOPERASI PERMODALAN KELANTAN BERHAD', - 'monthlyShareInstallment' => $monthlyShareInstallment, - 'stockMonthlyContribution' => number_format($stockMonthlyContribution, 2, '.', ''), - 'feeMonthlyContribution' => number_format((float) $applicant->fee_monthly_contribution, 2, '.', ''), - 'shareInstallmentMonths' => MembershipApplication::SHARE_INSTALLMENT_MONTHS, - 'minimumShareCapital' => number_format(MembershipApplication::MINIMUM_SHARE_CAPITAL, 2, '.', ''), + ? 'KELULUSAN PERMOHONAN MENJADI ANGGOTA KOPERASI PERMODALAN KELANTAN BERHAD (KoPKB)' + : 'KEPUTUSAN PERMOHONAN MENJADI ANGGOTA KOPERASI PERMODALAN KELANTAN BERHAD (KoPKB)', 'memberNumber' => $isPassed ? $application->user?->member_number : null, ]; } diff --git a/be/Modules/User/Config/config.php b/be/Modules/User/Config/config.php index 6484b21..00f0faa 100644 --- a/be/Modules/User/Config/config.php +++ b/be/Modules/User/Config/config.php @@ -4,5 +4,5 @@ return [ 'name' => 'User', 'public_profile_token_ttl_days' => (int) env('PUBLIC_PROFILE_TOKEN_TTL_DAYS', 90), 'frontend_url' => rtrim(env('FRONTEND_URL', env('APP_URL', 'http://localhost')), '/'), - 'digital_card_logo_path' => env('DIGITAL_CARD_LOGO_PATH', public_path('images/logo/logo.svg')), + 'digital_card_logo_path' => env('DIGITAL_CARD_LOGO_PATH', public_path('images/logo/logo-original.svg')), ]; diff --git a/be/config/mail.php b/be/config/mail.php index ffe986e..73415b6 100644 --- a/be/config/mail.php +++ b/be/config/mail.php @@ -124,6 +124,6 @@ return [ | */ - 'logo_path' => public_path('images/logo/logo-letterhead.png'), + 'logo_path' => public_path('images/logo/logo-original.svg'), ]; diff --git a/be/public/images/logo/logo-letterhead.png b/be/public/images/logo/logo-letterhead.png deleted file mode 100644 index 380b8be..0000000 Binary files a/be/public/images/logo/logo-letterhead.png and /dev/null differ diff --git a/be/public/images/logo/logo-original.svg b/be/public/images/logo/logo-original.svg new file mode 100644 index 0000000..9fa3c6a --- /dev/null +++ b/be/public/images/logo/logo-original.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + KOPERASI PERMODALAN KELANTAN BERHAD + + + diff --git a/be/resources/views/pdf/layouts/letter.blade.php b/be/resources/views/pdf/layouts/letter.blade.php index 35f34c6..8b9813d 100644 --- a/be/resources/views/pdf/layouts/letter.blade.php +++ b/be/resources/views/pdf/layouts/letter.blade.php @@ -8,6 +8,7 @@ @section('letter-reference') Ruj: APP-2026-00001 @endsection @section('letter-date') {{ now()->format('d F Y') }} @endsection @section('letter-recipient') ... @endsection + @section('letter-honorific') Tuan/Puan @endsection @section('letter-subject') Perkara: ... @endsection @section('letter-salutation') Assalamualaikum dan Salam Sejahtera, @endsection @section('letter-body') ... @endsection @@ -110,74 +111,27 @@ border-collapse: collapse; } - .letterhead-line { - vertical-align: middle; - padding-right: 16px; - } - - .letterhead-rule { - width: 100%; - border-collapse: collapse; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } - - .letterhead-rule-blue { - background-color: var(--letter-color-blue); - font-size: 0; - line-height: 0; - } - - .letterhead-rule-orange { - background-color: var(--letter-color-orange); - font-size: 0; - line-height: 0; - } - - .letterhead-logo { - vertical-align: middle; - white-space: nowrap; - padding-right: 10px; + .letterhead-center { + text-align: center; + vertical-align: top; } .letterhead-logo-img { height: var(--letter-logo-height); width: auto; display: block; + margin: 0 auto 8px; } - .letterhead-brand { - vertical-align: middle; - white-space: nowrap; - padding-left: 4px; - } - - .letterhead-brand-inner { - border-collapse: collapse; - } - - .letterhead-name { - vertical-align: middle; - text-align: left; - padding-left: 2px; - } - - .letterhead-name-line { - font-size: var(--letter-font-size); - font-weight: bold; - line-height: 1.25; - color: var(--letter-color-black); - text-transform: uppercase; - letter-spacing: 0.3px; - } - - .letterhead-registration { + .letterhead-address, + .letterhead-contact { font-size: var(--letterhead-registration-size); - font-weight: bold; - line-height: 1.3; + line-height: 1.4; color: var(--letter-color-black); - margin-top: 2px; - letter-spacing: 0.5px; + } + + .letterhead-address { + margin-bottom: 4px; } .meta-table { @@ -192,22 +146,25 @@ } .meta-label { - width: 70px; font-weight: bold; + white-space: nowrap; + padding-right: 4px; + vertical-align: top; } .meta-value { padding-left: 4px; } - .meta-date { - margin-top: 4px; - } - .recipient-block { margin-bottom: var(--letter-recipient-margin-bottom); } + .letter-honorific { + font-weight: bold; + margin-bottom: 8px; + } + .subject-block { font-weight: bold; margin-bottom: var(--letter-subject-margin-bottom); @@ -223,6 +180,26 @@ margin: 0 0 var(--letter-body-paragraph-margin-bottom); } + .letter-info-table { + width: 80%; + /* max-width: 50%; */ + margin: 0 auto var(--letter-body-paragraph-margin-bottom); + border-collapse: collapse; + line-height: 1; + } + + .letter-info-table th, + .letter-info-table td { + border: 1px solid var(--letter-color-black); + padding: 4px 8px; + vertical-align: top; + text-align: left; + } + + .letter-info-table th { + font-weight: bold; + } + .signature-block { padding-left: var(--letter-content-padding-x); padding-right: var(--letter-content-padding-x); @@ -267,43 +244,18 @@ table-layout: fixed; } - .letter-footer-contact-box { - background-color: var(--letter-color-orange); - padding: 8px 12px; + .letter-footer-divider { + border-top: 1px solid var(--letter-color-black); + font-size: 0; + line-height: 0; + padding: 0; + } + + .letter-footer-nota { + padding-top: 8px; font-size: var(--letter-footer-font-size); line-height: var(--letter-footer-line-height); - color: var(--letter-color-white); - vertical-align: middle; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } - - .letter-footer-address-inner { - background-color: var(--letter-color-blue); - color: var(--letter-color-white); - padding: 8px 12px; - font-size: var(--letter-footer-font-size); - line-height: var(--letter-footer-line-height); - text-align: left; - vertical-align: middle; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } - - .letter-footer-lines { - border-collapse: collapse; - } - - .letter-footer-icon { - width: 18px; - padding: 2px 8px 2px 0; - vertical-align: middle; - } - - .letter-footer-line-text { - padding: 2px 0; - color: var(--letter-color-white); - vertical-align: middle; + color: var(--letter-body-color); } @media print { @@ -328,17 +280,22 @@
- @if ($showReference && trim($__env->yieldContent('letter-reference')) !== '') + @if (($showReference && trim($__env->yieldContent('letter-reference')) !== '') || ($showDate && $letterDate !== '')) - - - - + @if ($showReference && trim($__env->yieldContent('letter-reference')) !== '') + + + + + @endif + @if ($showDate && $letterDate !== '') + + + + + @endif
Ruj.: @yield('letter-reference')
RUJ. KAMI: @yield('letter-reference')
TARIKH: {{ $letterDate }}
@endif - @if ($showDate && $letterDate !== '') -
{{ $letterDate }}
- @endif
@hasSection('letter-recipient') @@ -347,6 +304,10 @@
@endif + @hasSection('letter-honorific') +
@yield('letter-honorific')
+ @endif + @if ($showSubject && trim($__env->yieldContent('letter-subject')) !== '')
@yield('letter-subject') diff --git a/be/resources/views/pdf/partials/letterfooter.blade.php b/be/resources/views/pdf/partials/letterfooter.blade.php index de80b00..97e8ed1 100644 --- a/be/resources/views/pdf/partials/letterfooter.blade.php +++ b/be/resources/views/pdf/partials/letterfooter.blade.php @@ -1,38 +1,11 @@ - - + + + diff --git a/be/resources/views/pdf/partials/letterhead.blade.php b/be/resources/views/pdf/partials/letterhead.blade.php index e27b900..6efb920 100644 --- a/be/resources/views/pdf/partials/letterhead.blade.php +++ b/be/resources/views/pdf/partials/letterhead.blade.php @@ -1,28 +1,15 @@ - -
- - - - - -
  
-
- - - @if (! empty($logoUrl)) - - @endif - - -
-
KOPERASI PERMODALAN
-
KELANTAN BERHAD
-
D-5-0228
-
+
+ @if (! empty($logoUrl)) + Koperasi Permodalan Kelantan Berhad + @endif +
+ Lot Pt 448, Tingkat 1, Jalan Kuala Krai, Batu 3, Wakaf Che Yeh, 15150 Kota Bharu, Kelantan. +
+
+ Tel: 09-7414331 | Fax: 09-7414330 | Web:www.koppkb.com +
diff --git a/be/resources/views/pdf/preview-approved-membership.blade.php b/be/resources/views/pdf/preview-approved-membership.blade.php index 620b66f..5e37896 100644 --- a/be/resources/views/pdf/preview-approved-membership.blade.php +++ b/be/resources/views/pdf/preview-approved-membership.blade.php @@ -13,8 +13,12 @@ 43000 Kajang, Selangor @endsection +@section('letter-honorific') + Tuan/Puan, +@endsection + @section('letter-subject') - KELULUSAN KEANGGOTAAN KOPERASI PERMODALAN KELANTAN BERHAD + KELULUSAN PERMOHONAN MENJADI ANGGOTA KOPERASI PERMODALAN KELANTAN BERHAD (KoPKB) @endsection {{-- @section('letter-salutation') @@ -23,38 +27,57 @@ @section('letter-body')

- Dengan segala hormatnya saya merujuk kepada perkara di atas. + Dengan segala hormatnya perkara di atas adalah dirujuk.

- 2. Sukacita dimaklumkan bahawa, permohonan tuan untuk menjadi anggota Koperasi - Permodalan Kelantan Berhad(KoPKB) telah dalam + 2. Sukacita dimaklumkan bahawa, permohonan tuan/puan untuk menjadi + Anggota Koperasi Permodalan Kelantan Berhad(KoPKB) telah dipertimbangkan dan DILULUSKAN + oleh Lembaga Koperasi dalam Mesyuarat Lembaga Bil. yang diadakan pada 01 Jan 2026.

- 3. Pihak Koperasi akan membuat potongan gaji melalui majikan tuan sebagaiman ketetapan berikut:- -

+ 3. Maklumat keanggotaan tuan/puan adalah seperti berikut:- +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PerkaraMaklumat
Nama AnggotaAli
No. Kad Pengenalan0123456789012345
No. Anggota123
Tarikh Kuat Kuasa Keanggotaan01 Jan
Modal Syer PermulaanRM500.00 (perlu dijelaskan dalam tempoh 6 bulan)
Fi MasukRM10.00
Yuran TahunanRM30.00 sebulan
+

+ 4. Sebagai anggota KoPKB, tuan/puan adalah tertakluk kepada Undang-Undang Kecil (UUK), dasar, polisi dan peraturan-peraturan KoPKB yang berkuat kuasa dari semasa ke semasa.

- 4. Untuk makluman tuan, kelulusan keanggotaan ini adalah bersyarat di mana tuan dikehendaki - menjelaskan modal syer minimum sebanyak RM500.00. Bayaran tersebut - perlu dibuat secara ansuran melalui potongan gaji dalam tempoh enam (6) bulan. - Pemenuhan syarat ini adalah bagi melayakkan tuan memperoleh hak, kewajipan dan - liabiliti sebagai anggota KoPKB sebagaimana terkandung di dalam Undang-Undang Kecil (UUK), - Perkara 59 yang telah didaftarkan pada 11 September 2024. Manakala had maksima modal syer adalah - sebanyak RM5,000.00. + 5. Sebagai anggota, tuan/puan adalah layak menikmati kemudahan, produk dan manfaat yang ditawarkan oleh koperasi dari semasa ke semasa, tertakluk kepada syarat-syarat yang ditetapkan.

+

+ 6. Pihak KoPKB mengucapkan tahniah dan mengalu-alukan penyertaan tuan/puan sebagai anggota. Semoga keanggotaan ini akan memberi manfaat kepada tuan/puan serta menyumbang kepada kemajuan dan kecemerlangan KoPKB.

@endsection @@ -63,14 +86,5 @@ @endsection @section('letter-signature') -
Dengan hormatnya,
KOPERASI PERMODALAN KELANTAN BERHAD
-
- @if (! empty($signatureUrl)) - - @endif -
-
MUHAMMAD NAFIS BIN ZAINUDDIN
-
Pengurus Besar
-
b/p Setiausaha
@endsection diff --git a/be/resources/views/pdf/preview.blade.php b/be/resources/views/pdf/preview.blade.php deleted file mode 100644 index e81357a..0000000 --- a/be/resources/views/pdf/preview.blade.php +++ /dev/null @@ -1,43 +0,0 @@ -@extends('pdf.layouts.letter') - -@section('letter-title', 'Pratonton Surat') - -@section('letter-reference', 'APP-2026-00001') - -@section('letter-date', now()->translatedFormat('d F Y')) - -@section('letter-recipient') - Ahmad bin Abdullah
- No. 12, Jalan Contoh,
- 43000 Kajang, Selangor -@endsection - -@section('letter-subject') - PERKARA: PRATONTON REKA BENTUK SURAT RASMI -@endsection - -@section('letter-salutation') - Assalamualaikum dan Salam Sejahtera, -@endsection - -@section('letter-body') -

- Dengan hormatnya perkara di atas adalah dirujuk. -

-

- 1. Ini adalah kandungan contoh untuk pratonton reka bentuk surat. Gunakan halaman ini - untuk menguji susun atur, fon, jarak, dan elemen letterhead sebelum menghasilkan PDF sebenar. -

-

- 2. Teks ini boleh diganti apabila templat surat muktamad disediakan. -

-@endsection - -@section('letter-closing') - Sekian, terima kasih. -@endsection - -@section('letter-signature') -
NAMA PENGARAH
-
Jawatan / {{ config('app.name') }}
-@endsection diff --git a/be/routes/web.php b/be/routes/web.php index a4def31..67db217 100644 --- a/be/routes/web.php +++ b/be/routes/web.php @@ -7,8 +7,5 @@ Route::get('/', function () { return redirect('/login'); }); -Route::get('/letters/preview', [LetterPreviewController::class, 'show']) - ->name('letters.preview'); - Route::get('/letters/preview-approved-membership', [LetterPreviewController::class, 'showMembershipApprovedLetter']) ->name('letters.preview-membership-approved-membership'); \ No newline at end of file diff --git a/fe/src/assets/images/fakers/food-beverage-1.jpg b/fe/src/assets/images/fakers/food-beverage-1.jpg deleted file mode 100755 index 547586c..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-1.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-10.jpg b/fe/src/assets/images/fakers/food-beverage-10.jpg deleted file mode 100755 index b860fcc..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-10.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-11.jpg b/fe/src/assets/images/fakers/food-beverage-11.jpg deleted file mode 100755 index 8136d3b..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-11.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-12.jpg b/fe/src/assets/images/fakers/food-beverage-12.jpg deleted file mode 100755 index dc18308..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-12.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-13.jpg b/fe/src/assets/images/fakers/food-beverage-13.jpg deleted file mode 100755 index 2daf94d..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-13.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-14.jpg b/fe/src/assets/images/fakers/food-beverage-14.jpg deleted file mode 100755 index 53ab2f9..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-14.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-15.jpg b/fe/src/assets/images/fakers/food-beverage-15.jpg deleted file mode 100755 index a78064a..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-15.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-16.jpg b/fe/src/assets/images/fakers/food-beverage-16.jpg deleted file mode 100755 index 39282a9..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-16.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-17.jpg b/fe/src/assets/images/fakers/food-beverage-17.jpg deleted file mode 100755 index 8ad5e92..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-17.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-18.jpg b/fe/src/assets/images/fakers/food-beverage-18.jpg deleted file mode 100755 index 70f6ba7..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-18.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-19.jpg b/fe/src/assets/images/fakers/food-beverage-19.jpg deleted file mode 100755 index b76b335..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-19.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-2.jpg b/fe/src/assets/images/fakers/food-beverage-2.jpg deleted file mode 100755 index c49f8d1..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-2.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-3.jpg b/fe/src/assets/images/fakers/food-beverage-3.jpg deleted file mode 100755 index 9d027e1..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-3.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-4.jpg b/fe/src/assets/images/fakers/food-beverage-4.jpg deleted file mode 100755 index 94579c6..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-4.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-5.jpg b/fe/src/assets/images/fakers/food-beverage-5.jpg deleted file mode 100755 index ddf354f..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-5.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-6.jpg b/fe/src/assets/images/fakers/food-beverage-6.jpg deleted file mode 100755 index efe41d6..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-6.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-7.jpg b/fe/src/assets/images/fakers/food-beverage-7.jpg deleted file mode 100755 index 7ce9373..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-7.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-8.jpg b/fe/src/assets/images/fakers/food-beverage-8.jpg deleted file mode 100755 index 70ce8da..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-8.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/food-beverage-9.jpg b/fe/src/assets/images/fakers/food-beverage-9.jpg deleted file mode 100755 index ff4e94b..0000000 Binary files a/fe/src/assets/images/fakers/food-beverage-9.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-1.jpg b/fe/src/assets/images/fakers/preview-1.jpg deleted file mode 100755 index e303cc2..0000000 Binary files a/fe/src/assets/images/fakers/preview-1.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-10.jpg b/fe/src/assets/images/fakers/preview-10.jpg deleted file mode 100755 index c0b6e3f..0000000 Binary files a/fe/src/assets/images/fakers/preview-10.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-11.jpg b/fe/src/assets/images/fakers/preview-11.jpg deleted file mode 100755 index 3f2ef43..0000000 Binary files a/fe/src/assets/images/fakers/preview-11.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-12.jpg b/fe/src/assets/images/fakers/preview-12.jpg deleted file mode 100755 index e88dcf2..0000000 Binary files a/fe/src/assets/images/fakers/preview-12.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-13.jpg b/fe/src/assets/images/fakers/preview-13.jpg deleted file mode 100755 index 348cbfe..0000000 Binary files a/fe/src/assets/images/fakers/preview-13.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-14.jpg b/fe/src/assets/images/fakers/preview-14.jpg deleted file mode 100755 index da2cf26..0000000 Binary files a/fe/src/assets/images/fakers/preview-14.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-15.jpg b/fe/src/assets/images/fakers/preview-15.jpg deleted file mode 100755 index 61008f8..0000000 Binary files a/fe/src/assets/images/fakers/preview-15.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-2.jpg b/fe/src/assets/images/fakers/preview-2.jpg deleted file mode 100755 index b709fe7..0000000 Binary files a/fe/src/assets/images/fakers/preview-2.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-3.jpg b/fe/src/assets/images/fakers/preview-3.jpg deleted file mode 100755 index 3c7c5fa..0000000 Binary files a/fe/src/assets/images/fakers/preview-3.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-4.jpg b/fe/src/assets/images/fakers/preview-4.jpg deleted file mode 100755 index ddd80c8..0000000 Binary files a/fe/src/assets/images/fakers/preview-4.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-5.jpg b/fe/src/assets/images/fakers/preview-5.jpg deleted file mode 100755 index 736a6bd..0000000 Binary files a/fe/src/assets/images/fakers/preview-5.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-6.jpg b/fe/src/assets/images/fakers/preview-6.jpg deleted file mode 100755 index d696a88..0000000 Binary files a/fe/src/assets/images/fakers/preview-6.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-7.jpg b/fe/src/assets/images/fakers/preview-7.jpg deleted file mode 100755 index d43746a..0000000 Binary files a/fe/src/assets/images/fakers/preview-7.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-8.jpg b/fe/src/assets/images/fakers/preview-8.jpg deleted file mode 100755 index 9bd6b3a..0000000 Binary files a/fe/src/assets/images/fakers/preview-8.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/preview-9.jpg b/fe/src/assets/images/fakers/preview-9.jpg deleted file mode 100755 index 2df7b9c..0000000 Binary files a/fe/src/assets/images/fakers/preview-9.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-1.jpg b/fe/src/assets/images/fakers/profile-1.jpg deleted file mode 100755 index 766ead5..0000000 Binary files a/fe/src/assets/images/fakers/profile-1.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-10.jpg b/fe/src/assets/images/fakers/profile-10.jpg deleted file mode 100755 index c8cfd39..0000000 Binary files a/fe/src/assets/images/fakers/profile-10.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-11.jpg b/fe/src/assets/images/fakers/profile-11.jpg deleted file mode 100755 index 345e72e..0000000 Binary files a/fe/src/assets/images/fakers/profile-11.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-12.jpg b/fe/src/assets/images/fakers/profile-12.jpg deleted file mode 100755 index 0f1549e..0000000 Binary files a/fe/src/assets/images/fakers/profile-12.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-13.jpg b/fe/src/assets/images/fakers/profile-13.jpg deleted file mode 100755 index 825cfc2..0000000 Binary files a/fe/src/assets/images/fakers/profile-13.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-14.jpg b/fe/src/assets/images/fakers/profile-14.jpg deleted file mode 100755 index c398c6a..0000000 Binary files a/fe/src/assets/images/fakers/profile-14.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-15.jpg b/fe/src/assets/images/fakers/profile-15.jpg deleted file mode 100755 index 0a8a04e..0000000 Binary files a/fe/src/assets/images/fakers/profile-15.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-2.jpg b/fe/src/assets/images/fakers/profile-2.jpg deleted file mode 100755 index 262fdd3..0000000 Binary files a/fe/src/assets/images/fakers/profile-2.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-3.jpg b/fe/src/assets/images/fakers/profile-3.jpg deleted file mode 100755 index bc46fce..0000000 Binary files a/fe/src/assets/images/fakers/profile-3.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-4.jpg b/fe/src/assets/images/fakers/profile-4.jpg deleted file mode 100755 index ffa5e24..0000000 Binary files a/fe/src/assets/images/fakers/profile-4.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-5.jpg b/fe/src/assets/images/fakers/profile-5.jpg deleted file mode 100755 index 953420d..0000000 Binary files a/fe/src/assets/images/fakers/profile-5.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-6.jpg b/fe/src/assets/images/fakers/profile-6.jpg deleted file mode 100755 index ebf71d9..0000000 Binary files a/fe/src/assets/images/fakers/profile-6.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-7.jpg b/fe/src/assets/images/fakers/profile-7.jpg deleted file mode 100755 index b7f8cea..0000000 Binary files a/fe/src/assets/images/fakers/profile-7.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-8.jpg b/fe/src/assets/images/fakers/profile-8.jpg deleted file mode 100755 index 6caf684..0000000 Binary files a/fe/src/assets/images/fakers/profile-8.jpg and /dev/null differ diff --git a/fe/src/assets/images/fakers/profile-9.jpg b/fe/src/assets/images/fakers/profile-9.jpg deleted file mode 100755 index c5104d3..0000000 Binary files a/fe/src/assets/images/fakers/profile-9.jpg and /dev/null differ diff --git a/fe/src/assets/images/logo-original.svg b/fe/src/assets/images/logo-original.svg new file mode 100644 index 0000000..9fa3c6a --- /dev/null +++ b/fe/src/assets/images/logo-original.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + KOPERASI PERMODALAN KELANTAN BERHAD + + + diff --git a/fe/src/modules/external-system/data/dummy-external-systems.ts b/fe/src/modules/external-system/data/dummy-external-systems.ts index 380a3fb..f9ae832 100644 --- a/fe/src/modules/external-system/data/dummy-external-systems.ts +++ b/fe/src/modules/external-system/data/dummy-external-systems.ts @@ -3,7 +3,7 @@ import type { ExternalSystem } from '../types/external-system.types' export const dummyExternalSystems: ExternalSystem[] = [ { id: 'ext-001', - code: 'KOPKB-PORTAL', + code: 'portal-mykopkb', name: 'Portal Ahli KOPKB', description: 'Sistem utama keahlian Koperasi Permodalan Kelantan Berhad untuk semakan dividen, penyata dan maklumat ahli.', @@ -13,15 +13,15 @@ export const dummyExternalSystems: ExternalSystem[] = [ starts_at: '2026-01-01T00:00:00+08:00', ends_at: null, opens_in_new_tab: true, - contact_email: 'sokongan@koppkb.com', + contact_email: 'dev_kopkb@gmail.com', notes: 'Log masuk menggunakan e-mel berdaftar ahli KOPKB.', created_at: '2026-01-15T09:00:00+08:00', updated_at: '2026-06-01T14:30:00+08:00', }, { id: 'ext-002', - code: 'AGM-VOTE', - name: 'Sistem Pengundian AGM', + code: 'e-vote', + name: 'Sistem Pengundian AGM KOPKB', description: 'Platform pengundian dalam talian untuk Mesyuarat Agung Tahunan. Hanya tersedia semasa tempoh pengundian.', url: 'https://e-vote.erahn.com.my/login', @@ -30,7 +30,7 @@ export const dummyExternalSystems: ExternalSystem[] = [ starts_at: '2026-05-01T08:00:00+08:00', ends_at: null, opens_in_new_tab: true, - contact_email: 'agm@koppkb.com', + contact_email: 'dev_kopkb@gmail.com', notes: 'Sila lengkapkan profil sebelum mengundi.', created_at: '2026-05-20T10:00:00+08:00', updated_at: '2026-06-28T11:15:00+08:00', diff --git a/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue b/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue index 58651fa..9dd5694 100644 --- a/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue +++ b/fe/src/modules/membership-application/pages/MembershipApplicationDetail.vue @@ -93,6 +93,8 @@ const previewUrl = ref(null) const previewDocument = ref(null) const boardMeetingReference = ref('') const boardMeetingReferenceError = ref(null) +const boardMeetingDate = ref(dayjs().format('YYYY-MM-DD')) +const boardMeetingDateError = ref(null) const deleteConfirmDialogOpen = ref(false) const pendingDeleteDocument = ref(null) @@ -372,6 +374,8 @@ function openConfirmAction( if (action.type === 'complete') { boardMeetingReference.value = '' boardMeetingReferenceError.value = null + boardMeetingDate.value = dayjs().format('YYYY-MM-DD') + boardMeetingDateError.value = null } pendingAction.value = action @@ -383,6 +387,8 @@ function closeConfirmDialog() { pendingAction.value = null boardMeetingReference.value = '' boardMeetingReferenceError.value = null + boardMeetingDate.value = dayjs().format('YYYY-MM-DD') + boardMeetingDateError.value = null } async function confirmPendingAction() { @@ -391,6 +397,7 @@ async function confirmPendingAction() { error.value = null successMessage.value = null boardMeetingReferenceError.value = null + boardMeetingDateError.value = null if (pendingAction.value.type === 'complete') { const reference = boardMeetingReference.value.trim() @@ -398,6 +405,10 @@ async function confirmPendingAction() { boardMeetingReferenceError.value = 'Rujukan mesyuarat lembaga diperlukan.' return } + if (!boardMeetingDate.value) { + boardMeetingDateError.value = 'Tarikh mesyuarat lembaga diperlukan.' + return + } completeSubmitting.value = true } else { reviewSubmitting.value = true @@ -421,6 +432,7 @@ async function confirmPendingAction() { } else { response = await completeMembershipApplication(applicationId.value, { board_meeting_reference: boardMeetingReference.value.trim(), + board_meeting_date: boardMeetingDate.value, }) } @@ -436,6 +448,9 @@ async function confirmPendingAction() { if (validationErrors?.board_meeting_reference?.[0]) { boardMeetingReferenceError.value = validationErrors.board_meeting_reference[0] } + if (validationErrors?.board_meeting_date?.[0]) { + boardMeetingDateError.value = validationErrors.board_meeting_date[0] + } } finally { reviewSubmitting.value = false completeSubmitting.value = false @@ -1003,6 +1018,12 @@ onUnmounted(() => { @input="boardMeetingReferenceError = null" /> {{ boardMeetingReferenceError }} + + Tarikh Mesyuarat Lembaga + + {{ boardMeetingDateError }} +
diff --git a/fe/src/modules/membership-application/pages/MembershipApplicationList.vue b/fe/src/modules/membership-application/pages/MembershipApplicationList.vue index 84b4f46..ba8da7b 100644 --- a/fe/src/modules/membership-application/pages/MembershipApplicationList.vue +++ b/fe/src/modules/membership-application/pages/MembershipApplicationList.vue @@ -110,6 +110,8 @@ const batchSuccessMessage = ref(null) const batchFailedItems = ref([]) const boardMeetingReference = ref('') const boardMeetingReferenceError = ref(null) +const boardMeetingDate = ref(dayjs().format('YYYY-MM-DD')) +const boardMeetingDateError = ref(null) const downloadingResultLetterId = ref(null) const selectableApplications = computed(() => @@ -162,6 +164,8 @@ function openBatchConfirm() { if (!selectedIds.value.length) return boardMeetingReference.value = '' boardMeetingReferenceError.value = null + boardMeetingDate.value = dayjs().format('YYYY-MM-DD') + boardMeetingDateError.value = null batchConfirmOpen.value = true } @@ -173,15 +177,23 @@ async function confirmBatchComplete() { boardMeetingReferenceError.value = 'Rujukan mesyuarat lembaga diperlukan.' return } + if (!boardMeetingDate.value) { + boardMeetingDateError.value = 'Tarikh mesyuarat lembaga diperlukan.' + return + } batchSubmitting.value = true batchSuccessMessage.value = null batchFailedItems.value = [] boardMeetingReferenceError.value = null + boardMeetingDateError.value = null error.value = null try { - const response = await batchCompleteMembershipApplications(selectedIds.value, reference) + const response = await batchCompleteMembershipApplications(selectedIds.value, { + board_meeting_reference: reference, + board_meeting_date: boardMeetingDate.value, + }) if (response.success) { batchSuccessMessage.value = response.message @@ -200,6 +212,7 @@ async function confirmBatchComplete() { error.value = responseData.message ?? getApiErrorMessage(err, 'Gagal menyelesaikan permohonan.') const validationErrors = getApiValidationErrors(err) boardMeetingReferenceError.value = validationErrors?.board_meeting_reference?.[0] ?? null + boardMeetingDateError.value = validationErrors?.board_meeting_date?.[0] ?? null } else { error.value = getApiErrorMessage(err, 'Gagal menyelesaikan permohonan.') } @@ -245,30 +258,6 @@ function goToApplicationEdit(id: string) { router.push({ name: 'edit-membership-application', params: { id } }) } -function canDownloadLetter(item: MembershipApplicationListItem): boolean { - return !!item.has_result_letter && !!item.result_letter_document -} - -async function handleDownloadResultLetter(item: MembershipApplicationListItem) { - const document = item.result_letter_document - if (!document) return - - downloadingResultLetterId.value = item.id - - try { - await downloadMembershipApplicationDocument( - item.id, - document.id, - document.name, - document.mime_type, - ) - } catch (err) { - error.value = getApiErrorMessage(err, 'Gagal memuat turun surat keputusan.') - } finally { - downloadingResultLetterId.value = null - } -} - const headers = computed(() => { const base: TableHeader[] = [ { title: 'Bil.', key: '#', sortable: false }, @@ -450,12 +439,6 @@ const headers = computed(() => { @click="goToApplicationEdit((item as MembershipApplicationListItem).id)">
@@ -475,6 +458,12 @@ const headers = computed(() => { @input="boardMeetingReferenceError = null" /> {{ boardMeetingReferenceError }} + + Tarikh Mesyuarat Lembaga + + {{ boardMeetingDateError }} +
diff --git a/fe/src/modules/membership-application/services/membership-application.service.ts b/fe/src/modules/membership-application/services/membership-application.service.ts index 40a685e..be08142 100644 --- a/fe/src/modules/membership-application/services/membership-application.service.ts +++ b/fe/src/modules/membership-application/services/membership-application.service.ts @@ -225,13 +225,13 @@ export async function completeMembershipApplication( export async function batchCompleteMembershipApplications( applicationIds: string[], - boardMeetingReference: string, + payload: GenerateResultLetterPayload, ): Promise { const { data } = await api.post( '/v1/membership-applications/batch-complete', { application_ids: applicationIds, - board_meeting_reference: boardMeetingReference, + ...payload, }, ) diff --git a/fe/src/modules/membership-application/types/membership-application.types.ts b/fe/src/modules/membership-application/types/membership-application.types.ts index fd3b416..db8981b 100644 --- a/fe/src/modules/membership-application/types/membership-application.types.ts +++ b/fe/src/modules/membership-application/types/membership-application.types.ts @@ -245,6 +245,7 @@ export type BatchCompleteResponse = { export interface GenerateResultLetterPayload { board_meeting_reference: string + board_meeting_date: string } export type GenerateResultLetterResponse = { diff --git a/fe/src/modules/profile/components/MemberDigitalCardBack.vue b/fe/src/modules/profile/components/MemberDigitalCardBack.vue index bc49c86..c8fbd35 100644 --- a/fe/src/modules/profile/components/MemberDigitalCardBack.vue +++ b/fe/src/modules/profile/components/MemberDigitalCardBack.vue @@ -1,7 +1,7 @@