mirror of
https://github.com/moghtech/komodo.git
synced 2026-08-01 18:59:59 -05:00
alert details
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { Section } from "@components/layouts";
|
||||
import { ResourceComponents } from "@components/resources";
|
||||
import { ResourceLink } from "@components/util";
|
||||
import {
|
||||
alert_level_intention,
|
||||
text_color_class_by_intention,
|
||||
} from "@lib/color";
|
||||
import { fmt_date_with_minutes } from "@lib/formatting";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { Types } from "@monitor/client";
|
||||
import { UsableResource } from "@types";
|
||||
import { Button } from "@ui/button";
|
||||
import { DataTable } from "@ui/data-table";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@ui/dialog";
|
||||
import { useAtom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
const openAtom = atomWithStorage("show-alerts-v0", true);
|
||||
|
||||
export const OpenAlerts = () => {
|
||||
const [open, setOpen] = useAtom(openAtom);
|
||||
const alerts = useRead("ListAlerts", { query: { resolved: false } }).data
|
||||
?.alerts;
|
||||
if (!alerts || alerts.length === 0) return null;
|
||||
return (
|
||||
<Section
|
||||
title="Open Alerts"
|
||||
icon={<AlertTriangle className="w-4 h-4" />}
|
||||
actions={
|
||||
<Button variant="ghost" onClick={() => setOpen(!open)}>
|
||||
{open ? "close" : "open"}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{open && (
|
||||
<DataTable
|
||||
data={alerts ?? []}
|
||||
columns={[
|
||||
{
|
||||
header: "Details",
|
||||
cell: ({ row }) =>
|
||||
row.original._id?.$oid && (
|
||||
<AlertDetailsDialog id={row.original._id?.$oid} />
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "Target",
|
||||
cell: ({ row }) => {
|
||||
switch (row.original.target.type) {
|
||||
case "Server":
|
||||
return (
|
||||
<ResourceComponents.Server.Link
|
||||
id={row.original.target.id}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Level",
|
||||
cell: ({ row }) => <AlertLevel level={row.original.level} />,
|
||||
},
|
||||
{
|
||||
header: "Alert",
|
||||
accessorKey: "variant",
|
||||
},
|
||||
{
|
||||
header: "Open Since",
|
||||
accessorFn: ({ ts }) => fmt_date_with_minutes(new Date(ts)),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AlertLevel = ({ level }: { level: Types.SeverityLevel }) => {
|
||||
return (
|
||||
<div
|
||||
className={text_color_class_by_intention(alert_level_intention(level))}
|
||||
>
|
||||
{level}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AlertDetailsDialog = ({ id }: { id: string }) => {
|
||||
const [open, set] = useState(false);
|
||||
const alert = useRead("ListAlerts", {}).data?.alerts.find(
|
||||
(alert) => alert._id?.$oid === id
|
||||
);
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={set}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="secondary" className="items-center gap-2">
|
||||
Details
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
{alert && (
|
||||
<>
|
||||
<DialogHeader className="flex-row justify-between w-full">
|
||||
{alert && (
|
||||
<>
|
||||
<div className="flex gap-4 items-center">
|
||||
<ResourceLink
|
||||
type={alert.target.type as UsableResource}
|
||||
id={alert.target.id}
|
||||
/>
|
||||
<AlertLevel level={alert.level} />
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
{fmt_date_with_minutes(new Date(alert.ts))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</DialogHeader>
|
||||
<pre>{JSON.stringify(alert.data, undefined, 2)}</pre>
|
||||
</>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { Button } from "@ui/button";
|
||||
import { Input } from "@ui/input";
|
||||
import { Switch } from "@ui/switch";
|
||||
import {
|
||||
CheckCircle,
|
||||
ChevronsUpDown,
|
||||
MinusCircle,
|
||||
PlusCircle,
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
CommandList,
|
||||
} from "@ui/command";
|
||||
import { snake_case_to_upper_space_case } from "@lib/formatting";
|
||||
import { ConfirmButton } from "@components/util";
|
||||
|
||||
export const ConfigItem = ({
|
||||
label,
|
||||
@@ -350,14 +352,14 @@ export const ConfirmUpdate = ({ content, onConfirm }: ConfirmUpdateProps) => {
|
||||
<pre className="h-[300px] overflow-auto">{content}</pre>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
<ConfirmButton
|
||||
title="Update"
|
||||
icon={<CheckCircle className="w-4 h-4" />}
|
||||
onClick={() => {
|
||||
onConfirm();
|
||||
set(false);
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
/>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@ui/dropdown-menu";
|
||||
import { Types } from "@monitor/client";
|
||||
import { AUTH_TOKEN_STORAGE_KEY } from "@main";
|
||||
import { UsableResource } from "@types";
|
||||
import { ResourceComponents } from "./resources";
|
||||
@@ -335,24 +334,6 @@ export const CopyButton = ({ content }: { content: string | undefined }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const alert_level_color = (level: Types.SeverityLevel) => {
|
||||
if (level === Types.SeverityLevel.Ok) return "green-500";
|
||||
if (level === Types.SeverityLevel.Warning) return "orange-500";
|
||||
if (level === Types.SeverityLevel.Critical) return "red-500";
|
||||
};
|
||||
|
||||
// const alert_level_fill_color = (level: Types.SeverityLevel) => {
|
||||
// return `fill-${alert_level_color(level)}`;
|
||||
// };
|
||||
|
||||
const alert_level_text_color = (level: Types.SeverityLevel) => {
|
||||
return `text-${alert_level_color(level)}`;
|
||||
};
|
||||
|
||||
export const AlertLevel = ({ level }: { level: Types.SeverityLevel }) => {
|
||||
return <div className={alert_level_text_color(level)}>{level}</div>;
|
||||
};
|
||||
|
||||
export const ResourceLink = ({
|
||||
type,
|
||||
id,
|
||||
|
||||
@@ -100,3 +100,13 @@ export const deployment_state_intention: (
|
||||
return "Critical";
|
||||
}
|
||||
};
|
||||
|
||||
export const alert_level_intention: (
|
||||
level: Types.SeverityLevel
|
||||
) => ColorIntention = (level) => {
|
||||
switch (level) {
|
||||
case Types.SeverityLevel.Ok: return "Good"
|
||||
case Types.SeverityLevel.Warning: return "Warning"
|
||||
case Types.SeverityLevel.Critical: return "Critical"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,11 @@
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { Page, Section } from "@components/layouts";
|
||||
import { AlertTriangle, Box, FolderTree } from "lucide-react";
|
||||
import { DataTable } from "@ui/data-table";
|
||||
import { Box, FolderTree } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { AlertLevel } from "@components/util";
|
||||
import { Button } from "@ui/button";
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
|
||||
import { TagsSummary } from "@components/dashboard/tags";
|
||||
import { ApiKeysSummary } from "@components/dashboard/api-keys";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { useAtom } from "jotai";
|
||||
import { fmt_date_with_minutes } from "@lib/formatting";
|
||||
import { ResourceComponents } from "@components/resources";
|
||||
import { OpenAlerts } from "@components/alert";
|
||||
|
||||
export const Dashboard = () => {
|
||||
return (
|
||||
@@ -23,63 +17,6 @@ export const Dashboard = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const openAtom = atomWithStorage("show-alerts-v0", true);
|
||||
|
||||
const OpenAlerts = () => {
|
||||
const [open, setOpen] = useAtom(openAtom);
|
||||
const alerts = useRead("ListAlerts", { query: { resolved: false } }).data
|
||||
?.alerts;
|
||||
if (!alerts || alerts.length === 0) return null;
|
||||
return (
|
||||
<Section
|
||||
title="Open Alerts"
|
||||
icon={<AlertTriangle className="w-4 h-4" />}
|
||||
actions={
|
||||
<Button variant="ghost" onClick={() => setOpen(!open)}>
|
||||
{open ? "close" : "open"}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{open && (
|
||||
<DataTable
|
||||
data={alerts ?? []}
|
||||
columns={[
|
||||
{
|
||||
header: "Target",
|
||||
cell: ({ row }) => {
|
||||
switch (row.original.target.type) {
|
||||
case "Server":
|
||||
return (
|
||||
<Link to={`/servers/${row.original.target.id}`}>
|
||||
<ResourceComponents.Server.Name
|
||||
id={row.original.target.id}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Level",
|
||||
cell: ({ row }) => <AlertLevel level={row.original.level} />,
|
||||
},
|
||||
{
|
||||
header: "Alert",
|
||||
accessorKey: "variant",
|
||||
},
|
||||
{
|
||||
header: "Open Since",
|
||||
accessorFn: ({ ts }) => fmt_date_with_minutes(new Date(ts)),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const Resources = () => (
|
||||
<Section title="Resources" icon={<Box className="w-4 h-4" />} actions="">
|
||||
<div className="flex flex-col lg:flex-row gap-4 w-full">
|
||||
|
||||
Reference in New Issue
Block a user