diff --git a/package.json b/package.json index 20a4267886..cddf3d1479 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@better-giving/assets": "1.0.18", + "@better-giving/registration": "1.0.1", "@gsap/react": "2.1.1", "@headlessui/react": "2.1.0", "@hookform/error-message": "2.0.1", @@ -65,6 +66,7 @@ "react-router-dom": "6.23.0", "swiper": "11.1.4", "type-fest": "4.20.0", + "valibot": "0.42.0", "yup": "1.4.0" }, "devDependencies": { diff --git a/src/pages/Registration/Resume/useSubmit.ts b/src/pages/Registration/Resume/useSubmit.ts index fa989a6085..be81a9b515 100644 --- a/src/pages/Registration/Resume/useSubmit.ts +++ b/src/pages/Registration/Resume/useSubmit.ts @@ -25,7 +25,7 @@ export default function useSubmit() { storeRegistrationReference(reference); const { state, nextStep } = getRegistrationState(data); - const init = state.data.init; + const init = state.data; navigate(`../${regRoutes.steps}/${nextStep}`, { state: init }); } catch (err) { diff --git a/src/pages/Registration/Steps/Banking/Banking.tsx b/src/pages/Registration/Steps/Banking/Banking.tsx index f91bb194b0..3084a32a1f 100644 --- a/src/pages/Registration/Steps/Banking/Banking.tsx +++ b/src/pages/Registration/Steps/Banking/Banking.tsx @@ -27,7 +27,7 @@ function Banking() {

diff --git a/src/pages/Registration/Steps/Banking/useSubmit.ts b/src/pages/Registration/Steps/Banking/useSubmit.ts index 418b316b23..0b3e76108b 100644 --- a/src/pages/Registration/Steps/Banking/useSubmit.ts +++ b/src/pages/Registration/Steps/Banking/useSubmit.ts @@ -19,9 +19,9 @@ export default function useSubmit() { }); await updateReg({ - reference: data.init.reference, + id: data.init.id, type: "banking", - BankStatementFile: bankStatementPreview.bankStatementFile[0], + bank_statement: bankStatementPreview.bankStatementFile[0], wise_recipient_id: recipient.id, }).unwrap(); diff --git a/src/pages/Registration/Steps/ContactDetails/Form/index.tsx b/src/pages/Registration/Steps/ContactDetails/Form/index.tsx index 0bb84e8eb4..8a9248ded3 100644 --- a/src/pages/Registration/Steps/ContactDetails/Form/index.tsx +++ b/src/pages/Registration/Steps/ContactDetails/Form/index.tsx @@ -23,30 +23,35 @@ export default function Form({ classes = "" }: { classes?: string }) {

Personal information

- name="FirstName" + name="first_name" label="First name" placeholder="e.g. John" required classes={{ container: "mb-4" }} /> - name="LastName" + name="last_name" label="Last name" placeholder="e.g. Doe" required classes={{ container: "mb-4" }} /> - name="PhoneNumber" + name="contact_number" label="Phone number" placeholder="000000000" required={false} classes={{ container: "mb-4" }} /> - name="Email" label="E-mail address" required disabled /> + + name="registrant_id" + label="E-mail address" + required + disabled + />

Organization information

- name="OrganizationName" + name="org_name" label="Name of your organization" placeholder="Organization name" classes={{ container: "mb-4" }} @@ -55,15 +60,15 @@ export default function Form({ classes = "" }: { classes?: string }) { - - name="Role" + + name="org_role" options={roleOptions} classes={{ options: "text-sm" }} > {({ value }) => value === "other" && ( - name="OtherRole" + name="other_role" label="Specify your role" required classes={{ container: "mt-4" }} @@ -76,8 +81,8 @@ export default function Form({ classes = "" }: { classes?: string }) { - - name="ReferralMethod" + + name="referral_method" options={referralOptions} classes={{ options: "text-sm" }} > @@ -85,7 +90,7 @@ export default function Form({ classes = "" }: { classes?: string }) { <> {value === "other" && ( - name="OtherReferralMethod" + name="other_referral_method" label="Please provide additional information" required classes={{ container: "mt-4" }} @@ -93,7 +98,7 @@ export default function Form({ classes = "" }: { classes?: string }) { )} {value === "referral" && ( - name="ReferralCode" + name="referral_code" label="Referral Code" required classes={{ container: "mt-4" }} @@ -103,7 +108,7 @@ export default function Form({ classes = "" }: { classes?: string }) { )} - name="Goals" + name="goals" label="Goals" placeholder={`What is your goal working with ${APP_NAME}?`} classes={{ container: "mt-4" }} diff --git a/src/pages/Registration/Steps/ContactDetails/Form/useSubmit.ts b/src/pages/Registration/Steps/ContactDetails/Form/useSubmit.ts index 5cebe72f8c..037fe16d30 100644 --- a/src/pages/Registration/Steps/ContactDetails/Form/useSubmit.ts +++ b/src/pages/Registration/Steps/ContactDetails/Form/useSubmit.ts @@ -24,24 +24,14 @@ export default function useSubmit() { return navigate(`../${steps.orgDetails}`, { state: init }); // go to latest step } + const { org_role, referral_method, id, registrant_id, ...rest } = fv; + const result = await updateReg({ - type: "contact-details", - reference: fv.PK, - ContactPerson: { - FirstName: fv.FirstName, - LastName: fv.LastName, - Email: fv.Email, - Goals: fv.Goals, - PhoneNumber: fv.PhoneNumber, - ReferralMethod: fv.ReferralMethod.value, - OtherReferralMethod: fv.OtherReferralMethod, - ReferralCode: fv.ReferralCode, - Role: fv.Role.value, - OtherRole: fv.OtherRole, - }, - Registration: { - OrganizationName: fv.OrganizationName, - }, + type: "contact", + ...rest, + org_role: org_role.value, + referral_method: referral_method.value, + id, }); if ("error" in result) { diff --git a/src/pages/Registration/Steps/ContactDetails/index.tsx b/src/pages/Registration/Steps/ContactDetails/index.tsx index 506bc9a0e7..abc41b0cb8 100644 --- a/src/pages/Registration/Steps/ContactDetails/index.tsx +++ b/src/pages/Registration/Steps/ContactDetails/index.tsx @@ -21,26 +21,27 @@ function ContactDetails() { defaultValues: contact ? { ...contact, - PK: init.reference, - Role: toRoleOption(contact.Role), - ReferralMethod: toReferralOption( - contact.ReferralMethod === "angel-alliance" + id: init.id, + registrant_id: init.registrant_id, + org_role: toRoleOption(contact.org_role), + referral_method: toReferralOption( + contact.referral_method === "better-giving-alliance" ? "better-giving-alliance" - : contact.ReferralMethod + : contact.referral_method ), - OrganizationName: contact.orgName, } : { - PK: init.reference, - Email: init.email, - Role: { value: "", label: roles[""] }, - ReferralMethod: { + id: init.id, + registrant_id: init.registrant_id, + org_role: { value: "", label: roles[""] }, + referral_method: { value: "", label: referralMethods[""], }, - FirstName: firstName, - LastName: lastName, - OrganizationName: init.claim?.name, + first_name: firstName, + last_name: lastName, + org_name: init.claim?.name ?? "", + goals: "", }, }); diff --git a/src/pages/Registration/Steps/ContactDetails/schema.ts b/src/pages/Registration/Steps/ContactDetails/schema.ts index f5ddedad80..d09bb3cca4 100644 --- a/src/pages/Registration/Steps/ContactDetails/schema.ts +++ b/src/pages/Registration/Steps/ContactDetails/schema.ts @@ -7,8 +7,8 @@ import { type ObjectSchema, object, string } from "yup"; import type { FormValues } from "./types"; type Key = keyof FormValues; -const roleKey: Key = "Role"; -const referralMethodKey: Key = "ReferralMethod"; +const roleKey: Key = "org_role"; +const referralMethodKey: Key = "referral_method"; const otherRole = string() .trim() @@ -28,14 +28,14 @@ const otherReferralMethod = (referralMethod: ReferralMethods) => ); export const schema = object>({ - OrganizationName: requiredString.trim(), - FirstName: requiredString.trim(), - LastName: requiredString.trim(), + org_name: requiredString.trim(), + first_name: requiredString.trim(), + last_name: requiredString.trim(), //email: disabled: already validated at signup - Goals: requiredString.trim(), - Role: optionType({ required: true }), - ReferralMethod: optionType({ required: true }), - OtherReferralMethod: otherReferralMethod("other"), - ReferralCode: otherReferralMethod("referral"), - OtherRole: otherRole, + goals: requiredString.trim(), + org_role: optionType({ required: true }), + referral_method: optionType({ required: true }), + other_referral_method: otherReferralMethod("other"), + referral_code: otherReferralMethod("referral"), + other_role: otherRole, }) as ObjectSchema; diff --git a/src/pages/Registration/Steps/ContactDetails/types.ts b/src/pages/Registration/Steps/ContactDetails/types.ts index 94776849cb..50f2612c4d 100644 --- a/src/pages/Registration/Steps/ContactDetails/types.ts +++ b/src/pages/Registration/Steps/ContactDetails/types.ts @@ -1,18 +1,11 @@ import type { OverrideProperties } from "type-fest"; -import type { - ContactDetails, - ContactRoles, - InitContact, - OrgDataForStep1, - ReferralMethods, -} from "types/aws"; +import type { ContactRoles, ReferralMethods, RegV2 } from "types/aws"; import type { OptionType } from "types/components"; export type FormValues = OverrideProperties< - ContactDetails & OrgDataForStep1, + RegV2.Contact & Pick, { - Role: OptionType; - ReferralMethod: OptionType; + org_role: OptionType; + referral_method: OptionType; } -> & - Pick; +>; diff --git a/src/pages/Registration/Steps/Dashboard/EndowmentStatus.tsx b/src/pages/Registration/Steps/Dashboard/EndowmentStatus.tsx index 8c448260b5..81b3ee3e60 100644 --- a/src/pages/Registration/Steps/Dashboard/EndowmentStatus.tsx +++ b/src/pages/Registration/Steps/Dashboard/EndowmentStatus.tsx @@ -3,14 +3,13 @@ import LoadText from "components/LoadText"; import { steps } from "pages/Registration/routes"; import type { MouseEventHandler } from "react"; import { Link } from "react-router-dom"; -import type { RegistrationStatus } from "types/aws"; +import type { RegV2 } from "types/aws"; import { useRegState } from "../StepGuard"; type Props = { isSubmitting: boolean; - status: Exclude; + status: Exclude; onSubmit: MouseEventHandler; - endowId?: number; classes?: string; }; @@ -23,7 +22,7 @@ export default function EndowmentStatus({ const { data } = useRegState<3>(); switch (status) { - case "Rejected": + case "rejected": return (

@@ -47,7 +46,7 @@ export default function EndowmentStatus({

); - case "Under Review": + case "in-review": return (
{ + const submit = async ({ init }: CompleteReg) => { try { - await submitApplication(init.reference).unwrap(); + await submitApplication(init.id).unwrap(); if (window.hasOwnProperty("lintrk")) { (window as any).lintrk("track", { conversion_id: 12807754 }); } @@ -37,10 +37,10 @@ function Dashboard() { } }; - const { status } = data; - const isStepDisabled = isSubmitting || status === "Under Review"; + const { submission } = data; + const isStepDisabled = isSubmitting || submission === "in-review"; - if (status === "Active") { + if (typeof submission === "object") { return ; } @@ -61,8 +61,7 @@ function Dashboard() { submit(data)} - status={status} - endowId={data.endowId} + status={submission} classes="mt-6" />
diff --git a/src/pages/Registration/Steps/Documentation/Documentation.tsx b/src/pages/Registration/Steps/Documentation/Documentation.tsx index a50bae48b6..df507722a6 100644 --- a/src/pages/Registration/Steps/Documentation/Documentation.tsx +++ b/src/pages/Registration/Steps/Documentation/Documentation.tsx @@ -1,3 +1,4 @@ +import { isIrs501c3 } from "types/aws"; import { useRegState, withStepGuard } from "../StepGuard"; import FSADocumentation from "./FSA"; import NonFSA from "./NonFSA"; @@ -6,16 +7,16 @@ function Documentation() { const { data } = useRegState<4>(); //documentation is previously completed - if (data.documentation && data.documentation.DocType === "FSA") { - return ; + if (data.docs && !isIrs501c3(data.docs)) { + return ; } - if (data.documentation && data.documentation.DocType === "Non-FSA") { - return ; + if (data.docs && isIrs501c3(data.docs)) { + return ; } //if not previously completed, depend on fsaInquiry - if (data.fsaInquiry.AuthorizedToReceiveTaxDeductibleDonations) { + if (data.irs501c3) { return ; } diff --git a/src/pages/Registration/Steps/Documentation/FSA/Form/index.tsx b/src/pages/Registration/Steps/Documentation/FSA/Form/index.tsx index bf4557f5aa..cc8c024bab 100644 --- a/src/pages/Registration/Steps/Documentation/FSA/Form/index.tsx +++ b/src/pages/Registration/Steps/Documentation/FSA/Form/index.tsx @@ -20,13 +20,13 @@ export default function Form(props: Props) { - - name="ProofOfIdentity" + + name="proof_of_identity" specs={{ mbLimit: MB_LIMIT, mimeTypes: VALID_MIME_TYPES }} /> - name="RegistrationNumber" + name="registration_number" label="Registration number (numbers and letters only)" required classes={{ container: "mb-6 mt-10", label: "font-semibold" }} @@ -36,13 +36,13 @@ export default function Form(props: Props) { - - name="ProofOfRegistration" + + name="proof_of_reg" specs={{ mbLimit: MB_LIMIT, mimeTypes: VALID_MIME_TYPES }} /> - name="LegalEntityType" + name="legal_entity_type" label="What type of legal entity is your organization registered as? This can usually be found in your registration/organizing document" required @@ -52,16 +52,16 @@ export default function Form(props: Props) { type="textarea" - name="ProjectDescription" + name="project_description" label="Please provide a description of your organization's charitable activities as well as your charitable mission." required classes={{ container: "mb-6 mt-10" }} placeholder="" /> - {props.doc?.SignedFiscalSponsorshipAgreement ? ( + {props.doc?.fsa_signed_doc_url ? ( @@ -87,7 +87,7 @@ export default function Form(props: Props) { isLoading={isSubmitting || isRedirecting} text={isSubmitting ? "Submitting.." : "Redirecting.."} > - {props.doc?.SignedFiscalSponsorshipAgreement ? "Continue" : "Sign"} + {props.doc?.fsa_signed_doc_url ? "Continue" : "Sign"} diff --git a/src/pages/Registration/Steps/Documentation/FSA/Form/useSubmit.ts b/src/pages/Registration/Steps/Documentation/FSA/Form/useSubmit.ts index ff9eb8d1d1..32789f88e1 100644 --- a/src/pages/Registration/Steps/Documentation/FSA/Form/useSubmit.ts +++ b/src/pages/Registration/Steps/Documentation/FSA/Form/useSubmit.ts @@ -10,7 +10,7 @@ import type { FormValues, Props } from "../types"; export default function useSubmit({ doc }: Props) { const { - data: { contact, init, orgDetails }, + data: { contact, init, org }, } = useRegState<4>(); const { handleSubmit, @@ -29,35 +29,38 @@ export default function useSubmit({ doc }: Props) { const submit: SubmitHandler = async (fv) => { try { //signed agreement and user didn't change any documents - if (!isDirty && doc?.SignedFiscalSponsorshipAgreement) { + if (!isDirty && doc?.fsa_signed_doc_url) { return navigate(`../${steps.banking}`, { state: init }); } //existing url and user doesn't change any documents - if (!isDirty && doc?.FiscalSponsorshipAgreementSigningURL) { + if (!isDirty && doc?.fsa_signing_url) { setRedirecting(true); - window.location.href = doc.FiscalSponsorshipAgreementSigningURL; + window.location.href = doc.fsa_signing_url; } const previews = await getFilePreviews({ - POI: fv.ProofOfIdentity, - POR: fv.ProofOfRegistration, + POI: fv.proof_of_identity, + POR: fv.proof_of_reg, }); const { url } = await generateSigningURL({ - id: init.reference, - email: init.email, - firstName: contact.FirstName, - lastName: contact.LastName, - role: contact.Role === "other" ? contact.OtherRole : contact.Role, + id: init.id, + email: init.registrant_id, + first_name: contact.first_name, + last_name: contact.last_name, + role: + contact.org_role === "other" + ? contact.other_role ?? "" + : contact.org_role, docs: { - OrgName: contact.orgName, - HqCountry: orgDetails.HqCountry, - RegistrationNumber: fv.RegistrationNumber, - ProofOfIdentity: previews.POI[0], - ProofOfRegistration: previews.POR[0], - LegalEntityType: fv.LegalEntityType, - ProjectDescription: fv.ProjectDescription, + org_name: contact.org_name, + hq_country: org.hq_country, + registration_number: fv.registration_number, + proof_of_identity: previews.POI[0], + proof_of_reg: previews.POR[0], + legal_entity_type: fv.legal_entity_type, + project_description: fv.project_description, }, }).unwrap(); setRedirecting(true); diff --git a/src/pages/Registration/Steps/Documentation/FSA/index.tsx b/src/pages/Registration/Steps/Documentation/FSA/index.tsx index 192887a1f3..4c1d040601 100644 --- a/src/pages/Registration/Steps/Documentation/FSA/index.tsx +++ b/src/pages/Registration/Steps/Documentation/FSA/index.tsx @@ -1,9 +1,6 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { FormProvider, useForm } from "react-hook-form"; -import type { - FileObject, - FSADocumentation as TFSADocumentation, -} from "types/aws"; +import type { FileObject, RegV2 } from "types/aws"; import type { FileDropzoneAsset } from "types/components"; import Form from "./Form"; import { schema } from "./schema"; @@ -16,11 +13,11 @@ export default function FSADocumentation(props: Props) { defaultValues: doc ? formFormat(doc) : { - ProofOfIdentity: asset([]), - RegistrationNumber: "", - ProofOfRegistration: asset([]), - LegalEntityType: "", - ProjectDescription: "", + proof_of_identity: asset([]), + registration_number: "", + proof_of_reg: asset([]), + legal_entity_type: "", + project_description: "", }, }); @@ -31,14 +28,15 @@ export default function FSADocumentation(props: Props) { ); } -function formFormat(doc: TFSADocumentation): FormValues { +function formFormat(doc: RegV2.FsaDocs): FormValues { return { //level 1 - ProofOfIdentity: asset([doc.ProofOfRegistration]), - RegistrationNumber: doc.RegistrationNumber, - ProofOfRegistration: asset([doc.ProofOfRegistration]), - LegalEntityType: doc.LegalEntityType, - ProjectDescription: doc.ProjectDescription, + outdated: doc.outdated, + proof_of_identity: asset([doc.proof_of_identity]), + registration_number: doc.registration_number, + proof_of_reg: asset([doc.proof_of_reg]), + legal_entity_type: doc.legal_entity_type, + project_description: doc.project_description, }; } diff --git a/src/pages/Registration/Steps/Documentation/FSA/schema.ts b/src/pages/Registration/Steps/Documentation/FSA/schema.ts index 7232dc5f04..d55aa6c7f5 100644 --- a/src/pages/Registration/Steps/Documentation/FSA/schema.ts +++ b/src/pages/Registration/Steps/Documentation/FSA/schema.ts @@ -22,14 +22,14 @@ const assetShape = fileDropzoneAssetShape( ); export const schema = object>({ - RegistrationNumber: requiredString.matches( + registration_number: requiredString.matches( alphanumeric, "must only contain numbers and letters" ), - ProofOfIdentity: assetShape, - ProofOfRegistration: assetShape, - LegalEntityType: requiredString.trim(), - ProjectDescription: requiredString + proof_of_identity: assetShape, + proof_of_reg: assetShape, + legal_entity_type: requiredString.trim(), + project_description: requiredString .trim() .max(4000, "maximum 4000 characters allowed"), }) as ObjectSchema; diff --git a/src/pages/Registration/Steps/Documentation/FSA/types.ts b/src/pages/Registration/Steps/Documentation/FSA/types.ts index a9ef2c66f5..fd610dc682 100644 --- a/src/pages/Registration/Steps/Documentation/FSA/types.ts +++ b/src/pages/Registration/Steps/Documentation/FSA/types.ts @@ -1,20 +1,15 @@ import type { Except, OverrideProperties } from "type-fest"; -import type { FSADocumentation } from "types/aws"; +import type { RegV2 } from "types/aws"; import type { FileDropzoneAsset } from "types/components"; export type FormValues = OverrideProperties< - Except< - FSADocumentation, - | "DocType" - | "FiscalSponsorshipAgreementSigningURL" - | "SignedFiscalSponsorshipAgreement" - >, + Except, { - ProofOfIdentity: FileDropzoneAsset; - ProofOfRegistration: FileDropzoneAsset; + proof_of_identity: FileDropzoneAsset; + proof_of_reg: FileDropzoneAsset; } >; export type Props = { - doc: FSADocumentation | undefined; + doc: RegV2.FsaDocs | undefined; }; diff --git a/src/pages/Registration/Steps/Documentation/NonFSA/NonFSA.tsx b/src/pages/Registration/Steps/Documentation/NonFSA/NonFSA.tsx index 6adda1b94c..d67e558ded 100644 --- a/src/pages/Registration/Steps/Documentation/NonFSA/NonFSA.tsx +++ b/src/pages/Registration/Steps/Documentation/NonFSA/NonFSA.tsx @@ -17,7 +17,7 @@ export default function NonFSA(props: Props) { const methods = useForm({ resolver: yupResolver( object({ - EIN: requiredString.matches( + ein: requiredString.matches( alphanumeric, "must only contain numbers and letters" ), @@ -26,7 +26,7 @@ export default function NonFSA(props: Props) { defaultValues: doc ? doc : { - EIN: claimEin ?? "", + ein: claimEin ?? "", }, }); const { submit, isSubmitting } = useSubmit({ props, form: methods }); @@ -37,7 +37,7 @@ export default function NonFSA(props: Props) { /** claimer should not change EIN */ disabled={!!claimEin} - name="EIN" + name="ein" label="EIN# (numbers and letters only)" required classes={{ container: "mb-6 mt-1" }} diff --git a/src/pages/Registration/Steps/Documentation/NonFSA/types.ts b/src/pages/Registration/Steps/Documentation/NonFSA/types.ts index e30313ae17..e265c801e5 100644 --- a/src/pages/Registration/Steps/Documentation/NonFSA/types.ts +++ b/src/pages/Registration/Steps/Documentation/NonFSA/types.ts @@ -1,8 +1,8 @@ import type { Except } from "type-fest"; -import type { NonFSADocumentation } from "types/aws"; +import type { RegV2 } from "types/aws"; -export type FormValues = Except; +export type FormValues = Except; export type Props = { - doc: NonFSADocumentation | undefined; + doc: RegV2.TaxDeductibleDocs | undefined; }; diff --git a/src/pages/Registration/Steps/Documentation/NonFSA/useSubmit.ts b/src/pages/Registration/Steps/Documentation/NonFSA/useSubmit.ts index 5ea4178504..c8a9637aae 100644 --- a/src/pages/Registration/Steps/Documentation/NonFSA/useSubmit.ts +++ b/src/pages/Registration/Steps/Documentation/NonFSA/useSubmit.ts @@ -29,27 +29,26 @@ export default function useSubmit({ form, props }: Args) { return navigate(`../${steps.banking}`, { state: data.init }); } - if (!data.init.claim && fv.EIN !== props.doc?.EIN) { - const res = await endowByEin(fv.EIN); + if (!data.init.claim && fv.ein !== props.doc?.ein) { + const res = await endowByEin(fv.ein); if ("data" in res && res.data) { const endow = res.data; if (endow.claimed ?? true) { return handleError( - `Nonprofit: ${endow.name} with EIN: ${fv.EIN} already exists on our app. You must speak with an existing user of your NPO Account's members in order to be invited on as a member.` + `Nonprofit: ${endow.name} with EIN: ${fv.ein} already exists on our app. You must speak with an existing user of your NPO Account's members in order to be invited on as a member.` ); } - const convertToClaimNotif = `Nonprofit: ${endow.name} with EIN: ${fv.EIN} already exists on our app. Would you like to claim this organization instead?`; + const convertToClaimNotif = `Nonprofit: ${endow.name} with EIN: ${fv.ein} already exists on our app. Would you like to claim this organization instead?`; if (!window.confirm(convertToClaimNotif)) return; } } const result = await updateReg({ - reference: data.init.reference, - type: "documentation", - DocType: "Non-FSA", - EIN: fv.EIN, + id: data.init.id, + type: "docs", + ein: fv.ein, }); if ("error" in result) { diff --git a/src/pages/Registration/Steps/FSAInquiry/FSAInquiry.tsx b/src/pages/Registration/Steps/FSAInquiry/FSAInquiry.tsx index 31e4cb1994..032eb10e49 100644 --- a/src/pages/Registration/Steps/FSAInquiry/FSAInquiry.tsx +++ b/src/pages/Registration/Steps/FSAInquiry/FSAInquiry.tsx @@ -11,21 +11,19 @@ import useSubmit from "./useSubmit"; const countryWhiteList = ["United States"]; //will add more in the future; function FSAInquiry() { const { data } = useRegState<3>(); - const possiblyTaxExempt = countryWhiteList.includes( - data.orgDetails.HqCountry - ); + const possiblyTaxExempt = countryWhiteList.includes(data.org.hq_country); const methods = useForm({ defaultValues: { - AuthorizedToReceiveTaxDeductibleDonations: - data.fsaInquiry?.AuthorizedToReceiveTaxDeductibleDonations || + irs501c3: + data.irs501c3 || /** US-based unclaimed endowments are authorized by default */ data.init.claim - ? "Yes" - : "No", + ? "yes" + : "no", }, }); const { watch } = methods; - const answer = watch("AuthorizedToReceiveTaxDeductibleDonations"); + const answer = watch("irs501c3"); const { submit, isSubmitting } = useSubmit(data, methods); const optionsDisabled = !possiblyTaxExempt || !!data.init.claim; @@ -40,14 +38,14 @@ function FSAInquiry() { a nonprofit organization exempt under IRC 501(c)(3)?{" "}
- - name="AuthorizedToReceiveTaxDeductibleDonations" - value="Yes" + + name="irs501c3" + value="yes" disabled={optionsDisabled} /> - - name="AuthorizedToReceiveTaxDeductibleDonations" - value="No" + + name="irs501c3" + value="no" disabled={optionsDisabled} />
@@ -55,12 +53,12 @@ function FSAInquiry() { ) : (

Great news: Nonprofit Organizations in{" "} - {data.orgDetails.HqCountry}{" "} - can now take advantage of {APP_NAME}’s Fiscal Sponsorship service. + {data.org.hq_country} can now + take advantage of {APP_NAME}’s Fiscal Sponsorship service.

)} - {!possiblyTaxExempt || answer === "No" ? ( + {!possiblyTaxExempt || answer === "no" ? (

{APP_NAME} provides fiscal sponsorship services at market-leading cost (2.9%) for our partner organizations worldwide to enable them diff --git a/src/pages/Registration/Steps/FSAInquiry/types.ts b/src/pages/Registration/Steps/FSAInquiry/types.ts index 762c8fa435..98eef65a2f 100644 --- a/src/pages/Registration/Steps/FSAInquiry/types.ts +++ b/src/pages/Registration/Steps/FSAInquiry/types.ts @@ -1,7 +1,4 @@ import type { OverrideProperties } from "type-fest"; -import type { FSAInquiry } from "types/aws"; +import type { RegV2 } from "types/aws"; -export type FV = OverrideProperties< - FSAInquiry, - { AuthorizedToReceiveTaxDeductibleDonations: "Yes" | "No" } ->; +export type FV = OverrideProperties; diff --git a/src/pages/Registration/Steps/FSAInquiry/useSubmit.ts b/src/pages/Registration/Steps/FSAInquiry/useSubmit.ts index 5ea305c27c..b4a86afd24 100644 --- a/src/pages/Registration/Steps/FSAInquiry/useSubmit.ts +++ b/src/pages/Registration/Steps/FSAInquiry/useSubmit.ts @@ -17,14 +17,13 @@ export default function useSubmit(data: Step3Data, form: UseFormReturn) { const navigate = useNavigate(); const submit: SubmitHandler = async (fv) => { - if (!isDirty && data.fsaInquiry !== undefined) { + if (!isDirty && data.irs501c3 !== undefined) { return navigate(`../${steps.docs}`, { state: data.init }); } const result = await updateReg({ - reference: data.init.reference, - type: "fsa-inquiry", - AuthorizedToReceiveTaxDeductibleDonations: - fv.AuthorizedToReceiveTaxDeductibleDonations === "Yes", + id: data.init.id, + type: "fsa-inq", + irs501c3: fv.irs501c3 === "yes", }); if ("error" in result) { diff --git a/src/pages/Registration/Steps/OrgDetails/Form/index.tsx b/src/pages/Registration/Steps/OrgDetails/Form/index.tsx index 3acd790f19..fedc91e41e 100644 --- a/src/pages/Registration/Steps/OrgDetails/Form/index.tsx +++ b/src/pages/Registration/Steps/OrgDetails/Form/index.tsx @@ -33,7 +33,7 @@ export default function Form() { - name="Website" + name="website" label="Website of your organization" required classes={{ container: "mb-6 mt-4" }} @@ -44,16 +44,16 @@ export default function Form() { Select the Sustainable Development Goals your organization is the most aligned with - - name="UN_SDG" + + name="un_sdg" options={sdgOptions} classes={{ options: "text-sm" }} /> - - name="EndowDesignation" + + name="designation" classes={{ options: "text-sm" }} options={endowDesignations.map((designation) => ({ label: designation, @@ -63,10 +63,10 @@ export default function Form() { - + //endowment claims are US-based and shoudn't be changed by claimer disabled={!!data.init.claim} - fieldName="HqCountry" + fieldName="hq_country" placeholder="Select a country" classes={{ container: "px-4", @@ -79,8 +79,8 @@ export default function Form() { - - name="ActiveInCountries" + + name="active_in_countries" classes={{ options: "text-sm" }} /> @@ -91,11 +91,11 @@ export default function Form() {

name="isAnonymousDonationsAllowed" - value="Yes" + value="yes" /> name="isAnonymousDonationsAllowed" - value="No" + value="yes" />
diff --git a/src/pages/Registration/Steps/OrgDetails/Form/useSubmit.ts b/src/pages/Registration/Steps/OrgDetails/Form/useSubmit.ts index 103bfd5d8b..5a621a7686 100644 --- a/src/pages/Registration/Steps/OrgDetails/Form/useSubmit.ts +++ b/src/pages/Registration/Steps/OrgDetails/Form/useSubmit.ts @@ -13,7 +13,7 @@ export default function useSubmit() { } = useFormContext(); const { - data: { init, orgDetails }, + data: { init, org }, } = useRegState<2>(); const [updateReg] = useUpdateRegMutation(); @@ -21,22 +21,22 @@ export default function useSubmit() { const navigate = useNavigate(); const submit: SubmitHandler = async (fv) => { - if (!isDirty && orgDetails) { + if (!isDirty && org) { return navigate(`../${steps.fsaInquiry}`, { state: init }); } const result = await updateReg({ - type: "org-details", - reference: init.reference, + type: "org", + id: init.id, //payload - Website: fv.Website, - UN_SDG: fv.UN_SDG.map( + website: fv.website, + un_sdg: fv.un_sdg.map( (sdg) => sdg.value ) /**TODO: AWS update to accept number[] */, - KycDonorsOnly: fv.isAnonymousDonationsAllowed === "No", - HqCountry: fv.HqCountry.name, - EndowDesignation: fv.EndowDesignation.value, - ActiveInCountries: fv.ActiveInCountries.map((opt) => opt.value), + kyc_donors_only: fv.isAnonymousDonationsAllowed === "no", + hq_country: fv.hq_country.name, + designation: fv.designation.value, + active_in_countries: fv.active_in_countries.map((opt) => opt.value), }); if ("error" in result) { diff --git a/src/pages/Registration/Steps/OrgDetails/index.tsx b/src/pages/Registration/Steps/OrgDetails/index.tsx index 0f7be5dbe4..f14752f6d6 100644 --- a/src/pages/Registration/Steps/OrgDetails/index.tsx +++ b/src/pages/Registration/Steps/OrgDetails/index.tsx @@ -2,7 +2,7 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { country } from "components/CountrySelector"; import { unsdgs } from "constants/unsdgs"; import { FormProvider, useForm } from "react-hook-form"; -import type { OrgDetails as TOrgDetails } from "types/aws"; +import type { RegV2 } from "types/aws"; import { useRegState, withStepGuard } from "../StepGuard"; import Form from "./Form"; import { schema } from "./schema"; @@ -10,7 +10,7 @@ import type { FormValues } from "./types"; function OrgDetails() { const { - data: { orgDetails: org, init }, + data: { org, init }, } = useRegState<2>(); const methods = useForm({ @@ -18,15 +18,15 @@ function OrgDetails() { defaultValues: org ? formFomat(org) : { - Website: "", - UN_SDG: [], - HqCountry: init.claim + website: "", + un_sdg: [], + hq_country: init.claim ? /** all unclaimed endowments are US-based */ { name: "United States", flag: "🇺🇸", code: "US" } : { name: "", flag: "", code: "" }, - EndowDesignation: { value: "", label: "" }, - ActiveInCountries: [], - isAnonymousDonationsAllowed: "Yes", + designation: { value: "", label: "" }, + active_in_countries: [], + isAnonymousDonationsAllowed: "yes", }, }); @@ -39,26 +39,26 @@ function OrgDetails() { export default withStepGuard(OrgDetails); -function formFomat(org: TOrgDetails): FormValues { +function formFomat(org: RegV2.Org): FormValues { return { //level 1 - Website: org.Website, - UN_SDG: org.UN_SDG.map((sdg) => ({ + website: org.website, + un_sdg: org.un_sdg.map((sdg) => ({ value: sdg, label: `${sdg} - ${unsdgs[sdg].title}`, })), - HqCountry: country(org.HqCountry), - EndowDesignation: { - value: org.EndowDesignation, - label: org.EndowDesignation, + hq_country: country(org.hq_country), + designation: { + value: org.designation, + label: org.designation, }, //general - ActiveInCountries: org.ActiveInCountries.map((c) => ({ + active_in_countries: (org.active_in_countries || []).map((c) => ({ label: c, value: c, })), //meta - isAnonymousDonationsAllowed: org.KycDonorsOnly ? "No" : "Yes", + isAnonymousDonationsAllowed: org.kyc_donors_only ? "no" : "yes", }; } diff --git a/src/pages/Registration/Steps/OrgDetails/schema.ts b/src/pages/Registration/Steps/OrgDetails/schema.ts index 45ea9770ed..3a8787926d 100644 --- a/src/pages/Registration/Steps/OrgDetails/schema.ts +++ b/src/pages/Registration/Steps/OrgDetails/schema.ts @@ -7,12 +7,12 @@ import { type ObjectSchema, array, object } from "yup"; import type { FormValues } from "./types"; export const schema = object>({ - Website: url.required("required"), - UN_SDG: array() + website: url.required("required"), + un_sdg: array() .min(1, "required") .max(MAX_SDGS, `maximum ${MAX_SDGS} selections allowed`), - HqCountry: object>({ + hq_country: object>({ name: requiredString.trim(), }), - EndowDesignation: optionType({ required: true }), + designation: optionType({ required: true }), }) as ObjectSchema; diff --git a/src/pages/Registration/Steps/OrgDetails/types.ts b/src/pages/Registration/Steps/OrgDetails/types.ts index 1b5595d30b..212072c18e 100644 --- a/src/pages/Registration/Steps/OrgDetails/types.ts +++ b/src/pages/Registration/Steps/OrgDetails/types.ts @@ -1,14 +1,14 @@ import type { Except, OverrideProperties } from "type-fest"; -import type { EndowDesignation, OrgDetails } from "types/aws"; +import type { EndowDesignation, RegV2 } from "types/aws"; import type { Country, OptionType } from "types/components"; import type { UNSDG_NUMS } from "types/lists"; export type FormValues = OverrideProperties< - Except, + Except, { - HqCountry: Country; - EndowDesignation: OptionType; - ActiveInCountries: OptionType[]; - UN_SDG: OptionType[]; + hq_country: Country; + designation: OptionType; + active_in_countries: OptionType[]; + un_sdg: OptionType[]; } -> & { isAnonymousDonationsAllowed: "Yes" | "No" }; +> & { isAnonymousDonationsAllowed: "yes" | "no" }; diff --git a/src/pages/Registration/Steps/StepGuard.tsx b/src/pages/Registration/Steps/StepGuard.tsx index f029340387..783284ccea 100644 --- a/src/pages/Registration/Steps/StepGuard.tsx +++ b/src/pages/Registration/Steps/StepGuard.tsx @@ -1,10 +1,11 @@ import { isEmpty } from "helpers"; import { type FC, createContext, useContext, useEffect } from "react"; import { useNavigate, useOutletContext } from "react-router-dom"; -import type { InitReg, RegStep, RegistrationState } from "../types"; +import type { RegV2 } from "types/aws"; +import type { RegStep, RegistrationState } from "../types"; export type StepGuardProps = { - init: InitReg; + init: RegV2.Init; state: RegistrationState; }; diff --git a/src/pages/Registration/Steps/getRegistrationState.ts b/src/pages/Registration/Steps/getRegistrationState.ts index 82d35b1c39..c6a7d70f36 100644 --- a/src/pages/Registration/Steps/getRegistrationState.ts +++ b/src/pages/Registration/Steps/getRegistrationState.ts @@ -6,28 +6,43 @@ export function getRegistrationState(reg: RegV2.Record): { state: RegistrationState; nextStep: steps; } { - if (isDone.step6(reg)) { - return { state: { step: 6, data: reg }, nextStep: steps.summary }; + if (isDone.submission(reg)) { + return { state: { step: 6, data: toData(reg) }, nextStep: steps.summary }; } - if (isDone.step5(reg)) { - return { state: { step: 5, data: reg }, nextStep: steps.summary }; + if (isDone.banking(reg)) { + return { state: { step: 5, data: toData(reg) }, nextStep: steps.summary }; } - if (isDone.step4(reg)) { - return { state: { step: 4, data: reg }, nextStep: steps.banking }; + if (isDone.docs(reg)) { + return { state: { step: 4, data: toData(reg) }, nextStep: steps.banking }; } - if (isDone.step3(reg)) { - return { state: { step: 3, data: reg }, nextStep: steps.docs }; + if (isDone.fsaInq(reg)) { + return { state: { step: 3, data: toData(reg) }, nextStep: steps.docs }; } - if (isDone.step2(reg)) { - return { state: { step: 2, data: reg }, nextStep: steps.fsaInquiry }; + if (isDone.contact(reg)) { + return { + state: { step: 2, data: toData(reg) }, + nextStep: steps.fsaInquiry, + }; } + return { state: { step: 1, data: toData(reg) }, nextStep: steps.contact }; +} + +function toData({ + id, + registrant_id, + hubspot_contact_id, + created_at, + env, + claim, + ...rest +}: T): { init: RegV2.Init } & Omit { return { - state: { step: 1, data: reg satisfies RegV2.Step1 }, - nextStep: steps.contact, + init: { id, registrant_id, hubspot_contact_id, created_at, env, claim }, + ...rest, }; } diff --git a/src/pages/Registration/Steps/index.tsx b/src/pages/Registration/Steps/index.tsx index 1ae9a4ec20..9d9fa044ea 100644 --- a/src/pages/Registration/Steps/index.tsx +++ b/src/pages/Registration/Steps/index.tsx @@ -8,8 +8,9 @@ import { useLocation, } from "react-router-dom"; import { useRegQuery } from "services/aws/registration"; +import { type RegV2, isIrs501c3 } from "types/aws"; import { steps } from "../routes"; -import type { InitReg, RegStep4, RegistrationState } from "../types"; +import type { RegStep4, RegistrationState } from "../types"; import Banking from "./Banking"; import ContactDetails from "./ContactDetails"; import Dashboard from "./Dashboard"; @@ -23,9 +24,9 @@ import { getRegistrationState } from "./getRegistrationState"; function Layout() { const { state } = useLocation(); - const initReg = state as InitReg | undefined; + const initReg = state as RegV2.Init | undefined; - const ref = initReg?.reference || ""; + const ref = initReg?.id || ""; const { data, isLoading, isError } = useRegQuery(ref, { skip: !ref, }); @@ -88,7 +89,7 @@ function Layout() {
- + ); } @@ -102,11 +103,7 @@ function getClaim(reg: RegistrationState) { * i.e. inputs org's EIN that happens to be already in our marketplace */ const { data } = reg as RegStep4; - const doc = data.documentation; - - if (doc?.DocType === "FSA") return; - - return doc?.Claim || reg.data.init.claim; + if (!data.docs || isIrs501c3(data.docs)) return reg.data.init.claim; } export const route: RouteObject = { diff --git a/src/pages/Registration/Success.tsx b/src/pages/Registration/Success.tsx index 82fdb0f548..1493fbd036 100644 --- a/src/pages/Registration/Success.tsx +++ b/src/pages/Registration/Success.tsx @@ -2,13 +2,13 @@ import Icon from "components/Icon"; import { adminRoutes, appRoutes } from "constants/routes"; import { Navigate, useLocation } from "react-router-dom"; import { Link } from "react-router-dom"; -import type { CompleteRegistration } from "./types"; +import type { CompleteReg } from "./types"; export function Component({ classes = "" }: { classes?: string }) { const { state } = useLocation(); - const reg = state as CompleteRegistration | undefined; + const reg = state as CompleteReg | undefined; - if (!reg || !reg.endowId) { + if (!reg || typeof reg.submission !== "object") { return ; } @@ -20,13 +20,13 @@ export function Component({ classes = "" }: { classes?: string }) { >

- {contact.orgName}’s account has been created! + {contact.org_name}’s account has been created!

- Start filling out {contact.orgName}’s profile and attract donors! Thank + Start filling out {contact.org_name}’s profile and attract donors! Thank you! diff --git a/src/pages/Registration/Welcome.tsx b/src/pages/Registration/Welcome.tsx index 65755f857e..ccb4868524 100644 --- a/src/pages/Registration/Welcome.tsx +++ b/src/pages/Registration/Welcome.tsx @@ -8,9 +8,8 @@ import { storeRegistrationReference } from "helpers"; import { useEffect } from "react"; import { Link, useLocation } from "react-router-dom"; import { useNewApplicationQuery } from "services/aws/registration"; -import type { EndowClaim } from "types/aws"; +import type { EndowClaim, RegV2 } from "types/aws"; import { steps } from "./routes"; -import type { InitReg } from "./types"; export function Component() { const { email } = useAuthenticatedUser(); @@ -44,9 +43,9 @@ export function Component() { state={ { //link is disabled if no reg - email: reg?.ContactPerson.Email!, - reference: reg?.ContactPerson.PK!, - } satisfies InitReg + registrant_id: reg?.ContactPerson.Email!, + id: reg?.ContactPerson.PK!, + } satisfies RegV2.Init } > diff --git a/src/pages/Registration/types.ts b/src/pages/Registration/types.ts index 808d5c8edc..8edaf4481d 100644 --- a/src/pages/Registration/types.ts +++ b/src/pages/Registration/types.ts @@ -1,82 +1,65 @@ -import type { - BankingDetails, - ContactDetails, - EndowClaim, - FSAInquiry, - OrgDetails, - RegV2, - TDocumentation, -} from "types/aws"; +import type { RegV2 } from "types/aws"; -//REF_ID is global to registration -export type InitReg = { - reference: string; - email: string; - claim?: EndowClaim; -}; - -export type CompleteRegistration = { - init: InitReg; - contact: ContactDetails & { orgName: string }; - orgDetails: OrgDetails; - fsaInquiry: FSAInquiry; - documentation: TDocumentation["Documentation"]; - banking: BankingDetails; - endowId?: number; +type InitKeys = keyof RegV2.Init; +export type CompleteReg = Omit & { + init: RegV2.Init; }; type Data< - Done extends keyof CompleteRegistration, - Pending extends Exclude, -> = Pick & { - [key in Pending]?: CompleteRegistration[key]; + Done extends keyof CompleteReg, + Pending extends Exclude, +> = Pick & { + [key in Pending]?: CompleteReg[key]; }; type Step1Data = Data<"init", "contact">; -type Step2Data = Data<"init" | "contact", "orgDetails">; -export type Step3Data = Data<"init" | "contact" | "orgDetails", "fsaInquiry">; -type Step4Data = Data< - "init" | "contact" | "orgDetails" | "fsaInquiry", - "documentation" ->; +type Step2Data = Data<"init" | "contact", "org">; +export type Step3Data = Data<"init" | "contact" | "org", "irs501c3">; +type Step4Data = Omit< + Data<"init" | "contact" | "org" | "irs501c3", "docs">, + "docs" +> & { + // override fsa docs required in step 5 member + docs?: RegV2.FsaDocs | RegV2.TaxDeductibleDocs; +}; type Step5Data = Data< - "init" | "contact" | "orgDetails" | "fsaInquiry" | "documentation", + "init" | "contact" | "org" | "irs501c3" | "docs", "banking" >; /** contact details */ type RegStep1 = { step: 1; - data: RegV2.Step1; + data: Step1Data; }; /** org details */ type RegStep2 = { step: 2; - data: RegV2.Step2; + data: Step2Data; }; /** fsa inquiry */ type RegStep3 = { step: 3; - data: RegV2.Step3; + data: Step3Data; }; /** documentation */ export type RegStep4 = { step: 4; - data: RegV2.Step4; + data: Step4Data; }; /** banking */ type RegStep5 = { step: 5; - data: RegV2.Step5; + data: Step5Data; }; type RegStep6 = { step: 6; - data: RegV2.Step6; + data: CompleteReg; }; export type RegistrationState = diff --git a/src/services/aws/registration/registration.ts b/src/services/aws/registration/registration.ts index 74d6046de8..b13884a8f6 100644 --- a/src/services/aws/registration/registration.ts +++ b/src/services/aws/registration/registration.ts @@ -1,17 +1,9 @@ import { TEMP_JWT } from "constants/auth"; import { EMAIL_SUPPORT } from "constants/env"; import { logger } from "helpers"; -import { apiEnv } from "services/constants"; -import type { - ContactUpdateResult, - EndowClaim, - InitApplication, - RegistrationUpdate, - SavedRegistration, - SubmitResult, -} from "types/aws"; +import type { EndowClaim, InitApplication, RegV2 } from "types/aws"; import { version as v } from "../../helpers"; -import type { FiscalSponsorhipAgreementSigner } from "../../types"; +import type { FSASigner } from "../../types"; import { aws } from "../aws"; const registration_api = aws.injectEndpoints({ @@ -27,22 +19,22 @@ const registration_api = aws.injectEndpoints({ headers: { authorization: TEMP_JWT }, }), }), - reg: builder.query({ + reg: builder.query({ providesTags: ["registration"], query: (uuid) => { return { - url: `v5/registration/${apiEnv}`, + url: `${v(6)}/registration/${uuid}`, params: { uuid }, headers: { authorization: TEMP_JWT }, }; }, - transformResponse(res: SavedRegistration) { + transformResponse(res: RegV2.Record) { return { ...res, reqId: 0 }; }, }), fiscalSponsorshipAgreementSigningURL: builder.mutation< { url: string }, - FiscalSponsorhipAgreementSigner + FSASigner >({ //no need to invalidate registration as latest would be fetched on redirect/success query: (signer) => { @@ -56,12 +48,12 @@ const registration_api = aws.injectEndpoints({ }; }, }), - updateReg: builder.mutation({ - query: ({ reference, ...payload }) => { + updateReg: builder.mutation({ + query: ({ id, type, ...val }) => { return { - url: `${v(7)}/registration/${reference}`, + url: `${v(7)}/registration/${id}`, method: "PATCH", - body: payload, + body: { type, val }, headers: { authorization: TEMP_JWT }, }; }, @@ -74,45 +66,11 @@ const registration_api = aws.injectEndpoints({ /** pessimistic manual cache update so not to GET fresh data */ async onQueryStarted(args, { dispatch, queryFulfilled }) { try { - const { type, reference } = args; + const { id } = args; const { data } = await queryFulfilled; dispatch( - registration_api.util.updateQueryData("reg", reference, (draft) => { - switch (type) { - case "contact-details": - { - const { ContactPerson, Registration } = - data as ContactUpdateResult; - draft.ContactPerson = Object.assign( - draft.ContactPerson, - ContactPerson - ); - draft.Registration = { - ...draft.Registration, - OrganizationName: Registration.OrganizationName, - }; - } - break; - - //other updates are to draft.Registration - default: { - let tempData = data; - - // since `wise_recipient_id` is not returned as a result of updateReg, - // (only `BankStatementFile` is returned), we need to pass it manually - // and set/update it in the draft.Registration - if (type === "banking") { - tempData = Object.assign({}, data, { - wise_recipient_id: args.wise_recipient_id, - }); - } - - draft.Registration = Object.assign( - draft.Registration, - tempData - ); - } - } + registration_api.util.updateQueryData("reg", id, (draft) => { + Object.assign(draft, data); }) ); } catch (err) { @@ -120,7 +78,7 @@ const registration_api = aws.injectEndpoints({ } }, }), - submit: builder.mutation({ + submit: builder.mutation({ invalidatesTags: ["registration"], query: (referenceID) => ({ url: `${v(5)}/registration/${referenceID}/submit`, diff --git a/src/services/types.ts b/src/services/types.ts index 2584fdcd62..993ac103c1 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -1,8 +1,9 @@ +import type { Except } from "type-fest"; import type { BankingApplication, EndowmentProfileUpdate, EndowmentSettingsUpdate, - FSASignerDocumentation, + RegV2, V2RecipientAccount, } from "types/aws"; import type { SemiPartial } from "types/utils"; @@ -14,14 +15,19 @@ export type EndowmentUpdate = SemiPartial< export type ProgramDeleteMsg = { id: number; program_id: string }; -export type FiscalSponsorhipAgreementSigner = +export type FSASigner = | { id: string; - firstName: string; - lastName: string; - email: string; + first_name: string; + last_name: string; role: string; - docs: FSASignerDocumentation; + email: string; + docs: Except< + RegV2.FsaDocs, + "fsa_signed_doc_url" | "fsa_signing_url" | "outdated" + > & + Pick & + Pick; } | string; //signerEID; diff --git a/src/types/aws/ap/registration.ts b/src/types/aws/ap/registration.ts index b05bd34519..fc61366c1e 100644 --- a/src/types/aws/ap/registration.ts +++ b/src/types/aws/ap/registration.ts @@ -1,5 +1,5 @@ import type { Except } from "type-fest"; -import type { APIEnvironment, UNSDG_NUMS } from "types/lists"; +import type { UNSDG_NUMS } from "types/lists"; import type { EndowDesignation } from "."; import type { FileObject } from "../common"; @@ -97,7 +97,6 @@ export type OrgDetails = { UN_SDG: UNSDG_NUMS[]; }; -type Reset = { [K in keyof T]: null }; export type FSAInquiry = { AuthorizedToReceiveTaxDeductibleDonations: boolean; }; @@ -140,10 +139,8 @@ type DoneContact = Append; export type DoneOrgDetails = Append; export type DoneFSAInquiry = Append; -type ResetFSAInquiry = Append, {}>; export type DoneDocs = Append; -type ResetDocs = Append, {}>; export type DoneBanking = Append; @@ -151,61 +148,6 @@ type SubmissionDetails = { Email: string; EndowmentId?: number }; type InReview = Append; -export type SavedRegistration = - | InitApplication - | DoneContact - | DoneOrgDetails - | DoneFSAInquiry - | ResetFSAInquiry - | DoneDocs - | ResetDocs - | DoneBanking - | InReview; - -type ContactUpdate = { - type: "contact-details"; - ContactPerson: Pick & Partial; - Registration: OrgDataForStep1; -}; - -type OrgDetailsUpdate = { - type: "org-details"; -} & Partial & - Partial>; - -type FSAInquiryUpdate = { - type: "fsa-inquiry"; -} & Partial; - -type DocumentationUpdate = { - type: "documentation"; - //FSADocumentation handled in pdf-signing-url generator -} & NonFSADocumentation; - -type BankingUpdate = { - type: "banking"; -} & Partial; - -export type RegistrationUpdate = ( - | ContactUpdate - | OrgDetailsUpdate - | FSAInquiryUpdate - | DocumentationUpdate - | BankingUpdate -) & { - reference: string; -}; - -export type ContactUpdateResult = { - ContactPerson: ContactDetails; - Registration: OrgDataForStep1; -}; - -export type SubmitResult = { - RegistrationStatus: RegistrationStatus; - Email: string; -}; - export type Application = InReview["Registration"] & Pick; type WiseRecipient = { @@ -224,118 +166,32 @@ export type ApplicationVerdict = { PK: string } & ( ); export namespace RegV2 { - export interface Init { - id: number; - registrant_id: string; - hubspot_contact_id?: string; - created_at: string; - env: APIEnvironment; - claim?: EndowClaim; - } - - export interface Contact { - first_name: string; - last_name: string; - /** phone number */ - contact_email?: string; - goals: string; - org_name: string; - org_role: ContactRoles; - /** when `org_role` is `Other` */ - other_role?: string; - referral_method: ReferralMethods; - /** when `referral_method` is `Referral` */ - referral_code?: string; - /** when `referral_method` is `Other` */ - other_referral_method?: string; - } - - export interface Org { - website: string; - hq_country: string; - active_in_countries?: string[]; - designation: EndowDesignation; - kyc_donors_only: boolean; - un_sdg: UNSDG_NUMS[]; - hubspot_company_id?: string; - } - - export interface Docs { - outdated: boolean; - } - export interface FsaDocs extends Docs { - proof_of_identity: FileObject; - registration_number: string; - proof_of_reg: FileObject; - legal_entity_type: string; - project_description: string; - fsa_signing_url?: string; - fsa_signed_doc_url?: string; - } - export interface TaxDeductibleDocs extends Docs { - ein: string; - claim?: EndowClaim; - } - - export interface Banking { - bank_statement: FileObject; - wise_recipient_id: number; - } - - export type Submission = { endowment_id: number } | "in-review"; - - export interface Step1 extends Init { - contact: Contact; - } - export interface Step2 extends Step1 { - org: Org; - } - export interface Step3 extends Step2 { - irs501c3: boolean; - } - export interface Step4 extends Step3 { - docs: FsaDocs | TaxDeductibleDocs; - } - - export interface Step5 extends Omit { - docs: Required | TaxDeductibleDocs; - banking: Banking; - } - export interface Step6 extends Step5 { - submission: Submission; - } - - export type Record = Step1 | Step2 | Step3 | Step4 | Step5 | Step6; - namespace Update { - export interface Contact { + export interface Contact extends RegV2.Contact { type: "contact"; - val: Contact; } - export interface Org { + export interface Org extends Omit { type: "org"; - val: Omit; } - export interface FsaInq { + export interface FsaInq extends RegV2.FsaInq { type: "fsa-inq"; - val: boolean; } - export interface Docs { + export interface Docs + extends Omit { type: "docs"; - /** ein */ - val: string; } - export interface Banking { + export interface Banking extends RegV2.Banking { type: "banking"; - val: Banking; } } - export type Update = + + export type Update = ( | Update.Contact | Update.Org | Update.FsaInq | Update.Docs - | Update.Banking; + | Update.Banking + ) & { id: string }; } export const isIrs501c3 = (docs: RegV2.Docs): docs is RegV2.TaxDeductibleDocs => @@ -343,23 +199,23 @@ export const isIrs501c3 = (docs: RegV2.Docs): docs is RegV2.TaxDeductibleDocs => // type guards class StepChecker { - step1 = (data: RegV2.Record): data is RegV2.Step1 => "contact" in data; - step2 = (data: RegV2.Record): data is RegV2.Step2 => - "org" in data && this.step1(data); - step3 = (data: RegV2.Record): data is RegV2.Step3 => - "irs501c3" in data && this.step2(data); - step4 = (data: RegV2.Record): data is RegV2.Step4 => - "docs" in data && this.step3(data) && !data.docs.outdated; - step5 = (data: RegV2.Record): data is RegV2.Step5 => + contact = (data: RegV2.Record): data is RegV2.Step1 => "contact" in data; + org = (data: RegV2.Record): data is RegV2.Step2 => + "org" in data && this.contact(data); + fsaInq = (data: RegV2.Record): data is RegV2.Step3 => + "irs501c3" in data && this.org(data); + docs = (data: RegV2.Record): data is RegV2.Step4 => + "docs" in data && this.fsaInq(data) && !data.docs.outdated; + banking = (data: RegV2.Record): data is RegV2.Step5 => "banking" in data && - this.step4(data) && + this.docs(data) && ("ein" in data.docs ? true : //check if FSA docs are complete !!data.docs.fsa_signed_doc_url && !!data.docs.fsa_signing_url); - step6 = (data: RegV2.Record): data is RegV2.Step6 => - "submission" in data && this.step5(data); + submission = (data: RegV2.Record): data is RegV2.Step6 => + "submission" in data && this.banking(data); } export const isDone = new StepChecker(); diff --git a/yarn.lock b/yarn.lock index 806b6fc775..5b8604c317 100644 --- a/yarn.lock +++ b/yarn.lock @@ -822,6 +822,15 @@ __metadata: languageName: node linkType: hard +"@better-giving/registration@npm:1.0.1": + version: 1.0.1 + resolution: "@better-giving/registration@npm:1.0.1" + peerDependencies: + valibot: 0.42.0 + checksum: 10/7ecf268dc627e9853d9ec6afd6ac7f000170fb270545c3225999d32494ed22be34efb28bf96f4ac339584ddcf32fa9ff3deb68f756f7671dd479744f548d58f1 + languageName: node + linkType: hard + "@biomejs/biome@npm:1.8.1": version: 1.8.1 resolution: "@biomejs/biome@npm:1.8.1" @@ -3071,6 +3080,7 @@ __metadata: resolution: "angelprotocol-web-app@workspace:." dependencies: "@better-giving/assets": "npm:1.0.18" + "@better-giving/registration": "npm:1.0.1" "@biomejs/biome": "npm:1.8.1" "@gsap/react": "npm:2.1.1" "@headlessui/react": "npm:2.1.0" @@ -3124,6 +3134,7 @@ __metadata: tailwindcss: "npm:3.4.3" type-fest: "npm:4.20.0" typescript: "npm:5.5" + valibot: "npm:0.42.0" vite-tsconfig-paths: "npm:4.3.2" vitest: "npm:2.0.1" yup: "npm:1.4.0" @@ -6388,6 +6399,18 @@ __metadata: languageName: node linkType: hard +"valibot@npm:0.42.0": + version: 0.42.0 + resolution: "valibot@npm:0.42.0" + peerDependencies: + typescript: ">=5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/e648061c07a8418d719c797ac86530818e3bfa68edaf8f53aa41713651cad5e6faef96c66002d7350c7dcf1a32a8a93323bf3a7d75dc0df0f0ad1fce9f2ac785 + languageName: node + linkType: hard + "vite-node@npm:2.0.1": version: 2.0.1 resolution: "vite-node@npm:2.0.1"