change(ui) - preferences - edit button

This commit is contained in:
Shekar Siri 2022-08-08 12:00:31 +02:00
parent 050ce038cc
commit ca6a6db63f
7 changed files with 355 additions and 374 deletions

View file

@ -1,6 +1,6 @@
import React from 'react';
import cn from 'classnames';
import { Icon } from 'UI';
import { Button } from 'UI';
import styles from './listItem.module.css';
const ListItem = ({ field, onEdit, disabled }) => {
@ -17,9 +17,7 @@ const ListItem = ({ field, onEdit, disabled }) => {
>
<span>{field.key}</span>
<div className="invisible group-hover:visible" data-hidden={field.index === 0}>
<div className={styles.button}>
<Icon name="edit" color="teal" size="18" />
</div>
<Button variant="text-primary" icon="pencil" />
</div>
</div>
);

View file

@ -1,156 +1,154 @@
import React, { useState, useEffect } from 'react'
import cn from 'classnames'
import { Loader, IconButton, Popup, NoContent, SlideModal } from 'UI'
import { connect } from 'react-redux'
import stl from './roles.module.css'
import RoleForm from './components/RoleForm'
import React, { useState, useEffect } from 'react';
import cn from 'classnames';
import { Loader, IconButton, Popup, NoContent, SlideModal } from 'UI';
import { connect } from 'react-redux';
import stl from './roles.module.css';
import RoleForm from './components/RoleForm';
import { init, edit, fetchList, remove as deleteRole, resetErrors } from 'Duck/roles';
import RoleItem from './components/RoleItem'
import RoleItem from './components/RoleItem';
import { confirm } from 'UI';
import { toast } from 'react-toastify';
import withPageTitle from 'HOCs/withPageTitle';
import { useModal } from 'App/components/Modal';
interface Props {
loading: boolean
init: (role?: any) => void,
edit: (role: any) => void,
instance: any,
roles: any[],
deleteRole: (id: any) => Promise<void>,
fetchList: () => Promise<void>,
account: any,
permissionsMap: any,
removeErrors: any,
resetErrors: () => void,
projectsMap: any,
loading: boolean;
init: (role?: any) => void;
edit: (role: any) => void;
instance: any;
roles: any[];
deleteRole: (id: any) => Promise<void>;
fetchList: () => Promise<void>;
account: any;
permissionsMap: any;
removeErrors: any;
resetErrors: () => void;
projectsMap: any;
}
function Roles(props: Props) {
const { loading, instance, roles, init, edit, deleteRole, account, permissionsMap, projectsMap, removeErrors } = props
const [showModal, setShowmModal] = useState(false)
const isAdmin = account.admin || account.superAdmin;
const { loading, instance, roles, init, edit, deleteRole, account, permissionsMap, projectsMap, removeErrors } = props;
// const [showModal, setShowmModal] = useState(false);
const { showModal, hideModal } = useModal();
const isAdmin = account.admin || account.superAdmin;
useEffect(() => {
props.fetchList()
}, [])
useEffect(() => {
props.fetchList();
}, []);
useEffect(() => {
if (removeErrors && removeErrors.size > 0) {
removeErrors.forEach(e => {
toast.error(e)
})
}
return () => {
props.resetErrors()
}
}, [removeErrors])
useEffect(() => {
if (removeErrors && removeErrors.size > 0) {
removeErrors.forEach((e) => {
toast.error(e);
});
}
return () => {
props.resetErrors();
};
}, [removeErrors]);
const closeModal = (showToastMessage) => {
if (showToastMessage) {
toast.success(showToastMessage)
props.fetchList()
}
setShowmModal(false)
setTimeout(() => {
init()
}, 100)
}
const closeModal = (showToastMessage) => {
if (showToastMessage) {
toast.success(showToastMessage);
props.fetchList();
}
setShowmModal(false);
setTimeout(() => {
init();
}, 100);
};
const editHandler = role => {
init(role)
setShowmModal(true)
}
const editHandler = (role: any) => {
init(role);
showModal(<RoleForm closeModal={hideModal} permissionsMap={permissionsMap} deleteHandler={deleteHandler} />, { right: true });
// setShowmModal(true);
};
const deleteHandler = async (role) => {
if (await confirm({
header: 'Roles',
confirmation: `Are you sure you want to remove this role?`
})) {
deleteRole(role.roleId)
}
}
const deleteHandler = async (role: any) => {
if (
await confirm({
header: 'Roles',
confirmation: `Are you sure you want to remove this role?`,
})
) {
deleteRole(role.roleId);
}
};
return (
<React.Fragment>
<Loader loading={ loading }>
<SlideModal
title={ instance.exists() ? "Edit Role" : "Create Role" }
size="small"
isDisplayed={showModal }
content={ showModal && <RoleForm closeModal={closeModal} permissionsMap={permissionsMap} deleteHandler={deleteHandler} /> }
onClose={ closeModal }
/>
<div className={ stl.wrapper }>
<div className={ cn(stl.tabHeader, 'flex items-center') }>
<div className="flex items-center mr-auto">
<h3 className={ cn(stl.tabTitle, "text-2xl") }>Roles and Access</h3>
<Popup
content="You dont have the permissions to perform this action."
disabled={ isAdmin }
>
<div>
<IconButton
id="add-button"
circle
icon="plus"
outline
disabled={ !isAdmin }
onClick={ () => setShowmModal(true) }
/>
return (
<React.Fragment>
<Loader loading={loading}>
{/* <SlideModal
title={instance.exists() ? 'Edit Role' : 'Create Role'}
size="small"
isDisplayed={showModal}
content={showModal && <RoleForm closeModal={closeModal} permissionsMap={permissionsMap} deleteHandler={deleteHandler} />}
onClose={closeModal}
/> */}
<div className={stl.wrapper}>
<div className={cn(stl.tabHeader, 'flex items-center')}>
<div className="flex items-center mr-auto">
<h3 className={cn(stl.tabTitle, 'text-2xl')}>Roles and Access</h3>
<Popup content="You dont have the permissions to perform this action." disabled={isAdmin}>
<div>
<IconButton id="add-button" circle icon="plus" outline disabled={!isAdmin} onClick={() => setShowmModal(true)} />
</div>
</Popup>
</div>
</div>
<NoContent title="No roles are available." size="small" show={false} icon>
<div className={''}>
<div className={cn('flex items-start py-3 border-b px-3 pr-20 font-medium')}>
<div className="" style={{ width: '20%' }}>
Title
</div>
<div className="" style={{ width: '30%' }}>
Project Access
</div>
<div className="" style={{ width: '50%' }}>
Feature Access
</div>
<div></div>
</div>
{roles.map((role) => (
<RoleItem
role={role}
isAdmin={isAdmin}
permissions={permissionsMap}
projects={projectsMap}
editHandler={editHandler}
deleteHandler={deleteHandler}
/>
))}
</div>
</NoContent>
</div>
</Popup>
</div>
</div>
<NoContent
title="No roles are available."
size="small"
show={ false }
icon
>
<div className={''}>
<div className={cn('flex items-start py-3 border-b px-3 pr-20 font-medium')}>
<div className="" style={{ width: '20%'}}>Title</div>
<div className="" style={{ width: '30%'}}>Project Access</div>
<div className="" style={{ width: '50%'}}>Feature Access</div>
<div></div>
</div>
{roles.map(role => (
<RoleItem
role={role}
isAdmin={isAdmin}
permissions={permissionsMap}
projects={projectsMap}
editHandler={editHandler}
deleteHandler={deleteHandler}
/>
))}
</div>
</NoContent>
</div>
</Loader>
</React.Fragment>
)
</Loader>
</React.Fragment>
);
}
export default connect(state => {
const permissions = state.getIn(['roles', 'permissions'])
const permissionsMap = {}
permissions.forEach(p => {
permissionsMap[p.value] = p.text
});
const projects = state.getIn([ 'site', 'list' ])
return {
instance: state.getIn(['roles', 'instance']) || null,
permissionsMap: permissionsMap,
roles: state.getIn(['roles', 'list']),
removeErrors: state.getIn(['roles', 'removeRequest', 'errors']),
loading: state.getIn(['roles', 'fetchRequest', 'loading']),
account: state.getIn([ 'user', 'account' ]),
projectsMap: projects.reduce((acc, p) => {
acc[ p.get('id') ] = p.get('name')
return acc
}
, {}),
}
}, { init, edit, fetchList, deleteRole, resetErrors })(withPageTitle('Roles & Access - OpenReplay Preferences')(Roles))
export default connect(
(state: any) => {
const permissions = state.getIn(['roles', 'permissions']);
const permissionsMap = {};
permissions.forEach((p: any) => {
permissionsMap[p.value] = p.text;
});
const projects = state.getIn(['site', 'list']);
return {
instance: state.getIn(['roles', 'instance']) || null,
permissionsMap: permissionsMap,
roles: state.getIn(['roles', 'list']),
removeErrors: state.getIn(['roles', 'removeRequest', 'errors']),
loading: state.getIn(['roles', 'fetchRequest', 'loading']),
account: state.getIn(['user', 'account']),
projectsMap: projects.reduce((acc: any, p: any) => {
acc[p.get('id')] = p.get('name');
return acc;
}, {}),
};
},
{ init, edit, fetchList, deleteRole, resetErrors }
)(withPageTitle('Roles & Access - OpenReplay Preferences')(Roles));

View file

@ -1,203 +1,195 @@
import React, { useRef, useEffect } from 'react'
import { connect } from 'react-redux'
import stl from './roleForm.module.css'
import { save, edit } from 'Duck/roles'
import { Form, Input, Button, Checkbox, Icon } from 'UI'
import React, { useRef, useEffect } from 'react';
import { connect } from 'react-redux';
import stl from './roleForm.module.css';
import { save, edit } from 'Duck/roles';
import { Form, Input, Button, Checkbox, Icon } from 'UI';
import Select from 'Shared/Select';
interface Permission {
name: string,
value: string
name: string;
value: string;
}
interface Props {
role: any,
edit: (role: any) => void,
save: (role: any) => Promise<void>,
closeModal: (toastMessage?: string) => void,
saving: boolean,
permissions: Array<Permission>[]
projectOptions: Array<any>[],
permissionsMap: any,
projectsMap: any,
deleteHandler: (id: any) => Promise<void>,
role: any;
edit: (role: any) => void;
save: (role: any) => Promise<void>;
closeModal: (toastMessage?: string) => void;
saving: boolean;
permissions: Array<Permission>[];
projectOptions: Array<any>[];
permissionsMap: any;
projectsMap: any;
deleteHandler: (id: any) => Promise<void>;
}
const RoleForm = (props: Props) => {
const { role, edit, save, closeModal, saving, permissions, projectOptions, permissionsMap, projectsMap } = props
let focusElement = useRef<any>(null)
const _save = () => {
save(role).then(() => {
closeModal(role.exists() ? "Role updated" : "Role created");
})
}
const { role, edit, save, closeModal, saving, permissions, projectOptions, permissionsMap, projectsMap } = props;
let focusElement = useRef<any>(null);
const _save = () => {
save(role).then(() => {
closeModal(role.exists() ? 'Role updated' : 'Role created');
});
};
const write = ({ target: { value, name } }) => edit({ [ name ]: value })
const write = ({ target: { value, name } }) => edit({ [name]: value });
const onChangePermissions = (e) => {
const { permissions } = role
const index = permissions.indexOf(e)
const _perms = permissions.contains(e) ? permissions.remove(index) : permissions.push(e)
edit({ permissions: _perms })
}
const onChangePermissions = (e) => {
const { permissions } = role;
const index = permissions.indexOf(e);
const _perms = permissions.contains(e) ? permissions.remove(index) : permissions.push(e);
edit({ permissions: _perms });
};
const onChangeProjects = (e) => {
const { projects } = role
const index = projects.indexOf(e)
const _projects = index === -1 ? projects.push(e) : projects.remove(index)
edit({ projects: _projects })
}
const onChangeProjects = (e) => {
const { projects } = role;
const index = projects.indexOf(e);
const _projects = index === -1 ? projects.push(e) : projects.remove(index);
edit({ projects: _projects });
};
const writeOption = ({ name, value }: any) => {
if (name === 'permissions') {
onChangePermissions(value)
} else if (name === 'projects') {
onChangeProjects(value)
}
}
const writeOption = ({ name, value }: any) => {
if (name === 'permissions') {
onChangePermissions(value);
} else if (name === 'projects') {
onChangeProjects(value);
}
};
const toggleAllProjects = () => {
const { allProjects } = role
edit({ allProjects: !allProjects })
}
const toggleAllProjects = () => {
const { allProjects } = role;
edit({ allProjects: !allProjects });
};
useEffect(() => {
focusElement && focusElement.current && focusElement.current.focus()
}, [])
useEffect(() => {
focusElement && focusElement.current && focusElement.current.focus();
}, []);
return (
<div className={ stl.form }>
<Form onSubmit={ _save } >
<Form.Field>
<label>{ 'Title' }</label>
<Input
ref={ focusElement }
name="name"
value={ role.name }
onChange={ write }
className={ stl.input }
id="name-field"
placeholder="Ex. Admin"
/>
</Form.Field>
return (
<div className="bg-white h-screen overflow-y-auto" style={{ width: '350px' }}>
<h3 className="p-5 text-2xl">{role.exists() ? 'Edit Role' : 'Create Role'}</h3>
<div className="px-5">
<Form onSubmit={_save}>
<Form.Field>
<label>{'Title'}</label>
<Input
ref={focusElement}
name="name"
value={role.name}
onChange={write}
className={stl.input}
id="name-field"
placeholder="Ex. Admin"
/>
</Form.Field>
<Form.Field>
<label>{ 'Project Access' }</label>
<Form.Field>
<label>{'Project Access'}</label>
<div className="flex my-3">
<Checkbox
name="allProjects"
className="font-medium"
type="checkbox"
checked={ role.allProjects }
onClick={toggleAllProjects}
label={''}
/>
<div className="cursor-pointer" onClick={toggleAllProjects}>
<div>All Projects</div>
<span className="text-xs text-gray-600">
(Uncheck to select specific projects)
</span>
</div>
</div>
{ !role.allProjects && (
<>
<Select
isSearchable
name="projects"
options={ projectOptions }
onChange={ ({ value }: any) => writeOption({ name: 'projects', value: value.value }) }
value={null}
/>
{ role.projects.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{ role.projects.map(p => (
OptionLabel(projectsMap, p, onChangeProjects)
)) }
<div className="flex my-3">
<Checkbox
name="allProjects"
className="font-medium mr-3"
type="checkbox"
checked={role.allProjects}
onClick={toggleAllProjects}
label={''}
/>
<div className="cursor-pointer leading-none select-none" onClick={toggleAllProjects}>
<div>All Projects</div>
<span className="text-xs text-gray-600">(Uncheck to select specific projects)</span>
</div>
</div>
{!role.allProjects && (
<>
<Select
isSearchable
name="projects"
options={projectOptions}
onChange={({ value }: any) => writeOption({ name: 'projects', value: value.value })}
value={null}
/>
{role.projects.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{role.projects.map((p) => OptionLabel(projectsMap, p, onChangeProjects))}
</div>
)}
</>
)}
</Form.Field>
<Form.Field>
<label>{'Capability Access'}</label>
<Select
isSearchable
name="permissions"
options={permissions}
onChange={({ value }: any) => writeOption({ name: 'permissions', value: value.value })}
value={null}
/>
{role.permissions.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{role.permissions.map((p) => OptionLabel(permissionsMap, p, onChangePermissions))}
</div>
)}
</Form.Field>
</Form>
<div className="flex items-center">
<div className="flex items-center mr-auto">
<Button onClick={_save} disabled={!role.validate()} loading={saving} variant="primary" className="float-left mr-2">
{role.exists() ? 'Update' : 'Add'}
</Button>
{role.exists() && <Button onClick={closeModal}>{'Cancel'}</Button>}
</div>
{role.exists() && (
<Button variant="text" onClick={() => props.deleteHandler(role)}>
<Icon name="trash" size="18" />
</Button>
)}
</div>
)}
</>
)}
</Form.Field>
<Form.Field>
<label>{ 'Capability Access' }</label>
<Select
isSearchable
name="permissions"
options={ permissions }
onChange={ ({ value }: any) => writeOption({ name: 'permissions', value: value.value }) }
value={null}
/>
{ role.permissions.size > 0 && (
<div className="flex flex-row items-start flex-wrap mt-4">
{ role.permissions.map(p => (
OptionLabel(permissionsMap, p, onChangePermissions)
)) }
</div>
)}
</Form.Field>
</Form>
<div className="flex items-center">
<div className="flex items-center mr-auto">
<Button
onClick={ _save }
disabled={ !role.validate() }
loading={ saving }
variant="primary"
className="float-left mr-2"
>
{ role.exists() ? 'Update' : 'Add' }
</Button>
{ role.exists() && (
<Button
onClick={ closeModal }
>
{ 'Cancel' }
</Button>
)}
</div>
{ role.exists() && (
<Button
variant="text"
onClick={ () => props.deleteHandler(role) }
>
<Icon name="trash" size="18"/>
</Button>
)}
</div>
</div>
);
}
);
};
export default connect((state: any) => {
const role = state.getIn(['roles', 'instance'])
const projects = state.getIn([ 'site', 'list' ])
return {
role,
projectOptions: projects.map((p: any) => ({
key: p.get('id'),
value: p.get('id'),
label: p.get('name'),
// isDisabled: role.projects.includes(p.get('id')),
})).filter(({ value }: any) => !role.projects.includes(value)).toJS(),
permissions: state.getIn(['roles', 'permissions']).filter(({ value }: any) => !role.permissions.includes(value))
.map(({ text, value }: any) => ({ label: text, value })).toJS(),
saving: state.getIn([ 'roles', 'saveRequest', 'loading' ]),
projectsMap: projects.reduce((acc: any, p: any) => {
acc[ p.get('id') ] = p.get('name')
return acc
}
, {}),
}
}, { edit, save })(RoleForm);
export default connect(
(state: any) => {
const role = state.getIn(['roles', 'instance']);
const projects = state.getIn(['site', 'list']);
return {
role,
projectOptions: projects
.map((p: any) => ({
key: p.get('id'),
value: p.get('id'),
label: p.get('name'),
// isDisabled: role.projects.includes(p.get('id')),
}))
.filter(({ value }: any) => !role.projects.includes(value))
.toJS(),
permissions: state
.getIn(['roles', 'permissions'])
.filter(({ value }: any) => !role.permissions.includes(value))
.map(({ text, value }: any) => ({ label: text, value }))
.toJS(),
saving: state.getIn(['roles', 'saveRequest', 'loading']),
projectsMap: projects.reduce((acc: any, p: any) => {
acc[p.get('id')] = p.get('name');
return acc;
}, {}),
};
},
{ edit, save }
)(RoleForm);
function OptionLabel(nameMap: any, p: any, onChangeOption: (e: any) => void) {
return <div className="px-2 py-1 rounded bg-gray-lightest mr-2 mb-2 border flex items-center justify-between">
<div>{nameMap[p]}</div>
<div className="cursor-pointer ml-2" onClick={() => onChangeOption(p)}>
<Icon name="close" size="12" />
</div>
</div>
return (
<div className="px-2 py-1 rounded bg-gray-lightest mr-2 mb-2 border flex items-center justify-between">
<div>{nameMap[p]}</div>
<div className="cursor-pointer ml-2" onClick={() => onChangeOption(p)}>
<Icon name="close" size="12" />
</div>
</div>
);
}

View file

@ -1,64 +1,58 @@
import React from 'react'
import { Icon, Link } from 'UI'
import stl from './roleItem.module.css'
import cn from 'classnames'
import React from 'react';
import { Icon, Link, Button } from 'UI';
import stl from './roleItem.module.css';
import cn from 'classnames';
import { CLIENT_TABS, client as clientRoute } from 'App/routes';
function PermisionLabel({ label }: any) {
return (
<div className={cn(stl.label, 'mb-2')}>{ label }</div>
);
return <div className={cn(stl.label, 'mb-2')}>{label}</div>;
}
function PermisionLabelLinked({ label, route }: any) {
return (
<Link to={route}><div className={cn(stl.label, 'mb-2 bg-active-blue color-teal')}>{ label }</div></Link>
);
return (
<Link to={route}>
<div className={cn(stl.label, 'mb-2 bg-active-blue color-teal')}>{label}</div>
</Link>
);
}
interface Props {
role: any,
deleteHandler?: (role: any) => void,
editHandler?: (role: any) => void,
permissions: any,
isAdmin: boolean,
projects: any,
role: any;
deleteHandler?: (role: any) => void;
editHandler?: (role: any) => void;
permissions: any;
isAdmin: boolean;
projects: any;
}
function RoleItem({ role, deleteHandler, editHandler, isAdmin, permissions, projects }: Props) {
return (
<div className={cn('flex items-start relative py-4 hover border-b last:border-none px-3 pr-20 group')}>
<div className="flex" style={{ width: '20%'}}>
<Icon name="user-alt" size="16" marginRight="10" />
{ role.name }
</div>
<div className="flex items-start flex-wrap" style={{ width: '30%'}}>
{role.allProjects ? (
<PermisionLabelLinked label="All projects" route={clientRoute(CLIENT_TABS.SITES)}/>
) : (
role.projects.map(p => (
<PermisionLabel label={projects[p]} />
))
)}
</div>
<div className="flex items-start flex-wrap" style={{ width: '50%'}}>
<div className="flex items-center flex-wrap">
{role.permissions.map((permission: any) => (
<PermisionLabel label={permissions[permission]} key={permission.id} />
))}
</div>
<div className={ cn(stl.actions, 'absolute right-0 top-0 bottom-0 mr-8 invisible group-hover:visible') }>
{isAdmin && !!editHandler &&
<div className={ cn(stl.button, {[stl.disabled] : role.protected }) } onClick={ () => editHandler(role) }>
<Icon name="edit" size="16" color="teal"/>
return (
<div className={cn('flex items-start relative py-4 hover border-b last:border-none px-3 pr-20 group')}>
<div className="flex" style={{ width: '20%' }}>
<Icon name="user-alt" size="16" marginRight="10" />
{role.name}
</div>
<div className="flex items-start flex-wrap" style={{ width: '30%' }}>
{role.allProjects ? (
<PermisionLabelLinked label="All projects" route={clientRoute(CLIENT_TABS.SITES)} />
) : (
role.projects.map((p) => <PermisionLabel label={projects[p]} />)
)}
</div>
<div className="flex items-start flex-wrap" style={{ width: '50%' }}>
<div className="flex items-center flex-wrap">
{role.permissions.map((permission: any) => (
<PermisionLabel label={permissions[permission]} key={permission.id} />
))}
</div>
<div className={cn(stl.actions, 'absolute right-0 top-0 bottom-0 mr-8 invisible group-hover:visible')}>
{isAdmin && !!editHandler && (
<Button variant="text-primary" icon="pencil" disabled={role.protected} onClick={() => editHandler(role)} />
)}
</div>
</div>
}
</div>
</div>
</div>
);
);
}
export default RoleItem;
export default RoleItem;

View file

@ -16,7 +16,7 @@ function InstallButton(props: Props) {
);
};
return (
<Button size="small" variant="primary" onClick={onClick}>
<Button size="small" variant="text-primary" onClick={onClick}>
{'Installation Steps'}
</Button>
);

View file

@ -130,5 +130,5 @@ function EditButton({ isAdmin, onClick }) {
onClick();
showModal(<NewSiteForm onClose={hideModal} />);
};
return <Button icon="edit" variant="text" disabled={!isAdmin} onClick={_onClick} />;
return <Button icon="edit" variant="text-primary" disabled={!isAdmin} onClick={_onClick} />;
}

View file

@ -1,6 +1,7 @@
import React from 'react';
import { Icon } from 'UI';
import styles from './listItem.module.css';
import { Button } from 'UI';
const ListItem = ({ webhook, onEdit, onDelete }) => {
return (
@ -10,9 +11,7 @@ const ListItem = ({ webhook, onEdit, onDelete }) => {
<div className={styles.endpoint}>{webhook.endpoint}</div>
</div>
<div className="invisible group-hover:visible">
<div className={styles.button}>
<Icon name="edit" color="teal" size="16" />
</div>
<Button variant="text-primary" icon="pencil" />
</div>
</div>
);