Skip to content

Commit

Permalink
refactor(forms): zod form validation and components (#283)
Browse files Browse the repository at this point in the history
Co-authored-by: Iaroslav Gryshaiev <[email protected]>
  • Loading branch information
baktun14 and ygrishajev authored Aug 6, 2024
1 parent 2cdfafd commit 3b8279d
Show file tree
Hide file tree
Showing 80 changed files with 2,277 additions and 2,394 deletions.
2 changes: 1 addition & 1 deletion apps/deploy-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"@emotion/react": "^11.11.0",
"@emotion/server": "^11.4.0",
"@emotion/styled": "^11.8.1",
"@hookform/resolvers": "^3.3.4",
"@leapwallet/elements": "^1.3.4",
"@hookform/resolvers": "^3.9.0",
"@leapwallet/name-matcha": "^1.7.1",
"@monaco-editor/react": "^4.6.0",
"@mui/icons-material": "^5.11.11",
Expand Down
2 changes: 2 additions & 0 deletions apps/deploy-web/public/sw.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/deploy-web/public/sw.js.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apps/deploy-web/public/workbox-495fd258.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/deploy-web/public/workbox-495fd258.js.map

Large diffs are not rendered by default.

191 changes: 80 additions & 111 deletions apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
import { useRef, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { FormattedDate } from "react-intl";
import { Alert, Popup } from "@akashnetwork/ui/components";
import { Alert, Form, FormField, FormInput, Popup } from "@akashnetwork/ui/components";
import { EncodeObject } from "@cosmjs/proto-signing";
import FormControl from "@mui/material/FormControl";
import InputAdornment from "@mui/material/InputAdornment";
import TextField from "@mui/material/TextField";
import { zodResolver } from "@hookform/resolvers/zod";
import { addYears, format } from "date-fns";
import { event } from "nextjs-google-analytics";
import { z } from "zod";

import { LinkTo } from "@src/components/shared/LinkTo";
import { useWallet } from "@src/context/WalletProvider";
Expand All @@ -19,31 +18,33 @@ import { uAktDenom } from "@src/utils/constants";
import { aktToUakt, coinToDenom } from "@src/utils/priceUtils";
import { TransactionMessageData } from "@src/utils/TransactionMessageData";

type AllowanceFormValues = {
amount: number;
expiration: string;
useDepositor: boolean;
granteeAddress: string;
};

type Props = {
address: string;
editingAllowance?: AllowanceType | null;
onClose: () => void;
};

const formSchema = z.object({
amount: z.coerce.number().min(0, {
message: "Amount must be greater than 0."
}),
expiration: z.string().min(1, "Expiration is required."),
granteeAddress: z.string().min(1, "Grantee address is required.")
});

export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowance, address, onClose }) => {
const formRef = useRef<HTMLFormElement>(null);
const [error, setError] = useState("");
const { signAndBroadcastTx } = useWallet();
const { handleSubmit, control, watch, clearErrors, setValue } = useForm<AllowanceFormValues>({
const form = useForm<z.infer<typeof formSchema>>({
defaultValues: {
amount: editingAllowance ? coinToDenom(editingAllowance.allowance.spend_limit[0]) : 0,
expiration: format(addYears(new Date(), 1), "yyyy-MM-dd'T'HH:mm"),
useDepositor: false,
granteeAddress: editingAllowance?.grantee ?? ""
}
},
resolver: zodResolver(formSchema)
});
const { handleSubmit, control, watch, clearErrors, setValue } = form;
const { amount, granteeAddress, expiration } = watch();
const denomData = useDenomData(uAktDenom);

Expand All @@ -52,7 +53,7 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc
formRef.current?.dispatchEvent(new Event("submit", { cancelable: true, bubbles: true }));
};

const onSubmit = async ({ amount, expiration, granteeAddress }: AllowanceFormValues) => {
const onSubmit = async ({ amount, expiration, granteeAddress }: z.infer<typeof formSchema>) => {
setError("");
clearErrors();

Expand Down Expand Up @@ -114,106 +115,74 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc
enableCloseOnBackdropClick
title="Authorize Fee Spending"
>
<form onSubmit={handleSubmit(onSubmit)} ref={formRef}>
<Alert className="mb-4">
<p className="text-sm text-muted-foreground">
<LinkTo onClick={ev => handleDocClick(ev, "https://docs.cosmos.network/v0.46/modules/feegrant/")}>Authorized Fee Spend</LinkTo> allows users to
authorize spend of a set number of tokens on fees from a source wallet to a destination, funded wallet.
</p>
</Alert>

<div className="mb-2 mt-2 text-right">
<LinkTo onClick={() => onBalanceClick()}>
Balance: {denomData?.balance} {denomData?.label}
</LinkTo>
</div>

<FormControl className="mb-4" fullWidth>
<Controller
control={control}
name="amount"
rules={{
required: true
}}
render={({ fieldState, field }) => {
const helperText = fieldState.error?.type === "validate" ? "Invalid amount." : "Amount is required.";

return (
<TextField
{...field}
type="number"
variant="outlined"
label="Spending Limit"
autoFocus
error={!!fieldState.error}
helperText={fieldState.error && helperText}
inputProps={{ min: 0, step: 0.000001, max: denomData?.inputMax }}
InputProps={{
startAdornment: <InputAdornment position="start">AKT</InputAdornment>
}}
/>
);
}}
/>
</FormControl>

<FormControl className="mb-4" fullWidth>
<Controller
control={control}
name="granteeAddress"
defaultValue=""
rules={{
required: true
}}
render={({ fieldState, field }) => {
return (
<TextField
{...field}
type="text"
variant="outlined"
label="Grantee Address"
disabled={!!editingAllowance}
error={!!fieldState.error}
helperText={fieldState.error && "Grantee address is required."}
/>
);
}}
/>
</FormControl>

<FormControl className="mb-4" fullWidth>
<Controller
control={control}
name="expiration"
rules={{
required: true
}}
render={({ fieldState, field }) => {
return (
<TextField
{...field}
type="datetime-local"
variant="outlined"
label="Expiration"
error={!!fieldState.error}
helperText={fieldState.error && "Expiration is required."}
/>
);
}}
/>
</FormControl>

{!!amount && granteeAddress && (
<Alert>
<Form {...form}>
<form onSubmit={handleSubmit(onSubmit)} ref={formRef}>
<Alert className="mb-4">
<p className="text-sm text-muted-foreground">
This address will be able to spend up to {amount} AKT on <b>transaction fees</b> on your behalf ending on{" "}
<FormattedDate value={expiration} year="numeric" month="2-digit" day="2-digit" hour="2-digit" minute="2-digit" />.
<LinkTo onClick={ev => handleDocClick(ev, "https://docs.cosmos.network/v0.46/modules/feegrant/")}>Authorized Fee Spend</LinkTo> allows users to
authorize spend of a set number of tokens on fees from a source wallet to a destination, funded wallet.
</p>
</Alert>
)}

{error && <Alert variant="warning">{error}</Alert>}
</form>
<div className="mb-2 mt-2 text-right">
<LinkTo onClick={() => onBalanceClick()}>
Balance: {denomData?.balance} {denomData?.label}
</LinkTo>
</div>

<div className="mb-4 w-full">
<FormField
control={control}
name="amount"
render={({ field }) => {
return (
<FormInput
{...field}
type="number"
label="Spending Limit"
autoFocus
min={0}
step={0.000001}
max={denomData?.inputMax}
startIcon={<span className="text-xs pl-2">{denomData?.label}</span>}
/>
);
}}
/>
</div>

<div className="mb-4 w-full">
<FormField
control={control}
name="granteeAddress"
render={({ field }) => {
return <FormInput {...field} type="text" label="Grantee Address" disabled={!!editingAllowance} />;
}}
/>
</div>

<div className="mb-4 w-full">
<Controller
control={control}
name="expiration"
render={({ field }) => {
return <FormInput {...field} type="datetime-local" label="Expiration" />;
}}
/>
</div>

{!!amount && granteeAddress && (
<Alert>
<p className="text-sm text-muted-foreground">
This address will be able to spend up to {amount} AKT on <b>transaction fees</b> on your behalf ending on{" "}
<FormattedDate value={expiration} year="numeric" month="2-digit" day="2-digit" hour="2-digit" minute="2-digit" />.
</p>
</Alert>
)}

{error && <Alert variant="warning">{error}</Alert>}
</form>
</Form>
</Popup>
);
};
Loading

0 comments on commit 3b8279d

Please sign in to comment.