This commit is contained in:
Alex Holliday committed 2025-11-19 20:53:25 +00:00
1 parent eea6b12be6
commit 459d78cbcb
11 files changed
+287 -15

No files matched your search

+17
View File
@@ -46,6 +46,7 @@
"eslint-plugin-react-refresh": "^0.4.22",
"globals": "^16.4.0",
"i18n-unused": "0.19.0",
"prettier": "^3.6.2",
"typescript": "~5.9.3",
"typescript-eslint": "^8.45.0",
"vite": "^7.1.7"
@@ -4522,6 +4523,22 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-ms": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz",
+1
View File
@@ -49,6 +49,7 @@
"eslint-plugin-react-refresh": "^0.4.22",
"globals": "^16.4.0",
"i18n-unused": "0.19.0",
"prettier": "^3.6.2",
"typescript": "~5.9.3",
"typescript-eslint": "^8.45.0",
"vite": "^7.1.7"
@@ -27,13 +27,26 @@ const PageSpeedMonitorsPage = () => {
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const [selectedMonitor, setSelectedMonitor] = useState<IMonitor | null>(null);
const isDialogOpen = Boolean(selectedMonitor);
const [sortField, setSortField] = useState<string>("name");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const { deleteFn } = useDelete();
const requestParams = [
"embedChecks=true",
"type=infrastructure",
`sortField=${sortField}`,
`sortOrder=${sortOrder}`,
`page=${page}`,
`rowsPerPage=${rowsPerPage}`,
];
const monitorRequestUrl = `/monitors?${requestParams.join("&")}`;
const { response, loading, isValidating, error, refetch } = useGet<
ApiResponse<IMonitorWithStats>
>(
`/monitors?embedChecks=true&type=infrastructure&page=${page}&rowsPerPage=${rowsPerPage}`,
monitorRequestUrl,
{},
{
refreshInterval: GLOBAL_REFRESH,
@@ -82,6 +95,10 @@ const PageSpeedMonitorsPage = () => {
monitors={monitors}
refetch={refetch}
setSelectedMonitor={setSelectedMonitor}
sortField={sortField}
setSortField={setSortField}
sortOrder={sortOrder}
setSortOrder={setSortOrder}
count={count}
page={page}
setPage={setPage}
@@ -4,6 +4,7 @@ import { Table, Pagination, StatusLabel } from "@/components/design-elements";
import type { Header } from "@/components/design-elements/Table";
import { ActionsMenu } from "@/components/actions-menu";
import { Gauge } from "@/components/design-elements";
import { ArrowUp, ArrowDown } from "lucide-react";
import { useTranslation } from "react-i18next";
import useMediaQuery from "@mui/material/useMediaQuery";
@@ -18,6 +19,10 @@ export const InfraMonitorsTable = ({
monitors,
refetch,
setSelectedMonitor,
sortField,
setSortField,
sortOrder,
setSortOrder,
count,
page,
setPage,
@@ -27,6 +32,10 @@ export const InfraMonitorsTable = ({
monitors: IMonitor[];
refetch: Function;
setSelectedMonitor: Function;
sortField: string;
setSortField: (field: string) => void;
sortOrder: "asc" | "desc";
setSortOrder: (order: "asc" | "desc") => void;
count: number;
page: number;
setPage: (page: number) => void;
@@ -58,6 +67,19 @@ export const InfraMonitorsTable = ({
setRowsPerPage(value);
};
const handleSort = (e: any, field: string) => {
e.preventDefault();
e.stopPropagation();
if (sortField === field) {
const newOrder = sortOrder === "asc" ? "desc" : "asc";
setSortOrder(newOrder);
} else {
setSortField(field);
setSortOrder("desc");
}
refetch();
};
const getActions = (monitor: IMonitor): ActionMenuItem[] => {
return [
{
@@ -117,17 +139,50 @@ export const InfraMonitorsTable = ({
};
const getHeaders = () => {
const renderSortIcon = (isActive: boolean) => (
<Box width={16} display="inline-flex" justifyContent="center">
{isActive ? (
sortOrder === "asc" ? (
<ArrowUp size={16} />
) : (
<ArrowDown size={16} />
)
) : null}
</Box>
);
const headers: Header<IMonitor>[] = [
{
id: "name",
content: t("host"),
content: (
<Typography
display="inline-flex"
alignItems="center"
gap={theme.spacing(4)}
onClick={(e) => handleSort(e, "name")}
sx={{ cursor: "pointer" }}
>
{t("host")}
{renderSortIcon(sortField === "name")}
</Typography>
),
render: (row) => {
return row.name;
},
},
{
id: "status",
content: t("status"),
content: (
<Typography
display="inline-flex"
alignItems="center"
gap={theme.spacing(4)}
onClick={(e) => handleSort(e, "status")}
sx={{ cursor: "pointer" }}
>
{t("status")}
{renderSortIcon(sortField === "status")}
</Typography>
),
render: (row) => {
return <StatusLabel status={row.status} />;
},
@@ -3,6 +3,7 @@ import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { Table, Pagination, StatusLabel } from "@/components/design-elements";
import { HistogramPageSpeed } from "@/components/monitors";
import { ArrowDown, ArrowUp } from "lucide-react";
import type { Header } from "@/components/design-elements/Table";
import { ActionsMenu } from "@/components/actions-menu";
@@ -19,6 +20,10 @@ export const PageSpeedMonitorTable = ({
monitors,
refetch,
setSelectedMonitor,
sortField,
setSortField,
sortOrder,
setSortOrder,
count,
page,
setPage,
@@ -28,6 +33,10 @@ export const PageSpeedMonitorTable = ({
monitors: IMonitor[];
refetch: Function;
setSelectedMonitor: Function;
sortField: string;
setSortField: (field: string) => void;
sortOrder: "asc" | "desc";
setSortOrder: (order: "asc" | "desc") => void;
count: number;
page: number;
setPage: (page: number) => void;
@@ -59,6 +68,19 @@ export const PageSpeedMonitorTable = ({
setRowsPerPage(value);
};
const handleSort = (e: any, field: string) => {
e.preventDefault();
e.stopPropagation();
if (sortField === field) {
const newOrder = sortOrder === "asc" ? "desc" : "asc";
setSortOrder(newOrder);
} else {
setSortField(field);
setSortOrder("desc");
}
refetch();
};
const getActions = (monitor: IMonitor): ActionMenuItem[] => {
return [
{
@@ -118,17 +140,52 @@ export const PageSpeedMonitorTable = ({
};
const getHeaders = () => {
const renderSortIcon = (isActive: boolean) => (
<Box width={16} display="inline-flex" justifyContent="center">
{isActive ? (
sortOrder === "asc" ? (
<ArrowUp size={16} />
) : (
<ArrowDown size={16} />
)
) : null}
</Box>
);
const headers: Header<IMonitor>[] = [
{
id: "name",
content: t("host"),
content: (
<Stack
gap={theme.spacing(4)}
direction={"row"}
alignItems={"center"}
onClick={(e) => handleSort(e, "name")}
sx={{ cursor: "pointer" }}
>
{t("host")}
{renderSortIcon(sortField === "name")}
</Stack>
),
render: (row) => {
return row.name;
},
},
{
id: "status",
content: t("status"),
content: (
<Stack
gap={theme.spacing(4)}
direction={"row"}
justifyContent={"center"}
alignItems={"center"}
onClick={(e) => handleSort(e, "status")}
sx={{ cursor: "pointer" }}
>
{t("status")}
{renderSortIcon(sortField === "status")}
</Stack>
),
render: (row) => {
return <StatusLabel status={row.status} />;
},
@@ -27,13 +27,26 @@ const PageSpeedMonitorsPage = () => {
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const [selectedMonitor, setSelectedMonitor] = useState<IMonitor | null>(null);
const isDialogOpen = Boolean(selectedMonitor);
const [sortField, setSortField] = useState<string>("name");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const { deleteFn } = useDelete();
const requestParams = [
"embedChecks=true",
"type=pagespeed",
`sortField=${sortField}`,
`sortOrder=${sortOrder}`,
`page=${page}`,
`rowsPerPage=${rowsPerPage}`,
];
const monitorRequestUrl = `/monitors?${requestParams.join("&")}`;
const { response, loading, isValidating, error, refetch } = useGet<
ApiResponse<IMonitorWithStats>
>(
`/monitors?embedChecks=true&type=pagespeed&page=${page}&rowsPerPage=${rowsPerPage}`,
monitorRequestUrl,
{},
{
refreshInterval: GLOBAL_REFRESH,
@@ -82,6 +95,10 @@ const PageSpeedMonitorsPage = () => {
monitors={monitors}
refetch={refetch}
setSelectedMonitor={setSelectedMonitor}
sortField={sortField}
setSortField={setSortField}
sortOrder={sortOrder}
setSortOrder={setSortOrder}
count={count}
page={page}
setPage={setPage}
+71 -4
View File
@@ -5,6 +5,7 @@ import { Table, Pagination, StatusLabel } from "@/components/design-elements";
import { HistogramResponseTime } from "@/components/monitors";
import type { Header } from "@/components/design-elements/Table";
import { ActionsMenu } from "@/components/actions-menu";
import { ArrowDown, ArrowUp } from "lucide-react";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material/styles";
@@ -18,6 +19,10 @@ export const MonitorTable = ({
monitors,
refetch,
setSelectedMonitor,
sortField,
setSortField,
sortOrder,
setSortOrder,
count,
page,
setPage,
@@ -27,6 +32,10 @@ export const MonitorTable = ({
monitors: IMonitor[];
refetch: Function;
setSelectedMonitor: Function;
sortField: string;
setSortField: (field: string) => void;
sortOrder: "asc" | "desc";
setSortOrder: (order: "asc" | "desc") => void;
count: number;
page: number;
setPage: (page: number) => void;
@@ -57,6 +66,19 @@ export const MonitorTable = ({
setRowsPerPage(value);
};
const handleSort = (e: any, field: string) => {
e.preventDefault();
e.stopPropagation();
if (sortField === field) {
const newOrder = sortOrder === "asc" ? "desc" : "asc";
setSortOrder(newOrder);
} else {
setSortField(field);
setSortOrder("desc");
}
refetch();
};
const getActions = (monitor: IMonitor): ActionMenuItem[] => {
return [
{
@@ -116,17 +138,50 @@ export const MonitorTable = ({
};
const getHeaders = () => {
const renderSortIcon = (isActive: boolean) => (
<ArrowUp
size={16}
style={{
visibility: isActive ? "visible" : "hidden",
transform: sortOrder === "asc" ? "none" : "rotate(180deg)",
}}
/>
);
const headers: Header<IMonitor>[] = [
{
id: "name",
content: t("host"),
content: (
<Stack
gap={theme.spacing(4)}
direction={"row"}
alignItems={"center"}
onClick={(e) => handleSort(e, "name")}
sx={{ cursor: "pointer" }}
>
{t("name")}
{renderSortIcon(sortField === "name")}
</Stack>
),
render: (row) => {
return row.name;
return row?.name;
},
},
{
id: "status",
content: t("status"),
content: (
<Stack
gap={theme.spacing(4)}
direction={"row"}
justifyContent={"center"}
alignItems={"center"}
onClick={(e) => handleSort(e, "status")}
sx={{ cursor: "pointer" }}
>
{t("status")}
{renderSortIcon(sortField === "status")}
</Stack>
),
render: (row) => {
return <StatusLabel status={row.status} />;
},
@@ -144,7 +199,19 @@ export const MonitorTable = ({
},
{
id: "type",
content: t("type"),
content: (
<Stack
gap={theme.spacing(4)}
direction={"row"}
justifyContent={"center"}
alignItems={"center"}
onClick={(e) => handleSort(e, "type")}
sx={{ cursor: "pointer" }}
>
{t("type")}
{renderSortIcon(sortField === "type")}
</Stack>
),
render: (row) => {
return row.type;
},
@@ -34,6 +34,8 @@ const UptimeMonitors = () => {
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const [selectedMonitor, setSelectedMonitor] = useState<IMonitor | null>(null);
const isDialogOpen = Boolean(selectedMonitor);
const [sortField, setSortField] = useState<string>("name");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selectedTypes, setSelectedTypes] = useState<UptimeMonitorType[]>([
@@ -66,6 +68,8 @@ const UptimeMonitors = () => {
"embedChecks=true",
typeQuery,
statusQuery,
`sortField=${sortField}`,
`sortOrder=${sortOrder}`,
`page=${page}`,
`rowsPerPage=${rowsPerPage}`,
].filter(Boolean);
@@ -140,6 +144,10 @@ const UptimeMonitors = () => {
monitors={monitors}
refetch={refetch}
setSelectedMonitor={setSelectedMonitor}
sortField={sortField}
setSortField={setSortField}
sortOrder={sortOrder}
setSortOrder={setSortOrder}
count={count}
page={page}
setPage={setPage}
@@ -75,6 +75,8 @@ class MonitorController {
let result;
if (req.validatedQuery.embedChecks === true) {
const sortField = req.validatedQuery.sortField || "name";
const sortOrder = req.validatedQuery.sortOrder || "desc";
const page = req.validatedQuery.page || 0;
const rowsPerPage = req.validatedQuery.rowsPerPage || 10;
const type = req.validatedQuery.type;
@@ -82,6 +84,8 @@ class MonitorController {
result = await this.monitorService.getAllEmbedChecks(
teamId,
sortField,
sortOrder,
page,
rowsPerPage,
type,
+32 -5
View File
@@ -18,6 +18,8 @@ export interface IMonitorService {
getAll: (teamId: string) => Promise<IMonitor[]>;
getAllEmbedChecks: (
teamId: string,
sortField: string,
sortOrder: string,
page: number,
limit: number,
type: MonitorType[],
@@ -87,6 +89,8 @@ class MonitorService implements IMonitorService {
getAllEmbedChecks = async (
teamId: string,
sortField: string,
sortOrder: string,
page: number,
rowsPerPage: number,
type: MonitorType[] = [],
@@ -134,7 +138,7 @@ class MonitorService implements IMonitorService {
const monitors = await Monitor.find(matchConditions)
.lean()
.sort({ name: 1 })
.sort({ [sortField]: sortOrder === "asc" ? 1 : -1 })
.skip(skip)
.limit(rowsPerPage);
@@ -157,7 +161,11 @@ class MonitorService implements IMonitorService {
latestChecks: { $push: "$$ROOT" },
},
},
{ $project: { latestChecks: { $slice: [{ $ifNull: ["$latestChecks", []] }, 25] } } },
{
$project: {
latestChecks: { $slice: [{ $ifNull: ["$latestChecks", []] }, 25] },
},
},
]);
const checksMap = new Map(
@@ -336,7 +344,14 @@ class MonitorService implements IMonitorService {
temperature: {
$map: {
input: {
$range: [0, { $size: { $ifNull: [{ $arrayElemAt: ["$tempsArrays", 0] }, []] } }],
$range: [
0,
{
$size: {
$ifNull: [{ $arrayElemAt: ["$tempsArrays", 0] }, []],
},
},
],
},
as: "idx",
in: {
@@ -362,7 +377,14 @@ class MonitorService implements IMonitorService {
disk: {
$map: {
input: {
$range: [0, { $size: { $ifNull: [{ $arrayElemAt: ["$disksArray", 0] }, []] } }],
$range: [
0,
{
$size: {
$ifNull: [{ $arrayElemAt: ["$disksArray", 0] }, []],
},
},
],
},
as: "idx",
in: {
@@ -406,7 +428,12 @@ class MonitorService implements IMonitorService {
net: {
$map: {
input: {
$range: [0, { $size: { $ifNull: [{ $arrayElemAt: ["$netsArray", 0] }, []] } }],
$range: [
0,
{
$size: { $ifNull: [{ $arrayElemAt: ["$netsArray", 0] }, []] },
},
],
},
as: "idx",
in: {
+2
View File
@@ -135,6 +135,8 @@ export const monitorAllEmbedChecksQuerySchema = z.object({
if (!val) return [];
return Array.isArray(val) ? val : [val];
}),
sortField: z.string().optional(),
sortOrder: z.string().optional(),
page: z
.string()
.optional()