workspace + user settings

This commit is contained in:
Leon
2022-01-25 17:51:07 +02:00
parent e392bbd279
commit 6c88b0ef46
16 changed files with 186 additions and 63 deletions

View File

@@ -11,6 +11,7 @@ import LoadingOverlay from "./LoadingOverlay";
import SystemViewer from "./Pages/SystemViewer/SystemViewer";
import Api from "../helpers/api";
import {TrafficPage} from "./Pages/TrafficPage/TrafficPage";
import SettingsPage from "./Pages/SettingsPage/SettingsPage";
const api = Api.getInstance();
@@ -69,8 +70,9 @@ const AppSwitchRoutes = () => {
return <Routes>
<Route path={"/"} element={<SystemViewer isFirstLogin={isFirstLogin} setIsFirstLogin={setIsFirstLogin}/>}>
<Route path={RouterRoutes.SETTINGS} element={<SettingsPage/>} /> {/*todo: set settings component*/}
<Route path={"/"} element={<TrafficPage/>} />
<Route path={RouterRoutes.SETTINGS} element={<></>} /> {/*todo: set settings component*/}
</Route>
<Route path={RouterRoutes.LOGIN} element={<AuthPageBase><LoginPage/></AuthPageBase>}/>
<Route path={RouterRoutes.SETUP} element={<AuthPageBase><InstallPage onFirstLogin={() => setIsFirstLogin(true)}/></AuthPageBase>}/>

View File

@@ -13,6 +13,7 @@ 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";
const api = Api.getInstance();
@@ -43,7 +44,7 @@ export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLog
</div>
</div>
<div style={{display: "flex", alignItems: "center"}}>
<img className="headerIcon" alt="settings" src={settingImg} style={{marginRight: 25}} onClick={() => setIsSettingsModalOpen(true)}/>
<img className="headerIcon" alt="settings" src={settingImg} style={{marginRight: 25}} onClick={() => navigate(RouterRoutes.SETTINGS)}/>
<ProfileButton/>
</div>
{/* <SettingsModal isOpen={isSettingsModalOpen} onClose={onSettingsModalClose} isFirstLogin={isFirstLogin}/> */}

View File

@@ -0,0 +1,11 @@
import React, { lazy, Suspense } from 'react';
const LazyAddUserModal = lazy(() => import('./AddUserModal'));
const AddUserModal = (props: JSX.IntrinsicAttributes & { children?: React.ReactNode;isOpen:boolean }) => (
<Suspense fallback={null}>
<LazyAddUserModal {...props} />
</Suspense>
);
export default AddUserModal;

View File

@@ -0,0 +1 @@
.AddUserModal {}

View File

@@ -0,0 +1,28 @@
import React, { FC, useEffect, useState } from 'react';
import ConfirmationModal from '../../UI/Modals/ConfirmationModal';
import './AddUserModal.less';
interface AddUserModalProps {
isOpen : boolean
}
const AddUserModal: FC<AddUserModalProps> = ({isOpen}) => {
const [isOpenModal,setIsOpen] = useState(isOpen)
useEffect(() => {
setIsOpen(isOpen)
},[isOpen])
const onClose = () => {}
const onConfirm = () => {}
return (<>
<ConfirmationModal isOpen={isOpenModal} onClose={onClose} onConfirm={onConfirm} title=''>
</ConfirmationModal>
</>);
};
export default AddUserModal;

View File

@@ -1,4 +1,4 @@
@import '../../variables.module.scss'
@import '../../../variables.module.scss'
.NotSelectedMessage
margin-left: 41%

View File

@@ -1,9 +1,9 @@
import { Box, Fade, FormControl, MenuItem, Modal } from "@material-ui/core";
import { useEffect, useState } from "react";
import { RedocStandalone } from "redoc";
import Api from "../../helpers/api";
import { Select } from "../UI/Select";
import closeIcon from "../assets/closeIcon.svg";
import Api from "../../../helpers/api";
import { Select } from "../../UI/Select";
import closeIcon from "../../assets/closeIcon.svg";
import { toast } from 'react-toastify';
import './OasModal.sass'

View File

@@ -1,5 +1,7 @@
import React from "react";
import { useState } from "react";
import Tabs from "../../UI/Tabs"
import { UserSettings } from "../../UserSettings/UserSettings";
const AdminSettings: React.FC<any> = ({color}) => {
var TABS = [
@@ -10,6 +12,12 @@ const AdminSettings: React.FC<any> = ({color}) => {
return (
<div style={{padding:" 0 1rem"}}>
<Tabs tabs={TABS} currentTab={currentTab} color={color} onChange={setCurrentTab} leftAligned/>
{currentTab === TABS[0].tab && <React.Fragment>
<UserSettings/>
</React.Fragment>}
{currentTab === TABS[1].tab && <React.Fragment>
</React.Fragment>}
</div>
)
}

View File

@@ -18,7 +18,7 @@ import entriesAtom from "../../../recoil/entries";
import focusedEntryIdAtom from "../../../recoil/focusedEntryId";
import websocketConnectionAtom, {WsConnectionStatus} from "../../../recoil/wsConnection";
import queryAtom from "../../../recoil/query";
import OasModal from "../../OasModal/OasModal";
import OasModal from "../../Modals/OasModal/OasModal";
import {useCommonStyles} from "../../../helpers/commonStyle"
import {TLSWarning} from "../../TLSWarning/TLSWarning";
import serviceMapModalOpenAtom from "../../../recoil/serviceMapModalOpen";

View File

@@ -1,5 +1,5 @@
import { Button } from "@material-ui/core";
import React, { useEffect, useState } from "react";
import React, { useEffect, useMemo, useState } from "react";
import { Table } from "./Table";
import {useCommonStyles} from "../../helpers/commonStyle";
import {ColsType} from "../UI/Table"
@@ -24,28 +24,31 @@ export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, s
const [tableRows,setRows] = useState(rows as any[])
const [inputSearch, setInputSearch] = useState("")
let allRows = rows;
useEffect(() => {
allRows = rows;
setRows(rows);
},[rows])
useEffect(()=> {
if(inputSearch !== ""){
const searchFunc = searchConfig.filterRows(inputSearch)
const filtered = tableRows.filter(searchFunc)
setRows(filtered)
}
else{
setRows(allRows);
}
},[inputSearch])
// useEffect(()=> {
// if(inputSearch !== ""){
// const searchFunc = searchConfig.filterRows(inputSearch)
// const filtered = tableRows.filter(searchFunc)
// setRows(filtered)
// }
// else{
// setRows(allRows);
// }
// },[inputSearch])
const onChange = (e) => {
setInputSearch(e.target.value)
}
const filteredValues = useMemo(() => {
const searchFunc = searchConfig.filterRows(inputSearch)
return tableRows.filter(searchFunc)
},[tableRows, inputSearch])
return (<>
<div className="filterable-table">
<div className="actions-header">
@@ -54,7 +57,7 @@ export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, s
{buttonConfig.text}
</Button>
</div>
<Table rows={tableRows} cols={cols} onRowEdit={onRowEdit} onRowDelete={onRowDelete}></Table>
<Table rows={filteredValues} cols={cols} onRowEdit={onRowEdit} onRowDelete={onRowDelete}></Table>
</div>
</>);
};

View File

@@ -35,11 +35,16 @@ export const Table: React.FC<TableProps> = ({rows, cols, onRowDelete, onRowEdit,
const _onRowDelete = (row) => {
onRowDelete(row);
}
// const filteredValues = useMemo(() => {
// return tableRows.filter((listValue) => listValue.find(row));
// },[tableRows, searchValue])
return <table cellPadding={5} style={{borderCollapse: "collapse"}} className="mui-table">
<thead className="mui-table__thead">
<tr style={{borderBottomWidth: "2px"}} className="mui-table__tr">
{cols?.map((col)=> {
return <th className="mui-table__th">{col.header}</th>
return <th key={col.header} className="mui-table__th">{col.header}</th>
})}
<th></th>
</tr>
@@ -48,18 +53,19 @@ export const Table: React.FC<TableProps> = ({rows, cols, onRowDelete, onRowEdit,
{
((tableRows == null) || (tableRows.length === 0)) ?
<tr className="mui-table__no-data">
<img src={circleImg} alt="No data Found"></img>
<div className="mui-table__no-data-message">{noDataMeesage}</div>
{/* <img src={circleImg} alt="No data Found"></img>
<div className="mui-table__no-data-message">{noDataMeesage}</div> */}
</tr>
:
tableRows?.map(rowData => {
return <tr key={rowData?.id} className="mui-table__tr">
{cols.map(col => {
return <td className={`${col.getCellClassName ? col.getCellClassName(col.field, rowData[col.field]) : ""}
${col?.cellClassName ?? ""} mui-table__td`}>
{rowData[col.field]}
tableRows?.map((rowData,index) => {
return <tr key={rowData?.id ?? index} className="mui-table__tr">
{cols.map((col,index) => {
return <td key={`${rowData?.id} + ${index}`} className="mui-table__td">
<span key={Math.random().toString()} className={`${col.getCellClassName ? col.getCellClassName(col.field, rowData[col.field]) : ""}${col?.cellClassName ?? ""}`}>
{rowData[col.field]}
</span>
</td>
})}
<td className="mui-table__td mui-table__row-actions">

View File

@@ -68,25 +68,3 @@
display: flex
justify-content: flex-end
@mixin status-base($bg_color, $color, $border)
box-sizing: border-box
border-radius: 4px
height : 20px
width : 63px
display: flex
align-content: center
justify-content: center
align-items: center
background: $bg_color
color: $color
border: $border
.status
&--active
@include status-base(rgba(111, 207, 151, 0.5), #247E4B, 1px solid #219653)
&--pending
@include status-base(rgba(242, 201, 76, 0.5), #8C7325, 1px solid #F2994A)

View File

@@ -1,2 +1,21 @@
@import '../../../variables.module'
@import '../../variables.module'
@mixin status-base($bg_color, $color, $border)
box-sizing: border-box
border-radius: 4px
height : 20px
width : 63px
display: flex
align-content: center
justify-content: center
align-items: center
background: $bg_color
color: $color
border: $border
.status
&--active
@include status-base(rgba(111, 207, 151, 0.5), #247E4B, 1px solid #219653)
&--pending
@include status-base(rgba(242, 201, 76, 0.5), #8C7325, 1px solid #F2994A)

View File

@@ -1,8 +1,9 @@
import "./UserSettings"
import "./UserSettings.sass"
import {useCommonStyles} from "../../helpers/commonStyle";
import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction"
import Api from "../../helpers/api"
import { useEffect, useState } from "react";
import AddUserModal from "../Modals/AddUserModal/AddUserModal";
interface Props {
@@ -13,14 +14,18 @@ const api = Api.getInstance();
export const UserSettings : React.FC<Props> = ({}) => {
const [usersRows, setUserRows] = useState([]);
const cols : ColsType[] = [{field : "userName",header:"User"},{field : "role",header:"Role"}, {field : "role",header:"Role"}]
const cols : ColsType[] = [{field : "userName",header:"User"},
{field : "role",header:"Role"},
{field : "status",header:"Status",getCellClassName : (field, val) =>{
return val === "Active" ? "status--active" : "status--pending"
}}]
const [isOpenModal,setIsOpen] = useState(false)
useEffect(() => {
(async () => {
try {
const users = await api.getUsers()
setUserRows(usersRows)
const users = [{userName:"asd",role:"Admin",status:"Active"}]//await api.getUsers()
setUserRows(users)
} catch (e) {
console.error(e);
}
@@ -29,7 +34,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
const filterFuncFactory = (searchQuery: string) => {
return (row) => {
return row.userName.toLowercase().includes(searchQuery.toLowerCase()) > -1
return row.userName.toLowerCase().includes(searchQuery.toLowerCase())
}
}
@@ -42,12 +47,16 @@ export const UserSettings : React.FC<Props> = ({}) => {
}
const onRowEdit = (row) => {
// open Edit user Modal
}
const buttonConfig = {onClick: () => {}, text:"Add User"}
const buttonConfig = {onClick: () => {setIsOpen(true)}, text:"Add User"}
return (<>
<FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig} buttonConfig={buttonConfig} rows={usersRows} cols={cols}>
<FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig}
buttonConfig={buttonConfig} rows={usersRows} cols={cols}>
</FilterableTableAction>
<AddUserModal isOpen={isOpenModal}>
</AddUserModal>
</>);
}

View File

@@ -0,0 +1,52 @@
import "./UserSettings"
import {useCommonStyles} from "../../helpers/commonStyle";
import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction"
import Api from "../../helpers/api"
import { useEffect, useState } from "react";
interface Props {}
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"}]
useEffect(() => {
(async () => {
try {
const workspaces = await api.getUsers()
setWorkspaces(workspaces)
} catch (e) {
console.error(e);
}
})();
},[])
const filterFuncFactory = (searchQuery: string) => {
return (row) => {
return row.userName.toLowercase().includes(searchQuery.toLowerCase()) > -1
}
}
const searchConfig = { searchPlaceholder: "Search Workspace",filterRows: filterFuncFactory}
const onRowDelete = (row) => {
const filterFunc = filterFuncFactory(row.userName)
const newUserList = workspaces.filter(filterFunc)
setWorkspaces(newUserList)
}
const onRowEdit = (row) => {
}
const buttonConfig = {onClick: () => {}, text:"Add User"}
return (<>
<FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig}
buttonConfig={buttonConfig} rows={workspaces} cols={cols}>
</FilterableTableAction>
</>);
}

View File

@@ -53,6 +53,11 @@ export default class Api {
return response.data;
}
getWorkspaces = async() =>{
const response = await this.client.get(``);
return response.data;
}
analyzeStatus = async () => {
const response = await this.client.get("/status/analyze");
return response.data;