This commit is contained in:
Alex Holliday committed 2026-01-31 01:12:05 +00:00
1 parent 6b67f4977f
commit bcc57c7135
7 files changed
+143 -36

No files matched your search

+15 -2
View File
@@ -1,4 +1,5 @@
import Dialog from "@mui/material/Dialog";
import type { DialogProps } from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
@@ -6,6 +7,7 @@ import DialogTitle from "@mui/material/DialogTitle";
import { Button } from "@/Components/v2/inputs";
import { typographyLevels } from "@/Utils/Theme/v2Palette";
import { useTranslation } from "react-i18next";
import type { ReactNode } from "react";
export const DialogInput = ({
open,
@@ -18,6 +20,9 @@ export const DialogInput = ({
loading = false,
cancelText,
confirmText,
children,
maxWidth,
fullWidth = false,
}: {
open: boolean;
title?: string;
@@ -29,13 +34,21 @@ export const DialogInput = ({
loading?: boolean;
cancelText?: string;
confirmText?: string;
children?: ReactNode;
maxWidth?: DialogProps["maxWidth"];
fullWidth?: boolean;
}) => {
const { t } = useTranslation();
return (
<Dialog open={open}>
<Dialog
open={open}
maxWidth={maxWidth}
fullWidth={fullWidth}
>
<DialogTitle sx={{ fontSize: typographyLevels.l }}>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{content}</DialogContentText>
{content && <DialogContentText>{content}</DialogContentText>}
{children}
</DialogContent>
<DialogActions>
<Button
Whitespace-only changes.
@@ -0,0 +1,70 @@
import { useState, useEffect } from "react";
import Box from "@mui/material/Box";
import { Dialog, TextField } from "@/Components/v2/inputs";
import { usePut } from "@/Hooks/UseApi";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material";
interface DialogResolutionProps {
open: boolean;
incidentId: string | null;
onClose: () => void;
onResolved?: () => void;
}
export const DialogResolution = ({
open,
incidentId,
onClose,
onResolved,
}: DialogResolutionProps) => {
const { t } = useTranslation();
const theme = useTheme();
const [comment, setComment] = useState("");
const { put: resolveIncident, loading: isResolving } = usePut();
useEffect(() => {
if (open) {
setComment("");
}
}, [open]);
const handleCancel = () => {
onClose();
};
const handleConfirm = async () => {
if (!incidentId) return;
const body = comment.trim() ? { comment: comment.trim() } : {};
const result = await resolveIncident(`/incidents/${incidentId}/resolve`, body);
if (result) {
onClose();
onResolved?.();
}
};
return (
<Dialog
open={open}
title={t("incidentsPage.resolveIncidentDialogTitle")}
onCancel={handleCancel}
onConfirm={handleConfirm}
confirmText={t("incidentsPage.resolveIncidentDialogConfirm")}
confirmColor="error"
cancelColor="primary"
loading={isResolving}
maxWidth="sm"
fullWidth
>
<Box sx={{ mt: theme.spacing(4) }}>
<TextField
fieldLabel={t("incidentsPage.resolveIncidentDialogCommentLabel")}
placeholder={t("incidentsPage.resolveIncidentDialogCommentPlaceholder")}
value={comment}
onChange={(e) => setComment(e.target.value)}
fullWidth
/>
</Box>
</Dialog>
);
};
Whitespace-only changes.
@@ -1,7 +1,7 @@
import { Table } from "@/Components/v2/design-elements";
import { Pagination } from "@/Components/v2/design-elements/Table";
import type { Header } from "@/Components/v2/design-elements/Table";
import { StatusLabel, ValueLabel } from "@/Components/v2/design-elements";
import { ValueLabel } from "@/Components/v2/design-elements";
import type { ValueType } from "@/Components/v2/design-elements/StatusLabel";
import { ActionsMenu } from "@/Components/v2/actions-menu";
import type { Incident } from "@/Types/Incident";
@@ -52,13 +52,13 @@ export const IncidentsTable = ({
const actions: ActionMenuItem[] = [
{
id: "details",
label: t("incidentsPage.incidentsTableActionDetails"),
label: t("pages.incidents.table.actions.details"),
action: () => onOpenDetails?.(incident.id),
closeMenu: true,
},
{
id: "goToMonitor",
label: t("incidentsPage.incidentsTableActionGoToMonitor"),
label: t("pages.incidents.table.actions.goToMonitor"),
action: () => {
if (monitor) {
const path = TypeToPathMap[monitor.type as keyof typeof TypeToPathMap];
@@ -74,7 +74,7 @@ export const IncidentsTable = ({
if (isActive) {
actions.push({
id: "resolve",
label: t("incidentsPage.incidentsTableActionResolveManually"),
label: t("pages.incidents.table.actions.resolveManually"),
action: () => onResolve?.(incident.id),
closeMenu: true,
});
@@ -97,13 +97,18 @@ export const IncidentsTable = ({
id: "status",
content: t("common.table.headers.status"),
render: (row) => {
const status = row.status === true ? false : true;
return <StatusLabel status={status} />;
const isActive = row.status === true;
return (
<ValueLabel
value={isActive ? "negative" : "positive"}
text={isActive ? t("pages.incidents.table.status.active") : t("pages.incidents.table.status.resolved")}
/>
);
},
},
{
id: "startTime",
content: t("incidentsPage.startTime"),
content: t("pages.incidents.table.headers.startTime"),
render: (row) => {
return formatDateWithTz(
row.createdAt,
@@ -114,7 +119,7 @@ export const IncidentsTable = ({
},
{
id: "endTime",
content: t("incidentsPage.endTime"),
content: t("pages.incidents.table.headers.endTime"),
render: (row) => {
if (row.endTime) {
return formatDateWithTz(
@@ -128,7 +133,7 @@ export const IncidentsTable = ({
},
{
id: "resolutionType",
content: t("incidentsPage.resolutionType"),
content: t("pages.incidents.table.headers.resolutionType"),
render: (row) => {
if (row.resolutionType) {
return (
@@ -151,7 +156,7 @@ export const IncidentsTable = ({
},
{
id: "statusCode",
content: t("incidentsPage.statusCode"),
content: t("pages.incidents.table.headers.statusCode"),
render: (row) => {
const code = row.statusCode;
if (!code) return "N/A";
+28 -23
View File
@@ -6,6 +6,7 @@ import {
SummaryCardStats,
} from "@/Pages/Incidents/Components/SummaryCard";
import { IncidentsTable } from "@/Pages/Incidents/Components/IncidentTable";
import { DialogResolution } from "@/Pages/Incidents/Components/DialogResolution";
import { useGet } from "@/Hooks/UseApi";
import { useState, useEffect, useMemo } from "react";
@@ -14,10 +15,6 @@ import type { IncidentsResponse, IncidentSummary } from "@/Types/Incident";
import type { Monitor } from "@/Types/Monitor";
import { useTheme } from "@mui/material";
interface MonitorLookup {
[key: string]: { id: string; name: string; type: string };
}
const IncidentsPage = () => {
const theme = useTheme();
const { monitorId } = useParams();
@@ -29,6 +26,10 @@ const IncidentsPage = () => {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
// Resolve dialog state
const [isResolveDialogOpen, setIsResolveDialogOpen] = useState(false);
const [resolveIncidentId, setResolveIncidentId] = useState<string | null>(null);
// Build incidents URL with query params
const incidentsUrl = useMemo(() => {
const params = new URLSearchParams();
@@ -50,7 +51,11 @@ const IncidentsPage = () => {
isLoading: isLoadingIncidents,
error: incidentsError,
refetch: refetchIncidents,
} = useGet<IncidentsResponse>(incidentsUrl);
} = useGet<IncidentsResponse>(
incidentsUrl,
{},
{ keepPreviousData: true, refreshInterval: 10000 }
);
// Fetch monitors for lookup
const {
@@ -67,19 +72,6 @@ const IncidentsPage = () => {
refetch: refetchSummary,
} = useGet<IncidentSummary>("/incidents/team/summary");
// Build monitor lookup
const monitorLookup = useMemo<MonitorLookup | undefined>(() => {
if (!monitorsData) return undefined;
return monitorsData.reduce<MonitorLookup>((acc, monitor) => {
acc[monitor.id] = {
id: monitor.id,
name: monitor.name,
type: monitor.type,
};
return acc;
}, {});
}, [monitorsData]);
// Reset page when filters change
useEffect(() => {
setPage(0);
@@ -100,10 +92,6 @@ const IncidentsPage = () => {
void isLoadingIncidents;
void isLoadingMonitors;
void isLoadingSummary;
void refetchIncidents;
void refetchSummary;
void monitorLookup;
void summaryData;
void networkError;
const handleOpenDetails = (incidentId: string) => {
@@ -111,7 +99,18 @@ const IncidentsPage = () => {
};
const handleResolve = (incidentId: string) => {
console.log("Resolve incident:", incidentId);
setResolveIncidentId(incidentId);
setIsResolveDialogOpen(true);
};
const handleResolveClose = () => {
setIsResolveDialogOpen(false);
setResolveIncidentId(null);
};
const handleResolved = () => {
refetchIncidents();
refetchSummary();
};
return (
@@ -138,6 +137,12 @@ const IncidentsPage = () => {
onOpenDetails={handleOpenDetails}
onResolve={handleResolve}
/>
<DialogResolution
open={isResolveDialogOpen}
incidentId={resolveIncidentId}
onClose={handleResolveClose}
onResolved={handleResolved}
/>
</BasePage>
);
};
+15 -1
View File
@@ -368,7 +368,21 @@
}
},
"table": {
"headers": {}
"headers": {
"startTime": "Start Time",
"endTime": "End Time",
"resolutionType": "Resolution Type",
"statusCode": "Status Code"
},
"status": {
"active": "Active",
"resolved": "Resolved"
},
"actions": {
"details": "Details",
"goToMonitor": "Go to monitor",
"resolveManually": "Resolve Manually"
}
}
}
},