Skip to content

Commit

Permalink
convert to valibot
Browse files Browse the repository at this point in the history
  • Loading branch information
ap-justin committed Oct 4, 2024
1 parent 8d6e728 commit f058e6c
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 87 deletions.
26 changes: 14 additions & 12 deletions src/pages/Funds/CreateFund/CreateFund.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { valibotResolver } from "@hookform/resolvers/valibot";
import { ControlledImgEditor as ImgEditor } from "components/ImgEditor";
import Prompt from "components/Prompt";
import {
Expand All @@ -22,8 +22,7 @@ import { useCreateFundMutation } from "services/aws/funds";
import type { Fund } from "types/aws";
import { GoalSelector, MAX_SIZE_IN_BYTES, VALID_MIME_TYPES } from "../common";
import { EndowmentSelector } from "./EndowmentSelector";
import { schema } from "./schema";
import type { FormValues as FV } from "./types";
import { type FV, schema } from "./schema";

export default withAuth(function CreateFund() {
const {
Expand All @@ -36,7 +35,7 @@ export default withAuth(function CreateFund() {
formState: { errors, isSubmitting },
watch,
} = useForm<FV>({
resolver: yupResolver(schema),
resolver: valibotResolver(schema),
defaultValues: {
name: "",
description: "",
Expand All @@ -49,7 +48,9 @@ export default withAuth(function CreateFund() {
from: "fund",
allowBgTip: true,
},
targetType: "smart",
target: {
type: "smart",
},
},
});
const { field: banner } = useController({ control, name: "banner" });
Expand All @@ -60,7 +61,7 @@ export default withAuth(function CreateFund() {
});
const { field: targetType } = useController({
control,
name: "targetType",
name: "target.type",
});

const customAllowBgTipRef = useRef(true);
Expand Down Expand Up @@ -99,11 +100,11 @@ export default withAuth(function CreateFund() {
allowBgTip: fv.settings.allowBgTip,
},
target:
fv.targetType === "none"
fv.target.type === "none"
? `${0}`
: fv.targetType === "smart"
: fv.target.type === "smart"
? "smart"
: `${+fv.fixedTarget}`,
: `${+fv.target.value}`, //fixedTarget is required when targetType is fixed
};

if (fv.expiration) fund.expiration = fv.expiration;
Expand Down Expand Up @@ -145,6 +146,7 @@ export default withAuth(function CreateFund() {
className="grid border border-gray-l4 rounded-lg p-6 my-4 w-full max-w-4xl"
>
<h4 className="font-bold text-xl mb-4">Fund information</h4>

<Field
{...register("name")}
label="Name"
Expand Down Expand Up @@ -215,11 +217,11 @@ export default withAuth(function CreateFund() {
/>
{targetType.value === "fixed" && (
<Field
{...register("fixedTarget", { shouldUnregister: true })}
{...register("target.value", { shouldUnregister: true })}
label="How much money do you want to raise?"
classes="mt-2"
placeholder="$"
error={errors.fixedTarget?.message}
error={errors.target?.value?.message}
/>
)}

Expand Down Expand Up @@ -266,7 +268,7 @@ export default withAuth(function CreateFund() {
dropzone: "aspect-[1/1] w-60",
}}
maxSize={MAX_SIZE_IN_BYTES}
error={errors.banner?.file?.message}
error={errors.logo?.file?.message}
/>

<Field
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ import {
import Icon from "components/Icon";
import Image from "components/Image";
import { forwardRef, useState } from "react";
import type { FundMember } from "../types";
import type { EndowOption } from "../schema";
import { Options } from "./Options";

interface EndowmentOption extends FundMember {}

type OnChange = (opts: EndowmentOption[]) => void;
type OnChange = (opts: EndowOption[]) => void;
interface Props {
values: EndowmentOption[];
values: EndowOption[];
onChange: OnChange;
classes?: string;
disabled?: boolean;
Expand Down Expand Up @@ -87,8 +85,8 @@ export const EndowmentSelector = forwardRef<El, Props>((props, ref) => {
);
});

interface ISelectedOption extends EndowmentOption {
onDeselect: (thisOpt: EndowmentOption) => void;
interface ISelectedOption extends EndowOption {
onDeselect: (thisOpt: EndowOption) => void;
}

function SelectedOption({ onDeselect, ...props }: ISelectedOption) {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Funds/CreateFund/EndowmentSelector/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Image from "components/Image";
import { ErrorStatus, Info, LoadingStatus } from "components/Status";
import useDebouncer from "hooks/useDebouncer";
import { useEndowmentCardsQuery } from "services/aws/aws";
import type { FundMember } from "../types";
import type { EndowOption } from "../schema";

interface Props {
searchText: string;
Expand Down Expand Up @@ -52,7 +52,7 @@ export function Options({ classes = "", searchText }: Props) {
logo: o.card_img,
name: o.name,
id: o.id,
} satisfies FundMember
} satisfies EndowOption
}
className="flex gap-x-2 p-2 data-[selected]:text-blue-d1 data-[selected]:pointer-events-none hover:bg-blue-l4 select-none"
>
Expand Down
84 changes: 50 additions & 34 deletions src/pages/Funds/CreateFund/schema.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,57 @@
import type { ImgLink } from "components/ImgEditor";
import { genFileSchema } from "schemas/file";
import { schema as schemaFn, stringNumber } from "schemas/shape";
import { requiredString } from "schemas/string";
import { array, string } from "yup";
import { MAX_SIZE_IN_BYTES, VALID_MIME_TYPES } from "../common";
import type { FormValues as FV } from "./types";
import * as v from "valibot";
import { MAX_SIZE_IN_BYTES, VALID_MIME_TYPES, target } from "../common";

const fileObj = schemaFn<ImgLink>({
file: genFileSchema(MAX_SIZE_IN_BYTES, VALID_MIME_TYPES).required("required"),
const str = v.pipe(v.string(), v.trim());

const fileObject = v.object({
name: str,
publicUrl: v.pipe(str, v.url()),
});

export const imgLink = v.object({
file: v.optional(
v.pipe(
v.file(),
v.mimeType(VALID_MIME_TYPES, "invalid type"),
v.maxSize(MAX_SIZE_IN_BYTES, "exceeds size limit")
)
),
preview: v.pipe(str, v.url()),
...fileObject.entries,
});

const targetTypeKey: keyof FV = "targetType";
export const endowOption = v.object({
id: v.number(),
name: str,
logo: v.optional(v.pipe(str, v.url())),
});

export const schema = schemaFn<FV>({
name: requiredString,
description: requiredString,
banner: fileObj,
logo: fileObj,
members: array().min(1, "must contain at least one endowment"),
expiration: string()
.transform((v) => {
if (!v) return "";
return new Date(v).toISOString();
})
.datetime("invalid date")
.test(
"",
"must be in the future",
(v) => !v || v >= new Date().toISOString()
),
export const settings = v.object({
from: str,
allowBgTip: v.boolean(),
});

fixedTarget: stringNumber(
(s) =>
s.when(targetTypeKey, (values, schema) => {
const [type] = values as [FV["targetType"]];
return type === "fixed" ? schema.required("required") : schema;
}),
(n) => n.positive("must be greater than 0")
export const schema = v.object({
name: v.pipe(str, v.nonEmpty("required")),
description: v.pipe(str, v.nonEmpty("required")),
banner: imgLink,
logo: imgLink,
members: v.pipe(
v.array(endowOption),
v.minLength(1, "must contain at least one endowment")
),
featured: v.boolean(),
settings,
expiration: v.pipe(
str,
v.nonEmpty("required"),
v.transform((v) => new Date(v).toISOString()),
v.isoTimestamp("invalid date"),
v.minValue(new Date().toISOString(), "must be in the future")
),
target,
});

export interface FundMember extends v.InferOutput<typeof endowOption> {}
export interface EndowOption extends FundMember {}
export type FV = v.InferOutput<typeof schema>;
27 changes: 0 additions & 27 deletions src/pages/Funds/CreateFund/types.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/pages/Funds/CreateFund/useEndow.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect } from "react";
import { useLazyProfileQuery } from "services/aws/aws";
import type { Endowment } from "types/aws";
import type { FundMember } from "./types";
import type { FundMember } from "./schema";

export type Endow = Pick<Endowment, "hide_bg_tip" | "name">;

Expand Down
3 changes: 1 addition & 2 deletions src/pages/Funds/common/GoalSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Field, Label, Radio, RadioGroup } from "@headlessui/react";

export type TargetType = "fixed" | "none" | "smart";
import type { TargetType } from "./types";

const options: { [T in TargetType]: string } = {
smart: "Use smart milestones",
Expand Down
1 change: 1 addition & 0 deletions src/pages/Funds/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ImageMIMEType } from "types/lists";
export { target, type TargetType } from "./types";

export * from "./GoalSelector";

Expand Down
20 changes: 20 additions & 0 deletions src/pages/Funds/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as v from "valibot";
export const target = v.variant("type", [
v.object({
type: v.literal("fixed"),
value: v.pipe(
v.string("required"),
v.nonEmpty("required"),
v.transform((x) => +x),
v.number("invalid number"),
v.minValue(0, "must be greater than 0"),
/** so that the inferred type is string */
v.transform((x) => x.toString())
),
}),
v.object({ type: v.literal("smart"), value: v.optional(v.string()) }),
v.object({ type: v.literal("none"), value: v.optional(v.string()) }),
]);

export type Target = v.InferOutput<typeof target>;
export type TargetType = Target["type"];
2 changes: 0 additions & 2 deletions src/pages/Registration/Steps/ContactDetails/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ export default function Form({ classes = "" }: { classes?: string }) {
}
};

console.log({ errors });

return (
<form
// disabled={isSubmitting}
Expand Down

0 comments on commit f058e6c

Please sign in to comment.