feature: send thankscard page
This commit is contained in:
@@ -63,3 +63,6 @@ AWS_BUCKET=
|
|||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
CHATOPS_API_URL=http://10.1.12.160/aiapi/sent-message-user
|
||||||
|
CHATOPS_API_TOKEN=
|
||||||
|
|||||||
@@ -97,8 +97,26 @@ class AdminController extends Controller
|
|||||||
public function update(UpdateUserRequest $request, string $msnv)
|
public function update(UpdateUserRequest $request, string $msnv)
|
||||||
{
|
{
|
||||||
$user = $this->adminService->getUserByMsnv($msnv);
|
$user = $this->adminService->getUserByMsnv($msnv);
|
||||||
|
$validatedData = $request->validated();
|
||||||
|
|
||||||
$this->adminService->updateUser($user, $request->validated());
|
$this->adminService->updateUser($user, $validatedData);
|
||||||
|
|
||||||
|
$currentUser = auth()->user();
|
||||||
|
if ($currentUser && $user->id === $currentUser->id && isset($validatedData['status']) && $validatedData['status'] == config('constants.STATUS_INACTIVE')) {
|
||||||
|
auth()->logout();
|
||||||
|
$request->session()->invalidate();
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
|
if ($request->expectsJson() || $request->ajax() || $request->wantsJson()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Tài khoản của bạn đã chuyển sang trạng thái nghỉ việc. Bạn đã được đăng xuất.',
|
||||||
|
'redirect' => route('login'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('login')->with('success', 'Tài khoản của bạn đã chuyển sang trạng thái nghỉ việc. Bạn đã được đăng xuất.');
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->expectsJson()) {
|
if ($request->expectsJson()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -116,8 +134,19 @@ class AdminController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(string $msnv): RedirectResponse
|
public function destroy(string $msnv): RedirectResponse
|
||||||
{
|
{
|
||||||
|
$user = $this->adminService->getUserByMsnv($msnv);
|
||||||
|
|
||||||
$this->adminService->deactivateUser($msnv);
|
$this->adminService->deactivateUser($msnv);
|
||||||
|
|
||||||
|
$currentUser = auth()->user();
|
||||||
|
if ($currentUser && $user->id === $currentUser->id) {
|
||||||
|
auth()->logout();
|
||||||
|
request()->session()->invalidate();
|
||||||
|
request()->session()->regenerateToken();
|
||||||
|
|
||||||
|
return redirect()->route('login')->with('success', 'Tài khoản của bạn đã chuyển sang trạng thái nghỉ việc. Bạn đã được đăng xuất.');
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('admin.users.index')->with('success', __('messages.user_deactivate_success'));
|
return redirect()->route('admin.users.index')->with('success', __('messages.user_deactivate_success'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,9 +61,6 @@ class UserController extends Controller
|
|||||||
return view('user.send', compact('users'));
|
return view('user.send', compact('users'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Store and execute sending thank cards to another user.
|
|
||||||
*/
|
|
||||||
public function storeThankcards(SendThankCardRequest $request): JsonResponse
|
public function storeThankcards(SendThankCardRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -76,7 +73,13 @@ class UserController extends Controller
|
|||||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
|
return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => __('messages.thank_card_send_success')]);
|
$receiverUser = \App\Models\User::where('msnv', $request->input('receiver'))->first();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('messages.thank_card_send_success'),
|
||||||
|
'receiver_email' => $receiverUser ? $receiverUser->mail : '',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -88,5 +88,20 @@ class User extends Authenticatable
|
|||||||
|
|
||||||
return asset($value);
|
return asset($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user's department name.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getDepartmentNameAttribute()
|
||||||
|
{
|
||||||
|
$deptVal = $this->departments;
|
||||||
|
if (is_numeric($deptVal)) {
|
||||||
|
$idx = ((int)$deptVal) - 1;
|
||||||
|
return config('constants.DEPARTMENTS')[$idx] ?? $deptVal;
|
||||||
|
}
|
||||||
|
return $deptVal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,5 +29,8 @@ return [
|
|||||||
'DL-SSD - Smart Solutions Delivery Division / DL-SSD - Ho Chi Minh',
|
'DL-SSD - Smart Solutions Delivery Division / DL-SSD - Ho Chi Minh',
|
||||||
'FC-HRD - Human Resources Division / FC-HRD - Ho Chi Minh',
|
'FC-HRD - Human Resources Division / FC-HRD - Ho Chi Minh',
|
||||||
'FC-RMD - Risk Management Division / FC-RMD - Ho Chi Minh'
|
'FC-RMD - Risk Management Division / FC-RMD - Ho Chi Minh'
|
||||||
]
|
],
|
||||||
|
|
||||||
|
'CHATOPS_API_URL' => env('CHATOPS_API_URL', 'http://10.1.12.160/aiapi/sent-message-user'),
|
||||||
|
'CHATOPS_API_TOKEN' => env('CHATOPS_API_TOKEN', 'ifOgk13pXIa6p2q6vvJB'),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class MockDataSeeder extends Seeder
|
|||||||
|
|
||||||
// The default password is now set to 1234@Dcba according to the design requirements
|
// The default password is now set to 1234@Dcba according to the design requirements
|
||||||
$password = md5('1234@Dcba');
|
$password = md5('1234@Dcba');
|
||||||
|
$depts = config('constants.DEPARTMENTS');
|
||||||
|
|
||||||
// 1. CREATE USERS
|
// 1. CREATE USERS
|
||||||
// Admins
|
// Admins
|
||||||
@@ -34,7 +35,7 @@ class MockDataSeeder extends Seeder
|
|||||||
'name' => 'System Admin',
|
'name' => 'System Admin',
|
||||||
'mail' => 'admin@runsystem.net',
|
'mail' => 'admin@runsystem.net',
|
||||||
'pass' => $password,
|
'pass' => $password,
|
||||||
'departments' => 1,
|
'departments' => $depts[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 0,
|
'card' => 0,
|
||||||
@@ -47,7 +48,7 @@ class MockDataSeeder extends Seeder
|
|||||||
'name' => 'Second Admin',
|
'name' => 'Second Admin',
|
||||||
'mail' => 'admin2@runsystem.net',
|
'mail' => 'admin2@runsystem.net',
|
||||||
'pass' => $password,
|
'pass' => $password,
|
||||||
'departments' => 1,
|
'departments' => $depts[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 0,
|
'card' => 0,
|
||||||
@@ -81,13 +82,13 @@ class MockDataSeeder extends Seeder
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($users as $u) {
|
foreach ($users as $idx => $u) {
|
||||||
User::create([
|
User::create([
|
||||||
'msnv' => $u['msnv'],
|
'msnv' => $u['msnv'],
|
||||||
'name' => $u['name'],
|
'name' => $u['name'],
|
||||||
'mail' => $u['mail'],
|
'mail' => $u['mail'],
|
||||||
'pass' => $password,
|
'pass' => $password,
|
||||||
'departments' => rand(1, 5),
|
'departments' => $depts[$idx % count($depts)],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => $u['card'],
|
'card' => $u['card'],
|
||||||
@@ -114,7 +115,7 @@ class MockDataSeeder extends Seeder
|
|||||||
'name' => 'Nghi Duy',
|
'name' => 'Nghi Duy',
|
||||||
'mail' => 'nghiduy@runsystem.net',
|
'mail' => 'nghiduy@runsystem.net',
|
||||||
'pass' => $password,
|
'pass' => $password,
|
||||||
'departments' => 2,
|
'departments' => $depts[1],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_INACTIVE'),
|
'status' => config('constants.STATUS_INACTIVE'),
|
||||||
'card' => 0,
|
'card' => 0,
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ return [
|
|||||||
'thank_card_send_success' => 'Đã gửi Thank card thành công!',
|
'thank_card_send_success' => 'Đã gửi Thank card thành công!',
|
||||||
|
|
||||||
'error' => [
|
'error' => [
|
||||||
'no_send_permission' => 'Bạn hiện không có quyền gửi Thank Card.',
|
'no_send_permission' => "Bạn không thể gửi Thank Card.\nCó thể bạn đã sử dụng hết số lượng Thank Card hoặc chưa được cấp quyền gửi. Vui lòng liên hệ Admin để được hỗ trợ.",
|
||||||
'not_enough_cards' => 'Số lượng card của bạn không đủ. Vui lòng liên hệ Admin.',
|
'not_enough_cards' => "Bạn không thể gửi Thank Card.\nCó thể bạn đã sử dụng hết số lượng Thank Card hoặc chưa được cấp quyền gửi. Vui lòng liên hệ Admin để được hỗ trợ.",
|
||||||
'max_send_limit_template' => 'Một tháng bạn chỉ được gửi tối đa :max card cho một người. Bạn đã gửi :sent card cho nhân viên này.',
|
'max_send_limit_template' => 'Một tháng bạn chỉ được gửi tối đa :max card cho một người. Bạn đã gửi :sent card cho nhân viên này.',
|
||||||
'receiver_invalid' => 'Người nhận không tồn tại hoặc đã nghỉ việc.',
|
'receiver_invalid' => 'Người nhận không tồn tại hoặc đã nghỉ việc.',
|
||||||
]
|
]
|
||||||
|
|||||||
Vendored
+4
File diff suppressed because one or more lines are too long
@@ -1,3 +1,5 @@
|
|||||||
|
@import "./fonts.css";
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
@section('title', $mode === 'create' ? 'Thêm User Mới' : 'Chi tiết User')
|
@section('title', $mode === 'create' ? 'Thêm User Mới' : 'Chi tiết User')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pl-1">
|
|
||||||
<div>
|
|
||||||
<h1 class="text-[28px] font-extrabold text-[#1a2b49] flex items-center gap-2 mb-1.5 tracking-tight">
|
|
||||||
{{ $mode === 'create' ? 'Thêm User Mới' : 'Chi tiết User' }}
|
|
||||||
</h1>
|
|
||||||
<p class="text-gray-500 font-medium text-[14px]">
|
|
||||||
{{ $mode === 'create' ? 'Tạo tài khoản nhân viên mới và thiết lập các thông tin cơ bản.' : 'Quản lý thông tin thẻ và theo dõi lịch sử nhận gửi của nhân viên.' }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<x-user-page :mode="$mode" :user="$user ?? null" :administrations="$administrations ?? null" :selectedMonth="$selectedMonth ?? null" :addCards="$addCards ?? null" :admins="$admins ?? null" />
|
<x-user-page :mode="$mode" :user="$user ?? null" :administrations="$administrations ?? null" :selectedMonth="$selectedMonth ?? null" :addCards="$addCards ?? null" :admins="$admins ?? null" />
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -38,13 +38,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="table-body-cell">
|
<td class="table-body-cell">
|
||||||
@php
|
@php
|
||||||
$deptVal = $u->departments;
|
$deptName = $u->department_name ?? 'Team';
|
||||||
if (is_numeric($deptVal)) {
|
|
||||||
$idx = ((int)$deptVal) - 1;
|
|
||||||
$deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
|
|
||||||
} else {
|
|
||||||
$deptName = $deptVal ?? 'Team';
|
|
||||||
}
|
|
||||||
@endphp
|
@endphp
|
||||||
<div class="max-w-[200px] truncate font-medium text-gray-600" title="{{ $deptName }}">
|
<div class="max-w-[200px] truncate font-medium text-gray-600" title="{{ $deptName }}">
|
||||||
{{ $deptName }}
|
{{ $deptName }}
|
||||||
|
|||||||
@@ -23,13 +23,7 @@
|
|||||||
<div class="w-10 h-10 rounded-full overflow-hidden shrink-0 border border-gray-200">
|
<div class="w-10 h-10 rounded-full overflow-hidden shrink-0 border border-gray-200">
|
||||||
@php
|
@php
|
||||||
$avatarUrl = Auth::user()->avatar ? Auth::user()->avatar : asset('images/avatar.png');
|
$avatarUrl = Auth::user()->avatar ? Auth::user()->avatar : asset('images/avatar.png');
|
||||||
$deptVal = Auth::user()->departments;
|
$deptName = Auth::user()->department_name ?? 'Team';
|
||||||
if (is_numeric($deptVal)) {
|
|
||||||
$idx = ((int)$deptVal) - 1;
|
|
||||||
$deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
|
|
||||||
} else {
|
|
||||||
$deptName = $deptVal ?? 'Team';
|
|
||||||
}
|
|
||||||
@endphp
|
@endphp
|
||||||
<img src="{{ $avatarUrl }}" alt="Avatar" class="w-full h-full object-cover" onerror="this.onerror=null; this.src='{{ asset('images/avatar.png') }}';">
|
<img src="{{ $avatarUrl }}" alt="Avatar" class="w-full h-full object-cover" onerror="this.onerror=null; this.src='{{ asset('images/avatar.png') }}';">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -61,6 +61,9 @@
|
|||||||
<!-- Department -->
|
<!-- Department -->
|
||||||
<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>
|
||||||
|
@php
|
||||||
|
$deptVal = old('departments', $user?->department_name);
|
||||||
|
@endphp
|
||||||
@if($mode === 'self')
|
@if($mode === 'self')
|
||||||
<div class="relative flex items-center w-full">
|
<div class="relative flex items-center w-full">
|
||||||
<span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
|
<span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center">
|
||||||
@@ -72,8 +75,8 @@
|
|||||||
class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed w-full"
|
class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed w-full"
|
||||||
style="padding-left: 48px;"
|
style="padding-left: 48px;"
|
||||||
readonly
|
readonly
|
||||||
title="{{ $user?->departments }}"
|
title="{{ $deptVal }}"
|
||||||
value="{{ $user?->departments }}">
|
value="{{ $deptVal }}">
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<div class="relative flex items-center w-full">
|
<div class="relative flex items-center w-full">
|
||||||
@@ -85,7 +88,7 @@
|
|||||||
<select name="departments" id="departments" class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20 w-full" style="padding-left: 48px;" required>
|
<select name="departments" id="departments" class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20 w-full" style="padding-left: 48px;" required>
|
||||||
<option value="">-- Chọn phòng ban --</option>
|
<option value="">-- Chọn phòng ban --</option>
|
||||||
@foreach(config('constants.DEPARTMENTS') as $dept)
|
@foreach(config('constants.DEPARTMENTS') as $dept)
|
||||||
<option value="{{ $dept }}" {{ old('departments', $user?->departments) == $dept ? 'selected' : '' }}>{{ $dept }}</option>
|
<option value="{{ $dept }}" {{ $deptVal == $dept ? 'selected' : '' }}>{{ $dept }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<form id="filterForm" action="{{ $action }}" method="GET" class="flex flex-col md:flex-row md:flex-wrap items-stretch md:items-center gap-3.5 w-full mb-6 bg-white p-5 rounded-[24px] border border-white shadow-[0_10px_30px_rgba(15,23,42,0.05)]">
|
<form id="filterForm" action="{{ $action }}" method="GET" class="flex flex-col md:flex-row md:flex-wrap items-stretch md:items-center gap-3.5 w-full mb-6 bg-white p-5 rounded-[24px] border border-white shadow-[0_10px_30px_rgba(15,23,42,0.05)]">
|
||||||
<div class="search-wrapper flex-1 relative flex items-center w-full">
|
<div class="search-wrapper flex-1 relative flex items-center w-full">
|
||||||
<span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center z-10">
|
<span class="absolute left-4 text-gray-400 pointer-events-none w-5 h-5 flex items-center justify-center z-10">
|
||||||
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20 w-full"
|
class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20 w-full"
|
||||||
style="padding-left: 44px; padding-right: 40px;" />
|
style="padding-left: 44px; padding-right: 40px;" />
|
||||||
<button type="button" id="btnClearSearch" class="{{ empty($search) ? 'hidden' : '' }} absolute right-3.5 text-gray-400 hover:text-gray-600 focus:outline-none">
|
<button type="button" id="btnClearSearch" class="{{ empty($search) ? 'hidden' : '' }} absolute right-3.5 text-gray-400 hover:text-gray-600 focus:outline-none">
|
||||||
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
field: "text",
|
field: "text",
|
||||||
direction: "asc"
|
direction: "asc"
|
||||||
},
|
},
|
||||||
placeholder: "🔍 Tìm phòng ban...",
|
placeholder: "Tìm phòng ban...",
|
||||||
render: {
|
render: {
|
||||||
item: function(data, escape) {
|
item: function(data, escape) {
|
||||||
return '<div class="item" title="' + escape(data.text) + '">' + escape(data.text) + '</div>';
|
return '<div class="item" title="' + escape(data.text) + '">' + escape(data.text) + '</div>';
|
||||||
|
|||||||
@@ -26,9 +26,9 @@
|
|||||||
<div class="bg-gradient-to-r from-blue-50/70 via-indigo-50/50 to-purple-50/30 px-6 py-6 sm:px-8 border-b border-gray-100">
|
<div class="bg-gradient-to-r from-blue-50/70 via-indigo-50/50 to-purple-50/30 px-6 py-6 sm:px-8 border-b border-gray-100">
|
||||||
<h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight flex items-center gap-2">
|
<h3 class="text-[17px] font-extrabold text-[#1a2b49] leading-tight flex items-center gap-2">
|
||||||
<span>{{ match($mode) {
|
<span>{{ match($mode) {
|
||||||
'create' => '👥 Thêm User Mới',
|
'create' => 'Thêm User Mới',
|
||||||
'delete' => '⚠️ Xác nhận nghỉ việc: ' . ($user?->msnv),
|
'delete' => 'Xác nhận nghỉ việc: ' . ($user?->name),
|
||||||
'self' => '👤 Thông tin cá nhân: ' . ($user?->msnv),
|
'self' => 'Thông tin cá nhân',
|
||||||
} }}</span>
|
} }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,23 +40,20 @@
|
|||||||
<div class="w-12 h-12 rounded-full bg-blue-50 text-[#3462f7] flex items-center justify-center font-extrabold text-lg shadow-inner flex-shrink-0">
|
<div class="w-12 h-12 rounded-full bg-blue-50 text-[#3462f7] flex items-center justify-center font-extrabold text-lg shadow-inner flex-shrink-0">
|
||||||
{{ strtoupper(substr($user->name ?? $user->msnv, 0, 2)) }}
|
{{ strtoupper(substr($user->name ?? $user->msnv, 0, 2)) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1">
|
||||||
<h4 class="font-extrabold text-[#1a2b49] text-base truncate">{{ $user->name }}</h4>
|
<h4 class="text-base font-extrabold text-[#1a2b49]">{{ $user->name }}</h4>
|
||||||
<p class="text-xs text-gray-500 font-medium truncate mb-1">{{ $user->mail }}</p>
|
<p class="text-xs text-gray-500 font-semibold mt-1 flex items-center gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<span>MSNV: {{ $user->msnv }}</span>
|
||||||
<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="w-1 h-1 bg-gray-300 rounded-full"></span>
|
||||||
{{ $user->role == config('constants.ROLE_ADMIN') ? 'Admin' : 'Member' }}
|
<span>{{ $user->mail }}</span>
|
||||||
</span>
|
</p>
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if($actionUrl)
|
@if($actionUrl)
|
||||||
<form action="{{ $actionUrl }}" method="POST" enctype="multipart/form-data" class="flex flex-col flex-1 justify-between">
|
<form action="{{ $actionUrl }}" method="POST" id="userForm" enctype="multipart/form-data" class="flex-1 flex flex-col justify-between">
|
||||||
@csrf
|
@csrf
|
||||||
@if($method !== 'POST')
|
@if($method !== 'POST')
|
||||||
@method($method)
|
@method($method)
|
||||||
@@ -66,7 +63,7 @@
|
|||||||
<div class="p-6 sm:p-8 {{ !$isCreate && !$isEdit && !$isSelf ? 'pt-0' : '' }} space-y-5">
|
<div class="p-6 sm:p-8 {{ !$isCreate && !$isEdit && !$isSelf ? 'pt-0' : '' }} space-y-5">
|
||||||
@if($isDelete && $user)
|
@if($isDelete && $user)
|
||||||
<p class="text-sm text-red-600 leading-relaxed font-semibold">
|
<p class="text-sm text-red-600 leading-relaxed font-semibold">
|
||||||
Bạn có chắc chắn muốn cho nhân viên {{ $user->msnv }} nghỉ việc không? Tài khoản này sẽ không thể đăng nhập hoặc thực hiện các giao dịch thẻ sau khi xác nhận.
|
Bạn có chắc chắn muốn cho nhân viên {{ $user->name }} (MSNV: {{ $user->msnv }}) nghỉ việc không? Tài khoản này sẽ không thể đăng nhập hoặc thực hiện các giao dịch thẻ sau khi xác nhận.
|
||||||
</p>
|
</p>
|
||||||
@else
|
@else
|
||||||
<!-- 2. User Basic Information Section -->
|
<!-- 2. User Basic Information Section -->
|
||||||
|
|||||||
@@ -344,11 +344,15 @@
|
|||||||
|
|
||||||
await reloadListsSection();
|
await reloadListsSection();
|
||||||
} else {
|
} else {
|
||||||
alert(result.message || 'Đã xảy ra lỗi. Vui lòng thử lại.');
|
if (window.showToast) {
|
||||||
|
window.showToast(result.message || 'Đã xảy ra lỗi. Vui lòng thử lại.', 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
alert('Đã xảy ra lỗi kết nối.');
|
if (window.showToast) {
|
||||||
|
window.showToast('Đã xảy ra lỗi kết nối.', 'error');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (submitBtn) submitBtn.disabled = false;
|
if (submitBtn) submitBtn.disabled = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0V10.5m-2.812 10.5h14.625c.621 0 1.125-.504 1.125-1.125V11.25c0-.621-.504-1.125-1.125-1.125H3.75c-.621 0-1.125.504-1.125 1.125v7.875c0 .621.504 1.125 1.125 1.125z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0V10.5m-2.812 10.5h14.625c.621 0 1.125-.504 1.125-1.125V11.25c0-.621-.504-1.125-1.125-1.125H3.75c-.621 0-1.125.504-1.125 1.125v7.875c0 .621.504 1.125 1.125 1.125z" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<input type="text" name="password" id="password" class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed" style="padding-left: 48px;" readonly required value="1234@Dcba">
|
<input type="text" name="password" id="password" class="form-input form-input-with-icon !rounded-xl !focus:border-[#3462f7] !focus:ring-[#3462f7]/20" style="padding-left: 48px;" required value="{{ old('password', '1234@Dcba') }}">
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[13px] text-gray-400 mt-2 block">Mật khẩu mặc định cho tất cả user mới tạo.</span>
|
@error('password')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
||||||
|
<span class="text-[13px] text-gray-400 mt-2 block">Thiết lập mật khẩu khởi tạo cho user (mặc định là 1234@Dcba).</span>
|
||||||
</div>
|
</div>
|
||||||
@elseif($mode === 'edit')
|
@elseif($mode === 'edit')
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
@elseif($mode === 'self')
|
@elseif($mode === 'self')
|
||||||
<div class="border-t border-gray-100 pt-5 mt-5">
|
<div class="border-t border-gray-100 pt-5 mt-5">
|
||||||
<h4 class="text-sm font-bold text-[#1a2b49] mb-4 flex items-center gap-2">
|
<h4 class="text-sm font-bold text-[#1a2b49] mb-4 flex items-center gap-2">
|
||||||
🔑 Bảo mật & Đổi mật khẩu
|
Bảo mật & Đổi mật khẩu
|
||||||
</h4>
|
</h4>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<!-- Current Password -->
|
<!-- Current Password -->
|
||||||
|
|||||||
@@ -141,15 +141,48 @@
|
|||||||
closeBtn.addEventListener('click', closeSidebar);
|
closeBtn.addEventListener('click', closeSidebar);
|
||||||
overlay.addEventListener('click', closeSidebar);
|
overlay.addEventListener('click', closeSidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client-side flash message support
|
||||||
|
const flashSuccess = localStorage.getItem('flash_success');
|
||||||
|
if (flashSuccess) {
|
||||||
|
window.showToast(flashSuccess, 'success');
|
||||||
|
localStorage.removeItem('flash_success');
|
||||||
|
}
|
||||||
|
const flashError = localStorage.getItem('flash_error');
|
||||||
|
if (flashError) {
|
||||||
|
window.showToast(flashError, 'error');
|
||||||
|
localStorage.removeItem('flash_error');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.showToast = function(message) {
|
window.showToast = function(message, type = 'success') {
|
||||||
const toast = document.getElementById('global-toast');
|
const toast = document.getElementById('global-toast');
|
||||||
if (!toast) return;
|
if (!toast) return;
|
||||||
|
|
||||||
const msgEl = toast.querySelector('.text-sm');
|
const msgEl = toast.querySelector('.text-sm');
|
||||||
if (msgEl) msgEl.textContent = message;
|
if (msgEl) msgEl.textContent = message;
|
||||||
|
|
||||||
|
// Dynamically set theme styling and icon
|
||||||
|
const iconContainer = toast.querySelector('.shrink-0');
|
||||||
|
if (iconContainer) {
|
||||||
|
iconContainer.className = 'inline-flex items-center justify-center shrink-0 w-9 h-9 rounded-lg';
|
||||||
|
let iconHtml = '';
|
||||||
|
if (type === 'success') {
|
||||||
|
iconContainer.classList.add('bg-emerald-50', 'text-emerald-500');
|
||||||
|
iconHtml = '<svg class="w-5.5 h-5.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>';
|
||||||
|
} else if (type === 'error') {
|
||||||
|
iconContainer.classList.add('bg-red-50', 'text-red-500');
|
||||||
|
iconHtml = '<svg class="w-5.5 h-5.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>';
|
||||||
|
} else if (type === 'warning') {
|
||||||
|
iconContainer.classList.add('bg-amber-50', 'text-amber-500');
|
||||||
|
iconHtml = '<svg class="w-5.5 h-5.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><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" /></svg>';
|
||||||
|
} else {
|
||||||
|
iconContainer.classList.add('bg-blue-50', 'text-blue-500');
|
||||||
|
iconHtml = '<svg class="w-5.5 h-5.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>';
|
||||||
|
}
|
||||||
|
iconContainer.innerHTML = iconHtml;
|
||||||
|
}
|
||||||
|
|
||||||
toast.classList.remove('hidden');
|
toast.classList.remove('hidden');
|
||||||
toast.classList.add('flex');
|
toast.classList.add('flex');
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
<title>GMO-Z.com RUNSYSTEM - {{ __('auth.login') }}</title>
|
<title>GMO-Z.com RUNSYSTEM - {{ __('auth.login') }}</title>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@700&display=swap" rel="stylesheet">
|
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen flex items-center justify-center font-sans antialiased overflow-hidden relative bg-[#f0f4f9] p-4 sm:p-6 lg:p-10">
|
<body class="min-h-screen flex items-center justify-center font-sans antialiased overflow-hidden relative bg-[#f0f4f9] p-4 sm:p-6 lg:p-10">
|
||||||
|
|||||||
@@ -3,13 +3,7 @@
|
|||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
@foreach($rankers as $item)
|
@foreach($rankers as $item)
|
||||||
@php
|
@php
|
||||||
$deptVal = $item->departments;
|
$teamName = $item->department_name ?? 'Team';
|
||||||
if (is_numeric($deptVal)) {
|
|
||||||
$idx = ((int)$deptVal) - 1;
|
|
||||||
$teamName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
|
|
||||||
} else {
|
|
||||||
$teamName = $deptVal ?? 'Team';
|
|
||||||
}
|
|
||||||
@endphp
|
@endphp
|
||||||
<x-ranking-item
|
<x-ranking-item
|
||||||
rank="{{ $item->rank }}"
|
rank="{{ $item->rank }}"
|
||||||
@@ -33,13 +27,7 @@
|
|||||||
<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
|
@php
|
||||||
$currDeptVal = $currentUserRankItem->departments;
|
$currTeamName = $currentUserRankItem->department_name ?? 'Team';
|
||||||
if (is_numeric($currDeptVal)) {
|
|
||||||
$idx = ((int)$currDeptVal) - 1;
|
|
||||||
$currTeamName = config('constants.DEPARTMENTS')[$idx] ?? 'Team';
|
|
||||||
} else {
|
|
||||||
$currTeamName = $currDeptVal ?? 'Team';
|
|
||||||
}
|
|
||||||
@endphp
|
@endphp
|
||||||
<x-ranking-item
|
<x-ranking-item
|
||||||
rank="{{ $currentUserRankItem->rank > 0 ? $currentUserRankItem->rank : '-' }}"
|
rank="{{ $currentUserRankItem->rank > 0 ? $currentUserRankItem->rank : '-' }}"
|
||||||
|
|||||||
+248
-120
@@ -2,185 +2,313 @@
|
|||||||
@section('title', 'Gửi Thank Card')
|
@section('title', 'Gửi Thank Card')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="max-w-[640px] mx-auto w-full pb-10">
|
@php
|
||||||
<div class="bg-white rounded-[24px] shadow-[0_10px_30px_rgba(15,23,42,0.05)] border border-white overflow-hidden">
|
$tpls = [
|
||||||
|
1=>['icon'=>'💌','name'=>'Thư cảm ơn', 'thumb'=>'from-[#FFF9EC] to-[#FFF5DD]','thumbAccent'=>'#FFD066'],
|
||||||
|
2=>['icon'=>'🌸','name'=>'Sakura', 'thumb'=>'from-[#FFF5F9] to-[#FFECF4]','thumbAccent'=>'#F9A8D4'],
|
||||||
|
3=>['icon'=>'⭐','name'=>'Appreciation','thumb'=>'from-[#EFF8FF] to-[#DBEAFE]','thumbAccent'=>'#93C5FD'],
|
||||||
|
4=>['icon'=>'🎉','name'=>'Celebration', 'thumb'=>'from-[#F5F0FF] to-[#EDE9FE]','thumbAccent'=>'#C4B5FD'],
|
||||||
|
];
|
||||||
|
@endphp
|
||||||
|
|
||||||
<div class="bg-gradient-to-r from-[#eff4ff] to-[#faeffa] px-8 py-8 flex flex-col relative overflow-hidden">
|
<div class="max-w-[760px] mx-auto w-full pb-12 px-4 lg:px-0">
|
||||||
<div class="absolute -top-10 -right-10 w-32 h-32 bg-white/30 rounded-full blur-xl"></div>
|
|
||||||
<div class="absolute -bottom-10 -left-10 w-24 h-24 bg-white/20 rounded-full blur-lg"></div>
|
|
||||||
|
|
||||||
<div class="relative z-10 flex items-center gap-3">
|
{{-- Header --}}
|
||||||
<h3 class="text-[22px] font-extrabold text-[#1a2b49] tracking-tight">Gửi Thank Card</h3>
|
<div class="relative bg-gradient-to-r from-[#eff4ff] to-[#faeffa] rounded-[24px] p-7 md:p-10 mb-7 overflow-hidden border border-white shadow-[0_10px_30px_rgba(15,23,42,0.05)]">
|
||||||
<span class="text-xl animate-pulse">💌</span>
|
<div class="absolute -top-10 -right-10 w-44 h-44 bg-white/25 rounded-full blur-2xl"></div>
|
||||||
|
<div class="absolute -bottom-8 -left-8 w-32 h-32 bg-white/20 rounded-full blur-xl"></div>
|
||||||
|
<div class="relative z-10 flex items-center gap-4">
|
||||||
|
<div class="w-14 h-14 rounded-2xl bg-white/80 shadow-sm flex items-center justify-center text-3xl shrink-0">💌</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-[26px] md:text-[30px] font-extrabold text-[#1a2b49] tracking-tight leading-tight">Gửi Thank Card</h1>
|
||||||
|
<p class="text-[14px] text-gray-500 font-medium mt-1 max-w-[380px] leading-relaxed">Gửi lời cảm ơn chân thành đến đồng nghiệp và lan tỏa văn hóa ghi nhận trong tổ chức.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-[13px] text-gray-500 font-medium mt-1.5 relative z-10">
|
|
||||||
Gửi gắm lời cảm ơn chân thành đến người đồng nghiệp tuyệt vời của bạn!
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="sendCardForm">
|
<form id="sendCardForm">
|
||||||
@csrf
|
@csrf
|
||||||
|
<input type="hidden" name="template_id" id="template_id" value="1">
|
||||||
|
|
||||||
<div class="p-8 space-y-6">
|
{{-- Template Gallery --}}
|
||||||
<div class="flex flex-col gap-2">
|
<div class="bg-white rounded-[20px] shadow-[0_4px_20px_rgba(15,23,42,0.06)] border border-[#f0f4fa] p-6 mb-5">
|
||||||
<label class="text-[14px] font-bold text-text-dark" for="receiver">
|
<p class="text-[13px] font-bold text-gray-400 uppercase tracking-widest mb-4">Chọn mẫu thiệp <span class="text-[#3462f7]">*</span></p>
|
||||||
Chọn người nhận <span class="text-red-500">*</span>
|
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
</label>
|
@foreach($tpls as $id => $t)
|
||||||
<select name="receiver" id="receiver" class="form-input rounded-xl border-[#d9dfe7]" required>
|
<button type="button" onclick="selectTpl(this,{{$id}})"
|
||||||
<option value="" disabled selected>-- Chọn đồng nghiệp --</option>
|
data-tpl="{{$id}}"
|
||||||
|
class="tpl-btn relative group flex flex-col items-center gap-2 p-4 rounded-2xl border-2 transition-all duration-200 bg-gradient-to-br {{$t['thumb']}} cursor-pointer
|
||||||
|
{{$loop->first ? 'border-[#3462f7] ring-2 ring-[#3462f7]/20 shadow-md' : 'border-[#e8edf4] hover:border-[#3462f7]/40 hover:shadow-md'}}">
|
||||||
|
<span class="tpl-check absolute top-2 right-2 w-5 h-5 rounded-full bg-[#3462f7] flex items-center justify-center {{$loop->first ? 'opacity-100':'opacity-0'}} transition-opacity duration-200">
|
||||||
|
<svg class="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||||
|
</span>
|
||||||
|
<span class="text-3xl group-hover:scale-110 transition-transform duration-200 select-none">{{$t['icon']}}</span>
|
||||||
|
<span class="text-[12px] font-bold text-[#1a2b49] text-center leading-tight">{{$t['name']}}</span>
|
||||||
|
</button>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- e-Card (form inside the card) --}}
|
||||||
|
<div id="eCard" class="relative rounded-[24px] overflow-hidden shadow-[0_20px_60px_rgba(15,23,42,0.12)] border-2 mb-5 transition-all duration-500"
|
||||||
|
style="background:linear-gradient(145deg,#FFF9EC,#FFFDF7);border-color:#FFE4A0;">
|
||||||
|
|
||||||
|
{{-- Decorative SVG top --}}
|
||||||
|
<div id="cardDeco" class="relative overflow-hidden" style="height:140px;background:linear-gradient(135deg,#FFF5CC,#FFE8A0);">
|
||||||
|
{{-- blob 1 --}}
|
||||||
|
<div class="absolute -top-8 -left-8 w-36 h-36 rounded-full opacity-40 blur-2xl" id="blob1" style="background:#FFD066;"></div>
|
||||||
|
<div class="absolute -bottom-6 -right-6 w-28 h-28 rounded-full opacity-30 blur-xl" id="blob2" style="background:#FFAD00;"></div>
|
||||||
|
{{-- SVG decoration --}}
|
||||||
|
<div id="decoSvg" class="absolute inset-0 flex items-center justify-center select-none pointer-events-none">
|
||||||
|
{{-- Template 1 default: envelope --}}
|
||||||
|
<div class="flex flex-col items-center gap-1 z-10">
|
||||||
|
<span id="cardBigIcon" class="text-[72px] leading-none drop-shadow-lg" style="filter:drop-shadow(0 4px 16px rgba(0,0,0,0.10))">💌</span>
|
||||||
|
<span id="cardLabel" class="text-[13px] font-bold uppercase tracking-[0.2em] mt-1" style="color:#B8860B;">Thank Card</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{-- wavy bottom --}}
|
||||||
|
<svg class="absolute bottom-0 left-0 w-full" viewBox="0 0 760 30" preserveAspectRatio="none" style="height:30px;">
|
||||||
|
<path id="wavePath" d="M0,20 Q190,0 380,20 Q570,40 760,20 L760,30 L0,30 Z" fill="white" opacity="0.8"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Card body: form fields ON the card --}}
|
||||||
|
<div class="px-8 py-7 bg-white/0">
|
||||||
|
|
||||||
|
{{-- To: Người nhận --}}
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-[11px] font-bold uppercase tracking-widest mb-2" id="labelTo" style="color:#B8860B;">✉ Gửi đến</label>
|
||||||
|
<select name="receiver" id="receiver" class="form-input rounded-xl border-[#e8edf4] bg-white/90" required>
|
||||||
|
<option value="" disabled selected></option>
|
||||||
@foreach($users as $u)
|
@foreach($users as $u)
|
||||||
<option value="{{ $u->msnv }}">{{ $u->name }} - {{ $u->msnv }} ({{ $u->mail }})</option>
|
@php $dept=$u->department_name??'Team'; @endphp
|
||||||
|
<option value="{{$u->msnv}}">{{$u->name}} - {{$u->msnv}} ({{$u->mail}}) - {{$dept}}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
{{-- Số lượng --}}
|
||||||
<label class="text-[14px] font-bold text-text-dark" for="amount">
|
<div class="mb-6">
|
||||||
Số lượng thẻ <span class="text-red-500">*</span>
|
<label class="block text-[11px] font-bold uppercase tracking-widest mb-2" id="labelQty" style="color:#B8860B;">🎴 Số lượng thẻ</label>
|
||||||
</label>
|
<div class="flex items-center gap-3">
|
||||||
<select name="amount" id="amount" class="form-input rounded-xl border-[#d9dfe7]" required>
|
<select name="amount" id="amount" class="form-input rounded-xl border-[#e8edf4] bg-white/90 !w-auto !px-5" required>
|
||||||
<option value="1">1 Thẻ</option>
|
<option value="1">1 Thẻ</option>
|
||||||
<option value="2">2 Thẻ</option>
|
<option value="2">2 Thẻ</option>
|
||||||
<option value="3">3 Thẻ</option>
|
<option value="3">3 Thẻ</option>
|
||||||
<option value="4">4 Thẻ</option>
|
<option value="4">4 Thẻ</option>
|
||||||
<option value="{{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }}">{{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} Thẻ (Tối đa)</option>
|
<option value="{{App\Models\Administration::MAX_SEND_CARD_PER_MONTH}}">{{App\Models\Administration::MAX_SEND_CARD_PER_MONTH}} Thẻ (Tối đa)</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="mt-1 flex items-start gap-2 bg-[#eff4ff] p-3.5 rounded-xl border border-blue-50">
|
<span class="text-[12px] text-gray-400 font-medium">/ tháng</span>
|
||||||
<svg class="w-5 h-5 text-[#3462f7] shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
<p class="text-[12px] text-gray-600 font-medium leading-relaxed">
|
|
||||||
Lưu ý: Chỉ được gửi tối đa {{ \App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} thẻ cho cùng 1 người trong 1 tháng để đảm bảo tính công bằng.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 pt-2">
|
{{-- Confirm checkbox --}}
|
||||||
<div class="flex items-center gap-3 bg-gray-50/50 p-4 rounded-xl border border-[#d9dfe7]/60 hover:bg-gray-50 transition-colors">
|
<label class="flex items-start gap-3 p-4 rounded-2xl border border-[#e8edf4] bg-white/70 hover:bg-white/90 transition-colors cursor-pointer mb-2">
|
||||||
<input type="checkbox" name="confirm_written" id="confirm_written" value="1" class="w-5 h-5 text-[#3462f7] border-[#d9dfe7] rounded-md focus:ring-[#3462f7]/20 transition-all cursor-pointer" required>
|
<input type="checkbox" name="confirm_written" id="confirm_written" value="1"
|
||||||
<label for="confirm_written" class="text-[13px] font-medium text-gray-700 cursor-pointer select-none">
|
class="w-4 h-4 mt-0.5 text-[#3462f7] border-[#d9dfe7] rounded focus:ring-[#3462f7]/20 cursor-pointer shrink-0" required>
|
||||||
Xác nhận đã viết card <span class="text-red-500">*</span>
|
<span class="text-[13px] font-medium text-gray-600 leading-relaxed select-none">
|
||||||
|
Tôi xác nhận đã viết nội dung trên ThankCard và sẵn sàng gửi đến người nhận. <span class="text-[#3462f7] font-bold">*</span>
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
{{-- Card footer line --}}
|
||||||
|
<div class="mt-6 pt-4 border-t border-dashed" id="cardFooterLine" style="border-color:#FFE4A0;">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-[11px] text-gray-400 font-medium">Từ: <span class="font-bold text-gray-500">{{ Auth::user()->name ?? 'Bạn' }}</span></span>
|
||||||
|
<span id="cardFooterBadge" class="text-[10px] font-bold px-2.5 py-1 rounded-full" style="background:rgba(255,176,0,0.15);color:#B8860B;">Thư cảm ơn</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-8 py-5 border-t border-[#d9dfe7]/50 bg-gray-50/50 flex flex-col sm:flex-row justify-end gap-3">
|
{{-- Info note --}}
|
||||||
<a href="{{ route('user.dashboard') }}" class="btn-secondary rounded-xl font-bold">Hủy bỏ</a>
|
<div class="flex items-start gap-3 bg-gradient-to-r from-[#eff4ff] to-[#f5f0ff] p-4 rounded-2xl border border-[#dce7ff] mb-5">
|
||||||
<button type="button" id="btnSubmitSend" class="btn-primary rounded-xl font-bold !bg-[#3462f7] hover:!bg-blue-700 shadow-md shadow-blue-500/10">
|
<div class="w-8 h-8 rounded-xl bg-white flex items-center justify-center shrink-0 shadow-sm">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-4 h-4 mr-2">
|
<svg class="w-4 h-4 text-[#3462f7]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" />
|
</div>
|
||||||
</svg>
|
<div>
|
||||||
Gửi Card
|
<p class="text-[12px] font-bold text-[#3462f7] mb-0.5">Lưu ý</p>
|
||||||
|
<p class="text-[13px] text-gray-600 font-medium leading-relaxed">Chỉ được gửi tối đa <strong>{{ App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} thẻ</strong> cho cùng một người trong tháng để đảm bảo tính công bằng.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Actions --}}
|
||||||
|
<div class="flex flex-col sm:flex-row justify-end gap-3">
|
||||||
|
<a href="{{ route('user.dashboard') }}" class="btn-secondary rounded-xl font-bold flex items-center justify-center gap-2 order-2 sm:order-1">
|
||||||
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>Hủy bỏ
|
||||||
|
</a>
|
||||||
|
<button type="button" id="btnSubmitSend" class="btn-primary rounded-xl font-bold !bg-[#3462f7] hover:!bg-blue-700 shadow-md shadow-blue-500/15 flex items-center justify-center gap-2 order-1 sm:order-2">
|
||||||
|
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"/></svg>Gửi Card
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<x-modal id="customModal" title="Thông báo" maxWidth="md">
|
<x-modal id="customModal" title="Thông báo" maxWidth="md">
|
||||||
<p id="modalMessage" class="text-sm text-gray-600 leading-relaxed"></p>
|
<p id="modalMessage" class="text-sm text-gray-600 leading-relaxed"></p>
|
||||||
|
|
||||||
<x-slot name="footer">
|
<x-slot name="footer">
|
||||||
<div id="modalActions" class="w-full flex flex-col sm:flex-row-reverse gap-3">
|
<div id="modalActions" class="w-full flex flex-col sm:flex-row-reverse gap-3"></div>
|
||||||
</div>
|
|
||||||
</x-slot>
|
</x-slot>
|
||||||
</x-modal>
|
</x-modal>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
|
<script src="{{ asset('js/jquery.min.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
new TomSelect("#receiver", {
|
|
||||||
create: false,
|
// ── Theme data ───────────────────────────────────────────────────
|
||||||
sortField: {
|
const T = {
|
||||||
field: "text",
|
1:{icon:'💌',name:'Thư cảm ơn', label:'THANK CARD',
|
||||||
direction: "asc"
|
deco:'linear-gradient(135deg,#FFF5CC,#FFE8A0)',
|
||||||
},
|
card:'linear-gradient(145deg,#FFF9EC,#FFFDF7)',
|
||||||
placeholder: "🔍 Nhập tên, mã NV hoặc email...",
|
border:'#FFE4A0', blob1:'#FFD066', blob2:'#FFAD00',
|
||||||
render: {
|
accent:'#B8860B', badgeBg:'rgba(255,176,0,0.15)', wave:'white',
|
||||||
no_results: function(data, escape) {
|
footerBorder:'#FFE4A0'},
|
||||||
return '<div class="no-results p-3 text-gray-500">Không tìm thấy nhân viên nào</div>';
|
2:{icon:'🌸',name:'Sakura', label:'SAKURA CARD',
|
||||||
|
deco:'linear-gradient(135deg,#FFE8F4,#FFD6EC)',
|
||||||
|
card:'linear-gradient(145deg,#FFF8FB,#FFF3F8)',
|
||||||
|
border:'#FFBCD9', blob1:'#F9A8D4', blob2:'#F472B6',
|
||||||
|
accent:'#be185d', badgeBg:'rgba(244,114,182,0.12)', wave:'white',
|
||||||
|
footerBorder:'#FFBCD9'},
|
||||||
|
3:{icon:'⭐',name:'Appreciation',label:'APPRECIATION',
|
||||||
|
deco:'linear-gradient(135deg,#DBEAFE,#BFDBFE)',
|
||||||
|
card:'linear-gradient(145deg,#F0F8FF,#EAF3FF)',
|
||||||
|
border:'#BFDBFE', blob1:'#93C5FD', blob2:'#60A5FA',
|
||||||
|
accent:'#1d4ed8', badgeBg:'rgba(96,165,250,0.12)', wave:'white',
|
||||||
|
footerBorder:'#BFDBFE'},
|
||||||
|
4:{icon:'🎉',name:'Celebration', label:'CELEBRATION',
|
||||||
|
deco:'linear-gradient(135deg,#EDE9FE,#DDD6FE)',
|
||||||
|
card:'linear-gradient(145deg,#F8F5FF,#F3EFFF)',
|
||||||
|
border:'#DDD6FE', blob1:'#C4B5FD', blob2:'#A78BFA',
|
||||||
|
accent:'#6d28d9', badgeBg:'rgba(167,139,250,0.12)', wave:'white',
|
||||||
|
footerBorder:'#DDD6FE'},
|
||||||
|
};
|
||||||
|
|
||||||
|
function applyTheme(id) {
|
||||||
|
const t = T[id]; if (!t) return;
|
||||||
|
const $card = document.getElementById('eCard');
|
||||||
|
const $deco = document.getElementById('cardDeco');
|
||||||
|
const $b1 = document.getElementById('blob1');
|
||||||
|
const $b2 = document.getElementById('blob2');
|
||||||
|
const $bigIcon = document.getElementById('cardBigIcon');
|
||||||
|
const $label = document.getElementById('cardLabel');
|
||||||
|
const $wave = document.getElementById('wavePath');
|
||||||
|
const $lTo = document.getElementById('labelTo');
|
||||||
|
const $lQty = document.getElementById('labelQty');
|
||||||
|
const $badge = document.getElementById('cardFooterBadge');
|
||||||
|
const $footerL = document.getElementById('cardFooterLine');
|
||||||
|
|
||||||
|
$card.style.background = t.card;
|
||||||
|
$card.style.borderColor = t.border;
|
||||||
|
$deco.style.background = t.deco;
|
||||||
|
$b1.style.background = t.blob1;
|
||||||
|
$b2.style.background = t.blob2;
|
||||||
|
$bigIcon.textContent = t.icon;
|
||||||
|
$label.textContent = t.label;
|
||||||
|
$label.style.color = t.accent;
|
||||||
|
$wave.setAttribute('fill', t.wave);
|
||||||
|
$lTo.style.color = t.accent;
|
||||||
|
$lQty.style.color = t.accent;
|
||||||
|
$badge.textContent = t.name;
|
||||||
|
$badge.style.background = t.badgeBg;
|
||||||
|
$badge.style.color = t.accent;
|
||||||
|
$footerL.style.borderColor = t.footerBorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Template picker ──────────────────────────────────────────────
|
||||||
|
window.selectTpl = function(el, id) {
|
||||||
|
document.querySelectorAll('.tpl-btn').forEach(b => {
|
||||||
|
b.classList.remove('border-[#3462f7]','ring-2','ring-[#3462f7]/20','shadow-md');
|
||||||
|
b.classList.add('border-[#e8edf4]');
|
||||||
|
b.querySelector('.tpl-check').classList.replace('opacity-100','opacity-0');
|
||||||
|
});
|
||||||
|
el.classList.remove('border-[#e8edf4]');
|
||||||
|
el.classList.add('border-[#3462f7]','ring-2','ring-[#3462f7]/20','shadow-md');
|
||||||
|
el.querySelector('.tpl-check').classList.replace('opacity-0','opacity-100');
|
||||||
|
document.getElementById('template_id').value = id;
|
||||||
|
applyTheme(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
applyTheme(1);
|
||||||
|
|
||||||
|
// ── TomSelect ────────────────────────────────────────────────────
|
||||||
|
new TomSelect('#receiver', {
|
||||||
|
create:false, searchField:['text','value'], maxOptions:10,
|
||||||
|
sortField:{field:'text',direction:'asc'},
|
||||||
|
placeholder:'Nhập tên hoặc mã nhân viên...',
|
||||||
|
render:{
|
||||||
|
option:function(d,e){
|
||||||
|
const p=d.text.split(' - '),n=p[0]||'',i=p.slice(1).join(' · ')||'';
|
||||||
|
return`<div class="px-3 py-2.5 flex flex-col gap-0.5 hover:bg-[#eff4ff] cursor-pointer transition-colors">
|
||||||
|
<span class="font-bold text-[14px] text-[#1a2b49]">${e(n)}</span>
|
||||||
|
<span class="text-[11px] text-gray-400">${e(i)}</span></div>`;
|
||||||
|
},
|
||||||
|
item:function(d,e){
|
||||||
|
const n=d.text.split(' - ')[0]||'';
|
||||||
|
return`<div class="font-semibold text-[14px] text-[#1a2b49]">${e(n)} <span class="text-gray-400 font-normal text-[12px]">(${e(d.value)})</span></div>`;
|
||||||
|
},
|
||||||
|
no_results:()=>'<div class="p-3 text-[13px] text-gray-400 text-center">Không tìm thấy nhân viên nào</div>'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const btnSubmit = document.getElementById('btnSubmitSend');
|
// ── Modal ────────────────────────────────────────────────────────
|
||||||
const form = document.getElementById('sendCardForm');
|
const $mTitle = document.getElementById('modal-title-customModal');
|
||||||
|
const $mMsg = document.getElementById('modalMessage');
|
||||||
|
const $mAct = document.getElementById('modalActions');
|
||||||
|
|
||||||
const modalTitle = document.getElementById('modal-title-customModal');
|
const showModal = (title, msg, confirm, cb) => {
|
||||||
const modalMessage = document.getElementById('modalMessage');
|
$mTitle.textContent = title;
|
||||||
const modalActions = document.getElementById('modalActions');
|
$mMsg.innerHTML = msg.replace(/\n/g,'<br>');
|
||||||
|
if (confirm) {
|
||||||
const showModal = (title, message, isConfirm, confirmCallback) => {
|
$mTitle.className = 'text-lg font-bold text-primary';
|
||||||
modalTitle.textContent = title;
|
$mAct.innerHTML = `<button id="btnOk" class="w-full sm:w-auto btn-primary !px-6 rounded-xl font-bold">Xác nhận gửi</button>
|
||||||
modalMessage.innerHTML = message;
|
<button id="btnCancel" class="w-full sm:w-auto btn-secondary !px-6 rounded-xl font-bold">Hủy</button>`;
|
||||||
|
document.getElementById('btnCancel').onclick = () => window.closeModal('customModal');
|
||||||
if(isConfirm) {
|
document.getElementById('btnOk').addEventListener('click', function(){
|
||||||
modalTitle.className = "text-lg font-bold text-primary";
|
|
||||||
modalActions.innerHTML = `
|
|
||||||
<button id="btnConfirmOk" class="w-full sm:w-auto btn-primary !px-6 shadow-sm rounded-xl font-bold">Xác nhận gửi</button>
|
|
||||||
<button id="btnCancel" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm rounded-xl font-bold">Hủy</button>
|
|
||||||
`;
|
|
||||||
document.getElementById('btnCancel').addEventListener('click', () => window.closeModal('customModal'));
|
|
||||||
document.getElementById('btnConfirmOk').addEventListener('click', function() {
|
|
||||||
this.disabled=true;
|
this.disabled=true;
|
||||||
this.innerHTML='<span class="flex items-center gap-2"><svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Đang xử lý...</span>';
|
this.innerHTML='<span class="flex items-center gap-2"><svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Đang xử lý...</span>';
|
||||||
confirmCallback();
|
cb();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
modalTitle.className = "text-lg font-bold text-red-600";
|
$mTitle.className = 'text-lg font-bold text-[#1a2b49]';
|
||||||
modalActions.innerHTML = `
|
$mAct.innerHTML = `<button id="btnClose" class="w-full sm:w-auto btn-secondary !px-6 rounded-xl font-bold">Đóng</button>`;
|
||||||
<button id="btnOkOnly" class="w-full sm:w-auto btn-secondary !px-6 shadow-sm rounded-xl font-bold">Đóng</button>
|
document.getElementById('btnClose').onclick = () => window.closeModal('customModal');
|
||||||
`;
|
|
||||||
document.getElementById('btnOkOnly').addEventListener('click', () => window.closeModal('customModal'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.openModal('customModal');
|
window.openModal('customModal');
|
||||||
};
|
};
|
||||||
|
|
||||||
btnSubmit.addEventListener('click', function() {
|
// ── Submit ───────────────────────────────────────────────────────
|
||||||
if(!form.checkValidity()) {
|
document.getElementById('btnSubmitSend').addEventListener('click', function(){
|
||||||
form.reportValidity();
|
const form = document.getElementById('sendCardForm');
|
||||||
return;
|
if (!form.checkValidity()) { form.reportValidity(); return; }
|
||||||
}
|
const msnv = document.getElementById('receiver').value;
|
||||||
|
|
||||||
const receiver = document.getElementById('receiver').value;
|
|
||||||
const amount = document.getElementById('amount').value;
|
const amount = document.getElementById('amount').value;
|
||||||
|
showModal('Xác nhận gửi Thank Card',
|
||||||
showModal(
|
'Đừng quên hoàn thành Thank Card của bạn nhé! Chúng mình sẽ gửi thông báo đến người nhận để họ đến khu vực nhận thư.',
|
||||||
'Xác nhận gửi Thank Card',
|
true, () => {
|
||||||
`Bạn có chắc chắn muốn gửi <b>${amount} thẻ</b> cho nhân viên <span class="font-bold text-primary">${receiver}</span> không? <br><br><span class="text-xs text-text-light">Hành động này không thể hoàn tác, tin nhắn CO sẽ được gửi đi!</span>`,
|
$.ajax({
|
||||||
true,
|
url:'{{ route("user.store_send") }}', method:'POST', contentType:'application/json',
|
||||||
() => {
|
headers:{'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')},
|
||||||
fetch('{{ route('user.store_send') }}', {
|
data:JSON.stringify({receiver:msnv,amount:amount}),
|
||||||
method: 'POST',
|
success:function(res){
|
||||||
headers: {
|
if(res.success){
|
||||||
'Content-Type': 'application/json',
|
var email=res.receiver_email; email="antv@runsystem.net";
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
$.ajax({
|
||||||
},
|
url:'{{ config("constants.CHATOPS_API_URL") }}', method:'POST', contentType:'application/json',
|
||||||
body: JSON.stringify({
|
data:JSON.stringify({message:"💌 Bạn nhận được một Thank Card mới!\nNhanh chân đến khu vực nhận thư để nhận ngay nhé!",userEmail:email,token:'{{ config("constants.CHATOPS_API_TOKEN") }}'}),
|
||||||
receiver: receiver,
|
complete:function(){localStorage.setItem('flash_success',res.message);window.location.href='{{ route("user.dashboard") }}';}
|
||||||
amount: amount
|
});
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json().then(data => ({status: response.status, body: data})))
|
|
||||||
.then(res => {
|
|
||||||
if (res.status === 200 && res.body.success) {
|
|
||||||
alert(res.body.message);
|
|
||||||
window.location.href = '{{ route('user.dashboard') }}';
|
|
||||||
} else {
|
} else {
|
||||||
window.closeModal('customModal');
|
window.closeModal('customModal');
|
||||||
setTimeout(() => {
|
setTimeout(()=>showModal('Lỗi Giao Dịch',res.message||'Có lỗi xảy ra.',false,null),350);
|
||||||
showModal('Lỗi Giao Dịch', res.body.message || 'Có lỗi xảy ra, vui lòng thử lại.', false, null);
|
|
||||||
}, 350);
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.catch(err => {
|
error:function(xhr){
|
||||||
window.closeModal('customModal');
|
window.closeModal('customModal');
|
||||||
setTimeout(() => {
|
setTimeout(()=>showModal('Lỗi Hệ Thống',xhr.responseJSON?.message||'Không thể kết nối đến máy chủ.',false,null),350);
|
||||||
showModal('Lỗi Hệ Thống', 'Không thể kết nối đến máy chủ.', false, null);
|
}
|
||||||
}, 350);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Regular → Executable
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
@@ -29,7 +29,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'User A',
|
'name' => 'User A',
|
||||||
'mail' => 'usera@example.com',
|
'mail' => 'usera@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -42,7 +42,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'User B',
|
'name' => 'User B',
|
||||||
'mail' => 'userb@example.com',
|
'mail' => 'userb@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -109,7 +109,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'User A',
|
'name' => 'User A',
|
||||||
'mail' => 'usera@example.com',
|
'mail' => 'usera@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -166,7 +166,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'DEV ALPHA',
|
'name' => 'DEV ALPHA',
|
||||||
'mail' => 'alpha@example.com',
|
'mail' => 'alpha@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -179,7 +179,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'DEV BETA',
|
'name' => 'DEV BETA',
|
||||||
'mail' => 'beta@example.com',
|
'mail' => 'beta@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -210,7 +210,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'DEV ACTIVE',
|
'name' => 'DEV ACTIVE',
|
||||||
'mail' => 'active@example.com',
|
'mail' => 'active@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -223,7 +223,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'DEV INACTIVE',
|
'name' => 'DEV INACTIVE',
|
||||||
'mail' => 'inactive@example.com',
|
'mail' => 'inactive@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_INACTIVE'),
|
'status' => config('constants.STATUS_INACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -299,7 +299,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -312,7 +312,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 5,
|
'card' => 5,
|
||||||
@@ -521,7 +521,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -534,7 +534,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10, // currently has 10 cards
|
'card' => 10, // currently has 10 cards
|
||||||
@@ -573,7 +573,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -586,7 +586,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -621,7 +621,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -634,7 +634,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -669,7 +669,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -682,7 +682,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 2, // has 2 cards left
|
'card' => 2, // has 2 cards left
|
||||||
@@ -717,7 +717,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -730,7 +730,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 15, // has 15 cards
|
'card' => 15, // has 15 cards
|
||||||
@@ -778,7 +778,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 0,
|
'card' => 0,
|
||||||
@@ -791,7 +791,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 15,
|
'card' => 15,
|
||||||
@@ -844,7 +844,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -857,7 +857,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 15,
|
'card' => 15,
|
||||||
@@ -890,7 +890,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -903,7 +903,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -946,7 +946,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -959,7 +959,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -998,7 +998,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 0,
|
'card' => 0,
|
||||||
@@ -1011,7 +1011,7 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 5,
|
'card' => 5,
|
||||||
@@ -1037,4 +1037,48 @@ class AdminUserListStatsTest extends TestCase
|
|||||||
$this->assertCount(1, $addCards);
|
$this->assertCount(1, $addCards);
|
||||||
$this->assertEquals(15, $addCards->first()->num_card);
|
$this->assertEquals(15, $addCards->first()->num_card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_admin_updating_self_status_to_inactive_logs_out(): void
|
||||||
|
{
|
||||||
|
$admin = User::create([
|
||||||
|
'msnv' => 9001,
|
||||||
|
'name' => 'Admin User',
|
||||||
|
'mail' => 'admin@example.com',
|
||||||
|
'pass' => md5('password'),
|
||||||
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
|
'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'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($admin)->put(route('admin.users.update', 9001), [
|
||||||
|
'status' => config('constants.STATUS_INACTIVE'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertRedirect(route('login'));
|
||||||
|
$this->assertFalse(\Illuminate\Support\Facades\Auth::check());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_deactivating_self_via_destroy_logs_out(): void
|
||||||
|
{
|
||||||
|
$admin = User::create([
|
||||||
|
'msnv' => 9001,
|
||||||
|
'name' => 'Admin User',
|
||||||
|
'mail' => 'admin@example.com',
|
||||||
|
'pass' => md5('password'),
|
||||||
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
|
'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'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($admin)->delete(route('admin.users.destroy', 9001));
|
||||||
|
|
||||||
|
$response->assertRedirect(route('login'));
|
||||||
|
$this->assertFalse(\Illuminate\Support\Facades\Auth::check());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'Test User',
|
'name' => 'Test User',
|
||||||
'mail' => 'test@example.com',
|
'mail' => 'test@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -87,7 +87,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'Inactive User',
|
'name' => 'Inactive User',
|
||||||
'mail' => 'test@example.com',
|
'mail' => 'test@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_INACTIVE'), // 0
|
'status' => config('constants.STATUS_INACTIVE'), // 0
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -127,7 +127,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'Member User',
|
'name' => 'Member User',
|
||||||
'mail' => 'member@example.com',
|
'mail' => 'member@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -152,7 +152,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'Admin User',
|
'name' => 'Admin User',
|
||||||
'mail' => 'admin@example.com',
|
'mail' => 'admin@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -179,7 +179,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'First Login User',
|
'name' => 'First Login User',
|
||||||
'mail' => 'first@example.com',
|
'mail' => 'first@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -204,7 +204,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'First Login Admin',
|
'name' => 'First Login Admin',
|
||||||
'mail' => 'first_admin@example.com',
|
'mail' => 'first_admin@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -232,7 +232,7 @@ class LoginNotificationTest extends TestCase
|
|||||||
'name' => 'Lockout User',
|
'name' => 'Lockout User',
|
||||||
'mail' => 'test@example.com',
|
'mail' => 'test@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class PasswordMd5Test extends TestCase
|
|||||||
'name' => 'Test User',
|
'name' => 'Test User',
|
||||||
'mail' => 'test@example.com',
|
'mail' => 'test@example.com',
|
||||||
'pass' => md5('old_password'),
|
'pass' => md5('old_password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -51,7 +51,7 @@ class PasswordMd5Test extends TestCase
|
|||||||
'msnv' => 4002,
|
'msnv' => 4002,
|
||||||
'name' => 'Created User',
|
'name' => 'Created User',
|
||||||
'mail' => 'created@example.com',
|
'mail' => 'created@example.com',
|
||||||
'departments' => 2,
|
'departments' => config('constants.DEPARTMENTS')[1],
|
||||||
'password' => 'admin_created_pass',
|
'password' => 'admin_created_pass',
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class PasswordValidationTest extends TestCase
|
|||||||
'name' => 'Test User',
|
'name' => 'Test User',
|
||||||
'mail' => 'test@example.com',
|
'mail' => 'test@example.com',
|
||||||
'pass' => md5('password123'),
|
'pass' => md5('password123'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class SendThankCardTest extends TestCase
|
|||||||
'name' => 'Sender User',
|
'name' => 'Sender User',
|
||||||
'mail' => 'sender@example.com',
|
'mail' => 'sender@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -40,7 +40,7 @@ class SendThankCardTest extends TestCase
|
|||||||
'name' => 'Receiver User',
|
'name' => 'Receiver User',
|
||||||
'mail' => 'receiver@example.com',
|
'mail' => 'receiver@example.com',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 0,
|
'card' => 0,
|
||||||
@@ -84,7 +84,10 @@ class SendThankCardTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
$response->assertJson(['success' => true]);
|
$response->assertJson([
|
||||||
|
'success' => true,
|
||||||
|
'receiver_email' => 'receiver@example.com',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->sender->refresh();
|
$this->sender->refresh();
|
||||||
$this->assertEquals(8, $this->sender->card);
|
$this->assertEquals(8, $this->sender->card);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'System Admin',
|
'name' => 'System Admin',
|
||||||
'mail' => 'admin@runsystem.net',
|
'mail' => 'admin@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_ADMIN'),
|
'role' => config('constants.ROLE_ADMIN'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -45,7 +45,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'User A',
|
'name' => 'User A',
|
||||||
'mail' => 'usera@runsystem.net',
|
'mail' => 'usera@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -58,7 +58,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'User B',
|
'name' => 'User B',
|
||||||
'mail' => 'userb@runsystem.net',
|
'mail' => 'userb@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 2,
|
'departments' => config('constants.DEPARTMENTS')[1],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -100,7 +100,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'User A',
|
'name' => 'User A',
|
||||||
'mail' => 'usera@runsystem.net',
|
'mail' => 'usera@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -125,7 +125,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => "User {$char}",
|
'name' => "User {$char}",
|
||||||
'mail' => "user{$char}@runsystem.net",
|
'mail' => "user{$char}@runsystem.net",
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -162,7 +162,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'Active User',
|
'name' => 'Active User',
|
||||||
'mail' => 'active@runsystem.net',
|
'mail' => 'active@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -175,7 +175,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'Inactive User',
|
'name' => 'Inactive User',
|
||||||
'mail' => 'inactive@runsystem.net',
|
'mail' => 'inactive@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_INACTIVE'),
|
'status' => config('constants.STATUS_INACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
@@ -217,7 +217,7 @@ class UserDashboardLayoutTest extends TestCase
|
|||||||
'name' => 'User A',
|
'name' => 'User A',
|
||||||
'mail' => 'usera@runsystem.net',
|
'mail' => 'usera@runsystem.net',
|
||||||
'pass' => md5('password'),
|
'pass' => md5('password'),
|
||||||
'departments' => 1,
|
'departments' => config('constants.DEPARTMENTS')[0],
|
||||||
'role' => config('constants.ROLE_MEMBER'),
|
'role' => config('constants.ROLE_MEMBER'),
|
||||||
'status' => config('constants.STATUS_ACTIVE'),
|
'status' => config('constants.STATUS_ACTIVE'),
|
||||||
'card' => 10,
|
'card' => 10,
|
||||||
|
|||||||
Reference in New Issue
Block a user