From 3a6920798f11cae71d327768efa62fe91cbaa72e Mon Sep 17 00:00:00 2001 From: Filip Lelek Date: Mon, 16 Dec 2024 19:19:04 +0100 Subject: [PATCH] Extract the allocation modal into a separate component and unify --- src/components/DatacapAmountModel.tsx | 191 ++++++++++++ src/components/cards/AppInfoCard.tsx | 416 +++++-------------------- src/helpers/calculateAmountToRefill.ts | 10 +- src/hooks/useApplicationActions.ts | 6 +- src/lib/apiClient.ts | 4 +- src/type.ts | 4 +- 6 files changed, 279 insertions(+), 352 deletions(-) create mode 100644 src/components/DatacapAmountModel.tsx diff --git a/src/components/DatacapAmountModel.tsx b/src/components/DatacapAmountModel.tsx new file mode 100644 index 0000000..8ac1c9c --- /dev/null +++ b/src/components/DatacapAmountModel.tsx @@ -0,0 +1,191 @@ +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, +} from '@mui/material' +import { Spinner } from '@/components/ui/spinner' +import FormControl from '@mui/material/FormControl' +import FormLabel from '@mui/material/FormLabel' +import RadioGroup from '@mui/material/RadioGroup' +import FormControlLabel from '@mui/material/FormControlLabel' +import Radio from '@mui/material/Radio' +import Box from '@mui/material/Box' +import InputLabel from '@mui/material/InputLabel' +import Select, { type SelectChangeEvent } from '@mui/material/Select' +import MenuItem from '@mui/material/MenuItem' +import TextField from '@mui/material/TextField' +import { Button } from '@/components/ui/button' +import { AllocationUnit, type Allocation, type Application } from '@/type' +import { type ReactNode } from 'react' + +type DeviationType = 'contract' | 'directly' + +interface AllocationConfig { + isDialogOpen: boolean + amount: string + unit: AllocationUnit + deviationType?: DeviationType +} + +interface DatacapAmountModalProps { + allocationConfig: AllocationConfig + title: string + isApiCalling: boolean + isWalletConnecting: boolean + allocation: Allocation | undefined + application: Application + clientContractAddress?: string | null + setAllocationConfig: (config: any) => void + onClose: () => void + onCancel: () => void + onConfirm: () => void +} + +const DatacapAmountModal = ({ + allocationConfig, + setAllocationConfig, + isApiCalling, + isWalletConnecting, + onClose, + onCancel, + onConfirm, + allocation, + application, + title, + clientContractAddress, +}: DatacapAmountModalProps): ReactNode => { + return ( + + {title} + + {(isApiCalling || isWalletConnecting) && ( +
+ +
+ )} +
+ {clientContractAddress && + clientContractAddress !== null && + [ + 'KYCRequested', + 'Submitted', + 'AdditionalInfoRequired', + 'AdditionalInfoSubmitted', + ].includes(application?.Lifecycle?.State) && ( +
+ + + Deviation type + + { + setAllocationConfig({ + ...allocationConfig, + deviationType: (e.target as HTMLInputElement) + .value as DeviationType, + }) + }} + > + } + label="Directly" + /> + } + label={'Contract'} + /> + + +
+ )} +
+ {clientContractAddress && + clientContractAddress !== null && + [ + 'KYCRequested', + 'Submitted', + 'AdditionalInfoRequired', + 'AdditionalInfoSubmitted', + ].includes(application?.Lifecycle?.State) && ( + Allocation + )} +
+
+
+ + + ) => { + setAllocationConfig((prev: AllocationConfig) => ({ + ...prev, + amount: e.target.value, + })) + }} + variant="outlined" + required + /> + + +
+
+ + + Unit + + + +
+
+
+
+ + + + +
+ ) +} + +export default DatacapAmountModal diff --git a/src/components/cards/AppInfoCard.tsx b/src/components/cards/AppInfoCard.tsx index f094f26..9669828 100644 --- a/src/components/cards/AppInfoCard.tsx +++ b/src/components/cards/AppInfoCard.tsx @@ -5,6 +5,7 @@ import { Modal } from '@/components/ui/modal' import ProgressBar from '@/components/ui/progress-bar' import { Spinner } from '@/components/ui/spinner' import calculateAmountToRequest, { + splitString, validateAmount, } from '@/helpers/calculateAmountToRefill' import useApplicationActions from '@/hooks/useApplicationActions' @@ -12,15 +13,10 @@ import useWallet from '@/hooks/useWallet' import { useAllocator } from '@/lib/AllocatorProvider' import { stateColor, stateMapping } from '@/lib/constants' import { getAllowanceForClient } from '@/lib/glifApi' -import { - anyToBytes, - bytesToiB, - calculateDatacap, - getLastDatacapAllocation, -} from '@/lib/utils' +import { anyToBytes, bytesToiB, getLastDatacapAllocation } from '@/lib/utils' import { LDNActorType, - RefillUnit, + AllocationUnit, type Allocation, type Application, } from '@/type' @@ -30,15 +26,6 @@ import { DialogContent, DialogTitle, } from '@mui/material' -import Box from '@mui/material/Box' -import FormControl from '@mui/material/FormControl' -import FormControlLabel from '@mui/material/FormControlLabel' -import FormLabel from '@mui/material/FormLabel' -import InputLabel from '@mui/material/InputLabel' -import MenuItem from '@mui/material/MenuItem' -import Radio from '@mui/material/Radio' -import RadioGroup from '@mui/material/RadioGroup' -import Select, { type SelectChangeEvent } from '@mui/material/Select' import TextField from '@mui/material/TextField' import axios from 'axios' import { useSession } from 'next-auth/react' @@ -54,6 +41,7 @@ import { useQuery } from 'react-query' import { toast } from 'react-toastify' import AllocatorBalance from '../AllocatorBalance' import AllowedSps from './dialogs/allowedSps' +import DatacapAmountModal from '../DatacapAmountModel' interface ComponentProps { initialApplication: Application @@ -125,11 +113,11 @@ const AppInfoCard: React.FC = ({ const [refillInfoParams, setRefillInfoParams] = useState<{ amount: string - unit: RefillUnit + unit: AllocationUnit isDialogOpen: boolean }>({ amount: '0', - unit: RefillUnit.GIB, + unit: AllocationUnit.GIB, isDialogOpen: false, }) @@ -143,13 +131,13 @@ const AppInfoCard: React.FC = ({ const [allocationAmountConfig, setAllocationAmountConfig] = useState<{ amount: string - allocationType: string deviationType: DeviationType + unit: AllocationUnit isDialogOpen: boolean }>({ amount: '', - allocationType: 'fixed', deviationType: 'directly', + unit: AllocationUnit.GIB, isDialogOpen: false, }) @@ -612,20 +600,23 @@ const AppInfoCard: React.FC = ({ ): Promise => { if (!shouldSubmit) { setAllocationAmountConfig((prev) => ({ - ...prev, isDialogOpen: false, - amount: '', + amount: prev.amount || '0', + unit: prev.unit || AllocationUnit.GIB, deviationType: 'directly', })) return } - - if (anyToBytes(allocationAmountConfig.amount) > allowance) { + const amountWithUnit = allocationAmountConfig.amount.concat( + ' ', + allocationAmountConfig.unit.toString(), + ) + if (anyToBytes(amountWithUnit) > allowance) { toast.error('Amount is bigger than the allowance') return } - if (anyToBytes(allocationAmountConfig.amount) > remaining) { + if (anyToBytes(amountWithUnit) > remaining) { toast.error('Amount is bigger than remaning') return } @@ -634,7 +625,7 @@ const AppInfoCard: React.FC = ({ const userName = session.data?.user?.githubUsername if (!userName) return const validatedAllocationAmount = validateAndReturnDatacap( - allocationAmountConfig.amount, + amountWithUnit, application.Datacap['Total Requested Amount'], ) @@ -739,14 +730,20 @@ const AppInfoCard: React.FC = ({ application.Lifecycle.State === 'ReadyToSign' && application['Allocation Requests'].length > 1 ) { - setAllocationAmountConfig((prev) => ({ + let [amount, unit] = splitString( + application['Allocation Requests'].find((e) => e.Active)?.[ + 'Allocation Amount' + ] ?? '', + ) + if (unit === 'B') { + unit = AllocationUnit.GIB + } + + setAllocationAmountConfig(() => ({ deviationType: 'directly', - allocationType: 'manual', - amount: - application['Allocation Requests'].find((e) => e.Active)?.[ - 'Allocation Amount' - ] ?? '', + amount, isDialogOpen: false, + unit: unit as AllocationUnit, })) } @@ -1269,20 +1266,19 @@ const AppInfoCard: React.FC = ({ ) : ( progress > 75 && - remaining > 0 && ( - - ) + remaining > 0 && + ) ) : ( @@ -1294,105 +1290,30 @@ const AppInfoCard: React.FC = ({ - { setRefillInfoParams((prev) => ({ ...prev, isDialogOpen: false, })) }} - fullWidth - > - Datacap Refill Request -
{ - e.preventDefault() - void handleSSASubmit() - }} - > - - {(isApiCalling || isWalletConnecting) && ( -
- -
- )} -
-
- - - ) => { - setRefillInfoParams((prev) => ({ - ...prev, - amount: e.target.value, - })) - }} - variant="outlined" - required - /> - - -
-
- - - Unit - - - -
-
-
- - - - -
-
+ onCancel={() => { + setRefillInfoParams((prev) => ({ + ...prev, + isDialogOpen: false, + })) + }} + onConfirm={() => { + void handleSSASubmit() + }} + /> { @@ -1451,210 +1372,25 @@ const AppInfoCard: React.FC = ({ - { void handleAllocationAmountClose(false) }} - fullWidth - > - - Fill DataCap Amount for current allocation - - - {(isApiCalling || isWalletConnecting) && ( -
- -
- )} -
- {typeof selectedAllocator === 'object' && - selectedAllocator?.client_contract_address && - selectedAllocator?.client_contract_address !== null && - [ - 'KYCRequested', - 'Submitted', - 'AdditionalInfoRequired', - 'AdditionalInfoSubmitted', - ].includes(application?.Lifecycle?.State) && ( -
- - - Deviation type - - { - setAllocationAmountConfig((prev) => ({ - ...prev, - deviationType: (e.target as HTMLInputElement) - .value as DeviationType, - })) - }} - > - } - label="Directly" - /> - } - label={'Contract'} - /> - - -
- )} -
- - - Allocation Amount Type - - { - if (e.target.value !== 'manual') { - setAllocationAmountConfig({ - ...allocationAmountConfig, - amount: '', - }) - } - setAllocationAmountConfig((prev) => ({ - ...prev, - allocationType: (e.target as HTMLInputElement).value, - })) - }} - > - } - label={ - allocation?.allocation_amount_type - ? allocation.allocation_amount_type - .charAt(0) - .toUpperCase() + - allocation.allocation_amount_type.slice(1) - : 'Fixed' - } - /> - } - label="Manual" - /> - - -
- {!allocationAmountConfig.allocationType || - allocationAmountConfig.allocationType === 'percentage' || - allocationAmountConfig.allocationType === 'fixed' ? ( - - - Amount - - - - ) : ( - - , - ) => { - setAllocationAmountConfig((prev) => ({ - ...prev, - amount: event.target.value, - })) - }} - /> - - )} -
-
-
-
- - - - -
+ onCancel={() => void handleAllocationAmountClose(false)} + onConfirm={() => void handleAllocationAmountClose(true)} + isApiCalling={isApiCalling} + isWalletConnecting={isWalletConnecting} + title="Fill DataCap Amount for current allocation" + clientContractAddress={ + typeof selectedAllocator === 'object' + ? selectedAllocator?.client_contract_address + : null + } + /> ) } diff --git a/src/helpers/calculateAmountToRefill.ts b/src/helpers/calculateAmountToRefill.ts index cb7bb9a..a723c00 100644 --- a/src/helpers/calculateAmountToRefill.ts +++ b/src/helpers/calculateAmountToRefill.ts @@ -1,5 +1,5 @@ import { anyToBytes, bytesToiB } from '@/lib/utils' -import { type Application, type RequestAmount, RefillUnit } from '@/type' +import { type Application, type RequestAmount, AllocationUnit } from '@/type' export default function calculateAmountToRequest( application: Application, @@ -50,7 +50,7 @@ export default function calculateAmountToRequest( let retObj: RequestAmount = { amount: '0', - amountType: RefillUnit.GIB, + amountType: AllocationUnit.GIB, } if (sumTotalAmountWithNextRequest > totaldDcRequestedByClient) { nextRequest = totaldDcRequestedByClient - totalDcGrantedForClientSoFar @@ -59,13 +59,13 @@ export default function calculateAmountToRequest( if (nextRequest <= 0) { retObj = { amount: '0', - amountType: RefillUnit.GIB, + amountType: AllocationUnit.GIB, } return retObj } const [amount, amountType] = splitString(bytesToiB(Math.floor(nextRequest))) - const matchedAvailableType = Object.values(RefillUnit).find( + const matchedAvailableType = Object.values(AllocationUnit).find( (type) => type === amountType, ) if (matchedAvailableType) { @@ -99,7 +99,7 @@ export default function calculateAmountToRequest( weeklyDcAllocationBytes, ) } -const splitString = (input: string): [string, string] => { +export const splitString = (input: string): [string, string] => { // Regex to match expressions like "100PiB" or "0.5TiB" const regex = /^(\d+(\.\d+)?)([A-Za-z]iB)$/ diff --git a/src/hooks/useApplicationActions.ts b/src/hooks/useApplicationActions.ts index 68f8079..8b0a16a 100644 --- a/src/hooks/useApplicationActions.ts +++ b/src/hooks/useApplicationActions.ts @@ -21,7 +21,7 @@ import { import { AllocatorTypeEnum, type Application, - type RefillUnit, + type AllocationUnit, type StorageProvidersChangeRequest, } from '@/type' import { useMemo, useState } from 'react' @@ -39,7 +39,7 @@ interface ApplicationActions { mutationTriggerSSA: UseMutationResult< Application | undefined, unknown, - { userName: string; amount: string; unit: RefillUnit }, + { userName: string; amount: string; unit: AllocationUnit }, unknown > mutationRequestInfo: UseMutationResult< @@ -374,7 +374,7 @@ const useApplicationActions = ( const mutationTriggerSSA = useMutation< Application | undefined, Error, - { userName: string; amount: string; unit: RefillUnit } + { userName: string; amount: string; unit: AllocationUnit } >( async ({ userName, amount, unit }) => { return await triggerSSA( diff --git a/src/lib/apiClient.ts b/src/lib/apiClient.ts index 9d9f63e..ac38c0a 100644 --- a/src/lib/apiClient.ts +++ b/src/lib/apiClient.ts @@ -3,7 +3,7 @@ import { type Allocator, type Application, type LDNActorsResponse, - type RefillUnit, + type AllocationUnit, } from '@/type' import axios from 'axios' import { getAccessToken } from './session' @@ -218,7 +218,7 @@ export const postAdditionalInfoRequest = async ( export const triggerSSA = async ( amount: string, - unit: RefillUnit, + unit: AllocationUnit, id: string, repo: string, owner: string, diff --git a/src/type.ts b/src/type.ts index d517822..9677920 100644 --- a/src/type.ts +++ b/src/type.ts @@ -1,6 +1,6 @@ import type { Address } from 'viem' -export enum RefillUnit { +export enum AllocationUnit { PIB = 'PiB', TIB = 'TiB', GIB = 'GiB', @@ -8,7 +8,7 @@ export enum RefillUnit { export interface RequestAmount { amount: string - amountType: RefillUnit + amountType: AllocationUnit } export interface Application {