Merge branch 'origin/ui/TRA-4204_user_managment' of github.com:up9inc/mizu into feature/ui/TRA-4192_workspace_management

# Conflicts:
#	ui/src/components/Modals/AddUserModal/AddUserModal.tsx
#	ui/src/helpers/api.js
This commit is contained in:
Amit Fainholts
2022-01-31 18:11:01 +02:00
6 changed files with 125 additions and 67 deletions

View File

@@ -55,4 +55,7 @@
.search-workspace .search-workspace
width: 186px width: 186px
.u-margin-left
margin-left : 20px

View File

@@ -74,15 +74,6 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
(async () => { (async () => {
try { try {
setEditMode(isEditMode) setEditMode(isEditMode)
if (isEditMode) {
//const userDetails = await api.getUserDetails(userData)
//const data = {...userData,...userDetails}
}
else{
}
setUserData(userData as UserData) setUserData(userData as UserData)
} catch (e) { } catch (e) {
toast.error("Error getting user details") toast.error("Error getting user details")
@@ -98,6 +89,18 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
onCloseModal() onCloseModal()
setUserData({} as UserData) setUserData({} as UserData)
setInvite({sent:false,isSuceeded:false,link:""}) setInvite({sent:false,isSuceeded:false,link:""})
setEditMode(false)
setDisable(true)
}
const updateUser = async() =>{
try {
const res = await api.updateUser(userDataModel)
onClose()
toast.success("User has been modified")
} catch (error) {
toast.error("Error accured modifing user")
}
} }
const workspaceChange = (workspaces) => { const workspaceChange = (workspaces) => {
@@ -125,8 +128,7 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
}; };
const isFormValid = () : boolean => { const isFormValid = () : boolean => {
return true; return (Object.values(userDataModel).length >= 3) && Object.values(userDataModel).every(val => val !== null)
//return (Object.values(userDataModel).length === 3) && Object.values(userDataModel).every(val => val !== null)
} }
const setGenarateDisabledState = () => { const setGenarateDisabledState = () => {
@@ -134,39 +136,59 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
setDisable(!isValid) setDisable(!isValid)
} }
const generateLink = () => { const mapTokenToLink = (token) => {
try { return`${window.location.origin}/${token}`
if (editMode) { }
//await api.updateUser(userDataModel)
setInvite({...invite,isSuceeded:true,sent:true,link:"asdasdasdasdasdasdasdasdads"}) const generateLink = async() => {
toast.success("User has been modified") try {
} const res = await api.genareteInviteLink(userDataModel)
else{ setInvite({...invite,isSuceeded:true,sent:true, link: mapTokenToLink(res.inviteToken)})
//const res = await api.genareteInviteLink(userDataModel)
setInvite({...invite,isSuceeded:true,sent:true, link:"asdasdasdasdasdasdasdasdads"})
toast.success("User has been added") toast.success("User has been added")
} onUserChange(userDataModel)
} catch (e) {
toast.error("Error accrued generating link")
}
}
const inviteExistingUser = async() => {
try {
const res = await api.inviteExistingUser(userDataModel.userId)
setInvite({...invite,isSuceeded:true,sent:true, link: mapTokenToLink(res.inviteToken)})
toast.success("Invite link created")
onUserChange(userDataModel) onUserChange(userDataModel)
} catch (e) { } catch (e) {
toast.error("Error accrued generating link") toast.error("Error accrued generating link")
} }
}
const isShowInviteLink = () => {
return ((invite.isSuceeded && invite.link));
}
const showGenerateButton = () => {
return (!invite.isSuceeded || !(invite.link && invite.sent))
} }
const handleCopyinviteLink = (e) => {navigator.clipboard.writeText(invite.link)} const handleCopyinviteLink = (e) => {navigator.clipboard.writeText(invite.link)}
const modalCustomActions = <> const addUsermodalCustomActions = <>
{(!invite.isSuceeded || !(invite.link && invite.sent)) && <Button {showGenerateButton() && <Button
className={classes.button + " generate-link-button"} size={"small"} onClick={generateLink} className={classes.button + " generate-link-button"} size={"small"}
onClick={!isEditMode ? generateLink : inviteExistingUser}
disabled={disable} disabled={disable}
endIcon={isLoading && <img src={spinner} alt="spinner"/>}> endIcon={isLoading && <img src={spinner} alt="spinner"/>}>
<span className='generate-link-button__icon'></span> <span className='generate-link-button__icon'></span>
{"Generate Invite Link"} {"Generate Invite Link"}
</Button>} </Button>}
{invite.isSuceeded && invite.link && <div className="invite-link-row"> {
<FormControl variant="outlined" size={"small"} className='invite-link-field'> isEditMode && <Button style={{height: '100%'}} disabled={disable} className={classes.button + " u-margin-left"} size={"small"} onClick={updateUser}>
Save
</Button>
}
<div className="invite-link-row">
{isShowInviteLink() && <FormControl variant="outlined" size={"small"} className='invite-link-field'>
<InputLabel htmlFor="outlined-adornment-password">Invite link</InputLabel> <InputLabel htmlFor="outlined-adornment-password">Invite link</InputLabel>
<OutlinedInput <OutlinedInput
type={'text'} type={'text'}
@@ -178,20 +200,20 @@ export const AddUserModal: FC<AddUserModalProps> = ({isOpen, onCloseModal, userD
{<span className='generate-link-button__icon'></span>} {<span className='generate-link-button__icon'></span>}
</IconButton> </IconButton>
</InputAdornment> </InputAdornment>
} } label="Invite link"/>
label="Invite link" </FormControl>}
/> {!isEditMode && isShowInviteLink() && <Button style={{height: '100%'}} className={classes.button + " u-margin-left"} size={"small"} onClick={onClose}>
</FormControl>
<Button style={{height: '100%'}} className={classes.button} size={"small"} onClick={onClose}>
Done Done
</Button> </Button>}
</div>}
</>; </div>
</>;
return (<> return (<>
<ConfirmationModal isOpen={isOpen} onClose={onClose} onConfirm={onClose} <ConfirmationModal isOpen={isOpen} onClose={onClose} onConfirm={onClose}
title={`${editMode ? "Edit" : "Add"} User`} customActions={modalCustomActions}> title={`${editMode ? "Edit" : "Add"} User`} customActions={addUsermodalCustomActions}>
<h3 className='comfirmation-modal__sub-section-header'>DETAILS</h3> <h3 className='comfirmation-modal__sub-section-header'>DETAILS</h3>
<div className='comfirmation-modal__sub-section'> <div className='comfirmation-modal__sub-section'>

View File

@@ -1,14 +1,17 @@
@import "../../../variables.module" @import "../../../variables.module"
@mixin tab-container
max-width: 513px
margin: 0 auto
.settings-page .settings-page
max-width: 513px background: #F7F9FC;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-content: center; align-content: center;
justify-content: center; /* justify-content: center; */
margin: 0 auto; margin: 0 auto;
height: 100vh;
& .tabs-nav & .tabs-nav
padding-top: 0px; padding-top: 0px;
@@ -16,8 +19,10 @@
.header-section .header-section
margin-bottom: 30px;
background: #F7F9FC;
&__container
@include tab-container
&__title &__title
@@ -27,3 +32,8 @@
margin-bottom: 60px; margin-bottom: 60px;
margin-top: 20px; margin-top: 20px;
.tab-content
padding-top: 30px;
&__container
@include tab-container

View File

@@ -13,19 +13,22 @@ const AdminSettings: React.FC<any> = ({color}) => {
const [currentTab, setCurrentTab] = useState(TABS[0].tab); const [currentTab, setCurrentTab] = useState(TABS[0].tab);
return (<> return (<>
<div className="settings-page"> <div className="settings-page">
<div className="header-section"> <div className="header-section">
<div className="header-section__title">Settings</div> <div className="header-section__container">
<Tabs tabs={TABS} currentTab={currentTab} color={color} onChange={setCurrentTab} leftAligned classes={{root:"tabs-nav"}}/> <div className="header-section__title">Settings</div>
</div> <Tabs tabs={TABS} currentTab={currentTab} color={color} onChange={setCurrentTab} leftAligned classes={{root:"tabs-nav"}}/>
<div> </div>
</div>
{currentTab === TABS[0].tab && <React.Fragment> <div className="tab-content">
<UserSettings/> <div className="tab-content__container">
</React.Fragment>} {currentTab === TABS[0].tab && <React.Fragment>
{currentTab === TABS[1].tab && <React.Fragment> <UserSettings/>
<WorkspaceSettings/> </React.Fragment>}
</React.Fragment>} {currentTab === TABS[1].tab && <React.Fragment>
</div> <WorkspaceSettings/>
</React.Fragment>}
</div>
</div>
</div> </div>
</> </>
) )

View File

@@ -13,6 +13,11 @@ interface Props {
const api = Api.getInstance(); const api = Api.getInstance();
enum InviteStatus{
active = "Active",
pending = "Pending"
}
export const UserSettings : React.FC<Props> = ({}) => { export const UserSettings : React.FC<Props> = ({}) => {
const [usersRows, setUserRows] = useState([]); const [usersRows, setUserRows] = useState([]);
@@ -20,7 +25,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
const cols : ColsType[] = [{field : "username",header:"User"}, const cols : ColsType[] = [{field : "username",header:"User"},
{field : "role",header:"Role"}, {field : "role",header:"Role"},
{field : "status",header:"Status",getCellClassName : (field, val) =>{ {field : "status",header:"Status",getCellClassName : (field, val) =>{
return val === "Active" ? "status--active" : "status--pending" return val === InviteStatus.active ? "status--active" : "status--pending"
}}] }}]
const [isOpenModal,setIsOpen] = useState(false) const [isOpenModal,setIsOpen] = useState(false)
const [editMode, setEditMode] = useState(false); const [editMode, setEditMode] = useState(false);
@@ -28,9 +33,13 @@ export const UserSettings : React.FC<Props> = ({}) => {
const getUserList = (async () => { const getUserList = (async () => {
try { try {
let users = [{username:"asd",role:"Admin",status:"Active",userId : "1"}, // let users = [{username:"asd",role:"Admin",status:"Active",userId : "1"},
{username:"aaaaaaa",role:"User",status:"Active",userId : "2"}]//await api.getUsers() // {username:"asdasdasdasdasdasd",role:"User",status:"Active",userId : "2"}]
setUserRows(users) let users = await api.getUsers()
const mappedUsers = users.map((user) => {
return {...user,status: capitalizeFirstLetter(user.status), role: capitalizeFirstLetter(user.role)}
})
setUserRows(mappedUsers)
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@@ -41,6 +50,10 @@ export const UserSettings : React.FC<Props> = ({}) => {
getUserList(); getUserList();
},[]) },[])
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
const filterFuncFactory = (searchQuery: string) => { const filterFuncFactory = (searchQuery: string) => {
return (row) => { return (row) => {
return row.username.toLowerCase().includes(searchQuery.toLowerCase()) || return row.username.toLowerCase().includes(searchQuery.toLowerCase()) ||
@@ -64,7 +77,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
const onConfirmDelete = () => { const onConfirmDelete = () => {
(async() => { (async() => {
try { try {
//await api.deleteUser(user) await api.deleteUser(userData)
const findFunc = filterFuncFactory(userData.userId); const findFunc = filterFuncFactory(userData.userId);
const usersLeft = usersRows.filter(e => !findFunc(e)) const usersLeft = usersRows.filter(e => !findFunc(e))
setUserRows(usersLeft) setUserRows(usersLeft)
@@ -72,6 +85,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
} catch (error) { } catch (error) {
toast.error("User want not deleted") toast.error("User want not deleted")
} }
setConfirmModalOpen(false);
})() })()
} }
@@ -85,7 +99,7 @@ export const UserSettings : React.FC<Props> = ({}) => {
getUserList() getUserList()
} }
const buttonConfig = {onClick: () => {setIsOpen(true)}, text:"Add User"} const buttonConfig = {onClick: () => {setIsOpen(true);setEditMode(false);}, text:"Add User"}
return (<> return (<>
<FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig} <FilterableTableAction onRowEdit={onRowEdit} onRowDelete={onRowDelete} searchConfig={searchConfig}

View File

@@ -68,7 +68,12 @@ export default class Api {
return response.data; return response.data;
} }
genareteInviteLink = async(userData) =>{ inviteExistingUser = async(userId) =>{
const response = await this.client.post(`/user/${userId}/invite`);
return response.data;
}
genareteInviteLink = async(userData) =>{
const response = await this.client.post(`/user/createUserAndInvite`,userData); const response = await this.client.post(`/user/createUserAndInvite`,userData);
return response.data; return response.data;
} }
@@ -224,6 +229,7 @@ export default class Api {
} }
} }
login = async (username, password) => { login = async (username, password) => {
const form = new FormData(); const form = new FormData();
form.append('username', username); form.append('username', username);