commit
3d4df4a14a
25 changed files with 93 additions and 53 deletions
|
|
@ -108,8 +108,8 @@ class Notifications extends React.Component {
|
|||
content={
|
||||
<div className="">
|
||||
<NoContent
|
||||
title="No Alerts!"
|
||||
subtext="There are no alerts."
|
||||
title=""
|
||||
subtext="There are no alerts to show."
|
||||
icon="exclamation-circle"
|
||||
show={ !loading && notifications.size === 0 }
|
||||
size="small"
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class Announcements extends React.Component {
|
|||
content={
|
||||
<div className="mx-4">
|
||||
<NoContent
|
||||
title="No Announcements found!"
|
||||
title=""
|
||||
subtext="There are no announcements to show."
|
||||
icon="exclamation-circle"
|
||||
show={ !loading && announcements.size === 0 }
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ function AssistActions({ toggleChatWindow, userId, calling, peerConnectionStatus
|
|||
<span className={cn("ml-2", { 'color-red' : inCall })}>{ inCall ? 'End Call' : 'Call' }</span>
|
||||
</div>
|
||||
}
|
||||
content={ `Call ${userId ? userId : 'User'}` }
|
||||
content={ cannotCall ? "You don’t have the permissions to perform this action." : `Call ${userId ? userId : 'User'}` }
|
||||
size="tiny"
|
||||
inverted
|
||||
position="top right"
|
||||
|
|
|
|||
|
|
@ -130,7 +130,8 @@ export default class EventFilter extends React.PureComponent {
|
|||
onClick={ this.onPlaceholderClick }
|
||||
>
|
||||
{ !searchQuery &&
|
||||
<RandomPlaceholder onClick={ this.onPlaceholderItemClick } appliedFilterKeys={ appliedFilterKeys } />
|
||||
<div className={ stl.placeholder }>Search for users, clicks, page visits, requests, errors and more</div>
|
||||
// <RandomPlaceholder onClick={ this.onPlaceholderItemClick } appliedFilterKeys={ appliedFilterKeys } />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,4 +56,22 @@
|
|||
position: absolute;
|
||||
right: 9px;
|
||||
top: 9px;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: $gray-medium;
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
user-select: none;
|
||||
|
||||
& span {
|
||||
font-weight: 400;
|
||||
color: $teal;
|
||||
cursor: pointer;
|
||||
border-bottom: dashed thin $teal;
|
||||
|
||||
&:hover {
|
||||
color: $teal-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -100,6 +100,7 @@ class CustomFields extends React.Component {
|
|||
title="No data available."
|
||||
size="small"
|
||||
show={ fields.size === 0 }
|
||||
icon
|
||||
>
|
||||
<div className={ styles.list }>
|
||||
{ fields.filter(i => i.index).map(field => (
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ function AssistScript(props) {
|
|||
r.issue=function(k,p){r.push([6,k,p])};
|
||||
r.isActive=function(){return false};
|
||||
r.getSessionToken=function(){};
|
||||
})(0, "${props.projectKey}", "//static.openreplay.com/3.4.0/openreplay-assist.js",1,28);
|
||||
})(0, "${props.projectKey}", "//static.openreplay.com/3.4.9/openreplay-assist.js",1,28);
|
||||
</script>`}
|
||||
</Highlight>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class ManageUsers extends React.PureComponent {
|
|||
}
|
||||
|
||||
adminLabel = (user) => {
|
||||
if (user.superAdmin) return 'Owner';
|
||||
if (user.superAdmin) return null;
|
||||
return user.admin ? 'Admin' : '';
|
||||
};
|
||||
|
||||
|
|
@ -207,8 +207,8 @@ class ManageUsers extends React.PureComponent {
|
|||
<div className={ styles.wrapper }>
|
||||
<div className={ cn(styles.tabHeader, 'flex items-center') }>
|
||||
<div className="flex items-center mr-auto">
|
||||
{ !hideHeader && <h3 className={ cn(styles.tabTitle, "text-2xl") }>{ (isAdmin ? 'Manage ' : '') + 'Users' }</h3> }
|
||||
{ hideHeader && <h3 className={ cn(styles.tabTitle, "text-xl") }>{ `Team Size ${members.size}` }</h3>}
|
||||
{ !hideHeader && <h3 className={ cn(styles.tabTitle, "text-2xl") }>{ (isAdmin ? 'Manage ' : '') + `Users (${members.size})` }</h3> }
|
||||
{ hideHeader && <h3 className={ cn(styles.tabTitle, "text-xl") }>{ `Users (${members.size})` }</h3>}
|
||||
<Popup
|
||||
trigger={
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const UserItem = ({ user, adminLabel, deleteHandler, editHandler, generateInvite
|
|||
<div className={ styles.wrapper } id="user-row">
|
||||
<Icon name="user-alt" size="16" marginRight="10" />
|
||||
<div id="user-name">{ user.name || user.email }</div>
|
||||
<div className="px-2"/>
|
||||
{ adminLabel && <div className={ styles.adminLabel }>{ adminLabel }</div>}
|
||||
{ user.roleName && <div className={ styles.adminLabel }>{ user.roleName }</div>}
|
||||
<div className={ styles.actions }>
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@
|
|||
}
|
||||
|
||||
& .adminLabel {
|
||||
margin-left: 10px;
|
||||
margin-left: 5px;
|
||||
padding: 0 10px;
|
||||
border-radius: 3px;
|
||||
background-color: $gray-lightest;
|
||||
font-size: 12px;
|
||||
font-size: 10px;
|
||||
border: solid thin $gray-light;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ function Licenses({ account }) {
|
|||
<div>
|
||||
<div>{account.license}</div>
|
||||
{account.expirationDate && (
|
||||
<div className="font-medium text-sm">
|
||||
Expires At: {account.expirationDate.toFormat('LLL dd, yyyy')}
|
||||
<div className="">
|
||||
(Expires on {account.expirationDate.toFormat('LLL dd, yyyy')})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ export default class ProfileSettings extends React.PureComponent {
|
|||
<div className="flex items-center">
|
||||
<div className={ styles.left }>
|
||||
<h4 className="text-lg mb-4">{ 'License' }</h4>
|
||||
<div className={ styles.info }>{ 'License key and expiration date.' }</div>
|
||||
</div>
|
||||
<div><Licenses /></div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import RoleForm from './components/RoleForm'
|
|||
import { init, edit, fetchList, remove as deleteRole } from 'Duck/roles';
|
||||
import RoleItem from './components/RoleItem'
|
||||
import { confirm } from 'UI/Confirmation';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface Props {
|
||||
loading: boolean
|
||||
|
|
@ -14,20 +15,18 @@ interface Props {
|
|||
edit: (role: any) => void,
|
||||
instance: any,
|
||||
roles: any[],
|
||||
deleteRole: (id: any) => void,
|
||||
deleteRole: (id: any) => Promise<void>,
|
||||
fetchList: () => Promise<void>,
|
||||
account: any,
|
||||
permissionsMap: any
|
||||
permissionsMap: any,
|
||||
removeErrors: any
|
||||
}
|
||||
|
||||
function Roles(props: Props) {
|
||||
const { loading, instance, roles, init, edit, deleteRole, account, permissionsMap } = props
|
||||
const { loading, instance, roles, init, edit, deleteRole, account, permissionsMap, removeErrors } = props
|
||||
const [showModal, setShowmModal] = useState(false)
|
||||
const isAdmin = account.admin || account.superAdmin;
|
||||
|
||||
console.log('permissionsMap', permissionsMap)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
props.fetchList()
|
||||
}, [])
|
||||
|
|
@ -49,7 +48,13 @@ function Roles(props: Props) {
|
|||
header: 'Roles',
|
||||
confirmation: `Are you sure you want to remove this role?`
|
||||
})) {
|
||||
deleteRole(role.roleId)
|
||||
deleteRole(role.roleId).then(() => {
|
||||
if (removeErrors && removeErrors.size > 0) {
|
||||
removeErrors.forEach(e => {
|
||||
toast.error(e)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +85,7 @@ function Roles(props: Props) {
|
|||
/>
|
||||
</div>
|
||||
}
|
||||
content="You don’t have the permissions to perform this action."
|
||||
disabled={ isAdmin }
|
||||
size="tiny"
|
||||
inverted
|
||||
|
|
@ -98,6 +104,7 @@ function Roles(props: Props) {
|
|||
{roles.map(role => (
|
||||
<RoleItem
|
||||
role={role}
|
||||
isAdmin={isAdmin}
|
||||
permissions={permissionsMap}
|
||||
editHandler={editHandler}
|
||||
deleteHandler={deleteHandler}
|
||||
|
|
@ -121,6 +128,7 @@ export default connect(state => {
|
|||
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' ])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,34 +13,37 @@ interface Props {
|
|||
role: any,
|
||||
deleteHandler?: (role: any) => void,
|
||||
editHandler?: (role: any) => void,
|
||||
permissions: any
|
||||
permissions: any,
|
||||
isAdmin: boolean
|
||||
}
|
||||
function RoleItem({ role, deleteHandler, editHandler, permissions }: Props) {
|
||||
function RoleItem({ role, deleteHandler, editHandler, isAdmin, permissions }: Props) {
|
||||
return (
|
||||
<div className={cn(stl.wrapper)}>
|
||||
<Icon name="user-alt" size="16" marginRight="10" />
|
||||
<div className="flex items-center">
|
||||
<span>{ role.name }</span>
|
||||
<div className="grid grid-flow-col auto-cols-max gap-2">
|
||||
<div className="mr-4">{ role.name }</div>
|
||||
<div className="grid grid-flow-col auto-cols-max">
|
||||
{role.permissions.map((permission: any) => (
|
||||
<PermisionLabel permission={permissions[permission]} key={permission.id} />
|
||||
// <span key={permission.id} className={cn(stl.permission)}>{ permissions[permission].name }</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{ isAdmin && (
|
||||
<div className={ stl.actions }>
|
||||
{ !!deleteHandler &&
|
||||
<div className={ cn(stl.button, {[stl.disabled] : role.protected }) } onClick={ () => deleteHandler(role) } id="trash">
|
||||
<Icon name="trash" size="16" color="teal"/>
|
||||
</div>
|
||||
}
|
||||
{ !!editHandler &&
|
||||
<div className={ cn(stl.button, {[stl.disabled] : role.protected }) } onClick={ () => editHandler(role) }>
|
||||
<Icon name="edit" size="16" color="teal"/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={ stl.actions }>
|
||||
{ !!deleteHandler &&
|
||||
<div className={ cn(stl.button, {[stl.disabled] : role.protected }) } onClick={ () => deleteHandler(role) } id="trash">
|
||||
<Icon name="trash" size="16" color="teal"/>
|
||||
</div>
|
||||
}
|
||||
{ !!editHandler &&
|
||||
<div className={ cn(stl.button, {[stl.disabled] : role.protected }) } onClick={ () => editHandler(role) }>
|
||||
<Icon name="edit" size="16" color="teal"/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@
|
|||
|
||||
.label {
|
||||
margin-left: 10px;
|
||||
padding: 0 10px;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
background-color: $gray-lightest;
|
||||
font-size: 12px;
|
||||
font-size: 10px;
|
||||
border: solid thin $gray-light;
|
||||
width: fit-content;
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ class Webhooks extends React.PureComponent {
|
|||
|
||||
<Loader loading={ loading }>
|
||||
<NoContent
|
||||
title="No webhooks are available."
|
||||
title="No webhooks available."
|
||||
size="small"
|
||||
show={ noSlackWebhooks.size === 0 }
|
||||
icon
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import cn from "classnames";
|
|||
function ErrorText({ className, icon, name, message, bold, lineThrough = false }) {
|
||||
return (
|
||||
<div className={ cn("mb-1 truncate", { "font-weight-bold": bold }) }>
|
||||
{ icon && <Icon name={icon} className="float-left mr-2" size="14" style={{ marginTop: '1px'}}/> }
|
||||
{/* { icon && <Icon name={icon} className="float-left mr-2" size="14" style={{ marginTop: '1px'}}/> } */}
|
||||
<span className={cn("code-font color-red", className, { 'line-through' : lineThrough })}>{ name }</span>
|
||||
<span className={cn('color-gray-darkest ml-2', { 'line-through' : lineThrough })}>{ message }</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { fetch as fetchSession } from 'Duck/sessions';
|
|||
import { fetchList as fetchSlackList } from 'Duck/integrations/slack';
|
||||
import { Link, NoContent, Loader } from 'UI';
|
||||
import { sessions as sessionsRoute } from 'App/routes';
|
||||
import withPermissions from 'HOCs/withPermissions'
|
||||
|
||||
import LivePlayer from './LivePlayer';
|
||||
import WebPlayer from './WebPlayer';
|
||||
|
|
@ -56,7 +57,7 @@ function Session({
|
|||
);
|
||||
}
|
||||
|
||||
export default connect((state, props) => {
|
||||
export default withPermissions(['SESSION_REPLAY'])(connect((state, props) => {
|
||||
const { match: { params: { sessionId } } } = props;
|
||||
return {
|
||||
sessionId,
|
||||
|
|
@ -67,4 +68,4 @@ export default connect((state, props) => {
|
|||
}, {
|
||||
fetchSession,
|
||||
fetchSlackList,
|
||||
})(Session);
|
||||
})(Session));
|
||||
|
|
@ -24,7 +24,7 @@ function Autoplay(props) {
|
|||
name="sessionsLive"
|
||||
onChange={ props.toggleAutoplay }
|
||||
checked={ autoplay }
|
||||
style={{ margin: '0 10px' }}
|
||||
style={{ margin: '0px 10px 0px 12px'}}
|
||||
/>
|
||||
}
|
||||
tooltip={'Autoplay'}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default connect(state => ({
|
|||
/>
|
||||
{ visible &&
|
||||
<div className={ stl.modal } >
|
||||
<NoContent show={ metadata.size === 0 } size="small" >
|
||||
<NoContent show={ metadata.size === 0 } size="small">
|
||||
{ metadata.map((i) => {
|
||||
const key = Object.keys(i)[0]
|
||||
const value = i[key]
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export default class Fetch extends React.PureComponent {
|
|||
const { filter, current, currentIndex } = this.state;
|
||||
const filterRE = getRE(filter, 'i');
|
||||
const filtered = list
|
||||
.filter(({ name }) => filterRE.test(name));
|
||||
.filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status));
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
@ -90,7 +90,7 @@ export default class Fetch extends React.PureComponent {
|
|||
<h4 className="text-lg">Fetch</h4>
|
||||
<Input
|
||||
className="input-small"
|
||||
placeholder="Filter by Name"
|
||||
placeholder="Filter"
|
||||
icon="search"
|
||||
iconPosition="left"
|
||||
name="filter"
|
||||
|
|
|
|||
|
|
@ -55,11 +55,13 @@ export default class PlayerBlockHeader extends React.PureComponent {
|
|||
|
||||
backHandler = () => {
|
||||
const { history, siteId, funnelPage } = this.props;
|
||||
if (funnelPage) {
|
||||
if (funnelPage.get('issueId')) {
|
||||
history.push(withSiteId(funnelIssueRoute(funnelPage.get('funnelId'), funnelPage.get('issueId')), siteId))
|
||||
const funnelId = funnelPage && funnelPage.get('funnelId');
|
||||
const issueId = funnelPage && funnelPage.get('issueId');
|
||||
if (funnelId || issueId) {
|
||||
if (issueId) {
|
||||
history.push(withSiteId(funnelIssueRoute(funnelId, issueId), siteId))
|
||||
} else
|
||||
history.push(withSiteId(funnelRoute(funnelPage.get('funnelId')), siteId));
|
||||
history.push(withSiteId(funnelRoute(funnelId), siteId));
|
||||
} else
|
||||
history.push(withSiteId(SESSIONS_ROUTE), siteId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ const initialState = Map({
|
|||
{ name: 'Developer Tools', value: 'DEV_TOOLS' },
|
||||
{ name: 'Errors', value: 'ERRORS' },
|
||||
{ name: 'Metrics', value: 'METRICS' },
|
||||
{ name: 'Assist Live', value: 'ASSIST_LIVE' },
|
||||
{ name: 'Assist Call', value: 'ASSIST_CALL' },
|
||||
{ name: 'Assist (Live)', value: 'ASSIST_LIVE' },
|
||||
{ name: 'Assist (Call)', value: 'ASSIST_CALL' },
|
||||
])
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -309,4 +309,8 @@
|
|||
|
||||
a:hover {
|
||||
color: $teal;
|
||||
}
|
||||
|
||||
.ui.toggle.checkbox {
|
||||
min-height: 20px !important;
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ aws_region: "us-east-1"
|
|||
assets_bucket: sessions-assets
|
||||
recordings_bucket: mobs
|
||||
sourcemaps_bucket: sourcemaps
|
||||
kafka_endpoint: kafka.db.svc.cluster.local:9042
|
||||
kafka_endpoint: kafka.db.svc.cluster.local:9092
|
||||
kafka_ssl: 'false'
|
||||
postgres_endpoint: postgresql.db.svc.cluster.local
|
||||
postgres_port: 5432
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue