diff --git a/messages/en-US.json b/messages/en-US.json index 633f84484..89ef232c5 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1848,8 +1848,8 @@ "retryAttempts": "Retry Attempts", "expectedResponseCodes": "Expected Response Codes", "expectedResponseCodesDescription": "HTTP status code that indicates healthy status. If left blank, 200-300 is considered healthy.", - "customHeaders": "Custom Headers", - "customHeadersDescription": "Headers new line separated: Header-Name: value", + "customHeaders": "Custom Request Headers", + "customHeadersDescription": "Request headers sent to the downstream targets. Headers new line separated: Header-Name: value", "headersValidationError": "Headers must be in the format: Header-Name: value", "saveHealthCheck": "Save Health Check", "healthCheckSaved": "Health Check Saved", diff --git a/src/components/ContactSalesBanner.tsx b/src/components/ContactSalesBanner.tsx new file mode 100644 index 000000000..fedd5e49a --- /dev/null +++ b/src/components/ContactSalesBanner.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { KeyRound, ExternalLink } from "lucide-react"; +import Link from "next/link"; + +export function ContactSalesBanner() { + return ( +
+
+
+ + + Contact sales to enable this feature.{" "} + + Book a demo + + + {" or "} + + contact us + + + . + +
+
+
+ ); +} diff --git a/src/components/DatadogDestinationCredenza.tsx b/src/components/DatadogDestinationCredenza.tsx index b2d74ae0a..c81323e50 100644 --- a/src/components/DatadogDestinationCredenza.tsx +++ b/src/components/DatadogDestinationCredenza.tsx @@ -12,8 +12,7 @@ import { CredenzaTitle } from "@app/components/Credenza"; import { Button } from "@app/components/ui/button"; -import { Plus, X, KeyRound, ExternalLink } from "lucide-react"; -import Link from "next/link"; +import { ContactSalesBanner } from "@app/components/ContactSalesBanner"; import { useTranslations } from "next-intl"; export interface DatadogDestinationCredenzaProps { @@ -50,36 +49,7 @@ export function DatadogDestinationCredenza({ -
-
-
- - - Contact sales to enable this feature.{" "} - - Book a demo - - - {" or "} - - contact us - - - . - -
-
-
+
diff --git a/src/components/HealthCheckCredenza.tsx b/src/components/HealthCheckCredenza.tsx index ac109710e..f29fccccd 100644 --- a/src/components/HealthCheckCredenza.tsx +++ b/src/components/HealthCheckCredenza.tsx @@ -40,8 +40,7 @@ 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"; +import { ContactSalesBanner } from "@app/components/ContactSalesBanner"; export type HealthCheckConfig = { hcEnabled: boolean; @@ -273,12 +272,10 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) { hcUnhealthyInterval: initialValues.hcUnhealthyInterval ?? 30, hcTimeout: initialValues.hcTimeout ?? 5, - hcHealthyThreshold: - initialValues.hcHealthyThreshold ?? 1, + hcHealthyThreshold: initialValues.hcHealthyThreshold ?? 1, hcUnhealthyThreshold: initialValues.hcUnhealthyThreshold ?? 1, - hcFollowRedirects: - initialValues.hcFollowRedirects ?? true, + hcFollowRedirects: initialValues.hcFollowRedirects ?? true, hcTlsServerName: initialValues.hcTlsServerName ?? "", hcStatus: initialValues.hcStatus ?? null, hcHeaders: parsedHeaders @@ -415,477 +412,527 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) { : undefined } > - - {/* ── Strategy tab ──────────────────────── */} -
- {/* Name (submit mode only) */} - {mode === "submit" && ( - ( - - - {t( - "standaloneHcNameLabel" - )} - - - - - - - )} - /> + {/* Name (submit mode only) */} + {mode === "submit" && ( + ( + + + {t("standaloneHcNameLabel")} + + + + + + )} + /> + )} - {/* Enable toggle (autoSave mode only) */} - {mode === "autoSave" && ( - ( - -
- - {t( - "enableHealthChecks" - )} - -
- - - handleChange( - "hcEnabled", - value, - field.onChange - ) - } - /> - -
- )} - /> - )} +
+ ( - - - + {/* ── Strategy tab ──────────────────────── */} +
+ {/* Enable toggle (autoSave mode only) */} + {mode === "autoSave" && ( + ( + +
+ + {t( + "enableHealthChecks" + )} + +
+ + - handleChange( - "hcMode", - value, - field.onChange - ) + onCheckedChange={( + value + ) => + handleChange( + "hcEnabled", + value, + field.onChange + ) + } + /> + +
+ )} + /> + )} + + {/* Strategy picker */} + {showFields && ( + ( + + + + handleChange( + "hcMode", + value, + field.onChange + ) + } + /> + + + + )} + /> + )} +
+ + {/* ── Connection tab ────────────────────── */} +
+ {!showFields && ( +

+ {t("enableHealthChecks")} +

+ )} + + {/* Contact-sales banner for SNMP / ICMP */} + {showFields && isSnmpOrIcmp && ( + + )} + + {showFields && !isSnmpOrIcmp && ( + <> + {/* Scheme / Hostname / Port */} + {isTcp ? ( +
+ ( + + + {t( + "healthHostname" + )} + + + + handleChange( + "hcHostname", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> + + + + )} /> - - - - )} - /> - )} - - {/* 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" - )} - - - ( + + + {t( + "healthPort" + )} + + + + handleChange( + "hcPort", + e + .target + .value, + field.onChange + ) + } + /> + + + + )} + /> +
+ ) : ( +
+ ( + + + {t( + "healthScheme" + )} + + - handleChange( - "hcPort", - e - .target - .value, + "hcScheme", + value, field.onChange ) } - /> - - - - )} - /> -
- ) : ( -
- ( - - - {t( - "healthScheme" - )} - - + + + )} + /> + ( + + + {t( + "healthHostname" + )} + - - - + + handleChange( + "hcHostname", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> - - - HTTP - - - HTTPS - - - - - - )} - /> - ( - - - {t( - "healthHostname" - )} - - - + + )} + /> + ( + + + {t( + "healthPort" + )} + + + + handleChange( + "hcPort", + e + .target + .value, + field.onChange + ) + } + /> + + + + )} + /> +
+ )} + + {/* Method / Path / Timeout (HTTP) */} + {!isTcp && ( +
+ ( + + + {t( + "httpMethod" + )} + + - handleChange( - "hcPort", - e - .target - .value, + "hcMethod", + value, field.onChange ) } - /> - - - - )} - /> -
- )} - - {/* Method / Path / Timeout (HTTP) */} - {!isTcp && ( -
- ( - - - {t( - "httpMethod" - )} - - - - - )} - /> - ( - - - {t( - "healthCheckPath" - )} - - - - handleChange( - "hcPath", - e - .target - .value, - () => - field.onChange( - e - ) - ) + value={ + field.value } - /> - - - - )} - /> + > + + + + + + + + GET + + + POST + + + HEAD + + + PUT + + + DELETE + + + + + + )} + /> + ( + + + {t( + "healthCheckPath" + )} + + + + handleChange( + "hcPath", + e + .target + .value, + () => + field.onChange( + e + ) + ) + } + /> + + + + )} + /> + ( + + + {t( + "timeoutSeconds" + )} + + + + handleChange( + "hcTimeout", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> +
+ )} + + {/* Timeout for TCP */} + {isTcp && ( )} /> -
- )} + )} + + )} +
- {/* Timeout for TCP */} - {isTcp && ( - ( - - - {t( - "timeoutSeconds" - )} - - - - handleChange( - "hcTimeout", - parseInt( - e - .target - .value - ), - field.onChange - ) - } - /> - - - - )} - /> - )} - - )} + {/* ── Advanced tab ──────────────────────── */} +
+ {!showFields && ( +

+ {t("enableHealthChecks")} +

+ )} - {showFields && isSnmpOrIcmp && ( -

- {t("healthCheckStrategyNotAvailable")} -

- )} -
+ {/* Contact-sales banner for SNMP / ICMP */} + {showFields && isSnmpOrIcmp && ( + + )} - {/* ── 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 */} + {showFields && !isSnmpOrIcmp && ( + <> + {/* Healthy interval + threshold */} +
( - - - {t( - "followRedirects" - )} - - - - handleChange( - "hcFollowRedirects", - value, - field.onChange - ) - } - /> - - - )} - /> - - {/* Custom headers */} - ( {t( - "customHeaders" + "healthyIntervalSeconds" )} - handleChange( - "hcHeaders", - value, + "hcInterval", + parseInt( + e + .target + .value + ), field.onChange ) } - rows={4} /> - - {t( - "customHeadersDescription" - )} - )} /> - - )} - - )} + ( + + + {t( + "healthyThreshold" + )} + + + + handleChange( + "hcHealthyThreshold", + parseInt( + e + .target + .value + ), + field.onChange + ) + } + /> + + + + )} + /> +
- {showFields && isSnmpOrIcmp && ( -

- {t("healthCheckStrategyNotAvailable")} -

- )} -
-
+ {/* 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" + )} + + + + )} + /> + + )} + + )} +
+ +
@@ -1318,4 +1331,4 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) { ); } -export default HealthCheckCredenza; \ No newline at end of file +export default HealthCheckCredenza; diff --git a/src/components/HealthChecksTable.tsx b/src/components/HealthChecksTable.tsx index 9f1297fef..a5d35b2e0 100644 --- a/src/components/HealthChecksTable.tsx +++ b/src/components/HealthChecksTable.tsx @@ -150,7 +150,7 @@ export default function HealthChecksTable({ ), cell: ({ row }) => ( - {row.original.name} + {row.original.name ? row.original.name : "-"} ) }, { diff --git a/src/components/S3DestinationCredenza.tsx b/src/components/S3DestinationCredenza.tsx index d94293cf0..7702e7932 100644 --- a/src/components/S3DestinationCredenza.tsx +++ b/src/components/S3DestinationCredenza.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; + import { Credenza, CredenzaBody, @@ -12,8 +12,7 @@ import { CredenzaTitle } from "@app/components/Credenza"; import { Button } from "@app/components/ui/button"; -import { Plus, X, KeyRound, ExternalLink } from "lucide-react"; -import Link from "next/link"; +import { ContactSalesBanner } from "@app/components/ContactSalesBanner"; import { useTranslations } from "next-intl"; export interface S3DestinationCredenzaProps { @@ -50,36 +49,7 @@ export function S3DestinationCredenza({ -
-
-
- - - Contact sales to enable this feature.{" "} - - Book a demo - - - {" or "} - - contact us - - - . - -
-
-
+