diff --git a/TODO.md b/TODO.md index ce714ce..fff4912 100644 --- a/TODO.md +++ b/TODO.md @@ -15,4 +15,6 @@ [ ] boleh print semua borang [ ] jana surat lepas lulus anggota -## \ No newline at end of file +## Present to Boss (2/7/2026) +[ ] discuss logo baru MyKOPKB +[ ] layout footer untuk surat rasmi \ No newline at end of file diff --git a/be/.env.example b/be/.env.example index 30da688..f0a4c9c 100644 --- a/be/.env.example +++ b/be/.env.example @@ -67,11 +67,12 @@ AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" # Browsershot (PDF generation via headless Chrome + Puppeteer in be/) +# Required in Docker/Sail. After first sail up: sail npm install && sail npx puppeteer browsers install chrome-headless-shell +BROWSERSHOT_NO_SANDBOX=true # BROWSERSHOT_NODE_BINARY= # BROWSERSHOT_NPM_BINARY= # BROWSERSHOT_NODE_MODULE_PATH= # BROWSERSHOT_CHROME_PATH= -# BROWSERSHOT_NO_SANDBOX=false # BROWSERSHOT_TIMEOUT=60 # HttpOnly Sanctum token cookie (set on api.* domain) diff --git a/be/Modules/MembershipApplication/Entities/MembershipApplication.php b/be/Modules/MembershipApplication/Entities/MembershipApplication.php index 57409a1..9464541 100644 --- a/be/Modules/MembershipApplication/Entities/MembershipApplication.php +++ b/be/Modules/MembershipApplication/Entities/MembershipApplication.php @@ -2,11 +2,13 @@ namespace Modules\MembershipApplication\Entities; +use App\Models\Document; use App\Traits\HasDocuments; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Relations\MorphOne; use Modules\Auth\Entities\User; use Modules\MembershipApplication\Enums\ApplicationStatus; @@ -14,6 +16,12 @@ class MembershipApplication extends Model { use HasDocuments, HasUuids; + public const RESULT_LETTER_DOCUMENT_TYPE = 'result_letter'; + + public const MINIMUM_SHARE_CAPITAL = 500; + + public const SHARE_INSTALLMENT_MONTHS = 6; + protected $table = 'membership_applications'; /** @@ -65,4 +73,10 @@ class MembershipApplication extends Model { return $this->belongsTo(User::class); } + + public function resultLetterDocument(): MorphOne + { + return $this->morphOne(Document::class, 'documentable') + ->where('type', self::RESULT_LETTER_DOCUMENT_TYPE); + } } diff --git a/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php b/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php index 2a842f6..006de7b 100644 --- a/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php +++ b/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php @@ -17,10 +17,12 @@ use Modules\MembershipApplication\Enums\ManagementDecision; use Modules\MembershipApplication\Http\Requests\AssignReferencesRequest; use Modules\MembershipApplication\Http\Requests\BatchCompleteRequest; use Modules\MembershipApplication\Http\Requests\BoardReviewRequest; +use Modules\MembershipApplication\Http\Requests\GenerateResultLetterRequest; use Modules\MembershipApplication\Http\Requests\ManagementReviewRequest; use Modules\MembershipApplication\Http\Requests\UpdateMembershipApplicationRequest; use Modules\MembershipApplication\Http\Requests\UploadMembershipApplicationDocumentRequest; use Modules\MembershipApplication\Services\MembershipApplicationService; +use Modules\MembershipApplication\Transformers\MembershipApplicationDocumentResource; use Modules\MembershipApplication\Transformers\MembershipApplicationListResource; use Modules\MembershipApplication\Transformers\MembershipApplicationResource; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -243,6 +245,29 @@ class MembershipApplicationController extends Controller 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); @@ -253,10 +278,18 @@ class MembershipApplicationController extends Controller abort(404, 'Permohonan tidak dijumpai.'); } + $boardMeetingReference = (string) $request->query( + 'board_meeting_reference', + 'Mesyuarat Lembaga', + ); + $view = 'membershipapplication::pdf.approved-letter'; - $data = ['application' => $application]; + $data = $this->membershipApplicationService->getResultLetterViewData( + $application, + $boardMeetingReference, + ); $format = $request->query('format', 'html'); - $filename = 'surat-kelulusan-'.$application->application_number.'.pdf'; + $filename = 'surat-keputusan-'.$application->application_number.'.pdf'; if ($format === 'pdf') { return $this->letterService->pdfResponse($view, $data, $filename); diff --git a/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php b/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php new file mode 100644 index 0000000..72aa39c --- /dev/null +++ b/be/Modules/MembershipApplication/Http/Requests/GenerateResultLetterRequest.php @@ -0,0 +1,34 @@ +|string> + */ + public function rules(): array + { + return [ + 'board_meeting_reference' => 'required|string|max:255', + ]; + } + + /** + * @return array + */ + public function messages(): array + { + return [ + 'board_meeting_reference.required' => 'Rujukan mesyuarat lembaga diperlukan.', + 'board_meeting_reference.max' => 'Rujukan mesyuarat lembaga tidak boleh melebihi 255 aksara.', + ]; + } +} diff --git a/be/Modules/MembershipApplication/Policies/MembershipApplicationPolicy.php b/be/Modules/MembershipApplication/Policies/MembershipApplicationPolicy.php index 9ce7624..764e7f7 100644 --- a/be/Modules/MembershipApplication/Policies/MembershipApplicationPolicy.php +++ b/be/Modules/MembershipApplication/Policies/MembershipApplicationPolicy.php @@ -65,6 +65,14 @@ class MembershipApplicationPolicy return $user->hasPermissionTo('tetapkan pencadang penyokong'); } + /** + * Generate the result letter PDF for a completed application. + */ + public function generateResultLetter($user, ?MembershipApplication $membershipApplication = null): bool + { + return $user->hasPermissionTo('jana surat keputusan keahlian'); + } + /** * Admin: edit application details before completion. */ diff --git a/be/Modules/MembershipApplication/Providers/MembershipApplicationServiceProvider.php b/be/Modules/MembershipApplication/Providers/MembershipApplicationServiceProvider.php index 26c48fc..bef9c73 100644 --- a/be/Modules/MembershipApplication/Providers/MembershipApplicationServiceProvider.php +++ b/be/Modules/MembershipApplication/Providers/MembershipApplicationServiceProvider.php @@ -155,7 +155,7 @@ class MembershipApplicationServiceProvider extends ServiceProvider public function registerViews(): void { $viewPath = resource_path('views/modules/'.$this->nameLower); - $sourcePath = module_path($this->name, 'Resources/Views'); + $sourcePath = module_path($this->name, 'Resources/views'); $this->publishes([$sourcePath => $viewPath], ['views', $this->nameLower.'-module-views']); diff --git a/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php b/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php index 9e3d1fd..679ef22 100644 --- a/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php +++ b/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php @@ -46,7 +46,14 @@ class MembershipApplicationRepository implements MembershipApplicationRepository $sortOrder = strtolower($sortOrder) === 'asc' ? 'asc' : 'desc'; $query = MembershipApplication::query() - ->with(['applicant:id,membership_application_id,name,email,ic_number']) + ->with([ + 'applicant:id,membership_application_id,name,email,ic_number', + 'resultLetterDocument:id,documentable_type,documentable_id,name,type,mime_type,file_size', + ]) + ->withCount([ + 'documents as result_letter_count' => fn ($documentQuery) => $documentQuery + ->where('type', MembershipApplication::RESULT_LETTER_DOCUMENT_TYPE), + ]) ->orderBy($sortBy, $sortOrder); if ($status !== null && $status !== '') { 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 3358ce7..cdfc055 100644 --- a/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php +++ b/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php @@ -1,12 +1,73 @@ -{{-- - Approval letter — extends the shared base layout. - Fill in sections below when the final design is ready. ---}} @extends('pdf.layouts.letter') -{{-- @section('letter-reference', $application->application_number) --}} -{{-- @section('letter-date', ...) --}} -{{-- @section('letter-recipient') ... @endsection --}} -{{-- @section('letter-subject') PERKARA: ... @endsection --}} -{{-- @section('letter-body') ... @endsection --}} -{{-- @section('letter-signature') ... @endsection --}} +@section('letter-title', 'Surat') + +@section('letter-reference', $application->application_number) + +@section('letter-date', now()->translatedFormat('d F Y')) + +@section('letter-recipient') + {{ $applicant->name }}
+ {!! nl2br(e($applicant->address)) !!}
+ {{ $applicant->postcode }} +@endsection + +@section('letter-subject') + {{ $letterSubject }} +@endsection + +@section('letter-body') +

+ Dengan segala hormatnya saya merujuk kepada perkara di atas. +

+

+ 2. {{ $openingTone }} dimaklumkan bahawa, permohonan tuan untuk menjadi anggota Koperasi + Permodalan Kelantan Berhad (KoPKB) telah {{ $boardResultLabel }} dalam {{ $boardMeetingReference }}. +

+ + @if ($isPassed) +

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

+

+

+ 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. +

+ @else +

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

+ @endif +@endsection + +@section('letter-closing') + Sekian, terima kasih. +@endsection + +@section('letter-signature') +
Dengan hormatnya,
+
KOPERASI PERMODALAN KELANTAN BERHAD
+
+
MUHAMMAD NAFIS BIN ZAINUDDIN
+
Pengurus Besar
+
b/p Setiausaha
+@endsection diff --git a/be/Modules/MembershipApplication/Routes/api.php b/be/Modules/MembershipApplication/Routes/api.php index 7989d78..5259d36 100644 --- a/be/Modules/MembershipApplication/Routes/api.php +++ b/be/Modules/MembershipApplication/Routes/api.php @@ -43,4 +43,6 @@ Route::middleware(['auth:sanctum', 'single.session'])->prefix('v1')->group(funct // approved letter preview (html or pdf) Route::get('membership-applications/{membershipApplication}/approved-letter', [MembershipApplicationController::class, 'approvedLetter'])->name('membership-application.approved-letter'); + // generate and store result letter (one-time) + Route::post('membership-applications/{membershipApplication}/result-letter', [MembershipApplicationController::class, 'generateResultLetter'])->name('membership-application.generate-result-letter'); }); diff --git a/be/Modules/MembershipApplication/Services/MembershipApplicationService.php b/be/Modules/MembershipApplication/Services/MembershipApplicationService.php index 7cb0712..10c6284 100644 --- a/be/Modules/MembershipApplication/Services/MembershipApplicationService.php +++ b/be/Modules/MembershipApplication/Services/MembershipApplicationService.php @@ -3,6 +3,7 @@ namespace Modules\MembershipApplication\Services; use App\Services\DocumentService; +use App\Services\LetterService; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\DB; @@ -30,6 +31,7 @@ class MembershipApplicationService public function __construct( protected MembershipApplicationRepositoryInterface $repository, protected DocumentService $documentService, + protected LetterService $letterService, ) {} /** @@ -301,6 +303,106 @@ class MembershipApplicationService ]; } + public function generateResultLetter(MembershipApplication $application, string $boardMeetingReference,): MembershipApplication { + if ($application->status !== ApplicationStatus::Completed) { + throw ValidationException::withMessages([ + 'status' => ['Surat keputusan hanya boleh dijana untuk permohonan yang telah selesai.'], + ]); + } + + if (! in_array($application->board_result, [BoardResult::Pass->value, BoardResult::Fail->value], true)) { + throw ValidationException::withMessages([ + 'board_result' => ['Keputusan lembaga belum ditetapkan.'], + ]); + } + + return DB::transaction(function () use ($application, $boardMeetingReference) { + $lockedApplication = MembershipApplication::query() + ->whereKey($application->id) + ->lockForUpdate() + ->firstOrFail(); + + if ($lockedApplication->hasDocumentsOfType(MembershipApplication::RESULT_LETTER_DOCUMENT_TYPE)) { + throw ValidationException::withMessages([ + 'result_letter' => ['Surat keputusan telah dijana dan tidak boleh dijana semula.'], + ]); + } + + $application = $this->repository->findByIdWithRelations($lockedApplication->id); + + if (! $application || ! $application->applicant) { + throw ValidationException::withMessages([ + 'application' => ['Maklumat pemohon tidak lengkap.'], + ]); + } + + $viewData = $this->buildResultLetterViewData($application, $boardMeetingReference); + $pdf = $this->letterService->renderPdf('membershipapplication::pdf.approved-letter', $viewData); + $fileName = 'surat-keputusan-'.$application->application_number.'.pdf'; + + $this->documentService->storeGeneratedDocument( + $application, + $pdf, + $fileName, + MembershipApplication::RESULT_LETTER_DOCUMENT_TYPE, + ); + + return $this->repository->findByIdWithRelations($application->id); + }); + } + + /** + * @return array + */ + public function getResultLetterViewData( + MembershipApplication $application, + string $boardMeetingReference, + ): array { + $application = $this->repository->findByIdWithRelations($application->id); + + if (! $application || ! $application->applicant) { + throw ValidationException::withMessages([ + 'application' => ['Maklumat pemohon tidak lengkap.'], + ]); + } + + return $this->buildResultLetterViewData($application, $boardMeetingReference); + } + + /** + * @return array + */ + protected function buildResultLetterViewData( + MembershipApplication $application, + string $boardMeetingReference, + ): array { + $applicant = $application->applicant; + $isPassed = $application->board_result === BoardResult::Pass->value; + $monthlyShareInstallment = number_format( + MembershipApplication::MINIMUM_SHARE_CAPITAL / MembershipApplication::SHARE_INSTALLMENT_MONTHS, + 2, + '.', + '', + ); + + return [ + 'application' => $application, + 'applicant' => $applicant, + 'boardMeetingReference' => $boardMeetingReference, + '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((float) $applicant->stock_monthly_contribution, 2, '.', ''), + 'feeMonthlyContribution' => number_format((float) $applicant->fee_monthly_contribution, 2, '.', ''), + 'shareInstallmentMonths' => MembershipApplication::SHARE_INSTALLMENT_MONTHS, + 'minimumShareCapital' => number_format(MembershipApplication::MINIMUM_SHARE_CAPITAL, 2, '.', ''), + ]; + } + protected function createMemberFromApplication(MembershipApplication $application): User { $application->loadMissing(['applicant', 'heirs']); diff --git a/be/Modules/MembershipApplication/Transformers/MembershipApplicationListResource.php b/be/Modules/MembershipApplication/Transformers/MembershipApplicationListResource.php index eafacb6..161060d 100644 --- a/be/Modules/MembershipApplication/Transformers/MembershipApplicationListResource.php +++ b/be/Modules/MembershipApplication/Transformers/MembershipApplicationListResource.php @@ -15,6 +15,11 @@ class MembershipApplicationListResource extends JsonResource 'status' => $this->status?->value ?? $this->status, 'board_result' => $this->board_result ?: null, 'submitted_at' => $this->submitted_at, + 'has_result_letter' => ($this->result_letter_count ?? 0) > 0, + 'result_letter_document' => $this->when( + ($this->result_letter_count ?? 0) > 0 && $this->relationLoaded('resultLetterDocument') && $this->resultLetterDocument, + fn () => new MembershipApplicationDocumentResource($this->resultLetterDocument), + ), 'applicant' => $this->whenLoaded('applicant', fn () => [ 'name' => $this->applicant->name, 'email' => $this->applicant->email, diff --git a/be/app/Services/DocumentService.php b/be/app/Services/DocumentService.php index 4b78240..a69d239 100644 --- a/be/app/Services/DocumentService.php +++ b/be/app/Services/DocumentService.php @@ -13,12 +13,7 @@ class DocumentService /** * Upload a document for any model. */ - public function uploadDocument( - Model $model, - UploadedFile $file, - string $documentType = 'general', - ?string $description = null - ): Document { + public function uploadDocument(Model $model, UploadedFile $file, string $documentType = 'general', ?string $description = null): Document { $fileName = time().'_'.$file->getClientOriginalName(); $folderName = strtolower(class_basename($model)); $filePath = $file->storeAs("documents/{$folderName}", $fileName, Document::STORAGE_DISK); @@ -34,6 +29,27 @@ class DocumentService ]); } + /** + * Store generated file contents (e.g. PDF) for a model. + */ + public function storeGeneratedDocument(Model $model, string $contents, string $fileName, string $documentType = 'general', string $mimeType = 'application/pdf', ?string $description = null,): Document { + $folderName = strtolower(class_basename($model)); + $storedFileName = time().'_'.$fileName; + $filePath = "documents/{$folderName}/{$storedFileName}"; + + Storage::disk(Document::STORAGE_DISK)->put($filePath, $contents); + + return $model->documents()->create([ + 'name' => $fileName, + 'path' => $filePath, + 'file_size' => strlen($contents), + 'mime_type' => $mimeType, + 'type' => $documentType, + 'description' => $description, + 'uploaded_by' => auth()->id(), + ]); + } + /** * Delete a document. */ diff --git a/be/app/Services/LetterService.php b/be/app/Services/LetterService.php index efc6931..ba27182 100644 --- a/be/app/Services/LetterService.php +++ b/be/app/Services/LetterService.php @@ -33,7 +33,7 @@ class LetterService */ public function pdfResponse(string $view, array $data = [], string $filename = 'letter.pdf'): Response { - $pdf = $this->makeBrowsershot($this->renderHtml($view, $data))->pdf(); + $pdf = $this->renderPdf($view, $data); return response($pdf, SymfonyResponse::HTTP_OK, [ 'Content-Type' => 'application/pdf', @@ -41,6 +41,14 @@ class LetterService ]); } + /** + * @param array $data + */ + public function renderPdf(string $view, array $data = []): string + { + return $this->makeBrowsershot($this->renderHtml($view, $data))->pdf(); + } + /** * @param array $overrides * @return array diff --git a/be/docker-compose.yml b/be/docker-compose.yml index c0b34fc..5b2aaf7 100644 --- a/be/docker-compose.yml +++ b/be/docker-compose.yml @@ -1,5 +1,8 @@ services: laravel.test: + # Apple Silicon / OrbStack: Puppeteer's Chrome must match container arch. + # amd64 emulation avoids linux_arm + linux64 chrome-headless-shell mismatch. + platform: linux/amd64 build: context: "./vendor/laravel/sail/runtimes/8.4" dockerfile: Dockerfile diff --git a/be/package.json b/be/package.json index 63e1661..3b8cc5f 100644 --- a/be/package.json +++ b/be/package.json @@ -4,7 +4,8 @@ "type": "module", "scripts": { "build": "vite build", - "dev": "vite" + "dev": "vite", + "browsershot:install": "puppeteer browsers install chrome-headless-shell" }, "devDependencies": { "@tailwindcss/vite": "^4.0.0", diff --git a/be/public/favicon.ico b/be/public/favicon.ico index e69de29..4aea46d 100644 Binary files a/be/public/favicon.ico and b/be/public/favicon.ico differ diff --git a/be/resources/views/pdf/preview-approved-membership.blade.php b/be/resources/views/pdf/preview-approved-membership.blade.php index 89c7937..fd2819b 100644 --- a/be/resources/views/pdf/preview-approved-membership.blade.php +++ b/be/resources/views/pdf/preview-approved-membership.blade.php @@ -7,6 +7,7 @@ @section('letter-date', now()->translatedFormat('d F Y')) @section('letter-recipient') +{{-- auto fetch from membership_application_applicants --}} Ahmad bin Abdullah
No. 12, Jalan Contoh,
43000 Kajang, Selangor @@ -26,20 +27,19 @@

2. Sukacita dimaklumkan bahawa, permohonan tuan untuk menjadi anggota Koperasi - Permodalan Kelantan Berhad(KoPKB) telah DILULUSKAN dalam Mesyuarat Lembaga - Koperasi Bil. 4-2025/2026 pada 24 Jun 2026. + Permodalan Kelantan Berhad(KoPKB) telah {{ membership_applications.board_result }} dalam {{ User manual fills before generate }}

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

  • Saham
    • -
    • Potongan sebanyak RM 84.00 (Mei 2026 - Jun 2026)
    • -
    • Potongan sebanyak RM 50.00 sehingga Syer Maksima
    • +
    • Potongan sebanyak RM 84.00 {{(6 months auto calculate)}}
    • +
    • Potongan sebanyak RM {{ membership_application_applicants.stock_monthly_contribution }} sehingga Syer Maksima
  • Yuran
    • -
    • Potongan sebanyak RM 50.00 setiap bulan
    • +
    • Potongan sebanyak RM {{ membership_application_applicants.fee_monthly_contribution }} setiap bulan
  • Fi Masuk
    • diff --git a/fe/.DS_Store b/fe/.DS_Store index bea6f35..8dd9799 100644 Binary files a/fe/.DS_Store and b/fe/.DS_Store differ diff --git a/fe/docker/Dockerfile b/fe/docker/Dockerfile index b383b46..08dfbe8 100644 --- a/fe/docker/Dockerfile +++ b/fe/docker/Dockerfile @@ -7,10 +7,16 @@ RUN npm install -g pnpm && \ pnpm install --frozen-lockfile --ignore-scripts && \ pnpm rebuild esbuild COPY fe/ ./ +ARG VITE_APP_NAME="MyKOPKB" +ARG VITE_APP_VERSION="1.0" ARG VITE_API_BASE_URL=https://api.koppkb.com ARG VITE_APP_URL=https://anggota.koppkb.com + ENV VITE_API_BASE_URL=$VITE_API_BASE_URL ENV VITE_APP_URL=$VITE_APP_URL +ENV VITE_APP_NAME=$VITE_APP_NAME +ENV VITE_APP_VERSION=$VITE_APP_VERSION + # RUN pnpm run typecheck # uncomment this to run typecheck RUN pnpm exec vite build --mode=production diff --git a/fe/src/assets/images/Logo-RAJD.png b/fe/src/assets/images/Logo-RAJD.png deleted file mode 100644 index 37e434c..0000000 Binary files a/fe/src/assets/images/Logo-RAJD.png and /dev/null differ diff --git a/fe/src/assets/images/logo.svg b/fe/src/assets/images/logo.svg deleted file mode 100644 index 28c5228..0000000 --- a/fe/src/assets/images/logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/fe/src/assets/images/logonew-mykopkb.svg b/fe/src/assets/images/logonew-mykopkb.svg new file mode 100644 index 0000000..ce2be41 --- /dev/null +++ b/fe/src/assets/images/logonew-mykopkb.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SISTEM INFORMASI KOPERASI ONLINE + My + + + diff --git a/fe/src/assets/images/phone-illustration.svg b/fe/src/assets/images/phone-illustration.svg deleted file mode 100644 index 279c65a..0000000 --- a/fe/src/assets/images/phone-illustration.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/fe/src/assets/images/woman-illustration.svg b/fe/src/assets/images/woman-illustration.svg deleted file mode 100644 index f023ddf..0000000 --- a/fe/src/assets/images/woman-illustration.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fe/src/components/account-dropdown/AccountDropdown.vue b/fe/src/components/account-dropdown/AccountDropdown.vue index 859874c..302a1dd 100644 --- a/fe/src/components/account-dropdown/AccountDropdown.vue +++ b/fe/src/components/account-dropdown/AccountDropdown.vue @@ -19,6 +19,7 @@ import { useRoleSwitcher } from '@/composables/useRoleSwitcher' interface Props { class?: string boxClass?: string + open?: boolean } const props = defineProps() @@ -61,7 +62,9 @@ const handleLogout = async (event: MouseEvent) => {