This commit is contained in:
Alex Holliday committed 2026-01-23 21:30:34 +00:00
1 parent 69018a4944
commit c2dece862c
4 files changed
+208 -10

No files matched your search

@@ -1,11 +1,92 @@
import Stack from "@mui/material/Stack";
import { Select } from "@/Components/v2/inputs";
export const FilterControls = () => {
import { Select, Button } from "@/Components/v2/inputs";
import MenuItem from "@mui/material/MenuItem";
import type { MonitorType } from "@/Types/Monitor";
import { Typography, useTheme } from "@mui/material";
const types = ["http", "ping", "port", "docker"];
const statuses = ["up", "down"];
const states = ["active", "paused"];
export const FilterControls = ({
selectedTypes,
setSelectedTypes,
selectedStatus,
setSelectedStatus,
selectedState,
setSelectedState,
}: {
selectedTypes: MonitorType[];
setSelectedTypes: React.Dispatch<React.SetStateAction<MonitorType[]>>;
selectedStatus: string;
setSelectedStatus: React.Dispatch<React.SetStateAction<string>>;
selectedState: string;
setSelectedState: React.Dispatch<React.SetStateAction<string>>;
}) => {
const theme = useTheme();
const isFilterActive =
selectedTypes.length > 0 || selectedStatus !== "" || selectedState !== "";
return (
<Stack direction="row">
<Select placeholder="Type"></Select>
<Select placeholder="Status" />
<Select placeholder="Priority" />
<Stack
direction="row"
gap={theme.spacing(2)}
>
<Select
multiple
placeholder="Type"
value={selectedTypes}
onChange={(e) => setSelectedTypes(e.target.value as MonitorType[])}
>
{types.map((type) => (
<MenuItem
key={type}
value={type}
>
<Typography textTransform={"capitalize"}>{type}</Typography>
</MenuItem>
))}
</Select>
<Select
placeholder="Status"
value={selectedStatus}
onChange={(e) => setSelectedStatus(e.target.value)}
>
{statuses.map((status) => (
<MenuItem
key={status}
value={status}
>
<Typography textTransform={"capitalize"}>{status}</Typography>
</MenuItem>
))}
</Select>
<Select
placeholder="State"
value={selectedState}
onChange={(e) => setSelectedState(e.target.value)}
>
{states.map((state) => (
<MenuItem
key={state}
value={state}
>
<Typography textTransform={"capitalize"}>{state}</Typography>
</MenuItem>
))}
</Select>
{isFilterActive && (
<Button
variant="contained"
onClick={() => {
setSelectedTypes([]);
setSelectedStatus("");
setSelectedState("");
}}
>
Clear Filters
</Button>
)}
</Stack>
);
};
+51 -4
View File
@@ -1,24 +1,71 @@
import { useGet } from "@/Hooks/UseApi";
import { MonitorBasePageWithStates } from "@/Components/v2/design-elements";
import { type Monitor } from "@/Types/Monitor";
import type { Monitor, MonitorType, MonitorsWithChecksResponse } from "@/Types/Monitor";
import { FilterControls } from "@/Components/v2/monitors";
import { useState, useMemo } from "react";
import { useSelector } from "react-redux";
import type { RootState } from "@/Types/state";
const UptimeMonitorsPage = () => {
// Redux state
const rowsPerPage = useSelector((state: RootState) => state.ui.monitors.rowsPerPage);
// Local state
const [selectedTypes, setSelectedTypes] = useState<MonitorType[]>([]);
const [selectedStatus, setSelectedStatus] = useState<string>("");
const [selectedState, setSelectedState] = useState<string>("");
const [page, setPage] = useState(0);
const [search, setSearch] = useState("");
const [sort, setSort] = useState<
{ field: string; order: "asc" | "desc" } | undefined
>();
// Build URL for monitors with checks
const monitorsWithChecksUrl = useMemo(() => {
const params = new URLSearchParams();
const types =
selectedTypes.length > 0 ? selectedTypes : ["http", "ping", "docker", "port"];
types.forEach((type) => params.append("type", type));
params.append("limit", "25");
if (page) params.append("page", String(page));
if (rowsPerPage) params.append("rowsPerPage", String(rowsPerPage));
if (search) params.append("filter", search);
if (sort?.field) params.append("field", sort.field);
if (sort?.order) params.append("order", sort.order);
return `/monitors/team/with-checks?${params.toString()}`;
}, [selectedTypes, page, rowsPerPage, search, sort]);
// Data fetching
const {
data: monitors,
isLoading,
error,
} = useGet<Monitor[]>("/monitors/team?type=http&type=ping&type=port&type=docker");
const {
data: monitorsWithChecksData,
isLoading: monitorsWithChecksLoading,
error: monitorsWithChecksError,
} = useGet<MonitorsWithChecksResponse>(monitorsWithChecksUrl);
const { monitors: monitorsWithChecks, summary, count } = monitorsWithChecksData ?? {};
return (
<MonitorBasePageWithStates
loading={isLoading}
error={error}
loading={isLoading || monitorsWithChecksLoading}
error={error || monitorsWithChecksError}
items={monitors || []}
page="uptime"
actionLink="/uptime/create"
>
<FilterControls />
<FilterControls
selectedTypes={selectedTypes}
setSelectedTypes={setSelectedTypes}
selectedStatus={selectedStatus}
setSelectedStatus={setSelectedStatus}
selectedState={selectedState}
setSelectedState={setSelectedState}
/>
<pre>{JSON.stringify(monitors, null, 2)}</pre>
</MonitorBasePageWithStates>
);
+30
View File
@@ -1,6 +1,18 @@
import type { Check } from "@/Types/Check";
export type MonitorStatus = "up" | "down" | "initializing";
export const MonitorTypes = [
"http",
"ping",
"pagespeed",
"hardware",
"docker",
"port",
"game",
"unknown",
] as const;
export type MonitorType = (typeof MonitorTypes)[number];
export interface Monitor {
checks: Check[];
createdAt: string;
@@ -18,3 +30,21 @@ export interface Monitor {
__v: number;
_id: string;
}
export interface MonitorWithChecks extends Monitor {
checks: Check[];
uptimePercentage?: number;
}
export interface MonitorsSummary {
totalMonitors: number;
totalUp: number;
totalDown: number;
totalPaused: number;
}
export interface MonitorsWithChecksResponse {
count: number;
monitors: MonitorWithChecks[];
summary: MonitorsSummary;
}
+40
View File
@@ -0,0 +1,40 @@
export interface AuthState {
isLoading: boolean;
authToken: string;
user: string;
success: boolean | null;
msg: string | null;
}
export interface UIState {
monitors: {
rowsPerPage: number;
};
team: {
rowsPerPage: number;
};
maintenance: {
rowsPerPage: number;
};
infrastructure: {
rowsPerPage: number;
};
sidebar: {
collapsed: boolean;
};
mode: "light" | "dark";
showURL: boolean;
greeting: {
index: number;
lastUpdate: string | null;
};
timezone: string;
distributedUptimeEnabled: boolean;
language: string;
starPromptOpen: boolean;
}
export interface RootState {
auth: AuthState;
ui: UIState;
}