Skip to content
Snippets Groups Projects
Commit 9b63411d authored by Imre Toth's avatar Imre Toth
Browse files

Merge branch 'CE-1802_Create_user_page' into 'develop'

Resolve CE-1802 "Create user page"

See merge request !316
parents 58861173 d1b61187
No related branches found
No related tags found
2 merge requests!407CE-2141: 3.0.0,!316Resolve CE-1802 "Create user page"
Pipeline #156035 passed
...@@ -27,6 +27,7 @@ import { RecordListView } from "./views/records/RecordListView"; ...@@ -27,6 +27,7 @@ import { RecordListView } from "./views/records/RecordListView";
import { RecordDetailsView } from "./views/records/RecordDetailsView"; import { RecordDetailsView } from "./views/records/RecordDetailsView";
import { GlobalAppBarContext } from "@ess-ics/ce-ui-common"; import { GlobalAppBarContext } from "@ess-ics/ce-ui-common";
import { applicationTitle } from "./components/common/Helper"; import { applicationTitle } from "./components/common/Helper";
import { UserPageView } from "./views/UserPage/UserPageView";
// setting up the application (TAB)title // setting up the application (TAB)title
function App() { function App() {
...@@ -112,6 +113,11 @@ function App() { ...@@ -112,6 +113,11 @@ function App() {
element={<LoginView />} element={<LoginView />}
exact exact
/> />
<Route
path="/user/:userName"
element={<UserPageView />}
exact
/>
<Route <Route
path="*" path="*"
element={<NotFound />} element={<NotFound />}
......
...@@ -563,8 +563,10 @@ export function useUser() { ...@@ -563,8 +563,10 @@ export function useUser() {
console.warn("useUser error: ", message); console.warn("useUser error: ", message);
}, []); }, []);
const [userInfo, getUserInfo, resetUserInfo, userInfoLoading] = const [userInfo, getUserInfo, resetUserInfo, userInfoLoading] = useUserInfo(
useUserInfo(onError); null,
onError
);
const [userRoles, getUserRoles, resetUserRoles, userRolesLoading] = const [userRoles, getUserRoles, resetUserRoles, userRolesLoading] =
useUserRoles(onError); useUserRoles(onError);
const [user, setUser] = useState(); const [user, setUser] = useState();
...@@ -598,10 +600,13 @@ export function useUser() { ...@@ -598,10 +600,13 @@ export function useUser() {
return [user, getUser, resetUser, loading]; return [user, getUser, resetUser, loading];
} }
export function useUserInfo(onError) { export function useUserInfo(userName, onError) {
const api = useContext(apiContext); const api = useContext(apiContext);
const method = useCallAndUnpack(api.apis.Git.infoFromUserName, unpackUser); const method = useCallAndUnpack(api.apis.Git.infoFromUserName, unpackUser);
return useAsync({ fcn: method, call: false, onError: onError }); const boundMethod = useCallback(method.bind(null, { user_name: userName }), [
userName
]);
return useAsync({ fcn: boundMethod, call: false, onError: onError });
} }
export function unpackUserRoles(roles) { export function unpackUserRoles(roles) {
......
import React from "react";
import { Card, CardHeader } from "@mui/material";
import { useState, useEffect } from "react";
import { useIOCSearch } from "../../../api/SwaggerApi";
import { IOCAsyncList } from "../../IOC/IOCAsyncList";
import { initRequestParams } from "../Helper";
export function UserIocList({ userName }) {
const [iocs, getIocs, , loading] = useIOCSearch();
const [iocList, setIocList] = useState([]);
const rowsPerPage = [20, 50];
const [lazyParams, setLazyParams] = useState({
first: 0,
rows: 20,
page: 0
});
useEffect(() => {
setIocList(iocs.iocList);
}, [iocs, setIocList]);
useEffect(() => {
let requestParams = initRequestParams(lazyParams);
requestParams.owner = userName;
getIocs(requestParams);
}, [getIocs, lazyParams, userName]);
return (
<Card>
<CardHeader
title={`IOCs registered by ${userName}`}
titleTypographyProps={{
variant: "h2",
component: "h2"
}}
/>
<IOCAsyncList
iocs={iocList}
setIocs={setIocList}
loading={loading}
rowType="explore"
lazyParams={lazyParams}
setLazyParams={setLazyParams}
totalCount={iocs.totalCount}
rowsPerPage={rowsPerPage}
/>
</Card>
);
}
import React from "react";
import { Card, CardHeader } from "@mui/material";
import { initRequestParams } from "../Helper";
import { useEffect, useState } from "react";
import { useOperationsSearch } from "../../../api/SwaggerApi";
import { JobAsyncList } from "../../Job/JobAsyncList";
export function UserOperationList({ userName }) {
const [operations, getOperations, , loading] = useOperationsSearch();
const [lazyParams, setLazyParams] = useState({
first: 0,
rows: 20,
page: 0
});
const rowsPerPage = [20, 50];
const [jobList, setJobList] = useState([]);
useEffect(() => {
let requestParams = initRequestParams(lazyParams);
requestParams.user = userName;
getOperations(requestParams);
}, [getOperations, lazyParams, userName]);
useEffect(() => {
setJobList(operations.operationsList);
}, [operations, setJobList]);
return (
<Card>
<CardHeader
title={`Operations by ${userName}`}
titleTypographyProps={{
variant: "h2",
component: "h2"
}}
/>
<JobAsyncList
jobs={jobList}
setJobs={setJobList}
loading={loading}
totalCount={operations.totalCount}
lazyParams={lazyParams}
setLazyParams={setLazyParams}
rowsPerPage={rowsPerPage}
/>
</Card>
);
}
import React from "react";
import { Avatar, Card, CardHeader, Typography } from "@mui/material";
import { KeyValueTable } from "@ess-ics/ce-ui-common/dist/components/common/KeyValueTable";
export function UserProfile({ userInfo, errorMessage }) {
const userData = {
username: userInfo?.loginName,
avatar: (
<Avatar
src={userInfo?.avatar}
variant="rounded"
/>
)
};
return (
<Card>
<CardHeader
title="User profile page"
titleTypographyProps={{
variant: "h2",
component: "h2"
}}
/>
{errorMessage ? (
<Typography>{errorMessage}</Typography>
) : (
<KeyValueTable
obj={userData}
variant="overline"
/>
)}
</Card>
);
}
import { UserIocList } from "./UserIOCList";
import { UserOperationList } from "./UserOperationList";
import { UserProfile } from "./UserProfile";
export { UserIocList, UserOperationList, UserProfile };
...@@ -8,6 +8,7 @@ import React, { ...@@ -8,6 +8,7 @@ import React, {
import { string } from "prop-types"; import { string } from "prop-types";
import { Button, Avatar, Chip } from "@mui/material"; import { Button, Avatar, Chip } from "@mui/material";
import LockOpenIcon from "@mui/icons-material/LockOpen"; import LockOpenIcon from "@mui/icons-material/LockOpen";
import PersonIcon from "@mui/icons-material/Person";
import { import {
userContext, userContext,
LoginDialog, LoginDialog,
...@@ -21,6 +22,7 @@ const propTypes = { ...@@ -21,6 +22,7 @@ const propTypes = {
export function ProfileMenu({ user }) { export function ProfileMenu({ user }) {
const { userRoles, logout } = useContext(userContext); const { userRoles, logout } = useContext(userContext);
const navigate = useNavigate();
const objUserRoles = userRoles const objUserRoles = userRoles
?.filter((role) => role.includes("DeploymentTool")) ?.filter((role) => role.includes("DeploymentTool"))
...@@ -33,6 +35,10 @@ export function ProfileMenu({ user }) { ...@@ -33,6 +35,10 @@ export function ProfileMenu({ user }) {
) )
})); }));
function userProfile() {
navigate(`user/${user.loginName}`);
}
const profileMenuProps = { const profileMenuProps = {
/* /*
If editing this id, be sure to edit the check for this in If editing this id, be sure to edit the check for this in
...@@ -52,6 +58,11 @@ export function ProfileMenu({ user }) { ...@@ -52,6 +58,11 @@ export function ProfileMenu({ user }) {
), ),
menuItems: [ menuItems: [
...objUserRoles, ...objUserRoles,
{
text: "User profile page",
action: () => userProfile(),
icon: <PersonIcon fontSize="small" />
},
{ {
text: "Logout", text: "Logout",
action: () => logout(), action: () => logout(),
......
import React, { useContext, useState } from "react";
import { Grid } from "@mui/material";
import { styled } from "@mui/material/styles";
import { RootContainer } from "../../components/common/Container/RootContainer";
import { useParams } from "react-router-dom";
import { useUserInfo } from "../../api/SwaggerApi";
import { userContext } from "@ess-ics/ce-ui-common/dist/contexts/User";
import { UserProfile } from "../../components/common/User/UserProfile";
import { useEffect } from "react";
import { UserIocList } from "../../components/common/User/UserIOCList";
import { UserOperationList } from "../../components/common/User/UserOperationList";
const PREFIX = "UserPage";
const classes = {
paper: `${PREFIX}-paper`
};
const StyledRootContainer = styled(RootContainer)(({ theme }) => ({
[`& .${classes.paper}`]: {
maxWidth: "80%",
padding: theme.spacing(4)
}
}));
export function UserPageView() {
const { userName } = useParams();
const { user } = useContext(userContext);
const [errorMessage, setErrorMessage] = useState();
const [userInfo, getUserInfo, ,] = useUserInfo(userName, (m, s) => {
// log error message for userInfo
console.warn(m);
// user not found
if (s === 404) {
setErrorMessage("User not found!");
// do not show snackbar error
return false;
}
// user doesn't have permission to fetch userInfo
if (s === 401) {
setErrorMessage("No activity");
// do not show snackbar error
return false;
}
// show any other errors with snackbar
});
useEffect(() => {
// user logs in => clear error message, and try to re-request userInfo
if (user) {
setErrorMessage(null);
} else {
// user is not logged in => show a message
setErrorMessage("No activity!");
}
}, [user]);
useEffect(() => {
// request userInfo only if user is logged in
if (user) {
getUserInfo();
}
}, [getUserInfo, userName, user]);
// content for userActivity, e.g. operations that the user done, or IOCs that owns
const userActivityContent = (
<>
<Grid
item
xs={12}
>
<UserOperationList userName={userName} />
</Grid>
<Grid
item
xs={12}
>
<UserIocList userName={userName} />
</Grid>
</>
);
// show userPage for everyone, but hide userActivity for non-logged in users, and for not existing user
const content = (
<>
<Grid
item
xs={12}
>
<UserProfile
userInfo={userInfo}
errorMessage={errorMessage}
/>
</Grid>
{user && !errorMessage && userActivityContent}
</>
);
return (
<StyledRootContainer>
<Grid
container
spacing={1}
>
{content}
</Grid>
</StyledRootContainer>
);
}
import { UserPageView } from "./UserPageView";
export { UserPageView };
export default UserPageView;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment