From 05605ed1eb2fb145d9bd277f011948caeeeb72a6 Mon Sep 17 00:00:00 2001 From: antv Date: Tue, 14 Jul 2026 16:23:04 +0700 Subject: [PATCH] feature: send thankscard page --- .env.example | 3 + .../Controllers/Admin/AdminController.php | 31 +- app/Http/Controllers/User/UserController.php | 11 +- app/Models/User.php | 15 + config/constants.php | 5 +- database/seeders/MockDataSeeder.php | 11 +- lang/vi/messages.php | 4 +- public/js/jquery.min.js | 4 + resources/css/app.css | 2 + resources/views/admin/users/form.blade.php | 11 +- .../admin/users/partials/table.blade.php | 8 +- resources/views/components/header.blade.php | 8 +- .../components/user-basic-info.blade.php | 9 +- .../components/user-filter-form.blade.php | 6 +- .../views/components/user-form.blade.php | 27 +- .../views/components/user-page.blade.php | 8 +- .../user-security-section.blade.php | 7 +- resources/views/layouts/app.blade.php | 35 +- resources/views/login.blade.php | 1 - .../user/partials/ranking_list.blade.php | 16 +- resources/views/user/send.blade.php | 464 +++++++++++------- ...64MaXbeS9DVpQJV4u9Y6BaeCKj9iGUZm5UnEyG.png | Bin tests/Feature/AdminUserListStatsTest.php | 102 ++-- tests/Feature/LoginNotificationTest.php | 14 +- tests/Feature/PasswordMd5Test.php | 4 +- tests/Feature/PasswordValidationTest.php | 2 +- tests/Feature/SendThankCardTest.php | 9 +- tests/Feature/UserDashboardLayoutTest.php | 16 +- 28 files changed, 536 insertions(+), 297 deletions(-) create mode 100644 public/js/jquery.min.js mode change 100644 => 100755 storage/app/public/avatars/9i64MaXbeS9DVpQJV4u9Y6BaeCKj9iGUZm5UnEyG.png diff --git a/.env.example b/.env.example index 85bf68b..1025028 100644 --- a/.env.example +++ b/.env.example @@ -63,3 +63,6 @@ AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" + +CHATOPS_API_URL=http://10.1.12.160/aiapi/sent-message-user +CHATOPS_API_TOKEN= diff --git a/app/Http/Controllers/Admin/AdminController.php b/app/Http/Controllers/Admin/AdminController.php index 3b87a3b..3a2664d 100644 --- a/app/Http/Controllers/Admin/AdminController.php +++ b/app/Http/Controllers/Admin/AdminController.php @@ -97,8 +97,26 @@ class AdminController extends Controller public function update(UpdateUserRequest $request, string $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()) { return response()->json([ @@ -116,8 +134,19 @@ class AdminController extends Controller */ public function destroy(string $msnv): RedirectResponse { + $user = $this->adminService->getUserByMsnv($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')); } diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php index cc452f8..8794b41 100644 --- a/app/Http/Controllers/User/UserController.php +++ b/app/Http/Controllers/User/UserController.php @@ -61,9 +61,6 @@ class UserController extends Controller return view('user.send', compact('users')); } - /** - * Store and execute sending thank cards to another user. - */ public function storeThankcards(SendThankCardRequest $request): JsonResponse { try { @@ -76,7 +73,13 @@ class UserController extends Controller 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 : '', + ]); } /** diff --git a/app/Models/User.php b/app/Models/User.php index 590f8de..941f97f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -88,5 +88,20 @@ class User extends Authenticatable 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; + } } diff --git a/config/constants.php b/config/constants.php index 2743af2..d7cfa5c 100644 --- a/config/constants.php +++ b/config/constants.php @@ -29,5 +29,8 @@ return [ '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' - ] + ], + + 'CHATOPS_API_URL' => env('CHATOPS_API_URL', 'http://10.1.12.160/aiapi/sent-message-user'), + 'CHATOPS_API_TOKEN' => env('CHATOPS_API_TOKEN', 'ifOgk13pXIa6p2q6vvJB'), ]; diff --git a/database/seeders/MockDataSeeder.php b/database/seeders/MockDataSeeder.php index 93037ce..b212d43 100644 --- a/database/seeders/MockDataSeeder.php +++ b/database/seeders/MockDataSeeder.php @@ -26,6 +26,7 @@ class MockDataSeeder extends Seeder // The default password is now set to 1234@Dcba according to the design requirements $password = md5('1234@Dcba'); + $depts = config('constants.DEPARTMENTS'); // 1. CREATE USERS // Admins @@ -34,7 +35,7 @@ class MockDataSeeder extends Seeder 'name' => 'System Admin', 'mail' => 'admin@runsystem.net', 'pass' => $password, - 'departments' => 1, + 'departments' => $depts[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 0, @@ -47,7 +48,7 @@ class MockDataSeeder extends Seeder 'name' => 'Second Admin', 'mail' => 'admin2@runsystem.net', 'pass' => $password, - 'departments' => 1, + 'departments' => $depts[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 0, @@ -81,13 +82,13 @@ class MockDataSeeder extends Seeder ]; } - foreach ($users as $u) { + foreach ($users as $idx => $u) { User::create([ 'msnv' => $u['msnv'], 'name' => $u['name'], 'mail' => $u['mail'], 'pass' => $password, - 'departments' => rand(1, 5), + 'departments' => $depts[$idx % count($depts)], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => $u['card'], @@ -114,7 +115,7 @@ class MockDataSeeder extends Seeder 'name' => 'Nghi Duy', 'mail' => 'nghiduy@runsystem.net', 'pass' => $password, - 'departments' => 2, + 'departments' => $depts[1], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_INACTIVE'), 'card' => 0, diff --git a/lang/vi/messages.php b/lang/vi/messages.php index a9928f3..c48ec45 100644 --- a/lang/vi/messages.php +++ b/lang/vi/messages.php @@ -9,8 +9,8 @@ return [ 'thank_card_send_success' => 'Đã gửi Thank card thành công!', 'error' => [ - 'no_send_permission' => 'Bạn hiện không có quyền gửi Thank Card.', - 'not_enough_cards' => 'Số lượng card của bạn không đủ. Vui lòng liên hệ Admin.', + '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' => "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.', 'receiver_invalid' => 'Người nhận không tồn tại hoặc đã nghỉ việc.', ] diff --git a/public/js/jquery.min.js b/public/js/jquery.min.js new file mode 100644 index 0000000..63cdae5 --- /dev/null +++ b/public/js/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ + +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 -
-

- {{ $mode === 'create' ? 'Thêm User Mới' : 'Chi tiết User' }} -

-

- {{ $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.' }} -

-
- + @endsection diff --git a/resources/views/admin/users/partials/table.blade.php b/resources/views/admin/users/partials/table.blade.php index 331ef08..f6cbfa2 100644 --- a/resources/views/admin/users/partials/table.blade.php +++ b/resources/views/admin/users/partials/table.blade.php @@ -38,13 +38,7 @@ @php - $deptVal = $u->departments; - if (is_numeric($deptVal)) { - $idx = ((int)$deptVal) - 1; - $deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team'; - } else { - $deptName = $deptVal ?? 'Team'; - } + $deptName = $u->department_name ?? 'Team'; @endphp
{{ $deptName }} diff --git a/resources/views/components/header.blade.php b/resources/views/components/header.blade.php index bd60cfb..57d6533 100644 --- a/resources/views/components/header.blade.php +++ b/resources/views/components/header.blade.php @@ -23,13 +23,7 @@
@php $avatarUrl = Auth::user()->avatar ? Auth::user()->avatar : asset('images/avatar.png'); - $deptVal = Auth::user()->departments; - if (is_numeric($deptVal)) { - $idx = ((int)$deptVal) - 1; - $deptName = config('constants.DEPARTMENTS')[$idx] ?? 'Team'; - } else { - $deptName = $deptVal ?? 'Team'; - } + $deptName = Auth::user()->department_name ?? 'Team'; @endphp Avatar
diff --git a/resources/views/components/user-basic-info.blade.php b/resources/views/components/user-basic-info.blade.php index 785dc6c..c6fd94f 100644 --- a/resources/views/components/user-basic-info.blade.php +++ b/resources/views/components/user-basic-info.blade.php @@ -61,6 +61,9 @@
+ @php + $deptVal = old('departments', $user?->department_name); + @endphp @if($mode === 'self')
@@ -72,8 +75,8 @@ class="form-input form-input-with-icon !rounded-xl !bg-gray-50 !text-gray-500 cursor-not-allowed w-full" style="padding-left: 48px;" readonly - title="{{ $user?->departments }}" - value="{{ $user?->departments }}"> + title="{{ $deptVal }}" + value="{{ $deptVal }}">
@else
@@ -85,7 +88,7 @@
diff --git a/resources/views/components/user-filter-form.blade.php b/resources/views/components/user-filter-form.blade.php index 0e3f866..a260006 100644 --- a/resources/views/components/user-filter-form.blade.php +++ b/resources/views/components/user-filter-form.blade.php @@ -11,7 +11,7 @@
- + @@ -19,7 +19,7 @@ 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;" /> @@ -84,7 +84,7 @@ field: "text", direction: "asc" }, - placeholder: "🔍 Tìm phòng ban...", + placeholder: "Tìm phòng ban...", render: { item: function(data, escape) { return '
' + escape(data.text) + '
'; diff --git a/resources/views/components/user-form.blade.php b/resources/views/components/user-form.blade.php index d6f1469..074ab86 100644 --- a/resources/views/components/user-form.blade.php +++ b/resources/views/components/user-form.blade.php @@ -26,9 +26,9 @@

{{ match($mode) { - 'create' => '👥 Thêm User Mới', - 'delete' => '⚠️ Xác nhận nghỉ việc: ' . ($user?->msnv), - 'self' => '👤 Thông tin cá nhân: ' . ($user?->msnv), + 'create' => 'Thêm User Mới', + 'delete' => 'Xác nhận nghỉ việc: ' . ($user?->name), + 'self' => 'Thông tin cá nhân', } }}

@@ -40,23 +40,20 @@
{{ strtoupper(substr($user->name ?? $user->msnv, 0, 2)) }}
-
-

{{ $user->name }}

-

{{ $user->mail }}

-
- - {{ $user->role == config('constants.ROLE_ADMIN') ? 'Admin' : 'Member' }} - - - Thẻ: {{ $user->card }} -
+
+

{{ $user->name }}

+

+ MSNV: {{ $user->msnv }} + + {{ $user->mail }} +

@endif @if($actionUrl) - + @csrf @if($method !== 'POST') @method($method) @@ -66,7 +63,7 @@
@if($isDelete && $user)

- 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.

@else diff --git a/resources/views/components/user-page.blade.php b/resources/views/components/user-page.blade.php index 5fb495d..b8975f6 100644 --- a/resources/views/components/user-page.blade.php +++ b/resources/views/components/user-page.blade.php @@ -344,11 +344,15 @@ await reloadListsSection(); } 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) { 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 { if (submitBtn) submitBtn.disabled = false; } diff --git a/resources/views/components/user-security-section.blade.php b/resources/views/components/user-security-section.blade.php index ec3971a..5c77a8b 100644 --- a/resources/views/components/user-security-section.blade.php +++ b/resources/views/components/user-security-section.blade.php @@ -9,9 +9,10 @@ - +
- Mật khẩu mặc định cho tất cả user mới tạo. + @error('password'){{ $message }}@enderror + Thiết lập mật khẩu khởi tạo cho user (mặc định là 1234@Dcba).
@elseif($mode === 'edit')
@@ -28,7 +29,7 @@ @elseif($mode === 'self')

- 🔑 Bảo mật & Đổi mật khẩu + Bảo mật & Đổi mật khẩu

diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 593295b..111f141 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -141,14 +141,47 @@ closeBtn.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'); if (!toast) return; const msgEl = toast.querySelector('.text-sm'); 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 = ''; + } else if (type === 'error') { + iconContainer.classList.add('bg-red-50', 'text-red-500'); + iconHtml = ''; + } else if (type === 'warning') { + iconContainer.classList.add('bg-amber-50', 'text-amber-500'); + iconHtml = ''; + } else { + iconContainer.classList.add('bg-blue-50', 'text-blue-500'); + iconHtml = ''; + } + iconContainer.innerHTML = iconHtml; + } toast.classList.remove('hidden'); toast.classList.add('flex'); diff --git a/resources/views/login.blade.php b/resources/views/login.blade.php index 74c92e2..57b89d8 100644 --- a/resources/views/login.blade.php +++ b/resources/views/login.blade.php @@ -5,7 +5,6 @@ GMO-Z.com RUNSYSTEM - {{ __('auth.login') }} - @vite(['resources/css/app.css', 'resources/js/app.js']) diff --git a/resources/views/user/partials/ranking_list.blade.php b/resources/views/user/partials/ranking_list.blade.php index 1e6e364..ca70f4f 100644 --- a/resources/views/user/partials/ranking_list.blade.php +++ b/resources/views/user/partials/ranking_list.blade.php @@ -3,13 +3,7 @@
@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'; - } + $teamName = $item->department_name ?? 'Team'; @endphp
@php - $currDeptVal = $currentUserRankItem->departments; - if (is_numeric($currDeptVal)) { - $idx = ((int)$currDeptVal) - 1; - $currTeamName = config('constants.DEPARTMENTS')[$idx] ?? 'Team'; - } else { - $currTeamName = $currDeptVal ?? 'Team'; - } + $currTeamName = $currentUserRankItem->department_name ?? 'Team'; @endphp -
- -
-
-
- -
-

Gửi Thank Card

- 💌 -
-

- 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! -

-
- - - @csrf - -
-
- - -
+@php +$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 -
- - -
- - - -

- 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. -

-
-
+
-
-
- - -
-
-
- -
- Hủy bỏ - -
- +{{-- Header --}} +
+
+
+
+
💌
+
+

Gửi Thank Card

+

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.

+
+
+ +
+@csrf + + +{{-- Template Gallery --}} +
+

Chọn mẫu thiệp *

+
+ @foreach($tpls as $id => $t) + + @endforeach +
+
+ +{{-- e-Card (form inside the card) --}} +
+ + {{-- Decorative SVG top --}} +
+ {{-- blob 1 --}} +
+
+ {{-- SVG decoration --}} +
+ {{-- Template 1 default: envelope --}} +
+ 💌 + Thank Card +
+
+ {{-- wavy bottom --}} + + + +
+ + {{-- Card body: form fields ON the card --}} +
+ + {{-- To: Người nhận --}} +
+ + +
+ + {{-- Số lượng --}} +
+ +
+ + / tháng +
+
+ + {{-- Confirm checkbox --}} + + + {{-- Card footer line --}} +
+
+ Từ: {{ Auth::user()->name ?? 'Bạn' }} + Thư cảm ơn +
+
+
+
+ +{{-- Info note --}} +
+
+ +
+
+

Lưu ý

+

Chỉ được gửi tối đa {{ App\Models\Administration::MAX_SEND_CARD_PER_MONTH }} thẻ cho cùng một người trong tháng để đảm bảo tính công bằng.

+
+
+ +{{-- Actions --}} +
+ + Hủy bỏ + + +
+ +
-

- - -
-
-
+

+ +
+
@endsection @push('scripts') + @endpush diff --git a/storage/app/public/avatars/9i64MaXbeS9DVpQJV4u9Y6BaeCKj9iGUZm5UnEyG.png b/storage/app/public/avatars/9i64MaXbeS9DVpQJV4u9Y6BaeCKj9iGUZm5UnEyG.png old mode 100644 new mode 100755 diff --git a/tests/Feature/AdminUserListStatsTest.php b/tests/Feature/AdminUserListStatsTest.php index da3890d..9fc521e 100644 --- a/tests/Feature/AdminUserListStatsTest.php +++ b/tests/Feature/AdminUserListStatsTest.php @@ -29,7 +29,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'User A', 'mail' => 'usera@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -42,7 +42,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'User B', 'mail' => 'userb@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -109,7 +109,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'User A', 'mail' => 'usera@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -166,7 +166,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'DEV ALPHA', 'mail' => 'alpha@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -179,7 +179,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'DEV BETA', 'mail' => 'beta@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -210,7 +210,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'DEV ACTIVE', 'mail' => 'active@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -223,7 +223,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'DEV INACTIVE', 'mail' => 'inactive@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_INACTIVE'), 'card' => 10, @@ -299,7 +299,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -312,7 +312,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 5, @@ -521,7 +521,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -534,7 +534,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, // currently has 10 cards @@ -573,7 +573,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -586,7 +586,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -621,7 +621,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -634,7 +634,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -669,7 +669,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -682,7 +682,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 2, // has 2 cards left @@ -717,7 +717,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -730,7 +730,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 15, // has 15 cards @@ -778,7 +778,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 0, @@ -791,7 +791,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 15, @@ -844,7 +844,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -857,7 +857,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 15, @@ -890,7 +890,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -903,7 +903,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -946,7 +946,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -959,7 +959,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -998,7 +998,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 0, @@ -1011,7 +1011,7 @@ class AdminUserListStatsTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 5, @@ -1037,4 +1037,48 @@ class AdminUserListStatsTest extends TestCase $this->assertCount(1, $addCards); $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()); + } } diff --git a/tests/Feature/LoginNotificationTest.php b/tests/Feature/LoginNotificationTest.php index 736d515..042e1f6 100644 --- a/tests/Feature/LoginNotificationTest.php +++ b/tests/Feature/LoginNotificationTest.php @@ -28,7 +28,7 @@ class LoginNotificationTest extends TestCase 'name' => 'Test User', 'mail' => 'test@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -87,7 +87,7 @@ class LoginNotificationTest extends TestCase 'name' => 'Inactive User', 'mail' => 'test@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_INACTIVE'), // 0 'card' => 10, @@ -127,7 +127,7 @@ class LoginNotificationTest extends TestCase 'name' => 'Member User', 'mail' => 'member@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -152,7 +152,7 @@ class LoginNotificationTest extends TestCase 'name' => 'Admin User', 'mail' => 'admin@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -179,7 +179,7 @@ class LoginNotificationTest extends TestCase 'name' => 'First Login User', 'mail' => 'first@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -204,7 +204,7 @@ class LoginNotificationTest extends TestCase 'name' => 'First Login Admin', 'mail' => 'first_admin@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -232,7 +232,7 @@ class LoginNotificationTest extends TestCase 'name' => 'Lockout User', 'mail' => 'test@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, diff --git a/tests/Feature/PasswordMd5Test.php b/tests/Feature/PasswordMd5Test.php index 5095ddd..667a49b 100644 --- a/tests/Feature/PasswordMd5Test.php +++ b/tests/Feature/PasswordMd5Test.php @@ -29,7 +29,7 @@ class PasswordMd5Test extends TestCase 'name' => 'Test User', 'mail' => 'test@example.com', 'pass' => md5('old_password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -51,7 +51,7 @@ class PasswordMd5Test extends TestCase 'msnv' => 4002, 'name' => 'Created User', 'mail' => 'created@example.com', - 'departments' => 2, + 'departments' => config('constants.DEPARTMENTS')[1], 'password' => 'admin_created_pass', 'role' => config('constants.ROLE_MEMBER'), ]; diff --git a/tests/Feature/PasswordValidationTest.php b/tests/Feature/PasswordValidationTest.php index 33562ad..2fe69be 100644 --- a/tests/Feature/PasswordValidationTest.php +++ b/tests/Feature/PasswordValidationTest.php @@ -21,7 +21,7 @@ class PasswordValidationTest extends TestCase 'name' => 'Test User', 'mail' => 'test@example.com', 'pass' => md5('password123'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, diff --git a/tests/Feature/SendThankCardTest.php b/tests/Feature/SendThankCardTest.php index a3a0eff..959f0d6 100644 --- a/tests/Feature/SendThankCardTest.php +++ b/tests/Feature/SendThankCardTest.php @@ -27,7 +27,7 @@ class SendThankCardTest extends TestCase 'name' => 'Sender User', 'mail' => 'sender@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -40,7 +40,7 @@ class SendThankCardTest extends TestCase 'name' => 'Receiver User', 'mail' => 'receiver@example.com', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 0, @@ -84,7 +84,10 @@ class SendThankCardTest extends TestCase ]); $response->assertStatus(200); - $response->assertJson(['success' => true]); + $response->assertJson([ + 'success' => true, + 'receiver_email' => 'receiver@example.com', + ]); $this->sender->refresh(); $this->assertEquals(8, $this->sender->card); diff --git a/tests/Feature/UserDashboardLayoutTest.php b/tests/Feature/UserDashboardLayoutTest.php index 6140f3a..74beed5 100644 --- a/tests/Feature/UserDashboardLayoutTest.php +++ b/tests/Feature/UserDashboardLayoutTest.php @@ -16,7 +16,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'System Admin', 'mail' => 'admin@runsystem.net', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_ADMIN'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -45,7 +45,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'User A', 'mail' => 'usera@runsystem.net', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -58,7 +58,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'User B', 'mail' => 'userb@runsystem.net', 'pass' => md5('password'), - 'departments' => 2, + 'departments' => config('constants.DEPARTMENTS')[1], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -100,7 +100,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'User A', 'mail' => 'usera@runsystem.net', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -125,7 +125,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => "User {$char}", 'mail' => "user{$char}@runsystem.net", 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -162,7 +162,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'Active User', 'mail' => 'active@runsystem.net', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10, @@ -175,7 +175,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'Inactive User', 'mail' => 'inactive@runsystem.net', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_INACTIVE'), 'card' => 10, @@ -217,7 +217,7 @@ class UserDashboardLayoutTest extends TestCase 'name' => 'User A', 'mail' => 'usera@runsystem.net', 'pass' => md5('password'), - 'departments' => 1, + 'departments' => config('constants.DEPARTMENTS')[0], 'role' => config('constants.ROLE_MEMBER'), 'status' => config('constants.STATUS_ACTIVE'), 'card' => 10,