From 2d091cb2fed752963e3f23178219a22a4f80b35d Mon Sep 17 00:00:00 2001 From: Hoda Noori Date: Mon, 17 Aug 2026 10:09:22 +0200 Subject: [PATCH 1/4] fix(heureka,doop,supernova): coerce numeric searchTerm URL param to string When a user types a single digit in the search box, TanStack Router parses it as a number from the URL. z.string().optional() rejects numbers, causing a parse error and breaking the app. Add z.preprocess to coerce numbers to strings before validation, matching the fix already in the vulnerabilities route. Signed-off-by: Hoda Noori --- apps/doop/src/routes/violations.tsx | 2 +- apps/heureka/src/routes/services/index.tsx | 2 +- apps/supernova/src/routes/alerts.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/doop/src/routes/violations.tsx b/apps/doop/src/routes/violations.tsx index a77359f553..f3a3967f94 100644 --- a/apps/doop/src/routes/violations.tsx +++ b/apps/doop/src/routes/violations.tsx @@ -23,7 +23,7 @@ const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined( const searchSchema = z .object({ - searchTerm: z.string().optional(), + searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), violationGroup: z.string().optional(), }) .catchall(filterValueSchema) diff --git a/apps/heureka/src/routes/services/index.tsx b/apps/heureka/src/routes/services/index.tsx index 6209eefd39..2f52c5ae7c 100644 --- a/apps/heureka/src/routes/services/index.tsx +++ b/apps/heureka/src/routes/services/index.tsx @@ -20,7 +20,7 @@ const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined( const servicesSearchSchema = z .object({ service: z.string().optional(), - searchTerm: z.string().optional(), + searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), }) .catchall(filterValueSchema) diff --git a/apps/supernova/src/routes/alerts.tsx b/apps/supernova/src/routes/alerts.tsx index 4bf24e57cc..c2c5c5aae8 100644 --- a/apps/supernova/src/routes/alerts.tsx +++ b/apps/supernova/src/routes/alerts.tsx @@ -20,7 +20,7 @@ const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined( const searchSchema = z .object({ - searchTerm: z.string().optional(), + searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), showDetailsFor: z.string().optional(), predefinedFilter: z.string().optional(), }) From 237687e8c65a4cc2c9dc93777d8eb00edc8e23c1 Mon Sep 17 00:00:00 2001 From: Hoda Noori Date: Mon, 17 Aug 2026 10:25:05 +0200 Subject: [PATCH 2/4] refactor(heureka,doop,supernova): extract optionalStringSchema helper and broaden coercion - Add optionalStringSchema to each app's utils/helpers file to avoid duplicating the preprocess logic across routes - Broaden coercion from number-only to any primitive (number | boolean) with explicit type cast to satisfy no-base-to-string lint rule - Use the shared schema in all affected routes Signed-off-by: Hoda Noori --- apps/doop/src/lib/helpers.ts | 8 ++++++++ apps/doop/src/routes/violations.tsx | 4 ++-- apps/heureka/src/routes/services/index.tsx | 4 ++-- apps/heureka/src/routes/vulnerabilities/index.tsx | 4 ++-- apps/heureka/src/utils.ts | 7 +++++++ apps/supernova/src/lib/utils.ts | 8 ++++++++ apps/supernova/src/routes/alerts.tsx | 5 ++--- 7 files changed, 31 insertions(+), 9 deletions(-) diff --git a/apps/doop/src/lib/helpers.ts b/apps/doop/src/lib/helpers.ts index 6d3a1443f2..0782d6d97d 100644 --- a/apps/doop/src/lib/helpers.ts +++ b/apps/doop/src/lib/helpers.ts @@ -3,6 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { z } from "zod" + +/** Zod schema for optional URL search params that may be parsed as numbers by TanStack Router. */ +export const optionalStringSchema = z.preprocess( + (val) => (val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean)), + z.string().optional() +) + export const parseError = (error: any) => { if (!error || (typeof error === "object" && Object.keys(error).length === 0)) return "An error occurred. There is no further information" diff --git a/apps/doop/src/routes/violations.tsx b/apps/doop/src/routes/violations.tsx index f3a3967f94..664e19dd8e 100644 --- a/apps/doop/src/routes/violations.tsx +++ b/apps/doop/src/routes/violations.tsx @@ -17,13 +17,13 @@ import { } from "../components/StoreProvider" import { parseInitialFilters } from "../lib/store/createFiltersSlice" import { isObjectWithKeys } from "../lib/helpers" -import { filterSearchParamsByPrefix } from "../lib/helpers" +import { filterSearchParamsByPrefix, optionalStringSchema } from "../lib/helpers" const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const searchSchema = z .object({ - searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), + searchTerm: optionalStringSchema, violationGroup: z.string().optional(), }) .catchall(filterValueSchema) diff --git a/apps/heureka/src/routes/services/index.tsx b/apps/heureka/src/routes/services/index.tsx index 2f52c5ae7c..1705cd4bcf 100644 --- a/apps/heureka/src/routes/services/index.tsx +++ b/apps/heureka/src/routes/services/index.tsx @@ -13,14 +13,14 @@ import { SELECTED_FILTER_PREFIX } from "../../constants" import { fetchServices } from "../../api/fetchServices" import { fetchServicesFilters } from "../../api/fetchServicesFilters" import { extractFilterSettingsFromSearchParams } from "../../components/Services/utils" -import { filterSearchParamsByPrefix } from "../../utils" +import { filterSearchParamsByPrefix, optionalStringSchema } from "../../utils" const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const servicesSearchSchema = z .object({ service: z.string().optional(), - searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), + searchTerm: optionalStringSchema, }) .catchall(filterValueSchema) diff --git a/apps/heureka/src/routes/vulnerabilities/index.tsx b/apps/heureka/src/routes/vulnerabilities/index.tsx index e39330b1bb..c4f0a09fd3 100644 --- a/apps/heureka/src/routes/vulnerabilities/index.tsx +++ b/apps/heureka/src/routes/vulnerabilities/index.tsx @@ -10,14 +10,14 @@ import { SELECTED_FILTER_PREFIX } from "../../constants" import { fetchVulnerabilities } from "../../api/fetchVulnerabilities" import { fetchVulnerabilityFilters } from "../../api/fetchVulnerabilityFilters" import { extractFilterSettingsFromSearchParams } from "../../components/Vulnerabilities/utils" -import { filterSearchParamsByPrefix } from "../../utils" +import { filterSearchParamsByPrefix, optionalStringSchema } from "../../utils" const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const vulnerabilitiesSearchSchema = z .object({ vulnerability: z.string().optional(), - searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), + searchTerm: optionalStringSchema, }) .catchall(filterValueSchema) diff --git a/apps/heureka/src/utils.ts b/apps/heureka/src/utils.ts index 94e9e35bbd..478bd3b64e 100644 --- a/apps/heureka/src/utils.ts +++ b/apps/heureka/src/utils.ts @@ -5,6 +5,13 @@ import { useState, useRef, useEffect, Dispatch, SetStateAction } from "react" import { KnownIcons } from "@cloudoperators/juno-ui-components" +import { z } from "zod" + +/** Zod schema for optional URL search params that may be parsed as numbers by TanStack Router. */ +export const optionalStringSchema = z.preprocess( + (val) => (val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean)), + z.string().optional() +) export const capitalizeFirstLetter = (str: string): string => { return str.charAt(0).toUpperCase() + str.slice(1) diff --git a/apps/supernova/src/lib/utils.ts b/apps/supernova/src/lib/utils.ts index cb9c9e850b..fb97a79405 100644 --- a/apps/supernova/src/lib/utils.ts +++ b/apps/supernova/src/lib/utils.ts @@ -3,6 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { z } from "zod" + +/** Zod schema for optional URL search params that may be parsed as numbers by TanStack Router. */ +export const optionalStringSchema = z.preprocess( + (val) => (val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean)), + z.string().optional() +) + import { AlertItem, SeverityCounts } from "./createAlertsSlice" export const severityToSemanticName = (severity: any) => { diff --git a/apps/supernova/src/routes/alerts.tsx b/apps/supernova/src/routes/alerts.tsx index c2c5c5aae8..f37e04118d 100644 --- a/apps/supernova/src/routes/alerts.tsx +++ b/apps/supernova/src/routes/alerts.tsx @@ -13,14 +13,13 @@ import AlertsTab from "../components/alerts/AlertsTab" import { ACTIVE_FILTERS_PREFIX, PAUSED_FILTERS_PREFIX } from "../constants" import { convertUrlStateToAppState, getFiltersForUrl } from "../lib/urlStateUtils" import { useGlobalsActions, useFilterActions, useGlobalsInitialFiltersApplied } from "../components/StoreProvider" -import { isObjectWithKeys } from "../lib/utils" -import { filterSearchParamsByPrefix } from "../lib/utils" +import { isObjectWithKeys, filterSearchParamsByPrefix, optionalStringSchema } from "../lib/utils" const filterValueSchema = z.union([z.string(), z.array(z.string()), z.undefined()]) const searchSchema = z .object({ - searchTerm: z.preprocess((val) => (typeof val === "number" ? val.toString() : val), z.string().optional()), + searchTerm: optionalStringSchema, showDetailsFor: z.string().optional(), predefinedFilter: z.string().optional(), }) From 25978a42c2d2f88535d1f0bfd3894205f98f4113 Mon Sep 17 00:00:00 2001 From: Hoda Noori Date: Mon, 17 Aug 2026 11:02:55 +0200 Subject: [PATCH 3/4] chore: stage utils changes from Prettier reformat Signed-off-by: Hoda Noori --- apps/doop/src/lib/helpers.ts | 3 ++- apps/heureka/src/utils.ts | 3 ++- apps/supernova/src/lib/utils.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/doop/src/lib/helpers.ts b/apps/doop/src/lib/helpers.ts index 0782d6d97d..57673926b5 100644 --- a/apps/doop/src/lib/helpers.ts +++ b/apps/doop/src/lib/helpers.ts @@ -7,7 +7,8 @@ import { z } from "zod" /** Zod schema for optional URL search params that may be parsed as numbers by TanStack Router. */ export const optionalStringSchema = z.preprocess( - (val) => (val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean)), + (val) => + val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean), z.string().optional() ) diff --git a/apps/heureka/src/utils.ts b/apps/heureka/src/utils.ts index 478bd3b64e..34766f6c52 100644 --- a/apps/heureka/src/utils.ts +++ b/apps/heureka/src/utils.ts @@ -9,7 +9,8 @@ import { z } from "zod" /** Zod schema for optional URL search params that may be parsed as numbers by TanStack Router. */ export const optionalStringSchema = z.preprocess( - (val) => (val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean)), + (val) => + val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean), z.string().optional() ) diff --git a/apps/supernova/src/lib/utils.ts b/apps/supernova/src/lib/utils.ts index fb97a79405..7e119c216e 100644 --- a/apps/supernova/src/lib/utils.ts +++ b/apps/supernova/src/lib/utils.ts @@ -7,7 +7,8 @@ import { z } from "zod" /** Zod schema for optional URL search params that may be parsed as numbers by TanStack Router. */ export const optionalStringSchema = z.preprocess( - (val) => (val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean)), + (val) => + val === undefined || val === null ? undefined : typeof val === "string" ? val : String(val as number | boolean), z.string().optional() ) From 400a34a1b19b1982303dab3e32906d0effe977f8 Mon Sep 17 00:00:00 2001 From: Hoda <107242553+hodanoori@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:01:24 +0200 Subject: [PATCH 4/4] Create nine-cougars-kneel.md Signed-off-by: Hoda <107242553+hodanoori@users.noreply.github.com> --- .changeset/nine-cougars-kneel.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/nine-cougars-kneel.md diff --git a/.changeset/nine-cougars-kneel.md b/.changeset/nine-cougars-kneel.md new file mode 100644 index 0000000000..acafb614cd --- /dev/null +++ b/.changeset/nine-cougars-kneel.md @@ -0,0 +1,7 @@ +--- +"@cloudoperators/juno-app-doop": patch +"@cloudoperators/juno-app-heureka": patch +"@cloudoperators/juno-app-supernova": patch +--- + +fix(heureka): correct numeric searchTerm URL param to string in search box