diff --git a/README.md b/README.md
index 24a70ef..532fca3 100644
--- a/README.md
+++ b/README.md
@@ -76,4 +76,7 @@ SELECT setval('public.permissions_id_seq', COALESCE((SELECT MAX(id) FROM permiss
## DO NOT RUN THIS BECAUSE IT WILL REMOVE ALL THE UNTAGGED FILES IN LOCAL
```bash
git reset --hard development # moves the current branch (e.g., main) to point exactly where development
-```
\ No newline at end of file
+```
+
+## Public endpoint to apply membership:
+## http://localhost:5173/apply-membership
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
index fff4912..f934127 100644
--- a/TODO.md
+++ b/TODO.md
@@ -16,5 +16,16 @@
[ ] jana surat lepas lulus anggota
## Present to Boss (2/7/2026)
-[ ] discuss logo baru MyKOPKB
-[ ] layout footer untuk surat rasmi
\ No newline at end of file
+[x] discuss logo baru MyKOPKB
+[x] layout footer untuk surat rasmi
+[ ] maklumat pekerjaan kena buat dropdown untuk nama majikan
+[ ] tambah minima untuk borang permohonan (tukar jadi caruman saham potongan bulanan)
+[x] tukar waris ke penama
+[x] ic dan surat pengesahan majikkan perlu mandatori
+[ ] semakan lembaga tukar jadi maklumat keputusan
+[x] boleh attach document untuk peringkat pentadbir
+[x] surat attach dekat email selepas bos
+[ ] pembayaran pertama tukar jadi one-off
+[ ] syer maksima silap tukar jadi rm50.00
+[ ] letak sign digital
+[x] running no anggota dalam surat dan masa.
\ No newline at end of file
diff --git a/be/.env.development b/be/.env.development
index b5c5094..74b36c6 100644
--- a/be/.env.development
+++ b/be/.env.development
@@ -95,4 +95,7 @@ API_ALLOWED_USER_AGENTS=
ENABLE_API_KEY_AUTH=true
API_VALID_KEYS=86f58825e5c1e0872d8786092d47e3bd,6461fc5390660c55dc47bdfcd85ed9ae
-API_LOG_KEY_USAGE=true
\ No newline at end of file
+API_LOG_KEY_USAGE=true
+
+PUBLIC_PROFILE_TOKEN_TTL_DAYS=7
+FRONTEND_URL=https://mykopkb.koppkb.com
\ No newline at end of file
diff --git a/be/.env.production b/be/.env.production
index 8cfae54..80a9c56 100644
--- a/be/.env.production
+++ b/be/.env.production
@@ -102,4 +102,7 @@ API_ALLOWED_USER_AGENTS=
ENABLE_API_KEY_AUTH=true
API_VALID_KEYS=86f58825e5c1e0872d8786092d47e3bd,6461fc5390660c55dc47bdfcd85ed9ae
-API_LOG_KEY_USAGE=true
\ No newline at end of file
+API_LOG_KEY_USAGE=true
+
+PUBLIC_PROFILE_TOKEN_TTL_DAYS=7
+FRONTEND_URL=https://mykopkb.koppkb.com
\ No newline at end of file
diff --git a/be/Modules/ActivityLog/Http/Controllers/ActivityLogController.php b/be/Modules/ActivityLog/Http/Controllers/ActivityLogController.php
index 0a1e8d7..fdbbe69 100644
--- a/be/Modules/ActivityLog/Http/Controllers/ActivityLogController.php
+++ b/be/Modules/ActivityLog/Http/Controllers/ActivityLogController.php
@@ -3,17 +3,22 @@
namespace Modules\ActivityLog\Http\Controllers;
use App\Http\Controllers\Controller;
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\Request;
use Spatie\Activitylog\Models\Activity;
use App\Services\ActivityLogger;
class ActivityLogController extends Controller
{
+ use AuthorizesRequests;
+
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
+ $this->authorize('viewAny', Activity::class);
+
// ActivityLogger::logView(new Activity, 'Viewed activity logs');
$perPage = $request->get('per_page', 10);
@@ -27,8 +32,10 @@ class ActivityLogController extends Controller
$q->where('description', 'ILIKE', "%{$search}%")
->orWhere('subject_type', 'ILIKE', "%{$search}%")
->orWhere('event', 'ILIKE', "%{$search}%")
- ->orWhere('causer.name', 'ILIKE', "%{$search}%")
- ->orWhere('causer.email', 'ILIKE', "%{$search}%");
+ ->orWhereHas('causer', function ($causerQuery) use ($search) {
+ $causerQuery->where('name', 'ILIKE', "%{$search}%")
+ ->orWhere('email', 'ILIKE', "%{$search}%");
+ });
});
}
diff --git a/be/Modules/ActivityLog/Providers/ActivityLogServiceProvider.php b/be/Modules/ActivityLog/Providers/ActivityLogServiceProvider.php
index d1fad58..69101d9 100644
--- a/be/Modules/ActivityLog/Providers/ActivityLogServiceProvider.php
+++ b/be/Modules/ActivityLog/Providers/ActivityLogServiceProvider.php
@@ -3,7 +3,10 @@
namespace Modules\ActivityLog\Providers;
use Illuminate\Support\Facades\Blade;
+use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
+use Modules\ActivityLog\Policies\ActivityLogPolicy;
+use Spatie\Activitylog\Models\Activity;
use Nwidart\Modules\Traits\PathNamespace;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
@@ -21,6 +24,8 @@ class ActivityLogServiceProvider extends ServiceProvider
*/
public function boot(): void
{
+ Gate::policy(Activity::class, ActivityLogPolicy::class);
+
$this->registerCommands();
$this->registerCommandSchedules();
$this->registerTranslations();
diff --git a/be/Modules/Auth/Entities/User.php b/be/Modules/Auth/Entities/User.php
index 3c094b5..a20c45f 100644
--- a/be/Modules/Auth/Entities/User.php
+++ b/be/Modules/Auth/Entities/User.php
@@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Str;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
@@ -51,9 +52,11 @@ class User extends Authenticatable
'marriage_status',
'member_number',
'member_type',
+ 'public_profile_token',
'join_date',
'birth_date',
'birth_place',
+ 'onboarding_completed_at',
];
/**
@@ -67,8 +70,55 @@ class User extends Authenticatable
'two_factor_secret',
'two_factor_recovery_codes',
'two_factor_confirmed_at',
+ 'public_profile_token',
];
+ protected static function booted(): void
+ {
+ static::creating(function (User $user) {
+ $expiryDays = self::publicProfileTokenTtlDays();
+
+ if (empty($user->public_profile_token)) {
+ $user->public_profile_token = Str::random(48);
+ }
+
+ if (empty($user->public_profile_token_expires_at)) {
+ $user->public_profile_token_expires_at = now()->addDays($expiryDays);
+ }
+ });
+ }
+
+ public static function publicProfileTokenTtlDays(): int
+ {
+ return (int) config('user.public_profile_token_ttl_days', 90);
+ }
+
+ public function isPublicProfileTokenExpired(): bool
+ {
+ return $this->public_profile_token_expires_at !== null
+ && $this->public_profile_token_expires_at->isPast();
+ }
+
+ public function ensurePublicProfileToken(): string
+ {
+ $expiryDays = self::publicProfileTokenTtlDays();
+
+ if (blank($this->public_profile_token) || $this->isPublicProfileTokenExpired()) {
+ $this->forceFill([
+ 'public_profile_token' => Str::random(48),
+ 'public_profile_token_expires_at' => now()->addDays($expiryDays),
+ ])->save();
+
+ return $this->public_profile_token;
+ }
+
+ $this->forceFill([
+ 'public_profile_token_expires_at' => now()->addDays($expiryDays),
+ ])->save();
+
+ return $this->public_profile_token;
+ }
+
/**
* Get the attributes that should be cast.
*
@@ -84,9 +134,11 @@ class User extends Authenticatable
'marriage_status' => 'string',
'member_number' => 'integer',
'member_type' => 'string',
+ 'public_profile_token_expires_at' => 'datetime',
'join_date' => 'date',
'birth_date' => 'date',
'birth_place' => 'string',
+ 'onboarding_completed_at' => 'datetime',
];
}
diff --git a/be/Modules/Auth/Http/Controllers/SessionController.php b/be/Modules/Auth/Http/Controllers/SessionController.php
index 34a0652..cfcde33 100644
--- a/be/Modules/Auth/Http/Controllers/SessionController.php
+++ b/be/Modules/Auth/Http/Controllers/SessionController.php
@@ -24,6 +24,7 @@ class SessionController extends Controller
], 401);
}
+ $user->ensurePublicProfileToken();
$user->load(['roles.permissions']);
return response()->json([
diff --git a/be/Modules/Auth/Services/AuthSessionService.php b/be/Modules/Auth/Services/AuthSessionService.php
index bca4178..8cd803c 100644
--- a/be/Modules/Auth/Services/AuthSessionService.php
+++ b/be/Modules/Auth/Services/AuthSessionService.php
@@ -12,6 +12,8 @@ class AuthSessionService
{
public function createAuthResponse(User $user, string $message, int $status = 200): JsonResponse
{
+ $user->ensurePublicProfileToken();
+
if (config('app.env') !== 'local') {
$user->tokens()->delete();
}
diff --git a/be/Modules/Auth/Transformers/UserResource.php b/be/Modules/Auth/Transformers/UserResource.php
index 98518ef..b3cc612 100644
--- a/be/Modules/Auth/Transformers/UserResource.php
+++ b/be/Modules/Auth/Transformers/UserResource.php
@@ -36,9 +36,11 @@ class UserResource extends JsonResource
'marriage_status' => $this->marriage_status,
'member_number' => $this->member_number,
'member_type' => $this->member_type,
+ 'public_profile_token' => $this->public_profile_token,
'join_date' => $this->join_date,
'birth_date' => $this->birth_date,
'birth_place' => $this->birth_place,
+ 'onboarding_completed_at' => $this->onboarding_completed_at,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'deleted_at' => $this->deleted_at,
diff --git a/be/Modules/MembershipApplication/Emails/MembershipApplicationFailedNotification.php b/be/Modules/MembershipApplication/Emails/MembershipApplicationFailedNotification.php
index ba3f09a..36018fc 100644
--- a/be/Modules/MembershipApplication/Emails/MembershipApplicationFailedNotification.php
+++ b/be/Modules/MembershipApplication/Emails/MembershipApplicationFailedNotification.php
@@ -2,19 +2,28 @@
namespace Modules\MembershipApplication\Emails;
+use App\Models\Document;
+use App\Notifications\Concerns\BuildsMailMessage;
use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
+use Illuminate\Support\Facades\Storage;
use Modules\MembershipApplication\Entities\MembershipApplication;
-class MembershipApplicationFailedNotification extends Notification
+class MembershipApplicationFailedNotification extends Notification implements ShouldQueue
{
+ use BuildsMailMessage;
use Queueable;
public function __construct(
- protected MembershipApplication $application
+ public MembershipApplication $application,
+ public Document $resultLetter,
) {}
+ /**
+ * @return array
+ */
public function via(object $notifiable): array
{
return ['mail'];
@@ -23,13 +32,21 @@ class MembershipApplicationFailedNotification extends Notification
public function toMail(object $notifiable): MailMessage
{
$this->application->loadMissing('applicant');
+ $applicant = $this->application->applicant;
- return (new MailMessage)
- ->subject('Keputusan Permohonan Keahlian')
- ->markdown('membershipapplication::emails.failed', [
- 'name' => $this->application->applicant->name,
- 'applicationNumber' => $this->application->application_number,
- 'logoPath' => public_path('images/logo-kopkb.svg'),
- ]);
+ return $this->mailMessage(
+ subject: 'Keputusan Permohonan Keahlian KOPKB - TIDAK LULUS',
+ view: 'membershipapplication::emails.failed',
+ data: [
+ 'application' => $this->application,
+ 'applicant' => $applicant,
+ ],
+ )->attach(
+ Storage::disk(Document::STORAGE_DISK)->path($this->resultLetter->path),
+ [
+ 'as' => $this->resultLetter->name,
+ 'mime' => $this->resultLetter->mime_type,
+ ]
+ );
}
}
diff --git a/be/Modules/MembershipApplication/Emails/MembershipApplicationPassedNotification.php b/be/Modules/MembershipApplication/Emails/MembershipApplicationPassedNotification.php
index e9ccbd8..c07ec92 100644
--- a/be/Modules/MembershipApplication/Emails/MembershipApplicationPassedNotification.php
+++ b/be/Modules/MembershipApplication/Emails/MembershipApplicationPassedNotification.php
@@ -2,19 +2,28 @@
namespace Modules\MembershipApplication\Emails;
+use App\Models\Document;
+use App\Notifications\Concerns\BuildsMailMessage;
use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
+use Illuminate\Support\Facades\Storage;
use Modules\MembershipApplication\Entities\MembershipApplication;
-class MembershipApplicationPassedNotification extends Notification
+class MembershipApplicationPassedNotification extends Notification implements ShouldQueue
{
+ use BuildsMailMessage;
use Queueable;
public function __construct(
- protected MembershipApplication $application
+ public MembershipApplication $application,
+ public Document $resultLetter,
) {}
+ /**
+ * @return array
+ */
public function via(object $notifiable): array
{
return ['mail'];
@@ -23,13 +32,21 @@ class MembershipApplicationPassedNotification extends Notification
public function toMail(object $notifiable): MailMessage
{
$this->application->loadMissing('applicant');
+ $applicant = $this->application->applicant;
- return (new MailMessage)
- ->subject('Permohonan Keahlian Diluluskan')
- ->markdown('membershipapplication::emails.passed', [
- 'name' => $this->application->applicant->name,
- 'applicationNumber' => $this->application->application_number,
- 'logoPath' => public_path('images/logo-kopkb.svg'),
- ]);
+ return $this->mailMessage(
+ subject: 'Keputusan Permohonan Keahlian KOPKB - LULUS',
+ view: 'membershipapplication::emails.passed',
+ data: [
+ 'application' => $this->application,
+ 'applicant' => $applicant,
+ ],
+ )->attach(
+ Storage::disk(Document::STORAGE_DISK)->path($this->resultLetter->path),
+ [
+ 'as' => $this->resultLetter->name,
+ 'mime' => $this->resultLetter->mime_type,
+ ]
+ );
}
}
diff --git a/be/Modules/MembershipApplication/Emails/MembershipApplicationSubmittedNotification.php b/be/Modules/MembershipApplication/Emails/MembershipApplicationSubmittedNotification.php
index 4b77632..97d5f68 100644
--- a/be/Modules/MembershipApplication/Emails/MembershipApplicationSubmittedNotification.php
+++ b/be/Modules/MembershipApplication/Emails/MembershipApplicationSubmittedNotification.php
@@ -2,6 +2,7 @@
namespace Modules\MembershipApplication\Emails;
+use App\Notifications\Concerns\BuildsMailMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
@@ -9,6 +10,7 @@ use Modules\MembershipApplication\Entities\MembershipApplication;
class MembershipApplicationSubmittedNotification extends Notification
{
+ use BuildsMailMessage;
use Queueable;
public function __construct(
@@ -24,9 +26,10 @@ class MembershipApplicationSubmittedNotification extends Notification
{
$this->application->loadMissing(['applicant', 'heirs', 'documents', 'references.member']);
- return (new MailMessage)
- ->subject('Pengesahan Permohonan Keahlian')
- ->markdown('membershipapplication::emails.submitted', [
+ return $this->mailMessage(
+ subject: 'Pengesahan Permohonan Keahlian',
+ view: 'membershipapplication::emails.submitted',
+ data: [
'application' => $this->application,
'applicant' => $this->application->applicant,
'heirs' => $this->application->heirs,
@@ -37,7 +40,7 @@ class MembershipApplicationSubmittedNotification extends Notification
'salary_slip' => 'Slip Gaji',
'employer_letter' => 'Surat Pengesahan Majikan',
],
- 'logoPath' => public_path('images/logo-kopkb.svg'),
- ]);
+ ],
+ );
}
}
diff --git a/be/Modules/MembershipApplication/Entities/MembershipApplication.php b/be/Modules/MembershipApplication/Entities/MembershipApplication.php
index 9464541..15a7ff0 100644
--- a/be/Modules/MembershipApplication/Entities/MembershipApplication.php
+++ b/be/Modules/MembershipApplication/Entities/MembershipApplication.php
@@ -18,6 +18,8 @@ class MembershipApplication extends Model
public const RESULT_LETTER_DOCUMENT_TYPE = 'result_letter';
+ public const ADMIN_ATTACHMENT_DOCUMENT_TYPE = 'admin_attachment';
+
public const MINIMUM_SHARE_CAPITAL = 500;
public const SHARE_INSTALLMENT_MONTHS = 6;
diff --git a/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php b/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php
index 006de7b..7da1e09 100644
--- a/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php
+++ b/be/Modules/MembershipApplication/Http/Controllers/MembershipApplicationController.php
@@ -17,6 +17,7 @@ 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\CompleteMembershipApplicationRequest;
use Modules\MembershipApplication\Http\Requests\GenerateResultLetterRequest;
use Modules\MembershipApplication\Http\Requests\ManagementReviewRequest;
use Modules\MembershipApplication\Http\Requests\UpdateMembershipApplicationRequest;
@@ -180,18 +181,19 @@ class MembershipApplicationController extends Controller
]);
}
- public function complete(Request $request, MembershipApplication $membershipApplication): JsonResponse
+ public function complete(CompleteMembershipApplicationRequest $request, MembershipApplication $membershipApplication): JsonResponse
{
$this->authorize('complete', $membershipApplication);
$application = $this->membershipApplicationService->complete(
$membershipApplication,
- $request->user()
+ $request->user(),
+ $request->validated('board_meeting_reference'),
);
$message = $application->board_result === BoardResult::Pass->value
- ? 'Permohonan selesai. Akaun ahli telah dicipta dan e-mel dihantar.'
- : 'Permohonan selesai. E-mel keputusan dihantar.';
+ ? 'Permohonan selesai. Surat keputusan dijana, akaun ahli dicipta dan e-mel dihantar.'
+ : 'Permohonan selesai. Surat keputusan dijana dan e-mel dihantar.';
return response()->json([
'success' => true,
@@ -206,7 +208,8 @@ class MembershipApplicationController extends Controller
$result = $this->membershipApplicationService->completeBatch(
$request->validated('application_ids'),
- $request->user()
+ $request->user(),
+ $request->validated('board_meeting_reference'),
);
$succeededCount = count($result['succeeded']);
@@ -245,6 +248,22 @@ class MembershipApplicationController extends Controller
return $this->documentService->downloadDocument($document->id);
}
+ public function deleteDocument(MembershipApplication $membershipApplication, string $documentId): JsonResponse
+ {
+ $this->authorize('update', $membershipApplication);
+
+ $application = $this->membershipApplicationService->deleteDocument(
+ $membershipApplication,
+ $documentId,
+ );
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Lampiran pentadbir berjaya dipadam.',
+ 'data' => new MembershipApplicationResource($application),
+ ]);
+ }
+
public function generateResultLetter(GenerateResultLetterRequest $request, MembershipApplication $membershipApplication,): JsonResponse {
$this->authorize('generateResultLetter', $membershipApplication);
diff --git a/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php b/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php
index deb9c19..0a4c3ae 100644
--- a/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php
+++ b/be/Modules/MembershipApplication/Http/Requests/BatchCompleteRequest.php
@@ -19,6 +19,7 @@ class BatchCompleteRequest extends FormRequest
return [
'application_ids' => 'required|array|min:1|max:100',
'application_ids.*' => 'required|uuid|distinct|exists:membership_applications,id',
+ 'board_meeting_reference' => 'required|string|max:255',
];
}
@@ -33,6 +34,8 @@ class BatchCompleteRequest extends FormRequest
'application_ids.max' => 'Maksimum 100 permohonan setiap batch.',
'application_ids.*.exists' => 'Salah satu permohonan tidak dijumpai.',
'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.',
];
}
}
diff --git a/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.php b/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.php
new file mode 100644
index 0000000..971bfde
--- /dev/null
+++ b/be/Modules/MembershipApplication/Http/Requests/CompleteMembershipApplicationRequest.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/Http/Requests/StoreMembershipApplicationRequest.php b/be/Modules/MembershipApplication/Http/Requests/StoreMembershipApplicationRequest.php
index 8027dc1..f15abed 100644
--- a/be/Modules/MembershipApplication/Http/Requests/StoreMembershipApplicationRequest.php
+++ b/be/Modules/MembershipApplication/Http/Requests/StoreMembershipApplicationRequest.php
@@ -48,7 +48,7 @@ class StoreMembershipApplicationRequest extends FormRequest
'documents.ic_copy' => 'required|file|mimes:pdf,jpg,jpeg,png|max:10240',
'documents.photo' => 'nullable|file|mimes:jpg,jpeg,png|max:10240',
'documents.salary_slip' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:10240',
- 'documents.employer_letter' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:10240',
+ 'documents.employer_letter' => 'required|file|mimes:pdf,jpg,jpeg,png|max:10240',
];
}
@@ -89,6 +89,8 @@ class StoreMembershipApplicationRequest extends FormRequest
'documents.ic_copy.required' => 'Salinan kad pengenalan diperlukan.',
'documents.ic_copy.mimes' => 'Salinan kad pengenalan mestilah PDF, JPG, JPEG atau PNG.',
+ 'documents.employer_letter.required' => 'Surat pengesahan majikan diperlukan.',
+ 'documents.employer_letter.mimes' => 'Surat pengesahan majikan mestilah PDF, JPG, JPEG atau PNG.',
];
}
}
diff --git a/be/Modules/MembershipApplication/Http/Requests/UploadMembershipApplicationDocumentRequest.php b/be/Modules/MembershipApplication/Http/Requests/UploadMembershipApplicationDocumentRequest.php
index 542495f..47198a4 100644
--- a/be/Modules/MembershipApplication/Http/Requests/UploadMembershipApplicationDocumentRequest.php
+++ b/be/Modules/MembershipApplication/Http/Requests/UploadMembershipApplicationDocumentRequest.php
@@ -4,6 +4,7 @@ namespace Modules\MembershipApplication\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
+use Modules\MembershipApplication\Entities\MembershipApplication;
class UploadMembershipApplicationDocumentRequest extends FormRequest
{
@@ -18,7 +19,13 @@ class UploadMembershipApplicationDocumentRequest extends FormRequest
public function rules(): array
{
return [
- 'type' => ['required', Rule::in(['ic_copy', 'photo', 'salary_slip', 'employer_letter'])],
+ 'type' => ['required', Rule::in([
+ 'ic_copy',
+ 'photo',
+ 'salary_slip',
+ 'employer_letter',
+ MembershipApplication::ADMIN_ATTACHMENT_DOCUMENT_TYPE,
+ ])],
'file' => [
'required',
'file',
diff --git a/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php b/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php
index 679ef22..06fccd4 100644
--- a/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php
+++ b/be/Modules/MembershipApplication/Repositories/MembershipApplicationRepository.php
@@ -23,6 +23,7 @@ class MembershipApplicationRepository implements MembershipApplicationRepository
return MembershipApplication::with([
'applicant',
'heirs',
+ 'user:id,member_number',
'references.member:id,name,ic_number',
'documents',
'reviews.reviewer:id,name',
diff --git a/be/Modules/MembershipApplication/Resources/views/emails/failed.blade.php b/be/Modules/MembershipApplication/Resources/views/emails/failed.blade.php
index 62b70a8..2982892 100644
--- a/be/Modules/MembershipApplication/Resources/views/emails/failed.blade.php
+++ b/be/Modules/MembershipApplication/Resources/views/emails/failed.blade.php
@@ -1,18 +1,15 @@
@component('mail::message')
-@if (! empty($logoPath) && file_exists($logoPath))
-
-
-
-@endif
+@include('emails.partials.header')
# Keputusan Permohonan Keahlian
-Assalamualaikum **{{ $name }}**,
+Assalamualaikum **{{ $applicant->name }}**,
-Permohonan keahlian anda (**{{ $applicationNumber }}**) **tidak diluluskan** pada peringkat lembaga.
+Permohonan keahlian anda (**{{ $application->application_number }}**) **tidak diluluskan** pada peringkat lembaga.
+
+Surat keputusan rasmi dilampirkan dalam e-mel ini.
Terima kasih atas minat anda. Untuk sebarang pertanyaan, sila hubungi pejabat koperasi.
-Terima kasih,
-{{ config('app.name') }}
+@include('emails.partials.footer')
@endcomponent
diff --git a/be/Modules/MembershipApplication/Resources/views/emails/passed.blade.php b/be/Modules/MembershipApplication/Resources/views/emails/passed.blade.php
index 3c962c2..b9752cd 100644
--- a/be/Modules/MembershipApplication/Resources/views/emails/passed.blade.php
+++ b/be/Modules/MembershipApplication/Resources/views/emails/passed.blade.php
@@ -1,18 +1,15 @@
@component('mail::message')
-@if (! empty($logoPath) && file_exists($logoPath))
-
-
-
-@endif
+@include('emails.partials.header')
# Keputusan Permohonan Keahlian
-Assalamualaikum **{{ $name }}**,
+Assalamualaikum **{{ $applicant->name }}**,
-Permohonan keahlian anda (**{{ $applicationNumber }}**) **diluluskan** pada peringkat lembaga.
+Permohonan keahlian anda (**{{ $application->application_number }}**) **diluluskan** pada peringkat lembaga.
+
+Surat keputusan rasmi dilampirkan dalam e-mel ini.
Terima kasih atas minat anda. Untuk sebarang pertanyaan, sila hubungi pejabat koperasi.
-Terima kasih,
-{{ config('app.name') }}
+@include('emails.partials.footer')
@endcomponent
diff --git a/be/Modules/MembershipApplication/Resources/views/emails/submitted.blade.php b/be/Modules/MembershipApplication/Resources/views/emails/submitted.blade.php
index 7a75b9e..5782b9b 100644
--- a/be/Modules/MembershipApplication/Resources/views/emails/submitted.blade.php
+++ b/be/Modules/MembershipApplication/Resources/views/emails/submitted.blade.php
@@ -1,9 +1,5 @@
@component('mail::message')
-@if (! empty($logoPath) && file_exists($logoPath))
-
-
-
-@endif
+@include('emails.partials.header')
# Pengesahan Permohonan Keahlian
@@ -74,6 +70,5 @@ Tiada dokumen.
Anda akan menerima emel apabila keputusan permohonan tersedia.
-Terima kasih,
-{{ config('app.name') }}
+@include('emails.partials.footer')
@endcomponent
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 cdfc055..21c1fea 100644
--- a/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php
+++ b/be/Modules/MembershipApplication/Resources/views/pdf/approved-letter.blade.php
@@ -22,7 +22,7 @@
2. {{ $openingTone }} dimaklumkan bahawa, permohonan tuan untuk menjadi anggota Koperasi
- Permodalan Kelantan Berhad (KoPKB) telah {{ $boardResultLabel }} dalam {{ $boardMeetingReference }}.
+ Permodalan Kelantan Berhad (KoPKB) telah {{ $boardResultLabel }} dalam {{ $boardMeetingReference }}@if ($isPassed && $memberNumber). Nombor keanggotaan tuan adalah {{ $memberNumber }}@endif.
2. Sukacita dimaklumkan bahawa, permohonan tuan untuk menjadi anggota Koperasi
- Permodalan Kelantan Berhad(KoPKB) telah {{ membership_applications.board_result }} dalam {{ User manual fills before generate }}
+ Permodalan Kelantan Berhad(KoPKB) telah dalam
3. Pihak Koperasi akan membuat potongan gaji melalui majikan tuan sebagaiman ketetapan berikut:-
Saham
-
Potongan sebanyak RM 84.00 {{(6 months auto calculate)}}
-
Potongan sebanyak RM {{ membership_application_applicants.stock_monthly_contribution }} sehingga Syer Maksima
+
Potongan sebanyak RM 84.00
+
Potongan sebanyak RM sehingga Syer Maksima
Yuran
-
Potongan sebanyak RM {{ membership_application_applicants.fee_monthly_contribution }} setiap bulan