mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-09-25 20:46:13 +00:00
workspace + user settings
This commit is contained in:
@@ -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>}/>
|
||||
|
@@ -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}/> */}
|
||||
|
11
ui/src/components/Modals/AddUserModal/AddUserModal.lazy.tsx
Normal file
11
ui/src/components/Modals/AddUserModal/AddUserModal.lazy.tsx
Normal 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;
|
1
ui/src/components/Modals/AddUserModal/AddUserModal.less
Normal file
1
ui/src/components/Modals/AddUserModal/AddUserModal.less
Normal file
@@ -0,0 +1 @@
|
||||
.AddUserModal {}
|
28
ui/src/components/Modals/AddUserModal/AddUserModal.tsx
Normal file
28
ui/src/components/Modals/AddUserModal/AddUserModal.tsx
Normal 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;
|
@@ -1,4 +1,4 @@
|
||||
@import '../../variables.module.scss'
|
||||
@import '../../../variables.module.scss'
|
||||
|
||||
.NotSelectedMessage
|
||||
margin-left: 41%
|
@@ -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'
|
||||
|
@@ -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>
|
||||
)
|
||||
}
|
||||
|
@@ -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";
|
||||
|
@@ -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>
|
||||
</>);
|
||||
};
|
@@ -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">
|
||||
|
@@ -67,26 +67,4 @@
|
||||
@mixin row-actions
|
||||
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)
|
||||
|
||||
|
||||
|
@@ -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)
|
@@ -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>
|
||||
</>);
|
||||
}
|
||||
|
52
ui/src/components/WorkspaceSettings/WorkspaceSettings.tsx
Normal file
52
ui/src/components/WorkspaceSettings/WorkspaceSettings.tsx
Normal 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>
|
||||
</>);
|
||||
}
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user