Skip to content
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

[M2-4833] Feature: Additional text response #356

Merged
merged 19 commits into from
Feb 2, 2024

Conversation

sultanofcardio
Copy link
Contributor

@sultanofcardio sultanofcardio commented Jan 30, 2024

resolves: M2-4833

Objective

This PR adds an additional text response field in the respondent web app, for those items that support it:

  • Single Selection
  • Multiple Selection
  • Slider
  • Date
  • Time
  • Time Range
  • Audio Player

Note

There are some items missing, and some present that are not even supported in the web app yet. I just added support based on the items that are represented in the codebase.

The PR covers these criteria:

  • When an item type that allows an additional text response is presented to the respondent, clearly display a text input field where they can enter their response.
  • Require the user to provide an additional text response when the item type is configured to require it. Respondents will not be able to move to the next Item without entering a text response if it is configured to require it.
  • Additional text responses are properly collected, stored, and associated with the corresponding item type response in the system.

Warning

It's not currently possible to prevent a submission with missing additional text for items that require it. The API will need to be updated to facilitate this.

Notes

There are a number of scenarios that need to be tested to verify this change

  • The additional text field should appear for items with additional text
  • The additional text field shouldn't appear for items without additional text
  • Auto-advance shouldn't work when additional text field is present
  • The next button should be blocked when additional text is required, and the user hasn't provided any
  • The additional text field should be repopulated when the user resumes an activity
  • The provided additional text should be submitted to the backend

Most of this is easy enough to validate by creating an applet in the admin app, configuring it with additional text, and then verifying the behaviour in the web app on this branch. However, I think it bears mentioning that in order to confirm that the submission includes the additional text, you'll need to do an applet data export from the admin side (since the answers are encrypted on the backend, and the admin app currently doesn't display any additional text with the answers in the UI).

You may perform this data export by selecting the applet in the dashboard, identifying the respondent, and selecting the export data option

Screen.Recording.2024-01-30.at.3.19.41.PM.mov

This will download a report.csv file containing all the answers from that respondent. In the response field, answers containing additional text will be formatted like this

value: 2 | text: Some text

Where 2 is the answer to the item and Some text is the additional text.

Follow up tasks

N/A

@sultanofcardio sultanofcardio self-assigned this Jan 30, 2024
@sultanofcardio sultanofcardio marked this pull request as draft January 30, 2024 05:38
@moiskillnadne moiskillnadne added Feature New feature or request In progress Work in progress labels Jan 30, 2024
@sultanofcardio sultanofcardio marked this pull request as ready for review January 30, 2024 17:19
Copy link
Contributor

@mbanting mbanting left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw on Slack that this was looking for reviewers but title says WIP. Went ahead and added a few minor comments anyways. Overall it looks great and thank you for the amazing and helpful PR description!

@sultanofcardio
Copy link
Contributor Author

Thanks for taking a look @mbanting. I forgot to remove the WIP from the title.

I'll address your other comments in the morning. Regarding the TODO, I was supposed to enquire about the user events today to see if it's something I could add here or if it needs support in the API first. I'll ask the question in Slack to see what needs to happen

@sultanofcardio sultanofcardio changed the title [M2-4833] Feature: Additional text response [WIP] [M2-4833] Feature: Additional text response Jan 31, 2024
The CardItem component is already configured with a conditional label so the one I'm passing is redundant. Highlighting optionality is also more in line with the existing pattern
@@ -13,7 +13,7 @@ import {
TimeRangeItem,
} from "~/entities/activity/lib"

export type UserEventTypes = "SET_ANSWER" | "PREV" | "NEXT" | "SKIP" | "DONE"
export type UserEventTypes = "SET_ANSWER" | "SET_ADDITIONAL_TEXT" | "PREV" | "NEXT" | "SKIP" | "DONE"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create a new type. Reuse "SET_ANSWER"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here a bit different logic is expected. Basically, additional text changes are not a new event. It extends current events. Let me show some examples:

We have the singleSelect with the additional text.

User choose answer first
The user event will looks like
{
type: "SET_ANSWER",
screen: activityItemScreenId,
time: Date.now(),
response: item.answer[0],
}

Then, the user fills in additional text, and user event will be updated
{
type: "SET_ANSWER",
screen: activityItemScreenId,
time: Date.now(),
response: item.answer[0],
additionalAnswer: "asd"
}

Take into account please that the additional answer has "like text" behavior. So it updates the same event while the user fills in additional text.

And also handle the case when user fills in additional text first and then choose the answer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any specific reason we want to reuse the SET_ANSWER event instead of creating a new one? I briefly contemplated doing that, but I didn't have a reason to lean either way and I'd like to understand the reasoning

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sure. The main reason is the parity between the web application and the mobile application. The mobile app already has the same functionality and has agreements with the admin panel on what event types and DTOs are used for communication.

So I have discussed with @BamMironov how it was implemented in the mobile app and I think we should implement the feature the same way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense. Thanks for explaining. I'll take another look at the mobile app for inspiration

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the change but with a slight caveat. The Mobile App stores its user events with the response portion in this shape:

export type PipelineItemAnswerBase = {
  type: ActivityItemType;
  value: {
    answer?: PipelineItemResponse;
    additionalAnswer?: string;
  };
};

Where PipelineItemResponse is one of many types corresponding to the item answers. The web app stores it like this:

export type UserEvents = {
  type: UserEventTypes
  time: number
  screen: string
  response?: UserEventResponse
}

export type UserEventResponse =
  | string
  | {
      value: number[]
      text?: string
    }

Here UserEventResponse is supposed to be the equivalent of PipelineItemResponse but it is much less fleshed out. I didn't want to turn this PR into bringing the two types in line with each other, so I made a small update to support additional text on more types besides singleSelect and multiSelect items.

export type UserEventResponse =
  | string
  | {
      value: string | string[] | number[]
      text?: string
    }

This is to avoid storing the additional text by itself for those items (like slider), because it would look like the user cleared the answer to set the additional text. Let me know if this is a suitable compromise until a wider change can be made

Copy link
Contributor

@farmerpaul farmerpaul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR @sultanofcardio, love the clear commit-by-commit breakdown. Tested the flow with various activity/item configurations and it works as expected. I just had a couple very minor code-related questions.

Copy link
Contributor

@moiskillnadne moiskillnadne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thanks

@moiskillnadne moiskillnadne merged commit 4879c3f into dev2.5 Feb 2, 2024
2 checks passed
@moiskillnadne moiskillnadne deleted the feature-2.5/M2-4833 branch February 23, 2024 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New feature or request In progress Work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants