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

[#4504] Add step-wise partner profile edit form #4766

Merged
merged 7 commits into from
Nov 17, 2024

Conversation

danielabar
Copy link
Collaborator

@danielabar danielabar commented Nov 3, 2024

Resolves #4504

Description

This splits up the very long partner profile edit form into multiple collapsible sections, using the Bootstrap Accordion component. This project already uses Bootstrap so this does not introduce a new dependency.

The first attempt had a "Save and Next" button on each collapsible section, i.e. each one was a separate form that could be submitted individually. The original idea was to save one section and have the user returned to the edit view with the next section automatically opened for a kind of "guided" experience. It was also automatically saving work in progress edits as user collapsed/expanded sections. But this proved to be unworkable for a number of reasons. See #4504 comments for details.

So this solution simplifies things by only having a single form outside of the accordion section. User can click Save Progress which submits the entire form, or Submit for Approval. These action buttons appear both at the top and bottom of the accordion. If any validation errors occur, the user is returned to the edit view with all the sections that have error(s) expanded.

Type of change

New feature (non-breaking change which adds functionality)

How Has This Been Tested?

To exercise this feature, start by launching a Rails console bin/rails c and enable the new feature flag:

Flipper.enable(:partner_step_form)

Then with the Rails server running bin/start, navigate to http://localhost:3000, login as an Org admin, create a new Partner Agency (eg: [email protected]) and invite them. Then logout.

A new browser tab should open with the invitation email sent to the new partner. Click on the accept invitation button in this email, and create a password for the new partner. Once logged in, click "Edit my Profile" from the left navigation. You should now see the new step-wise form.

Expand and collapse as many sections as you'd like, and click Save Progress as often as you'd like.

Notice that the bootstrap open/close caret icons change to the word "Saving..." when the form is being submitted. This is to prevent the user from opening further sections and trying to edit in the middle of an update.

To see what the form looks like with multiple validation errors, fill it out as follows, then click Save Progress.

  1. Media Information: Make all the social media links blank, and uncheck "No social media"
  2. Pick up Person: Enter 4 comma separated emails for pick up email, eg: [email protected], [email protected], [email protected], [email protected]
  3. Area served: Add some area(s) that don't add up to 100%
  4. Partner Settings: Uncheck all the request settings

If the form renders with validation errors, then the Submit for Approval button should be disabled. Otherwise if partner is in Invited or Recertification status, then the Approval button is enabled. Otherwise the Approval button doesn't appear at all.

Also see system test for automated test: spec/system/partners/profile_edit_system_spec.rb

Optionally, disable the feature flag in Rails console Flipper.disable(:partner_step_form) and run through the invitation flow again (or use the same partner you already created). In this case, it should behave exactly as it does currently with the very large form.

Screenshots

Default rendering of step-wise edit form - all sections closed:
image

Multiple sections can be open/closed at once for editing:
image

Multiple sections open on validation errors:
image

- Introduce feature flag to control rendering of new partner step forms.
- Update controller edit and update actions to use feature flag.
- Add accordion UI for expand/collapse of sections in step forms.
- Build out partner step forms for each section.
- Update partner profile validation to set errors at field level.
- Add service to map profile field errors to sections they belong in.
- Update accordion styles to render as open if they contain an error.
- Replace bootstrap caret icons with saving indicator during form submission.
- Add system tests for partner profile step-wise editing.
@cielf cielf requested review from cielf and dorner November 4, 2024 01:27
Copy link
Collaborator

@dorner dorner left a comment

Choose a reason for hiding this comment

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

Looks amazing! Please see a few questions/suggestions.

# sections_with_errors = service.call
# # => ["media_information", "pick_up_person", "partner_settings"]
#
class SectionErrorService
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this basically an overgrown hash lookup? I feel like this can be squished into a single class-level method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated with class method.

Comment on lines 35 to 53
<div class="accordion mx-5 mt-3" id="accordionExample">

<%# Agency Information %>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button <%= section_with_errors?('agency_information', @sections_with_errors) ? '' : 'collapsed' %>" type="button" data-bs-toggle="collapse" data-bs-target="#agency_information" aria-expanded="<%= section_with_errors?('agency_information', @sections_with_errors) %>" aria-controls="agency_information">
<div class='d-flex justify-content-between align-items-center w-100'>
<div class='fs-4'>
<i class="fa fa-edit mr-2"></i>Agency Information
</div>
</div>
</button>
</h2>
<div id="agency_information" class="accordion-collapse collapse <%= section_with_errors?('agency_information', @sections_with_errors) ? 'show' : '' %>">
<div class="accordion-body">
<%= render 'partners/profiles/step/agency_information_form', f: f, partner: current_partner, profile: current_partner.profile %>
</div>
</div>
</div>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this be extracted into a partial with relevant locals (name, partial ID etc.)? Partial renders can take blocks, so if you need specific HTML within the big skeleton you can do that if necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Extracted to partial.

end

it "sets error at field level when feature flag enabled for partner step form" do
allow(Flipper).to receive(:enabled?).with("partner_step_form").and_return(true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add the and_return(false) to the previous method? It's always best to call out explicitly when you depend on a value to be true/false rather than relying on defaults.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

@cielf
Copy link
Collaborator

cielf commented Nov 7, 2024

It's pretty nice! I need to think a bit about two things regarding a smooth passage through the process for the partners
1/ we might want to only check the social media when submitting for approval.
2/ It feels a bit off to make them save progress again when they have an error before they can try submitting for approval -- I know that in the current version, they have to have a clean save before submitting, but this is a different world.

@danielabar
Copy link
Collaborator Author

@dorner Technical feedback has been addressed.

@cielf Let me know if it should have further changes to the workflow.

@cielf
Copy link
Collaborator

cielf commented Nov 9, 2024

Hey @danielabar -- I would like to make the change so that we don't disable the submit for approval button on error.
I also see, though, that on submit for approval with error, it's going to the view rather than returning to the edit -- it makes sense to me for it to return to the edit if there is an error.

I'm still pondering whether we should only check the social media when submitting for approval -- I don't like that you can't save current progress without doing it, but there would be a side effect of essentially not checking that section properly as it goes through -- and I haven't landed on which is less desirable.

@@ -0,0 +1,24 @@
<%# locals: (f:, partner:, section_id:, section_title:, icon_class:, partial_name:, sections_with_errors:) %>
Copy link
Collaborator

Choose a reason for hiding this comment

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

🎉

@dorner
Copy link
Collaborator

dorner commented Nov 9, 2024

Tech review passed. @cielf are there functional changes that have to happen?

Copy link
Collaborator

@cielf cielf left a comment

Choose a reason for hiding this comment

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

  • I would like to make the change so that we don't disable the submit for approval button on error.
    I also see, though, that on submit for approval with error, it's going to the view rather than returning to the edit -- it makes sense to me for it to return to the edit if there is an error.

@danielabar
Copy link
Collaborator Author

@cielf To date, I've only made the technical changes on this PR, not any workflow changes, since earlier comment mentioned it requires further thought.

The reason the Submit for Approval button is disabled on error state is from an earlier discussion on the issue. Don't recall which comment it was, but the idea was user shouldn't be able to Submit for Approval while form is in error. I could remove this logic if its no longer needed.

When form initially loads, it is considered valid because the social media validation runs only on edit, and the profile is created in an invalid state. That's why you can initially Submit for Approval, even though its guaranteed to generate the social media error.

Yes I see now that it goes to view rather than edit in this case, I'll look into that.

To confirm, I'll make the following changes:

  1. Submit for Approval button is always enabled
  2. Submit for Approval + error -> return to edit

@cielf
Copy link
Collaborator

cielf commented Nov 10, 2024

That sounds right, with the caveat that on most forms we disable the save /submit buttons during the round-trip to the server so that we don't get double-clicks.

@danielabar
Copy link
Collaborator Author

danielabar commented Nov 10, 2024

@cielf Forgot to mention - for Submit for Approval, there is additional logic to only display it if parter is Invited or Recertification Required, otherwise it doesn't render at all. Is that still needed or should the button be displayed at all times?

Also fyi for this part "Submit for Approval + error -> return to edit", could be surprisingly thorny, requires modifications to the existing Approval Service which currently only seems to validate edit rules (i.e. only social media validation). I tried to change it (but should only change if step feature enabled), but strange things are happening. I still have to sort through it all but it will take more time.

I think the issue is the Approval Service wasn't intended as a full update service, but now as of this feature, it needs to do two things: Update the profile, and then request approval.

@danielabar
Copy link
Collaborator Author

An overall thought/possible future issue (don't want to scope creep this one):

It seems like the complexity around social media validation and profile being invalid from the start is due to the existing PartnerCreateService which creates the profile in an invalid state:

Partners::Profile.create!({
                                    partner_id: @partner.id,
                                    name: @partner.name,
                                    enable_child_based_requests: organization.enable_child_based_requests,
                                    enable_individual_requests: organization.enable_individual_requests,
                                    enable_quantity_based_requests: organization.enable_quantity_based_requests
                                  })

If the above was modified to add:

no_social_media_presence: true

Then the profile would be valid right from the start, and it wouldn't require validate :check_social_media, on: :edit in the Profile model (could get rid of the on: :edit and other places like in Approval service where it needs to maintain awareness of edit state.

If the intention of creating a profile invalid from the beginning was to remind the user that they should fill in at least one social media field, perhaps this could be handled as a gentle reminder with some JS in the edit form, rather than creating a validation error right from the start.

@cielf
Copy link
Collaborator

cielf commented Nov 10, 2024

"Then the profile would be valid right from the start, and it wouldn't require validate :check_social_media, on: :edit in the Profile model (could get rid of the on: :edit and other places like in Approval service where it needs to maintain awareness of edit state."

We'd still need it because they could uncheck it, or am I missing something?

I expect, though, that the intent was to make the partner think about it. Defaulting to none makes it easy to skip. Let me see if I can find the original ask. (or at least the issue for it)

@danielabar
Copy link
Collaborator Author

danielabar commented Nov 11, 2024

@cielf To clarify about the Submit for Approval. Currently it only validates the current state of the profile, but it's not a form submission, so if user made any other edits, they're not considered.

Should it also be doing a save in case user made further edits, then clicked the Submit for Approval without first clicking Save Progress?

i.e. it's really Save + Submit for Approval?

Note that combining these two separate services (Update Profile, Submit for Approval) is complicated so it would be good to confirm that's really the requirement.

@dorner Do you know if there's an example of a service that calls other services? I'm thinking of something like Organizer in the interactor gem, but for the custom service mixin that the existing services use.

@cielf
Copy link
Collaborator

cielf commented Nov 11, 2024

(Thinking) We have two ways we could go here. 1/ Save and submit for approval. 2/ Save and Review - taking them to the view screen, where they would then submit for approval (matching the current flow).

Let me check in with one of our banks -- I believe we have a certain amount of partners not actually submitting for approval with the current model, but I want to confirm that.

(Edit. The bank I was going to consult with doesn't have time today. Maybe tomorrow.)

@cielf
Copy link
Collaborator

cielf commented Nov 12, 2024

@danielabar Ok -- I've had a conversation with one of our main banks -- here's what we settled on:
1/ Change "Submit for Approval" to a "Save and Review", which will go to the Partner's profile view.
2/ On the partner profile view, twin the "Update Information" and "Submit for Approval" buttons to also be at the top

That gives the partners a final change to confirm that everything is right before submitting, but should also mitigate the problem of partners not actually submitting for approval. I can break out step 2 to be a separate issue if you want to keep it tight.

Is that workable?

@danielabar
Copy link
Collaborator Author

@cielf I've changed:

  1. "Submit for Approval" to "Save and Review". This new button performs the same logic as "Save Progress", but if the save is successful, then it redirects user to Profile view. I'm using the same flash success message "Details were successfully updated." for this case, let me know if it should be a different message?
  2. Removed the conditional logic around displaying "Save and Review". Before it was only rendering for Invited and Recertification, but now that it simply saves and redirects user, I figure it can be run from any status, let me know if that's not the case?
  3. Just like a previous discussion, the "Save and Review" button remains enabled, even when form is in an error state. User can always click it and if there are errors, they're taken back to edit view with error sections opened (just as if they had clicked "Save Progress"
  4. Realized the Bootstrap primary color wasn't being applied to the "Save Progress" button, that's been fixed so it now looks like this:
    image

I can look into the second part of the new requirement re: buttons at top and bottom of Partner Profile view.

@danielabar
Copy link
Collaborator Author

Observation, not sure if this is a bug or expected:

I can only save one Additional Documents attachment. i.e. if I return to the edit view after adding a document and add another one, the new one replaces the old one.

It seems to behave this way even with the feature flag disabled:

Flipper.enable(:partner_step_form)
# OR
Flipper.disable(:partner_step_form)

The Partner::Profile model does in theory support multiple attached documents:

has_many_attached :documents

But when I attach a new one, there's an ActiveStorage::PurgeJob running that removes the old one.

There may be an additional related bug that for the very first attachment, it doesn't get saved because the PurgeJob also runs. May require further investigation.

@cielf
Copy link
Collaborator

cielf commented Nov 13, 2024

@danielabar I know there are some problems around Additional Documents in the current incarnation. it's out of scope for this PR. I'll see if I can find the appropriate issue -- the PurgeJob information may be new to it.

Oh -- you can add two documents at once -- just not add one, then add another one, but it's deleting the files if you edit again. Yup, that's no good.

Confirmed - we have a "proto issue" for this (i.e. its in our backlog to be written up), i'll throw the info about PurgeJob into it. I think we might need a little design thought to make this right.

@cielf
Copy link
Collaborator

cielf commented Nov 13, 2024

@danielabar FYI, I'm probably not reviewing this today (severely underslept, and it needs a better brain than I have atm)

@cielf
Copy link
Collaborator

cielf commented Nov 15, 2024

I'm pretty happy with this. Do I think we might want a tweak or two once we see how it flies? Yes. Do I think it's good to go public? Also yes -- though we'll probably hold off enabling it until we have the equivalent page for the bank.
@dorner Can you give a last tech look-over, since there have been a few additional changes, please?

Copy link
Collaborator

@dorner dorner 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!

@dorner
Copy link
Collaborator

dorner commented Nov 16, 2024

@cielf is this good to merge?

@cielf
Copy link
Collaborator

cielf commented Nov 17, 2024

@dorner Let me take a quick pass through with the flags off to make sure nothing broke.

@cielf cielf merged commit 11541b1 into rubyforgood:main Nov 17, 2024
12 checks passed
Copy link
Contributor

@danielabar: Your PR [#4504] Add step-wise partner profile edit form is part of today's Human Essentials production release: 2024.11.17.
Thank you very much for your contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Convert partner profile to step-wise form
3 participants