front end

This commit is contained in:
Alex Holliday committed 2026-02-22 23:25:04 +00:00
1 parent b4f8fa7d72
commit 19fc85ae2d
5 files changed
+123 -1

No files matched your search

+3
View File
@@ -14,6 +14,9 @@ const getBaseDefaults = (data?: Monitor | null) => ({
notifications: data?.notifications || [],
statusWindowSize: data?.statusWindowSize || 5,
statusWindowThreshold: data?.statusWindowThreshold || 60,
geoCheckEnabled: data?.geoCheckEnabled || false,
geoCheckLocations: data?.geoCheckLocations || [],
geoCheckInterval: data?.geoCheckInterval || 300000,
});
export const useMonitorForm = ({
+86 -1
View File
@@ -29,7 +29,8 @@ import {
} from "@/Components/inputs";
import { useGet, usePost, usePatch, useDelete } from "@/Hooks/UseApi";
import { useMonitorForm } from "@/Hooks/useMonitorForm";
import type { Monitor, MonitorType, GamesMap } from "@/Types/Monitor";
import type { Monitor, MonitorType, GamesMap, GeoCheckLocation } from "@/Types/Monitor";
import { GeoCheckLocations } from "@/Types/Monitor";
import type { Notification } from "@/Types/Notification";
import type { MonitorFormData } from "@/Validation/monitor";
@@ -165,6 +166,7 @@ const CreateMonitorPage = () => {
const watchedType = watch("type") as MonitorType;
const watchedUseAdvancedMatching = watch("useAdvancedMatching") as boolean;
const watchGeoCheckEnabled = watch("geoCheckEnabled") as boolean;
useEffect(() => {
clearErrors();
@@ -823,6 +825,89 @@ const CreateMonitorPage = () => {
/>
)}
{/* Geo-distributed Checks Configuration */}
{watchedType === "http" && (
<ConfigBox
title={t("pages.createMonitor.form.geoChecks.title")}
subtitle={t("pages.createMonitor.form.geoChecks.description")}
rightContent={
<Stack spacing={theme.spacing(8)}>
<Controller
name="geoCheckEnabled"
control={control}
render={({ field }) => (
<Stack
direction="row"
alignItems="center"
spacing={theme.spacing(2)}
>
<Switch
checked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
<Typography>
{t("pages.createMonitor.form.geoChecks.option.enabled.label")}
</Typography>
</Stack>
)}
/>
{watchGeoCheckEnabled && (
<Stack spacing={theme.spacing(8)}>
<Controller
name="geoCheckLocations"
control={control}
render={({ field }) => {
const locationOptions = GeoCheckLocations.map((loc) => ({
id: loc,
name: t(
`pages.createMonitor.form.geoChecks.option.locations.options.${loc}`
),
}));
const selectedLocations = locationOptions.filter((loc) =>
(field.value ?? []).includes(loc.id)
);
return (
<Autocomplete
multiple={true}
options={locationOptions}
value={selectedLocations}
getOptionLabel={(option) => option.name}
onChange={(_, newValue: typeof locationOptions) => {
field.onChange(newValue.map((loc) => loc.id));
}}
isOptionEqualToValue={(option, value) => option.id === value.id}
fieldLabel={t(
"pages.createMonitor.form.geoChecks.option.locations.label"
)}
/>
);
}}
/>
<Controller
name="geoCheckInterval"
control={control}
render={({ field }) => (
<Select
value={field.value ?? 300000}
onChange={(e) => field.onChange(Number(e.target.value))}
fieldLabel={t(
"pages.createMonitor.form.geoChecks.option.interval.label"
)}
>
<MenuItem value={300000}>5 min</MenuItem>
<MenuItem value={600000}>10 min</MenuItem>
<MenuItem value={900000}>15 min</MenuItem>
<MenuItem value={1800000}>30 min</MenuItem>
</Select>
)}
/>
</Stack>
)}
</Stack>
}
/>
)}
<Stack
direction="row"
justifyContent="flex-end"
+6
View File
@@ -25,6 +25,9 @@ export type MonitorStatus = (typeof MonitorStatuses)[number];
export type MonitorMatchMethod = "equal" | "include" | "regex" | "";
export const GeoCheckLocations = ["EU", "NA", "AS", "SA", "AF", "OC"] as const;
export type GeoCheckLocation = (typeof GeoCheckLocations)[number];
export interface Monitor {
id: string;
userId: string;
@@ -59,6 +62,9 @@ export interface Monitor {
selectedDisks: string[];
gameId?: string;
group: string | null;
geoCheckEnabled?: boolean;
geoCheckLocations?: GeoCheckLocation[];
geoCheckInterval?: number;
recentChecks: CheckSnapshot[];
createdAt: string;
updatedAt: string;
+3
View File
@@ -20,6 +20,9 @@ const baseSchema = z.object({
.number({ message: "Threshold percentage is required" })
.min(1, "Incident percentage must be at least 1")
.max(100, "Incident percentage must be at most 100"),
geoCheckEnabled: z.boolean().optional(),
geoCheckLocations: z.array(z.enum(["EU", "NA", "AS", "SA", "AF", "OC"])).optional(),
geoCheckInterval: z.number().min(5, "Interval must be at least 5 minutes").optional(),
});
// HTTP monitor schema
+25
View File
@@ -431,6 +431,31 @@
},
"title": "Advanced settings"
},
"geoChecks": {
"title": "Geo-distributed checks",
"description": "Run checks from multiple geographic locations to monitor global availability and performance.",
"option": {
"enabled": {
"label": "Enable geo-distributed checks"
},
"locations": {
"label": "Locations",
"placeholder": "Select locations",
"options": {
"EU": "Europe",
"NA": "North America",
"AS": "Asia",
"SA": "South America",
"AF": "Africa",
"OC": "Oceania"
}
},
"interval": {
"label": "Check interval (minutes)",
"helper": "Minimum 5 minutes between geo checks"
}
}
},
"frequency": {
"description": "How often do you want to check the status of this monitor?",
"option": {