diff --git a/ui/src/components/AppSwitchRoutes.tsx b/ui/src/components/AppSwitchRoutes.tsx index 29f623b1a..4a27da32b 100644 --- a/ui/src/components/AppSwitchRoutes.tsx +++ b/ui/src/components/AppSwitchRoutes.tsx @@ -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 }> + } /> {/*todo: set settings component*/} } /> - } /> {/*todo: set settings component*/} + }/> setIsFirstLogin(true)}/>}/> diff --git a/ui/src/components/Header/EntHeader.tsx b/ui/src/components/Header/EntHeader.tsx index 9d0e9dc1f..edf0edcd9 100644 --- a/ui/src/components/Header/EntHeader.tsx +++ b/ui/src/components/Header/EntHeader.tsx @@ -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,13 +6,12 @@ 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"; const api = Api.getInstance(); @@ -23,18 +22,6 @@ interface EntHeaderProps { export const EntHeader: React.FC = ({isFirstLogin, setIsFirstLogin}) => { const navigate = useNavigate(); - const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); - - useEffect(() => { - if(isFirstLogin) { - setIsSettingsModalOpen(true) - } - }, [isFirstLogin]) - - const onSettingsModalClose = () => { - setIsSettingsModalOpen(false); - setIsFirstLogin(false); - } return
@@ -43,11 +30,9 @@ export const EntHeader: React.FC = ({isFirstLogin, setIsFirstLog
- settings setIsSettingsModalOpen(true)}/> + settings navigate(RouterRoutes.SETTINGS)}/>
- {/* */} - {/* */} ; } diff --git a/ui/src/components/Modals/AddUserModal/AddUserModal.lazy.tsx b/ui/src/components/Modals/AddUserModal/AddUserModal.lazy.tsx new file mode 100644 index 000000000..b661c6602 --- /dev/null +++ b/ui/src/components/Modals/AddUserModal/AddUserModal.lazy.tsx @@ -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 }) => ( + + + +); + +export default AddUserModal; diff --git a/ui/src/components/Modals/AddUserModal/AddUserModal.sass b/ui/src/components/Modals/AddUserModal/AddUserModal.sass new file mode 100644 index 000000000..06dfc9e98 --- /dev/null +++ b/ui/src/components/Modals/AddUserModal/AddUserModal.sass @@ -0,0 +1 @@ +.AddUserModal {} \ No newline at end of file diff --git a/ui/src/components/Modals/AddUserModal/AddUserModal.tsx b/ui/src/components/Modals/AddUserModal/AddUserModal.tsx new file mode 100644 index 000000000..7223d7560 --- /dev/null +++ b/ui/src/components/Modals/AddUserModal/AddUserModal.tsx @@ -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 = ({isOpen}) => { + + const [isOpenModal,setIsOpen] = useState(isOpen) + + useEffect(() => { + setIsOpen(isOpen) + },[isOpen]) + + const onClose = () => {} + + const onConfirm = () => {} + + return (<> + + + + ); +}; + +export default AddUserModal; diff --git a/ui/src/components/Modals/AddWorkspaceModal/AddWorkspaceModal.ts b/ui/src/components/Modals/AddWorkspaceModal/AddWorkspaceModal.ts new file mode 100644 index 000000000..9e5c18996 --- /dev/null +++ b/ui/src/components/Modals/AddWorkspaceModal/AddWorkspaceModal.ts @@ -0,0 +1,28 @@ +import React, { FC, useEffect, useState } from 'react'; +import ConfirmationModal from '../../UI/Modals/ConfirmationModal'; +// import './AddUserModal.sass'; + +interface AddUserModalProps { + isOpen : boolean +} + +const AddUserModal: FC = ({isOpen}) => { + + const [isOpenModal,setIsOpen] = useState(isOpen) + + useEffect(() => { + setIsOpen(isOpen) + },[isOpen]) + + const onClose = () => {} + + const onConfirm = () => {} + + return (<> + + + + ); +}; + +export default AddUserModal; diff --git a/ui/src/components/OasModal/OasModal.sass b/ui/src/components/Modals/OasModal/OasModal.sass similarity index 66% rename from ui/src/components/OasModal/OasModal.sass rename to ui/src/components/Modals/OasModal/OasModal.sass index 739064c05..068a03b53 100644 --- a/ui/src/components/OasModal/OasModal.sass +++ b/ui/src/components/Modals/OasModal/OasModal.sass @@ -1,4 +1,4 @@ -@import '../../variables.module.scss' +@import '../../../variables.module.scss' .NotSelectedMessage margin-left: 41% diff --git a/ui/src/components/OasModal/OasModal.tsx b/ui/src/components/Modals/OasModal/OasModal.tsx similarity index 95% rename from ui/src/components/OasModal/OasModal.tsx rename to ui/src/components/Modals/OasModal/OasModal.tsx index 2614d9185..61250d93e 100644 --- a/ui/src/components/OasModal/OasModal.tsx +++ b/ui/src/components/Modals/OasModal/OasModal.tsx @@ -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' diff --git a/ui/src/components/Pages/SettingsPage/SettingsPage.tsx b/ui/src/components/Pages/SettingsPage/SettingsPage.tsx index 5fd55e170..802b507a0 100644 --- a/ui/src/components/Pages/SettingsPage/SettingsPage.tsx +++ b/ui/src/components/Pages/SettingsPage/SettingsPage.tsx @@ -1,5 +1,8 @@ +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 = ({color}) => { var TABS = [ @@ -10,6 +13,12 @@ const AdminSettings: React.FC = ({color}) => { return (
+ {currentTab === TABS[0].tab && + + } + {currentTab === TABS[1].tab && + + }
) } diff --git a/ui/src/components/Pages/TrafficPage/TrafficPage.tsx b/ui/src/components/Pages/TrafficPage/TrafficPage.tsx index 9ccb729c1..b350bcae9 100644 --- a/ui/src/components/Pages/TrafficPage/TrafficPage.tsx +++ b/ui/src/components/Pages/TrafficPage/TrafficPage.tsx @@ -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"; diff --git a/ui/src/components/UI/FilterableTableAction.tsx b/ui/src/components/UI/FilterableTableAction.tsx new file mode 100644 index 000000000..b19135f1c --- /dev/null +++ b/ui/src/components/UI/FilterableTableAction.tsx @@ -0,0 +1,63 @@ +import { Button } from "@material-ui/core"; +import React, { useEffect, useMemo, useState } from "react"; +import { Table } from "./Table"; +import {useCommonStyles} from "../../helpers/commonStyle"; +import {ColsType} from "../UI/Table" +import './style/FilterableTableAction.sass'; + + +export type {ColsType} from "../UI/Table" + +type filterFuncFactory = (query:string) => (any) => boolean +export interface Props { + onRowEdit : (any) => void; + onRowDelete : (any) => void; + searchConfig : {searchPlaceholder : string;filterRows: filterFuncFactory}; + buttonConfig : {onClick : () => void, text:string} + rows: any[]; + cols: ColsType[]; +} + +export const FilterableTableAction: React.FC = ({onRowDelete,onRowEdit, searchConfig, buttonConfig, rows, cols}) => { + + const classes = useCommonStyles() + + const [tableRows,setRows] = useState(rows as any[]) + const [inputSearch, setInputSearch] = useState("") + + useEffect(() => { + setRows(rows); + },[rows]) + + // 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 (<> +
+
+ + +
+
+
+ ); +}; \ No newline at end of file diff --git a/ui/src/components/UI/Modals/CustomModal.tsx b/ui/src/components/UI/Modals/CustomModal.tsx index f0fd0887b..f2509da20 100644 --- a/ui/src/components/UI/Modals/CustomModal.tsx +++ b/ui/src/components/UI/Modals/CustomModal.tsx @@ -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: { diff --git a/ui/src/components/UI/Table.tsx b/ui/src/components/UI/Table.tsx index e8506c7f5..02498a824 100644 --- a/ui/src/components/UI/Table.tsx +++ b/ui/src/components/UI/Table.tsx @@ -1,22 +1,25 @@ import React, {useEffect, useState} from "react"; import './style/Table.sass'; -import editImg from "../assets/edit.svg"; -import deleteImg from "../assets/delete.svg" -import circleImg from "../assets/dotted-circle.svg" import Delete from '@material-ui/icons/Delete'; import Edit from '@material-ui/icons/Edit'; +export interface ColsType { + field:string, + cellClassName?: string, + header:string, + width?:number, + getCellClassName?:(field:string,value : any) => string +}; -interface Props { +interface TableProps { rows : any[]; - cols : {field:string, cellClassName?: string,header:string, width?:number, - getCellClassName?:(field:string,value : any) => string}[]; + cols : ColsType[] onRowEdit : (any) => void; onRowDelete : (any) => void; - noDataMeesage : string; + noDataMeesage? : string; } -export const Table: React.FC = ({rows, cols, onRowDelete, onRowEdit,noDataMeesage}) => { +export const Table: React.FC = ({rows, cols, onRowDelete, onRowEdit, noDataMeesage = "No Data Found"}) => { const [tableRows, updateTableRows] = useState(rows); @@ -31,11 +34,16 @@ export const Table: React.FC = ({rows, cols, onRowDelete, onRowEdit,noDat const _onRowDelete = (row) => { onRowDelete(row); } + + // const filteredValues = useMemo(() => { + // return tableRows.filter((listValue) => listValue.find(row)); + // },[tableRows, searchValue]) + return {cols?.map((col)=> { - return + return })} @@ -44,18 +52,19 @@ export const Table: React.FC = ({rows, cols, onRowDelete, onRowEdit,noDat { ((tableRows == null) || (tableRows.length === 0)) ? - No data Found -
{noDataMeesage}
+ {/* No data Found +
{noDataMeesage}
*/} : - tableRows?.map(rowData => { - return - {cols.map(col => { - return + {cols.map((col,index) => { + return })}
{col.header}{col.header}
- {rowData[col.field]} + tableRows?.map((rowData,index) => { + return
+ + {rowData[col.field]} + diff --git a/ui/src/components/UI/style/FilterableTableAction.sass b/ui/src/components/UI/style/FilterableTableAction.sass new file mode 100644 index 000000000..bea6b042b --- /dev/null +++ b/ui/src/components/UI/style/FilterableTableAction.sass @@ -0,0 +1,15 @@ +@import '../../../variables.module' + +.filterable-table + padding: 0 5%; + +.actions-header + + display: flex; + justify-content: space-between; + + &__search-box + width : 313px; + + &__action-button + height: 100% \ No newline at end of file diff --git a/ui/src/components/UI/style/Table.sass b/ui/src/components/UI/style/Table.sass index bcd4223f7..3fe00b952 100644 --- a/ui/src/components/UI/style/Table.sass +++ b/ui/src/components/UI/style/Table.sass @@ -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) - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/src/components/UserSettings/UserSettings.sass b/ui/src/components/UserSettings/UserSettings.sass new file mode 100644 index 000000000..2e25e20a7 --- /dev/null +++ b/ui/src/components/UserSettings/UserSettings.sass @@ -0,0 +1,21 @@ +@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) \ No newline at end of file diff --git a/ui/src/components/UserSettings/UserSettings.tsx b/ui/src/components/UserSettings/UserSettings.tsx new file mode 100644 index 000000000..dfd019cfe --- /dev/null +++ b/ui/src/components/UserSettings/UserSettings.tsx @@ -0,0 +1,61 @@ +import "./UserSettings.sass" +import {ColsType, FilterableTableAction} from "../UI/FilterableTableAction" +// import Api from "../../helpers/api" +import { useEffect, useState } from "react"; +import AddUserModal from "../Modals/AddUserModal/AddUserModal"; + +interface Props { + +} + +// const api = Api.getInstance(); + +export const UserSettings : React.FC = ({}) => { + + const [usersRows, setUserRows] = useState([]); + 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 = [{userName:"asd",role:"Admin",status:"Active"}]//await api.getUsers() + setUserRows(users) + } catch (e) { + console.error(e); + } + })(); + },[]) + + const filterFuncFactory = (searchQuery: string) => { + return (row) => { + return row.userName.toLowerCase().includes(searchQuery.toLowerCase()) + } + } + + const searchConfig = { searchPlaceholder: "Search User",filterRows: filterFuncFactory} + + const onRowDelete = (row) => { + const filterFunc = filterFuncFactory(row.userName) + const newUserList = usersRows.filter(filterFunc) + setUserRows(newUserList) + } + + const onRowEdit = (row) => { + // open Edit user Modal + } + + const buttonConfig = {onClick: () => {setIsOpen(true)}, text:"Add User"} + return (<> + + + + + + ); +} diff --git a/ui/src/components/WorkspaceSettings/WorkspaceSettings.tsx b/ui/src/components/WorkspaceSettings/WorkspaceSettings.tsx new file mode 100644 index 000000000..d279cfa26 --- /dev/null +++ b/ui/src/components/WorkspaceSettings/WorkspaceSettings.tsx @@ -0,0 +1,51 @@ +import "../UserSettings/UserSettings.sass" +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 = ({}) => { + + const [workspacesRows, setWorkspaces] = useState([]); + const cols : ColsType[] = [{field : "id",header:"Id"},{field : "name",header:"Name"}] + + + useEffect(() => { + (async () => { + try { + const workspacesDemo = [{id:"1", name:"Worksapce1"}] + setWorkspaces(workspacesDemo) + } catch (e) { + console.error(e); + } + })(); + },[]) + + const filterFuncFactory = (searchQuery: string) => { + return (row) => { + return row.name.toLowerCase().includes(searchQuery.toLowerCase()) + } + } + + const searchConfig = { searchPlaceholder: "Search Workspace",filterRows: filterFuncFactory} + + const onRowDelete = (row) => { + const filterFunc = filterFuncFactory(row.name) + const newWorkspaceList = workspacesRows.filter(filterFunc) + setWorkspaces(newWorkspaceList) + } + + const onRowEdit = (row) => { + + } + + const buttonConfig = {onClick: () => {}, text:"Add Workspace"} + return (<> + + + ); +} diff --git a/ui/src/helpers/api.js b/ui/src/helpers/api.js index ae85c78a6..19644d0af 100644 --- a/ui/src/helpers/api.js +++ b/ui/src/helpers/api.js @@ -48,6 +48,16 @@ export default class Api { return response.data; } + getUsers = async(filter = "") =>{ + const response = await this.client.get(`/user/listUsers?usernameFilter=${filter}`); + 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; diff --git a/ui/src/index.tsx b/ui/src/index.tsx index f1efbbf31..2a926ba39 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -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( <>