diff --git a/ui/src/admin/components/chronograf/OrganizationsTableRow.tsx b/ui/src/admin/components/chronograf/OrganizationsTableRow.tsx index 424959942..c29dc40b3 100644 --- a/ui/src/admin/components/chronograf/OrganizationsTableRow.tsx +++ b/ui/src/admin/components/chronograf/OrganizationsTableRow.tsx @@ -15,24 +15,13 @@ import {ErrorHandling} from 'src/shared/decorators/errors' import {DEFAULT_ORG_ID} from 'src/admin/constants/chronografAdmin' import {USER_ROLES} from 'src/admin/constants/chronografAdmin' import {Organization} from 'src/types' +import {Links} from 'src/types/auth' interface CurrentOrganization { name: string id: string } -interface ExternalLink { - name: string - url: string -} -interface ExternalLinks { - custom: ExternalLink[] -} -interface Links { - me: string - external: ExternalLinks -} - interface Props { organization: Organization currentOrganization: CurrentOrganization diff --git a/ui/src/admin/containers/chronograf/AllUsersPage.tsx b/ui/src/admin/containers/chronograf/AllUsersPage.tsx index c2d514132..49e9ba901 100644 --- a/ui/src/admin/containers/chronograf/AllUsersPage.tsx +++ b/ui/src/admin/containers/chronograf/AllUsersPage.tsx @@ -9,7 +9,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors' import AllUsersTable from 'src/admin/components/chronograf/AllUsersTable' import { - AuthLinks, + Links, Organization, Role, User, @@ -19,7 +19,7 @@ import { interface Props { notify: (message: Notification | NotificationFunc) => void - links: AuthLinks + links: Links meID: string users: User[] organizations: Organization[] diff --git a/ui/src/shared/actions/links.ts b/ui/src/shared/actions/links.ts index 6fb4cac73..7c4e64371 100644 --- a/ui/src/shared/actions/links.ts +++ b/ui/src/shared/actions/links.ts @@ -6,7 +6,7 @@ import {errorThrown} from 'src/shared/actions/errors' import {linksLink} from 'src/shared/constants' -import {AuthLinks} from 'src/types/auth' +import {Links} from 'src/types/auth' export enum ActionTypes { LinksGetRequested = 'LINKS_GET_REQUESTED', @@ -23,11 +23,9 @@ const linksGetRequested = (): LinksGetRequestedAction => ({ export interface LinksGetCompletedAction { type: ActionTypes.LinksGetCompleted - payload: {links: AuthLinks} + payload: {links: Links} } -export const linksGetCompleted = ( - links: AuthLinks -): LinksGetCompletedAction => ({ +export const linksGetCompleted = (links: Links): LinksGetCompletedAction => ({ type: ActionTypes.LinksGetCompleted, payload: {links}, }) diff --git a/ui/src/shared/reducers/auth.ts b/ui/src/shared/reducers/auth.ts index 27cb69eb2..4e9ca3f53 100644 --- a/ui/src/shared/reducers/auth.ts +++ b/ui/src/shared/reducers/auth.ts @@ -1,4 +1,4 @@ -import {AuthMe, AuthLink} from 'src/types/auth' +import {Me, AuthLink} from 'src/types/auth' import {getMeRole} from 'src/shared/reducers/helpers/auth' import {getDeep} from 'src/utils/wrappers' @@ -6,7 +6,7 @@ import {ActionTypes} from 'src/shared/actions/auth' interface State { links: AuthLink[] | null - me: AuthMe | null + me: Me | null isMeLoading: boolean isAuthLoading: boolean logoutLink: string | null @@ -32,7 +32,7 @@ export const initialState = getInitialState() const meGetCompleted = ( state: State, - {me}: {me: AuthMe}, + {me}: {me: Me}, isUsingAuth: boolean ): State => { let newMe = me diff --git a/ui/src/shared/reducers/helpers/auth.ts b/ui/src/shared/reducers/helpers/auth.ts index 679fba661..4e009e069 100644 --- a/ui/src/shared/reducers/helpers/auth.ts +++ b/ui/src/shared/reducers/helpers/auth.ts @@ -1,9 +1,9 @@ import _ from 'lodash' import {SUPERADMIN_ROLE, MEMBER_ROLE} from 'src/auth/Authorized' -import {AuthMe} from 'src/types/auth' +import {Me} from 'src/types/auth' -export const getMeRole = (me: AuthMe): string => { +export const getMeRole = (me: Me): string => { const currentRoleOrg = me.roles.find( role => me.currentOrganization.id === role.organization ) diff --git a/ui/src/side_nav/components/OrgLink.tsx b/ui/src/side_nav/components/OrgLink.tsx new file mode 100644 index 000000000..522bbdd2f --- /dev/null +++ b/ui/src/side_nav/components/OrgLink.tsx @@ -0,0 +1,67 @@ +// Libraries +import classnames from 'classnames' +import React, {PureComponent} from 'react' +import {withRouter, WithRouterProps} from 'react-router' + +// Types +import {Me, Role} from 'src/types' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface OrgID { + organization: string +} + +interface Props { + me: Me + role: Role + meLink: string + onMeChangeOrg: (meLink: string, orgID: OrgID) => void +} + +@ErrorHandling +class OrgLink extends PureComponent { + public render() { + const {role} = this.props + + return ( + + {this.orgName} ({role.name}) + + ) + } + + private get orgName(): string { + const {me, role} = this.props + const org = me.organizations.find(o => o.id === role.organization) + + if (!org) { + return '' + } + + return org.name + } + + private get isCurrentOrg(): boolean { + const {me, role} = this.props + return me.currentOrganization.id === role.organization + } + + private get className(): string { + return classnames('sidebar-menu--item', { + active: this.isCurrentOrg, + }) + } + + private handleChangeOrganization = async () => { + const {router, meLink, onMeChangeOrg, role} = this.props + try { + await onMeChangeOrg(meLink, {organization: role.organization}) + router.push('') + } catch (error) { + console.error(error) + } + } +} + +export default withRouter(OrgLink) diff --git a/ui/src/side_nav/components/UserNavBlock.js b/ui/src/side_nav/components/UserNavBlock.js deleted file mode 100644 index cbb6d63d3..000000000 --- a/ui/src/side_nav/components/UserNavBlock.js +++ /dev/null @@ -1,158 +0,0 @@ -import React, {Component} from 'react' -import PropTypes from 'prop-types' -import {connect} from 'react-redux' -import {bindActionCreators} from 'redux' -import {withRouter} from 'react-router' - -import classnames from 'classnames' - -import FancyScrollbar from 'shared/components/FancyScrollbar' - -import {meChangeOrganizationAsync} from 'shared/actions/auth' - -import {SUPERADMIN_ROLE} from 'src/auth/Authorized' -import {ErrorHandling} from 'src/shared/decorators/errors' - -@ErrorHandling -class UserNavBlock extends Component { - handleChangeCurrentOrganization = organizationID => async () => { - const {router, links, meChangeOrganization} = this.props - await meChangeOrganization(links.me, {organization: organizationID}) - router.push('') - } - - render() { - const { - logoutLink, - links: { - external: {custom: customLinks}, - }, - me, - me: {currentOrganization, organizations, roles}, - me: {role}, - } = this.props - - const isSuperAdmin = role === SUPERADMIN_ROLE - - return ( -
-
-
- {isSuperAdmin ? ( - - ) : null} -
-
- {customLinks ? ( -
- Custom Links -
- ) : null} - {customLinks - ? customLinks.map((link, i) => ( - - {link.name} - - )) - : null} -
- Switch Organizations -
- - {roles.map((r, i) => { - const isLinkCurrentOrg = currentOrganization.id === r.organization - return ( - - {organizations.find(o => o.id === r.organization).name}{' '} - ({r.name}) - - ) - })} - -
- Account -
-
-
- {me.scheme} / {me.provider} -
-
- - Log out - -
- {me.name} -
-
-
-
- ) - } -} - -const {arrayOf, func, shape, string} = PropTypes - -UserNavBlock.propTypes = { - router: shape({ - push: func.isRequired, - }).isRequired, - links: shape({ - me: string, - external: shape({ - custom: arrayOf( - shape({ - name: string.isRequired, - url: string.isRequired, - }) - ), - }), - }), - logoutLink: string.isRequired, - me: shape({ - currentOrganization: shape({ - id: string.isRequired, - name: string.isRequired, - }), - name: string, - organizations: arrayOf( - shape({ - id: string.isRequired, - name: string.isRequired, - }) - ), - roles: arrayOf( - shape({ - id: string, - name: string, - }) - ), - role: string, - }).isRequired, - meChangeOrganization: func.isRequired, -} - -const mapDispatchToProps = dispatch => ({ - meChangeOrganization: bindActionCreators(meChangeOrganizationAsync, dispatch), -}) - -export default connect(null, mapDispatchToProps)(withRouter(UserNavBlock)) diff --git a/ui/src/side_nav/components/UserNavBlock.tsx b/ui/src/side_nav/components/UserNavBlock.tsx new file mode 100644 index 000000000..64c50d403 --- /dev/null +++ b/ui/src/side_nav/components/UserNavBlock.tsx @@ -0,0 +1,117 @@ +// Libraries +import React, {PureComponent} from 'react' +import {connect} from 'react-redux' + +// Components +import OrgLink from 'src/side_nav/components/OrgLink' +import FancyScrollbar from 'src/shared/components/FancyScrollbar' + +// Actions +import {meChangeOrganizationAsync} from 'src/shared/actions/auth' + +// Constants +import {SUPERADMIN_ROLE} from 'src/auth/Authorized' + +// Types +import {Me} from 'src/types' +import {Links, ExternalLink} from 'src/types/auth' + +import {ErrorHandling} from 'src/shared/decorators/errors' + +interface OrgID { + organization: string +} + +interface Props { + me: Me + links: Links + logoutLink: string + meChangeOrg: (meLink: string, orgID: OrgID) => void +} + +@ErrorHandling +class UserNavBlock extends PureComponent { + public render() { + const {logoutLink, me, links, meChangeOrg} = this.props + + return ( +
+
+
+ {this.isSuperAdmin && ( + + )} +
+
+ {!!this.customLinks && ( +
+ Custom Links +
+ )} + {!!this.customLinks && + this.customLinks.map((link, i) => ( + + {link.name} + + ))} +
+ Switch Organizations +
+ + {me.roles.map((r, i) => ( + + ))} + +
+ Account +
+
+
+ {me.scheme} / {me.provider} +
+
+ + Log out + +
+ {me.name} +
+
+
+
+ ) + } + + private get customLinks(): ExternalLink[] { + return this.props.links.external.custom + } + + private get isSuperAdmin(): boolean { + return this.props.me.role === SUPERADMIN_ROLE + } +} + +const mdtp = { + meChangeOrg: meChangeOrganizationAsync, +} + +export default connect(null, mdtp)(UserNavBlock) diff --git a/ui/src/sources/components/SourceForm.tsx b/ui/src/sources/components/SourceForm.tsx index 39071b10f..921df039d 100644 --- a/ui/src/sources/components/SourceForm.tsx +++ b/ui/src/sources/components/SourceForm.tsx @@ -6,7 +6,12 @@ import _ from 'lodash' import {insecureSkipVerifyText} from 'src/shared/copy/tooltipText' import {SUPERADMIN_ROLE} from 'src/auth/Authorized' -import {Source, Me} from 'src/types' +import {Source, Role, Organization} from 'src/types' + +interface Me { + role: Role + currentOrganization: Organization +} interface Props { me: Me @@ -33,7 +38,7 @@ export class SourceForm extends PureComponent { } = this.props return (
- {isUsingAuth && isInitialSource && this.authIndicatior} + {isUsingAuth && isInitialSource && this.authIndicator}
@@ -167,7 +172,7 @@ export class SourceForm extends PureComponent { ) } - private get authIndicatior(): JSX.Element { + private get authIndicator(): JSX.Element { const {me} = this.props return (
diff --git a/ui/src/types/auth.ts b/ui/src/types/auth.ts index 166fdb67d..7261d0681 100644 --- a/ui/src/types/auth.ts +++ b/ui/src/types/auth.ts @@ -8,27 +8,14 @@ export interface Organization { } export interface Me { - role: Role currentOrganization?: Organization -} - -export interface AuthMe { - id?: string - role: string - name: string - provider: string - scheme: string superAdmin: boolean - logoutLink: string + role: string + scheme: string + provider: string + name: string roles: Role[] - links: { - self: string - } - organizations?: Organization[] - currentOrganization?: Organization - isUsingAuth: boolean - isMeLoading: boolean - isAuthLoading: boolean + organizations: Organization[] } export enum InfluxDBPermissions { @@ -79,7 +66,7 @@ export interface User { superAdmin: boolean } -export interface Auth { +export interface AuthLink { callback: string label: string login: string @@ -92,15 +79,13 @@ export interface AuthConfig { self: string } -export interface AuthLinks { +export interface Links { allUsers: string - auth: Auth[] + auth: AuthLink[] config: AuthConfig dashboards: string environment: string - external: { - statusFeed?: string - } + external: ExternalLinks layouts: string logout: string mappings: string @@ -110,10 +95,12 @@ export interface AuthLinks { users: string } -export interface AuthLink { +export interface ExternalLink { name: string - label: string - login: string - logout: string - callback: string + url: string +} + +interface ExternalLinks { + statusFeed?: string + custom?: ExternalLink[] } diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts index 8d90df5d2..7f85914b2 100644 --- a/ui/src/types/index.ts +++ b/ui/src/types/index.ts @@ -1,6 +1,6 @@ import {LayoutCell, LayoutQuery} from './layouts' import {Service, NewService} from './services' -import {AuthLinks, Organization, Role, Permission, User, Me} from './auth' +import {Links, Organization, Role, Permission, User, Me} from './auth' import {Cell, CellQuery, Legend, Axes, Dashboard, CellType} from './dashboards' import { Template, @@ -54,7 +54,7 @@ import {WriteDataMode} from './dataExplorer' export { Me, - AuthLinks, + Links, Role, User, Organization,