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