Skip to content

Commit

Permalink
feat: improve input validation in ManaCalculator
Browse files Browse the repository at this point in the history
  • Loading branch information
VmMad committed Nov 28, 2023
1 parent 8ab5c57 commit 626746c
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 109 deletions.
13 changes: 7 additions & 6 deletions src/components/ManaCalculator/components/DelegatorSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { Details } from '@docusaurus/theme-common/Details';
import { useManaState } from '../hooks';
import { fromMicro, toMicro } from '../utils';
import { fromMicro } from '../utils';
import Select from 'react-select';
import { ManaCalculatorInput } from '.';

export function DelegatorSettings() {
const { state, handleOwnStakeChange, handleValidatorChange } = useManaState();
Expand Down Expand Up @@ -30,13 +31,13 @@ export function DelegatorSettings() {
<label className='inlined-label'>
Delegated amount ({state.network})
</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact inlined'
type='number'
min='0'
value={fromMicro(state.stakedOrDelegatedTokens)}
onChange={(e) => handleOwnStakeChange(toMicro(Number(e.target.value)))}
></input>
min={0}
max={fromMicro(state.heldTokens)}
onChange={handleOwnStakeChange}
/>
</Details>
);
}
78 changes: 78 additions & 0 deletions src/components/ManaCalculator/components/ManaCalculatorInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { JSX, useEffect, useState } from 'react';

interface IManaCalculatorCustomInput {
value: string | number;
onChange: (value: string) => void;
className?: string;
min?: number;
max?: number;
}

export function ManaCalculatorInput({
value,
onChange,
className,
min,
max,
}: IManaCalculatorCustomInput): JSX.Element {
const [errorMessage, setErrorMessage] = useState<string>('');
const [inputValue, setInputValue] = useState<string | number>(value);

useEffect(() => {
try {
verifyMinAndMax(value);
} catch (error) {
setErrorMessage(error.message);
}
}, [min, max]);

function verifyMinAndMax(value: string | number) {
let message = '';

if (!isNaN(min) && Number(value) < min) {
message = 'Minimum value is ' + min;
}
if (!isNaN(min) && Number(value) > max) {
message = 'Maximum value is ' + max;
}

if (
!isNaN(min) &&
!isNaN(max) &&
(Number(value) < min || Number(value) > max)
) {
message = 'Min: ' + min + ', Max: ' + max;
}
if (message) {
throw new Error(message);
}
}

function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
try {
setInputValue(e.target.value);
verifyMinAndMax(e.target.value);
setErrorMessage('');
onChange(e.target.value);
} catch (error) {
setErrorMessage(error.message);
}
}

return (
<div
className={`mana_calculator__custom_wrapper ${
errorMessage ? 'errored' : ''
}`}
>
<input
className={className}
min={min}
max={max}
value={inputValue}
onChange={handleInputChange}
/>
{errorMessage && <p className='mana_calculator__error'>{errorMessage}</p>}
</div>
);
}
20 changes: 10 additions & 10 deletions src/components/ManaCalculator/components/OtherParametersSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Select from 'react-select';
import { UserType } from '../enums';
import { CongestionType } from '../enums';
import { AdvancedSettingsValidator } from './AdvancedSettingsValidator';
import { ManaCalculatorInput } from '.';

export function OtherParametersSection() {
const {
Expand Down Expand Up @@ -34,22 +35,21 @@ export function OtherParametersSection() {
/>
<br />
<label className='inlined-label'>Initial epoch</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact inlined'
type='number'
min='0'
min={1}
max={state.finalEpoch}
value={state.initialEpoch}
onChange={(e) => handleInitialEpochChange(Number(e.target.value))}
></input>
onChange={handleInitialEpochChange}
/>
<br />
<label className='inlined-label'>Final epoch</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact inlined'
type='number'
min={state.initialEpoch + 1}
min={state.initialEpoch}
value={state.finalEpoch}
onChange={(e) => handleFinalEpochChange(Number(e.target.value))}
></input>
onChange={handleFinalEpochChange}
/>
{state.userType === UserType.HOLDER ? (
<></>
) : (
Expand Down
16 changes: 9 additions & 7 deletions src/components/ManaCalculator/components/RoleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import React from 'react';
import { useManaState } from '../hooks';
import Select from 'react-select';
import { UserType } from '../enums';
import { fromMicro, toMicro } from '../utils';
import { fromMicro } from '../utils';
import { ValidatorSettings } from './ValidatorSettings';
import { DelegatorSettings } from './DelegatorSettings';
import { ManaCalculatorInput } from '.';

export function RoleSection() {
const { state, handleUserChange, handleOwnHoldChange } = useManaState();
const { state, handleUserChange, handleOwnHoldChange, maxTotalSupply } =
useManaState();
return (
<div className='mana_calculator__card'>
<h4>Role configuration</h4>
Expand All @@ -27,13 +29,13 @@ export function RoleSection() {
/>
<br />
<label className='inlined-label'>Owned amount ({state.network})</label>
<input
<ManaCalculatorInput
min={0}
max={maxTotalSupply}
className='mana_calculator__compact inlined'
type='number'
min='0'
value={fromMicro(state.heldTokens)}
onChange={(e) => handleOwnHoldChange(toMicro(Number(e.target.value)))}
></input>
onChange={handleOwnHoldChange}
/>
<br />
{state.userType === UserType.VALIDATOR ? (
<ValidatorSettings />
Expand Down
73 changes: 40 additions & 33 deletions src/components/ManaCalculator/components/ValidatorCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import { useManaState } from '../hooks';
import { ValidatorProps } from '../types';
import { fromMicro, toMicro } from '../utils';
import { fromMicro } from '../utils';
import { ManaCalculatorInput } from '.';

export function ValidatorCard({
validator,
Expand All @@ -16,42 +17,48 @@ export function ValidatorCard({
handleDelegatedStakeChange,
handlePFChange,
handleFCChange,
maxTotalSupply,
} = useManaState();

return (
<>
<div className='col col--2'>Validator {id + 1}</div>
<input
className='col col--2 align-right horizontal-spaced'
type='number'
min='0'
value={fromMicro(validator.lockedStake)}
onChange={(e) => handleStakeChange(toMicro(Number(e.target.value)), id)}
></input>
<input
className='col col--2 align-right horizontal-spaced'
type='number'
value={fromMicro(validator.delegatedStake)}
onChange={(e) =>
handleDelegatedStakeChange(toMicro(Number(e.target.value)), id)
}
></input>
<input
className='col col--2 align-right horizontal-spaced'
type='number'
step='0.01'
min='0'
max='1'
value={validator.performanceFactor}
onChange={(e) => handlePFChange(Number(e.target.value), id)}
></input>
<input
className='col col--2 align-right horizontal-spaced'
type='number'
min='0'
step='0.01'
value={validator.fixedCost}
onChange={(e) => handleFCChange(Number(e.target.value), id)}
></input>
<div className='validator_card col col--2 align-right horizontal-spaced'>
<ManaCalculatorInput
className='w-full'
min={0}
max={maxTotalSupply}
value={fromMicro(validator.lockedStake)}
onChange={(value: string) => handleStakeChange(value, id)}
/>
</div>
<div className='validator_card col col--2 align-right horizontal-spaced'>
<ManaCalculatorInput
className='w-full'
min={0}
max={maxTotalSupply}
value={fromMicro(validator.delegatedStake)}
onChange={(value: string) => handleDelegatedStakeChange(value, id)}
/>
</div>
<div className='validator_card col col--2 align-right horizontal-spaced'>
<ManaCalculatorInput
className='w-full'
min={0}
max={1}
value={validator.performanceFactor}
onChange={(value: string) => handlePFChange(value, id)}
/>
</div>
<div className='validator_card col col--2 align-right horizontal-spaced'>
<ManaCalculatorInput
className='w-full'
min={0}
max={Number.MAX_SAFE_INTEGER}
value={validator.fixedCost}
onChange={(value: string) => handleFCChange(value, id)}
/>
</div>
<button
className=' button button--remove mana-calculator__button mana-calculator__transparent-button'
onClick={() => handleDelete(id)}
Expand Down
49 changes: 23 additions & 26 deletions src/components/ManaCalculator/components/ValidatorSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import { Details } from '@docusaurus/theme-common/Details';
import { useManaState } from '../hooks';
import { fromMicro, toMicro, roundMax } from '../utils';
import { fromMicro, roundMax } from '../utils';
import { ManaCalculatorInput } from '.';

export function ValidatorSettings() {
const {
Expand All @@ -10,6 +11,7 @@ export function ValidatorSettings() {
handleOwnFCChange,
handleOwnStakeChange,
handleAttractedNewDelegatedStakeChange,
maxTotalSupply,
} = useManaState();
return (
<Details
Expand All @@ -19,49 +21,44 @@ export function ValidatorSettings() {
<label className='inlined-long-label'>
Staked amount ({state.network})
</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact inlined'
type='number'
min='0'
min={0}
max={fromMicro(state.heldTokens)}
value={fromMicro(state.stakedOrDelegatedTokens)}
onChange={(e) => handleOwnStakeChange(toMicro(Number(e.target.value)))}
></input>
onChange={handleOwnStakeChange}
/>
<br />
<label className='inlined-long-label'>Performance factor</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact input--vertical-spaced'
min='0'
max='1'
type='number'
step='0.01'
min={0}
max={1}
value={state.validator.performanceFactor}
onChange={(e) => handleOwnPFChange(Number(e.target.value))}
></input>
onChange={handleOwnPFChange}
/>
<br />
<label className='inlined-long-label'>Fixed costs</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact input--vertical-spaced'
min='0'
type='number'
step='0.01'
min={0}
max={Number.MAX_SAFE_INTEGER}
value={state.validator.fixedCost}
onChange={(e) => handleOwnFCChange(Number(e.target.value))}
></input>
onChange={handleOwnFCChange}
/>
<label className='inlined-long-label'>
Attracted new delegated stake ({state.network})
</label>
<input
<ManaCalculatorInput
className='mana_calculator__compact input--vertical-spaced'
type='number'
min='0'
min={0}
max={maxTotalSupply}
value={roundMax(
fromMicro(state.validator.attractedNewDelegatedStake),
0,
)}
onChange={(e) =>
handleAttractedNewDelegatedStakeChange(Number(e.target.value))
}
></input>
onChange={handleAttractedNewDelegatedStakeChange}
></ManaCalculatorInput>
</Details>
);
}
1 change: 1 addition & 0 deletions src/components/ManaCalculator/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './RoleSection';
export * from './OutputForm';
export * from './NetworkSection';
export * from './ManaAcculation';
export * from './ManaCalculatorInput';
Loading

0 comments on commit 626746c

Please sign in to comment.