feat(client): split uptime monitor creation into a 3-step wizard

This commit is contained in:
Br0wnHammer committed 2026-05-27 12:07:07 +05:30
1 parent 967e415b0a
commit 37dfc67e33
2 files changed
+38 -27

No files matched your search

+27 -26
View File
@@ -47,7 +47,7 @@ import {
} from "@/Types/Monitor";
import type { Notification } from "@/Types/Notification";
import type { Tag } from "@/Types/Tag";
import type { MonitorFormData } from "@/Validation/monitor";
import { monitorFieldsByType, type MonitorFormData } from "@/Validation/monitor";
interface GeneralSettingsConfig {
urlLabel: string;
@@ -218,30 +218,31 @@ const getGeneralSettingsConfig = (
const hasAdvancedStep = (type: MonitorType): boolean =>
type === "http" || type === "grpc" || type === "websocket" || supportsGeoCheck(type);
// Fields validated before advancing past each wizard step (uptime create only).
const STEP_FIELDS: FieldPath<MonitorFormData>[][] = [
[
"type",
"url",
"name",
"port",
"gameId",
"grpcServiceName",
"dnsServer",
"dnsRecordType",
],
["interval", "statusWindowSize", "statusWindowThreshold", "notifications", "tags"],
[
"ignoreTlsErrors",
"useAdvancedMatching",
"matchMethod",
"expectedValue",
"jsonPath",
"geoCheckEnabled",
"geoCheckLocations",
"geoCheckInterval",
],
];
// The wizard step on which each field is validated. Fields not listed default
// to step 0, so a field added to the schema is validated by default rather than
// silently skipped. Step membership is the only thing hardcoded here; the field
// set itself is derived from the schema via `monitorFieldsByType`.
const FIELD_STEP: Record<string, number> = {
interval: 1,
statusWindowSize: 1,
statusWindowThreshold: 1,
notifications: 1,
tags: 1,
ignoreTlsErrors: 2,
useAdvancedMatching: 2,
matchMethod: 2,
expectedValue: 2,
jsonPath: 2,
geoCheckEnabled: 2,
geoCheckLocations: 2,
geoCheckInterval: 2,
};
// Exactly the selected type's fields that belong to the given step.
const stepFieldsFor = (type: MonitorType, step: number): FieldPath<MonitorFormData>[] =>
(monitorFieldsByType[type] ?? []).filter(
(field) => (FIELD_STEP[field] ?? 0) === step
) as FieldPath<MonitorFormData>[];
const CreateMonitorPage = () => {
const theme = useTheme();
@@ -304,7 +305,7 @@ const CreateMonitorPage = () => {
const totalSteps = hasAdvancedStep(watchedType) ? 3 : 2;
const handleNext = async () => {
const isValid = await trigger(STEP_FIELDS[currentStep]);
const isValid = await trigger(stepFieldsFor(watchedType, currentStep));
if (isValid) {
setCurrentStep((step) => step + 1);
}
+11 -1
View File
@@ -1,6 +1,6 @@
import { z } from "zod";
import { GeoContinents } from "@/Types/GeoCheck";
import { DnsRecordTypes, PageSpeedStrategies } from "@/Types/Monitor";
import { DnsRecordTypes, PageSpeedStrategies, type MonitorType } from "@/Types/Monitor";
// URL schema with custom error message
const urlSchema = z.url({ message: "Please enter a valid URL" });
@@ -164,6 +164,16 @@ export const monitorSchema = z.discriminatedUnion("type", [
export type MonitorFormData = z.infer<typeof monitorSchema>;
// Field names present in each monitor type's schema, derived from the schema
// itself. Consumers (e.g. the create wizard's per-step validation) should read
// this rather than hardcoding field lists, so renames/additions stay in sync.
export const monitorFieldsByType = Object.fromEntries(
monitorSchema.options.map((option) => [
option.shape.type.value,
Object.keys(option.shape),
])
) as Record<MonitorType, string[]>;
// Type-specific schemas exported for individual use
export {
httpSchema,