From 30770740d33e795858122ffed7812858983765d2 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Wed, 26 Nov 2025 12:23:38 +0300 Subject: [PATCH 1/3] use DataSourceRef from @grafana/schema --- src/panel-triggers/ProblemsPanel.tsx | 3 ++- src/panel-triggers/components/AlertList/AlertCard.tsx | 2 +- src/panel-triggers/components/AlertList/AlertList.tsx | 2 +- src/panel-triggers/components/EventTag.tsx | 3 ++- src/panel-triggers/components/Problems/ProblemDetails.tsx | 5 +++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/panel-triggers/ProblemsPanel.tsx b/src/panel-triggers/ProblemsPanel.tsx index 14d79a26..f2e8d3e8 100644 --- a/src/panel-triggers/ProblemsPanel.tsx +++ b/src/panel-triggers/ProblemsPanel.tsx @@ -1,6 +1,7 @@ import React from 'react'; import _ from 'lodash'; -import { DataSourceRef, dateMath, PanelProps } from '@grafana/data'; +import { dateMath, PanelProps } from '@grafana/data'; +import { DataSourceRef } from '@grafana/schema'; import { getDataSourceSrv } from '@grafana/runtime'; import { contextSrv } from 'grafana/app/core/core'; import { ProblemsPanelOptions, RTResized } from './types'; diff --git a/src/panel-triggers/components/AlertList/AlertCard.tsx b/src/panel-triggers/components/AlertList/AlertCard.tsx index c3573b6e..5005eb8f 100644 --- a/src/panel-triggers/components/AlertList/AlertCard.tsx +++ b/src/panel-triggers/components/AlertList/AlertCard.tsx @@ -11,7 +11,7 @@ import AlertAcknowledges from './AlertAcknowledges'; import AlertIcon from './AlertIcon'; import { ProblemDTO, ZBXTag } from '../../../datasource/types'; import { ModalController } from '../../../components'; -import { DataSourceRef } from '@grafana/data'; +import { DataSourceRef } from '@grafana/schema'; import { Tooltip } from '@grafana/ui'; import { getDataSourceSrv } from '@grafana/runtime'; diff --git a/src/panel-triggers/components/AlertList/AlertList.tsx b/src/panel-triggers/components/AlertList/AlertList.tsx index b9c73add..d5e4934f 100644 --- a/src/panel-triggers/components/AlertList/AlertList.tsx +++ b/src/panel-triggers/components/AlertList/AlertList.tsx @@ -4,7 +4,7 @@ import { ProblemsPanelOptions } from '../../types'; import { AckProblemData } from '../AckModal'; import AlertCard from './AlertCard'; import { ProblemDTO, ZBXTag } from '../../../datasource/types'; -import { DataSourceRef } from '@grafana/data'; +import { DataSourceRef } from '@grafana/schema'; export interface AlertListProps { problems: ProblemDTO[]; diff --git a/src/panel-triggers/components/EventTag.tsx b/src/panel-triggers/components/EventTag.tsx index 602c3ea5..c87af032 100644 --- a/src/panel-triggers/components/EventTag.tsx +++ b/src/panel-triggers/components/EventTag.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { css } from '@emotion/css'; import { Icon, Tooltip, useStyles2 } from '@grafana/ui'; -import { DataSourceRef, GrafanaTheme2 } from '@grafana/data'; +import { GrafanaTheme2 } from '@grafana/data'; +import { DataSourceRef } from '@grafana/schema'; import { ZBXTag } from '../../datasource/types'; const TAG_COLORS = [ diff --git a/src/panel-triggers/components/Problems/ProblemDetails.tsx b/src/panel-triggers/components/Problems/ProblemDetails.tsx index 20daf9f9..0a428530 100644 --- a/src/panel-triggers/components/Problems/ProblemDetails.tsx +++ b/src/panel-triggers/components/Problems/ProblemDetails.tsx @@ -1,8 +1,9 @@ -import React, { useState, useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { css } from '@emotion/css'; // eslint-disable-next-line import moment from 'moment'; -import { TimeRange, DataSourceRef, GrafanaTheme2 } from '@grafana/data'; +import { GrafanaTheme2, TimeRange } from '@grafana/data'; +import { DataSourceRef } from '@grafana/schema'; import { Tooltip, useStyles2 } from '@grafana/ui'; import { getDataSourceSrv } from '@grafana/runtime'; import { ProblemDTO, ZBXAlert, ZBXEvent, ZBXTag } from '../../../datasource/types'; From 231d25eecb83f69f1facae49a3c132946218dcf0 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Wed, 26 Nov 2025 12:24:04 +0300 Subject: [PATCH 2/3] Make Problems.tsx functional component --- src/panel-triggers/ProblemsPanel.tsx | 2 +- .../components/Problems/Problems.tsx | 270 +++++++++--------- 2 files changed, 131 insertions(+), 141 deletions(-) diff --git a/src/panel-triggers/ProblemsPanel.tsx b/src/panel-triggers/ProblemsPanel.tsx index f2e8d3e8..9907795a 100644 --- a/src/panel-triggers/ProblemsPanel.tsx +++ b/src/panel-triggers/ProblemsPanel.tsx @@ -8,7 +8,7 @@ import { ProblemsPanelOptions, RTResized } from './types'; import { ZabbixMetricsQuery } from '../datasource/types/query'; import { ProblemDTO, ZBXQueryUpdatedEvent, ZBXTag } from '../datasource/types'; import { APIExecuteScriptResponse } from '../datasource/zabbix/connectors/zabbix_api/types'; -import ProblemList from './components/Problems/Problems'; +import { ProblemList } from './components/Problems/Problems'; import { AckProblemData } from './components/AckModal'; import AlertList from './components/AlertList/AlertList'; diff --git a/src/panel-triggers/components/Problems/Problems.tsx b/src/panel-triggers/components/Problems/Problems.tsx index bf7f2274..b540a7d2 100644 --- a/src/panel-triggers/components/Problems/Problems.tsx +++ b/src/panel-triggers/components/Problems/Problems.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, useRef, useState, useMemo } from 'react'; import { cx } from '@emotion/css'; import ReactTable from 'react-table-6'; import _ from 'lodash'; @@ -13,7 +13,8 @@ import { ProblemsPanelOptions, RTCell, RTResized, TriggerSeverity } from '../../ import { ProblemDTO, ZBXAlert, ZBXEvent, ZBXTag } from '../../../datasource/types'; import { APIExecuteScriptResponse, ZBXScript } from '../../../datasource/zabbix/connectors/zabbix_api/types'; import { AckCell } from './AckCell'; -import { DataSourceRef, TimeRange } from '@grafana/data'; +import { TimeRange } from '@grafana/data'; +import { DataSourceRef } from '@grafana/schema'; import { reportInteraction } from '@grafana/runtime'; export interface ProblemListProps { @@ -35,93 +36,81 @@ export interface ProblemListProps { onColumnResize?: (newResized: RTResized) => void; } -interface ProblemListState { - expanded: any; - expandedProblems: any; - page: number; -} - -export default class ProblemList extends PureComponent { - rootWidth: number; - rootRef: any; - - constructor(props: ProblemListProps) { - super(props); - this.rootWidth = 0; - this.state = { - expanded: {}, - expandedProblems: {}, - page: 0, - }; - } - - setRootRef = (ref: any) => { - this.rootRef = ref; +export const ProblemList = (props: ProblemListProps) => { + const { + pageSize, + fontSize, + problems, + panelOptions, + onProblemAck, + onPageSizeChange, + onColumnResize, + onTagClick, + loading, + timeRange, + panelId, + getProblemEvents, + getProblemAlerts, + getScripts, + onExecuteScript, + } = props; + + const [expanded, setExpanded] = useState({}); + const [expandedProblems, setExpandedProblems] = useState({}); + const [page, setPage] = useState(0); + const rootRef = useRef(null); + + // Default pageSize to 10 if not provided + const effectivePageSize = pageSize || 10; + + const handleProblemAck = (problem: ProblemDTO, data: AckProblemData) => { + return onProblemAck!(problem, data); }; - handleProblemAck = (problem: ProblemDTO, data: AckProblemData) => { - return this.props.onProblemAck!(problem, data); + const handlePageSizeChange = (pageSize, pageIndex) => { + onPageSizeChange?.(pageSize, pageIndex); }; - onExecuteScript = (problem: ProblemDTO, data: AckProblemData) => {}; - - handlePageSizeChange = (pageSize, pageIndex) => { - if (this.props.onPageSizeChange) { - this.props.onPageSizeChange(pageSize, pageIndex); - } + const handleResizedChange = (newResized, event) => { + onColumnResize?.(newResized); }; - handleResizedChange = (newResized, event) => { - if (this.props.onColumnResize) { - this.props.onColumnResize(newResized); - } - }; - - handleExpandedChange = (expanded: any, event: any) => { + const handleExpandedChange = (expandedChange: any, event: any) => { reportInteraction('grafana_zabbix_panel_row_expanded', {}); + const newExpandedProblems = {}; - const { problems, pageSize } = this.props; - const { page } = this.state; - const expandedProblems = {}; - - for (const row in expanded) { + for (const row in expandedChange) { const rowId = Number(row); - const problemIndex = pageSize * page + rowId; - if (expanded[row] && problemIndex < problems.length) { + const problemIndex = effectivePageSize * page + rowId; + if (expandedChange[row] && problemIndex < problems.length) { const expandedProblem = problems[problemIndex].eventid; if (expandedProblem) { - expandedProblems[expandedProblem] = true; + newExpandedProblems[expandedProblem] = true; } } } - const nextExpanded = { ...this.state.expanded }; - nextExpanded[page] = expanded; + const nextExpanded = { ...expanded }; + nextExpanded[page] = expandedChange; - const nextExpandedProblems = { ...this.state.expandedProblems }; - nextExpandedProblems[page] = expandedProblems; + const nextExpandedProblems = { ...expandedProblems }; + nextExpandedProblems[page] = newExpandedProblems; - this.setState({ - expanded: nextExpanded, - expandedProblems: nextExpandedProblems, - }); + setExpanded(nextExpanded); + setExpandedProblems(nextExpandedProblems); }; - handleTagClick = (tag: ZBXTag, datasource: DataSourceRef, ctrlKey?: boolean, shiftKey?: boolean) => { - if (this.props.onTagClick) { - this.props.onTagClick(tag, datasource, ctrlKey, shiftKey); - } + const handleTagClick = (tag: ZBXTag, datasource: DataSourceRef, ctrlKey?: boolean, shiftKey?: boolean) => { + onTagClick?.(tag, datasource, ctrlKey, shiftKey); }; - getExpandedPage = (page: number) => { - const { problems, pageSize } = this.props; - const { expandedProblems } = this.state; + const getExpandedPage = (page: number) => { const expandedProblemsPage = expandedProblems[page] || {}; const expandedPage = {}; // Go through the page and search for expanded problems - const startIndex = pageSize * page; - const endIndex = Math.min(startIndex + pageSize, problems.length); + const startIndex = effectivePageSize * page; + const endIndex = Math.min(startIndex + effectivePageSize, problems.length); for (let i = startIndex; i < endIndex; i++) { const problem = problems[i]; if (expandedProblemsPage[problem.eventid]) { @@ -132,10 +121,9 @@ export default class ProblemList extends PureComponent { const result = []; - const options = this.props.panelOptions; - const highlightNewerThan = options.highlightNewEvents && options.highlightNewerThan; + const highlightNewerThan = panelOptions.highlightNewEvents && panelOptions.highlightNewerThan; const statusCell = (props) => StatusCell(props, highlightNewerThan); const statusIconCell = (props) => StatusIconCell(props, highlightNewerThan); const hostNameCell = (props) => ( @@ -145,14 +133,19 @@ export default class ProblemList extends PureComponent ); - const columns = [ - { Header: 'Host', id: 'host', show: options.hostField, Cell: hostNameCell }, - { Header: 'Host (Technical Name)', id: 'hostTechName', show: options.hostTechNameField, Cell: hostTechNameCell }, - { Header: 'Host Groups', accessor: 'groups', show: options.hostGroups, Cell: GroupCell }, - { Header: 'Proxy', accessor: 'proxy', show: options.hostProxy }, + const allColumns = [ + { Header: 'Host', id: 'host', show: panelOptions.hostField, Cell: hostNameCell }, + { + Header: 'Host (Technical Name)', + id: 'hostTechName', + show: panelOptions.hostTechNameField, + Cell: hostTechNameCell, + }, + { Header: 'Host Groups', accessor: 'groups', show: panelOptions.hostGroups, Cell: GroupCell }, + { Header: 'Proxy', accessor: 'proxy', show: panelOptions.hostProxy }, { Header: 'Severity', - show: options.severityField, + show: panelOptions.severityField, className: 'problem-severity', width: 120, accessor: (problem) => problem.priority, @@ -160,43 +153,43 @@ export default class ProblemList extends PureComponent SeverityCell( props, - options.triggerSeverity, - options.markAckEvents, - options.ackEventColor, - options.okEventColor + panelOptions.triggerSeverity, + panelOptions.markAckEvents, + panelOptions.ackEventColor, + panelOptions.okEventColor ), }, { Header: '', id: 'statusIcon', - show: options.statusIcon, + show: panelOptions.statusIcon, className: 'problem-status-icon', width: 50, accessor: 'value', Cell: statusIconCell, }, - { Header: 'Status', accessor: 'value', show: options.statusField, width: 100, Cell: statusCell }, + { Header: 'Status', accessor: 'value', show: panelOptions.statusField, width: 100, Cell: statusCell }, { Header: 'Problem', accessor: 'name', minWidth: 200, Cell: ProblemCell }, - { Header: 'Operational data', accessor: 'opdata', show: options.opdataField, width: 150, Cell: OpdataCell }, + { Header: 'Operational data', accessor: 'opdata', show: panelOptions.opdataField, width: 150, Cell: OpdataCell }, { Header: 'Ack', id: 'ack', - show: options.ackField, + show: panelOptions.ackField, width: 70, Cell: (props) => , }, { Header: 'Tags', accessor: 'tags', - show: options.showTags, + show: panelOptions.showTags, className: 'problem-tags', - Cell: (props) => , + Cell: (props) => , }, { Header: 'Age', className: 'problem-age', width: 100, - show: options.ageField, + show: panelOptions.ageField, accessor: 'timestamp', id: 'age', Cell: AgeCell, @@ -207,75 +200,72 @@ export default class ProblemList extends PureComponent LastChangeCell(props, options.customLastChangeFormat && options.lastChangeFormat), + Cell: (props) => LastChangeCell(props, panelOptions.customLastChangeFormat && panelOptions.lastChangeFormat), }, { Header: '', className: 'custom-expander', width: 60, expander: true, Expander: CustomExpander }, ]; - for (const column of columns) { + for (const column of allColumns) { if (column.show || column.show === undefined) { delete column.show; result.push(column); } } return result; - } + }, [panelOptions, handleTagClick]); - render() { - const columns = this.buildColumns(); - this.rootWidth = this.rootRef && this.rootRef.clientWidth; - const { pageSize, fontSize, panelOptions } = this.props; - const panelClass = cx('panel-problems', { [`font-size--${fontSize}`]: !!fontSize }); - let pageSizeOptions = [5, 10, 20, 25, 50, 100]; + const pageSizeOptions = useMemo(() => { + let options = [5, 10, 20, 25, 50, 100]; if (pageSize) { - pageSizeOptions.push(pageSize); - pageSizeOptions = _.uniq(_.sortBy(pageSizeOptions)); + options.push(pageSize); + options = _.uniq(_.sortBy(options)); } + return options; + }, [pageSize]); - return ( -
- ( - - )} - expanded={this.getExpandedPage(this.state.page)} - onExpandedChange={this.handleExpandedChange} - onPageChange={(page) => { - reportInteraction('grafana_zabbix_panel_page_change', { - action: page > this.state.page ? 'next' : 'prev', - }); - - this.setState({ page }); - }} - onPageSizeChange={this.handlePageSizeChange} - onResizedChange={this.handleResizedChange} - /> -
- ); - } -} + return ( +
+ ( + + )} + expanded={getExpandedPage(page)} + onExpandedChange={handleExpandedChange} + onPageChange={(newPage) => { + reportInteraction('grafana_zabbix_panel_page_change', { + action: newPage > page ? 'next' : 'prev', + }); + + setPage(newPage); + }} + onPageSizeChange={handlePageSizeChange} + onResizedChange={handleResizedChange} + /> +
+ ); +}; interface HostCellProps { name: string; From 3febbc7e0dc9729a9fc99cf6e9a2c5c14d926904 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Wed, 26 Nov 2025 12:56:39 +0300 Subject: [PATCH 3/3] lint --- src/panel-triggers/components/Problems/Problems.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panel-triggers/components/Problems/Problems.tsx b/src/panel-triggers/components/Problems/Problems.tsx index b540a7d2..0585a042 100644 --- a/src/panel-triggers/components/Problems/Problems.tsx +++ b/src/panel-triggers/components/Problems/Problems.tsx @@ -59,7 +59,7 @@ export const ProblemList = (props: ProblemListProps) => { const [expandedProblems, setExpandedProblems] = useState({}); const [page, setPage] = useState(0); const rootRef = useRef(null); - + // Default pageSize to 10 if not provided const effectivePageSize = pageSize || 10;