import { FileBox } from '@/components/form-fields/AppWorkweekCheckbox/AppFileUploader';
import { FileInfo } from '@/components/form-fields/AppWorkweekCheckbox/AppFileUploader/types';
import Approvers from '@/components/sub-components/Approvers';
import StatusLabel, {
StatusLabelType,
} from '@/components/sub-components/StatusLabel';
import AppButton from '@/components/templates/AppButton';
import LabelValue from '@/components/templates/LabelValue';
import {
CACHE_KEY_EMPLOYEE_APPROVE_LEAVE,
LEAVE_DETAIL_ENDPOINT,
} from '@/constants/my-company/employee-directory';
import useGet from '@/hooks/useGet';
import usePut from '@/hooks/usePut';
import { Domain } from '@/services/api-client';
import useUserStore from '@/store/user.store';
import { fDate } from '@/utils/formatTime';
import { HorizontalRule } from '@mui/icons-material';
import { Box, Grid, Paper, Stack, Typography } from '@mui/material';
import { useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useMemo } from 'react';
import { toast } from 'react-toastify';
interface ILeaveView {
id: string;
setTitle: any;
isApprover?: Boolean;
setOpen: React.Dispatch<React.SetStateAction<any>>;
closeDrawer : any
onApprove:any;
}
interface ILeave {
leaveId: string;
dateFrom: string;
dateTo: string;
leaveTypeName: string;
workflowStatus: StatusLabelType;
workflowId: string;
requestId: string;
createdAt: string;
attachments: FileInfo[];
log: {
logId: string;
agentId: string;
agentName: string;
action: 'Approved' | 'Pending' | 'Not Assigned' | 'INPROCESS' | 'Initiated';
comments: string;
}[];
}
export default function LeaveView({ id, setTitle, isApprover,setOpen,closeDrawer }: ILeaveView) {
const { data, error, isLoading } = useGet<ILeave>(LEAVE_DETAIL_ENDPOINT(id), [
'leave_details' + id,
]);
if (data) {
setTitle('Request' + '' + '#' + '' + data.requestId);
}
const user = useUserStore((state) => state.user);
const queryClient = useQueryClient();
const leaveAction = usePut(
'/request/LeaveRequest/action',
['employee', 'leave', 'action'],
(data) => {
queryClient.invalidateQueries(['leave_details' + id]);
queryClient.invalidateQueries(CACHE_KEY_EMPLOYEE_APPROVE_LEAVE);
}
);
const workflowLogId = useMemo(() => {
return (data as any)?.log.find((l: any) => l.agentId == user?.Employee.id)
?.logId;
}, [data]);
const canApprove = useMemo(() => {
return (
(data as any)?.log.find((l: any) => l.agentId == user?.Employee.id)
?.action != 'Approved'
);
}, [data]);
const onAction = useCallback(
(isApproved: Boolean) => {
leaveAction.mutate(
{
workflowLogId: workflowLogId,
action: isApproved ? 'APPROVED' : 'REJECTED',
workflowId: (data as any)?.workflowId,
},
{
onSuccess: () => {
toast.success(
isApproved
? 'Leave approved successfully'
: 'Leave rejected successfully'
);
closeDrawer(false);
},
onError: () => {
toast.error('Failed to perform action');
},
}
);
if (data) {
setTitle('#' + (data as any).requestId);
}
},
[data]
);
return (
<Stack justifyContent='space-between' sx={{ height: '100%' }}>
<Box
sx={{
padding: '16px',
}}
>
<Typography
variant='body1'
sx={{ color: 'primary.black', fontWeight: 'fontWeightBold' }}
>
Leave Details
</Typography>
</Box>
<Box
sx={{
padding: '16px',
}}
>
<Grid container>
<Grid xs={6} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue label='Request Id' value={'#' + data?.requestId} />
</Grid>
<Grid xs={6} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue
label='Status'
value={<StatusLabel label={(data as any)?.workflowStatus} />}
/>
</Grid>
{/* <Grid xs={6} sx={{ padding: '0 16px 16px 0' }}>
<StatusLabel label={data?.workflowStatus} />
</Grid> */}
<Grid xs={6} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue
label='Request On'
value={fDate((data as any)?.createdAt.split('T')[0] || '')}
/>
</Grid>
<Grid xs={6} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue
label='Applied for'
value={(data as any)?.leaveTypeName}
/>
</Grid>
<Grid xs={12} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue
label='Leave on'
value={
fDate((data as any)?.dateFrom || '') +
' - ' +
fDate((data as any)?.dateTo || '')
}
/>
</Grid>
<Grid xs={12} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue label='Your Comment' value={''} />
</Grid>
{/* included this line by Priti */}
<Grid xs={12} sx={{ padding: '0 16px 16px 0' }}>
<LabelValue
label='Attachment'
value={
data &&
(data as any).attachments &&
(data as any).attachments.length > 0 ? (
(data as any).attachments.map((attachment: any) => (
<div
key={attachment.filename}
style={{
display: 'inline-block',
margin: '0 10px 10px 0',
}}
>
<span role='img' aria-label='attachment'>
????
</span>{' '}
<a
href={`${Domain}/uploads/${attachment.filename}`}
target='_blank'
rel='noopener noreferrer'
style={{
textDecoration: 'none',
color: 'your-desired-color',
cursor: 'pointer',
}}
>
{attachment.originalName}
</a>
</div>
))
) : (
<div
style={{
display: 'inline-block',
margin: '0 10px 10px 0',
}}
>
<span role='img' aria-label='attachment'>
????
</span>{' '}
<a
href='#'
style={{
textDecoration: 'none',
color: '#0067FB',
fontWeight: 'bold',
cursor: 'pointer',
}}
>
View Attachment
</a>
</div>
)
}
/>
</Grid>
</Grid>
<Box sx={{ padding: '16px 0' }}>
<hr style={{ opacity: 0.4 }}></hr>
</Box>
<Typography fontWeight={500}>Approvers</Typography>
<Stack gap={3} sx={{ marginTop: '24px' }}>
{data ? (
(data as any)?.log.map((log: any) => (
<Approvers
name={log.agentName}
label={log.action || 'NOT ASSIGNED'}
comment={log.comments}
/>
))
) : (
<p></p>
)}
</Stack>
<Stack gap={3} sx={{ marginTop: '24px' }}>
{(data as any)?.attachments?.map((att: any) => (
<FileBox
name={att.originalName}
downloadLink={`${Domain}/uploads/${att.filename}`}
isDeletable={false}
/>
))}
</Stack>
</Box>
{isApprover && (
<Paper square elevation={2} sx={{ padding: 2 }}>
<Stack
flexDirection='row'
alignItems='center'
justifyContent='flex-end'
gap={2}
>
<AppButton
type='submit'
variant='outlined'
onClick={() => onAction(false)}
disabled={isLoading}
>
Reject
</AppButton>
<AppButton
type='submit'
variant='contained'
onClick={() => onAction(true)}
disabled={!canApprove || isLoading}
>
Approve
</AppButton>
</Stack>
</Paper>
)}
</Stack>
);
}
here i used to declare onClosedrawer which closes the side drawer, but it won’t works , i’m passing the props correctly but till i made some mistake in onAction , here i provide also index please some one help me close the side drawer after approve button is clicked
import { Button, Grid, Stack, Typography } from '@mui/material';
import React, { useState, useEffect } from 'react';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import AppTextInput from '@/components/form-fields/AppTextInput';
import AppButton from '@/components/templates/AppButton';
import AppDataGrid from '@/components/templates/AppDataGrid';
import AppDrawer from '@/components/templates/AppDrawer';
import AppInfo from '@/components/templates/AppInfo';
import AppLink from '@/components/templates/AppLink';
import AppPaper from '@/components/templates/AppPaper';
import TableSkeleton from '@/components/templates/AppTable/TableSkeleton';
import LeaveForm from './LeaveForm';
import {
CACHE_KEY_EMPLOYEE_APPROVE_LEAVE,
CACHE_KEY_EMPLOYEE_LEAVE,
EMPLOYEE_LEAVE_ENDPOINT,
EMPLOYEE_LEAVE_PENDING_WITH_ENDPOINT,
} from '@/constants/my-company/employee-directory';
import useGetAll from '@/hooks/useGetAll';
import { Icon } from '@iconify/react';
import { useNavigate, useParams } from 'react-router-dom';
import LeaveView from './LeaveView';
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
import useUserStore from '@/store/user.store';
import { tabView } from '../HOC/tabView';
import StatusLabel from '@/components/sub-components/StatusLabel';
import { SearchOutlined } from '@mui/icons-material';
import Iconify from '@/components/iconify';
dayjs.extend(customParseFormat);
interface FormData {
leaveType: any;
leaveNote: any;
leaveHistoryWf: any;
dateFrom: string; // Assuming this is the property representing the start date
// Add other properties within 'leaveDetails' based on your actual data structure
}
const LeaveListing = ({ isApprover = false, isHistory = false }) => {
const user = useUserStore((state) => state.user);
const { id } = useParams();
const nav = useNavigate();
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(e.target.value);
};
useEffect(() => {
// Close the drawer when the id changes
setImportDrawer(false);
}, [id]);
const { data, error, isLoading } = useGetAll<FormData>(
isApprover
? EMPLOYEE_LEAVE_PENDING_WITH_ENDPOINT(user?.Employee.id) +
(!isHistory ? '/X' : '')
: EMPLOYEE_LEAVE_ENDPOINT(user?.Employee.id),
isApprover
? CACHE_KEY_EMPLOYEE_APPROVE_LEAVE.concat(
!isHistory ? ['history'] : ['completed']
)
: CACHE_KEY_EMPLOYEE_LEAVE
);
const [importDrawer, setImportDrawer] = React.useState<boolean>(false);
const [addEmpDrawer, setAddEmpDrawer] = React.useState<boolean>(false);
const [searchTerm, setSearchTerm] = useState('');
const [viewTitle, setViewTitle] = useState('');
const [openSideMenu, setOpenSideMenu] = useState<boolean>(false);
// added the filtering for search
const filteredData = data
? data.filter((row) => {
const searchText = searchTerm.toLowerCase();
const requestIdMatch = row?.leaveHistoryWf?.requestId
?.toLowerCase()
.includes(searchText);
const leaveTypeMatch = row?.leaveType
?.toLowerCase()
.includes(searchText);
const leaveNoteMatch = row?.leaveNote
?.toLowerCase()
.includes(searchText);
const statusMatch = row?.leaveHistoryWf?.status
?.toLowerCase()
.includes(searchText);
const dateFrom = row?.dateFrom;
const isDateFromValid = dateFrom && typeof dateFrom === 'string';
const dateMatch =
isDateFromValid &&
dayjs(dateFrom)
.format('MMM DD YYYY')
.toLowerCase()
.includes(searchText);
return (
requestIdMatch ||
leaveTypeMatch ||
leaveNoteMatch ||
statusMatch ||
dateMatch
);
})
: [];
React.useEffect(() => {
// Update filteredData whenever the data or searchTerm changes
if (data) {
const filtered = data.filter((row) => {
const dateFrom = row?.dateFrom;
const isDateFromValid = dateFrom && typeof dateFrom === 'string';
setSearchTerm(searchTerm); // Update the searchTerm state variable
});
}
}, [data, searchTerm]);
const isWorkflowPending = isApprover && !isHistory;
let columns: GridColDef[] = [
{
field: 'id',
flex: 1,
headerName: 'Request Id',
renderCell: (params: GridRenderCellParams) => {
const { value, row }: { value?: string; row: any } = params;
return <AppInfo mainText={'#' + row?.leaveHistoryWf?.requestId} />;
},
},
{
field: 'leaveType',
flex: 1,
headerName: 'Leave Type',
renderCell: (params: GridRenderCellParams) => {
const { value, row }: { value?: string; row: any } = params;
return (
<AppInfo
mainText={row?.leaveHistoryLeave?.leaveName}
subText={`Requested on ${dayjs(row.createdAt).format(
'MMM DD YYYY'
)}`}
/>
);
},
},
{
field: 'leaveDate',
flex: 2,
headerName: 'Leave Date',
renderCell: (params: GridRenderCellParams) => {
const { value, row }: { value?: string; row: any } = params;
const start = dayjs(row.dateFrom);
const to = dayjs(row.dateTo);
const diff = (row?.dateTo && Math.abs(start.diff(to, 'day'))) + 1 || 1;
return (
<AppInfo
mainText={
<>
<Icon width={16} height={16} icon={'ion:calendar-outline'} />
{` ${start.format('MMM DD YYYY')} ${
row?.dateTo && `- ${to.format('MMM DD YYYY')}`
}`}{' '}
</>
}
subText={`${diff} ${diff > 1 ? 'days' : 'day'} Leave`}
/>
);
},
},
{
field: 'leaveNote',
flex: 1.2,
headerName: 'Status',
renderCell: (params: GridRenderCellParams) => {
const { value, row }: { value?: string; row: any } = params;
return <StatusLabel label={row.leaveHistoryWf?.status} />;
},
},
{
field: 'action',
flex: 1,
headerName: 'Action',
renderCell: (params: GridRenderCellParams) => {
const { row }: { row: any } = params;
return (
<Stack
sx={{ width: '70px' }}
direction='row'
alignItems='center'
justifyContent='space-between'
>
<AppLink path={row.id}>View</AppLink>
</Stack>
);
},
},
];
if (isWorkflowPending) {
// If it's a workflow pending tab, remove the 'Action Token' column
columns = columns.filter((column) => column.field !== 'leaveNote');
}
const handleCloseDrawer = () => {
setImportDrawer(false);
};
if (error) return <p>error</p>;
return (
<AppPaper>
<Grid container spacing={2}>
<Grid item xs={12}>
<Stack
padding={2}
direction='row'
sx={{ width: '100%' }}
justifyContent='space-between'
alignItems='center'
>
<Typography variant='subtitle1' fontWeight='fontWeightBold'>
{isApprover ? 'Leave Approvals' : 'Leave Requests'}
</Typography>
<Stack direction='row' spacing={2}>
<AppTextInput
startIcon={<SearchOutlined />}
placeholder='Search by Request Id, date...'
value={searchTerm}
onChange={handleSearchChange}
></AppTextInput>
{!isApprover && (
// <AppButton
// variant='contained'
// onClick={() => setImportDrawer(true)}
// >
// Apply Leave
// </AppButton>
<Button
variant='contained'
onClick={() => setImportDrawer(true)}
startIcon={<Iconify icon='ic:round-add' width={20} />}
>
Apply Leave
</Button>
)}
<AppDrawer
header='New Leave Request'
open={importDrawer}
setOpen={setImportDrawer}
>
<LeaveForm closeDrawer={setImportDrawer} />
</AppDrawer>
</Stack>
</Stack>
</Grid>
<Grid item xs={12} sx={{ overflow: 'auto' }}>
{data && !isLoading ? (
<AppDataGrid
columns={columns}
rows={filteredData}
rowHeight={75}
getRowId={(row: any) => row.id}
/>
) : (
<TableSkeleton columns={columns} />
)}
</Grid>
</Grid>
<AppDrawer
header={viewTitle || 'View Leave'}
open={!!id}
setOpen={(state: boolean) =>
!state
? isApprover
? nav('/approval/leave')
: nav('/ess/leave')
: null
}
>
{id && (
<LeaveView setTitle={setViewTitle} id={id} isApprover={isApprover} setOpen={setOpenSideMenu} closeDrawer={setImportDrawer} onApprove={handleCloseDrawer} />
)}
</AppDrawer>
</AppPaper>
);
};
export default tabView(LeaveListing);
it is index page which calls leave view component in here also i try somany methods but nothings works , i set drawer to false but it keep till keeps the drawer open, please some one give me the solution
mohamed azath is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.