From 831b9e199e0a40643177c3d8ecd50ba410e447b0 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Thu, 23 Apr 2026 10:08:03 -0700 Subject: [PATCH 1/2] fix: add typeguards for deployment window date rendering --- .../rule-results/DeploymentWindowDetail.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx b/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx index 55634d0ef..d0d36655a 100644 --- a/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx +++ b/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx @@ -35,11 +35,19 @@ type DeploymentWindowProperties = { time_until_window: string; }; +function isValidDateString(value: string | undefined | null): value is string { + if (value == null) return false; + return !isNaN(new Date(value).getTime()); +} + function parseWindowDetails( window: DeploymentWindow, ): DeploymentWindowProperties | null { const details = window.details as Partial; if (details.rrule == null || details.window_type == null) return null; + if (typeof details.duration_minutes !== "number") return null; + if (!isValidDateString(details.next_window_start)) return null; + if (!isValidDateString(details.next_window_end)) return null; return details as DeploymentWindowProperties; } From 76ce9962bab6211dbd838bf4e910865e56ef1d22 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Thu, 23 Apr 2026 10:21:55 -0700 Subject: [PATCH 2/2] fix --- .../rule-results/DeploymentWindowDetail.tsx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx b/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx index d0d36655a..35afc5b84 100644 --- a/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx +++ b/apps/web/app/routes/ws/deployments/_components/environmentversiondecisions/rule-results/DeploymentWindowDetail.tsx @@ -27,28 +27,41 @@ export type DeploymentWindowDetailProps = { type DeploymentWindowProperties = { rrule: string; - timezone: string; duration_minutes: number; next_window_start: string; next_window_end: string; window_type: string; - time_until_window: string; }; -function isValidDateString(value: string | undefined | null): value is string { - if (value == null) return false; +function isValidDateString(value: unknown): value is string { + if (typeof value !== "string") return false; return !isNaN(new Date(value).getTime()); } function parseWindowDetails( window: DeploymentWindow, ): DeploymentWindowProperties | null { - const details = window.details as Partial; - if (details.rrule == null || details.window_type == null) return null; - if (typeof details.duration_minutes !== "number") return null; - if (!isValidDateString(details.next_window_start)) return null; - if (!isValidDateString(details.next_window_end)) return null; - return details as DeploymentWindowProperties; + const details = window.details; + if (details == null || typeof details !== "object") return null; + const { + rrule, + window_type, + duration_minutes, + next_window_start, + next_window_end, + } = details as Record; + if (typeof rrule !== "string") return null; + if (typeof window_type !== "string") return null; + if (typeof duration_minutes !== "number") return null; + if (!isValidDateString(next_window_start)) return null; + if (!isValidDateString(next_window_end)) return null; + return { + rrule, + window_type, + duration_minutes, + next_window_start, + next_window_end, + }; } function usePolicyNameByRuleId(): Map {