Merge remote-tracking branch 'origin/feature/ui/TRA-4192_workspace_management' into origin/ui/TRA-4204_user_managment

This commit is contained in:
Leon
2022-01-27 11:59:21 +02:00
14 changed files with 172 additions and 68 deletions

View File

@@ -1,4 +1,4 @@
import React, {useEffect, useState} from "react"; import React from "react";
import logo from '../assets/MizuEntLogo.svg'; import logo from '../assets/MizuEntLogo.svg';
import './Header.sass'; import './Header.sass';
import userImg from '../assets/user-circle.svg'; import userImg from '../assets/user-circle.svg';
@@ -6,12 +6,10 @@ import settingImg from '../assets/settings.svg';
import {Menu, MenuItem} from "@material-ui/core"; import {Menu, MenuItem} from "@material-ui/core";
import PopupState, {bindMenu, bindTrigger} from "material-ui-popup-state"; import PopupState, {bindMenu, bindTrigger} from "material-ui-popup-state";
import logoutIcon from '../assets/logout.png'; import logoutIcon from '../assets/logout.png';
import {SettingsModal} from "../SettingsModal/SettingModal";
import Api from "../../helpers/api"; import Api from "../../helpers/api";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {useSetRecoilState} from "recoil"; import {useSetRecoilState} from "recoil";
import entPageAtom, {Page} from "../../recoil/entPage"; import entPageAtom, {Page} from "../../recoil/entPage";
import AdminSettings from "../Pages/SettingsPage/SettingsPage";
import {useNavigate} from "react-router-dom"; import {useNavigate} from "react-router-dom";
import {RouterRoutes} from "../../helpers/routes"; import {RouterRoutes} from "../../helpers/routes";
@@ -24,18 +22,6 @@ interface EntHeaderProps {
export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLogin}) => { export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLogin}) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
useEffect(() => {
if(isFirstLogin) {
setIsSettingsModalOpen(true)
}
}, [isFirstLogin])
const onSettingsModalClose = () => {
setIsSettingsModalOpen(false);
setIsFirstLogin(false);
}
return <div className="header"> return <div className="header">
<div> <div>
@@ -47,8 +33,6 @@ export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLog
<img className="headerIcon" alt="settings" src={settingImg} style={{marginRight: 25}} onClick={() => navigate(RouterRoutes.SETTINGS)}/> <img className="headerIcon" alt="settings" src={settingImg} style={{marginRight: 25}} onClick={() => navigate(RouterRoutes.SETTINGS)}/>
<ProfileButton/> <ProfileButton/>
</div> </div>
{/* <SettingsModal isOpen={isSettingsModalOpen} onClose={onSettingsModalClose} isFirstLogin={isFirstLogin}/> */}
{/* <AdminSettings isOpen={isSettingsModalOpen} onClose={onSettingsModalClose}/> */}
</div>; </div>;
} }

View File

@@ -6,7 +6,6 @@ import ConfirmationModal from '../../UI/Modals/ConfirmationModal';
import SelectList from '../../UI/SelectList'; import SelectList from '../../UI/SelectList';
import './AddUserModal.sass'; import './AddUserModal.sass';
import spinner from "../../assets/spinner.svg"; import spinner from "../../assets/spinner.svg";
import { useForm,Controller } from "react-hook-form";
export type UserData = { export type UserData = {
role:string; role:string;
@@ -25,7 +24,6 @@ const api = Api.getInstance();
export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userData = {}, setShowAlert}) => { export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userData = {}, setShowAlert}) => {
const [isOpenModal,setIsOpen] = useState(isOpen)
//const [editUserData, setEditUserData] = useState(userData) //const [editUserData, setEditUserData] = useState(userData)
const [searchValue, setSearchValue] = useState(""); const [searchValue, setSearchValue] = useState("");
const [workspaces, setWorkspaces] = useState([]) const [workspaces, setWorkspaces] = useState([])

View File

@@ -0,0 +1,10 @@
@import '../../../variables.module.scss'
.workspace__name
width: 58%
.searchNamespace
width: 32%
.headline
color: $font-color

View File

@@ -0,0 +1,76 @@
import { FC, useEffect, useState } from 'react';
import Api from '../../../helpers/api';
import { useCommonStyles } from '../../../helpers/commonStyle';
import ConfirmationModal from '../../UI/Modals/ConfirmationModal';
import SelectList from '../../UI/SelectList';
import './AddWorkspaceModal.sass'
export type WorkspaceData = {
name:string;
namespaces: string[];
}
interface AddWorkspaceModalProp {
isOpen : boolean,
onCloseModal: () => void,
workspaceData: WorkspaceData,
onEdit: boolean
}
const api = Api.getInstance();
const AddWorkspaceModal: FC<AddWorkspaceModalProp> = ({isOpen,onCloseModal, workspaceData ={}, onEdit}) => {
const [workspaceDataModel, setUserData] = useState(workspaceData as WorkspaceData);
const [searchValue, setSearchValue] = useState("");
const classes = useCommonStyles()
const [namespaces, setNamespaces] = useState({});
const title = onEdit ? "Edit Workspace" : "Add Workspace";
useEffect(() => {
if(!isOpen) return;
(async () => {
try {
setSearchValue("");
const tapConfig = await api.getTapConfig();
console.log(tapConfig);
// if(isFirstLogin) {
const namespacesObj = {...tapConfig?.tappedNamespaces}
Object.keys(tapConfig?.tappedNamespaces ?? {}).forEach(namespace => {
namespacesObj[namespace] = true;
})
setNamespaces(namespacesObj);
// } else {
setNamespaces(tapConfig?.tappedNamespaces);
// }
} catch (e) {
console.error(e);
} finally {
}
})()
}, [isOpen])
const onConfirm = () => {}
return (<>
<ConfirmationModal isOpen={isOpen} onClose={onCloseModal} onConfirm={onConfirm} title={title}>
<h3 className='headline'>DETAILS</h3>
<div>
<input type="text" value={workspaceData?.name ?? ""} className={classes.textField + " workspace__name"} placeholder={"Workspace Name"}
onChange={(e) => {}}></input>
</div>
<h3 className='headline'>TAP SETTINGS</h3>
<div className="namespacesSettingsContainer">
<div>
<input className={classes.textField + " searchNamespace"} placeholder="Search" value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}/>
</div>
<SelectList valuesListInput={namespaces} tableName={"Namespaces"} multiSelect={true} searchValue={searchValue} setValues={setUserData} tabelClassName={undefined}></SelectList>
</div>
</ConfirmationModal>
</>);
};
export default AddWorkspaceModal;

View File

@@ -2,6 +2,7 @@ import React from "react";
import { useState } from "react"; import { useState } from "react";
import Tabs from "../../UI/Tabs" import Tabs from "../../UI/Tabs"
import { UserSettings } from "../../UserSettings/UserSettings"; import { UserSettings } from "../../UserSettings/UserSettings";
import { WorkspaceSettings } from "../../WorkspaceSettings/WorkspaceSettings";
const AdminSettings: React.FC<any> = ({color}) => { const AdminSettings: React.FC<any> = ({color}) => {
var TABS = [ var TABS = [
@@ -16,7 +17,7 @@ const AdminSettings: React.FC<any> = ({color}) => {
<UserSettings/> <UserSettings/>
</React.Fragment>} </React.Fragment>}
{currentTab === TABS[1].tab && <React.Fragment> {currentTab === TABS[1].tab && <React.Fragment>
<WorkspaceSettings/>
</React.Fragment>} </React.Fragment>}
</div> </div>
) )

View File

@@ -47,14 +47,14 @@ export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, s
const filteredValues = useMemo(() => { const filteredValues = useMemo(() => {
const searchFunc = searchConfig.filterRows(inputSearch) const searchFunc = searchConfig.filterRows(inputSearch)
return tableRows.filter(searchFunc) return tableRows.filter(searchFunc)
},[tableRows, inputSearch]) },[tableRows, inputSearch,searchConfig])
return (<> return (<>
<div className="filterable-table"> <div className="filterable-table">
<div className="actions-header"> <div className="actions-header">
<input type="text" className={classes.textField + " actions-header__search-box"} placeholder={searchConfig.searchPlaceholder} onChange={onChange}></input> <input type="text" className={classes.textField + " actions-header__search-box"} placeholder={searchConfig.searchPlaceholder} onChange={onChange}></input>
<Button style={{height: '100%'}} className={classes.button + " actions-header__action-button"} size={"small"} onClick={buttonConfig.onClick}> <Button style={{height: '100%'}} className={classes.button + " actions-header__action-button"} size={"small"} onClick={buttonConfig.onClick}>
{buttonConfig.text} {buttonConfig.text}
</Button> </Button>
</div> </div>
<Table rows={filteredValues} cols={cols} onRowEdit={onRowEdit} onRowDelete={onRowDelete}></Table> <Table rows={filteredValues} cols={cols} onRowEdit={onRowEdit} onRowDelete={onRowDelete}></Table>

View File

@@ -52,4 +52,4 @@
width: 100%; width: 100%;
width: -moz-available; width: -moz-available;
width: -webkit-fill-available; width: -webkit-fill-available;
width: fill-available; width: strech;

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { makeStyles, withStyles, Modal, Backdrop, Fade, Box } from '@material-ui/core'; import { makeStyles, Modal, Backdrop, Fade, Box } from '@material-ui/core';
import {useCommonStyles} from "../../../helpers/commonStyle"; import {useCommonStyles} from "../../../helpers/commonStyle";
import { PropertiesTable } from 'redoc/typings/common-elements';
const useStyles = makeStyles({ const useStyles = makeStyles({
modal: { modal: {

View File

@@ -0,0 +1,17 @@
import React from "react";
export interface Props {
checked: boolean;
onToggle: (checked:boolean) => any;
}
const Radio: React.FC<Props> = ({checked, onToggle}) => {
return (
<div>
<input style={{cursor: "pointer"}} type="radio" checked={checked} onChange={(event) => onToggle(event.target.checked)}/>
</div>
);
};
export default Radio;

View File

@@ -1,5 +1,6 @@
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import Checkbox from "./Checkbox" import Checkbox from "./Checkbox"
import Radio from "./Radio";
import './style/SelectList.sass'; import './style/SelectList.sass';
export interface Props { export interface Props {
@@ -11,58 +12,64 @@ export interface Props {
tabelClassName tabelClassName
} }
const SelectList: React.FC<Props> = ({valuesListInput,tableName,multiSelect=true,searchValue="",setValues,tabelClassName}) => { export type ValuesListInput = {
const [valuesList, setValuesList] = useState(valuesListInput); key: string;
value: string;
isChecked: boolean;
}[]
const toggleValues = (value) => { const SelectList: React.FC<Props> = ({valuesListInput ,tableName,multiSelect=true,searchValue="",setValues,tabelClassName}) => {
const [valuesList, setValuesList] = useState(valuesListInput as ValuesListInput);
const toggleValues = (checkedKey) => {
if (!multiSelect){ if (!multiSelect){
unToggleAll(value); unToggleAll(checkedKey);
} }
else { else {
const newValues = {...valuesList}; const newValues: ValuesListInput = [...valuesList];
newValues[value] = !valuesList[value]; newValues[checkedKey].isChecked = !valuesList[checkedKey].isChecked;
setValuesList(newValues); setValuesList(newValues);
setValues(newValues); setValues(newValues);
} }
} }
const unToggleAll = (value) => { const unToggleAll = (checkedKey) => {
const newValues = {}; const newValues = [];
Object.keys(valuesList).forEach(key => { valuesList.forEach(valueList => {
if (key !== value) if (valueList.key !== checkedKey)
newValues[key] = false; newValues[checkedKey] = false;
else else
newValues[key] = true; newValues[checkedKey] = true;
}) })
setValuesList(newValues); setValuesList(newValues);
setValues(newValues); setValues(newValues);
} }
const toggleAll = () => { const toggleAll = () => {
const isChecked = Object.values(valuesList).every(tap => tap === true); const isChecked = valuesList.every(valueTap => valueTap.isChecked === true);
setAllCheckedValue(!isChecked); setAllCheckedValue(!isChecked);
} }
const setAllCheckedValue = (isTap: boolean) => { const setAllCheckedValue = (isTap: boolean) => {
const newValues = {}; const newValues = [];
Object.keys(valuesList).forEach(key => { valuesList.forEach(valueList => {
newValues[key] = isTap; newValues[valueList.key] = isTap;
}) })
setValuesList(newValues); setValuesList(newValues);
setValues(newValues); setValues(newValues);
} }
const tableHead = multiSelect ? <tr style={{borderBottomWidth: "2px"}}> const tableHead = multiSelect ? <tr style={{borderBottomWidth: "2px"}}>
<th style={{width: 50}}><Checkbox checked={Object.values(valuesList).every(tap => tap === true)} <th style={{width: 50}}><Checkbox checked={valuesList.every(valueTap => valueTap.isChecked === false)}
onToggle={toggleAll}/></th> onToggle={toggleAll}/></th>
<th>{tableName}</th> <th>{tableName}</th>
</tr> : </tr> :
<tr style={{borderBottomWidth: "2px"}}> <tr style={{borderBottomWidth: "2px"}}>
<th>{tableName}</th> <th>{tableName}</th>
</tr> </tr>
const filteredValues = useMemo(() => { const filteredValues = useMemo(() => {
return Object.keys(valuesList).filter((listValue) => listValue.includes(searchValue)); return valuesList.filter((listValue) => listValue.value.includes(searchValue));
},[valuesList, searchValue]) },[valuesList, searchValue])
return <div className={tabelClassName + " select-list-table"}> return <div className={tabelClassName + " select-list-table"}>
@@ -72,12 +79,13 @@ const SelectList: React.FC<Props> = ({valuesListInput,tableName,multiSelect=true
</thead> </thead>
<tbody> <tbody>
{filteredValues?.map(listValue => { {filteredValues?.map(listValue => {
return <tr key={listValue}> return <tr key={listValue.key}>
<td style={{width: 50}}> <td style={{width: 50}}>
<Checkbox checked={valuesList[listValue]} onToggle={() => toggleValues(listValue)}/> {multiSelect && <Checkbox checked={valuesList[listValue.key].isChecked} onToggle={() => toggleValues(listValue.key)}/>}
{!multiSelect && <Radio checked={valuesList[listValue.key].isChecked} onToggle={() => toggleValues(listValue.key)}/>}
</td> </td>
<td>{listValue}</td> <td>{listValue}</td>
</tr> </tr>
} }
)} )}
</tbody> </tbody>

View File

@@ -1,6 +1,5 @@
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import './style/Table.sass'; import './style/Table.sass';
import circleImg from "../assets/dotted-circle.svg"
import Delete from '@material-ui/icons/Delete'; import Delete from '@material-ui/icons/Delete';
import Edit from '@material-ui/icons/Edit'; import Edit from '@material-ui/icons/Edit';

View File

@@ -1,7 +1,6 @@
import "./UserSettings.sass" import "./UserSettings.sass"
import {useCommonStyles} from "../../helpers/commonStyle";
import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction" import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction"
import Api from "../../helpers/api" // import Api from "../../helpers/api"
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { UserData,AddUserModal } from "../Modals/AddUserModal/AddUserModal"; import { UserData,AddUserModal } from "../Modals/AddUserModal/AddUserModal";
import {Snackbar} from "@material-ui/core"; import {Snackbar} from "@material-ui/core";
@@ -16,7 +15,7 @@ interface Props {
} }
const api = Api.getInstance(); // const api = Api.getInstance();
export const UserSettings : React.FC<Props> = ({}) => { export const UserSettings : React.FC<Props> = ({}) => {

View File

@@ -1,24 +1,29 @@
import "./UserSettings" import "../UserSettings/UserSettings.sass"
import {useCommonStyles} from "../../helpers/commonStyle";
import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction" import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction"
import Api from "../../helpers/api" // import Api from "../../helpers/api"
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import AddWorkspaceModal, { WorkspaceData } from "../Modals/AddWorkspaceModal/AddWorkspaceModal";
interface Props {} interface Props {}
const api = Api.getInstance(); // const api = Api.getInstance();
export const WorkspaceSettings : React.FC<Props> = ({}) => { export const WorkspaceSettings : React.FC<Props> = ({}) => {
const [workspaces, setWorkspaces] = useState([]); const [workspacesRows, setWorkspacesRows] = useState([]);
const cols : ColsType[] = [{field : "userName",header:"User"},{field : "role",header:"Role"}, {field : "role",header:"Role"}] const [workspaceData,SetWorkspaceData] = useState({} as WorkspaceData);
const [isOpenModal,setIsOpen] = useState(false);
const [isEditMode,setIsEditMode] = useState(false);
const cols : ColsType[] = [{field : "id",header:"Id"},{field : "name",header:"Name"}];
const buttonConfig = {onClick: () => {setIsOpen(true); setIsEditMode(false);SetWorkspaceData({} as WorkspaceData)}, text:"Add Workspace"}
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
const workspaces = await api.getUsers() const workspacesDemo = [{id:"1", name:"Worksapce1"}]
setWorkspaces(workspaces) setWorkspacesRows(workspacesDemo)
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@@ -26,27 +31,32 @@ export const WorkspaceSettings : React.FC<Props> = ({}) => {
},[]) },[])
const filterFuncFactory = (searchQuery: string) => { const filterFuncFactory = (searchQuery: string) => {
return (row) => { return (row) => row.name.toLowerCase().includes(searchQuery.toLowerCase())
return row.userName.toLowercase().includes(searchQuery.toLowerCase()) > -1
} }
}
const searchConfig = { searchPlaceholder: "Search Workspace",filterRows: filterFuncFactory} const searchConfig = { searchPlaceholder: "Search Workspace",filterRows: filterFuncFactory}
const onRowDelete = (row) => { const onRowDelete = (row) => {
const filterFunc = filterFuncFactory(row.userName) const filterFunc = filterFuncFactory(row.name)
const newUserList = workspaces.filter(filterFunc) const newWorkspaceList = workspacesRows.filter(filterFunc)
setWorkspaces(newUserList) setWorkspacesRows(newWorkspaceList)
} }
const onRowEdit = (row) => { const onRowEdit = (row) => {
setIsOpen(true);
setIsEditMode(true);
SetWorkspaceData(row);
} }
const buttonConfig = {onClick: () => {}, text:"Add User"}
return (<> return (<>
<FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig} <FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig}
buttonConfig={buttonConfig} rows={workspaces} cols={cols}> buttonConfig={buttonConfig} rows={workspacesRows} cols={cols}>
</FilterableTableAction> </FilterableTableAction>
<AddWorkspaceModal isOpen={isOpenModal} workspaceData={workspaceData} onEdit={isEditMode} onCloseModal={() => { setIsOpen(false);} } >
</AddWorkspaceModal>
</>); </>);
} }

View File

@@ -6,6 +6,9 @@ import 'react-toastify/dist/ReactToastify.css';
import {RecoilRoot} from "recoil"; import {RecoilRoot} from "recoil";
import AppChooser from "./AppChooser"; import AppChooser from "./AppChooser";
window["isOasEnabled"]=true;
window["isServiceMapEnabled"]=true;
ReactDOM.render( <> ReactDOM.render( <>
<RecoilRoot> <RecoilRoot>
<AppChooser/> <AppChooser/>