Skip to content
Snippets Groups Projects
Commit 29de93cb authored by Lukas's avatar Lukas Committed by mcfarhat
Browse files

Update SearchRanges component

parent caea956b
No related branches found
No related tags found
1 merge request!596Update SearchRanges component
Pipeline #118748 canceled
import React, { useEffect, useState } from "react";
import React, { useState, useEffect } 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";
interface SearchRangesProps {
rangesProps: SearchRangesResult;
safeTimeRangeDisplay?: boolean;
}
const SearchRanges: React.FC<SearchRangesProps> = ({
rangesProps,
safeTimeRangeDisplay,
}) => {
const SearchRanges: React.FC<SearchRangesProps> = ({ rangesProps }) => {
const {
rangeSelectOptions,
timeSelectOptions,
......@@ -38,79 +35,100 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
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;
const [localLastBlocks, setLocalLastBlocks] = useState(
lastBlocksValue !== undefined ? String(lastBlocksValue) : ""
);
const [localLastTimeUnit, setLocalLastTimeUnit] = useState(
lastTimeUnitValue !== undefined ? String(lastTimeUnitValue) : ""
);
const [localFromBlock, setLocalFromBlock] = useState(
fromBlock !== undefined ? String(fromBlock) : ""
);
const [localToBlock, setLocalToBlock] = useState(
toBlock !== undefined ? String(toBlock) : ""
);
// Fetch the latest block number dynamically
let validated = true;
if (validateField) {
validated = validateField(e, numericValue);
useEffect(() => {
setLocalLastBlocks(
lastBlocksValue !== undefined ? String(lastBlocksValue) : ""
);
setLocalLastTimeUnit(
lastTimeUnitValue !== undefined ? String(lastTimeUnitValue) : ""
);
setLocalFromBlock(fromBlock !== undefined ? String(fromBlock) : "");
setLocalToBlock(toBlock !== undefined ? String(toBlock) : "");
}, [lastBlocksValue, lastTimeUnitValue, fromBlock, toBlock]);
const sanitizeNumericInput = (value: string, allowDecimal = false) => {
let cleaned = allowDecimal
? value.replace(/[^0-9.]/g, "")
: value.replace(/[^0-9]/g, "");
if (allowDecimal && cleaned.split(".").length > 2) {
const parts = cleaned.split(".");
cleaned = parts.shift() + "." + parts.join("");
}
validated ? fieldSetter(numericValue) : fieldSetter(null);
if (cleaned.length > 15) {
cleaned = cleaned.slice(0, 15);
}
return cleaned;
};
const validateToBlock = (
e: React.FocusEvent<HTMLInputElement>,
value: number | undefined
) => {
if (value !== undefined && value <= 0) {
const validateFromBlock = (numVal: number | undefined) => {
if (numVal !== undefined && numVal <= 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 = "";
if (numVal && toBlock && !isNaN(numVal) && numVal > toBlock) {
setRangeError("From block must be less than To block");
return false;
}
return true;
};
const validateFromBlock = (
e: React.FocusEvent<HTMLInputElement>,
value: number | undefined
) => {
if (value !== undefined && value <= 0) {
const validateToBlock = (numVal: number | undefined) => {
if (numVal !== undefined && numVal <= 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 = "";
if (numVal && fromBlock && !isNaN(numVal) && numVal < fromBlock) {
setRangeError("To block must be greater than From block");
return false;
}
return true;
};
const handleNumericInput = (
e: React.ChangeEvent<HTMLInputElement>,
allowDecimal: boolean = false
) => {
let cleanedValue = e.target.value;
const handleLastBlocksBlur = () => {
const val = localLastBlocks ? Number(localLastBlocks) : undefined;
setLastBlocksValue(val);
setRangeError(null);
};
// 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
const handleLastTimeUnitBlur = () => {
const val = localLastTimeUnit ? Number(localLastTimeUnit) : undefined;
setLastTimeUnitValue(val);
setRangeError(null);
};
if (allowDecimal && cleanedValue.split(".").length > 2) {
cleanedValue =
cleanedValue.slice(0, cleanedValue.indexOf(".") + 1) +
cleanedValue.split(".").slice(1).join(""); // Remove extra decimals
const handleFromBlockBlur = () => {
const val = localFromBlock ? Number(localFromBlock) : undefined;
if (!validateFromBlock(val)) {
setFromBlock(undefined);
return;
}
setFromBlock(val);
setRangeError(null);
};
if (cleanedValue.length > 15) {
cleanedValue = cleanedValue.slice(0, 15); // Limit to 15 digits
const handleToBlockBlur = () => {
const val = localToBlock ? Number(localToBlock) : undefined;
if (!validateToBlock(val)) {
setToBlock(undefined);
return;
}
e.target.value = cleanedValue;
setToBlock(val);
setRangeError(null);
};
useEffect(() => {
......@@ -130,22 +148,17 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
value={rangeSelectKey}
>
<SelectTrigger className="w-1/2 border-0 border-b-2 bg-theme text-text">
{
rangeSelectOptions.find(
(selectOption) => selectOption.key === rangeSelectKey
)?.name
}
{rangeSelectOptions.find((opt) => opt.key === rangeSelectKey)?.name}
</SelectTrigger>
<SelectContent className="bg-theme text-text rounded-sm max-h-[31rem]">
{rangeSelectOptions.map((selectOption, index) => (
{rangeSelectOptions.map((option, idx) => (
<SelectItem
className="text-center"
key={index}
value={selectOption.key}
defaultChecked={false}
key={idx}
value={option.key}
data-testid="search-select-option"
>
{selectOption.name}
{option.name}
</SelectItem>
))}
</SelectContent>
......@@ -153,90 +166,91 @@ const SearchRanges: React.FC<SearchRangesProps> = ({
{rangeSelectKey === "lastBlocks" && (
<div className="flex items-center">
<div className="flex flex-col w-full">
<Input
className="w-1/2 border-0 border-b-2 bg-theme"
type="text" // Use type="text" to allow custom validation
defaultValue={lastBlocksValue || ""}
onChange={(e) => handleNumericInput(e)}
onBlur={(e) => handleOnBlur(e, setLastBlocksValue, null)}
placeholder={"Last"}
/>
</div>
<Input
className="w-1/2 border-0 border-b-2 bg-theme"
type="text"
value={localLastBlocks}
onChange={(e) =>
setLocalLastBlocks(sanitizeNumericInput(e.target.value))
}
onBlur={handleLastBlocksBlur}
placeholder="Last"
/>
</div>
)}
{rangeSelectKey === "lastTime" && (
<>
<div className="flex items-center justify-center">
<div className="flex flex-col w-full mr-2">
<Input
type="text"
className="bg-theme border-0 border-b-2 text-text"
defaultValue={lastTimeUnitValue || ""}
onChange={(e) => handleNumericInput(e, true)}
onBlur={(e) => handleOnBlur(e, setLastTimeUnitValue, null)}
placeholder={"Last"}
/>
</div>
<Select onValueChange={setTimeUnitSelectKey}>
<SelectTrigger className="pl-2 bg-theme border-0 border-b-2 text-text">
{
timeSelectOptions.find(
(selectOption) => selectOption.key === timeUnitSelectKey
)?.name
}
</SelectTrigger>
<SelectContent
className="bg-theme text-text rounded-sm max-h-[31rem]"
data-testid="select-time-option-units"
>
{timeSelectOptions.map((selectOption, index) => (
<SelectItem
className="text-center"
key={index}
value={selectOption.key}
defaultChecked={false}
>
{selectOption.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</>
<div className="flex items-center justify-center">
<Input
type="text"
className="bg-theme border-0 border-b-2 text-text mr-2"
value={localLastTimeUnit}
onChange={(e) =>
setLocalLastTimeUnit(sanitizeNumericInput(e.target.value, true))
}
onBlur={handleLastTimeUnitBlur}
placeholder="Last"
/>
<Select
onValueChange={setTimeUnitSelectKey}
value={timeUnitSelectKey}
>
<SelectTrigger className="pl-2 bg-theme border-0 border-b-2 text-text">
{
timeSelectOptions.find((opt) => opt.key === timeUnitSelectKey)
?.name
}
</SelectTrigger>
<SelectContent className="bg-theme text-text rounded-sm max-h-[31rem]">
{timeSelectOptions.map((option, index) => (
<SelectItem
className="text-center"
key={index}
value={option.key}
>
{option.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{rangeSelectKey === "blockRange" && (
<div className="flex items-center">
<div className="flex flex-col w-full mr-2">
<div className="mr-2 w-full">
<Input
type="text"
className="bg-theme border-0 border-b-2"
data-testid="from-block-input"
defaultValue={fromBlock || ""}
onChange={(e) => handleNumericInput(e)}
onBlur={(e) => handleOnBlur(e, setFromBlock, validateFromBlock)}
value={localFromBlock}
onChange={(e) =>
setLocalFromBlock(sanitizeNumericInput(e.target.value))
}
onBlur={handleFromBlockBlur}
placeholder="From"
/>
</div>
<div className="flex flex-col w-full">
<div className="w-full">
<Input
className="bg-theme border-0 border-b-2"
data-testid="headblock-number"
type="text"
defaultValue={toBlock || ""}
onChange={(e) => handleNumericInput(e)}
placeholder={"To"}
onBlur={(e) => handleOnBlur(e, setToBlock, validateToBlock)}
value={localToBlock}
onChange={(e) =>
setLocalToBlock(sanitizeNumericInput(e.target.value))
}
onBlur={handleToBlockBlur}
placeholder="To"
/>
</div>
</div>
)}
{rangeError && (
<ErrorMessage
message={rangeError}
onClose={() => setRangeError(null)} // Close the error message
onClose={() => setRangeError(null)}
timeout={3000}
/>
)}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment