DONE: merge heir tab into profile tab, santize userIcNum and name, rename to malay, add stat card on user list

This commit is contained in:
ISMAIL MASSERAN
2026-07-09 12:48:26 +08:00
parent ebd3caecc6
commit af7f47bf71
30 changed files with 562 additions and 578 deletions
@@ -269,6 +269,43 @@ class UserController extends BaseCrudController
}
}
public function stats(Request $request): JsonResponse
{
$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',
]);
$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'),
]);
$stats = $this->userService->getListStats(
$request->get('search', ''),
$request->get('status', ''),
$dateFilters
);
return response()->json([
'success' => true,
'data' => $stats,
'message' => 'User stats retrieved successfully.',
]);
} catch (Exception $e) {
Log::error("Error fetching {$this->resourceNamePlural} stats: ".$e->getMessage());
return $this->errorResponse('Failed to retrieve user stats.', 500);
}
}
public function restore(string $id): JsonResponse
{
$this->authorize('restore', $this->modelClass);
@@ -55,6 +55,17 @@ interface UserRepositoryInterface
string $sortOrder = 'desc'
);
/**
* Get stats for the Users list (filtered).
*
* @return array{total: int, joined_this_month: int}
*/
public function getListStats(
string $search = '',
string $status = '',
array $dateFilters = []
): array;
/**
* Find soft-deleted User by ID
*/
@@ -3,6 +3,7 @@
namespace Modules\User\Repositories;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Carbon;
use Modules\Auth\Entities\User;
use Modules\User\Repositories\Contracts\UserRepositoryInterface;
@@ -85,7 +86,18 @@ class UserRepository implements UserRepositoryInterface
$query = User::excludeDevelopersUnlessDeveloper()
->with([
'roles:id,name,guard_name'
'roles:id,name,guard_name',
'employments' => function ($q) {
$q->select([
'id',
'user_id',
'company_name',
'is_current',
'start_date',
])
->orderByDesc('is_current')
->orderByDesc('start_date');
},
])
->orderBy($sortBy, $sortOrder);
@@ -129,6 +141,17 @@ class UserRepository implements UserRepositoryInterface
->excludeDevelopersUnlessDeveloper()
->with([
'roles:id,name,guard_name',
'employments' => function ($q) {
$q->select([
'id',
'user_id',
'company_name',
'is_current',
'start_date',
])
->orderByDesc('is_current')
->orderByDesc('start_date');
},
])
->orderBy($sortBy, $sortOrder);
@@ -141,6 +164,36 @@ class UserRepository implements UserRepositoryInterface
return $query->paginate($perPage);
}
public function getListStats(
string $search = '',
string $status = '',
array $dateFilters = []
): array {
$baseQuery = User::excludeDevelopersUnlessDeveloper();
$this->applySearch($baseQuery, $search);
if (! empty($status)) {
$baseQuery->where('status', $status);
}
$this->applyDateRangeFilters($baseQuery, $dateFilters);
$total = (int) (clone $baseQuery)->count();
$monthStart = Carbon::now()->startOfMonth()->toDateString();
$monthEnd = Carbon::now()->endOfMonth()->toDateString();
$joinedThisMonth = (int) (clone $baseQuery)
->whereDate('join_date', '>=', $monthStart)
->whereDate('join_date', '<=', $monthEnd)
->count();
return [
'total' => $total,
'joined_this_month' => $joinedThisMonth,
];
}
/**
* Get all Users with their relationships and search
*/
+1
View File
@@ -19,6 +19,7 @@ Route::prefix('v1/public')->group(function () {
Route::middleware(['auth:sanctum', 'single.session'])->prefix('v1')->group(function () {
Route::get('users/deleted', [UserController::class, 'deletedIndex'])->name('users.deleted.index');
Route::get('users/stats', [UserController::class, 'stats'])->name('users.stats');
Route::post('users/{id}/restore', [UserController::class, 'restore'])->name('users.restore');
Route::apiResource('users', UserController::class)->names('user');
Route::apiResource('addresses', AddressController::class)->names('address');
+8
View File
@@ -50,6 +50,14 @@ class UserService
);
}
/**
* @return array{total: int, joined_this_month: int}
*/
public function getListStats(string $search, string $status, array $dateFilters = []): array
{
return $this->repository->getListStats($search, $status, $dateFilters);
}
public function restoreUser(string $id): ?User
{
if (! $this->repository->restore($id)) {
@@ -5,6 +5,7 @@ namespace Modules\User\Transformers;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Storage;
use Modules\User\Transformers\EmploymentResource;
class UserListResource extends JsonResource
{
@@ -31,6 +32,7 @@ class UserListResource extends JsonResource
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'deleted_at' => $this->deleted_at,
'employments' => EmploymentResource::collection($this->whenLoaded('employments')),
'roles' => $this->roles->map(function ($role) {
return [
'id' => $role->id,