update timekeeping
This commit is contained in:
		
							parent
							
								
									8cc13276f0
								
							
						
					
					
						commit
						54e5d8e780
					
				| 
						 | 
					@ -0,0 +1,74 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Modules\Admin\app\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Traits\HasFilterRequest;
 | 
				
			||||||
 | 
					use App\Traits\HasOrderByRequest;
 | 
				
			||||||
 | 
					use App\Traits\HasSearchRequest;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\Admin;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\Tracking;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use function PHPSTORM_META\type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TimekeepingController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasOrderByRequest;
 | 
				
			||||||
 | 
					    use HasFilterRequest;
 | 
				
			||||||
 | 
					    use HasSearchRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function get(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // dd($request->date);
 | 
				
			||||||
 | 
					        $now = Carbon::create($request->year, $request->month);
 | 
				
			||||||
 | 
					        // $now = Carbon::create(2024, 5, 30); // Nếu muốn khởi tạo với một ngày cụ thể
 | 
				
			||||||
 | 
					        $daysInMonth = $now->daysInMonth;
 | 
				
			||||||
 | 
					        // return response()->json(['status'=> true, 'data'=>$request->all()]);
 | 
				
			||||||
 | 
					        // Lấy ngày đầu tháng
 | 
				
			||||||
 | 
					        $startOfMonth = $now->startOfMonth()->toDateString();
 | 
				
			||||||
 | 
					        // Lấy ngày cuối tháng
 | 
				
			||||||
 | 
					        $endOfMonth = $now->endOfMonth()->toDateString();
 | 
				
			||||||
 | 
					        $admins = Admin::all();
 | 
				
			||||||
 | 
					        $history = DB::table('tracking')->select('*')
 | 
				
			||||||
 | 
					            ->whereBetween('tracking.created_at', [$startOfMonth, $endOfMonth])->orderBy('tracking.created_at', 'asc')->get();
 | 
				
			||||||
 | 
					        $history = collect($history);
 | 
				
			||||||
 | 
					        $result = [];
 | 
				
			||||||
 | 
					        foreach ($admins as $admin) {
 | 
				
			||||||
 | 
					            $user_data = [];
 | 
				
			||||||
 | 
					            for ($i = 1; $i <= $daysInMonth; $i++) {
 | 
				
			||||||
 | 
					                // Tạo ngày cụ thể trong tháng
 | 
				
			||||||
 | 
					                $date = Carbon::create($now->year, $now->month, $i)->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) {
 | 
				
			||||||
 | 
					                    return Carbon::parse($entry->created_at)->format('Y-m-d') === $date && $entry->user_id == $admin->id;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (count($hasEntry) > 0) {
 | 
				
			||||||
 | 
					                    $values = array_values($hasEntry->toArray());
 | 
				
			||||||
 | 
					                    $last_checkin = null;
 | 
				
			||||||
 | 
					                    $total = 0;
 | 
				
			||||||
 | 
					                    foreach ($values as $value) {
 | 
				
			||||||
 | 
					                        $createdAt = Carbon::parse($value->created_at);
 | 
				
			||||||
 | 
					                        if ($value->status == 'check out' && $last_checkin != null) {
 | 
				
			||||||
 | 
					                            $lastCheckInTime = Carbon::parse($last_checkin);
 | 
				
			||||||
 | 
					                            // Tính thời gian làm việc bằng hiệu của thời gian check out và check in
 | 
				
			||||||
 | 
					                            $workingTime = $createdAt->diffInSeconds($lastCheckInTime);
 | 
				
			||||||
 | 
					                            $total += $workingTime;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if ($value->status == 'check in') {
 | 
				
			||||||
 | 
					                            $last_checkin = $createdAt;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $user_data[] = ['values'=>array_values($hasEntry->toArray()), 'total' => $total, 'day' => $i];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $result[] = ['user' => $admin, 'history' => $user_data];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response()->json(['status'=> true, 'data'=>$result]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@ class TrackingController extends Controller
 | 
				
			||||||
        // Order by
 | 
					        // Order by
 | 
				
			||||||
        $this->orderByRequest($tracking, $request);
 | 
					        $this->orderByRequest($tracking, $request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $tracking->orderBy('created_at', 'desc');
 | 
				
			||||||
        // Filter
 | 
					        // Filter
 | 
				
			||||||
        $this->filterRequest(
 | 
					        $this->filterRequest(
 | 
				
			||||||
            builder: $tracking,
 | 
					            builder: $tracking,
 | 
				
			||||||
| 
						 | 
					@ -59,6 +60,8 @@ class TrackingController extends Controller
 | 
				
			||||||
            ['status' => true]
 | 
					            ['status' => true]
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $users = Admin::where('permission', '!=', 'admin')->get();
 | 
				
			||||||
 | 
					        $responseData['users'] = $users;
 | 
				
			||||||
        return response()->json($responseData);
 | 
					        return response()->json($responseData);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +75,9 @@ class TrackingController extends Controller
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $payload = $request->only(['name', 'time_string', 'status']);
 | 
					            $payload = $request->only(['name', 'time_string', 'status']);
 | 
				
			||||||
 | 
					            if($request->has('created_at')){
 | 
				
			||||||
 | 
					              $payload['created_at'] = $request->created_at;              
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $user = Admin::where('name', $payload['name'])->first();
 | 
					            $user = Admin::where('name', $payload['name'])->first();
 | 
				
			||||||
            if ($user) {
 | 
					            if ($user) {
 | 
				
			||||||
                $payload['user_id'] = $user->id;
 | 
					                $payload['user_id'] = $user->id;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,17 +4,12 @@ use Illuminate\Support\Facades\Route;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\AdminController;
 | 
					use Modules\Admin\app\Http\Controllers\AdminController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\BannerController;
 | 
					use Modules\Admin\app\Http\Controllers\BannerController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\ClientController;
 | 
					use Modules\Admin\app\Http\Controllers\ClientController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\ContactController;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\CountryController;
 | 
					use Modules\Admin\app\Http\Controllers\CountryController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\CustomThemeController;
 | 
					use Modules\Admin\app\Http\Controllers\CustomThemeController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\DashboardController;
 | 
					use Modules\Admin\app\Http\Controllers\DashboardController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\DiscountController;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\DiscountTypeController;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\JiraController;
 | 
					use Modules\Admin\app\Http\Controllers\JiraController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\OrderController;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\PackageController;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\SNCheckController;
 | 
					 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\SettingController;
 | 
					use Modules\Admin\app\Http\Controllers\SettingController;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Http\Controllers\TimekeepingController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\TrackingController;
 | 
					use Modules\Admin\app\Http\Controllers\TrackingController;
 | 
				
			||||||
use Modules\Admin\app\Http\Middleware\AdminMiddleware;
 | 
					use Modules\Admin\app\Http\Middleware\AdminMiddleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,28 +50,6 @@ Route::middleware('api')
 | 
				
			||||||
                Route::get('/clear-cache', [SettingController::class, 'clearCache']);
 | 
					                Route::get('/clear-cache', [SettingController::class, 'clearCache']);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					 | 
				
			||||||
                'prefix' => 'package',
 | 
					 | 
				
			||||||
            ], function () {
 | 
					 | 
				
			||||||
                Route::get('/all', [PackageController::class, 'all']);
 | 
					 | 
				
			||||||
                Route::post('/create', [PackageController::class, 'create']);
 | 
					 | 
				
			||||||
                Route::post('/update', [PackageController::class, 'update']);
 | 
					 | 
				
			||||||
                Route::get('/delete', [PackageController::class, 'delete']);
 | 
					 | 
				
			||||||
                Route::post('/updates', [PackageController::class, 'updates']);
 | 
					 | 
				
			||||||
                Route::post('/deletes', [PackageController::class, 'deletes']);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Route::group([
 | 
					 | 
				
			||||||
                'prefix' => 'discount',
 | 
					 | 
				
			||||||
            ], function () {
 | 
					 | 
				
			||||||
                Route::get('/get', [DiscountController::class, 'get']);
 | 
					 | 
				
			||||||
                Route::post('/create', [DiscountController::class, 'create']);
 | 
					 | 
				
			||||||
                Route::post('/update', [DiscountController::class, 'update']);
 | 
					 | 
				
			||||||
                Route::get('/delete', [DiscountController::class, 'delete']);
 | 
					 | 
				
			||||||
                Route::post('/updates', [DiscountController::class, 'updates']);
 | 
					 | 
				
			||||||
                Route::post('/deletes', [DiscountController::class, 'deletes']);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'client',
 | 
					                'prefix' => 'client',
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
| 
						 | 
					@ -99,25 +72,6 @@ Route::middleware('api')
 | 
				
			||||||
                Route::post('/deletes', [BannerController::class, 'deletes']);
 | 
					                Route::post('/deletes', [BannerController::class, 'deletes']);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					 | 
				
			||||||
                'prefix' => 'order',
 | 
					 | 
				
			||||||
            ], function () {
 | 
					 | 
				
			||||||
                Route::get('/get', [OrderController::class, 'get']);
 | 
					 | 
				
			||||||
                Route::post('/update', [OrderController::class, 'update']);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Route::group([
 | 
					 | 
				
			||||||
                'prefix' => 'sn-check-history',
 | 
					 | 
				
			||||||
            ], function () {
 | 
					 | 
				
			||||||
                Route::get('/get', [SNCheckController::class, 'get']);
 | 
					 | 
				
			||||||
                Route::get('/show-detail', [SNCheckController::class, 'showDetail']);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Route::group([
 | 
					 | 
				
			||||||
                'prefix' => 'discount-type',
 | 
					 | 
				
			||||||
            ], function () {
 | 
					 | 
				
			||||||
                Route::get('/all', [DiscountTypeController::class, 'all']);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'custom-theme',
 | 
					                'prefix' => 'custom-theme',
 | 
				
			||||||
| 
						 | 
					@ -130,11 +84,6 @@ Route::middleware('api')
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
                Route::get('/', [CountryController::class, 'all']);
 | 
					                Route::get('/', [CountryController::class, 'all']);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            Route::group([
 | 
					 | 
				
			||||||
                'prefix' => 'contact',
 | 
					 | 
				
			||||||
            ], function () {
 | 
					 | 
				
			||||||
                Route::get('/get', [ContactController::class, 'get']);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'dashboard',
 | 
					                'prefix' => 'dashboard',
 | 
				
			||||||
| 
						 | 
					@ -146,13 +95,26 @@ Route::middleware('api')
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'jira',
 | 
					                'prefix' => 'jira',
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                Route::get('/fetch-issues', [JiraController::class, 'fetchAllIssues']);
 | 
					                Route::get('/fetch-issues', [JiraController::class, 'fetchAllIssues']);
 | 
				
			||||||
                Route::get('/export-issues', [JiraController::class, 'exportToExcel']);
 | 
					                Route::get('/export-issues', [JiraController::class, 'exportToExcel']);
 | 
				
			||||||
                Route::get('/all-project', [JiraController::class, 'getAllProject']);
 | 
					                Route::get('/all-project', [JiraController::class, 'getAllProject']);
 | 
				
			||||||
                Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']);
 | 
					                Route::get('/all-issue-by-project', [JiraController::class, 'fetchIssuesByProject']);
 | 
				
			||||||
                Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs']);
 | 
					                Route::get('/worklogs', [JiraController::class, 'getAllUserWorkLogs']);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Route::group([
 | 
				
			||||||
 | 
					                'prefix' => 'timekeeping',
 | 
				
			||||||
 | 
					            ], function () {
 | 
				
			||||||
 | 
					                Route::get('/', [TimekeepingController::class, 'get']);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Route::group([
 | 
				
			||||||
 | 
					                'prefix' => 'tracking',
 | 
				
			||||||
 | 
					            ], function () {
 | 
				
			||||||
 | 
					                Route::post('/create', [TrackingController::class, 'create']);
 | 
				
			||||||
 | 
					                Route::post('/update', [TrackingController::class, 'update']);
 | 
				
			||||||
 | 
					                Route::get('/delete', [TrackingController::class, 'delete']);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,7 +123,6 @@ Route::middleware('api')
 | 
				
			||||||
    ], function () {
 | 
					    ], function () {
 | 
				
			||||||
        Route::get('/', [TrackingController::class, 'get']);
 | 
					        Route::get('/', [TrackingController::class, 'get']);
 | 
				
			||||||
        Route::post('/scan-create', [TrackingController::class, 'create']);
 | 
					        Route::post('/scan-create', [TrackingController::class, 'create']);
 | 
				
			||||||
        Route::get('/delete', [TrackingController::class, 'delete']);
 | 
					 | 
				
			||||||
        // Route::get('/clear-cache', [SettingController::class, 'clearCache']);
 | 
					        // Route::get('/clear-cache', [SettingController::class, 'clearCache']);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,11 +53,16 @@ export const statisticRevenuesByMonth = API_URL + 'v1/admin/dashboard/statistics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Tracking
 | 
					// Tracking
 | 
				
			||||||
export const getListTracking = API_URL + 'v1/admin/tracking'
 | 
					export const getListTracking = API_URL + 'v1/admin/tracking'
 | 
				
			||||||
 | 
					export const addTracking = API_URL + 'v1/admin/tracking/create'
 | 
				
			||||||
 | 
					export const updateTracking = API_URL + 'v1/admin/tracking/update'
 | 
				
			||||||
export const deleteTracking = API_URL + 'v1/admin/tracking/delete'
 | 
					export const deleteTracking = API_URL + 'v1/admin/tracking/delete'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// // Tracking
 | 
					// Worklogs
 | 
				
			||||||
export const fetchAllIssues = API_URL + 'v1/admin/jira/fetch-issues'
 | 
					export const fetchAllIssues = API_URL + 'v1/admin/jira/fetch-issues'
 | 
				
			||||||
export const exportIssues = API_URL + 'v1/admin/jira/export-issues'
 | 
					export const exportIssues = API_URL + 'v1/admin/jira/export-issues'
 | 
				
			||||||
export const getAllProjects = API_URL + 'v1/admin/jira/all-project'
 | 
					export const getAllProjects = API_URL + 'v1/admin/jira/all-project'
 | 
				
			||||||
export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-project'
 | 
					export const getAllIssuesByProject = API_URL + 'v1/admin/jira/all-issue-by-project'
 | 
				
			||||||
export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs'
 | 
					export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Timekeeping
 | 
				
			||||||
 | 
					export const getTheTimesheet = API_URL + 'v1/admin/timekeeping'
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@ import {
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  IconBrush,
 | 
					  IconBrush,
 | 
				
			||||||
 | 
					  IconCalendar,
 | 
				
			||||||
  IconLayoutSidebarLeftExpand,
 | 
					  IconLayoutSidebarLeftExpand,
 | 
				
			||||||
  IconLayoutSidebarRightExpand,
 | 
					  IconLayoutSidebarRightExpand,
 | 
				
			||||||
  IconLogout,
 | 
					  IconLogout,
 | 
				
			||||||
| 
						 | 
					@ -38,6 +39,7 @@ import classes from './NavbarSimpleColored.module.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const data = [
 | 
					const data = [
 | 
				
			||||||
  // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
 | 
					  // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
 | 
				
			||||||
 | 
					  { link: '/timekeeping', label: 'Timekeeping', icon: IconCalendar },
 | 
				
			||||||
  { link: '/tracking', label: 'Check in/out', icon: IconScan },
 | 
					  { link: '/tracking', label: 'Check in/out', icon: IconScan },
 | 
				
			||||||
  { link: '/worklogs', label: 'Worklogs', icon: IconReport },
 | 
					  { link: '/worklogs', label: 'Worklogs', icon: IconReport },
 | 
				
			||||||
  { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
					  { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,51 @@
 | 
				
			||||||
 | 
					.title {
 | 
				
			||||||
 | 
					    background-color: light-dark(var(white), var(--mantine-color-dark-7));
 | 
				
			||||||
 | 
					    z-index: 100;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    justify-content: space-between;
 | 
				
			||||||
 | 
					    padding: 0 var(--mantine-spacing-sm) var(--mantine-spacing-lg)
 | 
				
			||||||
 | 
					      var(--mantine-spacing-sm);
 | 
				
			||||||
 | 
					    border-bottom: solid rgba(201, 201, 201, 0.377) 1px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .optionIcon {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    justify-content: space-evenly;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleteIcon {
 | 
				
			||||||
 | 
					    color: red;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    padding: 2px;
 | 
				
			||||||
 | 
					    border-radius: 25%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .deleteIcon:hover {
 | 
				
			||||||
 | 
					    background-color: rgba(203, 203, 203, 0.809);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .editIcon {
 | 
				
			||||||
 | 
					    color: rgb(9, 132, 132);
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    padding: 2px;
 | 
				
			||||||
 | 
					    border-radius: 25%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .editIcon:hover {
 | 
				
			||||||
 | 
					    background-color: rgba(203, 203, 203, 0.809);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .dialog {
 | 
				
			||||||
 | 
					    background-color: light-dark(white, #2d353c);
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    border: solid 1px rgb(255, 145, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .dialogText {
 | 
				
			||||||
 | 
					    color: light-dark(#2d353c, white);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .tableTr:hover {
 | 
				
			||||||
 | 
					    background-color: rgb(205, 255, 255);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,239 @@
 | 
				
			||||||
 | 
					import { getTheTimesheet } from '@/api/Admin'
 | 
				
			||||||
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					import { Box, 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'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface User {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  email: string
 | 
				
			||||||
 | 
					  email_verified_at: string | null
 | 
				
			||||||
 | 
					  permission: string
 | 
				
			||||||
 | 
					  remember_token: string | null
 | 
				
			||||||
 | 
					  created_at: string | null
 | 
				
			||||||
 | 
					  updated_at: string | null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface HistoryValue {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  user_id: number
 | 
				
			||||||
 | 
					  status: string
 | 
				
			||||||
 | 
					  time_string: string
 | 
				
			||||||
 | 
					  created_at: string
 | 
				
			||||||
 | 
					  updated_at: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface History {
 | 
				
			||||||
 | 
					  values: HistoryValue[]
 | 
				
			||||||
 | 
					  total: number
 | 
				
			||||||
 | 
					  day: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface UserData {
 | 
				
			||||||
 | 
					  user: User
 | 
				
			||||||
 | 
					  history: History[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Timekeeping = () => {
 | 
				
			||||||
 | 
					  let permission = ['staff']
 | 
				
			||||||
 | 
					  const [daysInMonth, setDaysInMonth] = useState(
 | 
				
			||||||
 | 
					    Array.from({ length: 31 }, (_, index) => index + 1),
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  const [data, setData] = useState<UserData[]>([])
 | 
				
			||||||
 | 
					  const [date, setDate] = useState({
 | 
				
			||||||
 | 
					    month: (new Date().getMonth() + 1).toString(),
 | 
				
			||||||
 | 
					    year: new Date().getFullYear().toString(),
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getTimeSheet = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await get(getTheTimesheet, {
 | 
				
			||||||
 | 
					        month: date.month,
 | 
				
			||||||
 | 
					        year: date.year,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        setData(res.data.filter((u:UserData)=>permission.includes(u.user.permission)))
 | 
				
			||||||
 | 
					        setDaysInMonth(
 | 
				
			||||||
 | 
					          Array.from({ length: getDaysInMonth() }, (_, index) => index + 1),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function getDayName(dateString: string) {
 | 
				
			||||||
 | 
					    // Tạo đối tượng Date từ chuỗi ngày tháng năm
 | 
				
			||||||
 | 
					    const date = new Date(dateString)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Lấy ngày trong tuần (0-6)
 | 
				
			||||||
 | 
					    const dayNumber = date.getDay()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Mảng chứa các tên ngày bằng tiếng Việt
 | 
				
			||||||
 | 
					    const daysInVietnamese = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Trả về tên ngày bằng tiếng Việt
 | 
				
			||||||
 | 
					    return daysInVietnamese[dayNumber]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function getDaysInMonth() {
 | 
				
			||||||
 | 
					    // Tạo một đối tượng Date cho ngày đầu tiên của tháng tiếp theo
 | 
				
			||||||
 | 
					    // Chú ý: tháng trong đối tượng Date là từ 0 (tháng 1) đến 11 (tháng 12)
 | 
				
			||||||
 | 
					    const days = new Date(parseInt(date.year), parseInt(date.month), 0)
 | 
				
			||||||
 | 
					    // Trả về ngày, tức là số ngày trong tháng
 | 
				
			||||||
 | 
					    return days.getDate()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getTimeSheet()
 | 
				
			||||||
 | 
					  }, [date])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <div className={classes.title}>
 | 
				
			||||||
 | 
					        <h3>
 | 
				
			||||||
 | 
					          <Text>Admin/</Text>
 | 
				
			||||||
 | 
					          Timekeeping
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <Box w="50%" display={'flex'}>
 | 
				
			||||||
 | 
					        <Select
 | 
				
			||||||
 | 
					          w="10%"
 | 
				
			||||||
 | 
					          value={date.month}
 | 
				
			||||||
 | 
					          size='xs'
 | 
				
			||||||
 | 
					          label="Month"
 | 
				
			||||||
 | 
					          data={Array.from({ length: 12 }, (_, index) =>
 | 
				
			||||||
 | 
					            (1 + index).toString(),
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setDate({ ...date, month: e! })
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        ></Select>
 | 
				
			||||||
 | 
					        <Select
 | 
				
			||||||
 | 
					          w="10%"
 | 
				
			||||||
 | 
					          value={date.year}
 | 
				
			||||||
 | 
					          size='xs'
 | 
				
			||||||
 | 
					          ml={'sm'}
 | 
				
			||||||
 | 
					          label="Year"
 | 
				
			||||||
 | 
					          data={Array.from({ length: 20 }, (_, index) =>
 | 
				
			||||||
 | 
					            (2023 + 1 + index).toString(),
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setDate({ ...date, year: e! })
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        ></Select>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					      <Box>
 | 
				
			||||||
 | 
					        <Table striped highlightOnHover withTableBorder withColumnBorders mt={'md'}>
 | 
				
			||||||
 | 
					          <Table.Thead>
 | 
				
			||||||
 | 
					            <Table.Tr bg={'#228be66b'}>
 | 
				
			||||||
 | 
					              <Table.Th>Day</Table.Th>
 | 
				
			||||||
 | 
					              {daysInMonth.map((d) => {
 | 
				
			||||||
 | 
					                return <Table.Th key={d} ta={'center'}>{d}</Table.Th>
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					            </Table.Tr>
 | 
				
			||||||
 | 
					            <Table.Tr bg={'#228be66b'}>
 | 
				
			||||||
 | 
					              <Table.Th></Table.Th>
 | 
				
			||||||
 | 
					              {daysInMonth.map((d) => {
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                  <Table.Th key={d} ta={'center'}>
 | 
				
			||||||
 | 
					                    {getDayName(`${date.year}-${date.month}-${d}`)}
 | 
				
			||||||
 | 
					                  </Table.Th>
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					            </Table.Tr>
 | 
				
			||||||
 | 
					          </Table.Thead>
 | 
				
			||||||
 | 
					          <Table.Tbody>
 | 
				
			||||||
 | 
					            {data.map((user) => {
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <Table.Tr
 | 
				
			||||||
 | 
					                  key={user.user.id}
 | 
				
			||||||
 | 
					                  className={classes.tableTr}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Table.Td >{user.user.name}</Table.Td>
 | 
				
			||||||
 | 
					                  {daysInMonth.map((d) => {
 | 
				
			||||||
 | 
					                    var total =
 | 
				
			||||||
 | 
					                      user.history.find((h) => h.day === d)?.total ?? 0
 | 
				
			||||||
 | 
					                    return (
 | 
				
			||||||
 | 
					                      <Table.Td key={d} ta={'center'}>
 | 
				
			||||||
 | 
					                        {total / 60 / 60 < 8 && total !== 0 ? (
 | 
				
			||||||
 | 
					                          <Tooltip
 | 
				
			||||||
 | 
					                            multiline
 | 
				
			||||||
 | 
					                            label={
 | 
				
			||||||
 | 
					                              <div>
 | 
				
			||||||
 | 
					                                {`Total: ${(total / 60 / 60).toFixed(1)}h`}
 | 
				
			||||||
 | 
					                                {user.history
 | 
				
			||||||
 | 
					                                  .find((h) => h.day === d)
 | 
				
			||||||
 | 
					                                  ?.values.map((v) => {
 | 
				
			||||||
 | 
					                                    return (
 | 
				
			||||||
 | 
					                                      <p>{v.status + ': ' + v.time_string}</p>
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                  })}
 | 
				
			||||||
 | 
					                              </div>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                          >
 | 
				
			||||||
 | 
					                            <IconCheck
 | 
				
			||||||
 | 
					                              size={20}
 | 
				
			||||||
 | 
					                              style={{
 | 
				
			||||||
 | 
					                                backgroundColor: 'orange',
 | 
				
			||||||
 | 
					                                color: 'white',
 | 
				
			||||||
 | 
					                                borderRadius:"5px",
 | 
				
			||||||
 | 
					                                padding: "2px",
 | 
				
			||||||
 | 
					                                fontWeight: 700
 | 
				
			||||||
 | 
					                              }}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                          </Tooltip>
 | 
				
			||||||
 | 
					                        ) : total >= 8 ? (
 | 
				
			||||||
 | 
					                          <Tooltip
 | 
				
			||||||
 | 
					                            multiline
 | 
				
			||||||
 | 
					                            label={
 | 
				
			||||||
 | 
					                              <div>
 | 
				
			||||||
 | 
					                                {`Total: ${(total / 60 / 60).toFixed(1)}h`}
 | 
				
			||||||
 | 
					                                {user.history
 | 
				
			||||||
 | 
					                                  .find((h) => h.day === d)
 | 
				
			||||||
 | 
					                                  ?.values.map((v) => {
 | 
				
			||||||
 | 
					                                    return (
 | 
				
			||||||
 | 
					                                      <p>{v.status + ': ' + v.time_string}</p>
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                  })}
 | 
				
			||||||
 | 
					                              </div>
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                          >
 | 
				
			||||||
 | 
					                            <IconCheck
 | 
				
			||||||
 | 
					                              size={20}
 | 
				
			||||||
 | 
					                              style={{
 | 
				
			||||||
 | 
					                                backgroundColor: 'green',
 | 
				
			||||||
 | 
					                                color: 'white',
 | 
				
			||||||
 | 
					                                borderRadius:"5px",
 | 
				
			||||||
 | 
					                                padding: "2px"
 | 
				
			||||||
 | 
					                              }}
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                          </Tooltip>
 | 
				
			||||||
 | 
					                        ) : (
 | 
				
			||||||
 | 
					                          <IconX
 | 
				
			||||||
 | 
					                            size={20}
 | 
				
			||||||
 | 
					                            style={{
 | 
				
			||||||
 | 
					                              backgroundColor: '#ff4646',
 | 
				
			||||||
 | 
					                              color: 'white',
 | 
				
			||||||
 | 
					                              borderRadius:"5px",
 | 
				
			||||||
 | 
					                              padding: "2px"
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
 | 
					                          />
 | 
				
			||||||
 | 
					                        )}
 | 
				
			||||||
 | 
					                      </Table.Td>
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                  })}
 | 
				
			||||||
 | 
					                </Table.Tr>
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					          </Table.Tbody>
 | 
				
			||||||
 | 
					        </Table>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Timekeeping
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,18 @@
 | 
				
			||||||
    border-radius: 25%;
 | 
					    border-radius: 25%;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  .editIcon {
 | 
				
			||||||
 | 
					    color: rgb(9, 132, 132);
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    padding: 2px;
 | 
				
			||||||
 | 
					    border-radius: 25%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .editIcon:hover {
 | 
				
			||||||
 | 
					    background-color: rgba(203, 203, 203, 0.809);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  .deleteIcon:hover {
 | 
					  .deleteIcon:hover {
 | 
				
			||||||
    background-color: rgba(203, 203, 203, 0.809);
 | 
					    background-color: rgba(203, 203, 203, 0.809);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,55 @@
 | 
				
			||||||
import { deleteTracking, getListTracking } from "@/api/Admin"
 | 
					import { addTracking, deleteTracking, getListTracking, updateTracking } from '@/api/Admin'
 | 
				
			||||||
import { DataTablePagination } from "@/components/DataTable/DataTable"
 | 
					import { DataTablePagination } from '@/components/DataTable/DataTable'
 | 
				
			||||||
import { Xdelete } from "@/rtk/helpers/CRUD"
 | 
					import { Xdelete, create, update } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
import { get } from "@/rtk/helpers/apiService"
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
import { Box, Button, Dialog, Group, Text } from "@mantine/core"
 | 
					import {
 | 
				
			||||||
import { IconTrash } from "@tabler/icons-react"
 | 
					  Box,
 | 
				
			||||||
import moment from "moment"
 | 
					  Button,
 | 
				
			||||||
import { useEffect, useState } from "react"
 | 
					  Dialog,
 | 
				
			||||||
 | 
					  Group,
 | 
				
			||||||
 | 
					  Modal,
 | 
				
			||||||
 | 
					  Select,
 | 
				
			||||||
 | 
					  Text
 | 
				
			||||||
 | 
					} from '@mantine/core'
 | 
				
			||||||
 | 
					import { DateTimePicker } from '@mantine/dates'
 | 
				
			||||||
 | 
					import { useForm } from '@mantine/form'
 | 
				
			||||||
 | 
					import { IconEdit, IconTrash } from '@tabler/icons-react'
 | 
				
			||||||
 | 
					import moment from 'moment'
 | 
				
			||||||
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
import classes from './Tracking.module.css'
 | 
					import classes from './Tracking.module.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TLog = {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  status: string
 | 
				
			||||||
 | 
					  time_string: Date
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TListTracking = {
 | 
				
			||||||
 | 
					  data: TLog[]
 | 
				
			||||||
 | 
					  users: User[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface User {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  email: string
 | 
				
			||||||
 | 
					  email_verified_at: string | null
 | 
				
			||||||
 | 
					  permission: string
 | 
				
			||||||
 | 
					  remember_token: string | null
 | 
				
			||||||
 | 
					  created_at: string | null
 | 
				
			||||||
 | 
					  updated_at: string | null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Tracking = () => {
 | 
					const Tracking = () => {
 | 
				
			||||||
  const [listTracking, setListTracking] = useState({
 | 
					  const [listTracking, setListTracking] = useState<TListTracking>({
 | 
				
			||||||
    data: []
 | 
					    data: [],
 | 
				
			||||||
 | 
					    users: [],
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  const [action, setAction] = useState('')
 | 
					  const [action, setAction] = useState('')
 | 
				
			||||||
  const [item, setItem] = useState({id: 0})
 | 
					  const [item, setItem] = useState({ id: 0 })
 | 
				
			||||||
  const [activeBtn, setActiveBtn] = useState(false)
 | 
					  const [activeBtn, setActiveBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
  const columns = [
 | 
					  const columns = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'id',
 | 
					      name: 'id',
 | 
				
			||||||
| 
						 | 
					@ -40,7 +76,7 @@ const Tracking = () => {
 | 
				
			||||||
      size: '15%',
 | 
					      size: '15%',
 | 
				
			||||||
      header: 'Created At',
 | 
					      header: 'Created At',
 | 
				
			||||||
      render: (row: any) => {
 | 
					      render: (row: any) => {
 | 
				
			||||||
        return moment(row.updated_at).format('YYYY/MM/DD - HH:mm:ss')
 | 
					        return moment(row.created_at).format('YYYY/MM/DD - HH:mm:ss')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -49,9 +85,7 @@ const Tracking = () => {
 | 
				
			||||||
      header: 'Action',
 | 
					      header: 'Action',
 | 
				
			||||||
      render: (row: any) => {
 | 
					      render: (row: any) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
          <Box
 | 
					          <Box className={classes.optionIcon}>
 | 
				
			||||||
            className={classes.optionIcon}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <IconTrash
 | 
					            <IconTrash
 | 
				
			||||||
              className={classes.deleteIcon}
 | 
					              className={classes.deleteIcon}
 | 
				
			||||||
              onClick={() => {
 | 
					              onClick={() => {
 | 
				
			||||||
| 
						 | 
					@ -61,6 +95,16 @@ const Tracking = () => {
 | 
				
			||||||
              width={20}
 | 
					              width={20}
 | 
				
			||||||
              height={20}
 | 
					              height={20}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					            <IconEdit
 | 
				
			||||||
 | 
					              className={classes.editIcon}
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setAction('edit')
 | 
				
			||||||
 | 
					                console.log(row)
 | 
				
			||||||
 | 
					                form.setValues(row)
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              width={20}
 | 
				
			||||||
 | 
					              height={20}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
          </Box>
 | 
					          </Box>
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
| 
						 | 
					@ -85,7 +129,17 @@ const Tracking = () => {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getAllTracking = async () =>{
 | 
					  const form = useForm({
 | 
				
			||||||
 | 
					    initialValues: {
 | 
				
			||||||
 | 
					      id: 0,
 | 
				
			||||||
 | 
					      name: '',
 | 
				
			||||||
 | 
					      time_string: new Date(),
 | 
				
			||||||
 | 
					      status: '',
 | 
				
			||||||
 | 
					      created_at: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getAllTracking = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const searchParams = new URLSearchParams(window.location.search)
 | 
					      const searchParams = new URLSearchParams(window.location.search)
 | 
				
			||||||
      const params = {}
 | 
					      const params = {}
 | 
				
			||||||
| 
						 | 
					@ -99,7 +153,7 @@ const Tracking = () => {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const res = await get(getListTracking, params)
 | 
					      const res = await get(getListTracking, params)
 | 
				
			||||||
      if(res.status){
 | 
					      if (res.status) {
 | 
				
			||||||
        setListTracking(res)
 | 
					        setListTracking(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
| 
						 | 
					@ -107,6 +161,41 @@ const Tracking = () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleCreate = async (values: TLog) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await create(addTracking, {
 | 
				
			||||||
 | 
					        status: values.status,
 | 
				
			||||||
 | 
					        name: values.name,
 | 
				
			||||||
 | 
					        time_string: moment(values.time_string).format('YYYY-MM-DD HH:mm:ss'),
 | 
				
			||||||
 | 
					        created_at: values.time_string
 | 
				
			||||||
 | 
					      }, getAllTracking)
 | 
				
			||||||
 | 
					      if (res === true) {
 | 
				
			||||||
 | 
					        setAction('')
 | 
				
			||||||
 | 
					        form.reset()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleUpdate = async (values: TLog) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await update(updateTracking, {
 | 
				
			||||||
 | 
					        status: values.status,
 | 
				
			||||||
 | 
					        name: values.name,
 | 
				
			||||||
 | 
					        time_string: moment(values.time_string).format('YYYY-MM-DD HH:mm:ss'),
 | 
				
			||||||
 | 
					        created_at: new Date(values.time_string),
 | 
				
			||||||
 | 
					        id: values.id
 | 
				
			||||||
 | 
					      }, getAllTracking)
 | 
				
			||||||
 | 
					      if (res === true) {
 | 
				
			||||||
 | 
					        setAction('')
 | 
				
			||||||
 | 
					        form.reset()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleDelete = async (id: number) => {
 | 
					  const handleDelete = async (id: number) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await Xdelete(deleteTracking, { id: id }, getAllTracking)
 | 
					      await Xdelete(deleteTracking, { id: id }, getAllTracking)
 | 
				
			||||||
| 
						 | 
					@ -115,15 +204,15 @@ const Tracking = () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(()=>{
 | 
					  useEffect(() => {
 | 
				
			||||||
    if(listTracking.data.length === 0){
 | 
					    if (listTracking.data.length === 0) {
 | 
				
			||||||
      getAllTracking()
 | 
					      getAllTracking()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    setInterval(()=>{
 | 
					    // setInterval(() => {
 | 
				
			||||||
      if(window.location.pathname.includes('tracking')){
 | 
					    //   if (window.location.pathname.includes('tracking')) {
 | 
				
			||||||
        getAllTracking()
 | 
					    //     getAllTracking()
 | 
				
			||||||
      }
 | 
					    //   }
 | 
				
			||||||
    }, 7000)
 | 
					    // }, 7000)
 | 
				
			||||||
  }, [])
 | 
					  }, [])
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
| 
						 | 
					@ -132,28 +221,101 @@ const Tracking = () => {
 | 
				
			||||||
          <Text>Admin/</Text>
 | 
					          <Text>Admin/</Text>
 | 
				
			||||||
          Tracking
 | 
					          Tracking
 | 
				
			||||||
        </h3>
 | 
					        </h3>
 | 
				
			||||||
        {/* <Button
 | 
					        <Button
 | 
				
			||||||
          m={5}
 | 
					          m={5}
 | 
				
			||||||
          onClick={() => {
 | 
					          onClick={() => {
 | 
				
			||||||
            setAction('add')
 | 
					            setAction('add')
 | 
				
			||||||
            form.reset()
 | 
					            form.reset()
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
        > */}
 | 
					        >
 | 
				
			||||||
          {/* Add discount */}
 | 
					          + Add
 | 
				
			||||||
        {/* </Button> */}
 | 
					        </Button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <Box p={20}>
 | 
					      <Box p={20}>
 | 
				
			||||||
        {
 | 
					        {listTracking.data.length > 0 && (
 | 
				
			||||||
          listTracking.data.length>0 && <DataTablePagination
 | 
					          <DataTablePagination
 | 
				
			||||||
          filterInfo={filterInfo}
 | 
					            filterInfo={filterInfo}
 | 
				
			||||||
          data={listTracking}
 | 
					            data={listTracking}
 | 
				
			||||||
          columns={columns}
 | 
					            columns={columns}
 | 
				
			||||||
          searchInput
 | 
					            searchInput
 | 
				
			||||||
          size=""
 | 
					            size=""
 | 
				
			||||||
        />
 | 
					          />
 | 
				
			||||||
        }
 | 
					        )}
 | 
				
			||||||
      </Box>
 | 
					      </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Add/Edit User modal */}
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        opened={action === 'add' || action === 'edit'}
 | 
				
			||||||
 | 
					        onClose={() => {
 | 
				
			||||||
 | 
					          setAction('')
 | 
				
			||||||
 | 
					          form.reset()
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        title={
 | 
				
			||||||
 | 
					          <Text pl={'sm'} fw={700} fz={'lg'}>
 | 
				
			||||||
 | 
					            {action === 'add' ? 'Add log' : 'Update log'}
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <form
 | 
				
			||||||
 | 
					          onSubmit={form.onSubmit(async (values) => {
 | 
				
			||||||
 | 
					            setDisableBtn(true)
 | 
				
			||||||
 | 
					            action === 'edit'
 | 
				
			||||||
 | 
					              ? await handleUpdate(values)
 | 
				
			||||||
 | 
					              : await handleCreate(values)
 | 
				
			||||||
 | 
					            setDisableBtn(false)
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Box pl={'md'} pr={'md'}>
 | 
				
			||||||
 | 
					            <Select
 | 
				
			||||||
 | 
					              label={'Name'}
 | 
				
			||||||
 | 
					              placeholder="Banner x"
 | 
				
			||||||
 | 
					              maxLength={255}
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					              data={listTracking.users.map((i: User) => i.name)}
 | 
				
			||||||
 | 
					              value={form.values.name}
 | 
				
			||||||
 | 
					              error={form.errors.name}
 | 
				
			||||||
 | 
					              onChange={(e) => form.setFieldValue('name', e!)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <Select
 | 
				
			||||||
 | 
					              label={'Status'}
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					              data={['check in', 'check out']}
 | 
				
			||||||
 | 
					              value={form.values.status}
 | 
				
			||||||
 | 
					              error={form.errors.status}
 | 
				
			||||||
 | 
					              onChange={(e) => form.setFieldValue('status', e!)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <DateTimePicker
 | 
				
			||||||
 | 
					              label={'Time'}
 | 
				
			||||||
 | 
					              value={new Date(form.values.time_string)}
 | 
				
			||||||
 | 
					              onChange={(e) => form.setFieldValue('time_string', e!)}
 | 
				
			||||||
 | 
					            ></DateTimePicker>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <Box ta={'center'}>
 | 
				
			||||||
 | 
					              {action === 'add' ? (
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  mt={'lg'}
 | 
				
			||||||
 | 
					                  bg={'green'}
 | 
				
			||||||
 | 
					                  type="submit"
 | 
				
			||||||
 | 
					                  disabled={disableBtn}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  Create
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              ) : (
 | 
				
			||||||
 | 
					                <Button
 | 
				
			||||||
 | 
					                  mt={'lg'}
 | 
				
			||||||
 | 
					                  bg={'green'}
 | 
				
			||||||
 | 
					                  type="submit"
 | 
				
			||||||
 | 
					                  disabled={disableBtn}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  Save
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Dialog
 | 
					      <Dialog
 | 
				
			||||||
        className={classes.dialog}
 | 
					        className={classes.dialog}
 | 
				
			||||||
        opened={action === 'delete'}
 | 
					        opened={action === 'delete'}
 | 
				
			||||||
| 
						 | 
					@ -195,4 +357,4 @@ const Tracking = () => {
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Tracking
 | 
					export default Tracking
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -221,7 +221,7 @@ const Worklogs = () => {
 | 
				
			||||||
              label="From date:"
 | 
					              label="From date:"
 | 
				
			||||||
              value={new Date(date.startDate)}
 | 
					              value={new Date(date.startDate)}
 | 
				
			||||||
              w={'20%'}
 | 
					              w={'20%'}
 | 
				
			||||||
              clearable
 | 
					              // clearable
 | 
				
			||||||
              onChange={(e) => {
 | 
					              onChange={(e) => {
 | 
				
			||||||
                setDate({ ...date, startDate: moment(e).format('YYYY-MM-DD') })
 | 
					                setDate({ ...date, startDate: moment(e).format('YYYY-MM-DD') })
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
| 
						 | 
					@ -230,7 +230,8 @@ const Worklogs = () => {
 | 
				
			||||||
              size="xs"
 | 
					              size="xs"
 | 
				
			||||||
              label="To date:"
 | 
					              label="To date:"
 | 
				
			||||||
              value={new Date(date.endDate)}
 | 
					              value={new Date(date.endDate)}
 | 
				
			||||||
              clearable
 | 
					              
 | 
				
			||||||
 | 
					              // clearable
 | 
				
			||||||
              m={'0 10px'}
 | 
					              m={'0 10px'}
 | 
				
			||||||
              w={'20%'}
 | 
					              w={'20%'}
 | 
				
			||||||
              onChange={(e) => {
 | 
					              onChange={(e) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import Dashboard from '@/pages/Dashboard/Dashboard'
 | 
				
			||||||
import GeneralSetting from '@/pages/GeneralSetting/GeneralSetting'
 | 
					import GeneralSetting from '@/pages/GeneralSetting/GeneralSetting'
 | 
				
			||||||
import Jira from '@/pages/Jira/Jira'
 | 
					import Jira from '@/pages/Jira/Jira'
 | 
				
			||||||
import PageNotFound from '@/pages/NotFound/NotFound'
 | 
					import PageNotFound from '@/pages/NotFound/NotFound'
 | 
				
			||||||
 | 
					import Timekeeping from '@/pages/Timekeeping/Timekeeping'
 | 
				
			||||||
import Tracking from '@/pages/Tracking/Tracking'
 | 
					import Tracking from '@/pages/Tracking/Tracking'
 | 
				
			||||||
import PageWelcome from '@/pages/Welcome/Welcome'
 | 
					import PageWelcome from '@/pages/Welcome/Welcome'
 | 
				
			||||||
import Worklogs from '@/pages/Worklogs/Worklogs'
 | 
					import Worklogs from '@/pages/Worklogs/Worklogs'
 | 
				
			||||||
| 
						 | 
					@ -19,7 +20,7 @@ const mainRoutes = [
 | 
				
			||||||
    // element: <ProtectedRoute mode="home"><PageHome /></ProtectedRoute>,
 | 
					    // element: <ProtectedRoute mode="home"><PageHome /></ProtectedRoute>,
 | 
				
			||||||
    element: (
 | 
					    element: (
 | 
				
			||||||
      <ProtectedRoute mode="home">
 | 
					      <ProtectedRoute mode="home">
 | 
				
			||||||
        <Navigate to="/tracking"></Navigate>
 | 
					        <Navigate to="/timekeeping"></Navigate>
 | 
				
			||||||
      </ProtectedRoute>
 | 
					      </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
| 
						 | 
					@ -115,6 +116,20 @@ const mainRoutes = [
 | 
				
			||||||
      </ProtectedRoute>
 | 
					      </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/timekeeping',
 | 
				
			||||||
 | 
					    element: (
 | 
				
			||||||
 | 
					      <ProtectedRoute mode="route">
 | 
				
			||||||
 | 
					        <BasePage
 | 
				
			||||||
 | 
					          main={
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					              <Timekeeping />
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ></BasePage>
 | 
				
			||||||
 | 
					      </ProtectedRoute>
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  // {
 | 
					  // {
 | 
				
			||||||
  //   path: '/packages',
 | 
					  //   path: '/packages',
 | 
				
			||||||
  //   element: (
 | 
					  //   element: (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue