Merge remote-tracking branch 'origin/master' into feature/custom_user_links-1550

pull/10616/head
Jared Scheib 2017-06-26 18:05:22 -07:00
commit f064342440
21 changed files with 620 additions and 480 deletions

View File

@ -1,11 +1,26 @@
## v1.3.4.0 [unreleased] ## v1.3.4.0 [unreleased]
### Bug Fixes ### Bug Fixes
1. [#1612](https://github.com/influxdata/chronograf/pull/1612): Prevent users from being able to write to internal system databases
1. [#1655](https://github.com/influxdata/chronograf/pull/1655): Add more than one color to Line+Stat graphs
### Features ### Features
1. [#1645](https://github.com/influxdata/chronograf/pull/1645): Add Auth0 as a supported OAuth2 provider 1. [#1645](https://github.com/influxdata/chronograf/pull/1645): Add Auth0 as a supported OAuth2 provider
1. [#1660](https://github.com/influxdata/chronograf/pull/1660): Add ability to add custom links to User menu via server CLI or ENV vars 1. [#1660](https://github.com/influxdata/chronograf/pull/1660): Add ability to add custom links to User menu via server CLI or ENV vars
### UI Improvements ### UI Improvements
1. [#1644](https://github.com/influxdata/chronograf/pull/1644): Redesign Alerts History table to have sticky headers
1. [#1581](https://github.com/influxdata/chronograf/pull/1581): Refresh template variable values on dashboard page load 1. [#1581](https://github.com/influxdata/chronograf/pull/1581): Refresh template variable values on dashboard page load
1. [#1612](https://github.com/influxdata/chronograf/pull/1612): Prevent users from being able to write to internal system databases
1. [#1655](https://github.com/influxdata/chronograf/pull/1655): Add version number item to the navbar
1. [#1655](https://github.com/influxdata/chronograf/pull/1655): Redesign dashboards table and sort alphabetically by name
1. [#1655](https://github.com/influxdata/chronograf/pull/1655): Redesign navbar to be consistent with navbar in Branding Documentation
## v1.3.3.3 [2017-06-21]
### Bug Fixes
1. [1651](https://github.com/influxdata/chronograf/pull/1651): Add back in x and y axes and revert some style changes on Line + Single Stat graphs
## v1.3.3.2 [2017-06-21]
### Bug Fixes
## v1.3.3.3 [2017-06-21] ## v1.3.3.3 [2017-06-21]
### Bug Fixes ### Bug Fixes

View File

@ -2,6 +2,10 @@ import React, {Component, PropTypes} from 'react'
import _ from 'lodash' import _ from 'lodash'
import {Link} from 'react-router' import {Link} from 'react-router'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import {ALERTS_TABLE} from 'src/alerts/constants/tableSizing'
class AlertsTable extends Component { class AlertsTable extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
@ -55,11 +59,11 @@ class AlertsTable extends Component {
sortableClasses(key) { sortableClasses(key) {
if (this.state.sortKey === key) { if (this.state.sortKey === key) {
if (this.state.sortDirection === 'asc') { if (this.state.sortDirection === 'asc') {
return 'sortable-header sorting-ascending' return 'alert-history-table--th sortable-header sorting-ascending'
} }
return 'sortable-header sorting-descending' return 'alert-history-table--th sortable-header sorting-descending'
} }
return 'sortable-header' return 'alert-history-table--th sortable-header'
} }
sort(alerts, key, direction) { sort(alerts, key, direction) {
@ -80,64 +84,93 @@ class AlertsTable extends Component {
this.state.sortKey, this.state.sortKey,
this.state.sortDirection this.state.sortDirection
) )
const {colName, colLevel, colTime, colHost, colValue} = ALERTS_TABLE
return this.props.alerts.length return this.props.alerts.length
? <table className="table v-center table-highlight"> ? <div className="alert-history-table">
<thead> <div className="alert-history-table--thead">
<tr> <div
<th onClick={() => this.changeSort('name')}
onClick={() => this.changeSort('name')} className={this.sortableClasses('name')}
className={this.sortableClasses('name')} style={{width: colName}}
> >
Name Name
</th> </div>
<th <div
onClick={() => this.changeSort('level')} onClick={() => this.changeSort('level')}
className={this.sortableClasses('level')} className={this.sortableClasses('level')}
> style={{width: colLevel}}
Level >
</th> Level
<th </div>
onClick={() => this.changeSort('time')} <div
className={this.sortableClasses('time')} onClick={() => this.changeSort('time')}
> className={this.sortableClasses('time')}
Time style={{width: colTime}}
</th> >
<th Time
onClick={() => this.changeSort('host')} </div>
className={this.sortableClasses('host')} <div
> onClick={() => this.changeSort('host')}
Host className={this.sortableClasses('host')}
</th> style={{width: colHost}}
<th >
onClick={() => this.changeSort('value')} Host
className={this.sortableClasses('value')} </div>
> <div
Value onClick={() => this.changeSort('value')}
</th> className={this.sortableClasses('value')}
</tr> style={{width: colValue}}
</thead> >
<tbody> Value
</div>
</div>
<FancyScrollbar
className="alert-history-table--tbody"
autoHide={false}
>
{alerts.map(({name, level, time, host, value}) => { {alerts.map(({name, level, time, host, value}) => {
return ( return (
<tr key={`${name}-${level}-${time}-${host}-${value}`}> <div
<td className="monotype">{name}</td> className="alert-history-table--tr"
<td className={`monotype alert-level-${level.toLowerCase()}`}> key={`${name}-${level}-${time}-${host}-${value}`}
>
<div
className="alert-history-table--td"
style={{width: colName}}
>
{name}
</div>
<div
className={`alert-history-table--td alert-level-${level.toLowerCase()}`}
style={{width: colLevel}}
>
{level} {level}
</td> </div>
<td className="monotype"> <div
className="alert-history-table--td"
style={{width: colTime}}
>
{new Date(Number(time)).toISOString()} {new Date(Number(time)).toISOString()}
</td> </div>
<td className="monotype"> <div
className="alert-history-table--td"
style={{width: colHost}}
>
<Link to={`/sources/${id}/hosts/${host}`}> <Link to={`/sources/${id}/hosts/${host}`}>
{host} {host}
</Link> </Link>
</td> </div>
<td className="monotype">{value}</td> <div
</tr> className="alert-history-table--td"
style={{width: colValue}}
>
{value}
</div>
</div>
) )
})} })}
</tbody> </FancyScrollbar>
</table> </div>
: this.renderTableEmpty() : this.renderTableEmpty()
} }
@ -239,7 +272,7 @@ class SearchBar extends Component {
<input <input
type="text" type="text"
className="form-control" className="form-control"
placeholder="Filter Alerts by Name..." placeholder="Filter Alerts..."
onChange={this.handleChange} onChange={this.handleChange}
value={this.state.searchTerm} value={this.state.searchTerm}
/> />

View File

@ -0,0 +1,7 @@
export const ALERTS_TABLE = {
colName: '15%',
colLevel: '10%',
colTime: '25%',
colHost: '25%',
colValue: '25%',
}

View File

@ -4,7 +4,6 @@ import SourceIndicator from 'shared/components/SourceIndicator'
import AlertsTable from 'src/alerts/components/AlertsTable' import AlertsTable from 'src/alerts/components/AlertsTable'
import NoKapacitorError from 'shared/components/NoKapacitorError' import NoKapacitorError from 'shared/components/NoKapacitorError'
import CustomTimeRangeDropdown from 'shared/components/CustomTimeRangeDropdown' import CustomTimeRangeDropdown from 'shared/components/CustomTimeRangeDropdown'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import {getAlerts} from 'src/alerts/apis' import {getAlerts} from 'src/alerts/apis'
import AJAX from 'utils/ajax' import AJAX from 'utils/ajax'
@ -160,10 +159,8 @@ class AlertsApp extends Component {
} }
return isWidget return isWidget
? <FancyScrollbar autoHide={false}> ? this.renderSubComponents()
{this.renderSubComponents()} : <div className="page alert-history-page">
</FancyScrollbar>
: <div className="page">
<div className="page-header"> <div className="page-header">
<div className="page-header__container"> <div className="page-header__container">
<div className="page-header__left"> <div className="page-header__left">
@ -183,7 +180,7 @@ class AlertsApp extends Component {
</div> </div>
</div> </div>
</div> </div>
<FancyScrollbar className="page-contents"> <div className="page-contents">
<div className="container-fluid"> <div className="container-fluid">
<div className="row"> <div className="row">
<div className="col-md-12"> <div className="col-md-12">
@ -191,7 +188,7 @@ class AlertsApp extends Component {
</div> </div>
</div> </div>
</div> </div>
</FancyScrollbar> </div>
</div> </div>
} }
} }

View File

@ -0,0 +1,28 @@
import React, {PropTypes} from 'react'
import SourceIndicator from 'shared/components/SourceIndicator'
const DashboardsHeader = ({sourceName}) => {
return (
<div className="page-header">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">
Dashboards
</h1>
</div>
<div className="page-header__right">
<SourceIndicator sourceName={sourceName} />
</div>
</div>
</div>
)
}
const {string} = PropTypes
DashboardsHeader.propTypes = {
sourceName: string.isRequired,
}
export default DashboardsHeader

View File

@ -0,0 +1,61 @@
import React, {PropTypes} from 'react'
import DashboardsTable from 'src/dashboards/components/DashboardsTable'
import FancyScrollbar from 'shared/components/FancyScrollbar'
const DashboardsPageContents = ({
dashboards,
onDeleteDashboard,
onCreateDashboard,
dashboardLink,
}) => {
let tableHeader
if (dashboards === null) {
tableHeader = 'Loading Dashboards...'
} else if (dashboards.length === 1) {
tableHeader = '1 Dashboard'
} else {
tableHeader = `${dashboards.length} Dashboards`
}
return (
<FancyScrollbar className="page-contents">
<div className="container-fluid">
<div className="row">
<div className="col-md-12">
<div className="panel panel-minimal">
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
<h2 className="panel-title">{tableHeader}</h2>
<button
className="btn btn-sm btn-primary"
onClick={onCreateDashboard}
>
Create Dashboard
</button>
</div>
<div className="panel-body">
<DashboardsTable
dashboards={dashboards}
onDeleteDashboard={onDeleteDashboard}
onCreateDashboard={onCreateDashboard}
dashboardLink={dashboardLink}
/>
</div>
</div>
</div>
</div>
</div>
</FancyScrollbar>
)
}
const {arrayOf, func, shape, string} = PropTypes
DashboardsPageContents.propTypes = {
dashboards: arrayOf(shape()),
onDeleteDashboard: func.isRequired,
onCreateDashboard: func.isRequired,
dashboardLink: string.isRequired,
}
export default DashboardsPageContents

View File

@ -0,0 +1,73 @@
import React, {PropTypes} from 'react'
import {Link} from 'react-router'
import _ from 'lodash'
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
const DashboardsTable = ({
dashboards,
onDeleteDashboard,
onCreateDashboard,
dashboardLink,
}) => {
return dashboards && dashboards.length
? <table className="table v-center admin-table table-highlight">
<thead>
<tr>
<th>Name</th>
<th>Template Variables</th>
<th />
</tr>
</thead>
<tbody>
{_.sortBy(dashboards, d => d.name.toLowerCase()).map(dashboard =>
<tr key={dashboard.id}>
<td>
<Link to={`${dashboardLink}/dashboards/${dashboard.id}`}>
{dashboard.name}
</Link>
</td>
<td>
{dashboard.templates.length
? dashboard.templates.map(tv =>
<code className="table--temp-var" key={tv.id}>
{tv.tempVar}
</code>
)
: <span className="empty-string">
None
</span>}
</td>
<DeleteConfirmTableCell
onDelete={onDeleteDashboard}
item={dashboard}
buttonSize="btn-xs"
/>
</tr>
)}
</tbody>
</table>
: <div className="generic-empty-state">
<h4 style={{marginTop: '90px'}}>
Looks like you dont have any dashboards
</h4>
<button
className="btn btn-sm btn-primary"
onClick={onCreateDashboard}
style={{marginBottom: '90px'}}
>
Create Dashboard
</button>
</div>
}
const {arrayOf, func, shape, string} = PropTypes
DashboardsTable.propTypes = {
dashboards: arrayOf(shape()),
onDeleteDashboard: func.isRequired,
onCreateDashboard: func.isRequired,
dashboardLink: string.isRequired,
}
export default DashboardsTable

View File

@ -1,11 +1,10 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import {Link, withRouter} from 'react-router' import {withRouter} from 'react-router'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
import SourceIndicator from 'shared/components/SourceIndicator' import DashboardsHeader from 'src/dashboards/components/DashboardsHeader'
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell' import DashboardsContents from 'src/dashboards/components/DashboardsPageContents'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import {createDashboard} from 'src/dashboards/apis' import {createDashboard} from 'src/dashboards/apis'
import {getDashboardsAsync, deleteDashboardAsync} from 'src/dashboards/actions' import {getDashboardsAsync, deleteDashboardAsync} from 'src/dashboards/actions'
@ -50,89 +49,16 @@ const DashboardsPage = React.createClass({
render() { render() {
const {dashboards} = this.props const {dashboards} = this.props
const dashboardLink = `/sources/${this.props.source.id}` const dashboardLink = `/sources/${this.props.source.id}`
let tableHeader
if (dashboards === null) {
tableHeader = 'Loading Dashboards...'
} else if (dashboards.length === 1) {
tableHeader = '1 Dashboard'
} else {
tableHeader = `${dashboards.length} Dashboards`
}
return ( return (
<div className="page"> <div className="page">
<div className="page-header"> <DashboardsHeader sourceName={this.props.source.name} />
<div className="page-header__container"> <DashboardsContents
<div className="page-header__left"> dashboardLink={dashboardLink}
<h1 className="page-header__title"> dashboards={dashboards}
Dashboards onDeleteDashboard={this.handleDeleteDashboard}
</h1> onCreateDashboard={this.handleCreateDashbord}
</div> />
<div className="page-header__right">
<SourceIndicator sourceName={this.props.source.name} />
</div>
</div>
</div>
<FancyScrollbar className="page-contents">
<div className="container-fluid">
<div className="row">
<div className="col-md-12">
<div className="panel panel-minimal">
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
<h2 className="panel-title">{tableHeader}</h2>
<button
className="btn btn-sm btn-primary"
onClick={this.handleCreateDashbord}
>
Create Dashboard
</button>
</div>
<div className="panel-body">
{dashboards && dashboards.length
? <table className="table v-center admin-table table-highlight">
<thead>
<tr>
<th>Name</th>
<th />
</tr>
</thead>
<tbody>
{dashboards.map(dashboard =>
<tr key={dashboard.id} className="">
<td>
<Link
to={`${dashboardLink}/dashboards/${dashboard.id}`}
>
{dashboard.name}
</Link>
</td>
<DeleteConfirmTableCell
onDelete={this.handleDeleteDashboard}
item={dashboard}
buttonSize="btn-xs"
/>
</tr>
)}
</tbody>
</table>
: <div className="generic-empty-state">
<h4 style={{marginTop: '90px'}}>
Looks like you dont have any dashboards
</h4>
<button
className="btn btn-sm btn-primary"
onClick={this.handleCreateDashbord}
style={{marginBottom: '90px'}}
>
Create Dashboard
</button>
</div>}
</div>
</div>
</div>
</div>
</div>
</FancyScrollbar>
</div> </div>
) )
}, },

View File

@ -43,12 +43,17 @@ class DatabaseDropdown extends Component {
const proxy = source.links.proxy const proxy = source.links.proxy
try { try {
const {data} = await showDatabases(proxy) const {data} = await showDatabases(proxy)
const {databases} = showDatabasesParser(data) const {databases, errors} = showDatabasesParser(data)
if (errors.length > 0) {
throw errors[0] // only one error can come back from this, but it's returned as an array
}
this.setState({databases}) const nonSystemDatabases = databases.filter(name => name !== '_internal')
const selectedDatabaseText = databases.includes(database)
this.setState({databases: nonSystemDatabases})
const selectedDatabaseText = nonSystemDatabases.includes(database)
? database ? database
: databases[0] || 'No databases' : nonSystemDatabases[0] || 'No databases'
onSelectDatabase({text: selectedDatabaseText}) onSelectDatabase({text: selectedDatabaseText})
} catch (error) { } catch (error) {
console.error(error) console.error(error)

View File

@ -33,6 +33,7 @@ class FancyScrollbar extends Component {
<div {...props} className="fancy-scroll--thumb-h" />} <div {...props} className="fancy-scroll--thumb-h" />}
renderThumbVertical={props => renderThumbVertical={props =>
<div {...props} className="fancy-scroll--thumb-v" />} <div {...props} className="fancy-scroll--thumb-v" />}
renderView={props => <div {...props} className="fancy-scroll--view" />}
> >
{children} {children}
</Scrollbars> </Scrollbars>

View File

@ -131,7 +131,17 @@ export default React.createClass({
strokeWidth: 1.5, strokeWidth: 1.5,
}, },
} }
const singleStatLineColor = ['#7A65F2'] const singleStatLineColors = [
'#7A65F2',
'#FFD255',
'#7CE490',
'#F95F53',
'#4591ED',
'#B1B6FF',
'#FFF6B8',
'#C6FFD0',
'#6BDFFF',
]
let roundedValue let roundedValue
if (showSingleStat) { if (showSingleStat) {
@ -152,7 +162,7 @@ export default React.createClass({
<Dygraph <Dygraph
containerStyle={{width: '100%', height: '100%'}} containerStyle={{width: '100%', height: '100%'}}
overrideLineColors={ overrideLineColors={
showSingleStat ? singleStatLineColor : overrideLineColors showSingleStat ? singleStatLineColors : overrideLineColors
} }
isGraphFilled={showSingleStat ? false : isGraphFilled} isGraphFilled={showSingleStat ? false : isGraphFilled}
isBarGraph={isBarGraph} isBarGraph={isBarGraph}

View File

@ -19,14 +19,14 @@ const NavListItem = React.createClass({
return useAnchor return useAnchor
? <a ? <a
className={classnames('sidebar__menu-item', {active: isActive})} className={classnames('sidebar-menu--item', {active: isActive})}
href={link} href={link}
target={isExternal ? '_blank' : '_self'} target={isExternal ? '_blank' : '_self'}
> >
{children} {children}
</a> </a>
: <Link : <Link
className={classnames('sidebar__menu-item', {active: isActive})} className={classnames('sidebar-menu--item', {active: isActive})}
to={link} to={link}
> >
{children} {children}
@ -46,11 +46,11 @@ const NavHeader = React.createClass({
// Some nav items, such as Logout, need to hit an external link rather // Some nav items, such as Logout, need to hit an external link rather
// than simply route to an internal page. Anchor tags serve that purpose. // than simply route to an internal page. Anchor tags serve that purpose.
return useAnchor return useAnchor
? <a className="sidebar__menu-route" href={link}> ? <a className="sidebar-menu--heading" href={link}>
<h3 className="sidebar__menu-heading">{title}</h3> {title}
</a> </a>
: <Link className="sidebar__menu-route" to={link}> : <Link className="sidebar-menu--heading" to={link}>
<h3 className="sidebar__menu-heading">{title}</h3> {title}
</Link> </Link>
}, },
}) })
@ -62,11 +62,10 @@ const NavBlock = React.createClass({
icon: string.isRequired, icon: string.isRequired,
location: string, location: string,
className: string, className: string,
wrapperClassName: string,
}, },
render() { render() {
const {location, className, wrapperClassName} = this.props const {location, className} = this.props
const isActive = React.Children.toArray(this.props.children).find(child => { const isActive = React.Children.toArray(this.props.children).find(child => {
return location.startsWith(child.props.link) return location.startsWith(child.props.link)
@ -82,34 +81,30 @@ const NavBlock = React.createClass({
return ( return (
<div <div
className={classnames('sidebar__square', className, {active: isActive})} className={classnames('sidebar--item', className, {active: isActive})}
> >
{this.renderLink()} {this.renderSquare()}
<div className={wrapperClassName || 'sidebar__menu-wrapper'}> <div className="sidebar-menu">
<div className="sidebar__menu"> {children}
{children}
</div>
</div> </div>
</div> </div>
) )
}, },
renderLink() { renderSquare() {
const {link, icon} = this.props const {link, icon} = this.props
if (!link) { if (!link) {
return ( return (
<div className="sidebar__icon"> <div className="sidebar--square">
<span className={`icon ${icon}`} /> <div className={`sidebar--icon icon ${icon}`} />
</div> </div>
) )
} }
return ( return (
<Link className="sidebar__menu-route" to={link}> <Link className="sidebar--square" to={link}>
<div className="sidebar__icon"> <div className={`sidebar--icon icon ${icon}`} />
<span className={`icon ${icon}`} />
</div>
</Link> </Link>
) )
}, },
@ -131,7 +126,7 @@ const NavBar = React.createClass({
return child return child
}) })
return <aside className="sidebar">{children}</aside> return <nav className="sidebar">{children}</nav>
}, },
}) })

View File

@ -13,6 +13,8 @@ import {DEFAULT_HOME_PAGE} from 'shared/constants'
const {arrayOf, bool, shape, string} = PropTypes const {arrayOf, bool, shape, string} = PropTypes
const V_NUMBER = VERSION // eslint-disable-line no-undef
const SideNav = React.createClass({ const SideNav = React.createClass({
propTypes: { propTypes: {
params: shape({ params: shape({
@ -45,7 +47,11 @@ const SideNav = React.createClass({
</NavListItem> </NavListItem>
) )
.concat( .concat(
<NavListItem useAnchor={true} link={logoutLink}> <NavListItem
key={customLinks.length + 1}
useAnchor={true}
link={logoutLink}
>
Logout Logout
</NavListItem> </NavListItem>
) )
@ -68,12 +74,14 @@ const SideNav = React.createClass({
return isHidden return isHidden
? null ? null
: <NavBar location={location}> : <NavBar location={location}>
<Link <div className="sidebar--item">
to={`${sourcePrefix}/${DEFAULT_HOME_PAGE}`} <Link
className="sidebar__logo" to={`${sourcePrefix}/${DEFAULT_HOME_PAGE}`}
> className="sidebar--square sidebar--logo"
<span className="icon cubo-uniform" /> >
</Link> <span className="sidebar--icon icon cubo-uniform" />
</Link>
</div>
<NavBlock icon="cubo-node" link={`${sourcePrefix}/hosts`}> <NavBlock icon="cubo-node" link={`${sourcePrefix}/hosts`}>
<NavHeader link={`${sourcePrefix}/hosts`} title="Host List" /> <NavHeader link={`${sourcePrefix}/hosts`} title="Host List" />
</NavBlock> </NavBlock>
@ -108,8 +116,20 @@ const SideNav = React.createClass({
title="Configuration" title="Configuration"
/> />
</NavBlock> </NavBlock>
<div className="sidebar--bottom">
<div className="sidebar--item">
<div className="sidebar--square">
<span className="sidebar--icon icon zap" />
</div>
<div className="sidebar-menu">
<div className="sidebar-menu--heading">
Version: {V_NUMBER}
</div>
</div>
</div>
</div>
{isUsingAuth {isUsingAuth
? <NavBlock icon="user" className="sidebar__square-last"> ? <NavBlock icon="user" className="sidebar--item-last">
{customLinks {customLinks
? this.renderUserMenuBlockWithCustomLinks( ? this.renderUserMenuBlockWithCustomLinks(
customLinks, customLinks,

View File

@ -55,7 +55,11 @@ ul.dropdown-menu {
.fancy-scroll--thumb-v { @include gradient-v($c-neutrino,$c-laser); } .fancy-scroll--thumb-v { @include gradient-v($c-neutrino,$c-laser); }
} }
/* Hacky Fix to make this work in Safari */ /* Hacky fix to hide strange white lines in chrome */
.fancy-scroll--view {
margin-right: -16px !important
}
/* Hacky Fix to make fancy scrollbars work in Safari */
.query-builder--list { .query-builder--list {
position: relative; position: relative;

View File

@ -12,7 +12,7 @@
} }
.chronograf-root > .page-spinner { .chronograf-root > .page-spinner {
// Center the spinner based on the main content window, not the entire screen // Center the spinner based on the main content window, not the entire screen
left: calc(50% + #{$sidebar-width}); left: calc(50% + #{$sidebar--width});
} }
@keyframes pageSpinner { @keyframes pageSpinner {

View File

@ -36,7 +36,6 @@ $breakpoint-c: 2100px;
.query-builder--heading { .query-builder--heading {
font-size: 17px; font-size: 17px;
font-weight: 400; font-weight: 400;
text-transform: uppercase;
} }
.query-maker .multi-select-dropdown .dropdown-toggle { .query-maker .multi-select-dropdown .dropdown-toggle {
width: 140px; width: 140px;

View File

@ -50,7 +50,8 @@
Sortable Tables Sortable Tables
---------------------------------------------- ----------------------------------------------
*/ */
table.table thead th.sortable-header { table.table thead th.sortable-header,
.alert-history-table--th.sortable-header {
transition: transition:
color 0.25s ease, color 0.25s ease,
background-color 0.25s ease; background-color 0.25s ease;
@ -172,15 +173,84 @@ $table-tab-scrollbar-height: 6px;
.table.table-highlight > tbody > tr.highlight { .table.table-highlight > tbody > tr.highlight {
background-color: $g4-onyx; background-color: $g4-onyx;
} }
/* /*
Responsive Tables Alert History "Page"
----------------------------------------------
*/
.alert-history-page {
.page-contents > .container-fluid,
.page-contents > .container-fluid > .row,
.page-contents > .container-fluid > .row > .col-md-12,
.page-contents > .container-fluid > .row > .col-md-12 > .panel {
height: 100%;
}
.col-md-12 > .panel {
display: flex;
flex-direction: column;
align-items: stretch;
> .panel-body {flex: 1 0 0;}
.generic-empty-state {height: 100%;}
}
}
/*
Misc
----------------------------------------------
*/
.table .empty-string {
font-weight: 500;
color: $g8-storm;
font-style: italic;
}
.table .table--temp-var {
color: $c-comet;
font-weight: 600;
}
/*
Alert History "Table"
---------------------------------------------- ----------------------------------------------
*/ */
@media screen and (max-width: 767px) { .alert-history-table {
.table-responsive { height: 100%;
border-radius: 3px; display: flex;
border-color: $g5-pepper; flex-direction: column;
@include custom-scrollbar($g5-pepper, $c-pool); align-items: stretch;
}
.alert-history-table--thead {
display: flex;
width: 100%;
border-bottom: 2px solid $g5-pepper;
}
.alert-history-table--th {
@include no-user-select();
padding: 8px;
font-size: 13px;
font-weight: 500;
color: $g17-whisper;
}
.alert-history-table--tbody {
flex: 1 0 0;
width: 100%;
}
.alert-history-table--tr {
display: flex;
width: 100%;
&:hover {
background-color: $g4-onyx;
} }
} }
.alert-history-table--td {
font-size: 12px;
font-family: $code-font;
font-weight: 500;
padding: 4px 8px;
line-height: 1.42857143em;
color: $g13-mist;
white-space: pre-wrap;
word-break: break-all;
}

View File

@ -19,5 +19,5 @@
// Make Overlay Technology full screen // Make Overlay Technology full screen
.overlay-technology { .overlay-technology {
left: -($sidebar-width) !important; left: -($sidebar--width) !important;
} }

View File

@ -3,293 +3,177 @@
---------------------------------------------- ----------------------------------------------
*/ */
$sidebar-width: 60px; $sidebar--width: 60px;
$sidebar-menu-gutter: 30px;
$sidebar-menu-indent: 13px;
$sidebar-hover: $g8-storm; $sidebar--gradient-start: $g7-graphite;
$sidebar-item-hover: $g7-graphite; $sidebar--gradient-end: $g4-onyx;
$sidebar-lighter: $g8-storm;
$sidebar-light: $g7-graphite; $sidebar--logo-bg: $g19-ghost;
$sidebar-dark: $g4-onyx; $sidebar--logo-color: $c-pool;
$sidebar-icon: $g11-sidewalk; $sidebar--logo-bg-hover: $g20-white;
$sidebar-text: $g13-mist; $sidebar--logo-color-hover: $c-laser;
$sidebar-icon-hover: $g20-white;
$sidebar-icon-active: $g20-white; $sidebar--item-bg: transparent;
$sidebar-active-bg: $c-pool; $sidebar--item-bg-hover: $c-pool;
$sidebar-active-accent: $c-laser; $sidebar--item-bg-active: $g4-onyx;
$sidebar-logo-bg: $g17-whisper; $sidebar--icon: $g11-sidewalk;
$sidebar-logo-color: $g8-storm; $sidebar--icon-hover: $g20-white;
$sidebar--icon-active: $g20-white;
$sidebar-menu--bg: $c-pool;
$sidebar-menu--bg-accent: $c-comet;
$sidebar-menu--item-bg: $c-ocean;
$sidebar-menu--item-bg-accent: $c-star;
$sidebar-menu--item-bg-hover: $c-laser;
$sidebar-menu--item-bg-hover-accent: $c-potassium;
$sidebar-menu--item-text: $c-neutrino;
$sidebar-menu--item-text-hover: $g20-white;
$sidebar-menu--item-text-active: $g20-white;
$sidebar-menu--gutter: 18px;
// Sidebar styles
.sidebar { .sidebar {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: $sidebar-width; width: $sidebar--width;
@include gradient-v($sidebar-light,$sidebar-dark); @include gradient-v($sidebar--gradient-start,$sidebar--gradient-end);
}
.sidebar--bottom {
position: absolute;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
width: $sidebar--width;
}
&__logo { /*
width: $sidebar-width; Sidebar Items
height: $sidebar-width; ----------------------------------------------
background-color: $sidebar-logo-bg; */
position: relative; .sidebar--item {
color: $sidebar-logo-color; width: $sidebar--width;
height: $sidebar--width;
span.icon { position: relative;
position: absolute; }
top: 50%; .sidebar--square {
left: 50%; display: block;
transform: translate(-50%,-50%); position: relative;
font-size: $sidebar-width * 0.4222; width: 100%;
} height: 100%;
} background-color: $sidebar--item-bg;
transition: none;
&__icon { }
position: absolute; .sidebar--icon {
top: 0; position: absolute;
left: 0; color: $sidebar--icon;
width: 100%; top: 50%;
height: 100%; left: 50%;
transition: transform: translate(-50%,-50%);
background-color 0.25s ease, font-size: $sidebar--width * 0.4222;
color 0.25s ease; transition:
color: $sidebar-icon; text-shadow 0.4s ease;
}
span.icon { /*
position: absolute; Sidebar Item Active State
top: 50%; */
left: 50%; .sidebar--item.active {
transform: translate(-50%,-50%); .sidebar--square {background-color: $sidebar--item-bg-active;}
font-size: $sidebar-width * 0.4222; .sidebar--icon {
} color: $sidebar--icon-active;
} text-shadow:
0 0 9px $c-laser,
&__square { 0 0 15px $c-ocean,
z-index: 999; 0 0 20px $c-amethyst;
width: $sidebar-width;
height: $sidebar-width;
background-color: transparent;
position: relative;
color: $sidebar-icon;
/* Here for specificity issues */
.sidebar__icon {
&:link,
&:active,
&:visited {
transition:
background-color 0.25s ease,
color 0.25s ease;
color: $sidebar-icon;
}
}
&:hover {
cursor: pointer;
.sidebar__icon {
color: $sidebar-icon-hover;
background-color: $sidebar-hover;
}
/* Show menu on hover */
.sidebar__menu-wrapper,
.sidebar__menu-wrapper-bottom {
visibility: visible;
}
.sidebar__menu {
opacity: 1;
}
}
/* Active Indicator */
&:after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 4px;
height: 100%;
transform: translateY(-50%) scale(1,0);
background-color: $c-pool;
z-index: 999;
backface-visibility: hidden;
transition:
transform 0.4s ease;
}
/* Active State Styles */
&.active {
&:after {
transform: translateY(-50%) scale(1,1);
transition:
transform 0.31s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.sidebar__icon {
color: $sidebar-icon-active;
&:link,
&:active,
&:visited,
&:hover {
color: $sidebar-icon-active;
}
}
}
&-last {
position: absolute;
bottom: 0;
.sidebar__menu-wrapper {
bottom: 0;
top: initial;
}
.sidebar__menu-route {
order: 1;
}
}
}
&__menu {
border-radius: 0 $radius $radius 0;
background: $sidebar-hover;
opacity: 0;
transition: opacity 0.25s ease;
display: flex;
flex-direction: column;
list-style: none;
&-wrapper {
position: absolute;
top: 0;
left: 100%;
z-index: 999;
visibility: hidden;
display: flex;
transition: all 0.25s ease;
}
&-wrapper-bottom {
position: absolute;
bottom: 0;
left: 100%;
z-index: 999;
visibility: hidden;
display: flex;
transition: all 0.25s ease;
}
}
&__menu-heading,
&__menu-item {
width: 100%;
white-space: nowrap;
margin: 0;
display: block;
@include no-user-select();
&:hover {
cursor: pointer;
}
}
&__menu-item {
color: $sidebar-text;
font-size: 15px;
line-height: 22px;
font-weight: 500;
position: relative;
padding: 4px $sidebar-menu-gutter;
transition:
color 0.25s ease,
background-color 0.25s ease;
// Overriding link styles
&:link,
&:active,
&:visited {
color: $sidebar-text;
font-weight: 500;
transition:
color 0.25s ease,
background-color 0.25s ease;
}
// Rounding top outside corner to match container
&:first-child {
border-top-right-radius: $radius;
}
// Rounding bottom outside corner of match container
&:last-child {
border-bottom-right-radius: $radius;
}
// Used for sub-navigation
small {
font-size: 13px;
border-left: 2px solid $sidebar-icon;
padding-left: $sidebar-menu-indent;
display: inline-block;
transition: border-color 0.25s ease;
}
// Invisible until item is active, indicates active item
&:after {
position: absolute;
top: 50%;
left: $sidebar-menu-gutter /2;
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
background-color: transparent;
transform: translate(-50%,-50%);
}
&:hover {
background-color: $sidebar-item-hover;
color: $sidebar-icon-hover;
small {
border-color: $sidebar-icon-hover;
}
// Emphasize rename icon on hover
.js-open-rename-cluster-modal {
opacity: 1;
}
}
&.active {
@include gradient-h($sidebar-active-bg,$sidebar-active-accent);
color: $sidebar-icon-hover;
&:link,
&:active,
&:visited {
color: $sidebar-icon-hover;
}
// Show indicator
&:after {
background-color: $sidebar-icon-active;
}
// Show rename icon when active
.js-open-rename-cluster-modal {
background-color: $c-laser;
opacity: 1;
&:hover {
background-color: $c-hydrogen;
}
}
}
}
&__menu-heading {
color: $g20-white;
height: $sidebar-width;
line-height: $sidebar-width;
font-size: 17px;
text-transform: uppercase;
font-weight: 400;
padding: 0px $sidebar-menu-gutter;
} }
} }
/*
Sidebar Item Hover State
*/
.sidebar--item:hover {
cursor: pointer;
.sidebar--square {background-color: $sidebar--item-bg-hover;}
.sidebar--icon {color: $sidebar--icon-hover;}
.sidebar-menu {display: flex;}
}
.sidebar--item.active:hover .sidebar--icon {
text-shadow:
0 0 9px $c-yeti,
0 0 15px $c-hydrogen,
0 0 20px $c-laser;
}
/*
Sidebar Logo Square
*/
.sidebar--square.sidebar--logo {
background-color: $sidebar--logo-bg;
.sidebar--icon {color: $sidebar--logo-color;}
}
.sidebar--item:hover .sidebar--square.sidebar--logo {
background-color: $sidebar--logo-bg-hover;
.sidebar--icon {color: $sidebar--logo-color-hover;}
}
/*
Sidebar Sub Menus
----------------------------------------------
*/
.sidebar-menu {
position: absolute;
top: 0;
left: 100%;
border-radius: 0 $radius $radius 0;
@include gradient-h($sidebar-menu--bg,$sidebar-menu--bg-accent);
transition: opacity 0.25s ease;
display: none;
flex-direction: column;
}
.sidebar-menu--heading,
.sidebar-menu--item {
width: 100%;
white-space: nowrap;
display: block;
@include no-user-select();
&:hover {
cursor: pointer;
}
}
.sidebar-menu--item:link,
.sidebar-menu--item:active,
.sidebar-menu--item:visited {
color: $sidebar-menu--item-text;
font-size: 15px;
line-height: 22px;
font-weight: 500;
position: relative;
padding: 4px $sidebar-menu--gutter;
transition: none;
// Rounding bottom outside corner of match container
&:last-child {border-bottom-right-radius: $radius;}
}
.sidebar-menu--item.active:link,
.sidebar-menu--item.active:active,
.sidebar-menu--item.active:visited {
@include gradient-h($sidebar-menu--item-bg,$sidebar-menu--item-bg-accent);
color: $sidebar-menu--item-text-active;
}
.sidebar-menu--item:hover,
.sidebar-menu--item.active:hover {
@include gradient-h($sidebar-menu--item-bg-hover,$sidebar-menu--item-bg-hover-accent);
color: $sidebar-menu--item-text-hover;
}
.sidebar-menu--heading,
.sidebar-menu--heading:link,
.sidebar-menu--heading:visited,
.sidebar-menu--heading:active,
.sidebar-menu--heading:hover, {
color: $g20-white;
height: $sidebar--width;
line-height: $sidebar--width;
font-size: 19px;
font-weight: 400;
padding: 0px $sidebar-menu--gutter;
}

View File

@ -11,7 +11,7 @@
overflow: auto; overflow: auto;
@include custom-scrollbar($g3-castle, $c-pool); @include custom-scrollbar($g3-castle, $c-pool);
@include gradient-v($g3-castle, $g0-obsidian); @include gradient-v($g3-castle, $g0-obsidian);
padding: $sidebar-width; padding: $sidebar--width;
} }
.auth-image { .auth-image {
background-image: url(assets/images/auth-bg.svg); background-image: url(assets/images/auth-bg.svg);
@ -47,8 +47,8 @@
} }
.btn { .btn {
margin-top: ($sidebar-width / 2); margin-top: ($sidebar--width / 2);
margin-bottom: $sidebar-width; margin-bottom: $sidebar--width;
> .icon { > .icon {
font-size: 20px; font-size: 20px;
@ -67,7 +67,7 @@
.auth-credits { .auth-credits {
z-index: 90; z-index: 90;
position: absolute; position: absolute;
bottom: ($sidebar-width / 4); bottom: ($sidebar--width / 4);
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 12px; font-size: 12px;

View File

@ -192,3 +192,15 @@ br {
p {font-size: 13px;} p {font-size: 13px;}
} }
.alerts-widget {
height: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
> .btn {margin: 20px 0;}
.alert-history-table {
flex: 1 0 0;
}
}