diff --git a/BACKEND/Modules/Admin/app/Http/Controllers/TimekeepingController.php b/BACKEND/Modules/Admin/app/Http/Controllers/TimekeepingController.php index 3c667f0..9137bc1 100644 --- a/BACKEND/Modules/Admin/app/Http/Controllers/TimekeepingController.php +++ b/BACKEND/Modules/Admin/app/Http/Controllers/TimekeepingController.php @@ -43,10 +43,12 @@ class TimekeepingController extends Controller $date = Carbon::create($now->year, $now->month, $i)->setTimezone(env('TIME_ZONE'))->format('Y-m-d'); // Kiểm tra xem có mục nào trong $history có created_at trùng với $date $hasEntry = $history->filter(function ($entry) use ($date, $admin) { + // echo($hasEntry); return Carbon::parse($entry->created_at)->setTimezone(env('TIME_ZONE'))->format('Y-m-d') === $date && $entry->user_id == $admin->id; - }); - - if (count($hasEntry) > 0) { + }); + // echo($hasEntry); + + if (count($hasEntry) > 0) { $values = array_values($hasEntry->toArray()); $last_checkin = null; $total = 0; @@ -71,4 +73,37 @@ class TimekeepingController extends Controller } return response()->json(['status'=> true, 'data'=>$result]); } + + public function addWorkingTimeForMultipleUser(Request $request) + { + $user_ids = $request->users; + $year = $request->year; + $month = $request->month; + $day = $request->day; + $type = $request->type; + foreach($user_ids as $id){ + $user = Admin::find($id); + $date = Carbon::create($year, $month, $day)->setTimezone(env('TIME_ZONE')); + $start = $date->copy()->setTime(7, 31, 11); + $end = $type == 'half' ? $date->copy()->setTime(11, 31, 11) : $date->copy()->setTime(17, 1, 11); + Tracking::insert([ + [ + 'name' => $user->name, + 'user_id' => $user->id, + 'status' => 'check in', + 'time_string' => $start ->format('Y-m-d H:i:s'), + 'created_at' => $start->setTimezone('UTC') + ], + [ + 'name' => $user->name, + 'user_id' => $user->id, + 'status' => 'check out', + 'time_string' => $end->format('Y-m-d H:i:s'), + 'created_at' => $end->setTimezone('UTC') + ] + ]); + } + + return response()->json(['status'=> true, 'message'=> 'Add successfully']); + } } diff --git a/BACKEND/Modules/Admin/routes/api.php b/BACKEND/Modules/Admin/routes/api.php index d95fa71..dd18084 100755 --- a/BACKEND/Modules/Admin/routes/api.php +++ b/BACKEND/Modules/Admin/routes/api.php @@ -100,18 +100,19 @@ Route::middleware('api') Route::get('/all-project', [JiraController::class, 'getAllProject']); Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']); Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs'])->middleware('check.permission:admin'); - }); - - Route::group([ - 'prefix' => 'timekeeping', - ], function () { - Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff'); - }); - - Route::group([ - 'prefix' => 'tracking', - ], function () { - Route::post('/create', [TrackingController::class, 'create'])->middleware('check.permission:admin.hr'); + }); + + Route::group([ + 'prefix' => 'timekeeping', + ], function () { + Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff'); + Route::post('/addMutilple', [TimekeepingController::class, 'addWorkingTimeForMultipleUser'])->middleware('check.permission:admin.hr'); + }); + + Route::group([ + 'prefix' => 'tracking', + ], function () { + 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'); }); diff --git a/FRONTEND/src/api/Admin.ts b/FRONTEND/src/api/Admin.ts index eda18e0..feaaa77 100755 --- a/FRONTEND/src/api/Admin.ts +++ b/FRONTEND/src/api/Admin.ts @@ -65,4 +65,5 @@ export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-proje export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs' //Timekeeping -export const getTheTimesheet = API_URL + 'v1/admin/timekeeping' \ No newline at end of file +export const getTheTimesheet = API_URL + 'v1/admin/timekeeping' +export const updateMultipleUserWorkingTime = API_URL + 'v1/admin/timekeeping/addMutilple' \ No newline at end of file diff --git a/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx b/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx index da00545..c6662cc 100644 --- a/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx +++ b/FRONTEND/src/pages/Timekeeping/Timekeeping.tsx @@ -1,11 +1,12 @@ -import { getTheTimesheet } from '@/api/Admin' +import { getTheTimesheet, updateMultipleUserWorkingTime } from '@/api/Admin' import { get } from '@/rtk/helpers/apiService' -import { Box, Image, Select, Table, Text, Tooltip } from '@mantine/core' +import { Box, Image, Menu, Select, Table, Text, TextInput, Tooltip } from '@mantine/core' import { IconCheck, IconExclamationMark, IconX } from '@tabler/icons-react' import { useEffect, useState } from 'react' import classes from './Timekeeping.module.css' import { notifications } from '@mantine/notifications' import moment from 'moment' +import { update } from '@/rtk/helpers/CRUD' interface User { id: number @@ -44,6 +45,7 @@ const Timekeeping = () => { const [daysInMonth, setDaysInMonth] = useState( Array.from({ length: 31 }, (_, index) => index + 1), ) + const [workingDays, setWorkingDays] = useState(30) const [data, setData] = useState([]) const [date, setDate] = useState({ month: (new Date().getMonth() + 1).toString(), @@ -59,12 +61,14 @@ const Timekeeping = () => { if (res.status) { setData( res.data.filter((u: UserData) => - !u.user.email.includes('admin'), + u.user.permission.includes('staff'), ), ) setDaysInMonth( Array.from({ length: getDaysInMonth() }, (_, index) => index + 1), ) + + localStorage.getItem('workingdays') ? setWorkingDays(parseFloat(localStorage.getItem('workingdays')!)) : setWorkingDays(getDaysInMonth()) } } catch (error: any) { console.log(error) @@ -98,6 +102,20 @@ const Timekeeping = () => { return days.getDate() } + const updateMultipleUser = async(users:number[], day:number, type:string)=>{ + try { + await update(updateMultipleUserWorkingTime, { + users: users, + year: date.year, + month: date.month, + day:day, + type: type + }, getTimeSheet) + } catch (error) { + console.log(error) + } + } + useEffect(() => { getTimeSheet() }, [date]) @@ -111,34 +129,42 @@ const Timekeeping = () => { - - - + + + + + + + { + setWorkingDays(parseFloat(e.target.value)) + localStorage.setItem('workingdays', e.target.value) + }}/> + - + { Day + + {daysInMonth.map((d) => { return ( - - {d} - + + + + {d} + + + + + updateMultipleUser(data.map((u)=>u.user.id), d, 'half')}> + + Add half a day's work + + updateMultipleUser(data.map((u)=>u.user.id), d, 'one')} + > + + Add 1 day of work + + + ) })} + Total + Off {daysInMonth.map((d) => { return ( - + {getDayName(`${date.year}-${date.month}-${d}`)} ) @@ -225,16 +270,19 @@ const Timekeeping = () => { {data.map((user) => { + let totalDays = user.history.filter((h) => h.total / 60 / 60 >= 7).length + (user.history.filter((h) => h.total / 60 / 60 < 7 && h.total / 60 / 60 >= 3.5).length / 2) return ( {user.user.name} + {totalDays} + {workingDays-totalDays} {daysInMonth.map((d) => { var total = user.history.find((h) => h.day === d)?.total ?? 0 return ( - + {total / 60 / 60 < 7 && user.history.find((h) => h.day === d) ? - total / 60 / 60 >= 3.5 ? + total / 60 / 60 >= 3.5 ? { alignItems: 'center', justifyContent: 'space-between', }} + key={v.id} >

{v.status + ': ' + v.time_string}

{' '} {v.image && ( @@ -291,7 +340,7 @@ const Timekeeping = () => { : +
{`Total: ${(total / 60 / 60).toFixed(1)}h`} {user.history .find((h) => h.day === d) @@ -303,6 +352,7 @@ const Timekeeping = () => { alignItems: 'center', justifyContent: 'space-between', }} + key={v.id} >

{v.status + ': ' + v.time_string}

{' '} {v.image && ( @@ -357,6 +407,7 @@ const Timekeeping = () => { alignItems: 'center', justifyContent: 'space-between', }} + key={v.id} >

{v.status + ': ' + v.time_string}

{' '} {v.image && (