From fdee2f12b9e84bf1d9a4325066248f8ccfc1345a Mon Sep 17 00:00:00 2001 From: ap-justin <89639563+ap-justin@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:31:21 +0800 Subject: [PATCH] registration state --- .../Steps/getRegistrationState.ts | 189 ++---------------- src/pages/Registration/types.ts | 14 +- src/types/aws/ap/registration.ts | 167 +++++++++++++--- 3 files changed, 158 insertions(+), 212 deletions(-) diff --git a/src/pages/Registration/Steps/getRegistrationState.ts b/src/pages/Registration/Steps/getRegistrationState.ts index 22d6fbd009..82d35b1c39 100644 --- a/src/pages/Registration/Steps/getRegistrationState.ts +++ b/src/pages/Registration/Steps/getRegistrationState.ts @@ -1,194 +1,33 @@ -import { - type BankingDetails, - type DoneBanking, - type DoneDocs, - type DoneFSAInquiry, - type DoneOrgDetails, - type EndowClaim, - type FSAInquiry, - type InitContact, - type OrgDetails, - type SavedRegistration, - type TDocumentation, - isDoneBanking, - isDoneContact, - isDoneDocs, - isDoneFSAInquiry, - isDoneOrgDetails, - isSubmitted, -} from "types/aws"; +import { type RegV2, isDone } from "types/aws"; import { steps } from "../routes"; -import type { InitReg, RegistrationState } from "../types"; +import type { RegistrationState } from "../types"; -export function getRegistrationState(reg: SavedRegistration): { +export function getRegistrationState(reg: RegV2.Record): { state: RegistrationState; nextStep: steps; } { - if (isSubmitted(reg)) { - const { ContactPerson: c, Registration: r } = reg; - return { - state: { - step: 6, - data: { - init: initReg(c, r.InitClaim), - contact: { ...c, orgName: r.OrganizationName }, - orgDetails: orgDetails(r), - fsaInquiry: fsaInquiry(r), - documentation: docs(r), - banking: bankDetails(r), - status: r.RegistrationStatus, - endowId: r.EndowmentId, - }, - }, - nextStep: steps.summary, - }; + if (isDone.step6(reg)) { + return { state: { step: 6, data: reg }, nextStep: steps.summary }; } - if (isDoneBanking(reg)) { - const { ContactPerson: c, Registration: r } = reg; - return { - state: { - step: 5, - data: { - init: initReg(c, r.InitClaim), - contact: { ...c, orgName: r.OrganizationName }, - orgDetails: orgDetails(r), - fsaInquiry: fsaInquiry(r), - documentation: docs(r), - banking: bankDetails(r), - }, - }, - nextStep: steps.summary, - }; + if (isDone.step5(reg)) { + return { state: { step: 5, data: reg }, nextStep: steps.summary }; } - if (isDoneDocs(reg)) { - const { ContactPerson: c, Registration: r } = reg; - const isSignedFSA = - r.Documentation.DocType === "FSA" - ? !!r.Documentation.SignedFiscalSponsorshipAgreement - : true; - - return { - state: { - //cast to 4 (type only) to conform to type `RegStep4` which accepts documentation - step: (isSignedFSA ? 4 : 3) as 4, - data: { - init: initReg(c, r.InitClaim), - contact: { ...c, orgName: r.OrganizationName }, - orgDetails: orgDetails(r), - fsaInquiry: fsaInquiry(r), - //even step is has value of `3` user could still go to step 4 with documentation pre-filled from previous uploads - documentation: docs(r), - }, - }, - nextStep: steps.banking, - }; + if (isDone.step4(reg)) { + return { state: { step: 4, data: reg }, nextStep: steps.banking }; } - if (isDoneFSAInquiry(reg)) { - const { ContactPerson: c, Registration: r } = reg; - return { - state: { - step: 3, - data: { - init: initReg(c, r.InitClaim), - contact: { ...c, orgName: r.OrganizationName }, - orgDetails: orgDetails(r), - fsaInquiry: fsaInquiry(r), - }, - }, - nextStep: steps.docs, - }; - } - if (isDoneOrgDetails(reg)) { - const { ContactPerson: c, Registration: r } = reg; - return { - state: { - step: 2, - data: { - init: initReg(c, r.InitClaim), - contact: { ...c, orgName: r.OrganizationName }, - orgDetails: orgDetails(r), - }, - }, - nextStep: steps.fsaInquiry, - }; + if (isDone.step3(reg)) { + return { state: { step: 3, data: reg }, nextStep: steps.docs }; } - if (isDoneContact(reg)) { - const { ContactPerson: c, Registration: r } = reg; - return { - state: { - step: 1, - data: { - init: initReg(c, r.InitClaim), - contact: { ...c, orgName: r.OrganizationName }, - }, - }, - nextStep: steps.orgDetails, - }; + if (isDone.step2(reg)) { + return { state: { step: 2, data: reg }, nextStep: steps.fsaInquiry }; } - const { ContactPerson: c, Registration: r } = reg; return { - state: { - step: 1, - data: { - init: initReg(c, r.InitClaim), - }, - }, + state: { step: 1, data: reg satisfies RegV2.Step1 }, nextStep: steps.contact, }; } - -function initReg(i: InitContact, claim?: EndowClaim): InitReg { - return { - email: i.Email, - reference: i.PK, - claim, - }; -} - -function orgDetails(reg: DoneOrgDetails["Registration"]): OrgDetails { - return { - Website: reg.Website, - HqCountry: reg.HqCountry, - ActiveInCountries: reg.ActiveInCountries, - EndowDesignation: reg.EndowDesignation, - KycDonorsOnly: reg.KycDonorsOnly, - UN_SDG: reg.UN_SDG, - }; -} - -function docs(reg: DoneDocs["Registration"]): TDocumentation["Documentation"] { - const doc = reg.Documentation; - if (doc.DocType === "Non-FSA") { - return { EIN: doc.EIN, DocType: doc.DocType, Claim: doc.Claim }; - } - return { - DocType: doc.DocType, - ProofOfIdentity: doc.ProofOfIdentity, - RegistrationNumber: doc.RegistrationNumber, - ProofOfRegistration: doc.ProofOfRegistration, - LegalEntityType: doc.LegalEntityType, - ProjectDescription: doc.ProjectDescription, - FiscalSponsorshipAgreementSigningURL: - doc.FiscalSponsorshipAgreementSigningURL, - SignedFiscalSponsorshipAgreement: doc.SignedFiscalSponsorshipAgreement, - }; -} - -function bankDetails(reg: DoneBanking["Registration"]): BankingDetails { - return { - BankStatementFile: reg.BankStatementFile, - wise_recipient_id: reg.wise_recipient_id, - }; -} - -function fsaInquiry(reg: DoneFSAInquiry["Registration"]): FSAInquiry { - return { - AuthorizedToReceiveTaxDeductibleDonations: - reg.AuthorizedToReceiveTaxDeductibleDonations, - }; -} diff --git a/src/pages/Registration/types.ts b/src/pages/Registration/types.ts index a78a338f98..808d5c8edc 100644 --- a/src/pages/Registration/types.ts +++ b/src/pages/Registration/types.ts @@ -4,7 +4,7 @@ import type { EndowClaim, FSAInquiry, OrgDetails, - RegistrationStatus, + RegV2, TDocumentation, } from "types/aws"; @@ -47,36 +47,36 @@ type Step5Data = Data< /** contact details */ type RegStep1 = { step: 1; - data: Step1Data; + data: RegV2.Step1; }; /** org details */ type RegStep2 = { step: 2; - data: Step2Data; + data: RegV2.Step2; }; /** fsa inquiry */ type RegStep3 = { step: 3; - data: Step3Data; + data: RegV2.Step3; }; /** documentation */ export type RegStep4 = { step: 4; - data: Step4Data; + data: RegV2.Step4; }; /** banking */ type RegStep5 = { step: 5; - data: Step5Data; + data: RegV2.Step5; }; type RegStep6 = { step: 6; - data: CompleteRegistration & { status: RegistrationStatus }; + data: RegV2.Step6; }; export type RegistrationState = diff --git a/src/types/aws/ap/registration.ts b/src/types/aws/ap/registration.ts index 5d457097cb..b05bd34519 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 { UNSDG_NUMS } from "types/lists"; +import type { APIEnvironment, UNSDG_NUMS } from "types/lists"; import type { EndowDesignation } from "."; import type { FileObject } from "../common"; @@ -223,36 +223,143 @@ export type ApplicationVerdict = { PK: string } & ( | { verdict: "rejected"; rejectionReason: string } ); -/** type guards */ -export function isDoneContact(data: SavedRegistration): data is DoneContact { - return !!(data.ContactPerson as ContactDetails).FirstName; +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 { + type: "contact"; + val: Contact; + } + export interface Org { + type: "org"; + val: Omit; + } + export interface FsaInq { + type: "fsa-inq"; + val: boolean; + } + export interface Docs { + type: "docs"; + /** ein */ + val: string; + } + export interface Banking { + type: "banking"; + val: Banking; + } + } + export type Update = + | Update.Contact + | Update.Org + | Update.FsaInq + | Update.Docs + | Update.Banking; } -export function isDoneOrgDetails( - data: SavedRegistration -): data is DoneOrgDetails { - return !!(data.Registration as OrgDetails).Website; +export const isIrs501c3 = (docs: RegV2.Docs): docs is RegV2.TaxDeductibleDocs => + "ein" in docs; + +// 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 => + "banking" in data && + this.step4(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); } -export function isDoneFSAInquiry( - data: SavedRegistration -): data is DoneFSAInquiry { - const { Registration: reg } = data as DoneFSAInquiry; - return reg.AuthorizedToReceiveTaxDeductibleDonations != null; -} - -export function isDoneDocs(data: SavedRegistration): data is DoneDocs { - const { Registration: reg } = data as DoneDocs; - return ( - !!reg.Documentation && reg.AuthorizedToReceiveTaxDeductibleDonations != null - ); -} - -export function isDoneBanking(data: SavedRegistration): data is DoneBanking { - const { Registration: reg } = data as DoneBanking; - return !!reg.BankStatementFile && !!reg.Documentation; -} - -export function isSubmitted(data: SavedRegistration): data is InReview { - return !!(data.Registration as SubmissionDetails).Email; -} +export const isDone = new StepChecker();