Thực hiện chức năng ngày phép
This commit is contained in:
		
							parent
							
								
									2cc8472bc6
								
							
						
					
					
						commit
						2fdd687fc9
					
				| 
						 | 
					@ -0,0 +1,125 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Modules\Admin\app\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Jobs\InitializeLeaveDays;
 | 
				
			||||||
 | 
					use App\Models\LeaveDays;
 | 
				
			||||||
 | 
					use App\Models\Notes;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LeaveManagementController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function get(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $yearNow = $request->query('year', now()->year);
 | 
				
			||||||
 | 
					        $year = $request->year ?? $yearNow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $leaveDays = self::getDataByYear($year);
 | 
				
			||||||
 | 
					        if ($leaveDays->count() == 0) {
 | 
				
			||||||
 | 
					            InitializeLeaveDays::dispatch($year);
 | 
				
			||||||
 | 
					            $leaveDays = self::getDataByYear($year);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return AbstractController::ResultSuccess($leaveDays);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getDataByYear($year)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
				
			||||||
 | 
					            $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
				
			||||||
 | 
					                ->where('categories.c_type', 'TIME_TYPE');
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            ->select(
 | 
				
			||||||
 | 
					                DB::raw('notes.n_user_id as n_user_id'),
 | 
				
			||||||
 | 
					                DB::raw('notes.n_year as year'),
 | 
				
			||||||
 | 
					                DB::raw('notes.n_month as month'),
 | 
				
			||||||
 | 
					                DB::raw('SUM(categories.c_value) as leave_days')
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            ->where('notes.n_year', $year)
 | 
				
			||||||
 | 
					            ->where('notes.n_reason', 'ONLEAVE')
 | 
				
			||||||
 | 
					            ->groupBy("notes.n_user_id", DB::raw('notes.n_month'), DB::raw('notes.n_year'))
 | 
				
			||||||
 | 
					            ->get()
 | 
				
			||||||
 | 
					            ->map(function ($item) {
 | 
				
			||||||
 | 
					                return [
 | 
				
			||||||
 | 
					                    "n_user_id" => $item->n_user_id,
 | 
				
			||||||
 | 
					                    "month" => $item->month,
 | 
				
			||||||
 | 
					                    "leave_days" => $item->leave_days
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            ->toArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $leaveDays = LeaveDays::join('users', 'leave_days.ld_user_id', '=', 'users.id')
 | 
				
			||||||
 | 
					            ->select(
 | 
				
			||||||
 | 
					                'leave_days.*',
 | 
				
			||||||
 | 
					                'users.id as user_id',
 | 
				
			||||||
 | 
					                'users.name as user_name',
 | 
				
			||||||
 | 
					                'users.email',
 | 
				
			||||||
 | 
					                'users.created_at as user_created_at',
 | 
				
			||||||
 | 
					                'users.permission',
 | 
				
			||||||
 | 
					                'users.updated_at as user_updated_at',
 | 
				
			||||||
 | 
					                'users.remember_token',
 | 
				
			||||||
 | 
					                'users.email_verified_at'
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            ->where('leave_days.ld_year', $year)
 | 
				
			||||||
 | 
					            ->get()
 | 
				
			||||||
 | 
					            ->map(function ($item) use ($totalLeaveDaysByMonth) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $monthlyLeaveDays = [];
 | 
				
			||||||
 | 
					                foreach ($totalLeaveDaysByMonth as $key => $totalDays) {
 | 
				
			||||||
 | 
					                    if ($item->ld_user_id == $totalDays["n_user_id"]) {
 | 
				
			||||||
 | 
					                        $monthlyLeaveDays[] = $totalDays;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return [
 | 
				
			||||||
 | 
					                    'user' => [
 | 
				
			||||||
 | 
					                        'id' => $item->user_id,
 | 
				
			||||||
 | 
					                        'name' => $item->user_name,
 | 
				
			||||||
 | 
					                        'email' => $item->email,
 | 
				
			||||||
 | 
					                        'created_at' => $item->user_created_at,
 | 
				
			||||||
 | 
					                        'permission' => $item->permission,
 | 
				
			||||||
 | 
					                        'updated_at' => $item->user_updated_at,
 | 
				
			||||||
 | 
					                        'remember_token' => $item->remember_token,
 | 
				
			||||||
 | 
					                        'email_verified_at' => $item->email_verified_at,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'leaveDay' => [
 | 
				
			||||||
 | 
					                        'id' => $item->id,
 | 
				
			||||||
 | 
					                        'ld_user_id' => $item->ld_user_id,
 | 
				
			||||||
 | 
					                        'ld_day' => $item->ld_day,
 | 
				
			||||||
 | 
					                        'ld_year' => $item->ld_year,
 | 
				
			||||||
 | 
					                        'ld_date_additional' => $item->ld_date_additional,
 | 
				
			||||||
 | 
					                        'ld_note' => $item->ld_note,
 | 
				
			||||||
 | 
					                        'created_at' => $item->created_at,
 | 
				
			||||||
 | 
					                        'updated_at' => $item->updated_at,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'monthlyLeaveDays' => $monthlyLeaveDays,
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        return $leaveDays;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function saveNoteLeave(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $validator = Validator::make($request->all(), [
 | 
				
			||||||
 | 
					            'totalLeave' => 'required|numeric|min:0|max:20',
 | 
				
			||||||
 | 
					            // 'dayAdditional' => 'required|numeric|min:0|max:20',
 | 
				
			||||||
 | 
					            // 'note' => 'required|string|max:255',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($validator->fails()) {
 | 
				
			||||||
 | 
					            return AbstractController::ResultError($validator->errors());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $validatedData = $request->all();
 | 
				
			||||||
 | 
					        $leaveDays = LeaveDays::find($validatedData['id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $leaveDays->ld_day = $validatedData['totalLeave'];
 | 
				
			||||||
 | 
					        $leaveDays->ld_date_additional = $validatedData['dayAdditional']; // Assuming you have this field to store additional days
 | 
				
			||||||
 | 
					        $leaveDays->ld_note = $validatedData['note'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $leaveDays->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->json(['status' => true, 'message' => 'Updated successfully']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ 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\JiraController;
 | 
					use Modules\Admin\app\Http\Controllers\JiraController;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Http\Controllers\LeaveManagementController;
 | 
				
			||||||
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\TimekeepingController;
 | 
				
			||||||
use Modules\Admin\app\Http\Controllers\TrackingController;
 | 
					use Modules\Admin\app\Http\Controllers\TrackingController;
 | 
				
			||||||
| 
						 | 
					@ -126,6 +127,14 @@ Route::middleware('api')
 | 
				
			||||||
                Route::get('/get-list-master', [CategoryController::class, 'getListMaster']);
 | 
					                Route::get('/get-list-master', [CategoryController::class, 'getListMaster']);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Route::group([
 | 
				
			||||||
 | 
					                'prefix' => 'leave-management',
 | 
				
			||||||
 | 
					            ], function () {
 | 
				
			||||||
 | 
					                Route::get('/', [LeaveManagementController::class, 'get'])->middleware('check.permission:admin.hr.staff');
 | 
				
			||||||
 | 
					                Route::post('/saveNoteLeave', [LeaveManagementController::class, 'saveNoteLeave'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					use App\Jobs\DeductLeaveDays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeductLeaveDaysCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $signature = 'leave:deduct {year?}';
 | 
				
			||||||
 | 
					    protected $description = 'Trừ đi số ngày nghỉ thêm vào năm hiện tại';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new command instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the console command.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $year = $this->argument('year');
 | 
				
			||||||
 | 
					        DeductLeaveDays::dispatch($year);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					use App\Jobs\InitializeLeaveDays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InitializeLeaveDaysCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $signature = 'initialize:leavedays {year?}';
 | 
				
			||||||
 | 
					    protected $description = 'Initialize leave days for users';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $year = $this->argument('year');
 | 
				
			||||||
 | 
					        InitializeLeaveDays::dispatch($year);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2,20 +2,30 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace App\Console;
 | 
					namespace App\Console;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Jobs\DeductLeaveDays;
 | 
				
			||||||
use Illuminate\Console\Scheduling\Schedule;
 | 
					use Illuminate\Console\Scheduling\Schedule;
 | 
				
			||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
					use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Kernel extends ConsoleKernel
 | 
					class Kernel extends ConsoleKernel
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    protected $commands = [
 | 
				
			||||||
 | 
					        \App\Console\Commands\InitializeLeaveDaysCommand::class,
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Define the application's command schedule.
 | 
					     * Define the application's command schedule.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function schedule(Schedule $schedule): void
 | 
					    protected function schedule(Schedule $schedule): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $schedule->command('clean_logs')->daily();
 | 
					        $schedule->command('clean_logs')->daily();
 | 
				
			||||||
         // Chạy command 'daily:api-call' vào mỗi ngày lúc 9h sáng
 | 
					        // Chạy command 'daily:api-call' vào mỗi ngày lúc 9h sáng
 | 
				
			||||||
        // $schedule->command('daily:api-call')
 | 
					        // $schedule->command('daily:api-call')
 | 
				
			||||||
        // ->dailyAt('18:00');
 | 
					        // ->dailyAt('18:00');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Chạy command vào ngày 31/12 lúc 23:59:59 mỗi năm
 | 
				
			||||||
 | 
					        $schedule->command('initialize:leavedays')->yearlyOn(12, 31, '23:59:59');
 | 
				
			||||||
 | 
					        $schedule->command('leave:deduct')->yearlyOn(3, 31, '23:59:59');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -23,7 +33,7 @@ class Kernel extends ConsoleKernel
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected function commands(): void
 | 
					    protected function commands(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->load(__DIR__.'/Commands');
 | 
					        $this->load(__DIR__ . '/Commands');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        require base_path('routes/console.php');
 | 
					        require base_path('routes/console.php');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,78 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\LeaveDays;
 | 
				
			||||||
 | 
					use App\Models\Notes;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeductLeaveDays implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					    protected $year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new job instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct($year = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Nếu không có năm được truyền vào, sử dụng năm hiện tại
 | 
				
			||||||
 | 
					        $this->year = $year ?? Carbon::now()->year;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the job.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $users = User::get();
 | 
				
			||||||
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
 | 
					            $existingData = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('ld_year', $this->year)
 | 
				
			||||||
 | 
					                ->where('ld_date_additional', ">", 0)
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					            if (!$existingData) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
				
			||||||
 | 
					                $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
				
			||||||
 | 
					                    ->where('categories.c_type', 'TIME_TYPE');
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					                ->select(
 | 
				
			||||||
 | 
					                    DB::raw('notes.n_user_id as n_user_id'),
 | 
				
			||||||
 | 
					                    DB::raw('notes.n_year as year'),
 | 
				
			||||||
 | 
					                    DB::raw('SUM(categories.c_value) as leave_days')
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                ->where('notes.n_year', $this->year)
 | 
				
			||||||
 | 
					                ->where('notes.n_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('notes.n_reason', 'ONLEAVE')
 | 
				
			||||||
 | 
					                ->groupBy(DB::raw('notes.n_year'))
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            if ($totalLeaveDaysByMonth) {
 | 
				
			||||||
 | 
					                //Nếu ngày phép thừa năm trước chưa sử dụng hết => cập nhật lại ngày đó 
 | 
				
			||||||
 | 
					                if ($existingData->ld_date_additional > $totalLeaveDaysByMonth->leave_days) {
 | 
				
			||||||
 | 
					                    LeaveDays::where('ld_year', $this->year)
 | 
				
			||||||
 | 
					                        ->where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                        ->update([
 | 
				
			||||||
 | 
					                            'ld_date_additional' => $totalLeaveDaysByMonth->leave_days,
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                LeaveDays::where('ld_year', $this->year)
 | 
				
			||||||
 | 
					                    ->where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                    ->update([
 | 
				
			||||||
 | 
					                        'ld_date_additional' => "0",
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,94 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\LeaveDays;
 | 
				
			||||||
 | 
					use App\Models\Notes;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InitializeLeaveDays implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new job instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct($year = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Nếu không có năm được truyền vào, sử dụng năm hiện tại
 | 
				
			||||||
 | 
					        $this->year = $year ?? Carbon::now()->year;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the job.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $users = User::get();
 | 
				
			||||||
 | 
					        $ld_day = 12;
 | 
				
			||||||
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
 | 
					            // Kiểm tra xem dữ liệu của user này đã tồn tại cho năm hiện tại chưa
 | 
				
			||||||
 | 
					            $existingData = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('ld_year', $this->year)
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($existingData) {
 | 
				
			||||||
 | 
					                // Nếu dữ liệu đã tồn tại, bỏ qua user này
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Kiểm tra dữ liệu của user này trong năm trước
 | 
				
			||||||
 | 
					            $previousYearData = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('ld_year', $this->year - 1)
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $ld_date_additional = 0;
 | 
				
			||||||
 | 
					            $ld_note = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($previousYearData) {
 | 
				
			||||||
 | 
					                $ld_date_additional = $previousYearData->ld_day;
 | 
				
			||||||
 | 
					                $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
				
			||||||
 | 
					                    $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
				
			||||||
 | 
					                        ->where('categories.c_type', 'TIME_TYPE');
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                    ->select(
 | 
				
			||||||
 | 
					                        DB::raw('notes.n_user_id as n_user_id'),
 | 
				
			||||||
 | 
					                        DB::raw('notes.n_year as year'),
 | 
				
			||||||
 | 
					                        DB::raw('SUM(categories.c_value) as leave_days')
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    ->where('notes.n_year', $this->year - 1)
 | 
				
			||||||
 | 
					                    ->where('notes.n_user_id', $user->id)
 | 
				
			||||||
 | 
					                    ->where('notes.n_reason', 'ONLEAVE')
 | 
				
			||||||
 | 
					                    ->groupBy(DB::raw('notes.n_year'))
 | 
				
			||||||
 | 
					                    ->first();
 | 
				
			||||||
 | 
					                if ($totalLeaveDaysByMonth) {
 | 
				
			||||||
 | 
					                    $ld_date_additional = $ld_date_additional - $totalLeaveDaysByMonth->leave_days;
 | 
				
			||||||
 | 
					                    if ($ld_date_additional < 0) {
 | 
				
			||||||
 | 
					                        $ld_date_additional = 0;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $ld_note = 'Cộng dồn ngày phép năm cũ';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Tạo dữ liệu cho năm hiện tại
 | 
				
			||||||
 | 
					            LeaveDays::insert([
 | 
				
			||||||
 | 
					                'ld_user_id' => $user->id,
 | 
				
			||||||
 | 
					                'ld_day' => $ld_day,
 | 
				
			||||||
 | 
					                'ld_year' => $this->year,
 | 
				
			||||||
 | 
					                'ld_date_additional' => $ld_date_additional,
 | 
				
			||||||
 | 
					                'ld_note' => $ld_note,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LeaveDays extends Model
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $fillable = [
 | 
				
			||||||
 | 
					        'id', 'ld_user_id', 'ld_day', 'ld_year', 'ld_date_additional', 'ld_note'
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $table = 'leave_days';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,6 @@ return new class extends Migration
 | 
				
			||||||
            $table->float('ld_date_additional')->default(0);
 | 
					            $table->float('ld_date_additional')->default(0);
 | 
				
			||||||
            $table->text('ld_note')->nullable();
 | 
					            $table->text('ld_note')->nullable();
 | 
				
			||||||
            $table->timestamps();
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Nếu cần khóa ngoại, uncomment dòng dưới và điều chỉnh tên bảng và cột phù hợp
 | 
					 | 
				
			||||||
            // $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,4 +71,8 @@ export const updateNote = API_URL + 'v1/admin/timekeeping/addNote'
 | 
				
			||||||
export const updateWorkingDays = API_URL + 'v1/admin/timekeeping/update-working-days'
 | 
					export const updateWorkingDays = API_URL + 'v1/admin/timekeeping/update-working-days'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//Category
 | 
					//Category
 | 
				
			||||||
export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
 | 
					export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//LeaveManagement
 | 
				
			||||||
 | 
					export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
 | 
				
			||||||
 | 
					export const updateNoteLeave = API_URL + 'v1/admin/leave-management/saveNoteLeave'
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,7 @@ import {
 | 
				
			||||||
  Text,
 | 
					  Text,
 | 
				
			||||||
  TextInput,
 | 
					  TextInput,
 | 
				
			||||||
  useComputedColorScheme,
 | 
					  useComputedColorScheme,
 | 
				
			||||||
  useMantineColorScheme
 | 
					  useMantineColorScheme,
 | 
				
			||||||
} from '@mantine/core'
 | 
					} from '@mantine/core'
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,8 @@ import {
 | 
				
			||||||
  IconReport,
 | 
					  IconReport,
 | 
				
			||||||
  IconScan,
 | 
					  IconScan,
 | 
				
			||||||
  IconSubtask,
 | 
					  IconSubtask,
 | 
				
			||||||
  IconSun
 | 
					  IconSun,
 | 
				
			||||||
 | 
					  IconCalendarClock,
 | 
				
			||||||
} from '@tabler/icons-react'
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
import { useCallback, useEffect, useState } from 'react'
 | 
					import { useCallback, useEffect, useState } from 'react'
 | 
				
			||||||
import { useLocation, useNavigate } from 'react-router-dom'
 | 
					import { useLocation, useNavigate } from 'react-router-dom'
 | 
				
			||||||
| 
						 | 
					@ -39,6 +40,11 @@ const data = [
 | 
				
			||||||
  { link: '/timekeeping', label: 'Timekeeping', icon: IconCalendar },
 | 
					  { 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: '/leave-management',
 | 
				
			||||||
 | 
					    label: 'Leave Management',
 | 
				
			||||||
 | 
					    icon: IconCalendarClock,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  // { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
					  // { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
				
			||||||
  // { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
 | 
					  // { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
 | 
				
			||||||
  // { link: '/general-setting', label: 'General Setting', icon: IconSettings },
 | 
					  // { link: '/general-setting', label: 'General Setting', icon: IconSettings },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					.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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.popoverFooter {
 | 
				
			||||||
 | 
					  margin-top: 5px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.historyRow td {
 | 
				
			||||||
 | 
					  padding-top: 5px;
 | 
				
			||||||
 | 
					  padding-bottom: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,465 @@
 | 
				
			||||||
 | 
					import { getLeaveManagement, updateNoteLeave } from '@/api/Admin'
 | 
				
			||||||
 | 
					import { update } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Box,
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  Drawer,
 | 
				
			||||||
 | 
					  Menu,
 | 
				
			||||||
 | 
					  Select,
 | 
				
			||||||
 | 
					  Table,
 | 
				
			||||||
 | 
					  Text,
 | 
				
			||||||
 | 
					  Textarea,
 | 
				
			||||||
 | 
					  TextInput,
 | 
				
			||||||
 | 
					  Tooltip,
 | 
				
			||||||
 | 
					} from '@mantine/core'
 | 
				
			||||||
 | 
					import { useDisclosure } from '@mantine/hooks'
 | 
				
			||||||
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
 | 
					import moment from 'moment'
 | 
				
			||||||
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { IconEdit } from '@tabler/icons-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import classes from './LeaveManagement.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 LeaveDay {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  ld_user_id: number
 | 
				
			||||||
 | 
					  ld_year: number
 | 
				
			||||||
 | 
					  ld_day: number
 | 
				
			||||||
 | 
					  ld_date_additional: number
 | 
				
			||||||
 | 
					  ld_note: string
 | 
				
			||||||
 | 
					  created_at: string | null
 | 
				
			||||||
 | 
					  updated_at: string | null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface MonthlyLeaveDays {
 | 
				
			||||||
 | 
					  month: number
 | 
				
			||||||
 | 
					  leave_days: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface UserData {
 | 
				
			||||||
 | 
					  user: User
 | 
				
			||||||
 | 
					  leaveDay: LeaveDay
 | 
				
			||||||
 | 
					  monthlyLeaveDays: MonthlyLeaveDays[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const LeaveManagement = () => {
 | 
				
			||||||
 | 
					  const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
 | 
				
			||||||
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
 | 
					  const monthInYear = getMonthNames()
 | 
				
			||||||
 | 
					  const [customAddNotes, setCustomAddNotes] = useState<{
 | 
				
			||||||
 | 
					    id: number
 | 
				
			||||||
 | 
					    user: {
 | 
				
			||||||
 | 
					      id: number
 | 
				
			||||||
 | 
					      name: string
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    note: string
 | 
				
			||||||
 | 
					    totalLeave: string
 | 
				
			||||||
 | 
					    dayAdditional: string
 | 
				
			||||||
 | 
					  }>({
 | 
				
			||||||
 | 
					    id: 0,
 | 
				
			||||||
 | 
					    user: {
 | 
				
			||||||
 | 
					      id: 0,
 | 
				
			||||||
 | 
					      name: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    note: '',
 | 
				
			||||||
 | 
					    totalLeave: '',
 | 
				
			||||||
 | 
					    dayAdditional: '',
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [data, setData] = useState<UserData[]>([])
 | 
				
			||||||
 | 
					  const [date, setDate] = useState({
 | 
				
			||||||
 | 
					    year: new Date().getFullYear().toString(),
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  const getLeaveList = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await get(getLeaveManagement, {
 | 
				
			||||||
 | 
					        year: date.year,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        setData(
 | 
				
			||||||
 | 
					          res.data.filter((u: UserData) => u.user.permission.includes('staff')),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error: any) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					      notifications.show({
 | 
				
			||||||
 | 
					        title: 'Error',
 | 
				
			||||||
 | 
					        message: error.message ?? error,
 | 
				
			||||||
 | 
					        color: 'red',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getLeaveList()
 | 
				
			||||||
 | 
					  }, [date])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const updateInfoNote = async (
 | 
				
			||||||
 | 
					    id: number,
 | 
				
			||||||
 | 
					    users: {
 | 
				
			||||||
 | 
					      id: number
 | 
				
			||||||
 | 
					      name: string
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    totalLeave: string,
 | 
				
			||||||
 | 
					    dayAdditional: string,
 | 
				
			||||||
 | 
					    note: string,
 | 
				
			||||||
 | 
					  ) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await update(
 | 
				
			||||||
 | 
					        updateNoteLeave,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: id,
 | 
				
			||||||
 | 
					          users: users,
 | 
				
			||||||
 | 
					          totalLeave: totalLeave,
 | 
				
			||||||
 | 
					          dayAdditional: dayAdditional,
 | 
				
			||||||
 | 
					          note: note,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        getLeaveList,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setDisableBtn(false)
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function getMonthNames() {
 | 
				
			||||||
 | 
					    const monthNames = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 1,
 | 
				
			||||||
 | 
					        name: 'January',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 2,
 | 
				
			||||||
 | 
					        name: 'February',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 3,
 | 
				
			||||||
 | 
					        name: 'March',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 4,
 | 
				
			||||||
 | 
					        name: 'April',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 5,
 | 
				
			||||||
 | 
					        name: 'May',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 6,
 | 
				
			||||||
 | 
					        name: 'June',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 7,
 | 
				
			||||||
 | 
					        name: 'July',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 8,
 | 
				
			||||||
 | 
					        name: 'August',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 9,
 | 
				
			||||||
 | 
					        name: 'September',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 10,
 | 
				
			||||||
 | 
					        name: 'October',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 11,
 | 
				
			||||||
 | 
					        name: 'November',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        value: 12,
 | 
				
			||||||
 | 
					        name: 'December',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    return monthNames.map((month) => {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        value: month.value,
 | 
				
			||||||
 | 
					        name: month.name.substring(0, 3),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // console.log(customAddNotes, 'customAddNotes')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <div className={classes.title}>
 | 
				
			||||||
 | 
					        <h3>
 | 
				
			||||||
 | 
					          <Text>Admin/</Text> Leave Management
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <Drawer
 | 
				
			||||||
 | 
					        opened={opened1}
 | 
				
			||||||
 | 
					        onClose={close1}
 | 
				
			||||||
 | 
					        position="right"
 | 
				
			||||||
 | 
					        title={<strong>Update Day Leave</strong>}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <TextInput
 | 
				
			||||||
 | 
					          mb={'md'}
 | 
				
			||||||
 | 
					          value={customAddNotes.totalLeave}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            const value = e.target.value
 | 
				
			||||||
 | 
					            if (value) {
 | 
				
			||||||
 | 
					              const floatValue = parseFloat(value)
 | 
				
			||||||
 | 
					              if (
 | 
				
			||||||
 | 
					                /^\d*\.?\d?$/.test(value) &&
 | 
				
			||||||
 | 
					                floatValue >= 0 &&
 | 
				
			||||||
 | 
					                floatValue <= 20
 | 
				
			||||||
 | 
					              ) {
 | 
				
			||||||
 | 
					                setCustomAddNotes({
 | 
				
			||||||
 | 
					                  ...customAddNotes,
 | 
				
			||||||
 | 
					                  totalLeave: value,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              setCustomAddNotes({
 | 
				
			||||||
 | 
					                ...customAddNotes,
 | 
				
			||||||
 | 
					                totalLeave: value,
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          label={'Total Leave'}
 | 
				
			||||||
 | 
					          placeholder="Input placeholder"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <TextInput
 | 
				
			||||||
 | 
					          mb={'md'}
 | 
				
			||||||
 | 
					          value={customAddNotes.dayAdditional}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            const value = e.target.value
 | 
				
			||||||
 | 
					            if (value) {
 | 
				
			||||||
 | 
					              const floatValue = parseFloat(value)
 | 
				
			||||||
 | 
					              if (
 | 
				
			||||||
 | 
					                /^\d*\.?\d?$/.test(value) &&
 | 
				
			||||||
 | 
					                floatValue >= 0 &&
 | 
				
			||||||
 | 
					                floatValue <= 20
 | 
				
			||||||
 | 
					              ) {
 | 
				
			||||||
 | 
					                setCustomAddNotes({
 | 
				
			||||||
 | 
					                  ...customAddNotes,
 | 
				
			||||||
 | 
					                  dayAdditional: value,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              setCustomAddNotes({
 | 
				
			||||||
 | 
					                ...customAddNotes,
 | 
				
			||||||
 | 
					                dayAdditional: '',
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          label={'Day additional leave'}
 | 
				
			||||||
 | 
					          placeholder="Input placeholder"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Textarea
 | 
				
			||||||
 | 
					          mb={'md'}
 | 
				
			||||||
 | 
					          label="Note"
 | 
				
			||||||
 | 
					          value={customAddNotes.note}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setCustomAddNotes({ ...customAddNotes, note: e.target.value })
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Button
 | 
				
			||||||
 | 
					          onClick={() => {
 | 
				
			||||||
 | 
					            setDisableBtn(true)
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					              customAddNotes.id === 0 ||
 | 
				
			||||||
 | 
					              customAddNotes.totalLeave === '' ||
 | 
				
			||||||
 | 
					              customAddNotes.totalLeave === '0'
 | 
				
			||||||
 | 
					              // customAddNotes.dayAdditional === 0 ||
 | 
				
			||||||
 | 
					              // customAddNotes.note === ''
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					              notifications.show({
 | 
				
			||||||
 | 
					                title: 'Error',
 | 
				
			||||||
 | 
					                message: 'Input data required',
 | 
				
			||||||
 | 
					                color: 'red',
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					              setDisableBtn(false)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              updateInfoNote(
 | 
				
			||||||
 | 
					                customAddNotes.id,
 | 
				
			||||||
 | 
					                customAddNotes.user,
 | 
				
			||||||
 | 
					                customAddNotes.totalLeave,
 | 
				
			||||||
 | 
					                customAddNotes.dayAdditional,
 | 
				
			||||||
 | 
					                customAddNotes.note,
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          disabled={disableBtn}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          Save
 | 
				
			||||||
 | 
					        </Button>
 | 
				
			||||||
 | 
					      </Drawer>
 | 
				
			||||||
 | 
					      <Box display={'flex'}>
 | 
				
			||||||
 | 
					        <Box style={{ display: 'flex', flexFlow: 'column' }} w={'30%'}>
 | 
				
			||||||
 | 
					          <Box w="100%" display={'flex'}>
 | 
				
			||||||
 | 
					            <Select
 | 
				
			||||||
 | 
					              w="50%"
 | 
				
			||||||
 | 
					              value={date.year}
 | 
				
			||||||
 | 
					              size="xs"
 | 
				
			||||||
 | 
					              ml={'sm'}
 | 
				
			||||||
 | 
					              label="Year"
 | 
				
			||||||
 | 
					              data={Array.from({ length: 10 }, (_, index) => {
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                  value: (
 | 
				
			||||||
 | 
					                    parseInt(moment(Date.now()).format('YYYY')) -
 | 
				
			||||||
 | 
					                    3 +
 | 
				
			||||||
 | 
					                    index
 | 
				
			||||||
 | 
					                  ).toString(),
 | 
				
			||||||
 | 
					                  label: (
 | 
				
			||||||
 | 
					                    parseInt(moment(Date.now()).format('YYYY')) -
 | 
				
			||||||
 | 
					                    3 +
 | 
				
			||||||
 | 
					                    index
 | 
				
			||||||
 | 
					                  ).toString(),
 | 
				
			||||||
 | 
					                  disabled:
 | 
				
			||||||
 | 
					                    parseInt(moment(Date.now()).format('YYYY')) - 3 + index >
 | 
				
			||||||
 | 
					                    parseInt(moment(Date.now()).format('YYYY')),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					              onChange={(e) => {
 | 
				
			||||||
 | 
					                setDate({ ...date, year: e! })
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            ></Select>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					        <Box
 | 
				
			||||||
 | 
					          w="70%"
 | 
				
			||||||
 | 
					          pl={200}
 | 
				
			||||||
 | 
					          style={{
 | 
				
			||||||
 | 
					            display: 'flex',
 | 
				
			||||||
 | 
					            // alignItems: 'end',
 | 
				
			||||||
 | 
					            justifyContent: 'end',
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Box display={'flex'} style={{ alignItems: 'end' }}>
 | 
				
			||||||
 | 
					            <Tooltip label="Save working days">
 | 
				
			||||||
 | 
					              <Button
 | 
				
			||||||
 | 
					                size="xs"
 | 
				
			||||||
 | 
					                ml={'sm'}
 | 
				
			||||||
 | 
					                onClick={() => {
 | 
				
			||||||
 | 
					                  //form add user new
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Add
 | 
				
			||||||
 | 
					              </Button>
 | 
				
			||||||
 | 
					            </Tooltip>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					      <Box>
 | 
				
			||||||
 | 
					        <Table
 | 
				
			||||||
 | 
					          striped
 | 
				
			||||||
 | 
					          highlightOnHover
 | 
				
			||||||
 | 
					          withTableBorder
 | 
				
			||||||
 | 
					          withColumnBorders
 | 
				
			||||||
 | 
					          mt={'md'}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Table.Thead>
 | 
				
			||||||
 | 
					            <Table.Tr bg={'#228be66b'}>
 | 
				
			||||||
 | 
					              <Table.Th></Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th>User</Table.Th>
 | 
				
			||||||
 | 
					              {monthInYear.map((d) => {
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                  <Menu width={200} shadow="md" key={d.value}>
 | 
				
			||||||
 | 
					                    <Menu.Target>
 | 
				
			||||||
 | 
					                      <Table.Th ta={'center'} style={{ cursor: 'pointer' }}>
 | 
				
			||||||
 | 
					                        <span>{d.name}</span>
 | 
				
			||||||
 | 
					                      </Table.Th>
 | 
				
			||||||
 | 
					                    </Menu.Target>
 | 
				
			||||||
 | 
					                  </Menu>
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					              <Table.Th ta={'center'}>Total Leave</Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th ta={'center'}>Day Off</Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th ta={'center'}>Remaining</Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th ta={'center'}>Notes</Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th ta={'center'}></Table.Th>
 | 
				
			||||||
 | 
					            </Table.Tr>
 | 
				
			||||||
 | 
					          </Table.Thead>
 | 
				
			||||||
 | 
					          <Table.Tbody>
 | 
				
			||||||
 | 
					            {data.map((user, index) => {
 | 
				
			||||||
 | 
					              let totalDayOff = 0
 | 
				
			||||||
 | 
					              let totalDayLeave =
 | 
				
			||||||
 | 
					                user.leaveDay.ld_day + user.leaveDay.ld_date_additional
 | 
				
			||||||
 | 
					              let ld_note = user.leaveDay.ld_note
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <Table.Tr key={user.user.id} className={classes.tableTr}>
 | 
				
			||||||
 | 
					                  <Table.Td ta={'center'}>{index + 1}</Table.Td>
 | 
				
			||||||
 | 
					                  <Table.Td>
 | 
				
			||||||
 | 
					                    <Tooltip multiline label={user.user.name}>
 | 
				
			||||||
 | 
					                      <div>{user.user.name}</div>
 | 
				
			||||||
 | 
					                    </Tooltip>
 | 
				
			||||||
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  {monthInYear.map((d, i) => {
 | 
				
			||||||
 | 
					                    let total =
 | 
				
			||||||
 | 
					                      user.monthlyLeaveDays.find((item) => {
 | 
				
			||||||
 | 
					                        return item.month == d.value
 | 
				
			||||||
 | 
					                      })?.leave_days ?? 0
 | 
				
			||||||
 | 
					                    totalDayOff = totalDayOff + total
 | 
				
			||||||
 | 
					                    return (
 | 
				
			||||||
 | 
					                      <Table.Td key={i} ta={'center'}>
 | 
				
			||||||
 | 
					                        {total === 0 ? '' : total}
 | 
				
			||||||
 | 
					                      </Table.Td>
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                  })}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  <Table.Td ta={'center'}>{totalDayLeave}</Table.Td>
 | 
				
			||||||
 | 
					                  <Table.Td ta={'center'}>{totalDayOff}</Table.Td>
 | 
				
			||||||
 | 
					                  <Table.Td ta={'center'}>
 | 
				
			||||||
 | 
					                    {totalDayLeave - totalDayOff}
 | 
				
			||||||
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					                  <Table.Td>{ld_note}</Table.Td>
 | 
				
			||||||
 | 
					                  <Table.Td ta={'center'}>
 | 
				
			||||||
 | 
					                    <IconEdit
 | 
				
			||||||
 | 
					                      color="green"
 | 
				
			||||||
 | 
					                      onClick={() => {
 | 
				
			||||||
 | 
					                        let totalLeave =
 | 
				
			||||||
 | 
					                          user.leaveDay.ld_day == 0
 | 
				
			||||||
 | 
					                            ? ''
 | 
				
			||||||
 | 
					                            : String(user.leaveDay.ld_day)
 | 
				
			||||||
 | 
					                        let dayAdditional =
 | 
				
			||||||
 | 
					                          user.leaveDay.ld_date_additional == 0
 | 
				
			||||||
 | 
					                            ? ''
 | 
				
			||||||
 | 
					                            : String(user.leaveDay.ld_date_additional)
 | 
				
			||||||
 | 
					                        open1()
 | 
				
			||||||
 | 
					                        setCustomAddNotes({
 | 
				
			||||||
 | 
					                          ...customAddNotes,
 | 
				
			||||||
 | 
					                          id: user.leaveDay.id,
 | 
				
			||||||
 | 
					                          note: ld_note,
 | 
				
			||||||
 | 
					                          totalLeave: totalLeave,
 | 
				
			||||||
 | 
					                          dayAdditional: dayAdditional,
 | 
				
			||||||
 | 
					                          user: {
 | 
				
			||||||
 | 
					                            id: user.user.id,
 | 
				
			||||||
 | 
					                            name: user.user.name,
 | 
				
			||||||
 | 
					                          },
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                      }}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					                </Table.Tr>
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					          </Table.Tbody>
 | 
				
			||||||
 | 
					        </Table>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default LeaveManagement
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ import CustomTheme from '@/pages/CustomTheme/CustomTheme'
 | 
				
			||||||
import Dashboard from '@/pages/Dashboard/Dashboard'
 | 
					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 LeaveManagement from '@/pages/LeaveManagement/LeaveManagement'
 | 
				
			||||||
import PageNotFound from '@/pages/NotFound/NotFound'
 | 
					import PageNotFound from '@/pages/NotFound/NotFound'
 | 
				
			||||||
import Timekeeping from '@/pages/Timekeeping/Timekeeping'
 | 
					import Timekeeping from '@/pages/Timekeeping/Timekeeping'
 | 
				
			||||||
import Tracking from '@/pages/Tracking/Tracking'
 | 
					import Tracking from '@/pages/Tracking/Tracking'
 | 
				
			||||||
| 
						 | 
					@ -88,20 +89,20 @@ const mainRoutes = [
 | 
				
			||||||
      </ProtectedRoute>
 | 
					      </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  // {
 | 
				
			||||||
    path: '/jira',
 | 
					  //   path: '/jira',
 | 
				
			||||||
    element: (
 | 
					  //   element: (
 | 
				
			||||||
      <ProtectedRoute mode="route">
 | 
					  //     <ProtectedRoute mode="route">
 | 
				
			||||||
        <BasePage
 | 
					  //       <BasePage
 | 
				
			||||||
          main={
 | 
					  //         main={
 | 
				
			||||||
            <>
 | 
					  //           <>
 | 
				
			||||||
              <Jira />
 | 
					  //             <Jira />
 | 
				
			||||||
            </>
 | 
					  //           </>
 | 
				
			||||||
          }
 | 
					  //         }
 | 
				
			||||||
        ></BasePage>
 | 
					  //       ></BasePage>
 | 
				
			||||||
      </ProtectedRoute>
 | 
					  //     </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					  //   ),
 | 
				
			||||||
  },
 | 
					  // },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    path: '/worklogs',
 | 
					    path: '/worklogs',
 | 
				
			||||||
    element: (
 | 
					    element: (
 | 
				
			||||||
| 
						 | 
					@ -130,6 +131,20 @@ const mainRoutes = [
 | 
				
			||||||
      </ProtectedRoute>
 | 
					      </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/leave-management',
 | 
				
			||||||
 | 
					    element: (
 | 
				
			||||||
 | 
					      <ProtectedRoute mode="route">
 | 
				
			||||||
 | 
					        <BasePage
 | 
				
			||||||
 | 
					          main={
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					              <LeaveManagement />
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ></BasePage>
 | 
				
			||||||
 | 
					      </ProtectedRoute>
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  // {
 | 
					  // {
 | 
				
			||||||
  //   path: '/packages',
 | 
					  //   path: '/packages',
 | 
				
			||||||
  //   element: (
 | 
					  //   element: (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue