DONE: use full name for date in letter, include password in email after membership is done, block ic_number to exclude - (#9)
Build Docker Image / build-backend (push) Successful in 44s
Build Docker Image / build-frontend (push) Successful in 18s

Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #9
This commit was merged in pull request #9.
This commit is contained in:
2026-07-08 12:15:41 +08:00
parent 18c0d268b1
commit ebd3caecc6
23 changed files with 352 additions and 41 deletions
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->date('leave_date')->nullable()->after('join_date');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('leave_date');
});
}
};
@@ -44,7 +44,7 @@ class UserController extends BaseCrudController
$validated['uuid'] = Str::uuid();
$validated['password'] = Hash::make('suteraselamanya');
return $validated;
return $this->applyLeaveDateForStatus($validated);
}
/**
@@ -58,6 +58,25 @@ class UserController extends BaseCrudController
unset($validated['email']);
}
return $this->applyLeaveDateForStatus($validated, $user);
}
/**
* Set or clear leave_date based on membership status transitions.
*/
protected function applyLeaveDateForStatus(array $validated, ?User $user = null): array
{
$newStatus = $validated['status'] ?? $user?->status;
$currentStatus = $user?->status;
if ($newStatus === 'inactive') {
if ($currentStatus !== 'inactive') {
$validated['leave_date'] = $validated['leave_date'] ?? now()->toDateString();
}
} elseif (in_array($newStatus, ['active', 'pending'], true)) {
$validated['leave_date'] = null;
}
return $validated;
}
@@ -171,13 +190,27 @@ class UserController extends BaseCrudController
$this->authorize('viewAny', $this->modelClass);
try {
$request->validate([
'join_date_from' => 'nullable|date',
'join_date_to' => 'nullable|date',
'leave_date_from' => 'nullable|date',
'leave_date_to' => 'nullable|date',
]);
$perPage = min((int) $request->get('per_page', 10), 500);
$dateFilters = array_filter([
'join_date_from' => $request->get('join_date_from'),
'join_date_to' => $request->get('join_date_to'),
'leave_date_from' => $request->get('leave_date_from'),
'leave_date_to' => $request->get('leave_date_to'),
]);
$items = $this->userService->getPaginatedList(
$perPage,
$request->get('search', ''),
$request->get('status', ''),
$request->get('sort_by', 'id'),
$request->get('sort_order', 'asc')
$request->get('sort_order', 'asc'),
$dateFilters
);
return response()->json([
@@ -44,6 +44,7 @@ class UserRequest extends FormRequest
'member_number' => 'required|integer',
'member_type' => 'required|string|max:255',
'join_date' => 'nullable|date',
'leave_date' => 'nullable|date',
'birth_date' => 'nullable|date',
'birth_place' => 'nullable|string|max:255',
];
@@ -74,6 +75,7 @@ class UserRequest extends FormRequest
'member_number' => 'required|integer',
'member_type' => 'required|string|max:255',
'join_date' => 'nullable|date',
'leave_date' => 'nullable|date',
'birth_date' => 'nullable|date',
'birth_place' => 'nullable|string|max:255',
];
@@ -100,6 +102,7 @@ class UserRequest extends FormRequest
'member_number' => 'required|integer',
'member_type' => 'required|string|max:255',
'join_date' => 'required|date',
'leave_date' => 'nullable|date',
'birth_date' => 'required|date',
'birth_place' => 'required|string|max:255',
];
@@ -136,6 +139,7 @@ class UserRequest extends FormRequest
'member_type.max' => 'Jenis anggota tidak boleh melebihi 255 aksara.',
'join_date.required' => 'Tarikh join diperlukan.',
'join_date.date' => 'Tarikh join tidak sah.',
'leave_date.date' => 'Tarikh berhenti tidak sah.',
'birth_date.required' => 'Tarikh lahir diperlukan.',
'birth_date.date' => 'Tarikh lahir tidak sah.',
'birth_place.required' => 'Tempat lahir diperlukan.',
@@ -20,7 +20,8 @@ interface UserRepositoryInterface
string $search = '',
string $status = '',
string $sortBy = 'name',
string $sortOrder = 'asc'
string $sortOrder = 'asc',
array $dateFilters = []
);
/**
@@ -26,6 +26,25 @@ class UserRepository implements UserRepositoryInterface
});
}
private function applyDateRangeFilters($query, array $dateFilters): void
{
if (! empty($dateFilters['join_date_from'])) {
$query->whereDate('join_date', '>=', $dateFilters['join_date_from']);
}
if (! empty($dateFilters['join_date_to'])) {
$query->whereDate('join_date', '<=', $dateFilters['join_date_to']);
}
if (! empty($dateFilters['leave_date_from'])) {
$query->whereDate('leave_date', '>=', $dateFilters['leave_date_from']);
}
if (! empty($dateFilters['leave_date_to'])) {
$query->whereDate('leave_date', '<=', $dateFilters['leave_date_to']);
}
}
/**
* Get all Users with pagination and search
*/
@@ -46,7 +65,8 @@ class UserRepository implements UserRepositoryInterface
string $search = '',
string $status = '',
string $sortBy = 'name',
string $sortOrder = 'asc'
string $sortOrder = 'asc',
array $dateFilters = []
) {
$allowedSortColumns = [
'id',
@@ -55,6 +75,8 @@ class UserRepository implements UserRepositoryInterface
'position',
'status',
'member_number',
'join_date',
'leave_date',
'created_at',
'deleted_at',
];
@@ -73,6 +95,8 @@ class UserRepository implements UserRepositoryInterface
$query->where('status', $status);
}
$this->applyDateRangeFilters($query, $dateFilters);
return $query->paginate($perPage);
}
@@ -93,6 +117,8 @@ class UserRepository implements UserRepositoryInterface
'position',
'status',
'member_number',
'join_date',
'leave_date',
'created_at',
'deleted_at',
];
+4 -2
View File
@@ -21,14 +21,16 @@ class UserService
string $search,
string $status,
string $sortBy,
string $sortOrder
string $sortOrder,
array $dateFilters = []
): LengthAwarePaginator {
return $this->repository->getAllWithRelationsPaginated(
$perPage,
$search,
$status,
$sortBy,
$sortOrder
$sortOrder,
$dateFilters
);
}
@@ -25,6 +25,7 @@ class UserListResource extends JsonResource
'member_number' => $this->member_number,
'member_type' => $this->member_type,
'join_date' => $this->join_date,
'leave_date' => $this->leave_date,
'birth_date' => $this->birth_date,
'birth_place' => $this->birth_place,
'created_at' => $this->created_at,