feat(ui): implement new navigation design (#17206)
* refactor(ui): WIP implement tree nav * feat(ui): enable expand/collapse behavior of nav tree * chore(ui): rename "settings" to "organization" * fix(ui): add nav tree key to mock state * refactor(ui): remove CloudNav component * fix(ui): cleanup * refactor(ui): move nav tree object to constants file * fix(ui): use correct icon for data nav item * fix(ui): repair affected test * refactor(ui): use a more explicit type than boolean for nav state * chore(ui): write test for nav bar state action * refactor(ui): handle org switching with an overlay * chore(ui): replace reference to local clockface with reference to package * fix(ui): update nav selector in tasks e2e test * chore(ui): add testids to all nav items for easier selection * fix(ui): prevent expanded navbar from breaking e2e tests * fix(ui): update broken login e2e test * fix(ui): udpate selectors in query builder test * refactor(ui): align nav structure with quartz counterpart * fix(ui): prettier * refactor(ui): move usage and billing into user widget * refactor(ui): use correct url for usage and billing * chore(ui): upgrade clockface dependency to 2.0.3 * refactor(ui): implement short labels in the navigation tree * refactor(ui): wrap tree nav in feature flag * chore(ui): prettier * chore(ui): remove deleted file accidentally included in rebase * refactor(ui): use get in TreeNav mstp to prevent potential future breakage * chore(ui): set default values for treeNav feature flag * refactor(ui): reinstate cloud nav but wrapped with a feature flag * refactor(ui): reinstate old navbar wrapped in feature flag * feat(ui): add Upgrade button to all page headers except DashboardPage * feat(ui): add upgrade banner to treenav * chore(ui): remove comments * refactor(ui): polish upgrade banner * refactor(ui): only show tiny banner ad if screen is large enough and menu is collapsed * refactor(ui): ensure settings page supports old and new navigation * chore(ui): cleanup * chore(ui): revert comment change * chore(ui): ensure custom classname exists on new upgrade button * chore(ui): cleanup * fix(ui): remove duplicate route * refactor(ui): make const for repeated comparison * refactor(ui): cleanup from pr review * fix(ui): oops * refactor(ui): update user widget to follow most recent logout approach Co-Authored-By: Ariel Salem <ariel.salem1989@gmail.com> Co-authored-by: Ariel Salem <ariel.salem1989@gmail.com>pull/17424/head
parent
36d99bfbf8
commit
f1e2dad6d6
|
@ -45,7 +45,7 @@ describe('The Query Builder', () => {
|
|||
|
||||
cy.get('.giraffe-plot').should('exist')
|
||||
|
||||
cy.contains('Save As').click()
|
||||
cy.getByTestID('save-query-as').click()
|
||||
|
||||
// open the dashboard selector dropdown
|
||||
cy.getByTestID('save-as-dashboard-cell--dropdown').click()
|
||||
|
|
|
@ -326,6 +326,7 @@ http.post(
|
|||
.click()
|
||||
// verify that it is the correct data
|
||||
cy.getByInputValue(secondTask)
|
||||
|
||||
cy.get('div.cf-nav--item.active').click()
|
||||
// navigate back to the first one to verify that the name is correct
|
||||
cy.getByTestID('task-card--name')
|
||||
|
|
|
@ -6,11 +6,13 @@ import classnames from 'classnames'
|
|||
|
||||
// Components
|
||||
import {AppWrapper} from '@influxdata/clockface'
|
||||
import Nav from 'src/pageLayout'
|
||||
import TreeNav from 'src/pageLayout/containers/TreeNav'
|
||||
import Nav from 'src/pageLayout/containers/Nav'
|
||||
import TooltipPortal from 'src/portals/TooltipPortal'
|
||||
import NotesPortal from 'src/portals/NotesPortal'
|
||||
import Notifications from 'src/shared/components/notifications/Notifications'
|
||||
import OverlayController from 'src/overlays/components/OverlayController'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
import CloudNav from 'src/pageLayout/components/CloudNav'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
|
||||
|
@ -40,9 +42,11 @@ const App: SFC<Props> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<CloudOnly>
|
||||
<CloudNav />
|
||||
</CloudOnly>
|
||||
<FeatureFlag name="treeNav" equals={false}>
|
||||
<CloudOnly>
|
||||
<CloudNav />
|
||||
</CloudOnly>
|
||||
</FeatureFlag>
|
||||
<AppWrapper
|
||||
presentationMode={inPresentationMode}
|
||||
className={appWrapperClass}
|
||||
|
@ -51,7 +55,12 @@ const App: SFC<Props> = ({
|
|||
<TooltipPortal />
|
||||
<NotesPortal />
|
||||
<OverlayController />
|
||||
<Nav />
|
||||
<FeatureFlag name="treeNav">
|
||||
<TreeNav />
|
||||
</FeatureFlag>
|
||||
<FeatureFlag name="treeNav" equals={false}>
|
||||
<Nav />
|
||||
</FeatureFlag>
|
||||
{children}
|
||||
</AppWrapper>
|
||||
</>
|
||||
|
|
|
@ -9,6 +9,7 @@ import EventTable from 'src/eventViewer/components/EventTable'
|
|||
import AlertHistoryControls from 'src/alerting/components/AlertHistoryControls'
|
||||
import AlertHistoryQueryParams from 'src/alerting/components/AlertHistoryQueryParams'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Constants
|
||||
import {
|
||||
|
@ -77,6 +78,7 @@ const AlertHistoryIndex: FC<Props> = ({params: {orgID}, resourceIDs}) => {
|
|||
title="Check Statuses"
|
||||
testID="alert-history-title"
|
||||
/>
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={true}>
|
||||
<AlertHistoryQueryParams
|
||||
|
|
|
@ -10,6 +10,7 @@ import EndpointsColumn from 'src/notifications/endpoints/components/EndpointsCol
|
|||
import GetAssetLimits from 'src/cloud/components/GetAssetLimits'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Utils
|
||||
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
||||
|
@ -34,9 +35,10 @@ const AlertingIndex: FunctionComponent<StateProps> = ({
|
|||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Page titleTag={pageTitleSuffixer(['Monitoring & Alerting'])}>
|
||||
<Page titleTag={pageTitleSuffixer(['Alerts'])}>
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Monitoring & Alerting" />
|
||||
<Page.Title title="Alerts" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.Contents fullWidth={false} scrollable={false}>
|
||||
<GetResources resources={[ResourceType.Labels]}>
|
||||
|
|
|
@ -10,6 +10,7 @@ import CheckHistoryVisualization from 'src/checks/components/CheckHistoryVisuali
|
|||
import AlertHistoryQueryParams from 'src/alerting/components/AlertHistoryQueryParams'
|
||||
import EventTable from 'src/eventViewer/components/EventTable'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
//Context
|
||||
import {ResourceIDsContext} from 'src/alerting/components/AlertHistoryIndex'
|
||||
|
@ -63,6 +64,7 @@ const CheckHistory: FC<Props> = ({
|
|||
title="Check Statuses"
|
||||
testID="alert-history-title"
|
||||
/>
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={true}>
|
||||
<Page.ControlBarLeft>
|
||||
|
|
|
@ -13,6 +13,7 @@ import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
|||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import GetAssetLimits from 'src/cloud/components/GetAssetLimits'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Utils
|
||||
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
||||
|
@ -66,6 +67,7 @@ class DashboardIndex extends PureComponent<Props, State> {
|
|||
>
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Dashboards" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={false}>
|
||||
<Page.ControlBarLeft>
|
||||
|
|
|
@ -10,6 +10,7 @@ import ViewTypeDropdown from 'src/timeMachine/components/view_options/ViewTypeDr
|
|||
import GetResources from 'src/resources/components/GetResources'
|
||||
import TimeZoneDropdown from 'src/shared/components/TimeZoneDropdown'
|
||||
import DeleteDataButton from 'src/dataExplorer/components/DeleteDataButton'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Types
|
||||
import {ResourceType} from 'src/types'
|
||||
|
@ -24,6 +25,7 @@ const DataExplorerPage: SFC = ({children}) => {
|
|||
<GetResources resources={[ResourceType.Variables, ResourceType.Buckets]}>
|
||||
<Page.Header fullWidth={true}>
|
||||
<Page.Title title="Data Explorer" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={true}>
|
||||
<Page.ControlBarLeft>
|
||||
|
|
|
@ -415,7 +415,7 @@ class Root extends PureComponent {
|
|||
/>
|
||||
</Route>
|
||||
<Route path="labels" component={LabelsIndex} />
|
||||
<Route path="profile" component={OrgProfilePage}>
|
||||
<Route path="about" component={OrgProfilePage}>
|
||||
<Route
|
||||
path="rename"
|
||||
component={RenameOrgOverlay}
|
||||
|
@ -460,6 +460,12 @@ class Root extends PureComponent {
|
|||
path="checks/:checkID"
|
||||
component={CheckHistory}
|
||||
/>
|
||||
<Route path="about" component={OrgProfilePage}>
|
||||
<Route path="rename" component={RenameOrgOverlay} />
|
||||
</Route>
|
||||
{!CLOUD && (
|
||||
<Route path="members" component={MembersIndex} />
|
||||
)}
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
import Resources from 'src/me/components/Resources'
|
||||
import Docs from 'src/me/components/Docs'
|
||||
import GettingStarted from 'src/me/components/GettingStarted'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Utils
|
||||
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
||||
|
@ -39,6 +40,7 @@ export class MePage extends PureComponent<StateProps> {
|
|||
<Page titleTag={pageTitleSuffixer(['Home'])}>
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Getting Started" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.Contents fullWidth={false} scrollable={true}>
|
||||
<Grid>
|
||||
|
|
|
@ -4,11 +4,14 @@ import {connect} from 'react-redux'
|
|||
|
||||
// Components
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import OrgTabbedPage from 'src/organizations/components/OrgTabbedPage'
|
||||
import OrgHeader from 'src/organizations/components/OrgHeader'
|
||||
import SettingsTabbedPage from 'src/settings/components/SettingsTabbedPage'
|
||||
import SettingsHeader from 'src/settings/components/SettingsHeader'
|
||||
import {Page} from '@influxdata/clockface'
|
||||
import GetResources from 'src/resources/components/GetResources'
|
||||
import Members from 'src/members/components/Members'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
|
||||
// Utils
|
||||
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
||||
|
@ -34,14 +37,26 @@ class MembersIndex extends Component<Props> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Page titleTag={pageTitleSuffixer(['Members', 'Settings'])}>
|
||||
<SettingsHeader />
|
||||
<SettingsTabbedPage activeTab="members" orgID={org.id}>
|
||||
<GetResources resources={[ResourceType.Members]}>
|
||||
<Members />
|
||||
</GetResources>
|
||||
</SettingsTabbedPage>
|
||||
</Page>
|
||||
<FeatureFlag name="treeNav">
|
||||
<Page titleTag={pageTitleSuffixer(['Members', 'Organization'])}>
|
||||
<OrgHeader />
|
||||
<OrgTabbedPage activeTab="members" orgID={org.id}>
|
||||
<GetResources resources={[ResourceType.Members]}>
|
||||
<Members />
|
||||
</GetResources>
|
||||
</OrgTabbedPage>
|
||||
</Page>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag name="treeNav" equals={false}>
|
||||
<Page titleTag={pageTitleSuffixer(['Members', 'Settings'])}>
|
||||
<SettingsHeader />
|
||||
<SettingsTabbedPage activeTab="members" orgID={org.id}>
|
||||
<GetResources resources={[ResourceType.Members]}>
|
||||
<Members />
|
||||
</GetResources>
|
||||
</SettingsTabbedPage>
|
||||
</Page>
|
||||
</FeatureFlag>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -20,6 +20,7 @@ export const localState: LocalStorage = {
|
|||
persisted: {
|
||||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
timeZone: 'Local' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import React, {Component} from 'react'
|
||||
|
||||
// Components
|
||||
import {Page} from '@influxdata/clockface'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
class OrgHeader extends Component {
|
||||
public render() {
|
||||
return (
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Organization" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default OrgHeader
|
|
@ -0,0 +1,110 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
|
||||
// Components
|
||||
import {Tabs, Orientation, ComponentSize} from '@influxdata/clockface'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
|
||||
// Constants
|
||||
import {CLOUD_USERS_PATH} from 'src/shared/constants'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface OwnProps {
|
||||
activeTab: string
|
||||
orgID: string
|
||||
}
|
||||
|
||||
type Props = OwnProps & WithRouterProps
|
||||
|
||||
interface OrgPageTab {
|
||||
text: string
|
||||
id: string
|
||||
cloudExclude?: boolean
|
||||
cloudOnly?: boolean
|
||||
onClick: () => void
|
||||
featureFlag?: string
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class OrgNavigation extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {activeTab, orgID, router} = this.props
|
||||
|
||||
const tabs: OrgPageTab[] = [
|
||||
{
|
||||
text: 'Members',
|
||||
id: 'members-oss',
|
||||
cloudExclude: true,
|
||||
onClick: () => {
|
||||
router.push(`/orgs/${orgID}/members`)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Members',
|
||||
id: 'members-cloud',
|
||||
featureFlag: 'multiUser',
|
||||
cloudOnly: true,
|
||||
onClick: () => {
|
||||
window.location.assign(`/orgs/${orgID}/${CLOUD_USERS_PATH}`)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'About',
|
||||
id: 'about',
|
||||
onClick: () => {
|
||||
router.push(`/orgs/${orgID}/about`)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<Tabs orientation={Orientation.Horizontal} size={ComponentSize.Large}>
|
||||
{tabs.map(t => {
|
||||
let isActive = t.id === activeTab
|
||||
|
||||
if (t.id === 'members-oss' || t.id === 'members-cloud') {
|
||||
if (activeTab === 'members') {
|
||||
isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
let tab = (
|
||||
<Tabs.Tab
|
||||
key={t.id}
|
||||
text={t.text}
|
||||
id={t.id}
|
||||
onClick={t.onClick}
|
||||
active={isActive}
|
||||
/>
|
||||
)
|
||||
|
||||
if (t.cloudExclude) {
|
||||
tab = <CloudExclude key={t.id}>{tab}</CloudExclude>
|
||||
}
|
||||
|
||||
if (t.cloudOnly) {
|
||||
tab = <CloudOnly key={t.id}>{tab}</CloudOnly>
|
||||
}
|
||||
|
||||
if (t.featureFlag) {
|
||||
tab = (
|
||||
<FeatureFlag key={t.id} name={t.featureFlag}>
|
||||
{tab}
|
||||
</FeatureFlag>
|
||||
)
|
||||
}
|
||||
|
||||
return tab
|
||||
})}
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(OrgNavigation)
|
|
@ -73,7 +73,7 @@ class OrgProfileTab extends PureComponent<Props> {
|
|||
router,
|
||||
} = this.props
|
||||
|
||||
router.push(`/orgs/${orgID}/settings/profile/rename`)
|
||||
router.push(`/orgs/${orgID}/settings/about/rename`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import OrgNavigation from 'src/organizations/components/OrgNavigation'
|
||||
import {Tabs, Orientation, Page} from '@influxdata/clockface'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props {
|
||||
activeTab: string
|
||||
orgID: string
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class OrgTabbedPage extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {activeTab, orgID, children} = this.props
|
||||
|
||||
return (
|
||||
<Page.Contents fullWidth={false} scrollable={true}>
|
||||
<Tabs.Container orientation={Orientation.Horizontal}>
|
||||
<OrgNavigation activeTab={activeTab} orgID={orgID} />
|
||||
<Tabs.TabContents>{children}</Tabs.TabContents>
|
||||
</Tabs.Container>
|
||||
</Page.Contents>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default OrgTabbedPage
|
|
@ -118,7 +118,7 @@ class RenameOrgForm extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private handleGoBack = () => {
|
||||
this.props.router.push(`/orgs/${this.props.startOrg.id}/settings/profile`)
|
||||
this.props.router.push(`/orgs/${this.props.startOrg.id}/settings/about`)
|
||||
}
|
||||
|
||||
private handleValidation = (orgName: string): string | null => {
|
||||
|
|
|
@ -47,7 +47,7 @@ class RenameOrgOverlay extends PureComponent<WithRouterProps> {
|
|||
params: {orgID},
|
||||
} = this.props
|
||||
|
||||
router.push(`/orgs/${orgID}/settings/profile`)
|
||||
router.push(`/orgs/${orgID}/settings/about`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,12 @@ import {connect} from 'react-redux'
|
|||
|
||||
// Components
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import OrgTabbedPage from 'src/organizations/components/OrgTabbedPage'
|
||||
import OrgHeader from 'src/organizations/components/OrgHeader'
|
||||
import SettingsTabbedPage from 'src/settings/components/SettingsTabbedPage'
|
||||
import SettingsHeader from 'src/settings/components/SettingsHeader'
|
||||
import {Grid, Columns, Page} from '@influxdata/clockface'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
|
||||
// Utils
|
||||
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
||||
|
@ -27,18 +30,34 @@ class OrgProfilePage extends Component<StateProps> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Page titleTag={pageTitleSuffixer(['Org Profile', 'Settings'])}>
|
||||
<SettingsHeader />
|
||||
<SettingsTabbedPage activeTab="profile" orgID={org.id}>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthXS={Columns.Twelve} widthSM={Columns.Six}>
|
||||
<OrgProfileTab />
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</SettingsTabbedPage>
|
||||
</Page>
|
||||
<FeatureFlag name="treeNav">
|
||||
<Page titleTag={pageTitleSuffixer(['About', 'Organization'])}>
|
||||
<OrgHeader />
|
||||
<OrgTabbedPage activeTab="about" orgID={org.id}>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthXS={Columns.Twelve} widthSM={Columns.Six}>
|
||||
<OrgProfileTab />
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</OrgTabbedPage>
|
||||
</Page>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag name="treeNav" equals={false}>
|
||||
<Page titleTag={pageTitleSuffixer(['About', 'Settings'])}>
|
||||
<SettingsHeader />
|
||||
<SettingsTabbedPage activeTab="about" orgID={org.id}>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthXS={Columns.Twelve} widthSM={Columns.Six}>
|
||||
<OrgProfileTab />
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</SettingsTabbedPage>
|
||||
</Page>
|
||||
</FeatureFlag>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -13,6 +13,9 @@ import AllAccessTokenOverlay from 'src/authorizations/components/AllAccessTokenO
|
|||
import BucketsTokenOverlay from 'src/authorizations/components/BucketsTokenOverlay'
|
||||
import TelegrafConfigOverlay from 'src/telegrafs/components/TelegrafConfigOverlay'
|
||||
import TelegrafOutputOverlay from 'src/telegrafs/components/TelegrafOutputOverlay'
|
||||
import OrgSwitcherOverlay from 'src/pageLayout/components/OrgSwitcherOverlay'
|
||||
|
||||
// Actions
|
||||
import {dismissOverlay} from 'src/overlays/actions/overlays'
|
||||
|
||||
interface StateProps {
|
||||
|
@ -56,6 +59,9 @@ const OverlayController: FunctionComponent<OverlayControllerProps> = props => {
|
|||
case 'telegraf-output':
|
||||
activeOverlay = <TelegrafOutputOverlay onClose={closer} />
|
||||
break
|
||||
case 'switch-organizations':
|
||||
activeOverlay = <OrgSwitcherOverlay onClose={closer} />
|
||||
break
|
||||
default:
|
||||
visibility = false
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export type OverlayID =
|
|||
| 'add-token'
|
||||
| 'telegraf-config'
|
||||
| 'telegraf-output'
|
||||
| 'switch-organizations'
|
||||
|
||||
export interface OverlayParams {
|
||||
[key: string]: string
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
|
||||
// Components
|
||||
import {
|
||||
TreeNav,
|
||||
IconFont,
|
||||
InfluxDBCloudLogo,
|
||||
Icon,
|
||||
ComponentColor,
|
||||
} from '@influxdata/clockface'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
|
||||
interface Props {
|
||||
link: string
|
||||
}
|
||||
|
||||
const NavHeader: FC<Props> = ({link}) => {
|
||||
return (
|
||||
<>
|
||||
<CloudExclude>
|
||||
<TreeNav.Header
|
||||
id="getting-started"
|
||||
icon={<Icon glyph={IconFont.CuboNav} />}
|
||||
label={<InfluxDBCloudLogo cloud={false} />}
|
||||
color={ComponentColor.Secondary}
|
||||
linkElement={className => <Link className={className} to={link} />}
|
||||
/>
|
||||
</CloudExclude>
|
||||
<CloudOnly>
|
||||
<TreeNav.Header
|
||||
id="getting-started"
|
||||
icon={<Icon glyph={IconFont.CuboNav} />}
|
||||
label={<InfluxDBCloudLogo cloud={true} />}
|
||||
color={ComponentColor.Primary}
|
||||
linkElement={className => <Link className={className} to={link} />}
|
||||
/>
|
||||
</CloudOnly>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavHeader
|
|
@ -0,0 +1,51 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import {WithRouterProps, withRouter} from 'react-router'
|
||||
|
||||
// Components
|
||||
import {IconFont, Icon} from '@influxdata/clockface'
|
||||
|
||||
interface ComponentProps {
|
||||
orgName: string
|
||||
orgID: string
|
||||
selected: boolean
|
||||
onDismiss: () => void
|
||||
}
|
||||
|
||||
type Props = ComponentProps & WithRouterProps
|
||||
|
||||
const OrgSwitcherItem: FC<Props> = ({
|
||||
orgName,
|
||||
selected,
|
||||
onDismiss,
|
||||
orgID,
|
||||
router,
|
||||
}) => {
|
||||
const orgSwitcherItemClass = classnames('org-switcher--item', {
|
||||
'org-switcher--item__selected': selected,
|
||||
})
|
||||
|
||||
const orgSwitcherIcon = selected ? IconFont.Checkmark : IconFont.CaretRight
|
||||
|
||||
const handleClick = (): void => {
|
||||
onDismiss()
|
||||
router.push(`orgs/${orgID}`)
|
||||
}
|
||||
|
||||
const currentOrgIndicator = selected ? <em>Current</em> : null
|
||||
|
||||
return (
|
||||
<li className={orgSwitcherItemClass} onClick={handleClick}>
|
||||
<div className="org-switcher--item-circle">
|
||||
<Icon glyph={orgSwitcherIcon} className="org-switcher--item-icon" />
|
||||
</div>
|
||||
<span className="org-switcher--item-label">
|
||||
{orgName}
|
||||
{currentOrgIndicator}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default withRouter(OrgSwitcherItem)
|
|
@ -0,0 +1,77 @@
|
|||
.org-switcher--list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.org-switcher--prompt {
|
||||
text-align: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: $cf-marg-a;
|
||||
color: $g9-mountain;
|
||||
}
|
||||
|
||||
.org-switcher--item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: $g11-sidewalk;
|
||||
background-color: $g2-kevlar;
|
||||
padding: $cf-marg-c;
|
||||
margin-bottom: $cf-marg-a;
|
||||
border-radius: $cf-radius;
|
||||
transition: background-color 0.25s ease, color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g15-platinum;
|
||||
background-color: $g4-onyx;
|
||||
}
|
||||
}
|
||||
|
||||
.org-switcher--item-label {
|
||||
user-select: none;
|
||||
font-size: 1.125em;
|
||||
font-weight: $cf-font-weight--medium;
|
||||
|
||||
em {
|
||||
font-weight: $cf-font-weight--regular;
|
||||
font-size: 0.8em;
|
||||
font-style: normal;
|
||||
opacity: 0.3;
|
||||
display: inline-block;
|
||||
margin-left: $cf-marg-b;
|
||||
}
|
||||
}
|
||||
|
||||
.org-switcher--item-circle {
|
||||
width: $cf-marg-d;
|
||||
height: $cf-marg-d;
|
||||
border-radius: 50%;
|
||||
background-color: $g1-raven;
|
||||
margin-right: $cf-marg-c;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
transition: background-color 0.25s ease, color 0.25s ease, text-shadow 0.25s ease;
|
||||
color: $g1-raven;
|
||||
|
||||
.org-switcher--item:hover & {
|
||||
text-shadow: 0 0 4px $c-ocean;
|
||||
background-color: $g2-kevlar;
|
||||
color: $c-pool;
|
||||
}
|
||||
}
|
||||
|
||||
.org-switcher--item__selected,
|
||||
.org-switcher--item__selected:hover {
|
||||
cursor: default;
|
||||
background-color: $g5-pepper;
|
||||
color: $g20-white;
|
||||
|
||||
.org-switcher--item-circle {
|
||||
text-shadow: 0 0 4px $c-viridian;
|
||||
color: $c-honeydew;
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import {Overlay, Sort} from '@influxdata/clockface'
|
||||
import OrgSwitcherItem from 'src/pageLayout/components/OrgSwitcherItem'
|
||||
import SortingHat from 'src/shared/components/sorting_hat/SortingHat'
|
||||
|
||||
// Types
|
||||
import {AppState, Organization, ResourceType} from 'src/types'
|
||||
|
||||
// Selectors
|
||||
import {getOrg} from 'src/organizations/selectors'
|
||||
import {getAll} from 'src/resources/selectors'
|
||||
|
||||
interface ComponentProps {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
orgs: Organization[]
|
||||
currentOrg: Organization
|
||||
}
|
||||
|
||||
type Props = ComponentProps & StateProps
|
||||
|
||||
const OrgSwitcherOverlay: FC<Props> = ({orgs, onClose, currentOrg}) => {
|
||||
return (
|
||||
<Overlay.Container maxWidth={500}>
|
||||
<Overlay.Header title="Switch Organizations" onDismiss={onClose} />
|
||||
<Overlay.Body>
|
||||
<p className="org-switcher--prompt">Choose an organization</p>
|
||||
<SortingHat list={orgs} sortKey="name" direction={Sort.Ascending}>
|
||||
{sortedOrgs => (
|
||||
<div className="org-switcher--list">
|
||||
{sortedOrgs.map(org => (
|
||||
<OrgSwitcherItem
|
||||
key={org.id}
|
||||
orgID={org.id}
|
||||
orgName={org.name}
|
||||
selected={org.id === currentOrg.id}
|
||||
onDismiss={onClose}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</SortingHat>
|
||||
</Overlay.Body>
|
||||
</Overlay.Container>
|
||||
)
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const orgs = getAll<Organization>(state, ResourceType.Orgs)
|
||||
const currentOrg = getOrg(state)
|
||||
|
||||
return {
|
||||
orgs,
|
||||
currentOrg,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect<StateProps, {}, ComponentProps>(
|
||||
mstp,
|
||||
null
|
||||
)(OrgSwitcherOverlay)
|
|
@ -0,0 +1,149 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import {TreeNav} from '@influxdata/clockface'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
|
||||
// Actions
|
||||
import {showOverlay, dismissOverlay} from 'src/overlays/actions/overlays'
|
||||
|
||||
// Constants
|
||||
import {
|
||||
CLOUD_URL,
|
||||
CLOUD_LOGOUT_PATH,
|
||||
CLOUD_USAGE_PATH,
|
||||
CLOUD_BILLING_PATH,
|
||||
} from 'src/shared/constants'
|
||||
|
||||
// Types
|
||||
import {AppState, Organization} from 'src/types'
|
||||
import {MeState} from 'src/shared/reducers/me'
|
||||
|
||||
// Selectors
|
||||
import {getOrg} from 'src/organizations/selectors'
|
||||
|
||||
interface StateProps {
|
||||
org: Organization
|
||||
me: MeState
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
handleShowOverlay: typeof showOverlay
|
||||
handleDismissOverlay: typeof dismissOverlay
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps
|
||||
|
||||
const UserWidget: FC<Props> = ({
|
||||
org,
|
||||
me,
|
||||
handleShowOverlay,
|
||||
handleDismissOverlay,
|
||||
}) => {
|
||||
if (!org) {
|
||||
return null
|
||||
}
|
||||
|
||||
const logoutURL = `${CLOUD_URL}${CLOUD_LOGOUT_PATH}`
|
||||
|
||||
const handleSwitchOrganizations = (): void => {
|
||||
handleShowOverlay('switch-organizations', {}, handleDismissOverlay)
|
||||
}
|
||||
|
||||
const logoutLink = (
|
||||
<>
|
||||
<FeatureFlag name="regionBasedLoginPage">
|
||||
<TreeNav.UserItem
|
||||
id="logout"
|
||||
label="Logout"
|
||||
linkElement={className => <Link className={className} to="/logout" />}
|
||||
/>
|
||||
</FeatureFlag>
|
||||
<FeatureFlag name="regionBasedLoginPage" equals={false}>
|
||||
<CloudExclude>
|
||||
<TreeNav.UserItem
|
||||
id="logout"
|
||||
label="Logout"
|
||||
linkElement={className => (
|
||||
<Link className={className} to="/logout" />
|
||||
)}
|
||||
/>
|
||||
</CloudExclude>
|
||||
<CloudOnly>
|
||||
<TreeNav.UserItem
|
||||
id="logout"
|
||||
label="Logout"
|
||||
linkElement={className => (
|
||||
<a className={className} href={logoutURL} />
|
||||
)}
|
||||
/>
|
||||
</CloudOnly>
|
||||
</FeatureFlag>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<TreeNav.User username={me.name} team={org.name}>
|
||||
<CloudOnly>
|
||||
<TreeNav.UserItem
|
||||
id="usage"
|
||||
label="Usage"
|
||||
linkElement={className => (
|
||||
<a
|
||||
className={className}
|
||||
href={`${CLOUD_URL}/organizations/${org.id}/${CLOUD_USAGE_PATH}`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TreeNav.UserItem
|
||||
id="billing"
|
||||
label="Billing"
|
||||
linkElement={className => (
|
||||
<a
|
||||
className={className}
|
||||
href={`${CLOUD_URL}/organizations/${
|
||||
org.id
|
||||
}/${CLOUD_BILLING_PATH}`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</CloudOnly>
|
||||
<CloudExclude>
|
||||
<TreeNav.UserItem
|
||||
id="switch-orgs"
|
||||
label="Switch Organizations"
|
||||
onClick={handleSwitchOrganizations}
|
||||
/>
|
||||
<TreeNav.UserItem
|
||||
id="create-org"
|
||||
label="Create Organization"
|
||||
linkElement={className => (
|
||||
<Link className={className} to="/orgs/new" />
|
||||
)}
|
||||
/>
|
||||
</CloudExclude>
|
||||
{logoutLink}
|
||||
</TreeNav.User>
|
||||
)
|
||||
}
|
||||
|
||||
const mstp = (state: AppState) => {
|
||||
const org = getOrg(state)
|
||||
const me = state.me
|
||||
return {org, me}
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
handleShowOverlay: showOverlay,
|
||||
handleDismissOverlay: dismissOverlay,
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(UserWidget)
|
|
@ -0,0 +1,176 @@
|
|||
import {IconFont} from '@influxdata/clockface'
|
||||
import {CLOUD_URL, CLOUD_USERS_PATH} from 'src/shared/constants'
|
||||
|
||||
export interface NavSubItem {
|
||||
id: string
|
||||
testID: string
|
||||
label: string
|
||||
link: string
|
||||
cloudExclude?: boolean
|
||||
cloudOnly?: boolean
|
||||
featureFlag?: string
|
||||
}
|
||||
|
||||
export interface NavItem {
|
||||
id: string
|
||||
testID: string
|
||||
label: string
|
||||
shortLabel?: string
|
||||
link: string
|
||||
icon: IconFont
|
||||
cloudExclude?: boolean
|
||||
cloudOnly?: boolean
|
||||
featureFlag?: string
|
||||
menu?: NavSubItem[]
|
||||
activeKeywords: string[]
|
||||
}
|
||||
|
||||
export const generateNavItems = (orgID: string): NavItem[] => {
|
||||
const orgPrefix = `/orgs/${orgID}`
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'load-data',
|
||||
testID: 'nav-item-load-data',
|
||||
icon: IconFont.DisksNav,
|
||||
label: 'Load Data',
|
||||
shortLabel: 'Data',
|
||||
link: `${orgPrefix}/load-data/buckets`,
|
||||
activeKeywords: ['load-data'],
|
||||
menu: [
|
||||
{
|
||||
id: 'buckets',
|
||||
testID: 'nav-subitem-buckets',
|
||||
label: 'Buckets',
|
||||
link: `${orgPrefix}/load-data/buckets`,
|
||||
},
|
||||
{
|
||||
id: 'telegrafs',
|
||||
testID: 'nav-subitem-telegrafs',
|
||||
label: 'Telegraf',
|
||||
link: `${orgPrefix}/load-data/telegrafs`,
|
||||
},
|
||||
{
|
||||
id: 'scrapers',
|
||||
testID: 'nav-subitem-scrapers',
|
||||
label: 'Scrapers',
|
||||
link: `${orgPrefix}/load-data/scrapers`,
|
||||
cloudExclude: true,
|
||||
},
|
||||
{
|
||||
id: 'tokens',
|
||||
testID: 'nav-subitem-tokens',
|
||||
label: 'Tokens',
|
||||
link: `${orgPrefix}/load-data/tokens`,
|
||||
},
|
||||
{
|
||||
id: 'client-libraries',
|
||||
testID: 'nav-subitem-client-libraries',
|
||||
label: 'Client Libraries',
|
||||
link: `${orgPrefix}/load-data/client-libraries`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'data-explorer',
|
||||
testID: 'nav-item-data-explorer',
|
||||
icon: IconFont.GraphLine,
|
||||
label: 'Data Explorer',
|
||||
shortLabel: 'Explore',
|
||||
link: `${orgPrefix}/data-explorer`,
|
||||
activeKeywords: ['data-explorer'],
|
||||
},
|
||||
{
|
||||
id: 'org',
|
||||
testID: 'nav-item-org',
|
||||
icon: IconFont.UsersTrio,
|
||||
label: 'Organization',
|
||||
shortLabel: 'Org',
|
||||
link: `${orgPrefix}/members`,
|
||||
activeKeywords: ['members', 'about'],
|
||||
menu: [
|
||||
{
|
||||
id: 'members',
|
||||
testID: 'nav-subitem-members',
|
||||
label: 'Members',
|
||||
link: `${orgPrefix}/members`,
|
||||
cloudExclude: true,
|
||||
},
|
||||
{
|
||||
id: 'multi-user-members',
|
||||
testID: 'nav-subitem-multi-user-members',
|
||||
label: 'Members',
|
||||
featureFlag: 'multiUser',
|
||||
link: `${CLOUD_URL}/organizations/${orgID}/${CLOUD_USERS_PATH}`,
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
testID: 'nav-subitem-about',
|
||||
label: 'About',
|
||||
link: `${orgPrefix}/about`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dashboards',
|
||||
testID: 'nav-item-dashboards',
|
||||
icon: IconFont.Dashboards,
|
||||
label: 'Dashboards',
|
||||
shortLabel: 'Boards',
|
||||
link: `${orgPrefix}/dashboards`,
|
||||
activeKeywords: ['dashboards'],
|
||||
},
|
||||
{
|
||||
id: 'tasks',
|
||||
testID: 'nav-item-tasks',
|
||||
icon: IconFont.Calendar,
|
||||
label: 'Tasks',
|
||||
link: `${orgPrefix}/tasks`,
|
||||
activeKeywords: ['tasks'],
|
||||
},
|
||||
{
|
||||
id: 'alerting',
|
||||
testID: 'nav-item-alerting',
|
||||
icon: IconFont.Bell,
|
||||
label: 'Alerts',
|
||||
link: `${orgPrefix}/alerting`,
|
||||
activeKeywords: ['alerting'],
|
||||
menu: [
|
||||
{
|
||||
id: 'history',
|
||||
testID: 'nav-subitem-history',
|
||||
label: 'Alert History',
|
||||
link: `${orgPrefix}/alert-history`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
testID: 'nav-item-settings',
|
||||
icon: IconFont.WrenchNav,
|
||||
label: 'Settings',
|
||||
link: `${orgPrefix}/settings/variables`,
|
||||
activeKeywords: ['settings'],
|
||||
menu: [
|
||||
{
|
||||
id: 'variables',
|
||||
testID: 'nav-subitem-variables',
|
||||
label: 'Variables',
|
||||
link: `${orgPrefix}/settings/variables`,
|
||||
},
|
||||
{
|
||||
id: 'templates',
|
||||
testID: 'nav-subitem-templates',
|
||||
label: 'Templates',
|
||||
link: `${orgPrefix}/settings/templates`,
|
||||
},
|
||||
{
|
||||
id: 'labels',
|
||||
testID: 'nav-subitem-labels',
|
||||
label: 'Labels',
|
||||
link: `${orgPrefix}/settings/labels`,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
|
@ -89,7 +89,7 @@ class SideNav extends PureComponent<Props, State> {
|
|||
const variablesLink = `${orgPrefix}/settings/variables`
|
||||
const templatesLink = `${orgPrefix}/settings/templates`
|
||||
const labelsLink = `${orgPrefix}/settings/labels`
|
||||
const profileLink = `${orgPrefix}/settings/profile`
|
||||
const profileLink = `${orgPrefix}/settings/about`
|
||||
// Feedback
|
||||
const feedbackLink =
|
||||
'https://docs.google.com/forms/d/e/1FAIpQLSdGJpnIZGotN1VFJPkgZEhrt4t4f6QY1lMgMSRUnMeN3FjCKA/viewform?usp=sf_link'
|
||||
|
@ -329,7 +329,7 @@ class SideNav extends PureComponent<Props, State> {
|
|||
Profile
|
||||
</Link>
|
||||
)}
|
||||
active={getNavItemActivation(['profile'], location.pathname)}
|
||||
active={getNavItemActivation(['about'], location.pathname)}
|
||||
key="profile"
|
||||
/>
|
||||
</NavMenu.Item>
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import {withRouter, WithRouterProps, Link} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import {get} from 'lodash'
|
||||
|
||||
// Components
|
||||
import {Icon, TreeNav} from '@influxdata/clockface'
|
||||
import UserWidget from 'src/pageLayout/components/UserWidget'
|
||||
import NavHeader from 'src/pageLayout/components/NavHeader'
|
||||
import CloudUpgradeNavBanner from 'src/shared/components/CloudUpgradeNavBanner'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
|
||||
// Constants
|
||||
import {generateNavItems} from 'src/pageLayout/constants/navigationHierarchy'
|
||||
|
||||
// Utils
|
||||
import {getNavItemActivation} from 'src/pageLayout/utils'
|
||||
|
||||
// Types
|
||||
import {AppState, NavBarState} from 'src/types'
|
||||
|
||||
// Actions
|
||||
import {setNavBarState} from 'src/shared/actions/app'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface StateProps {
|
||||
isHidden: boolean
|
||||
navBarState: NavBarState
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
handleSetNavBarState: typeof setNavBarState
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps & WithRouterProps
|
||||
|
||||
@ErrorHandling
|
||||
class TreeSidebar extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {
|
||||
isHidden,
|
||||
params: {orgID},
|
||||
navBarState,
|
||||
handleSetNavBarState,
|
||||
} = this.props
|
||||
|
||||
if (isHidden) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isExpanded = navBarState === 'expanded'
|
||||
|
||||
const handleToggleNavExpansion = (): void => {
|
||||
if (isExpanded) {
|
||||
handleSetNavBarState('collapsed')
|
||||
} else {
|
||||
handleSetNavBarState('expanded')
|
||||
}
|
||||
}
|
||||
|
||||
const orgPrefix = `/orgs/${orgID}`
|
||||
const navItems = generateNavItems(orgID)
|
||||
|
||||
return (
|
||||
<TreeNav
|
||||
expanded={isExpanded}
|
||||
headerElement={<NavHeader link={orgPrefix} />}
|
||||
userElement={<UserWidget />}
|
||||
onToggleClick={handleToggleNavExpansion}
|
||||
bannerElement={<CloudUpgradeNavBanner />}
|
||||
>
|
||||
{navItems.map(item => {
|
||||
let navItemElement = (
|
||||
<TreeNav.Item
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
testID={item.testID}
|
||||
icon={<Icon glyph={item.icon} />}
|
||||
label={item.label}
|
||||
shortLabel={item.shortLabel}
|
||||
active={getNavItemActivation(
|
||||
item.activeKeywords,
|
||||
location.pathname
|
||||
)}
|
||||
linkElement={className => (
|
||||
<Link className={className} to={item.link} />
|
||||
)}
|
||||
>
|
||||
{Boolean(item.menu) && (
|
||||
<TreeNav.SubMenu>
|
||||
{item.menu.map(menuItem => {
|
||||
let navSubItemElement = (
|
||||
<TreeNav.SubItem
|
||||
key={menuItem.id}
|
||||
id={menuItem.id}
|
||||
testID={menuItem.testID}
|
||||
active={getNavItemActivation(
|
||||
[menuItem.id],
|
||||
location.pathname
|
||||
)}
|
||||
label={menuItem.label}
|
||||
linkElement={className => (
|
||||
<Link className={className} to={menuItem.link} />
|
||||
)}
|
||||
/>
|
||||
)
|
||||
|
||||
if (menuItem.cloudExclude) {
|
||||
navSubItemElement = (
|
||||
<CloudExclude key={menuItem.id}>
|
||||
{navSubItemElement}
|
||||
</CloudExclude>
|
||||
)
|
||||
}
|
||||
|
||||
if (menuItem.cloudOnly) {
|
||||
navSubItemElement = (
|
||||
<CloudOnly key={menuItem.id}>
|
||||
{navSubItemElement}
|
||||
</CloudOnly>
|
||||
)
|
||||
}
|
||||
|
||||
if (menuItem.featureFlag) {
|
||||
navSubItemElement = (
|
||||
<FeatureFlag
|
||||
key={menuItem.id}
|
||||
name={menuItem.featureFlag}
|
||||
>
|
||||
{navSubItemElement}
|
||||
</FeatureFlag>
|
||||
)
|
||||
}
|
||||
|
||||
return navSubItemElement
|
||||
})}
|
||||
</TreeNav.SubMenu>
|
||||
)}
|
||||
</TreeNav.Item>
|
||||
)
|
||||
|
||||
if (item.cloudExclude) {
|
||||
navItemElement = (
|
||||
<CloudExclude key={item.id}>{navItemElement}</CloudExclude>
|
||||
)
|
||||
}
|
||||
|
||||
if (item.cloudOnly) {
|
||||
navItemElement = (
|
||||
<CloudOnly key={item.id}>{navItemElement}</CloudOnly>
|
||||
)
|
||||
}
|
||||
|
||||
if (item.featureFlag) {
|
||||
navItemElement = (
|
||||
<FeatureFlag key={item.id} name={item.featureFlag}>
|
||||
{navItemElement}
|
||||
</FeatureFlag>
|
||||
)
|
||||
}
|
||||
|
||||
return navItemElement
|
||||
})}
|
||||
</TreeNav>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
handleSetNavBarState: setNavBarState,
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const isHidden = get(state, 'app.ephemeral.inPresentationMode', false)
|
||||
const navBarState = get(state, 'app.persisted.navBarState', 'collapsed')
|
||||
|
||||
return {isHidden, navBarState}
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(withRouter(TreeSidebar))
|
|
@ -1,3 +0,0 @@
|
|||
import Nav from 'src/pageLayout/containers/Nav'
|
||||
|
||||
export default Nav
|
|
@ -2,12 +2,14 @@ import React, {Component} from 'react'
|
|||
|
||||
// Components
|
||||
import {Page} from '@influxdata/clockface'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
class LoadDataHeader extends Component {
|
||||
public render() {
|
||||
return (
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Load Data" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@ import React, {Component} from 'react'
|
|||
|
||||
// Components
|
||||
import {Page} from '@influxdata/clockface'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
class SettingsHeader extends Component {
|
||||
public render() {
|
||||
return (
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Settings" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@ import _ from 'lodash'
|
|||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
|
||||
// Components
|
||||
import {Tabs, Orientation, ComponentSize} from '@influxdata/clockface'
|
||||
import TabbedPageTabs from 'src/shared/tabbedPage/TabbedPageTabs'
|
||||
|
||||
// Types
|
||||
import {TabbedPageTab} from 'src/shared/tabbedPage/TabbedPageTabs'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
|
||||
interface OwnProps {
|
||||
activeTab: string
|
||||
|
@ -26,60 +28,39 @@ class SettingsNavigation extends PureComponent<Props> {
|
|||
router.push(`/orgs/${orgID}/settings/${id}`)
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
const tabs: TabbedPageTab[] = [
|
||||
{
|
||||
text: 'Members',
|
||||
id: 'members',
|
||||
cloudExclude: true,
|
||||
featureFlagName: 'treeNav',
|
||||
featureFlagValue: false,
|
||||
},
|
||||
{
|
||||
text: 'Variables',
|
||||
id: 'variables',
|
||||
cloudExclude: false,
|
||||
},
|
||||
{
|
||||
text: 'Templates',
|
||||
id: 'templates',
|
||||
cloudExclude: false,
|
||||
},
|
||||
{
|
||||
text: 'Labels',
|
||||
id: 'labels',
|
||||
cloudExclude: false,
|
||||
},
|
||||
{
|
||||
text: 'Org Profile',
|
||||
id: 'profile',
|
||||
cloudExclude: false,
|
||||
text: 'Profile',
|
||||
id: 'about',
|
||||
featureFlagName: 'treeNav',
|
||||
featureFlagValue: false,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<Tabs orientation={Orientation.Horizontal} size={ComponentSize.Large}>
|
||||
{tabs.map(t => {
|
||||
if (t.cloudExclude) {
|
||||
return (
|
||||
<CloudExclude key={t.id}>
|
||||
<Tabs.Tab
|
||||
text={t.text}
|
||||
id={t.id}
|
||||
onClick={handleTabClick}
|
||||
active={t.id === activeTab}
|
||||
/>
|
||||
</CloudExclude>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Tabs.Tab
|
||||
key={t.id}
|
||||
text={t.text}
|
||||
id={t.id}
|
||||
onClick={handleTabClick}
|
||||
active={t.id === activeTab}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
<TabbedPageTabs
|
||||
tabs={tabs}
|
||||
activeTab={activeTab}
|
||||
onTabClick={handleTabClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ import {notify} from 'src/shared/actions/notifications'
|
|||
import {presentationMode} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Dispatch} from 'redux'
|
||||
|
||||
import {TimeZone, Theme} from 'src/types'
|
||||
import {TimeZone, Theme, NavBarState} from 'src/types'
|
||||
|
||||
export enum ActionTypes {
|
||||
EnablePresentationMode = 'ENABLE_PRESENTATION_MODE',
|
||||
DisablePresentationMode = 'DISABLE_PRESENTATION_MODE',
|
||||
SetNavBarState = 'SET_NAV_BAR_STATE',
|
||||
SetAutoRefresh = 'SET_AUTOREFRESH',
|
||||
SetTimeZone = 'SET_APP_TIME_ZONE',
|
||||
TemplateControlBarVisibilityToggled = 'TemplateControlBarVisibilityToggledAction',
|
||||
|
@ -19,12 +19,11 @@ export enum ActionTypes {
|
|||
export type Action =
|
||||
| ReturnType<typeof enablePresentationMode>
|
||||
| ReturnType<typeof disablePresentationMode>
|
||||
| ReturnType<typeof setNavBarState>
|
||||
| ReturnType<typeof setAutoRefresh>
|
||||
| ReturnType<typeof setTimeZone>
|
||||
| ReturnType<typeof setTheme>
|
||||
|
||||
export const setTheme = (theme: Theme) => ({type: 'SET_THEME', theme} as const)
|
||||
|
||||
// ephemeral state action creators
|
||||
|
||||
export const enablePresentationMode = () =>
|
||||
|
@ -47,6 +46,14 @@ export const delayEnablePresentationMode = () => (
|
|||
|
||||
// persistent state action creators
|
||||
|
||||
export const setTheme = (theme: Theme) => ({type: 'SET_THEME', theme} as const)
|
||||
|
||||
export const setNavBarState = (navBarState: NavBarState) =>
|
||||
({
|
||||
type: ActionTypes.SetNavBarState,
|
||||
navBarState,
|
||||
} as const)
|
||||
|
||||
export const setAutoRefresh = (milliseconds: number) =>
|
||||
({
|
||||
type: ActionTypes.SetAutoRefresh,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
|
||||
// Components
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
|
||||
// Constants
|
||||
import {CLOUD_URL, CLOUD_CHECKOUT_PATH} from 'src/shared/constants'
|
||||
|
||||
const CloudUpgradeButton: FC = () => {
|
||||
return (
|
||||
<CloudOnly>
|
||||
<FeatureFlag name="treeNav">
|
||||
<Link
|
||||
className="cf-button cf-button-sm cf-button-success upgrade-payg--button"
|
||||
to={`${CLOUD_URL}${CLOUD_CHECKOUT_PATH}`}
|
||||
>
|
||||
Upgrade Now
|
||||
</Link>
|
||||
</FeatureFlag>
|
||||
</CloudOnly>
|
||||
)
|
||||
}
|
||||
|
||||
export default CloudUpgradeButton
|
|
@ -0,0 +1,36 @@
|
|||
.cloud-upgrade-banner {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cloud-upgrade-banner--button {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a.cloud-upgrade-banner__collapsed {
|
||||
text-align: center;
|
||||
border-radius: $cf-radius;
|
||||
margin: $cf-marg-b 0;
|
||||
display: none;
|
||||
width: 100%;
|
||||
|
||||
h5 {
|
||||
font-size: $cf-text-tiny;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: $cf-marg-a;
|
||||
}
|
||||
|
||||
.cf-icon {
|
||||
font-size: 2.75em;
|
||||
margin-bottom: $cf-marg-a;
|
||||
}
|
||||
}
|
||||
|
||||
// Collapsed mode
|
||||
@media screen and (min-width: $cf-nav-menu--breakpoint) {
|
||||
.cf-tree-nav__collapsed a.cloud-upgrade-banner__collapsed {
|
||||
display: inline-block;
|
||||
}
|
||||
.cf-tree-nav__collapsed .cloud-upgrade-banner {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
|
||||
// Components
|
||||
import {
|
||||
Panel,
|
||||
ComponentSize,
|
||||
Heading,
|
||||
HeadingElement,
|
||||
Gradients,
|
||||
JustifyContent,
|
||||
Icon,
|
||||
IconFont,
|
||||
} from '@influxdata/clockface'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
|
||||
// Constants
|
||||
import {CLOUD_URL, CLOUD_CHECKOUT_PATH} from 'src/shared/constants'
|
||||
|
||||
const CloudUpgradeNavBanner: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<CloudOnly>
|
||||
<Panel
|
||||
gradient={Gradients.HotelBreakfast}
|
||||
className="cloud-upgrade-banner"
|
||||
>
|
||||
<Panel.Header
|
||||
size={ComponentSize.ExtraSmall}
|
||||
justifyContent={JustifyContent.Center}
|
||||
>
|
||||
<Heading element={HeadingElement.H5}>
|
||||
Need more wiggle room?
|
||||
</Heading>
|
||||
</Panel.Header>
|
||||
<Panel.Footer size={ComponentSize.ExtraSmall}>
|
||||
<Link
|
||||
className="cf-button cf-button-md cf-button-primary cf-button-stretch cloud-upgrade-banner--button"
|
||||
to={`${CLOUD_URL}${CLOUD_CHECKOUT_PATH}`}
|
||||
>
|
||||
Upgrade Now
|
||||
</Link>
|
||||
</Panel.Footer>
|
||||
</Panel>
|
||||
<Link
|
||||
className="cloud-upgrade-banner__collapsed"
|
||||
to={`${CLOUD_URL}${CLOUD_CHECKOUT_PATH}`}
|
||||
>
|
||||
<Icon glyph={IconFont.Star} />
|
||||
<Heading element={HeadingElement.H5}>Upgrade Now</Heading>
|
||||
</Link>
|
||||
</CloudOnly>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CloudUpgradeNavBanner
|
|
@ -12,4 +12,9 @@
|
|||
|
||||
.load-data--asset-alert {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
// Might need to remove this eventually
|
||||
.cf-page-header--fixed {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {PureComponent} from 'react'
|
|||
import {orderBy} from 'lodash'
|
||||
|
||||
// Types
|
||||
import {Sort} from 'src/clockface/types'
|
||||
import {Sort} from '@influxdata/clockface'
|
||||
|
||||
interface Props<T> {
|
||||
list: T[]
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
enablePresentationMode,
|
||||
disablePresentationMode,
|
||||
setTheme,
|
||||
setNavBarState,
|
||||
setAutoRefresh,
|
||||
} from 'src/shared/actions/app'
|
||||
import {TimeZone} from 'src/types'
|
||||
|
@ -16,6 +17,7 @@ describe('Shared.Reducers.appReducer', () => {
|
|||
persisted: {
|
||||
autoRefresh: 0,
|
||||
showTemplateControlBar: false,
|
||||
navBarState: 'expanded',
|
||||
timeZone: 'Local' as TimeZone,
|
||||
theme: 'dark',
|
||||
},
|
||||
|
@ -49,6 +51,20 @@ describe('Shared.Reducers.appReducer', () => {
|
|||
expect(reducedState.persisted.theme).toBe('dark')
|
||||
})
|
||||
|
||||
it('should handle SET_NAV_BAR_STATE to collapsed', () => {
|
||||
const reducedState = appReducer(initialState, setNavBarState('collapsed'))
|
||||
|
||||
expect(reducedState.persisted.navBarState).toBe('collapsed')
|
||||
})
|
||||
|
||||
it('should handle SET_NAV_BAR_STATE to expanded', () => {
|
||||
Object.assign(initialState, {persisted: {navBarState: 'collapsed'}})
|
||||
|
||||
const reducedState = appReducer(initialState, setNavBarState('expanded'))
|
||||
|
||||
expect(reducedState.persisted.navBarState).toBe('expanded')
|
||||
})
|
||||
|
||||
it('should handle SET_AUTOREFRESH', () => {
|
||||
const expectedMs = 15000
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import {combineReducers} from 'redux'
|
|||
// Types
|
||||
import {ActionTypes, Action} from 'src/shared/actions/app'
|
||||
import {AUTOREFRESH_DEFAULT_INTERVAL} from 'src/shared/constants'
|
||||
import {TimeZone} from 'src/types'
|
||||
import {TimeZone, NavBarState, Theme} from 'src/types'
|
||||
|
||||
export interface AppState {
|
||||
ephemeral: {
|
||||
|
@ -13,7 +13,8 @@ export interface AppState {
|
|||
autoRefresh: number
|
||||
showTemplateControlBar: boolean
|
||||
timeZone: TimeZone
|
||||
theme: 'dark' | 'light'
|
||||
navBarState: NavBarState
|
||||
theme: Theme
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +27,7 @@ const initialState: AppState = {
|
|||
autoRefresh: AUTOREFRESH_DEFAULT_INTERVAL,
|
||||
showTemplateControlBar: false,
|
||||
timeZone: 'Local',
|
||||
navBarState: 'collapsed',
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -80,6 +82,14 @@ const appPersistedReducer = (
|
|||
return {...state, timeZone}
|
||||
}
|
||||
|
||||
case 'SET_NAV_BAR_STATE': {
|
||||
const navBarState = action.navBarState
|
||||
return {
|
||||
...state,
|
||||
navBarState,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import {Tabs, Orientation, ComponentSize} from '@influxdata/clockface'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
import CloudOnly from 'src/shared/components/cloud/CloudOnly'
|
||||
import {FeatureFlag} from 'src/shared/utils/featureFlag'
|
||||
|
||||
export interface TabbedPageTab {
|
||||
text: string
|
||||
id: string
|
||||
cloudExclude?: boolean
|
||||
cloudOnly?: boolean
|
||||
featureFlagName?: string
|
||||
featureFlagValue?: boolean
|
||||
}
|
||||
|
||||
interface Props {
|
||||
activeTab: string
|
||||
tabs: TabbedPageTab[]
|
||||
onTabClick: (id: string) => void
|
||||
}
|
||||
|
||||
const SettingsNavigation: FC<Props> = ({activeTab, tabs, onTabClick}) => {
|
||||
return (
|
||||
<Tabs orientation={Orientation.Horizontal} size={ComponentSize.Large}>
|
||||
{tabs.map(t => {
|
||||
let tab = (
|
||||
<Tabs.Tab
|
||||
key={t.id}
|
||||
text={t.text}
|
||||
id={t.id}
|
||||
onClick={onTabClick}
|
||||
active={t.id === activeTab}
|
||||
/>
|
||||
)
|
||||
|
||||
if (t.cloudExclude) {
|
||||
tab = <CloudExclude key={t.id}>{tab}</CloudExclude>
|
||||
}
|
||||
|
||||
if (t.cloudOnly) {
|
||||
tab = <CloudOnly key={t.id}>{tab}</CloudOnly>
|
||||
}
|
||||
|
||||
if (t.featureFlagName) {
|
||||
tab = (
|
||||
<FeatureFlag
|
||||
key={t.id}
|
||||
name={t.featureFlagName}
|
||||
equals={t.featureFlagValue}
|
||||
>
|
||||
{tab}
|
||||
</FeatureFlag>
|
||||
)
|
||||
}
|
||||
|
||||
return tab
|
||||
})}
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
export default SettingsNavigation
|
|
@ -8,6 +8,7 @@ export const OSS_FLAGS = {
|
|||
customCheckQuery: false,
|
||||
matchingNotificationRules: false,
|
||||
regionBasedLoginPage: false,
|
||||
treeNav: false,
|
||||
}
|
||||
|
||||
export const CLOUD_FLAGS = {
|
||||
|
@ -19,6 +20,7 @@ export const CLOUD_FLAGS = {
|
|||
customCheckQuery: false,
|
||||
matchingNotificationRules: false,
|
||||
regionBasedLoginPage: false,
|
||||
treeNav: false,
|
||||
}
|
||||
|
||||
export const isFlagEnabled = (flagName: string, equals?: string | boolean) => {
|
||||
|
|
|
@ -75,8 +75,9 @@
|
|||
@import 'src/me/graphics/ExploreGraphic.scss';
|
||||
@import 'src/me/graphics/DashboardingGraphic.scss';
|
||||
@import 'src/me/graphics/CollectorGraphic.scss';
|
||||
@import 'src/pageLayout/components/RenamablePageTitle.scss';
|
||||
@import 'src/pageLayout/components/CloudNav.scss';
|
||||
@import 'src/pageLayout/components/RenamablePageTitle.scss';
|
||||
@import 'src/pageLayout/components/OrgSwitcherOverlay.scss';
|
||||
@import 'src/timeMachine/components/SelectorList.scss';
|
||||
@import 'src/timeMachine/components/Queries.scss';
|
||||
@import 'src/timeMachine/components/EditorShortcutsTooltip.scss';
|
||||
|
@ -110,6 +111,7 @@
|
|||
@import 'src/shared/components/EventMarkerTooltip.scss';
|
||||
@import 'src/shared/components/DeleteDataForm/DeleteDataForm.scss';
|
||||
@import 'src/shared/components/cloud/CloudOnly.scss';
|
||||
@import 'src/shared/components/CloudUpgradeNavBanner.scss';
|
||||
@import 'src/notifications/rules/components/NewRuleOverlay.scss';
|
||||
@import 'src/shared/components/dashed_button/DashedButton.scss';
|
||||
@import 'src/alerting/components/AlertingIndex.scss';
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
// Types
|
||||
// Components
|
||||
import {
|
||||
ComponentColor,
|
||||
Button,
|
||||
ComponentStatus,
|
||||
Page,
|
||||
} from '@influxdata/clockface'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
|
@ -23,6 +24,7 @@ export default class TaskHeader extends PureComponent<Props> {
|
|||
<>
|
||||
<Page.Header fullWidth={true}>
|
||||
<Page.Title title={title} />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={true}>
|
||||
<Page.ControlBarRight>
|
||||
|
|
|
@ -6,6 +6,7 @@ import {withRouter, WithRouterProps} from 'react-router'
|
|||
// Components
|
||||
import {Page, IconFont, Sort} from '@influxdata/clockface'
|
||||
import TaskRunsList from 'src/tasks/components/TaskRunsList'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Types
|
||||
import {AppState, RemoteDataState, Task, Run} from 'src/types'
|
||||
|
@ -72,6 +73,7 @@ class TaskRunsPage extends PureComponent<Props & WithRouterProps, State> {
|
|||
<Page titleTag={pageTitleSuffixer(['Task Runs'])}>
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title={this.title} />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={false}>
|
||||
<Page.ControlBarLeft>
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
Page,
|
||||
} from '@influxdata/clockface'
|
||||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import CloudUpgradeButton from 'src/shared/components/CloudUpgradeButton'
|
||||
|
||||
// Types
|
||||
import {LimitStatus} from 'src/cloud/actions/limits'
|
||||
|
@ -37,6 +38,7 @@ export default class TasksHeader extends PureComponent<Props> {
|
|||
<>
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Title title="Tasks" />
|
||||
<CloudUpgradeButton />
|
||||
</Page.Header>
|
||||
<Page.ControlBar fullWidth={false}>
|
||||
<Page.ControlBarLeft>
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export type CurrentPage = 'dashboard' | 'not set'
|
||||
export type Theme = 'light' | 'dark'
|
||||
export type NavBarState = 'expanded' | 'collapsed'
|
||||
|
|
Loading…
Reference in New Issue