From 0872fd58182449eb65545935ab80e65e921b08af Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 17 Apr 2026 15:38:38 -0700 Subject: [PATCH] Make the healch checks tabs --- messages/en-US.json | 6 +- src/components/HealthCheckCredenza.tsx | 926 ++++++++++++++++++++++++- 2 files changed, 916 insertions(+), 16 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index f3fd12900..633f84484 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -3043,5 +3043,9 @@ "healthCheckStrategyHttp": "Validates connectivity and checks the HTTP response status.", "healthCheckStrategyTcp": "Verifies TCP connectivity only, without inspecting the response.", "healthCheckStrategySnmp": "Makes an SNMP get request to check the health of network devices and infrastructure.", - "healthCheckStrategyIcmp": "Uses ICMP echo requests (pings) to check if a resource is reachable and responsive." + "healthCheckStrategyIcmp": "Uses ICMP echo requests (pings) to check if a resource is reachable and responsive.", + "healthCheckTabStrategy": "Strategy", + "healthCheckTabConnection": "Connection", + "healthCheckTabAdvanced": "Advanced", + "healthCheckStrategyNotAvailable": "This strategy is not available. Please contact sales to enable this feature." } diff --git a/src/components/HealthCheckCredenza.tsx b/src/components/HealthCheckCredenza.tsx index f575784f4..ac109710e 100644 --- a/src/components/HealthCheckCredenza.tsx +++ b/src/components/HealthCheckCredenza.tsx @@ -5,8 +5,27 @@ import { Button } from "@/components/ui/button"; import { z } from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Form } from "@/components/ui/form"; -import { HealthCheckFormFields } from "@app/components/HealthCheckFormFields"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@/components/ui/select"; +import { StrategySelect } from "@app/components/StrategySelect"; +import { HeadersInput } from "@app/components/HeadersInput"; +import { HorizontalTabs } from "@app/components/HorizontalTabs"; import { Credenza, CredenzaBody, @@ -21,6 +40,8 @@ import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useTranslations } from "next-intl"; +import { ExternalLink, KeyRound } from "lucide-react"; +import Link from "next/link"; export type HealthCheckConfig = { hcEnabled: boolean; @@ -294,6 +315,17 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) { } }; + const handleChange = ( + fieldName: string, + value: any, + fieldOnChange: (v: any) => void + ) => { + fieldOnChange(value); + if (mode === "autoSave") { + handleFieldChange(fieldName, value); + } + }; + const onSubmit = async (values: FormValues) => { if (mode !== "submit") return; const { initialValues, onSaved } = props; @@ -362,6 +394,10 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) { }) : t("standaloneHcDescription"); + const showFields = mode === "submit" || watchedEnabled; + const isSnmpOrIcmp = watchedMode === "snmp" || watchedMode === "icmp"; + const isTcp = watchedMode === "tcp"; + return ( @@ -378,20 +414,880 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) { ? form.handleSubmit(onSubmit) : undefined } - className="space-y-6" > - + + {/* ── Strategy tab ──────────────────────── */} +
+ {/* Name (submit mode only) */} + {mode === "submit" && ( + ( + + + {t( + "standaloneHcNameLabel" + )} + + + + + + + )} + /> + )} + + {/* Enable toggle (autoSave mode only) */} + {mode === "autoSave" && ( + ( + +
+ + {t( + "enableHealthChecks" + )} + +
+ + + handleChange( + "hcEnabled", + value, + field.onChange + ) + } + /> + +
+ )} + /> + )} + + {/* Strategy picker */} + {showFields && ( + ( + + + + handleChange( + "hcMode", + value, + field.onChange + ) + } + /> + + + + )} + /> + )} + + {/* Contact-sales banner for SNMP / ICMP */} + {showFields && isSnmpOrIcmp && ( +
+
+
+ + + Contact sales to enable + this feature.{" "} + + Book a demo + + + {" or "} + + contact us + + + . + +
+
+
+ )} +
+ + {/* ── Connection tab ────────────────────── */} +
+ {!showFields && ( +

+ {t("enableHealthChecks")} +

+ )} + + {showFields && !isSnmpOrIcmp && ( + <> + {/* Scheme / Hostname / Port */} + {isTcp ? ( +
+ ( + + + {t( + "healthHostname" + )} + + + + handleChange( + "hcHostname", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> + + + + )} + /> + ( + + + {t( + "healthPort" + )} + + + + handleChange( + "hcPort", + e + .target + .value, + field.onChange + ) + } + /> + + + + )} + /> +
+ ) : ( +
+ ( + + + {t( + "healthScheme" + )} + + + + + )} + /> + ( + + + {t( + "healthHostname" + )} + + + + handleChange( + "hcHostname", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> + + + + )} + /> + ( + + + {t( + "healthPort" + )} + + + + handleChange( + "hcPort", + e + .target + .value, + field.onChange + ) + } + /> + + + + )} + /> +
+ )} + + {/* Method / Path / Timeout (HTTP) */} + {!isTcp && ( +
+ ( + + + {t( + "httpMethod" + )} + + + + + )} + /> + ( + + + {t( + "healthCheckPath" + )} + + + + handleChange( + "hcPath", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> + + + + )} + /> + ( + + + {t( + "timeoutSeconds" + )} + + + + handleChange( + "hcTimeout", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> +
+ )} + + {/* Timeout for TCP */} + {isTcp && ( + ( + + + {t( + "timeoutSeconds" + )} + + + + handleChange( + "hcTimeout", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> + )} + + )} + + {showFields && isSnmpOrIcmp && ( +

+ {t("healthCheckStrategyNotAvailable")} +

+ )} +
+ + {/* ── Advanced tab ──────────────────────── */} +
+ {!showFields && ( +

+ {t("enableHealthChecks")} +

+ )} + + {showFields && !isSnmpOrIcmp && ( + <> + {/* Healthy interval + threshold */} +
+ ( + + + {t( + "healthyIntervalSeconds" + )} + + + + handleChange( + "hcInterval", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> + ( + + + {t( + "healthyThreshold" + )} + + + + handleChange( + "hcHealthyThreshold", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> +
+ + {/* Unhealthy interval + threshold */} +
+ ( + + + {t( + "unhealthyIntervalSeconds" + )} + + + + handleChange( + "hcUnhealthyInterval", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> + ( + + + {t( + "unhealthyThreshold" + )} + + + + handleChange( + "hcUnhealthyThreshold", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> +
+ + {/* HTTP-only advanced fields */} + {!isTcp && ( + <> + {/* Expected status + TLS server name */} +
+ ( + + + {t( + "expectedResponseCodes" + )} + + + { + const val = + e + .target + .value; + const value = + val + ? parseInt( + val + ) + : null; + handleChange( + "hcStatus", + value, + field.onChange + ); + }} + /> + + + + )} + /> + ( + + + {t( + "tlsServerName" + )} + + + + handleChange( + "hcTlsServerName", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> + + + + )} + /> +
+ + {/* Follow redirects */} + ( + + + {t( + "followRedirects" + )} + + + + handleChange( + "hcFollowRedirects", + value, + field.onChange + ) + } + /> + + + )} + /> + + {/* Custom headers */} + ( + + + {t( + "customHeaders" + )} + + + + handleChange( + "hcHeaders", + value, + field.onChange + ) + } + rows={4} + /> + + + {t( + "customHeadersDescription" + )} + + + + )} + /> + + )} + + )} + + {showFields && isSnmpOrIcmp && ( +

+ {t("healthCheckStrategyNotAvailable")} +

+ )} +
+