602 lines
17 KiB
TypeScript
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
|