feat(incidents): consistent typography, icons, and layout across the page

- All uppercase headers (Active Incidents, Latest Incidents, Incident
  Statistics, Resolved Incidents, summary card titles, details card
  sections) now use the existing 'eyebrow' typography variant. That's
  the same style used on the Logs page and lives in Theme.ts so future
  tweaks land in one place.
- Latest Incidents and Incident Statistics rows now share a minHeight
  so both cards line up vertically; Latest Incidents items dropped
  their extra dividers and Box wrappers.
- Body text in the summary cards (and the details dialog) uses
  variant=body1 (13px) consistently — previously a mix of body1/body2
  and unspecified Typography.
- All lucide icons in CardSummary go through the design-system <Icon>
  wrapper so they share the project's strokeWidth=1.5 default rather
  than lucide's default 2.
- ControlsIncidentFilter: replaced the missing
  pages.incidents.filters.clearFilters key (which was rendering as
  raw text) with the existing common.buttons.clearFilters.
- CardDetails: extracted local SectionHeading and Cell helpers; switched
  the cards' bare <Typography> cells to variant=body1 + variant=eyebrow
  for headings; dropped redundant title-dividers (eyebrow style already
  separates visually).
This commit is contained in:
gorkem-bwl committed 2026-04-28 23:56:10 -04:00
1 parent fef3f2c294
commit 5d5d1dbb23
4 files changed
+85 -91

No files matched your search

@@ -20,6 +20,20 @@ interface CardDetailsProps {
sx?: object;
}
const SectionHeading = ({ children }: { children: React.ReactNode }) => (
<Typography
component="h2"
variant="eyebrow"
color="text.secondary"
>
{children}
</Typography>
);
const Cell = ({ children }: { children: React.ReactNode }) => (
<Typography variant="body1">{children}</Typography>
);
export const CardDetails = ({ incident, monitor, sx }: CardDetailsProps) => {
const { t } = useTranslation();
const theme = useTheme();
@@ -33,22 +47,18 @@ export const CardDetails = ({ incident, monitor, sx }: CardDetailsProps) => {
gap={theme.spacing(LAYOUT.MD)}
sx={sx}
>
<Typography textTransform={"uppercase"}>
{t("pages.incidents.dialog.details.title")}
</Typography>
<SectionHeading>{t("pages.incidents.dialog.details.title")}</SectionHeading>
<BaseBox padding={LAYOUT.MD}>
<Stack gap={theme.spacing(LAYOUT.MD)}>
<Typography textTransform={"uppercase"}>
{t("pages.incidents.dialog.details.overview")}
</Typography>
<Divider />
<SectionHeading>{t("pages.incidents.dialog.details.overview")}</SectionHeading>
<Grid
container
spacing={theme.spacing(LAYOUT.MD)}
alignItems="center"
>
<Grid size={2}>{t("pages.incidents.dialog.details.status")}</Grid>
<Grid size={2}>
<Cell>{t("pages.incidents.dialog.details.status")}</Cell>
</Grid>
<Grid size={10}>
<ValueLabel
value={incident.status ? "negative" : "positive"}
@@ -61,15 +71,17 @@ export const CardDetails = ({ incident, monitor, sx }: CardDetailsProps) => {
</Grid>
{monitor && (
<>
<Grid size={2}>{t("pages.incidents.dialog.details.monitor")}</Grid>
<Grid size={2}>
<Cell>{t("pages.incidents.dialog.details.monitor")}</Cell>
</Grid>
<Grid size={10}>
<Typography>{monitor.name ?? "N/A"}</Typography>
<Cell>{monitor.name ?? "N/A"}</Cell>
</Grid>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.url")}</Typography>
<Cell>{t("pages.incidents.dialog.details.url")}</Cell>
</Grid>
<Grid size={10}>
<Typography>{monitor.url ?? "N/A"}</Typography>
<Cell>{monitor.url ?? "N/A"}</Cell>
</Grid>
</>
)}
@@ -78,51 +90,48 @@ export const CardDetails = ({ incident, monitor, sx }: CardDetailsProps) => {
</BaseBox>
<BaseBox padding={LAYOUT.MD}>
<Stack gap={theme.spacing(LAYOUT.MD)}>
<Typography textTransform={"uppercase"}>
{t("pages.incidents.dialog.details.analysis")}
</Typography>
<Divider />
<SectionHeading>{t("pages.incidents.dialog.details.analysis")}</SectionHeading>
<Grid
container
spacing={theme.spacing(LAYOUT.MD)}
>
<Grid size={6}>
<Typography>{t("pages.incidents.dialog.details.timeline")}</Typography>
<Cell>{t("pages.incidents.dialog.details.timeline")}</Cell>
</Grid>
<Grid size={6}>
<Typography>{t("pages.incidents.dialog.details.detailsLabel")}</Typography>
<Cell>{t("pages.incidents.dialog.details.detailsLabel")}</Cell>
</Grid>
<Grid size={6}>
<Divider></Divider>
<Divider />
</Grid>
<Grid size={6}>
<Divider></Divider>
<Divider />
</Grid>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.startedAt")}</Typography>
<Cell>{t("pages.incidents.dialog.details.startedAt")}</Cell>
</Grid>
<Grid size={4}>
<Typography>
<Cell>
{formatDateWithTz(incident.startTime, "D MMM YYYY, h:mm A", uiTimezone)}
</Typography>
</Cell>
</Grid>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.statusCode")}</Typography>
<Cell>{t("pages.incidents.dialog.details.statusCode")}</Cell>
</Grid>
<Grid size={4}>
<Typography>{incident.statusCode ?? "N/A"}</Typography>
<Cell>{incident.statusCode ?? "N/A"}</Cell>
</Grid>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.downtime")}</Typography>
<Cell>{t("pages.incidents.dialog.details.downtime")}</Cell>
</Grid>
<Grid size={4}>
<Typography>{getIncidentsDuration(incident)}</Typography>
<Cell>{getIncidentsDuration(incident)}</Cell>
</Grid>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.message")}</Typography>
<Cell>{t("pages.incidents.dialog.details.message")}</Cell>
</Grid>
<Grid size={4}>
<Typography>{incident.message ?? "N/A"}</Typography>
<Cell>{incident.message ?? "N/A"}</Cell>
</Grid>
</Grid>
</Stack>
@@ -130,60 +139,53 @@ export const CardDetails = ({ incident, monitor, sx }: CardDetailsProps) => {
{!incident.status && (
<BaseBox padding={LAYOUT.MD}>
<Stack gap={theme.spacing(LAYOUT.XS)}>
<Typography textTransform={"uppercase"}>
<SectionHeading>
{t("pages.incidents.dialog.details.resolutionDetails")}
</Typography>
<Divider />
</SectionHeading>
<Grid
container
spacing={theme.spacing(LAYOUT.MD)}
alignItems="center"
>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.resolvedAt")}</Typography>
<Cell>{t("pages.incidents.dialog.details.resolvedAt")}</Cell>
</Grid>
<Grid size={10}>
<Typography>
<Cell>
{incident.endTime
? formatDateWithTz(incident.endTime, "D MMM YYYY, h:mm A", uiTimezone)
: "N/A"}
</Typography>
</Cell>
</Grid>
<Grid size={2}>
<Typography>
{t("pages.incidents.dialog.details.resolutionType")}
</Typography>
<Cell>{t("pages.incidents.dialog.details.resolutionType")}</Cell>
</Grid>
<Grid size={10}>
<Typography>
<Cell>
{incident.resolutionType
? t(
`pages.incidents.dialog.details.resolutionTypes.${incident.resolutionType}`
)
: "N/A"}
</Typography>
</Cell>
</Grid>
{incident.resolvedBy && (
<>
<Grid size={2}>
<Typography>
{t("pages.incidents.dialog.details.resolvedBy")}
</Typography>
<Cell>{t("pages.incidents.dialog.details.resolvedBy")}</Cell>
</Grid>
<Grid size={10}>
<Typography>
{incident.resolvedByEmail ?? incident.resolvedBy}
</Typography>
<Cell>{incident.resolvedByEmail ?? incident.resolvedBy}</Cell>
</Grid>
</>
)}
{incident.comment && (
<>
<Grid size={2}>
<Typography>{t("pages.incidents.dialog.details.comment")}</Typography>
<Cell>{t("pages.incidents.dialog.details.comment")}</Cell>
</Grid>
<Grid size={10}>
<Typography>{incident.comment}</Typography>
<Cell>{incident.comment}</Cell>
</Grid>
</>
)}
@@ -1,8 +1,7 @@
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Divider from "@mui/material/Divider";
import Grid from "@mui/material/Grid";
import { BaseBox, ValueLabel } from "@/Components/design-elements";
import { BaseBox, Icon, ValueLabel } from "@/Components/design-elements";
import { CircleCheck, TriangleAlert, Bell, Wrench, Globe } from "lucide-react";
import Box from "@mui/material/Box";
@@ -25,6 +24,7 @@ const SummaryItem = ({ icon, label, value }: SummaryItemProps) => {
alignItems="center"
justifyContent="space-between"
gap={theme.spacing(2)}
minHeight={32}
>
<Stack
direction="row"
@@ -32,10 +32,10 @@ const SummaryItem = ({ icon, label, value }: SummaryItemProps) => {
gap={theme.spacing(2)}
>
{icon}
<Typography variant="body2">{label}</Typography>
<Typography variant="body1">{label}</Typography>
</Stack>
<Typography
variant="body2"
variant="body1"
fontWeight={600}
>
{value}
@@ -67,15 +67,11 @@ export const SummaryCard = ({
>
<Typography
component="h2"
sx={{
textTransform: "uppercase",
fontWeight: 500,
fontSize: 13,
}}
variant="eyebrow"
color="text.secondary"
>
{title}
</Typography>
<Divider />
{children}
</Stack>
</BaseBox>
@@ -97,10 +93,13 @@ export const SummaryCardActiveIncidents = ({
const activeCount = summary.totalActive;
const hasActive = activeCount > 0;
const color = hasActive ? theme.palette.error.main : theme.palette.success.main;
const icon = hasActive ? (
<TriangleAlert color={color} />
) : (
<CircleCheck color={color} />
const icon = (
<Box sx={{ color, display: "inline-flex" }}>
<Icon
icon={hasActive ? TriangleAlert : CircleCheck}
size={32}
/>
</Box>
);
const msg = t("pages.incidents.summaryCard.activeIncidents.active", {
count: activeCount,
@@ -133,10 +132,7 @@ const SummaryIncidentItem = ({ incident }: { incident: IncidentSummaryItem }) =>
container
alignItems="center"
spacing={2}
sx={{
width: "100%",
py: theme.spacing(0.5),
}}
sx={{ width: "100%", minHeight: 32 }}
>
<Grid
size={{ xs: 12, lg: 5 }}
@@ -147,7 +143,7 @@ const SummaryIncidentItem = ({ incident }: { incident: IncidentSummaryItem }) =>
gap: theme.spacing(2),
}}
>
<Globe />
<Icon icon={Globe} />
<Typography
variant="body1"
fontWeight={500}
@@ -159,9 +155,7 @@ const SummaryIncidentItem = ({ incident }: { incident: IncidentSummaryItem }) =>
<Grid
size={{ xs: 12, md: 6, lg: 3 }}
sx={{
display: "flex",
}}
sx={{ display: "flex" }}
>
<ValueLabel
value={incident.status ? "negative" : "positive"}
@@ -173,7 +167,7 @@ const SummaryIncidentItem = ({ incident }: { incident: IncidentSummaryItem }) =>
size={{ xs: 12, md: 6, lg: 4 }}
sx={{
textAlign: { xs: "left", md: "right" },
fontWeight: 500,
fontWeight: 600,
}}
>
<Typography variant="body1">{duration}</Typography>
@@ -190,22 +184,17 @@ export const SummaryCardLatestIncidents = ({
summary,
}: SummaryCardLatestIncidentsProps) => {
const { t } = useTranslation();
const theme = useTheme();
const latestIncidents = summary?.latestIncidents ?? [];
return (
<SummaryCard title={t("pages.incidents.summaryCard.latestIncidents.title")}>
<Stack gap={theme.spacing(4)}>
{latestIncidents.slice(0, 3).map((incident, index) => (
<Box key={incident.id}>
<SummaryIncidentItem incident={incident} />
{index < latestIncidents.length - 1 && (
<Divider sx={{ mt: theme.spacing(2) }} />
)}
</Box>
))}
</Stack>
{latestIncidents.slice(0, 3).map((incident) => (
<SummaryIncidentItem
key={incident.id}
incident={incident}
/>
))}
</SummaryCard>
);
};
@@ -224,17 +213,17 @@ export const SummaryCardStats = ({ summary }: SummaryCardStatsProps) => {
return (
<SummaryCard title={t("pages.incidents.summaryCard.incidentStats.title")}>
<SummaryItem
icon={<Bell size={18} />}
icon={<Icon icon={Bell} />}
label={t("pages.incidents.summaryCard.incidentStats.totalIncidents")}
value={summary?.total || 0}
/>
<SummaryItem
icon={<TriangleAlert size={18} />}
icon={<Icon icon={TriangleAlert} />}
label={t("pages.incidents.summaryCard.incidentStats.mostAffectedMonitor")}
value={mostAffected}
/>
<SummaryItem
icon={<Wrench size={18} />}
icon={<Icon icon={Wrench} />}
label={t("pages.incidents.summaryCard.incidentStats.avgResolutionTime")}
value={
summary.total > 0
@@ -75,7 +75,7 @@ export const ControlsIncidentFilter = ({
variant="contained"
onClick={onClearFilters}
>
{t("pages.incidents.filters.clearFilters")}
{t("common.buttons.clearFilters")}
</Button>
)}
</Stack>
+7 -4
View File
@@ -15,6 +15,7 @@ import { HeaderTimeRange } from "@/Components/common";
import { ControlsIncidentFilter } from "@/Pages/Incidents/Components/ControlsIncidentFilter";
import { useGet } from "@/Hooks/UseApi";
import { LAYOUT } from "@/Utils/Theme/constants";
import { useState, useEffect, useMemo } from "react";
import { useParams } from "react-router-dom";
import type { Incident, IncidentsResponse, IncidentSummary } from "@/Types/Incident";
@@ -186,8 +187,9 @@ const IncidentsPage = () => {
{activeIncidentsCount > 0 && (
<>
<Typography
variant="h6"
sx={{ mb: theme.spacing(4), textTransform: "uppercase" }}
variant="eyebrow"
color="text.secondary"
sx={{ mb: theme.spacing(LAYOUT.XS) }}
>
{t("pages.incidents.table.activeIncidents")}
</Typography>
@@ -208,8 +210,9 @@ const IncidentsPage = () => {
)}
<Typography
variant="h6"
sx={{ mb: theme.spacing(4), textTransform: "uppercase" }}
variant="eyebrow"
color="text.secondary"
sx={{ mb: theme.spacing(LAYOUT.XS) }}
>
{t("pages.incidents.table.resolvedIncidents")}
</Typography>