From 45e7f2c78c0a8238e6801cba678c08b607d9e9ca Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 22 Aug 2023 17:29:21 +0300 Subject: [PATCH 01/22] first changes to fix integrations navigation --- .../src/pages/integrations/Integrations.tsx | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index d1b9752e08..5c84165000 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -62,20 +62,23 @@ interface IntegrationsProps extends WithStoreProps, PageProps, RouteComponentPro @observer class Integrations extends React.Component { - state: IntegrationsState = { - integrationsFilters: { searchTerm: '' }, - errorData: initErrorDataState(), - page: 1, - confirmationModal: undefined, - }; + constructor(props: IntegrationsProps) { + super(props); - async componentDidMount() { const { query: { p }, - } = this.props; + } = props; - this.setState({ page: p ? Number(p) : 1 }, this.update); + this.state = { + integrationsFilters: { searchTerm: '' }, + errorData: initErrorDataState(), + page: p ? Number(p) : 1, + confirmationModal: undefined, + }; + } + async componentDidMount() { + this.update(); this.parseQueryParams(); } @@ -119,6 +122,7 @@ class Integrations extends React.Component update = () => { const { store } = this.props; const { page, integrationsFilters } = this.state; + LocationHelper.update({ p: page }, 'partial'); return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page); @@ -493,11 +497,17 @@ class Integrations extends React.Component applyFilters = () => { const { store } = this.props; const { alertReceiveChannelStore } = store; - const { integrationsFilters } = this.state; + const { integrationsFilters, page } = this.state; + + // by default it has the the page param (`p`) within + const query = { ...this.props.query }; + delete query.p; + + const newUpdatedPage = Object.keys(query).length === 0 ? page : 1; - return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters).then(() => { - this.setState({ page: 1 }); - LocationHelper.update({ p: 1 }, 'partial'); + return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, newUpdatedPage).then(() => { + this.setState({ page: newUpdatedPage }); + LocationHelper.update({ p: newUpdatedPage }, 'partial'); }); }; From a8df7b6edc40340cc3ed9ccad2421eb7b69ed0c6 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 23 Aug 2023 14:50:09 +0300 Subject: [PATCH 02/22] tweaks & optimizations --- ...IntegrationCollapsibleTreeView.module.scss | 4 ++ .../IntegrationCollapsibleTreeView.tsx | 3 +- .../IntegrationContactPoint.tsx | 3 +- .../alert_receive_channel.ts | 21 ++++++- grafana-plugin/src/models/user/user.ts | 56 +++++++++---------- .../pages/integration/Integration.helper.ts | 2 + .../src/pages/integration/Integration.tsx | 34 ++++++----- .../src/pages/integrations/Integrations.tsx | 14 ++++- 8 files changed, 87 insertions(+), 50 deletions(-) diff --git a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.module.scss b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.module.scss index 9910b41891..96b652bdcf 100644 --- a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.module.scss +++ b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.module.scss @@ -27,6 +27,10 @@ .integrationTree__group { position: relative; margin-bottom: 12px; + + &--hidden { + display: none; + } } .integrationTree__icon { diff --git a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx index 0375c96382..321766512d 100644 --- a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx +++ b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx @@ -9,6 +9,7 @@ import styles from './IntegrationCollapsibleTreeView.module.scss'; const cx = cn.bind(styles); export interface IntegrationCollapsibleItem { + isHidden?: boolean; customIcon?: IconName; canHoverIcon: boolean; collapsedView: (toggle?: () => void) => React.ReactNode; // needs toggle param for toggling on click @@ -107,7 +108,7 @@ const IntegrationCollapsibleTreeItem: React.FC<{ const iconOnClickFn = !item.isCollapsible ? undefined : onClick; return ( -
+
{item.canHoverIcon ? ( diff --git a/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx b/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx index d7653c7736..c3b245d1dc 100644 --- a/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx +++ b/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx @@ -38,8 +38,7 @@ const IntegrationContactPoint: React.FC<{ }> = observer(({ id }) => { const { alertReceiveChannelStore } = useStore(); const contactPoints = alertReceiveChannelStore.connectedContactPoints[id]; - const warnings = contactPoints.filter((cp) => !cp.notificationConnected); - + const warnings = contactPoints?.filter((cp) => !cp.notificationConnected); const [ { isLoading, diff --git a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts index 927922dfe5..4ec690739b 100644 --- a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts +++ b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts @@ -131,7 +131,7 @@ export class AlertReceiveChannelStore extends BaseStore { return results; } - async updatePaginatedItems(query: any = '', page = 1) { + async updatePaginatedItems(query: any = '', page = 1, updateCounters: boolean = false) { const filters = typeof query === 'string' ? { search: query } : query; const { count, results } = await makeRequest(this.path, { params: { ...filters, page } }); @@ -153,7 +153,9 @@ export class AlertReceiveChannelStore extends BaseStore { results: results.map((item: AlertReceiveChannel) => item.id), }; - this.updateCounters(); + if (updateCounters) { + this.updateCounters(); + } return results; } @@ -499,6 +501,21 @@ export class AlertReceiveChannelStore extends BaseStore { this.counters = counters; } + async updateCountersForIntegration(id: AlertReceiveChannel['id']): Promise { + const counters = await makeRequest(`${this.path}${id}/counters`, { + method: 'GET', + }); + + this.counters = { + ...this.counters, + [id]: { + ...counters[id], + }, + }; + + return counters; + } + startMaintenanceMode = (id: AlertReceiveChannel['id'], mode: MaintenanceMode, duration: number): Promise => makeRequest(`${this.path}${id}/start_maintenance/`, { method: 'POST', diff --git a/grafana-plugin/src/models/user/user.ts b/grafana-plugin/src/models/user/user.ts index 11fbe7fb24..5586f496d9 100644 --- a/grafana-plugin/src/models/user/user.ts +++ b/grafana-plugin/src/models/user/user.ts @@ -111,35 +111,35 @@ export class UserStore extends BaseStore { } @action - async updateItems(f: any = { searchTerm: '' }, page = 1) { - return new Promise(async (resolve) => { - const filters = typeof f === 'string' ? { searchTerm: f } : f; // for GSelect compatibility - const { searchTerm: search } = filters; - const { count, results } = await makeRequest(this.path, { - params: { search, page }, - }); - - this.items = { - ...this.items, - ...results.reduce( - (acc: { [key: number]: User }, item: User) => ({ - ...acc, - [item.pk]: { - ...item, - timezone: getTimezone(item), - }, - }), - {} - ), - }; - - this.searchResult = { - count, - results: results.map((item: User) => item.pk), - }; - - resolve(); + async updateItems(f: any = { searchTerm: '' }, page = 1): Promise { + const filters = typeof f === 'string' ? { searchTerm: f } : f; // for GSelect compatibility + const { searchTerm: search } = filters; + const response = await makeRequest(this.path, { + params: { search, page }, }); + + const { count, results } = response; + + this.items = { + ...this.items, + ...results.reduce( + (acc: { [key: number]: User }, item: User) => ({ + ...acc, + [item.pk]: { + ...item, + timezone: getTimezone(item), + }, + }), + {} + ), + }; + + this.searchResult = { + count, + results: results.map((item: User) => item.pk), + }; + + return response; } getSearchResult() { diff --git a/grafana-plugin/src/pages/integration/Integration.helper.ts b/grafana-plugin/src/pages/integration/Integration.helper.ts index 0766c71ea4..1fdf3bebd0 100644 --- a/grafana-plugin/src/pages/integration/Integration.helper.ts +++ b/grafana-plugin/src/pages/integration/Integration.helper.ts @@ -15,6 +15,8 @@ import { MAX_CHARACTERS_COUNT, TEXTAREA_ROWS_COUNT } from './IntegrationCommon.c const IntegrationHelper = { isGrafanaAlerting: (alertReceiveChannel: AlertReceiveChannel | string) => { + if (!alertReceiveChannel) return false; + if (typeof alertReceiveChannel === 'string') { return alertReceiveChannel === 'grafana_alerting'; } diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index 53a05aed3b..983df76678 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -112,7 +112,7 @@ class Integration extends React.Component { this.openEditTemplateModal(query.template, query.routeId && query.routeId); } - await this.loadIntegration(); + await this.loadData(); } render() { @@ -200,7 +200,7 @@ class Integration extends React.Component { {this.renderDescriptionMaybe(alertReceiveChannel)} {/* MobX seems to have issues updating contact points if we don't reference it here */} - {!contactPoints?.length && this.renderContactPointsWarningMaybe(alertReceiveChannel)} + {contactPoints && contactPoints.length === 0 && this.renderContactPointsWarningMaybe(alertReceiveChannel)}
{ } = this.props; const alertReceiveChannel = alertReceiveChannelStore.items[id]; + const contactPoints = alertReceiveChannelStore.connectedContactPoints[id]; return [ IntegrationHelper.isGrafanaAlerting(alertReceiveChannel) && { + isHidden: contactPoints === null || contactPoints === undefined, isCollapsible: false, customIcon: 'grafana', canHoverIcon: false, @@ -655,7 +657,7 @@ class Integration extends React.Component { alertReceiveChannelStore.deleteAlertReceiveChannel(id).then(() => history.push(`${PLUGIN_ROOT}/integrations/`)); }; - async loadIntegration() { + async loadData() { const { store, store: { alertReceiveChannelStore }, @@ -668,12 +670,9 @@ class Integration extends React.Component { const promises = []; if (!alertReceiveChannelStore.items[id]) { - // See what happens if the request fails - promises.push(alertReceiveChannelStore.loadItem(id)); - } - - if (!alertReceiveChannelStore.counters[id]) { - promises.push(alertReceiveChannelStore.updateCounters()); + promises.push(alertReceiveChannelStore.loadItem(id).then(() => this.loadContactPointsMaybe(id))); + } else { + promises.push(this.loadContactPointsMaybe(id)); } if (!alertReceiveChannelStore.channelFilterIds[id]) { @@ -681,12 +680,8 @@ class Integration extends React.Component { } promises.push(alertReceiveChannelStore.updateTemplates(id)); - promises.push(IntegrationHelper.fetchChatOps(store)); - - // skip checking for grafana alerting so that we don't wait for the first request to complete - // at the cost of getting a failed network request for all other types other than alerting - promises.push(alertReceiveChannelStore.updateConnectedContactPoints(id).catch(noop)); + promises.push(alertReceiveChannelStore.updateCountersForIntegration(id)); await Promise.all(promises) .catch(() => { @@ -697,6 +692,17 @@ class Integration extends React.Component { }) .finally(() => this.setState({ isLoading: false })); } + + async loadContactPointsMaybe(id: AlertReceiveChannel['id']) { + const { alertReceiveChannelStore } = this.props.store; + + if (IntegrationHelper.isGrafanaAlerting(alertReceiveChannelStore.items[id])) { + // this will be delayed and not awaitable so that we don't delay the whole page load + return await alertReceiveChannelStore.updateConnectedContactPoints(id).catch(noop); + } + + return Promise.resolve(); + } } interface IntegrationActionsProps { diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 5c84165000..4702895e3a 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -78,7 +78,14 @@ class Integrations extends React.Component } async componentDidMount() { - this.update(); + // TODO: figure out parsing filters + // let { filters, values } = parseFilters( + // { ...this.props.query, ...this.props.store.filtersStore.globalValues }, + // filterOptions, + // this.props.query + // ); + + this.update(true); this.parseQueryParams(); } @@ -119,13 +126,13 @@ class Integrations extends React.Component } }; - update = () => { + update = (isOnMount: boolean = false) => { const { store } = this.props; const { page, integrationsFilters } = this.state; LocationHelper.update({ p: page }, 'partial'); - return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page); + return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page, isOnMount); }; render() { @@ -182,6 +189,7 @@ class Integrations extends React.Component />
+ {alertReceiveChannelId && ( { From 1ab9996fc16037b1b64f018fa98654abbbd6b468 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 23 Aug 2023 14:54:00 +0300 Subject: [PATCH 03/22] fix --- .../src/models/alert_receive_channel/alert_receive_channel.ts | 2 +- grafana-plugin/src/pages/integration/Integration.helper.ts | 4 +++- grafana-plugin/src/pages/integrations/Integrations.tsx | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts index 4ec690739b..f1e3595e3b 100644 --- a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts +++ b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts @@ -131,7 +131,7 @@ export class AlertReceiveChannelStore extends BaseStore { return results; } - async updatePaginatedItems(query: any = '', page = 1, updateCounters: boolean = false) { + async updatePaginatedItems(query: any = '', page = 1, updateCounters = false) { const filters = typeof query === 'string' ? { search: query } : query; const { count, results } = await makeRequest(this.path, { params: { ...filters, page } }); diff --git a/grafana-plugin/src/pages/integration/Integration.helper.ts b/grafana-plugin/src/pages/integration/Integration.helper.ts index 1fdf3bebd0..f4254c4b5e 100644 --- a/grafana-plugin/src/pages/integration/Integration.helper.ts +++ b/grafana-plugin/src/pages/integration/Integration.helper.ts @@ -15,7 +15,9 @@ import { MAX_CHARACTERS_COUNT, TEXTAREA_ROWS_COUNT } from './IntegrationCommon.c const IntegrationHelper = { isGrafanaAlerting: (alertReceiveChannel: AlertReceiveChannel | string) => { - if (!alertReceiveChannel) return false; + if (!alertReceiveChannel) { + return false; + } if (typeof alertReceiveChannel === 'string') { return alertReceiveChannel === 'grafana_alerting'; diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 100b3ebdb7..89b0ad1e92 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -126,7 +126,7 @@ class Integrations extends React.Component } }; - update = (isOnMount: boolean = false) => { + update = (isOnMount = false) => { const { store } = this.props; const { page, integrationsFilters } = this.state; From 5ec0607cd9d832ab22090dc710c6d222ac7347ac Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 23 Aug 2023 16:36:16 +0300 Subject: [PATCH 04/22] fix for reapplying the filters --- .../RemoteFilters/RemoteFilters.tsx | 6 +++++- .../src/pages/integrations/Integrations.tsx | 21 ++++--------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx index 6f8b92c6c5..52bdab7ce3 100644 --- a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx +++ b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx @@ -378,7 +378,11 @@ class RemoteFilters extends Component { } LocationHelper.update({ ...values }, 'partial'); - onChange(values, isOnMount); + + if (!isOnMount) { + // don't trigger change on the first render + onChange(values, isOnMount); + } }; debouncedOnChange = debounce(this.onChange, 500); diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 89b0ad1e92..48979ea789 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -78,13 +78,6 @@ class Integrations extends React.Component } async componentDidMount() { - // TODO: figure out parsing filters - // let { filters, values } = parseFilters( - // { ...this.props.query, ...this.props.store.filtersStore.globalValues }, - // filterOptions, - // this.props.query - // ); - this.update(true); this.parseQueryParams(); } @@ -505,17 +498,11 @@ class Integrations extends React.Component applyFilters = () => { const { store } = this.props; const { alertReceiveChannelStore } = store; - const { integrationsFilters, page } = this.state; - - // by default it has the the page param (`p`) within - const query = { ...this.props.query }; - delete query.p; - - const newUpdatedPage = Object.keys(query).length === 0 ? page : 1; + const { integrationsFilters } = this.state; - return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, newUpdatedPage).then(() => { - this.setState({ page: newUpdatedPage }); - LocationHelper.update({ p: newUpdatedPage }, 'partial'); + return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, 1).then(() => { + this.setState({ page: 1 }); + LocationHelper.update({ p: 1 }, 'partial'); }); }; From 0445b65349f0725f513f65c3b51fb55a404ebf1c Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 23 Aug 2023 16:54:12 +0300 Subject: [PATCH 05/22] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5754ca9b74..8a5ec1ce82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Public API for actions now wraps webhooks @mderynck ([#2790](https://github.com/grafana/oncall/pull/2790)) - Allow mobile app to access status endpoint @mderynck ([#2791](https://github.com/grafana/oncall/pull/2791)) +- Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869)) ## v1.3.26 (2023-08-22) From 6f1b6cd87d777055c2df6556625039f0869e78c3 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Mon, 28 Aug 2023 15:15:00 +0300 Subject: [PATCH 06/22] fixed table filters update --- .../IntegrationContactPoint.tsx | 1 + .../CollapsedIntegrationRouteDisplay.tsx | 36 ++++++++----- .../ExpandedIntegrationRouteDisplay.tsx | 14 +++-- .../RemoteFilters/RemoteFilters.tsx | 5 +- grafana-plugin/src/models/filters/filters.ts | 5 ++ .../src/pages/incidents/Incidents.tsx | 19 ++++--- .../src/pages/integration/Integration.tsx | 54 +++++++++++-------- .../src/pages/integrations/Integrations.tsx | 27 ++++------ 8 files changed, 96 insertions(+), 65 deletions(-) diff --git a/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx b/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx index c3b245d1dc..c386482ffb 100644 --- a/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx +++ b/grafana-plugin/src/components/IntegrationContactPoint/IntegrationContactPoint.tsx @@ -87,6 +87,7 @@ const IntegrationContactPoint: React.FC<{
void; openEditTemplateModal: (templateName: string | string[], channelFilterId?: ChannelFilter['id']) => void; onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void; + onRouteDelete: (routeId: string) => void; } const CollapsedIntegrationRouteDisplay: React.FC = observer( - ({ channelFilterId, alertReceiveChannelId, routeIndex, toggle, openEditTemplateModal, onEditRegexpTemplate }) => { + ({ + channelFilterId, + alertReceiveChannelId, + routeIndex, + toggle, + openEditTemplateModal, + onEditRegexpTemplate, + onRouteDelete, + }) => { const store = useStore(); const { escalationChainStore, alertReceiveChannelStore } = store; const [routeIdForDeletion, setRouteIdForDeletion] = useState(undefined); const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId]; + + const routeWording = useMemo(() => { + return CommonIntegrationHelper.getRouteConditionWording( + alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId], + routeIndex + ); + }, [routeIndex, alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]]); + + console.log({ routeIndex, routeWording }); + if (!channelFilter) { return null; } const escalationChain = escalationChainStore.items[channelFilter.escalation_chain]; - const routeWording = CommonIntegrationHelper.getRouteConditionWording( - alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId], - routeIndex - ); const chatOpsAvailableChannels = IntegrationHelper.getChatOpsChannels(channelFilter, store).filter( (channel) => channel ); @@ -59,10 +73,7 @@ const CollapsedIntegrationRouteDisplay: React.FC void; onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void; + onRouteDelete: (routeId: string) => void; } interface ExpandedIntegrationRouteDisplayState { @@ -59,7 +60,15 @@ interface ExpandedIntegrationRouteDisplayState { } const ExpandedIntegrationRouteDisplay: React.FC = observer( - ({ alertReceiveChannelId, channelFilterId, templates, routeIndex, openEditTemplateModal, onEditRegexpTemplate }) => { + ({ + alertReceiveChannelId, + channelFilterId, + templates, + routeIndex, + openEditTemplateModal, + onEditRegexpTemplate, + onRouteDelete, + }) => { const store = useStore(); const { telegramChannelStore, @@ -278,8 +287,7 @@ const ExpandedIntegrationRouteDisplay: React.FC { LocationHelper.update({ ...values }, 'partial'); - if (!isOnMount) { - // don't trigger change on the first render - onChange(values, isOnMount); - } + onChange(values, isOnMount); }; debouncedOnChange = debounce(this.onChange, 500); diff --git a/grafana-plugin/src/models/filters/filters.ts b/grafana-plugin/src/models/filters/filters.ts index eaca60d7c8..8941a5481c 100644 --- a/grafana-plugin/src/models/filters/filters.ts +++ b/grafana-plugin/src/models/filters/filters.ts @@ -17,6 +17,9 @@ export class FiltersStore extends BaseStore { @observable.shallow public values: { [page: string]: FiltersValues } = {}; + @observable + public isLoading = true; + private _globalValues: FiltersValues = {}; constructor(rootStore: RootStore) { @@ -52,6 +55,8 @@ export class FiltersStore extends BaseStore { [page]: result, }; + this.isLoading = false; + return result; } diff --git a/grafana-plugin/src/pages/incidents/Incidents.tsx b/grafana-plugin/src/pages/incidents/Incidents.tsx index f8ed88e3f4..c975d2fd70 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.tsx +++ b/grafana-plugin/src/pages/incidents/Incidents.tsx @@ -434,12 +434,15 @@ class Incidents extends React.Component renderTable() { const { selectedIncidentIds, pagination } = this.state; - const { store } = this.props; - const { alertGroupsLoading } = store.alertGroupStore; + const { + store, + store: { alertGroupStore, filtersStore }, + } = this.props; - const results = store.alertGroupStore.getAlertSearchResult('default'); - const prev = get(store.alertGroupStore.alertsSearchResult, `default.prev`); - const next = get(store.alertGroupStore.alertsSearchResult, `default.next`); + const results = alertGroupStore.getAlertSearchResult('default'); + const prev = get(alertGroupStore.alertsSearchResult, `default.prev`); + const next = get(alertGroupStore.alertsSearchResult, `default.next`); + const isLoading = alertGroupStore.alertGroupsLoading || filtersStore.isLoading; if (results && !results.length) { return ( @@ -517,8 +520,8 @@ class Incidents extends React.Component
{this.renderBulkActions()}
{ } getConfigForTreeComponent(id: string, templates: AlertTemplatesDTO[]) { - const { - store: { alertReceiveChannelStore }, - } = this.props; + // const { + // store: { alertReceiveChannelStore }, + // } = this.props; - const alertReceiveChannel = alertReceiveChannelStore.items[id]; - const contactPoints = alertReceiveChannelStore.connectedContactPoints[id]; + // const alertReceiveChannel = alertReceiveChannelStore.items[id]; + // const contactPoints = alertReceiveChannelStore.connectedContactPoints[id]; return [ - IntegrationHelper.isGrafanaAlerting(alertReceiveChannel) && { - isHidden: contactPoints === null || contactPoints === undefined, - isCollapsible: false, - customIcon: 'grafana', - canHoverIcon: false, - collapsedView: null, - expandedView: () => , - }, + // IntegrationHelper.isGrafanaAlerting(alertReceiveChannel) && { + // isHidden: contactPoints === null || contactPoints === undefined, + // isCollapsible: false, + // customIcon: 'grafana', + // canHoverIcon: false, + // collapsedView: null, + // expandedView: () => , + // }, { isCollapsible: false, customIcon: 'plug', @@ -484,7 +484,7 @@ class Integration extends React.Component { }; handleAddNewRoute = () => { - const { alertReceiveChannelStore, escalationPolicyStore } = this.props.store; + const { alertReceiveChannelStore } = this.props.store; const { params: { id }, } = this.props.match; @@ -501,12 +501,16 @@ class Integration extends React.Component { filtering_term_type: 1, // non-regex }) .then(async (channelFilter: ChannelFilter) => { - this.setState((prevState) => ({ - isAddingRoute: false, - openRoutes: prevState.openRoutes.concat(channelFilter.id), - })); - await alertReceiveChannelStore.updateChannelFilters(id, true); - await escalationPolicyStore.updateEscalationPolicies(channelFilter.escalation_chain); + await alertReceiveChannelStore.updateChannelFilters(id); + + this.setState( + (prevState) => ({ + isAddingRoute: false, + openRoutes: prevState.openRoutes.concat(channelFilter.id), + }), + () => this.forceUpdate() + ); + openNotification('A new route has been added'); }) .catch((err) => { @@ -532,6 +536,12 @@ class Integration extends React.Component { const templates = alertReceiveChannelStore.templates[id]; const channelFilterIds = alertReceiveChannelStore.channelFilterIds[id]; + const onRouteDelete = async (routeId: string) => { + await alertReceiveChannelStore.deleteChannelFilter(routeId); + // this.forceUpdate(); + openNotification('Route has been deleted'); + }; + return channelFilterIds.map( (channelFilterId: ChannelFilter['id'], routeIndex: number) => ({ @@ -554,6 +564,7 @@ class Integration extends React.Component { toggle={toggle} openEditTemplateModal={this.openEditTemplateModal} onEditRegexpTemplate={this.handleEditRegexpRouteTemplate} + onRouteDelete={onRouteDelete} /> ), expandedView: () => ( @@ -564,6 +575,7 @@ class Integration extends React.Component { templates={templates} openEditTemplateModal={this.openEditTemplateModal} onEditRegexpTemplate={this.handleEditRegexpRouteTemplate} + onRouteDelete={onRouteDelete} /> ), } as IntegrationCollapsibleItem) diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 48979ea789..457d135333 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -167,7 +167,8 @@ class Integrations extends React.Component onChange={this.handleIntegrationsFiltersChange} /> ); } - renderNotFound() { - return ( -
- Not found -
- ); - } - renderName = (item: AlertReceiveChannel) => { const { query: { p }, @@ -491,18 +484,20 @@ class Integrations extends React.Component this.setState({ confirmationModal: undefined }); }; - handleIntegrationsFiltersChange = (integrationsFilters: Filters) => { - this.setState({ integrationsFilters }, () => this.debouncedUpdateIntegrations()); + handleIntegrationsFiltersChange = (integrationsFilters: Filters, isOnMount: boolean) => { + this.setState({ integrationsFilters }, () => this.debouncedUpdateIntegrations(isOnMount)); }; - applyFilters = () => { + applyFilters = async (isOnMount: boolean) => { const { store } = this.props; const { alertReceiveChannelStore } = store; - const { integrationsFilters } = this.state; + const { integrationsFilters, page } = this.state; + + const newPage = isOnMount ? page : 1; - return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, 1).then(() => { - this.setState({ page: 1 }); - LocationHelper.update({ p: 1 }, 'partial'); + return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, newPage).then(() => { + this.setState({ page: newPage }); + LocationHelper.update({ p: newPage }, 'partial'); }); }; From d4b01997df10fdfd60b156b0f6d73b485825e93a Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Mon, 28 Aug 2023 15:35:04 +0300 Subject: [PATCH 07/22] param typing --- .../src/components/CursorPagination/CursorPagination.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx b/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx index 907f6a74a4..140a226dad 100644 --- a/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx +++ b/grafana-plugin/src/components/CursorPagination/CursorPagination.tsx @@ -24,7 +24,7 @@ const CursorPagination: FC = (props) => { setDisabled(false); }, [prev, next]); - const onChangeItemsPerPageCallback = useCallback((option) => { + const onChangeItemsPerPageCallback = useCallback((option: SelectableValue) => { setDisabled(true); onChangeItemsPerPage(option.value); }, []); From 2299146233429a639b8e96d10665aaed1c16a094 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Mon, 28 Aug 2023 15:36:47 +0300 Subject: [PATCH 08/22] review --- .../CollapsedIntegrationRouteDisplay.tsx | 2 -- grafana-plugin/src/pages/integration/Integration.tsx | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx index 0c4b0fbeb1..320e0b97b6 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx @@ -51,8 +51,6 @@ const CollapsedIntegrationRouteDisplay: React.FC { const promises = []; if (!alertReceiveChannelStore.items[id]) { - promises.push(alertReceiveChannelStore.loadItem(id).then(() => this.loadContactPointsMaybe(id))); + promises.push(alertReceiveChannelStore.loadItem(id).then(() => this.loadExtraDataMaybe(id))); } else { - promises.push(this.loadContactPointsMaybe(id)); + promises.push(this.loadExtraDataMaybe(id)); } if (!alertReceiveChannelStore.channelFilterIds[id]) { @@ -705,12 +705,12 @@ class Integration extends React.Component { .finally(() => this.setState({ isLoading: false })); } - async loadContactPointsMaybe(id: AlertReceiveChannel['id']) { + async loadExtraDataMaybe(id: AlertReceiveChannel['id']) { const { alertReceiveChannelStore } = this.props.store; if (IntegrationHelper.isGrafanaAlerting(alertReceiveChannelStore.items[id])) { // this will be delayed and not awaitable so that we don't delay the whole page load - return await alertReceiveChannelStore.updateConnectedContactPoints(id).catch(noop); + return await alertReceiveChannelStore.updateConnectedContactPoints(id); } return Promise.resolve(); From 988687a2f7e053194e6e0f2b899870ab8a8c6035 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Mon, 28 Aug 2023 15:57:43 +0300 Subject: [PATCH 09/22] fix passed page param in table + build fix --- grafana-plugin/src/pages/integration/Integration.tsx | 2 +- grafana-plugin/src/pages/integrations/Integrations.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index 52c4da4c80..600b5fcf8d 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -13,7 +13,7 @@ import { Alert, } from '@grafana/ui'; import cn from 'classnames/bind'; -import { get, noop } from 'lodash-es'; +import { get } from 'lodash-es'; import { observer } from 'mobx-react'; import CopyToClipboard from 'react-copy-to-clipboard'; import Emoji from 'react-emoji-render'; diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 457d135333..3b3e4d9cc2 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -216,12 +216,10 @@ class Integrations extends React.Component } renderName = (item: AlertReceiveChannel) => { - const { - query: { p }, - } = this.props; + const { page } = this.state; return ( - + Date: Tue, 29 Aug 2023 11:27:09 +0300 Subject: [PATCH 10/22] fixed Integrations page losing its filters, fixed initial page load flickering --- .../src/pages/integration/Integration.tsx | 4 ++-- .../src/pages/integrations/Integrations.tsx | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index 600b5fcf8d..7aa51c1d27 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -127,7 +127,7 @@ class Integration extends React.Component { } = this.state; const { store: { alertReceiveChannelStore }, - query: { p }, + query, match: { params: { id }, }, @@ -181,7 +181,7 @@ class Integration extends React.Component { )}
- +

diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 3b3e4d9cc2..59dd2209ac 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -78,7 +78,6 @@ class Integrations extends React.Component } async componentDidMount() { - this.update(true); this.parseQueryParams(); } @@ -119,13 +118,13 @@ class Integrations extends React.Component } }; - update = (isOnMount = false) => { + update = () => { const { store } = this.props; const { page, integrationsFilters } = this.state; LocationHelper.update({ p: page }, 'partial'); - return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page, isOnMount); + return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page, false); }; render() { @@ -216,10 +215,16 @@ class Integrations extends React.Component } renderName = (item: AlertReceiveChannel) => { - const { page } = this.state; + const { query } = this.props; return ( - + const { alertReceiveChannelStore } = store; const { integrationsFilters, page } = this.state; + console.log('Apply filters wtf'); + const newPage = isOnMount ? page : 1; return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, newPage).then(() => { From 1a9278c31c0778519a3004e4b7324b4613283060 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 29 Aug 2023 11:52:50 +0300 Subject: [PATCH 11/22] removed maintenance page, updated changelog --- CHANGELOG.md | 5 ++- .../pages/maintenance/Maintenance.module.css | 16 -------- .../src/pages/maintenance/Maintenance.tsx | 37 ------------------- grafana-plugin/src/pages/routes.tsx | 5 --- .../src/plugin/GrafanaPluginRootPage.tsx | 4 -- 5 files changed, 4 insertions(+), 63 deletions(-) delete mode 100644 grafana-plugin/src/pages/maintenance/Maintenance.module.css delete mode 100644 grafana-plugin/src/pages/maintenance/Maintenance.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c14011d0b..b98e4eec67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Address bug when a Shift Swap Request is accepted either via the web or mobile UI, and the Slack message is not updated to reflect the latest state by @joeyorlando ([#2886](https://github.com/grafana/oncall/pull/2886)) +### Changed + +- Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869)) + ## v1.3.27 (2023-08-25) ### Added @@ -26,7 +30,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow mobile app to access status endpoint @mderynck ([#2791](https://github.com/grafana/oncall/pull/2791)) - Enable shifts export endpoint for all schedule types ([#2863](https://github.com/grafana/oncall/pull/2863)) - Use priority field to track primary/overrides calendar in schedule iCal export ([#2871](https://github.com/grafana/oncall/pull/2871)) -- Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869)) ### Fixed diff --git a/grafana-plugin/src/pages/maintenance/Maintenance.module.css b/grafana-plugin/src/pages/maintenance/Maintenance.module.css deleted file mode 100644 index af82596401..0000000000 --- a/grafana-plugin/src/pages/maintenance/Maintenance.module.css +++ /dev/null @@ -1,16 +0,0 @@ -.select { - width: 400px; -} - -.header { - display: flex; - justify-content: space-between; -} - -.title { - margin-bottom: var(--title-marginBottom); -} - -.info-box { - width: 100%; -} diff --git a/grafana-plugin/src/pages/maintenance/Maintenance.tsx b/grafana-plugin/src/pages/maintenance/Maintenance.tsx deleted file mode 100644 index a90d1561f4..0000000000 --- a/grafana-plugin/src/pages/maintenance/Maintenance.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; - -import { Alert } from '@grafana/ui'; -import cn from 'classnames/bind'; -import { observer } from 'mobx-react'; - -import PluginLink from 'components/PluginLink/PluginLink'; - -import styles from './Maintenance.module.css'; - -const cx = cn.bind(styles); - -interface MaintenancePageProps {} - -@observer -class MaintenancePage extends React.Component { - render() { - return ( - <> - - Maintenance mode is now controlled at the{' '} - Integration level. This page will soon be - removed. - - } - /> - - ); - } -} - -export default MaintenancePage; diff --git a/grafana-plugin/src/pages/routes.tsx b/grafana-plugin/src/pages/routes.tsx index e6b1480ff0..a224529dc5 100644 --- a/grafana-plugin/src/pages/routes.tsx +++ b/grafana-plugin/src/pages/routes.tsx @@ -1,7 +1,6 @@ import EscalationsChainsPage from 'pages/escalation-chains/EscalationChains'; import IncidentPage from 'pages/incident/Incident'; import IncidentsPage from 'pages/incidents/Incidents'; -import MaintenancePage from 'pages/maintenance/Maintenance'; import OutgoingWebhooks from 'pages/outgoing_webhooks/OutgoingWebhooks'; import SchedulePage from 'pages/schedule/Schedule'; import SchedulesPage from 'pages/schedules/Schedules'; @@ -55,10 +54,6 @@ export const routes: { [id: string]: NavRoute } = [ component: OutgoingWebhooks, id: 'outgoing_webhooks', }, - { - component: MaintenancePage, - id: 'maintenance', - }, { component: SettingsPage, id: 'settings', diff --git a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx index 59ede18466..844f697e2c 100644 --- a/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx +++ b/grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx @@ -27,7 +27,6 @@ import Incident from 'pages/incident/Incident'; import Incidents from 'pages/incidents/Incidents'; import Integration from 'pages/integration/Integration'; import Integrations from 'pages/integrations/Integrations'; -import Maintenance from 'pages/maintenance/Maintenance'; import OutgoingWebhooks from 'pages/outgoing_webhooks/OutgoingWebhooks'; import Schedule from 'pages/schedule/Schedule'; import Schedules from 'pages/schedules/Schedules'; @@ -157,9 +156,6 @@ export const Root = observer((props: AppRootProps) => { - - - From 8f402367404ec48ce180f06fe16f1d525247ecb8 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 29 Aug 2023 11:54:38 +0300 Subject: [PATCH 12/22] maintenance breadcrumb removal --- grafana-plugin/src/pages/index.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/grafana-plugin/src/pages/index.tsx b/grafana-plugin/src/pages/index.tsx index 3271b2a019..ca3639640f 100644 --- a/grafana-plugin/src/pages/index.tsx +++ b/grafana-plugin/src/pages/index.tsx @@ -97,14 +97,6 @@ export const pages: { [id: string]: PageDefinition } = [ hideFromTabs: isTopNavbar(), action: UserActions.ChatOpsRead, }, - { - icon: 'wrench', - id: 'maintenance', - text: 'Maintenance', - hideFromBreadcrumbs: true, - path: getPath('maintenance'), - action: UserActions.MaintenanceRead, - }, { icon: 'cog', id: 'settings', @@ -169,7 +161,6 @@ export const ROUTES = { schedules: ['schedules'], schedule: ['schedules/:id'], outgoing_webhooks: ['outgoing_webhooks', 'outgoing_webhooks/:id', 'outgoing_webhooks/:action/:id'], - maintenance: ['maintenance'], settings: ['settings'], 'chat-ops': ['chat-ops'], 'live-settings': ['live-settings'], From 619fa4aa7e855c538ce82cd7bd661f8b250be5bc Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 29 Aug 2023 14:08:54 +0300 Subject: [PATCH 13/22] fixed mobx not updating the routes tree component --- .../IntegrationCollapsibleTreeView.tsx | 5 +-- .../alert_receive_channel.ts | 2 +- .../src/pages/integration/Integration.tsx | 31 +++++++++---------- .../src/pages/integrations/Integrations.tsx | 2 -- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx index 321766512d..d1bbc49730 100644 --- a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx +++ b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx @@ -5,6 +5,7 @@ import cn from 'classnames/bind'; import { isArray, isUndefined } from 'lodash-es'; import styles from './IntegrationCollapsibleTreeView.module.scss'; +import { observer } from 'mobx-react'; const cx = cn.bind(styles); @@ -23,7 +24,7 @@ interface IntegrationCollapsibleTreeViewProps { configElements: Array; } -const IntegrationCollapsibleTreeView: React.FC = (props) => { +const IntegrationCollapsibleTreeView: React.FC = observer((props) => { const { configElements } = props; const [expandedList, setExpandedList] = useState(getStartingExpandedState()); @@ -98,7 +99,7 @@ const IntegrationCollapsibleTreeView: React.FC { } getConfigForTreeComponent(id: string, templates: AlertTemplatesDTO[]) { - // const { - // store: { alertReceiveChannelStore }, - // } = this.props; + const { + store: { alertReceiveChannelStore }, + } = this.props; - // const alertReceiveChannel = alertReceiveChannelStore.items[id]; - // const contactPoints = alertReceiveChannelStore.connectedContactPoints[id]; + const alertReceiveChannel = alertReceiveChannelStore.items[id]; + const contactPoints = alertReceiveChannelStore.connectedContactPoints[id]; return [ - // IntegrationHelper.isGrafanaAlerting(alertReceiveChannel) && { - // isHidden: contactPoints === null || contactPoints === undefined, - // isCollapsible: false, - // customIcon: 'grafana', - // canHoverIcon: false, - // collapsedView: null, - // expandedView: () => , - // }, + IntegrationHelper.isGrafanaAlerting(alertReceiveChannel) && { + isHidden: contactPoints === null || contactPoints === undefined, + isCollapsible: false, + customIcon: 'grafana', + canHoverIcon: false, + collapsedView: null, + expandedView: () => , + }, { isCollapsible: false, customIcon: 'plug', @@ -537,8 +537,7 @@ class Integration extends React.Component { const channelFilterIds = alertReceiveChannelStore.channelFilterIds[id]; const onRouteDelete = async (routeId: string) => { - await alertReceiveChannelStore.deleteChannelFilter(routeId); - // this.forceUpdate(); + await alertReceiveChannelStore.deleteChannelFilter(routeId).then(() => this.forceUpdate()); openNotification('Route has been deleted'); }; diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 59dd2209ac..3a3f835c71 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -496,8 +496,6 @@ class Integrations extends React.Component const { alertReceiveChannelStore } = store; const { integrationsFilters, page } = this.state; - console.log('Apply filters wtf'); - const newPage = isOnMount ? page : 1; return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, newPage).then(() => { From 055a4f620a20fb027c12e6c7b4a69c204d065cc5 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 29 Aug 2023 14:23:59 +0300 Subject: [PATCH 14/22] changelog --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19dfb5082e..b443b52461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 updated to reflect the latest state by @joeyorlando ([#2886](https://github.com/grafana/oncall/pull/2886)) - Fix issue where Grafana integration would fail to parse alerting config for routes without receivers @mderynck ([#2894](https://github.com/grafana/oncall/pull/2894)) - -### Changed - - Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869)) ## v1.3.27 (2023-08-25) From f99987b75247db22b69abde07405e5650505ee19 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 29 Aug 2023 14:33:34 +0300 Subject: [PATCH 15/22] build fix --- .../IntegrationCollapsibleTreeView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx index d1bbc49730..97c0e7c496 100644 --- a/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx +++ b/grafana-plugin/src/components/IntegrationCollapsibleTreeView/IntegrationCollapsibleTreeView.tsx @@ -3,9 +3,9 @@ import React, { useEffect, useState } from 'react'; import { Icon, IconButton, IconName } from '@grafana/ui'; import cn from 'classnames/bind'; import { isArray, isUndefined } from 'lodash-es'; +import { observer } from 'mobx-react'; import styles from './IntegrationCollapsibleTreeView.module.scss'; -import { observer } from 'mobx-react'; const cx = cn.bind(styles); From 58f0a23e5f89bbb9ed334fdccf970daed09b4cf9 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Tue, 29 Aug 2023 14:58:18 +0300 Subject: [PATCH 16/22] fixed routes not moving up/down --- .../CollapsedIntegrationRouteDisplay.tsx | 3 +++ .../ExpandedIntegrationRouteDisplay.tsx | 7 +++++++ grafana-plugin/src/pages/integration/Integration.tsx | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx index 320e0b97b6..04665e918d 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/CollapsedIntegrationRouteDisplay/CollapsedIntegrationRouteDisplay.tsx @@ -26,6 +26,7 @@ interface CollapsedIntegrationRouteDisplayProps { openEditTemplateModal: (templateName: string | string[], channelFilterId?: ChannelFilter['id']) => void; onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void; onRouteDelete: (routeId: string) => void; + onItemMove: () => void; } const CollapsedIntegrationRouteDisplay: React.FC = observer( @@ -37,6 +38,7 @@ const CollapsedIntegrationRouteDisplay: React.FC { const store = useStore(); const { escalationChainStore, alertReceiveChannelStore } = store; @@ -102,6 +104,7 @@ const CollapsedIntegrationRouteDisplay: React.FC setRouteIdForDeletion(channelFilterId)} openRouteTemplateEditor={() => handleEditRoutingTemplate(channelFilter, channelFilterId)} /> diff --git a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx index 39326f63f9..d508d7d02d 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx @@ -51,6 +51,7 @@ interface ExpandedIntegrationRouteDisplayProps { openEditTemplateModal: (templateName: string | string[], channelFilterId?: ChannelFilter['id']) => void; onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void; onRouteDelete: (routeId: string) => void; + onItemMove: () => void; } interface ExpandedIntegrationRouteDisplayState { @@ -68,6 +69,7 @@ const ExpandedIntegrationRouteDisplay: React.FC { const store = useStore(); const { @@ -139,6 +141,7 @@ const ExpandedIntegrationRouteDisplay: React.FC setState({ routeIdForDeletion: channelFilterId })} openRouteTemplateEditor={() => handleEditRoutingTemplate(channelFilter, channelFilterId)} /> @@ -327,6 +330,7 @@ interface RouteButtonsDisplayProps { routeIndex: number; setRouteIdForDeletion(): void; openRouteTemplateEditor(): void; + onItemMove(); } export const RouteButtonsDisplay: React.FC = ({ @@ -335,6 +339,7 @@ export const RouteButtonsDisplay: React.FC = ({ routeIndex, setRouteIdForDeletion, openRouteTemplateEditor, + onItemMove, }) => { const { alertReceiveChannelStore } = useStore(); const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId]; @@ -412,11 +417,13 @@ export const RouteButtonsDisplay: React.FC = ({ function onRouteMoveDown(e: React.SyntheticEvent) { e.stopPropagation(); alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex + 1); + onItemMove(); } function onRouteMoveUp(e: React.SyntheticEvent) { e.stopPropagation(); alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex - 1); + onItemMove(); } }; diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index 2de9ba6cf4..f96a8ba457 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -286,7 +286,7 @@ class Integration extends React.Component { To ensure a smooth transition you can migrate now using "Migrate" button in the menu on the right. - Please, check{' '} + Please check out the{' '} { channelFilterId={channelFilterId} routeIndex={routeIndex} toggle={toggle} + onItemMove={() => this.forceUpdate()} openEditTemplateModal={this.openEditTemplateModal} onEditRegexpTemplate={this.handleEditRegexpRouteTemplate} onRouteDelete={onRouteDelete} @@ -574,6 +575,7 @@ class Integration extends React.Component { templates={templates} openEditTemplateModal={this.openEditTemplateModal} onEditRegexpTemplate={this.handleEditRegexpRouteTemplate} + onItemMove={() => this.forceUpdate()} onRouteDelete={onRouteDelete} /> ), From 2ae899be536c2a698f3771e497cd5b8971f1fd60 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 30 Aug 2023 11:02:07 +0300 Subject: [PATCH 17/22] few other tweaks --- .../RemoteFilters/RemoteFilters.tsx | 3 +- .../alert_receive_channel.ts | 6 +- grafana-plugin/src/models/filters/filters.ts | 5 -- .../escalation-chains/EscalationChains.tsx | 4 +- .../src/pages/incidents/Incidents.tsx | 6 +- .../src/pages/integration/Integration.tsx | 8 +-- .../src/pages/integrations/Integrations.tsx | 59 +++++++++++++------ .../outgoing_webhooks/OutgoingWebhooks.tsx | 4 +- .../src/pages/schedules/Schedules.tsx | 4 +- .../src/state/rootBaseStore/index.ts | 12 ++++ grafana-plugin/src/utils/consts.ts | 8 +++ 11 files changed, 82 insertions(+), 37 deletions(-) diff --git a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx index e18c24ab77..8c94601ded 100644 --- a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx +++ b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx @@ -32,6 +32,7 @@ import { parseFilters } from './RemoteFilters.helpers'; import { FilterOption, RemoteFiltersType } from './RemoteFilters.types'; import styles from './RemoteFilters.module.css'; +import { PAGE } from 'utils/consts'; const cx = cn.bind(styles); @@ -39,7 +40,7 @@ interface RemoteFiltersProps extends WithStoreProps { value: RemoteFiltersType; onChange: (filters: { [key: string]: any }, isOnMount: boolean) => void; query: { [key: string]: any }; - page: string; + page: PAGE; defaultFilters?: FiltersValues; extraFilters?: (state, setState, onFiltersValueChange) => React.ReactNode; grafanaTeamStore: GrafanaTeamStore; diff --git a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts index cc434dfc4a..f5871d3e43 100644 --- a/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts +++ b/grafana-plugin/src/models/alert_receive_channel/alert_receive_channel.ts @@ -131,10 +131,14 @@ export class AlertReceiveChannelStore extends BaseStore { return results; } - async updatePaginatedItems(query: any = '', page = 1, updateCounters = false) { + async updatePaginatedItems(query: any = '', page = 1, updateCounters = false, invalidateFn = undefined) { const filters = typeof query === 'string' ? { search: query } : query; const { count, results } = await makeRequest(this.path, { params: { ...filters, page } }); + if (invalidateFn?.()) { + return undefined; + } + this.items = { ...this.items, ...results.reduce( diff --git a/grafana-plugin/src/models/filters/filters.ts b/grafana-plugin/src/models/filters/filters.ts index 8941a5481c..eaca60d7c8 100644 --- a/grafana-plugin/src/models/filters/filters.ts +++ b/grafana-plugin/src/models/filters/filters.ts @@ -17,9 +17,6 @@ export class FiltersStore extends BaseStore { @observable.shallow public values: { [page: string]: FiltersValues } = {}; - @observable - public isLoading = true; - private _globalValues: FiltersValues = {}; constructor(rootStore: RootStore) { @@ -55,8 +52,6 @@ export class FiltersStore extends BaseStore { [page]: result, }; - this.isLoading = false; - return result; } diff --git a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx index 9262e03892..faf5db7404 100644 --- a/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx +++ b/grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx @@ -28,7 +28,7 @@ import { FiltersValues } from 'models/filters/filters.types'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import { UserActions } from 'utils/authorization'; -import { PLUGIN_ROOT } from 'utils/consts'; +import { PAGE, PLUGIN_ROOT } from 'utils/consts'; import styles from './EscalationChains.module.css'; @@ -231,7 +231,7 @@ class EscalationChainsPage extends React.Component diff --git a/grafana-plugin/src/pages/incidents/Incidents.tsx b/grafana-plugin/src/pages/incidents/Incidents.tsx index c975d2fd70..8148d9b607 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.tsx +++ b/grafana-plugin/src/pages/incidents/Incidents.tsx @@ -27,7 +27,7 @@ import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; import { UserActions } from 'utils/authorization'; -import { PLUGIN_ROOT } from 'utils/consts'; +import { PAGE, PLUGIN_ROOT } from 'utils/consts'; import styles from './Incidents.module.scss'; import { IncidentDropdown } from './parts/IncidentDropdown'; @@ -264,7 +264,7 @@ class Incidents extends React.Component
const results = alertGroupStore.getAlertSearchResult('default'); const prev = get(alertGroupStore.alertsSearchResult, `default.prev`); const next = get(alertGroupStore.alertsSearchResult, `default.next`); - const isLoading = alertGroupStore.alertGroupsLoading || filtersStore.isLoading; + const isLoading = alertGroupStore.alertGroupsLoading || filtersStore.options['incidents'] === undefined; if (results && !results.length) { return ( diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index f96a8ba457..afe9128b8c 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -541,7 +541,7 @@ class Integration extends React.Component { openNotification('Route has been deleted'); }; - return channelFilterIds.map( + return (channelFilterIds || []).map( (channelFilterId: ChannelFilter['id'], routeIndex: number) => ({ canHoverIcon: true, @@ -683,9 +683,9 @@ class Integration extends React.Component { const promises = []; if (!alertReceiveChannelStore.items[id]) { - promises.push(alertReceiveChannelStore.loadItem(id).then(() => this.loadExtraDataMaybe(id))); + promises.push(alertReceiveChannelStore.loadItem(id).then(() => this.loadExtraData(id))); } else { - promises.push(this.loadExtraDataMaybe(id)); + promises.push(this.loadExtraData(id)); } if (!alertReceiveChannelStore.channelFilterIds[id]) { @@ -706,7 +706,7 @@ class Integration extends React.Component { .finally(() => this.setState({ isLoading: false })); } - async loadExtraDataMaybe(id: AlertReceiveChannel['id']) { + async loadExtraData(id: AlertReceiveChannel['id']) { const { alertReceiveChannelStore } = this.props.store; if (IntegrationHelper.isGrafanaAlerting(alertReceiveChannelStore.items[id])) { diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 3a3f835c71..ae13fa9ca8 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -36,6 +36,7 @@ import LocationHelper from 'utils/LocationHelper'; import { UserActions } from 'utils/authorization'; import styles from './Integrations.module.scss'; +import { PAGE } from 'utils/consts'; const cx = cn.bind(styles); const FILTERS_DEBOUNCE_MS = 500; @@ -45,7 +46,7 @@ const MAX_LINE_LENGTH = 40; interface IntegrationsState extends PageBaseState { integrationsFilters: Filters; alertReceiveChannelId?: AlertReceiveChannel['id'] | 'new'; - page: number; + lastQueriedPage: number; confirmationModal: { isOpen: boolean; title: any; @@ -65,16 +66,16 @@ class Integrations extends React.Component constructor(props: IntegrationsProps) { super(props); - const { - query: { p }, - } = props; + const { query, store } = props; this.state = { integrationsFilters: { searchTerm: '' }, errorData: initErrorDataState(), - page: p ? Number(p) : 1, confirmationModal: undefined, + lastQueriedPage: undefined, }; + + store.currentPage['integrations'] = Number(store.currentPage['integrations'] || query.p || 1); } async componentDidMount() { @@ -120,16 +121,22 @@ class Integrations extends React.Component update = () => { const { store } = this.props; - const { page, integrationsFilters } = this.state; + const { integrationsFilters } = this.state; + const page = store.currentPage['integrations']; LocationHelper.update({ p: page }, 'partial'); - return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page, false); + return store.alertReceiveChannelStore.updatePaginatedItems( + integrationsFilters, + page, + false, + this.invalidateRequestFn + ); }; render() { const { store, query } = this.props; - const { alertReceiveChannelId, page, confirmationModal } = this.state; + const { alertReceiveChannelId, confirmationModal } = this.state; const { alertReceiveChannelStore } = store; const { count, results } = alertReceiveChannelStore.getPaginatedSearchResult(); @@ -161,7 +168,7 @@ class Integrations extends React.Component
@@ -175,7 +182,7 @@ class Integrations extends React.Component className={cx('integrations-table')} rowClassName={cx('integrations-table-row')} pagination={{ - page, + page: store.currentPage['integrations'], total: Math.ceil((count || 0) / ITEMS_PER_PAGE), onChange: this.handleChangePage, }} @@ -470,8 +477,24 @@ class Integrations extends React.Component ]; }; + invalidateRequestFn = (page) => { + const { store } = this.props; + const { lastQueriedPage } = this.state; + + const urlPage = store.currentPage['integrations']; + const lastReqPage = page || urlPage; + + if (lastQueriedPage === undefined) { + return false; + } + return lastReqPage !== lastQueriedPage; + }; + handleChangePage = (page: number) => { - this.setState({ page }, this.update); + const { store } = this.props; + + store.currentPage['integrations'] = page; + this.update(); }; onIntegrationEditClick = (id: AlertReceiveChannel['id']) => { @@ -494,14 +517,16 @@ class Integrations extends React.Component applyFilters = async (isOnMount: boolean) => { const { store } = this.props; const { alertReceiveChannelStore } = store; - const { integrationsFilters, page } = this.state; + const { integrationsFilters } = this.state; - const newPage = isOnMount ? page : 1; + const newPage = isOnMount ? store.getCurrentPage(PAGE.Integrations) : 1; - return alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, newPage).then(() => { - this.setState({ page: newPage }); - LocationHelper.update({ p: newPage }, 'partial'); - }); + return alertReceiveChannelStore + .updatePaginatedItems(integrationsFilters, newPage, false, () => this.invalidateRequestFn(newPage)) + .then(() => { + store.setCurrentPage(PAGE.Integrations, newPage); + LocationHelper.update({ p: newPage }, 'partial'); + }); }; debouncedUpdateIntegrations = debounce(this.applyFilters, FILTERS_DEBOUNCE_MS); diff --git a/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx b/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx index 899aeecdb7..230a46497a 100644 --- a/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx +++ b/grafana-plugin/src/pages/outgoing_webhooks/OutgoingWebhooks.tsx @@ -36,7 +36,7 @@ import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import { openErrorNotification, openNotification } from 'utils'; import { isUserActionAllowed, UserActions } from 'utils/authorization'; -import { PLUGIN_ROOT } from 'utils/consts'; +import { PAGE, PLUGIN_ROOT } from 'utils/consts'; import styles from './OutgoingWebhooks.module.scss'; import { WebhookFormActionType } from './OutgoingWebhooks.types'; @@ -225,7 +225,7 @@ class OutgoingWebhooks extends React.Component diff --git a/grafana-plugin/src/pages/schedules/Schedules.tsx b/grafana-plugin/src/pages/schedules/Schedules.tsx index 1db9849097..99ae23ac69 100644 --- a/grafana-plugin/src/pages/schedules/Schedules.tsx +++ b/grafana-plugin/src/pages/schedules/Schedules.tsx @@ -32,7 +32,7 @@ import { WithStoreProps, PageProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; import { UserActions } from 'utils/authorization'; -import { PLUGIN_ROOT, TABLE_COLUMN_MAX_WIDTH } from 'utils/consts'; +import { PAGE, PLUGIN_ROOT, TABLE_COLUMN_MAX_WIDTH } from 'utils/consts'; import styles from './Schedules.module.css'; @@ -153,7 +153,7 @@ class SchedulesPage extends React.Component diff --git a/grafana-plugin/src/state/rootBaseStore/index.ts b/grafana-plugin/src/state/rootBaseStore/index.ts index ce2f0154be..144658d9b1 100644 --- a/grafana-plugin/src/state/rootBaseStore/index.ts +++ b/grafana-plugin/src/state/rootBaseStore/index.ts @@ -37,6 +37,7 @@ import { CLOUD_VERSION_REGEX, GRAFANA_LICENSE_CLOUD, GRAFANA_LICENSE_OSS, + PAGE, PLUGIN_ROOT, } from 'utils/consts'; import FaroHelper from 'utils/faro'; @@ -79,6 +80,9 @@ export class RootBaseStore { @observable incidentsPage: any = this.initialQuery.p ? Number(this.initialQuery.p) : 1; + @observable + currentPage: { [key: string]: number } = {}; + @observable onCallApiUrl: string; @@ -297,4 +301,12 @@ export class RootBaseStore { const settings = await PluginState.getGrafanaPluginSettings(); return settings.jsonData?.onCallApiUrl; } + + getCurrentPage = (page: PAGE): number => { + return this.currentPage[page]; + }; + + setCurrentPage = (page: PAGE, value: number) => { + this.currentPage[page] = value; + }; } diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/utils/consts.ts index 407864f036..0182abe7ad 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/utils/consts.ts @@ -46,3 +46,11 @@ export const TABLE_COLUMN_MAX_WIDTH = 1500; export const generateAssignToTeamInputDescription = (objectName: string): string => `Assigning to a team allows you to filter ${objectName} and configure their visibility. Go to OnCall -> Settings -> Team and Access Settings for more details.`; + +export enum PAGE { + Integrations = 'integrations', + Escalations = 'escalation_chains', + Incidents = 'incidents', + Webhooks = 'webhooks', + Schedules = 'schedules', +} From e3c095cfac6a3b9b9750398fcc14658690765671 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 30 Aug 2023 11:36:00 +0300 Subject: [PATCH 18/22] fixed didInvalidateFn for async calls --- .../src/pages/integrations/Integrations.tsx | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index ae13fa9ca8..5437b7398e 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -46,7 +46,6 @@ const MAX_LINE_LENGTH = 40; interface IntegrationsState extends PageBaseState { integrationsFilters: Filters; alertReceiveChannelId?: AlertReceiveChannel['id'] | 'new'; - lastQueriedPage: number; confirmationModal: { isOpen: boolean; title: any; @@ -72,7 +71,6 @@ class Integrations extends React.Component integrationsFilters: { searchTerm: '' }, errorData: initErrorDataState(), confirmationModal: undefined, - lastQueriedPage: undefined, }; store.currentPage['integrations'] = Number(store.currentPage['integrations'] || query.p || 1); @@ -126,11 +124,8 @@ class Integrations extends React.Component LocationHelper.update({ p: page }, 'partial'); - return store.alertReceiveChannelStore.updatePaginatedItems( - integrationsFilters, - page, - false, - this.invalidateRequestFn + return store.alertReceiveChannelStore.updatePaginatedItems(integrationsFilters, page, false, () => + this.invalidateRequestFn(page) ); }; @@ -477,17 +472,9 @@ class Integrations extends React.Component ]; }; - invalidateRequestFn = (page) => { + invalidateRequestFn = (requestedPage: number) => { const { store } = this.props; - const { lastQueriedPage } = this.state; - - const urlPage = store.currentPage['integrations']; - const lastReqPage = page || urlPage; - - if (lastQueriedPage === undefined) { - return false; - } - return lastReqPage !== lastQueriedPage; + return requestedPage !== store.getCurrentPage(PAGE.Integrations); }; handleChangePage = (page: number) => { From 8bc5afd994990bd566e897d074d4dd9df7032ef7 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 30 Aug 2023 11:39:18 +0300 Subject: [PATCH 19/22] lint --- grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx | 2 +- grafana-plugin/src/pages/integrations/Integrations.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx index 8c94601ded..37825c232d 100644 --- a/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx +++ b/grafana-plugin/src/containers/RemoteFilters/RemoteFilters.tsx @@ -27,12 +27,12 @@ import { GrafanaTeamStore } from 'models/grafana_team/grafana_team'; import { SelectOption, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; import LocationHelper from 'utils/LocationHelper'; +import { PAGE } from 'utils/consts'; import { parseFilters } from './RemoteFilters.helpers'; import { FilterOption, RemoteFiltersType } from './RemoteFilters.types'; import styles from './RemoteFilters.module.css'; -import { PAGE } from 'utils/consts'; const cx = cn.bind(styles); diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 5437b7398e..5b5cfcfd69 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -34,9 +34,9 @@ import { withMobXProviderContext } from 'state/withStore'; import { openNotification } from 'utils'; import LocationHelper from 'utils/LocationHelper'; import { UserActions } from 'utils/authorization'; +import { PAGE } from 'utils/consts'; import styles from './Integrations.module.scss'; -import { PAGE } from 'utils/consts'; const cx = cn.bind(styles); const FILTERS_DEBOUNCE_MS = 500; From 52cbbe05f8dba03a089205caab5abb31651b8de4 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 30 Aug 2023 12:01:21 +0300 Subject: [PATCH 20/22] @action on mobx action --- grafana-plugin/src/state/rootBaseStore/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/grafana-plugin/src/state/rootBaseStore/index.ts b/grafana-plugin/src/state/rootBaseStore/index.ts index 144658d9b1..264cdca2d4 100644 --- a/grafana-plugin/src/state/rootBaseStore/index.ts +++ b/grafana-plugin/src/state/rootBaseStore/index.ts @@ -306,6 +306,7 @@ export class RootBaseStore { return this.currentPage[page]; }; + @action setCurrentPage = (page: PAGE, value: number) => { this.currentPage[page] = value; }; From 9d2244d8bc9cd742b49f223cd341c3ef59f4b22a Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 30 Aug 2023 12:03:24 +0300 Subject: [PATCH 21/22] corrected changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90660cd3d5..d1229b142f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869)) + ## v1.3.29 (2023-08-29) ### Fixed @@ -24,7 +30,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 updated to reflect the latest state by @joeyorlando ([#2886](https://github.com/grafana/oncall/pull/2886)) - Fix issue where Grafana integration would fail to parse alerting config for routes without receivers @mderynck ([#2894](https://github.com/grafana/oncall/pull/2894)) -- Performance and UX tweaks to integrations page ([#2869](https://github.com/grafana/oncall/pull/2869)) ## v1.3.27 (2023-08-25) From b8f2b7256d8786e38f3b1f4fe9f0465b87abcbdb Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Wed, 30 Aug 2023 12:30:23 +0300 Subject: [PATCH 22/22] removed maintenance from plugin.json --- grafana-plugin/src/plugin.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/grafana-plugin/src/plugin.json b/grafana-plugin/src/plugin.json index 6e5a6ce850..2479e4c99e 100644 --- a/grafana-plugin/src/plugin.json +++ b/grafana-plugin/src/plugin.json @@ -86,14 +86,6 @@ "action": "grafana-oncall-app.outgoing-webhooks:read", "addToNav": true }, - { - "type": "page", - "name": "Maintenance", - "path": "/a/grafana-oncall-app/maintenance", - "role": "Viewer", - "action": "grafana-oncall-app.maintenance:read", - "addToNav": true - }, { "type": "page", "name": "Settings",