update UI leave management, add func update old data #127
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -553,11 +553,20 @@ class TicketController extends Controller
 | 
			
		|||
            // Ngày phép còn lại < ngày yêu cầu (Không đủ phép)
 | 
			
		||||
            else if ($remainingOnleaveDaysInMonth < $monthData['days_requested']) {
 | 
			
		||||
                // Vượt limit
 | 
			
		||||
                if ($onleaveDaysInMonth >= $maxDaysPerMonth) {
 | 
			
		||||
                if ($willUsedDaysInMonth > $maxDaysPerMonth) {
 | 
			
		||||
                    $hasInsufficientDays = true;
 | 
			
		||||
                    $month_data_status = 'exceed_max_days';
 | 
			
		||||
                    $onleave_days_will_use = $maxDaysPerMonth - $onleaveDaysInMonth;
 | 
			
		||||
                    $nopay_days_will_use = $monthData['days_requested'] - $maxDaysPerMonth + $onleaveDaysInMonth;
 | 
			
		||||
 | 
			
		||||
                    // Phép còn lại > limit
 | 
			
		||||
                    if ($remainingOnleaveDaysInMonth > $maxDaysPerMonth) {
 | 
			
		||||
                        $onleave_days_will_use = $maxDaysPerMonth - $onleaveDaysInMonth;
 | 
			
		||||
                        $nopay_days_will_use = $monthData['days_requested'] - $maxDaysPerMonth + $onleaveDaysInMonth;
 | 
			
		||||
                    }
 | 
			
		||||
                    // Phép còn lại < limit
 | 
			
		||||
                    else {
 | 
			
		||||
                        $onleave_days_will_use = $remainingOnleaveDaysInMonth;
 | 
			
		||||
                        $nopay_days_will_use = $monthData['days_requested'] - $remainingOnleaveDaysInMonth;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Log::debug("--- Không đủ phép trong tháng, vượt quá limit ---", [
 | 
			
		||||
                        "Phep" => $onleave_days_will_use,
 | 
			
		||||
| 
						 | 
				
			
			@ -1409,4 +1418,147 @@ class TicketController extends Controller
 | 
			
		|||
 | 
			
		||||
        return $totalDays;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function updateOldData(int $month, int $year)
 | 
			
		||||
    {
 | 
			
		||||
        LeaveDays::where('ld_year', $year)
 | 
			
		||||
            ->update(['ld_day_total' => $month]);
 | 
			
		||||
 | 
			
		||||
        $users = Admin::all();
 | 
			
		||||
 | 
			
		||||
        foreach ($users as $user) {
 | 
			
		||||
            $leaveDay = LeaveDays::where('ld_user_id', $user->id)
 | 
			
		||||
                ->where('ld_year', $year)
 | 
			
		||||
                ->first();
 | 
			
		||||
            $notes = Notes::where('n_user_id', $user->id)
 | 
			
		||||
                ->where('n_year', $year)
 | 
			
		||||
                ->where('n_reason', 'ONLEAVE')
 | 
			
		||||
                ->orderBy('n_month')
 | 
			
		||||
                ->orderBy('n_day')
 | 
			
		||||
                ->get()
 | 
			
		||||
                ->groupBy('n_month');
 | 
			
		||||
 | 
			
		||||
            $onleaveDaysTotal = $leaveDay->ld_additional_day;
 | 
			
		||||
 | 
			
		||||
            $previousYearData = LeaveDays::where('ld_user_id', $user->id)
 | 
			
		||||
                ->where('ld_year', $year - 1)
 | 
			
		||||
                ->first();
 | 
			
		||||
 | 
			
		||||
            $ld_additional_day = 0;
 | 
			
		||||
            $ld_note = '';
 | 
			
		||||
 | 
			
		||||
            if ($previousYearData) {
 | 
			
		||||
                $ld_additional_day = $previousYearData->ld_day_total + $previousYearData->ld_additional_day;
 | 
			
		||||
                $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
			
		||||
                    $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
			
		||||
                        ->where('categories.c_type', 'TIME_TYPE');
 | 
			
		||||
                })
 | 
			
		||||
                    ->select(
 | 
			
		||||
                        DB::raw('notes.n_user_id as n_user_id'),
 | 
			
		||||
                        DB::raw('notes.n_year as year'),
 | 
			
		||||
                        DB::raw('SUM(categories.c_value) as leave_days')
 | 
			
		||||
                    )
 | 
			
		||||
                    ->where('notes.n_year', $year - 1)
 | 
			
		||||
                    ->where('notes.n_user_id', $user->id)
 | 
			
		||||
                    ->where('notes.n_reason', 'ONLEAVE')
 | 
			
		||||
                    ->groupBy(DB::raw('notes.n_year'))
 | 
			
		||||
                    ->first();
 | 
			
		||||
                if ($totalLeaveDaysByMonth) {
 | 
			
		||||
                    $ld_additional_day = $ld_additional_day - $totalLeaveDaysByMonth->leave_days;
 | 
			
		||||
                    if ($ld_additional_day < 0) {
 | 
			
		||||
                        $ld_additional_day = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ($ld_additional_day > 0) {
 | 
			
		||||
                    $ld_note = "Cộng " . $ld_additional_day . " ngày phép tồn năm trước. \n";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for ($i = 1; $i <= $month; $i++) {
 | 
			
		||||
                // Giả lập cộng phép
 | 
			
		||||
                $onleaveDaysTotal++;
 | 
			
		||||
                $ld_note = $ld_note . "Cộng phép tháng " . $i . ".\n";
 | 
			
		||||
                // $tmpOnleaveDaysTotal = $onleaveDaysTotal;
 | 
			
		||||
 | 
			
		||||
                $onleaveDaysInMonth = 0;
 | 
			
		||||
                $nopayDaysInMonth = 0;
 | 
			
		||||
 | 
			
		||||
                if ($notes->has($i)) {
 | 
			
		||||
                    foreach ($notes[$i] as $note) {
 | 
			
		||||
                        $onleaveDaysInMonth += $note->n_time_type == 'ALL' ? 1.0 : 0.5;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if ($onleaveDaysInMonth > $onleaveDaysTotal) {
 | 
			
		||||
                        $nopayDaysInMonth = $onleaveDaysInMonth - $onleaveDaysTotal;
 | 
			
		||||
                        $onleaveDaysTotal = 0;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $onleaveDaysTotal -= $onleaveDaysInMonth;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Xử lý cập nhật lại các note có phép thành không phép
 | 
			
		||||
                    if ($nopayDaysInMonth > 0) {
 | 
			
		||||
                        $revertNotes = $notes->get($i, collect())->reverse();
 | 
			
		||||
                        $nopayDaysUpdated = 0;
 | 
			
		||||
 | 
			
		||||
                        foreach ($revertNotes as $note) {
 | 
			
		||||
                            if ($note->n_time_type == 'ALL') {
 | 
			
		||||
                                if ($nopayDaysInMonth - $nopayDaysUpdated == 0.5) {
 | 
			
		||||
                                    Notes::create([
 | 
			
		||||
                                        'n_user_id' => $user->id,
 | 
			
		||||
                                        'n_day' => $note->n_day,
 | 
			
		||||
                                        'n_month' => $note->n_month,
 | 
			
		||||
                                        'n_year' => $note->n_year,
 | 
			
		||||
                                        'n_time_type' => 'S',
 | 
			
		||||
                                        'n_reason' => 'ONLEAVE',
 | 
			
		||||
                                        'n_note' => $note->n_note,
 | 
			
		||||
                                        'ticket_id' => $note->ticket_id
 | 
			
		||||
                                    ]);
 | 
			
		||||
                                    Notes::create([
 | 
			
		||||
                                        'n_user_id' => $user->id,
 | 
			
		||||
                                        'n_day' => $note->n_day,
 | 
			
		||||
                                        'n_month' => $note->n_month,
 | 
			
		||||
                                        'n_year' => $note->n_year,
 | 
			
		||||
                                        'n_time_type' => 'C',
 | 
			
		||||
                                        'n_reason' => 'LEAVE_WITHOUT_PAY',
 | 
			
		||||
                                        'n_note' => $note->n_note,
 | 
			
		||||
                                        'ticket_id' => $note->ticket_id
 | 
			
		||||
                                    ]);
 | 
			
		||||
 | 
			
		||||
                                    $note->delete();
 | 
			
		||||
                                    break;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                $nopayDaysUpdated += 1.0;
 | 
			
		||||
                                $note->update([
 | 
			
		||||
                                    'n_reason' => "LEAVE_WITHOUT_PAY"
 | 
			
		||||
                                ]);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                $nopayDaysUpdated += 0.5;
 | 
			
		||||
                                $note->update([
 | 
			
		||||
                                    'n_reason' => "LEAVE_WITHOUT_PAY"
 | 
			
		||||
                                ]);
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if ($nopayDaysUpdated >= $nopayDaysInMonth) {
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Log thông kê sau mỗi tháng
 | 
			
		||||
                // Log::debug(
 | 
			
		||||
                //     "📊 Thống kê ngày phép Tháng {$i}:\n" .
 | 
			
		||||
                //         " - Tổng phép đầu tháng: $tmpOnleaveDaysTotal\n" .
 | 
			
		||||
                //         " - Có phép: $onleaveDaysInMonth\n" .
 | 
			
		||||
                //         " - Không phép: $nopayDaysInMonth\n" .
 | 
			
		||||
                //         " - Tổng phép cuối tháng: $onleaveDaysTotal\n"
 | 
			
		||||
                // );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $leaveDay->ld_note = $ld_note;
 | 
			
		||||
            $leaveDay->save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ class AddMonthlyLeaveDays implements ShouldQueue
 | 
			
		|||
              $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;
 | 
			
		||||
              $newNote = "Cộng phép tháng " . $leaveDay->ld_day_total . ".\n";
 | 
			
		||||
              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;
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ class AddMonthlyLeaveDays implements ShouldQueue
 | 
			
		|||
          $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;
 | 
			
		||||
          $newNote = "Cộng phép tháng " . $leaveDay->ld_day_total . ".\n";
 | 
			
		||||
          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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,12 +52,16 @@ class DeductLeaveDays implements ShouldQueue
 | 
			
		|||
                ->where('n_reason', 'ONLEAVE')
 | 
			
		||||
                ->sum('categories.c_value');
 | 
			
		||||
 | 
			
		||||
            if($usedOnleaveDaysTotal) {
 | 
			
		||||
                $existingData->ld_additional_day = $existingData->ld_additional_day >= $usedOnleaveDaysTotal ? $usedOnleaveDaysTotal : $existingData->ld_additional_day;
 | 
			
		||||
            if ($usedOnleaveDaysTotal) {
 | 
			
		||||
                if ($existingData->ld_additional_day > $usedOnleaveDaysTotal) {
 | 
			
		||||
                    $ld_note = "Trừ " . $existingData->ld_additional_day - $usedOnleaveDaysTotal . " ngày phép tồn năm trước. \n";
 | 
			
		||||
                    $existingData->ld_note = $existingData->ld_note . "\n" . $ld_note;
 | 
			
		||||
                    $existingData->ld_additional_day = $usedOnleaveDaysTotal;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $existingData->ld_additional_day = 0;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            $existingData->save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,7 +77,10 @@ class InitializeLeaveDays implements ShouldQueue
 | 
			
		|||
                        $ld_additional_day = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                $ld_note = 'Cộng dồn ngày phép năm cũ';
 | 
			
		||||
 | 
			
		||||
                if ($ld_additional_day > 0) {
 | 
			
		||||
                    $ld_note = "Cộng " . $ld_additional_day . " ngày phép tồn năm trước. \n";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Tạo dữ liệu cho năm hiện tại
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
use Carbon\Carbon;
 | 
			
		||||
 | 
			
		||||
require_once __DIR__ . "/../vendor/autoload.php";
 | 
			
		||||
$app = include_once __DIR__ . '/../bootstrap/app.php';
 | 
			
		||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
 | 
			
		||||
$kernel->bootstrap();
 | 
			
		||||
 | 
			
		||||
// Cập nhật lại data cho tới tháng hiện tại
 | 
			
		||||
$currentMonth = Carbon::now()->month;
 | 
			
		||||
$tmpClass = $app->make('Modules\Admin\app\Http\Controllers\TicketController');
 | 
			
		||||
$tmpClass->updateOldData($currentMonth, 2025); // Params: month, year
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +1,18 @@
 | 
			
		|||
import {
 | 
			
		||||
  getLeaveManagement,
 | 
			
		||||
  updateNoteLeave,
 | 
			
		||||
  exportLeaveManagement,
 | 
			
		||||
  // getListMaster,
 | 
			
		||||
} from '@/api/Admin'
 | 
			
		||||
import { update } from '@/rtk/helpers/CRUD'
 | 
			
		||||
import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
			
		||||
import { useEffect, useState } from 'react'
 | 
			
		||||
import moment from 'moment'
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  Avatar,
 | 
			
		||||
  Badge,
 | 
			
		||||
  Box,
 | 
			
		||||
  Button,
 | 
			
		||||
  Drawer,
 | 
			
		||||
  Flex,
 | 
			
		||||
  Group,
 | 
			
		||||
  HoverCard,
 | 
			
		||||
  Menu,
 | 
			
		||||
  Select,
 | 
			
		||||
  Stack,
 | 
			
		||||
  Table,
 | 
			
		||||
  Text,
 | 
			
		||||
  Textarea,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,13 +21,17 @@ import {
 | 
			
		|||
} from '@mantine/core'
 | 
			
		||||
import { useDisclosure } from '@mantine/hooks'
 | 
			
		||||
import { notifications } from '@mantine/notifications'
 | 
			
		||||
import moment from 'moment'
 | 
			
		||||
import { useEffect, useState } from 'react'
 | 
			
		||||
 | 
			
		||||
import { IconEdit, IconFileExcel } from '@tabler/icons-react'
 | 
			
		||||
 | 
			
		||||
import classes from './LeaveManagement.module.css'
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  getLeaveManagement,
 | 
			
		||||
  updateNoteLeave,
 | 
			
		||||
  exportLeaveManagement,
 | 
			
		||||
} from '@/api/Admin'
 | 
			
		||||
import { update } from '@/rtk/helpers/CRUD'
 | 
			
		||||
import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
			
		||||
 | 
			
		||||
interface User {
 | 
			
		||||
  id: number
 | 
			
		||||
  name: string
 | 
			
		||||
| 
						 | 
				
			
			@ -265,8 +268,6 @@ const LeaveManagement = () => {
 | 
			
		|||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // console.log(customAddNotes, 'customAddNotes')
 | 
			
		||||
 | 
			
		||||
  const getDetailLeaveDay = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
 | 
			
		||||
    type MonthlyLeaveDaysAcc = {
 | 
			
		||||
      [key: string]: { n_user_id: number; month: number; leave_days: number }
 | 
			
		||||
| 
						 | 
				
			
			@ -290,29 +291,87 @@ const LeaveManagement = () => {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  const showAllOff = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
 | 
			
		||||
    let lastmonth = 0
 | 
			
		||||
    return monthlyLeaveDays.map((itemDay, indexDay) => {
 | 
			
		||||
      const isNewMonth = lastmonth !== itemDay.month
 | 
			
		||||
      if (isNewMonth) {
 | 
			
		||||
        lastmonth = itemDay.month
 | 
			
		||||
    return monthInYear.map((d, i) => {
 | 
			
		||||
      let totalOnLeaveMonth = 0
 | 
			
		||||
      let totalLeaveWithoutPayMonth = 0
 | 
			
		||||
 | 
			
		||||
      monthlyLeaveDays
 | 
			
		||||
        .filter((item) => item.month === d.value)
 | 
			
		||||
        .map((item) => {
 | 
			
		||||
          if (item.reason_code === 'ONLEAVE') {
 | 
			
		||||
            totalOnLeaveMonth += Number(item.leave_days)
 | 
			
		||||
          } else {
 | 
			
		||||
            totalLeaveWithoutPayMonth += Number(item.leave_days)
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      if (totalOnLeaveMonth === 0 && totalLeaveWithoutPayMonth === 0) {
 | 
			
		||||
        return ''
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
        <div key={indexDay}>
 | 
			
		||||
          {isNewMonth && <p>Month {lastmonth}</p>}
 | 
			
		||||
          <p style={{ paddingLeft: '20px' }}>
 | 
			
		||||
            - {itemDay.reason_name} ({itemDay.time_type_name}) {itemDay.day}/
 | 
			
		||||
            {itemDay.month}
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <Box key={i} px="xs" my="xs">
 | 
			
		||||
          <Group gap="xs">
 | 
			
		||||
            {totalOnLeaveMonth > 0 && (
 | 
			
		||||
              <Badge color="teal" variant="light">
 | 
			
		||||
                {totalOnLeaveMonth} có phép
 | 
			
		||||
              </Badge>
 | 
			
		||||
            )}
 | 
			
		||||
            {totalLeaveWithoutPayMonth > 0 && (
 | 
			
		||||
              <Badge color="red" variant="light">
 | 
			
		||||
                {totalLeaveWithoutPayMonth} không phép
 | 
			
		||||
              </Badge>
 | 
			
		||||
            )}
 | 
			
		||||
            <Text size="xs" color="dimmed">
 | 
			
		||||
              ({d.value}/2025)
 | 
			
		||||
            </Text>
 | 
			
		||||
          </Group>
 | 
			
		||||
        </Box>
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const showAllTotal = (
 | 
			
		||||
    ld_day_total: number,
 | 
			
		||||
    ld_additional_day: number,
 | 
			
		||||
    ld_special_leave_day: number,
 | 
			
		||||
  ): JSX.Element => {
 | 
			
		||||
    const showItem = (label: string, value: number, color: string = 'gray') => {
 | 
			
		||||
      if (value === 0) return null
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
        <Group justify="space-between" gap="xs">
 | 
			
		||||
          <Text size="sm" c="dimmed">
 | 
			
		||||
            {label}
 | 
			
		||||
          </Text>
 | 
			
		||||
          <Text size="sm" fw={500} c={color}>
 | 
			
		||||
            {value}
 | 
			
		||||
          </Text>
 | 
			
		||||
        </Group>
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <Box p="sm">
 | 
			
		||||
        <Stack gap={4}>
 | 
			
		||||
          {showItem(
 | 
			
		||||
            'Tổng phép hiện có:',
 | 
			
		||||
            ld_day_total + ld_additional_day + ld_special_leave_day,
 | 
			
		||||
            'white',
 | 
			
		||||
          )}
 | 
			
		||||
          {showItem('+ Phép được cấp năm nay:', ld_day_total, 'teal')}
 | 
			
		||||
          {showItem('+ Phép tồn năm trước:', ld_additional_day, 'violet')}
 | 
			
		||||
          {showItem('+ Phép đặc biệt:', ld_special_leave_day, 'orange')}
 | 
			
		||||
        </Stack>
 | 
			
		||||
      </Box>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const handleExport = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      const timestamp = moment().format('DDMMYYYY_HHmmss')
 | 
			
		||||
      const fileName = `LeaveManagement_${date.year}_${timestamp}.xlsx`
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      await exportFile(
 | 
			
		||||
        exportLeaveManagement,
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -335,6 +394,7 @@ const LeaveManagement = () => {
 | 
			
		|||
      <div className={classes.title}>
 | 
			
		||||
        <h3>Leave Management</h3>
 | 
			
		||||
      </div>
 | 
			
		||||
      {/* Update Leave Day */}
 | 
			
		||||
      <Drawer
 | 
			
		||||
        opened={opened1}
 | 
			
		||||
        onClose={close1}
 | 
			
		||||
| 
						 | 
				
			
			@ -429,6 +489,7 @@ const LeaveManagement = () => {
 | 
			
		|||
          onChange={(e) => {
 | 
			
		||||
            setCustomAddNotes({ ...customAddNotes, note: e.target.value })
 | 
			
		||||
          }}
 | 
			
		||||
          rows={10}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <Button
 | 
			
		||||
| 
						 | 
				
			
			@ -464,65 +525,54 @@ const LeaveManagement = () => {
 | 
			
		|||
          Save
 | 
			
		||||
        </Button>
 | 
			
		||||
      </Drawer>
 | 
			
		||||
      <Box display={'flex'}>
 | 
			
		||||
        <Box style={{ display: 'flex', flexFlow: 'column' }} w={'30%'}>
 | 
			
		||||
          <Box w="100%" display={'flex'}>
 | 
			
		||||
            <Select
 | 
			
		||||
              w="50%"
 | 
			
		||||
              value={date.year}
 | 
			
		||||
              size="xs"
 | 
			
		||||
              ml={'sm'}
 | 
			
		||||
              label="Year"
 | 
			
		||||
              data={Array.from({ length: 10 }, (_, index) => {
 | 
			
		||||
                return {
 | 
			
		||||
                  value: (
 | 
			
		||||
                    parseInt(moment(Date.now()).format('YYYY')) -
 | 
			
		||||
                    3 +
 | 
			
		||||
                    index
 | 
			
		||||
                  ).toString(),
 | 
			
		||||
                  label: (
 | 
			
		||||
                    parseInt(moment(Date.now()).format('YYYY')) -
 | 
			
		||||
                    3 +
 | 
			
		||||
                    index
 | 
			
		||||
                  ).toString(),
 | 
			
		||||
                  disabled:
 | 
			
		||||
                    parseInt(moment(Date.now()).format('YYYY')) - 3 + index >
 | 
			
		||||
                    parseInt(moment(Date.now()).format('YYYY')),
 | 
			
		||||
                }
 | 
			
		||||
              })}
 | 
			
		||||
              onChange={(e) => {
 | 
			
		||||
                setDate({ ...date, year: e! })
 | 
			
		||||
              }}
 | 
			
		||||
            ></Select>
 | 
			
		||||
          </Box>
 | 
			
		||||
        </Box>
 | 
			
		||||
        <Box
 | 
			
		||||
          w="70%"
 | 
			
		||||
          pl={200}
 | 
			
		||||
          style={{
 | 
			
		||||
            display: 'flex',
 | 
			
		||||
            justifyContent: 'end',
 | 
			
		||||
 | 
			
		||||
      {/* Filter Year, Export Btn */}
 | 
			
		||||
      <Flex justify="space-between" align="flex-end">
 | 
			
		||||
        <Select
 | 
			
		||||
          value={date.year}
 | 
			
		||||
          size="xs"
 | 
			
		||||
          label="Year"
 | 
			
		||||
          data={Array.from({ length: 10 }, (_, index) => {
 | 
			
		||||
            return {
 | 
			
		||||
              value: (
 | 
			
		||||
                parseInt(moment(Date.now()).format('YYYY')) -
 | 
			
		||||
                3 +
 | 
			
		||||
                index
 | 
			
		||||
              ).toString(),
 | 
			
		||||
              label: (
 | 
			
		||||
                parseInt(moment(Date.now()).format('YYYY')) -
 | 
			
		||||
                3 +
 | 
			
		||||
                index
 | 
			
		||||
              ).toString(),
 | 
			
		||||
              disabled:
 | 
			
		||||
                parseInt(moment(Date.now()).format('YYYY')) - 3 + index >
 | 
			
		||||
                parseInt(moment(Date.now()).format('YYYY')),
 | 
			
		||||
            }
 | 
			
		||||
          })}
 | 
			
		||||
          onChange={(e) => {
 | 
			
		||||
            setDate({ ...date, year: e! })
 | 
			
		||||
          }}
 | 
			
		||||
          w={200}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <Button
 | 
			
		||||
          size="xs"
 | 
			
		||||
          onClick={handleExport}
 | 
			
		||||
          leftSection={<IconFileExcel size={16} />}
 | 
			
		||||
        >
 | 
			
		||||
          <Box display={'flex'} style={{ alignItems: 'end' }}>
 | 
			
		||||
            <Button
 | 
			
		||||
              size="xs"
 | 
			
		||||
              ml={'sm'}
 | 
			
		||||
              onClick={handleExport}
 | 
			
		||||
              leftSection={<IconFileExcel size={16} />}
 | 
			
		||||
            >
 | 
			
		||||
              Export Excel
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Box>
 | 
			
		||||
        </Box>
 | 
			
		||||
      </Box>
 | 
			
		||||
      <Box>
 | 
			
		||||
          Export Excel
 | 
			
		||||
        </Button>
 | 
			
		||||
      </Flex>
 | 
			
		||||
 | 
			
		||||
      {/* Leave Day Table */}
 | 
			
		||||
      <Box style={{ overflowX: 'auto' }}>
 | 
			
		||||
        <Table
 | 
			
		||||
          striped
 | 
			
		||||
          highlightOnHover
 | 
			
		||||
          withTableBorder
 | 
			
		||||
          withColumnBorders
 | 
			
		||||
          mt={'md'}
 | 
			
		||||
          miw={1580}
 | 
			
		||||
        >
 | 
			
		||||
          <Table.Thead>
 | 
			
		||||
            <Table.Tr bg={'#228be66b'}>
 | 
			
		||||
| 
						 | 
				
			
			@ -539,7 +589,9 @@ const LeaveManagement = () => {
 | 
			
		|||
                        style={{
 | 
			
		||||
                          cursor: 'pointer',
 | 
			
		||||
                          width: '40px',
 | 
			
		||||
                          backgroundColor: isCurrentMonth ? '#ffe066' : undefined,
 | 
			
		||||
                          backgroundColor: isCurrentMonth
 | 
			
		||||
                            ? '#F2E891'
 | 
			
		||||
                            : undefined,
 | 
			
		||||
                          color: isCurrentMonth ? '#000' : undefined,
 | 
			
		||||
                          fontWeight: isCurrentMonth ? 'bold' : undefined,
 | 
			
		||||
                        }}
 | 
			
		||||
| 
						 | 
				
			
			@ -550,7 +602,7 @@ const LeaveManagement = () => {
 | 
			
		|||
                  </Menu>
 | 
			
		||||
                )
 | 
			
		||||
              })}
 | 
			
		||||
              <Table.Th ta={'center'} style={{ width: '150px' }}>
 | 
			
		||||
              <Table.Th ta={'center'} style={{ width: '80px' }}>
 | 
			
		||||
                Total
 | 
			
		||||
              </Table.Th>
 | 
			
		||||
              <Table.Th ta={'center'} style={{ width: '130px' }}>
 | 
			
		||||
| 
						 | 
				
			
			@ -588,11 +640,11 @@ const LeaveManagement = () => {
 | 
			
		|||
                          mr={'md'}
 | 
			
		||||
                          src={
 | 
			
		||||
                            import.meta.env.VITE_BACKEND_URL.includes('local')
 | 
			
		||||
                          ? import.meta.env.VITE_BACKEND_URL +
 | 
			
		||||
                            'storage/' +
 | 
			
		||||
                           user.user.avatar
 | 
			
		||||
                          : import.meta.env.VITE_BACKEND_URL +
 | 
			
		||||
                            'image/storage/' +
 | 
			
		||||
                              ? import.meta.env.VITE_BACKEND_URL +
 | 
			
		||||
                                'storage/' +
 | 
			
		||||
                                user.user.avatar
 | 
			
		||||
                              : import.meta.env.VITE_BACKEND_URL +
 | 
			
		||||
                                'image/storage/' +
 | 
			
		||||
                                user.user.avatar
 | 
			
		||||
                          }
 | 
			
		||||
                        />
 | 
			
		||||
| 
						 | 
				
			
			@ -601,9 +653,8 @@ const LeaveManagement = () => {
 | 
			
		|||
                    </Tooltip>
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
 | 
			
		||||
                  {/* On leave per month */}
 | 
			
		||||
                  {monthInYear.map((d, i) => {
 | 
			
		||||
                    // const isCurrentMonth =
 | 
			
		||||
                    //   Number(date.year) === currentYear && d.value === currentMonth
 | 
			
		||||
                    let leaveDataByMonth = getDetailLeaveDay(
 | 
			
		||||
                      user.monthlyLeaveDays,
 | 
			
		||||
                    )
 | 
			
		||||
| 
						 | 
				
			
			@ -612,17 +663,43 @@ const LeaveManagement = () => {
 | 
			
		|||
                    let total = monthData ? monthData.leave_days : 0
 | 
			
		||||
                    totalDayOff = totalDayOff + total
 | 
			
		||||
 | 
			
		||||
                    let onleaveDaysInMonth: MonthlyLeaveDays[] = []
 | 
			
		||||
                    let nopayDaysInMonth: MonthlyLeaveDays[] = []
 | 
			
		||||
 | 
			
		||||
                    let totalOnLeaveMonth = 0
 | 
			
		||||
                    let totalLeaveWithoutPayMonth = 0
 | 
			
		||||
                    let usedAdditionalDay = 0
 | 
			
		||||
 | 
			
		||||
                    user.monthlyLeaveDays
 | 
			
		||||
                      .filter((item) => item.month === d.value)
 | 
			
		||||
                      .map((item) => {
 | 
			
		||||
                        if (item.reason_code === 'ONLEAVE') {
 | 
			
		||||
                          totalOnLeave = totalOnLeave + Number(item.leave_days)
 | 
			
		||||
                          totalOnLeaveMonth += Number(item.leave_days)
 | 
			
		||||
                          onleaveDaysInMonth.push(item)
 | 
			
		||||
                        } else {
 | 
			
		||||
                          totalLeaveWithoutPay =
 | 
			
		||||
                            totalLeaveWithoutPay + Number(item.leave_days)
 | 
			
		||||
                          totalLeaveWithoutPayMonth += Number(item.leave_days)
 | 
			
		||||
                          nopayDaysInMonth.push(item)
 | 
			
		||||
                        }
 | 
			
		||||
                      })
 | 
			
		||||
 | 
			
		||||
                    // Xử lý hiện thị phép tồn sử dụng
 | 
			
		||||
                    let tmpTotalOnleave = totalOnLeave
 | 
			
		||||
                    totalOnLeave += totalOnLeaveMonth
 | 
			
		||||
 | 
			
		||||
                    if (d.value < 4) {
 | 
			
		||||
                      if (totalOnLeave < ld_additional_day) {
 | 
			
		||||
                        usedAdditionalDay = totalOnLeaveMonth
 | 
			
		||||
                        totalOnLeaveMonth = 0
 | 
			
		||||
                      } else {
 | 
			
		||||
                        usedAdditionalDay = ld_additional_day - tmpTotalOnleave
 | 
			
		||||
                        if (usedAdditionalDay >= 0) {
 | 
			
		||||
                          totalOnLeaveMonth -= usedAdditionalDay
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    totalLeaveWithoutPay += totalLeaveWithoutPayMonth
 | 
			
		||||
 | 
			
		||||
                    return (
 | 
			
		||||
                      <Table.Td
 | 
			
		||||
                        bg={total > 0 ? '#ffb5b5' : ''}
 | 
			
		||||
| 
						 | 
				
			
			@ -631,17 +708,57 @@ const LeaveManagement = () => {
 | 
			
		|||
                      >
 | 
			
		||||
                        <Tooltip
 | 
			
		||||
                          multiline
 | 
			
		||||
                          label={user.monthlyLeaveDays
 | 
			
		||||
                            .filter((item) => item.month === d.value)
 | 
			
		||||
                            .map((itemDay, indexDay) => {
 | 
			
		||||
                              return (
 | 
			
		||||
                                <p key={indexDay}>
 | 
			
		||||
                                  - {itemDay.reason_name} (
 | 
			
		||||
                                  {itemDay.time_type_name}) {itemDay.day}/
 | 
			
		||||
                                  {itemDay.month}
 | 
			
		||||
                                </p>
 | 
			
		||||
                              )
 | 
			
		||||
                            })}
 | 
			
		||||
                          label={
 | 
			
		||||
                            <Box p={4}>
 | 
			
		||||
                              {usedAdditionalDay > 0 && (
 | 
			
		||||
                                <Text fw={500} c="violet" size="sm" mb={4}>
 | 
			
		||||
                                  Phép tồn: {usedAdditionalDay}
 | 
			
		||||
                                </Text>
 | 
			
		||||
                              )}
 | 
			
		||||
 | 
			
		||||
                              {totalOnLeaveMonth > 0 && (
 | 
			
		||||
                                <Box>
 | 
			
		||||
                                  <Text fw={500} c="teal" size="sm" mb={4}>
 | 
			
		||||
                                    Có phép: {totalOnLeaveMonth}
 | 
			
		||||
                                  </Text>
 | 
			
		||||
                                </Box>
 | 
			
		||||
                              )}
 | 
			
		||||
 | 
			
		||||
                              {totalOnLeaveMonth > 0 ||
 | 
			
		||||
                              usedAdditionalDay > 0 ? (
 | 
			
		||||
                                <Stack gap={2} pl="md">
 | 
			
		||||
                                  {onleaveDaysInMonth?.map(
 | 
			
		||||
                                    (itemDay: any, indexDay: number) => (
 | 
			
		||||
                                      <Text size="xs" key={indexDay}>
 | 
			
		||||
                                        • {itemDay.time_type_name} (
 | 
			
		||||
                                        {itemDay.day}/{itemDay.month})
 | 
			
		||||
                                      </Text>
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  )}
 | 
			
		||||
                                </Stack>
 | 
			
		||||
                              ) : (
 | 
			
		||||
                                ''
 | 
			
		||||
                              )}
 | 
			
		||||
 | 
			
		||||
                              {totalLeaveWithoutPayMonth > 0 && (
 | 
			
		||||
                                <Box mt={6}>
 | 
			
		||||
                                  <Text fw={500} c="red" size="sm" mb={4}>
 | 
			
		||||
                                    Không phép: {totalLeaveWithoutPayMonth}
 | 
			
		||||
                                  </Text>
 | 
			
		||||
                                  <Stack gap={2} pl="md">
 | 
			
		||||
                                    {nopayDaysInMonth?.map(
 | 
			
		||||
                                      (itemDay: any, indexDay: number) => (
 | 
			
		||||
                                        <Text size="xs" key={indexDay}>
 | 
			
		||||
                                          • {itemDay.time_type_name} (
 | 
			
		||||
                                          {itemDay.day}/{itemDay.month})
 | 
			
		||||
                                        </Text>
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    )}
 | 
			
		||||
                                  </Stack>
 | 
			
		||||
                                </Box>
 | 
			
		||||
                              )}
 | 
			
		||||
                            </Box>
 | 
			
		||||
                          }
 | 
			
		||||
                        >
 | 
			
		||||
                          <p>{total === 0 ? '' : total}</p>
 | 
			
		||||
                        </Tooltip>
 | 
			
		||||
| 
						 | 
				
			
			@ -650,128 +767,68 @@ const LeaveManagement = () => {
 | 
			
		|||
                  })}
 | 
			
		||||
 | 
			
		||||
                  {/* Total */}
 | 
			
		||||
                  <Table.Td
 | 
			
		||||
                    ta={'center'}
 | 
			
		||||
                    // bg={totalDayLeave > 0 ? '#92e6f2' : ''}
 | 
			
		||||
                  >
 | 
			
		||||
                    <p
 | 
			
		||||
                      style={{
 | 
			
		||||
                        // backgroundColor: '#c3ffc3',
 | 
			
		||||
                        display: ld_day_total > 0 ? 'block' : 'none',
 | 
			
		||||
                      }}
 | 
			
		||||
                  <Table.Td ta={'center'} bg="#92e6f2">
 | 
			
		||||
                    <Tooltip
 | 
			
		||||
                      multiline
 | 
			
		||||
                      label={showAllTotal(
 | 
			
		||||
                        ld_day_total,
 | 
			
		||||
                        ld_additional_day,
 | 
			
		||||
                        ld_special_leave_day,
 | 
			
		||||
                      )}
 | 
			
		||||
                    >
 | 
			
		||||
                      {'Phép năm:'}{' '}
 | 
			
		||||
                      <span
 | 
			
		||||
                        style={{
 | 
			
		||||
                          backgroundColor: '#c3ffc3',
 | 
			
		||||
                          padding: '5px',
 | 
			
		||||
                          borderRadius: '5px',
 | 
			
		||||
                          fontWeight: 'bold',
 | 
			
		||||
                          color: 'black',
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {ld_day_total}
 | 
			
		||||
                      </span>
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <p
 | 
			
		||||
                      style={{
 | 
			
		||||
                        // backgroundColor: '#92e6f2',
 | 
			
		||||
                        display: ld_additional_day > 0 ? 'block' : 'none',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {'Phép năm cũ:'}{' '}
 | 
			
		||||
                      <span
 | 
			
		||||
                        style={{
 | 
			
		||||
                          backgroundColor: '#92e6f2',
 | 
			
		||||
                          padding: '5px',
 | 
			
		||||
                          borderRadius: '5px',
 | 
			
		||||
                          fontWeight: 'bold',
 | 
			
		||||
                          color: 'black',
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {ld_additional_day}
 | 
			
		||||
                      </span>
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <p
 | 
			
		||||
                      style={{
 | 
			
		||||
                        display: ld_special_leave_day > 0 ? 'block' : 'none',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {'Phép đặc biệt:'}{' '}
 | 
			
		||||
                      <span
 | 
			
		||||
                        style={{
 | 
			
		||||
                          backgroundColor: '#b5cafb',
 | 
			
		||||
                          padding: '5px',
 | 
			
		||||
                          borderRadius: '5px',
 | 
			
		||||
                          fontWeight: 'bold',
 | 
			
		||||
                          color: 'black',
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {ld_special_leave_day}
 | 
			
		||||
                      </span>
 | 
			
		||||
                    </p>
 | 
			
		||||
                      <Text size="sm">{totalDayLeave}</Text>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
 | 
			
		||||
                  {/* Off */}
 | 
			
		||||
                  <Table.Td ta={'center'}>
 | 
			
		||||
                  <Table.Td>
 | 
			
		||||
                    {totalDayOff > 0 ? (
 | 
			
		||||
                      <Tooltip
 | 
			
		||||
                        multiline
 | 
			
		||||
                        label={showAllOff(user.monthlyLeaveDays)}
 | 
			
		||||
                      >
 | 
			
		||||
                        <div>
 | 
			
		||||
                          <p
 | 
			
		||||
                          // style={{ backgroundColor: '#c3ffc3' }}
 | 
			
		||||
                          >
 | 
			
		||||
                            {'Nghỉ phép:'}{' '}
 | 
			
		||||
                            <span
 | 
			
		||||
                              style={{
 | 
			
		||||
                                fontWeight: 'bold',
 | 
			
		||||
                                color: 'black',
 | 
			
		||||
                                backgroundColor: '#c3ffc3',
 | 
			
		||||
                                padding: '5px',
 | 
			
		||||
                                borderRadius: '5px',
 | 
			
		||||
                              }}
 | 
			
		||||
                        <Box>
 | 
			
		||||
                          <Flex justify="space-between" mb="xs" align="center">
 | 
			
		||||
                            <Text size="sm">Có phép: </Text>
 | 
			
		||||
                            <Text
 | 
			
		||||
                              size="sm"
 | 
			
		||||
                              bg="#c3ffc3"
 | 
			
		||||
                              fw="bold"
 | 
			
		||||
                              p={5}
 | 
			
		||||
                              style={{ borderRadius: 5 }}
 | 
			
		||||
                            >
 | 
			
		||||
                              {totalOnLeave}
 | 
			
		||||
                            </span>
 | 
			
		||||
                          </p>
 | 
			
		||||
                          <p
 | 
			
		||||
                          //  style={{ backgroundColor: '#ffb5b5' }}
 | 
			
		||||
                          >
 | 
			
		||||
                            {'Không phép:'}{' '}
 | 
			
		||||
                            <span
 | 
			
		||||
                              style={{
 | 
			
		||||
                                fontWeight: 'bold',
 | 
			
		||||
                                color: 'black',
 | 
			
		||||
                                backgroundColor: '#ffb5b5',
 | 
			
		||||
                                padding: '5px',
 | 
			
		||||
                                borderRadius: '5px',
 | 
			
		||||
                              }}
 | 
			
		||||
                            </Text>
 | 
			
		||||
                          </Flex>
 | 
			
		||||
 | 
			
		||||
                          <Flex justify="space-between" align="center">
 | 
			
		||||
                            <Text size="sm">Không phép: </Text>
 | 
			
		||||
                            <Text
 | 
			
		||||
                              size="sm"
 | 
			
		||||
                              bg="#ffb5b5"
 | 
			
		||||
                              fw="bold"
 | 
			
		||||
                              p={5}
 | 
			
		||||
                              style={{ borderRadius: 5 }}
 | 
			
		||||
                            >
 | 
			
		||||
                              {totalLeaveWithoutPay}
 | 
			
		||||
                            </span>
 | 
			
		||||
                          </p>
 | 
			
		||||
                        </div>
 | 
			
		||||
                            </Text>
 | 
			
		||||
                          </Flex>
 | 
			
		||||
                        </Box>
 | 
			
		||||
                      </Tooltip>
 | 
			
		||||
                    ) : (
 | 
			
		||||
                      <></>
 | 
			
		||||
                      ''
 | 
			
		||||
                    )}
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
 | 
			
		||||
                  {/* Remaining */}
 | 
			
		||||
                  <Table.Td
 | 
			
		||||
                    ta={'center'}
 | 
			
		||||
                    bg={
 | 
			
		||||
                      totalDayLeave - totalOnLeave == 0
 | 
			
		||||
                        ? ''
 | 
			
		||||
                        : totalDayLeave - totalOnLeave > 0
 | 
			
		||||
                        ? '#c3ffc3'
 | 
			
		||||
                        : '#ffb5b5'
 | 
			
		||||
                    }
 | 
			
		||||
                    bg={totalDayLeave - totalOnLeave > 0 ? '#b5cafb' : ''}
 | 
			
		||||
                  >
 | 
			
		||||
                    {totalDayLeave - totalOnLeave}
 | 
			
		||||
                    <Text size="sm">{totalDayLeave - totalOnLeave}</Text>
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
 | 
			
		||||
                  {/* Note */}
 | 
			
		||||
                  <Table.Td>
 | 
			
		||||
                    <Box
 | 
			
		||||
                      style={{
 | 
			
		||||
| 
						 | 
				
			
			@ -800,6 +857,8 @@ const LeaveManagement = () => {
 | 
			
		|||
                      </HoverCard>
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
 | 
			
		||||
                  {/* Action */}
 | 
			
		||||
                  <Table.Td ta={'center'}>
 | 
			
		||||
                    <IconEdit
 | 
			
		||||
                      color="green"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue