mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-09-25 20:46:13 +00:00
Merge remote-tracking branch 'origin/feature/ui/TRA-4192_workspace_management' into origin/ui/TRA-4204_user_managment
This commit is contained in:
@@ -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>;
|
||||
}
|
||||
|
||||
|
@@ -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([])
|
||||
|
@@ -0,0 +1,10 @@
|
||||
@import '../../../variables.module.scss'
|
||||
|
||||
.workspace__name
|
||||
width: 58%
|
||||
|
||||
.searchNamespace
|
||||
width: 32%
|
||||
|
||||
.headline
|
||||
color: $font-color
|
@@ -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;
|
@@ -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>
|
||||
)
|
||||
|
@@ -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">
|
||||
|
@@ -52,4 +52,4 @@
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
width: -webkit-fill-available;
|
||||
width: fill-available;
|
||||
width: strech;
|
@@ -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: {
|
||||
|
17
ui/src/components/UI/Radio.tsx
Normal file
17
ui/src/components/UI/Radio.tsx
Normal 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;
|
@@ -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>
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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> = ({}) => {
|
||||
|
||||
|
@@ -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>
|
||||
</>);
|
||||
}
|
||||
|
||||
|
@@ -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/>
|
||||
|
Reference in New Issue
Block a user