feat(ratecard): enhance ratecard update logic and add unsaved changes alert
This commit is contained in:
@@ -97,10 +97,26 @@ export default class ProjectRateCardController extends WorklenzControllerBase {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { job_title_id, rate } = req.body;
|
const { job_title_id, rate } = req.body;
|
||||||
const q = `
|
const q = `
|
||||||
|
WITH updated AS (
|
||||||
UPDATE finance_project_rate_card_roles
|
UPDATE finance_project_rate_card_roles
|
||||||
SET job_title_id = $1, rate = $2, updated_at = NOW()
|
SET job_title_id = $1, rate = $2, updated_at = NOW()
|
||||||
WHERE id = $3
|
WHERE id = $3
|
||||||
RETURNING *;
|
RETURNING *
|
||||||
|
),
|
||||||
|
jobtitles AS (
|
||||||
|
SELECT u.*, jt.name AS jobtitle
|
||||||
|
FROM updated u
|
||||||
|
JOIN job_titles jt ON jt.id = u.job_title_id
|
||||||
|
),
|
||||||
|
members AS (
|
||||||
|
SELECT json_agg(pm.id) AS members, pm.project_rate_card_role_id
|
||||||
|
FROM project_members pm
|
||||||
|
WHERE pm.project_rate_card_role_id IN (SELECT id FROM jobtitles)
|
||||||
|
GROUP BY pm.project_rate_card_role_id
|
||||||
|
)
|
||||||
|
SELECT jt.*, m.members
|
||||||
|
FROM jobtitles jt
|
||||||
|
LEFT JOIN members m ON m.project_rate_card_role_id = jt.id;
|
||||||
`;
|
`;
|
||||||
const result = await db.query(q, [job_title_id, rate, id]);
|
const result = await db.query(q, [job_title_id, rate, id]);
|
||||||
return res.status(200).send(new ServerResponse(true, result.rows[0]));
|
return res.status(200).send(new ServerResponse(true, result.rows[0]));
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
"actionsColumn": "Actions",
|
"actionsColumn": "Actions",
|
||||||
"addAllButton": "Add All",
|
"addAllButton": "Add All",
|
||||||
"removeAllButton": "Remove All",
|
"removeAllButton": "Remove All",
|
||||||
"selectJobTitle": "Select job title"
|
"selectJobTitle": "Select job title",
|
||||||
|
"unsavedChangesTitle": "Unsaved changes",
|
||||||
|
"ratecardNameRequired": "Rate card name is required"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Drawer, Select, Typography, Flex, Button, Input, Table, Popconfirm, Tooltip } from 'antd';
|
import { Drawer, Select, Typography, Flex, Button, Input, Table, Tooltip, Alert, Space, message, Popconfirm } from 'antd';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAppSelector } from '../../../hooks/useAppSelector';
|
import { useAppSelector } from '../../../hooks/useAppSelector';
|
||||||
@@ -19,6 +19,7 @@ interface PaginationType {
|
|||||||
pageSizeOptions: string[];
|
pageSizeOptions: string[];
|
||||||
size: 'small' | 'default';
|
size: 'small' | 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
const RatecardDrawer = ({
|
const RatecardDrawer = ({
|
||||||
type,
|
type,
|
||||||
ratecardId,
|
ratecardId,
|
||||||
@@ -29,11 +30,12 @@ const RatecardDrawer = ({
|
|||||||
onSaved?: () => void;
|
onSaved?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [ratecardsList, setRatecardsList] = useState<RatecardType[]>([]);
|
const [ratecardsList, setRatecardsList] = useState<RatecardType[]>([]);
|
||||||
// initial Job Roles List (dummy data)
|
|
||||||
const [roles, setRoles] = useState<IJobType[]>([]);
|
const [roles, setRoles] = useState<IJobType[]>([]);
|
||||||
|
const [initialRoles, setInitialRoles] = useState<IJobType[]>([]);
|
||||||
|
const [initialName, setInitialName] = useState<string>('Untitled Rate Card');
|
||||||
|
const [initialCurrency, setInitialCurrency] = useState<string>('USD');
|
||||||
const [addingRowIndex, setAddingRowIndex] = useState<number | null>(null);
|
const [addingRowIndex, setAddingRowIndex] = useState<number | null>(null);
|
||||||
const { t } = useTranslation('settings/ratecard-settings');
|
const { t } = useTranslation('settings/ratecard-settings');
|
||||||
// get drawer state from client reducer
|
|
||||||
const drawerLoading = useAppSelector(state => state.financeReducer.isFinanceDrawerloading);
|
const drawerLoading = useAppSelector(state => state.financeReducer.isFinanceDrawerloading);
|
||||||
const drawerRatecard = useAppSelector(state => state.financeReducer.drawerRatecard);
|
const drawerRatecard = useAppSelector(state => state.financeReducer.drawerRatecard);
|
||||||
const isDrawerOpen = useAppSelector(
|
const isDrawerOpen = useAppSelector(
|
||||||
@@ -57,6 +59,15 @@ const RatecardDrawer = ({
|
|||||||
size: 'small',
|
size: 'small',
|
||||||
});
|
});
|
||||||
const [editingRowIndex, setEditingRowIndex] = useState<number | null>(null);
|
const [editingRowIndex, setEditingRowIndex] = useState<number | null>(null);
|
||||||
|
const [showUnsavedAlert, setShowUnsavedAlert] = useState(false);
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
// Detect changes
|
||||||
|
const hasChanges = useMemo(() => {
|
||||||
|
const rolesChanged = JSON.stringify(roles) !== JSON.stringify(initialRoles);
|
||||||
|
const nameChanged = name !== initialName;
|
||||||
|
const currencyChanged = currency !== initialCurrency;
|
||||||
|
return rolesChanged || nameChanged || currencyChanged;
|
||||||
|
}, [roles, name, currency, initialRoles, initialName, initialCurrency]);
|
||||||
|
|
||||||
const getJobTitles = useMemo(() => {
|
const getJobTitles = useMemo(() => {
|
||||||
return async () => {
|
return async () => {
|
||||||
@@ -74,12 +85,10 @@ const RatecardDrawer = ({
|
|||||||
};
|
};
|
||||||
}, [pagination.current, pagination.pageSize, pagination.field, pagination.order, searchQuery]);
|
}, [pagination.current, pagination.pageSize, pagination.field, pagination.order, searchQuery]);
|
||||||
|
|
||||||
// fetch rate cards data
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getJobTitles();
|
getJobTitles();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// get currently selected ratecard
|
|
||||||
const selectedRatecard = ratecardsList.find(
|
const selectedRatecard = ratecardsList.find(
|
||||||
(ratecard) => ratecard.id === ratecardId
|
(ratecard) => ratecard.id === ratecardId
|
||||||
);
|
);
|
||||||
@@ -88,24 +97,22 @@ const RatecardDrawer = ({
|
|||||||
if (type === 'update' && ratecardId) {
|
if (type === 'update' && ratecardId) {
|
||||||
dispatch(fetchRateCardById(ratecardId));
|
dispatch(fetchRateCardById(ratecardId));
|
||||||
}
|
}
|
||||||
// ...reset logic for create...
|
|
||||||
}, [type, ratecardId, dispatch]);
|
}, [type, ratecardId, dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (type === 'update' && drawerRatecard) {
|
if (type === 'update' && drawerRatecard) {
|
||||||
setRoles(drawerRatecard.jobRolesList || []);
|
setRoles(drawerRatecard.jobRolesList || []);
|
||||||
|
setInitialRoles(drawerRatecard.jobRolesList || []);
|
||||||
setName(drawerRatecard.name || '');
|
setName(drawerRatecard.name || '');
|
||||||
|
setInitialName(drawerRatecard.name || '');
|
||||||
setCurrency(drawerRatecard.currency || 'USD');
|
setCurrency(drawerRatecard.currency || 'USD');
|
||||||
|
setInitialCurrency(drawerRatecard.currency || 'USD');
|
||||||
}
|
}
|
||||||
}, [drawerRatecard, type]);
|
}, [drawerRatecard, type]);
|
||||||
|
|
||||||
// Add All handler
|
|
||||||
const handleAddAllRoles = () => {
|
const handleAddAllRoles = () => {
|
||||||
if (!jobTitles.data) return;
|
if (!jobTitles.data) return;
|
||||||
// Get current job_title_ids in roles
|
|
||||||
const existingIds = new Set(roles.map(r => r.job_title_id));
|
const existingIds = new Set(roles.map(r => r.job_title_id));
|
||||||
// Only add job titles not already present
|
|
||||||
const newRoles = jobTitles.data
|
const newRoles = jobTitles.data
|
||||||
.filter(jt => jt.id && !existingIds.has(jt.id))
|
.filter(jt => jt.id && !existingIds.has(jt.id))
|
||||||
.map(jt => ({
|
.map(jt => ({
|
||||||
@@ -114,7 +121,6 @@ const RatecardDrawer = ({
|
|||||||
job_title_id: jt.id!,
|
job_title_id: jt.id!,
|
||||||
rate: 0,
|
rate: 0,
|
||||||
}));
|
}));
|
||||||
// Prevent any accidental duplicates by merging and filtering again
|
|
||||||
const mergedRoles = [...roles, ...newRoles].filter(
|
const mergedRoles = [...roles, ...newRoles].filter(
|
||||||
(role, idx, arr) =>
|
(role, idx, arr) =>
|
||||||
arr.findIndex(r => r.job_title_id === role.job_title_id) === idx
|
arr.findIndex(r => r.job_title_id === role.job_title_id) === idx
|
||||||
@@ -122,7 +128,6 @@ const RatecardDrawer = ({
|
|||||||
setRoles(mergedRoles);
|
setRoles(mergedRoles);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleAddRole = () => {
|
const handleAddRole = () => {
|
||||||
const existingIds = new Set(roles.map(r => r.job_title_id));
|
const existingIds = new Set(roles.map(r => r.job_title_id));
|
||||||
const availableJobTitles = jobTitles.data?.filter(jt => !existingIds.has(jt.id!));
|
const availableJobTitles = jobTitles.data?.filter(jt => !existingIds.has(jt.id!));
|
||||||
@@ -138,8 +143,8 @@ const RatecardDrawer = ({
|
|||||||
updatedRoles.splice(index, 1);
|
updatedRoles.splice(index, 1);
|
||||||
setRoles(updatedRoles);
|
setRoles(updatedRoles);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectJobTitle = (jobTitleId: string) => {
|
const handleSelectJobTitle = (jobTitleId: string) => {
|
||||||
// Prevent duplicate job_title_id
|
|
||||||
if (roles.some(role => role.job_title_id === jobTitleId)) {
|
if (roles.some(role => role.job_title_id === jobTitleId)) {
|
||||||
setIsAddingRole(false);
|
setIsAddingRole(false);
|
||||||
setSelectedJobTitleId(undefined);
|
setSelectedJobTitleId(undefined);
|
||||||
@@ -162,7 +167,6 @@ const RatecardDrawer = ({
|
|||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (type === 'update' && ratecardId) {
|
if (type === 'update' && ratecardId) {
|
||||||
try {
|
try {
|
||||||
// Filter out roles with no jobtitle or empty jobtitle
|
|
||||||
const filteredRoles = roles.filter(role => role.jobtitle && role.jobtitle.trim() !== '');
|
const filteredRoles = roles.filter(role => role.jobtitle && role.jobtitle.trim() !== '');
|
||||||
await dispatch(updateRateCard({
|
await dispatch(updateRateCard({
|
||||||
id: ratecardId,
|
id: ratecardId,
|
||||||
@@ -172,7 +176,6 @@ const RatecardDrawer = ({
|
|||||||
jobRolesList: filteredRoles,
|
jobRolesList: filteredRoles,
|
||||||
},
|
},
|
||||||
}) as any);
|
}) as any);
|
||||||
// Refresh the rate cards list in Redux
|
|
||||||
await dispatch(fetchRateCards({
|
await dispatch(fetchRateCards({
|
||||||
index: 1,
|
index: 1,
|
||||||
size: 10,
|
size: 10,
|
||||||
@@ -182,18 +185,24 @@ const RatecardDrawer = ({
|
|||||||
}) as any);
|
}) as any);
|
||||||
if (onSaved) onSaved();
|
if (onSaved) onSaved();
|
||||||
dispatch(toggleRatecardDrawer());
|
dispatch(toggleRatecardDrawer());
|
||||||
|
// Reset initial states after save
|
||||||
|
setInitialRoles(filteredRoles);
|
||||||
|
setInitialName(name);
|
||||||
|
setInitialCurrency(currency);
|
||||||
|
setShowUnsavedAlert(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update rate card', error);
|
console.error('Failed to update rate card', error);
|
||||||
} finally {
|
} finally {
|
||||||
setRoles([]);
|
setRoles([]);
|
||||||
setName('Untitled Rate Card');
|
setName('Untitled Rate Card');
|
||||||
setCurrency('USD');
|
setCurrency('USD');
|
||||||
|
setInitialRoles([]);
|
||||||
|
setInitialName('Untitled Rate Card');
|
||||||
|
setInitialCurrency('USD');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// table columns
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t('jobTitleColumn'),
|
title: t('jobTitleColumn'),
|
||||||
@@ -208,7 +217,6 @@ const RatecardDrawer = ({
|
|||||||
style={{ minWidth: 150 }}
|
style={{ minWidth: 150 }}
|
||||||
value={record.job_title_id || undefined}
|
value={record.job_title_id || undefined}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
// Prevent duplicate job_title_id
|
|
||||||
if (roles.some((role, idx) => role.job_title_id === value && idx !== index)) {
|
if (roles.some((role, idx) => role.job_title_id === value && idx !== index)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -241,11 +249,9 @@ const RatecardDrawer = ({
|
|||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Render as clickable text for existing rows
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
// onClick={() => setEditingRowIndex(index)}
|
|
||||||
>
|
>
|
||||||
{record.jobtitle}
|
{record.jobtitle}
|
||||||
</span>
|
</span>
|
||||||
@@ -287,9 +293,7 @@ const RatecardDrawer = ({
|
|||||||
okText={t('deleteConfirmationOk')}
|
okText={t('deleteConfirmationOk')}
|
||||||
cancelText={t('deleteConfirmationCancel')}
|
cancelText={t('deleteConfirmationCancel')}
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
if (index) {
|
handleDeleteRole(index);
|
||||||
handleDeleteRole(index);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip title="Delete">
|
<Tooltip title="Delete">
|
||||||
@@ -299,22 +303,43 @@ const RatecardDrawer = ({
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const handleDrawerClose = async () => {
|
|
||||||
if (
|
const handleDrawerClose = () => {
|
||||||
drawerRatecard &&
|
if (!name || name.trim() === '' || name === 'Untitled Rate Card') {
|
||||||
(drawerRatecard.jobRolesList?.length === 0 || !drawerRatecard.jobRolesList) &&
|
messageApi.open({
|
||||||
(name === 'Untitled Rate Card' || name === '' || name === undefined)
|
type: 'warning',
|
||||||
) {
|
content: t('ratecardNameRequired') || 'Rate card name is required.',
|
||||||
await dispatch(deleteRateCard(drawerRatecard.id as string));
|
});
|
||||||
|
return;
|
||||||
|
} else if (hasChanges) {
|
||||||
|
setShowUnsavedAlert(true);
|
||||||
|
} else {
|
||||||
|
dispatch(toggleRatecardDrawer());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmSave = async () => {
|
||||||
|
await handleSave();
|
||||||
|
setShowUnsavedAlert(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmDiscard = () => {
|
||||||
dispatch(toggleRatecardDrawer());
|
dispatch(toggleRatecardDrawer());
|
||||||
|
setRoles([]);
|
||||||
|
setName('Untitled Rate Card');
|
||||||
|
setCurrency('USD');
|
||||||
|
setInitialRoles([]);
|
||||||
|
setInitialName('Untitled Rate Card');
|
||||||
|
setInitialCurrency('USD');
|
||||||
|
setShowUnsavedAlert(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{contextHolder}
|
||||||
<Drawer
|
<Drawer
|
||||||
loading={drawerLoading}
|
loading={drawerLoading}
|
||||||
onClose={handleDrawerClose}
|
onClose={handleDrawerClose}
|
||||||
@@ -348,7 +373,6 @@ const RatecardDrawer = ({
|
|||||||
]}
|
]}
|
||||||
onChange={(value) => setCurrency(value)}
|
onChange={(value) => setCurrency(value)}
|
||||||
/>
|
/>
|
||||||
{/* Add All Button */}
|
|
||||||
<Button onClick={handleAddAllRoles} type="default">
|
<Button onClick={handleAddAllRoles} type="default">
|
||||||
{t('addAllButton') || 'Add All'}
|
{t('addAllButton') || 'Add All'}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -359,11 +383,32 @@ const RatecardDrawer = ({
|
|||||||
width={700}
|
width={700}
|
||||||
footer={
|
footer={
|
||||||
<Flex justify="end" gap={16} style={{ marginTop: 16 }}>
|
<Flex justify="end" gap={16} style={{ marginTop: 16 }}>
|
||||||
<Button style={{ marginBottom: 24 }} onClick={handleSave} type="primary" disabled={name === '' || name === 'Untitled Rate Card' && roles.length === 0}>{t('saveButton')}</Button>
|
<Button style={{ marginBottom: 24 }} onClick={handleSave} type="primary" disabled={name === '' || (name === 'Untitled Rate Card' && roles.length === 0)}>
|
||||||
|
{t('saveButton')}
|
||||||
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{/* ratecard Table directly inside the Drawer */}
|
{showUnsavedAlert && (
|
||||||
|
<Alert
|
||||||
|
message={t('unsavedChangesTitle') || 'Unsaved Changes'}
|
||||||
|
type="warning"
|
||||||
|
showIcon
|
||||||
|
closable
|
||||||
|
onClose={() => setShowUnsavedAlert(false)}
|
||||||
|
action={
|
||||||
|
<Space direction="horizontal">
|
||||||
|
<Button size="small" type="primary" onClick={handleConfirmSave}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
<Button size="small" danger onClick={handleConfirmDiscard}>
|
||||||
|
Discard
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
style={{ marginBottom: 16 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Table
|
<Table
|
||||||
dataSource={roles}
|
dataSource={roles}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -381,7 +426,9 @@ const RatecardDrawer = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RatecardDrawer;
|
export default RatecardDrawer;
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
deleteProjectRateCardRoleById,
|
deleteProjectRateCardRoleById,
|
||||||
fetchProjectRateCardRoles,
|
fetchProjectRateCardRoles,
|
||||||
insertProjectRateCardRole,
|
insertProjectRateCardRole,
|
||||||
|
updateProjectRateCardRoleById,
|
||||||
updateProjectRateCardRolesByProjectId,
|
updateProjectRateCardRolesByProjectId,
|
||||||
} from '@/features/finance/project-finance-slice';
|
} from '@/features/finance/project-finance-slice';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@@ -18,6 +19,7 @@ import { jobTitlesApiService } from '@/api/settings/job-titles/job-titles.api.se
|
|||||||
import RateCardAssigneeSelector from '@/components/project-ratecard/ratecard-assignee-selector';
|
import RateCardAssigneeSelector from '@/components/project-ratecard/ratecard-assignee-selector';
|
||||||
import { projectsApiService } from '@/api/projects/projects.api.service';
|
import { projectsApiService } from '@/api/projects/projects.api.service';
|
||||||
import { IProjectMemberViewModel } from '@/types/projectMember.types';
|
import { IProjectMemberViewModel } from '@/types/projectMember.types';
|
||||||
|
import { parse } from 'path';
|
||||||
|
|
||||||
const RatecardTable: React.FC = () => {
|
const RatecardTable: React.FC = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -226,6 +228,17 @@ const RatecardTable: React.FC = () => {
|
|||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
}}
|
}}
|
||||||
onChange={(e) => handleRateChange(e.target.value, index)}
|
onChange={(e) => handleRateChange(e.target.value, index)}
|
||||||
|
onBlur={(e) => {
|
||||||
|
if (e.target.value !== roles[index].rate) {
|
||||||
|
dispatch(updateProjectRateCardRoleById({
|
||||||
|
id: roles[index].id!,
|
||||||
|
body: {
|
||||||
|
job_title_id: roles[index].job_title_id,
|
||||||
|
rate: e.target.value,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const RatecardSettings: React.FC = () => {
|
|||||||
const { t } = useTranslation('/settings/ratecard-settings');
|
const { t } = useTranslation('/settings/ratecard-settings');
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
useDocumentTitle('Manage Rate Cards');
|
useDocumentTitle('Manage Rate Cards');
|
||||||
|
const isDrawerOpen = useAppSelector(state => state.financeReducer.isRatecardDrawerOpen);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [ratecardsList, setRatecardsList] = useState<RatecardType[]>([]);
|
const [ratecardsList, setRatecardsList] = useState<RatecardType[]>([]);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
@@ -87,7 +87,7 @@ const RatecardSettings: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchRateCards();
|
fetchRateCards();
|
||||||
}, [toggleRatecardDrawer, dispatch]);
|
}, [toggleRatecardDrawer, isDrawerOpen]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user