feature: user list

This commit is contained in:
antv
2026-07-08 15:29:13 +07:00
parent 7d9ba527a3
commit e476c2768a
32 changed files with 1091 additions and 284 deletions
+46 -10
View File
@@ -18,18 +18,23 @@ class AdminController extends Controller
{ {
$selectedMonth = $request->input('month', Carbon::now()->format('Y-m')); $selectedMonth = $request->input('month', Carbon::now()->format('Y-m'));
$search = $request->input('search'); $search = $request->input('search');
$status = $request->input('status', (string)User::STATUS_ACTIVE); $status = $request->input('status', (string)config('constants.STATUS_ACTIVE'));
$role = $request->input('role'); $role = $request->input('role');
$department = $request->input('department'); $department = $request->input('department');
$flagSend = $request->input('flag_send'); $flagSend = $request->input('flag_send');
['users' => $users, 'topReceivedUser' => $topReceivedUser, 'topSentUser' => $topSentUser] [
= $this->adminService->getUserListWithStats($selectedMonth, $search, $status, $role, $department, $flagSend); 'users' => $users,
'topReceivedUser' => $topReceivedUser,
'topSentUser' => $topSentUser,
'topReceivedUsers' => $topReceivedUsers,
'topSentUsers' => $topSentUsers
] = $this->adminService->getUserListWithStats($selectedMonth, $search, $status, $role, $department, $flagSend);
if ($request->ajax() || $request->wantsJson()) { if ($request->ajax() || $request->wantsJson()) {
return response()->json([ return response()->json([
'title' => view('admin.users.partials.title', compact('selectedMonth'))->render(), 'title' => view('admin.users.partials.title', compact('selectedMonth'))->render(),
'stats' => view('admin.users.partials.stats', compact('topReceivedUser', 'topSentUser'))->render(), 'stats' => view('admin.users.partials.stats', compact('topReceivedUser', 'topSentUser', 'topReceivedUsers', 'topSentUsers'))->render(),
'table' => view('admin.users.partials.table', compact('users'))->render(), 'table' => view('admin.users.partials.table', compact('users'))->render(),
]); ]);
} }
@@ -39,6 +44,8 @@ class AdminController extends Controller
'selectedMonth', 'selectedMonth',
'topReceivedUser', 'topReceivedUser',
'topSentUser', 'topSentUser',
'topReceivedUsers',
'topSentUsers',
'search', 'search',
'status', 'status',
'role', 'role',
@@ -58,9 +65,9 @@ class AdminController extends Controller
'msnv' => 'required|integer|unique:user,msnv', 'msnv' => 'required|integer|unique:user,msnv',
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'mail' => 'required|email|unique:user,mail', 'mail' => 'required|email|unique:user,mail',
'departments' => 'required|integer|min:1|max:5', 'departments' => 'required|string|in:' . implode(',', config('constants.DEPARTMENTS')),
'password' => 'required|string', 'password' => 'required|string',
'role' => 'required|in:' . User::ROLE_MEMBER . ',' . User::ROLE_ADMIN, 'role' => 'required|in:' . config('constants.ROLE_MEMBER') . ',' . config('constants.ROLE_ADMIN'),
]); ]);
$this->adminService->createUser($request->only('msnv', 'name', 'mail', 'departments', 'password', 'role')); $this->adminService->createUser($request->only('msnv', 'name', 'mail', 'departments', 'password', 'role'));
@@ -75,17 +82,30 @@ class AdminController extends Controller
$administrations = $this->adminService->getUserTransactions($msnv, $selectedMonth); $administrations = $this->adminService->getUserTransactions($msnv, $selectedMonth);
return view('admin.users.edit', compact('user', 'administrations', 'selectedMonth')); $addCards = \App\Models\AddCard::with('sellerUser')
->where('buyer', $user->msnv)
->orderBy('date', 'desc')
->orderBy('id', 'desc')
->get();
$admins = User::where('role', config('constants.ROLE_ADMIN'))
->where('status', config('constants.STATUS_ACTIVE'))
->get();
return view('admin.users.edit', compact('user', 'administrations', 'selectedMonth', 'addCards', 'admins'));
} }
public function update(Request $request, $msnv) public function update(Request $request, $msnv)
{ {
$request->validate([ $request->validate([
'num_card' => 'nullable|integer|min:1', 'card' => 'required|integer|min:0',
'flag_send' => 'nullable|boolean', 'role' => 'required|in:' . config('constants.ROLE_MEMBER') . ',' . config('constants.ROLE_ADMIN'),
'status' => 'required|in:' . config('constants.STATUS_INACTIVE') . ',' . config('constants.STATUS_ACTIVE'),
'flag_send' => 'required|in:' . config('constants.FLAG_SEND_DISABLED') . ',' . config('constants.FLAG_SEND_ENABLED'),
'first_login' => 'required|in:' . config('constants.FIRST_LOGIN_FALSE') . ',' . config('constants.FIRST_LOGIN_TRUE'),
]); ]);
$this->adminService->updateUser($msnv, $request->only('num_card', 'flag_send')); $this->adminService->updateUser($msnv, $request->only('card', 'role', 'status', 'flag_send', 'first_login'));
return redirect()->back()->with('success', __('messages.user_update_success')); return redirect()->back()->with('success', __('messages.user_update_success'));
} }
@@ -97,6 +117,22 @@ class AdminController extends Controller
return redirect()->route('admin.users.index')->with('success', __('messages.user_deactivate_success')); return redirect()->route('admin.users.index')->with('success', __('messages.user_deactivate_success'));
} }
public function updateAddCard(Request $request, $id)
{
$request->validate([
'num_card' => 'required|integer|min:1',
'seller' => 'required|exists:user,msnv',
'date' => 'required|date',
]);
try {
$this->adminService->updateAddCard(intval($id), $request->only('num_card', 'seller', 'date'));
return redirect()->back()->with('success', 'Cập nhật lịch sử cấp phát thẻ thành công.');
} catch (\Exception $e) {
return redirect()->back()->withErrors(['error' => $e->getMessage()]);
}
}
public function resetCards() public function resetCards()
{ {
$this->adminService->resetAllCards(); $this->adminService->resetAllCards();
+1 -1
View File
@@ -62,7 +62,7 @@ class AuthController extends Controller
\Illuminate\Support\Facades\RateLimiter::clear($throttleKey); \Illuminate\Support\Facades\RateLimiter::clear($throttleKey);
$user = $result['user']; $user = $result['user'];
if ($user->role != \App\Models\User::ROLE_ADMIN && $user->first_login == \App\Models\User::FIRST_LOGIN_TRUE) { if ($user->role != config('constants.ROLE_ADMIN') && $user->first_login == config('constants.FIRST_LOGIN_TRUE')) {
return redirect()->route('login')->with('dialog_first_login', true); return redirect()->route('login')->with('dialog_first_login', true);
} }
+2 -2
View File
@@ -48,7 +48,7 @@ class UserController extends Controller
$request->validate([ $request->validate([
'receiver' => [ 'receiver' => [
'required', 'required',
\Illuminate\Validation\Rule::exists('user', 'msnv')->where('status', \App\Models\User::STATUS_ACTIVE) \Illuminate\Validation\Rule::exists('user', 'msnv')->where('status', config('constants.STATUS_ACTIVE'))
], ],
'amount' => 'required|integer|min:1|max:' . Administration::MAX_SEND_CARD_PER_MONTH, 'amount' => 'required|integer|min:1|max:' . Administration::MAX_SEND_CARD_PER_MONTH,
]); ]);
@@ -84,7 +84,7 @@ class UserController extends Controller
$user = Auth::user(); $user = Auth::user();
$this->userService->updatePassword($user, $request->password); $this->userService->updatePassword($user, $request->password);
$route = $user->role == \App\Models\User::ROLE_ADMIN ? 'admin.dashboard' : 'user.dashboard'; $route = $user->role == config('constants.ROLE_ADMIN') ? 'admin.dashboard' : 'user.dashboard';
return redirect()->route($route)->with('success', __('messages.password_change_success')); return redirect()->route($route)->with('success', __('messages.password_change_success'));
} }
} }
@@ -12,7 +12,7 @@ class AdminMiddleware
{ {
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {
if (Auth::check() && Auth::user()->role == User::ROLE_ADMIN && Auth::user()->status == User::STATUS_ACTIVE) { if (Auth::check() && Auth::user()->role == config('constants.ROLE_ADMIN') && Auth::user()->status == config('constants.STATUS_ACTIVE')) {
return $next($request); return $next($request);
} }
return redirect()->route('login'); return redirect()->route('login');
@@ -12,7 +12,7 @@ class ForceChangePasswordMiddleware
{ {
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {
if (Auth::check() && Auth::user()->role != User::ROLE_ADMIN && Auth::user()->first_login == User::FIRST_LOGIN_TRUE) { if (Auth::check() && Auth::user()->role != config('constants.ROLE_ADMIN') && Auth::user()->first_login == config('constants.FIRST_LOGIN_TRUE')) {
if (!$request->routeIs('user.change_password') && !$request->routeIs('user.update_password') && !$request->routeIs('logout')) { if (!$request->routeIs('user.change_password') && !$request->routeIs('user.update_password') && !$request->routeIs('logout')) {
return redirect()->route('user.change_password'); return redirect()->route('user.change_password');
} }
@@ -12,7 +12,7 @@ class MemberMiddleware
{ {
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {
if (Auth::check() && in_array(Auth::user()->role, [User::ROLE_MEMBER, User::ROLE_ADMIN]) && Auth::user()->status == User::STATUS_ACTIVE) { if (Auth::check() && in_array(Auth::user()->role, [config('constants.ROLE_MEMBER'), config('constants.ROLE_ADMIN')]) && Auth::user()->status == config('constants.STATUS_ACTIVE')) {
return $next($request); return $next($request);
} }
return redirect()->route('login'); return redirect()->route('login');
+5
View File
@@ -19,4 +19,9 @@ class AddCard extends Model
'seller', 'seller',
'date', 'date',
]; ];
public function sellerUser()
{
return $this->belongsTo(User::class, 'seller', 'msnv');
}
} }
-12
View File
@@ -10,18 +10,6 @@ class User extends Authenticatable
{ {
use HasFactory, Notifiable; use HasFactory, Notifiable;
const ROLE_MEMBER = 0;
const ROLE_ADMIN = 1;
const STATUS_INACTIVE = 0;
const STATUS_ACTIVE = 1;
const FLAG_SEND_DISABLED = 0;
const FLAG_SEND_ENABLED = 1;
const FIRST_LOGIN_FALSE = 0;
const FIRST_LOGIN_TRUE = 1;
protected $table = 'user'; protected $table = 'user';
public $timestamps = false; public $timestamps = false;
+71 -14
View File
@@ -35,8 +35,21 @@ class AdminService implements AdminServiceInterface
->whereBetween('date', [$startOfMonth, $endOfMonth]) ->whereBetween('date', [$startOfMonth, $endOfMonth])
]); ]);
$topReceivedUser = (clone $usersQuery)->where('status', User::STATUS_ACTIVE)->orderByDesc('total_received')->first(); $activeUsers = (clone $usersQuery)->where('status', config('constants.STATUS_ACTIVE'))->get();
$topSentUser = (clone $usersQuery)->where('status', User::STATUS_ACTIVE)->orderByDesc('total_sent')->first();
$maxReceived = $activeUsers->max('total_received') ?? 0;
$maxSent = $activeUsers->max('total_sent') ?? 0;
$topReceivedUsers = $maxReceived > 0
? $activeUsers->filter(fn($u) => $u->total_received == $maxReceived)->values()
: collect();
$topSentUsers = $maxSent > 0
? $activeUsers->filter(fn($u) => $u->total_sent == $maxSent)->values()
: collect();
$topReceivedUser = $topReceivedUsers->first();
$topSentUser = $topSentUsers->first();
if (!is_null($status) && $status !== '') { if (!is_null($status) && $status !== '') {
$usersQuery->where('status', intval($status)); $usersQuery->where('status', intval($status));
@@ -47,7 +60,7 @@ class AdminService implements AdminServiceInterface
} }
if (!is_null($department) && $department !== '') { if (!is_null($department) && $department !== '') {
$usersQuery->where('departments', intval($department)); $usersQuery->where('departments', $department);
} }
if (!is_null($flagSend) && $flagSend !== '') { if (!is_null($flagSend) && $flagSend !== '') {
@@ -62,10 +75,9 @@ class AdminService implements AdminServiceInterface
->orWhereRaw('LOWER(mail) LIKE ?', ['%' . strtolower($search) . '%']); ->orWhereRaw('LOWER(mail) LIKE ?', ['%' . strtolower($search) . '%']);
}); });
} }
$users = $usersQuery->paginate(20)->withQueryString(); $users = $usersQuery->paginate(20)->withQueryString();
return compact('users', 'topReceivedUser', 'topSentUser'); return compact('users', 'topReceivedUser', 'topSentUser', 'topReceivedUsers', 'topSentUsers');
} }
public function getUserTransactions(string $msnv, string $selectedMonth): LengthAwarePaginator public function getUserTransactions(string $msnv, string $selectedMonth): LengthAwarePaginator
@@ -89,10 +101,10 @@ class AdminService implements AdminServiceInterface
'pass' => md5($data['password']), 'pass' => md5($data['password']),
'departments' => $data['departments'], 'departments' => $data['departments'],
'role' => $data['role'], 'role' => $data['role'],
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 0, 'card' => 0,
'flag_send' => User::FLAG_SEND_DISABLED, 'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => $data['role'] == User::ROLE_ADMIN ? User::FIRST_LOGIN_FALSE : User::FIRST_LOGIN_TRUE, 'first_login' => $data['role'] == config('constants.ROLE_ADMIN') ? config('constants.FIRST_LOGIN_FALSE') : config('constants.FIRST_LOGIN_TRUE'),
]); ]);
} }
@@ -101,17 +113,24 @@ class AdminService implements AdminServiceInterface
$user = User::where('msnv', $msnv)->firstOrFail(); $user = User::where('msnv', $msnv)->firstOrFail();
DB::transaction(function () use ($data, $user) { DB::transaction(function () use ($data, $user) {
if (!empty($data['num_card'])) { $newCard = intval($data['card']);
$oldCard = intval($user->card);
if ($newCard > $oldCard) {
$diff = $newCard - $oldCard;
AddCard::create([ AddCard::create([
'buyer' => $user->msnv, 'buyer' => $user->msnv,
'num_card' => $data['num_card'], 'num_card' => $diff,
'seller' => Auth::user()->msnv, 'seller' => Auth::user()->msnv,
'date' => Carbon::today(), 'date' => Carbon::today(),
]); ]);
$user->card += $data['num_card'];
} }
$user->flag_send = isset($data['flag_send']) ? User::FLAG_SEND_ENABLED : User::FLAG_SEND_DISABLED; $user->card = $newCard;
$user->role = intval($data['role']);
$user->status = intval($data['status']);
$user->flag_send = intval($data['flag_send']);
$user->first_login = intval($data['first_login']);
$user->save(); $user->save();
}); });
} }
@@ -119,12 +138,50 @@ class AdminService implements AdminServiceInterface
public function deactivateUser(string $msnv): void public function deactivateUser(string $msnv): void
{ {
$user = User::where('msnv', $msnv)->firstOrFail(); $user = User::where('msnv', $msnv)->firstOrFail();
$user->status = User::STATUS_INACTIVE; $user->status = config('constants.STATUS_INACTIVE');
$user->save(); $user->save();
} }
public function resetAllCards(): void public function resetAllCards(): void
{ {
User::where('status', User::STATUS_ACTIVE)->update(['card' => 0]); User::where('status', config('constants.STATUS_ACTIVE'))->update(['card' => 0]);
}
public function updateAddCard(int $id, array $data): void
{
$addCard = AddCard::findOrFail($id);
$currentMonth = Carbon::now()->format('Y-m');
$recordMonth = Carbon::parse($addCard->date)->format('Y-m');
if ($recordMonth !== $currentMonth) {
throw new \Exception('Không cho phép chỉnh sửa dữ liệu card của các tháng trước.');
}
$newDateMonth = Carbon::parse($data['date'])->format('Y-m');
if ($newDateMonth !== $currentMonth) {
throw new \Exception('Chỉ được phép chỉnh sửa dữ liệu của tháng hiện tại.');
}
$user = User::where('msnv', $addCard->buyer)->firstOrFail();
DB::transaction(function () use ($addCard, $user, $data) {
$oldNumCard = intval($addCard->num_card);
$newNumCard = intval($data['num_card']);
$diff = $newNumCard - $oldNumCard;
if ($user->card + $diff < 0) {
throw new \Exception('Số lượng card cập nhật không hợp lệ vì tổng số card của user không được nhỏ hơn 0.');
}
$user->card += $diff;
$user->save();
$addCard->num_card = $newNumCard;
$addCard->seller = $data['seller'];
$addCard->date = $data['date'];
$addCard->save();
});
} }
} }
@@ -24,4 +24,6 @@ interface AdminServiceInterface
public function deactivateUser(string $msnv): void; public function deactivateUser(string $msnv): void;
public function resetAllCards(): void; public function resetAllCards(): void;
public function updateAddCard(int $id, array $data): void;
} }
+3 -3
View File
@@ -20,7 +20,7 @@ class AuthService implements AuthServiceInterface
// Log the user in manually // Log the user in manually
Auth::login($user); Auth::login($user);
$request->session()->regenerate(); $request->session()->regenerate();
if ($user->status != User::STATUS_ACTIVE) { if ($user->status != config('constants.STATUS_ACTIVE')) {
Auth::logout(); Auth::logout();
return ['success' => false, 'error_key' => 'permission', 'error_msg' => 'Bạn không có quyền truy cập. Vui lòng liên hệ quản trị viên']; return ['success' => false, 'error_key' => 'permission', 'error_msg' => 'Bạn không có quyền truy cập. Vui lòng liên hệ quản trị viên'];
} }
@@ -37,11 +37,11 @@ class AuthService implements AuthServiceInterface
public function getRedirectRouteForUser(User $user): string public function getRedirectRouteForUser(User $user): string
{ {
if ($user->role == User::ROLE_ADMIN) { if ($user->role == config('constants.ROLE_ADMIN')) {
return route('admin.dashboard'); return route('admin.dashboard');
} }
if ($user->first_login == User::FIRST_LOGIN_TRUE) { if ($user->first_login == config('constants.FIRST_LOGIN_TRUE')) {
return route('user.change_password'); return route('user.change_password');
} }
+6 -6
View File
@@ -27,19 +27,19 @@ class UserService implements UserServiceInterface
public function getOtherActiveUsers(User $currentUser): Collection public function getOtherActiveUsers(User $currentUser): Collection
{ {
return User::where('status', User::STATUS_ACTIVE) return User::where('status', config('constants.STATUS_ACTIVE'))
->where('msnv', '!=', $currentUser->msnv) ->where('msnv', '!=', $currentUser->msnv)
->get(); ->get();
} }
public function sendThankcards(User $sender, string $receiverMsnv, int $amount): void public function sendThankcards(User $sender, string $receiverMsnv, int $amount): void
{ {
if ($sender->flag_send == User::FLAG_SEND_DISABLED) { if ($sender->flag_send == config('constants.FLAG_SEND_DISABLED')) {
throw new \RuntimeException(__('messages.error.no_send_permission')); throw new \RuntimeException(__('messages.error.no_send_permission'));
} }
$receiver = User::where('msnv', $receiverMsnv)->first(); $receiver = User::where('msnv', $receiverMsnv)->first();
if (!$receiver || $receiver->status != User::STATUS_ACTIVE) { if (!$receiver || $receiver->status != config('constants.STATUS_ACTIVE')) {
throw new \RuntimeException(__('messages.error.receiver_invalid')); throw new \RuntimeException(__('messages.error.receiver_invalid'));
} }
@@ -91,7 +91,7 @@ class UserService implements UserServiceInterface
public function updatePassword(User $user, string $newPassword): void public function updatePassword(User $user, string $newPassword): void
{ {
$user->pass = md5($newPassword); $user->pass = md5($newPassword);
$user->first_login = User::FIRST_LOGIN_FALSE; $user->first_login = config('constants.FIRST_LOGIN_FALSE');
$user->save(); $user->save();
} }
@@ -109,7 +109,7 @@ class UserService implements UserServiceInterface
->sum('sent'); ->sum('sent');
// Calculate current rank (based on received) // Calculate current rank (based on received)
$rankings = User::where('status', User::STATUS_ACTIVE) $rankings = User::where('status', config('constants.STATUS_ACTIVE'))
->addSelect([ ->addSelect([
'score' => Administration::selectRaw('COALESCE(SUM(received), 0)') 'score' => Administration::selectRaw('COALESCE(SUM(received), 0)')
->whereColumn('msnv', 'user.msnv') ->whereColumn('msnv', 'user.msnv')
@@ -156,7 +156,7 @@ class UserService implements UserServiceInterface
$column = $type === 'sent' ? 'sent' : 'received'; $column = $type === 'sent' ? 'sent' : 'received';
$allActiveUsers = User::where('status', User::STATUS_ACTIVE) $allActiveUsers = User::where('status', config('constants.STATUS_ACTIVE'))
->addSelect([ ->addSelect([
'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)') 'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)')
->whereColumn('msnv', 'user.msnv') ->whereColumn('msnv', 'user.msnv')
+33
View File
@@ -0,0 +1,33 @@
<?php
return [
'ROLE_MEMBER' => 0,
'ROLE_ADMIN' => 1,
'STATUS_INACTIVE' => 0,
'STATUS_ACTIVE' => 1,
'FLAG_SEND_DISABLED' => 0,
'FLAG_SEND_ENABLED' => 1,
'FIRST_LOGIN_FALSE' => 0,
'FIRST_LOGIN_TRUE' => 1,
'DEPARTMENTS' => [
'BIZ-IID - Internet Infra Business Division / BIZ-IID - Sales Enterprise Team (HCM)',
'BIZ-ITOVN - Vietnam Business Division / BIZ-ITOVN - Ho Chi Minh',
'BIZ-SSD - Smart Solutions Business Division / BIZ-SSD - Ho Chi Minh',
'CoE - Center of Excellence / CoE - Data (Ho Chi Minh)',
'CoE - Center of Excellence / CoE - R&D (Ho Chi Minh)',
'CoE - Center of Excellence / CoE - UI/UX (Ho Chi Minh)',
'DL-ITOHCM - ITO Delivery Division (HCM Branch)',
'DL-ITOHCM - ITO Delivery Division (HCM Branch) / DL-ITOHCM - BrSE Team',
'DL-ITOHCM - ITO Delivery Division (HCM Branch) / DL-ITOHCM-DU1 - Delivery Unit 1',
'DL-ITOHCM - ITO Delivery Division (HCM Branch) / DL-ITOHCM-DU2 - Delivery Unit 2',
'DL-ITOHCM - ITO Delivery Division (HCM Branch) / DL-ITOHCM-DU3 - Delivery Unit 3',
'DL-ITOHCM - ITO Delivery Division (HCM Branch) / DL-ITOHCM-DU4 - Delivery Unit 4',
'DL-SSD - Smart Solutions Delivery Division / DL-SSD - Ho Chi Minh',
'FC-HRD - Human Resources Division / FC-HRD - Ho Chi Minh',
'FC-RMD - Risk Management Division / FC-RMD - Ho Chi Minh'
]
];
+65 -21
View File
@@ -35,11 +35,11 @@ class MockDataSeeder extends Seeder
'mail' => 'admin@runsystem.net', 'mail' => 'admin@runsystem.net',
'pass' => $password, 'pass' => $password,
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_ADMIN, 'role' => config('constants.ROLE_ADMIN'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 0, 'card' => 0,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
User::create([ User::create([
@@ -48,20 +48,20 @@ class MockDataSeeder extends Seeder
'mail' => 'admin2@runsystem.net', 'mail' => 'admin2@runsystem.net',
'pass' => $password, 'pass' => $password,
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_ADMIN, 'role' => config('constants.ROLE_ADMIN'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 0, 'card' => 0,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
// Active Users (Initialize with specific users needed for logs/transactions) // Active Users (Initialize with specific users needed for logs/transactions)
$users = [ $users = [
['msnv' => 1001, 'name' => 'Nguyễn Văn A', 'mail' => 'nguyenvana@runsystem.net', 'card' => 10, 'flag' => User::FLAG_SEND_ENABLED, 'first_login' => User::FIRST_LOGIN_FALSE], ['msnv' => 1001, 'name' => 'Nguyễn Văn A', 'mail' => 'nguyenvana@runsystem.net', 'card' => 10, 'flag' => config('constants.FLAG_SEND_ENABLED'), 'first_login' => config('constants.FIRST_LOGIN_FALSE')],
['msnv' => 1002, 'name' => 'Trần Thị Ngọc', 'mail' => 'tranthingoc@runsystem.net', 'card' => 3, 'flag' => User::FLAG_SEND_ENABLED, 'first_login' => User::FIRST_LOGIN_FALSE], ['msnv' => 1002, 'name' => 'Trần Thị Ngọc', 'mail' => 'tranthingoc@runsystem.net', 'card' => 3, 'flag' => config('constants.FLAG_SEND_ENABLED'), 'first_login' => config('constants.FIRST_LOGIN_FALSE')],
['msnv' => 1003, 'name' => 'Lê Kim Thư', 'mail' => 'lekiemthu@runsystem.net', 'card' => 6, 'flag' => User::FLAG_SEND_ENABLED, 'first_login' => User::FIRST_LOGIN_TRUE], // First-time login ['msnv' => 1003, 'name' => 'Lê Kim Thư', 'mail' => 'lekiemthu@runsystem.net', 'card' => 6, 'flag' => config('constants.FLAG_SEND_ENABLED'), 'first_login' => config('constants.FIRST_LOGIN_TRUE')], // First-time login
['msnv' => 1004, 'name' => 'Phòng Nhân Sự', 'mail' => 'phongnhansu@runsystem.net', 'card' => 0, 'flag' => User::FLAG_SEND_DISABLED, 'first_login' => User::FIRST_LOGIN_FALSE], // Disabled sending permission ['msnv' => 1004, 'name' => 'Phòng Nhân Sự', 'mail' => 'phongnhansu@runsystem.net', 'card' => 0, 'flag' => config('constants.FLAG_SEND_DISABLED'), 'first_login' => config('constants.FIRST_LOGIN_FALSE')], // Disabled sending permission
['msnv' => 1005, 'name' => 'Bản Giang', 'mail' => 'bangiang@runsystem.net', 'card' => 36, 'flag' => User::FLAG_SEND_ENABLED, 'first_login' => User::FIRST_LOGIN_FALSE], ['msnv' => 1005, 'name' => 'Bản Giang', 'mail' => 'bangiang@runsystem.net', 'card' => 36, 'flag' => config('constants.FLAG_SEND_ENABLED'), 'first_login' => config('constants.FIRST_LOGIN_FALSE')],
]; ];
// Generate additional users to reach exactly 100 member users // Generate additional users to reach exactly 100 member users
@@ -76,8 +76,8 @@ class MockDataSeeder extends Seeder
'name' => $randomName, 'name' => $randomName,
'mail' => 'dev' . str_pad($i, 3, '0', STR_PAD_LEFT) . '@runsystem.net', 'mail' => 'dev' . str_pad($i, 3, '0', STR_PAD_LEFT) . '@runsystem.net',
'card' => rand(0, 30), 'card' => rand(0, 30),
'flag' => rand(0, 5) > 0 ? User::FLAG_SEND_ENABLED : User::FLAG_SEND_DISABLED, 'flag' => rand(0, 5) > 0 ? config('constants.FLAG_SEND_ENABLED') : config('constants.FLAG_SEND_DISABLED'),
'first_login' => rand(0, 4) == 0 ? User::FIRST_LOGIN_TRUE : User::FIRST_LOGIN_FALSE 'first_login' => rand(0, 4) == 0 ? config('constants.FIRST_LOGIN_TRUE') : config('constants.FIRST_LOGIN_FALSE')
]; ];
} }
@@ -88,8 +88,8 @@ class MockDataSeeder extends Seeder
'mail' => $u['mail'], 'mail' => $u['mail'],
'pass' => $password, 'pass' => $password,
'departments' => rand(1, 5), 'departments' => rand(1, 5),
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => $u['card'], 'card' => $u['card'],
'flag_send' => $u['flag'], 'flag_send' => $u['flag'],
'first_login' => $u['first_login'], 'first_login' => $u['first_login'],
@@ -115,11 +115,11 @@ class MockDataSeeder extends Seeder
'mail' => 'nghiduy@runsystem.net', 'mail' => 'nghiduy@runsystem.net',
'pass' => $password, 'pass' => $password,
'departments' => 2, 'departments' => 2,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_INACTIVE, 'status' => config('constants.STATUS_INACTIVE'),
'card' => 0, 'card' => 0,
'flag_send' => User::FLAG_SEND_DISABLED, 'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
// 2. CREATE CARD ADDITION LOGS (add_card table) // 2. CREATE CARD ADDITION LOGS (add_card table)
@@ -195,5 +195,49 @@ class MockDataSeeder extends Seeder
'date' => $t['date'] 'date' => $t['date']
]); ]);
} }
// 4. CREATE 10 USERS FOR TIE-RANKING IN CURRENT MONTH
$currentMonth = Carbon::now()->format('Y-m');
$currentDate = Carbon::now()->format('Y-m-d');
$depts = config('constants.DEPARTMENTS');
for ($i = 1; $i <= 10; $i++) {
$tieMsnv = 8000 + $i;
User::create([
'msnv' => $tieMsnv,
'name' => "Top 1 User {$i}",
'mail' => "top1_user_{$i}@runsystem.net",
'pass' => $password,
'departments' => $depts[($i - 1) % count($depts)],
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 100,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$senderMsnv = 8000 + $i;
$receiverMsnv = 8000 + ($i % 10 + 1);
// Sender record
Administration::create([
'msnv' => $senderMsnv,
'received' => 0,
'sender' => null,
'sent' => 100,
'receiver' => $receiverMsnv,
'date' => $currentDate,
]);
// Receiver record
Administration::create([
'msnv' => $receiverMsnv,
'received' => 100,
'sender' => $senderMsnv,
'sent' => 0,
'receiver' => null,
'date' => $currentDate,
]);
}
} }
} }
+27 -27
View File
@@ -29,79 +29,79 @@ file_put_contents('app/Models/Administration.php', $content);
// 3. Update app/Http/Middleware/AdminMiddleware.php // 3. Update app/Http/Middleware/AdminMiddleware.php
$content = file_get_contents('app/Http/Middleware/AdminMiddleware.php'); $content = file_get_contents('app/Http/Middleware/AdminMiddleware.php');
$content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content); $content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content);
$content = str_replace('Auth::user()->role == 1', 'Auth::user()->role == User::ROLE_ADMIN', $content); $content = str_replace('Auth::user()->role == 1', 'Auth::user()->role == config('constants.ROLE_ADMIN')', $content);
$content = str_replace('Auth::user()->status == 1', 'Auth::user()->status == User::STATUS_ACTIVE', $content); $content = str_replace('Auth::user()->status == 1', 'Auth::user()->status == config('constants.STATUS_ACTIVE')', $content);
file_put_contents('app/Http/Middleware/AdminMiddleware.php', $content); file_put_contents('app/Http/Middleware/AdminMiddleware.php', $content);
// 4. Update app/Http/Middleware/MemberMiddleware.php // 4. Update app/Http/Middleware/MemberMiddleware.php
$content = file_get_contents('app/Http/Middleware/MemberMiddleware.php'); $content = file_get_contents('app/Http/Middleware/MemberMiddleware.php');
$content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content); $content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content);
$content = str_replace('Auth::user()->role == 0', 'Auth::user()->role == User::ROLE_MEMBER', $content); $content = str_replace('Auth::user()->role == 0', 'Auth::user()->role == config('constants.ROLE_MEMBER')', $content);
$content = str_replace('Auth::user()->status == 1', 'Auth::user()->status == User::STATUS_ACTIVE', $content); $content = str_replace('Auth::user()->status == 1', 'Auth::user()->status == config('constants.STATUS_ACTIVE')', $content);
file_put_contents('app/Http/Middleware/MemberMiddleware.php', $content); file_put_contents('app/Http/Middleware/MemberMiddleware.php', $content);
// 5. Update app/Http/Middleware/ForceChangePasswordMiddleware.php // 5. Update app/Http/Middleware/ForceChangePasswordMiddleware.php
$content = file_get_contents('app/Http/Middleware/ForceChangePasswordMiddleware.php'); $content = file_get_contents('app/Http/Middleware/ForceChangePasswordMiddleware.php');
$content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content); $content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content);
$content = str_replace('Auth::user()->first_login == 1', 'Auth::user()->first_login == User::FIRST_LOGIN_TRUE', $content); $content = str_replace('Auth::user()->first_login == 1', 'Auth::user()->first_login == config('constants.FIRST_LOGIN_TRUE')', $content);
file_put_contents('app/Http/Middleware/ForceChangePasswordMiddleware.php', $content); file_put_contents('app/Http/Middleware/ForceChangePasswordMiddleware.php', $content);
// 6. Update app/Http/Controllers/AuthController.php // 6. Update app/Http/Controllers/AuthController.php
$content = file_get_contents('app/Http/Controllers/AuthController.php'); $content = file_get_contents('app/Http/Controllers/AuthController.php');
$content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content); $content = str_replace('use Illuminate\Support\Facades\Auth;', "use Illuminate\Support\Facades\Auth;\nuse App\Models\User;", $content);
$content = str_replace('$user->status != 1', '$user->status != User::STATUS_ACTIVE', $content); $content = str_replace('$user->status != 1', '$user->status != config('constants.STATUS_ACTIVE')', $content);
$content = str_replace('$user->first_login == 1', '$user->first_login == User::FIRST_LOGIN_TRUE', $content); $content = str_replace('$user->first_login == 1', '$user->first_login == config('constants.FIRST_LOGIN_TRUE')', $content);
$content = str_replace('$user->role == 1', '$user->role == User::ROLE_ADMIN', $content); $content = str_replace('$user->role == 1', '$user->role == config('constants.ROLE_ADMIN')', $content);
file_put_contents('app/Http/Controllers/AuthController.php', $content); file_put_contents('app/Http/Controllers/AuthController.php', $content);
// 7. Update app/Http/Controllers/AdminController.php // 7. Update app/Http/Controllers/AdminController.php
$content = file_get_contents('app/Http/Controllers/AdminController.php'); $content = file_get_contents('app/Http/Controllers/AdminController.php');
$content = str_replace("where('status', 1)", "where('status', User::STATUS_ACTIVE)", $content); $content = str_replace("where('status', 1)", "where('status', config('constants.STATUS_ACTIVE'))", $content);
$content = str_replace("'role' => 'required|in:0,1'", "'role' => 'required|in:' . User::ROLE_MEMBER . ',' . User::ROLE_ADMIN", $content); $content = str_replace("'role' => 'required|in:0,1'", "'role' => 'required|in:' . config('constants.ROLE_MEMBER') . ',' . config('constants.ROLE_ADMIN')", $content);
$content = str_replace("'status' => 1", "'status' => User::STATUS_ACTIVE", $content); $content = str_replace("'status' => 1", "'status' => config('constants.STATUS_ACTIVE')", $content);
$content = str_replace("'flag_send' => 0", "'flag_send' => User::FLAG_SEND_DISABLED", $content); $content = str_replace("'flag_send' => 0", "'flag_send' => config('constants.FLAG_SEND_DISABLED')", $content);
$content = str_replace("'first_login' => 1", "'first_login' => User::FIRST_LOGIN_TRUE", $content); $content = str_replace("'first_login' => 1", "'first_login' => config('constants.FIRST_LOGIN_TRUE')", $content);
$content = str_replace("\$request->has('flag_send') ? 1 : 0", "\$request->has('flag_send') ? User::FLAG_SEND_ENABLED : User::FLAG_SEND_DISABLED", $content); $content = str_replace("\$request->has('flag_send') ? 1 : 0", "\$request->has('flag_send') ? config('constants.FLAG_SEND_ENABLED') : config('constants.FLAG_SEND_DISABLED')", $content);
$content = str_replace("\$user->status = 0", "\$user->status = User::STATUS_INACTIVE", $content); $content = str_replace("\$user->status = 0", "\$user->status = config('constants.STATUS_INACTIVE')", $content);
file_put_contents('app/Http/Controllers/AdminController.php', $content); file_put_contents('app/Http/Controllers/AdminController.php', $content);
// 8. Update app/Http/Controllers/UserController.php // 8. Update app/Http/Controllers/UserController.php
$content = file_get_contents('app/Http/Controllers/UserController.php'); $content = file_get_contents('app/Http/Controllers/UserController.php');
$content = str_replace("where('status', 1)", "where('status', User::STATUS_ACTIVE)", $content); $content = str_replace("where('status', 1)", "where('status', config('constants.STATUS_ACTIVE'))", $content);
$content = str_replace("max:5'", "max:' . Administration::MAX_SEND_CARD_PER_MONTH", $content); $content = str_replace("max:5'", "max:' . Administration::MAX_SEND_CARD_PER_MONTH", $content);
$content = str_replace("\$sender->flag_send == 0", "\$sender->flag_send == User::FLAG_SEND_DISABLED", $content); $content = str_replace("\$sender->flag_send == 0", "\$sender->flag_send == config('constants.FLAG_SEND_DISABLED')", $content);
$content = str_replace("> 5)", "> Administration::MAX_SEND_CARD_PER_MONTH)", $content); $content = str_replace("> 5)", "> Administration::MAX_SEND_CARD_PER_MONTH)", $content);
$content = str_replace("tối đa 5 card", 'tối đa " . Administration::MAX_SEND_CARD_PER_MONTH . " card', $content); $content = str_replace("tối đa 5 card", 'tối đa " . Administration::MAX_SEND_CARD_PER_MONTH . " card', $content);
$content = str_replace("\$user->first_login = 0", "\$user->first_login = User::FIRST_LOGIN_FALSE", $content); $content = str_replace("\$user->first_login = 0", "\$user->first_login = config('constants.FIRST_LOGIN_FALSE')", $content);
$content = str_replace("\$user->role == 1", "\$user->role == User::ROLE_ADMIN", $content); $content = str_replace("\$user->role == 1", "\$user->role == config('constants.ROLE_ADMIN')", $content);
file_put_contents('app/Http/Controllers/UserController.php', $content); file_put_contents('app/Http/Controllers/UserController.php', $content);
// 9. Update app/Console/Commands/ResetCardsCommand.php // 9. Update app/Console/Commands/ResetCardsCommand.php
$content = file_get_contents('app/Console/Commands/ResetCardsCommand.php'); $content = file_get_contents('app/Console/Commands/ResetCardsCommand.php');
$content = str_replace("where('status', 1)", "where('status', User::STATUS_ACTIVE)", $content); $content = str_replace("where('status', 1)", "where('status', config('constants.STATUS_ACTIVE'))", $content);
file_put_contents('app/Console/Commands/ResetCardsCommand.php', $content); file_put_contents('app/Console/Commands/ResetCardsCommand.php', $content);
// 10. Update resources/views/layouts/app.blade.php // 10. Update resources/views/layouts/app.blade.php
$content = file_get_contents('resources/views/layouts/app.blade.php'); $content = file_get_contents('resources/views/layouts/app.blade.php');
$content = str_replace("Auth::user()->role == 1", "Auth::user()->role == \App\Models\User::ROLE_ADMIN", $content); $content = str_replace("Auth::user()->role == 1", "Auth::user()->role == config('constants.ROLE_ADMIN')", $content);
file_put_contents('resources/views/layouts/app.blade.php', $content); file_put_contents('resources/views/layouts/app.blade.php', $content);
// 11. Update resources/views/admin/users/create.blade.php // 11. Update resources/views/admin/users/create.blade.php
$content = file_get_contents('resources/views/admin/users/create.blade.php'); $content = file_get_contents('resources/views/admin/users/create.blade.php');
$content = str_replace('value="0"', 'value="{{ \App\Models\User::ROLE_MEMBER }}"', $content); $content = str_replace('value="0"', 'value="{{ config('constants.ROLE_MEMBER') }}"', $content);
$content = str_replace('value="1"', 'value="{{ \App\Models\User::ROLE_ADMIN }}"', $content); $content = str_replace('value="1"', 'value="{{ config('constants.ROLE_ADMIN') }}"', $content);
$content = str_replace("old('role') == '0'", "old('role') == \App\Models\User::ROLE_MEMBER", $content); $content = str_replace("old('role') == '0'", "old('role') == config('constants.ROLE_MEMBER')", $content);
$content = str_replace("old('role') == '1'", "old('role') == \App\Models\User::ROLE_ADMIN", $content); $content = str_replace("old('role') == '1'", "old('role') == config('constants.ROLE_ADMIN')", $content);
file_put_contents('resources/views/admin/users/create.blade.php', $content); file_put_contents('resources/views/admin/users/create.blade.php', $content);
// 12. Update resources/views/admin/users/edit.blade.php // 12. Update resources/views/admin/users/edit.blade.php
$content = file_get_contents('resources/views/admin/users/edit.blade.php'); $content = file_get_contents('resources/views/admin/users/edit.blade.php');
$content = str_replace("\$user->role == 1", "\$user->role == \App\Models\User::ROLE_ADMIN", $content); $content = str_replace("\$user->role == 1", "\$user->role == config('constants.ROLE_ADMIN')", $content);
file_put_contents('resources/views/admin/users/edit.blade.php', $content); file_put_contents('resources/views/admin/users/edit.blade.php', $content);
// 13. Update resources/views/user/change_password.blade.php // 13. Update resources/views/user/change_password.blade.php
$content = file_get_contents('resources/views/user/change_password.blade.php'); $content = file_get_contents('resources/views/user/change_password.blade.php');
$content = str_replace("Auth::user()->first_login == 1", "Auth::user()->first_login == \App\Models\User::FIRST_LOGIN_TRUE", $content); $content = str_replace("Auth::user()->first_login == 1", "Auth::user()->first_login == config('constants.FIRST_LOGIN_TRUE')", $content);
file_put_contents('resources/views/user/change_password.blade.php', $content); file_put_contents('resources/views/user/change_password.blade.php', $content);
echo "Refactored successfully!"; echo "Refactored successfully!";
@@ -1,45 +1,69 @@
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] p-6 border border-white hover:shadow-md transition-all flex items-center justify-between"> <div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] p-6 border border-white hover:shadow-md transition-all flex items-start justify-between">
<div class="flex items-center gap-4"> <div class="flex items-start gap-4">
<div class="w-14 h-14 rounded-2xl bg-purple-50 flex items-center justify-center text-purple-500 shrink-0"> <div class="w-14 h-14 rounded-2xl bg-purple-50 flex items-center justify-center text-purple-500 shrink-0">
<svg class="w-7 h-7" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8"> <svg class="w-7 h-7" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg> </svg>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-1">User nhận nhiều nhất</span> <span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-2">User nhận nhiều nhất</span>
@if($topReceivedUser) @if(isset($topReceivedUsers) && $topReceivedUsers->isNotEmpty())
<span class="text-[16px] font-extrabold text-[#1a2b49] leading-tight">{{ $topReceivedUser->name }}</span> <div class="flex flex-col gap-3">
<span class="text-[12px] text-gray-500 font-semibold mt-0.5">MSNV: {{ $topReceivedUser->msnv }}</span> @foreach($topReceivedUsers as $user)
<div class="flex flex-col leading-tight">
<span class="text-[16px] font-extrabold text-[#1a2b49]">{{ $user->name }}</span>
<div class="text-[12px] text-gray-500 font-semibold mt-0.5 flex flex-wrap gap-x-2 gap-y-0.5">
<span>MSNV: {{ $user->msnv }}</span>
@if($user->mail)
<span class="text-gray-300"></span>
<span>Email: {{ $user->mail }}</span>
@endif
</div>
</div>
@endforeach
</div>
@else @else
<span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span> <span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span>
@endif @endif
</div> </div>
</div> </div>
<div class="text-right"> <div class="text-right shrink-0">
<span class="text-[36px] font-extrabold text-purple-600 leading-none">{{ $topReceivedUser?->total_received ?? 0 }}</span> <span class="text-[36px] font-extrabold text-purple-600 leading-none">{{ isset($topReceivedUsers) && $topReceivedUsers->isNotEmpty() ? $topReceivedUsers->first()->total_received : 0 }}</span>
<span class="block text-[11px] text-gray-400 font-bold uppercase mt-1">Thẻ Nhận</span> <span class="block text-[11px] text-gray-400 font-bold uppercase mt-1">Thẻ Nhận</span>
</div> </div>
</div> </div>
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] p-6 border border-white hover:shadow-md transition-all flex items-center justify-between"> <div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] p-6 border border-white hover:shadow-md transition-all flex items-start justify-between">
<div class="flex items-center gap-4"> <div class="flex items-start gap-4">
<div class="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500 shrink-0"> <div class="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center text-blue-500 shrink-0">
<svg class="w-7 h-7 transform -rotate-45" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8"> <svg class="w-7 h-7 transform -rotate-45" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /> <path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg> </svg>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-1">User gửi nhiều nhất</span> <span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-2">User gửi nhiều nhất</span>
@if($topSentUser) @if(isset($topSentUsers) && $topSentUsers->isNotEmpty())
<span class="text-[16px] font-extrabold text-[#1a2b49] leading-tight">{{ $topSentUser->name }}</span> <div class="flex flex-col gap-3">
<span class="text-[12px] text-gray-500 font-semibold mt-0.5">MSNV: {{ $topSentUser->msnv }}</span> @foreach($topSentUsers as $user)
<div class="flex flex-col leading-tight">
<span class="text-[16px] font-extrabold text-[#1a2b49]">{{ $user->name }}</span>
<div class="text-[12px] text-gray-500 font-semibold mt-0.5 flex flex-wrap gap-x-2 gap-y-0.5">
<span>MSNV: {{ $user->msnv }}</span>
@if($user->mail)
<span class="text-gray-300"></span>
<span>Email: {{ $user->mail }}</span>
@endif
</div>
</div>
@endforeach
</div>
@else @else
<span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span> <span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span>
@endif @endif
</div> </div>
</div> </div>
<div class="text-right"> <div class="text-right shrink-0">
<span class="text-[36px] font-extrabold text-blue-600 leading-none">{{ $topSentUser?->total_sent ?? 0 }}</span> <span class="text-[36px] font-extrabold text-blue-600 leading-none">{{ isset($topSentUsers) && $topSentUsers->isNotEmpty() ? $topSentUsers->first()->total_sent : 0 }}</span>
<span class="block text-[11px] text-gray-400 font-bold uppercase mt-1">Thẻ Gửi</span> <span class="block text-[11px] text-gray-400 font-bold uppercase mt-1">Thẻ Gửi</span>
</div> </div>
</div> </div>
@@ -11,19 +11,24 @@
<table class="min-w-full divide-y divide-gray-100 text-sm"> <table class="min-w-full divide-y divide-gray-100 text-sm">
<thead class="bg-gray-50/50"> <thead class="bg-gray-50/50">
<tr> <tr>
<th class="table-header-cell">Thao tác</th>
<th class="table-header-cell">MSNV</th> <th class="table-header-cell">MSNV</th>
<th class="table-header-cell">Nhân viên</th> <th class="table-header-cell">Nhân viên</th>
<th class="table-header-cell">Phòng ban</th>
<th class="table-header-cell">Đã Nhận</th> <th class="table-header-cell">Đã Nhận</th>
<th class="table-header-cell">Đã Gửi</th> <th class="table-header-cell">Đã Gửi</th>
<th class="table-header-cell">Số thẻ</th> <th class="table-header-cell">Số thẻ</th>
<th class="table-header-cell">Quyền gửi</th> <th class="table-header-cell">Quyền gửi</th>
<th class="table-header-cell">Vai trò</th>
<th class="table-header-cell">Trạng thái</th> <th class="table-header-cell">Trạng thái</th>
<th class="table-header-cell text-right">Thao tác</th>
</tr> </tr>
</thead> </thead>
<tbody class="bg-white divide-y divide-gray-100"> <tbody class="bg-white divide-y divide-gray-100">
@forelse($users as $u) @forelse($users as $u)
<tr class="hover:bg-slate-50/60 transition-colors"> <tr class="hover:bg-slate-50/60 transition-colors">
<td class="table-body-cell font-medium space-x-3">
<a href="{{ route('admin.users.edit', $u->msnv) }}" class="table-action-link !text-[#3462f7] hover:!text-blue-800">Chỉnh sửa</a>
</td>
<td class="table-body-cell font-bold text-text-dark">{{ $u->msnv }}</td> <td class="table-body-cell font-bold text-text-dark">{{ $u->msnv }}</td>
<td class="table-body-cell"> <td class="table-body-cell">
<div class="flex flex-col"> <div class="flex flex-col">
@@ -31,6 +36,20 @@
<span class="text-xs text-gray-400 mt-0.5">{{ $u->mail }}</span> <span class="text-xs text-gray-400 mt-0.5">{{ $u->mail }}</span>
</div> </div>
</td> </td>
<td class="table-body-cell">
@php
$deptVal = $u->departments;
if (is_numeric($deptVal)) {
$idx = ((int)$deptVal) - 1;
$deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
} else {
$deptName = $deptVal ?? 'Team';
}
@endphp
<div class="max-w-[200px] truncate font-medium text-gray-600" title="{{ $deptName }}">
{{ $deptName }}
</div>
</td>
<td class="table-body-cell font-extrabold text-green-600">+{{ $u->total_received }}</td> <td class="table-body-cell font-extrabold text-green-600">+{{ $u->total_received }}</td>
<td class="table-body-cell font-extrabold text-blue-600">{{ $u->total_sent }}</td> <td class="table-body-cell font-extrabold text-blue-600">{{ $u->total_sent }}</td>
<td class="table-body-cell font-extrabold text-orange-500">{{ $u->card }}</td> <td class="table-body-cell font-extrabold text-orange-500">{{ $u->card }}</td>
@@ -42,19 +61,23 @@
@endif @endif
</td> </td>
<td class="table-body-cell"> <td class="table-body-cell">
@if($u->status == \App\Models\User::STATUS_ACTIVE) @if($u->role == config('constants.ROLE_ADMIN'))
<span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100">Admin</span>
@else
<span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold text-gray-500">Member</span>
@endif
</td>
<td class="table-body-cell">
@if($u->status == config('constants.STATUS_ACTIVE'))
<span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100">Đang làm việc</span> <span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100">Đang làm việc</span>
@else @else
<span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-red-50 text-red-700 border border-red-100">Đã nghỉ việc</span> <span class="px-2.5 py-1 inline-flex text-[10px] leading-5 font-bold rounded-full bg-red-50 text-red-700 border border-red-100">Đã nghỉ việc</span>
@endif @endif
</td> </td>
<td class="table-body-cell text-right font-medium space-x-3">
<a href="{{ route('admin.users.edit', $u->msnv) }}" class="table-action-link !text-[#3462f7] hover:!text-blue-800">Quản </a>
</td>
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="8" class="px-6 py-10 text-center text-text-light font-medium">Chưa user nào đang hoạt động.</td> <td colspan="10" class="px-6 py-10 text-center text-text-light font-medium">Chưa user nào đang hoạt động.</td>
</tr> </tr>
@endforelse @endforelse
</tbody> </tbody>
@@ -1,9 +1,9 @@
@props(['rank', 'name', 'team', 'avatar', 'score', 'isCurrent' => false, 'unit' => 'điểm']) @props(['rank', 'name', 'team', 'avatar', 'score', 'isCurrent' => false, 'unit' => 'điểm'])
<div class="flex items-center justify-between py-3 border-b border-gray-50 last:border-0 hover:bg-gray-50 rounded-xl px-3 transition-colors {{ $isCurrent ? 'bg-[#F5F8FF]' : '' }}"> <div class="flex items-center justify-between py-3 border-b border-gray-50 last:border-0 hover:bg-gray-50 rounded-xl px-3 transition-colors {{ $isCurrent ? 'bg-[#F5F8FF]' : '' }}">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3 min-w-0 flex-1">
<!-- Rank --> <!-- Rank -->
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center flex-shrink-0">
@if($rank == 1) @if($rank == 1)
<span class="text-xl">👑</span> <span class="text-xl">👑</span>
<span class="absolute text-[10px] font-bold text-amber-500 translate-y-3">1</span> <span class="absolute text-[10px] font-bold text-amber-500 translate-y-3">1</span>
@@ -17,17 +17,17 @@
</div> </div>
<!-- Avatar & Info --> <!-- Avatar & Info -->
<div class="flex items-center gap-3 ml-1"> <div class="flex items-center gap-3 ml-1 min-w-0 flex-1">
<img src="{{ $avatar }}" alt="{{ $name }}" class="w-10 h-10 rounded-full object-cover shadow-sm border border-gray-100"> <img src="{{ $avatar }}" alt="{{ $name }}" class="w-10 h-10 rounded-full object-cover shadow-sm border border-gray-100 flex-shrink-0">
<div class="flex flex-col"> <div class="flex flex-col min-w-0 flex-1">
<span class="text-[14px] font-bold {{ $isCurrent ? 'text-[#1a2b49]' : 'text-gray-800' }} leading-tight mb-0.5">{{ $name }}</span> <span class="text-[14px] font-bold {{ $isCurrent ? 'text-[#1a2b49]' : 'text-gray-800' }} leading-tight mb-0.5 truncate">{{ $name }}</span>
<span class="text-[12px] text-gray-500 font-medium">{{ $team }}</span> <span class="text-[12px] text-gray-500 font-medium truncate" title="{{ $team }}">{{ $team }}</span>
</div> </div>
</div> </div>
</div> </div>
<!-- Score --> <!-- Score -->
<div class="text-[14px]"> <div class="text-[14px] flex-shrink-0 ml-3">
<span class="font-extrabold {{ $isCurrent ? 'text-blue-600' : 'text-gray-800' }}">{{ number_format($score, 0, ',', '.') }}</span> <span class="font-extrabold {{ $isCurrent ? 'text-blue-600' : 'text-gray-800' }}">{{ number_format($score, 0, ',', '.') }}</span>
<span class="text-gray-400 font-medium ml-1 text-[13px]">{{ $unit }}</span> <span class="text-gray-400 font-medium ml-1 text-[13px]">{{ $unit }}</span>
</div> </div>
+2 -2
View File
@@ -32,7 +32,7 @@
</x-sidebar-item> </x-sidebar-item>
@if(Auth::check() && Auth::user()->role == \App\Models\User::ROLE_ADMIN) @if(Auth::check() && Auth::user()->role == config('constants.ROLE_ADMIN'))
<x-sidebar-item href="{{ route('admin.users.index') }}" :active="request()->routeIs('admin.users.index')" icon="users"> <x-sidebar-item href="{{ route('admin.users.index') }}" :active="request()->routeIs('admin.users.index')" icon="users">
Quản User Quản User
</x-sidebar-item> </x-sidebar-item>
@@ -63,7 +63,7 @@
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-[14px] font-bold text-[#1e293b] leading-tight truncate max-w-[130px]">{{ Auth::check() ? (Auth::user()->name ?? 'Nguyễn Minh Anh') : 'Nguyễn Minh Anh' }}</span> <span class="text-[14px] font-bold text-[#1e293b] leading-tight truncate max-w-[130px]">{{ Auth::check() ? (Auth::user()->name ?? 'Nguyễn Minh Anh') : 'Nguyễn Minh Anh' }}</span>
<span class="text-[12px] text-gray-500 font-medium">{{ Auth::check() ? (Auth::user()->role == \App\Models\User::ROLE_ADMIN ? 'Administrator' : 'Product Team') : 'Product Team' }}</span> <span class="text-[12px] text-gray-500 font-medium">{{ Auth::check() ? (Auth::user()->role == config('constants.ROLE_ADMIN') ? 'Administrator' : 'Product Team') : 'Product Team' }}</span>
</div> </div>
</div> </div>
<svg class="w-4 h-4 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" /></svg> <svg class="w-4 h-4 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" /></svg>
@@ -38,11 +38,9 @@
<!-- Department Filter --> <!-- Department Filter -->
<select name="department" id="departmentFilter" class="form-input !py-1.5 !text-sm !w-full md:!w-[170px] border-[#d9dfe7] rounded-xl bg-white shrink-0"> <select name="department" id="departmentFilter" class="form-input !py-1.5 !text-sm !w-full md:!w-[170px] border-[#d9dfe7] rounded-xl bg-white shrink-0">
<option value="" {{ $department === '' ? 'selected' : '' }}>Tất cả phòng ban</option> <option value="" {{ $department === '' ? 'selected' : '' }}>Tất cả phòng ban</option>
<option value="1" {{ $department === '1' ? 'selected' : '' }}>Phòng Phát Triển (Dev)</option> @foreach(config('constants.DEPARTMENTS') as $dept)
<option value="2" {{ $department === '2' ? 'selected' : '' }}>Phòng Nhân Sự (HR)</option> <option value="{{ $dept }}" {{ $department === $dept ? 'selected' : '' }}>{{ $dept }}</option>
<option value="3" {{ $department === '3' ? 'selected' : '' }}>Phòng Kinh Doanh (Sale)</option> @endforeach
<option value="4" {{ $department === '4' ? 'selected' : '' }}>Ban Giám Đốc (Board)</option>
<option value="5" {{ $department === '5' ? 'selected' : '' }}>Phòng Marketing</option>
</select> </select>
<!-- Flag Send Filter --> <!-- Flag Send Filter -->
@@ -43,7 +43,7 @@
<p class="text-xs text-gray-500 font-medium truncate mb-1">{{ $user->mail }}</p> <p class="text-xs text-gray-500 font-medium truncate mb-1">{{ $user->mail }}</p>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="px-2 py-0.5 text-[10px] font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100"> <span class="px-2 py-0.5 text-[10px] font-bold rounded-full bg-blue-50 text-blue-700 border border-blue-100">
{{ $user->role == \App\Models\User::ROLE_ADMIN ? 'Admin' : 'Member' }} {{ $user->role == config('constants.ROLE_ADMIN') ? 'Admin' : 'Member' }}
</span> </span>
<span class="text-xs text-gray-300 font-semibold"></span> <span class="text-xs text-gray-300 font-semibold"></span>
<span class="text-xs font-bold text-gray-600">Thẻ: <span class="text-orange-500 font-extrabold text-sm">{{ $user->card }}</span></span> <span class="text-xs font-bold text-gray-600">Thẻ: <span class="text-orange-500 font-extrabold text-sm">{{ $user->card }}</span></span>
@@ -105,11 +105,10 @@
<div class="relative"> <div class="relative">
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="departments">Phòng ban <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="departments">Phòng ban <span class="text-red-500">*</span></label>
<select name="departments" id="departments" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required> <select name="departments" id="departments" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required>
<option value="1" {{ old('departments') == '1' ? 'selected' : '' }}>Phòng Phát Triển (Dev)</option> <option value="">-- Chọn phòng ban --</option>
<option value="2" {{ old('departments') == '2' ? 'selected' : '' }}>Phòng Nhân Sự (HR)</option> @foreach(config('constants.DEPARTMENTS') as $dept)
<option value="3" {{ old('departments') == '3' ? 'selected' : '' }}>Phòng Kinh Doanh (Sale)</option> <option value="{{ $dept }}" {{ old('departments') == $dept ? 'selected' : '' }}>{{ $dept }}</option>
<option value="4" {{ old('departments') == '4' ? 'selected' : '' }}>Ban Giám Đốc (Board)</option> @endforeach
<option value="5" {{ old('departments') == '5' ? 'selected' : '' }}>Phòng Marketing</option>
</select> </select>
@error('departments')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror @error('departments')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
@@ -131,8 +130,8 @@
<div class="relative"> <div class="relative">
<label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="role">Quyền hạn <span class="text-red-500">*</span></label> <label class="block text-[14px] font-bold text-[#1a2b49] mb-2" for="role">Quyền hạn <span class="text-red-500">*</span></label>
<select name="role" id="role" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required> <select name="role" id="role" class="form-input !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" required>
<option value="{{ \App\Models\User::ROLE_MEMBER }}" {{ old('role') == \App\Models\User::ROLE_MEMBER ? 'selected' : '' }}>Nhân viên (Member)</option> <option value="{{ config('constants.ROLE_MEMBER') }}" {{ old('role') == config('constants.ROLE_MEMBER') ? 'selected' : '' }}>Nhân viên (Member)</option>
<option value="{{ \App\Models\User::ROLE_ADMIN }}" {{ old('role') == \App\Models\User::ROLE_ADMIN ? 'selected' : '' }}>Quản trị viên (Admin)</option> <option value="{{ config('constants.ROLE_ADMIN') }}" {{ old('role') == config('constants.ROLE_ADMIN') ? 'selected' : '' }}>Quản trị viên (Admin)</option>
</select> </select>
@error('role')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror @error('role')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div> </div>
+106 -3
View File
@@ -1,4 +1,4 @@
@props(['mode', 'user' => null, 'administrations' => null, 'selectedMonth' => null]) @props(['mode', 'user' => null, 'administrations' => null, 'selectedMonth' => null, 'addCards' => null, 'admins' => null])
@if($mode === 'self' && $user) @if($mode === 'self' && $user)
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6"> <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
@@ -29,11 +29,114 @@
@endif @endif
</div> </div>
<!-- RIGHT PANEL: Transaction History --> <!-- RIGHT PANEL: Transaction History & Add Card History -->
@if(in_array($mode, ['edit', 'delete', 'self']) && $user && $administrations) @if(in_array($mode, ['edit', 'delete', 'self']) && $user && $administrations)
<div class="col-span-1 lg:col-span-2"> <div class="col-span-1 lg:col-span-2 flex flex-col gap-6">
<x-transaction-history :mode="$mode" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" /> <x-transaction-history :mode="$mode" :user="$user" :administrations="$administrations" :selectedMonth="$selectedMonth" />
@if($mode === 'edit' && isset($addCards))
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden flex flex-col">
<div class="px-6 py-5 border-b border-gray-100 bg-white">
<h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight">
Lịch sử cấp phát thẻ
</h3>
</div>
<div class="p-0 overflow-y-auto max-h-[600px]">
<table class="min-w-full divide-y divide-gray-100 text-sm">
<thead class="bg-gray-50/50 sticky top-0">
<tr>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Ngày cấp</th>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Người cấp (Admin)</th>
<th class="px-6 py-4 text-left text-xs font-bold text-text-light uppercase tracking-wider">Số lượng thẻ</th>
<th class="px-6 py-4 text-center text-xs font-bold text-text-light uppercase tracking-wider w-24">Hành động</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
@forelse($addCards as $addCard)
@php
$isCurrentMonth = Carbon\Carbon::parse($addCard->date)->format('Y-m') === Carbon\Carbon::now()->format('Y-m');
@endphp
<tr class="hover:bg-slate-50/60 transition-colors">
<td class="px-6 py-3.5 whitespace-nowrap text-text-medium font-medium">{{ Carbon\Carbon::parse($addCard->date)->format('d/m/Y') }}</td>
<td class="px-6 py-3.5 whitespace-nowrap font-bold text-[#1a2b49]">
@if($addCard->sellerUser)
{{ $addCard->sellerUser->name }} <span class="text-xs font-medium text-gray-500">(MSNV: {{ $addCard->seller }})</span>
@else
MSNV: {{ $addCard->seller }}
@endif
</td>
<td class="px-6 py-3.5 whitespace-nowrap font-extrabold text-base text-green-600">+{{ $addCard->num_card }}</td>
<td class="px-6 py-3.5 whitespace-nowrap text-center">
@if($isCurrentMonth)
<button type="button" onclick="openEditAddCardModal({{ json_encode($addCard) }})" class="text-blue-600 hover:text-blue-900 font-bold transition-colors">Sửa</button>
@else
<span class="text-gray-400 font-semibold text-xs cursor-not-allowed" title="Không thể chỉnh sửa giao dịch tháng trước">Tháng trước</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-12 text-center text-text-light font-medium text-base">
Chưa lịch sử cấp phát thẻ nào.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endif
</div> </div>
@endif @endif
</div> </div>
@if($mode === 'edit' && isset($addCards) && isset($admins))
<x-modal id="editAddCardModal" title="Chỉnh sửa giao dịch cấp phát thẻ">
<form id="editAddCardForm" method="POST" action="">
@csrf
@method('PUT')
<div class="mb-5">
<label for="modal_num_card" class="block text-sm font-bold text-gray-700 mb-2">Số lượng card</label>
<input type="number" id="modal_num_card" name="num_card" min="1" required class="w-full border border-gray-300 rounded-xl px-4 py-2.5 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200 outline-none transition-all">
</div>
<div class="mb-5">
<label for="modal_seller" class="block text-sm font-bold text-gray-700 mb-2">Người cấp (Admin)</label>
<select id="modal_seller" name="seller" required class="w-full border border-gray-300 rounded-xl px-4 py-2.5 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200 outline-none transition-all">
@foreach($admins as $admin)
<option value="{{ $admin->msnv }}">{{ $admin->name }} (MSNV: {{ $admin->msnv }})</option>
@endforeach
</select>
</div>
<div class="mb-6">
<label for="modal_date" class="block text-sm font-bold text-gray-700 mb-2">Ngày cấp</label>
<input type="date" id="modal_date" name="date" required min="{{ Carbon\Carbon::now()->startOfMonth()->format('Y-m-d') }}" max="{{ Carbon\Carbon::now()->endOfMonth()->format('Y-m-d') }}" class="w-full border border-gray-300 rounded-xl px-4 py-2.5 text-sm focus:border-blue-500 focus:ring focus:ring-blue-200 outline-none transition-all">
</div>
<div class="flex justify-end gap-3 pt-4 border-t border-gray-100">
<button type="button" onclick="closeModal('editAddCardModal')" class="btn-secondary !px-5">Hủy bỏ</button>
<button type="submit" class="btn-primary !px-5 bg-[#3462f7] hover:bg-blue-700">Lưu thay đổi</button>
</div>
</form>
</x-modal>
<script>
function openEditAddCardModal(record) {
const form = document.getElementById('editAddCardForm');
form.action = `/admin/add-cards/${record.id}`;
document.getElementById('modal_num_card').value = record.num_card;
document.getElementById('modal_seller').value = record.seller;
if (record.date) {
document.getElementById('modal_date').value = record.date.substring(0, 10);
}
openModal('editAddCardModal');
}
</script>
@endif
+10
View File
@@ -37,6 +37,16 @@
</div> </div>
@endif @endif
@if($errors->any())
<div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded relative">
<ul class="list-disc pl-5">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@yield('content') @yield('content')
</div> </div>
</main> </main>
@@ -16,7 +16,7 @@
<form action="{{ route('user.update_password') }}" method="POST"> <form action="{{ route('user.update_password') }}" method="POST">
@csrf @csrf
<div class="space-y-6"> <div class="space-y-6">
@if(Auth::user()->first_login == \App\Models\User::FIRST_LOGIN_TRUE) @if(Auth::user()->first_login == config('constants.FIRST_LOGIN_TRUE'))
<div class="p-4 bg-amber-50/70 border border-amber-100 text-amber-800 rounded-2xl text-xs font-medium leading-relaxed flex items-start gap-2 shadow-sm"> <div class="p-4 bg-amber-50/70 border border-amber-100 text-amber-800 rounded-2xl text-xs font-medium leading-relaxed flex items-start gap-2 shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 shrink-0 text-amber-600"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 shrink-0 text-amber-600">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
@@ -63,7 +63,7 @@
</div> </div>
<div class="flex items-center gap-3 pt-4"> <div class="flex items-center gap-3 pt-4">
@if(Auth::user()->first_login != \App\Models\User::FIRST_LOGIN_TRUE) @if(Auth::user()->first_login != config('constants.FIRST_LOGIN_TRUE'))
<a href="{{ route('user.dashboard') }}" class="cursor-pointer flex-1 flex items-center justify-center h-[46px] rounded-xl border border-gray-200 bg-white hover:bg-gray-50 text-gray-600 text-[14px] font-bold shadow-sm transition-all duration-200">Hủy bỏ</a> <a href="{{ route('user.dashboard') }}" class="cursor-pointer flex-1 flex items-center justify-center h-[46px] rounded-xl border border-gray-200 bg-white hover:bg-gray-50 text-gray-600 text-[14px] font-bold shadow-sm transition-all duration-200">Hủy bỏ</a>
@endif @endif
<button type="submit" class="cursor-pointer flex-1 flex items-center justify-center h-[46px] rounded-xl bg-[#3462f7] hover:bg-[#254edb] text-white text-[14px] font-bold shadow-md shadow-blue-500/10 transition-all duration-200">Xác nhận đổi mật khẩu</button> <button type="submit" class="cursor-pointer flex-1 flex items-center justify-center h-[46px] rounded-xl bg-[#3462f7] hover:bg-[#254edb] text-white text-[14px] font-bold shadow-md shadow-blue-500/10 transition-all duration-200">Xác nhận đổi mật khẩu</button>
@@ -2,17 +2,19 @@
@if(count($rankers) > 0) @if(count($rankers) > 0)
<div class="flex flex-col"> <div class="flex flex-col">
@foreach($rankers as $item) @foreach($rankers as $item)
@php
$deptVal = $item->departments;
if (is_numeric($deptVal)) {
$idx = ((int)$deptVal) - 1;
$teamName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
} else {
$teamName = $deptVal ?? 'Team';
}
@endphp
<x-ranking-item <x-ranking-item
rank="{{ $item->rank }}" rank="{{ $item->rank }}"
name="{{ $item->name }}" name="{{ $item->name }}"
team="{{ match((int) $item->departments) { team="{{ $teamName }}"
1 => 'Dev Team',
2 => 'HR Team',
3 => 'Sale Team',
4 => 'Board Team',
5 => 'Marketing Team',
default => 'Team'
} }}"
avatar="{{ $item->avatar ?? 'https://api.dicebear.com/7.x/notionists/svg?seed=' . urlencode($item->name) . '&backgroundColor=bfdbfe' }}" avatar="{{ $item->avatar ?? 'https://api.dicebear.com/7.x/notionists/svg?seed=' . urlencode($item->name) . '&backgroundColor=bfdbfe' }}"
score="{{ $item->score }}" score="{{ $item->score }}"
unit="thẻ" unit="thẻ"
@@ -30,17 +32,19 @@
@if($currentUserRankItem) @if($currentUserRankItem)
<div class="mt-auto"> <div class="mt-auto">
<div class="my-3 border-t border-dashed border-gray-100 mx-2"></div> <div class="my-3 border-t border-dashed border-gray-100 mx-2"></div>
@php
$currDeptVal = $currentUserRankItem->departments;
if (is_numeric($currDeptVal)) {
$idx = ((int)$currDeptVal) - 1;
$currTeamName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
} else {
$currTeamName = $currDeptVal ?? 'Team';
}
@endphp
<x-ranking-item <x-ranking-item
rank="{{ $currentUserRankItem->rank > 0 ? $currentUserRankItem->rank : '-' }}" rank="{{ $currentUserRankItem->rank > 0 ? $currentUserRankItem->rank : '-' }}"
name="{{ $currentUserRankItem->name }}" name="{{ $currentUserRankItem->name }}"
team="{{ match((int) $currentUserRankItem->departments) { team="{{ $currTeamName }}"
1 => 'Dev Team',
2 => 'HR Team',
3 => 'Sale Team',
4 => 'Board Team',
5 => 'Marketing Team',
default => 'Team'
} }}"
avatar="{{ $currentUserRankItem->avatar ?? 'https://api.dicebear.com/7.x/notionists/svg?seed=' . urlencode($currentUserRankItem->name) . '&backgroundColor=bfdbfe' }}" avatar="{{ $currentUserRankItem->avatar ?? 'https://api.dicebear.com/7.x/notionists/svg?seed=' . urlencode($currentUserRankItem->name) . '&backgroundColor=bfdbfe' }}"
score="{{ $currentUserRankItem->score }}" score="{{ $currentUserRankItem->score }}"
unit="thẻ" unit="thẻ"
+2
View File
@@ -13,5 +13,7 @@ Route::middleware(['auth', 'force_change_password', 'admin'])->prefix('admin')->
Route::put('/users/{msnv}', [AdminController::class, 'update'])->name('users.update'); Route::put('/users/{msnv}', [AdminController::class, 'update'])->name('users.update');
Route::delete('/users/{msnv}', [AdminController::class, 'destroy'])->name('users.destroy'); Route::delete('/users/{msnv}', [AdminController::class, 'destroy'])->name('users.destroy');
Route::put('/add-cards/{id}', [AdminController::class, 'updateAddCard'])->name('add_cards.update');
Route::post('/reset-cards', [AdminController::class, 'resetCards'])->name('reset_cards'); Route::post('/reset-cards', [AdminController::class, 'resetCards'])->name('reset_cards');
}); });
+519 -40
View File
@@ -30,11 +30,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'usera@example.com', 'mail' => 'usera@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$userB = User::create([ $userB = User::create([
@@ -43,11 +43,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'userb@example.com', 'mail' => 'userb@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$selectedMonth = '2026-07'; $selectedMonth = '2026-07';
@@ -110,11 +110,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'usera@example.com', 'mail' => 'usera@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$selectedMonth = '2026-07'; $selectedMonth = '2026-07';
@@ -167,11 +167,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'alpha@example.com', 'mail' => 'alpha@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
User::create([ User::create([
@@ -180,11 +180,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'beta@example.com', 'mail' => 'beta@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
// 2. Fetch statistics via AdminService with search for '2001' (msnv) // 2. Fetch statistics via AdminService with search for '2001' (msnv)
@@ -211,11 +211,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'active@example.com', 'mail' => 'active@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
User::create([ User::create([
@@ -224,11 +224,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'inactive@example.com', 'mail' => 'inactive@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_INACTIVE, 'status' => config('constants.STATUS_INACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_DISABLED, 'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
// 2. Fetch statistics via AdminService with status = '1' // 2. Fetch statistics via AdminService with status = '1'
@@ -252,12 +252,12 @@ class AdminUserListStatsTest extends TestCase
'name' => 'ADMIN DEV ENABLED', 'name' => 'ADMIN DEV ENABLED',
'mail' => 'admin_dev@example.com', 'mail' => 'admin_dev@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => config('constants.DEPARTMENTS')[0],
'role' => User::ROLE_ADMIN, 'role' => config('constants.ROLE_ADMIN'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
User::create([ User::create([
@@ -265,12 +265,12 @@ class AdminUserListStatsTest extends TestCase
'name' => 'MEMBER HR DISABLED', 'name' => 'MEMBER HR DISABLED',
'mail' => 'member_hr@example.com', 'mail' => 'member_hr@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 2, 'departments' => config('constants.DEPARTMENTS')[13],
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_DISABLED, 'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
// Filter by role = Admin ('1') // Filter by role = Admin ('1')
@@ -279,8 +279,8 @@ class AdminUserListStatsTest extends TestCase
$this->assertTrue($users->contains('msnv', 4001)); $this->assertTrue($users->contains('msnv', 4001));
$this->assertFalse($users->contains('msnv', 4002)); $this->assertFalse($users->contains('msnv', 4002));
// Filter by department = HR ('2') // Filter by department = HR (config('constants.DEPARTMENTS')[13])
$stats2 = $this->adminService->getUserListWithStats('2026-07', null, '1', null, '2'); $stats2 = $this->adminService->getUserListWithStats('2026-07', null, '1', null, config('constants.DEPARTMENTS')[13]);
$users2 = collect($stats2['users']->items()); $users2 = collect($stats2['users']->items());
$this->assertTrue($users2->contains('msnv', 4002)); $this->assertTrue($users2->contains('msnv', 4002));
$this->assertFalse($users2->contains('msnv', 4001)); $this->assertFalse($users2->contains('msnv', 4001));
@@ -291,4 +291,483 @@ class AdminUserListStatsTest extends TestCase
$this->assertTrue($users3->contains('msnv', 4001)); $this->assertTrue($users3->contains('msnv', 4001));
$this->assertFalse($users3->contains('msnv', 4002)); $this->assertFalse($users3->contains('msnv', 4002));
} }
public function test_admin_can_update_user_fields_including_card_role_status_flag_send_first_login(): void
{
$admin = User::create([
'msnv' => 9001,
'name' => 'Admin User',
'mail' => 'admin@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$member = User::create([
'msnv' => 9002,
'name' => 'Member User',
'mail' => 'member@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 5,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$response = $this->actingAs($admin)->put(route('admin.users.update', 9002), [
'card' => 20, // increased by 15
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_INACTIVE'),
'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]);
$response->assertRedirect();
$member->refresh();
$this->assertEquals(20, $member->card);
$this->assertEquals(config('constants.ROLE_ADMIN'), $member->role);
$this->assertEquals(config('constants.STATUS_INACTIVE'), $member->status);
$this->assertEquals(config('constants.FLAG_SEND_DISABLED'), $member->flag_send);
$this->assertEquals(config('constants.FIRST_LOGIN_TRUE'), $member->first_login);
// Assert AddCard record was created with diff (15)
$this->assertDatabaseHas('add_card', [
'buyer' => 9002,
'num_card' => 15,
'seller' => 9001,
]);
}
public function test_user_list_stats_handles_ties_for_top_sender_receiver(): void
{
// Clear existing mock data first to have a clean slate for ties
User::truncate();
Administration::truncate();
// 1. Create three users
$user1 = User::create([
'msnv' => 3001,
'name' => 'User One',
'mail' => 'user1@example.com',
'pass' => md5('password'),
'departments' => config('constants.DEPARTMENTS')[0],
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$user2 = User::create([
'msnv' => 3002,
'name' => 'User Two',
'mail' => 'user2@example.com',
'pass' => md5('password'),
'departments' => config('constants.DEPARTMENTS')[0],
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$user3 = User::create([
'msnv' => 3003,
'name' => 'User Three',
'mail' => 'user3@example.com',
'pass' => md5('password'),
'departments' => config('constants.DEPARTMENTS')[0],
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$selectedMonth = '2026-07';
$testDate = '2026-07-15';
// User 1 sends 5 cards to User 2
Administration::create([
'msnv' => 3001,
'received' => 0,
'sender' => null,
'sent' => 5,
'receiver' => 3002,
'date' => $testDate,
]);
Administration::create([
'msnv' => 3002,
'received' => 5,
'sender' => 3001,
'sent' => 0,
'receiver' => null,
'date' => $testDate,
]);
// User 3 sends 5 cards to User 1
Administration::create([
'msnv' => 3003,
'received' => 0,
'sender' => null,
'sent' => 5,
'receiver' => 3001,
'date' => $testDate,
]);
Administration::create([
'msnv' => 3001,
'received' => 5,
'sender' => 3003,
'sent' => 0,
'receiver' => null,
'date' => $testDate,
]);
// Now:
// User 1 has sent = 5, received = 5
// User 2 has sent = 0, received = 5
// User 3 has sent = 5, received = 0
// Top senders should be: User 1 and User 3 (both sent 5)
// Top receivers should be: User 1 and User 2 (both received 5)
$stats = $this->adminService->getUserListWithStats($selectedMonth);
$this->assertCount(2, $stats['topReceivedUsers']);
$this->assertCount(2, $stats['topSentUsers']);
$topReceivedMsnvs = $stats['topReceivedUsers']->pluck('msnv')->toArray();
$this->assertContains(3001, $topReceivedMsnvs);
$this->assertContains(3002, $topReceivedMsnvs);
$topSentMsnvs = $stats['topSentUsers']->pluck('msnv')->toArray();
$this->assertContains(3001, $topSentMsnvs);
$this->assertContains(3003, $topSentMsnvs);
}
public function test_user_list_stats_handles_10_way_tie_for_top_sender_receiver(): void
{
$users = [];
$selectedMonth = '2026-07';
$testDate = '2026-07-15';
// Create 10 users
for ($i = 1; $i <= 10; $i++) {
$users[$i] = User::create([
'msnv' => 5000 + $i,
'name' => "User Tenfold {$i}",
'mail' => "user_tenfold_{$i}@example.com",
'pass' => md5('password'),
'departments' => config('constants.DEPARTMENTS')[0],
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
}
// Each user sends 1 card to the next user, and user 10 sends to user 1
for ($i = 1; $i <= 10; $i++) {
$senderMsnv = 5000 + $i;
$receiverMsnv = 5000 + ($i % 10 + 1);
// Sender record
Administration::create([
'msnv' => $senderMsnv,
'received' => 0,
'sender' => null,
'sent' => 1,
'receiver' => $receiverMsnv,
'date' => $testDate,
]);
// Receiver record
Administration::create([
'msnv' => $receiverMsnv,
'received' => 1,
'sender' => $senderMsnv,
'sent' => 0,
'receiver' => null,
'date' => $testDate,
]);
}
// Run the service call
$stats = $this->adminService->getUserListWithStats($selectedMonth);
// Assert all 10 users are returned in topReceivedUsers and topSentUsers
$this->assertCount(10, $stats['topReceivedUsers']);
$this->assertCount(10, $stats['topSentUsers']);
// Verify all their MSNVs are present
for ($i = 1; $i <= 10; $i++) {
$msnv = 5000 + $i;
$this->assertContains($msnv, $stats['topReceivedUsers']->pluck('msnv')->toArray());
$this->assertContains($msnv, $stats['topSentUsers']->pluck('msnv')->toArray());
}
}
public function test_update_add_card_successfully_updates_balance(): void
{
$admin = User::create([
'msnv' => 9001,
'name' => 'Admin User',
'mail' => 'admin@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$member = User::create([
'msnv' => 9002,
'name' => 'Member User',
'mail' => 'member@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, // currently has 10 cards
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Created in the current month
$addCard = \App\Models\AddCard::create([
'buyer' => 9002,
'num_card' => 5,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
// Action: Update it from 5 to 3 (decrease of 2, new user balance should be 10 - 2 = 8)
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $addCard->id), [
'num_card' => 3,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
$response->assertRedirect();
$addCard->refresh();
$this->assertEquals(3, $addCard->num_card);
$member->refresh();
$this->assertEquals(8, $member->card);
}
public function test_update_add_card_fails_if_past_month(): void
{
$admin = User::create([
'msnv' => 9001,
'name' => 'Admin User',
'mail' => 'admin@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$member = User::create([
'msnv' => 9002,
'name' => 'Member User',
'mail' => 'member@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Created in a past month
$addCard = \App\Models\AddCard::create([
'buyer' => 9002,
'num_card' => 5,
'seller' => 9001,
'date' => Carbon::now()->subMonth()->format('Y-m-d'),
]);
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $addCard->id), [
'num_card' => 3,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
$response->assertRedirect();
$response->assertSessionHasErrors('error');
$this->assertTrue(session('errors')->has('error'));
$this->assertEquals('Không cho phép chỉnh sửa dữ liệu card của các tháng trước.', session('errors')->first('error'));
}
public function test_update_add_card_fails_if_new_date_not_current_month(): void
{
$admin = User::create([
'msnv' => 9001,
'name' => 'Admin User',
'mail' => 'admin@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$member = User::create([
'msnv' => 9002,
'name' => 'Member User',
'mail' => 'member@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Created in current month
$addCard = \App\Models\AddCard::create([
'buyer' => 9002,
'num_card' => 5,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
// Update with date in a past month
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $addCard->id), [
'num_card' => 3,
'seller' => 9001,
'date' => Carbon::now()->subMonth()->format('Y-m-d'),
]);
$response->assertRedirect();
$response->assertSessionHasErrors('error');
$this->assertEquals('Chỉ được phép chỉnh sửa dữ liệu của tháng hiện tại.', session('errors')->first('error'));
}
public function test_update_add_card_fails_if_new_balance_negative(): void
{
$admin = User::create([
'msnv' => 9001,
'name' => 'Admin User',
'mail' => 'admin@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$member = User::create([
'msnv' => 9002,
'name' => 'Member User',
'mail' => 'member@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 2, // has 2 cards left
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Created in current month
$addCard = \App\Models\AddCard::create([
'buyer' => 9002,
'num_card' => 5,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
// Decrease it from 5 to 1 (decrease of 4). Since member only has 2 cards, this would make balance -2 (2 - 4 = -2), which is illegal
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $addCard->id), [
'num_card' => 1,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
$response->assertRedirect();
$response->assertSessionHasErrors('error');
$this->assertEquals('Số lượng card cập nhật không hợp lệ vì tổng số card của user không được nhỏ hơn 0.', session('errors')->first('error'));
}
public function test_update_add_card_succeeds_for_older_transaction_in_current_month(): void
{
$admin = User::create([
'msnv' => 9001,
'name' => 'Admin User',
'mail' => 'admin@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$member = User::create([
'msnv' => 9002,
'name' => 'Member User',
'mail' => 'member@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 15, // has 15 cards
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Transaction 1 (older)
$oldAddCard = \App\Models\AddCard::create([
'buyer' => 9002,
'num_card' => 5,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
// Transaction 2 (newest)
$newAddCard = \App\Models\AddCard::create([
'buyer' => 9002,
'num_card' => 10,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
// Try to update older Transaction 1: change num_card from 5 to 6 (increase of 1)
$response = $this->actingAs($admin)->put(route('admin.add_cards.update', $oldAddCard->id), [
'num_card' => 6,
'seller' => 9001,
'date' => Carbon::now()->format('Y-m-d'),
]);
$response->assertRedirect();
$response->assertSessionHasNoErrors();
$oldAddCard->refresh();
$this->assertEquals(6, $oldAddCard->num_card);
$member->refresh();
$this->assertEquals(16, $member->card);
}
} }
+28 -28
View File
@@ -29,11 +29,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'test@example.com', 'mail' => 'test@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$response = $this->post('/login', [ $response = $this->post('/login', [
@@ -88,11 +88,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'test@example.com', 'mail' => 'test@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_INACTIVE, // 0 'status' => config('constants.STATUS_INACTIVE'), // 0
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$response = $this->post('/login', [ $response = $this->post('/login', [
@@ -128,11 +128,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'member@example.com', 'mail' => 'member@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$response = $this->post('/login', [ $response = $this->post('/login', [
@@ -153,11 +153,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'admin@example.com', 'mail' => 'admin@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_ADMIN, 'role' => config('constants.ROLE_ADMIN'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$response2 = $this->post('/login', [ $response2 = $this->post('/login', [
@@ -180,11 +180,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'first@example.com', 'mail' => 'first@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_TRUE, 'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]); ]);
$response = $this->post('/login', [ $response = $this->post('/login', [
@@ -205,11 +205,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'first_admin@example.com', 'mail' => 'first_admin@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_ADMIN, 'role' => config('constants.ROLE_ADMIN'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_TRUE, 'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]); ]);
$response = $this->post('/login', [ $response = $this->post('/login', [
@@ -233,11 +233,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'test@example.com', 'mail' => 'test@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
// First 5 attempts fail // First 5 attempts fail
+7 -7
View File
@@ -30,11 +30,11 @@ class PasswordMd5Test extends TestCase
'mail' => 'test@example.com', 'mail' => 'test@example.com',
'pass' => md5('old_password'), 'pass' => md5('old_password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_TRUE, 'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]); ]);
$this->userService->updatePassword($user, 'new_secret_password'); $this->userService->updatePassword($user, 'new_secret_password');
@@ -42,7 +42,7 @@ class PasswordMd5Test extends TestCase
$user->refresh(); $user->refresh();
$this->assertEquals(md5('new_secret_password'), $user->pass); $this->assertEquals(md5('new_secret_password'), $user->pass);
$this->assertEquals(User::FIRST_LOGIN_FALSE, $user->first_login); $this->assertEquals(config('constants.FIRST_LOGIN_FALSE'), $user->first_login);
} }
public function test_admin_service_create_user_stores_password_as_md5(): void public function test_admin_service_create_user_stores_password_as_md5(): void
@@ -53,7 +53,7 @@ class PasswordMd5Test extends TestCase
'mail' => 'created@example.com', 'mail' => 'created@example.com',
'departments' => 2, 'departments' => 2,
'password' => 'admin_created_pass', 'password' => 'admin_created_pass',
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
]; ];
$this->adminService->createUser($data); $this->adminService->createUser($data);
@@ -62,6 +62,6 @@ class PasswordMd5Test extends TestCase
$this->assertNotNull($user); $this->assertNotNull($user);
$this->assertEquals(md5('admin_created_pass'), $user->pass); $this->assertEquals(md5('admin_created_pass'), $user->pass);
$this->assertEquals(User::FIRST_LOGIN_TRUE, $user->first_login); $this->assertEquals(config('constants.FIRST_LOGIN_TRUE'), $user->first_login);
} }
} }
+4 -4
View File
@@ -22,11 +22,11 @@ class PasswordValidationTest extends TestCase
'mail' => 'test@example.com', 'mail' => 'test@example.com',
'pass' => md5('password123'), 'pass' => md5('password123'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_TRUE, 'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]); ]);
} }
+8 -8
View File
@@ -28,11 +28,11 @@ class SendThankCardTest extends TestCase
'mail' => 'sender@example.com', 'mail' => 'sender@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$this->receiver = User::create([ $this->receiver = User::create([
@@ -41,11 +41,11 @@ class SendThankCardTest extends TestCase
'mail' => 'receiver@example.com', 'mail' => 'receiver@example.com',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 0, 'card' => 0,
'flag_send' => User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
} }
+32 -32
View File
@@ -17,11 +17,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'admin@runsystem.net', 'mail' => 'admin@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_ADMIN, 'role' => config('constants.ROLE_ADMIN'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -46,11 +46,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'usera@runsystem.net', 'mail' => 'usera@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$user2 = \App\Models\User::create([ $user2 = \App\Models\User::create([
@@ -59,11 +59,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'userb@runsystem.net', 'mail' => 'userb@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 2, 'departments' => 2,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
\App\Models\Administration::create([ \App\Models\Administration::create([
@@ -101,11 +101,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'usera@runsystem.net', 'mail' => 'usera@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -126,11 +126,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => "user{$char}@runsystem.net", 'mail' => "user{$char}@runsystem.net",
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
\App\Models\Administration::create([ \App\Models\Administration::create([
@@ -163,11 +163,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'active@runsystem.net', 'mail' => 'active@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
$inactiveUser = \App\Models\User::create([ $inactiveUser = \App\Models\User::create([
@@ -176,11 +176,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'inactive@runsystem.net', 'mail' => 'inactive@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_INACTIVE, 'status' => config('constants.STATUS_INACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
\App\Models\Administration::create([ \App\Models\Administration::create([
@@ -218,11 +218,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'usera@runsystem.net', 'mail' => 'usera@runsystem.net',
'pass' => md5('password'), 'pass' => md5('password'),
'departments' => 1, 'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER, 'role' => config('constants.ROLE_MEMBER'),
'status' => \App\Models\User::STATUS_ACTIVE, 'status' => config('constants.STATUS_ACTIVE'),
'card' => 10, 'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED, 'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE, 'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]); ]);
\App\Models\Administration::create([ \App\Models\Administration::create([