dev #130
			
				
			
		
		
		
	
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -17,6 +17,7 @@ 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
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -177,78 +178,92 @@ class TimekeepingController extends Controller
 | 
				
			||||||
        $year = $request->year;
 | 
					        $year = $request->year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $note = Notes::find($id);
 | 
					        $note = Notes::find($id);
 | 
				
			||||||
        if ($note) {
 | 
					        if (!$note) {
 | 
				
			||||||
            $n_month = $note->n_month;
 | 
					            return response()->json(['message' => 'Note not found', 'status' => false]);
 | 
				
			||||||
            $n_year = $note->n_year;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if ($note->n_reason == "ONLEAVE") {
 | 
					 | 
				
			||||||
                // Get note reason ONLEAVE by $n_month, $n_year not include $note->id & include $note->n_user_id
 | 
					 | 
				
			||||||
                // $onleave = Notes::getNotesByMonthAndYearAndUserId($n_month, $n_year, $note->n_user_id, $note->id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Get note reason LEAVE_WITHOUT_PAY by $n_month, $n_year & include $note->n_user_id
 | 
					 | 
				
			||||||
                $leaveWithoutPay = Notes::getNotesByMonthAndYearAndUserIdAndReason($n_month, $n_year, $note->n_user_id, 'LEAVE_WITHOUT_PAY');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (count($leaveWithoutPay) > 0) {
 | 
					 | 
				
			||||||
                    $deletedValue = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
 | 
					 | 
				
			||||||
                    $needUpdate = $deletedValue;
 | 
					 | 
				
			||||||
                    // dd($needUpdate, $leaveWithoutPay);
 | 
					 | 
				
			||||||
                    foreach ($leaveWithoutPay as $lwNote) {
 | 
					 | 
				
			||||||
                        if ($needUpdate <= 0) break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if ($lwNote->n_time_type === 'ALL') {
 | 
					 | 
				
			||||||
                            if ($needUpdate == 1.0) {
 | 
					 | 
				
			||||||
                                // Chuyển cả note ALL thành phép
 | 
					 | 
				
			||||||
                                $lwNote->update(['n_reason' => 'ONLEAVE']);
 | 
					 | 
				
			||||||
                                $needUpdate = 0;
 | 
					 | 
				
			||||||
                                break;
 | 
					 | 
				
			||||||
                            } else { // $needUpdate == 0.5
 | 
					 | 
				
			||||||
                                // Tách ALL thành 2 note S và C, chuyển S thành phép, C giữ không phép
 | 
					 | 
				
			||||||
                                Notes::create([
 | 
					 | 
				
			||||||
                                    'n_user_id' => $lwNote->n_user_id,
 | 
					 | 
				
			||||||
                                    'n_day' => $lwNote->n_day,
 | 
					 | 
				
			||||||
                                    'n_month' => $lwNote->n_month,
 | 
					 | 
				
			||||||
                                    'n_year' => $lwNote->n_year,
 | 
					 | 
				
			||||||
                                    'n_time_type' => 'S',
 | 
					 | 
				
			||||||
                                    'n_reason' => 'ONLEAVE',
 | 
					 | 
				
			||||||
                                    'n_note' => $lwNote->n_note
 | 
					 | 
				
			||||||
                                ]);
 | 
					 | 
				
			||||||
                                Notes::create([
 | 
					 | 
				
			||||||
                                    'n_user_id' => $lwNote->n_user_id,
 | 
					 | 
				
			||||||
                                    'n_day' => $lwNote->n_day,
 | 
					 | 
				
			||||||
                                    'n_month' => $lwNote->n_month,
 | 
					 | 
				
			||||||
                                    'n_year' => $lwNote->n_year,
 | 
					 | 
				
			||||||
                                    'n_time_type' => 'C',
 | 
					 | 
				
			||||||
                                    'n_reason' => 'LEAVE_WITHOUT_PAY',
 | 
					 | 
				
			||||||
                                    'n_note' => $lwNote->n_note
 | 
					 | 
				
			||||||
                                ]);
 | 
					 | 
				
			||||||
                                $lwNote->delete();
 | 
					 | 
				
			||||||
                                $needUpdate = 0;
 | 
					 | 
				
			||||||
                                break;
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            // Nếu $lwNote->n_time_type == 'S' hoặc 'C' => 0.5
 | 
					 | 
				
			||||||
                            if ($needUpdate == 1.0) {
 | 
					 | 
				
			||||||
                                // Chuyển cả note ALL thành phép
 | 
					 | 
				
			||||||
                                $lwNote->update(['n_reason' => 'ONLEAVE']);
 | 
					 | 
				
			||||||
                                $needUpdate -= 0.5;
 | 
					 | 
				
			||||||
                            } else { // $needUpdate == 0.5
 | 
					 | 
				
			||||||
                                // S hoặc C, chỉ cần chuyển đúng 0.5 ngày
 | 
					 | 
				
			||||||
                                $lwNote->update(['n_reason' => 'ONLEAVE']);
 | 
					 | 
				
			||||||
                                $needUpdate = 0;
 | 
					 | 
				
			||||||
                                break;
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    // Khi note phép và k tồn tại nghỉ không phép => phép + dồn cho tháng sau
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $note->delete();
 | 
					 | 
				
			||||||
            $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();
 | 
				
			||||||
 | 
					        $ticket->updated_by = $admin->name;
 | 
				
			||||||
 | 
					        $ticket->status = "REFUSED";
 | 
				
			||||||
 | 
					        $ticket->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Notes::where('ticket_id', $ticket->id)->delete();
 | 
				
			||||||
 | 
					        $this->createOrUpdateRecordForCurrentMonth($month, $year);
 | 
				
			||||||
 | 
					        return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // $n_month = $note->n_month;
 | 
				
			||||||
 | 
					        // $n_year = $note->n_year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if ($note->n_reason == "ONLEAVE") {
 | 
				
			||||||
 | 
					        //     // Get note reason ONLEAVE by $n_month, $n_year not include $note->id & include $note->n_user_id
 | 
				
			||||||
 | 
					        //     // $onleave = Notes::getNotesByMonthAndYearAndUserId($n_month, $n_year, $note->n_user_id, $note->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //     // Get note reason LEAVE_WITHOUT_PAY by $n_month, $n_year & include $note->n_user_id
 | 
				
			||||||
 | 
					        //     $leaveWithoutPay = Notes::getNotesByMonthAndYearAndUserIdAndReason($n_month, $n_year, $note->n_user_id, 'LEAVE_WITHOUT_PAY');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //     if (count($leaveWithoutPay) > 0) {
 | 
				
			||||||
 | 
					        //         $deletedValue = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
 | 
				
			||||||
 | 
					        //         $needUpdate = $deletedValue;
 | 
				
			||||||
 | 
					        //         // dd($needUpdate, $leaveWithoutPay);
 | 
				
			||||||
 | 
					        //         foreach ($leaveWithoutPay as $lwNote) {
 | 
				
			||||||
 | 
					        //             if ($needUpdate <= 0) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //             if ($lwNote->n_time_type === 'ALL') {
 | 
				
			||||||
 | 
					        //                 if ($needUpdate == 1.0) {
 | 
				
			||||||
 | 
					        //                     // Chuyển cả note ALL thành phép
 | 
				
			||||||
 | 
					        //                     $lwNote->update(['n_reason' => 'ONLEAVE']);
 | 
				
			||||||
 | 
					        //                     $needUpdate = 0;
 | 
				
			||||||
 | 
					        //                     break;
 | 
				
			||||||
 | 
					        //                 } else { // $needUpdate == 0.5
 | 
				
			||||||
 | 
					        //                     // Tách ALL thành 2 note S và C, chuyển S thành phép, C giữ không phép
 | 
				
			||||||
 | 
					        //                     Notes::create([
 | 
				
			||||||
 | 
					        //                         'n_user_id' => $lwNote->n_user_id,
 | 
				
			||||||
 | 
					        //                         'n_day' => $lwNote->n_day,
 | 
				
			||||||
 | 
					        //                         'n_month' => $lwNote->n_month,
 | 
				
			||||||
 | 
					        //                         'n_year' => $lwNote->n_year,
 | 
				
			||||||
 | 
					        //                         'n_time_type' => 'S',
 | 
				
			||||||
 | 
					        //                         'n_reason' => 'ONLEAVE',
 | 
				
			||||||
 | 
					        //                         'n_note' => $lwNote->n_note
 | 
				
			||||||
 | 
					        //                     ]);
 | 
				
			||||||
 | 
					        //                     Notes::create([
 | 
				
			||||||
 | 
					        //                         'n_user_id' => $lwNote->n_user_id,
 | 
				
			||||||
 | 
					        //                         'n_day' => $lwNote->n_day,
 | 
				
			||||||
 | 
					        //                         'n_month' => $lwNote->n_month,
 | 
				
			||||||
 | 
					        //                         'n_year' => $lwNote->n_year,
 | 
				
			||||||
 | 
					        //                         'n_time_type' => 'C',
 | 
				
			||||||
 | 
					        //                         'n_reason' => 'LEAVE_WITHOUT_PAY',
 | 
				
			||||||
 | 
					        //                         'n_note' => $lwNote->n_note
 | 
				
			||||||
 | 
					        //                     ]);
 | 
				
			||||||
 | 
					        //                     $lwNote->delete();
 | 
				
			||||||
 | 
					        //                     $needUpdate = 0;
 | 
				
			||||||
 | 
					        //                     break;
 | 
				
			||||||
 | 
					        //                 }
 | 
				
			||||||
 | 
					        //             } else {
 | 
				
			||||||
 | 
					        //                 // Nếu $lwNote->n_time_type == 'S' hoặc 'C' => 0.5
 | 
				
			||||||
 | 
					        //                 if ($needUpdate == 1.0) {
 | 
				
			||||||
 | 
					        //                     // Chuyển cả note ALL thành phép
 | 
				
			||||||
 | 
					        //                     $lwNote->update(['n_reason' => 'ONLEAVE']);
 | 
				
			||||||
 | 
					        //                     $needUpdate -= 0.5;
 | 
				
			||||||
 | 
					        //                 } else { // $needUpdate == 0.5
 | 
				
			||||||
 | 
					        //                     // S hoặc C, chỉ cần chuyển đúng 0.5 ngày
 | 
				
			||||||
 | 
					        //                     $lwNote->update(['n_reason' => 'ONLEAVE']);
 | 
				
			||||||
 | 
					        //                     $needUpdate = 0;
 | 
				
			||||||
 | 
					        //                     break;
 | 
				
			||||||
 | 
					        //                 }
 | 
				
			||||||
 | 
					        //             }
 | 
				
			||||||
 | 
					        //         }
 | 
				
			||||||
 | 
					        //     } else {
 | 
				
			||||||
 | 
					        //         // Khi note phép và k tồn tại nghỉ không phép => phép + dồn cho tháng sau
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        // $note->delete();
 | 
				
			||||||
 | 
					        // $this->createOrUpdateRecordForCurrentMonth($month, $year);
 | 
				
			||||||
 | 
					        // return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function export(Request $request)
 | 
					    public function export(Request $request)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,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,36 @@ 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,7 +73,19 @@ 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(),
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $user_res = [
 | 
					            $user_res = [
 | 
				
			||||||
| 
						 | 
					@ -98,8 +139,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'
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
 | 
				
			||||||
        // ->dailyAt('18:00');
 | 
					        // ->dailyAt('18:00');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Chạy command vào ngày 31/12 lúc 23:59:59 mỗi năm
 | 
					        // Chạy command vào ngày 31/12 lúc 23:59:59 mỗi năm
 | 
				
			||||||
        // $schedule->command('initialize:leavedays')->yearlyOn(12, 31, '23:59:59');
 | 
					        $schedule->command('initialize:leavedays')->yearlyOn(12, 31, '23:59:59');
 | 
				
			||||||
        $schedule->command('leave:deduct')->yearlyOn(3, 31, '23:59:59');
 | 
					        $schedule->command('leave:deduct')->yearlyOn(3, 31, '23:59:59');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Chạy buổi sáng lúc 12:00
 | 
					        // Chạy buổi sáng lúc 12:00
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ 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 Modules\Admin\app\Models\Category;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddMonthlyLeaveDays implements ShouldQueue
 | 
					class AddMonthlyLeaveDays implements ShouldQueue
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -18,6 +19,8 @@ class AddMonthlyLeaveDays implements ShouldQueue
 | 
				
			||||||
  protected $month;
 | 
					  protected $month;
 | 
				
			||||||
  protected $year;
 | 
					  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)
 | 
					  public function __construct($month = null, $year = null)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    $this->month = $month ?? Carbon::now()->month;
 | 
					    $this->month = $month ?? Carbon::now()->month;
 | 
				
			||||||
| 
						 | 
					@ -29,6 +32,11 @@ class AddMonthlyLeaveDays implements ShouldQueue
 | 
				
			||||||
    $users = User::get();
 | 
					    $users = User::get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    foreach ($users as $user) {
 | 
					    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)
 | 
					      $leaveDay = LeaveDays::where('ld_user_id', $user->id)
 | 
				
			||||||
        ->where('ld_year', $this->year)
 | 
					        ->where('ld_year', $this->year)
 | 
				
			||||||
        ->first();
 | 
					        ->first();
 | 
				
			||||||
| 
						 | 
					@ -46,11 +54,36 @@ class AddMonthlyLeaveDays implements ShouldQueue
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        $leaveDay->save();
 | 
					        $leaveDay->save();
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // Kiểm tra nếu số ngày phép hiện tại nhỏ hơn tháng hiện tại
 | 
					        // 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Xử lý ghi chú
 | 
				
			||||||
 | 
					              $newNote = "Cập nhật ngày phép đến tháng " . $this->month;
 | 
				
			||||||
 | 
					              if (!empty($leaveDay->ld_note)) {
 | 
				
			||||||
 | 
					                // Nếu đã có ghi chú, thêm ghi chú mới vào và xuống dòng
 | 
				
			||||||
 | 
					                $leaveDay->ld_note = $leaveDay->ld_note . "\n" . $newNote;
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                // Nếu chưa có ghi chú, gán ghi chú mới
 | 
				
			||||||
 | 
					                $leaveDay->ld_note = $newNote;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              $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) {
 | 
					        if ($leaveDay->ld_day_total < $this->month) {
 | 
				
			||||||
          // Cập nhật số ngày phép bằng với tháng hiện tại
 | 
					          // Cộng mỗi tháng 1 ngày phép cho nhân viên
 | 
				
			||||||
          $oldDays = $leaveDay->ld_day_total;
 | 
					          $leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
 | 
				
			||||||
          $leaveDay->ld_day_total = $this->month;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // Xử lý ghi chú
 | 
					          // Xử lý ghi chú
 | 
				
			||||||
          $newNote = "Cập nhật ngày phép đến tháng " . $this->month;
 | 
					          $newNote = "Cập nhật ngày phép đến tháng " . $this->month;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -42,38 +41,19 @@ class DeductLeaveDays implements ShouldQueue
 | 
				
			||||||
                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)
 | 
					 | 
				
			||||||
                ->where('notes.n_reason', 'ONLEAVE')
 | 
					 | 
				
			||||||
                ->groupBy(DB::raw('notes.n_year'))
 | 
					 | 
				
			||||||
                ->first();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($totalLeaveDaysByMonth) {
 | 
					            $existingData->ld_additional_day = $usedOnleaveDaysTotal ?? 0;
 | 
				
			||||||
                //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)
 | 
					            $existingData->save();
 | 
				
			||||||
                if ($existingData->ld_additional_day > $totalLeaveDaysByMonth->leave_days) {
 | 
					 | 
				
			||||||
                    LeaveDays::where('ld_year', $this->year)
 | 
					 | 
				
			||||||
                        ->where('ld_user_id', $user->id)
 | 
					 | 
				
			||||||
                        ->update([
 | 
					 | 
				
			||||||
                            'ld_additional_day' => $totalLeaveDaysByMonth->leave_days,
 | 
					 | 
				
			||||||
                        ]);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } 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
 | 
					 | 
				
			||||||
                LeaveDays::where('ld_year', $this->year)
 | 
					 | 
				
			||||||
                    ->where('ld_user_id', $user->id)
 | 
					 | 
				
			||||||
                    ->update([
 | 
					 | 
				
			||||||
                        'ld_additional_day' => "0",
 | 
					 | 
				
			||||||
                    ]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ class InitializeLeaveDays implements ShouldQueue
 | 
				
			||||||
    public function handle(): void
 | 
					    public function handle(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $users = User::get();
 | 
					        $users = User::get();
 | 
				
			||||||
        $ld_day_total = 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)
 | 
				
			||||||
| 
						 | 
					@ -82,7 +83,7 @@ class InitializeLeaveDays implements ShouldQueue
 | 
				
			||||||
            // 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_total' => $ld_day_total,
 | 
					                '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_additional_day' => $ld_additional_day,
 | 
					                'ld_additional_day' => $ld_additional_day,
 | 
				
			||||||
                'ld_note' => $ld_note,
 | 
					                'ld_note' => $ld_note,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ class Notes extends Model
 | 
				
			||||||
        'n_time_type',
 | 
					        'n_time_type',
 | 
				
			||||||
        'n_reason',
 | 
					        'n_reason',
 | 
				
			||||||
        'n_note',
 | 
					        'n_note',
 | 
				
			||||||
 | 
					        'ticket_id'
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,35 @@
 | 
				
			||||||
 | 
					<?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' => 3,
 | 
				
			||||||
 | 
					                'c_active' => 1,
 | 
				
			||||||
 | 
					                'created_at' => now(),
 | 
				
			||||||
 | 
					                'updated_at' => now(),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        DB::table('categories')->where('c_code', 'PERMANENT')->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,199 +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>
 | 
					 | 
				
			||||||
                            </td>
 | 
					 | 
				
			||||||
                        </tr>
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                        <tr>
 | 
					 | 
				
			||||||
                            <td>
 | 
					 | 
				
			||||||
                                <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;">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;">Note: <span style="color: #222222;font-weight: bold;">{{ $data['note'] }}</span></p>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                            </td>
 | 
					 | 
				
			||||||
                        </tr>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        <tr>
 | 
					 | 
				
			||||||
                            <td>
 | 
					 | 
				
			||||||
                            <p
 | 
					 | 
				
			||||||
                                style="
 | 
					 | 
				
			||||||
                                    margin: 0 0 16px;
 | 
					 | 
				
			||||||
                                    padding: 5px;
 | 
					 | 
				
			||||||
                                    margin: 5px;
 | 
					 | 
				
			||||||
                                    text-align: center;
 | 
					 | 
				
			||||||
                                "
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                <a
 | 
					 | 
				
			||||||
                                    href="{{ config('app.client_url') . $data['link'] }}"
 | 
					 | 
				
			||||||
                                    style="
 | 
					 | 
				
			||||||
                                        color: #fff;
 | 
					 | 
				
			||||||
                                        border-radius: 10px;
 | 
					 | 
				
			||||||
                                        background-color: rgba(68, 115, 196);
 | 
					 | 
				
			||||||
                                        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;
 | 
					 | 
				
			||||||
                                    "
 | 
					 | 
				
			||||||
                                >
 | 
					 | 
				
			||||||
                                    Check now</a
 | 
					 | 
				
			||||||
                                >
 | 
					 | 
				
			||||||
                            </p>
 | 
					                            </p>
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <tr>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            <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;">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;">Note: <span style="color: #222222;font-weight: bold;">{{ $data['note'] }}</span></p>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <tr>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
                            <p style="text-align: center">
 | 
					                            <p style="text-align: center">
 | 
				
			||||||
                                Or you can quick
 | 
					                                You can quick
 | 
				
			||||||
                                <span style="font-weight: bold">Confirm</span> or
 | 
					                                <span style="font-weight: bold">Confirm</span> or
 | 
				
			||||||
                                <span style="font-weight: bold">Refuse</span> here:
 | 
					                                <span style="font-weight: bold">Refuse</span> here:
 | 
				
			||||||
                            </p>
 | 
					                            </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <div
 | 
					                            <div
 | 
				
			||||||
                                style="
 | 
					                                style="
 | 
				
			||||||
                                    display: flex;
 | 
					                                        display: flex;
 | 
				
			||||||
                                    justify-content: center;
 | 
					                                        justify-content: center;
 | 
				
			||||||
                                    gap: 10px;
 | 
					                                        gap: 10px;
 | 
				
			||||||
                                    margin-top: 10px;
 | 
					                                        margin-top: 10px;
 | 
				
			||||||
                                "
 | 
					                                    ">
 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                <a
 | 
					                                <a
 | 
				
			||||||
                                    href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'confirm', 'admin_email' => $data['admin_email']]) }}"
 | 
					                                    href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'confirm', 'admin_email' => $data['admin_email']]) }}"
 | 
				
			||||||
                                    style="
 | 
					                                    style="
 | 
				
			||||||
                                        color: #fff;
 | 
					                                            color: #fff;
 | 
				
			||||||
                                        border-radius: 10px;
 | 
					                                            border-radius: 10px;
 | 
				
			||||||
                                        background-color: #12b886;
 | 
					                                            background-color: #12b886;
 | 
				
			||||||
                                        background-image: linear-gradient(
 | 
					                                            background-image: linear-gradient(
 | 
				
			||||||
                                            to top left,
 | 
					                                                to top left,
 | 
				
			||||||
                                            rgba(0, 0, 0, 0.2),
 | 
					                                                rgba(0, 0, 0, 0.2),
 | 
				
			||||||
                                            rgba(0, 0, 0, 0.2) 30%,
 | 
					                                                rgba(0, 0, 0, 0.2) 30%,
 | 
				
			||||||
                                            rgba(0, 0, 0, 0)
 | 
					                                                rgba(0, 0, 0, 0)
 | 
				
			||||||
                                        );
 | 
					                                            );
 | 
				
			||||||
                                        text-decoration: none;
 | 
					                                            text-decoration: none;
 | 
				
			||||||
                                        display: inline-block;
 | 
					                                            display: inline-block;
 | 
				
			||||||
                                        font-weight: 600;
 | 
					                                            font-weight: 600;
 | 
				
			||||||
                                        font-size: 16px;
 | 
					                                            font-size: 16px;
 | 
				
			||||||
                                        line-height: 150%;
 | 
					                                            line-height: 150%;
 | 
				
			||||||
                                        text-align: center;
 | 
					                                            text-align: center;
 | 
				
			||||||
                                        margin: 0;
 | 
					                                            margin: 0;
 | 
				
			||||||
                                        padding: 10px 12px;
 | 
					                                            padding: 10px 12px;
 | 
				
			||||||
                                    "
 | 
					                                        ">
 | 
				
			||||||
                                >
 | 
					                                    Confirm</a>
 | 
				
			||||||
                                    Confirm</a
 | 
					 | 
				
			||||||
                                >
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <a
 | 
					                                <a
 | 
				
			||||||
                                    href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'refuse', 'admin_email' => $data['admin_email']]) }}"
 | 
					                                    href="{{ route('email.ticket.handle', ['ticket_id' => $data['ticket_id'], 'action' => 'refuse', 'admin_email' => $data['admin_email']]) }}"
 | 
				
			||||||
                                    style="
 | 
					                                    style="
 | 
				
			||||||
                                        color: #fff;
 | 
					                                            color: #fff;
 | 
				
			||||||
                                        border-radius: 10px;
 | 
					                                            border-radius: 10px;
 | 
				
			||||||
                                        background-color: #f03e3e;
 | 
					                                            background-color: #f03e3e;
 | 
				
			||||||
                                        background-image: linear-gradient(
 | 
					                                            background-image: linear-gradient(
 | 
				
			||||||
                                            to top left,
 | 
					                                                to top left,
 | 
				
			||||||
                                            rgba(0, 0, 0, 0.2),
 | 
					                                                rgba(0, 0, 0, 0.2),
 | 
				
			||||||
                                            rgba(0, 0, 0, 0.2) 30%,
 | 
					                                                rgba(0, 0, 0, 0.2) 30%,
 | 
				
			||||||
                                            rgba(0, 0, 0, 0)
 | 
					                                                rgba(0, 0, 0, 0)
 | 
				
			||||||
                                        );
 | 
					                                            );
 | 
				
			||||||
                                        text-decoration: none;
 | 
					                                            text-decoration: none;
 | 
				
			||||||
                                        display: inline-block;
 | 
					                                            display: inline-block;
 | 
				
			||||||
                                        font-weight: 600;
 | 
					                                            font-weight: 600;
 | 
				
			||||||
                                        font-size: 16px;
 | 
					                                            font-size: 16px;
 | 
				
			||||||
                                        line-height: 150%;
 | 
					                                            line-height: 150%;
 | 
				
			||||||
                                        text-align: center;
 | 
					                                            text-align: center;
 | 
				
			||||||
                                        margin: 0;
 | 
					                                            margin: 0;
 | 
				
			||||||
                                        padding: 10px 12px;
 | 
					                                            padding: 10px 12px;
 | 
				
			||||||
                                    "
 | 
					                                        ">
 | 
				
			||||||
                                >
 | 
					                                    Refuse</a>
 | 
				
			||||||
                                    Refuse</a
 | 
					 | 
				
			||||||
                                >
 | 
					 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                            </td>
 | 
					                        </td>
 | 
				
			||||||
                        </tr>
 | 
					                    </tr>
 | 
				
			||||||
                    </table>
 | 
					
 | 
				
			||||||
                </td>
 | 
					                    <tr>
 | 
				
			||||||
            </tr>
 | 
					                        <td style="color: #222222;">
 | 
				
			||||||
            <tr>
 | 
					                            <div style="margin-top: 3rem">
 | 
				
			||||||
                <td>
 | 
					                                <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>
 | 
				
			||||||
                    <table
 | 
					                                <p>1. The ticket has already been approved by another admin.</p>
 | 
				
			||||||
                        style="
 | 
					                                <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>
 | 
				
			||||||
										
											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,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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,3 +46,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 #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>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -493,7 +493,11 @@ const Timekeeping = () => {
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <Text className={classes.deleteModalTitle}>Confirm Delete</Text>
 | 
					      <Text className={classes.deleteModalTitle}>Confirm Delete</Text>
 | 
				
			||||||
      <Text className={classes.deleteModalContent}>
 | 
					      <Text className={classes.deleteModalContent}>
 | 
				
			||||||
        Are you sure you want to delete this note?
 | 
					        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>
 | 
					      </Text>
 | 
				
			||||||
      <Box className={classes.deleteModalFooter}>
 | 
					      <Box className={classes.deleteModalFooter}>
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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'}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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