Merge pull request #137 from shancds/feature/project-finance
Feature/project finance (project-ratecard-member-add)
This commit is contained in:
@@ -18,7 +18,7 @@ const initialState: financeState = {
|
||||
isRatecardDrawerOpen: false,
|
||||
isFinanceDrawerOpen: false,
|
||||
isImportRatecardsDrawerOpen: false,
|
||||
currency: 'LKR',
|
||||
currency: 'USD',
|
||||
isRatecardsLoading: false,
|
||||
isFinanceDrawerloading: false,
|
||||
drawerRatecard: null,
|
||||
|
||||
@@ -25,7 +25,6 @@ export const fetchProjectRateCardRoles = createAsyncThunk(
|
||||
async (project_id: string, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await projectRateCardApiService.getFromProjectId(project_id);
|
||||
console.log('Project RateCard Roles:', response);
|
||||
return response.body;
|
||||
} catch (error) {
|
||||
logger.error('Fetch Project RateCard Roles', error);
|
||||
@@ -63,6 +62,23 @@ export const insertProjectRateCardRoles = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
export const insertProjectRateCardRole = createAsyncThunk(
|
||||
'projectFinance/insertOne',
|
||||
async (
|
||||
{ project_id, job_title_id, rate }: { project_id: string; job_title_id: string; rate: number },
|
||||
{ rejectWithValue }
|
||||
) => {
|
||||
try {
|
||||
const response = await projectRateCardApiService.insertOne({ project_id, job_title_id, rate });
|
||||
return response.body;
|
||||
} catch (error) {
|
||||
logger.error('Insert Project RateCard Role', error);
|
||||
if (error instanceof Error) return rejectWithValue(error.message);
|
||||
return rejectWithValue('Failed to insert project rate card role');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const updateProjectRateCardRoleById = createAsyncThunk(
|
||||
'projectFinance/updateById',
|
||||
async ({ id, body }: { id: string; body: { job_title_id: string; rate: string } }, { rejectWithValue }) => {
|
||||
@@ -105,6 +121,14 @@ export const deleteProjectRateCardRoleById = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
export const assignMemberToRateCardRole = createAsyncThunk(
|
||||
'projectFinance/assignMemberToRateCardRole',
|
||||
async ({ project_id, member_id, project_rate_card_role_id }: { project_id: string; member_id: string; project_rate_card_role_id: string }) => {
|
||||
const response = await projectRateCardApiService.updateMemberRateCardRole(project_id, member_id, project_rate_card_role_id);
|
||||
return response.body;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteProjectRateCardRolesByProjectId = createAsyncThunk(
|
||||
'projectFinance/deleteByProjectId',
|
||||
async (project_id: string, { rejectWithValue }) => {
|
||||
|
||||
@@ -43,7 +43,7 @@ const RatecardDrawer = ({
|
||||
const [isAddingRole, setIsAddingRole] = useState(false);
|
||||
const [selectedJobTitleId, setSelectedJobTitleId] = useState<string | undefined>(undefined);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [currency, setCurrency] = useState('LKR');
|
||||
const [currency, setCurrency] = useState('USD');
|
||||
const [name, setName] = useState<string>('Untitled Rate Card');
|
||||
const [jobTitles, setJobTitles] = useState<IJobTitlesViewModel>({});
|
||||
const [pagination, setPagination] = useState<PaginationType>({
|
||||
@@ -95,7 +95,7 @@ const RatecardDrawer = ({
|
||||
if (type === 'update' && drawerRatecard) {
|
||||
setRoles(drawerRatecard.jobRolesList || []);
|
||||
setName(drawerRatecard.name || '');
|
||||
setCurrency(drawerRatecard.currency || 'LKR');
|
||||
setCurrency(drawerRatecard.currency || 'USD');
|
||||
}
|
||||
}, [drawerRatecard, type]);
|
||||
|
||||
@@ -121,15 +121,17 @@ const RatecardDrawer = ({
|
||||
setRoles(mergedRoles);
|
||||
};
|
||||
|
||||
|
||||
const handleAddRole = () => {
|
||||
// Only allow adding if there are job titles not already in roles
|
||||
const existingIds = new Set(roles.map(r => r.job_title_id));
|
||||
const availableJobTitles = jobTitles.data?.filter(jt => !existingIds.has(jt.id!));
|
||||
if (availableJobTitles && availableJobTitles.length > 0) {
|
||||
setRoles([...roles, { job_title_id: '', rate: 0 }]);
|
||||
setAddingRowIndex(roles.length); // index of the new row
|
||||
setAddingRowIndex(roles.length);
|
||||
setIsAddingRole(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteRole = (index: number) => {
|
||||
const updatedRoles = [...roles];
|
||||
updatedRoles.splice(index, 1);
|
||||
@@ -185,7 +187,7 @@ const RatecardDrawer = ({
|
||||
} finally {
|
||||
setRoles([]);
|
||||
setName('Untitled Rate Card');
|
||||
setCurrency('LKR');
|
||||
setCurrency('USD');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -218,6 +220,9 @@ const RatecardDrawer = ({
|
||||
setAddingRowIndex(null);
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (roles[index].job_title_id === ""){
|
||||
handleDeleteRole(index);
|
||||
}
|
||||
setEditingRowIndex(null);
|
||||
setAddingRowIndex(null);
|
||||
}}
|
||||
@@ -239,7 +244,7 @@ const RatecardDrawer = ({
|
||||
return (
|
||||
<span
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => setEditingRowIndex(index)}
|
||||
// onClick={() => setEditingRowIndex(index)}
|
||||
>
|
||||
{record.jobtitle}
|
||||
</span>
|
||||
@@ -249,6 +254,7 @@ const RatecardDrawer = ({
|
||||
{
|
||||
title: `${t('ratePerHourColumn')} (${currency})`,
|
||||
dataIndex: 'rate',
|
||||
align: 'right',
|
||||
render: (text: number, record: any, index: number) => (
|
||||
<Input
|
||||
type="number"
|
||||
@@ -257,6 +263,7 @@ const RatecardDrawer = ({
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
boxShadow: 'none',
|
||||
textAlign: 'right',
|
||||
padding: 0,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
@@ -319,8 +326,8 @@ const RatecardDrawer = ({
|
||||
<Select
|
||||
value={currency}
|
||||
options={[
|
||||
{ value: 'LKR', label: 'LKR' },
|
||||
{ value: 'USD', label: 'USD' },
|
||||
{ value: 'LKR', label: 'LKR' },
|
||||
{ value: 'INR', label: 'INR' },
|
||||
]}
|
||||
onChange={(value) => setCurrency(value)}
|
||||
@@ -347,27 +354,7 @@ const RatecardDrawer = ({
|
||||
rowKey={(record) => record.job_title_id}
|
||||
pagination={false}
|
||||
footer={() => (
|
||||
isAddingRole ? (
|
||||
<Select
|
||||
showSearch
|
||||
style={{ minWidth: 200 }}
|
||||
placeholder={t('selectJobTitle')}
|
||||
optionFilterProp="children"
|
||||
value={selectedJobTitleId}
|
||||
onChange={handleSelectJobTitle}
|
||||
onBlur={() => setIsAddingRole(false)}
|
||||
filterOption={(input, option) =>
|
||||
(option?.children as string).toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
>
|
||||
{jobTitles.data?.map((jt) => (
|
||||
<Select.Option key={jt.id} value={jt.id}>
|
||||
{jt.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
) : (
|
||||
<Button
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={handleAddRole}
|
||||
block
|
||||
@@ -375,7 +362,6 @@ const RatecardDrawer = ({
|
||||
>
|
||||
{t('addRoleButton')}
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Drawer>
|
||||
|
||||
Reference in New Issue
Block a user