Deploy to prod #132
			
				
			
		
		
		
	| 
						 | 
					@ -38,6 +38,7 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
                $join->on('reason.c_type', DB::raw("CONCAT('REASON_NOTES')"));
 | 
					                $join->on('reason.c_type', DB::raw("CONCAT('REASON_NOTES')"));
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            ->select(
 | 
					            ->select(
 | 
				
			||||||
 | 
					                DB::raw('notes.id as id'),
 | 
				
			||||||
                DB::raw('notes.n_user_id as n_user_id'),
 | 
					                DB::raw('notes.n_user_id as n_user_id'),
 | 
				
			||||||
                DB::raw('notes.n_time_type as time_type'),
 | 
					                DB::raw('notes.n_time_type as time_type'),
 | 
				
			||||||
                DB::raw('notes.n_year as year'),
 | 
					                DB::raw('notes.n_year as year'),
 | 
				
			||||||
| 
						 | 
					@ -58,6 +59,7 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
            ->get()
 | 
					            ->get()
 | 
				
			||||||
            ->map(function ($item) {
 | 
					            ->map(function ($item) {
 | 
				
			||||||
                return [
 | 
					                return [
 | 
				
			||||||
 | 
					                    "id" => $item->id,
 | 
				
			||||||
                    "day" => $item->day,
 | 
					                    "day" => $item->day,
 | 
				
			||||||
                    "n_user_id" => $item->n_user_id,
 | 
					                    "n_user_id" => $item->n_user_id,
 | 
				
			||||||
                    "reason_code" => $item->reason_code,
 | 
					                    "reason_code" => $item->reason_code,
 | 
				
			||||||
| 
						 | 
					@ -145,6 +147,28 @@ class LeaveManagementController extends Controller
 | 
				
			||||||
        return response()->json(['status' => true, 'message' => 'Updated successfully']);
 | 
					        return response()->json(['status' => true, 'message' => 'Updated successfully']);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function updateNoteStatus(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $rules = [
 | 
				
			||||||
 | 
					            'id' => 'required',
 | 
				
			||||||
 | 
					            'n_reason' => 'required|in:ONLEAVE,LEAVE_WITHOUT_PAY'
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Validate the request
 | 
				
			||||||
 | 
					        $request->validate($rules);
 | 
				
			||||||
 | 
					        $id = $request->input('id');
 | 
				
			||||||
 | 
					        $reason = $request->input('n_reason');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $note = Notes::find($id);
 | 
				
			||||||
 | 
					        if (!$note) {
 | 
				
			||||||
 | 
					            return response()->json(['message' => 'Note not found', 'status' => false]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $note->n_reason = $reason;
 | 
				
			||||||
 | 
					        $note->save();
 | 
				
			||||||
 | 
					        return response()->json(data: ['message' => 'Update success', 'status' => true]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function export(Request $request)
 | 
					    public function export(Request $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $year = $request->query('year', now()->year);
 | 
					        $year = $request->query('year', now()->year);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,52 +173,62 @@ class TimekeepingController extends Controller
 | 
				
			||||||
        // Validate the request
 | 
					        // Validate the request
 | 
				
			||||||
        $request->validate($rules);
 | 
					        $request->validate($rules);
 | 
				
			||||||
        $id = $request->input('id');
 | 
					        $id = $request->input('id');
 | 
				
			||||||
 | 
					        $month = $request->month;
 | 
				
			||||||
 | 
					        $year = $request->year;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $note = Notes::find($id);
 | 
					        $note = Notes::find($id);
 | 
				
			||||||
        if (!$note) {
 | 
					        if (!$note) {
 | 
				
			||||||
            return response()->json(['message' => 'Note not found', 'status' => false]);
 | 
					            return response()->json(['message' => 'Note not found', 'status' => false]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $ticket = Ticket::find($note->ticket_id);
 | 
					        if ($note->ticket_id != null) {
 | 
				
			||||||
        if (!$ticket) {
 | 
					            $ticket = Ticket::find($note->ticket_id);
 | 
				
			||||||
            return response()->json(['message' => 'Ticket not found, can not delete note', 'status' => false]);
 | 
					            if (!$ticket) {
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'Ticket not found, can not delete note', 'status' => false]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $admin = auth('admins')->user();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Handle send mail
 | 
				
			||||||
 | 
					            $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->start_period);
 | 
				
			||||||
 | 
					            $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->end_period);
 | 
				
			||||||
 | 
					            $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $ticket->type);
 | 
				
			||||||
 | 
					            $formattedStartDate = Carbon::createFromFormat('Y-m-d', $ticket->start_date)->format('d/m/Y');
 | 
				
			||||||
 | 
					            $formattedEndDate = Carbon::createFromFormat('Y-m-d', $ticket->end_date)->format('d/m/Y');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $user = Admin::find($ticket->user_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $data = array(
 | 
				
			||||||
 | 
					                "email_template" => "email.notification_tickets_user",
 | 
				
			||||||
 | 
					                "user_name" => $user->name,
 | 
				
			||||||
 | 
					                "email" => $user->email,
 | 
				
			||||||
 | 
					                "name" => $admin->name, //name admin duyệt
 | 
				
			||||||
 | 
					                "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
				
			||||||
 | 
					                "type" => $dataMasterType->c_name,
 | 
				
			||||||
 | 
					                "note" => $ticket->reason,
 | 
				
			||||||
 | 
					                "admin_note" => $ticket->admin_note,
 | 
				
			||||||
 | 
					                "link" => "/tickets", //link đến page admin
 | 
				
			||||||
 | 
					                "status" => "refused",
 | 
				
			||||||
 | 
					                "subject" => "[Ticket response] Ticket From " . $admin->name
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            Mail::to($user->email)->send(new TicketMail($data));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Update
 | 
				
			||||||
 | 
					            $ticket->updated_by = $admin->name;
 | 
				
			||||||
 | 
					            $ticket->status = "REFUSED";
 | 
				
			||||||
 | 
					            $ticket->save();
 | 
				
			||||||
 | 
					            Notes::where('ticket_id', $ticket->id)->delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Clear Timekeeping cache
 | 
				
			||||||
 | 
					            $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
 | 
				
			||||||
 | 
					            $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $admin = auth('admins')->user();
 | 
					        $note->delete();
 | 
				
			||||||
 | 
					        $this->createOrUpdateRecordForCurrentMonth($month, $year);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Handle send mail
 | 
					 | 
				
			||||||
        $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->start_period);
 | 
					 | 
				
			||||||
        $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->end_period);
 | 
					 | 
				
			||||||
        $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $ticket->type);
 | 
					 | 
				
			||||||
        $formattedStartDate = Carbon::createFromFormat('Y-m-d', $ticket->start_date)->format('d/m/Y');
 | 
					 | 
				
			||||||
        $formattedEndDate = Carbon::createFromFormat('Y-m-d', $ticket->end_date)->format('d/m/Y');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $user = Admin::find($ticket->user_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $data = array(
 | 
					 | 
				
			||||||
            "email_template" => "email.notification_tickets_user",
 | 
					 | 
				
			||||||
            "user_name" => $user->name,
 | 
					 | 
				
			||||||
            "email" => $user->email,
 | 
					 | 
				
			||||||
            "name" => $admin->name, //name admin duyệt
 | 
					 | 
				
			||||||
            "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
					 | 
				
			||||||
            "type" => $dataMasterType->c_name,
 | 
					 | 
				
			||||||
            "note" => $ticket->reason,
 | 
					 | 
				
			||||||
            "admin_note" => $ticket->admin_note,
 | 
					 | 
				
			||||||
            "link" => "/tickets", //link đến page admin
 | 
					 | 
				
			||||||
            "status" => "refused",
 | 
					 | 
				
			||||||
            "subject" => "[Ticket response] Ticket From " . $admin->name
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        Mail::to($user->email)->send(new TicketMail($data));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Update
 | 
					 | 
				
			||||||
        $ticket->updated_by = $admin->name;
 | 
					 | 
				
			||||||
        $ticket->status = "REFUSED";
 | 
					 | 
				
			||||||
        $ticket->save();
 | 
					 | 
				
			||||||
        Notes::where('ticket_id', $ticket->id)->delete();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Clear Timekeeping cache
 | 
					 | 
				
			||||||
        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
 | 
					 | 
				
			||||||
        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
 | 
					 | 
				
			||||||
        return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
					        return response()->json(['message' => 'Delete success', 'status' => true]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -157,6 +157,7 @@ Route::middleware('api')
 | 
				
			||||||
                Route::get('/', [LeaveManagementController::class, 'get'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
					                Route::get('/', [LeaveManagementController::class, 'get'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
                Route::get('/export', [LeaveManagementController::class, 'export'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
					                Route::get('/export', [LeaveManagementController::class, 'export'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
                Route::post('/saveNoteLeave', [LeaveManagementController::class, 'saveNoteLeave'])->middleware('check.permission:admin.hr');
 | 
					                Route::post('/saveNoteLeave', [LeaveManagementController::class, 'saveNoteLeave'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
 | 
					                Route::post('/updateNoteStatus', [LeaveManagementController::class, 'updateNoteStatus'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,10 +13,13 @@ use Modules\Auth\app\Models\User;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
use Modules\Admin\app\Models\Category;
 | 
					use Modules\Admin\app\Models\Category;
 | 
				
			||||||
use SimpleSoftwareIO\QrCode\Facades\QrCode;
 | 
					use SimpleSoftwareIO\QrCode\Facades\QrCode;
 | 
				
			||||||
 | 
					use App\Traits\AnalyzeData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserController extends Controller
 | 
					class UserController extends Controller
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use IsAPI;
 | 
					    use IsAPI;
 | 
				
			||||||
 | 
					    use AnalyzeData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct()
 | 
					    public function __construct()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->middleware('jwt.auth');
 | 
					        $this->middleware('jwt.auth');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,8 @@ export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
 | 
				
			||||||
export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
 | 
					export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
 | 
				
			||||||
export const updateNoteLeave =
 | 
					export const updateNoteLeave =
 | 
				
			||||||
  API_URL + 'v1/admin/leave-management/saveNoteLeave'
 | 
					  API_URL + 'v1/admin/leave-management/saveNoteLeave'
 | 
				
			||||||
 | 
					export const updateNoteStatus =
 | 
				
			||||||
 | 
					  API_URL + 'v1/admin/leave-management/updateNoteStatus'
 | 
				
			||||||
export const exportLeaveManagement =
 | 
					export const exportLeaveManagement =
 | 
				
			||||||
  API_URL + 'v1/admin/leave-management/export'
 | 
					  API_URL + 'v1/admin/leave-management/export'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,3 +60,37 @@
 | 
				
			||||||
  padding-top: 5px;
 | 
					  padding-top: 5px;
 | 
				
			||||||
  padding-bottom: 5px;
 | 
					  padding-bottom: 5px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Thêm styles cho Modal xác nhận xóa */
 | 
				
			||||||
 | 
					.deleteModal {
 | 
				
			||||||
 | 
					  background-color: light-dark(white, #2d353c);
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border: solid 1px #ff4646;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalTitle {
 | 
				
			||||||
 | 
					  color: #ff4646;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  font-size: 1.2rem;
 | 
				
			||||||
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalContent {
 | 
				
			||||||
 | 
					  color: light-dark(#2d353c, white);
 | 
				
			||||||
 | 
					  margin-bottom: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteModalFooter {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  justify-content: flex-end;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					  margin-top: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton {
 | 
				
			||||||
 | 
					  background-color: #ff4646;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.deleteButton:hover {
 | 
				
			||||||
 | 
					  background-color: #ff6b6b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ import {
 | 
				
			||||||
  Group,
 | 
					  Group,
 | 
				
			||||||
  HoverCard,
 | 
					  HoverCard,
 | 
				
			||||||
  Menu,
 | 
					  Menu,
 | 
				
			||||||
 | 
					  Modal,
 | 
				
			||||||
  Select,
 | 
					  Select,
 | 
				
			||||||
  Stack,
 | 
					  Stack,
 | 
				
			||||||
  Table,
 | 
					  Table,
 | 
				
			||||||
| 
						 | 
					@ -21,15 +22,23 @@ import {
 | 
				
			||||||
} from '@mantine/core'
 | 
					} from '@mantine/core'
 | 
				
			||||||
import { useDisclosure } from '@mantine/hooks'
 | 
					import { useDisclosure } from '@mantine/hooks'
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import { IconEdit, IconFileExcel, IconHelpCircle } from '@tabler/icons-react'
 | 
					import {
 | 
				
			||||||
 | 
					  IconEdit,
 | 
				
			||||||
 | 
					  IconFileExcel,
 | 
				
			||||||
 | 
					  IconHelpCircle,
 | 
				
			||||||
 | 
					  IconRefresh,
 | 
				
			||||||
 | 
					  IconTrash,
 | 
				
			||||||
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
import classes from './LeaveManagement.module.css'
 | 
					import classes from './LeaveManagement.module.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  getLeaveManagement,
 | 
					  getLeaveManagement,
 | 
				
			||||||
  updateNoteLeave,
 | 
					  updateNoteLeave,
 | 
				
			||||||
  exportLeaveManagement,
 | 
					  exportLeaveManagement,
 | 
				
			||||||
 | 
					  updateNoteStatus,
 | 
				
			||||||
 | 
					  deleteNote,
 | 
				
			||||||
} from '@/api/Admin'
 | 
					} from '@/api/Admin'
 | 
				
			||||||
import { update } from '@/rtk/helpers/CRUD'
 | 
					import { update, Xdelete } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
					import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface User {
 | 
					interface User {
 | 
				
			||||||
| 
						 | 
					@ -57,6 +66,7 @@ interface LeaveDay {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface MonthlyLeaveDays {
 | 
					interface MonthlyLeaveDays {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
  day: number
 | 
					  day: number
 | 
				
			||||||
  leave_days: number
 | 
					  leave_days: number
 | 
				
			||||||
  month: number
 | 
					  month: number
 | 
				
			||||||
| 
						 | 
					@ -86,6 +96,48 @@ interface UserData {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LeaveManagement = () => {
 | 
					const LeaveManagement = () => {
 | 
				
			||||||
  const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
 | 
					  const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
 | 
				
			||||||
 | 
					  const [openedDetailOff, { open: openDetailOff, close: closeDetailOff }] =
 | 
				
			||||||
 | 
					    useDisclosure(false)
 | 
				
			||||||
 | 
					  const [detailOffItem, setDetailOffItem] = useState<UserData>({
 | 
				
			||||||
 | 
					    user: {
 | 
				
			||||||
 | 
					      id: 0,
 | 
				
			||||||
 | 
					      name: '',
 | 
				
			||||||
 | 
					      email: '',
 | 
				
			||||||
 | 
					      email_verified_at: '',
 | 
				
			||||||
 | 
					      permission: '',
 | 
				
			||||||
 | 
					      remember_token: '',
 | 
				
			||||||
 | 
					      avatar: '',
 | 
				
			||||||
 | 
					      created_at: '',
 | 
				
			||||||
 | 
					      updated_at: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    leaveDay: {
 | 
				
			||||||
 | 
					      id: 0,
 | 
				
			||||||
 | 
					      ld_user_id: 0,
 | 
				
			||||||
 | 
					      ld_year: 0,
 | 
				
			||||||
 | 
					      ld_day_total: 0,
 | 
				
			||||||
 | 
					      ld_additional_day: 0,
 | 
				
			||||||
 | 
					      ld_special_leave_day: 0,
 | 
				
			||||||
 | 
					      ld_note: '',
 | 
				
			||||||
 | 
					      created_at: '',
 | 
				
			||||||
 | 
					      updated_at: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    monthlyLeaveDays: [],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [isStatusConfirmOpen, setIsStatusConfirmOpen] = useState(false)
 | 
				
			||||||
 | 
					  const [isDisableStatusBtn, setIsDisableStatusBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [noteStatus, setNoteStatus] = useState<{
 | 
				
			||||||
 | 
					    id: number
 | 
				
			||||||
 | 
					    reason: string
 | 
				
			||||||
 | 
					  }>({
 | 
				
			||||||
 | 
					    id: 0,
 | 
				
			||||||
 | 
					    reason: '',
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false)
 | 
				
			||||||
 | 
					  const [isDisableDeleteBtn, setIsDisableDeleteBtn] = useState(false)
 | 
				
			||||||
 | 
					  const [noteToDelete, setNoteToDelete] = useState<any>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [disableBtn, setDisableBtn] = useState(false)
 | 
					  const [disableBtn, setDisableBtn] = useState(false)
 | 
				
			||||||
  const monthInYear = getMonthNames()
 | 
					  const monthInYear = getMonthNames()
 | 
				
			||||||
  const [customAddNotes, setCustomAddNotes] = useState<{
 | 
					  const [customAddNotes, setCustomAddNotes] = useState<{
 | 
				
			||||||
| 
						 | 
					@ -209,6 +261,51 @@ const LeaveManagement = () => {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const updateNote = async () => {
 | 
				
			||||||
 | 
					    setIsDisableStatusBtn(true)
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await update(
 | 
				
			||||||
 | 
					        updateNoteStatus,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          id: noteStatus.id,
 | 
				
			||||||
 | 
					          n_reason: noteStatus.reason,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        getLeaveList,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      setIsDisableStatusBtn(false)
 | 
				
			||||||
 | 
					      closeDetailOff()
 | 
				
			||||||
 | 
					      setIsStatusConfirmOpen(false)
 | 
				
			||||||
 | 
					      setNoteStatus({
 | 
				
			||||||
 | 
					        id: 0,
 | 
				
			||||||
 | 
					        reason: '',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleDeleteNote = async () => {
 | 
				
			||||||
 | 
					    if (noteToDelete) {
 | 
				
			||||||
 | 
					      setIsDisableDeleteBtn(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        await Xdelete(
 | 
				
			||||||
 | 
					          deleteNote,
 | 
				
			||||||
 | 
					          { id: noteToDelete.id, month: noteToDelete.month, year: date.year },
 | 
				
			||||||
 | 
					          getLeaveList,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.log(error)
 | 
				
			||||||
 | 
					      } finally {
 | 
				
			||||||
 | 
					        setIsDisableDeleteBtn(false)
 | 
				
			||||||
 | 
					        closeDetailOff()
 | 
				
			||||||
 | 
					        setIsDeleteConfirmOpen(false)
 | 
				
			||||||
 | 
					        setNoteToDelete(null)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function getMonthNames() {
 | 
					  function getMonthNames() {
 | 
				
			||||||
    const monthNames = [
 | 
					    const monthNames = [
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
| 
						 | 
					@ -831,7 +928,12 @@ const LeaveManagement = () => {
 | 
				
			||||||
                  </Table.Td>
 | 
					                  </Table.Td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                  {/* Off */}
 | 
					                  {/* Off */}
 | 
				
			||||||
                  <Table.Td>
 | 
					                  <Table.Td
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      openDetailOff()
 | 
				
			||||||
 | 
					                      setDetailOffItem(user)
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
                    {totalDayOff > 0 ? (
 | 
					                    {totalDayOff > 0 ? (
 | 
				
			||||||
                      <Tooltip
 | 
					                      <Tooltip
 | 
				
			||||||
                        multiline
 | 
					                        multiline
 | 
				
			||||||
| 
						 | 
					@ -949,6 +1051,248 @@ const LeaveManagement = () => {
 | 
				
			||||||
          </Table.Tbody>
 | 
					          </Table.Tbody>
 | 
				
			||||||
        </Table>
 | 
					        </Table>
 | 
				
			||||||
      </Box>
 | 
					      </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Modal
 | 
				
			||||||
 | 
					        opened={openedDetailOff}
 | 
				
			||||||
 | 
					        onClose={() => {
 | 
				
			||||||
 | 
					          closeDetailOff()
 | 
				
			||||||
 | 
					          setDetailOffItem({
 | 
				
			||||||
 | 
					            user: {
 | 
				
			||||||
 | 
					              id: 0,
 | 
				
			||||||
 | 
					              name: '',
 | 
				
			||||||
 | 
					              email: '',
 | 
				
			||||||
 | 
					              email_verified_at: '',
 | 
				
			||||||
 | 
					              permission: '',
 | 
				
			||||||
 | 
					              remember_token: '',
 | 
				
			||||||
 | 
					              avatar: '',
 | 
				
			||||||
 | 
					              created_at: '',
 | 
				
			||||||
 | 
					              updated_at: '',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            leaveDay: {
 | 
				
			||||||
 | 
					              id: 0,
 | 
				
			||||||
 | 
					              ld_user_id: 0,
 | 
				
			||||||
 | 
					              ld_year: 0,
 | 
				
			||||||
 | 
					              ld_day_total: 0,
 | 
				
			||||||
 | 
					              ld_additional_day: 0,
 | 
				
			||||||
 | 
					              ld_special_leave_day: 0,
 | 
				
			||||||
 | 
					              ld_note: '',
 | 
				
			||||||
 | 
					              created_at: '',
 | 
				
			||||||
 | 
					              updated_at: '',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            monthlyLeaveDays: [],
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        title={
 | 
				
			||||||
 | 
					          <Stack>
 | 
				
			||||||
 | 
					            <Text size="lg" fw={600}>
 | 
				
			||||||
 | 
					              Chi tiết nghỉ phép {date.year}
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					            <Text size="md" c="dimmed">
 | 
				
			||||||
 | 
					              Nhân viên:{' '}
 | 
				
			||||||
 | 
					              <Text span fw={700} c="dark">
 | 
				
			||||||
 | 
					                {detailOffItem.user.name}
 | 
				
			||||||
 | 
					              </Text>
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					          </Stack>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        size="70%"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Table striped highlightOnHover withTableBorder withColumnBorders>
 | 
				
			||||||
 | 
					          <Table.Thead>
 | 
				
			||||||
 | 
					            <Table.Tr>
 | 
				
			||||||
 | 
					              <Table.Th>Time</Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th>Status</Table.Th>
 | 
				
			||||||
 | 
					              <Table.Th>Action</Table.Th>
 | 
				
			||||||
 | 
					            </Table.Tr>
 | 
				
			||||||
 | 
					          </Table.Thead>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <Table.Tbody>
 | 
				
			||||||
 | 
					            {detailOffItem.monthlyLeaveDays
 | 
				
			||||||
 | 
					              .slice()
 | 
				
			||||||
 | 
					              .reverse()
 | 
				
			||||||
 | 
					              .map((item: MonthlyLeaveDays) => {
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                  <Table.Tr key={item.id}>
 | 
				
			||||||
 | 
					                    <Table.Td>
 | 
				
			||||||
 | 
					                      {item.time_type_name} ({item.day}/{item.month})
 | 
				
			||||||
 | 
					                    </Table.Td>
 | 
				
			||||||
 | 
					                    <Table.Td>
 | 
				
			||||||
 | 
					                      <Badge
 | 
				
			||||||
 | 
					                        color={
 | 
				
			||||||
 | 
					                          item.reason_code === 'ONLEAVE'
 | 
				
			||||||
 | 
					                            ? 'teal'
 | 
				
			||||||
 | 
					                            : item.reason_code === 'LEAVE_WITHOUT_PAY'
 | 
				
			||||||
 | 
					                            ? 'red'
 | 
				
			||||||
 | 
					                            : 'blue'
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      >
 | 
				
			||||||
 | 
					                        {item.reason_name}
 | 
				
			||||||
 | 
					                      </Badge>
 | 
				
			||||||
 | 
					                    </Table.Td>
 | 
				
			||||||
 | 
					                    <Table.Td>
 | 
				
			||||||
 | 
					                      {item.reason_code === 'ONLEAVE' ? (
 | 
				
			||||||
 | 
					                        <IconRefresh
 | 
				
			||||||
 | 
					                          className={classes.deleteIcon}
 | 
				
			||||||
 | 
					                          onClick={() => {
 | 
				
			||||||
 | 
					                            setIsStatusConfirmOpen(true)
 | 
				
			||||||
 | 
					                            setNoteStatus({
 | 
				
			||||||
 | 
					                              id: item.id,
 | 
				
			||||||
 | 
					                              reason: 'LEAVE_WITHOUT_PAY',
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                          }}
 | 
				
			||||||
 | 
					                          title="Chuyển thành không phép"
 | 
				
			||||||
 | 
					                          width={20}
 | 
				
			||||||
 | 
					                          height={20}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                      ) : item.reason_code === 'LEAVE_WITHOUT_PAY' ? (
 | 
				
			||||||
 | 
					                        <IconRefresh
 | 
				
			||||||
 | 
					                          className={classes.editIcon}
 | 
				
			||||||
 | 
					                          onClick={() => {
 | 
				
			||||||
 | 
					                            setIsStatusConfirmOpen(true)
 | 
				
			||||||
 | 
					                            setNoteStatus({
 | 
				
			||||||
 | 
					                              id: item.id,
 | 
				
			||||||
 | 
					                              reason: 'ONLEAVE',
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                          }}
 | 
				
			||||||
 | 
					                          title="Chuyển thành có phép"
 | 
				
			||||||
 | 
					                          width={20}
 | 
				
			||||||
 | 
					                          height={20}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                      ) : (
 | 
				
			||||||
 | 
					                        ''
 | 
				
			||||||
 | 
					                      )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                      <IconTrash
 | 
				
			||||||
 | 
					                        className={classes.deleteIcon}
 | 
				
			||||||
 | 
					                        onClick={() => {
 | 
				
			||||||
 | 
					                          setNoteToDelete(item)
 | 
				
			||||||
 | 
					                          setIsDeleteConfirmOpen(true)
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                        title="Xóa Note"
 | 
				
			||||||
 | 
					                        width={20}
 | 
				
			||||||
 | 
					                        height={20}
 | 
				
			||||||
 | 
					                      />
 | 
				
			||||||
 | 
					                    </Table.Td>
 | 
				
			||||||
 | 
					                  </Table.Tr>
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					          </Table.Tbody>
 | 
				
			||||||
 | 
					        </Table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {/* Confirm Change Status Note Modal */}
 | 
				
			||||||
 | 
					        <Modal
 | 
				
			||||||
 | 
					          opened={isStatusConfirmOpen}
 | 
				
			||||||
 | 
					          onClose={() => {
 | 
				
			||||||
 | 
					            setIsStatusConfirmOpen(false)
 | 
				
			||||||
 | 
					            setNoteStatus({
 | 
				
			||||||
 | 
					              id: 0,
 | 
				
			||||||
 | 
					              reason: '',
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          centered
 | 
				
			||||||
 | 
					          size="sm"
 | 
				
			||||||
 | 
					          classNames={{
 | 
				
			||||||
 | 
					            content: classes.deleteModal,
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Text className={classes.deleteModalTitle} c="dark">
 | 
				
			||||||
 | 
					            Confirm Change Note Status
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          {noteStatus.reason === 'ONLEAVE' ? (
 | 
				
			||||||
 | 
					            <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					              This action will change the note status from{' '}
 | 
				
			||||||
 | 
					              <Text span c="red" fw="bold">
 | 
				
			||||||
 | 
					                Không phép
 | 
				
			||||||
 | 
					              </Text>{' '}
 | 
				
			||||||
 | 
					              to{' '}
 | 
				
			||||||
 | 
					              <Text span c="teal" fw="bold">
 | 
				
			||||||
 | 
					                Nghỉ phép
 | 
				
			||||||
 | 
					              </Text>
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					          ) : (
 | 
				
			||||||
 | 
					            <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					              This action will change the note status from{' '}
 | 
				
			||||||
 | 
					              <Text span c="teal" fw="bold">
 | 
				
			||||||
 | 
					                Nghỉ phép
 | 
				
			||||||
 | 
					              </Text>{' '}
 | 
				
			||||||
 | 
					              to{' '}
 | 
				
			||||||
 | 
					              <Text span c="red" fw="bold">
 | 
				
			||||||
 | 
					                Không phép
 | 
				
			||||||
 | 
					              </Text>
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					            Are you sure you want to proceed?
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Box className={classes.deleteModalFooter}>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              variant="outline"
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setIsStatusConfirmOpen(false)
 | 
				
			||||||
 | 
					                setNoteStatus({
 | 
				
			||||||
 | 
					                  id: 0,
 | 
				
			||||||
 | 
					                  reason: '',
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              disabled={isDisableStatusBtn}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Cancel
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              className={classes.deleteButton}
 | 
				
			||||||
 | 
					              onClick={updateNote}
 | 
				
			||||||
 | 
					              disabled={isDisableStatusBtn}
 | 
				
			||||||
 | 
					              bg={noteStatus.reason === 'ONLEAVE' ? 'teal' : 'red'}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Confirm
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {/* Confirm Delete Note Modal */}
 | 
				
			||||||
 | 
					        <Modal
 | 
				
			||||||
 | 
					          opened={isDeleteConfirmOpen}
 | 
				
			||||||
 | 
					          onClose={() => {
 | 
				
			||||||
 | 
					            setIsDeleteConfirmOpen(false)
 | 
				
			||||||
 | 
					            setNoteToDelete(null)
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          centered
 | 
				
			||||||
 | 
					          size="sm"
 | 
				
			||||||
 | 
					          classNames={{
 | 
				
			||||||
 | 
					            content: classes.deleteModal,
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Text className={classes.deleteModalTitle}>Confirm Delete</Text>
 | 
				
			||||||
 | 
					          <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					            This action will change the ticket status to{' '}
 | 
				
			||||||
 | 
					            <strong>Refused</strong> and delete all related notes.
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Text className={classes.deleteModalContent}>
 | 
				
			||||||
 | 
					            Are you sure you want to proceed?
 | 
				
			||||||
 | 
					          </Text>
 | 
				
			||||||
 | 
					          <Box className={classes.deleteModalFooter}>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              variant="outline"
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                setIsDeleteConfirmOpen(false)
 | 
				
			||||||
 | 
					                setNoteToDelete(null)
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              disabled={isDisableDeleteBtn}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Cancel
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              className={classes.deleteButton}
 | 
				
			||||||
 | 
					              onClick={handleDeleteNote}
 | 
				
			||||||
 | 
					              disabled={isDisableDeleteBtn}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Delete
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Modal>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -498,7 +498,7 @@ const TicketsManagement = () => {
 | 
				
			||||||
            setDisableBtn(true)
 | 
					            setDisableBtn(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (action === 'update') {
 | 
					            if (action === 'update') {
 | 
				
			||||||
              if (values.status === 'REFUSED') {
 | 
					              if (values.status === 'REFUSED' && item.status !== 'REFUSED') {
 | 
				
			||||||
                setIsRefuseConfirmOpen(true)
 | 
					                setIsRefuseConfirmOpen(true)
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                await handleUpdate(values)
 | 
					                await handleUpdate(values)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue