add check for hasWriteaccess

This commit is contained in:
Sina Blattmann 2023-04-06 14:31:31 +02:00
parent 85d982e62f
commit 7a74e5e5dc
14 changed files with 3514 additions and 51 deletions

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@
"sass": "^1.58.3", "sass": "^1.58.3",
"sass-loader": "^13.2.0", "sass-loader": "^13.2.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"testcafe": "^2.4.0",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },

View File

@ -48,7 +48,9 @@ const App = () => {
<Routes> <Routes>
<Route <Route
path="*" path="*"
element={<Navigate to={routes.installations} replace />} element={
<Navigate to={routes.installations + routes.list} replace />
}
/> />
<Route <Route
path={routes.installations + "*"} path={routes.installations + "*"}

View File

@ -1,9 +1,10 @@
import React, { useState } from "react"; import React, { useContext, useState } from "react";
import { Alert, CircularProgress, Grid } from "@mui/material"; import { Alert, CircularProgress, Grid } from "@mui/material";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import { axiosConfigWithoutToken } from "./config/axiosConfig"; import { axiosConfigWithoutToken } from "./config/axiosConfig";
import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield"; import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield";
import InnovenergyButton from "./components/Layout/InnovenergyButton"; import InnovenergyButton from "./components/Layout/InnovenergyButton";
import { UserContext } from "./components/Context/UserContextProvider";
const loginUser = async (username: string, password: string) => { const loginUser = async (username: string, password: string) => {
return axiosConfigWithoutToken.post("/Login", null, { return axiosConfigWithoutToken.post("/Login", null, {
@ -21,6 +22,7 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => {
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(); const [error, setError] = useState();
const { setCurrentUser } = useContext(UserContext);
const verifyToken = async (token: string) => { const verifyToken = async (token: string) => {
axiosConfigWithoutToken.get("/GetAllInstallations", { axiosConfigWithoutToken.get("/GetAllInstallations", {
@ -36,6 +38,7 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => {
verifyToken(data.token) verifyToken(data.token)
.then(() => { .then(() => {
setToken(data.token); setToken(data.token);
setCurrentUser(data.user);
setLoading(false); setLoading(false);
setLanguage(data.user.language); setLanguage(data.user.language);
}) })

View File

@ -4,13 +4,17 @@ import AvailableUserDialog from "./AvailableUserDialog";
import InnovenergyList from "./InnovenergyList"; import InnovenergyList from "./InnovenergyList";
import UsersWithDirectAccess from "./UsersWithDirectAccess"; import UsersWithDirectAccess from "./UsersWithDirectAccess";
import UsersWithInheritedAccess from "./UsersWithInheritedAccess"; import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
import { useContext } from "react";
import { UserContext } from "../../Context/UserContextProvider";
const AccessManagement = () => { const AccessManagement = () => {
const { currentUser } = useContext(UserContext);
return ( return (
<UsersContextProvider> <UsersContextProvider>
<Grid container sx={{ mt: 1 }}> <Grid container sx={{ mt: 1 }}>
<Grid item xs={6}> <Grid item xs={6}>
<AvailableUserDialog /> {currentUser?.hasWriteAccess && <AvailableUserDialog />}
<InnovenergyList id="access-management-list"> <InnovenergyList id="access-management-list">
<UsersWithDirectAccess /> <UsersWithDirectAccess />
<UsersWithInheritedAccess /> <UsersWithInheritedAccess />

View File

@ -13,12 +13,14 @@ import PersonIcon from "@mui/icons-material/Person";
import { UsersContext } from "../../Context/UsersContextProvider"; import { UsersContext } from "../../Context/UsersContextProvider";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { GroupContext } from "../../Context/GroupContextProvider"; import { GroupContext } from "../../Context/GroupContextProvider";
import { UserContext } from "../../Context/UserContextProvider";
const UsersWithDirectAccess = () => { const UsersWithDirectAccess = () => {
const { fetchUsersWithDirectAccessForResource, directAccessUsers } = const { fetchUsersWithDirectAccessForResource, directAccessUsers } =
useContext(UsersContext); useContext(UsersContext);
const { currentType } = useContext(GroupContext); const { currentType } = useContext(GroupContext);
const { id } = useParams(); const { id } = useParams();
const { currentUser } = useContext(UserContext);
useEffect(() => { useEffect(() => {
fetchUsersWithDirectAccessForResource(); fetchUsersWithDirectAccessForResource();
@ -45,15 +47,17 @@ const UsersWithDirectAccess = () => {
<ListItem <ListItem
id={"direct-access-user-" + user.id} id={"direct-access-user-" + user.id}
secondaryAction={ secondaryAction={
<IconButton currentUser?.hasWriteAccess && (
id={"direct-access-user-icon-button" + user.id} <IconButton
onClick={() => handleIconClick(user.id)} id={"direct-access-user-icon-button" + user.id}
edge="end" onClick={() => handleIconClick(user.id)}
> edge="end"
<PersonRemoveIcon >
id={"direct-access-user-icon" + user.id} <PersonRemoveIcon
/> id={"direct-access-user-icon" + user.id}
</IconButton> />
</IconButton>
)
} }
> >
<ListItemAvatar id={"direct-access-user-item-avatar" + user.id}> <ListItemAvatar id={"direct-access-user-item-avatar" + user.id}>

View File

@ -8,6 +8,7 @@ import { GroupContext } from "../Context/GroupContextProvider";
import InnovenergySnackbar from "../InnovenergySnackbar"; import InnovenergySnackbar from "../InnovenergySnackbar";
import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyButton from "../Layout/InnovenergyButton";
import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
import { UserContext } from "../Context/UserContextProvider";
interface I_CustomerFormProps { interface I_CustomerFormProps {
values: I_Folder; values: I_Folder;
@ -22,11 +23,14 @@ const FolderForm = (props: I_CustomerFormProps) => {
const { values, additionalButtons, handleSubmit } = props; const { values, additionalButtons, handleSubmit } = props;
const intl = useIntl(); const intl = useIntl();
const { fetchData } = useContext(GroupContext); const { fetchData } = useContext(GroupContext);
const { currentUser } = useContext(UserContext);
const [snackbarOpen, setSnackbarOpen] = useState(false); const [snackbarOpen, setSnackbarOpen] = useState(false);
const [error, setError] = useState(); const [error, setError] = useState();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const readOnly = !currentUser?.hasWriteAccess;
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
name: additionalButtons && additionalButtons.length ? values.name : "", name: additionalButtons && additionalButtons.length ? values.name : "",
@ -54,6 +58,7 @@ const FolderForm = (props: I_CustomerFormProps) => {
<form onSubmit={formik.handleSubmit}> <form onSubmit={formik.handleSubmit}>
<InnovenergyTextfield <InnovenergyTextfield
id="folder-name-textfield" id="folder-name-textfield"
disabled={readOnly}
label={intl.formatMessage({ label={intl.formatMessage({
id: "name", id: "name",
defaultMessage: "Name", defaultMessage: "Name",
@ -64,6 +69,7 @@ const FolderForm = (props: I_CustomerFormProps) => {
/> />
<InnovenergyTextfield <InnovenergyTextfield
id="folder-information-textfield" id="folder-information-textfield"
disabled={readOnly}
label={intl.formatMessage({ label={intl.formatMessage({
id: "information", id: "information",
defaultMessage: "Information", defaultMessage: "Information",
@ -74,14 +80,18 @@ const FolderForm = (props: I_CustomerFormProps) => {
/> />
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
{loading && <CircularProgress />} {loading && <CircularProgress />}
{additionalButtons && additionalButtons.map((button) => button)} {!readOnly &&
<InnovenergyButton additionalButtons &&
id="folder-form-submit-button" additionalButtons.map((button) => button)}
type="submit" {!readOnly && (
sx={{ ml: 2 }} <InnovenergyButton
> id="folder-form-submit-button"
<FormattedMessage id="submit" defaultMessage="Submit" /> type="submit"
</InnovenergyButton> sx={{ ml: 2 }}
>
<FormattedMessage id="submit" defaultMessage="Submit" />
</InnovenergyButton>
)}
</Grid> </Grid>
<InnovenergySnackbar <InnovenergySnackbar
error={error} error={error}

View File

@ -18,8 +18,6 @@ const GroupTabs = () => {
const intl = useIntl(); const intl = useIntl();
const { currentType } = useContext(GroupContext); const { currentType } = useContext(GroupContext);
console.log(routeMatch);
if (id) { if (id) {
return ( return (
<Box sx={{ width: "100%" }}> <Box sx={{ width: "100%" }}>

View File

@ -76,7 +76,7 @@ const GroupTree = () => {
height: 480, height: 480,
flexGrow: 1, flexGrow: 1,
overflow: "auto", overflow: "auto",
".MuiTreeView-root": { overflowX: "hidden" }, overflowX: "hidden",
}} }}
expanded={openNodes} expanded={openNodes}
onNodeToggle={(e, ids) => { onNodeToggle={(e, ids) => {

View File

@ -8,6 +8,7 @@ import { InstallationContext } from "../Context/InstallationContextProvider";
import MoveDialog from "../Groups/Tree/MoveDialog"; import MoveDialog from "../Groups/Tree/MoveDialog";
import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyButton from "../Layout/InnovenergyButton";
import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
import { UserContext } from "../Context/UserContextProvider";
interface I_InstallationFormProps { interface I_InstallationFormProps {
values: I_Installation; values: I_Installation;
@ -20,6 +21,9 @@ const InstallationForm = (props: I_InstallationFormProps) => {
const intl = useIntl(); const intl = useIntl();
const { fetchData } = useContext(InstallationContext); const { fetchData } = useContext(InstallationContext);
const { currentUser } = useContext(UserContext);
const readOnly = !currentUser?.hasWriteAccess;
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
@ -49,6 +53,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
return ( return (
<form onSubmit={formik.handleSubmit}> <form onSubmit={formik.handleSubmit}>
<InnovenergyTextfield <InnovenergyTextfield
disabled={readOnly}
id="installation-name-textfield" id="installation-name-textfield"
label={intl.formatMessage({ label={intl.formatMessage({
id: "customerName", id: "customerName",
@ -59,6 +64,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<InnovenergyTextfield <InnovenergyTextfield
disabled={readOnly}
id="installation-region-textfield" id="installation-region-textfield"
label={intl.formatMessage({ label={intl.formatMessage({
id: "region", id: "region",
@ -69,6 +75,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<InnovenergyTextfield <InnovenergyTextfield
disabled={readOnly}
id="installation-location-textfield" id="installation-location-textfield"
label={intl.formatMessage({ label={intl.formatMessage({
id: "location", id: "location",
@ -79,6 +86,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<InnovenergyTextfield <InnovenergyTextfield
disabled={readOnly}
id="installation-country-textfield" id="installation-country-textfield"
label={intl.formatMessage({ label={intl.formatMessage({
id: "country", id: "country",
@ -89,6 +97,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<InnovenergyTextfield <InnovenergyTextfield
disabled={readOnly}
id="installation-orderNumbers-textfield" id="installation-orderNumbers-textfield"
label={intl.formatMessage({ label={intl.formatMessage({
id: "orderNumbers", id: "orderNumbers",
@ -99,10 +108,15 @@ const InstallationForm = (props: I_InstallationFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
{hasMoveButton && <MoveDialog values={values} />} {hasMoveButton && !readOnly && <MoveDialog values={values} />}
<InnovenergyButton id="installation-form-submit-button" type="submit"> {!readOnly && (
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" /> <InnovenergyButton id="installation-form-submit-button" type="submit">
</InnovenergyButton> <FormattedMessage
id="applyChanges"
defaultMessage="Apply changes"
/>
</InnovenergyButton>
)}
</Grid> </Grid>
<Snackbar <Snackbar
open={open} open={open}

View File

@ -8,6 +8,7 @@ interface I_InnovenergyTextfieldProps {
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void; handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
type?: string; type?: string;
readOnly?: boolean; readOnly?: boolean;
disabled?: boolean;
} }
const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => { const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => {
@ -23,12 +24,19 @@ const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => {
name={props.name} name={props.name}
type={props.type} type={props.type}
fullWidth fullWidth
sx={{ my: 0.5 }} sx={{
my: 0.5,
".Mui-disabled": {
"-webkit-text-fill-color": "black",
color: "black",
},
}}
value={props.value || ""} value={props.value || ""}
onChange={props.handleChange} onChange={props.handleChange}
InputProps={{ InputProps={{
readOnly: props.readOnly, readOnly: props.readOnly,
}} }}
disabled={props.disabled}
/> />
</Grid> </Grid>
</Grid> </Grid>

View File

@ -22,12 +22,10 @@ const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
axiosConfig axiosConfig
.get("/GetUserById?id=" + id) .get("/GetUserById?id=" + id)
.then((res) => { .then((res) => {
console.log("noterr");
setValues(res.data); setValues(res.data);
setLoading(false); setLoading(false);
}) })
.catch((err: AxiosError) => { .catch((err: AxiosError) => {
console.log("err");
setError(err); setError(err);
setLoading(false); setLoading(false);
}); });
@ -43,7 +41,11 @@ const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
}; };
if (values && values.id && values.id.toString() === id) { if (values && values.id && values.id.toString() === id) {
return <UserForm values={values} handleSubmit={handleUpdate} />; return (
<Box sx={{ py: 3 }}>
<UserForm values={values} handleSubmit={handleUpdate} />
</Box>
);
} else if (loading) { } else if (loading) {
return ( return (
<Box <Box

View File

@ -1,11 +1,12 @@
import { Alert, CircularProgress, Grid, Snackbar } from "@mui/material"; import { Alert, CircularProgress, Grid, Snackbar } from "@mui/material";
import { AxiosError, AxiosResponse } from "axios"; import { AxiosError, AxiosResponse } from "axios";
import { useFormik } from "formik"; import { useFormik } from "formik";
import { useState } from "react"; import { useContext, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { I_User } from "../../util/user.util"; import { I_User } from "../../util/user.util";
import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyButton from "../Layout/InnovenergyButton";
import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
import { UserContext } from "../Context/UserContextProvider";
interface I_UserFormProps { interface I_UserFormProps {
handleSubmit: (formikValues: Partial<I_User>) => Promise<AxiosResponse>; handleSubmit: (formikValues: Partial<I_User>) => Promise<AxiosResponse>;
@ -18,6 +19,9 @@ const UserForm = (props: I_UserFormProps) => {
const [error, setError] = useState<AxiosError>(); const [error, setError] = useState<AxiosError>();
const intl = useIntl(); const intl = useIntl();
const { currentUser } = useContext(UserContext);
const readOnly = !currentUser?.hasWriteAccess;
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
@ -55,6 +59,7 @@ const UserForm = (props: I_UserFormProps) => {
name="name" name="name"
value={formik.values.name} value={formik.values.name}
handleChange={formik.handleChange} handleChange={formik.handleChange}
disabled={readOnly}
/> />
<InnovenergyTextfield <InnovenergyTextfield
id="email-textfield" id="email-textfield"
@ -65,6 +70,7 @@ const UserForm = (props: I_UserFormProps) => {
name="email" name="email"
value={formik.values.email} value={formik.values.email}
handleChange={formik.handleChange} handleChange={formik.handleChange}
disabled={readOnly}
/> />
<InnovenergyTextfield <InnovenergyTextfield
id="information-textfield" id="information-textfield"
@ -75,12 +81,15 @@ const UserForm = (props: I_UserFormProps) => {
name="information" name="information"
value={formik.values.information} value={formik.values.information}
handleChange={formik.handleChange} handleChange={formik.handleChange}
disabled={readOnly}
/> />
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
{loading && <CircularProgress />} {loading && <CircularProgress />}
<InnovenergyButton type="submit"> {currentUser?.hasWriteAccess && (
<FormattedMessage id="submit" defaultMessage="Submit" /> <InnovenergyButton type="submit">
</InnovenergyButton> <FormattedMessage id="submit" defaultMessage="Submit" />
</InnovenergyButton>
)}
</Grid> </Grid>
<Snackbar <Snackbar
open={open} open={open}

View File

@ -7,13 +7,17 @@ import AddUser from "./AddUser";
import User from "./User"; import User from "./User";
import UserList from "./UserList"; import UserList from "./UserList";
import UserTabs from "./UserTabs"; import UserTabs from "./UserTabs";
import { useContext } from "react";
import { UserContext } from "../Context/UserContextProvider";
const Users = () => { const Users = () => {
const { currentUser } = useContext(UserContext);
return ( return (
<UsersContextProvider> <UsersContextProvider>
<Grid container> <Grid container spacing={2}>
<Grid item xs={3}> <Grid item xs={3}>
<AddUser /> {currentUser?.hasWriteAccess && <AddUser />}
<SearchSidebar id="users-search-sidebar" listComponent={UserList} /> <SearchSidebar id="users-search-sidebar" listComponent={UserList} />
</Grid> </Grid>
<Grid item xs={9}> <Grid item xs={9}>