Enhance project member invitation feature with new localization and UI updates

- Added new localization strings for "invite member" and "also invite to project" across multiple languages (Albanian, German, English, Spanish, Portuguese, Chinese).
- Updated the project member invite drawer to conditionally display UI elements based on the context of the invitation (from assigner or not).
- Introduced a new state management feature to track if the invitation is initiated from the assignee selector, improving user experience.
This commit is contained in:
shancds
2025-07-22 16:21:18 +05:30
parent 3373dccc58
commit da791e2cb7
9 changed files with 41 additions and 14 deletions

View File

@@ -5,5 +5,7 @@
"inviteAsAMember": "Fto si anëtar", "inviteAsAMember": "Fto si anëtar",
"inviteNewMemberByEmail": "Fto anëtar të ri me email", "inviteNewMemberByEmail": "Fto anëtar të ri me email",
"members": "Anëtarë", "members": "Anëtarë",
"copyProjectLink": "Kopjo lidhjen e projektit" "copyProjectLink": "Kopjo lidhjen e projektit",
"inviteMember": "Fto anëtar",
"alsoInviteToProject": "Fto edhe në projekt"
} }

View File

@@ -5,5 +5,7 @@
"inviteAsAMember": "Als Mitglied einladen", "inviteAsAMember": "Als Mitglied einladen",
"inviteNewMemberByEmail": "Neues Mitglied per E-Mail einladen", "inviteNewMemberByEmail": "Neues Mitglied per E-Mail einladen",
"members": "Mitglieder", "members": "Mitglieder",
"copyProjectLink": "Projektlink kopieren" "copyProjectLink": "Projektlink kopieren",
"inviteMember": "Mitglied einladen",
"alsoInviteToProject": "Auch zum Projekt einladen"
} }

View File

@@ -5,5 +5,7 @@
"inviteAsAMember": "Invite as a member", "inviteAsAMember": "Invite as a member",
"inviteNewMemberByEmail": "Invite new member by email", "inviteNewMemberByEmail": "Invite new member by email",
"members": "Members", "members": "Members",
"copyProjectLink": "Copy project link" "copyProjectLink": "Copy project link",
"inviteMember": "Invite Member",
"alsoInviteToProject": "Also invite to project"
} }

View File

@@ -5,5 +5,7 @@
"inviteAsAMember": "Invitar como miembro", "inviteAsAMember": "Invitar como miembro",
"inviteNewMemberByEmail": "Invitar nuevo miembro por correo electrónico", "inviteNewMemberByEmail": "Invitar nuevo miembro por correo electrónico",
"members": "Miembros", "members": "Miembros",
"copyProjectLink": "Copiar enlace del proyecto" "copyProjectLink": "Copiar enlace del proyecto",
"inviteMember": "Invitar miembro",
"alsoInviteToProject": "También invitar al proyecto"
} }

View File

@@ -5,5 +5,7 @@
"inviteAsAMember": "Convidar como membro", "inviteAsAMember": "Convidar como membro",
"inviteNewMemberByEmail": "Convidar novo membro por e-mail", "inviteNewMemberByEmail": "Convidar novo membro por e-mail",
"members": "Membros", "members": "Membros",
"copyProjectLink": "Copiar link do projeto" "copyProjectLink": "Copiar link do projeto",
"inviteMember": "Convidar membro",
"alsoInviteToProject": "Convidar também para o projeto"
} }

View File

@@ -5,5 +5,7 @@
"inviteAsAMember": "邀请为成员", "inviteAsAMember": "邀请为成员",
"inviteNewMemberByEmail": "通过电子邮件邀请新成员", "inviteNewMemberByEmail": "通过电子邮件邀请新成员",
"members": "成员", "members": "成员",
"copyProjectLink": "复制项目链接" "copyProjectLink": "复制项目链接",
"inviteMember": "邀请成员",
"alsoInviteToProject": "也邀请到项目"
} }

View File

@@ -11,7 +11,7 @@ import { useAuthService } from '@/hooks/useAuth';
import { Avatar, Button, Checkbox } from '@/components'; import { Avatar, Button, Checkbox } from '@/components';
import { sortTeamMembers } from '@/utils/sort-team-members'; import { sortTeamMembers } from '@/utils/sort-team-members';
import { useAppDispatch } from '@/hooks/useAppDispatch'; import { useAppDispatch } from '@/hooks/useAppDispatch';
import { toggleProjectMemberDrawer } from '@/features/projects/singleProject/members/projectMembersSlice'; import { setIsFromAssigner, toggleProjectMemberDrawer } from '@/features/projects/singleProject/members/projectMembersSlice';
import { updateEnhancedKanbanTaskAssignees } from '@/features/enhanced-kanban/enhanced-kanban.slice'; import { updateEnhancedKanbanTaskAssignees } from '@/features/enhanced-kanban/enhanced-kanban.slice';
interface AssigneeSelectorProps { interface AssigneeSelectorProps {
@@ -206,6 +206,7 @@ const AssigneeSelector: React.FC<AssigneeSelectorProps> = ({
const handleInviteProjectMemberDrawer = () => { const handleInviteProjectMemberDrawer = () => {
setIsOpen(false); // Close the assignee dropdown first setIsOpen(false); // Close the assignee dropdown first
dispatch(setIsFromAssigner(true));
dispatch(toggleProjectMemberDrawer()); // Then open the invite drawer dispatch(toggleProjectMemberDrawer()); // Then open the invite drawer
}; };

View File

@@ -22,7 +22,7 @@ import { teamMembersApiService } from '@/api/team-members/teamMembers.api.servic
const ProjectMemberDrawer = () => { const ProjectMemberDrawer = () => {
const { t } = useTranslation('project-view/project-member-drawer'); const { t } = useTranslation('project-view/project-member-drawer');
const { isDrawerOpen, currentMembersList, isLoading } = useAppSelector( const { isDrawerOpen, currentMembersList, isLoading, isFromAssigner } = useAppSelector(
state => state.projectMemberReducer state => state.projectMemberReducer
); );
const { projectId } = useAppSelector(state => state.projectReducer); const { projectId } = useAppSelector(state => state.projectReducer);
@@ -176,8 +176,9 @@ const ProjectMemberDrawer = () => {
); );
const renderNotFoundContent = () => ( const renderNotFoundContent = () => (
<Flex> <Flex style={{ display: 'block'}}>
<Button <Button
className='mb-2'
block block
type="primary" type="primary"
onClick={sendInviteToProject} onClick={sendInviteToProject}
@@ -189,19 +190,23 @@ const ProjectMemberDrawer = () => {
{validateEmail(searchTerm) ? t('inviteAsAMember') : t('inviteNewMemberByEmail')} {validateEmail(searchTerm) ? t('inviteAsAMember') : t('inviteNewMemberByEmail')}
</span> </span>
</Button> </Button>
{isFromAssigner && <Flex>
<input className='mr-2' type="checkbox" checked={true} name={t('alsoInviteToProject')} id="AlsoInviteToProject" />
<label htmlFor={t('alsoInviteToProject')}>{t('alsoInviteToProject')}</label>
</Flex>}
</Flex> </Flex>
); );
return ( return (
<Modal <Modal
title={ title={
<Typography.Text style={{ fontWeight: 500, fontSize: 16 }}>{t('title')}</Typography.Text> <Typography.Text style={{ fontWeight: 500, fontSize: 16 }}>{isFromAssigner ? t('inviteMember') : t('title')}</Typography.Text>
} }
open={isDrawerOpen} open={isDrawerOpen}
onCancel={() => dispatch(toggleProjectMemberDrawer())} onCancel={() => dispatch(toggleProjectMemberDrawer())}
afterOpenChange={handleOpenChange} afterOpenChange={handleOpenChange}
footer={ footer={
<Button !isFromAssigner && <Button
style={{ width: 140, fontSize: 12 }} style={{ width: 140, fontSize: 12 }}
block block
icon={<LinkOutlined />} icon={<LinkOutlined />}
@@ -232,7 +237,7 @@ const ProjectMemberDrawer = () => {
/> />
</Form.Item> </Form.Item>
</Form> </Form>
<div style={{ fontSize: 14, fontWeight: 500, marginBottom: 8 }}>{t('members')}</div> {!isFromAssigner && <><div style={{ fontSize: 14, fontWeight: 500, marginBottom: 8 }}>{t('members')}</div>
<div style={{ maxHeight: 360, minHeight: 120, overflowY: 'auto', marginBottom: 16 }}> <div style={{ maxHeight: 360, minHeight: 120, overflowY: 'auto', marginBottom: 16 }}>
<List <List
loading={isLoading} loading={isLoading}
@@ -248,7 +253,8 @@ const ProjectMemberDrawer = () => {
</List.Item> </List.Item>
)} )}
/> />
</div> </div></>
}
</Modal> </Modal>
); );
}; };

View File

@@ -8,6 +8,7 @@ interface ProjectMembersState {
currentMembersList: IMentionMemberViewModel[]; currentMembersList: IMentionMemberViewModel[];
isDrawerOpen: boolean; isDrawerOpen: boolean;
isLoading: boolean; isLoading: boolean;
isFromAssigner: boolean;
error: string | null; error: string | null;
} }
@@ -16,6 +17,7 @@ const initialState: ProjectMembersState = {
currentMembersList: [], currentMembersList: [],
isDrawerOpen: false, isDrawerOpen: false,
isLoading: false, isLoading: false,
isFromAssigner: false,
error: null, error: null,
}; };
@@ -89,6 +91,12 @@ const projectMembersSlice = createSlice({
reducers: { reducers: {
toggleProjectMemberDrawer: state => { toggleProjectMemberDrawer: state => {
state.isDrawerOpen = !state.isDrawerOpen; state.isDrawerOpen = !state.isDrawerOpen;
if (state.isDrawerOpen === false) {
state.isFromAssigner = false;
}
},
setIsFromAssigner: (state, action: PayloadAction<boolean>) => {
state.isFromAssigner = action.payload;
}, },
}, },
extraReducers: builder => { extraReducers: builder => {
@@ -139,7 +147,7 @@ const projectMembersSlice = createSlice({
}, },
}); });
export const { toggleProjectMemberDrawer } = projectMembersSlice.actions; export const { toggleProjectMemberDrawer, setIsFromAssigner } = projectMembersSlice.actions;
export { export {
getProjectMembers, getProjectMembers,
getAllProjectMembers, getAllProjectMembers,