From d09122af5b5dc5580ec6b74bba5e8e84fb424f08 Mon Sep 17 00:00:00 2001
From: Dima Rifai <dima.rifai@gmail.com>
Date: Mon, 20 Jan 2025 16:18:08 +0200
Subject: [PATCH 1/3] Issue #413 - Refactor onBlur functionality

---
 components/searchRanges/SearchRanges.tsx | 120 +++++++++++++----------
 1 file changed, 70 insertions(+), 50 deletions(-)

diff --git a/components/searchRanges/SearchRanges.tsx b/components/searchRanges/SearchRanges.tsx
index d4c76606..8da6b19e 100644
--- a/components/searchRanges/SearchRanges.tsx
+++ b/components/searchRanges/SearchRanges.tsx
@@ -1,12 +1,11 @@
-import React, { useEffect , useState } from "react";
+import React, { useEffect, useState } from "react";
 import moment from "moment";
 
 import { SearchRangesResult } from "../../hooks/common/useSearchRanges";
 import { Select, SelectContent, SelectTrigger, SelectItem } from "../ui/select";
 import { Input } from "../ui/input";
 import DateTimePicker from "../DateTimePicker";
-import ErrorMessage from "../ErrorMessage";// Import the ErrorMessage component
-
+import ErrorMessage from "../ErrorMessage"; // Import the ErrorMessage component
 interface SearchRangesProps {
   rangesProps: SearchRangesResult;
   safeTimeRangeDisplay?: boolean;
@@ -37,23 +36,77 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
     setLastTimeUnitValue,
   } = rangesProps;
 
-  // Validate and update numeric values
+  
+  const [rangeError, setRangeError] = useState<string | null>(null); 
+
+  const handleOnBlur = (
+    e: React.FocusEvent<HTMLInputElement>,
+    fieldSetter: Function,
+    validateField: Function | null
+  ) => {
+    const value = e.target.value;
+    const numericValue = value ? Number(value) : undefined;
+
+    // Fetch the latest block number dynamically
+    let validated = true;
+    if (validateField) {
+      validated = validateField(e, numericValue); 
+    }
+
+    validated ? fieldSetter(numericValue) : fieldSetter(null);
+  };
+
+  const validateToBlock = (
+    e: React.FocusEvent<HTMLInputElement>,
+    value: number | undefined,
+  ) => {
+    if (value !== undefined && value <= 0) {
+      setRangeError("Block Number must be a positive number");
+      e.target.value = "";
+      return false;
+    }
+    if (value && fromBlock && !isNaN(value) && value < fromBlock) {
+      setRangeError("To block must be greater than From block");
+      e.target.value = "";
+      return false;
+    }
+    return true;
+  };
+
+  const validateFromBlock = (
+    e: React.FocusEvent<HTMLInputElement>,
+    value: number | undefined,
+  ) => {
+    if (value !== undefined && value <= 0) {
+      setRangeError("Block Number must be a positive number");
+      e.target.value = "";
+      return false;
+    }
+    if (value && toBlock && !isNaN(value) && value > toBlock) {
+      setRangeError("From block must be less than To block");
+      e.target.value = "";
+      return false;
+    }
+    return true;
+  };
+
   const handleNumericInput = (
     e: React.ChangeEvent<HTMLInputElement>,
     allowDecimal: boolean = false
   ) => {
     let cleanedValue = e.target.value;
-  
+
     // Clean the value based on the logic
     cleanedValue = allowDecimal
       ? cleanedValue.replace(/[^0-9.]/g, "") // Allow numbers and decimal point
       : cleanedValue.replace(/[^0-9]/g, ""); // Only allow numbers
-  
+
     if (allowDecimal && cleanedValue.split(".").length > 2) {
-      cleanedValue = cleanedValue.slice(0, cleanedValue.indexOf(".") + 1) + 
-      cleanedValue.split(".").slice(1).join(""); // Remove extra decimals
+      cleanedValue =
+        cleanedValue.slice(0, cleanedValue.indexOf(".") + 1) +
+        cleanedValue.split(".").slice(1).join(""); // Remove extra decimals
     }
-  
+
     if (cleanedValue.length > 15) {
       cleanedValue = cleanedValue.slice(0, 15); // Limit to 15 digits
     }
@@ -68,10 +121,9 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
     ) {
       setStartDate(moment(startDate).subtract(1, "hours").toDate());
     }
-    //eslint-disable-next-line react-hooks/exhaustive-deps
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [startDate, endDate]);
 
-  const [blockRangeError, setBlockRangeError] = useState<string | null>(null); // Error state for block range validation
 
   return (
     <div className="py-2 flex flex-col gap-y-2">
@@ -109,11 +161,7 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
               type="text" // Use type="text" to allow custom validation
               defaultValue={lastBlocksValue || ""}
               onChange={(e) => handleNumericInput(e)}
-              onBlur={(e) => {
-                const value = e.target.value;
-                const numericValue = value ? Number(value) : undefined; 
-                setLastBlocksValue(numericValue);
-              }}
+              onBlur={(e) => handleOnBlur(e,setLastBlocksValue,null)}
               placeholder={"Last"}
             />
           </div>
@@ -129,11 +177,7 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
                 className="bg-theme border-0 border-b-2 text-text"
                 defaultValue={lastTimeUnitValue || ""}
                 onChange={(e) => handleNumericInput(e,true)}
-                onBlur={(e) => {
-                  const value = e.target.value;
-                  const numericValue = value ? Number(value) : undefined;
-                  setLastTimeUnitValue(numericValue);
-                }}
+                onBlur={(e) => handleOnBlur(e,setLastTimeUnitValue,null)}
                 placeholder={"Last"}
               />
             </div>
@@ -174,11 +218,7 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
               data-testid="from-block-input"
               defaultValue={fromBlock || ""}
               onChange={(e) => handleNumericInput(e)}
-              onBlur={(e) => {
-                const value = e.target.value;
-                const numericValue = value ? Number(value) : undefined;
-                setFromBlock(numericValue);
-              }}
+              onBlur={(e) => handleOnBlur(e,setFromBlock,validateFromBlock)}
               placeholder="From"
             />
           </div>
@@ -190,35 +230,15 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
               defaultValue={toBlock || ""}
               onChange={(e) => handleNumericInput(e)}
               placeholder={"To"}
-              onBlur={(e: React.FocusEvent<HTMLInputElement>) => {
-                const value = e.target.value;
-                const numericValue = value ? Number(value) : undefined;
-                  if (
-                    numericValue &&
-                    fromBlock &&
-                    !isNaN(numericValue) &&
-                    !isNaN(Number(fromBlock)) &&
-                    numericValue < Number(fromBlock)
-                  ) {
-                    setBlockRangeError("To block must be greater than From block");
-                    e.target.value = "";
-                  } else if (numericValue !=undefined && numericValue <= 0 && fromBlock) {
-                    setBlockRangeError("To block must be greater than From block");
-                    e.target.value = "";
-                  } else {
-                    setToBlock(numericValue);
-                    setBlockRangeError(null);
-                  }
-                }
-              }
+              onBlur={(e) => handleOnBlur(e,setToBlock,validateToBlock)}
             />
           </div>
         </div>
       )}
-      {blockRangeError && (
+      {rangeError && (
         <ErrorMessage
-          message={blockRangeError}
-          onClose={() => setBlockRangeError(null)} // Close the error message
+          message={rangeError}
+          onClose={() => setRangeError(null)} // Close the error message
           timeout={3000}
         />
       )}
-- 
GitLab


From 1f2b415d8a3fc716604f2555d1fa45efa732e693 Mon Sep 17 00:00:00 2001
From: Dima Rifai <dima.rifai@gmail.com>
Date: Mon, 20 Jan 2025 16:20:36 +0200
Subject: [PATCH 2/3] Issue #413 - Add validation/fallback for payloadStartDate
 and payloadToBlock to prevent bad requests

---
 hooks/common/useSearchRanges.ts | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/hooks/common/useSearchRanges.ts b/hooks/common/useSearchRanges.ts
index 9309fb1d..d7dc8ad3 100644
--- a/hooks/common/useSearchRanges.ts
+++ b/hooks/common/useSearchRanges.ts
@@ -138,6 +138,19 @@ const useSearchRanges = (defaultSelectKey: string = "none") => {
         .milliseconds(0)
         .toDate();
     }
+    
+    //Validate that payloadStartDate is a valid
+    if (payloadStartDate && (isNaN(payloadStartDate?.getTime()) || payloadStartDate?.getTime() <= 0)) {
+      payloadStartDate = undefined; //fallback
+    }
+    //Validate that payloadToBlock does not exceed latest headblock number
+    if (payloadToBlock && (payloadToBlock)) {
+      const currentHeadBlockNumber = await checkTemporaryHeadBlockNumber();
+      if (payloadToBlock > currentHeadBlockNumber) {
+        payloadToBlock = currentHeadBlockNumber; //fallback
+      }
+    }
+    
     return {
       payloadFromBlock,
       payloadToBlock,
-- 
GitLab


From e328109f3e77ead3f5b478dd97e89de9439fc8a9 Mon Sep 17 00:00:00 2001
From: Dima Rifai <dima.rifai@gmail.com>
Date: Mon, 20 Jan 2025 16:27:49 +0200
Subject: [PATCH 3/3] Issue #413 - Add validation/fallback for payloadFromBlock
 to prevent bad requests

---
 hooks/common/useSearchRanges.ts | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/hooks/common/useSearchRanges.ts b/hooks/common/useSearchRanges.ts
index d7dc8ad3..abe65921 100644
--- a/hooks/common/useSearchRanges.ts
+++ b/hooks/common/useSearchRanges.ts
@@ -144,13 +144,20 @@ const useSearchRanges = (defaultSelectKey: string = "none") => {
       payloadStartDate = undefined; //fallback
     }
     //Validate that payloadToBlock does not exceed latest headblock number
-    if (payloadToBlock && (payloadToBlock)) {
+    if (payloadToBlock) {
       const currentHeadBlockNumber = await checkTemporaryHeadBlockNumber();
       if (payloadToBlock > currentHeadBlockNumber) {
         payloadToBlock = currentHeadBlockNumber; //fallback
       }
     }
-    
+    if(payloadFromBlock)
+    {
+      const currentHeadBlockNumber = await checkTemporaryHeadBlockNumber();
+      if (payloadFromBlock > currentHeadBlockNumber) {
+        payloadFromBlock = currentHeadBlockNumber; //fallback
+      }
+    }
+
     return {
       payloadFromBlock,
       payloadToBlock,
-- 
GitLab