ManagementSystem/FRONTEND/src/pages/LeaveManagement/LeaveManagement.tsx

602 lines
17 KiB
TypeScript

import { getLeaveManagement, updateNoteLeave } from '@/api/Admin'
import { update } from '@/rtk/helpers/CRUD'
import { get } from '@/rtk/helpers/apiService'
import {
Avatar,
Box,
Button,
Drawer,
HoverCard,
Menu,
Select,
Table,
Text,
Textarea,
TextInput,
Tooltip,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { notifications } from '@mantine/notifications'
import moment from 'moment'
import { useEffect, useState } from 'react'
import { IconEdit } from '@tabler/icons-react'
import classes from './LeaveManagement.module.css'
interface User {
id: number
name: string
email: string
email_verified_at: string | null
permission: string
remember_token: string | null
avatar: string
created_at: string | null
updated_at: string | null
}
interface LeaveDay {
id: number
ld_user_id: number
ld_year: number
ld_day: number
ld_date_additional: number
ld_note: string
created_at: string | null
updated_at: string | null
}
interface MonthlyLeaveDays {
day: number
leave_days: number
month: number
n_user_id: number
reason_name: string
time_type_name: string
}
interface UserData {
user: User
leaveDay: LeaveDay
monthlyLeaveDays: MonthlyLeaveDays[]
}
const LeaveManagement = () => {
const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
const [disableBtn, setDisableBtn] = useState(false)
const monthInYear = getMonthNames()
const [customAddNotes, setCustomAddNotes] = useState<{
id: number
user: {
id: number
name: string
}
note: string
totalLeave: string
dayAdditional: string
}>({
id: 0,
user: {
id: 0,
name: '',
},
note: '',
totalLeave: '',
dayAdditional: '',
})
const [data, setData] = useState<UserData[]>([])
const [date, setDate] = useState({
year: new Date().getFullYear().toString(),
})
const getLeaveList = async () => {
try {
const res = await get(getLeaveManagement, {
year: date.year,
})
if (res.status) {
setData(
res.data.filter((u: UserData) => u.user.permission.includes('staff')),
)
}
} catch (error: any) {
console.log(error)
notifications.show({
title: 'Error',
message: error.message ?? error,
color: 'red',
})
}
}
useEffect(() => {
getLeaveList()
}, [date])
const updateInfoNote = async (
id: number,
users: {
id: number
name: string
},
totalLeave: string,
dayAdditional: string,
note: string,
) => {
try {
await update(
updateNoteLeave,
{
id: id,
users: users,
totalLeave: totalLeave,
dayAdditional: dayAdditional,
note: note,
},
getLeaveList,
)
setDisableBtn(false)
} catch (error) {
console.log(error)
}
}
function getMonthNames() {
const monthNames = [
{
value: 1,
name: 'January',
},
{
value: 2,
name: 'February',
},
{
value: 3,
name: 'March',
},
{
value: 4,
name: 'April',
},
{
value: 5,
name: 'May',
},
{
value: 6,
name: 'June',
},
{
value: 7,
name: 'July',
},
{
value: 8,
name: 'August',
},
{
value: 9,
name: 'September',
},
{
value: 10,
name: 'October',
},
{
value: 11,
name: 'November',
},
{
value: 12,
name: 'December',
},
]
return monthNames.map((month) => {
return {
value: month.value,
name: month.name.substring(0, 3),
}
})
}
// console.log(customAddNotes, 'customAddNotes')
const getDetailLeaveDay = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
type MonthlyLeaveDaysAcc = {
[key: string]: { n_user_id: number; month: number; leave_days: number }
}
const summedLeaveDaysByUserAndMonth = monthlyLeaveDays.reduce(
(acc: MonthlyLeaveDaysAcc, record) => {
const { n_user_id, month, leave_days } = record
const key = `${month}`
if (!acc[key]) {
acc[key] = { n_user_id, month, leave_days: 0 }
}
acc[key].leave_days += Number(leave_days)
return acc
},
{},
)
return summedLeaveDaysByUserAndMonth
}
const showAllOff = (monthlyLeaveDays: MonthlyLeaveDays[]) => {
let lastmonth = 0
return monthlyLeaveDays.map((itemDay, indexDay) => {
const isNewMonth = lastmonth !== itemDay.month
if (isNewMonth) {
lastmonth = itemDay.month
}
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>
)
})
}
return (
<div>
<div className={classes.title}>
<h3>
Leave Management
</h3>
</div>
<Drawer
opened={opened1}
onClose={close1}
position="right"
title={<strong>Update Day Leave</strong>}
>
<TextInput
mb={'md'}
value={customAddNotes.totalLeave}
onChange={(e) => {
const value = e.target.value
if (value) {
const floatValue = parseFloat(value)
if (
/^\d*\.?\d?$/.test(value) &&
floatValue >= 0 &&
floatValue <= 20
) {
setCustomAddNotes({
...customAddNotes,
totalLeave: value,
})
}
} else {
setCustomAddNotes({
...customAddNotes,
totalLeave: value,
})
}
}}
label={'Total Leave'}
placeholder="Input placeholder"
/>
<TextInput
mb={'md'}
value={customAddNotes.dayAdditional}
onChange={(e) => {
const value = e.target.value
if (value) {
const floatValue = parseFloat(value)
if (
/^\d*\.?\d?$/.test(value) &&
floatValue >= 0 &&
floatValue <= 20
) {
setCustomAddNotes({
...customAddNotes,
dayAdditional: value,
})
}
} else {
setCustomAddNotes({
...customAddNotes,
dayAdditional: '',
})
}
}}
label={'Day additional leave'}
placeholder="Input placeholder"
/>
<Textarea
mb={'md'}
label="Note"
value={customAddNotes.note}
onChange={(e) => {
setCustomAddNotes({ ...customAddNotes, note: e.target.value })
}}
/>
<Button
onClick={() => {
setDisableBtn(true)
if (
customAddNotes.id === 0
// ||
// customAddNotes.totalLeave === '' ||
// customAddNotes.totalLeave === '0'
// customAddNotes.dayAdditional === 0 ||
// customAddNotes.note === ''
) {
notifications.show({
title: 'Error',
message: 'Input data required',
color: 'red',
})
setDisableBtn(false)
} else {
updateInfoNote(
customAddNotes.id,
customAddNotes.user,
customAddNotes.totalLeave,
customAddNotes.dayAdditional,
customAddNotes.note,
)
}
}}
disabled={disableBtn}
>
Save
</Button>
</Drawer>
<Box display={'flex'}>
<Box style={{ display: 'flex', flexFlow: 'column' }} w={'30%'}>
<Box w="100%" display={'flex'}>
<Select
w="50%"
value={date.year}
size="xs"
ml={'sm'}
label="Year"
data={Array.from({ length: 10 }, (_, index) => {
return {
value: (
parseInt(moment(Date.now()).format('YYYY')) -
3 +
index
).toString(),
label: (
parseInt(moment(Date.now()).format('YYYY')) -
3 +
index
).toString(),
disabled:
parseInt(moment(Date.now()).format('YYYY')) - 3 + index >
parseInt(moment(Date.now()).format('YYYY')),
}
})}
onChange={(e) => {
setDate({ ...date, year: e! })
}}
></Select>
</Box>
</Box>
<Box
w="70%"
pl={200}
style={{
display: 'flex',
// alignItems: 'end',
justifyContent: 'end',
}}
>
<Box display={'flex'} style={{ alignItems: 'end' }}>
{/* <Tooltip label="Save working days">
<Button
size="xs"
ml={'sm'}
onClick={() => {
//form add user new
}}
>
Add
</Button>
</Tooltip> */}
</Box>
</Box>
</Box>
<Box>
<Table
striped
highlightOnHover
withTableBorder
withColumnBorders
mt={'md'}
>
<Table.Thead>
<Table.Tr bg={'#228be66b'}>
<Table.Th ></Table.Th>
<Table.Th>User</Table.Th>
{monthInYear.map((d) => {
return (
<Menu width={200} shadow="md" key={d.value}>
<Menu.Target>
<Table.Th
ta={'center'}
style={{ cursor: 'pointer', width: '60px' }}
>
<span>{d.name}</span>
</Table.Th>
</Menu.Target>
</Menu>
)
})}
<Table.Th ta={'center'} style={{ width: '80px' }}>
Total
</Table.Th>
<Table.Th ta={'center'} style={{ width: '80px' }}>
Off
</Table.Th>
<Table.Th ta={'center'} style={{ width: '80px' }}>
Remaining
</Table.Th>
<Table.Th ta={'center'}>Notes</Table.Th>
<Table.Th ta={'center'} style={{ width: '50px' }}></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data.map((user, index) => {
let totalDayOff = 0
let totalDayLeave =
user.leaveDay.ld_day + user.leaveDay.ld_date_additional
let ld_note = user.leaveDay.ld_note
return (
<Table.Tr key={user.user.id} className={classes.tableTr}>
<Table.Td ta={'center'}>{index + 1}</Table.Td>
<Table.Td>
<Tooltip multiline label={user.user.name}>
<div style={{display:'flex', alignItems:'center'}}><Avatar size={'md'} 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/' +
user.user.avatar}/>{user.user.name}</div>
</Tooltip>
</Table.Td>
{monthInYear.map((d, i) => {
let leaveDataByMonth = getDetailLeaveDay(
user.monthlyLeaveDays,
)
const monthData = leaveDataByMonth[d.value]
let total = monthData ? monthData.leave_days : 0
totalDayOff = totalDayOff + total
return (
<Table.Td
bg={total > 0 ? '#ffb5b5' : ''}
key={i}
ta={'center'}
>
<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>
)
})}
>
<p>{total === 0 ? '' : total}</p>
</Tooltip>
</Table.Td>
)
})}
<Table.Td
ta={'center'}
bg={totalDayLeave > 0 ? '#92e6f2' : ''}
>
{totalDayLeave}
</Table.Td>
<Table.Td ta={'center'} bg={totalDayOff > 0 ? '#ffb5b5' : ''}>
{totalDayOff > 0 ? (
<Tooltip
multiline
label={showAllOff(user.monthlyLeaveDays)}
>
<p> {totalDayOff}</p>
</Tooltip>
) : (
<></>
)}
</Table.Td>
<Table.Td
ta={'center'}
bg={
totalDayLeave - totalDayOff == 0
? ''
: totalDayLeave - totalDayOff > 0
? '#c3ffc3'
: '#ffb5b5'
}
>
{totalDayLeave - totalDayOff}
</Table.Td>
<Table.Td>
<Box
style={{
display:
ld_note === '' || ld_note === null ? 'none' : 'block',
}}
>
<HoverCard width={280} shadow="md">
<HoverCard.Target>
<Text fz={'sm'}>
{ld_note !== null &&
ld_note !== '' &&
ld_note.length > 25
? ld_note.slice(0, 25) + '...'
: ld_note}
</Text>
</HoverCard.Target>
<HoverCard.Dropdown>
<Textarea size="sm" autosize>
{ld_note}
</Textarea>
</HoverCard.Dropdown>
</HoverCard>
</Box>
</Table.Td>
<Table.Td ta={'center'}>
<IconEdit
color="green"
width={20}
style={{ cursor: 'pointer' }}
onClick={() => {
let totalLeave =
user.leaveDay.ld_day == 0
? ''
: String(user.leaveDay.ld_day)
let dayAdditional =
user.leaveDay.ld_date_additional == 0
? ''
: String(user.leaveDay.ld_date_additional)
open1()
setCustomAddNotes({
...customAddNotes,
id: user.leaveDay.id,
note: ld_note,
totalLeave: totalLeave,
dayAdditional: dayAdditional,
user: {
id: user.user.id,
name: user.user.name,
},
})
}}
/>
</Table.Td>
</Table.Tr>
)
})}
</Table.Tbody>
</Table>
</Box>
</div>
)
}
export default LeaveManagement