diff --git a/src/components/cards/AppInfoCard.tsx b/src/components/cards/AppInfoCard.tsx index 1fcb967..4672106 100644 --- a/src/components/cards/AppInfoCard.tsx +++ b/src/components/cards/AppInfoCard.tsx @@ -4,7 +4,9 @@ import AccountSelectionDialog from '@/components/ui/ledger-account-select' import { Modal } from '@/components/ui/modal' import ProgressBar from '@/components/ui/progress-bar' import { Spinner } from '@/components/ui/spinner' -import calculateAmountToRequest from '@/helpers/calculateAmountToRefill' +import calculateAmountToRequest, { + validateAmount, +} from '@/helpers/calculateAmountToRefill' import useApplicationActions from '@/hooks/useApplicationActions' import { useAllocator } from '@/lib/AllocatorProvider' import { stateColor, stateMapping } from '@/lib/constants' @@ -40,7 +42,13 @@ import TextField from '@mui/material/TextField' import axios from 'axios' import { useSession } from 'next-auth/react' import { useRouter } from 'next/navigation' -import { useCallback, useEffect, useState } from 'react' +import { + useCallback, + useEffect, + useMemo, + useState, + type ReactNode, +} from 'react' import { toast } from 'react-toastify' import AllocatorBalance from '../AllocatorBalance' @@ -92,7 +100,7 @@ const AppInfoCard: React.FC = ({ mutationRemovePendingAllocation, } = useApplicationActions(initialApplication, repo, owner) const [buttonText, setButtonText] = useState('') - const [modalMessage, setModalMessage] = useState(null) + const [modalMessage, setModalMessage] = useState(null) const [error, setError] = useState(false) const [walletConnected, setWalletConnected] = useState(false) const [isWalletConnecting, setIsWalletConnecting] = useState(false) @@ -608,6 +616,30 @@ const AppInfoCard: React.FC = ({ })) } + const totalAmountIsValid = useMemo( + () => validateAmount(application.Datacap['Total Requested Amount']), + [application.Datacap], + ) + const weeklyAllocationRequestIsValid = useMemo( + () => validateAmount(application.Datacap['Weekly Allocation']), + [application.Datacap], + ) + + const createInvalidFieldsModalContent = ( + totalAmountIsValid: boolean, + weeklyAllocationRequestIsValid: boolean, + ): string => { + let modalMessage = '' + if (!totalAmountIsValid) modalMessage = '"Total Requested Amount"' + if (!weeklyAllocationRequestIsValid) { + if (!totalAmountIsValid) modalMessage += ' and ' + modalMessage += '"Weekly Allocation"' + } + modalMessage += + ' is not a valid amount format. Please navigate to the application below and update the field. (e.g 100TiB, 600.46GiB, 1.5PiB)' + return modalMessage + } + useEffect(() => { // if not the first allocation, prefill the amount with ssa bot suggested value if ( @@ -623,7 +655,38 @@ const AppInfoCard: React.FC = ({ isDialogOpen: false, })) } - }, [application]) + + if (!totalAmountIsValid || !weeklyAllocationRequestIsValid) { + const modalMessage = createInvalidFieldsModalContent( + totalAmountIsValid, + weeklyAllocationRequestIsValid, + ) + + const link = ( + + https://github.com/{owner}/{repo}/issues/ + {application['Issue Number']} + + ) + setModalMessage( + <> + {modalMessage} +
+ {link} + , + ) + setError(true) + } + }, [ + application, + totalAmountIsValid, + weeklyAllocationRequestIsValid, + owner, + repo, + ]) const stateLabel = stateMapping[application.Lifecycle.State as keyof typeof stateMapping] ?? diff --git a/src/components/ui/modal.tsx b/src/components/ui/modal.tsx index d061a1d..a4e9449 100644 --- a/src/components/ui/modal.tsx +++ b/src/components/ui/modal.tsx @@ -1,6 +1,6 @@ import { Button } from './button' interface ModalProps { - message?: string + message?: React.ReactNode onClose: () => void error?: boolean } diff --git a/src/helpers/calculateAmountToRefill.ts b/src/helpers/calculateAmountToRefill.ts index d97ab26..363557d 100644 --- a/src/helpers/calculateAmountToRefill.ts +++ b/src/helpers/calculateAmountToRefill.ts @@ -112,3 +112,8 @@ const splitString = (input: string): [string, string] => { return ['0', 'B'] } + +export const validateAmount = (amount: string): boolean => { + const regex = /^(\d+(\.\d+)?)([A-Za-z]iB)$/ + return regex.test(amount) +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 2183262..81cf98f 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,11 +1,11 @@ -import { type ClassValue, clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' -import ByteConverter from '@wtfcode/byte-converter' import { - type Application, type AllocationRequest, + type Application, type ByteConverterAutoscaleOptions, } from '@/type' +import ByteConverter from '@wtfcode/byte-converter' +import { type ClassValue, clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]): string { return twMerge(clsx(inputs)) @@ -44,8 +44,13 @@ export function anyToBytes(inputDatacap: string): number { .replace(/\s*/g, '') const ext = formatDc.replace(/[0-9.]/g, '') const datacap = formatDc.replace(/[^0-9.]/g, '') - const bytes = byteConverter.convert(parseFloat(datacap), ext, 'B') - return bytes + try { + const bytes = byteConverter.convert(parseFloat(datacap), ext, 'B') + return bytes + } catch (e) { + console.error(e) + return 0 + } } export const getLastDatacapAllocation = (