import AppTextInput from '@/components/form-fields/AppTextInput';
import Iconify from '@/components/iconify/Iconify';
import AppButton from '@/components/templates/AppButton';
import AppDataGrid from '@/components/templates/AppDataGrid';
import AppLink from '@/components/templates/AppLink';
import AppPersona from '@/components/templates/AppPersona';
import TableSkeleton from '@/components/templates/AppTable/TableSkeleton';
import AppTag from '@/components/templates/AppTag';
import AppTitle from '@/components/templates/AppTitle';
import {
CACHE_KEY_AVAILABLE_PAYROLL,
CACHE_KEY_GENERATED_PAYROLL,
CACHE_KEY_PAYROLL_CYCLE_PAYRUN,
CACHE_KEY_POSTED_PAYROLL,
CACHE_KEY_PROCESSED_PAYROLL,
PAYROLL_CYCLE_LOCK_ENDPOINT,
PAYROLL_CYCLE_PAYRUN_ENDPOINT,
PAYROLL_CYCLE_SIF_ENDPOINT,
PAYROLL_EMP_BY_STATUS_ENDPOINT,
} from '@/constants/payroll';
import useGetAll from '@/hooks/useGetAll';
import {
Box,
Button,
Divider,
Grid,
IconButton,
Menu,
MenuItem,
Paper,
Stack,
Step,
StepButton,
Stepper,
Typography,
} from '@mui/material';
import {
GridColDef,
GridRenderCellParams,
GridRowSelectionModel,
} from '@mui/x-data-grid';
import * as React from 'react';
import { useLocation, useParams } from 'react-router-dom';
import StatusCardList from './status-card-list';
import ProcessPopup, { PAYROLL_UPDATE_PAYLOAD } from './process-popup';
import usePut from '@/hooks/usePut';
import AppDrawer from '@/components/templates/AppDrawer';
import PayView from './payview';
import { useState } from 'react';
import nav from '@/AppLayout/nav';
import APIClient from '@/services/api-client';
import { SearchOutlined } from '@mui/icons-material';
import { useQueryClient } from '@tanstack/react-query';
import { PAYROLL } from '../payroll-list';
import { toast } from 'react-toastify';
import useUserStore from '@/store/user.store';
export type PAYROLL_PROCESS =
| 'AVAILABLE'
| 'GENERATED'
| 'PROCESSED'
| 'POSTED';
interface STEP {
id: PAYROLL_PROCESS;
label: string;
}
const steps: STEP[] = [
// { id: 'AVAILABLE', label: 'Available Payroll' }, //included this line by Priti
{ id: 'AVAILABLE', label: 'Available Employee' },
{ id: 'GENERATED', label: 'Generated Payroll' },
{ id: 'POSTED', label: 'Posted Payroll' },
{ id: 'PROCESSED', label: 'Processed Payroll' },
];
function Payrun() {
const params = useParams();
const [activeStep, setActiveStep] = React.useState<number>(0);
const [rowSelectionModel, setRowSelectionModel] =
React.useState<GridRowSelectionModel>([]);
const [selectedRows, setSelectedRows] =
React.useState<PAYROLL_UPDATE_PAYLOAD>();
const [open, setOpen] = React.useState(false);
const [deleteOpen, setDeleteOpen] = React.useState(false);
const { data, refetch, error, isLoading, isFetching } =
useGetAll<EMP_PAYROLL_STATUS>(
PAYROLL_EMP_BY_STATUS_ENDPOINT +
'?' +
'payrollCycleId=' +
params.id +
'&status=' +
steps[activeStep].id,
activeStep === 0
? CACHE_KEY_AVAILABLE_PAYROLL
: activeStep === 1
? CACHE_KEY_GENERATED_PAYROLL
: activeStep === 2
? CACHE_KEY_POSTED_PAYROLL
: CACHE_KEY_PROCESSED_PAYROLL
);
React.useEffect(() => {
if (isFetching) {
console.log('Refetching data...');
}
}, [isFetching]);
const handleRowSelection = (rowIds: GridRowSelectionModel) => {
setRowSelectionModel(rowIds);
const rows = data && data.filter((d) => rowIds.includes(d.id));
if (activeStep === 0)
setSelectedRows({
payrollCycleId: params.id!,
employees: rows?.map((r) => r.id),
});
else
setSelectedRows({
payrollCycleId: params.id!,
payrollId: rows?.map((r) => r.payrollId),
});
};
const [menuAnchor, setMenuAnchor] = React.useState<HTMLElement | null>(null);
const handleMenuClick = (event: React.MouseEvent<HTMLElement>) => {
setMenuAnchor(event.currentTarget);
};
const handleMenuClose = () => {
setMenuAnchor(null);
};
const getActionLabel = () => {
switch (steps[activeStep].id) {
case 'AVAILABLE':
return ['Generate Payroll'];
case 'GENERATED':
return ['Post Payroll', 'Delete'];
case 'POSTED':
return ['Process Payroll', 'Delete'];
case 'PROCESSED':
return ['No Actions needed'];
default:
return ['Action'];
}
};
const refetchTableData = () => {
const queryClient = useQueryClient(); // Get the query client instance
queryClient.invalidateQueries(CACHE_KEY_PAYROLL_CYCLE_PAYRUN);
console.log('Table data refetched');
refetch();
};
const user = useUserStore((state) => state.user);
const columns: GridColDef[] = [
{
field: 'firstName',
minWidth: 200,
flex: 1,
headerName: 'Employee Name',
renderCell: (params: GridRenderCellParams) => {
const { value, row }: { value?: string; row: any } = params;
return (
<AppPersona
profile={{
maintext: value + ' ' + row.lastName,
subtext: '#' + row.empCode,
}}
/>
);
},
},
{
field: 'departmentId',
minWidth: 200,
flex: 1,
headerName: 'Department & Division',
renderCell: (params: GridRenderCellParams) => {
const { value, row }: { value?: string; row: any } = params;
return (
<Box>
<Typography
variant='body2'
fontWeight={'fontWeightMedium'}
sx={{ color: 'text.primary' }}
>
{value!}
</Typography>
<Typography
variant='caption'
sx={{ color: 'text.secondary' }}
fontWeight='fontWeightMedium'
>
{row.divisionId !}
</Typography>
</Box>
);
// return <AppTitle title={value!} subtitle={row.division!} />;
},
},
// {
// field: 'salary',
// minWidth: 100,
// width: 150,
// headerName: 'Salary',
// renderCell: (params: GridRenderCellParams) => {
// return (
// <Typography variant='body2' fontWeight='fontWeightMedium'>
// INR. {params.value}
// </Typography>
// );
// },
// },
{
field: 'status',
minWidth: 80,
width: 150,
headerName: 'Status',
renderCell: (params: GridRenderCellParams) => {
const val = params?.value?.toUpperCase() || 'NA'!;
return (
<AppTag
variant={
val === 'GENERATED'
? 'ACTIVE'
: val === 'POSTED'
? 'INACTIVE'
: ''
}
text={val}
/>
);
},
},
{
field: 'action',
flex: 1,
headerAlign: 'center',
align: 'center',
headerName: 'Action',
renderCell: (params: GridRenderCellParams) => {
const { row }: { row: any } = params;
return (
<Stack
sx={{ width: '70px' }}
direction='row'
alignItems='center'
justifyContent='space-between'
>
<AppButton
sx={{
color: 'primary.main',
fontWeight: 600,
textDecoration: 'none',
padding: '4px 10px',
display: 'inline-flex',
alignItems: 'center',
}}
variant='text'
onClick={() => handleOpenDrawer(row?.payrollId)}
>
View
</AppButton>
{/* <Iconify icon='ic:sharp-more-vert' width={22} /> */}
{/* <IconButton onClick={handleMenuClick}>
<Iconify icon='ic:sharp-more-vert' width={18} />
</IconButton>
<Menu
anchorEl={menuAnchor}
open={Boolean(menuAnchor)}
onClose={handleMenuClose}
>
{getActionLabel().map((label, index) => (
<MenuItem
key={index}
onClick={() => handleMenuItemClick(label)}
>
{label}
</MenuItem>
))}
</Menu> */}
</Stack>
);
},
},
];
const [viewTitle, setViewTitle] = useState('');
const [isDrawerOpen, setIsDrawerOpen] = useState<string>(''); // New state to track drawer open/close
const { id } = useParams();
// Function to open the drawer
const handleOpenDrawer = (id: string) => {
setIsDrawerOpen(id);
};
// Function to close the drawer
const handleCloseDrawer = () => {
setIsDrawerOpen('');
};
React.useEffect(() => {
if (id) {
setIsDrawerOpen('');
}
}, [id]);
const downloadSif = () => {
const apiClient = new APIClient(PAYROLL_CYCLE_SIF_ENDPOINT(params.id!));
apiClient.get(true).then((data) => {
console.log(data);
const fileURL = URL.createObjectURL(data);
var link = document.createElement('a');
link.setAttribute('href', fileURL);
const dt = new Date();
const yr = dt.getFullYear().toString().slice(-2);
const mn = (dt.getMonth() + 1).toString().padStart(2, '0');
const dd = dt.getDate().toString().padStart(2, '0');
const hr = dt.getHours().toString().padStart(2, '0');
const min = dt.getMinutes().toString().padStart(2, '0');
const sec = dt.getSeconds().toString().padStart(2, '0');
link.setAttribute(
'download',
`${'0000001145097'}${yr + mn + dd + hr + min + sec}.txt`
);
document.body.appendChild(link); // Required for FF
link.click();
link.remove();
});
};
const lockPayroll = (payrollCycleId: string) => {
const service = new APIClient(PAYROLL_CYCLE_LOCK_ENDPOINT + params.id);
service
.put({ id: payrollCycleId })
.then((data) => {
// Invalidate relevant queries
queryClient.invalidateQueries(CACHE_KEY_PAYROLL_CYCLE_PAYRUN);
toast.success('Payroll locked !');
refetch();
})
.catch((error) => {
// Handle error
console.error('Error making employee inactive:', error);
toast.error('Failed to lock payroll');
});
};
const queryClient = useQueryClient();
// const onEdit = (editData: PAYROLL) => {
// queryClient.setQueryData<PAYROLL>(CACHE_KEY_PAYROLL_CYCLE_PAYRUN, editData);
// };
const location = useLocation();
const { state } = location;
console.log(state, 'state');
return (
<Paper
sx={{
border: '1px solid',
borderColor: 'background.neutral',
backgroundColor: 'background.paper',
borderRadius: 2.5,
m: 4,
overflow: 'auto',
}}
>
<Grid container direction='row' sx={{ height: '100%' }}>
<Grid item xs={12}>
<Stack sx={{ height: '100%' }} divider={<Divider />}>
<Stack
sx={{ px: 2.5, pt: 2.5, pb: 4 }}
direction='row'
justifyContent='space-between'
>
<AppTitle
title={'Payroll Cycle Details '}
subtitle={`${state?.dateFrom} - ${state?.dateTo}`}
/>
<Button
variant='outlined'
onClick={() => {
if (params.id) {
lockPayroll(params.id);
} else {
console.error('PayrollCycleId is undefined');
toast.error(
'Failed to lock payroll: PayrollCycleId is undefined'
);
}
}}
>
<Iconify icon='ic:round-lock' width={18} />
Lock Payroll
</Button>
</Stack>
<Box>
<StatusCardList />
<Stepper sx={{ pt: 2, px: 1 }} nonLinear activeStep={activeStep}>
{steps.map((step, index) => (
<Step key={step.label}>
<StepButton onClick={() => setActiveStep(index)}>
{step.label}
</StepButton>
</Step>
))}
</Stepper>
<Box sx={{ mt: 5 }}>
<Stack
px={2}
direction='row'
justifyContent='space-between'
alignItems='center'
>
<AppTitle title={'List of Payrolls'} />
<Stack direction='row' spacing={2} sx={{ width: '50%' }}>
<AppTextInput
startIcon={<SearchOutlined />}
placeholder='Search by Request Id, Emp no'
/>
<IconButton onClick={() => {}}>
<Iconify icon='mdi:filter-outline' width={18} />
</IconButton>
{activeStep !== 3 && (
<AppButton
onClick={() => {
setOpen(true); // Open the dialog or popup for the action
// Assuming the action is completed successfully and you want to update the table
// Call refetch to fetch the latest data from the API
refetch();
console.log('Refetching data...');
}}
variant='contained'
disabled={!rowSelectionModel.length}
disableRipple
>
{activeStep === 0
? 'Generate '
: activeStep === 1
? 'Post '
: 'Process '}
Payroll
</AppButton>
)}
{activeStep === 3 && (
<Button
variant='text'
onClick={downloadSif}
startIcon={
<Iconify
icon='ic:baseline-cloud-download'
width={16}
/>
}
>
SIF Download
</Button>
)}
{activeStep === 1 && (
<Button
variant='text'
disabled={!rowSelectionModel.length}
startIcon={<Iconify icon='ic:delete' width={16} />}
onClick={() => {
setDeleteOpen(true);
}}
>
Delete
</Button>
)}
</Stack>
</Stack>
<Box sx={{ mt: 2 }}>
{data && !isLoading ? (
<AppDataGrid
columns={columns}
rows={data}
rowHeight={75}
getRowId={(row: EMP_PAYROLL_STATUS) => row.id}
checkboxSelection={activeStep !== 3}
onRowSelectionModelChange={(
newRowSelectionModel: GridRowSelectionModel
) => {
handleRowSelection(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
/>
) : (
<TableSkeleton columns={columns} />
)}
</Box>
</Box>
</Box>
</Stack>
</Grid>
</Grid>
{selectedRows && (
<>
<ProcessPopup
data={selectedRows}
process={steps[activeStep].id}
open={open}
setOpen={setOpen}
refetchTableData={refetchTableData}
/>
{activeStep == 1 && (
<ProcessPopup
data={selectedRows}
process={'DELETE'}
open={deleteOpen}
setOpen={setDeleteOpen}
refetchTableData={refetchTableData}
/>
)}
</>
)}
<AppDrawer
header={viewTitle || 'View Leave'}
open={!!isDrawerOpen} // Use the state to control drawer open/close
setOpen={handleCloseDrawer} // Pass the close function to the AppDrawer
>
{isDrawerOpen && <PayView setTitle={setViewTitle} id={isDrawerOpen!} />}
</AppDrawer>
</Paper>
);
}
export default React.memo(Payrun);
interface EMP_PAYROLL_STATUS {
id: string;
payrollId: string;
payrollCycleId: string;
title: string;
firstName: string;
lastName: string;
empCode: string;
departmentId: string;
divisionId: string;
salary: number;
status: PAYROLL_PROCESS;
payMode: string;
}
Here i already used ‘queryClient’ for refteching but its not working what is the solution for refresh all api’s using queryclient
const refetchTableData = () => {
const queryClient = useQueryClient(); // Get the query client instance
queryClient.invalidateQueries(CACHE_KEY_PAYROLL_CYCLE_PAYRUN);
console.log('Table data refetched');
refetch();
};
created function with queryclient but it won’t works please help me refresh all api’s using query client please correct my code and logic thanks in advance friends,
what is my mistake please also provide what i made a mistake here.
New contributor
mohamed azath is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.