diff --git a/BACKEND/Modules/Admin/resources/views/mails/reset_password_success.blade.php b/BACKEND/Modules/Admin/resources/views/mails/reset_password_success.blade.php index 0902972..edb9fea 100755 --- a/BACKEND/Modules/Admin/resources/views/mails/reset_password_success.blade.php +++ b/BACKEND/Modules/Admin/resources/views/mails/reset_password_success.blade.php @@ -4,7 +4,7 @@ 'title' => 'Reset Password Successfully', 'name' => $name, 'body' => "We wanted to let you know that your password has been successfully reset. Your account is now secured with a new password.
- If you did not initiate this password reset or have any concerns about your account security, please contact our support team immediately at " . config('mail.from.address') . " or reply to this email.", + If you did not initiate this password reset or have any concerns about your account security, please contact our support team.", 'footer_detail' => "This email was sent $email.", ]; @endphp @@ -120,9 +120,9 @@
- +
diff --git a/BACKEND/Modules/Admin/routes/api.php b/BACKEND/Modules/Admin/routes/api.php index b9fe679..b5ac71d 100755 --- a/BACKEND/Modules/Admin/routes/api.php +++ b/BACKEND/Modules/Admin/routes/api.php @@ -1,5 +1,6 @@ middleware('check.permission:admin'); }); Route::group([ 'prefix' => 'timekeeping', ], function () { - Route::get('/', [TimekeepingController::class, 'get']); + Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff'); }); Route::group([ 'prefix' => 'tracking', ], function () { - Route::post('/create', [TrackingController::class, 'create']); - Route::post('/update', [TrackingController::class, 'update']); - Route::get('/delete', [TrackingController::class, 'delete']); + Route::post('/create', [TrackingController::class, 'create'])->middleware('check.permission:admin.hr'); + Route::post('/update', [TrackingController::class, 'update'])->middleware('check.permission:admin.hr'); + Route::get('/delete', [TrackingController::class, 'delete'])->middleware('check.permission:admin.hr'); }); }); }); @@ -121,7 +122,7 @@ Route::middleware('api') Route::group([ 'prefix' => 'v1/admin/tracking', ], function () { - Route::get('/', [TrackingController::class, 'get']); + Route::get('/', [TrackingController::class, 'get'])->middleware('check.permission:admin.hr.staff'); Route::post('/scan-create', [TrackingController::class, 'create']); Route::post('/send-image', [TrackingController::class, 'saveImage']); // Route::get('/clear-cache', [SettingController::class, 'clearCache']); diff --git a/BACKEND/app/Http/Kernel.php b/BACKEND/app/Http/Kernel.php index c196709..2a50584 100755 --- a/BACKEND/app/Http/Kernel.php +++ b/BACKEND/app/Http/Kernel.php @@ -21,9 +21,13 @@ class Kernel extends HttpKernel \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, - \App\Http\Middleware\CheckAccountStatus::class, + \App\Http\Middleware\CheckAccountStatus::class ]; + protected $routeMiddleware = [ + // Other middleware + 'check.permission' => \App\Http\Middleware\CheckPermission::class, + ]; /** * The application's route middleware groups. * diff --git a/BACKEND/app/Http/Middleware/CheckPermission.php b/BACKEND/app/Http/Middleware/CheckPermission.php new file mode 100644 index 0000000..1b8f9cf --- /dev/null +++ b/BACKEND/app/Http/Middleware/CheckPermission.php @@ -0,0 +1,36 @@ +user(); + // dd(auth('admins')->user()->permission); + $requiredPermission = explode('.', $requiredPermission); + if ($user) { + $permission = explode(',', $user->permission); + + // Kiểm tra nếu user có quyền yêu cầu + foreach ($requiredPermission as $perm) { + if (in_array($perm, $permission)) { + return $next($request); + } + } + } + + // Nếu không có quyền, trả về response 403 + return response()->json(['status' => false, 'message' => 'Forbidden'], 403); + } +} diff --git a/FRONTEND/src/components/DataTable/DataTable.tsx b/FRONTEND/src/components/DataTable/DataTable.tsx index 090a2c3..d64fe60 100755 --- a/FRONTEND/src/components/DataTable/DataTable.tsx +++ b/FRONTEND/src/components/DataTable/DataTable.tsx @@ -573,6 +573,19 @@ export const DataTablePagination = ({ ) }) + const removeParam = (name:string) => { + // Create a URL object + let url = new URL(window.location.href) + + // Get the search parameters object + let params = url.searchParams + + // Remove specific parameters + params.delete(name) + + // Update the URL without reloading the page + window.history.replaceState({}, document.title, url.toString()) + } // Refresh data when useEffect start const fetchData = async (url: string) => { try { @@ -583,7 +596,7 @@ export const DataTablePagination = ({ page: page, timezone: timeZone, } - + // console.log(statusSort.name) // Add 'order_by_[field_name] to 'params' if (statusSort.status !== 'clear') { Object.assign(params, { @@ -669,6 +682,7 @@ export const DataTablePagination = ({ search: urlParams.toString(), }) } + statusSort.status === 'clear' && removeParam(`order_by_${statusSort.name}`) } catch (error) { console.warn(error) } diff --git a/FRONTEND/src/components/Navbar/Navbar.tsx b/FRONTEND/src/components/Navbar/Navbar.tsx index f365d08..68da1bd 100755 --- a/FRONTEND/src/components/Navbar/Navbar.tsx +++ b/FRONTEND/src/components/Navbar/Navbar.tsx @@ -18,7 +18,6 @@ import { } from '@mantine/core' import { notifications } from '@mantine/notifications' import { - IconBrush, IconCalendar, IconLayoutSidebarLeftExpand, IconLayoutSidebarRightExpand, @@ -28,7 +27,6 @@ import { IconPasswordUser, IconReport, IconScan, - IconSettings, IconSubtask, IconSun } from '@tabler/icons-react' @@ -43,8 +41,8 @@ const data = [ { link: '/tracking', label: 'Check in/out', icon: IconScan }, { link: '/worklogs', label: 'Worklogs', icon: IconReport }, { link: '/jira', label: 'Jira', icon: IconSubtask }, - { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush }, - { link: '/general-setting', label: 'General Setting', icon: IconSettings }, + // { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush }, + // { link: '/general-setting', label: 'General Setting', icon: IconSettings }, // { link: '/packages', label: 'Packages', icon: IconPackages }, // { link: '/discounts', label: 'Discounts', icon: IconDiscount2 }, // { link: '/client', label: 'Clients', icon: IconUsersGroup }, @@ -200,10 +198,10 @@ const Navbar = ({ >
- + > */} v1.0.1 @@ -361,7 +359,6 @@ const Navbar = ({ loading === false && dataChange.password !== '' && passwordRegex.test(dataChange.new_password) && - passwordRegex.test(dataChange.password) && dataChange.new_password === dataChange.confirm_password && dataChange.new_password !== '' ? false diff --git a/FRONTEND/src/components/Navbar/NavbarSimpleColored.module.css b/FRONTEND/src/components/Navbar/NavbarSimpleColored.module.css index 4178f29..4fc6c96 100755 --- a/FRONTEND/src/components/Navbar/NavbarSimpleColored.module.css +++ b/FRONTEND/src/components/Navbar/NavbarSimpleColored.module.css @@ -2,7 +2,7 @@ height: rem(100%); display: flex; flex-direction: column; - background-color: #2d353c; + background-color: #1c4a73; width: 100%; } @@ -20,7 +20,7 @@ /* z-index: 1000; */ background-image: linear-gradient(rgba(27, 26, 26, 0.5), rgba(29, 27, 27, 0.5)), - url('https://upload.wikimedia.org/wikipedia/commons/5/5c/Dark_mountain_panorama.jpg'); + url('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT-bB7j40qe-mSJ-howmBO7T6tAghPrtfLvDw&s'); height: rem(100px); background-repeat: no-repeat; background-size: cover; @@ -66,7 +66,7 @@ &, &:hover { box-shadow: var(--mantine-shadow-sm); - background-color: #576470b7; + background-color: #6089afb7; color: var(--mantine-color-white); .linkIcon { diff --git a/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx b/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx index c1a09f5..e9829fe 100644 --- a/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx +++ b/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx @@ -4,6 +4,7 @@ import { Box, Image, Select, Table, Text, Tooltip } from '@mantine/core' import { IconCheck, IconX } from '@tabler/icons-react' import { useEffect, useState } from 'react' import classes from './Timekeeping.module.css' +import { notifications } from '@mantine/notifications' interface User { id: number @@ -39,7 +40,6 @@ interface UserData { } const Timekeeping = () => { - let permission = ['staff'] const [daysInMonth, setDaysInMonth] = useState( Array.from({ length: 31 }, (_, index) => index + 1), ) @@ -58,15 +58,20 @@ const Timekeeping = () => { if (res.status) { setData( res.data.filter((u: UserData) => - permission.includes(u.user.permission), + !u.user.email.includes('admin'), ), ) setDaysInMonth( Array.from({ length: getDaysInMonth() }, (_, index) => index + 1), ) } - } catch (error) { + } catch (error:any) { console.log(error) + notifications.show({ + title: 'Error', + message: error.message??error, + color: 'red', + }) } } @@ -175,7 +180,7 @@ const Timekeeping = () => { +
{`Total: ${(total / 60 / 60).toFixed(1)}h`} {user.history .find((h) => h.day === d) diff --git a/FRONTEND/src/pages/Tracking/Tracking.tsx b/FRONTEND/src/pages/Tracking/Tracking.tsx index be53433..50a81f6 100755 --- a/FRONTEND/src/pages/Tracking/Tracking.tsx +++ b/FRONTEND/src/pages/Tracking/Tracking.tsx @@ -17,6 +17,7 @@ import { IconEdit, IconTrash } from '@tabler/icons-react' import moment from 'moment' import { useEffect, useState } from 'react' import classes from './Tracking.module.css' +import { notifications } from '@mantine/notifications' type TLog = { id: number @@ -156,8 +157,12 @@ const Tracking = () => { if (res.status) { setListTracking(res) } - } catch (error) { - console.log(error) + } catch (error:any) { + notifications.show({ + title: 'Error', + message: error.message??error, + color: 'red', + }) } } @@ -205,14 +210,14 @@ const Tracking = () => { } useEffect(() => { - if (listTracking.data.length === 0) { + // if (listTracking.data.length === 0) { getAllTracking() - } - setInterval(() => { - if (window.location.pathname.includes('tracking')) { - getAllTracking() - } - }, 7000) + // } + // setInterval(() => { + // if (window.location.pathname.includes('tracking')) { + // getAllTracking() + // } + // }, 60000) }, []) return (
@@ -268,7 +273,7 @@ const Tracking = () => {