dev #130
			
				
			
		
		
		
	| 
						 | 
					@ -14,7 +14,7 @@ class CategoryController extends Controller
 | 
				
			||||||
     * @param Request $request The HTTP request object.
 | 
					     * @param Request $request The HTTP request object.
 | 
				
			||||||
     * @return \Illuminate\Http\JsonResponse The JSON response containing the list of master data.
 | 
					     * @return \Illuminate\Http\JsonResponse The JSON response containing the list of master data.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function getListMaster(Request $request)
 | 
					    public static function getListMaster(Request $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $data = Category::where('c_type', '=', $request->type)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->get();
 | 
					        $data = Category::where('c_type', '=', $request->type)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->get();
 | 
				
			||||||
        return AbstractController::ResultSuccess($data);
 | 
					        return AbstractController::ResultSuccess($data);
 | 
				
			||||||
| 
						 | 
					@ -24,4 +24,9 @@ class CategoryController extends Controller
 | 
				
			||||||
        $data = Category::where('c_type', '=', $type)->where('c_code', '=', $code)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->first();
 | 
					        $data = Category::where('c_type', '=', $type)->where('c_code', '=', $code)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->first();
 | 
				
			||||||
        return $data;
 | 
					        return $data;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public static function getListMasterByType($type)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $data = Category::where('c_type', '=', $type)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->get();
 | 
				
			||||||
 | 
					        return $data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,7 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
            ->leftJoin("categories as reason", function ($join) {
 | 
					            ->leftJoin("categories as reason", function ($join) {
 | 
				
			||||||
                $join->on('n_reason', '=', 'reason.c_code');
 | 
					                $join->on('n_reason', '=', 'reason.c_code');
 | 
				
			||||||
                $join->on('reason.c_type', DB::raw("CONCAT('REASON')"));
 | 
					                $join->on('reason.c_type', DB::raw("CONCAT('REASON_NOTES')"));
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            ->select(
 | 
					            ->select(
 | 
				
			||||||
                DB::raw('notes.n_user_id as n_user_id'),
 | 
					                DB::raw('notes.n_user_id as n_user_id'),
 | 
				
			||||||
| 
						 | 
					@ -44,13 +44,14 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
                DB::raw('notes.n_month as month'),
 | 
					                DB::raw('notes.n_month as month'),
 | 
				
			||||||
                DB::raw('categories.c_value as leave_days'),
 | 
					                DB::raw('categories.c_value as leave_days'),
 | 
				
			||||||
                DB::raw('notes.n_day as day'),
 | 
					                DB::raw('notes.n_day as day'),
 | 
				
			||||||
 | 
					                DB::raw('notes.n_reason as reason_code'),
 | 
				
			||||||
                'reason.c_name as reason_name',
 | 
					                'reason.c_name as reason_name',
 | 
				
			||||||
                'categories.c_name as time_type_name',
 | 
					                'categories.c_name as time_type_name',
 | 
				
			||||||
                // DB::raw('SUM(categories.c_value) as leave_days')
 | 
					                // DB::raw('SUM(categories.c_value) as leave_days')
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            // ->where('notes.n_user_id', "1")
 | 
					            // ->where('notes.n_user_id', "1")
 | 
				
			||||||
            ->where('notes.n_year', $year)
 | 
					            ->where('notes.n_year', $year)
 | 
				
			||||||
            ->where('notes.n_reason', 'ONLEAVE')
 | 
					            ->whereIn('notes.n_reason', ['ONLEAVE', 'LEAVE_WITHOUT_PAY', 'TEMPORARY_ONLEAVE'])
 | 
				
			||||||
            // ->groupBy("notes.n_user_id")
 | 
					            // ->groupBy("notes.n_user_id")
 | 
				
			||||||
            ->orderBy('notes.n_month')
 | 
					            ->orderBy('notes.n_month')
 | 
				
			||||||
            ->orderBy('notes.n_day')
 | 
					            ->orderBy('notes.n_day')
 | 
				
			||||||
| 
						 | 
					@ -59,7 +60,7 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
                return [
 | 
					                return [
 | 
				
			||||||
                    "day" => $item->day,
 | 
					                    "day" => $item->day,
 | 
				
			||||||
                    "n_user_id" => $item->n_user_id,
 | 
					                    "n_user_id" => $item->n_user_id,
 | 
				
			||||||
                    // "time_type" => $item->time_type,
 | 
					                    "reason_code" => $item->reason_code,
 | 
				
			||||||
                    "reason_name" => $item->reason_name,
 | 
					                    "reason_name" => $item->reason_name,
 | 
				
			||||||
                    "time_type_name" => $item->time_type_name,
 | 
					                    "time_type_name" => $item->time_type_name,
 | 
				
			||||||
                    "month" => $item->month,
 | 
					                    "month" => $item->month,
 | 
				
			||||||
| 
						 | 
					@ -105,9 +106,10 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
                    'leaveDay' => [
 | 
					                    'leaveDay' => [
 | 
				
			||||||
                        'id' => $item->id,
 | 
					                        'id' => $item->id,
 | 
				
			||||||
                        'ld_user_id' => $item->ld_user_id,
 | 
					                        'ld_user_id' => $item->ld_user_id,
 | 
				
			||||||
                        'ld_day' => $item->ld_day,
 | 
					                        'ld_day_total' => $item->ld_day_total,
 | 
				
			||||||
                        'ld_year' => $item->ld_year,
 | 
					                        'ld_year' => $item->ld_year,
 | 
				
			||||||
                        'ld_date_additional' => $item->ld_date_additional,
 | 
					                        'ld_additional_day' => $item->ld_additional_day,
 | 
				
			||||||
 | 
					                        'ld_special_leave_day' => $item->ld_special_leave_day,
 | 
				
			||||||
                        'ld_note' => $item->ld_note,
 | 
					                        'ld_note' => $item->ld_note,
 | 
				
			||||||
                        'created_at' => $item->created_at,
 | 
					                        'created_at' => $item->created_at,
 | 
				
			||||||
                        'updated_at' => $item->updated_at,
 | 
					                        'updated_at' => $item->updated_at,
 | 
				
			||||||
| 
						 | 
					@ -133,8 +135,9 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
        $validatedData = $request->all();
 | 
					        $validatedData = $request->all();
 | 
				
			||||||
        $leaveDays = LeaveDays::find($validatedData['id']);
 | 
					        $leaveDays = LeaveDays::find($validatedData['id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $leaveDays->ld_day = $validatedData['totalLeave'];
 | 
					        $leaveDays->ld_day_total = $validatedData['totalLeave'];
 | 
				
			||||||
        $leaveDays->ld_date_additional = $validatedData['dayAdditional']; // Assuming you have this field to store additional days
 | 
					        $leaveDays->ld_additional_day = $validatedData['dayAdditional'];
 | 
				
			||||||
 | 
					        $leaveDays->ld_special_leave_day = $validatedData['specialLeave'];
 | 
				
			||||||
        $leaveDays->ld_note = $validatedData['note'];
 | 
					        $leaveDays->ld_note = $validatedData['note'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $leaveDays->save();
 | 
					        $leaveDays->save();
 | 
				
			||||||
| 
						 | 
					@ -152,7 +155,7 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Lọc chỉ lấy user có permission bao gồm staff
 | 
					        // Lọc chỉ lấy user có permission bao gồm staff
 | 
				
			||||||
        $staffData = $leaveDays->filter(function($user) {
 | 
					        $staffData = $leaveDays->filter(function ($user) {
 | 
				
			||||||
            return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
 | 
					            return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -11,12 +11,14 @@ use App\Traits\HasOrderByRequest;
 | 
				
			||||||
use App\Traits\HasSearchRequest;
 | 
					use App\Traits\HasSearchRequest;
 | 
				
			||||||
use Carbon\Carbon;
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Illuminate\Support\Facades\DB;
 | 
					use Illuminate\Support\Facades\Mail;
 | 
				
			||||||
 | 
					use App\Mail\TicketMail;
 | 
				
			||||||
use Modules\Admin\app\Models\Admin;
 | 
					use Modules\Admin\app\Models\Admin;
 | 
				
			||||||
use Modules\Admin\app\Models\MonthlyTimekeeping;
 | 
					use Modules\Admin\app\Models\MonthlyTimekeeping;
 | 
				
			||||||
use Modules\Admin\app\Models\Tracking;
 | 
					use Modules\Admin\app\Models\Tracking;
 | 
				
			||||||
use Maatwebsite\Excel\Facades\Excel;
 | 
					use Maatwebsite\Excel\Facades\Excel;
 | 
				
			||||||
use App\Exports\TimekeepingExport;
 | 
					use App\Exports\TimekeepingExport;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\Ticket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TimekeepingController extends Controller
 | 
					class TimekeepingController extends Controller
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -152,8 +154,6 @@ class TimekeepingController extends Controller
 | 
				
			||||||
        return response()->json(['status' => true, 'message' => 'Add successfully']);
 | 
					        return response()->json(['status' => true, 'message' => 'Add successfully']);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function updateCacheMonth(Request $request)
 | 
					    public function updateCacheMonth(Request $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $month = $request->month;
 | 
					        $month = $request->month;
 | 
				
			||||||
| 
						 | 
					@ -172,20 +172,54 @@ class TimekeepingController extends Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Validate the request
 | 
					        // Validate the request
 | 
				
			||||||
        $request->validate($rules);
 | 
					        $request->validate($rules);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        $id = $request->input('id');
 | 
					        $id = $request->input('id');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $month = $request->month;
 | 
					 | 
				
			||||||
        $year = $request->year;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $note = Notes::find($id);
 | 
					        $note = Notes::find($id);
 | 
				
			||||||
        if ($note) {
 | 
					        if (!$note) {
 | 
				
			||||||
            $note->delete();
 | 
					            return response()->json(['message' => 'Note not found', 'status' => false]);
 | 
				
			||||||
            $this->createOrUpdateRecordForCurrentMonth($month, $year);
 | 
					 | 
				
			||||||
            return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response()->json(['message' => 'Delete fail', 'status' => false]);
 | 
					        $ticket = Ticket::find($note->ticket_id);
 | 
				
			||||||
 | 
					        if (!$ticket) {
 | 
				
			||||||
 | 
					            return response()->json(['message' => 'Ticket not found, can not delete note', 'status' => false]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $admin = auth('admins')->user();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Handle send mail
 | 
				
			||||||
 | 
					        $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->start_period);
 | 
				
			||||||
 | 
					        $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->end_period);
 | 
				
			||||||
 | 
					        $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $ticket->type);
 | 
				
			||||||
 | 
					        $formattedStartDate = Carbon::createFromFormat('Y-m-d', $ticket->start_date)->format('d/m/Y');
 | 
				
			||||||
 | 
					        $formattedEndDate = Carbon::createFromFormat('Y-m-d', $ticket->end_date)->format('d/m/Y');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $user = Admin::find($ticket->user_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = array(
 | 
				
			||||||
 | 
					            "email_template" => "email.notification_tickets_user",
 | 
				
			||||||
 | 
					            "user_name" => $user->name,
 | 
				
			||||||
 | 
					            "email" => $user->email,
 | 
				
			||||||
 | 
					            "name" => $admin->name, //name admin duyệt
 | 
				
			||||||
 | 
					            "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
				
			||||||
 | 
					            "type" => $dataMasterType->c_name,
 | 
				
			||||||
 | 
					            "note" => $ticket->reason,
 | 
				
			||||||
 | 
					            "admin_note" => $ticket->admin_note,
 | 
				
			||||||
 | 
					            "link" => "/tickets", //link đến page admin
 | 
				
			||||||
 | 
					            "status" => "refused",
 | 
				
			||||||
 | 
					            "subject" => "[Ticket response] Ticket From " . $admin->name
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        Mail::to($user->email)->send(new TicketMail($data));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Update
 | 
				
			||||||
 | 
					        $ticket->updated_by = $admin->name;
 | 
				
			||||||
 | 
					        $ticket->status = "REFUSED";
 | 
				
			||||||
 | 
					        $ticket->save();
 | 
				
			||||||
 | 
					        Notes::where('ticket_id', $ticket->id)->delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Clear Timekeeping cache
 | 
				
			||||||
 | 
					        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
 | 
				
			||||||
 | 
					        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
 | 
				
			||||||
 | 
					        return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function export(Request $request)
 | 
					    public function export(Request $request)
 | 
				
			||||||
| 
						 | 
					@ -206,10 +240,10 @@ class TimekeepingController extends Controller
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Lọc chỉ lấy user có permission bao gồm staff
 | 
					        // Lọc chỉ lấy user có permission bao gồm staff
 | 
				
			||||||
        $staffData = array_filter($responseData['data'], function($user) {
 | 
					        $staffData = array_filter($responseData['data'], function ($user) {
 | 
				
			||||||
            return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
 | 
					            return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        $currentDate = date('d_His');
 | 
					        $currentDate = date('d_His');
 | 
				
			||||||
        return Excel::download(
 | 
					        return Excel::download(
 | 
				
			||||||
            new TimekeepingExport(
 | 
					            new TimekeepingExport(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,7 @@ Route::middleware('api')
 | 
				
			||||||
            Route::post('login', [AdminController::class, 'login']);
 | 
					            Route::post('login', [AdminController::class, 'login']);
 | 
				
			||||||
            Route::post('reset-password', [AdminController::class, 'resetPassword']);
 | 
					            Route::post('reset-password', [AdminController::class, 'resetPassword']);
 | 
				
			||||||
            Route::get('forgot-password', [AdminController::class, 'forgotPassword']);
 | 
					            Route::get('forgot-password', [AdminController::class, 'forgotPassword']);
 | 
				
			||||||
 | 
					            Route::get('/email-handle-ticket', [TicketController::class, 'handleTicketEmail'])->name('email.ticket.handle');
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // NOTE after login
 | 
					        // NOTE after login
 | 
				
			||||||
| 
						 | 
					@ -163,6 +164,7 @@ Route::middleware('api')
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
                Route::get('/all', [TicketController::class, 'getAll'])->middleware('check.permission:admin.hr');
 | 
					                Route::get('/all', [TicketController::class, 'getAll'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
                Route::get('/getByUserId', [TicketController::class, 'getByUserId'])->middleware('check.permission:admin.hr.staff');
 | 
					                Route::get('/getByUserId', [TicketController::class, 'getByUserId'])->middleware('check.permission:admin.hr.staff');
 | 
				
			||||||
 | 
					                Route::post('/update', [TicketController::class, 'updateTicket'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
                Route::post('/create', [TicketController::class, 'createTicket'])->middleware('check.permission:admin.hr.staff');
 | 
					                Route::post('/create', [TicketController::class, 'createTicket'])->middleware('check.permission:admin.hr.staff');
 | 
				
			||||||
                Route::get('/delete', [TicketController::class, 'deleteTicket'])->middleware('check.permission:admin.hr.staff');
 | 
					                Route::get('/delete', [TicketController::class, 'deleteTicket'])->middleware('check.permission:admin.hr.staff');
 | 
				
			||||||
                Route::post('/handle-ticket', [TicketController::class, 'handleTicket'])->middleware('check.permission:admin');
 | 
					                Route::post('/handle-ticket', [TicketController::class, 'handleTicket'])->middleware('check.permission:admin');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,13 +3,15 @@
 | 
				
			||||||
namespace Modules\Auth\app\Http\Controllers;
 | 
					namespace Modules\Auth\app\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Http\Controllers\Controller;
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Models\LeaveDays;
 | 
				
			||||||
use App\Traits\IsAPI;
 | 
					use App\Traits\IsAPI;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Illuminate\Http\Response;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Http;
 | 
					use Illuminate\Support\Facades\Http;
 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
use Modules\Auth\app\Models\User;
 | 
					use Modules\Auth\app\Models\User;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\Category;
 | 
				
			||||||
use SimpleSoftwareIO\QrCode\Facades\QrCode;
 | 
					use SimpleSoftwareIO\QrCode\Facades\QrCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserController extends Controller
 | 
					class UserController extends Controller
 | 
				
			||||||
| 
						 | 
					@ -34,9 +36,37 @@ class UserController extends Controller
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($request->has('id')) {
 | 
					        if ($request->has('id')) {
 | 
				
			||||||
            $payload = $request->only(['name', 'email', 'permission']);
 | 
					            $payload = $request->only(['name', 'email', 'permission', 'is_permanent']);
 | 
				
			||||||
            $user = User::find($request->id);
 | 
					            $user = User::find($request->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Không cho chuyển từ chính thức thành lại thử việc
 | 
				
			||||||
 | 
					            if (!$request->is_permanent && $user->is_permanent) {
 | 
				
			||||||
 | 
					                return response()->json(['status' => false, 'message' => 'You cannot change an employee from permanent to probationary.']);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Thêm ngày phép khi thành nhân viên chính thức
 | 
				
			||||||
 | 
					            if ($request->is_permanent && !$user->is_permanent) {
 | 
				
			||||||
 | 
					                $userLeaveDay = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                    ->where('ld_year', Carbon::now()->year)
 | 
				
			||||||
 | 
					                    ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($userLeaveDay) {
 | 
				
			||||||
 | 
					                    $permanentCategory = Category::where('c_type', 'PERMANENT_ONLEAVE')->where('c_code', "PERMANENT")->first();
 | 
				
			||||||
 | 
					                    $permanentDefault = (int) $permanentCategory->c_value; // Ngày phép khi thành nv chính thức
 | 
				
			||||||
 | 
					                    $userLeaveDay->ld_day_total = $permanentDefault;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $newNote = "Cộng ngày phép cho nhân viên chính thức"; // Thêm ghi chú
 | 
				
			||||||
 | 
					                    if (!empty($userLeaveDay->ld_note)) {
 | 
				
			||||||
 | 
					                        $userLeaveDay->ld_note = $userLeaveDay->ld_note . "\n" . $newNote;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $userLeaveDay->ld_note = $newNote;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $userLeaveDay->save();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $payload['permanent_date'] = Carbon::now()->toDateString();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $user->update($payload);
 | 
					            $user->update($payload);
 | 
				
			||||||
            return response()->json(['data' => $user, 'status' => true, 'message' => 'Update successful']);
 | 
					            return response()->json(['data' => $user, 'status' => true, 'message' => 'Update successful']);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
| 
						 | 
					@ -44,9 +74,22 @@ class UserController extends Controller
 | 
				
			||||||
                'name' => $request->name,
 | 
					                'name' => $request->name,
 | 
				
			||||||
                'email' => $request->email,
 | 
					                'email' => $request->email,
 | 
				
			||||||
                'password' => bcrypt('Work@1234'),
 | 
					                'password' => bcrypt('Work@1234'),
 | 
				
			||||||
                'permission' => $request->permission
 | 
					                'permission' => $request->permission,
 | 
				
			||||||
 | 
					                'is_permanent' => false
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Khởi tạo LeaveDays cho nhân viên mới
 | 
				
			||||||
 | 
					            LeaveDays::insert([
 | 
				
			||||||
 | 
					                'ld_user_id' => $user->id,
 | 
				
			||||||
 | 
					                'ld_day_total' => 0,
 | 
				
			||||||
 | 
					                'ld_year' => Carbon::now()->year,
 | 
				
			||||||
 | 
					                'ld_additional_day' => 0,
 | 
				
			||||||
 | 
					                'ld_note' => '',
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            $this->createOrUpdateRecordForCurrentMonth(Carbon::now()->month, Carbon::now()->year);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $user_res = [
 | 
					            $user_res = [
 | 
				
			||||||
                'name' => $user->name,
 | 
					                'name' => $user->name,
 | 
				
			||||||
                'email' => $user->email,
 | 
					                'email' => $user->email,
 | 
				
			||||||
| 
						 | 
					@ -98,8 +141,6 @@ class UserController extends Controller
 | 
				
			||||||
                return response()->json(['data' => ['user' => $user_res, 'gitea' => "dev", 'zulip' => "dev"], 'status' => true, 'message' => 'Create successful']);
 | 
					                return response()->json(['data' => ['user' => $user_res, 'gitea' => "dev", 'zulip' => "dev"], 'status' => true, 'message' => 'Create successful']);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return response()->json(['status' => false, 'message' => 'Process fail']);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function delete(Request $request)
 | 
					    public function delete(Request $request)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,9 @@ class User extends Authenticatable implements JWTSubject
 | 
				
			||||||
        'name',
 | 
					        'name',
 | 
				
			||||||
        'email',
 | 
					        'email',
 | 
				
			||||||
        'password',
 | 
					        'password',
 | 
				
			||||||
        'permission'
 | 
					        'permission',
 | 
				
			||||||
 | 
					        'is_permanent',
 | 
				
			||||||
 | 
					        'permanent_date'
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					use App\Jobs\AddMonthlyLeaveDays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddMonthlyLeaveDaysCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $signature = 'add:monthly-leavedays {month?} {year?}';
 | 
				
			||||||
 | 
					    protected $description = 'Cộng 1 ngày phép hàng tháng cho tất cả người dùng';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $month = $this->argument('month');
 | 
				
			||||||
 | 
					        $year = $this->argument('year');
 | 
				
			||||||
 | 
					        AddMonthlyLeaveDays::dispatch($month, $year);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ use App\Jobs\InitializeLeaveDays;
 | 
				
			||||||
class InitializeLeaveDaysCommand extends Command
 | 
					class InitializeLeaveDaysCommand extends Command
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected $signature = 'initialize:leavedays {year?}';
 | 
					    protected $signature = 'initialize:leavedays {year?}';
 | 
				
			||||||
    protected $description = 'Initialize leave days for users';
 | 
					    protected $description = 'Cấp phép năm cho tất cả người dùng';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct()
 | 
					    public function __construct()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ class InitializeLeaveDaysCommand extends Command
 | 
				
			||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $year = $this->argument('year');
 | 
					        $year = $this->argument('year');
 | 
				
			||||||
        InitializeLeaveDays::dispatch($year);
 | 
					        // Không sử dụng nữa, theo rule mới
 | 
				
			||||||
 | 
					        // InitializeLeaveDays::dispatch($year);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					use App\Jobs\UpdateTemporaryLeaveDays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UpdateTemporaryLeaveDaysCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $signature = 'update:temporary-leavedays {month?} {year?}';
 | 
				
			||||||
 | 
					    protected $description = 'Tính lại ngày phép cho các note tạm.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $month = $this->argument('month');
 | 
				
			||||||
 | 
					        $year = $this->argument('year');
 | 
				
			||||||
 | 
					        UpdateTemporaryLeaveDays::dispatch($month, $year);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
namespace App\Console;
 | 
					namespace App\Console;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Jobs\DeductLeaveDays;
 | 
					use App\Jobs\DeductLeaveDays;
 | 
				
			||||||
 | 
					use App\Jobs\AddMonthlyLeaveDays;
 | 
				
			||||||
use Illuminate\Console\Scheduling\Schedule;
 | 
					use Illuminate\Console\Scheduling\Schedule;
 | 
				
			||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
					use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +33,10 @@ class Kernel extends ConsoleKernel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Chạy buổi chiều lúc 17:30
 | 
					        // Chạy buổi chiều lúc 17:30
 | 
				
			||||||
        $schedule->command('attendance:check C')->dailyAt('17:30');
 | 
					        $schedule->command('attendance:check C')->dailyAt('17:30');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Chạy vào ngày đầu tiên của mỗi tháng
 | 
				
			||||||
 | 
					        $schedule->command('add:monthly-leavedays')->monthlyOn(1, '00:01');
 | 
				
			||||||
 | 
					        $schedule->command('update:temporary-leavedays')->monthlyOn(1, '00:05');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@ class LeaveManagementExport implements FromArray, WithHeadings, WithStyles, With
 | 
				
			||||||
        $stt = 0;
 | 
					        $stt = 0;
 | 
				
			||||||
        foreach ($this->data as $index => $user) {
 | 
					        foreach ($this->data as $index => $user) {
 | 
				
			||||||
            $totalDayOff = 0;
 | 
					            $totalDayOff = 0;
 | 
				
			||||||
            $totalDayLeave = $user['leaveDay']['ld_day'] + $user['leaveDay']['ld_date_additional'];
 | 
					            $totalDayLeave = $user['leaveDay']['ld_day_total'] + $user['leaveDay']['ld_additional_day'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Tính tổng ngày nghỉ theo tháng
 | 
					            // Tính tổng ngày nghỉ theo tháng
 | 
				
			||||||
            $monthlyLeaves = array_fill(1, 12, 0);
 | 
					            $monthlyLeaves = array_fill(1, 12, 0);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\LeaveDays;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\Category;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddMonthlyLeaveDays implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected $month;
 | 
				
			||||||
 | 
					  protected $year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private const ONLEAVE_PER_MONTH = 1; // Ngày phép cộng mỗi tháng
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function __construct($month = null, $year = null)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    $this->month = $month ?? Carbon::now()->month;
 | 
				
			||||||
 | 
					    $this->year = $year ?? Carbon::now()->year;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public function handle(): void
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    $users = User::get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    foreach ($users as $user) {
 | 
				
			||||||
 | 
					      // Nếu là nhân viên chưa chính thức, ko cộng phép
 | 
				
			||||||
 | 
					      if (!$user->is_permanent) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      $leaveDay = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					        ->where('ld_year', $this->year)
 | 
				
			||||||
 | 
					        ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!$leaveDay) {
 | 
				
			||||||
 | 
					        // Nếu chưa có dữ liệu năm hiện tại, tạo mới
 | 
				
			||||||
 | 
					        // Số ngày phép bằng với tháng hiện tại
 | 
				
			||||||
 | 
					        $leaveDay = new LeaveDays([
 | 
				
			||||||
 | 
					          'ld_user_id' => $user->id,
 | 
				
			||||||
 | 
					          'ld_day_total' => $this->month, // Số ngày phép bằng tháng hiện tại
 | 
				
			||||||
 | 
					          'ld_year' => $this->year,
 | 
				
			||||||
 | 
					          'ld_additional_day' => 0,
 | 
				
			||||||
 | 
					          'ld_note' => 'Khởi tạo ngày phép đến tháng ' . $this->month,
 | 
				
			||||||
 | 
					          'ld_special_leave_day' => 0,
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        $leaveDay->save();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // Check có phải là nhân viên chính thức trong năm nay (Nhân viên mới)
 | 
				
			||||||
 | 
					        if ($user->permanent_date && $user->permanent_date !== '0000-00-00') {
 | 
				
			||||||
 | 
					          $permenantYear  = Carbon::parse($user->permanent_date)->year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if ($permenantYear === $this->year) {
 | 
				
			||||||
 | 
					            $permanentCategory = Category::where('c_type', 'PERMANENT_ONLEAVE')->where('c_code', "PERMANENT")->first();
 | 
				
			||||||
 | 
					            $permanentDefault = (int) $permanentCategory->c_value; // Ngày phép khi thành nv chính thức
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $permanentMonth = Carbon::parse($user->permanent_date)->month;
 | 
				
			||||||
 | 
					            if ($this->month > $leaveDay->ld_day_total - ($permanentDefault - $permanentMonth)) {
 | 
				
			||||||
 | 
					              $leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
 | 
				
			||||||
 | 
					              $leaveDay->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Kiểm tra nếu số ngày phép hiện tại nhỏ hơn tháng hiện tại (Nhân viên cũ)
 | 
				
			||||||
 | 
					        if ($leaveDay->ld_day_total < $this->month) {
 | 
				
			||||||
 | 
					          // Cộng mỗi tháng 1 ngày phép cho nhân viên
 | 
				
			||||||
 | 
					          $leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
 | 
				
			||||||
 | 
					          $leaveDay->save();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
use Illuminate\Support\Facades\DB;
 | 
					 | 
				
			||||||
use Carbon\Carbon;
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeductLeaveDays implements ShouldQueue
 | 
					class DeductLeaveDays implements ShouldQueue
 | 
				
			||||||
| 
						 | 
					@ -36,44 +35,34 @@ class DeductLeaveDays implements ShouldQueue
 | 
				
			||||||
        foreach ($users as $user) {
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
            $existingData = LeaveDays::where('ld_user_id', $user->id)
 | 
					            $existingData = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
                ->where('ld_year', $this->year)
 | 
					                ->where('ld_year', $this->year)
 | 
				
			||||||
                ->where('ld_date_additional', ">", 0)
 | 
					                ->where('ld_additional_day', ">", 0)
 | 
				
			||||||
                ->first();
 | 
					                ->first();
 | 
				
			||||||
            if (!$existingData) {
 | 
					            if (!$existingData) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
					            // Lấy tổng ngày nghỉ phép 3 tháng đầu trong năm
 | 
				
			||||||
 | 
					            $usedOnleaveDaysTotal = Notes::join('categories', function ($join) {
 | 
				
			||||||
                $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
					                $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
				
			||||||
                    ->where('categories.c_type', 'TIME_TYPE');
 | 
					                    ->where('categories.c_type', 'TIME_TYPE');
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
                ->select(
 | 
					                ->where('n_user_id', $user->id)
 | 
				
			||||||
                    DB::raw('notes.n_user_id as n_user_id'),
 | 
					                ->where('n_year', $this->year)
 | 
				
			||||||
                    DB::raw('notes.n_year as year'),
 | 
					                ->where('n_month', "<=", 3)
 | 
				
			||||||
                    DB::raw('SUM(categories.c_value) as leave_days')
 | 
					                ->where('n_reason', 'ONLEAVE')
 | 
				
			||||||
                )
 | 
					                ->sum('categories.c_value');
 | 
				
			||||||
                ->where('notes.n_year', $this->year)
 | 
					
 | 
				
			||||||
                ->where('notes.n_user_id', $user->id)
 | 
					            if ($usedOnleaveDaysTotal) {
 | 
				
			||||||
                ->where('notes.n_reason', 'ONLEAVE')
 | 
					                if ($existingData->ld_additional_day > $usedOnleaveDaysTotal) {
 | 
				
			||||||
                ->groupBy(DB::raw('notes.n_year'))
 | 
					                    $ld_note = "Trừ " . $existingData->ld_additional_day - $usedOnleaveDaysTotal . " ngày phép tồn năm trước. \n";
 | 
				
			||||||
                ->first();
 | 
					                    $existingData->ld_note = $existingData->ld_note . "\n" . $ld_note;
 | 
				
			||||||
                
 | 
					                    $existingData->ld_additional_day = $usedOnleaveDaysTotal;
 | 
				
			||||||
            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 đó (Ngày tồn đọng - ngày sử dụng)
 | 
					 | 
				
			||||||
                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 {
 | 
					            } else {
 | 
				
			||||||
                //Nếu không sử dụng ngày nghỉ còn lại ở năm rồi thì xóa => theo luật ld
 | 
					                $existingData->ld_additional_day = 0;
 | 
				
			||||||
                LeaveDays::where('ld_year', $this->year)
 | 
					 | 
				
			||||||
                    ->where('ld_user_id', $user->id)
 | 
					 | 
				
			||||||
                    ->update([
 | 
					 | 
				
			||||||
                        'ld_date_additional' => "0",
 | 
					 | 
				
			||||||
                    ]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $existingData->save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ class InitializeLeaveDays implements ShouldQueue
 | 
				
			||||||
    public function handle(): void
 | 
					    public function handle(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $users = User::get();
 | 
					        $users = User::get();
 | 
				
			||||||
        $ld_day = 12;
 | 
					        $ld_day_total = Carbon::now()->month; // Khởi tạo phép hiện có bằng tháng hiện tại
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($users as $user) {
 | 
					        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
 | 
					            // 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)
 | 
					            $existingData = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
| 
						 | 
					@ -51,11 +52,11 @@ class InitializeLeaveDays implements ShouldQueue
 | 
				
			||||||
                ->where('ld_year', $this->year - 1)
 | 
					                ->where('ld_year', $this->year - 1)
 | 
				
			||||||
                ->first();
 | 
					                ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $ld_date_additional = 0;
 | 
					            $ld_additional_day = 0;
 | 
				
			||||||
            $ld_note = '';
 | 
					            $ld_note = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($previousYearData) {
 | 
					            if ($previousYearData) {
 | 
				
			||||||
                $ld_date_additional = $previousYearData->ld_day + $previousYearData->ld_date_additional;
 | 
					                $ld_additional_day = $previousYearData->ld_day_total + $previousYearData->ld_additional_day;
 | 
				
			||||||
                $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
					                $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
				
			||||||
                    $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
					                    $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
				
			||||||
                        ->where('categories.c_type', 'TIME_TYPE');
 | 
					                        ->where('categories.c_type', 'TIME_TYPE');
 | 
				
			||||||
| 
						 | 
					@ -71,20 +72,23 @@ class InitializeLeaveDays implements ShouldQueue
 | 
				
			||||||
                    ->groupBy(DB::raw('notes.n_year'))
 | 
					                    ->groupBy(DB::raw('notes.n_year'))
 | 
				
			||||||
                    ->first();
 | 
					                    ->first();
 | 
				
			||||||
                if ($totalLeaveDaysByMonth) {
 | 
					                if ($totalLeaveDaysByMonth) {
 | 
				
			||||||
                    $ld_date_additional = $ld_date_additional - $totalLeaveDaysByMonth->leave_days;
 | 
					                    $ld_additional_day = $ld_additional_day - $totalLeaveDaysByMonth->leave_days;
 | 
				
			||||||
                    if ($ld_date_additional < 0) {
 | 
					                    if ($ld_additional_day < 0) {
 | 
				
			||||||
                        $ld_date_additional = 0;
 | 
					                        $ld_additional_day = 0;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                $ld_note = 'Cộng dồn ngày phép năm cũ';
 | 
					
 | 
				
			||||||
 | 
					                if ($ld_additional_day > 0) {
 | 
				
			||||||
 | 
					                    $ld_note = "Cộng " . $ld_additional_day . " ngày phép tồn năm trước. \n";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Tạo dữ liệu cho năm hiện tại
 | 
					            // Tạo dữ liệu cho năm hiện tại
 | 
				
			||||||
            LeaveDays::insert([
 | 
					            LeaveDays::insert([
 | 
				
			||||||
                'ld_user_id' => $user->id,
 | 
					                'ld_user_id' => $user->id,
 | 
				
			||||||
                'ld_day' => $ld_day,
 | 
					                'ld_day_total' => $user->is_permanent ? $ld_day_total : 0, // Nếu là nhân viên mới, ko cấp phép
 | 
				
			||||||
                'ld_year' => $this->year,
 | 
					                'ld_year' => $this->year,
 | 
				
			||||||
                'ld_date_additional' => $ld_date_additional,
 | 
					                'ld_additional_day' => $ld_additional_day,
 | 
				
			||||||
                'ld_note' => $ld_note,
 | 
					                'ld_note' => $ld_note,
 | 
				
			||||||
                'created_at' => now(),
 | 
					                'created_at' => now(),
 | 
				
			||||||
                'updated_at' => now(),
 | 
					                'updated_at' => now(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,220 @@
 | 
				
			||||||
 | 
					<?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 Carbon\Carbon;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Log;
 | 
				
			||||||
 | 
					use Modules\Admin\app\Models\Category;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UpdateTemporaryLeaveDays implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					    protected $month;
 | 
				
			||||||
 | 
					    protected $year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct($month = null, $year = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->month = $month ?? Carbon::now()->month;
 | 
				
			||||||
 | 
					        $this->year = $year ?? Carbon::now()->year;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the job.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $users = User::get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($users as $user) {
 | 
				
			||||||
 | 
					            $leaveDay = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('ld_year', $this->year)
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $notes = Notes::where('n_reason', 'TEMPORARY_ONLEAVE')
 | 
				
			||||||
 | 
					                ->where('n_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('n_year', $this->year)
 | 
				
			||||||
 | 
					                ->where('n_month', $this->month)
 | 
				
			||||||
 | 
					                ->whereExists(function ($query) use ($user) {
 | 
				
			||||||
 | 
					                    $query->select(DB::raw(1))
 | 
				
			||||||
 | 
					                        ->from('tickets')
 | 
				
			||||||
 | 
					                        ->where('tickets.user_id', $user->id)
 | 
				
			||||||
 | 
					                        ->where('tickets.status', 'CONFIRMED')
 | 
				
			||||||
 | 
					                        ->where('tickets.type', 'ONLEAVE');
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                ->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $maxDaysPerMonth = $this->getMaxLeaveDaysPerMonth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Tổng ngày nghỉ sẽ dùng trong tháng
 | 
				
			||||||
 | 
					            $willUsedDaysInMonth = 0;
 | 
				
			||||||
 | 
					            foreach ($notes as $note) {
 | 
				
			||||||
 | 
					                $willUsedDaysInMonth += $note->n_time_type == 'ALL' ? 1.0 : 0.5;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Tổng phép đang có
 | 
				
			||||||
 | 
					            $onleaveDaysTotal = $leaveDay->ld_day_total +  $leaveDay->ld_additional_day + $leaveDay->ld_special_leave_day;
 | 
				
			||||||
 | 
					            // Phép đã sử dụng tới tháng hiện tại
 | 
				
			||||||
 | 
					            $usedOnleaveDaysTotal = Notes::join('categories', function ($join) {
 | 
				
			||||||
 | 
					                $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
				
			||||||
 | 
					                    ->where('categories.c_type', 'TIME_TYPE');
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					                ->where('n_user_id', $user->id)
 | 
				
			||||||
 | 
					                ->where('n_year', $this->year)
 | 
				
			||||||
 | 
					                ->where('n_month',  "<=", $this->month)
 | 
				
			||||||
 | 
					                ->where('n_reason', 'ONLEAVE')
 | 
				
			||||||
 | 
					                ->sum('categories.c_value');
 | 
				
			||||||
 | 
					            // Phép còn lại
 | 
				
			||||||
 | 
					            $remainingOnleaveDays = $onleaveDaysTotal - $usedOnleaveDaysTotal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Log::debug("User {$user->name}\n");
 | 
				
			||||||
 | 
					            // Log::debug(
 | 
				
			||||||
 | 
					            //     "📊 Thống kê ngày phép:\n" .
 | 
				
			||||||
 | 
					            //         " - Tháng: {$this->month}\n" .
 | 
				
			||||||
 | 
					            //         " - Tổng ngày nghỉ sẽ dùng trong tháng: $willUsedDaysInMonth\n" .
 | 
				
			||||||
 | 
					            //         " - Tổng ngày phép: $onleaveDaysTotal\n" .
 | 
				
			||||||
 | 
					            //         " - Tổng ngày phép đã nghỉ: $usedOnleaveDaysTotal\n" .
 | 
				
			||||||
 | 
					            //         " - Tổng ngày phép còn lại: $remainingOnleaveDays\n"
 | 
				
			||||||
 | 
					            // );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $onleave_days_will_use = 0; // Ngày phép sẽ dùng
 | 
				
			||||||
 | 
					            $nopay_days_will_use = 0;   // Ngày ko phép sẽ dùng
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Ngày phép còn lại <= 0 (Hết phép)
 | 
				
			||||||
 | 
					            if ($remainingOnleaveDays <= 0) {
 | 
				
			||||||
 | 
					                $onleave_days_will_use = 0;
 | 
				
			||||||
 | 
					                $nopay_days_will_use = $willUsedDaysInMonth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Log::debug("--- Hết phép trong tháng ---");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Ngày phép còn lại < ngày yêu cầu (Không đủ phép)
 | 
				
			||||||
 | 
					            else if ($remainingOnleaveDays < $willUsedDaysInMonth) {
 | 
				
			||||||
 | 
					                // Vượt limit
 | 
				
			||||||
 | 
					                if ($willUsedDaysInMonth > $maxDaysPerMonth) {
 | 
				
			||||||
 | 
					                    // Phép còn lại > limit
 | 
				
			||||||
 | 
					                    if ($remainingOnleaveDays > $maxDaysPerMonth) {
 | 
				
			||||||
 | 
					                        $onleave_days_will_use = $maxDaysPerMonth;
 | 
				
			||||||
 | 
					                        $nopay_days_will_use = $willUsedDaysInMonth - $maxDaysPerMonth;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    // Phép còn lại < limit
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        $onleave_days_will_use = $remainingOnleaveDays;
 | 
				
			||||||
 | 
					                        $nopay_days_will_use = $willUsedDaysInMonth - $remainingOnleaveDays;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Log::debug("--- Không đủ phép trong tháng, vượt quá limit ---",);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Không vượt limit
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    $onleave_days_will_use = $remainingOnleaveDays;
 | 
				
			||||||
 | 
					                    $nopay_days_will_use = $willUsedDaysInMonth - $remainingOnleaveDays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Log::debug("--- Không đủ phép trong tháng, ko vượt limit ---");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Ngày phép còn lại >= ngày yêu cầu (Đủ phép)
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                // Vượt limit
 | 
				
			||||||
 | 
					                if ($willUsedDaysInMonth > $maxDaysPerMonth) {
 | 
				
			||||||
 | 
					                    $onleave_days_will_use = $maxDaysPerMonth;
 | 
				
			||||||
 | 
					                    $nopay_days_will_use = $willUsedDaysInMonth - $maxDaysPerMonth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Log::debug("--- Đủ phép, vượt limit ---");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Không vượt limit
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    $onleave_days_will_use = $willUsedDaysInMonth;
 | 
				
			||||||
 | 
					                    $nopay_days_will_use = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Log::debug("--- Đủ phép ---");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Log::debug("", [
 | 
				
			||||||
 | 
					                "Phep" => $onleave_days_will_use,
 | 
				
			||||||
 | 
					                "Khong Phep" => $nopay_days_will_use
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Có nghỉ không phép 
 | 
				
			||||||
 | 
					            if ($nopay_days_will_use > 0) {
 | 
				
			||||||
 | 
					                foreach ($notes as $note) {
 | 
				
			||||||
 | 
					                    $value = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if ($note->n_time_type === 'ALL' && $onleave_days_will_use == 0.5) {
 | 
				
			||||||
 | 
					                        // Chỉ còn 0.5 phép, chia thành 2 bản ghi: 1 phép, 1 không phép
 | 
				
			||||||
 | 
					                        // Ưu tiên phép cho buổi sáng (S), không phép cho buổi chiều (C)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Notes::create([
 | 
				
			||||||
 | 
					                            'n_user_id' => $user->id,
 | 
				
			||||||
 | 
					                            'n_day' => $note->n_day,
 | 
				
			||||||
 | 
					                            'n_month' => $note->n_month,
 | 
				
			||||||
 | 
					                            'n_year' => $note->n_year,
 | 
				
			||||||
 | 
					                            'n_time_type' => 'S',
 | 
				
			||||||
 | 
					                            'n_reason' => 'ONLEAVE',
 | 
				
			||||||
 | 
					                            'n_note' => $note->n_note,
 | 
				
			||||||
 | 
					                            'ticket_id' => $note->ticket_id
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                        Notes::create([
 | 
				
			||||||
 | 
					                            'n_user_id' => $user->id,
 | 
				
			||||||
 | 
					                            'n_day' => $note->n_day,
 | 
				
			||||||
 | 
					                            'n_month' => $note->n_month,
 | 
				
			||||||
 | 
					                            'n_year' => $note->n_year,
 | 
				
			||||||
 | 
					                            'n_time_type' => 'C',
 | 
				
			||||||
 | 
					                            'n_reason' => 'LEAVE_WITHOUT_PAY',
 | 
				
			||||||
 | 
					                            'n_note' => $note->n_note,
 | 
				
			||||||
 | 
					                            'ticket_id' => $note->ticket_id
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        $note->delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        $onleave_days_will_use = 0;
 | 
				
			||||||
 | 
					                        $nopay_days_will_use -= 0.5;
 | 
				
			||||||
 | 
					                    } elseif ($onleave_days_will_use > 0) {
 | 
				
			||||||
 | 
					                        // Dùng ngày phép trước
 | 
				
			||||||
 | 
					                        $use = min($onleave_days_will_use, $value);
 | 
				
			||||||
 | 
					                        $note->update([
 | 
				
			||||||
 | 
					                            'n_reason' => "ONLEAVE"
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                        $onleave_days_will_use -= $use;
 | 
				
			||||||
 | 
					                    } elseif ($nopay_days_will_use > 0) {
 | 
				
			||||||
 | 
					                        // Hết phép, chuyển sang không phép
 | 
				
			||||||
 | 
					                        $use = min($nopay_days_will_use,  $value);
 | 
				
			||||||
 | 
					                        $note->update([
 | 
				
			||||||
 | 
					                            'n_reason' => "LEAVE_WITHOUT_PAY"
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                        $nopay_days_will_use -= $use;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Đủ phép
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                foreach ($notes as $note) {
 | 
				
			||||||
 | 
					                    $note->update([
 | 
				
			||||||
 | 
					                        'n_reason' => "ONLEAVE"
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function getMaxLeaveDaysPerMonth(): int
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $limitLeaveMonth = Category::where('c_type', 'LIMIT_LEAVE_MONTH')->where('c_code', "LIMIT")->first();
 | 
				
			||||||
 | 
					        if ($limitLeaveMonth) {
 | 
				
			||||||
 | 
					            $maxDaysPerMonth = (int)$limitLeaveMonth->c_value;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $maxDaysPerMonth = 3; // default nếu k có setting
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $maxDaysPerMonth;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ class LeaveDays extends Model
 | 
				
			||||||
    use HasFactory;
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $fillable = [
 | 
					    protected $fillable = [
 | 
				
			||||||
        'id', 'ld_user_id', 'ld_day', 'ld_year', 'ld_date_additional', 'ld_note'
 | 
					        'id', 'ld_user_id', 'ld_day_total', 'ld_year', 'ld_additional_day', 'ld_note'
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $table = 'leave_days';
 | 
					    protected $table = 'leave_days';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,14 @@ class Notes extends Model
 | 
				
			||||||
    use HasFactory;
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $fillable = [
 | 
					    protected $fillable = [
 | 
				
			||||||
        'n_user_id', 'n_day', 'n_month', 'n_year', 'n_time_type', 'n_reason', 'n_note',
 | 
					        'n_user_id',
 | 
				
			||||||
 | 
					        'n_day',
 | 
				
			||||||
 | 
					        'n_month',
 | 
				
			||||||
 | 
					        'n_year',
 | 
				
			||||||
 | 
					        'n_time_type',
 | 
				
			||||||
 | 
					        'n_reason',
 | 
				
			||||||
 | 
					        'n_note',
 | 
				
			||||||
 | 
					        'ticket_id'
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -25,7 +32,7 @@ class Notes extends Model
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return self::leftJoin("categories as reason", function ($join) {
 | 
					        return self::leftJoin("categories as reason", function ($join) {
 | 
				
			||||||
            $join->on('n_reason', '=', 'reason.c_code');
 | 
					            $join->on('n_reason', '=', 'reason.c_code');
 | 
				
			||||||
            $join->on('reason.c_type', DB::raw("CONCAT('REASON')"));
 | 
					            $join->on('reason.c_type', DB::raw("CONCAT('REASON_NOTES')"));
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
            ->leftJoin("categories as timeTypes", function ($join) {
 | 
					            ->leftJoin("categories as timeTypes", function ($join) {
 | 
				
			||||||
                $join->on('n_time_type', '=', 'timeTypes.c_code');
 | 
					                $join->on('n_time_type', '=', 'timeTypes.c_code');
 | 
				
			||||||
| 
						 | 
					@ -47,4 +54,18 @@ class Notes extends Model
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            ->get();
 | 
					            ->get();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static function getNotesByMonthAndYearAndUserId($month, $year, $userId, $idNote)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return self::where('n_reason', 'ONLEAVE')->where('n_month', $month)->where('n_year', $year)
 | 
				
			||||||
 | 
					            ->where('n_user_id', $userId)
 | 
				
			||||||
 | 
					            ->where('id', '!=', $idNote)->get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static function getNotesByMonthAndYearAndUserIdAndReason($month, $year, $userId, $reason)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return self::where('n_reason', $reason)->where('n_month', $month)->where('n_year', $year)
 | 
				
			||||||
 | 
					            ->where('n_user_id', $userId)
 | 
				
			||||||
 | 
					            ->orderBy('n_day', 'asc')->orderBy('n_time_type', 'desc')->get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,8 @@ return [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'asset_url' => env('ASSET_URL'),
 | 
					    'asset_url' => env('ASSET_URL'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'client_url' => env('ADMIN_URL', 'http://localhost'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    |--------------------------------------------------------------------------
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
    | Application Timezone
 | 
					    | Application Timezone
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RenameLdDayToLdDayTotalInLeaveDaysTable extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('leave_days', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->renameColumn('ld_day', 'ld_day_total');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('leave_days', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->renameColumn('ld_day_total', 'ld_day');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddLdSpecialLeaveDayToLeaveDaysTable extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('leave_days', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->float('ld_special_leave_day')->default(0); // Adding the new field
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('leave_days', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('ld_special_leave_day'); // Dropping the field if needed
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RenameLdDateAdditionalToLdAdditionalDayInLeaveDaysTable extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('leave_days', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->renameColumn('ld_date_additional', 'ld_additional_day');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('leave_days', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->renameColumn('ld_date_additional', 'ld_additional_day');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'c_code' => 'LEAVE_WITHOUT_PAY',
 | 
				
			||||||
 | 
					                'c_name' => 'Nghỉ không hưởng lương',
 | 
				
			||||||
 | 
					                'c_type' => 'REASON',
 | 
				
			||||||
 | 
					                'c_value' => "",
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_name', 'Nghỉ phép')
 | 
				
			||||||
 | 
					            ->update(['c_name' => 'Nghỉ phép năm']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_name', 'Nghỉ phép năm')
 | 
				
			||||||
 | 
					            ->update(['c_name' => 'Nghỉ phép']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UpdateLeaveCategories extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Xóa item với type REASON và code LEAVE_WITHOUT_PAY
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_type', 'REASON')
 | 
				
			||||||
 | 
					            ->where('c_code', 'LEAVE_WITHOUT_PAY')
 | 
				
			||||||
 | 
					            ->delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Cập nhật tên "Nghỉ phép năm" thành "Nghỉ phép"
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_name', 'Nghỉ phép năm')
 | 
				
			||||||
 | 
					            ->update(['c_name' => 'Nghỉ phép']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Khôi phục item đã xóa
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            'c_code' => 'LEAVE_WITHOUT_PAY',
 | 
				
			||||||
 | 
					            'c_name' => 'Không phép',
 | 
				
			||||||
 | 
					            'c_type' => 'REASON',
 | 
				
			||||||
 | 
					            'c_value' => "",
 | 
				
			||||||
 | 
					            'c_active' => 1,
 | 
				
			||||||
 | 
					            'created_at' => now(),
 | 
				
			||||||
 | 
					            'updated_at' => now(),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Khôi phục tên cũ
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_name', 'Nghỉ phép')
 | 
				
			||||||
 | 
					            ->update(['c_name' => 'Nghỉ phép năm']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddLimitLeaveMonthCategory extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            'c_code' => 'LIMIT',
 | 
				
			||||||
 | 
					            'c_name' => 'Giới hạn số ngày nghỉ có phép/tháng',
 | 
				
			||||||
 | 
					            'c_type' => 'LIMIT_LEAVE_MONTH',
 | 
				
			||||||
 | 
					            'c_value' => '3',
 | 
				
			||||||
 | 
					            'c_active' => 1,
 | 
				
			||||||
 | 
					            'created_at' => now(),
 | 
				
			||||||
 | 
					            'updated_at' => now(),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_code', 'LIMIT')
 | 
				
			||||||
 | 
					            ->where('c_type', 'LIMIT_LEAVE_MONTH')
 | 
				
			||||||
 | 
					            ->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddSaturdayWorkScheduleCategory extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            'c_code' => '10-05-2025',
 | 
				
			||||||
 | 
					            'c_name' => 'Ngày bắt đầu làm việc thứ 7 trong năm',
 | 
				
			||||||
 | 
					            'c_type' => 'SATURDAY_WORK_SCHEDULE',
 | 
				
			||||||
 | 
					            'c_value' => '2025',
 | 
				
			||||||
 | 
					            'c_active' => 1,
 | 
				
			||||||
 | 
					            'created_at' => now(),
 | 
				
			||||||
 | 
					            'updated_at' => now(),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_code', '10-05-2025')
 | 
				
			||||||
 | 
					            ->where('c_type', 'SATURDAY_WORK_SCHEDULE')
 | 
				
			||||||
 | 
					            ->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddDayWorkSpecialCategory extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            'c_code' => '17-05-2025',
 | 
				
			||||||
 | 
					            'c_name' => 'Ngày làm việc đặc biệt',
 | 
				
			||||||
 | 
					            'c_type' => 'DAY_WORK_SPECIAL',
 | 
				
			||||||
 | 
					            'c_value' => '2025',
 | 
				
			||||||
 | 
					            'c_active' => 1,
 | 
				
			||||||
 | 
					            'created_at' => now(),
 | 
				
			||||||
 | 
					            'updated_at' => now(),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->where('c_code', '17-05-2025')
 | 
				
			||||||
 | 
					            ->where('c_type', 'DAY_WORK_SPECIAL')
 | 
				
			||||||
 | 
					            ->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddLeaveCategories extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'c_code' => 'LEAVE_WITHOUT_PAY',
 | 
				
			||||||
 | 
					                'c_name' => 'Không phép',
 | 
				
			||||||
 | 
					                'c_type' => 'REASON_NOTES',
 | 
				
			||||||
 | 
					                'c_value' => "",
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'c_code' => 'WFH',
 | 
				
			||||||
 | 
					                'c_name' => 'Work From Home',
 | 
				
			||||||
 | 
					                'c_type' => 'REASON_NOTES',
 | 
				
			||||||
 | 
					                'c_value' => "",
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'c_code' => 'ONLEAVE',
 | 
				
			||||||
 | 
					                'c_name' => 'Nghỉ phép',
 | 
				
			||||||
 | 
					                'c_type' => 'REASON_NOTES',
 | 
				
			||||||
 | 
					                'c_value' => "",
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')
 | 
				
			||||||
 | 
					            ->whereIn('c_code', ['LEAVE_WITHOUT_PAY', 'WFH', 'ONLEAVE'])
 | 
				
			||||||
 | 
					            ->where('c_type', 'REASON_NOTES')
 | 
				
			||||||
 | 
					            ->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('notes', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->foreignId('ticket_id')
 | 
				
			||||||
 | 
					                ->nullable()
 | 
				
			||||||
 | 
					                ->constrained('tickets')
 | 
				
			||||||
 | 
					                ->onDelete('cascade');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('notes', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropForeign(['ticket_id']);
 | 
				
			||||||
 | 
					            $table->dropColumn('ticket_id');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->boolean('is_permanent')->default(true);
 | 
				
			||||||
 | 
					            $table->date('permanent_date');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('users', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('is_permanent');
 | 
				
			||||||
 | 
					            $table->dropColumn('permanent_date');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->insert([
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'c_code' => 'PERMANENT',
 | 
				
			||||||
 | 
					                'c_name' => 'Phép cộng nhân viên chính thức',
 | 
				
			||||||
 | 
					                'c_type' => 'PERMANENT_ONLEAVE',
 | 
				
			||||||
 | 
					                'c_value' => 1,
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'c_code' => 'TEMPORARY_ONLEAVE',
 | 
				
			||||||
 | 
					                'c_name' => 'Nghỉ dự kiến',
 | 
				
			||||||
 | 
					                'c_type' => 'REASON_NOTES',
 | 
				
			||||||
 | 
					                'c_value' => "",
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->where('c_code', 'PERMANENT')->where('c_type', 'PERMANENT_ONLEAVE')->delete();
 | 
				
			||||||
 | 
					        DB::table('categories')->where('c_code', 'TEMPORARY_ONLEAVE')->where('c_type', 'REASON_NOTES')->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,119 +36,167 @@
 | 
				
			||||||
    <title>{{ $data['subject'] }}</title>
 | 
					    <title>{{ $data['subject'] }}</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body>
 | 
					<body style="
 | 
				
			||||||
 | 
					 | 
				
			||||||
    <body style="
 | 
					 | 
				
			||||||
        font-family: Arial, Helvetica, sans-serif;
 | 
					        font-family: Arial, Helvetica, sans-serif;
 | 
				
			||||||
        background-color: #edf2f7;
 | 
					        background-color: #edf2f7;
 | 
				
			||||||
    ">
 | 
					    ">
 | 
				
			||||||
        <table style="margin: 0 auto">
 | 
					    <table style="margin: 0 auto">
 | 
				
			||||||
            <tr>
 | 
					        <tr>
 | 
				
			||||||
                <td>
 | 
					            <td>
 | 
				
			||||||
                    <table
 | 
					                <table
 | 
				
			||||||
                        style="
 | 
					                    style="
 | 
				
			||||||
                        margin: 0 auto;
 | 
					                        margin: 0 auto;
 | 
				
			||||||
                        width: 768px;
 | 
					                        width: 768px;
 | 
				
			||||||
                    ">
 | 
					                    ">
 | 
				
			||||||
                        <tr>
 | 
					                    <tr>
 | 
				
			||||||
                            <td align="center" valign="top" style="padding: 36px 24px;">
 | 
					                        <td align="center" valign="top" style="padding: 36px 24px;">
 | 
				
			||||||
                                <a href="{{ config('app.url') }}" target="_blank" style="display: inline-block;">
 | 
					                            <a href="{{ config('app.url') }}" target="_blank" style="display: inline-block;">
 | 
				
			||||||
                                    <img src="https://apactech.io/wp-content/uploads/2022/12/APAC-TECH_side-e1670975093601-190x78.png"
 | 
					                                <img src="https://apactech.io/wp-content/uploads/2022/12/APAC-TECH_side-e1670975093601-190x78.png"
 | 
				
			||||||
                                        alt="Logo" border="0" width="100"
 | 
					                                    alt="Logo" border="0" width="100"
 | 
				
			||||||
                                        style="display: block; width: 100px; max-width: 100px; min-width: 48px;">
 | 
					                                    style="display: block; width: 100px; max-width: 100px; min-width: 48px;">
 | 
				
			||||||
                                </a>
 | 
					                            </a>
 | 
				
			||||||
                            </td>
 | 
					                        </td>
 | 
				
			||||||
                        </tr>
 | 
					                    </tr>
 | 
				
			||||||
                    </table>
 | 
					                </table>
 | 
				
			||||||
                </td>
 | 
					            </td>
 | 
				
			||||||
            </tr>
 | 
					        </tr>
 | 
				
			||||||
            <tr>
 | 
					        <tr>
 | 
				
			||||||
                <td>
 | 
					            <td>
 | 
				
			||||||
                    <table
 | 
					                <table
 | 
				
			||||||
                        style="
 | 
					                    style="
 | 
				
			||||||
                        margin: 0 auto;
 | 
					                        margin: 0 auto;
 | 
				
			||||||
                        background-color: #ffffff;
 | 
					                        background-color: #ffffff;
 | 
				
			||||||
                        width: 768px;
 | 
					                        width: 768px;
 | 
				
			||||||
                        padding: 24px;
 | 
					                        padding: 24px;
 | 
				
			||||||
                    ">
 | 
					                    ">
 | 
				
			||||||
                        <tr>
 | 
					                    <tr>
 | 
				
			||||||
                            <td>
 | 
					                        <td>
 | 
				
			||||||
                                <h3 style="color: #222222; margin: 5px 0 0 0; font-weight: bold">
 | 
					                            <h3 style="color: #222222; margin: 5px 0 0 0; font-weight: bold">
 | 
				
			||||||
                                    Dear Admin,
 | 
					                                Dear Admin,
 | 
				
			||||||
                                </h3>
 | 
					                            </h3>
 | 
				
			||||||
                            </td>
 | 
					                        </td>
 | 
				
			||||||
                        </tr>
 | 
					                    </tr>
 | 
				
			||||||
                
 | 
					
 | 
				
			||||||
                        <tr>
 | 
					                    <tr>
 | 
				
			||||||
                            <td>
 | 
					                        <td>
 | 
				
			||||||
                                <p style=" white-space:pre-line; margin: 0; margin-bottom: 5px">
 | 
					                            <p style=" white-space:pre-line; margin: 0; margin-bottom: 5px">
 | 
				
			||||||
                                    Employee <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span> has sent a request ticket, the specific content is as follows:
 | 
					                                Employee <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span> has sent a <a href="{{ config('app.client_url') . $data['link'] }}"> request ticket</a>, the specific content is as follows:
 | 
				
			||||||
                                </p>
 | 
					                            </p>
 | 
				
			||||||
                            </td>
 | 
					                        </td>
 | 
				
			||||||
                        </tr>
 | 
					                    </tr>
 | 
				
			||||||
                        
 | 
					
 | 
				
			||||||
                        <tr>
 | 
					                    <tr>
 | 
				
			||||||
                            <td>
 | 
					                        <td>
 | 
				
			||||||
                                <div style="padding-left: 10px;color: #696969;  margin-bottom: 15px">
 | 
					                            <div style="padding-left: 10px;color: #696969;  margin-bottom: 15px">
 | 
				
			||||||
                                    <p style="padding: 3px;">Name: <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span></p>
 | 
					                                <p style="padding: 3px;">Name: <span style="color: #222222;font-weight: bold;">{{ $data['name'] }}</span></p>
 | 
				
			||||||
                                    <p style="padding: 3px;">Date: <span style="color: #222222;font-weight: bold;">{{ $data['date'] }}</span></p>
 | 
					                                <p style="padding: 3px;">Date: <span style="color: #222222;font-weight: bold;">{{ $data['date'] }}</span></p>
 | 
				
			||||||
                                    <p style="padding: 3px;">Type: <span style="color: #222222;font-weight: bold;">{{ $data['type'] }}</span></p>
 | 
					                                <p style="padding: 3px;">Type: <span style="color: #222222;font-weight: bold;">{{ $data['type'] }}</span></p>
 | 
				
			||||||
                                    <p style="padding: 3px;">Note: <span style="color: #222222;font-weight: bold;">{{ $data['note'] }}</span></p>
 | 
					                                <p style="padding: 3px;">Note: <span style="color: #222222;font-weight: bold;">{{ $data['note'] }}</span></p>
 | 
				
			||||||
                                </div>
 | 
					                            </div>
 | 
				
			||||||
                            </td>
 | 
					                        </td>
 | 
				
			||||||
                        </tr>
 | 
					                    </tr>
 | 
				
			||||||
                        <tr>
 | 
					
 | 
				
			||||||
                            <td>
 | 
					                    <tr>
 | 
				
			||||||
                                <p style="margin:0 0 16px;padding:5px;margin: 5px;text-align: center;">
 | 
					                        <td>
 | 
				
			||||||
                                    <a href='{{ config('app.url') . $data['link'] }}'
 | 
					                            <p style="text-align: center">
 | 
				
			||||||
                                        style="
 | 
					                                You can quick
 | 
				
			||||||
                                  color: #fff;
 | 
					                                <span style="font-weight: bold">Confirm</span> or
 | 
				
			||||||
                                  border-radius: 10px;
 | 
					                                <span style="font-weight: bold">Refuse</span> here:
 | 
				
			||||||
                                  background-color: rgba(68,115,196);
 | 
					                            </p>
 | 
				
			||||||
                                  background-image: linear-gradient(to top left,rgba(0,0,0,.2),rgba(0,0,0,.2) 30%,rgba(0,0,0,0));
 | 
					
 | 
				
			||||||
                                  text-decoration: none;
 | 
					                            <div
 | 
				
			||||||
                                  display: inline-block;
 | 
					                                style="
 | 
				
			||||||
                                  font-weight: 600;
 | 
					                                        display: flex;
 | 
				
			||||||
                                  font-size: 16px;
 | 
					                                        justify-content: center;
 | 
				
			||||||
                                  line-height: 150%;
 | 
					                                        gap: 10px;
 | 
				
			||||||
                                  text-align: center;
 | 
					                                        margin-top: 10px;
 | 
				
			||||||
                                  margin: 0;
 | 
					                                    ">
 | 
				
			||||||
                                  padding: 10px 12px;
 | 
					                                <a
 | 
				
			||||||
                                  ">
 | 
					                                    href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'confirm', 'admin_email' => $data['admin_email']]) }}"
 | 
				
			||||||
                                        Check now</a>
 | 
					                                    style="
 | 
				
			||||||
                                </p>
 | 
					                                            color: #fff;
 | 
				
			||||||
                            </td>
 | 
					                                            border-radius: 10px;
 | 
				
			||||||
                        </tr>
 | 
					                                            background-color: #12b886;
 | 
				
			||||||
                       
 | 
					                                            background-image: linear-gradient(
 | 
				
			||||||
                    </table>
 | 
					                                                to top left,
 | 
				
			||||||
                </td>
 | 
					                                                rgba(0, 0, 0, 0.2),
 | 
				
			||||||
            </tr>
 | 
					                                                rgba(0, 0, 0, 0.2) 30%,
 | 
				
			||||||
            <tr>
 | 
					                                                rgba(0, 0, 0, 0)
 | 
				
			||||||
                <td>
 | 
					                                            );
 | 
				
			||||||
                    <table
 | 
					                                            text-decoration: none;
 | 
				
			||||||
                        style="
 | 
					                                            display: inline-block;
 | 
				
			||||||
 | 
					                                            font-weight: 600;
 | 
				
			||||||
 | 
					                                            font-size: 16px;
 | 
				
			||||||
 | 
					                                            line-height: 150%;
 | 
				
			||||||
 | 
					                                            text-align: center;
 | 
				
			||||||
 | 
					                                            margin: 0;
 | 
				
			||||||
 | 
					                                            padding: 10px 12px;
 | 
				
			||||||
 | 
					                                        ">
 | 
				
			||||||
 | 
					                                    Confirm</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <a
 | 
				
			||||||
 | 
					                                    href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'refuse', 'admin_email' => $data['admin_email']]) }}"
 | 
				
			||||||
 | 
					                                    style="
 | 
				
			||||||
 | 
					                                            color: #fff;
 | 
				
			||||||
 | 
					                                            border-radius: 10px;
 | 
				
			||||||
 | 
					                                            background-color: #f03e3e;
 | 
				
			||||||
 | 
					                                            background-image: linear-gradient(
 | 
				
			||||||
 | 
					                                                to top left,
 | 
				
			||||||
 | 
					                                                rgba(0, 0, 0, 0.2),
 | 
				
			||||||
 | 
					                                                rgba(0, 0, 0, 0.2) 30%,
 | 
				
			||||||
 | 
					                                                rgba(0, 0, 0, 0)
 | 
				
			||||||
 | 
					                                            );
 | 
				
			||||||
 | 
					                                            text-decoration: none;
 | 
				
			||||||
 | 
					                                            display: inline-block;
 | 
				
			||||||
 | 
					                                            font-weight: 600;
 | 
				
			||||||
 | 
					                                            font-size: 16px;
 | 
				
			||||||
 | 
					                                            line-height: 150%;
 | 
				
			||||||
 | 
					                                            text-align: center;
 | 
				
			||||||
 | 
					                                            margin: 0;
 | 
				
			||||||
 | 
					                                            padding: 10px 12px;
 | 
				
			||||||
 | 
					                                        ">
 | 
				
			||||||
 | 
					                                    Refuse</a>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <tr>
 | 
				
			||||||
 | 
					                        <td style="color: #222222;">
 | 
				
			||||||
 | 
					                            <div style="margin-top: 3rem">
 | 
				
			||||||
 | 
					                                <p><span style="font-weight: bold">Note</span>: If you are redirected to a <span style="font-weight: bold">404 page</span>, it means:</p>
 | 
				
			||||||
 | 
					                                <p>1. The ticket has already been approved by another admin.</p>
 | 
				
			||||||
 | 
					                                <p>2. The ticket has been deleted.</p>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					                </table>
 | 
				
			||||||
 | 
					            </td>
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					            <td>
 | 
				
			||||||
 | 
					                <table
 | 
				
			||||||
 | 
					                    style="
 | 
				
			||||||
                        margin: 0 auto;
 | 
					                        margin: 0 auto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        width: 768px;
 | 
					                        width: 768px;
 | 
				
			||||||
                    ">
 | 
					                    ">
 | 
				
			||||||
                        <tr>
 | 
					                    <tr>
 | 
				
			||||||
                            <td>
 | 
					                        <td>
 | 
				
			||||||
                                <h5
 | 
					                            <h5
 | 
				
			||||||
                                    style="
 | 
					                                style="
 | 
				
			||||||
                                    color: #222222;
 | 
					                                    color: #222222;
 | 
				
			||||||
                                    text-align: center;
 | 
					                                    text-align: center;
 | 
				
			||||||
                                    padding: 10px 36px;
 | 
					                                    padding: 10px 36px;
 | 
				
			||||||
                                    margin: 0;
 | 
					                                    margin: 0;
 | 
				
			||||||
                                ">
 | 
					                                ">
 | 
				
			||||||
                                    <p>© 2024 APAC Tech.</p>
 | 
					                                <p>© 2024 APAC Tech.</p>
 | 
				
			||||||
                                </h5>
 | 
					                            </h5>
 | 
				
			||||||
                            </td>
 | 
					                        </td>
 | 
				
			||||||
                        </tr>
 | 
					                    </tr>
 | 
				
			||||||
                    </table>
 | 
					                </table>
 | 
				
			||||||
                </td>
 | 
					            </td>
 | 
				
			||||||
            </tr>
 | 
					        </tr>
 | 
				
			||||||
        </table>
 | 
					    </table>
 | 
				
			||||||
    </body>
 | 
					 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require_once __DIR__ . "/../vendor/autoload.php";
 | 
				
			||||||
 | 
					$app = include_once __DIR__ . '/../bootstrap/app.php';
 | 
				
			||||||
 | 
					$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
 | 
				
			||||||
 | 
					$kernel->bootstrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Cập nhật lại data cho tới tháng hiện tại
 | 
				
			||||||
 | 
					$currentMonth = Carbon::now()->month;
 | 
				
			||||||
 | 
					$tmpClass = $app->make('Modules\Admin\app\Http\Controllers\TicketController');
 | 
				
			||||||
 | 
					$tmpClass->updateOldData($currentMonth, 2025); // Params: month, year
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 124 KiB  | 
| 
						 | 
					@ -48,6 +48,7 @@ export const getTickets = API_URL + 'v1/admin/ticket/all'
 | 
				
			||||||
export const getTicketsOfUser = API_URL + 'v1/admin/ticket/getByUserId'
 | 
					export const getTicketsOfUser = API_URL + 'v1/admin/ticket/getByUserId'
 | 
				
			||||||
export const deleteTicket = API_URL + 'v1/admin/ticket/delete'
 | 
					export const deleteTicket = API_URL + 'v1/admin/ticket/delete'
 | 
				
			||||||
export const addTicket = API_URL + 'v1/admin/ticket/create'
 | 
					export const addTicket = API_URL + 'v1/admin/ticket/create'
 | 
				
			||||||
 | 
					export const updateTicket = API_URL + 'v1/admin/ticket/update'
 | 
				
			||||||
export const handleTicket = API_URL + 'v1/admin/ticket/handle-ticket'
 | 
					export const handleTicket = API_URL + 'v1/admin/ticket/handle-ticket'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//Users
 | 
					//Users
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,18 @@
 | 
				
			||||||
import { getLeaveManagement, updateNoteLeave, exportLeaveManagement } from '@/api/Admin'
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
import { update } from '@/rtk/helpers/CRUD'
 | 
					import moment from 'moment'
 | 
				
			||||||
import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Avatar,
 | 
					  Avatar,
 | 
				
			||||||
 | 
					  Badge,
 | 
				
			||||||
  Box,
 | 
					  Box,
 | 
				
			||||||
  Button,
 | 
					  Button,
 | 
				
			||||||
  Drawer,
 | 
					  Drawer,
 | 
				
			||||||
 | 
					  Flex,
 | 
				
			||||||
 | 
					  Group,
 | 
				
			||||||
  HoverCard,
 | 
					  HoverCard,
 | 
				
			||||||
  Menu,
 | 
					  Menu,
 | 
				
			||||||
  Select,
 | 
					  Select,
 | 
				
			||||||
 | 
					  Stack,
 | 
				
			||||||
  Table,
 | 
					  Table,
 | 
				
			||||||
  Text,
 | 
					  Text,
 | 
				
			||||||
  Textarea,
 | 
					  Textarea,
 | 
				
			||||||
| 
						 | 
					@ -17,13 +21,17 @@ import {
 | 
				
			||||||
} from '@mantine/core'
 | 
					} from '@mantine/core'
 | 
				
			||||||
import { useDisclosure } from '@mantine/hooks'
 | 
					import { useDisclosure } from '@mantine/hooks'
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import moment from 'moment'
 | 
					import { IconEdit, IconFileExcel, IconHelpCircle } from '@tabler/icons-react'
 | 
				
			||||||
import { useEffect, useState } from 'react'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { IconEdit, IconFileExcel } from '@tabler/icons-react'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import classes from './LeaveManagement.module.css'
 | 
					import classes from './LeaveManagement.module.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  getLeaveManagement,
 | 
				
			||||||
 | 
					  updateNoteLeave,
 | 
				
			||||||
 | 
					  exportLeaveManagement,
 | 
				
			||||||
 | 
					} from '@/api/Admin'
 | 
				
			||||||
 | 
					import { update } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
 | 
					import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface User {
 | 
					interface User {
 | 
				
			||||||
  id: number
 | 
					  id: number
 | 
				
			||||||
  name: string
 | 
					  name: string
 | 
				
			||||||
| 
						 | 
					@ -40,8 +48,9 @@ interface LeaveDay {
 | 
				
			||||||
  id: number
 | 
					  id: number
 | 
				
			||||||
  ld_user_id: number
 | 
					  ld_user_id: number
 | 
				
			||||||
  ld_year: number
 | 
					  ld_year: number
 | 
				
			||||||
  ld_day: number
 | 
					  ld_day_total: number
 | 
				
			||||||
  ld_date_additional: number
 | 
					  ld_additional_day: number
 | 
				
			||||||
 | 
					  ld_special_leave_day: number
 | 
				
			||||||
  ld_note: string
 | 
					  ld_note: string
 | 
				
			||||||
  created_at: string | null
 | 
					  created_at: string | null
 | 
				
			||||||
  updated_at: string | null
 | 
					  updated_at: string | null
 | 
				
			||||||
| 
						 | 
					@ -52,6 +61,7 @@ interface MonthlyLeaveDays {
 | 
				
			||||||
  leave_days: number
 | 
					  leave_days: number
 | 
				
			||||||
  month: number
 | 
					  month: number
 | 
				
			||||||
  n_user_id: number
 | 
					  n_user_id: number
 | 
				
			||||||
 | 
					  reason_code: string
 | 
				
			||||||
  reason_name: string
 | 
					  reason_name: string
 | 
				
			||||||
  time_type_name: string
 | 
					  time_type_name: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -62,6 +72,18 @@ interface UserData {
 | 
				
			||||||
  monthlyLeaveDays: MonthlyLeaveDays[]
 | 
					  monthlyLeaveDays: MonthlyLeaveDays[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// interface DataReason {
 | 
				
			||||||
 | 
					//   id: number
 | 
				
			||||||
 | 
					//   c_code: string
 | 
				
			||||||
 | 
					//   c_name: string
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// interface DataTimeType {
 | 
				
			||||||
 | 
					//   id: number
 | 
				
			||||||
 | 
					//   c_code: string
 | 
				
			||||||
 | 
					//   c_name: string
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LeaveManagement = () => {
 | 
					const LeaveManagement = () => {
 | 
				
			||||||
  const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
 | 
					  const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
 | 
				
			||||||
  const [disableBtn, setDisableBtn] = useState(false)
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
| 
						 | 
					@ -75,6 +97,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
    note: string
 | 
					    note: string
 | 
				
			||||||
    totalLeave: string
 | 
					    totalLeave: string
 | 
				
			||||||
    dayAdditional: string
 | 
					    dayAdditional: string
 | 
				
			||||||
 | 
					    specialLeave: string
 | 
				
			||||||
  }>({
 | 
					  }>({
 | 
				
			||||||
    id: 0,
 | 
					    id: 0,
 | 
				
			||||||
    user: {
 | 
					    user: {
 | 
				
			||||||
| 
						 | 
					@ -84,12 +107,53 @@ const LeaveManagement = () => {
 | 
				
			||||||
    note: '',
 | 
					    note: '',
 | 
				
			||||||
    totalLeave: '',
 | 
					    totalLeave: '',
 | 
				
			||||||
    dayAdditional: '',
 | 
					    dayAdditional: '',
 | 
				
			||||||
 | 
					    specialLeave: '',
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [data, setData] = useState<UserData[]>([])
 | 
					  const [data, setData] = useState<UserData[]>([])
 | 
				
			||||||
  const [date, setDate] = useState({
 | 
					  const [date, setDate] = useState({
 | 
				
			||||||
    year: new Date().getFullYear().toString(),
 | 
					    year: new Date().getFullYear().toString(),
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					  // const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
 | 
				
			||||||
 | 
					  // const [dataReason, setDataReason] = useState<DataReason[]>([])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const now = new Date()
 | 
				
			||||||
 | 
					  const currentMonth = now.getMonth() + 1 // getMonth() trả về 0-11
 | 
				
			||||||
 | 
					  const currentYear = now.getFullYear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // const getListMasterByType = async (type: string) => {
 | 
				
			||||||
 | 
					  //   try {
 | 
				
			||||||
 | 
					  //     const params = {
 | 
				
			||||||
 | 
					  //       type: type,
 | 
				
			||||||
 | 
					  //     }
 | 
				
			||||||
 | 
					  //     const res = await get(getListMaster, params)
 | 
				
			||||||
 | 
					  //     if (res.status) {
 | 
				
			||||||
 | 
					  //       return res.data
 | 
				
			||||||
 | 
					  //     }
 | 
				
			||||||
 | 
					  //   } catch (error: any) {
 | 
				
			||||||
 | 
					  //     notifications.show({
 | 
				
			||||||
 | 
					  //       title: 'Error',
 | 
				
			||||||
 | 
					  //       message: error.message ?? error,
 | 
				
			||||||
 | 
					  //       color: 'red',
 | 
				
			||||||
 | 
					  //     })
 | 
				
			||||||
 | 
					  //   }
 | 
				
			||||||
 | 
					  //   return []
 | 
				
			||||||
 | 
					  // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // useEffect(() => {
 | 
				
			||||||
 | 
					  //   const fetchData = async () => {
 | 
				
			||||||
 | 
					  //     const resultTimeType = await getListMasterByType('TIME_TYPE')
 | 
				
			||||||
 | 
					  //     setDataTimeType(
 | 
				
			||||||
 | 
					  //       resultTimeType.filter((item: DataTimeType) => item.c_code !== 'ALL'),
 | 
				
			||||||
 | 
					  //     )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //     const resultReason = await getListMasterByType('REASON')
 | 
				
			||||||
 | 
					  //     setDataReason(resultReason)
 | 
				
			||||||
 | 
					  //   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //   fetchData()
 | 
				
			||||||
 | 
					  // }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getLeaveList = async () => {
 | 
					  const getLeaveList = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const res = await get(getLeaveManagement, {
 | 
					      const res = await get(getLeaveManagement, {
 | 
				
			||||||
| 
						 | 
					@ -122,6 +186,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    totalLeave: string,
 | 
					    totalLeave: string,
 | 
				
			||||||
    dayAdditional: string,
 | 
					    dayAdditional: string,
 | 
				
			||||||
 | 
					    specialLeave: string,
 | 
				
			||||||
    note: string,
 | 
					    note: string,
 | 
				
			||||||
  ) => {
 | 
					  ) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
| 
						 | 
					@ -132,6 +197,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
          users: users,
 | 
					          users: users,
 | 
				
			||||||
          totalLeave: totalLeave,
 | 
					          totalLeave: totalLeave,
 | 
				
			||||||
          dayAdditional: dayAdditional,
 | 
					          dayAdditional: dayAdditional,
 | 
				
			||||||
 | 
					          specialLeave: specialLeave,
 | 
				
			||||||
          note: note,
 | 
					          note: note,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        getLeaveList,
 | 
					        getLeaveList,
 | 
				
			||||||
| 
						 | 
					@ -202,8 +268,6 @@ const LeaveManagement = () => {
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // console.log(customAddNotes, 'customAddNotes')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const getDetailLeaveDay = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
 | 
					  const getDetailLeaveDay = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
 | 
				
			||||||
    type MonthlyLeaveDaysAcc = {
 | 
					    type MonthlyLeaveDaysAcc = {
 | 
				
			||||||
      [key: string]: { n_user_id: number; month: number; leave_days: number }
 | 
					      [key: string]: { n_user_id: number; month: number; leave_days: number }
 | 
				
			||||||
| 
						 | 
					@ -227,37 +291,97 @@ const LeaveManagement = () => {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const showAllOff = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
 | 
					  const showAllOff = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
 | 
				
			||||||
    let lastmonth = 0
 | 
					    return monthInYear.map((d, i) => {
 | 
				
			||||||
    return monthlyLeaveDays.map((itemDay, indexDay) => {
 | 
					      let totalOnLeaveMonth = 0
 | 
				
			||||||
      const isNewMonth = lastmonth !== itemDay.month
 | 
					      let totalLeaveWithoutPayMonth = 0
 | 
				
			||||||
      if (isNewMonth) {
 | 
					      let totalTempMonth = 0
 | 
				
			||||||
        lastmonth = itemDay.month
 | 
					
 | 
				
			||||||
 | 
					      monthlyLeaveDays
 | 
				
			||||||
 | 
					        .filter((item) => item.month === d.value)
 | 
				
			||||||
 | 
					        .map((item) => {
 | 
				
			||||||
 | 
					          if (item.reason_code === 'ONLEAVE') {
 | 
				
			||||||
 | 
					            totalOnLeaveMonth += Number(item.leave_days)
 | 
				
			||||||
 | 
					          } else if (item.reason_code === 'LEAVE_WITHOUT_PAY') {
 | 
				
			||||||
 | 
					            totalLeaveWithoutPayMonth += Number(item.leave_days)
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            totalTempMonth += Number(item.leave_days)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (totalOnLeaveMonth === 0 && totalLeaveWithoutPayMonth === 0) {
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <div key={indexDay}>
 | 
					        <Box key={i} px="xs" my="xs">
 | 
				
			||||||
          {isNewMonth && <p>Month {lastmonth}</p>}
 | 
					          <Group gap="xs">
 | 
				
			||||||
          <p style={{ paddingLeft: '20px' }}>
 | 
					            {totalOnLeaveMonth > 0 && (
 | 
				
			||||||
            - {itemDay.reason_name} ({itemDay.time_type_name}) {itemDay.day}
 | 
					              <Badge color="teal" variant="light">
 | 
				
			||||||
            /{itemDay.month}
 | 
					                {totalOnLeaveMonth} có phép
 | 
				
			||||||
          </p>
 | 
					              </Badge>
 | 
				
			||||||
        </div>
 | 
					            )}
 | 
				
			||||||
 | 
					            {totalLeaveWithoutPayMonth > 0 && (
 | 
				
			||||||
 | 
					              <Badge color="red" variant="light">
 | 
				
			||||||
 | 
					                {totalLeaveWithoutPayMonth} không phép
 | 
				
			||||||
 | 
					              </Badge>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					            <Text size="xs" color="dimmed">
 | 
				
			||||||
 | 
					              ({d.value}/2025)
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					          </Group>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const showAllTotal = (
 | 
				
			||||||
 | 
					    ld_day_total: number,
 | 
				
			||||||
 | 
					    ld_additional_day: number,
 | 
				
			||||||
 | 
					    ld_special_leave_day: number,
 | 
				
			||||||
 | 
					  ): JSX.Element => {
 | 
				
			||||||
 | 
					    const showItem = (label: string, value: number, color: string = 'gray') => {
 | 
				
			||||||
 | 
					      if (value === 0) return null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return (
 | 
				
			||||||
 | 
					        <Group justify="space-between" gap="xs">
 | 
				
			||||||
 | 
					          <Text size="sm" c="dimmed">
 | 
				
			||||||
 | 
					            {label}
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Text size="sm" fw={500} c={color}>
 | 
				
			||||||
 | 
					            {value}
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					        </Group>
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Box p="sm">
 | 
				
			||||||
 | 
					        <Stack gap={4}>
 | 
				
			||||||
 | 
					          {showItem(
 | 
				
			||||||
 | 
					            'Tổng phép hiện có:',
 | 
				
			||||||
 | 
					            ld_day_total + ld_additional_day + ld_special_leave_day,
 | 
				
			||||||
 | 
					            'white',
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          {showItem('+ Phép được cấp năm nay:', ld_day_total, 'teal')}
 | 
				
			||||||
 | 
					          {showItem('+ Phép tồn năm trước:', ld_additional_day, 'violet')}
 | 
				
			||||||
 | 
					          {showItem('+ Phép đặc biệt:', ld_special_leave_day, 'orange')}
 | 
				
			||||||
 | 
					        </Stack>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleExport = async () => {
 | 
					  const handleExport = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const timestamp = moment().format('DDMMYYYY_HHmmss')
 | 
					      const timestamp = moment().format('DDMMYYYY_HHmmss')
 | 
				
			||||||
      const fileName = `LeaveManagement_${date.year}_${timestamp}.xlsx`
 | 
					      const fileName = `LeaveManagement_${date.year}_${timestamp}.xlsx`
 | 
				
			||||||
      
 | 
					
 | 
				
			||||||
      await exportFile(
 | 
					      await exportFile(
 | 
				
			||||||
        exportLeaveManagement,
 | 
					        exportLeaveManagement,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          year: parseInt(date.year)
 | 
					          year: parseInt(date.year),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        fileName
 | 
					        fileName,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      console.error('Export error:', error)
 | 
					      console.error('Export error:', error)
 | 
				
			||||||
      notifications.show({
 | 
					      notifications.show({
 | 
				
			||||||
| 
						 | 
					@ -271,10 +395,9 @@ const LeaveManagement = () => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <div className={classes.title}>
 | 
					      <div className={classes.title}>
 | 
				
			||||||
        <h3>
 | 
					        <h3>Leave Management</h3>
 | 
				
			||||||
          Leave Management
 | 
					 | 
				
			||||||
        </h3>
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					      {/* Update Leave Day */}
 | 
				
			||||||
      <Drawer
 | 
					      <Drawer
 | 
				
			||||||
        opened={opened1}
 | 
					        opened={opened1}
 | 
				
			||||||
        onClose={close1}
 | 
					        onClose={close1}
 | 
				
			||||||
| 
						 | 
					@ -305,8 +428,8 @@ const LeaveManagement = () => {
 | 
				
			||||||
              })
 | 
					              })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
          label={'Total Leave'}
 | 
					          label={'Phép năm'}
 | 
				
			||||||
          placeholder="Input placeholder"
 | 
					          placeholder="Nhập số ngày phép năm"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <TextInput
 | 
					        <TextInput
 | 
				
			||||||
          mb={'md'}
 | 
					          mb={'md'}
 | 
				
			||||||
| 
						 | 
					@ -332,10 +455,36 @@ const LeaveManagement = () => {
 | 
				
			||||||
              })
 | 
					              })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
          label={'Day additional leave'}
 | 
					          label={'Phép năm cũ'}
 | 
				
			||||||
          placeholder="Input placeholder"
 | 
					          placeholder="Nhập số ngày phép năm cũ"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <TextInput
 | 
				
			||||||
 | 
					          mb={'md'}
 | 
				
			||||||
 | 
					          value={customAddNotes.specialLeave}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            const value = e.target.value
 | 
				
			||||||
 | 
					            if (value) {
 | 
				
			||||||
 | 
					              const floatValue = parseFloat(value)
 | 
				
			||||||
 | 
					              if (
 | 
				
			||||||
 | 
					                /^\d*\.?\d?$/.test(value) &&
 | 
				
			||||||
 | 
					                floatValue >= 0 &&
 | 
				
			||||||
 | 
					                floatValue <= 20
 | 
				
			||||||
 | 
					              ) {
 | 
				
			||||||
 | 
					                setCustomAddNotes({
 | 
				
			||||||
 | 
					                  ...customAddNotes,
 | 
				
			||||||
 | 
					                  specialLeave: value,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              setCustomAddNotes({
 | 
				
			||||||
 | 
					                ...customAddNotes,
 | 
				
			||||||
 | 
					                specialLeave: '',
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          label={'Phép đặc biệt'}
 | 
				
			||||||
 | 
					          placeholder="Nhập số ngày phép đặc biệt"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <Textarea
 | 
					        <Textarea
 | 
				
			||||||
          mb={'md'}
 | 
					          mb={'md'}
 | 
				
			||||||
          label="Note"
 | 
					          label="Note"
 | 
				
			||||||
| 
						 | 
					@ -343,6 +492,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
          onChange={(e) => {
 | 
					          onChange={(e) => {
 | 
				
			||||||
            setCustomAddNotes({ ...customAddNotes, note: e.target.value })
 | 
					            setCustomAddNotes({ ...customAddNotes, note: e.target.value })
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
 | 
					          rows={10}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
| 
						 | 
					@ -368,6 +518,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
                customAddNotes.user,
 | 
					                customAddNotes.user,
 | 
				
			||||||
                customAddNotes.totalLeave,
 | 
					                customAddNotes.totalLeave,
 | 
				
			||||||
                customAddNotes.dayAdditional,
 | 
					                customAddNotes.dayAdditional,
 | 
				
			||||||
 | 
					                customAddNotes.specialLeave,
 | 
				
			||||||
                customAddNotes.note,
 | 
					                customAddNotes.note,
 | 
				
			||||||
              )
 | 
					              )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -377,77 +528,76 @@ const LeaveManagement = () => {
 | 
				
			||||||
          Save
 | 
					          Save
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      </Drawer>
 | 
					      </Drawer>
 | 
				
			||||||
      <Box display={'flex'}>
 | 
					
 | 
				
			||||||
        <Box style={{ display: 'flex', flexFlow: 'column' }} w={'30%'}>
 | 
					      {/* Filter Year, Export Btn */}
 | 
				
			||||||
          <Box w="100%" display={'flex'}>
 | 
					      <Flex justify="space-between" align="flex-end">
 | 
				
			||||||
            <Select
 | 
					        <Select
 | 
				
			||||||
              w="50%"
 | 
					          value={date.year}
 | 
				
			||||||
              value={date.year}
 | 
					          size="xs"
 | 
				
			||||||
              size="xs"
 | 
					          label="Year"
 | 
				
			||||||
              ml={'sm'}
 | 
					          data={Array.from({ length: 10 }, (_, index) => {
 | 
				
			||||||
              label="Year"
 | 
					            return {
 | 
				
			||||||
              data={Array.from({ length: 10 }, (_, index) => {
 | 
					              value: (
 | 
				
			||||||
                return {
 | 
					                parseInt(moment(Date.now()).format('YYYY')) -
 | 
				
			||||||
                  value: (
 | 
					                3 +
 | 
				
			||||||
                    parseInt(moment(Date.now()).format('YYYY')) -
 | 
					                index
 | 
				
			||||||
                    3 +
 | 
					              ).toString(),
 | 
				
			||||||
                    index
 | 
					              label: (
 | 
				
			||||||
                  ).toString(),
 | 
					                parseInt(moment(Date.now()).format('YYYY')) -
 | 
				
			||||||
                  label: (
 | 
					                3 +
 | 
				
			||||||
                    parseInt(moment(Date.now()).format('YYYY')) -
 | 
					                index
 | 
				
			||||||
                    3 +
 | 
					              ).toString(),
 | 
				
			||||||
                    index
 | 
					              disabled:
 | 
				
			||||||
                  ).toString(),
 | 
					                parseInt(moment(Date.now()).format('YYYY')) - 3 + index >
 | 
				
			||||||
                  disabled:
 | 
					                parseInt(moment(Date.now()).format('YYYY')),
 | 
				
			||||||
                    parseInt(moment(Date.now()).format('YYYY')) - 3 + index >
 | 
					            }
 | 
				
			||||||
                    parseInt(moment(Date.now()).format('YYYY')),
 | 
					          })}
 | 
				
			||||||
                }
 | 
					          onChange={(e) => {
 | 
				
			||||||
              })}
 | 
					            setDate({ ...date, year: e! })
 | 
				
			||||||
              onChange={(e) => {
 | 
					 | 
				
			||||||
                setDate({ ...date, year: e! })
 | 
					 | 
				
			||||||
              }}
 | 
					 | 
				
			||||||
            ></Select>
 | 
					 | 
				
			||||||
          </Box>
 | 
					 | 
				
			||||||
        </Box>
 | 
					 | 
				
			||||||
        <Box
 | 
					 | 
				
			||||||
          w="70%"
 | 
					 | 
				
			||||||
          pl={200}
 | 
					 | 
				
			||||||
          style={{
 | 
					 | 
				
			||||||
            display: 'flex',
 | 
					 | 
				
			||||||
            justifyContent: 'end',
 | 
					 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
 | 
					          w={200}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Button
 | 
				
			||||||
 | 
					          size="xs"
 | 
				
			||||||
 | 
					          onClick={handleExport}
 | 
				
			||||||
 | 
					          leftSection={<IconFileExcel size={16} />}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <Box display={'flex'} style={{ alignItems: 'end' }}>
 | 
					          Export Excel
 | 
				
			||||||
            <Button
 | 
					        </Button>
 | 
				
			||||||
              size="xs"
 | 
					      </Flex>
 | 
				
			||||||
              ml={'sm'}
 | 
					
 | 
				
			||||||
              onClick={handleExport}
 | 
					      {/* Leave Day Table */}
 | 
				
			||||||
              leftSection={<IconFileExcel size={16} />}
 | 
					      <Box style={{ overflowX: 'auto' }}>
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              Export Excel
 | 
					 | 
				
			||||||
            </Button>
 | 
					 | 
				
			||||||
          </Box>
 | 
					 | 
				
			||||||
        </Box>
 | 
					 | 
				
			||||||
      </Box>
 | 
					 | 
				
			||||||
      <Box>
 | 
					 | 
				
			||||||
        <Table
 | 
					        <Table
 | 
				
			||||||
          striped
 | 
					          striped
 | 
				
			||||||
          highlightOnHover
 | 
					          highlightOnHover
 | 
				
			||||||
          withTableBorder
 | 
					          withTableBorder
 | 
				
			||||||
          withColumnBorders
 | 
					          withColumnBorders
 | 
				
			||||||
          mt={'md'}
 | 
					          mt={'md'}
 | 
				
			||||||
 | 
					          miw={1580}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <Table.Thead>
 | 
					          <Table.Thead>
 | 
				
			||||||
            <Table.Tr bg={'#228be66b'}>
 | 
					            <Table.Tr bg={'#228be66b'}>
 | 
				
			||||||
              <Table.Th ></Table.Th>
 | 
					              <Table.Th ta={'center'} style={{ width: '40px' }}></Table.Th>
 | 
				
			||||||
              <Table.Th>User</Table.Th>
 | 
					              <Table.Th>User</Table.Th>
 | 
				
			||||||
              {monthInYear.map((d) => {
 | 
					              {monthInYear.map((d) => {
 | 
				
			||||||
 | 
					                const isCurrentMonth =
 | 
				
			||||||
 | 
					                  Number(date.year) === currentYear && d.value === currentMonth
 | 
				
			||||||
                return (
 | 
					                return (
 | 
				
			||||||
                  <Menu width={200} shadow="md" key={d.value}>
 | 
					                  <Menu width={200} shadow="md" key={d.value}>
 | 
				
			||||||
                    <Menu.Target>
 | 
					                    <Menu.Target>
 | 
				
			||||||
                      <Table.Th
 | 
					                      <Table.Th
 | 
				
			||||||
                        ta={'center'}
 | 
					                        ta={'center'}
 | 
				
			||||||
                        style={{ cursor: 'pointer', width: '60px' }}
 | 
					                        style={{
 | 
				
			||||||
 | 
					                          cursor: 'pointer',
 | 
				
			||||||
 | 
					                          width: '40px',
 | 
				
			||||||
 | 
					                          backgroundColor: isCurrentMonth
 | 
				
			||||||
 | 
					                            ? '#F2E891'
 | 
				
			||||||
 | 
					                            : undefined,
 | 
				
			||||||
 | 
					                          color: isCurrentMonth ? '#000' : undefined,
 | 
				
			||||||
 | 
					                          fontWeight: isCurrentMonth ? 'bold' : undefined,
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
                      >
 | 
					                      >
 | 
				
			||||||
                        <span>{d.name}</span>
 | 
					                        <span>{d.name}</span>
 | 
				
			||||||
                      </Table.Th>
 | 
					                      </Table.Th>
 | 
				
			||||||
| 
						 | 
					@ -458,7 +608,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
              <Table.Th ta={'center'} style={{ width: '80px' }}>
 | 
					              <Table.Th ta={'center'} style={{ width: '80px' }}>
 | 
				
			||||||
                Total
 | 
					                Total
 | 
				
			||||||
              </Table.Th>
 | 
					              </Table.Th>
 | 
				
			||||||
              <Table.Th ta={'center'} style={{ width: '80px' }}>
 | 
					              <Table.Th ta={'center'} style={{ width: '130px' }}>
 | 
				
			||||||
                Off
 | 
					                Off
 | 
				
			||||||
              </Table.Th>
 | 
					              </Table.Th>
 | 
				
			||||||
              <Table.Th ta={'center'} style={{ width: '80px' }}>
 | 
					              <Table.Th ta={'center'} style={{ width: '80px' }}>
 | 
				
			||||||
| 
						 | 
					@ -472,23 +622,41 @@ const LeaveManagement = () => {
 | 
				
			||||||
            {data.map((user, index) => {
 | 
					            {data.map((user, index) => {
 | 
				
			||||||
              let totalDayOff = 0
 | 
					              let totalDayOff = 0
 | 
				
			||||||
              let totalDayLeave =
 | 
					              let totalDayLeave =
 | 
				
			||||||
                user.leaveDay.ld_day + user.leaveDay.ld_date_additional
 | 
					                user.leaveDay.ld_day_total +
 | 
				
			||||||
 | 
					                user.leaveDay.ld_additional_day +
 | 
				
			||||||
 | 
					                user.leaveDay.ld_special_leave_day
 | 
				
			||||||
 | 
					              let ld_day_total = user.leaveDay.ld_day_total
 | 
				
			||||||
 | 
					              let ld_additional_day = user.leaveDay.ld_additional_day
 | 
				
			||||||
 | 
					              let ld_special_leave_day = user.leaveDay.ld_special_leave_day
 | 
				
			||||||
              let ld_note = user.leaveDay.ld_note
 | 
					              let ld_note = user.leaveDay.ld_note
 | 
				
			||||||
 | 
					              let totalOnLeave = 0
 | 
				
			||||||
 | 
					              let totalLeaveWithoutPay = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              return (
 | 
					              return (
 | 
				
			||||||
                <Table.Tr key={user.user.id} className={classes.tableTr}>
 | 
					                <Table.Tr key={user.user.id} className={classes.tableTr}>
 | 
				
			||||||
                  <Table.Td ta={'center'}>{index + 1}</Table.Td>
 | 
					                  <Table.Td ta={'center'}>{index + 1}</Table.Td>
 | 
				
			||||||
                  <Table.Td>
 | 
					                  <Table.Td>
 | 
				
			||||||
                    <Tooltip multiline label={user.user.name}>
 | 
					                    <Tooltip multiline label={user.user.name}>
 | 
				
			||||||
                      <div style={{display:'flex', alignItems:'center'}}><Avatar size={'md'} mr={'md'} src={import.meta.env.VITE_BACKEND_URL.includes('local')
 | 
					                      <div style={{ display: 'flex', alignItems: 'center' }}>
 | 
				
			||||||
                          ? import.meta.env.VITE_BACKEND_URL +
 | 
					                        <Avatar
 | 
				
			||||||
                            'storage/' +
 | 
					                          size={'md'}
 | 
				
			||||||
                           user.user.avatar
 | 
					                          mr={'md'}
 | 
				
			||||||
                          : import.meta.env.VITE_BACKEND_URL +
 | 
					                          src={
 | 
				
			||||||
                            'image/storage/' +
 | 
					                            import.meta.env.VITE_BACKEND_URL.includes('local')
 | 
				
			||||||
                           user.user.avatar}/>{user.user.name}</div>
 | 
					                              ? import.meta.env.VITE_BACKEND_URL +
 | 
				
			||||||
 | 
					                                'storage/' +
 | 
				
			||||||
 | 
					                                user.user.avatar
 | 
				
			||||||
 | 
					                              : import.meta.env.VITE_BACKEND_URL +
 | 
				
			||||||
 | 
					                                'image/storage/' +
 | 
				
			||||||
 | 
					                                user.user.avatar
 | 
				
			||||||
 | 
					                          }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                        {user.user.name}
 | 
				
			||||||
 | 
					                      </div>
 | 
				
			||||||
                    </Tooltip>
 | 
					                    </Tooltip>
 | 
				
			||||||
                  </Table.Td>
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  {/* On leave per month */}
 | 
				
			||||||
                  {monthInYear.map((d, i) => {
 | 
					                  {monthInYear.map((d, i) => {
 | 
				
			||||||
                    let leaveDataByMonth = getDetailLeaveDay(
 | 
					                    let leaveDataByMonth = getDetailLeaveDay(
 | 
				
			||||||
                      user.monthlyLeaveDays,
 | 
					                      user.monthlyLeaveDays,
 | 
				
			||||||
| 
						 | 
					@ -497,62 +665,220 @@ const LeaveManagement = () => {
 | 
				
			||||||
                    const monthData = leaveDataByMonth[d.value]
 | 
					                    const monthData = leaveDataByMonth[d.value]
 | 
				
			||||||
                    let total = monthData ? monthData.leave_days : 0
 | 
					                    let total = monthData ? monthData.leave_days : 0
 | 
				
			||||||
                    totalDayOff = totalDayOff + total
 | 
					                    totalDayOff = totalDayOff + total
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let onleaveDaysInMonth: MonthlyLeaveDays[] = []
 | 
				
			||||||
 | 
					                    let nopayDaysInMonth: MonthlyLeaveDays[] = []
 | 
				
			||||||
 | 
					                    let tempDaysInMonth: MonthlyLeaveDays[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let totalOnLeaveMonth = 0
 | 
				
			||||||
 | 
					                    let totalLeaveWithoutPayMonth = 0
 | 
				
			||||||
 | 
					                    let totalTempMonth = 0
 | 
				
			||||||
 | 
					                    let usedAdditionalDay = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    user.monthlyLeaveDays
 | 
				
			||||||
 | 
					                      .filter((item) => item.month === d.value)
 | 
				
			||||||
 | 
					                      .map((item) => {
 | 
				
			||||||
 | 
					                        if (item.reason_code === 'ONLEAVE') {
 | 
				
			||||||
 | 
					                          totalOnLeaveMonth += Number(item.leave_days)
 | 
				
			||||||
 | 
					                          onleaveDaysInMonth.push(item)
 | 
				
			||||||
 | 
					                        } else if (item.reason_code === 'LEAVE_WITHOUT_PAY') {
 | 
				
			||||||
 | 
					                          totalLeaveWithoutPayMonth += Number(item.leave_days)
 | 
				
			||||||
 | 
					                          nopayDaysInMonth.push(item)
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                          totalTempMonth += Number(item.leave_days)
 | 
				
			||||||
 | 
					                          tempDaysInMonth.push(item)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Xử lý hiện thị phép tồn sử dụng
 | 
				
			||||||
 | 
					                    let tmpTotalOnleave = totalOnLeave
 | 
				
			||||||
 | 
					                    totalOnLeave += totalOnLeaveMonth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (d.value < 4) {
 | 
				
			||||||
 | 
					                      if (totalOnLeave < ld_additional_day) {
 | 
				
			||||||
 | 
					                        usedAdditionalDay = totalOnLeaveMonth
 | 
				
			||||||
 | 
					                        totalOnLeaveMonth = 0
 | 
				
			||||||
 | 
					                      } else {
 | 
				
			||||||
 | 
					                        usedAdditionalDay = ld_additional_day - tmpTotalOnleave
 | 
				
			||||||
 | 
					                        if (usedAdditionalDay >= 0) {
 | 
				
			||||||
 | 
					                          totalOnLeaveMonth -= usedAdditionalDay
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    totalLeaveWithoutPay += totalLeaveWithoutPayMonth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return (
 | 
					                    return (
 | 
				
			||||||
                      <Table.Td
 | 
					                      <Table.Td
 | 
				
			||||||
                        bg={total > 0 ? '#ffb5b5' : ''}
 | 
					                        bg={total > 0 ? '#ffb5b5' : ''}
 | 
				
			||||||
 | 
					                        opacity={d.value > currentMonth ? 0.4 : 1}
 | 
				
			||||||
                        key={i}
 | 
					                        key={i}
 | 
				
			||||||
                        ta={'center'}
 | 
					                        ta={'center'}
 | 
				
			||||||
                      >
 | 
					                      >
 | 
				
			||||||
                        <Tooltip
 | 
					                        <Tooltip
 | 
				
			||||||
                          multiline
 | 
					                          multiline
 | 
				
			||||||
                          label={user.monthlyLeaveDays
 | 
					                          label={
 | 
				
			||||||
                            .filter((item) => item.month === d.value)
 | 
					                            <Box p={4}>
 | 
				
			||||||
                            .map((itemDay, indexDay) => {
 | 
					                              {usedAdditionalDay > 0 && (
 | 
				
			||||||
                              return (
 | 
					                                <Text fw={500} c="violet" size="sm" mb={4}>
 | 
				
			||||||
                                <p key={indexDay}>
 | 
					                                  Phép tồn: {usedAdditionalDay}
 | 
				
			||||||
                                  - {itemDay.reason_name} (
 | 
					                                </Text>
 | 
				
			||||||
                                  {itemDay.time_type_name}) {itemDay.day}/
 | 
					                              )}
 | 
				
			||||||
                                  {itemDay.month}
 | 
					
 | 
				
			||||||
                                </p>
 | 
					                              {totalOnLeaveMonth > 0 && (
 | 
				
			||||||
                              )
 | 
					                                <Box>
 | 
				
			||||||
                            })}
 | 
					                                  <Text fw={500} c="teal" size="sm" mb={4}>
 | 
				
			||||||
 | 
					                                    Có phép: {totalOnLeaveMonth}
 | 
				
			||||||
 | 
					                                  </Text>
 | 
				
			||||||
 | 
					                                </Box>
 | 
				
			||||||
 | 
					                              )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                              {totalOnLeaveMonth > 0 ||
 | 
				
			||||||
 | 
					                              usedAdditionalDay > 0 ? (
 | 
				
			||||||
 | 
					                                <Stack gap={2} pl="md">
 | 
				
			||||||
 | 
					                                  {onleaveDaysInMonth?.map(
 | 
				
			||||||
 | 
					                                    (itemDay: any, indexDay: number) => (
 | 
				
			||||||
 | 
					                                      <Text size="xs" key={indexDay}>
 | 
				
			||||||
 | 
					                                        • {itemDay.time_type_name} (
 | 
				
			||||||
 | 
					                                        {itemDay.day}/{itemDay.month})
 | 
				
			||||||
 | 
					                                      </Text>
 | 
				
			||||||
 | 
					                                    ),
 | 
				
			||||||
 | 
					                                  )}
 | 
				
			||||||
 | 
					                                </Stack>
 | 
				
			||||||
 | 
					                              ) : (
 | 
				
			||||||
 | 
					                                ''
 | 
				
			||||||
 | 
					                              )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                              {totalLeaveWithoutPayMonth > 0 && (
 | 
				
			||||||
 | 
					                                <Box mt={6}>
 | 
				
			||||||
 | 
					                                  <Text fw={500} c="red" size="sm" mb={4}>
 | 
				
			||||||
 | 
					                                    Không phép: {totalLeaveWithoutPayMonth}
 | 
				
			||||||
 | 
					                                  </Text>
 | 
				
			||||||
 | 
					                                  <Stack gap={2} pl="md">
 | 
				
			||||||
 | 
					                                    {nopayDaysInMonth?.map(
 | 
				
			||||||
 | 
					                                      (itemDay: any, indexDay: number) => (
 | 
				
			||||||
 | 
					                                        <Text size="xs" key={indexDay}>
 | 
				
			||||||
 | 
					                                          • {itemDay.time_type_name} (
 | 
				
			||||||
 | 
					                                          {itemDay.day}/{itemDay.month})
 | 
				
			||||||
 | 
					                                        </Text>
 | 
				
			||||||
 | 
					                                      ),
 | 
				
			||||||
 | 
					                                    )}
 | 
				
			||||||
 | 
					                                  </Stack>
 | 
				
			||||||
 | 
					                                </Box>
 | 
				
			||||||
 | 
					                              )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                              {totalTempMonth > 0 && (
 | 
				
			||||||
 | 
					                                <Box mt={6}>
 | 
				
			||||||
 | 
					                                  <Group align="center">
 | 
				
			||||||
 | 
					                                    <Text fw={500} c="blue" size="sm" mb={3}>
 | 
				
			||||||
 | 
					                                      Nghỉ dự kiến: {totalTempMonth}
 | 
				
			||||||
 | 
					                                    </Text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                    <Tooltip
 | 
				
			||||||
 | 
					                                      multiline
 | 
				
			||||||
 | 
					                                      opened
 | 
				
			||||||
 | 
					                                      position="right"
 | 
				
			||||||
 | 
					                                      label={
 | 
				
			||||||
 | 
					                                        <Text size="xs">
 | 
				
			||||||
 | 
					                                          Ngày nghỉ đã được duyệt, sẽ tính phép
 | 
				
			||||||
 | 
					                                          khi đến tháng nghỉ.
 | 
				
			||||||
 | 
					                                        </Text>
 | 
				
			||||||
 | 
					                                      }
 | 
				
			||||||
 | 
					                                      offset={{ mainAxis: 15 }}
 | 
				
			||||||
 | 
					                                    >
 | 
				
			||||||
 | 
					                                      <Text fw={500} c="blue">
 | 
				
			||||||
 | 
					                                        <IconHelpCircle
 | 
				
			||||||
 | 
					                                          width={15}
 | 
				
			||||||
 | 
					                                          height={15}
 | 
				
			||||||
 | 
					                                        />
 | 
				
			||||||
 | 
					                                      </Text>
 | 
				
			||||||
 | 
					                                    </Tooltip>
 | 
				
			||||||
 | 
					                                  </Group>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                  <Stack gap={2} pl="md">
 | 
				
			||||||
 | 
					                                    {tempDaysInMonth?.map(
 | 
				
			||||||
 | 
					                                      (itemDay: any, indexDay: number) => (
 | 
				
			||||||
 | 
					                                        <Text size="xs" key={indexDay}>
 | 
				
			||||||
 | 
					                                          • {itemDay.time_type_name} (
 | 
				
			||||||
 | 
					                                          {itemDay.day}/{itemDay.month})
 | 
				
			||||||
 | 
					                                        </Text>
 | 
				
			||||||
 | 
					                                      ),
 | 
				
			||||||
 | 
					                                    )}
 | 
				
			||||||
 | 
					                                  </Stack>
 | 
				
			||||||
 | 
					                                </Box>
 | 
				
			||||||
 | 
					                              )}
 | 
				
			||||||
 | 
					                            </Box>
 | 
				
			||||||
 | 
					                          }
 | 
				
			||||||
                        >
 | 
					                        >
 | 
				
			||||||
                          <p>{total === 0 ? '' : total}</p>
 | 
					                          <Text size="sm">{total === 0 ? '' : total}</Text>
 | 
				
			||||||
                        </Tooltip>
 | 
					                        </Tooltip>
 | 
				
			||||||
                      </Table.Td>
 | 
					                      </Table.Td>
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                  })}
 | 
					                  })}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                  <Table.Td
 | 
					                  {/* Total */}
 | 
				
			||||||
                    ta={'center'}
 | 
					                  <Table.Td ta={'center'} bg="#92e6f2">
 | 
				
			||||||
                    bg={totalDayLeave > 0 ? '#92e6f2' : ''}
 | 
					                    <Tooltip
 | 
				
			||||||
                  >
 | 
					                      multiline
 | 
				
			||||||
                    {totalDayLeave}
 | 
					                      label={showAllTotal(
 | 
				
			||||||
 | 
					                        ld_day_total,
 | 
				
			||||||
 | 
					                        ld_additional_day,
 | 
				
			||||||
 | 
					                        ld_special_leave_day,
 | 
				
			||||||
 | 
					                      )}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      <Text size="sm">{totalDayLeave}</Text>
 | 
				
			||||||
 | 
					                    </Tooltip>
 | 
				
			||||||
                  </Table.Td>
 | 
					                  </Table.Td>
 | 
				
			||||||
                  <Table.Td ta={'center'} bg={totalDayOff > 0 ? '#ffb5b5' : ''}>
 | 
					
 | 
				
			||||||
 | 
					                  {/* Off */}
 | 
				
			||||||
 | 
					                  <Table.Td>
 | 
				
			||||||
                    {totalDayOff > 0 ? (
 | 
					                    {totalDayOff > 0 ? (
 | 
				
			||||||
                      <Tooltip
 | 
					                      <Tooltip
 | 
				
			||||||
                        multiline
 | 
					                        multiline
 | 
				
			||||||
                        label={showAllOff(user.monthlyLeaveDays)}
 | 
					                        label={showAllOff(user.monthlyLeaveDays)}
 | 
				
			||||||
                      >
 | 
					                      >
 | 
				
			||||||
                        <p> {totalDayOff}</p>
 | 
					                        <Box>
 | 
				
			||||||
 | 
					                          <Flex justify="space-between" mb="xs" align="center">
 | 
				
			||||||
 | 
					                            <Text size="sm">Có phép: </Text>
 | 
				
			||||||
 | 
					                            <Text
 | 
				
			||||||
 | 
					                              size="sm"
 | 
				
			||||||
 | 
					                              bg="#c3ffc3"
 | 
				
			||||||
 | 
					                              fw="bold"
 | 
				
			||||||
 | 
					                              p={5}
 | 
				
			||||||
 | 
					                              style={{ borderRadius: 5 }}
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                              {totalOnLeave}
 | 
				
			||||||
 | 
					                            </Text>
 | 
				
			||||||
 | 
					                          </Flex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                          <Flex justify="space-between" align="center">
 | 
				
			||||||
 | 
					                            <Text size="sm">Không phép: </Text>
 | 
				
			||||||
 | 
					                            <Text
 | 
				
			||||||
 | 
					                              size="sm"
 | 
				
			||||||
 | 
					                              bg="#ffb5b5"
 | 
				
			||||||
 | 
					                              fw="bold"
 | 
				
			||||||
 | 
					                              p={5}
 | 
				
			||||||
 | 
					                              style={{ borderRadius: 5 }}
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                              {totalLeaveWithoutPay}
 | 
				
			||||||
 | 
					                            </Text>
 | 
				
			||||||
 | 
					                          </Flex>
 | 
				
			||||||
 | 
					                        </Box>
 | 
				
			||||||
                      </Tooltip>
 | 
					                      </Tooltip>
 | 
				
			||||||
                    ) : (
 | 
					                    ) : (
 | 
				
			||||||
                      <></>
 | 
					                      ''
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                  </Table.Td>
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  {/* Remaining */}
 | 
				
			||||||
                  <Table.Td
 | 
					                  <Table.Td
 | 
				
			||||||
                    ta={'center'}
 | 
					                    ta={'center'}
 | 
				
			||||||
                    bg={
 | 
					                    bg={totalDayLeave - totalOnLeave > 0 ? '#b5cafb' : ''}
 | 
				
			||||||
                      totalDayLeave - totalDayOff == 0
 | 
					 | 
				
			||||||
                        ? ''
 | 
					 | 
				
			||||||
                        : totalDayLeave - totalDayOff > 0
 | 
					 | 
				
			||||||
                        ? '#c3ffc3'
 | 
					 | 
				
			||||||
                        : '#ffb5b5'
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    {totalDayLeave - totalDayOff}
 | 
					                    <Text size="sm">{totalDayLeave - totalOnLeave}</Text>
 | 
				
			||||||
                  </Table.Td>
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  {/* Note */}
 | 
				
			||||||
                  <Table.Td>
 | 
					                  <Table.Td>
 | 
				
			||||||
                    <Box
 | 
					                    <Box
 | 
				
			||||||
                      style={{
 | 
					                      style={{
 | 
				
			||||||
| 
						 | 
					@ -571,13 +897,18 @@ const LeaveManagement = () => {
 | 
				
			||||||
                          </Text>
 | 
					                          </Text>
 | 
				
			||||||
                        </HoverCard.Target>
 | 
					                        </HoverCard.Target>
 | 
				
			||||||
                        <HoverCard.Dropdown>
 | 
					                        <HoverCard.Dropdown>
 | 
				
			||||||
                          <Textarea size="sm" autosize>
 | 
					                          <Textarea
 | 
				
			||||||
                            {ld_note}
 | 
					                            size="sm"
 | 
				
			||||||
                          </Textarea>
 | 
					                            autosize
 | 
				
			||||||
 | 
					                            value={ld_note}
 | 
				
			||||||
 | 
					                            readOnly
 | 
				
			||||||
 | 
					                          />
 | 
				
			||||||
                        </HoverCard.Dropdown>
 | 
					                        </HoverCard.Dropdown>
 | 
				
			||||||
                      </HoverCard>
 | 
					                      </HoverCard>
 | 
				
			||||||
                    </Box>
 | 
					                    </Box>
 | 
				
			||||||
                  </Table.Td>
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  {/* Action */}
 | 
				
			||||||
                  <Table.Td ta={'center'}>
 | 
					                  <Table.Td ta={'center'}>
 | 
				
			||||||
                    <IconEdit
 | 
					                    <IconEdit
 | 
				
			||||||
                      color="green"
 | 
					                      color="green"
 | 
				
			||||||
| 
						 | 
					@ -585,13 +916,17 @@ const LeaveManagement = () => {
 | 
				
			||||||
                      style={{ cursor: 'pointer' }}
 | 
					                      style={{ cursor: 'pointer' }}
 | 
				
			||||||
                      onClick={() => {
 | 
					                      onClick={() => {
 | 
				
			||||||
                        let totalLeave =
 | 
					                        let totalLeave =
 | 
				
			||||||
                          user.leaveDay.ld_day == 0
 | 
					                          user.leaveDay.ld_day_total == 0
 | 
				
			||||||
                            ? ''
 | 
					                            ? ''
 | 
				
			||||||
                            : String(user.leaveDay.ld_day)
 | 
					                            : String(user.leaveDay.ld_day_total)
 | 
				
			||||||
                        let dayAdditional =
 | 
					                        let dayAdditional =
 | 
				
			||||||
                          user.leaveDay.ld_date_additional == 0
 | 
					                          user.leaveDay.ld_additional_day == 0
 | 
				
			||||||
                            ? ''
 | 
					                            ? ''
 | 
				
			||||||
                            : String(user.leaveDay.ld_date_additional)
 | 
					                            : String(user.leaveDay.ld_additional_day)
 | 
				
			||||||
 | 
					                        let specialLeave =
 | 
				
			||||||
 | 
					                          user.leaveDay.ld_special_leave_day == 0
 | 
				
			||||||
 | 
					                            ? ''
 | 
				
			||||||
 | 
					                            : String(user.leaveDay.ld_special_leave_day)
 | 
				
			||||||
                        open1()
 | 
					                        open1()
 | 
				
			||||||
                        setCustomAddNotes({
 | 
					                        setCustomAddNotes({
 | 
				
			||||||
                          ...customAddNotes,
 | 
					                          ...customAddNotes,
 | 
				
			||||||
| 
						 | 
					@ -599,6 +934,7 @@ const LeaveManagement = () => {
 | 
				
			||||||
                          note: ld_note,
 | 
					                          note: ld_note,
 | 
				
			||||||
                          totalLeave: totalLeave,
 | 
					                          totalLeave: totalLeave,
 | 
				
			||||||
                          dayAdditional: dayAdditional,
 | 
					                          dayAdditional: dayAdditional,
 | 
				
			||||||
 | 
					                          specialLeave: specialLeave,
 | 
				
			||||||
                          user: {
 | 
					                          user: {
 | 
				
			||||||
                            id: user.user.id,
 | 
					                            id: user.user.id,
 | 
				
			||||||
                            name: user.user.name,
 | 
					                            name: user.user.name,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,42 @@
 | 
				
			||||||
export const PageNotFound = () => {
 | 
					import {
 | 
				
			||||||
  return <>{'Not found!'}</>
 | 
					  Box,
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  Container,
 | 
				
			||||||
 | 
					  Image,
 | 
				
			||||||
 | 
					  SimpleGrid,
 | 
				
			||||||
 | 
					  Text,
 | 
				
			||||||
 | 
					  Title,
 | 
				
			||||||
 | 
					} from '@mantine/core'
 | 
				
			||||||
 | 
					import image404 from '../../../public/404Image.jpg'
 | 
				
			||||||
 | 
					import { useNavigate } from 'react-router-dom'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const PageNotFound = () => {
 | 
				
			||||||
 | 
					  const navigate = useNavigate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Container mt="lg">
 | 
				
			||||||
 | 
					      <SimpleGrid spacing={{ base: 40, sm: 80 }} cols={{ base: 1, sm: 2 }}>
 | 
				
			||||||
 | 
					        <Box py="3rem">
 | 
				
			||||||
 | 
					          <Title> Something is not right...</Title>
 | 
				
			||||||
 | 
					          <Text c="dimmed" size="lg">
 | 
				
			||||||
 | 
					            Page you are trying to open does not exist. You may have mistyped
 | 
				
			||||||
 | 
					            the address, or the page has been moved to another URL. If you think
 | 
				
			||||||
 | 
					            this is an error contact support.
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Button
 | 
				
			||||||
 | 
					            variant="outline"
 | 
				
			||||||
 | 
					            size="md"
 | 
				
			||||||
 | 
					            mt="xl"
 | 
				
			||||||
 | 
					            onClick={() => navigate('/')}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Get back to home page
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Image src={image404} />
 | 
				
			||||||
 | 
					      </SimpleGrid>
 | 
				
			||||||
 | 
					    </Container>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default PageNotFound
 | 
					export default PageNotFound
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,8 @@ import {
 | 
				
			||||||
  getTicketsOfUser,
 | 
					  getTicketsOfUser,
 | 
				
			||||||
} from '@/api/Admin'
 | 
					} from '@/api/Admin'
 | 
				
			||||||
import { DataTablePagination } from '@/components/DataTable/DataTable'
 | 
					import { DataTablePagination } from '@/components/DataTable/DataTable'
 | 
				
			||||||
import { Xdelete, create } from '@/rtk/helpers/CRUD'
 | 
					import { Xdelete } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
import { get } from '@/rtk/helpers/apiService'
 | 
					import { get, post } from '@/rtk/helpers/apiService'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Badge,
 | 
					  Badge,
 | 
				
			||||||
  Box,
 | 
					  Box,
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ import { IconTrash } from '@tabler/icons-react'
 | 
				
			||||||
import moment from 'moment'
 | 
					import moment from 'moment'
 | 
				
			||||||
import { useEffect, useState } from 'react'
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
import classes from './Tickets.module.css'
 | 
					import classes from './Tickets.module.css'
 | 
				
			||||||
 | 
					import { _NOTIFICATION_MESS } from '@/rtk/helpers/notificationMess'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TTickets = {
 | 
					type TTickets = {
 | 
				
			||||||
  id: number
 | 
					  id: number
 | 
				
			||||||
| 
						 | 
					@ -78,10 +79,16 @@ const Tickets = () => {
 | 
				
			||||||
  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 [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [isFetchData, setIsFetch] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
 | 
					  const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
 | 
				
			||||||
  const [dataReason, setDataReason] = useState<DataReason[]>([])
 | 
					  const [dataReason, setDataReason] = useState<DataReason[]>([])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [confirmModal, setConfirmModal] = useState(false)
 | 
				
			||||||
 | 
					  const [confirmMessage, setConfirmMessage] = useState('')
 | 
				
			||||||
 | 
					  const [confirmValues, setConfirmValues] = useState<TTickets | null>(null)
 | 
				
			||||||
 | 
					  const [confirmLoading, setConfirmLoading] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getListMasterByType = async (type: string) => {
 | 
					  const getListMasterByType = async (type: string) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const params = {
 | 
					      const params = {
 | 
				
			||||||
| 
						 | 
					@ -103,6 +110,8 @@ const Tickets = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const fetchData = async () => {
 | 
					    const fetchData = async () => {
 | 
				
			||||||
 | 
					      setIsFetch(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resultTimeType = await getListMasterByType('TIME_TYPE')
 | 
					      const resultTimeType = await getListMasterByType('TIME_TYPE')
 | 
				
			||||||
      setDataTimeType(
 | 
					      setDataTimeType(
 | 
				
			||||||
        resultTimeType.filter((item: DataTimeType) => item.c_code !== 'ALL'),
 | 
					        resultTimeType.filter((item: DataTimeType) => item.c_code !== 'ALL'),
 | 
				
			||||||
| 
						 | 
					@ -110,6 +119,8 @@ const Tickets = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const resultReason = await getListMasterByType('REASON')
 | 
					      const resultReason = await getListMasterByType('REASON')
 | 
				
			||||||
      setDataReason(resultReason)
 | 
					      setDataReason(resultReason)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setIsFetch(false)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetchData()
 | 
					    fetchData()
 | 
				
			||||||
| 
						 | 
					@ -271,27 +282,61 @@ const Tickets = () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleCreate = async (values: TTickets) => {
 | 
					  const handleCreate = async (values: TTickets, confirm: boolean = false) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const res = await create(
 | 
					      let params = {
 | 
				
			||||||
        addTicket,
 | 
					        // time_string: moment(values.time_string).format('YYYY-MM-DD HH:mm:ss'),
 | 
				
			||||||
        {
 | 
					        start_date: moment(values.start_date).format('YYYY-MM-DD'),
 | 
				
			||||||
          // time_string: moment(values.time_string).format('YYYY-MM-DD HH:mm:ss'),
 | 
					        start_period: values.start_period,
 | 
				
			||||||
          start_date: moment(values.start_date).format('YYYY-MM-DD'),
 | 
					        end_date: moment(values.end_date).format('YYYY-MM-DD'),
 | 
				
			||||||
          start_period: values.start_period,
 | 
					        end_period: values.end_period,
 | 
				
			||||||
          end_date: moment(values.end_date).format('YYYY-MM-DD'),
 | 
					        type: values.type,
 | 
				
			||||||
          end_period: values.end_period,
 | 
					        reason: values.reason,
 | 
				
			||||||
          type: values.type,
 | 
					        is_accept: confirm,
 | 
				
			||||||
          reason: values.reason,
 | 
					      }
 | 
				
			||||||
        },
 | 
					
 | 
				
			||||||
        getAllTickets,
 | 
					      let res = await post(addTicket, params)
 | 
				
			||||||
      )
 | 
					
 | 
				
			||||||
      if (res === true) {
 | 
					      if (res.status) {
 | 
				
			||||||
 | 
					        notifications.show({
 | 
				
			||||||
 | 
					          title: 'Success',
 | 
				
			||||||
 | 
					          message: _NOTIFICATION_MESS.create_success,
 | 
				
			||||||
 | 
					          color: 'green',
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
        setAction('')
 | 
					        setAction('')
 | 
				
			||||||
        form.reset()
 | 
					        form.reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getAllTickets()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!res.status && res.errors) {
 | 
				
			||||||
 | 
					        if (!res.data?.success && res.data?.message) {
 | 
				
			||||||
 | 
					          //popup notification confirm or cancel
 | 
				
			||||||
 | 
					          setConfirmMessage(res.data?.message)
 | 
				
			||||||
 | 
					          setConfirmValues(values)
 | 
				
			||||||
 | 
					          setConfirmModal(true)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          notifications.show({
 | 
				
			||||||
 | 
					            title: 'Error',
 | 
				
			||||||
 | 
					            message: (
 | 
				
			||||||
 | 
					              <div style={{ whiteSpace: 'pre-line' }}>
 | 
				
			||||||
 | 
					                {res.message ?? _NOTIFICATION_MESS.create_error}
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            color: 'red',
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      console.log(res, 'res')
 | 
				
			||||||
 | 
					      // return res.status
 | 
				
			||||||
 | 
					    } catch (error: any) {
 | 
				
			||||||
 | 
					      if (error.response.status === 422) {
 | 
				
			||||||
 | 
					        const errorMess = error.response.data.message
 | 
				
			||||||
 | 
					        notifications.show({
 | 
				
			||||||
 | 
					          title: 'Error',
 | 
				
			||||||
 | 
					          message: errorMess,
 | 
				
			||||||
 | 
					          color: 'red',
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error) {
 | 
					 | 
				
			||||||
      console.log(error)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -322,15 +367,14 @@ const Tickets = () => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <div className={classes.title}>
 | 
					      <div className={classes.title}>
 | 
				
			||||||
        <h3>
 | 
					        <h3>Tickets</h3>
 | 
				
			||||||
          Tickets
 | 
					 | 
				
			||||||
        </h3>
 | 
					 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          m={5}
 | 
					          m={5}
 | 
				
			||||||
          onClick={() => {
 | 
					          onClick={() => {
 | 
				
			||||||
            setAction('add')
 | 
					            setAction('add')
 | 
				
			||||||
            form.reset()
 | 
					            form.reset()
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
 | 
					          disabled={isFetchData}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          + Add
 | 
					          + Add
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
| 
						 | 
					@ -490,6 +534,57 @@ const Tickets = () => {
 | 
				
			||||||
          </Group>
 | 
					          </Group>
 | 
				
			||||||
        </Text>
 | 
					        </Text>
 | 
				
			||||||
      </Dialog>
 | 
					      </Dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Confirm Modal */}
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        opened={confirmModal}
 | 
				
			||||||
 | 
					        onClose={() => !confirmLoading && setConfirmModal(false)}
 | 
				
			||||||
 | 
					        title={
 | 
				
			||||||
 | 
					          <Text fw={700} fz="lg">
 | 
				
			||||||
 | 
					            Warning
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        centered
 | 
				
			||||||
 | 
					        closeOnClickOutside={!confirmLoading}
 | 
				
			||||||
 | 
					        closeOnEscape={!confirmLoading}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Box p="md">
 | 
				
			||||||
 | 
					          <Text style={{ whiteSpace: 'pre-line' }} mb={20}>
 | 
				
			||||||
 | 
					            {confirmMessage}
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Group justify="center">
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              color="green"
 | 
				
			||||||
 | 
					              loading={confirmLoading}
 | 
				
			||||||
 | 
					              onClick={async () => {
 | 
				
			||||||
 | 
					                if (confirmValues) {
 | 
				
			||||||
 | 
					                  try {
 | 
				
			||||||
 | 
					                    setConfirmLoading(true)
 | 
				
			||||||
 | 
					                    action === 'add' &&
 | 
				
			||||||
 | 
					                      (await handleCreate(confirmValues, true))
 | 
				
			||||||
 | 
					                    setConfirmLoading(false)
 | 
				
			||||||
 | 
					                    setConfirmModal(false)
 | 
				
			||||||
 | 
					                  } catch (error) {
 | 
				
			||||||
 | 
					                    setConfirmLoading(false)
 | 
				
			||||||
 | 
					                    console.error(error)
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Confirm
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              color="red"
 | 
				
			||||||
 | 
					              disabled={confirmLoading}
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setConfirmModal(false)
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Cancel
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </Group>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,4 +45,38 @@
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  .dialogText {
 | 
					  .dialogText {
 | 
				
			||||||
    color: light-dark(#2d353c, white);
 | 
					    color: light-dark(#2d353c, white);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Thêm styles cho Modal xác nhận xóa */
 | 
				
			||||||
 | 
					.deleteModal {
 | 
				
			||||||
 | 
					  background-color: light-dark(white, #2d353c);
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border: solid 1px #ff4646;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalTitle {
 | 
				
			||||||
 | 
					  color: #ff4646;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  font-size: 1.2rem;
 | 
				
			||||||
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalContent {
 | 
				
			||||||
 | 
					  color: light-dark(#2d353c, white);
 | 
				
			||||||
 | 
					  margin-bottom: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalFooter {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: flex-end;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					  margin-top: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton {
 | 
				
			||||||
 | 
					  background-color: #ff4646;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton:hover {
 | 
				
			||||||
 | 
					  background-color: #ff6b6b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,11 @@
 | 
				
			||||||
import { getListMaster, getTickets, handleTicket } from '@/api/Admin'
 | 
					import {
 | 
				
			||||||
 | 
					  getListMaster,
 | 
				
			||||||
 | 
					  getTickets,
 | 
				
			||||||
 | 
					  handleTicket,
 | 
				
			||||||
 | 
					  updateTicket,
 | 
				
			||||||
 | 
					} from '@/api/Admin'
 | 
				
			||||||
import { DataTablePagination } from '@/components/DataTable/DataTable'
 | 
					import { DataTablePagination } from '@/components/DataTable/DataTable'
 | 
				
			||||||
import { create } from '@/rtk/helpers/CRUD'
 | 
					import { create, update } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
import { get } from '@/rtk/helpers/apiService'
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Badge,
 | 
					  Badge,
 | 
				
			||||||
| 
						 | 
					@ -9,12 +14,13 @@ import {
 | 
				
			||||||
  HoverCard,
 | 
					  HoverCard,
 | 
				
			||||||
  Modal,
 | 
					  Modal,
 | 
				
			||||||
  Select,
 | 
					  Select,
 | 
				
			||||||
 | 
					  Switch,
 | 
				
			||||||
  Text,
 | 
					  Text,
 | 
				
			||||||
  Textarea,
 | 
					  Textarea,
 | 
				
			||||||
} from '@mantine/core'
 | 
					} from '@mantine/core'
 | 
				
			||||||
import { useForm } from '@mantine/form'
 | 
					import { useForm } from '@mantine/form'
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import { IconCheckbox, IconSquareXFilled } from '@tabler/icons-react'
 | 
					import { IconCheckbox, IconEdit, IconSquareXFilled } from '@tabler/icons-react'
 | 
				
			||||||
import moment from 'moment'
 | 
					import moment from 'moment'
 | 
				
			||||||
import { useEffect, useState } from 'react'
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
import classes from './TicketsManagement.module.css'
 | 
					import classes from './TicketsManagement.module.css'
 | 
				
			||||||
| 
						 | 
					@ -24,6 +30,7 @@ type TTickets = {
 | 
				
			||||||
  ticket_id: number
 | 
					  ticket_id: number
 | 
				
			||||||
  admin_note: string
 | 
					  admin_note: string
 | 
				
			||||||
  action: string
 | 
					  action: string
 | 
				
			||||||
 | 
					  status: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TListTickets = {
 | 
					type TListTickets = {
 | 
				
			||||||
| 
						 | 
					@ -78,6 +85,7 @@ const TicketsManagement = () => {
 | 
				
			||||||
    end_period: '',
 | 
					    end_period: '',
 | 
				
			||||||
    reason: '',
 | 
					    reason: '',
 | 
				
			||||||
    type: '',
 | 
					    type: '',
 | 
				
			||||||
 | 
					    status: '',
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  const [disableBtn, setDisableBtn] = useState(false)
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
  const [filter, setFilter] = useState({
 | 
					  const [filter, setFilter] = useState({
 | 
				
			||||||
| 
						 | 
					@ -86,6 +94,7 @@ const TicketsManagement = () => {
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
 | 
					  const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
 | 
				
			||||||
  const [dataReason, setDataReason] = useState<DataReason[]>([])
 | 
					  const [dataReason, setDataReason] = useState<DataReason[]>([])
 | 
				
			||||||
 | 
					  const [isRefuseConfirmOpen, setIsRefuseConfirmOpen] = useState<boolean>(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getListMasterByType = async (type: string) => {
 | 
					  const getListMasterByType = async (type: string) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
| 
						 | 
					@ -156,7 +165,7 @@ const TicketsManagement = () => {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'reason',
 | 
					      name: 'reason',
 | 
				
			||||||
      size: '15%',
 | 
					      size: '10%',
 | 
				
			||||||
      header: 'Notes',
 | 
					      header: 'Notes',
 | 
				
			||||||
      render: (row: any) => {
 | 
					      render: (row: any) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
| 
						 | 
					@ -198,7 +207,7 @@ const TicketsManagement = () => {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'admin_note',
 | 
					      name: 'admin_note',
 | 
				
			||||||
      size: '15%',
 | 
					      size: '10%',
 | 
				
			||||||
      header: 'Admin Notes',
 | 
					      header: 'Admin Notes',
 | 
				
			||||||
      render: (row: any) => {
 | 
					      render: (row: any) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
| 
						 | 
					@ -226,6 +235,18 @@ const TicketsManagement = () => {
 | 
				
			||||||
      size: '10%',
 | 
					      size: '10%',
 | 
				
			||||||
      header: 'Updated By',
 | 
					      header: 'Updated By',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'updated_at',
 | 
				
			||||||
 | 
					      size: '10%',
 | 
				
			||||||
 | 
					      header: 'Updated At',
 | 
				
			||||||
 | 
					      render: (row: any) => {
 | 
				
			||||||
 | 
					        if (row?.updated_at) {
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <Box>{moment(row?.updated_at).format('HH:mm:ss DD/MM/YYYY')}</Box>
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: '#',
 | 
					      name: '#',
 | 
				
			||||||
      size: '5%',
 | 
					      size: '5%',
 | 
				
			||||||
| 
						 | 
					@ -254,7 +275,23 @@ const TicketsManagement = () => {
 | 
				
			||||||
              height={20}
 | 
					              height={20}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </Box>
 | 
					          </Box>
 | 
				
			||||||
        ) : null
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <Box className={classes.optionIcon}>
 | 
				
			||||||
 | 
					            <IconEdit
 | 
				
			||||||
 | 
					              className={classes.editIcon}
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setAction('update')
 | 
				
			||||||
 | 
					                setItem(row)
 | 
				
			||||||
 | 
					                form.reset()
 | 
				
			||||||
 | 
					                console.log(row)
 | 
				
			||||||
 | 
					                form.setFieldValue('status', row.status)
 | 
				
			||||||
 | 
					                form.setFieldValue('admin_note', row.admin_note)
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              width={20}
 | 
				
			||||||
 | 
					              height={20}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
| 
						 | 
					@ -347,6 +384,27 @@ const TicketsManagement = () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleUpdate = async (values: TTickets) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await update(
 | 
				
			||||||
 | 
					        updateTicket + `?ticket_id=${item?.id}`,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          ticket_id: item.id,
 | 
				
			||||||
 | 
					          admin_note: values.admin_note,
 | 
				
			||||||
 | 
					          status: values.status,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        getAllTickets,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      if (res === true) {
 | 
				
			||||||
 | 
					        setAction('')
 | 
				
			||||||
 | 
					        setIsRefuseConfirmOpen(false)
 | 
				
			||||||
 | 
					        form.reset()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    getAllTickets()
 | 
					    getAllTickets()
 | 
				
			||||||
  }, [filter])
 | 
					  }, [filter])
 | 
				
			||||||
| 
						 | 
					@ -356,6 +414,7 @@ const TicketsManagement = () => {
 | 
				
			||||||
      ticket_id: 0,
 | 
					      ticket_id: 0,
 | 
				
			||||||
      action: '',
 | 
					      action: '',
 | 
				
			||||||
      admin_note: '',
 | 
					      admin_note: '',
 | 
				
			||||||
 | 
					      status: '',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -416,21 +475,38 @@ const TicketsManagement = () => {
 | 
				
			||||||
      </Box>
 | 
					      </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Modal
 | 
					      <Modal
 | 
				
			||||||
        opened={action === 'confirm' || action === 'refuse'}
 | 
					        opened={
 | 
				
			||||||
 | 
					          action === 'confirm' || action === 'refuse' || action === 'update'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        onClose={() => {
 | 
					        onClose={() => {
 | 
				
			||||||
          setAction('')
 | 
					          setAction('')
 | 
				
			||||||
 | 
					          setIsRefuseConfirmOpen(false)
 | 
				
			||||||
          form.reset()
 | 
					          form.reset()
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
        title={
 | 
					        title={
 | 
				
			||||||
          <Text pl={'sm'} fw={700} fz={'lg'}>
 | 
					          <Text pl={'sm'} fw={700} fz={'lg'}>
 | 
				
			||||||
            {action === 'confirm' ? 'Confirm Ticket' : 'Refuse Ticket'}
 | 
					            {action === 'confirm'
 | 
				
			||||||
 | 
					              ? 'Confirm Ticket'
 | 
				
			||||||
 | 
					              : action === 'refuse'
 | 
				
			||||||
 | 
					              ? 'Refuse Ticket'
 | 
				
			||||||
 | 
					              : 'Update Ticket'}
 | 
				
			||||||
          </Text>
 | 
					          </Text>
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <form
 | 
					        <form
 | 
				
			||||||
          onSubmit={form.onSubmit(async (values) => {
 | 
					          onSubmit={form.onSubmit(async (values) => {
 | 
				
			||||||
            setDisableBtn(true)
 | 
					            setDisableBtn(true)
 | 
				
			||||||
            await handleSave(values)
 | 
					
 | 
				
			||||||
 | 
					            if (action === 'update') {
 | 
				
			||||||
 | 
					              if (values.status === 'REFUSED') {
 | 
				
			||||||
 | 
					                setIsRefuseConfirmOpen(true)
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                await handleUpdate(values)
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              await handleSave(values)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            setDisableBtn(false)
 | 
					            setDisableBtn(false)
 | 
				
			||||||
          })}
 | 
					          })}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
| 
						 | 
					@ -509,14 +585,39 @@ const TicketsManagement = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <Textarea
 | 
					            <Textarea
 | 
				
			||||||
              label="Admin Notes"
 | 
					              label="Admin Notes"
 | 
				
			||||||
              // required
 | 
					 | 
				
			||||||
              value={form.values.admin_note}
 | 
					              value={form.values.admin_note}
 | 
				
			||||||
              onChange={(e) => form.setFieldValue('admin_note', e.target.value)}
 | 
					              onChange={(e) => form.setFieldValue('admin_note', e.target.value)}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {action === 'update' && item.status !== 'REFUSED' ? (
 | 
				
			||||||
 | 
					              <Switch
 | 
				
			||||||
 | 
					                mt="md"
 | 
				
			||||||
 | 
					                color="red"
 | 
				
			||||||
 | 
					                label={
 | 
				
			||||||
 | 
					                  <Text size="sm">
 | 
				
			||||||
 | 
					                    Change status to{' '}
 | 
				
			||||||
 | 
					                    <Text span color="red" fw="bold">
 | 
				
			||||||
 | 
					                      Refuse
 | 
				
			||||||
 | 
					                    </Text>
 | 
				
			||||||
 | 
					                  </Text>
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                style={{ width: 'fit-content' }}
 | 
				
			||||||
 | 
					                checked={form.values.status === 'REFUSED'}
 | 
				
			||||||
 | 
					                onChange={(event) =>
 | 
				
			||||||
 | 
					                  form.setFieldValue(
 | 
				
			||||||
 | 
					                    'status',
 | 
				
			||||||
 | 
					                    event.currentTarget.checked ? 'REFUSED' : 'CONFIRMED',
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              ''
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <Box ta={'center'}>
 | 
					            <Box ta={'center'}>
 | 
				
			||||||
              <Button
 | 
					              <Button
 | 
				
			||||||
                mt={'lg'}
 | 
					                mt={'lg'}
 | 
				
			||||||
                bg={'green'}
 | 
					                bg={action === 'update' ? 'blue' : 'green'}
 | 
				
			||||||
                type="submit"
 | 
					                type="submit"
 | 
				
			||||||
                disabled={disableBtn}
 | 
					                disabled={disableBtn}
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
| 
						 | 
					@ -526,6 +627,50 @@ const TicketsManagement = () => {
 | 
				
			||||||
          </Box>
 | 
					          </Box>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
      </Modal>
 | 
					      </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        opened={isRefuseConfirmOpen}
 | 
				
			||||||
 | 
					        onClose={() => {
 | 
				
			||||||
 | 
					          setIsRefuseConfirmOpen(false)
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        centered
 | 
				
			||||||
 | 
					        size="sm"
 | 
				
			||||||
 | 
					        classNames={{
 | 
				
			||||||
 | 
					          content: classes.deleteModal,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Text className={classes.deleteModalTitle}>Confirm Update</Text>
 | 
				
			||||||
 | 
					        <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					          Changing ticket status to <strong>Refused</strong> will also delete
 | 
				
			||||||
 | 
					          all related notes.
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					        <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					          Are you sure you want to proceed?
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Box className={classes.deleteModalFooter}>
 | 
				
			||||||
 | 
					          <Button
 | 
				
			||||||
 | 
					            variant="outline"
 | 
				
			||||||
 | 
					            onClick={() => {
 | 
				
			||||||
 | 
					              setIsRefuseConfirmOpen(false)
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            disabled={disableBtn}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Cancel
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					          <Button
 | 
				
			||||||
 | 
					            className={classes.deleteButton}
 | 
				
			||||||
 | 
					            onClick={async () => {
 | 
				
			||||||
 | 
					              setDisableBtn(true)
 | 
				
			||||||
 | 
					              await handleUpdate(form.values)
 | 
				
			||||||
 | 
					              setDisableBtn(false)
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            disabled={disableBtn}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Confirm
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,3 +60,37 @@
 | 
				
			||||||
  padding-top: 5px;
 | 
					  padding-top: 5px;
 | 
				
			||||||
  padding-bottom: 5px;
 | 
					  padding-bottom: 5px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Thêm styles cho Modal xác nhận xóa */
 | 
				
			||||||
 | 
					.deleteModal {
 | 
				
			||||||
 | 
					  background-color: light-dark(white, #2d353c);
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border: solid 1px #ff4646;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalTitle {
 | 
				
			||||||
 | 
					  color: #ff4646;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  font-size: 1.2rem;
 | 
				
			||||||
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalContent {
 | 
				
			||||||
 | 
					  color: light-dark(#2d353c, white);
 | 
				
			||||||
 | 
					  margin-bottom: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalFooter {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: flex-end;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					  margin-top: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton {
 | 
				
			||||||
 | 
					  background-color: #ff4646;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton:hover {
 | 
				
			||||||
 | 
					  background-color: #ff6b6b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,6 +147,10 @@ const Timekeeping = () => {
 | 
				
			||||||
  const [exportModalOpened, setExportModalOpened] = useState(false)
 | 
					  const [exportModalOpened, setExportModalOpened] = useState(false)
 | 
				
			||||||
  const [exportOption, setExportOption] = useState('default')
 | 
					  const [exportOption, setExportOption] = useState('default')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false)
 | 
				
			||||||
 | 
					  const [isDisableDeleteBtn, setIsDisableDeleteBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [noteToDelete, setNoteToDelete] = useState<any>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getListMasterByType = async (type: string) => {
 | 
					  const getListMasterByType = async (type: string) => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const params = {
 | 
					      const params = {
 | 
				
			||||||
| 
						 | 
					@ -445,16 +449,16 @@ const Timekeeping = () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const timestamp = moment().format('DDMMYYYY_HHmmss')
 | 
					      const timestamp = moment().format('DDMMYYYY_HHmmss')
 | 
				
			||||||
      const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx`
 | 
					      const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx`
 | 
				
			||||||
      
 | 
					
 | 
				
			||||||
      await exportFile(
 | 
					      await exportFile(
 | 
				
			||||||
        exportTimekeeping,
 | 
					        exportTimekeeping,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          month: date.month,
 | 
					          month: date.month,
 | 
				
			||||||
          year: date.year,
 | 
					          year: date.year,
 | 
				
			||||||
          working_days: workingDays,
 | 
					          working_days: workingDays,
 | 
				
			||||||
          option: option
 | 
					          option: option,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        fileName
 | 
					        fileName,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      setExportModalOpened(false)
 | 
					      setExportModalOpened(false)
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
| 
						 | 
					@ -462,6 +466,64 @@ const Timekeeping = () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleConfirmDelete = async () => {
 | 
				
			||||||
 | 
					    if (noteToDelete) {
 | 
				
			||||||
 | 
					      setIsDisableDeleteBtn(true)
 | 
				
			||||||
 | 
					      await handleDelete(noteToDelete.id)
 | 
				
			||||||
 | 
					      setIsDisableDeleteBtn(false)
 | 
				
			||||||
 | 
					      setIsDeleteConfirmOpen(false)
 | 
				
			||||||
 | 
					      setNoteToDelete(null)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const openDeleteConfirm = (note: any) => {
 | 
				
			||||||
 | 
					    setNoteToDelete(note)
 | 
				
			||||||
 | 
					    setIsDeleteConfirmOpen(true)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const DeleteConfirmModal = () => (
 | 
				
			||||||
 | 
					    <Modal
 | 
				
			||||||
 | 
					      opened={isDeleteConfirmOpen}
 | 
				
			||||||
 | 
					      onClose={() => {
 | 
				
			||||||
 | 
					        setIsDeleteConfirmOpen(false)
 | 
				
			||||||
 | 
					        setNoteToDelete(null)
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      centered
 | 
				
			||||||
 | 
					      size="sm"
 | 
				
			||||||
 | 
					      classNames={{
 | 
				
			||||||
 | 
					        content: classes.deleteModal,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Text className={classes.deleteModalTitle}>Confirm Delete</Text>
 | 
				
			||||||
 | 
					      <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					        This action will change the ticket status to <strong>Refused</strong>{' '}
 | 
				
			||||||
 | 
					        and delete all related notes.
 | 
				
			||||||
 | 
					      </Text>
 | 
				
			||||||
 | 
					      <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					        Are you sure you want to proceed?
 | 
				
			||||||
 | 
					      </Text>
 | 
				
			||||||
 | 
					      <Box className={classes.deleteModalFooter}>
 | 
				
			||||||
 | 
					        <Button
 | 
				
			||||||
 | 
					          variant="outline"
 | 
				
			||||||
 | 
					          onClick={() => {
 | 
				
			||||||
 | 
					            setIsDeleteConfirmOpen(false)
 | 
				
			||||||
 | 
					            setNoteToDelete(null)
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          disabled={isDisableDeleteBtn}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          Cancel
 | 
				
			||||||
 | 
					        </Button>
 | 
				
			||||||
 | 
					        <Button
 | 
				
			||||||
 | 
					          className={classes.deleteButton}
 | 
				
			||||||
 | 
					          onClick={handleConfirmDelete}
 | 
				
			||||||
 | 
					          disabled={isDisableDeleteBtn}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          Delete
 | 
				
			||||||
 | 
					        </Button>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </Modal>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <div className={classes.title}>
 | 
					      <div className={classes.title}>
 | 
				
			||||||
| 
						 | 
					@ -631,11 +693,7 @@ const Timekeeping = () => {
 | 
				
			||||||
              >
 | 
					              >
 | 
				
			||||||
                <IconTrash
 | 
					                <IconTrash
 | 
				
			||||||
                  className={classes.deleteIcon}
 | 
					                  className={classes.deleteIcon}
 | 
				
			||||||
                  onClick={async () => {
 | 
					                  onClick={() => openDeleteConfirm(item)}
 | 
				
			||||||
                    await handleDelete(item.id)
 | 
					 | 
				
			||||||
                    // handleUpdateCacheMonth()
 | 
					 | 
				
			||||||
                    // close2()
 | 
					 | 
				
			||||||
                  }}
 | 
					 | 
				
			||||||
                  width={20}
 | 
					                  width={20}
 | 
				
			||||||
                  height={20}
 | 
					                  height={20}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
| 
						 | 
					@ -655,7 +713,7 @@ const Timekeeping = () => {
 | 
				
			||||||
              data={Array.from({ length: 12 }, (_, index) => {
 | 
					              data={Array.from({ length: 12 }, (_, index) => {
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
                  value: (1 + index).toString(),
 | 
					                  value: (1 + index).toString(),
 | 
				
			||||||
                  label: (1 + index).toString()
 | 
					                  label: (1 + index).toString(),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              })}
 | 
					              })}
 | 
				
			||||||
              onChange={(e) => {
 | 
					              onChange={(e) => {
 | 
				
			||||||
| 
						 | 
					@ -710,9 +768,9 @@ const Timekeeping = () => {
 | 
				
			||||||
                Save
 | 
					                Save
 | 
				
			||||||
              </Button>
 | 
					              </Button>
 | 
				
			||||||
            </Tooltip>
 | 
					            </Tooltip>
 | 
				
			||||||
            <Button 
 | 
					            <Button
 | 
				
			||||||
              onClick={() => setExportModalOpened(true)} 
 | 
					              onClick={() => setExportModalOpened(true)}
 | 
				
			||||||
              size="xs" 
 | 
					              size="xs"
 | 
				
			||||||
              ml="md"
 | 
					              ml="md"
 | 
				
			||||||
              bg={'green'}
 | 
					              bg={'green'}
 | 
				
			||||||
              leftSection={<IconFileExcel size={16} />}
 | 
					              leftSection={<IconFileExcel size={16} />}
 | 
				
			||||||
| 
						 | 
					@ -1074,20 +1132,19 @@ const Timekeeping = () => {
 | 
				
			||||||
          placeholder="Choose an option"
 | 
					          placeholder="Choose an option"
 | 
				
			||||||
          value={exportOption}
 | 
					          value={exportOption}
 | 
				
			||||||
          onChange={(value) => setExportOption(value || 'default')}
 | 
					          onChange={(value) => setExportOption(value || 'default')}
 | 
				
			||||||
          data={[
 | 
					          data={[{ value: 'default', label: 'Default' }]}
 | 
				
			||||||
            { value: 'default', label: 'Default' }
 | 
					 | 
				
			||||||
          ]}
 | 
					 | 
				
			||||||
          mb="md"
 | 
					          mb="md"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <Box style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px' }}>
 | 
					        <Box
 | 
				
			||||||
 | 
					          style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px' }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
          <Button variant="outline" onClick={() => setExportModalOpened(false)}>
 | 
					          <Button variant="outline" onClick={() => setExportModalOpened(false)}>
 | 
				
			||||||
            Close
 | 
					            Close
 | 
				
			||||||
          </Button>
 | 
					          </Button>
 | 
				
			||||||
          <Button onClick={() => handleExport(exportOption)}>
 | 
					          <Button onClick={() => handleExport(exportOption)}>Export</Button>
 | 
				
			||||||
            Export
 | 
					 | 
				
			||||||
          </Button>
 | 
					 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
      </Modal>
 | 
					      </Modal>
 | 
				
			||||||
 | 
					      <DeleteConfirmModal />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,3 +45,37 @@
 | 
				
			||||||
.dialogText {
 | 
					.dialogText {
 | 
				
			||||||
  color: light-dark(#2d353c, white);
 | 
					  color: light-dark(#2d353c, white);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Thêm styles cho Modal xác nhận xóa */
 | 
				
			||||||
 | 
					.deleteModal {
 | 
				
			||||||
 | 
					  background-color: light-dark(white, #2d353c);
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border: solid 1px rgb(9, 132, 132);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalTitle {
 | 
				
			||||||
 | 
					  color: rgb(9, 132, 132);
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  font-size: 1.2rem;
 | 
				
			||||||
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalContent {
 | 
				
			||||||
 | 
					  color: light-dark(#2d353c, white);
 | 
				
			||||||
 | 
					  margin-bottom: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalFooter {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: flex-end;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					  margin-top: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton {
 | 
				
			||||||
 | 
					  background-color: rgb(9, 132, 132);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton:hover {
 | 
				
			||||||
 | 
					  background-color: rgb(9, 132, 132);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ import {
 | 
				
			||||||
  Group,
 | 
					  Group,
 | 
				
			||||||
  Modal,
 | 
					  Modal,
 | 
				
			||||||
  MultiSelect,
 | 
					  MultiSelect,
 | 
				
			||||||
 | 
					  Switch,
 | 
				
			||||||
  Text,
 | 
					  Text,
 | 
				
			||||||
  TextInput,
 | 
					  TextInput,
 | 
				
			||||||
} from '@mantine/core'
 | 
					} from '@mantine/core'
 | 
				
			||||||
| 
						 | 
					@ -24,28 +25,31 @@ const UsersManagement = () => {
 | 
				
			||||||
  const [users, setUsers] = useState<TUser[]>([])
 | 
					  const [users, setUsers] = useState<TUser[]>([])
 | 
				
			||||||
  const [action, setAction] = useState('')
 | 
					  const [action, setAction] = useState('')
 | 
				
			||||||
  const [activeBtn, setActiveBtn] = useState(false)
 | 
					  const [activeBtn, setActiveBtn] = useState(false)
 | 
				
			||||||
  const [item, setItem] = useState({ id: 0 })
 | 
					  const [item, setItem] = useState({ id: 0, is_permanent: false })
 | 
				
			||||||
  const [disableBtn, setDisableBtn] = useState(false)
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
  const [info, setInfo] = useState('')
 | 
					  const [info, setInfo] = useState('')
 | 
				
			||||||
 | 
					  const [isPermanentConfirmOpen, setIsPermanentConfirmOpen] =
 | 
				
			||||||
 | 
					    useState<boolean>(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const columns = [
 | 
					  const columns = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'id',
 | 
					      name: 'id',
 | 
				
			||||||
      size: '3%',
 | 
					      size: '5%',
 | 
				
			||||||
      header: 'ID',
 | 
					      header: 'ID',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'name',
 | 
					      name: 'name',
 | 
				
			||||||
      size: '17%',
 | 
					      size: '20%',
 | 
				
			||||||
      header: 'Name',
 | 
					      header: 'Name',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'email',
 | 
					      name: 'email',
 | 
				
			||||||
      size: '26%',
 | 
					      size: '25%',
 | 
				
			||||||
      header: 'Email',
 | 
					      header: 'Email',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'permission',
 | 
					      name: 'permission',
 | 
				
			||||||
      size: '10%',
 | 
					      size: '20%',
 | 
				
			||||||
      header: 'Permission',
 | 
					      header: 'Permission',
 | 
				
			||||||
      render: (row: TUser) => {
 | 
					      render: (row: TUser) => {
 | 
				
			||||||
        if (row.permission.includes(',')) {
 | 
					        if (row.permission.includes(',')) {
 | 
				
			||||||
| 
						 | 
					@ -57,9 +61,21 @@ const UsersManagement = () => {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'is_permanent',
 | 
				
			||||||
 | 
					      size: '20%',
 | 
				
			||||||
 | 
					      header: 'Employment Type',
 | 
				
			||||||
 | 
					      render: (row: TUser) => {
 | 
				
			||||||
 | 
					        return row.is_permanent ? (
 | 
				
			||||||
 | 
					          <Badge color="teal">Permanent</Badge>
 | 
				
			||||||
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <Badge color="violet">Probation</Badge>
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: '#',
 | 
					      name: '#',
 | 
				
			||||||
      size: '5%',
 | 
					      size: '10%',
 | 
				
			||||||
      header: 'Action',
 | 
					      header: 'Action',
 | 
				
			||||||
      render: (row: TUser) => {
 | 
					      render: (row: TUser) => {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
| 
						 | 
					@ -68,6 +84,8 @@ const UsersManagement = () => {
 | 
				
			||||||
              className={classes.editIcon}
 | 
					              className={classes.editIcon}
 | 
				
			||||||
              onClick={() => {
 | 
					              onClick={() => {
 | 
				
			||||||
                setAction('edit')
 | 
					                setAction('edit')
 | 
				
			||||||
 | 
					                setItem(row)
 | 
				
			||||||
 | 
					                form.reset()
 | 
				
			||||||
                form.setValues(row)
 | 
					                form.setValues(row)
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
              width={20}
 | 
					              width={20}
 | 
				
			||||||
| 
						 | 
					@ -94,6 +112,7 @@ const UsersManagement = () => {
 | 
				
			||||||
      name: '',
 | 
					      name: '',
 | 
				
			||||||
      email: '',
 | 
					      email: '',
 | 
				
			||||||
      permission: '',
 | 
					      permission: '',
 | 
				
			||||||
 | 
					      is_permanent: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,6 +147,7 @@ const UsersManagement = () => {
 | 
				
			||||||
      const res = await update(createOrUpdateUser, values, getAll)
 | 
					      const res = await update(createOrUpdateUser, values, getAll)
 | 
				
			||||||
      if (res === true) {
 | 
					      if (res === true) {
 | 
				
			||||||
        setAction('')
 | 
					        setAction('')
 | 
				
			||||||
 | 
					        setIsPermanentConfirmOpen(false)
 | 
				
			||||||
        form.reset()
 | 
					        form.reset()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
| 
						 | 
					@ -172,6 +192,7 @@ const UsersManagement = () => {
 | 
				
			||||||
        opened={action === 'add' || action === 'edit'}
 | 
					        opened={action === 'add' || action === 'edit'}
 | 
				
			||||||
        onClose={() => {
 | 
					        onClose={() => {
 | 
				
			||||||
          setAction('')
 | 
					          setAction('')
 | 
				
			||||||
 | 
					          setIsPermanentConfirmOpen(false)
 | 
				
			||||||
          form.reset()
 | 
					          form.reset()
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
        title={
 | 
					        title={
 | 
				
			||||||
| 
						 | 
					@ -183,9 +204,15 @@ const UsersManagement = () => {
 | 
				
			||||||
        <form
 | 
					        <form
 | 
				
			||||||
          onSubmit={form.onSubmit(async (values) => {
 | 
					          onSubmit={form.onSubmit(async (values) => {
 | 
				
			||||||
            setDisableBtn(true)
 | 
					            setDisableBtn(true)
 | 
				
			||||||
            action === 'edit'
 | 
					            if (action === 'edit') {
 | 
				
			||||||
              ? await handleUpdate(values)
 | 
					              if (values.is_permanent && !item.is_permanent) {
 | 
				
			||||||
              : await handleCreate(values)
 | 
					                setIsPermanentConfirmOpen(true)
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                await handleUpdate(values)
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              await handleCreate(values)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            setDisableBtn(false)
 | 
					            setDisableBtn(false)
 | 
				
			||||||
          })}
 | 
					          })}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
| 
						 | 
					@ -196,6 +223,7 @@ const UsersManagement = () => {
 | 
				
			||||||
              value={form.values.name}
 | 
					              value={form.values.name}
 | 
				
			||||||
              error={form.errors.name}
 | 
					              error={form.errors.name}
 | 
				
			||||||
              onChange={(e) => form.setFieldValue('name', e.target.value)}
 | 
					              onChange={(e) => form.setFieldValue('name', e.target.value)}
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextInput
 | 
					            <TextInput
 | 
				
			||||||
| 
						 | 
					@ -204,6 +232,7 @@ const UsersManagement = () => {
 | 
				
			||||||
              value={form.values.email}
 | 
					              value={form.values.email}
 | 
				
			||||||
              error={form.errors.email}
 | 
					              error={form.errors.email}
 | 
				
			||||||
              onChange={(e) => form.setFieldValue('email', e.target.value)}
 | 
					              onChange={(e) => form.setFieldValue('email', e.target.value)}
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <MultiSelect
 | 
					            <MultiSelect
 | 
				
			||||||
| 
						 | 
					@ -224,7 +253,25 @@ const UsersManagement = () => {
 | 
				
			||||||
                  e!.filter((p) => p.trim() !== '').join(','),
 | 
					                  e!.filter((p) => p.trim() !== '').join(','),
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					              mb={'md'}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {action === 'edit' && !item.is_permanent ? (
 | 
				
			||||||
 | 
					              <Switch
 | 
				
			||||||
 | 
					                label="Permanent employee"
 | 
				
			||||||
 | 
					                style={{ width: 'fit-content' }}
 | 
				
			||||||
 | 
					                checked={form.values.is_permanent}
 | 
				
			||||||
 | 
					                onChange={(event) =>
 | 
				
			||||||
 | 
					                  form.setFieldValue(
 | 
				
			||||||
 | 
					                    'is_permanent',
 | 
				
			||||||
 | 
					                    event.currentTarget.checked,
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              ''
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <Box ta={'center'}>
 | 
					            <Box ta={'center'}>
 | 
				
			||||||
              {action === 'add' ? (
 | 
					              {action === 'add' ? (
 | 
				
			||||||
                <Button
 | 
					                <Button
 | 
				
			||||||
| 
						 | 
					@ -273,6 +320,50 @@ const UsersManagement = () => {
 | 
				
			||||||
          {info}
 | 
					          {info}
 | 
				
			||||||
        </Code>
 | 
					        </Code>
 | 
				
			||||||
      </Modal>
 | 
					      </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Confirm change to permanent employee */}
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        opened={isPermanentConfirmOpen}
 | 
				
			||||||
 | 
					        onClose={() => setIsPermanentConfirmOpen(false)}
 | 
				
			||||||
 | 
					        centered
 | 
				
			||||||
 | 
					        size="sm"
 | 
				
			||||||
 | 
					        classNames={{
 | 
				
			||||||
 | 
					          content: classes.deleteModal,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Text className={classes.deleteModalTitle}>Confirm Update</Text>
 | 
				
			||||||
 | 
					        <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					          This action will change the employment type from{' '}
 | 
				
			||||||
 | 
					          <strong>Probation</strong> to <strong>Permanent</strong>.
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					        <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					          Are you sure you want to proceed?
 | 
				
			||||||
 | 
					        </Text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Box className={classes.deleteModalFooter}>
 | 
				
			||||||
 | 
					          <Button
 | 
				
			||||||
 | 
					            variant="outline"
 | 
				
			||||||
 | 
					            onClick={() => {
 | 
				
			||||||
 | 
					              setIsPermanentConfirmOpen(false)
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            disabled={disableBtn}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Cancel
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					          <Button
 | 
				
			||||||
 | 
					            className={classes.deleteButton}
 | 
				
			||||||
 | 
					            onClick={async () => {
 | 
				
			||||||
 | 
					              setDisableBtn(true)
 | 
				
			||||||
 | 
					              await handleUpdate(form.values)
 | 
				
			||||||
 | 
					              setDisableBtn(false)
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            disabled={disableBtn}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Confirm
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Dialog
 | 
					      <Dialog
 | 
				
			||||||
        className={classes.dialog}
 | 
					        className={classes.dialog}
 | 
				
			||||||
        opened={action === 'delete'}
 | 
					        opened={action === 'delete'}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ export const create = async (
 | 
				
			||||||
    if (res.status === false) {
 | 
					    if (res.status === false) {
 | 
				
			||||||
      notifications.show({
 | 
					      notifications.show({
 | 
				
			||||||
        title: 'Error',
 | 
					        title: 'Error',
 | 
				
			||||||
        message: res.message ?? _NOTIFICATION_MESS.create_error,
 | 
					        message: <div style={{ whiteSpace: 'pre-line' }}>{res.message ?? _NOTIFICATION_MESS.create_error}</div>,
 | 
				
			||||||
        color: 'red',
 | 
					        color: 'red',
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,6 +76,7 @@ export type TUser = {
 | 
				
			||||||
  email: string
 | 
					  email: string
 | 
				
			||||||
  name: string
 | 
					  name: string
 | 
				
			||||||
  permission: string
 | 
					  permission: string
 | 
				
			||||||
 | 
					  is_permanent: boolean
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type DataReason = {
 | 
					export type DataReason = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue