Merge pull request #145 from shancds/feature/project-finance
Feature/project finance
This commit is contained in:
@@ -35,7 +35,8 @@
|
||||
"ratecardsPluralText": "Rate Card Templates",
|
||||
"deleteConfirm": "Are you sure ?",
|
||||
"yes": "Yes",
|
||||
"no": "No"
|
||||
"no": "No",
|
||||
"alreadyImportedRateCardMessage": "A rate card has already been imported. Clear all imported rate cards to add a new one."
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ const RateCardAssigneeSelector = ({
|
||||
onChange,
|
||||
selectedMemberIds = [],
|
||||
memberlist = [],
|
||||
}: RateCardAssigneeSelectorProps) => {
|
||||
assignedMembers = [], // New prop: List of all assigned member IDs across all job titles
|
||||
}: RateCardAssigneeSelectorProps & { assignedMembers: string[] }) => {
|
||||
const membersInputRef = useRef<InputRef>(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [members, setMembers] = useState<IProjectMemberViewModel[]>(memberlist);
|
||||
@@ -46,7 +47,12 @@ const RateCardAssigneeSelector = ({
|
||||
/>
|
||||
<List style={{ padding: 0, maxHeight: 200, overflow: 'auto' }}>
|
||||
{filteredMembers.length ? (
|
||||
filteredMembers.map((member) => (
|
||||
filteredMembers.map((member) => {
|
||||
const isAssignedToAnotherJobTitle =
|
||||
assignedMembers.includes(member.id || '') &&
|
||||
!selectedMemberIds.includes(member.id || ''); // Check if the member is assigned elsewhere
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
key={member.id}
|
||||
style={{
|
||||
@@ -55,14 +61,14 @@ const RateCardAssigneeSelector = ({
|
||||
alignItems: 'center',
|
||||
padding: '4px 8px',
|
||||
border: 'none',
|
||||
opacity: member.pending_invitation ? 0.5 : 1,
|
||||
opacity: member.pending_invitation || isAssignedToAnotherJobTitle ? 0.5 : 1,
|
||||
justifyContent: 'flex-start',
|
||||
textAlign: 'left',
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selectedMemberIds.includes(member.id || '')}
|
||||
disabled={member.pending_invitation}
|
||||
disabled={member.pending_invitation || isAssignedToAnotherJobTitle}
|
||||
onChange={() => onChange?.(member.id || '')}
|
||||
/>
|
||||
<SingleAvatar
|
||||
@@ -72,7 +78,8 @@ const RateCardAssigneeSelector = ({
|
||||
/>
|
||||
<span>{member.name}</span>
|
||||
</List.Item>
|
||||
))
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Empty description="No members found" />
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Drawer, Typography, Button, Table, Menu, Flex, Spin } from 'antd';
|
||||
import { Drawer, Typography, Button, Table, Menu, Flex, Spin, Alert } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppSelector } from '../../../hooks/useAppSelector';
|
||||
@@ -84,10 +84,21 @@ const ImportRatecardsDrawer: React.FC = () => {
|
||||
</Typography.Text>
|
||||
}
|
||||
footer={
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{/* Alert message */}
|
||||
{rolesRedux.length !== 0 ? (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<Alert
|
||||
message={t('alreadyImportedRateCardMessage') || 'A rate card has already been imported. Clear all imported rate cards to add a new one.'}
|
||||
type="warning"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={rolesRedux.length !== 0}
|
||||
onClick={() => {
|
||||
if (!projectId) {
|
||||
// Handle missing project id (show error, etc.)
|
||||
@@ -112,6 +123,8 @@ const ImportRatecardsDrawer: React.FC = () => {
|
||||
{t('import')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
open={isDrawerOpen}
|
||||
onClose={() => dispatch(toggleImportRatecardsDrawer())}
|
||||
|
||||
@@ -307,8 +307,8 @@ const RatecardDrawer = ({
|
||||
},
|
||||
];
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
if (!name || name.trim() === '' || name === 'Untitled Rate Card') {
|
||||
const handleDrawerClose = async() => {
|
||||
if (!name || name.trim() === '') {
|
||||
messageApi.open({
|
||||
type: 'warning',
|
||||
content: t('ratecardNameRequired') || 'Rate card name is required.',
|
||||
@@ -316,7 +316,12 @@ const RatecardDrawer = ({
|
||||
return;
|
||||
} else if (hasChanges) {
|
||||
setShowUnsavedAlert(true);
|
||||
} else {
|
||||
}
|
||||
else if (name === 'Untitled Rate Card' && roles.length === 0){
|
||||
await dispatch(deleteRateCard(ratecardId));
|
||||
dispatch(toggleRatecardDrawer());
|
||||
}
|
||||
else {
|
||||
dispatch(toggleRatecardDrawer());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -191,6 +191,10 @@ const RatecardTable: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const assignedMembers = roles
|
||||
.flatMap((role) => role.members || [])
|
||||
.filter((memberId, index, self) => self.indexOf(memberId) === index);
|
||||
|
||||
// Columns
|
||||
const columns: TableProps<JobRoleType>['columns'] = [
|
||||
{
|
||||
@@ -267,6 +271,7 @@ const RatecardTable: React.FC = () => {
|
||||
selectedMemberIds={memberscol || []}
|
||||
onChange={(memberId) => handleMemberChange(memberId, index, record)}
|
||||
memberlist={members}
|
||||
assignedMembers={assignedMembers} // Pass assigned members here
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user