edit page - insert data bug

This commit is contained in:
Leon
2022-02-02 13:22:10 +02:00
parent 5f58d036c6
commit 4b8c00f2a7
15 changed files with 103 additions and 42 deletions

View File

@@ -48,7 +48,6 @@
& .user__email
width : 300px
height: 30px;
box-sizing: border-box;
& .user__role

View File

@@ -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>

View File

@@ -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>

View File

@@ -38,4 +38,6 @@
height: 100vh;
&__container
@include tab-container
.table-body-style
background: $main-background-color

View File

@@ -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}/>

View File

@@ -13,7 +13,7 @@
.namespacesSettingsContainer
border-radius: 4px
padding: 20px 0
.searchNamespace
width: 300px

View File

@@ -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>
</>);
};

View File

@@ -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>
})}

View File

@@ -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%

View File

@@ -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;

View File

@@ -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)

View File

@@ -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) } }

View 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
View File

@@ -0,0 +1,5 @@
export class Utils{
static capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}

View File

@@ -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;