changes to time off requests

This commit is contained in:
Flatlogic Bot 2026-02-17 18:57:20 +00:00
parent c1f17908e1
commit 40b50cdd6c
4 changed files with 301 additions and 218 deletions

View File

@ -1,4 +1,3 @@
const express = require('express');
const Time_off_requestsService = require('../services/time_off_requests');
@ -303,19 +302,33 @@ router.get('/', wrapAsync(async (req, res) => {
req.query, { currentUser }
);
if (filetype && filetype === 'csv') {
const fields = ['id','reason','manager_note','external_reference',
'hours','days',
'starts_at','ends_at','submitted_at','decided_at',
];
const fields = [
{ label: 'ID', value: 'id' },
{ label: 'Requester', value: (row) => row.requester ? `${row.requester.firstName} ${row.requester.lastName} (${row.requester.email})` : '' },
{ label: 'Approver', value: (row) => row.approver ? `${row.approver.firstName} ${row.approver.lastName} (${row.approver.email})` : '' },
{ label: 'Type', value: 'leave_type' },
{ label: 'Request Kind', value: 'request_kind' },
{ label: 'Start', value: 'starts_at' },
{ label: 'Finish', value: 'ends_at' },
{ label: 'Hours', value: 'hours' },
{ label: 'Days', value: 'days' },
{ label: 'Status', value: 'status' },
{ label: 'Requires Approval', value: 'requires_approval' },
{ label: 'Submitted At', value: 'submitted_at' },
{ label: 'Decided At', value: 'decided_at' },
{ label: 'Reason', value: 'reason' },
{ label: 'Manager Note', value: 'manager_note' },
{ label: 'External Reference', value: 'external_reference' }
];
const opts = { fields };
try {
const csv = parse(payload.rows, opts);
res.status(200).attachment(csv);
res.status(200).attachment('time_off_requests.csv');
res.send(csv)
} catch (err) {
console.error(err);
res.status(500).send('Error generating CSV');
}
} else {
res.status(200).send(payload);
@ -441,4 +454,4 @@ router.get('/:id', wrapAsync(async (req, res) => {
router.use('/', require('../helpers').commonErrorHandler);
module.exports = router;
module.exports = router;

View File

@ -12,6 +12,9 @@ import {
DataGrid,
GridColDef,
} from '@mui/x-data-grid';
import { Drawer, Box, Typography, IconButton } from '@mui/material';
import { mdiClose } from '@mdi/js';
import BaseIcon from '../BaseIcon';
import {loadColumns} from "./configureTime_off_requestsCols";
import _ from 'lodash';
import dataFormatter from '../../helpers/dataFormatter'
@ -20,12 +23,20 @@ import moment from 'moment';
import KanbanBoard from '../KanbanBoard/KanbanBoard';
import axios from 'axios';
const perPage = 10
const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, showGrid }) => {
const getUserLabel = (user: any) => {
if (!user) return '';
if (user.label) return user.label;
if (user.firstName || user.lastName) {
return `${user.firstName || ''} ${user.lastName || ''}`.trim();
}
return user.id || '';
}
const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, showGrid, isFilterDrawerOpen, setIsFilterDrawerOpen }) => {
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
const dispatch = useAppDispatch();
@ -131,11 +142,14 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
const [isModalCancelActive, setIsModalCancelActive] = useState(false)
const [isModalViewActive, setIsModalViewActive] = useState(false)
const [selectedItem, setSelectedItem] = useState<any>(null)
const handleModalAction = () => {
setIsModalInfoActive(false)
setIsModalTrashActive(false)
setIsModalCancelActive(false)
setIsModalViewActive(false)
}
@ -202,6 +216,11 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
}
}
const handleViewAction = (row: any) => {
setSelectedItem(row);
setIsModalViewActive(true);
}
const generateFilterRequests = useMemo(() => {
let request = '&';
filterItems.forEach((item) => {
@ -245,10 +264,10 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
};
const handleSubmit = () => {
loadData(0, generateFilterRequests);
loadData(currentPage, generateFilterRequests);
setKanbanFilters(generateFilterRequests);
setIsFilterDrawerOpen(false);
};
const handleChange = (id) => (e) => {
@ -270,7 +289,7 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
loadData(0, '');
setKanbanFilters('');
setIsFilterDrawerOpen(false);
};
const onPageChange = (page: number) => {
@ -286,7 +305,8 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
handleDeleteModalAction,
`time_off_requests`,
currentUser,
handleCancelModalAction
handleCancelModalAction,
handleViewAction
).then((newCols) => setColumns(newCols));
}, [currentUser]);
@ -385,175 +405,195 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
</div>
)
const addFilter = () => {
const newItem = {
id: _.uniqueId(),
fields: {
filterValue: '',
filterValueFrom: '',
filterValueTo: '',
selectedField: '',
},
};
newItem.fields.selectedField = filters[0]?.title || '';
setFilterItems([...filterItems, newItem]);
};
return (
<>
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
<CardBox>
<Drawer
anchor="right"
open={isFilterDrawerOpen}
onClose={() => setIsFilterDrawerOpen(false)}
ModalProps={{
keepMounted: true, // Better open performance on mobile
}}
>
<Box sx={{ width: 350, p: 4 }}>
<div className="flex justify-between items-center mb-6">
<Typography variant="h6">Filters</Typography>
<IconButton onClick={() => setIsFilterDrawerOpen(false)}>
<BaseIcon path={mdiClose} />
</IconButton>
</div>
<Formik
initialValues={{
checkboxes: ['lorem'],
switches: ['lorem'],
radio: 'lorem',
}}
initialValues={{}}
onSubmit={() => null}
>
<Form>
<>
{filterItems && filterItems.map((filterItem) => {
const selectedFilter = filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
);
return (
<div key={filterItem.id} className="flex mb-4">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Filter</div>
<Field
className={controlClasses}
name='selectedField'
id='selectedField'
component='select'
value={filterItem?.fields?.selectedField || ''}
onChange={handleChange(filterItem.id)}
>
{filters.map((selectOption) => (
<option
key={selectOption.title}
value={`${selectOption.title}`}
>
{selectOption.label}
</option>
))}
</Field>
</div>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.type === 'enum' ? (
<div className="flex flex-col w-full mr-3">
<div className="text-gray-500 font-bold">
Value
</div>
<Field
className={controlClasses}
name="filterValue"
id='filterValue'
component="select"
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
>
<option value="">Select Value</option>
{filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.options?.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
) : filters.find((filter) =>
filter.title === filterItem?.fields?.selectedField
)?.number ? (
<div className="flex flex-row w-full mr-3">
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">From</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className="flex flex-col w-full">
<div className=" text-gray-500 font-bold">To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : filters.find(
(filter) =>
filter.title ===
filterItem?.fields?.selectedField
)?.date ? (
<div className='flex flex-row w-full mr-3'>
<div className='flex flex-col w-full mr-3'>
<div className=' text-gray-500 font-bold'>
From
</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
type='datetime-local'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className='flex flex-col w-full'>
<div className=' text-gray-500 font-bold'>To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='to'
id='filterValueTo'
type='datetime-local'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : (
<div className="flex flex-col w-full mr-3">
<div className=" text-gray-500 font-bold">Contains</div>
<Field
className={controlClasses}
name='filterValue'
placeholder='Contained'
id='filterValue'
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
)}
<div className="flex flex-col">
<div className=" text-gray-500 font-bold">Action</div>
<CardBox key={filterItem.id} className="mb-4" noPadding>
<div className="p-4 border-b border-gray-100 dark:border-gray-700 flex justify-between items-center">
<span className="font-bold text-gray-500 text-sm">Condition</span>
<BaseButton
className="my-2"
type='reset'
color='danger'
label='Delete'
label='Remove'
small
onClick={() => {
deleteFilter(filterItem.id)
}}
/>
</div>
</div>
<div className="p-4">
<div className="mb-2">
<div className="text-gray-500 text-xs mb-1 font-bold">Field</div>
<Field
className={controlClasses}
name='selectedField'
id='selectedField'
component='select'
value={filterItem?.fields?.selectedField || ''}
onChange={handleChange(filterItem.id)}
>
{filters.map((selectOption) => (
<option
key={selectOption.title}
value={`${selectOption.title}`}
>
{selectOption.label}
</option>
))}
</Field>
</div>
{selectedFilter?.type === 'enum' ? (
<div className="mb-2">
<div className="text-gray-500 text-xs mb-1 font-bold">Value</div>
<Field
className={controlClasses}
name="filterValue"
id='filterValue'
component="select"
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
>
<option value="">Select Value</option>
{selectedFilter?.options?.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</Field>
</div>
) : selectedFilter?.number ? (
<div className="flex space-x-2">
<div className="w-1/2">
<div className="text-gray-500 text-xs mb-1 font-bold">From</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className="w-1/2">
<div className="text-gray-500 text-xs mb-1 font-bold">To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='To'
id='filterValueTo'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : selectedFilter?.date ? (
<div className="flex space-x-2">
<div className="w-1/2">
<div className="text-gray-500 text-xs mb-1 font-bold">From</div>
<Field
className={controlClasses}
name='filterValueFrom'
placeholder='From'
id='filterValueFrom'
type='datetime-local'
value={filterItem?.fields?.filterValueFrom || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
<div className="w-1/2">
<div className="text-gray-500 text-xs mb-1 font-bold">To</div>
<Field
className={controlClasses}
name='filterValueTo'
placeholder='To'
id='filterValueTo'
type='datetime-local'
value={filterItem?.fields?.filterValueTo || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
</div>
) : (
<div className="mb-2">
<div className="text-gray-500 text-xs mb-1 font-bold">Contains</div>
<Field
className={controlClasses}
name='filterValue'
placeholder='Contained'
id='filterValue'
value={filterItem?.fields?.filterValue || ''}
onChange={handleChange(filterItem.id)}
/>
</div>
)}
</div>
</CardBox>
)
})}
<div className="flex">
<div className="mt-6 flex flex-col space-y-3">
<BaseButton
className="my-2 mr-3"
color="success"
label='Apply'
color="info"
label='Add Condition'
outline
onClick={addFilter}
/>
<BaseButton
color="success"
label='Apply Filters'
onClick={handleSubmit}
/>
<BaseButton
className="my-2"
color='info'
label='Cancel'
color='info'
label='Clear All'
outline
onClick={handleReset}
/>
</div>
</>
</Form>
</Formik>
</CardBox> : null
}
</Box>
</Drawer>
<CardBoxModal
title="Please confirm"
buttonColor="info"
@ -576,6 +616,35 @@ const TableSampleTime_off_requests = ({ filterItems, setFilterItems, filters, sh
<p>Are you sure you want to cancel this time off request?</p>
</CardBoxModal>
<CardBoxModal
title="Request Details"
buttonColor="info"
buttonLabel="Close"
isActive={isModalViewActive}
onConfirm={handleModalAction}
onCancel={handleModalAction}
>
{selectedItem && (
<div className="space-y-2">
<p><strong>Requester:</strong> {getUserLabel(selectedItem.requester)}</p>
<p><strong>Approver:</strong> {getUserLabel(selectedItem.approver)}</p>
<p><strong>Type:</strong> {selectedItem.leave_type}</p>
<p><strong>Kind:</strong> {selectedItem.request_kind}</p>
<p><strong>Start:</strong> {moment(selectedItem.starts_at).format('YYYY-MM-DD HH:mm')}</p>
<p><strong>Finish:</strong> {moment(selectedItem.ends_at).format('YYYY-MM-DD HH:mm')}</p>
<p><strong>Hours:</strong> {selectedItem.hours}</p>
<p><strong>Days:</strong> {selectedItem.days}</p>
<p><strong>Status:</strong> {selectedItem.status}</p>
<p><strong>Requires Approval:</strong> {selectedItem.requires_approval ? 'Yes' : 'No'}</p>
<p><strong>Submitted At:</strong> {selectedItem.submitted_at ? moment(selectedItem.submitted_at).format('YYYY-MM-DD HH:mm') : 'N/A'}</p>
<p><strong>Decided At:</strong> {selectedItem.decided_at ? moment(selectedItem.decided_at).format('YYYY-MM-DD HH:mm') : 'N/A'}</p>
<p><strong>Reason:</strong> {selectedItem.reason}</p>
<p><strong>Manager Note:</strong> {selectedItem.manager_note}</p>
<p><strong>External Reference:</strong> {selectedItem.external_reference}</p>
</div>
)}
</CardBoxModal>
{!showGrid && kanbanColumns && (

View File

@ -1,29 +1,31 @@
import React from 'react';
import BaseIcon from '../BaseIcon';
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
import axios from 'axios';
import {
GridActionsCellItem,
GridRowParams,
GridValueGetterParams,
} from '@mui/x-data-grid';
import ImageField from '../ImageField';
import {saveFile} from "../../helpers/fileSaver";
import dataFormatter from '../../helpers/dataFormatter'
import DataGridMultiSelect from "../DataGridMultiSelect";
import ListActionsPopover from '../ListActionsPopover';
import {hasPermission} from "../../helpers/userPermissions";
type Params = (id: string) => void;
const getUserLabel = (user: any) => {
if (!user) return '';
if (user.label) return user.label;
if (user.firstName || user.lastName) {
return `${user.firstName || ''} ${user.lastName || ''}`.trim();
}
return user.id || '';
}
export const loadColumns = async (
onDelete: Params,
entityName: string,
user,
onCancel?: Params
onCancel?: Params,
onView?: (row: any) => void
) => {
async function callOptionsApi(entityName: string) {
@ -39,18 +41,26 @@ export const loadColumns = async (
}
const hasUpdatePermission = hasPermission(user, 'UPDATE_TIME_OFF_REQUESTS')
const isAdmin = user?.app_role?.name === 'Administrator';
return [
const columns: any[] = [
{
field: 'requester',
headerName: 'Requester',
flex: 1,
minWidth: 120,
minWidth: 150,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
renderCell: (params: GridValueGetterParams) => (
<span
className="cursor-pointer text-blue-600 hover:underline font-medium"
onClick={() => onView && onView(params.row)}
>
{getUserLabel(params?.row?.requester)}
</span>
),
editable: hasUpdatePermission,
@ -68,11 +78,15 @@ export const loadColumns = async (
field: 'approver',
headerName: 'Approver',
flex: 1,
minWidth: 120,
minWidth: 150,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
renderCell: (params: GridValueGetterParams) => (
<span>
{getUserLabel(params?.row?.approver)}
</span>
),
editable: hasUpdatePermission,
@ -88,7 +102,7 @@ export const loadColumns = async (
{
field: 'leave_type',
headerName: 'LeaveType',
headerName: 'Type',
flex: 1,
minWidth: 120,
filterable: false,
@ -109,7 +123,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: !isAdmin,
editable: hasUpdatePermission,
@ -118,7 +132,7 @@ export const loadColumns = async (
{
field: 'starts_at',
headerName: 'StartsAt',
headerName: 'Start',
flex: 1,
minWidth: 120,
filterable: false,
@ -136,7 +150,7 @@ export const loadColumns = async (
{
field: 'ends_at',
headerName: 'EndsAt',
headerName: 'Finish',
flex: 1,
minWidth: 120,
filterable: false,
@ -160,7 +174,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true,
editable: hasUpdatePermission,
@ -207,7 +221,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true, // Hidden by default
editable: hasUpdatePermission,
@ -223,7 +237,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true, // Hidden by default
editable: hasUpdatePermission,
@ -241,7 +255,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true, // Hidden by default
editable: hasUpdatePermission,
@ -259,7 +273,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true, // Hidden by default
editable: hasUpdatePermission,
@ -274,7 +288,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true, // Hidden by default
editable: hasUpdatePermission,
@ -289,22 +303,9 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true,
editable: false,
sortable: false,
renderCell: (params: GridValueGetterParams) => (
<>
{dataFormatter.filesFormatter(params.row.attachments).map(link => (
<button
key={link.publicUrl}
onClick={(e) => saveFile(e, link.publicUrl, link.name)}
>
{link.name}
</button>
))}
</>
),
},
{
@ -315,7 +316,7 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
hide: true,
editable: hasUpdatePermission,
@ -328,7 +329,8 @@ export const loadColumns = async (
minWidth: 30,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
hide: true,
getActions: (params: any) => {
return [
<div key={params?.row?.id}>
@ -348,4 +350,14 @@ export const loadColumns = async (
},
},
];
// Filter columns for non-admins if they are strictly admin-only
return columns.filter(col => {
if (!isAdmin) {
if (['requires_approval', 'submitted_at', 'decided_at', 'reason', 'manager_note'].includes(col.field)) {
return false;
}
}
return true;
});
};

View File

@ -1,6 +1,5 @@
import { mdiChartTimelineVariant } from '@mdi/js'
import Head from 'next/head'
import { uniqueId } from 'lodash';
import React, { ReactElement, useState } from 'react'
import CardBox from '../../components/CardBox'
import LayoutAuthenticated from '../../layouts/Authenticated'
@ -27,6 +26,7 @@ const Time_off_requestsTablesPage = () => {
const [csvFile, setCsvFile] = useState<File | null>(null);
const [isModalActive, setIsModalActive] = useState(false);
const [showTableView, setShowTableView] = useState(true);
const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false);
const { currentUser } = useAppSelector((state) => state.auth);
@ -35,10 +35,10 @@ const Time_off_requestsTablesPage = () => {
const dispatch = useAppDispatch();
const [filters] = useState([{label: 'Reason', title: 'reason'},{label: 'ManagerNote', title: 'manager_note'},{label: 'ExternalReference', title: 'external_reference'},
const [filters] = useState([{label: 'Reason', title: 'reason'},{label: 'Manager Note', title: 'manager_note'},{label: 'External Reference', title: 'external_reference'},
{label: 'Hours', title: 'hours', number: 'true'},{label: 'Days', title: 'days', number: 'true'},
{label: 'StartsAt', title: 'starts_at', date: 'true'},{label: 'EndsAt', title: 'ends_at', date: 'true'},{label: 'SubmittedAt', title: 'submitted_at', date: 'true'},{label: 'DecidedAt', title: 'decided_at', date: 'true'},
{label: 'Start', title: 'starts_at', date: 'true'},{label: 'Finish', title: 'ends_at', date: 'true'},{label: 'Submitted At', title: 'submitted_at', date: 'true'},{label: 'Decided At', title: 'decided_at', date: 'true'},
{label: 'Requester', title: 'requester'},
@ -50,7 +50,7 @@ const Time_off_requestsTablesPage = () => {
{label: 'LeaveType', title: 'leave_type', type: 'enum', options: ['regular_pto','unplanned_pto','medical_leave','bereavement','time_in_lieu']},{label: 'RequestKind', title: 'request_kind', type: 'enum', options: ['take_time_off','add_time_credit','manual_adjustment']},{label: 'Status', title: 'status', type: 'enum', options: ['draft','pending_approval','approved','rejected','cancelled']},
{label: 'Type', title: 'leave_type', type: 'enum', options: ['regular_pto','unplanned_pto','medical_leave','bereavement','time_in_lieu']},{label: 'Request Kind', title: 'request_kind', type: 'enum', options: ['take_time_off','add_time_credit','manual_adjustment']},{label: 'Status', title: 'status', type: 'enum', options: ['draft','pending_approval','approved','rejected','cancelled']},
]);
const hasCreatePermission = currentUser && hasPermission(currentUser, 'CREATE_TIME_OFF_REQUESTS');
@ -62,27 +62,13 @@ const Time_off_requestsTablesPage = () => {
}, undefined, { shallow: true });
};
const addFilter = () => {
const newItem = {
id: uniqueId(),
fields: {
filterValue: '',
filterValueFrom: '',
filterValueTo: '',
selectedField: '',
},
};
newItem.fields.selectedField = filters[0].title;
setFilterItems([...filterItems, newItem]);
};
const getTime_off_requestsCSV = async () => {
const response = await axios({url: '/time_off_requests?filetype=csv', method: 'GET',responseType: 'blob'});
const type = response.headers['content-type']
const blob = new Blob([response.data], { type: type })
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'time_off_requestsCSV.csv'
link.download = 'time_off_requests_export.csv'
link.click()
};
@ -102,10 +88,10 @@ const Time_off_requestsTablesPage = () => {
return (
<>
<Head>
<title>{getPageTitle('Time_off_requests')}</title>
<title>{getPageTitle('Time Off Requests')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Time_off_requests" main>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Time Off Requests" main>
{''}
</SectionTitleLineWithButton>
<CardBox className='mb-6' cardBoxClassName='flex flex-wrap'>
@ -116,7 +102,7 @@ const Time_off_requestsTablesPage = () => {
className={'mr-3'}
color='info'
label='Filter'
onClick={addFilter}
onClick={() => setIsFilterDrawerOpen(true)}
/>
<BaseButton
className={'mr-3'}
@ -124,10 +110,11 @@ const Time_off_requestsTablesPage = () => {
label='Pending'
onClick={showPending}
/>
<BaseButton className={'mr-3'} color='info' label='Download CSV' onClick={getTime_off_requestsCSV} />
<BaseButton className={'mr-3'} color='info' label='Export' onClick={getTime_off_requestsCSV} />
{hasCreatePermission && (
<BaseButton
className={'mr-3'}
color='info'
label='Upload CSV'
onClick={() => setIsModalActive(true)}
@ -153,6 +140,8 @@ const Time_off_requestsTablesPage = () => {
setFilterItems={setFilterItems}
filters={filters}
showGrid={showTableView}
isFilterDrawerOpen={isFilterDrawerOpen}
setIsFilterDrawerOpen={setIsFilterDrawerOpen}
/>
</SectionMain>