From d42c5bb3a02295412aa5dc7398346f8652f0594f Mon Sep 17 00:00:00 2001 From: shindigira Date: Fri, 2 Feb 2024 14:46:58 -0500 Subject: [PATCH 01/73] wip: Update Your Financial Profile - Skeleton --- src/App.tsx | 9 ++ src/components/FormWrapper.tsx | 17 +++ src/components/ProfileFormWrapper.tsx | 19 --- .../Filing/UpdateFinancialProfile/index.tsx | 128 ++++++++++++++++++ .../Filing/UpdateFinancialProfile/types.ts | 13 ++ .../ProfileForm/Step1Form/InputEntry.tsx | 17 ++- src/pages/ProfileForm/Step1Form/Step1Form.tsx | 21 +-- src/pages/ProfileForm/index.tsx | 25 ++-- src/store/useAppState.ts | 20 +++ src/store/useProfileForm.ts | 2 - 10 files changed, 221 insertions(+), 50 deletions(-) create mode 100644 src/components/FormWrapper.tsx delete mode 100644 src/components/ProfileFormWrapper.tsx create mode 100644 src/pages/Filing/UpdateFinancialProfile/index.tsx create mode 100644 src/pages/Filing/UpdateFinancialProfile/types.ts create mode 100644 src/store/useAppState.ts diff --git a/src/App.tsx b/src/App.tsx index 5de17d453..7c04d156a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import 'design-system-react/style.css'; import Error500 from 'pages/Error/Error500'; import { NotFound404 } from 'pages/Error/NotFound404'; import FilingApp from 'pages/Filing/FilingApp'; +import UpdateFinancialProfile from 'pages/Filing/UpdateFinancialProfile'; import ViewUserProfile from 'pages/Filing/ViewUserProfile'; import { Scenario } from 'pages/ProfileForm/Step2Form/Step2FormHeader.data'; import type { ReactElement } from 'react'; @@ -236,6 +237,14 @@ export default function App(): ReactElement { } /> + + + + } + /> } /> } /> +
+
{children}
+
+ + ); +} + +export default FormWrapper; diff --git a/src/components/ProfileFormWrapper.tsx b/src/components/ProfileFormWrapper.tsx deleted file mode 100644 index 875b33021..000000000 --- a/src/components/ProfileFormWrapper.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { ReactNode } from 'react'; - -interface Properties { - children: ReactNode -} - -function ProfileFormWrapper({children}: Properties): JSX.Element { - return ( -
-
-
- { children } -
-
-
- ) -} - -export default ProfileFormWrapper \ No newline at end of file diff --git a/src/pages/Filing/UpdateFinancialProfile/index.tsx b/src/pages/Filing/UpdateFinancialProfile/index.tsx new file mode 100644 index 000000000..290bd230c --- /dev/null +++ b/src/pages/Filing/UpdateFinancialProfile/index.tsx @@ -0,0 +1,128 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import FieldGroup from 'components/FieldGroup'; +import FormWrapper from 'components/FormWrapper'; +import SectionIntro from 'components/SectionIntro'; +import { Button, TextIntroduction } from 'design-system-react'; +import type { UFPSchema } from 'pages/Filing/UpdateFinancialProfile/types'; +import { ufpSchema } from 'pages/Filing/UpdateFinancialProfile/types'; +import InputEntry from 'pages/ProfileForm/Step1Form/InputEntry'; + +import type { SubmitHandler } from 'react-hook-form'; +import { Controller, useForm } from 'react-hook-form'; + +interface Properties {} + +function UpdateFinancialProfile(properties: Properties) { + const { + register, + control, + handleSubmit, + setValue, + // trigger, + getValues, + formState: { errors: formErrors }, + } = useForm({ + resolver: zodResolver(ufpSchema), + // defaultValues: { + // }, + }); + + const onSubmitButtonAction = () => + console.log('data to be submitted:', getValues()); + const clearForm = () => console.log('clicked'); + const onSubmit: SubmitHandler = data => { + // TODO: decide if real-time input validation or on submit button click validation is better UX + // console.log('data:', data); + }; + + // console.log(getValues()); + + return ( + +
+
+ + Requested updates are processed by our support staff. Please + allow 24-48 hours for a response during normal business hours. + + } + /> +
+ + + To update the email domains for your financial institution, contact + our support staff. To update any other data in this section, visit + GLEIF. + + +
+ + ( + + // + // setValue('checkboxes.option1', e.target.checked) + // } + // /> + )} + control={control} + name='checkboxes.option1' + // rules={{ required: 'This field is required' }} + /> + + Break + + + + +
+ +
+
+
+ +
+ ); +} + +export default UpdateFinancialProfile; diff --git a/src/pages/Filing/UpdateFinancialProfile/types.ts b/src/pages/Filing/UpdateFinancialProfile/types.ts new file mode 100644 index 000000000..748fba3eb --- /dev/null +++ b/src/pages/Filing/UpdateFinancialProfile/types.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; + +export const ufpSchema = z.object({ + tin: z.string().trim().min(1, { + message: 'You must enter your tin.', + }), + checkboxes: z.object({ + option1: z.boolean(), + option2: z.boolean(), + }), +}); + +export type UFPSchema = z.infer; diff --git a/src/pages/ProfileForm/Step1Form/InputEntry.tsx b/src/pages/ProfileForm/Step1Form/InputEntry.tsx index 07ab4371f..4b39117e5 100644 --- a/src/pages/ProfileForm/Step1Form/InputEntry.tsx +++ b/src/pages/ProfileForm/Step1Form/InputEntry.tsx @@ -1,9 +1,10 @@ +/* eslint-disable react/require-default-props */ import type { ReactNode } from 'react'; import { forwardRef } from 'react'; import { Element } from 'react-scroll'; -import { TextInput, Heading } from 'design-system-react'; import InputErrorMessage from 'components/InputErrorMessage'; +import { Heading, TextInput } from 'design-system-react'; interface InputEntryProperties extends React.PropsWithoutRef { @@ -11,8 +12,9 @@ interface InputEntryProperties label: string; errors: object; isDisabled: boolean; - isLast: boolean; - hideInput: boolean; + isLast?: boolean; + hideInput?: boolean; + showError?: boolean; children?: ReactNode; } @@ -25,6 +27,7 @@ const InputEntry = forwardRef( isDisabled = false, hideInput = false, isLast = false, + showError = true, children, ...properties }, @@ -55,7 +58,7 @@ const InputEntry = forwardRef( {...properties} /> - {errors[id] ? ( + {showError && errors[id] ? (
{errors[id].message}
@@ -65,8 +68,8 @@ const InputEntry = forwardRef( ), ); -InputEntry.defaultProps = { - children: null, -}; +// InputEntry.defaultProps = { +// children: null, +// }; export default InputEntry; diff --git a/src/pages/ProfileForm/Step1Form/Step1Form.tsx b/src/pages/ProfileForm/Step1Form/Step1Form.tsx index 77c65fb13..3ab8d89d1 100644 --- a/src/pages/ProfileForm/Step1Form/Step1Form.tsx +++ b/src/pages/ProfileForm/Step1Form/Step1Form.tsx @@ -5,23 +5,23 @@ import useSblAuth from 'api/useSblAuth'; import { useEffect, useState } from 'react'; import type { SubmitHandler } from 'react-hook-form'; import { useForm } from 'react-hook-form'; -import { Element, scroller } from 'react-scroll'; import { useNavigate } from 'react-router-dom'; +import { Element, scroller } from 'react-scroll'; -import AssociatedFinancialInstitutions from './AssociatedFinancialInstitutions'; -import NoDatabaseResultError from './NoDatabaseResultError'; -import FormParagraph from 'components/FormParagraph'; import FieldGroup from 'components/FieldGroup'; +import FormParagraph from 'components/FormParagraph'; import SectionIntro from 'components/SectionIntro'; +import AssociatedFinancialInstitutions from './AssociatedFinancialInstitutions'; +import NoDatabaseResultError from './NoDatabaseResultError'; import { Link } from 'components/Link'; -import { Button, Paragraph, Heading } from 'design-system-react'; +import { Button, Heading, Paragraph } from 'design-system-react'; -import { fiOptions, fiData } from 'pages/ProfileForm/ProfileForm.data'; +import { fiOptions } from 'pages/ProfileForm/ProfileForm.data'; import type { - InstitutionDetailsApiType, - InstitutionDetailsApiCheckedType, FinancialInstitutionRS, + InstitutionDetailsApiCheckedType, + InstitutionDetailsApiType, ValidationSchema, } from 'pages/ProfileForm/types'; import { @@ -33,14 +33,15 @@ import Step1FormErrorHeader from './Step1FormErrorHeader'; import Step1FormHeader from './Step1FormHeader'; import { useQuery } from '@tanstack/react-query'; +import useAppState from 'store/useAppState'; import useProfileForm from 'store/useProfileForm'; import Step1FormDropdownContainer from './Step1FormDropdownContainer'; import fetchInstitutions from 'api/fetchInstitutions'; import submitUserProfile from 'api/submitUserProfile'; import { - formatUserProfileObject, formatDataCheckedState, + formatUserProfileObject, } from 'pages/ProfileForm/ProfileFormUtils'; function Step1Form(): JSX.Element { @@ -136,7 +137,7 @@ function Step1Form(): JSX.Element { const navigate = useNavigate(); const enableMultiselect = useProfileForm(state => state.enableMultiselect); - const isSalesforce = useProfileForm(state => state.isSalesforce); + const isSalesforce = useAppState(state => state.isSalesforce); // 'Clear Form' function function clearForm(): void { diff --git a/src/pages/ProfileForm/index.tsx b/src/pages/ProfileForm/index.tsx index d1eefb19e..a518a2c4a 100644 --- a/src/pages/ProfileForm/index.tsx +++ b/src/pages/ProfileForm/index.tsx @@ -1,8 +1,8 @@ -import useProfileForm from "store/useProfileForm"; +import useProfileForm from 'store/useProfileForm'; -import ProfileFormWrapper from "components/ProfileFormWrapper"; -import Step1Form from "./Step1Form/Step1Form"; -import Step2Form from "./Step2Form/Step2Form"; +import FormWrapper from 'components/FormWrapper'; +import Step1Form from './Step1Form/Step1Form'; +import Step2Form from './Step2Form/Step2Form'; /** * Given a step, will render the proper StepForm @@ -24,19 +24,20 @@ function getStepForm(step = 1): () => JSX.Element { } /** - * + * * @returns Chooses which StepForm to return based on the store value */ function StepForm(): JSX.Element { - const step = useProfileForm((state) => state.step); + const step = useProfileForm(state => state.step); const StepFormComponent = getStepForm(step); - return (
- - - -
); + return ( +
+ + + +
+ ); } export default StepForm; - diff --git a/src/store/useAppState.ts b/src/store/useAppState.ts new file mode 100644 index 000000000..b12c137f1 --- /dev/null +++ b/src/store/useAppState.ts @@ -0,0 +1,20 @@ +/* eslint-disable no-param-reassign */ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface State { + isSalesforce: boolean; +} + +interface Actions {} + +/** + * Controls which form is rendered in ProfileForm + */ +const useAppState = create( + immer(set => ({ + isSalesforce: false, + })), +); + +export default useAppState; diff --git a/src/store/useProfileForm.ts b/src/store/useProfileForm.ts index a7bb1f8a2..7a3eb05f8 100644 --- a/src/store/useProfileForm.ts +++ b/src/store/useProfileForm.ts @@ -12,7 +12,6 @@ interface State { profileData?: ValidationSchema; selectedScenario: Scenario; enableMultiselect: boolean; - isSalesforce: boolean; } interface Actions { @@ -29,7 +28,6 @@ const useProfileForm = create( step: StepOne, // Step 1 toggles enableMultiselect: false, - isSalesforce: false, // Step 2 toggles selectedScenario: Scenario.Error1, // setters From 72586907a3ee60ec96d65330bdc6564ac1318f63 Mon Sep 17 00:00:00 2001 From: shindigira Date: Fri, 2 Feb 2024 15:22:46 -0500 Subject: [PATCH 02/73] feat: created wrapper components to abstract away some tailwind form styling --- src/components/FormButtonGroup.tsx | 11 ++++ src/components/FormHeaderWrapper.tsx | 15 ++++++ .../Filing/UpdateFinancialProfile/index.tsx | 27 +++++----- src/pages/ProfileForm/Step1Form/Step1Form.tsx | 50 +++++++++++++------ .../ProfileForm/Step1Form/Step1FormHeader.tsx | 29 ----------- 5 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 src/components/FormButtonGroup.tsx create mode 100644 src/components/FormHeaderWrapper.tsx delete mode 100644 src/pages/ProfileForm/Step1Form/Step1FormHeader.tsx diff --git a/src/components/FormButtonGroup.tsx b/src/components/FormButtonGroup.tsx new file mode 100644 index 000000000..1d7fceba7 --- /dev/null +++ b/src/components/FormButtonGroup.tsx @@ -0,0 +1,11 @@ +import type { ReactNode } from 'react'; + +interface Properties { + children: ReactNode; +} + +function FormButtonGroup({ children }: Properties): JSX.Element { + return
{children}
; +} + +export default FormButtonGroup; diff --git a/src/components/FormHeaderWrapper.tsx b/src/components/FormHeaderWrapper.tsx new file mode 100644 index 000000000..a2014137a --- /dev/null +++ b/src/components/FormHeaderWrapper.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from 'react'; + +interface Properties { + children: ReactNode; +} + +function FormHeaderWrapper({ children }: Properties): JSX.Element { + return ( +
+ {children} +
+ ); +} + +export default FormHeaderWrapper; diff --git a/src/pages/Filing/UpdateFinancialProfile/index.tsx b/src/pages/Filing/UpdateFinancialProfile/index.tsx index 290bd230c..648c473c8 100644 --- a/src/pages/Filing/UpdateFinancialProfile/index.tsx +++ b/src/pages/Filing/UpdateFinancialProfile/index.tsx @@ -1,5 +1,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import FieldGroup from 'components/FieldGroup'; +import FormButtonGroup from 'components/FormButtonGroup'; +import FormHeaderWrapper from 'components/FormHeaderWrapper'; import FormWrapper from 'components/FormWrapper'; import SectionIntro from 'components/SectionIntro'; import { Button, TextIntroduction } from 'design-system-react'; @@ -40,7 +42,7 @@ function UpdateFinancialProfile(properties: Properties) { return (
-
+ } /> -
- + To update the email domains for your financial institution, contact our support staff. To update any other data in this section, visit @@ -101,7 +102,7 @@ function UpdateFinancialProfile(properties: Properties) { -
+
-
+