-
-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generated intake profile fields #672
Conversation
@@ -0,0 +1,287 @@ | |||
/* eslint-disable */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is generated by MSW to integrate with the browser. Don't worry about reviewing it.
…feat/generated-profile
…feat/generated-profile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a suggestion to help with input data validation, but apart from that this PR looks great and a solid foundation for our profile fields. Thanks!
@erikguntner I agree the validation issue is to be it's own ticket. I don't see any other issues to address and this should be ready to merge 👍🏽 |
Closes: #610
This PR outlines a basic approach to rendering intake profiles based on objects defined in the database. This isn't a complete example, but I think it allows us to visualize further development of the data model to help fit our needs.
I accidentally created this branch off of the
fix-mocking-errors
branch, so I apologize for the added noise. If you would like to view only the relative changes use this link since it filters out the other files.What changes did you make?
As I mentioned there is quite a bit of noise in this PR so I wanted to highlight some of the most relevant files and what they are doing.
Data layer:
app/src/services/profile.ts
:app/src/utils/test/db/profile.ts
:app/src/utils/test/browser.ts
:app/src/utils/test/handlers/profile.ts
:app/src/services/profile.ts
. You can see that the endpoint/api/profile/:profileId
returns the profileapp/src/utils/test/db/profile.ts
based on the id passed in as an argument. The endpoint/api/profile/answers/:userId
returns an empty object for now since I have to define and answers array, but the components have been tested with both an empty answers array and a partial answers array.View layer:
app/src/views/IntakeProfile/index.tsx
:profileId
andgroupId
from the URL. TheprofileId
is used to fetch the corresponding profile and fields from the server. ThegroupId
is used to determine which field group/section to render.useGetProfileQuery
makes a get request using theprofileId
to return the given profileuseGetAnswersQuery
makes a get request using theuserId
to return all answers associated with a user. This is using a fixed id and returns an empty array for now.buildValidationSchema
uses thefieldGroups
from the returned profile and the groupId to build a validation schema for the group. More on the details of this below.createInitialValues
uses the returnfieldGroups
andanswers
to create an object used as the initial values for Formik. More details on this below.fieldGroups
and renders a list of links on the sidebar that update the URL with the associated fieldGroup id. This determines which fieldGroup's fields to render.Outlet
component is used which is a placeholder component provided byreact-router
that is replaced with theFieldGroupList
component. More on that below. ThisOutlet
also provides a context, which we use to pass values down to theFieldGroupList
.onSubmit
merges the updated answers with the existing answers objects, and if there are no validation errors, will create an alert with the answers to be submitted.app/src/views/IntakeProfile/constants/index.ts
fieldGroupBuilder
andfieldBuilder
are helper functions that generate random field groups and fields.createInitialValues
creates an object of initial values forFormik
. The object is this shape:fieldGroupId.fieldId
. This is how Formik handles nested data structures.fieldDefaultValue
. This returns a default value based on the field type if an answer doesn't exist.buildValidationSchema
creates a validation schema for the field group with a similar structure tocreateInitialValues
which isfieldGroupId.fieldId = schema
.createFieldValidationSchema
which creates a schema at the field level that takes the schema based on the field type and merges it with any further requirements. This is not fully fleshed out and needs more work, but shows how it would work for required fields and fields that have a constraint necessary for being required usingrequired_if
property.IntakeProfileGroups
components which render all the fields in a field group using a switch statement to render the required field based on the field type. Many of these are basic field types that could be seen throughout the profile.app/src/views/IntakeProfile/hooks/useFieldGroups.ts
Generates field groups and answers but is replaced by the profiles service. Could probably be removed.app/src/components/IntakeProfile/IntakeProfileGroups.tsx
Contains two components:FieldGroupList
uses thefieldGroups
andgroupId
to find the rightfieldGroup
and iterates over its field. Each field is passed to theRenderFields
component which uses a switch statement to render the corresponding field based on the field type. I think there are opportunities for refactoring here to make things a bit cleaner.views/constanst/intakeProfile
contains sets of types describing the view model, a set of helper functions to build groups and fields, as well as validation schemas for each field type.app/src/components/IntakeProfile/AdditionaGuestsField.tsx
I wanted to see what it was like adding a more complex field to this implementation and found it wasn't too bad.Rationale behind the changes?
I wanted to explore what was possible when using a data model that describes the intake profile to generate the necessary fields.
Pros:
Cons:
What did you learn or can share that is new?(optional)
Outlet
components: https://reactrouter.com/en/main/components/outletFormik
fields: https://formik.org/docs/guides/arraysResources
The data model is heavily inspired by Typeform's API with a few tweaks:
Data Model
Something that I believe is worth considering is whether we need to store the
INTAKE_PROFILE
,FIELD_GROUP
,FIELD
along with thePROPERTIES
, andVALIDATIONS
in the database at all. These only describe the intake profile and are not created, updated, or deleted by users. An alternative might be to define the two intake profiles in a more readable file format (like yaml) that could be read and parsed on both ends for their necessary purposes. These files could then act as a source of truth for the shape of the intake profiles and we wouldn't have to go through updating the database if you wanted to change the structure of the profile. Also, since the data model is still very much in flux it would make it easier to make updates to it. I think this would cut down on the complexity of the backend data model and allow to store just the information related to the users.Example Field Group
Screenshots of Proposed Changes Of The Website (if any, please do not screen shot code changes)
This video shows profiels 1 and 2 being rendered along with validation.
Screen.Recording.2024-04-08.at.11.46.19.AM.mov