diff --git a/ui/src/admin/components/chronograf/OrganizationsTable.js b/ui/src/admin/components/chronograf/OrganizationsTable.js
new file mode 100644
index 0000000000..2514d3ab21
--- /dev/null
+++ b/ui/src/admin/components/chronograf/OrganizationsTable.js
@@ -0,0 +1,89 @@
+import React, {Component, PropTypes} from 'react'
+
+import Organization from 'src/admin/components/chronograf/Organization'
+import NewOrganization from 'src/admin/components/chronograf/NewOrganization'
+
+class OrganizationsTable extends Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ isAddingOrganization: false,
+ }
+ }
+ handleClickCreateOrganization = () => {
+ this.setState({isAddingOrganization: true})
+ }
+
+ handleCancelCreateOrganization = () => {
+ this.setState({isAddingOrganization: false})
+ }
+
+ handleCreateOrganization = newOrganization => {
+ const {onCreateOrg} = this.props
+ onCreateOrg(newOrganization)
+ }
+
+ render() {
+ const {organizations, onDeleteOrg, onRenameOrg} = this.props
+ const {isAddingOrganization} = this.state
+
+ return (
+
+
+
+
+
+
22 Organizations
+
+
+
+
+ {isAddingOrganization
+ ?
+ : null}
+ {organizations.map(org =>
+
+ )}
+
+
+
+
+
+ )
+ }
+}
+
+const {arrayOf, func, shape, string} = PropTypes
+
+OrganizationsTable.propTypes = {
+ organizations: arrayOf(
+ shape({
+ id: string, // when optimistically created, organization will not have an id
+ name: string.isRequired,
+ })
+ ).isRequired,
+ onCreateOrg: func.isRequired,
+ onDeleteOrg: func.isRequired,
+ onRenameOrg: func.isRequired,
+}
+export default OrganizationsTable
diff --git a/ui/src/admin/containers/OrganizationsPage.js b/ui/src/admin/containers/OrganizationsPage.js
new file mode 100644
index 0000000000..dc1654a9b9
--- /dev/null
+++ b/ui/src/admin/containers/OrganizationsPage.js
@@ -0,0 +1,96 @@
+import React, {Component, PropTypes} from 'react'
+import {connect} from 'react-redux'
+import {bindActionCreators} from 'redux'
+
+import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
+import {publishAutoDismissingNotification} from 'shared/dispatchers'
+
+import OrganizationsTable from 'src/admin/components/chronograf/OrganizationsTable'
+import FancyScrollbar from 'shared/components/FancyScrollbar'
+
+class OrganizationsPage extends Component {
+ constructor(props) {
+ super(props)
+ }
+
+ componentDidMount() {
+ const {links, actions: {loadOrganizationsAsync}} = this.props
+
+ loadOrganizationsAsync(links.organizations)
+ }
+
+ handleCreateOrganization = organizationName => {
+ const {links, actions: {createOrganizationAsync}} = this.props
+ createOrganizationAsync(links.organizations, {name: organizationName})
+ }
+
+ handleRenameOrganization = (organization, name) => {
+ const {actions: {renameOrganizationAsync}} = this.props
+ renameOrganizationAsync(organization, {...organization, name})
+ }
+
+ handleDeleteOrganization = organization => {
+ const {actions: {deleteOrganizationAsync}} = this.props
+ deleteOrganizationAsync(organization)
+ }
+
+ render() {
+ const {organizations} = this.props
+
+ return (
+
+
+
+ {organizations
+ ?
+ : }
+
+
+ )
+ }
+}
+
+const {arrayOf, func, shape, string} = PropTypes
+
+OrganizationsPage.propTypes = {
+ links: shape({
+ organizations: string.isRequired,
+ }),
+ organizations: arrayOf(
+ shape({
+ id: string, // when optimistically created, it will not have an id
+ name: string.isRequired,
+ link: string,
+ })
+ ),
+ actions: shape({
+ loadOrganizationsAsync: func.isRequired,
+ createOrganizationAsync: func.isRequired,
+ renameOrganizationAsync: func.isRequired,
+ deleteOrganizationAsync: func.isRequired,
+ }),
+ notify: func.isRequired,
+}
+
+const mapStateToProps = ({links, adminChronograf: {organizations}}) => ({
+ links,
+ organizations,
+})
+
+const mapDispatchToProps = dispatch => ({
+ actions: bindActionCreators(adminChronografActionCreators, dispatch),
+ notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(OrganizationsPage)
diff --git a/ui/src/admin/index.js b/ui/src/admin/index.js
index a985904015..5663e7aeb3 100644
--- a/ui/src/admin/index.js
+++ b/ui/src/admin/index.js
@@ -1,4 +1,5 @@
import AdminInfluxDBPage from './containers/AdminInfluxDBPage'
import AdminChronografPage from './containers/AdminChronografPage'
+import OrganizationsPage from './containers/OrganizationsPage'
-export {AdminChronografPage, AdminInfluxDBPage}
+export {AdminChronografPage, AdminInfluxDBPage, OrganizationsPage}
diff --git a/ui/src/side_nav/components/UserNavBlock.js b/ui/src/side_nav/components/UserNavBlock.js
index eee12c2314..8d64518094 100644
--- a/ui/src/side_nav/components/UserNavBlock.js
+++ b/ui/src/side_nav/components/UserNavBlock.js
@@ -1,7 +1,10 @@
import React, {PropTypes, Component} from 'react'
+import {Link} from 'react-router'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
+import Authorized, {SUPERADMIN_ROLE} from 'src/auth/Authorized'
+
import classnames from 'classnames'
import {meChangeOrganizationAsync} from 'shared/actions/auth'
@@ -31,9 +34,25 @@ class UserNavBlock extends Component {
+ {currentOrganization.name} (Member)
+
+
{me.name}
-
Organizations
+
+
+ Manage Users
+
+
+
+
+ Manage Organizations
+
+
+
+ Logout
+
+
Switch Organizations
{roles.map((role, i) => {
const isLinkCurrentOrg =
currentOrganization.id === role.organization
@@ -53,10 +72,6 @@ class UserNavBlock extends Component {
)
})}
-
- Logout
-
- {customLinks ?
: null}
{customLinks
?
Custom Links
: null}
diff --git a/ui/src/side_nav/containers/SideNav.js b/ui/src/side_nav/containers/SideNav.js
index 8f73692a6e..41b01d9a58 100644
--- a/ui/src/side_nav/containers/SideNav.js
+++ b/ui/src/side_nav/containers/SideNav.js
@@ -88,6 +88,9 @@ const SideNav = React.createClass({
+ {isUsingAuth
+ ?
+ : null}
- {isUsingAuth
- ?
- : null}
},
})
diff --git a/ui/src/style/layout/sidebar.scss b/ui/src/style/layout/sidebar.scss
index 26261ad99c..c7c4fd2fe4 100644
--- a/ui/src/style/layout/sidebar.scss
+++ b/ui/src/style/layout/sidebar.scss
@@ -204,11 +204,7 @@ $sidebar-menu--gutter: 18px;
left: 0px;
transform: translate(-50%,-50%) rotate(45deg);
}
-.sidebar-menu--divider {
- width: 100%;
- height: 2px;
- @include gradient-h($c-laser,$c-potassium);
-}
+
.sidebar-menu--section {
white-space: nowrap;
font-size: 13px;
@@ -217,4 +213,16 @@ $sidebar-menu--gutter: 18px;
padding: 4px $sidebar-menu--gutter;
text-transform: uppercase;
color: $c-hydrogen;
+ @include no-user-select();
+ position: relative;
+
+ &:after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ @include gradient-h($c-laser,$c-potassium);
+ }
}