From e734896bad69d7c6f04136f8b94c3d099394b844 Mon Sep 17 00:00:00 2001 From: ap-justin <89639563+ap-justin@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:44:38 +0800 Subject: [PATCH] use fundraiser lib --- package.json | 2 + src/pages/Admin/Charity/Funds/Funds.tsx | 4 +- src/pages/Funds/Cards/Card.tsx | 4 +- src/pages/Funds/Cards/Progress.tsx | 4 +- src/pages/Funds/CreateFund/CreateFund.tsx | 4 +- src/pages/Funds/EditFund/Form.tsx | 27 ++++---- src/pages/Funds/EditFund/schema.ts | 27 ++++++++ src/pages/Funds/EditFund/types.ts | 10 --- src/pages/Funds/EditFund/useRhf.ts | 58 +++------------- src/pages/Funds/Fund/FundContext.ts | 6 +- src/services/aws/aws.ts | 4 +- src/services/aws/funds.ts | 15 +++-- src/types/aws/ap/index.ts | 81 ----------------------- yarn.lock | 23 +++++++ 14 files changed, 100 insertions(+), 169 deletions(-) create mode 100644 src/pages/Funds/EditFund/schema.ts delete mode 100644 src/pages/Funds/EditFund/types.ts diff --git a/package.json b/package.json index 7e91ddd465..18bd37d06f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,9 @@ }, "dependencies": { "@better-giving/assets": "1.0.18", + "@better-giving/fundraiser": "1.0.0-rc.5", "@better-giving/registration": "1.0.24", + "@better-giving/types": "1.0.0-rc.2", "@gsap/react": "2.1.1", "@headlessui/react": "2.1.0", "@hookform/error-message": "2.0.1", diff --git a/src/pages/Admin/Charity/Funds/Funds.tsx b/src/pages/Admin/Charity/Funds/Funds.tsx index 8ff4afb96c..66fcbfe32f 100644 --- a/src/pages/Admin/Charity/Funds/Funds.tsx +++ b/src/pages/Admin/Charity/Funds/Funds.tsx @@ -1,3 +1,4 @@ +import type { FundItem as TFundItem } from "@better-giving/fundraiser"; import ContentLoader from "components/ContentLoader"; import Image from "components/Image"; import QueryLoader from "components/QueryLoader"; @@ -5,7 +6,6 @@ import { appRoutes } from "constants/routes"; import { useAuthenticatedUser } from "contexts/Auth"; import { Link } from "react-router-dom"; import { useFundsEndowMemberOfQuery } from "services/aws/aws"; -import type { Fund } from "types/aws"; import { useAdminContext } from "../../Context"; export function Funds() { @@ -41,7 +41,7 @@ export function Funds() { ); } -const FundItem = (props: Fund.Card) => { +const FundItem = (props: TFundItem) => { const user = useAuthenticatedUser(); const isActive = new Date().toISOString() <= props.expiration && props.active; const isEditor = user.funds.includes(props.id); diff --git a/src/pages/Funds/Cards/Card.tsx b/src/pages/Funds/Cards/Card.tsx index 186c3f7240..1fcadfe37c 100644 --- a/src/pages/Funds/Cards/Card.tsx +++ b/src/pages/Funds/Cards/Card.tsx @@ -1,9 +1,9 @@ +import type { FundItem } from "@better-giving/fundraiser"; import flying_character from "assets/images/flying-character.png"; import Image from "components/Image"; import VerifiedIcon from "components/VerifiedIcon"; import { appRoutes } from "constants/routes"; import { Link } from "react-router-dom"; -import type { Fund } from "types/aws"; import { Progress } from "./Progress"; export default function Card({ @@ -14,7 +14,7 @@ export default function Card({ verified, donation_total_usd, target, -}: Fund.Card) { +}: FundItem) { return (
+ props: Pick ) { if (props.target === "0") { if (!props.donation_total_usd) return; diff --git a/src/pages/Funds/CreateFund/CreateFund.tsx b/src/pages/Funds/CreateFund/CreateFund.tsx index b0d840b7f1..3b03d6362d 100644 --- a/src/pages/Funds/CreateFund/CreateFund.tsx +++ b/src/pages/Funds/CreateFund/CreateFund.tsx @@ -1,3 +1,4 @@ +import type { NewFund } from "@better-giving/fundraiser/schema"; import { valibotResolver } from "@hookform/resolvers/valibot"; import { ControlledImgEditor as ImgEditor } from "components/ImgEditor"; import Prompt from "components/Prompt"; @@ -19,7 +20,6 @@ import { type SubmitHandler, useController, useForm } from "react-hook-form"; import { Link } from "react-router-dom"; import { useLazyProfileQuery } from "services/aws/aws"; 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 { type FV, schema } from "./schema"; @@ -88,7 +88,7 @@ export default withAuth(function CreateFund() { showModal(Prompt, { type: "loading", children: "Creating fund..." }); - const fund: Fund.New = { + const fund: NewFund = { name: fv.name, description: fv.description, banner: getFullURL(uploadBaseUrl, banner.file.name), diff --git a/src/pages/Funds/EditFund/Form.tsx b/src/pages/Funds/EditFund/Form.tsx index a74ec3dcb4..16fb14ce1d 100644 --- a/src/pages/Funds/EditFund/Form.tsx +++ b/src/pages/Funds/EditFund/Form.tsx @@ -1,3 +1,5 @@ +import type { SingleFund } from "@better-giving/fundraiser"; +import type { FundUpdate } from "@better-giving/fundraiser/schema"; import { ControlledImgEditor as ImgEditor, type ImgLink, @@ -9,13 +11,15 @@ import { useModalContext } from "contexts/ModalContext"; import { getFullURL, uploadFiles } from "helpers/uploadFiles"; import type { SubmitHandler } from "react-hook-form"; import { useCloseFundMutation, useEditFundMutation } from "services/aws/funds"; -import type { Fund } from "types/aws"; import { GoalSelector, MAX_SIZE_IN_BYTES, VALID_MIME_TYPES } from "../common"; import { FeatureBanner } from "./FeatureBanner"; -import type { FV } from "./types"; +import type { FV } from "./schema"; import { useRhf } from "./useRhf"; -export function Form({ classes = "", ...props }: Fund & { classes?: string }) { +export function Form({ + classes = "", + ...props +}: SingleFund & { classes?: string }) { const { showModal } = useModalContext(); const { handleError } = useErrorContext(); const rhf = useRhf(props); @@ -24,8 +28,7 @@ export function Form({ classes = "", ...props }: Fund & { classes?: string }) { const [closeFund, { isLoading: isClosingFund }] = useCloseFundMutation(); const onSubmit: SubmitHandler = async ({ - targetType, - fixedTarget, + target, logo, banner, ...fv @@ -40,15 +43,15 @@ export function Form({ classes = "", ...props }: Fund & { classes?: string }) { }); /// BUILD UPDATE /// - const update: Fund.Update = {}; + const update: FundUpdate = {}; - if (rhf.dirtyFields.targetType || rhf.dirtyFields.fixedTarget) { + if (rhf.dirtyFields.target) { update.target = - targetType === "none" + target.type === "none" ? "0" - : targetType === "smart" + : target.type === "smart" ? "smart" - : (fixedTarget as `${number}`); + : target.value; } if (rhf.dirtyFields.banner) update.banner = bannerUrl; @@ -161,11 +164,11 @@ export function Form({ classes = "", ...props }: Fund & { classes?: string }) { /> {rhf.targetType.value === "fixed" && ( )} diff --git a/src/pages/Funds/EditFund/schema.ts b/src/pages/Funds/EditFund/schema.ts new file mode 100644 index 0000000000..df00985a79 --- /dev/null +++ b/src/pages/Funds/EditFund/schema.ts @@ -0,0 +1,27 @@ +import { fileObject } from "@better-giving/types"; +import * as v from "valibot"; +import { MAX_SIZE_IN_BYTES, VALID_MIME_TYPES, target } from "../common"; + +const str = v.pipe(v.string(), v.trim()); + +export const imgLink = v.object({ + file: v.optional( + v.pipe( + v.file("required"), + v.mimeType(VALID_MIME_TYPES, "invalid type"), + v.maxSize(MAX_SIZE_IN_BYTES, "exceeds size limit") + ) + ), + preview: v.pipe(str, v.url()), + ...fileObject.entries, +}); + +export const schema = v.object({ + name: v.pipe(str, v.nonEmpty("required")), + description: v.pipe(str, v.nonEmpty("required")), + target, + banner: imgLink, + logo: imgLink, +}); + +export type FV = v.InferOutput; diff --git a/src/pages/Funds/EditFund/types.ts b/src/pages/Funds/EditFund/types.ts deleted file mode 100644 index 059ba933fa..0000000000 --- a/src/pages/Funds/EditFund/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ImgLink } from "components/ImgEditor"; -import type { Fund } from "types/aws"; -import type { TargetType } from "../common"; - -export interface FV extends Pick { - targetType: TargetType; - fixedTarget: string; - banner: ImgLink; - logo: ImgLink; -} diff --git a/src/pages/Funds/EditFund/useRhf.ts b/src/pages/Funds/EditFund/useRhf.ts index e68887e2b6..37d8063422 100644 --- a/src/pages/Funds/EditFund/useRhf.ts +++ b/src/pages/Funds/EditFund/useRhf.ts @@ -1,46 +1,9 @@ -import { yupResolver } from "@hookform/resolvers/yup"; -import type { ImgLink } from "components/ImgEditor"; +import type { SingleFund } from "@better-giving/fundraiser"; +import { valibotResolver } from "@hookform/resolvers/valibot"; import { useController, useForm } from "react-hook-form"; -import { genFileSchema } from "schemas/file"; -import { schema as schemaFn, stringNumber } from "schemas/shape"; -import type { Fund } from "types/aws"; -import { string } from "yup"; -import { - MAX_SIZE_IN_BYTES, - type TargetType, - VALID_MIME_TYPES, -} from "../common"; -import type { FV } from "./types"; +import { type FV, schema } from "./schema"; -const fileObj = schemaFn({ - file: genFileSchema(MAX_SIZE_IN_BYTES, VALID_MIME_TYPES), -}); - -const targetTypeKey: keyof FV = "targetType"; -const schema = schemaFn({ - name: string().required("required"), - description: string().required("required"), - fixedTarget: stringNumber( - (str) => { - return str.when(targetTypeKey, (values, schema) => { - const [type] = values as [TargetType]; - return type === "fixed" ? schema.required("required") : schema; - }); - }, - (n) => { - return n.when(targetTypeKey, (values, schema) => { - const [type] = values as [TargetType]; - return type === "fixed" - ? schema.positive("must be greater than 0") - : schema; - }); - } - ), - logo: fileObj, - banner: fileObj, -}); - -export function useRhf(init: Fund) { +export function useRhf(init: SingleFund) { const { register, handleSubmit, @@ -49,17 +12,16 @@ export function useRhf(init: Fund) { resetField, formState: { isSubmitting, errors, isDirty, dirtyFields }, } = useForm({ - resolver: yupResolver(schema), + resolver: valibotResolver(schema), values: { name: init.name, description: init.description, - targetType: + target: init.target === "0" - ? "none" + ? { type: "none" } : init.target === "smart" - ? "smart" - : "fixed", - fixedTarget: init.target === "smart" ? "" : init.target, + ? { type: "smart" } + : { type: "fixed", value: init.target }, logo: { name: "", preview: init.logo, publicUrl: init.logo }, banner: { name: "", preview: init.banner, publicUrl: init.banner }, }, @@ -67,7 +29,7 @@ export function useRhf(init: Fund) { const { field: targetType } = useController({ control, - name: "targetType", + name: "target.type", }); const { field: logo } = useController({ control, name: "logo" }); diff --git a/src/pages/Funds/Fund/FundContext.ts b/src/pages/Funds/Fund/FundContext.ts index 89a2135242..8d0d74ee09 100644 --- a/src/pages/Funds/Fund/FundContext.ts +++ b/src/pages/Funds/Fund/FundContext.ts @@ -1,10 +1,10 @@ +import type { SingleFund } from "@better-giving/fundraiser"; import { isEmpty } from "helpers"; import { createContext, useContext } from "react"; -import type { Fund } from "types/aws"; -export const FundContext = createContext({} as Fund); +export const FundContext = createContext({} as SingleFund); -export const useFundContext = (): Fund => { +export const useFundContext = (): SingleFund => { const val = useContext(FundContext); if (isEmpty(Object.entries(val))) { diff --git a/src/services/aws/aws.ts b/src/services/aws/aws.ts index 4c49cf2b85..e880001b26 100644 --- a/src/services/aws/aws.ts +++ b/src/services/aws/aws.ts @@ -1,3 +1,4 @@ +import type { FundItem } from "@better-giving/fundraiser"; import type { Application, Page, @@ -19,7 +20,6 @@ import type { EndowmentCard, EndowmentOption, EndowmentsQueryParams, - Fund, } from "types/aws"; import { version as v } from "../helpers"; import type { EndowmentUpdate, IdOrSlug } from "../types"; @@ -158,7 +158,7 @@ export const aws = createApi({ } }, }), - fundsEndowMemberOf: builder.query({ + fundsEndowMemberOf: builder.query({ providesTags: ["endowment"], query: ({ endowId }) => { return { diff --git a/src/services/aws/funds.ts b/src/services/aws/funds.ts index b32463b0d5..63733b7397 100644 --- a/src/services/aws/funds.ts +++ b/src/services/aws/funds.ts @@ -1,11 +1,16 @@ +import type { FundsPage, SingleFund } from "@better-giving/fundraiser"; +import type { + FundUpdate, + FundsParams, + NewFund, +} from "@better-giving/fundraiser/schema"; import { TEMP_JWT } from "constants/auth"; -import type { Fund } from "types/aws"; import { version as v } from "../helpers"; import { aws } from "./aws"; export const funds = aws.injectEndpoints({ endpoints: (builder) => ({ - createFund: builder.mutation<{ id: string }, Fund.New>({ + createFund: builder.mutation<{ id: string }, NewFund>({ invalidatesTags: ["funds"], query: (payload) => { return { @@ -16,7 +21,7 @@ export const funds = aws.injectEndpoints({ }; }, }), - editFund: builder.mutation({ + editFund: builder.mutation({ invalidatesTags: ["funds", "fund"], query: ({ id, ...payload }) => { return { @@ -37,7 +42,7 @@ export const funds = aws.injectEndpoints({ }; }, }), - funds: builder.query({ + funds: builder.query({ providesTags: ["funds"], query: (params) => { return { @@ -46,7 +51,7 @@ export const funds = aws.injectEndpoints({ }; }, }), - fund: builder.query({ + fund: builder.query({ providesTags: ["fund"], query: (fundId) => `${v(1)}/funds/${fundId}`, }), diff --git a/src/types/aws/ap/index.ts b/src/types/aws/ap/index.ts index 63ee1ea702..45cf3405f2 100644 --- a/src/types/aws/ap/index.ts +++ b/src/types/aws/ap/index.ts @@ -1,6 +1,5 @@ import type { Except } from "type-fest"; import type { PartialExcept } from "types/utils"; -import type { Environment } from "vitest"; import type { DonateMethodId, UNSDG_NUMS } from "../../lists"; export type Milestone = { @@ -234,83 +233,3 @@ export type UserAttributes = { }; export type UserUpdate = Partial; - -export interface Fund { - /** uuidv4 */ - id: string; - env: Environment; - name: string; - description: string; - banner: string; - logo: string; - members: Pick[]; - featured: boolean; - active: boolean; - settings: { - allowBgTip: boolean; - }; - /** iso */ - expiration?: string; - verified: boolean; - donation_total_usd: number; - /** "0": no target */ - target: "smart" | `${number}`; - /** endowIds that allows this fundraiser on their profile */ - approvers: number[]; -} -export namespace Fund { - export interface New - extends Pick< - Fund, - | "name" - | "description" - | "banner" - | "logo" - | "featured" - | "settings" - | "expiration" - | "target" - > { - /** endowment ids */ - members: number[]; - } - - export interface Update - extends Partial< - Pick< - Fund, - "name" | "description" | "banner" | "logo" | "featured" | "target" - > - > {} - - export interface Card - extends Pick< - Fund, - | "id" - | "name" - | "description" - | "env" - | "logo" - | "featured" - | "active" - | "verified" - | "donation_total_usd" - | "members" - | "target" - | "approvers" - > { - /** iso | "9999-12-31T23:59:59.000Z" year 9999 */ - expiration: string; - } - - export interface CardsPage { - items: Card[]; - page: number; - numPages: number; - } - - export interface CardsQueryParams { - query?: string; - page?: number; - } -} diff --git a/yarn.lock b/yarn.lock index a7b866cb6c..776b292d36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -822,6 +822,18 @@ __metadata: languageName: node linkType: hard +"@better-giving/fundraiser@npm:1.0.0-rc.5": + version: 1.0.0-rc.5 + resolution: "@better-giving/fundraiser@npm:1.0.0-rc.5" + dependencies: + "@better-giving/types": "npm:1.0.0-rc.2" + valibot: "npm:0.42.0" + peerDependencies: + valibot: 0.42.0 + checksum: 10/1c0c631b618790082cd84516fd3e039bd204847876d31f80eb0c47f44f83e6baba304e52cd7c5326412af7f59aa6e65e8a06d8889b3b4eb1287227dc44300ea7 + languageName: node + linkType: hard + "@better-giving/registration@npm:1.0.24": version: 1.0.24 resolution: "@better-giving/registration@npm:1.0.24" @@ -831,6 +843,15 @@ __metadata: languageName: node linkType: hard +"@better-giving/types@npm:1.0.0-rc.2": + version: 1.0.0-rc.2 + resolution: "@better-giving/types@npm:1.0.0-rc.2" + peerDependencies: + valibot: 0.42.0 + checksum: 10/a4b46d4fb5fce7d21982f636ff922f9a335ab754030142ef731944b8a4e682e0b3c19a133dc9d9e05ad8dbb32e513dcb66e59007393ce7ead474ed8559d36174 + languageName: node + linkType: hard + "@biomejs/biome@npm:1.8.1": version: 1.8.1 resolution: "@biomejs/biome@npm:1.8.1" @@ -3426,7 +3447,9 @@ __metadata: resolution: "angelprotocol-web-app@workspace:." dependencies: "@better-giving/assets": "npm:1.0.18" + "@better-giving/fundraiser": "npm:1.0.0-rc.5" "@better-giving/registration": "npm:1.0.24" + "@better-giving/types": "npm:1.0.0-rc.2" "@biomejs/biome": "npm:1.8.1" "@gsap/react": "npm:2.1.1" "@headlessui/react": "npm:2.1.0"