Skip to content

Commit

Permalink
Refactor tabs.
Browse files Browse the repository at this point in the history
  • Loading branch information
fniessink committed Jul 3, 2024
1 parent e747936 commit 3a988ec
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 326 deletions.
156 changes: 49 additions & 107 deletions components/frontend/src/metric/MetricDetails.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { bool, func, string } from "prop-types"
import { useContext, useEffect, useState } from "react"
import { Icon, Menu } from "semantic-ui-react"

import { get_metric_measurements } from "../api/measurement"
import { delete_metric, set_metric_attribute } from "../api/metric"
import { activeTabIndex, tabChangeHandler } from "../app_ui_settings"
import { ChangeLog } from "../changelog/ChangeLog"
import { DataModel } from "../context/DataModel"
import { EDIT_REPORT_PERMISSION, ReadOnlyOrEditable } from "../context/Permissions"
import { Label, Tab } from "../semantic_ui_react_wrappers"
import { Tab } from "../semantic_ui_react_wrappers"
import {
datePropType,
metricPropType,
Expand All @@ -21,7 +20,7 @@ import { SourceEntities } from "../source/SourceEntities"
import { Sources } from "../source/Sources"
import { getMetricScale, getSourceName, isMeasurementRequested } from "../utils"
import { ActionButton, DeleteButton, PermLinkButton, ReorderButtonGroup } from "../widgets/Button"
import { FocusableTab } from "../widgets/FocusableTab"
import { changelogTabPane, configurationTabPane, tabPane } from "../widgets/TabPane"
import { showMessage } from "../widgets/toast"
import { MetricConfigurationParameters } from "./MetricConfigurationParameters"
import { MetricDebtParameters } from "./MetricDebtParameters"
Expand Down Expand Up @@ -124,102 +123,51 @@ export function MetricDetails({
const subject = report.subjects[subject_uuid]
const metric = subject.metrics[metric_uuid]
const last_measurement = measurements[measurements.length - 1]
let any_error = last_measurement?.sources.some((source) => source.connection_error || source.parse_error)
any_error =
any_error ||
let anyError = last_measurement?.sources.some((source) => source.connection_error || source.parse_error)
anyError =
anyError ||
Object.values(metric.sources ?? {}).some(
(source) => !dataModel.metrics[metric.type].sources.includes(source.type),
)
const sources_menu_item = any_error ? <Label color="red">{"Sources"}</Label> : "Sources"
const metricUrl = `${window.location.href.split("#")[0]}#${metric_uuid}`
let panes = []
panes.push(
{
menuItem: (
<Menu.Item key="configuration">
<Icon name="cogs" />
<FocusableTab>{"Configuration"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<MetricConfigurationParameters
subject={subject}
metric={metric}
metric_uuid={metric_uuid}
report={report}
reload={reload}
/>
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="debt">
<Icon name="money" />
<FocusableTab>{"Technical debt"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<MetricDebtParameters metric={metric} metric_uuid={metric_uuid} report={report} reload={reload} />
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="sources">
<Icon name="server" />
<FocusableTab>{sources_menu_item}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<Sources
reports={reports}
report={report}
metric={metric}
metric_uuid={metric_uuid}
measurement={metric.latest_measurement}
changed_fields={changed_fields}
reload={reload}
/>
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="changelog">
<Icon name="history" />
<FocusableTab>{"Changelog"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<ChangeLog timestamp={report.timestamp} metric_uuid={metric_uuid} />
</Tab.Pane>
),
},
configurationTabPane(
<MetricConfigurationParameters
subject={subject}
metric={metric}
metric_uuid={metric_uuid}
report={report}
reload={reload}
/>,
),
tabPane(
"Technical debt",
<MetricDebtParameters metric={metric} metric_uuid={metric_uuid} report={report} reload={reload} />,
{ iconName: "money" },
),
tabPane(
"Sources",
<Sources
reports={reports}
report={report}
metric={metric}
metric_uuid={metric_uuid}
measurement={metric.latest_measurement}
changed_fields={changed_fields}
reload={reload}
/>,
{ iconName: "server", error: Boolean(anyError) },
),
changelogTabPane(<ChangeLog timestamp={report.timestamp} metric_uuid={metric_uuid} />),
)
if (measurements.length > 0) {
if (getMetricScale(metric, dataModel) !== "version_number") {
panes.push({
menuItem: (
<Menu.Item key="trend_graph">
<Icon
className="linegraph"
/* Using name="linegraph" results in "Invalid prop `name` of value `linegraph` supplied to `Icon`."
Using name="line graph" does not show the icon. Using className works around this. */
/>
<FocusableTab>{"Trend graph"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<TrendGraph metric={metric} measurements={measurements} />
</Tab.Pane>
),
})
panes.push(
tabPane("Trend graph", <TrendGraph metric={metric} measurements={measurements} />, {
iconName: "linegraph",
}),
)
}
last_measurement.sources.forEach((source) => {
const report_source = metric.sources[source.source_uuid]
Expand All @@ -231,24 +179,18 @@ export function MetricDetails({
return
} // no entities to show, continue
const source_name = getSourceName(report_source, dataModel)
panes.push({
menuItem: (
<Menu.Item key={source.source_uuid}>
<FocusableTab>{source_name}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<SourceEntities
report={report}
metric={metric}
metric_uuid={metric_uuid}
source={source}
reload={measurementsReload}
/>
</Tab.Pane>
panes.push(
tabPane(
source_name,
<SourceEntities
report={report}
metric={metric}
metric_uuid={metric_uuid}
source={source}
reload={measurementsReload}
/>,
),
})
)
})
}

Expand Down
86 changes: 15 additions & 71 deletions components/frontend/src/report/ReportTitle.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { func, string } from "prop-types"
import { Grid, Icon, Menu } from "semantic-ui-react"
import { Grid } from "semantic-ui-react"

import { delete_report, set_report_attribute } from "../api/report"
import { activeTabIndex, tabChangeHandler } from "../app_ui_settings"
Expand All @@ -14,9 +14,9 @@ import { Label, Segment, Tab } from "../semantic_ui_react_wrappers"
import { reportPropType, settingsPropType } from "../sharedPropTypes"
import { getDesiredResponseTime } from "../utils"
import { DeleteButton, PermLinkButton } from "../widgets/Button"
import { FocusableTab } from "../widgets/FocusableTab"
import { HeaderWithDetails } from "../widgets/HeaderWithDetails"
import { LabelWithHelp } from "../widgets/LabelWithHelp"
import { changelogTabPane, configurationTabPane, tabPane } from "../widgets/TabPane"
import { setDocumentTitle } from "./document_title"
import { IssueTracker } from "./IssueTracker"

Expand Down Expand Up @@ -313,75 +313,19 @@ export function ReportTitle({ report, openReportsOverview, reload, settings }) {
const tabIndex = activeTabIndex(settings.expandedItems, report_uuid)
const reportUrl = `${window.location}`
const panes = [
{
menuItem: (
<Menu.Item key="configuration">
<Icon name="settings" />
<FocusableTab>{"Configuration"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<ReportConfiguration report={report} reload={reload} />
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="reaction_times">
<Icon name="time" />
<FocusableTab>{"Desired reaction times"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<ReactionTimes report={report} reload={reload} />
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="notifications">
<Icon name="feed" />
<FocusableTab>{"Notifications"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<NotificationDestinations
destinations={report.notification_destinations || {}}
report_uuid={report_uuid}
reload={reload}
/>
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="issue_tracker">
<Icon name="tasks" />
<FocusableTab>{"Issue tracker"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<IssueTracker report={report} reload={reload} />
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="changelog">
<Icon name="history" />
<FocusableTab>{"Changelog"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<ChangeLog report_uuid={report_uuid} timestamp={report.timestamp} />
</Tab.Pane>
),
},
configurationTabPane(<ReportConfiguration report={report} reload={reload} />),
tabPane("Desired reaction times", <ReactionTimes report={report} reload={reload} />, { iconName: "time" }),
tabPane(
"Notifications",
<NotificationDestinations
destinations={report.notification_destinations || {}}
report_uuid={report_uuid}
reload={reload}
/>,
{ iconName: "feed" },
),
tabPane("Issue tracker", <IssueTracker report={report} reload={reload} />, { iconName: "tasks" }),
changelogTabPane(<ChangeLog report_uuid={report_uuid} timestamp={report.timestamp} />),
]
setDocumentTitle(report.title)

Expand Down
48 changes: 7 additions & 41 deletions components/frontend/src/report/ReportsOverviewTitle.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { func, shape } from "prop-types"
import { Grid, Icon, Menu } from "semantic-ui-react"
import { Grid } from "semantic-ui-react"

import { set_reports_attribute } from "../api/report"
import { activeTabIndex, tabChangeHandler } from "../app_ui_settings"
Expand All @@ -11,8 +11,8 @@ import { StringInput } from "../fields/StringInput"
import { Tab } from "../semantic_ui_react_wrappers"
import { permissionsPropType, reportsOverviewPropType, settingsPropType } from "../sharedPropTypes"
import { dropdownOptions } from "../utils"
import { FocusableTab } from "../widgets/FocusableTab"
import { HeaderWithDetails } from "../widgets/HeaderWithDetails"
import { changelogTabPane, configurationTabPane, tabPane } from "../widgets/TabPane"
import { setDocumentTitle } from "./document_title"

function ReportsOverviewConfiguration({ reports_overview, reload }) {
Expand Down Expand Up @@ -106,45 +106,11 @@ export function ReportsOverviewTitle({ reports_overview, reload, settings }) {
const uuid = "reports_overview"
const tabIndex = activeTabIndex(settings.expandedItems, uuid)
const panes = [
{
menuItem: (
<Menu.Item key="configuration">
<Icon name="settings" />
<FocusableTab>{"Configuration"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<ReportsOverviewConfiguration reports_overview={reports_overview} reload={reload} />
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="permissions">
<Icon name="lock" />
<FocusableTab>{"Permissions"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<Permissions permissions={reports_overview.permissions ?? {}} reload={reload} />
</Tab.Pane>
),
},
{
menuItem: (
<Menu.Item key="changelog">
<Icon name="history" />
<FocusableTab>{"Changelog"}</FocusableTab>
</Menu.Item>
),
render: () => (
<Tab.Pane>
<ChangeLog />
</Tab.Pane>
),
},
configurationTabPane(<ReportsOverviewConfiguration reports_overview={reports_overview} reload={reload} />),
tabPane("Permissions", <Permissions permissions={reports_overview.permissions ?? {}} reload={reload} />, {
iconName: "lock",
}),
changelogTabPane(<ChangeLog />),
]
setDocumentTitle(reports_overview.title)

Expand Down
15 changes: 14 additions & 1 deletion components/frontend/src/semantic_ui_react_wrappers/Icon.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import { string } from "prop-types"
import { useContext } from "react"
import { Icon as SemanticUIIcon } from "semantic-ui-react"

import { DarkMode } from "../context/DarkMode"

export function Icon(props) {
return <SemanticUIIcon inverted={useContext(DarkMode)} {...props} />
let { className, name, ...otherProps } = props
/* Using name="linegraph" results in "Invalid prop `name` of value `linegraph` supplied to `Icon`."
Using name="line graph" does not show the icon. Using className works around this. */
if (name === "linegraph") {
className = className ?? ""
className += ` ${name}`
name = ""
}
return <SemanticUIIcon className={className} inverted={useContext(DarkMode)} name={name} {...otherProps} />
}
Icon.propTypes = {
className: string,
name: string,
}
Loading

0 comments on commit 3a988ec

Please sign in to comment.