mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-09-26 13:04:13 +00:00
edit page - insert data bug
This commit is contained in:
@@ -48,7 +48,6 @@
|
||||
|
||||
& .user__email
|
||||
width : 300px
|
||||
height: 30px;
|
||||
box-sizing: border-box;
|
||||
|
||||
& .user__role
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Button, FormControl, IconButton, InputAdornment, InputLabel, MenuItem, OutlinedInput,Select } from '@material-ui/core';
|
||||
import { Button, FormControl, IconButton, InputAdornment, InputLabel, MenuItem, OutlinedInput,Select, TextField } from '@material-ui/core';
|
||||
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import Api from '../../../helpers/api';
|
||||
@@ -8,6 +8,10 @@ import {toast} from "react-toastify";
|
||||
import SelectList from '../../UI/SelectList';
|
||||
import './AddUserModal.sass';
|
||||
import spinner from "../../assets/spinner.svg";
|
||||
import {FormService} from "../../../helpers/FormService"
|
||||
import {RouterRoutes} from "../../../helpers/routes";
|
||||
import {Utils} from "../../../helpers/Utils"
|
||||
|
||||
|
||||
export type UserData = {
|
||||
role:string;
|
||||
@@ -25,6 +29,7 @@ interface AddUserModalProps {
|
||||
}
|
||||
|
||||
const api = Api.getInstance();
|
||||
const fromService = new FormService()
|
||||
|
||||
export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userData, isEditMode,onUserChange}) => {
|
||||
|
||||
@@ -127,7 +132,7 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
|
||||
|
||||
|
||||
const mapTokenToLink = (token) => {
|
||||
return`${window.location.origin}/${token}`
|
||||
return`${window.location.origin}/${RouterRoutes.SETUP}/${token}`
|
||||
}
|
||||
|
||||
const generateLink = async() => {
|
||||
@@ -170,7 +175,7 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
|
||||
<OutlinedInput type={'text'} value={invite.link} onChange={handleChange('password')} classes={{input: "u-input-padding"}}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton aria-label="cpoy invite link" onClick={handleCopyinviteLink} edge="end">
|
||||
<IconButton aria-label="copy invite link" onClick={handleCopyinviteLink} edge="end">
|
||||
{<span className='generate-link-button__icon'></span>}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
@@ -214,9 +219,10 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
|
||||
placeholder={"User Email"} onChange={userNameChange} disabled={editMode}/>
|
||||
</div> */}
|
||||
<FormControl variant="outlined" size={"small"} className={"user__email"}>
|
||||
<InputLabel htmlFor="">User Name</InputLabel>
|
||||
<OutlinedInput type={'text'} onChange={userNameChange} disabled={editMode} value={userDataModel?.username ?? ""} classes={{input: "u-input-padding"}}
|
||||
label="User Name"/>
|
||||
<InputLabel htmlFor="">User email</InputLabel>
|
||||
<OutlinedInput type={'text'} onChange={userNameChange} disabled={editMode} value={userDataModel?.username ?? ""}
|
||||
label="User email"/>
|
||||
{/* {!fromService.isValidEmail(userDataModel?.username) && <label>*Invalid email</label>} */}
|
||||
</FormControl>
|
||||
|
||||
{/* <Controller name="role" control={control} rules={{ required: true }}
|
||||
@@ -226,7 +232,7 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
|
||||
|
||||
{/* className='user__role u-input-padding' */}
|
||||
{/* classes={{ select : 'u-input-padding' }} */}
|
||||
<FormControl variant="outlined" className='user__role'>
|
||||
<FormControl variant="outlined" className='user__role' size={"small"}>
|
||||
<InputLabel id="user-role-outlined-label">User role</InputLabel>
|
||||
<Select
|
||||
labelId="user-role-outlined-label"
|
||||
@@ -235,7 +241,7 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
|
||||
label="User role">
|
||||
{roles.map((role) => (
|
||||
<MenuItem key={role.value} value={role.value}>
|
||||
{role.value}
|
||||
{Utils.capitalizeFirstLetter(role.value)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@@ -244,7 +250,7 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
|
||||
</div>
|
||||
<h3 className='comfirmation-modal__sub-section-header'>WORKSPACE ACCESS </h3>
|
||||
<div className="namespacesSettingsContainer">
|
||||
<div style={{margin: "10px 0"}}>
|
||||
<div style={{marginTop: "17px"}}>
|
||||
<input className={classes.textField + " search-workspace"} placeholder="Search" value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}/>
|
||||
</div>
|
||||
|
@@ -105,7 +105,7 @@ const AddWorkspaceModal: FC<AddWorkspaceModalProp> = ({isOpen,onCloseModal, work
|
||||
</div>
|
||||
<h3 className='comfirmation-modal__sub-section-header'>TAP SETTINGS</h3>
|
||||
<div className="namespacesSettingsContainer">
|
||||
<div>
|
||||
<div style={{marginTop: "17px"}}>
|
||||
<input className={classes.textField + " searchNamespace"} placeholder="Search" value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}/>
|
||||
</div>
|
||||
|
@@ -38,4 +38,6 @@
|
||||
height: 100vh;
|
||||
&__container
|
||||
@include tab-container
|
||||
|
||||
|
||||
.table-body-style
|
||||
background: $main-background-color
|
@@ -94,7 +94,7 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({isOpen, onClose, is
|
||||
<img alt="spinner" src={spinner} style={{height: 35}}/>
|
||||
</div> : <>
|
||||
<div className="namespacesSettingsContainer">
|
||||
<div style={{margin: "10px 0"}}>
|
||||
<div style={{marginTop: "17px"}}>
|
||||
<input className={classes.textField + " searchNamespace"} placeholder="Search" value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}/></div>
|
||||
<SelectList items={namespaces} tableName={'Namespace'} multiSelect={true} searchValue={searchValue} setCheckedValues={setCheckedNamespacesKeys} tabelClassName={'namespacesTable'} checkedValues={checkedNamespacesKeys}/>
|
||||
|
@@ -13,7 +13,7 @@
|
||||
|
||||
.namespacesSettingsContainer
|
||||
border-radius: 4px
|
||||
padding: 20px 0
|
||||
|
||||
|
||||
.searchNamespace
|
||||
width: 300px
|
||||
|
@@ -16,9 +16,10 @@ export interface Props {
|
||||
buttonConfig : {onClick : () => void, text:string}
|
||||
rows: any[];
|
||||
cols: ColsType[];
|
||||
bodyClass?: string;
|
||||
}
|
||||
|
||||
export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, searchConfig, buttonConfig, rows, cols}) => {
|
||||
export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, searchConfig, buttonConfig, rows, cols, bodyClass}) => {
|
||||
|
||||
const classes = useCommonStyles()
|
||||
|
||||
@@ -57,7 +58,7 @@ export const FilterableTableAction: React.FC<Props> = ({onRowDelete,onRowEdit, s
|
||||
{buttonConfig.text}
|
||||
</Button>
|
||||
</div>
|
||||
<Table rows={filteredValues} cols={cols} onRowEdit={onRowEdit} onRowDelete={onRowDelete}></Table>
|
||||
<Table rows={filteredValues} cols={cols} onRowEdit={onRowEdit} onRowDelete={onRowDelete} bodyClass={bodyClass}></Table>
|
||||
</div>
|
||||
</>);
|
||||
};
|
@@ -2,6 +2,7 @@ import React, {useEffect, useState} from "react";
|
||||
import './style/Table.sass';
|
||||
import Delete from '@material-ui/icons/Delete';
|
||||
import Edit from '@material-ui/icons/Edit';
|
||||
import circleImg from '../assets/dotted-circle.svg';
|
||||
|
||||
export interface ColsType {
|
||||
field:string,
|
||||
@@ -9,6 +10,7 @@ export interface ColsType {
|
||||
header:string,
|
||||
width?:string,
|
||||
getCellClassName?:(field:string,value : any) => string
|
||||
mapValue? : (val : any) => any
|
||||
};
|
||||
|
||||
interface TableProps {
|
||||
@@ -17,9 +19,10 @@ interface TableProps {
|
||||
onRowEdit : (any) => void;
|
||||
onRowDelete : (any) => void;
|
||||
noDataMeesage? : string;
|
||||
bodyClass?: string;
|
||||
}
|
||||
|
||||
export const Table: React.FC<TableProps> = ({rows, cols, onRowDelete, onRowEdit, noDataMeesage = "No Data Found"}) => {
|
||||
export const Table: React.FC<TableProps> = ({rows, cols, onRowDelete, onRowEdit, noDataMeesage = "No Data Found",bodyClass}) => {
|
||||
|
||||
const [tableRows, updateTableRows] = useState(rows);
|
||||
|
||||
@@ -48,12 +51,17 @@ export const Table: React.FC<TableProps> = ({rows, cols, onRowDelete, onRowEdit,
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="mui-table__tbody">
|
||||
<tbody className={`mui-table__tbody ${bodyClass}`}>
|
||||
{
|
||||
((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> */}
|
||||
<td>
|
||||
<div className="container">
|
||||
<img src={circleImg} alt="No data Found"></img>
|
||||
<div className="mui-table__no-data-message">{noDataMeesage}</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
:
|
||||
@@ -63,7 +71,7 @@ export const Table: React.FC<TableProps> = ({rows, cols, onRowDelete, onRowEdit,
|
||||
{cols.map((col,index) => {
|
||||
return <td key={`${rowData?.id} + ${index}`} className="mui-table__td" style={{width: col.width}}>
|
||||
<span key={Math.random().toString()} className={`${col.getCellClassName ? col.getCellClassName(col.field, rowData[col.field]) : ""}${col?.cellClassName ?? ""}`}>
|
||||
{rowData[col.field]}
|
||||
{col.mapValue ? col.mapValue(rowData[col.field]) : rowData[col.field]}
|
||||
</span>
|
||||
</td>
|
||||
})}
|
||||
|
@@ -1,15 +1,14 @@
|
||||
@import '../../../variables.module'
|
||||
|
||||
.filterable-table
|
||||
|
||||
|
||||
.actions-header
|
||||
|
||||
|
||||
.actions-header
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
&__search-box
|
||||
width : 313px;
|
||||
width: clamp(300px, 60%, 400px)
|
||||
|
||||
&__action-button
|
||||
height: 100%
|
@@ -30,13 +30,24 @@
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
height: 95px;
|
||||
overflow-y: auto;
|
||||
margin: 2%;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
padding-top: 3%;
|
||||
padding-bottom: 3%;
|
||||
|
||||
& td
|
||||
width: 100%
|
||||
|
||||
& .container
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
height: 95px;
|
||||
margin: 1%;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
|
||||
&-message
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
|
@@ -12,17 +12,11 @@
|
||||
background: $bg_color
|
||||
color: $color
|
||||
border: $border
|
||||
padding: 12px 19px;
|
||||
|
||||
.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)
|
||||
|
||||
.alert--right.MuiSnackbar-root
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
left: auto;
|
||||
right: 1vw;
|
||||
transform: unset;
|
||||
@include status-base(rgba(242, 201, 76, 0.5), #8C7325, 1px solid #F2994A)
|
@@ -6,6 +6,8 @@ import { UserData,AddUserModal } from "../Modals/AddUserModal/AddUserModal";
|
||||
import Api from '../../helpers/api';
|
||||
import { toast } from "react-toastify";
|
||||
import ConfirmationModal from "../UI/Modals/ConfirmationModal";
|
||||
import {Utils} from "../../helpers/Utils"
|
||||
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -23,7 +25,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
|
||||
const [usersRows, setUserRows] = useState([]);
|
||||
const [userData,userUserData] = useState({} as UserData)
|
||||
const cols : ColsType[] = [{field : "username",header:"User", width: '35%'},
|
||||
{field : "role",header:"Role"},
|
||||
{field : "role",header:"Role",mapValue : (val) => Utils.capitalizeFirstLetter(val)},
|
||||
{field : "status",header:"Status",getCellClassName : (field, val) =>{
|
||||
return val === InviteStatus.active ? "status--active" : "status--pending"
|
||||
}}]
|
||||
@@ -36,8 +38,9 @@ export const UserSettings : React.FC<Props> = ({}) => {
|
||||
// let users = [{username:"asd",role:"Admin",status:"Active",userId : "1"},
|
||||
// {username:"asdasdasdasdasdasd",role:"User",status:"Active",userId : "2"}]
|
||||
let users = await api.getUsers()
|
||||
const mappedUsers = users.map((user) => {
|
||||
return {...user,status: capitalizeFirstLetter(user.status), role: capitalizeFirstLetter(user.role)}
|
||||
const mappedUsers = users
|
||||
.map((user) => {
|
||||
return {...user,status: Utils.capitalizeFirstLetter(user.status), role: user.role}
|
||||
})
|
||||
setUserRows(mappedUsers)
|
||||
} catch (e) {
|
||||
@@ -50,10 +53,6 @@ export const UserSettings : React.FC<Props> = ({}) => {
|
||||
getUserList();
|
||||
},[])
|
||||
|
||||
function capitalizeFirstLetter(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
const filterFuncFactory = (searchQuery: string) => {
|
||||
return (row) => {
|
||||
return row.username.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
@@ -103,7 +102,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
|
||||
|
||||
return (<>
|
||||
<FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig}
|
||||
buttonConfig={buttonConfig} rows={usersRows} cols={cols}>
|
||||
buttonConfig={buttonConfig} rows={usersRows} cols={cols} bodyClass="table-body-style">
|
||||
</FilterableTableAction>
|
||||
<AddUserModal isOpen={isOpenModal} onCloseModal={() => {
|
||||
setIsOpen(false);userUserData({} as UserData) } }
|
||||
|
8
ui/src/helpers/FormService.ts
Normal file
8
ui/src/helpers/FormService.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export class FormService{
|
||||
|
||||
EMAIL_REGEXP = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
|
||||
|
||||
isValidEmail = (email : string) => {
|
||||
return new RegExp(this.EMAIL_REGEXP).test(email)
|
||||
}
|
||||
}
|
5
ui/src/helpers/Utils.ts
Normal file
5
ui/src/helpers/Utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class Utils{
|
||||
static capitalizeFirstLetter(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
}
|
@@ -48,6 +48,8 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//#region User api
|
||||
|
||||
getUsers = async(filter = "") =>{
|
||||
const response = await this.client.get(`/user/listUsers?usernameFilter=${filter}`);
|
||||
return response.data;
|
||||
@@ -68,6 +70,28 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
recoverUser = async(data) => {
|
||||
const form = new FormData();
|
||||
form.append('password', data.password);
|
||||
form.append('inviteToken', data.inviteToken);
|
||||
|
||||
try {
|
||||
const response = await this.client.post(`/user/recover`, form);
|
||||
this.persistToken(response.data.token);
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e.response.status === 400) {
|
||||
const error = {
|
||||
'type': FormValidationErrorType,
|
||||
'messages': e.response.data
|
||||
};
|
||||
throw error;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inviteExistingUser = async(userId) =>{
|
||||
const response = await this.client.post(`/user/${userId}/invite`);
|
||||
return response.data;
|
||||
@@ -78,6 +102,9 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Workspace api
|
||||
getWorkspaces = async() =>{
|
||||
const response = await this.client.get(`/workspace/`);
|
||||
return response.data;
|
||||
@@ -108,6 +135,8 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
analyzeStatus = async () => {
|
||||
const response = await this.client.get("/status/analyze");
|
||||
return response.data;
|
||||
|
Reference in New Issue
Block a user