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'));
$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');
$department = $request->input('department');
$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()) {
return response()->json([
'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(),
]);
}
@@ -39,6 +44,8 @@ class AdminController extends Controller
'selectedMonth',
'topReceivedUser',
'topSentUser',
'topReceivedUsers',
'topSentUsers',
'search',
'status',
'role',
@@ -58,9 +65,9 @@ class AdminController extends Controller
'msnv' => 'required|integer|unique:user,msnv',
'name' => 'required|string|max:255',
'mail' => 'required|email|unique:user,mail',
'departments' => 'required|integer|min:1|max:5',
'departments' => 'required|string|in:' . implode(',', config('constants.DEPARTMENTS')),
'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'));
@@ -75,17 +82,30 @@ class AdminController extends Controller
$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)
{
$request->validate([
'num_card' => 'nullable|integer|min:1',
'flag_send' => 'nullable|boolean',
'card' => 'required|integer|min:0',
'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'));
}
@@ -97,6 +117,22 @@ class AdminController extends Controller
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()
{
$this->adminService->resetAllCards();
+1 -1
View File
@@ -62,7 +62,7 @@ class AuthController extends Controller
\Illuminate\Support\Facades\RateLimiter::clear($throttleKey);
$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);
}
+2 -2
View File
@@ -48,7 +48,7 @@ class UserController extends Controller
$request->validate([
'receiver' => [
'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,
]);
@@ -84,7 +84,7 @@ class UserController extends Controller
$user = Auth::user();
$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'));
}
}
@@ -12,7 +12,7 @@ class AdminMiddleware
{
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 redirect()->route('login');
@@ -12,7 +12,7 @@ class ForceChangePasswordMiddleware
{
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')) {
return redirect()->route('user.change_password');
}
@@ -12,7 +12,7 @@ class MemberMiddleware
{
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 redirect()->route('login');
+5
View File
@@ -19,4 +19,9 @@ class AddCard extends Model
'seller',
'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;
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';
public $timestamps = false;
+71 -14
View File
@@ -35,8 +35,21 @@ class AdminService implements AdminServiceInterface
->whereBetween('date', [$startOfMonth, $endOfMonth])
]);
$topReceivedUser = (clone $usersQuery)->where('status', User::STATUS_ACTIVE)->orderByDesc('total_received')->first();
$topSentUser = (clone $usersQuery)->where('status', User::STATUS_ACTIVE)->orderByDesc('total_sent')->first();
$activeUsers = (clone $usersQuery)->where('status', config('constants.STATUS_ACTIVE'))->get();
$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 !== '') {
$usersQuery->where('status', intval($status));
@@ -47,7 +60,7 @@ class AdminService implements AdminServiceInterface
}
if (!is_null($department) && $department !== '') {
$usersQuery->where('departments', intval($department));
$usersQuery->where('departments', $department);
}
if (!is_null($flagSend) && $flagSend !== '') {
@@ -62,10 +75,9 @@ class AdminService implements AdminServiceInterface
->orWhereRaw('LOWER(mail) LIKE ?', ['%' . strtolower($search) . '%']);
});
}
$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
@@ -89,10 +101,10 @@ class AdminService implements AdminServiceInterface
'pass' => md5($data['password']),
'departments' => $data['departments'],
'role' => $data['role'],
'status' => User::STATUS_ACTIVE,
'status' => config('constants.STATUS_ACTIVE'),
'card' => 0,
'flag_send' => User::FLAG_SEND_DISABLED,
'first_login' => $data['role'] == User::ROLE_ADMIN ? User::FIRST_LOGIN_FALSE : User::FIRST_LOGIN_TRUE,
'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'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();
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([
'buyer' => $user->msnv,
'num_card' => $data['num_card'],
'num_card' => $diff,
'seller' => Auth::user()->msnv,
'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();
});
}
@@ -119,12 +138,50 @@ class AdminService implements AdminServiceInterface
public function deactivateUser(string $msnv): void
{
$user = User::where('msnv', $msnv)->firstOrFail();
$user->status = User::STATUS_INACTIVE;
$user->status = config('constants.STATUS_INACTIVE');
$user->save();
}
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 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
Auth::login($user);
$request->session()->regenerate();
if ($user->status != User::STATUS_ACTIVE) {
if ($user->status != config('constants.STATUS_ACTIVE')) {
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'];
}
@@ -37,11 +37,11 @@ class AuthService implements AuthServiceInterface
public function getRedirectRouteForUser(User $user): string
{
if ($user->role == User::ROLE_ADMIN) {
if ($user->role == config('constants.ROLE_ADMIN')) {
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');
}
+6 -6
View File
@@ -27,19 +27,19 @@ class UserService implements UserServiceInterface
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)
->get();
}
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'));
}
$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'));
}
@@ -91,7 +91,7 @@ class UserService implements UserServiceInterface
public function updatePassword(User $user, string $newPassword): void
{
$user->pass = md5($newPassword);
$user->first_login = User::FIRST_LOGIN_FALSE;
$user->first_login = config('constants.FIRST_LOGIN_FALSE');
$user->save();
}
@@ -109,7 +109,7 @@ class UserService implements UserServiceInterface
->sum('sent');
// Calculate current rank (based on received)
$rankings = User::where('status', User::STATUS_ACTIVE)
$rankings = User::where('status', config('constants.STATUS_ACTIVE'))
->addSelect([
'score' => Administration::selectRaw('COALESCE(SUM(received), 0)')
->whereColumn('msnv', 'user.msnv')
@@ -156,7 +156,7 @@ class UserService implements UserServiceInterface
$column = $type === 'sent' ? 'sent' : 'received';
$allActiveUsers = User::where('status', User::STATUS_ACTIVE)
$allActiveUsers = User::where('status', config('constants.STATUS_ACTIVE'))
->addSelect([
'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)')
->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',
'pass' => $password,
'departments' => 1,
'role' => User::ROLE_ADMIN,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 0,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
User::create([
@@ -48,20 +48,20 @@ class MockDataSeeder extends Seeder
'mail' => 'admin2@runsystem.net',
'pass' => $password,
'departments' => 1,
'role' => User::ROLE_ADMIN,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 0,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Active Users (Initialize with specific users needed for logs/transactions)
$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' => 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' => 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' => 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' => 1005, 'name' => 'Bản Giang', 'mail' => 'bangiang@runsystem.net', 'card' => 36, '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' => 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' => 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' => 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' => config('constants.FLAG_SEND_ENABLED'), 'first_login' => config('constants.FIRST_LOGIN_FALSE')],
];
// Generate additional users to reach exactly 100 member users
@@ -76,8 +76,8 @@ class MockDataSeeder extends Seeder
'name' => $randomName,
'mail' => 'dev' . str_pad($i, 3, '0', STR_PAD_LEFT) . '@runsystem.net',
'card' => rand(0, 30),
'flag' => rand(0, 5) > 0 ? User::FLAG_SEND_ENABLED : User::FLAG_SEND_DISABLED,
'first_login' => rand(0, 4) == 0 ? User::FIRST_LOGIN_TRUE : User::FIRST_LOGIN_FALSE
'flag' => rand(0, 5) > 0 ? config('constants.FLAG_SEND_ENABLED') : config('constants.FLAG_SEND_DISABLED'),
'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'],
'pass' => $password,
'departments' => rand(1, 5),
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => $u['card'],
'flag_send' => $u['flag'],
'first_login' => $u['first_login'],
@@ -115,11 +115,11 @@ class MockDataSeeder extends Seeder
'mail' => 'nghiduy@runsystem.net',
'pass' => $password,
'departments' => 2,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_INACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_INACTIVE'),
'card' => 0,
'flag_send' => User::FLAG_SEND_DISABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// 2. CREATE CARD ADDITION LOGS (add_card table)
@@ -195,5 +195,49 @@ class MockDataSeeder extends Seeder
'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
$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('Auth::user()->role == 1', 'Auth::user()->role == User::ROLE_ADMIN', $content);
$content = str_replace('Auth::user()->status == 1', 'Auth::user()->status == User::STATUS_ACTIVE', $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 == config('constants.STATUS_ACTIVE')', $content);
file_put_contents('app/Http/Middleware/AdminMiddleware.php', $content);
// 4. Update 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('Auth::user()->role == 0', 'Auth::user()->role == User::ROLE_MEMBER', $content);
$content = str_replace('Auth::user()->status == 1', 'Auth::user()->status == User::STATUS_ACTIVE', $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 == config('constants.STATUS_ACTIVE')', $content);
file_put_contents('app/Http/Middleware/MemberMiddleware.php', $content);
// 5. Update 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('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);
// 6. Update 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('$user->status != 1', '$user->status != User::STATUS_ACTIVE', $content);
$content = str_replace('$user->first_login == 1', '$user->first_login == User::FIRST_LOGIN_TRUE', $content);
$content = str_replace('$user->role == 1', '$user->role == User::ROLE_ADMIN', $content);
$content = str_replace('$user->status != 1', '$user->status != config('constants.STATUS_ACTIVE')', $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 == config('constants.ROLE_ADMIN')', $content);
file_put_contents('app/Http/Controllers/AuthController.php', $content);
// 7. Update 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("'role' => 'required|in:0,1'", "'role' => 'required|in:' . User::ROLE_MEMBER . ',' . User::ROLE_ADMIN", $content);
$content = str_replace("'status' => 1", "'status' => User::STATUS_ACTIVE", $content);
$content = str_replace("'flag_send' => 0", "'flag_send' => User::FLAG_SEND_DISABLED", $content);
$content = str_replace("'first_login' => 1", "'first_login' => User::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("\$user->status = 0", "\$user->status = User::STATUS_INACTIVE", $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:' . config('constants.ROLE_MEMBER') . ',' . config('constants.ROLE_ADMIN')", $content);
$content = str_replace("'status' => 1", "'status' => config('constants.STATUS_ACTIVE')", $content);
$content = str_replace("'flag_send' => 0", "'flag_send' => config('constants.FLAG_SEND_DISABLED')", $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') ? config('constants.FLAG_SEND_ENABLED') : config('constants.FLAG_SEND_DISABLED')", $content);
$content = str_replace("\$user->status = 0", "\$user->status = config('constants.STATUS_INACTIVE')", $content);
file_put_contents('app/Http/Controllers/AdminController.php', $content);
// 8. Update 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("\$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("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->role == 1", "\$user->role == User::ROLE_ADMIN", $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 == config('constants.ROLE_ADMIN')", $content);
file_put_contents('app/Http/Controllers/UserController.php', $content);
// 9. Update 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);
// 10. Update 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);
// 11. Update 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="1"', 'value="{{ \App\Models\User::ROLE_ADMIN }}"', $content);
$content = str_replace("old('role') == '0'", "old('role') == \App\Models\User::ROLE_MEMBER", $content);
$content = str_replace("old('role') == '1'", "old('role') == \App\Models\User::ROLE_ADMIN", $content);
$content = str_replace('value="0"', 'value="{{ config('constants.ROLE_MEMBER') }}"', $content);
$content = str_replace('value="1"', 'value="{{ config('constants.ROLE_ADMIN') }}"', $content);
$content = str_replace("old('role') == '0'", "old('role') == config('constants.ROLE_MEMBER')", $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);
// 12. Update 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);
// 13. Update 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);
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="flex items-center gap-4">
<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-start gap-4">
<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">
<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>
</div>
<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>
@if($topReceivedUser)
<span class="text-[16px] font-extrabold text-[#1a2b49] leading-tight">{{ $topReceivedUser->name }}</span>
<span class="text-[12px] text-gray-500 font-semibold mt-0.5">MSNV: {{ $topReceivedUser->msnv }}</span>
<span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-2">User nhận nhiều nhất</span>
@if(isset($topReceivedUsers) && $topReceivedUsers->isNotEmpty())
<div class="flex flex-col gap-3">
@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
<span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span>
@endif
</div>
</div>
<div class="text-right">
<span class="text-[36px] font-extrabold text-purple-600 leading-none">{{ $topReceivedUser?->total_received ?? 0 }}</span>
<div class="text-right shrink-0">
<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>
</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="flex items-center gap-4">
<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-start gap-4">
<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">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
</div>
<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>
@if($topSentUser)
<span class="text-[16px] font-extrabold text-[#1a2b49] leading-tight">{{ $topSentUser->name }}</span>
<span class="text-[12px] text-gray-500 font-semibold mt-0.5">MSNV: {{ $topSentUser->msnv }}</span>
<span class="text-[13px] font-bold text-gray-400 uppercase tracking-wider mb-2">User gửi nhiều nhất</span>
@if(isset($topSentUsers) && $topSentUsers->isNotEmpty())
<div class="flex flex-col gap-3">
@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
<span class="text-[15px] font-semibold text-gray-400">Không dữ liệu</span>
@endif
</div>
</div>
<div class="text-right">
<span class="text-[36px] font-extrabold text-blue-600 leading-none">{{ $topSentUser?->total_sent ?? 0 }}</span>
<div class="text-right shrink-0">
<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>
</div>
</div>
@@ -11,19 +11,24 @@
<table class="min-w-full divide-y divide-gray-100 text-sm">
<thead class="bg-gray-50/50">
<tr>
<th class="table-header-cell">Thao tác</th>
<th class="table-header-cell">MSNV</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">Đã Gửi</th>
<th class="table-header-cell">Số thẻ</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 text-right">Thao tác</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
@forelse($users as $u)
<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">
<div class="flex flex-col">
@@ -31,6 +36,20 @@
<span class="text-xs text-gray-400 mt-0.5">{{ $u->mail }}</span>
</div>
</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-blue-600">{{ $u->total_sent }}</td>
<td class="table-body-cell font-extrabold text-orange-500">{{ $u->card }}</td>
@@ -42,19 +61,23 @@
@endif
</td>
<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>
@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>
@endif
</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>
@empty
<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>
@endforelse
</tbody>
@@ -1,9 +1,9 @@
@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 gap-3">
<div class="flex items-center gap-3 min-w-0 flex-1">
<!-- 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)
<span class="text-xl">👑</span>
<span class="absolute text-[10px] font-bold text-amber-500 translate-y-3">1</span>
@@ -17,17 +17,17 @@
</div>
<!-- Avatar & Info -->
<div class="flex items-center gap-3 ml-1">
<img src="{{ $avatar }}" alt="{{ $name }}" class="w-10 h-10 rounded-full object-cover shadow-sm border border-gray-100">
<div class="flex flex-col">
<span class="text-[14px] font-bold {{ $isCurrent ? 'text-[#1a2b49]' : 'text-gray-800' }} leading-tight mb-0.5">{{ $name }}</span>
<span class="text-[12px] text-gray-500 font-medium">{{ $team }}</span>
<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 flex-shrink-0">
<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 truncate">{{ $name }}</span>
<span class="text-[12px] text-gray-500 font-medium truncate" title="{{ $team }}">{{ $team }}</span>
</div>
</div>
</div>
<!-- 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="text-gray-400 font-medium ml-1 text-[13px]">{{ $unit }}</span>
</div>
+2 -2
View File
@@ -32,7 +32,7 @@
</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">
Quản User
</x-sidebar-item>
@@ -63,7 +63,7 @@
</div>
<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-[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>
<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 -->
<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="1" {{ $department === '1' ? 'selected' : '' }}>Phòng Phát Triển (Dev)</option>
<option value="2" {{ $department === '2' ? 'selected' : '' }}>Phòng Nhân Sự (HR)</option>
<option value="3" {{ $department === '3' ? 'selected' : '' }}>Phòng Kinh Doanh (Sale)</option>
<option value="4" {{ $department === '4' ? 'selected' : '' }}>Ban Giám Đốc (Board)</option>
<option value="5" {{ $department === '5' ? 'selected' : '' }}>Phòng Marketing</option>
@foreach(config('constants.DEPARTMENTS') as $dept)
<option value="{{ $dept }}" {{ $department === $dept ? 'selected' : '' }}>{{ $dept }}</option>
@endforeach
</select>
<!-- Flag Send Filter -->
@@ -43,7 +43,7 @@
<p class="text-xs text-gray-500 font-medium truncate mb-1">{{ $user->mail }}</p>
<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">
{{ $user->role == \App\Models\User::ROLE_ADMIN ? 'Admin' : 'Member' }}
{{ $user->role == config('constants.ROLE_ADMIN') ? 'Admin' : 'Member' }}
</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>
@@ -105,11 +105,10 @@
<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>
<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="2" {{ old('departments') == '2' ? 'selected' : '' }}>Phòng Nhân Sự (HR)</option>
<option value="3" {{ old('departments') == '3' ? 'selected' : '' }}>Phòng Kinh Doanh (Sale)</option>
<option value="4" {{ old('departments') == '4' ? 'selected' : '' }}>Ban Giám Đốc (Board)</option>
<option value="5" {{ old('departments') == '5' ? 'selected' : '' }}>Phòng Marketing</option>
<option value="">-- Chọn phòng ban --</option>
@foreach(config('constants.DEPARTMENTS') as $dept)
<option value="{{ $dept }}" {{ old('departments') == $dept ? 'selected' : '' }}>{{ $dept }}</option>
@endforeach
</select>
@error('departments')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</div>
@@ -131,8 +130,8 @@
<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>
<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="{{ \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_MEMBER') }}" {{ old('role') == config('constants.ROLE_MEMBER') ? 'selected' : '' }}>Nhân viên (Member)</option>
<option value="{{ config('constants.ROLE_ADMIN') }}" {{ old('role') == config('constants.ROLE_ADMIN') ? 'selected' : '' }}>Quản trị viên (Admin)</option>
</select>
@error('role')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
</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)
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
@@ -29,11 +29,114 @@
@endif
</div>
<!-- RIGHT PANEL: Transaction History -->
<!-- RIGHT PANEL: Transaction History & Add Card History -->
@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" />
@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>
@endif
</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>
@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')
</div>
</main>
@@ -16,7 +16,7 @@
<form action="{{ route('user.update_password') }}" method="POST">
@csrf
<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">
<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" />
@@ -63,7 +63,7 @@
</div>
<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>
@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>
@@ -2,17 +2,19 @@
@if(count($rankers) > 0)
<div class="flex flex-col">
@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
rank="{{ $item->rank }}"
name="{{ $item->name }}"
team="{{ match((int) $item->departments) {
1 => 'Dev Team',
2 => 'HR Team',
3 => 'Sale Team',
4 => 'Board Team',
5 => 'Marketing Team',
default => 'Team'
} }}"
team="{{ $teamName }}"
avatar="{{ $item->avatar ?? 'https://api.dicebear.com/7.x/notionists/svg?seed=' . urlencode($item->name) . '&backgroundColor=bfdbfe' }}"
score="{{ $item->score }}"
unit="thẻ"
@@ -30,17 +32,19 @@
@if($currentUserRankItem)
<div class="mt-auto">
<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
rank="{{ $currentUserRankItem->rank > 0 ? $currentUserRankItem->rank : '-' }}"
name="{{ $currentUserRankItem->name }}"
team="{{ match((int) $currentUserRankItem->departments) {
1 => 'Dev Team',
2 => 'HR Team',
3 => 'Sale Team',
4 => 'Board Team',
5 => 'Marketing Team',
default => 'Team'
} }}"
team="{{ $currTeamName }}"
avatar="{{ $currentUserRankItem->avatar ?? 'https://api.dicebear.com/7.x/notionists/svg?seed=' . urlencode($currentUserRankItem->name) . '&backgroundColor=bfdbfe' }}"
score="{{ $currentUserRankItem->score }}"
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::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');
});
+519 -40
View File
@@ -30,11 +30,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'usera@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$userB = User::create([
@@ -43,11 +43,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'userb@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$selectedMonth = '2026-07';
@@ -110,11 +110,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'usera@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$selectedMonth = '2026-07';
@@ -167,11 +167,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'alpha@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
User::create([
@@ -180,11 +180,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'beta@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// 2. Fetch statistics via AdminService with search for '2001' (msnv)
@@ -211,11 +211,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'active@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
User::create([
@@ -224,11 +224,11 @@ class AdminUserListStatsTest extends TestCase
'mail' => 'inactive@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_INACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_INACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_DISABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// 2. Fetch statistics via AdminService with status = '1'
@@ -252,12 +252,12 @@ class AdminUserListStatsTest extends TestCase
'name' => 'ADMIN DEV ENABLED',
'mail' => 'admin_dev@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_ADMIN,
'status' => User::STATUS_ACTIVE,
'departments' => config('constants.DEPARTMENTS')[0],
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
User::create([
@@ -265,12 +265,12 @@ class AdminUserListStatsTest extends TestCase
'name' => 'MEMBER HR DISABLED',
'mail' => 'member_hr@example.com',
'pass' => md5('password'),
'departments' => 2,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'departments' => config('constants.DEPARTMENTS')[13],
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_DISABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_DISABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// Filter by role = Admin ('1')
@@ -279,8 +279,8 @@ class AdminUserListStatsTest extends TestCase
$this->assertTrue($users->contains('msnv', 4001));
$this->assertFalse($users->contains('msnv', 4002));
// Filter by department = HR ('2')
$stats2 = $this->adminService->getUserListWithStats('2026-07', null, '1', null, '2');
// Filter by department = HR (config('constants.DEPARTMENTS')[13])
$stats2 = $this->adminService->getUserListWithStats('2026-07', null, '1', null, config('constants.DEPARTMENTS')[13]);
$users2 = collect($stats2['users']->items());
$this->assertTrue($users2->contains('msnv', 4002));
$this->assertFalse($users2->contains('msnv', 4001));
@@ -291,4 +291,483 @@ class AdminUserListStatsTest extends TestCase
$this->assertTrue($users3->contains('msnv', 4001));
$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',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$response = $this->post('/login', [
@@ -88,11 +88,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'test@example.com',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_INACTIVE, // 0
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_INACTIVE'), // 0
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$response = $this->post('/login', [
@@ -128,11 +128,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'member@example.com',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$response = $this->post('/login', [
@@ -153,11 +153,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'admin@example.com',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_ADMIN,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$response2 = $this->post('/login', [
@@ -180,11 +180,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'first@example.com',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_TRUE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]);
$response = $this->post('/login', [
@@ -205,11 +205,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'first_admin@example.com',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_ADMIN,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_TRUE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]);
$response = $this->post('/login', [
@@ -233,11 +233,11 @@ class LoginNotificationTest extends TestCase
'mail' => 'test@example.com',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
// First 5 attempts fail
+7 -7
View File
@@ -30,11 +30,11 @@ class PasswordMd5Test extends TestCase
'mail' => 'test@example.com',
'pass' => md5('old_password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_TRUE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]);
$this->userService->updatePassword($user, 'new_secret_password');
@@ -42,7 +42,7 @@ class PasswordMd5Test extends TestCase
$user->refresh();
$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
@@ -53,7 +53,7 @@ class PasswordMd5Test extends TestCase
'mail' => 'created@example.com',
'departments' => 2,
'password' => 'admin_created_pass',
'role' => User::ROLE_MEMBER,
'role' => config('constants.ROLE_MEMBER'),
];
$this->adminService->createUser($data);
@@ -62,6 +62,6 @@ class PasswordMd5Test extends TestCase
$this->assertNotNull($user);
$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',
'pass' => md5('password123'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_TRUE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_TRUE'),
]);
}
+8 -8
View File
@@ -28,11 +28,11 @@ class SendThankCardTest extends TestCase
'mail' => 'sender@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$this->receiver = User::create([
@@ -41,11 +41,11 @@ class SendThankCardTest extends TestCase
'mail' => 'receiver@example.com',
'pass' => md5('password'),
'departments' => 1,
'role' => User::ROLE_MEMBER,
'status' => User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 0,
'flag_send' => User::FLAG_SEND_ENABLED,
'first_login' => User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
}
+32 -32
View File
@@ -17,11 +17,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'admin@runsystem.net',
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_ADMIN,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_ADMIN'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$this->actingAs($user);
@@ -46,11 +46,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'usera@runsystem.net',
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$user2 = \App\Models\User::create([
@@ -59,11 +59,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'userb@runsystem.net',
'pass' => md5('password'),
'departments' => 2,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
\App\Models\Administration::create([
@@ -101,11 +101,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'usera@runsystem.net',
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$this->actingAs($user);
@@ -126,11 +126,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => "user{$char}@runsystem.net",
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
\App\Models\Administration::create([
@@ -163,11 +163,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'active@runsystem.net',
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
$inactiveUser = \App\Models\User::create([
@@ -176,11 +176,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'inactive@runsystem.net',
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_INACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_INACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
\App\Models\Administration::create([
@@ -218,11 +218,11 @@ class UserDashboardLayoutTest extends TestCase
'mail' => 'usera@runsystem.net',
'pass' => md5('password'),
'departments' => 1,
'role' => \App\Models\User::ROLE_MEMBER,
'status' => \App\Models\User::STATUS_ACTIVE,
'role' => config('constants.ROLE_MEMBER'),
'status' => config('constants.STATUS_ACTIVE'),
'card' => 10,
'flag_send' => \App\Models\User::FLAG_SEND_ENABLED,
'first_login' => \App\Models\User::FIRST_LOGIN_FALSE,
'flag_send' => config('constants.FLAG_SEND_ENABLED'),
'first_login' => config('constants.FIRST_LOGIN_FALSE'),
]);
\App\Models\Administration::create([