diff --git a/backend/app/core/logging.py b/backend/app/core/logging.py
index e50132368..1c9124392 100644
--- a/backend/app/core/logging.py
+++ b/backend/app/core/logging.py
@@ -37,8 +37,10 @@ def emit(self, record):
)
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
-
- if settings.HUU_ENVIRONMENT.lower() in ["development", "qa", "local"]:
+ '''
+ A logging file can replace logging to standard out by changing sys.stdout to filename.log
+ '''
+ if settings.HUU_ENVIRONMENT.lower() in ["development", "local"]:
logger.add(
sys.stdout,
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function} - {message}",
diff --git a/backend/app/modules/deps.py b/backend/app/modules/deps.py
index 8e2fe0355..9ef7f5c03 100644
--- a/backend/app/modules/deps.py
+++ b/backend/app/modules/deps.py
@@ -26,6 +26,7 @@ def get_form_1():
import json
with open("form_data/form1.json", "r") as f:
FORM_1 = json.load(f)
+ FORM_1['id'] = 'guest'
return FORM_1
@@ -35,6 +36,7 @@ def get_form_2():
import json
with open("form_data/form2.json", "r") as f:
FORM_2 = json.load(f)
+ FORM_2['id'] = 'host'
return FORM_2
################################################################################
diff --git a/backend/app/modules/intake_profile/controller.py b/backend/app/modules/intake_profile/controller.py
index cfe79cba3..0498828b1 100644
--- a/backend/app/modules/intake_profile/controller.py
+++ b/backend/app/modules/intake_profile/controller.py
@@ -6,58 +6,77 @@
router = APIRouter()
-@router.put("/responses/{user_id}", status_code=status.HTTP_501_NOT_IMPLEMENTED)
-def update_intake_profile_responses(user_id, body, db_session: DbSessionDep):
- pass
- # TODO: Implement update intake profile responses
- # with db_session.begin() as session:
- # user_repo = UserRepository(session)
- # forms_repo = FormsRepository(session)
- # user = user_repo.get_user(token_info['Username'])
-
- # form = forms_repo.get_form(form_id)
- # if not form:
- # return f"Form with id {form_id} does not exist.", 404
-
- # valid_field_ids = form.get_field_ids()
- # for response in body:
- # response["user_id"] = user.id
- # if response["field_id"] not in valid_field_ids:
- # return f"Form {form_id} does not contain field id {response['field_id']}", 400
-
- # forms_repo.add_user_responses(user.id, body)
-
- # return {}, 204
-
-
-@router.get("/responses/{user_id}", status_code=status.HTTP_501_NOT_IMPLEMENTED)
-def get_intake_profile_responses(user_id, db_session: DbSessionDep):
- pass
- # TODO: Implement get Intake Profile responses
- # with db_session.begin() as session:
- # user_repo = UserRepository(session)
- # forms_repo = FormsRepository(session)
-
- # form = forms_repo.get_form_json(form_id)
- # if not form:
- # return f"Form with id {form_id} does not exist.", 404
-
- # user = user_repo.get_user(token_info['Username'])
- # responses = forms_repo.get_user_responses(user.id, form_id)
- # if responses:
- # return responses, 200
- # return [], 202
-
-
-@router.get("/form/{profile_id}", status_code=status.HTTP_200_OK)
-def get_intake_profile_form(profile_id: int,
- profile_form_1: Annotated[str, Depends(get_form_1)],
- profile_form_2: Annotated[str, Depends(get_form_2)]):
- """Get the Intake Profile form definition."""
- if profile_id == 1:
- return profile_form_1
- if profile_id == 2:
- return profile_form_2
+# @router.put("/responses/{user_id}", status_code=status.HTTP_501_NOT_IMPLEMENTED)
+# def update_intake_profile_responses(user_id, body, db_session: DbSessionDep):
+# pass
+ # TODO: Implement the functionality to update intake profile responses.
+ # This should:
+ # 1. Use the `db_session` to initiate a transaction.
+ # 2. Retrieve the user data using the `user_id`.
+ # 3. Get the specific form by `form_id`.
+ # 4. Check if the form exists. If not, return an error (404).
+ # 5. Validate the field IDs in the response body against the form's field IDs.
+ # 6. If any field ID is invalid, return an error (400).
+ # 7. Save the valid responses in the database.
+ # 8. Return a success response with status 204 (no content) if everything goes well.
+
+# @router.get("/responses/{user_id}", status_code=status.HTTP_501_NOT_IMPLEMENTED)
+# def get_intake_profile_responses(user_id, db_session: DbSessionDep):
+# pass
+ # TODO: Implement the functionality to retrieve intake profile responses for a user.
+ # This should:
+ # 1. Use the `db_session` to begin a transaction.
+ # 2. Retrieve the user using the `user_id`.
+ # 3. Fetch the form associated with the user's responses.
+ # 4. If the form does not exist, return a 404 error.
+ # 5. Get the user responses for the form from the database.
+ # 6. If responses are found, return them with status 200.
+ # 7. If no responses are found, return an empty list with status 202.
+
+# @router.get("/responses/{profile_type}", status_code=status.HTTP_200_OK)
+# def get_intake_profile_form(profile_type: str,
+# profile_form_1: Annotated[dict, Depends(get_form_1)],
+# profile_form_2: Annotated[dict, Depends(get_form_2)]):
+# """Get the Intake Profile form definition for host or guest."""
+# if profile_type == "guest":
+# return profile_form_1
+# if profile_type == "host":
+# return profile_form_2
+ # TODO: Return the appropriate form based on the `profile_type` (either "guest" or "host").
+ # 1. Use `profile_form_1` for the "guest" type and return it.
+ # 2. Use `profile_form_2` for the "host" type and return it.
+ # 3. If the `profile_type` is not recognized, raise a 404 error.
+
+# @router.get("/form/{profile_id}", status_code=status.HTTP_200_OK)
+# def get_intake_profile_form(profile_id: int,
+# profile_form_1: Annotated[str, Depends(get_form_1)],
+# profile_form_2: Annotated[str, Depends(get_form_2)]):
+# """Get the Intake Profile form definition."""
+# if profile_id == 1:
+# return profile_form_1
+# if profile_id == 2:
+# return profile_form_2
+ # TODO: Return the form based on the `profile_id`.
+ # 1. If the `profile_id` is 1, return `profile_form_1`.
+ # 2. If the `profile_id` is 2, return `profile_form_2`.
+ # 3. If the `profile_id` is not valid, raise a 404 error.
+
+# This is your current working function:
+@router.get("/form/{profile_type}", status_code=status.HTTP_200_OK)
+def get_intake_profile_form(profile_type: str,
+ profile_form_1: Annotated[dict, Depends(get_form_1)],
+ profile_form_2: Annotated[dict, Depends(get_form_2)]):
+ """Get the Intake Profile form definition and responses for host or guest."""
+ if profile_type == "guest":
+ return {
+ "form": profile_form_1,
+ "responses": profile_form_1.get("responses", [])
+ }
+ if profile_type == "host":
+ return {
+ "form": profile_form_2,
+ "responses": profile_form_2.get("responses", [])
+ }
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
- detail=f"Form with id {profile_id} does not exist.")
+ detail=f"Form type {profile_type} does not exist.")
diff --git a/backend/form_data/form1.json b/backend/form_data/form1.json
index db8ced8b7..8d79b7dbc 100644
--- a/backend/form_data/form1.json
+++ b/backend/form_data/form1.json
@@ -1 +1,344 @@
-{"id":"1","fieldGroups":[{"id":"1293","title":"Basic Information","fields":[{"id":"3922","title":"First Name","type":"short_text","properties":{},"validations":{"required":true}},{"id":"5572","title":"Last Name","type":"short_text","properties":{},"validations":{"required":true}},{"id":"2188","title":"Date of Birth","type":"date","properties":{},"validations":{"required":true}},{"id":"3924","title":"Gender Identity","type":"dropdown","properties":{"choices":[{"id":"1359","label":"Woman"},{"id":"5342","label":"Man"},{"id":"3151","label":"Questioning"},{"id":"2178","label":"Transgender"},{"id":"3667","label":"Non-binary"},{"id":"3708","label":"Doesn’t know or prefers not to answer\\t"}]},"validations":{"required":true}}]},{"id":"1348","title":"Contact Information","fields":[{"id":"7813","title":"Email","type":"email","properties":{},"validations":{"required":true}},{"id":"2044","title":"Phone Number","type":"number","properties":{},"validations":{"required":true}},{"id":"7504","title":"What is the best way to contact you?","type":"contact_method","properties":{},"validations":{"required":true},"linkedFields":{"emailFieldId":"3158","phoneFieldId":"9365"}}]},{"id":"6577","title":"Other Guests/Pets","fields":[{"id":"7709","title":"Additional Guests","type":"additional_guests","properties":{},"validations":{}},{"id":"7740","title":"Do you have any pets in your care?","type":"yes_no","properties":{},"validations":{"required":true}},{"id":"5056","title":"Pet Type","type":"pets","properties":{},"validations":{"required":false}}]},{"id":"1676","title":"Employment Information","fields":[{"id":"5478","title":"Are you currently employed?","type":"yes_no","properties":{},"validations":{"required":true}},{"id":"8533","title":"If yes, please describe your employment.","type":"long_text","properties":{},"validations":{"required_if":{"field_id":"5478","value":"yes"}}},{"id":"8487","title":"If no, are you currently looking for work? If so, what type?","type":"long_text","properties":{},"validations":{"required_if":{"field_id":"5478","value":"no"}}}]},{"id":"5996","title":"Education","fields":[{"id":"6478","title":"Are you enrolled in an Educational Program?","type":"yes_no","properties":{},"validations":{"required":true}},{"id":"7222","title":"If yes, please describe the program.","type":"long_text","properties":{},"validations":{"required_if":{"field_id":"6478","value":"yes"}}},{"id":"7661","title":"If no, are you hoping to enroll in an Educational Program? If so, what type?","type":"long_text","properties":{},"validations":{"required_if":{"field_id":"6478","value":"no"}}}]},{"id":"4480","title":"Language Proficiency","fields":[{"id":"5479","title":"Are you bilingual or multilingual?","type":"yes_no","properties":{},"validations":{"required":true}},{"id":"2359","title":"If yes, what languages do you speak?","type":"long_text","properties":{},"validations":{"required_if":{"field_id":"5479","value":"yes"}}}]},{"id":"5282","title":"Substance Use","fields":[{"id":"8229","title":"Do you smoke cigarettes?","type":"yes_no","properties":{},"validations":{"required":true}},{"id":"7844","title":"Do you drink alcohol?","type":"yes_no","properties":{},"validations":{"required":true}},{"id":"7494","title":"Do you use any other substances?","type":"yes_no","properties":{},"validations":{"required":true}}]},{"id":"3691","title":"Mental Health","fields":[{"id":"3831","title":"Do you suffer mental illness?","type":"yes_no","properties":{},"validations":{"required":true}}]},{"id":"7816","title":"Interest in Being a Guest","fields":[{"id":"1885","title":"Please share how you think participating in the Host Homes Program will help you obtain long-term housing and meet your educational and/or employment goals:","type":"long_text","properties":{},"validations":{"required":true}},{"id":"1185","title":"What kind of relationship do you hope to have with your host home?","type":"long_text","properties":{},"validations":{"required":true}},{"id":"3749","title":"Please describe any challenges you foresee encountering in participating in the Host Homes Program.","type":"long_text","properties":{},"validations":{"required":true}}]},{"id":"8837","title":"About You","fields":[{"id":"3411","title":"Please take some time to write an introduction of yourself that you would feel comfortable with the Host Homes Coordinator sharing with a potential host. Feel free to talk about your interests, your story or anything else that you think would be important to share:","type":"long_text","properties":{},"validations":{"required":true}}]}]}
+{
+ "id": "1",
+ "fieldGroups": [
+ {
+ "id": "1293",
+ "title": "Basic Information",
+ "fields": [
+ {
+ "id": "3922",
+ "title": "First Name",
+ "type": "short_text",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "5572",
+ "title": "Last Name",
+ "type": "short_text",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "2188",
+ "title": "Date of Birth",
+ "type": "date",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "3924",
+ "title": "Gender Identity",
+ "type": "dropdown",
+ "properties": {
+ "choices": [
+ {
+ "id": "1359",
+ "label": "Woman"
+ },
+ {
+ "id": "5342",
+ "label": "Man"
+ },
+ {
+ "id": "3151",
+ "label": "Questioning"
+ },
+ {
+ "id": "2178",
+ "label": "Transgender"
+ },
+ {
+ "id": "3667",
+ "label": "Non-binary"
+ },
+ {
+ "id": "3708",
+ "label": "Doesn’t know or prefers not to answer"
+ }
+ ]
+ },
+ "validations": {
+ "required": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "1348",
+ "title": "Contact Information",
+ "fields": [
+ {
+ "id": "7813",
+ "title": "Email",
+ "type": "email",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "2044",
+ "title": "Phone Number",
+ "type": "number",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "7504",
+ "title": "What is the best way to contact you?",
+ "type": "contact_method",
+ "properties": {},
+ "validations": {
+ "required": true
+ },
+ "linkedFields": {
+ "emailFieldId": "7813",
+ "phoneFieldId": "2044"
+ }
+ }
+ ]
+ },
+ {
+ "id": "6577",
+ "title": "Other Guests/Pets",
+ "fields": [
+ {
+ "id": "7709",
+ "title": "Additional Guests",
+ "type": "additional_guests",
+ "properties": {},
+ "validations": {}
+ },
+ {
+ "id": "7740",
+ "title": "Do you have any pets in your care?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "5056",
+ "title": "Pet Type",
+ "type": "pets",
+ "properties": {},
+ "validations": {
+ "required": false
+ }
+ }
+ ]
+ },
+ {
+ "id": "1676",
+ "title": "Employment Information",
+ "fields": [
+ {
+ "id": "5478",
+ "title": "Are you currently employed?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "8533",
+ "title": "If yes, please describe your employment.",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required_if": {
+ "field_id": "5478",
+ "value": "yes"
+ }
+ }
+ },
+ {
+ "id": "8487",
+ "title": "If no, are you currently looking for work? If so, what type?",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required_if": {
+ "field_id": "5478",
+ "value": "no"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "5996",
+ "title": "Education",
+ "fields": [
+ {
+ "id": "6478",
+ "title": "Are you enrolled in an Educational Program?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "7222",
+ "title": "If yes, please describe the program.",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required_if": {
+ "field_id": "6478",
+ "value": "yes"
+ }
+ }
+ },
+ {
+ "id": "7661",
+ "title": "If no, are you hoping to enroll in an Educational Program? If so, what type?",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required_if": {
+ "field_id": "6478",
+ "value": "no"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "4480",
+ "title": "Language Proficiency",
+ "fields": [
+ {
+ "id": "5479",
+ "title": "Are you bilingual or multilingual?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "2359",
+ "title": "If yes, what languages do you speak?",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required_if": {
+ "field_id": "5479",
+ "value": "yes"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "id": "5282",
+ "title": "Substance Use",
+ "fields": [
+ {
+ "id": "8229",
+ "title": "Do you smoke cigarettes?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "7844",
+ "title": "Do you drink alcohol?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "7494",
+ "title": "Do you use any other substances?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "3691",
+ "title": "Mental Health",
+ "fields": [
+ {
+ "id": "3831",
+ "title": "Do you suffer mental illness?",
+ "type": "yes_no",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "7816",
+ "title": "Interest in Being a Guest",
+ "fields": [
+ {
+ "id": "1885",
+ "title": "Please share how you think participating in the Host Homes Program will help you obtain long-term housing and meet your educational and/or employment goals:",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "1185",
+ "title": "What kind of relationship do you hope to have with your host home?",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ },
+ {
+ "id": "3749",
+ "title": "Please describe any challenges you foresee encountering in participating in the Host Homes Program.",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "8837",
+ "title": "About You",
+ "fields": [
+ {
+ "id": "3411",
+ "title": "Please take some time to write an introduction of yourself that you would feel comfortable with the Host Homes Coordinator sharing with a potential host. Feel free to talk about your interests, your story or anything else that you think would be important to share:",
+ "type": "long_text",
+ "properties": {},
+ "validations": {
+ "required": true
+ }
+ }
+ ]
+ }
+ ]
+ }
+
\ No newline at end of file
diff --git a/frontend/src/features/authentication/SignInForm.tsx b/frontend/src/features/authentication/SignInForm.tsx
index 0d968a245..30d8d08e4 100644
--- a/frontend/src/features/authentication/SignInForm.tsx
+++ b/frontend/src/features/authentication/SignInForm.tsx
@@ -26,9 +26,9 @@ const validationSchema = object({
});
export const SignInForm = ({
- onSubmit,
+ onSubmit,
isLoading,
- getTokenIsLoading
+ getTokenIsLoading,
}: SignInFormProps) => {
const {
handleSubmit,
diff --git a/frontend/src/features/authentication/SignUpForm.tsx b/frontend/src/features/authentication/SignUpForm.tsx
index cf435e5a1..3e8a18642 100644
--- a/frontend/src/features/authentication/SignUpForm.tsx
+++ b/frontend/src/features/authentication/SignUpForm.tsx
@@ -34,7 +34,7 @@ export const SignUpForm = ({
type,
getTokenIsLoading = false,
signUpHostIsLoading = false,
- signUpCoordinatorIsLoading = false
+ signUpCoordinatorIsLoading = false,
}: SignUpFormProps) => {
const isAnyLoading =
isLoading ||
diff --git a/frontend/src/features/intake-profile/IntakeProfileGroups.tsx b/frontend/src/features/intake-profile/IntakeProfileGroups.tsx
index 698385aef..9f10a71f4 100644
--- a/frontend/src/features/intake-profile/IntakeProfileGroups.tsx
+++ b/frontend/src/features/intake-profile/IntakeProfileGroups.tsx
@@ -49,3 +49,217 @@ export const FieldGroupList = () => {
);
};
+
+interface RenderFieldProps {
+ groupId: string;
+ field: Fields;
+}
+
+export const RenderFields = ({groupId, field}: RenderFieldProps) => {
+ const {errors, handleChange, setFieldValue, values} =
+ useFormikContext();
+ const groupValues = values[groupId];
+
+ const props = {
+ name: `${groupId}.${field.id}`,
+ value: groupValues[field.id],
+ onChange: handleChange,
+ };
+
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ const error = Boolean(errors[groupId]?.[field.id]);
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ const helperText = errors[groupId]?.[field.id];
+
+ switch (field.type) {
+ case 'additional_guests':
+ return (
+
+ );
+ case 'date':
+ return (
+ {
+ setFieldValue(name, value);
+ }}
+ />
+ );
+ case 'dropdown':
+ if (field.properties.choices === undefined)
+ throw new Error('Invalid field type');
+
+ return (
+
+
+ Select a choice
+
+ }
+ inputProps={{'aria-label': 'select-choice'}}
+ >
+
+ {field.properties.choices.map(choice => (
+
+ ))}
+
+ {helperText}
+
+ );
+ case 'email':
+ return (
+
+ );
+ case 'long_text':
+ return (
+
+ );
+ case 'number':
+ return (
+
+ );
+ case 'pets':
+ return (
+
+ );
+ case 'short_text':
+ return (
+
+ );
+ case 'contact_method':
+ // eslint-disable-next-line no-case-declarations
+ const {emailFieldId, phoneFieldId} = field.linkedFields || {};
+
+ // eslint-disable-next-line no-case-declarations
+ let isEmailFilled = false;
+ // eslint-disable-next-line no-case-declarations
+ let isPhoneFilled = false;
+ if (emailFieldId) {
+ const emailValue = values[groupId][emailFieldId];
+ isEmailFilled =
+ typeof emailValue === 'string' && /\S+@\S+\.\S+/.test(emailValue);
+ }
+ // eslint-disable-next-line no-case-declarations
+ if (phoneFieldId) {
+ const phoneValue = values[groupId][phoneFieldId];
+ isPhoneFilled =
+ typeof phoneValue === 'string' && phoneRegExp.test(phoneValue);
+ }
+
+ return (
+
+ {field.title}
+
+ }
+ label="Phone"
+ disabled={!isPhoneFilled}
+ />
+ }
+ label="Email"
+ disabled={!isEmailFilled}
+ />
+
+ {helperText}
+
+ );
+
+ case 'yes_no':
+ return (
+
+ {field.title}
+
+ } label="Yes" />
+ } label="No" />
+
+ {helperText}
+
+ );
+ default:
+ throw new Error('Invalid field type');
+ }
+};
diff --git a/frontend/src/features/intake-profile/helpers/createInitialValues.ts b/frontend/src/features/intake-profile/helpers/createInitialValues.ts
index f15c55997..01589cb04 100644
--- a/frontend/src/features/intake-profile/helpers/createInitialValues.ts
+++ b/frontend/src/features/intake-profile/helpers/createInitialValues.ts
@@ -2,7 +2,7 @@ import {InitialValues} from '../../../pages/intake-profile/IntakeProfile';
import {FieldGroup, FieldTypes, Response} from '../../../services/profile';
/**
- * Creates an object used for the initial Formik valiues
+ * Creates an object used for the initial Formik values
* It takes the form of:
* {
* fieldGroupId: {
@@ -26,6 +26,10 @@ const fieldDefaultValue = (fieldType: FieldTypes) => {
return '';
case 'yes_no':
return '';
+ case 'multiple_choice':
+ return [];
+ case 'contact_method':
+ return '';
case 'pets':
return [];
default:
@@ -34,16 +38,29 @@ const fieldDefaultValue = (fieldType: FieldTypes) => {
};
export const createInitialValues = (
- fieldGroups: FieldGroup[],
- responses: Response[],
+ fieldGroups: FieldGroup[] | undefined,
+ responses: Response[] | undefined,
): InitialValues => {
+ // Early return if fieldGroups is undefined or empty
+ if (!fieldGroups || fieldGroups.length === 0) {
+ return {};
+ }
+
+ // Use empty array if responses is undefined
+ const safeResponses = responses || [];
+
return fieldGroups.reduce((acc: InitialValues, fieldGroup) => {
- const fields = fieldGroup.fields.reduce((acc, field) => {
+ // Skip groups without fields
+ if (!fieldGroup.fields || fieldGroup.fields.length === 0) {
+ return acc;
+ }
+
+ const fields = fieldGroup.fields.reduce((fieldAcc, field) => {
return {
- ...acc,
+ ...fieldAcc,
[field.id]:
- responses.find(response => response.fieldId === field.id)?.value ||
- fieldDefaultValue(field.type),
+ safeResponses.find(response => response.fieldId === field.id)
+ ?.value || fieldDefaultValue(field.type),
};
}, {});
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 6f0eb0882..10f77c50d 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -46,7 +46,7 @@ import {
IntakeProfileSection,
} from './pages';
import {SystemAdminDashboard} from './pages/SystemAdminDashboard';
-import {enableMocking} from './utils/testing/browser';
+// import {enableMocking} from './utils/testing/browser';
import {useAppDispatch} from './redux/hooks/store';
import {setCredentials} from './redux/authSlice';
import NotFound from './pages/NotFound';
@@ -170,21 +170,21 @@ function HuuApp() {
const appRoot = document.getElementById('root') as HTMLElement;
-enableMocking().then(() => {
- ReactDOM.createRoot(appRoot).render(
-
-
-
-
-
-
-
-
-
-
-
-
-
- ,
- );
-});
+// enableMocking().then(() => {
+ReactDOM.createRoot(appRoot).render(
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+);
+// });
diff --git a/frontend/src/pages/authentication/SignUp.tsx b/frontend/src/pages/authentication/SignUp.tsx
index 260825d3b..ad58b8c20 100644
--- a/frontend/src/pages/authentication/SignUp.tsx
+++ b/frontend/src/pages/authentication/SignUp.tsx
@@ -18,25 +18,33 @@ import {
import {isErrorWithMessage, isFetchBaseQueryError} from '../../redux/helpers';
import {useAuthenticateWithOAuth} from '../../features/authentication/hooks/useAuthenticateWithOAuth';
import {FormContainer, SignUpForm} from '../../features/authentication';
-import { LocationState } from './SignIn';
+// Uncomment this import if it's in a separate file
+// import { LocationState } from './SignIn';
+
+// You might need to define LocationState if it's not imported
+interface LocationState {
+ from?: {
+ pathname: string;
+ };
+}
export const SignUp = () => {
const [errorMessage, setErrorMessage] = React.useState('');
const {type} = useParams();
const navigate = useNavigate();
+ const location = useLocation();
+
+ // Get location state information
+ const locationState = location.state as LocationState;
+
+ // Save location from which user was redirected to login page
+ const from = locationState?.from?.pathname || '/';
const [signUp, {isLoading: signUpIsLoading}] = useSignUpMutation();
const [googleSignUp, {isLoading: getTokenIsLoading}] =
useGoogleSignUpMutation();
- // const location = useLocation()
- // // get type from params
- // const locationState = location.state as LocationState;
-
- // // Save location from which user was redirected to login page
- // const from = locationState?.from?.pathname || '/';
-
const callbackUri = `/signup/${type}`;
useAuthenticateWithOAuth({
query: googleSignUp,
diff --git a/frontend/src/pages/guest-dashboard/GuestDashboard.tsx b/frontend/src/pages/guest-dashboard/GuestDashboard.tsx
index f7ae20d50..275a981b1 100644
--- a/frontend/src/pages/guest-dashboard/GuestDashboard.tsx
+++ b/frontend/src/pages/guest-dashboard/GuestDashboard.tsx
@@ -36,7 +36,8 @@ const tasks: Task[] = [
description:
'Start your guest application to move on to the next step.',
linkText: 'Start Application',
- url: 'profile/1',
+ // url: 'profile/1/group/1',
+ url: 'profile/guest',
},
{
id: 2,
diff --git a/frontend/src/pages/intake-profile/IntakeProfile.tsx b/frontend/src/pages/intake-profile/IntakeProfile.tsx
index e981abe8d..f91532861 100644
--- a/frontend/src/pages/intake-profile/IntakeProfile.tsx
+++ b/frontend/src/pages/intake-profile/IntakeProfile.tsx
@@ -1,6 +1,19 @@
+import React, {useState, useEffect} from 'react';
+import {
+ Button,
+ Stack,
+ useMediaQuery,
+ useTheme,
+ CircularProgress,
+ Typography,
+} from '@mui/material';
import {Container, Stack, useMediaQuery, useTheme} from '@mui/material';
import {Outlet, useLocation, useNavigate, useParams} from 'react-router-dom';
import {Formik} from 'formik';
+import {buildValidationSchema, createInitialValues} from './helpers';
+import {useGetProfileQuery, Response} from '../../services/profile';
+import {ProfileSidebar} from '../../features/intake-profile';
+
import {useState} from 'react';
import {createInitialValues} from '../../features/intake-profile/helpers';
import {
@@ -18,14 +31,93 @@ export type InitialValues = Record;
export const IntakeProfile = () => {
const theme = useTheme();
const navigate = useNavigate();
+ const toolbarHeight = Number(theme.mixins.toolbar.minHeight);
+ const params = useParams();
const {profileId, groupId} = useParams();
const location = useLocation();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [showSections, setShowSections] = useState(isMobile);
const [selectedItem, setSelectedItem] = useState(
- groupId || null,
+ params.groupId || null,
);
+ // Debug logging for all parameters
+ useEffect(() => {
+ console.log('FULL ROUTE PARAMETERS:', params);
+ console.log('CURRENT LOCATION:', location);
+ }, [params, location]);
+
+ // Determine profile type with extensive fallback
+ const profileType =
+ params.profileType || params.profileId?.split('-')[0] || 'guest';
+
+ // const userId =
+ // params.userId ||
+ // (params.profileId?.split('-')[1]) ||
+ // 'anonymous';
+
+ // Fetch profile data
+ const {
+ data: profileData,
+ isLoading: isProfileLoading,
+ error: profileError,
+ } = useGetProfileQuery({
+ profileId: profileType,
+ });
+
+ // Extensive error logging
+ useEffect(() => {
+ if (profileError) {
+ console.error('PROFILE FETCH ERROR:', profileError);
+ }
+ }, [profileError]);
+
+ // Loading state
+ if (isProfileLoading) {
+ return (
+
+
+
+ );
+ }
+
+ // Error handling
+ if (profileError) {
+ return (
+
+
+ Error loading profile. Please try again.
+
+ {JSON.stringify(profileError)}
+
+ );
+ }
+
+ // If no data after loading, return null or show error
+ if (!profileData) {
+ return (
+
+ No profile data found
+
+ );
+ }
+
+ const {form, responses = []} = profileData;
+ const fieldGroups = form?.fieldGroups || [];
+
+ // create validation schema for current group
+ const validationSchema =
+ params.groupId === undefined
+ ? {}
+ : buildValidationSchema(fieldGroups, params.groupId);
const {data: profileData} = useGetProfileQuery(
{profileId: profileId},
{skip: !profileId},
@@ -48,12 +140,15 @@ export const IntakeProfile = () => {
// groupId === undefined ? {} : buildValidationSchema(fieldGroups, groupId);
// create initial values from responses and fieldGroups
+ const initialValues = createInitialValues(fieldGroups, responses);
+
const initalValues = createInitialValues(fieldGroups, responses);
const currentIndex = fieldGroups.findIndex(
group => group.id === selectedItem,
);
+ // Navigation and section management functions
function toggleShowSections() {
setShowSections(!showSections);
}
@@ -63,7 +158,6 @@ export const IntakeProfile = () => {
const nextGroupId = fieldGroups[currentIndex + 1].id;
setSelectedItem(nextGroupId);
navigate(`group/${nextGroupId}`);
- //need to add autosave feature
}
}
@@ -72,33 +166,37 @@ export const IntakeProfile = () => {
const prevGroupId = fieldGroups[currentIndex - 1].id;
setSelectedItem(prevGroupId);
navigate(`group/${prevGroupId}`);
- //need to add autosave feature
}
}
const handleSubmitApplication = () => {
- // submit the application after review
+ // TODO: Implement application submission logic
+ console.log('Submitting application');
};
return (
{
- if (!groupId) {
+ if (!params.groupId) {
console.error('groupId is not defined in on submit');
return;
}
- const updateResponses = Object.entries(values[groupId]).map(
+ const updateResponses = Object.entries(values[params.groupId]).map(
([fieldId, value]) => {
const response = responses.find(
response => response.fieldId === fieldId,
);
if (response) {
- response.value = value;
- return response;
+ return {
+ ...response,
+ value,
+ };
} else {
return {
fieldId,
@@ -122,12 +220,77 @@ export const IntakeProfile = () => {
>
+
+
+
+
+
+
+ {location.pathname.includes('review') ? (
+
+ ) : (
+
+ )}
+
+
{
+// const id = req.params.profileId;
+// const profile = intakeProfiles.find(p => p.id === id);
+
+// if (profile) {
+// return HttpResponse.json(profile);
+// }
+
+// return new HttpResponse(null, {status: 404});
+// }),
+
+// http.get(
+// '/api/intake-profile/responses/:userId',
+// () => {
+// const fields = intakeProfiles[0].fieldGroups
+// .map(fieldGroup => fieldGroup.fields)
+// .flat();
+
+// const responses = fields.map(field => {
+// const value = getResponseValue(field);
+// return {
+// fieldId: field.id,
+// value,
+// };
+// });
+
+// // return a list of filled in responses
+// return HttpResponse.json({responses});
+// // return an empty list of responses
+// // return HttpResponse.json({responses: []});
+// },
+// ),
+// ];
+
export const handlers = [
http.post('/api/profile/responses', async ({request}) => {
const responses = await request.json();