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 './Header.sass';
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 PopupState, {bindMenu, bindTrigger} from "material-ui-popup-state";
import logoutIcon from '../assets/logout.png';
import {SettingsModal} from "../SettingsModal/SettingModal";
import Api from "../../helpers/api";
import {toast} from "react-toastify";
import {useSetRecoilState} from "recoil";
import entPageAtom, {Page} from "../../recoil/entPage";
import AdminSettings from "../Pages/SettingsPage/SettingsPage";
import {useNavigate} from "react-router-dom";
import {RouterRoutes} from "../../helpers/routes";
@@ -24,18 +22,6 @@ interface EntHeaderProps {
export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLogin}) => {
const navigate = useNavigate();
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
useEffect(() => {
if(isFirstLogin) {
setIsSettingsModalOpen(true)
}
}, [isFirstLogin])
const onSettingsModalClose = () => {
setIsSettingsModalOpen(false);
setIsFirstLogin(false);
}
return <div className="header">
<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)}/>
<ProfileButton/>
</div>
{/* <SettingsModal isOpen={isSettingsModalOpen} onClose={onSettingsModalClose} isFirstLogin={isFirstLogin}/> */}
{/* <AdminSettings isOpen={isSettingsModalOpen} onClose={onSettingsModalClose}/> */}
</div>;
}

View File

@@ -6,7 +6,6 @@ import ConfirmationModal from '../../UI/Modals/ConfirmationModal';
import SelectList from '../../UI/SelectList';
import './AddUserModal.sass';
import spinner from "../../assets/spinner.svg";
import { useForm,Controller } from "react-hook-form";
export type UserData = {
role:string;
@@ -25,7 +24,6 @@ const api = Api.getInstance();
export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userData = {}, setShowAlert}) => {
const [isOpenModal,setIsOpen] = useState(isOpen)
//const [editUserData, setEditUserData] = useState(userData)
const [searchValue, setSearchValue] = 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 Tabs from "../../UI/Tabs"
import { UserSettings } from "../../UserSettings/UserSettings";
import { WorkspaceSettings } from "../../WorkspaceSettings/WorkspaceSettings";
const AdminSettings: React.FC<any> = ({color}) => {
var TABS = [
@@ -16,7 +17,7 @@ const AdminSettings: React.FC<any> = ({color}) => {
<UserSettings/>
</React.Fragment>}
{currentTab === TABS[1].tab && <React.Fragment>
<WorkspaceSettings/>
</React.Fragment>}
</div>
)

View File

@@ -47,7 +47,7 @@ export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, s
const filteredValues = useMemo(() => {
const searchFunc = searchConfig.filterRows(inputSearch)
return tableRows.filter(searchFunc)
},[tableRows, inputSearch])
},[tableRows, inputSearch,searchConfig])
return (<>
<div className="filterable-table">

View File

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

View File

@@ -1,7 +1,6 @@
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 { PropertiesTable } from 'redoc/typings/common-elements';
const useStyles = makeStyles({
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 Checkbox from "./Checkbox"
import Radio from "./Radio";
import './style/SelectList.sass';
export interface Props {
@@ -11,49 +12,55 @@ export interface Props {
tabelClassName
}
const SelectList: React.FC<Props> = ({valuesListInput,tableName,multiSelect=true,searchValue="",setValues,tabelClassName}) => {
const [valuesList, setValuesList] = useState(valuesListInput);
export type 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){
unToggleAll(value);
unToggleAll(checkedKey);
}
else {
const newValues = {...valuesList};
newValues[value] = !valuesList[value];
const newValues: ValuesListInput = [...valuesList];
newValues[checkedKey].isChecked = !valuesList[checkedKey].isChecked;
setValuesList(newValues);
setValues(newValues);
}
}
const unToggleAll = (value) => {
const newValues = {};
Object.keys(valuesList).forEach(key => {
if (key !== value)
newValues[key] = false;
const unToggleAll = (checkedKey) => {
const newValues = [];
valuesList.forEach(valueList => {
if (valueList.key !== checkedKey)
newValues[checkedKey] = false;
else
newValues[key] = true;
newValues[checkedKey] = true;
})
setValuesList(newValues);
setValues(newValues);
}
const toggleAll = () => {
const isChecked = Object.values(valuesList).every(tap => tap === true);
const isChecked = valuesList.every(valueTap => valueTap.isChecked === true);
setAllCheckedValue(!isChecked);
}
const setAllCheckedValue = (isTap: boolean) => {
const newValues = {};
Object.keys(valuesList).forEach(key => {
newValues[key] = isTap;
const newValues = [];
valuesList.forEach(valueList => {
newValues[valueList.key] = isTap;
})
setValuesList(newValues);
setValues(newValues);
}
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>
<th>{tableName}</th>
</tr> :
@@ -62,7 +69,7 @@ const SelectList: React.FC<Props> = ({valuesListInput,tableName,multiSelect=true
</tr>
const filteredValues = useMemo(() => {
return Object.keys(valuesList).filter((listValue) => listValue.includes(searchValue));
return valuesList.filter((listValue) => listValue.value.includes(searchValue));
},[valuesList, searchValue])
return <div className={tabelClassName + " select-list-table"}>
@@ -72,9 +79,10 @@ const SelectList: React.FC<Props> = ({valuesListInput,tableName,multiSelect=true
</thead>
<tbody>
{filteredValues?.map(listValue => {
return <tr key={listValue}>
return <tr key={listValue.key}>
<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>{listValue}</td>
</tr>

View File

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

View File

@@ -1,7 +1,6 @@
import "./UserSettings.sass"
import {useCommonStyles} from "../../helpers/commonStyle";
import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction"
import Api from "../../helpers/api"
// import Api from "../../helpers/api"
import { useEffect, useState } from "react";
import { UserData,AddUserModal } from "../Modals/AddUserModal/AddUserModal";
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> = ({}) => {

View File

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