Skip to content

Commit c84025e

Browse files
Copilotimnasnainaec
andcommitted
Add frontend support for displaying user projects in deletion confirmation
Co-authored-by: imnasnainaec <6411521+imnasnainaec@users.noreply.github.com>
1 parent dfa9516 commit c84025e

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

public/locales/en/translation.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@
133133
"userList": "Users",
134134
"deleteUser": {
135135
"confirm": "Confirm deleting user from The Combine database.",
136+
"loadingProjects": "Loading user projects...",
137+
"projectsTitle": "This user has roles in the following projects:",
138+
"noProjects": "This user has no project roles.",
136139
"toastSuccess": "User successfully deleted from The Combine.",
137140
"toastFailure": "Failed to delete user from The Combine."
138141
},

src/backend/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ import { FileWithSpeakerId } from "types/word";
3838
import { Bcp47Code } from "types/writingSystem";
3939
import { convertGoalToEdit } from "utilities/goalUtilities";
4040

41+
export interface UserProjectInfo {
42+
projectId: string;
43+
projectName: string;
44+
role: Role;
45+
}
46+
4147
export const baseURL = `${RuntimeConfig.getInstance().baseUrl()}`;
4248
const apiBaseURL = `${baseURL}/v1`;
4349
const config_parameters: Api.ConfigurationParameters = { basePath: baseURL };
@@ -767,6 +773,17 @@ export async function deleteUser(userId: string): Promise<void> {
767773
await userApi.deleteUser({ userId }, defaultOptions());
768774
}
769775

776+
/** Note: Only usable by site admins. */
777+
export async function getUserProjects(
778+
userId: string
779+
): Promise<UserProjectInfo[]> {
780+
const response = await axiosInstance.get<UserProjectInfo[]>(
781+
`/users/${userId}/projects`,
782+
defaultOptions()
783+
);
784+
return response.data;
785+
}
786+
770787
/** Checks whether email address is okay: unchanged or not taken by a different user. */
771788
export async function isEmailOkay(email: string): Promise<boolean> {
772789
const user = await getCurrentUser();

src/components/SiteSettings/UserManagement/ConfirmDeletion.tsx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { Box, Button, Stack, Typography } from "@mui/material";
2-
import { Fragment, ReactElement } from "react";
2+
import { Fragment, ReactElement, useEffect, useState } from "react";
33
import { useTranslation } from "react-i18next";
44

55
import { User } from "api/models";
6+
import { getUserProjects, UserProjectInfo } from "backend";
67

78
interface ConfirmDeletionProps {
89
user?: User;
@@ -14,10 +15,32 @@ export default function ConfirmDeletion(
1415
props: ConfirmDeletionProps
1516
): ReactElement {
1617
const { t } = useTranslation();
18+
const [userProjects, setUserProjects] = useState<UserProjectInfo[]>([]);
19+
const [loading, setLoading] = useState(false);
20+
21+
useEffect(() => {
22+
if (props.user) {
23+
setLoading(true);
24+
getUserProjects(props.user.id)
25+
.then((projects) => {
26+
setUserProjects(projects);
27+
})
28+
.catch((err) => {
29+
console.error("Failed to fetch user projects:", err);
30+
setUserProjects([]);
31+
})
32+
.finally(() => {
33+
setLoading(false);
34+
});
35+
} else {
36+
setUserProjects([]);
37+
}
38+
}, [props.user]);
1739

1840
if (!props.user) {
1941
return <Fragment />;
2042
}
43+
2144
return (
2245
<Box sx={{ maxWidth: 500 }}>
2346
<Stack spacing={2}>
@@ -29,6 +52,31 @@ export default function ConfirmDeletion(
2952
{t("siteSettings.deleteUser.confirm")}
3053
</Typography>
3154

55+
{loading ? (
56+
<Typography align="center">
57+
{t("siteSettings.deleteUser.loadingProjects")}
58+
</Typography>
59+
) : userProjects.length > 0 ? (
60+
<>
61+
<Typography align="center" variant="subtitle1">
62+
{t("siteSettings.deleteUser.projectsTitle")}
63+
</Typography>
64+
<Box sx={{ maxHeight: 200, overflowY: "auto" }}>
65+
<Stack spacing={1}>
66+
{userProjects.map((project) => (
67+
<Typography key={project.projectId} variant="body2">
68+
{project.projectName} ({project.role})
69+
</Typography>
70+
))}
71+
</Stack>
72+
</Box>
73+
</>
74+
) : (
75+
<Typography align="center" variant="body2">
76+
{t("siteSettings.deleteUser.noProjects")}
77+
</Typography>
78+
)}
79+
3280
<Stack direction="row" justifyContent="space-evenly">
3381
<Button
3482
color="secondary"

0 commit comments

Comments
 (0)