Skip to content

Commit

Permalink
Impl [Alerts history] Add “Expandable Alert Table” with Expanded Rows (
Browse files Browse the repository at this point in the history
  • Loading branch information
pinis-gini-apps authored Dec 29, 2024
1 parent 4ca9807 commit 0654f37
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 185 deletions.
2 changes: 1 addition & 1 deletion src/common/TabsSlider/TabsSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ const TabsSlider = ({
className={tabClassName}
data-tab={tab.id}
to={generateUrlFromRouterPath(
`${window.location.pathname?.replace(/^$|([^/]+$)/, tab.id)}${location.search ?? ''}`
`${window.location.pathname?.replace(/^$|([^/]+$)/, tab.id)}${location.search ?? ''}${tab.query ?? ''}`
)}
onClick={() => onSelectTab(tab)}
key={tab.id}
Expand Down
1 change: 1 addition & 0 deletions src/components/ActionBar/ActionBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ const ActionBar = ({
onChange={(dates, isPredefined, optionId) =>
handleDateChange(dates, isPredefined, optionId, input, formState)
}
timeFrameLimit={filtersConfig[DATES_FILTER].timeFrameLimit}
type="date-range-time"
withLabels
/>
Expand Down
11 changes: 7 additions & 4 deletions src/components/Alerts/Alerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,23 @@ import AlertsView from './AlertsView'

import { ALERTS_PAGE } from '../../constants'
import { createAlertRowData } from '../../utils/createAlertsContent'
import { getAlertsFiltersConfig, parseAlertsQueryParamsCallback } from './alerts.util'
import { generatePageData } from './alerts.util'
import {
getAlertsFiltersConfig,
generatePageData,
parseAlertsQueryParamsCallback
} from './alerts.util'
import { getJobLogs } from '../../utils/getJobLogs.util'
import projectsAction from '../../actions/projects'
import { useAlertsPageData } from '../../hooks/useAlertsPageData'
import { useFiltersFromSearchParams } from '../../hooks/useFiltersFromSearchParams.hook'

const Alerts = () => {
const [selectedAlert, setSelectedAlert] = useState({})
const { id: projectId } = useParams()
const [, setProjectsRequestErrorMessage] = useState('')
const alertsStore = useSelector(state => state.alertsStore)
const filtersStore = useSelector(store => store.filtersStore)
const dispatch = useDispatch()
const { id: projectId } = useParams()
const params = useParams()

const isCrossProjects = useMemo(() => projectId === '*', [projectId])
Expand Down Expand Up @@ -93,7 +96,7 @@ const Alerts = () => {
)

const pageData = useMemo(
() => generatePageData(handleFetchJobLogs, selectedAlert),
() => generatePageData(selectedAlert, handleFetchJobLogs),
[handleFetchJobLogs, selectedAlert]
)

Expand Down
18 changes: 10 additions & 8 deletions src/components/Alerts/AlertsFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
SEVERITY
} from '../../constants'

const AlertsFilters = ({ isCrossProjects }) => {
const AlertsFilters = ({ isAlertsPage, isCrossProjects }) => {
const form = useForm()
const {
values: { [ENTITY_TYPE]: entityType }
Expand Down Expand Up @@ -94,13 +94,15 @@ const AlertsFilters = ({ isCrossProjects }) => {
<FormSelect label="Project name" name={PROJECTS_FILTER} options={projectsList} />
</div>
)}
<div className="form-row">
<FormSelect
label="Entity type"
name={ENTITY_TYPE}
options={filterAlertsEntityTypeOptions}
/>
</div>
{isAlertsPage && (
<div className="form-row">
<FormSelect
label="Entity type"
name={ENTITY_TYPE}
options={filterAlertsEntityTypeOptions}
/>
</div>
)}

{(entityType === FILTER_ALL_ITEMS || entityType === MODEL_MONITORING_APPLICATION) && (
<div className="form-row">
Expand Down
65 changes: 43 additions & 22 deletions src/components/Alerts/AlertsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ illegal under applicable law, and the grant of the foregoing license
under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import classNames from 'classnames'
import PropTypes from 'prop-types'

import ActionBar from '../ActionBar/ActionBar'
Expand All @@ -32,9 +33,12 @@ import { ALERTS_FILTERS, ALERTS_PAGE } from '../../constants'
import { getNoDataMessage } from '../../utils/getNoDataMessage'
import { getCloseDetailsAlertLink } from '../../utils/link-helper.util'

import './alerts.scss'

const AlertsView = ({
alertsFiltersConfig,
alertsStore,
isAlertsPage = true,
filters,
filtersStore,
handleCancel,
Expand All @@ -46,16 +50,22 @@ const AlertsView = ({
requestErrorMessage,
selectedAlert,
setSearchParams,
tableContent
selectedRowData,
tableContent,
toggleRow
}) => {
const content = classNames('content', !isAlertsPage && 'alerts-table__content')

return (
<>
<div className="content-wrapper">
<div className="content__header">
<Breadcrumbs />
</div>
<div className="content">
<div className="table-container">
{isAlertsPage && (
<div className="content__header">
<Breadcrumbs />
</div>
)}
<div className={content}>
<div className="table-container alerts-table__container">
<div className="content__action-bar-wrapper">
<ActionBar
autoRefreshIsStopped={true}
Expand All @@ -68,7 +78,7 @@ const AlertsView = ({
withRefreshButton
withoutExpandButton
>
<AlertsFilters isCrossProjects={isCrossProjects} />
<AlertsFilters isAlertsPage={isAlertsPage} isCrossProjects={isCrossProjects} />
</ActionBar>
</div>
{alertsStore.loading ? (
Expand All @@ -88,27 +98,36 @@ const AlertsView = ({
<>
<Table
actionsMenu={[]}
getCloseDetailsLink={() => getCloseDetailsAlertLink()} //TODO: the getCloseDetailsLink will be updated with ML-8368
getCloseDetailsLink={() => getCloseDetailsAlertLink()}
pageData={pageData}
retryRequest={handleRefreshWithFilters}
selectedItem={selectedAlert}
selectedItem={isAlertsPage ? selectedAlert : {}}
tableClassName="alerts-table"
handleCancel={handleCancel}
hideActionsMenu
tableHeaders={tableContent[0]?.content ?? []}
withActionMenu={false}
>
{tableContent.map((tableItem, index) => (
<AlertsTableRow
key={index}
hideActionsMenu
handleSelectItem={() => {}}
rowIndex={index}
rowItem={tableItem}
actionsMenu={[]}
selectedItem={selectedAlert}
/>
))}
{tableContent.map((tableItem, index) => {
const isRowSelected = tableItem?.data?.id === selectedAlert?.id && !isAlertsPage
const selectedRowClassName = `${isRowSelected ? 'alert-row__cell--expanded-selected-cell' : ''} `
return (
<AlertsTableRow
className={selectedRowClassName}
key={index}
hideActionsMenu
handleSelectItem={() => {}}
filters={filters}
isRowSelected={isRowSelected}
rowIndex={index}
rowItem={tableItem}
actionsMenu={[]}
toggleRow={toggleRow}
selectedItem={selectedAlert}
selectedRowData={selectedRowData}
/>
)
})}
</Table>
<Pagination
page={pageData.page}
Expand All @@ -128,15 +147,17 @@ AlertsView.propTypes = {
alertsStore: PropTypes.object.isRequired,
filters: PropTypes.object.isRequired,
filtersStore: PropTypes.object.isRequired,
handleCancel: PropTypes.func.isRequired,
handleCancel: PropTypes.func,
handleRefreshAlerts: PropTypes.func.isRequired,
handleRefreshWithFilters: PropTypes.func.isRequired,
isAlertsPage: PropTypes.bool,
isCrossProjects: PropTypes.bool.isRequired,
pageData: PropTypes.object.isRequired,
paginationConfigAlertsRef: PropTypes.object.isRequired,
requestErrorMessage: PropTypes.string.isRequired,
selectedAlert: PropTypes.object.isRequired,
setSearchParams: PropTypes.func.isRequired,
tableContent: PropTypes.arrayOf(PropTypes.object).isRequired
tableContent: PropTypes.arrayOf(PropTypes.object).isRequired,
toggleRow: PropTypes.func
}
export default AlertsView
21 changes: 8 additions & 13 deletions src/components/Alerts/alerts.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
@import '~igz-controls/scss/variables';
@import 'src/scss/mixins';

$alertsRowHeight: $rowHeight;
$alertsHeaderRowHeight: $headerRowHeight;
$alertsRowHeightExtended: $rowHeightExtended;

.alerts-table {
@include rowsHeight($alertsHeaderRowHeight, $alertsRowHeight, $alertsRowHeightExtended);
}
min-width: 1120px;

&__metrics {
margin: 8px 12px;
}

:export {
alertsRowHeight: $alertsRowHeight;
alertsHeaderRowHeight: $alertsHeaderRowHeight;
alertsRowHeightExtended: $alertsRowHeightExtended;
&__content {
padding: 0;
}
}
14 changes: 8 additions & 6 deletions src/components/Alerts/alerts.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,17 @@ import {
import {
datePickerPastOptions,
getDatePickerFilterValue,
PAST_24_HOUR_DATE_OPTION
PAST_24_HOUR_DATE_OPTION,
TIME_FRAME_LIMITS
} from '../../utils/datePicker.util'

export const getAlertsFiltersConfig = () => {
export const getAlertsFiltersConfig = (timeFrameLimit = false) => {
return {
[NAME_FILTER]: { label: 'Alert Name:', initialValue: '' },
[DATES_FILTER]: {
label: 'Start time:',
initialValue: getDatePickerFilterValue(datePickerPastOptions, PAST_24_HOUR_DATE_OPTION)
initialValue: getDatePickerFilterValue(datePickerPastOptions, PAST_24_HOUR_DATE_OPTION),
timeFrameLimit: timeFrameLimit ? TIME_FRAME_LIMITS.MONTH : Infinity
},
[PROJECTS_FILTER]: { label: 'Project:', initialValue: FILTER_ALL_ITEMS, isModal: true },
[ENTITY_TYPE]: { label: 'Entity Type:', initialValue: FILTER_ALL_ITEMS, isModal: true },
Expand Down Expand Up @@ -86,7 +88,7 @@ export const parseAlertsQueryParamsCallback = (paramName, paramValue) => {
return paramValue
}

export const generatePageData = (handleFetchJobLogs, selectedAlert) => {
export const generatePageData = (selectedAlert, handleFetchJobLogs = () => {}) => {
return {
page: ALERTS_PAGE,
details: {
Expand All @@ -109,8 +111,8 @@ export const allProjectsOption = [
export const filterAlertsEntityTypeOptions = [
{ label: upperFirst(FILTER_ALL_ITEMS), id: FILTER_ALL_ITEMS },
{ label: upperFirst(JOB), id: JOB_KIND_JOB },
{ label: upperFirst(ENDPOINT), id: 'model-endpoint-result' },
{ label: upperFirst(APPLICATION), id: 'model-monitoring-application' }
{ label: upperFirst(ENDPOINT), id: MODEL_ENDPOINT_RESULT },
{ label: upperFirst(APPLICATION), id: MODEL_MONITORING_APPLICATION }
]

export const filterAlertsSeverityOptions = [
Expand Down
86 changes: 78 additions & 8 deletions src/components/DetailsAlerts/DetailsAlerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,86 @@ illegal under applicable law, and the grant of the foregoing license
under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import React, { useCallback, useMemo, useState } from 'react'
import { useSelector } from 'react-redux'

import React from 'react'
import AlertsView from '../Alerts/AlertsView'

import { createAlertRowData } from '../../utils/createAlertsContent'
import {
generatePageData,
getAlertsFiltersConfig,
parseAlertsQueryParamsCallback
} from '../../components/Alerts/alerts.util'
import { useAlertsPageData } from '../../hooks/useAlertsPageData'
import { useFiltersFromSearchParams } from '../../hooks/useFiltersFromSearchParams.hook'

const DetailsAlerts = () => {
const [selectedAlert, setSelectedAlert] = useState({})
const alertsStore = useSelector(state => state.alertsStore)
const filtersStore = useSelector(store => store.filtersStore)

const alertsFiltersConfig = useMemo(() => getAlertsFiltersConfig(true), [])

const alertsFilters = useFiltersFromSearchParams(
alertsFiltersConfig,
parseAlertsQueryParamsCallback
)

const {
handleRefreshAlerts,
paginatedAlerts,
paginationConfigAlertsRef,
requestErrorMessage,
refreshAlerts,
setAlerts,
setSearchParams
} = useAlertsPageData(alertsFilters, false)

const handleRefreshWithFilters = useCallback(
filters => {
setAlerts([])

return refreshAlerts(filters)
},
[refreshAlerts, setAlerts]
)

const tableContent = useMemo(() => {
return paginatedAlerts.map(alert => createAlertRowData(alert, false, true))
}, [paginatedAlerts])

const pageData = useMemo(() => generatePageData(selectedAlert), [selectedAlert])

const toggleRow = useCallback(
(e, item) => {
setSelectedAlert(prev => {
const selectedAlert = tableContent.find(({ data }) => data?.id === item?.id)
return prev?.id !== item.id ? selectedAlert?.data || {} : {}
})
},
[tableContent]
)

const DetailsAlerts = ({ selectedItem }) => {
return (
<div>
<h2>Alerts</h2>
<p>This tab shows alerts for the selected item.</p>
</div>
<AlertsView
alerts={paginatedAlerts}
alertsFiltersConfig={alertsFiltersConfig}
alertsStore={alertsStore}
filters={alertsFilters}
filtersStore={filtersStore}
handleRefreshAlerts={handleRefreshAlerts}
handleRefreshWithFilters={handleRefreshWithFilters}
isAlertsPage={false}
isCrossProjects={false}
pageData={pageData}
paginationConfigAlertsRef={paginationConfigAlertsRef}
requestErrorMessage={requestErrorMessage}
selectedAlert={selectedAlert}
setSearchParams={setSearchParams}
tableContent={tableContent}
toggleRow={toggleRow}
/>
)
}

export default DetailsAlerts
export default React.memo(DetailsAlerts)
Loading

0 comments on commit 0654f37

Please sign in to comment.