diff --git a/grafana-plugin/src/PluginPage.tsx b/grafana-plugin/src/PluginPage.tsx index 43e0d91f..59a6188d 100644 --- a/grafana-plugin/src/PluginPage.tsx +++ b/grafana-plugin/src/PluginPage.tsx @@ -6,25 +6,34 @@ import Header from 'navbar/Header/Header'; import { pages } from 'pages'; import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { useStore } from 'state/useStore'; +import { DEFAULT_PAGE } from 'utils/consts'; import { useQueryParams } from 'utils/hooks'; -export const PluginPage = (isTopNavbar() ? RealPlugin : PluginPageFallback) as React.ComponentType; +export const PluginPage = ( + isTopNavbar() ? RealPlugin : PluginPageFallback +) as React.ComponentType; -function RealPlugin(props: PluginPageProps): React.ReactNode { +interface ExtendedPluginPageProps extends PluginPageProps { + renderAlertsFn?: () => React.ReactNode; +} + +function RealPlugin(props: ExtendedPluginPageProps): React.ReactNode { const store = useStore(); const queryParams = useQueryParams(); - const page = queryParams.get('page'); + const page = queryParams.get('page') || DEFAULT_PAGE; return ( + {/* Render alerts at the top */} + {props.renderAlertsFn && props.renderAlertsFn()}
-

{pages[page].text}

+ {pages[page].text &&

{pages[page].text}

} {props.children} ); } -function PluginPageFallback(props: PluginPageProps): React.ReactNode { +function PluginPageFallback(props: ExtendedPluginPageProps): React.ReactNode { return props.children; } diff --git a/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.module.scss b/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.module.scss index 4eb9b584..8526fd34 100644 --- a/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.module.scss +++ b/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.module.scss @@ -1,15 +1,32 @@ .root { + width: 100%; height: 100%; display: flex; flex-direction: column; +} - .alerts_horizontal { - display: flex; - gap: 10px; +.alerts-container { + display: flex; + flex-direction: column; + margin-bottom: 24px; + gap: 10px; + + &:empty { + display: none; } +} - .alert { - margin: 24px 0; +.navbar-legacy .alerts-container { + padding-top: 10px; +} + +.alert { + margin: 0; +} + +@media (max-width: 768px) { + .navbar-legacy { + padding-top: 50px; } } diff --git a/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx b/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx index e81aeada..cb2ebb2b 100644 --- a/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx +++ b/grafana-plugin/src/containers/DefaultPageLayout/DefaultPageLayout.tsx @@ -1,19 +1,24 @@ -import plugin from '../../../package.json'; // eslint-disable-line import React, { FC, useEffect, useState, useCallback } from 'react'; import { Alert } from '@grafana/ui'; +import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import { AppRootProps } from 'types'; import PluginLink from 'components/PluginLink/PluginLink'; import { getIfChatOpsConnected } from 'containers/DefaultPageLayout/helper'; +import { pages } from 'pages'; +import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; import LocationHelper from 'utils/LocationHelper'; import { isUserActionAllowed, UserActions } from 'utils/authorization'; -import { GRAFANA_LICENSE_OSS } from 'utils/consts'; -import { useForceUpdate } from 'utils/hooks'; +import { DEFAULT_PAGE, GRAFANA_LICENSE_OSS } from 'utils/consts'; +import { useForceUpdate, useQueryParams } from 'utils/hooks'; + +import plugin from '../../../package.json'; // eslint-disable-line + import { getItem, setItem } from 'utils/localStorage'; import sanitize from 'utils/sanitize'; @@ -33,10 +38,12 @@ enum AlertID { const DefaultPageLayout: FC = observer((props) => { const { children, query } = props; + const queryParams = useQueryParams(); const [showSlackInstallAlert, setShowSlackInstallAlert] = useState(); const forceUpdate = useForceUpdate(); + const page = queryParams.get('page') || DEFAULT_PAGE; const handleCloseInstallSlackAlert = useCallback(() => { setShowSlackInstallAlert(undefined); @@ -68,15 +75,41 @@ const DefaultPageLayout: FC = observer((props) => { const isChatOpsConnected = getIfChatOpsConnected(currentUser); const isPhoneVerified = currentUser?.cloud_connection_status === 3 || currentUser?.verified_phone_number; - return ( -
-
+ if (isTopNavbar()) { + return renderTopNavbar(); + } + + return renderLegacyNavbar(); + + function renderTopNavbar(): JSX.Element { + return ( + +
{children}
+
+ ); + } + + function renderLegacyNavbar(): JSX.Element { + return ( + +
+
+ {renderAlertsFn()} + {children} +
+
+
+ ); + } + + function renderAlertsFn(): JSX.Element { + return ( +
{showSlackInstallAlert && ( {getSlackMessage( @@ -88,7 +121,7 @@ const DefaultPageLayout: FC = observer((props) => { )} {currentTeam?.banner.title != null && !getItem(currentTeam?.banner.title) && ( = observer((props) => { store.backendVersion !== plugin?.version && !getItem(`version_mismatch_${store.backendVersion}_${plugin?.version}`) && ( = observer((props) => { ) && ( = observer((props) => { )}
- {children} -
- ); + ); + } }); export default DefaultPageLayout; diff --git a/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.module.scss b/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.module.scss index 8c90a385..cea8e371 100644 --- a/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.module.scss +++ b/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.module.scss @@ -1,24 +1,17 @@ .teamSelect { width: 200px; - right: 0; - position: absolute; - padding: 16px 0; - margin-right: 24px; - - &--topRight { - right: 14px; - top: 12px; - } - &--topRightIncident { - right: 32px; - top: 36px; - } } .teamSelectLabel { display: flex; } +.teamSelectText, +.teamSelectLink { + line-height: 1.25; + margin-bottom: 4px; +} + .teamSelectLink { margin-left: auto; } diff --git a/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx b/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx index 857dc370..89d50118 100644 --- a/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx +++ b/grafana-plugin/src/containers/GrafanaTeamSelect/GrafanaTeamSelect.tsx @@ -49,13 +49,15 @@ const GrafanaTeamSelect = observer((props: GrafanaTeamSelectProps) => { }; const content = ( -
+
diff --git a/grafana-plugin/src/img/grafanaGlobalStyles.css b/grafana-plugin/src/img/grafanaGlobalStyles.css index 9e29d7ba..aaebbf02 100644 --- a/grafana-plugin/src/img/grafanaGlobalStyles.css +++ b/grafana-plugin/src/img/grafanaGlobalStyles.css @@ -4,24 +4,12 @@ max-width: unset !important; } -.oncall-header { - padding-top: 0; - padding-bottom: 36px; -} - -.scrollbar-view h1:first-child { - margin-bottom: 0 !important; -} - -.page-container.page-body { - flex-grow: 1 !important; +[class$='-page-header'] { + display: none; } .page-container { max-width: unset !important; - flex-grow: unset !important; - flex-basis: unset !important; - overflow-x: auto; } .page-scrollbar-content > div:first-child { @@ -34,34 +22,6 @@ margin-right: 8px; } -/* This is for Grafana 8, remove later */ -@media (max-width: 1540px) { - .page-header__tabs > ul > li > a > div { - display: none; - } -} - -@media (max-width: 1540px) { - .page-header__tabs > div > div > a > div { - display: none; - } -} - -@media (max-width: 1300px) { - .sidemenu { - position: fixed !important; - height: 100%; - } - - .main-view { - padding-left: 50px; - } - - .page-header__tabs li a { - white-space: nowrap; - } -} - .page-header__info-block { flex-grow: 1; /* Stretch the navigation subtitle panel */ } diff --git a/grafana-plugin/src/navbar/Header/Header.module.scss b/grafana-plugin/src/navbar/Header/Header.module.scss index 6df19f38..8fb7ee27 100644 --- a/grafana-plugin/src/navbar/Header/Header.module.scss +++ b/grafana-plugin/src/navbar/Header/Header.module.scss @@ -2,6 +2,11 @@ margin-right: 4px; } +.header-topnavbar { + padding-top: 0; + padding-bottom: 36px; +} + .navbar-heading { padding: 4px; margin: 0 0 0 8px; @@ -16,3 +21,8 @@ align-items: center; padding-top: 6px; } + +.navbar-left { + display: flex; + flex-basis: 100%; +} diff --git a/grafana-plugin/src/navbar/Header/Header.tsx b/grafana-plugin/src/navbar/Header/Header.tsx index f1463c8f..157e74b9 100644 --- a/grafana-plugin/src/navbar/Header/Header.tsx +++ b/grafana-plugin/src/navbar/Header/Header.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Card } from '@grafana/ui'; -import classnames from 'classnames'; import cn from 'classnames/bind'; import gitHubStarSVG from 'assets/img/github_star.svg'; @@ -16,15 +15,16 @@ const cx = cn.bind(styles); export default function Header({ page, backendLicense }: { page: string; backendLicense: string }) { return ( -
-
-
+
+
+
Grafana OnCall
{renderHeading()}
- +
+
diff --git a/grafana-plugin/src/navbar/LegacyNavTabsBar.module.scss b/grafana-plugin/src/navbar/LegacyNavTabsBar.module.scss index c3b2ca3c..be25eb95 100644 --- a/grafana-plugin/src/navbar/LegacyNavTabsBar.module.scss +++ b/grafana-plugin/src/navbar/LegacyNavTabsBar.module.scss @@ -1,3 +1,4 @@ .root { - min-width: 1500px; + overflow-x: auto; + white-space: nowrap; } diff --git a/grafana-plugin/src/navbar/LegacyNavTabsBar.tsx b/grafana-plugin/src/navbar/LegacyNavTabsBar.tsx index c6564d3c..e46599f8 100644 --- a/grafana-plugin/src/navbar/LegacyNavTabsBar.tsx +++ b/grafana-plugin/src/navbar/LegacyNavTabsBar.tsx @@ -19,16 +19,18 @@ export default function LegacyNavTabsBar({ currentPage }: { currentPage: string .filter((page) => (page.hideFromTabsFn ? !page.hideFromTabsFn(store) : !page.hideFromTabs)); return ( - - {navigationPages.map((page, index) => ( - - ))} - +
+ + {navigationPages.map((page, index) => ( + + ))} + +
); } diff --git a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx index d01bd347..4570f32f 100644 --- a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx +++ b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, HorizontalGroup, Icon, IconButton, LoadingPlaceholder, Tooltip, VerticalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -25,7 +24,6 @@ import EscalationChainForm from 'containers/EscalationChainForm/EscalationChainF import EscalationChainSteps from 'containers/EscalationChainSteps/EscalationChainSteps'; import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; @@ -135,90 +133,88 @@ class EscalationChainsPage extends React.Component - - {() => ( - <> -
-
- + + {() => ( + <> +
+
+ +
+ {!searchResult || searchResult.length ? ( +
+
+ + + +
+ {searchResult ? ( + + {(item) => } + + ) : ( + + )} +
+
+
{this.renderEscalation()}
- {!searchResult || searchResult.length ? ( -
-
- + ) : ( + + No escalations found, check your filtering and current team. + -
- {searchResult ? ( - - {(item) => } - - ) : ( - - )} -
-
-
{this.renderEscalation()}
-
- ) : ( - - No escalations found, check your filtering and current team. - - - - - } - /> - )} -
- {showCreateEscalationChainModal && ( - { - this.setState({ - showCreateEscalationChainModal: false, - escalationChainIdToCopy: undefined, - }); - }} - onUpdate={this.handleEscalationChainCreate} + + } /> )} - - )} -
- +
+ {showCreateEscalationChainModal && ( + { + this.setState({ + showCreateEscalationChainModal: false, + escalationChainIdToCopy: undefined, + }); + }} + onUpdate={this.handleEscalationChainCreate} + /> + )} + + )} + ); } diff --git a/grafana-plugin/src/pages/incident/Incident.module.css b/grafana-plugin/src/pages/incident/Incident.module.css index 1460c4e7..eda3f4b5 100644 --- a/grafana-plugin/src/pages/incident/Incident.module.css +++ b/grafana-plugin/src/pages/incident/Incident.module.css @@ -6,6 +6,10 @@ flex-grow: 1; } +.block { + padding: 0 0 20px 0; +} + .payload-subtitle { margin-bottom: var(--title-marginBottom); } diff --git a/grafana-plugin/src/pages/incident/Incident.tsx b/grafana-plugin/src/pages/incident/Incident.tsx index b155f4c4..af34dadc 100644 --- a/grafana-plugin/src/pages/incident/Incident.tsx +++ b/grafana-plugin/src/pages/incident/Incident.tsx @@ -14,7 +14,6 @@ import { Modal, Tooltip, } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -46,7 +45,6 @@ import { GroupedAlert, } from 'models/alertgroup/alertgroup.types'; import { ResolutionNoteSourceTypesToDisplayName } from 'models/resolution_note/resolution_note.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { useStore } from 'state/useStore'; import { withMobXProviderContext } from 'state/withStore'; @@ -127,71 +125,69 @@ class IncidentPage extends React.Component } return ( - - - {() => ( -
- {errorData.isNotFoundError ? ( -
- - 404 - Incident not found - - - - -
- ) : ( - <> - {this.renderHeader()} -
-
- - - -
-
{this.renderTimeline()}
+ + {() => ( +
+ {errorData.isNotFoundError ? ( +
+ + 404 + Incident not found + + + + +
+ ) : ( + <> + {this.renderHeader()} +
+
+ + +
- {showIntegrationSettings && ( - { - alertReceiveChannelStore.updateItem(incident.alert_receive_channel.id); - }} - onUpdateTemplates={() => { - store.alertGroupStore.getAlert(id); - }} - startTab={IntegrationSettingsTab.Templates} - id={incident.alert_receive_channel.id} - onHide={() => - this.setState({ - showIntegrationSettings: undefined, - }) - } - /> - )} - {showAttachIncidentForm && ( - { - this.setState({ - showAttachIncidentForm: false, - }); - }} - onUpdate={this.update} - /> - )} - - )} -
- )} - - +
{this.renderTimeline()}
+
+ {showIntegrationSettings && ( + { + alertReceiveChannelStore.updateItem(incident.alert_receive_channel.id); + }} + onUpdateTemplates={() => { + store.alertGroupStore.getAlert(id); + }} + startTab={IntegrationSettingsTab.Templates} + id={incident.alert_receive_channel.id} + onHide={() => + this.setState({ + showIntegrationSettings: undefined, + }) + } + /> + )} + {showAttachIncidentForm && ( + { + this.setState({ + showAttachIncidentForm: false, + }); + }} + onUpdate={this.update} + /> + )} + + )} +
+ )} + ); } @@ -210,7 +206,7 @@ class IncidentPage extends React.Component const showLinkTo = !incident.dependent_alert_groups.length && !incident.root_alert_group && !incident.resolved; return ( - + diff --git a/grafana-plugin/src/pages/incidents/Incidents.tsx b/grafana-plugin/src/pages/incidents/Incidents.tsx index 10503e7b..ec980869 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.tsx +++ b/grafana-plugin/src/pages/incidents/Incidents.tsx @@ -1,7 +1,6 @@ import React, { ReactElement, SyntheticEvent } from 'react'; import { Button, Icon, Tooltip, VerticalGroup, LoadingPlaceholder, HorizontalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { get } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -20,7 +19,6 @@ import IncidentsFilters from 'containers/IncidentsFilters/IncidentsFilters'; import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { Alert, Alert as AlertType, AlertAction } from 'models/alertgroup/alertgroup.types'; import { User } from 'models/user/user.types'; -import { pages } from 'pages'; import { getActionButtons, getIncidentStatusTag, renderRelatedUsers } from 'pages/incident/Incident.helpers'; import { move } from 'state/helpers'; import { PageProps, WithStoreProps } from 'state/types'; @@ -101,12 +99,10 @@ class Incidents extends React.Component render() { return ( - -
- {this.renderIncidentFilters()} - {this.renderTable()} -
-
+
+ {this.renderIncidentFilters()} + {this.renderTable()} +
); } diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 5874adcb..bfe56d06 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -24,7 +23,6 @@ import { IntegrationSettingsTab } from 'containers/IntegrationSettings/Integrati import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { AlertReceiveChannel } from 'models/alert_receive_channel'; import { AlertReceiveChannelOption } from 'models/alert_receive_channel/alert_receive_channel.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; @@ -131,121 +129,119 @@ class Integrations extends React.Component const searchResult = alertReceiveChannelStore.getSearchResult(); return ( - - - {() => ( - <> -
-
- + + {() => ( + <> +
+
+ +
+ {searchResult?.length ? ( +
+
+ + + +
+ + {(item) => ( + { + this.setState({ + alertReceiveChannelToShowSettings: item.id, + integrationSettingsTab: IntegrationSettingsTab.Heartbeat, + }); + }} + /> + )} + +
+
+
+ { + this.setState({ + alertReceiveChannelToShowSettings: store.selectedAlertReceiveChannel, + integrationSettingsTab, + }); + }} + /> +
- {searchResult?.length ? ( -
-
+ ) : searchResult ? ( + + No integrations found. Review your filter and team settings. -
- - {(item) => ( - { - this.setState({ - alertReceiveChannelToShowSettings: item.id, - integrationSettingsTab: IntegrationSettingsTab.Heartbeat, - }); - }} - /> - )} - -
-
-
- { - this.setState({ - alertReceiveChannelToShowSettings: store.selectedAlertReceiveChannel, - integrationSettingsTab, - }); - }} - /> -
-
- ) : searchResult ? ( - - No integrations found. Review your filter and team settings. - - - - - } - /> - ) : ( - - )} -
- {alertReceiveChannelToShowSettings && ( - { - alertReceiveChannelStore.updateItem(alertReceiveChannelToShowSettings); - }} - startTab={integrationSettingsTab} - id={alertReceiveChannelToShowSettings} - onHide={() => { - this.setState({ - alertReceiveChannelToShowSettings: undefined, - integrationSettingsTab: undefined, - }); - LocationHelper.update({ tab: undefined }, 'partial'); - }} + + } /> + ) : ( + )} - {showCreateIntegrationModal && ( - { - this.setState({ showCreateIntegrationModal: false }); - }} - onCreate={this.handleCreateNewAlertReceiveChannel} - /> - )} - - )} -
- +
+ {alertReceiveChannelToShowSettings && ( + { + alertReceiveChannelStore.updateItem(alertReceiveChannelToShowSettings); + }} + startTab={integrationSettingsTab} + id={alertReceiveChannelToShowSettings} + onHide={() => { + this.setState({ + alertReceiveChannelToShowSettings: undefined, + integrationSettingsTab: undefined, + }); + LocationHelper.update({ tab: undefined }, 'partial'); + }} + /> + )} + {showCreateIntegrationModal && ( + { + this.setState({ showCreateIntegrationModal: false }); + }} + onCreate={this.handleCreateNewAlertReceiveChannel} + /> + )} + + )} + ); } diff --git a/grafana-plugin/src/pages/maintenance/Maintenance.tsx b/grafana-plugin/src/pages/maintenance/Maintenance.tsx index 90ebff26..0cccf565 100644 --- a/grafana-plugin/src/pages/maintenance/Maintenance.tsx +++ b/grafana-plugin/src/pages/maintenance/Maintenance.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, VerticalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -16,7 +15,6 @@ import { WithPermissionControl } from 'containers/WithPermissionControl/WithPerm import { getAlertReceiveChannelDisplayName } from 'models/alert_receive_channel/alert_receive_channel.helpers'; import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types'; import { Maintenance, MaintenanceMode, MaintenanceType } from 'models/maintenance/maintenance.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import { UserActions } from 'utils/authorization'; @@ -117,7 +115,7 @@ class MaintenancePage extends React.Component + <>
)} - + ); } diff --git a/grafana-plugin/src/pages/organization-logs/OrganizationLog.tsx b/grafana-plugin/src/pages/organization-logs/OrganizationLog.tsx index 6a063e0e..66a7d5b5 100644 --- a/grafana-plugin/src/pages/organization-logs/OrganizationLog.tsx +++ b/grafana-plugin/src/pages/organization-logs/OrganizationLog.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, HorizontalGroup, Tag, Tooltip } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -96,41 +95,39 @@ class OrganizationLogPage extends React.Component -
- - ( -
- - Organization Logs - - -
- )} - showHeader={true} - data={results} - loading={loading} - emptyText={results ? 'No logs found' : 'Loading...'} - columns={columns} - pagination={{ - page, - total: Math.ceil((total || 0) / ITEMS_PER_PAGE), - onChange: this.handleChangePage, - }} - rowClassName={cx('align-top')} - expandable={{ - expandedRowRender: this.renderFullDescription, - expandRowByClick: true, - expandedRowKeys: expandedLogsKeys, - onExpandedRowsChange: this.handleExpandedRowsChange, - }} - /> -
- +
+ + ( +
+ + Organization Logs + + +
+ )} + showHeader={true} + data={results} + loading={loading} + emptyText={results ? 'No logs found' : 'Loading...'} + columns={columns} + pagination={{ + page, + total: Math.ceil((total || 0) / ITEMS_PER_PAGE), + onChange: this.handleChangePage, + }} + rowClassName={cx('align-top')} + expandable={{ + expandedRowRender: this.renderFullDescription, + expandRowByClick: true, + expandedRowKeys: expandedLogsKeys, + onExpandedRowsChange: this.handleExpandedRowsChange, + }} + /> +
); } diff --git a/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx b/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx index 4fa62c40..deff31b9 100644 --- a/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx +++ b/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, HorizontalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import LegacyNavHeading from 'navbar/LegacyNavHeading'; @@ -19,7 +18,6 @@ import OutgoingWebhookForm from 'containers/OutgoingWebhookForm/OutgoingWebhookF import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { ActionDTO } from 'models/action'; import { OutgoingWebhook } from 'models/outgoing_webhook/outgoing_webhook.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; @@ -111,54 +109,52 @@ class OutgoingWebhooks extends React.Component - - {() => ( - <> -
- ( -
- - Outgoing Webhooks - -
- - - - - -
+ + {() => ( + <> +
+ ( +
+ + Outgoing Webhooks + +
+ + + + +
- )} - rowKey="id" - columns={columns} - data={webhooks} - /> -
- {outgoingWebhookIdToEdit && ( - - )} - - )} - - +
+ )} + rowKey="id" + columns={columns} + data={webhooks} + /> +
+ {outgoingWebhookIdToEdit && ( + + )} + + )} + ); } diff --git a/grafana-plugin/src/pages/schedule/Schedule.tsx b/grafana-plugin/src/pages/schedule/Schedule.tsx index edb20db2..a1c428f5 100644 --- a/grafana-plugin/src/pages/schedule/Schedule.tsx +++ b/grafana-plugin/src/pages/schedule/Schedule.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, HorizontalGroup, VerticalGroup, IconButton, ToolbarButton, Icon, Modal } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; import { observer } from 'mobx-react'; @@ -20,7 +19,6 @@ import ScheduleICalSettings from 'containers/ScheduleIcalLink/ScheduleIcalLink'; import UsersTimezones from 'containers/UsersTimezones/UsersTimezones'; import { Schedule, ScheduleType, Shift } from 'models/schedule/schedule.types'; import { Timezone } from 'models/timezone/timezone.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; @@ -112,156 +110,154 @@ class SchedulePage extends React.Component shiftIdToShowOverridesForm; return ( - - - {() => ( - <> -
- -
- + + {() => ( + <> +
+ +
+ + + + + + + {schedule?.name} + + {schedule && } + + + {users && ( + + Current timezone: + + + )} - - - - - {schedule?.name} - - {schedule && } - - - {users && ( - - Current timezone: - - - )} - - {(schedule?.type === ScheduleType.Ical || schedule?.type === ScheduleType.Calendar) && ( - - )} - { - this.setState({ showEditForm: true }); - }} - /> - - - + + {(schedule?.type === ScheduleType.Ical || schedule?.type === ScheduleType.Calendar) && ( + + )} + { + this.setState({ showEditForm: true }); + }} + /> + + + + + + +
+
+ +
+ +
+
+ + + + + + + + + {startMoment.format('DD MMM')} - {startMoment.add(6, 'day').format('DD MMM')} +
-
- -
- -
-
- - - - - - - - - {startMoment.format('DD MMM')} - {startMoment.add(6, 'day').format('DD MMM')} - - - -
- - - -
- -
- {showEditForm && ( - { - this.setState({ showEditForm: false }); - }} - /> - )} - {showScheduleICalSettings && ( - this.setState({ showScheduleICalSettings: false })} - > - - - )} - - )} - - + + + +
+ +
+ {showEditForm && ( + { + this.setState({ showEditForm: false }); + }} + /> + )} + {showScheduleICalSettings && ( + this.setState({ showScheduleICalSettings: false })} + > + + + )} + + )} + ); } diff --git a/grafana-plugin/src/pages/schedules/Schedules.tsx b/grafana-plugin/src/pages/schedules/Schedules.tsx index e0750a76..f72e3fbc 100644 --- a/grafana-plugin/src/pages/schedules/Schedules.tsx +++ b/grafana-plugin/src/pages/schedules/Schedules.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button, HorizontalGroup, IconButton, LoadingPlaceholder, VerticalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import dayjs from 'dayjs'; import { debounce } from 'lodash-es'; @@ -25,7 +24,6 @@ import { WithPermissionControl } from 'containers/WithPermissionControl/WithPerm import { Schedule, ScheduleType } from 'models/schedule/schedule.types'; import { getSlackChannelName } from 'models/slack_channel/slack_channel.helpers'; import { Timezone } from 'models/timezone/timezone.types'; -import { pages } from 'pages'; import { getStartOfWeek } from 'pages/schedule/Schedule.helpers'; import { WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; @@ -135,7 +133,7 @@ class SchedulesPage extends React.Component + <>
@@ -192,7 +190,7 @@ class SchedulesPage extends React.Component )} - + ); } diff --git a/grafana-plugin/src/pages/settings/SettingsPage.tsx b/grafana-plugin/src/pages/settings/SettingsPage.tsx index 1b1ab2cf..817bf9ea 100644 --- a/grafana-plugin/src/pages/settings/SettingsPage.tsx +++ b/grafana-plugin/src/pages/settings/SettingsPage.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Tab, TabsBar } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; @@ -35,11 +34,7 @@ class SettingsPage extends React.Component }; render() { - return ( - -
{this.renderContent()}
-
- ); + return
{this.renderContent()}
; } renderContent() { diff --git a/grafana-plugin/src/pages/test/Test.tsx b/grafana-plugin/src/pages/test/Test.tsx index ce1a8fe0..2309c6c9 100644 --- a/grafana-plugin/src/pages/test/Test.tsx +++ b/grafana-plugin/src/pages/test/Test.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Button } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; @@ -17,13 +16,11 @@ const cx = cn.bind(styles); class Test extends React.Component { render() { return ( - -
- - {(disabled) => } - -
-
+
+ + {(disabled) => } + +
); } } diff --git a/grafana-plugin/src/pages/users/Users.tsx b/grafana-plugin/src/pages/users/Users.tsx index 924ad5a0..4717ecfc 100644 --- a/grafana-plugin/src/pages/users/Users.tsx +++ b/grafana-plugin/src/pages/users/Users.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Alert, Button, HorizontalGroup, Icon, VerticalGroup } from '@grafana/ui'; -import { PluginPage } from 'PluginPage'; import cn from 'classnames/bind'; import { debounce } from 'lodash-es'; import { observer } from 'mobx-react'; @@ -20,7 +19,6 @@ import UsersFilters from 'components/UsersFilters/UsersFilters'; import UserSettings from 'containers/UserSettings/UserSettings'; import { WithPermissionControl } from 'containers/WithPermissionControl/WithPermissionControl'; import { User as UserType } from 'models/user/user.types'; -import { pages } from 'pages'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; @@ -159,85 +157,83 @@ class Users extends React.Component { const { count, results } = userStore.getSearchResult(); return ( - - - {() => ( - <> -
-
-
-
-
- - Users - - - To manage permissions or add users, please visit{' '} - Grafana user management - -
+ + {() => ( + <> +
+
+
+
+
+ + Users + + + To manage permissions or add users, please visit{' '} + Grafana user management +
- - -
- {isUserActionAllowed(UserActions.UserSettingsRead) ? ( - <> -
- - -
- - - - ) : ( - - You don't have enough permissions to view other users because you are not Admin.{' '} - Click here to open your profile - - } - severity="info" - /> - )} + + +
- {userPkToEdit && } + {isUserActionAllowed(UserActions.UserSettingsRead) ? ( + <> +
+ + +
+ + + + ) : ( + + You don't have enough permissions to view other users because you are not Admin.{' '} + Click here to open your profile + + } + severity="info" + /> + )}
- - )} - - + {userPkToEdit && } +
+ + )} +
); } diff --git a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx index 3eaa6d5d..16920d12 100644 --- a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx +++ b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx @@ -23,6 +23,7 @@ import { routes } from 'pages/routes'; import { rootStore } from 'state'; import { useStore } from 'state/useStore'; import { isUserActionAllowed } from 'utils/authorization'; +import { DEFAULT_PAGE } from 'utils/consts'; import { useQueryParams, useQueryPath } from 'utils/hooks'; dayjs.extend(utc); @@ -49,7 +50,7 @@ export const GrafanaPluginRootPage = (props: AppRootProps) => ( export const Root = observer((props: AppRootProps) => { const [didFinishLoading, setDidFinishLoading] = useState(false); const queryParams = useQueryParams(); - const page = queryParams.get('page'); + const page = queryParams.get('page') || DEFAULT_PAGE; const path = useQueryPath(); // Required to support grafana instances that use a custom `root_url`. @@ -93,18 +94,15 @@ export const Root = observer((props: AppRootProps) => { {!isTopNavbar() && ( <>
- + )}
{userHasAccess ? ( diff --git a/grafana-plugin/src/style/utils.css b/grafana-plugin/src/style/utils.css index 2a35481f..314abf57 100644 --- a/grafana-plugin/src/style/utils.css +++ b/grafana-plugin/src/style/utils.css @@ -2,6 +2,10 @@ position: relative; } +.u-overflow-x-auto { + overflow-x: auto; +} + .u-pull-right { margin-left: auto; } @@ -18,6 +22,10 @@ width: 100%; } +.u-height-100 { + height: 100%; +} + .u-flex { display: flex; flex-direction: row; @@ -28,6 +36,10 @@ align-items: center; } +.u-flex-grow-1 { + flex-grow: 1; +} + .u-align-items-center { align-items: center; } diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/utils/consts.ts index 3a5a2f79..5d66dc2d 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/utils/consts.ts @@ -7,3 +7,5 @@ export const GRAFANA_LICENSE_OSS = 'OpenSource'; // Reusable breakpoint sizes export const BREAKPOINT_TABS = 1024; + +export const DEFAULT_PAGE = 'incidents';