Skip to content

Commit 12e3f9b

Browse files
Cosmetics:
- [x] Removed Leave/Join buttons from the top of the project details page. Only left them in the Members section. - [x] Made Leave danger (red) and Join normal (blue border) - [x] Added confirmation dialog on Leave - [x] Updated a few localized messages
1 parent d29d2d2 commit 12e3f9b

File tree

4 files changed

+20
-90
lines changed

4 files changed

+20
-90
lines changed

frontend/src/locale/en.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,10 @@
172172
"runs": "Runs",
173173
"tags": "Tags",
174174
"settings": "Settings",
175-
"join": "Join Project",
176-
"leave": "Leave Project",
175+
"join": "Join",
176+
"leave": "Leave",
177+
"leave_confirm_title": "Leave project",
178+
"leave_confirm_message": "Are you sure you want to leave this project?",
177179
"join_success": "Successfully joined the project",
178180
"leave_success": "Successfully left the project",
179181
"join_error": "Failed to join project",
@@ -201,7 +203,7 @@
201203
"update_visibility_confirm_title": "Change project visibility",
202204
"update_visibility_confirm_message": "Are you sure you want to change the project visibility? This will affect who can access this project.",
203205
"change_visibility": "Change visibility",
204-
"project_visibility": "Project Visibility",
206+
"project_visibility": "Project visibility",
205207
"project_visibility_description": "Control who can access this project",
206208
"make_project_public": "Make project public",
207209
"delete_project_confirm_title": "Delete project",

frontend/src/pages/Project/Details/index.tsx

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,14 @@
1-
import React, { useMemo } from 'react';
2-
import { Outlet, useNavigate, useParams } from 'react-router-dom';
3-
import { useTranslation } from 'react-i18next';
1+
import React from 'react';
2+
import { Outlet, useParams } from 'react-router-dom';
43

5-
import { Button, ContentLayout, DetailsHeader } from 'components';
6-
7-
import { useAppSelector, useNotifications } from 'hooks';
8-
import { selectUserData } from 'App/slice';
9-
import { ROUTES } from 'routes';
10-
import { useGetProjectQuery, useAddProjectMemberMutation, useRemoveProjectMemberMutation } from 'services/project';
11-
import { getProjectRoleByUserName } from '../utils';
12-
import { useProjectMemberActions } from '../hooks/useProjectMemberActions';
4+
import { ContentLayout, DetailsHeader } from 'components';
135

146
export const ProjectDetails: React.FC = () => {
15-
const { t } = useTranslation();
167
const params = useParams();
17-
const navigate = useNavigate();
188
const paramProjectName = params.projectName ?? '';
19-
const userData = useAppSelector(selectUserData);
20-
const { handleJoinProject, handleLeaveProject, isMemberActionLoading } = useProjectMemberActions();
21-
22-
const { data: project } = useGetProjectQuery({ name: paramProjectName });
23-
24-
const currentUserRole = useMemo(() => {
25-
if (!userData?.username || !project) return null;
26-
return getProjectRoleByUserName(project, userData.username);
27-
}, [project, userData?.username]);
28-
29-
const isProjectOwner = userData?.username === project?.owner.username;
30-
31-
const isMember = currentUserRole !== null;
32-
33-
const renderJoinLeaveButton = () => {
34-
// Only show button if user is authenticated and project is loaded
35-
if (!userData?.username || !project) return null;
36-
37-
if (!isMember) {
38-
return (
39-
<Button
40-
onClick={() => handleJoinProject(project.project_name, userData.username!)}
41-
disabled={isMemberActionLoading}
42-
variant="primary"
43-
>
44-
{isMemberActionLoading ? t('common.loading') : t('projects.join')}
45-
</Button>
46-
);
47-
} else {
48-
// Check if user is the last admin - if so, don't show leave button
49-
const adminCount = project.members.filter(member => member.project_role === 'admin').length;
50-
const isLastAdmin = currentUserRole === 'admin' && adminCount <= 1;
51-
52-
if (isLastAdmin) {
53-
// Don't show leave button for the last admin
54-
return null;
55-
}
56-
57-
// Allow leaving for all other members
58-
return (
59-
<Button
60-
onClick={() => handleLeaveProject(project.project_name, userData.username!, () => navigate(ROUTES.PROJECT.LIST))}
61-
disabled={isMemberActionLoading}
62-
variant="normal"
63-
>
64-
{isMemberActionLoading
65-
? t('common.loading')
66-
: t('projects.leave')
67-
}
68-
</Button>
69-
);
70-
}
71-
};
729

7310
return (
74-
<ContentLayout
75-
header={
76-
<DetailsHeader
77-
title={paramProjectName}
78-
actionButtons={renderJoinLeaveButton()}
79-
/>
80-
}
81-
>
11+
<ContentLayout header={<DetailsHeader title={paramProjectName} />}>
8212
<Outlet />
8313
</ContentLayout>
8414
);

frontend/src/pages/Project/Members/index.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { useFieldArray, useForm } from 'react-hook-form';
33
import { useTranslation } from 'react-i18next';
44
import { useNavigate } from 'react-router-dom';
55

6-
import { Button, FormSelect, Header, Link, ListEmptyMessage, Pagination, SpaceBetween, Table } from 'components';
6+
import { Button, ButtonWithConfirmation, FormSelect, Header, Link, ListEmptyMessage, Pagination, SpaceBetween, Table } from 'components';
77

8-
import { useAppSelector, useCollection, useNotifications } from 'hooks';
8+
import { useAppSelector, useCollection } from 'hooks';
99
import { selectUserData } from 'App/slice';
1010
import { ROUTES } from 'routes';
1111
import { useGetUserListQuery } from 'services/user';
@@ -22,7 +22,6 @@ import styles from './styles.module.scss';
2222
export const ProjectMembers: React.FC<IProps> = ({ members, loading, onChange, readonly, isAdmin, project }) => {
2323
const { t } = useTranslation();
2424
const navigate = useNavigate();
25-
const [pushNotification] = useNotifications();
2625
const [selectedItems, setSelectedItems] = useState<TProjectMemberWithIndex[]>([]);
2726
const { data: usersData } = useGetUserListQuery();
2827
const userData = useAppSelector(selectUserData);
@@ -43,8 +42,6 @@ export const ProjectMembers: React.FC<IProps> = ({ members, loading, onChange, r
4342
return member?.project_role || null;
4443
}, [members, userData?.username]);
4544

46-
const isProjectOwner = userData?.username === project?.owner.username;
47-
4845
const isMember = currentUserRole !== null;
4946

5047
useEffect(() => {
@@ -132,7 +129,7 @@ export const ProjectMembers: React.FC<IProps> = ({ members, loading, onChange, r
132129
key="join"
133130
onClick={() => handleJoinProject(project.project_name, userData.username!)}
134131
disabled={isMemberActionLoading}
135-
variant="primary"
132+
variant="normal"
136133
>
137134
{isMemberActionLoading ? t('common.loading') : t('projects.join')}
138135
</Button>
@@ -145,17 +142,21 @@ export const ProjectMembers: React.FC<IProps> = ({ members, loading, onChange, r
145142
if (!isLastAdmin) {
146143
// Only show leave button if user is not the last admin
147144
actions.unshift(
148-
<Button
145+
<ButtonWithConfirmation
149146
key="leave"
150-
onClick={() => handleLeaveProject(project.project_name, userData.username!, () => navigate(ROUTES.PROJECT.LIST))}
151147
disabled={isMemberActionLoading}
152-
variant="normal"
148+
formAction="none"
149+
onClick={() => handleLeaveProject(project.project_name, userData.username!)}
150+
confirmTitle={t('projects.leave_confirm_title')}
151+
confirmContent={t('projects.leave_confirm_message')}
152+
confirmButtonLabel={t('projects.leave')}
153+
variant="danger-normal"
153154
>
154155
{isMemberActionLoading
155156
? t('common.loading')
156157
: t('projects.leave')
157158
}
158-
</Button>
159+
</ButtonWithConfirmation>
159160
);
160161
}
161162
}

frontend/src/pages/Project/hooks/useProjectMemberActions.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { useNavigate } from 'react-router-dom';
21
import { useTranslation } from 'react-i18next';
32

43
import { useNotifications } from 'hooks';
5-
import { ROUTES } from 'routes';
64
import { useAddProjectMemberMutation, useRemoveProjectMemberMutation } from 'services/project';
75

86
export const useProjectMemberActions = () => {
97
const { t } = useTranslation();
10-
const navigate = useNavigate();
118
const [pushNotification] = useNotifications();
129
const [addMember, { isLoading: isAdding }] = useAddProjectMemberMutation();
1310
const [removeMember, { isLoading: isRemoving }] = useRemoveProjectMemberMutation();

0 commit comments

Comments
 (0)