From ff3e585dfe90098b40f42d7e79afaaed2b0bf260 Mon Sep 17 00:00:00 2001 From: leigh-mil Date: Mon, 23 Dec 2019 16:15:10 -0500 Subject: [PATCH 01/16] Initial formatting and styling of portfolio managers table. Deleted unused css --- atst/routes/portfolios/admin.py | 78 +++++----- styles/components/_portfolio_layout.scss | 95 +----------- templates/portfolios/admin.html | 2 +- .../fragments/portfolio_members.html | 136 ++++++------------ translations.yaml | 13 ++ 5 files changed, 91 insertions(+), 233 deletions(-) diff --git a/atst/routes/portfolios/admin.py b/atst/routes/portfolios/admin.py index 1b4ac711..14ba4b65 100644 --- a/atst/routes/portfolios/admin.py +++ b/atst/routes/portfolios/admin.py @@ -17,63 +17,51 @@ from atst.utils.flash import formatted_flash as flash from atst.domain.exceptions import UnauthorizedError -def permission_str(member, edit_perm_set, view_perm_set): - if member.has_permission_set(edit_perm_set): - return edit_perm_set - else: - return view_perm_set - - -def serialize_member_form_data(member): - return { - "member_name": member.full_name, - "member_id": member.id, - "perms_app_mgmt": permission_str( - member, - PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT, - PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT, +def filter_perm_sets_data(member): + perm_sets_data = { + "perms_portfolio_mgmt": bool( + member.has_permission_set(PermissionSets.EDIT_PORTFOLIO_ADMIN) ), - "perms_funding": permission_str( - member, - PermissionSets.EDIT_PORTFOLIO_FUNDING, - PermissionSets.VIEW_PORTFOLIO_FUNDING, + "perms_app_mgmt": bool( + member.has_permission_set( + PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT + ) ), - "perms_reporting": permission_str( - member, - PermissionSets.EDIT_PORTFOLIO_REPORTS, - PermissionSets.VIEW_PORTFOLIO_REPORTS, + "perms_funding": bool( + member.has_permission_set(PermissionSets.EDIT_PORTFOLIO_FUNDING) ), - "perms_portfolio_mgmt": permission_str( - member, - PermissionSets.EDIT_PORTFOLIO_ADMIN, - PermissionSets.VIEW_PORTFOLIO_ADMIN, + "perms_reporting": bool( + member.has_permission_set(PermissionSets.EDIT_PORTFOLIO_REPORTS) ), } + return perm_sets_data -def get_members_data(portfolio): - members = sorted( - [serialize_member_form_data(member) for member in portfolio.members], - key=lambda member: member["member_name"], - ) - for member in members: - if member["member_id"] == portfolio.owner_role.id: - ppoc = member - members.remove(member) - members.insert(0, ppoc) - return members + +def filter_members_data(members_list): + members_data = [] + for member in members_list: + members_data.append( + { + "role_id": member.id, + "user_name": member.user_name, + "permission_sets": filter_perm_sets_data(member), + # add in stuff here for forms + } + ) + + return sorted(members_data, key=lambda member: member["user_name"]) def render_admin_page(portfolio, form=None): pagination_opts = Paginator.get_pagination_opts(http_request) audit_events = AuditLog.get_portfolio_events(portfolio, pagination_opts) - members_data = get_members_data(portfolio) portfolio_form = PortfolioForm(obj=portfolio) - member_perms_form = member_forms.MembersPermissionsForm( - data={"members_permissions": members_data} - ) - + ppoc = filter_members_data([portfolio.owner_role])[0] + member_list = portfolio.members + member_list.remove(portfolio.owner_role) assign_ppoc_form = member_forms.AssignPPOCForm() + for pf_role in portfolio.roles: if pf_role.user != portfolio.owner and pf_role.is_active: assign_ppoc_form.role_id.choices += [(pf_role.id, pf_role.full_name)] @@ -87,13 +75,13 @@ def render_admin_page(portfolio, form=None): "portfolios/admin.html", form=form, portfolio_form=portfolio_form, - member_perms_form=member_perms_form, + ppoc=ppoc, + members=filter_members_data(member_list), member_form=member_forms.NewForm(), assign_ppoc_form=assign_ppoc_form, portfolio=portfolio, audit_events=audit_events, user=g.current_user, - ppoc_id=members_data[0].get("member_id"), current_member_id=current_member_id, applications_count=len(portfolio.applications), ) diff --git a/styles/components/_portfolio_layout.scss b/styles/components/_portfolio_layout.scss index d7583b80..fffc468f 100644 --- a/styles/components/_portfolio_layout.scss +++ b/styles/components/_portfolio_layout.scss @@ -5,13 +5,6 @@ } margin-left: 2 * $gap; - - .line { - box-sizing: border-box; - height: 2px; - width: 100%; - border: 1px solid $color-gray-lightest; - } } .portfolio-header { @@ -40,36 +33,6 @@ } } - &__budget { - font-size: $small-font-size; - align-items: center; - - .icon-tooltip { - margin-left: -$gap / 2; - } - - button { - margin: 0; - padding: 0; - } - - &--dollars { - font-size: $h2-font-size; - font-weight: bold; - } - - &--amount { - white-space: nowrap; - } - - &--cents { - font-size: 2rem; - margin-top: 0.75rem; - margin-left: -0.7rem; - font-weight: bold; - } - } - .links { justify-content: center; font-size: $small-font-size; @@ -109,22 +72,6 @@ } } } - - .column-left { - width: 12.5rem; - float: left; - } - - .column-right { - margin-left: -0.4rem; - } - - .unfunded { - color: $color-red; - .icon { - @include icon-color($color-red); - } - } } @mixin subheading { @@ -138,6 +85,10 @@ .portfolio-content { margin: (4 * $gap) $gap 0 $gap; + .panel { + padding-bottom: 2rem; + } + a.add-new-button { display: inherit; margin-left: auto; @@ -157,44 +108,6 @@ } } - input.usa-button.usa-button-primary { - width: 9rem; - height: 4rem; - } - - select { - padding-left: 1.2rem; - } - - .members-table-ppoc { - select::-ms-expand { - display: none; - color: $color-gray; - } - - select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - display: block; - width: 100%; - float: right; - margin: 5px 0px; - padding: 0px 24px; - background-image: none; - -ms-word-break: normal; - word-break: normal; - padding-right: 3rem; - padding-left: 1.2rem; - color: $color-gray; - } - - select:hover { - box-shadow: none; - color: $color-gray; - } - } - a.modal-link.icon-link { float: right; diff --git a/templates/portfolios/admin.html b/templates/portfolios/admin.html index 75d31873..95278f86 100644 --- a/templates/portfolios/admin.html +++ b/templates/portfolios/admin.html @@ -9,7 +9,7 @@ {{ StickyCTA(text="Settings") }} -
+
diff --git a/templates/portfolios/fragments/portfolio_members.html b/templates/portfolios/fragments/portfolio_members.html index 4c29b509..074f3b9e 100644 --- a/templates/portfolios/fragments/portfolio_members.html +++ b/templates/portfolios/fragments/portfolio_members.html @@ -3,103 +3,47 @@ {% from "components/modal.html" import Modal %} {% from "components/alert.html" import Alert %} -
-
- {% if g.matchesPath("portfolio-members") %} - {% include "fragments/flash.html" %} - {% endif %} - -
- {{ member_perms_form.csrf_token }} +

Portfolio Managers

+
+
+
+ + + + + + + + + + + + + {% for member in members -%} + + + + + {%- endfor %} + +
NamePortfolio Permissions
{{ ppoc.user_name }} + {% for perm, value in ppoc.permission_sets.items() -%} +
+ {{ ("portfolios.admin.members.{}.{}".format(perm, value)) | translate }} +
+ {%-endfor %} +
{{ member.user_name }} + {% for perm, value in member.permission_sets.items() -%} +
+ {{ ("portfolios.admin.members.{}.{}".format(perm, value)) | translate }} +
+ {%-endfor %} +
+
+
-
-
-
-
-
{{ "portfolios.admin.portfolio_members_title" | translate }}
-
- {{ "portfolios.admin.portfolio_members_subheading" | translate }} -
-
- - - {{ Icon('info') }} - {{ "portfolios.admin.settings_info" | translate }} - -
-
- - {% if not portfolio.members %} -

{{ "portfolios.admin.no_members" | translate }}

- {% else %} - - - - - - - - - - - - - - - {% if user_can(permissions.EDIT_PORTFOLIO_USERS) %} - {% include "portfolios/fragments/members_edit.html" %} - {% elif user_can(permissions.VIEW_PORTFOLIO_USERS) %} - {% include "portfolios/fragments/members_view.html" %} - {% endif %} - - -
{{ "portfolios.members.permissions.name" | translate }}{{ "portfolios.members.permissions.app_mgmt" | translate }}{{ "portfolios.members.permissions.funding" | translate }}{{ "portfolios.members.permissions.reporting" | translate }}{{ "portfolios.members.permissions.portfolio_mgmt" | translate }}
-
- {% endif %} - - - - - {% if user_can(permissions.EDIT_PORTFOLIO_USERS) %} - {% for subform in member_perms_form.members_permissions %} - {% set modal_id = "portfolio_id_{}_user_id_{}".format(portfolio.id, subform.member_id.data) %} - {% call Modal(name=modal_id, dismissable=False) %} -

{{ "portfolios.admin.alert_header" | translate }}

-
- {{ - Alert( - title="portfolios.admin.alert_title" | translate, - message="portfolios.admin.alert_message" | translate, - level="warning" - ) - }} -
-
- {{ member_perms_form.csrf_token }} - -
- {{ "common.cancel" | translate }} -
- {% endcall %} - {% endfor %} - {% endif %} -
{% if user_can(permissions.CREATE_PORTFOLIO_USERS) %} + Add Portfolio Manager {% include "portfolios/fragments/add_new_portfolio_member.html" %} {% endif %} -
+
diff --git a/translations.yaml b/translations.yaml index 5f00a464..925cc07e 100644 --- a/translations.yaml +++ b/translations.yaml @@ -317,6 +317,19 @@ portfolios: portfolio_members_title: Portfolio members settings_info: Learn more about these settings portfolio_name: Portfolio name + members: + perms_portfolio_mgmt: + 'False': View Portfolio + 'True': Edit Portfolio + perms_app_mgmt: + 'False': View Applications + 'True': Edit Applications + perms_funding: + 'False': View Funding + 'True': Edit Funding + perms_reporting: + 'False': View Reporting + 'True': Edit Reporting applications: add_application_text: Add a new application add_environment: Create an Environment From 6e50a8cc1f74de03a9bdcf60e0eaf53b76a4fcf6 Mon Sep 17 00:00:00 2001 From: leigh-mil Date: Thu, 26 Dec 2019 13:01:34 -0500 Subject: [PATCH 02/16] Generalize macro for adding new member to an application or portfolio --- .../fragments/new_member_modal_content.html | 21 +------ .../components/member_form_template.html | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 templates/components/member_form_template.html diff --git a/templates/applications/fragments/new_member_modal_content.html b/templates/applications/fragments/new_member_modal_content.html index 14bb00a2..c7b5e3f1 100644 --- a/templates/applications/fragments/new_member_modal_content.html +++ b/templates/applications/fragments/new_member_modal_content.html @@ -1,25 +1,7 @@ {% from "components/icon.html" import Icon %} +{% from "components/member_form_template.html" import MemberFormTemplate %} {% import "applications/fragments/member_form_fields.html" as member_fields %} -{% macro MemberFormTemplate(title=None, next_button=None, previous=True) %} -
- {% if title %}

{{ title }}

{% endif %} - - {{ caller() }} - -
- {{ next_button }} - {% if previous %} - - {% endif %} - {{ "common.cancel" | translate }} -
-{% endmacro %} - {% macro MemberStepOne(member_form) %} {% set next_button %} + {% if title %}

{{ title }}

{% endif %} + + {{ caller() }} + +
+ {{ next_button }} + {% if previous %} + + {% endif %} + {{ "common.cancel" | translate }} +
+{% endmacro %} + +{% macro BasicStep( + title=None, + form=form, + next_button_text=next_button_text, + previous=True, + modal=modal +) %} + {% set next_button %} + + {% endset %} + + {% call MemberFormTemplate(title=title, next_button=next_button, previous=previous, modal=modal) %} + {{ form }} + {% endcall %} +{% endmacro %} + +{% macro SubmitStep( + title=None, + form=form, + submit_text=submit_text, + previous=True, + modal=modal +) %} + {% set next_button %} + + {% endset %} + + {% call MemberFormTemplate(title=title, next_button=next_button, previous=previous, modal=modal) %} + {{ form }} + {% endcall %} +{% endmacro %} From 4d2a175136b826d5220f4ddaf9636e8728fed200 Mon Sep 17 00:00:00 2001 From: leigh-mil Date: Thu, 26 Dec 2019 13:07:15 -0500 Subject: [PATCH 03/16] Use generalized macro for new member form in application settings --- templates/applications/fragments/members.html | 18 +++++++--- .../fragments/new_member_modal_content.html | 33 ------------------- 2 files changed, 13 insertions(+), 38 deletions(-) delete mode 100644 templates/applications/fragments/new_member_modal_content.html diff --git a/templates/applications/fragments/members.html b/templates/applications/fragments/members.html index be312351..bac92a3a 100644 --- a/templates/applications/fragments/members.html +++ b/templates/applications/fragments/members.html @@ -1,12 +1,10 @@ -{% from "components/alert.html" import Alert %} {% from "components/icon.html" import Icon %} {% from "components/label.html" import Label %} -{% import "applications/fragments/new_member_modal_content.html" as member_steps %} +{% import "components/member_form_template.html" as member_form %} {% import "applications/fragments/member_form_fields.html" as member_fields %} {% from "components/modal.html" import Modal %} {% from "components/multi_step_modal_form.html" import MultiStepModalForm %} {% from "components/save_button.html" import SaveButton %} -{% from "components/toggle_list.html" import ToggleButton, ToggleSection %} {% macro MemberManagementTemplate( application, @@ -179,8 +177,18 @@ form=new_member_form, form_action=url_for(action_new, application_id=application.id), steps=[ - member_steps.MemberStepOne(new_member_form), - member_steps.MemberStepTwo(new_member_form, application) + member_form.BasicStep( + title="portfolios.applications.members.form.add_member"|translate, + form=member_fields.InfoFields(new_member_form.user_data), + next_button_text="portfolios.applications.members.form.next_button"|translate, + previous=False, + modal=new_member_modal_name, + ), + member_form.SubmitStep( + form=member_fields.PermsFields(form=new_member_form, new=True), + submit_text="portfolios.applications.members.form.add_member"|translate, + modal=new_member_modal_name, + ) ], ) }} {% endif %} diff --git a/templates/applications/fragments/new_member_modal_content.html b/templates/applications/fragments/new_member_modal_content.html deleted file mode 100644 index c7b5e3f1..00000000 --- a/templates/applications/fragments/new_member_modal_content.html +++ /dev/null @@ -1,33 +0,0 @@ -{% from "components/icon.html" import Icon %} -{% from "components/member_form_template.html" import MemberFormTemplate %} -{% import "applications/fragments/member_form_fields.html" as member_fields %} - -{% macro MemberStepOne(member_form) %} - {% set next_button %} - - {% endset %} - - {% call MemberFormTemplate(title="portfolios.applications.members.form.add_member"|translate, next_button=next_button, previous=False) %} - {{ member_fields.InfoFields(member_form.user_data) }} - {% endcall %} -{% endmacro %} - -{% macro MemberStepTwo(member_form, application) %} - {% set next_button %} - - {% endset %} - - {% call MemberFormTemplate(next_button=next_button) %} - {{ member_fields.PermsFields(form=member_form, new=True) }} - {% endcall %} -{% endmacro %} From 79b27738522659171114265b1d4637e6e8def5a8 Mon Sep 17 00:00:00 2001 From: leigh-mil Date: Fri, 3 Jan 2020 16:03:32 -0500 Subject: [PATCH 04/16] Portfolio manager invite updates: - Update the form to use BooleanFields for the permissions and make the form more similar to the Application Members form - Use MemberFormTemplate macro in the portfolio settings template - fix tests affected by the form changes --- atst/domain/portfolios/portfolios.py | 4 +- atst/forms/portfolio_member.py | 77 +++++-------- atst/routes/portfolios/admin.py | 2 +- atst/routes/portfolios/invitations.py | 2 +- templates/applications/fragments/members.html | 1 + .../components/member_form_template.html | 3 +- .../fragments/add_new_portfolio_member.html | 104 ++++++------------ .../fragments/portfolio_members.html | 36 +++++- tests/domain/test_portfolios.py | 2 +- tests/routes/portfolios/test_invitations.py | 8 +- 10 files changed, 106 insertions(+), 133 deletions(-) diff --git a/atst/domain/portfolios/portfolios.py b/atst/domain/portfolios/portfolios.py index bb9e7aea..6b8a5c4e 100644 --- a/atst/domain/portfolios/portfolios.py +++ b/atst/domain/portfolios/portfolios.py @@ -75,10 +75,10 @@ class Portfolios(object): permission_sets = PortfolioRoles._permission_sets_for_names( member_data.get("permission_sets", []) ) - role = PortfolioRole(portfolio_id=portfolio.id, permission_sets=permission_sets) + role = PortfolioRole(portfolio=portfolio, permission_sets=permission_sets) invitation = PortfolioInvitations.create( - inviter=inviter, role=role, member_data=member_data + inviter=inviter, role=role, member_data=member_data["user_data"] ) PortfoliosQuery.add_and_commit(role) diff --git a/atst/forms/portfolio_member.py b/atst/forms/portfolio_member.py index 918d1112..a017539f 100644 --- a/atst/forms/portfolio_member.py +++ b/atst/forms/portfolio_member.py @@ -1,76 +1,59 @@ from wtforms.validators import Required -from wtforms.fields import StringField, FormField, FieldList, HiddenField +from wtforms.fields import BooleanField, FormField -from atst.domain.permission_sets import PermissionSets from .forms import BaseForm from .member import NewForm as BaseNewMemberForm +from atst.domain.permission_sets import PermissionSets from atst.forms.fields import SelectField from atst.utils.localization import translate class PermissionsForm(BaseForm): - member_name = StringField() - member_id = HiddenField() - perms_app_mgmt = SelectField( + perms_app_mgmt = BooleanField( translate("forms.new_member.app_mgmt"), - choices=[ - ( - PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT, - translate("common.view"), - ), - ( - PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT, - translate("common.edit"), - ), - ], + default=False, + description="Add, remove and edit applications in this Portfolio.", ) - perms_funding = SelectField( + perms_funding = BooleanField( translate("forms.new_member.funding"), - choices=[ - (PermissionSets.VIEW_PORTFOLIO_FUNDING, translate("common.view")), - (PermissionSets.EDIT_PORTFOLIO_FUNDING, translate("common.edit")), - ], + default=False, + description="Add and Modify Task Orders to fund this Portfolio.", ) - perms_reporting = SelectField( + perms_reporting = BooleanField( translate("forms.new_member.reporting"), - choices=[ - (PermissionSets.VIEW_PORTFOLIO_REPORTS, translate("common.view")), - (PermissionSets.EDIT_PORTFOLIO_REPORTS, translate("common.edit")), - ], + default=False, + description="View and export reports about this Portfolio's funding.", ) - perms_portfolio_mgmt = SelectField( + perms_portfolio_mgmt = BooleanField( translate("forms.new_member.portfolio_mgmt"), - choices=[ - (PermissionSets.VIEW_PORTFOLIO_ADMIN, translate("common.view")), - (PermissionSets.EDIT_PORTFOLIO_ADMIN, translate("common.edit")), - ], + default=False, + description="Edit this Portfolio's settings.", ) @property def data(self): _data = super().data - _data["permission_sets"] = [] - for field in _data: - if "perms" in field: - _data["permission_sets"].append(_data[field]) + _data.pop("csrf_token", None) + perm_sets = [] + if _data["perms_app_mgmt"]: + perm_sets.append(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT) + + if _data["perms_funding"]: + perm_sets.append(PermissionSets.EDIT_PORTFOLIO_FUNDING) + + if _data["perms_reporting"]: + perm_sets.append(PermissionSets.EDIT_PORTFOLIO_REPORTS) + + if _data["perms_portfolio_mgmt"]: + perm_sets.append(PermissionSets.EDIT_PORTFOLIO_ADMIN) + + _data["permission_sets"] = perm_sets return _data -class MembersPermissionsForm(BaseForm): - members_permissions = FieldList(FormField(PermissionsForm)) - - -class NewForm(BaseForm): +class NewForm(PermissionsForm): user_data = FormField(BaseNewMemberForm) - permission_sets = FormField(PermissionsForm) - - @property - def update_data(self): - return { - "permission_sets": self.data.get("permission_sets").get("permission_sets"), - **self.data.get("user_data"), - } class AssignPPOCForm(PermissionsForm): diff --git a/atst/routes/portfolios/admin.py b/atst/routes/portfolios/admin.py index 14ba4b65..19666907 100644 --- a/atst/routes/portfolios/admin.py +++ b/atst/routes/portfolios/admin.py @@ -77,7 +77,7 @@ def render_admin_page(portfolio, form=None): portfolio_form=portfolio_form, ppoc=ppoc, members=filter_members_data(member_list), - member_form=member_forms.NewForm(), + new_manager_form=member_forms.NewForm(), assign_ppoc_form=assign_ppoc_form, portfolio=portfolio, audit_events=audit_events, diff --git a/atst/routes/portfolios/invitations.py b/atst/routes/portfolios/invitations.py index 8f2e4701..09a22d1f 100644 --- a/atst/routes/portfolios/invitations.py +++ b/atst/routes/portfolios/invitations.py @@ -79,7 +79,7 @@ def invite_member(portfolio_id): if form.validate(): try: - invite = Portfolios.invite(portfolio, g.current_user, form.update_data) + invite = Portfolios.invite(portfolio, g.current_user, form.data) send_portfolio_invitation( invitee_email=invite.email, inviter_name=g.current_user.full_name, diff --git a/templates/applications/fragments/members.html b/templates/applications/fragments/members.html index bac92a3a..c82daa60 100644 --- a/templates/applications/fragments/members.html +++ b/templates/applications/fragments/members.html @@ -185,6 +185,7 @@ modal=new_member_modal_name, ), member_form.SubmitStep( + name=new_member_modal_name, form=member_fields.PermsFields(form=new_member_form, new=True), submit_text="portfolios.applications.members.form.add_member"|translate, modal=new_member_modal_name, diff --git a/templates/components/member_form_template.html b/templates/components/member_form_template.html index f878df34..f62f90c1 100644 --- a/templates/components/member_form_template.html +++ b/templates/components/member_form_template.html @@ -41,6 +41,7 @@ {% endmacro %} {% macro SubmitStep( + name=name, title=None, form=form, submit_text=submit_text, @@ -51,7 +52,7 @@ {% endset %} diff --git a/templates/portfolios/fragments/add_new_portfolio_member.html b/templates/portfolios/fragments/add_new_portfolio_member.html index 644f5062..ced2ef6c 100644 --- a/templates/portfolios/fragments/add_new_portfolio_member.html +++ b/templates/portfolios/fragments/add_new_portfolio_member.html @@ -1,79 +1,41 @@ +{% from "components/checkbox_input.html" import CheckboxInput %} {% from "components/icon.html" import Icon %} +{% from "components/phone_input.html" import PhoneInput %} {% from "components/text_input.html" import TextInput %} -{% from "components/multi_step_modal_form.html" import MultiStepModalForm %} -{% macro SimpleOptionsInput(field) %} -
-
- -
- {{ field.label | striptags}} -
-
- {{ field() }} -
-
-{% endmacro %} - -{% set step_one %} -
-

Invite new portfolio member

-
-
- {{ TextInput(member_form.user_data.first_name, validation='requiredField', optional=False) }} -
-
- {{ TextInput(member_form.user_data.last_name, validation='requiredField', optional=False) }} -
-
-
-
- {{ TextInput(member_form.user_data.email, validation='email', optional=False) }} -
-
- {{ TextInput(member_form.user_data.phone_number, validation='usPhone') }} -
-
-
-
- {{ TextInput(member_form.user_data.dod_id, validation='dodId', optional=False) }} -
-
-
-
-
- - Cancel -
-{% endset %} -{% set step_two %} -
-

Assign member permissions

+{% macro PermsFields(form, member_role_id=None) %} +

Assign member permissions

{{ Icon('info') }} {{ "portfolios.admin.permissions_info" | translate }} - {{ SimpleOptionsInput(member_form.permission_sets.perms_app_mgmt) }} - {{ SimpleOptionsInput(member_form.permission_sets.perms_funding) }} - {{ SimpleOptionsInput(member_form.permission_sets.perms_reporting) }} - {{ SimpleOptionsInput(member_form.permission_sets.perms_portfolio_mgmt) }} -
- - Cancel +
+ {% if new %} + {% set app_mgmt = form.perms_app_mgmt.name %} + {% set funding = form.perms_funding.name %} + {% set reporting = form.perms_reporting.name %} + {% set portfolio_mgmt = form.perms_portfolio_mgmt.name %} + {% else %} + {% set app_mgmt = "perms_app_mgmt-{}".format(member_role_id) %} + {% set funding = "perms_funding-{}".format(member_role_id) %} + {% set reporting = "perms_reporting-{}".format(member_role_id) %} + {% set portfolio_mgmt = "perms_portfolio_mgmt-{}".format(member_role_id) %} + {% endif %} + + {{ CheckboxInput(form.perms_app_mgmt, classes="input__inline-fields", key=app_mgmt, id=app_mgmt, optional=True) }} + {{ CheckboxInput(form.perms_funding, classes="input__inline-fields", key=funding, id=funding, optional=True) }} + {{ CheckboxInput(form.perms_reporting, classes="input__inline-fields", key=reporting, id=reporting, optional=True) }} + {{ CheckboxInput(form.perms_portfolio_mgmt, classes="input__inline-fields", key=portfolio_mgmt, id=portfolio_mgmt, optional=True) }}
-{% endset %} -{{ MultiStepModalForm( - 'add-port-mem', - member_form, - url_for("portfolios.invite_member", portfolio_id=portfolio.id), - [step_one, step_two], - ) }} +{% endmacro %} + +{% macro InfoFields(member_form) %} + +{% endmacro %} diff --git a/templates/portfolios/fragments/portfolio_members.html b/templates/portfolios/fragments/portfolio_members.html index 074f3b9e..5aee8fd7 100644 --- a/templates/portfolios/fragments/portfolio_members.html +++ b/templates/portfolios/fragments/portfolio_members.html @@ -1,7 +1,10 @@ -{% from "components/icon.html" import Icon %} -{% from 'components/save_button.html' import SaveButton %} -{% from "components/modal.html" import Modal %} {% from "components/alert.html" import Alert %} +{% from "components/icon.html" import Icon %} +{% import "components/member_form_template.html" as member_form %} +{% from "components/modal.html" import Modal %} +{% from "components/multi_step_modal_form.html" import MultiStepModalForm %} +{% from 'components/save_button.html' import SaveButton %} +{% import "portfolios/fragments/add_new_portfolio_member.html" as member_form_fields %}

Portfolio Managers

@@ -43,7 +46,30 @@ {% if user_can(permissions.CREATE_PORTFOLIO_USERS) %} - Add Portfolio Manager - {% include "portfolios/fragments/add_new_portfolio_member.html" %} + {% set new_manager_modal = "add-portfolio-manager" %} + + Add Portfolio Manager + + + {{ MultiStepModalForm( + name=new_manager_modal, + form=new_manager_form, + form_action=url_for("portfolios.invite_member", portfolio_id=portfolio.id), + steps=[ + member_form.BasicStep( + title="Add Manager", + form=member_form_fields.InfoFields(new_manager_form.user_data), + next_button_text="Next: Permissions", + previous=False, + modal=new_manager_modal_name, + ), + member_form.SubmitStep( + name=new_manager_modal, + form=member_form_fields.PermsFields(new_manager_form), + submit_text="Invite member", + modal=new_manager_modal_name, + ) + ], + ) }} {% endif %}
diff --git a/tests/domain/test_portfolios.py b/tests/domain/test_portfolios.py index 0390f69f..828fa1ba 100644 --- a/tests/domain/test_portfolios.py +++ b/tests/domain/test_portfolios.py @@ -205,7 +205,7 @@ def test_invite(): inviter = UserFactory.create() member_data = UserFactory.dictionary() - invitation = Portfolios.invite(portfolio, inviter, member_data) + invitation = Portfolios.invite(portfolio, inviter, {"user_data": member_data}) assert invitation.role assert invitation.role.portfolio == portfolio diff --git a/tests/routes/portfolios/test_invitations.py b/tests/routes/portfolios/test_invitations.py index 2638a9af..44c72460 100644 --- a/tests/routes/portfolios/test_invitations.py +++ b/tests/routes/portfolios/test_invitations.py @@ -269,10 +269,10 @@ def test_existing_member_invite_resent_to_email_submitted_in_form( _DEFAULT_PERMS_FORM_DATA = { - "permission_sets-perms_app_mgmt": PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT, - "permission_sets-perms_funding": PermissionSets.VIEW_PORTFOLIO_FUNDING, - "permission_sets-perms_reporting": PermissionSets.VIEW_PORTFOLIO_REPORTS, - "permission_sets-perms_portfolio_mgmt": PermissionSets.VIEW_PORTFOLIO_ADMIN, + "permission_sets-perms_app_mgmt": False, + "permission_sets-perms_funding": False, + "permission_sets-perms_reporting": False, + "permission_sets-perms_portfolio_mgmt": False, } From c9d0c64c1f44bc0fe389bb57d528578a1c4c7368 Mon Sep 17 00:00:00 2001 From: leigh-mil Date: Mon, 6 Jan 2020 13:54:12 -0500 Subject: [PATCH 05/16] Fix and generalize styling for member form macro Only display permissions with 'Edit' value Delete unused files and rename MemberForm macro file --- styles/atat.scss | 1 + styles/components/_member_form.scss | 55 +++++++++++++++++ styles/sections/_application_edit.scss | 60 ------------------- .../fragments/member_form_fields.html | 2 +- templates/applications/fragments/members.html | 2 +- ...er_form_template.html => member_form.html} | 20 ++++--- .../fragments/add_new_portfolio_member.html | 4 +- .../portfolios/fragments/members_edit.html | 38 ------------ .../portfolios/fragments/members_view.html | 26 -------- .../fragments/portfolio_members.html | 6 +- 10 files changed, 75 insertions(+), 139 deletions(-) create mode 100644 styles/components/_member_form.scss rename templates/components/{member_form_template.html => member_form.html} (71%) delete mode 100644 templates/portfolios/fragments/members_edit.html delete mode 100644 templates/portfolios/fragments/members_view.html diff --git a/styles/atat.scss b/styles/atat.scss index 4c8aa263..0134dd89 100644 --- a/styles/atat.scss +++ b/styles/atat.scss @@ -38,6 +38,7 @@ @import "components/dod_login_notice.scss"; @import "components/sticky_cta.scss"; @import "components/error_page.scss"; +@import "components/member_form.scss"; @import "sections/login"; @import "sections/home"; diff --git a/styles/components/_member_form.scss b/styles/components/_member_form.scss new file mode 100644 index 00000000..c734cfd1 --- /dev/null +++ b/styles/components/_member_form.scss @@ -0,0 +1,55 @@ +.member-form { + text-align: left; + min-width: 75rem; + + input[type="checkbox"] + label::before { + margin-left: 0; + } + + .input__inline-fields { + text-align: left; + + .usa-input__choices label { + font-weight: $font-bold; + } + } + + .input__inline-fields { + padding: $gap * 2; + border: 1px solid $color-gray-lighter; + + &.checked { + border: 1px solid $color-blue; + } + + label { + font-weight: $font-bold; + } + + p.usa-input__help { + margin-bottom: 0; + padding-left: 3rem; + } + } + + .user-info { + .usa-input { + width: 45rem; + + input, + label, + .usa-input__message { + max-width: unset; + } + + label .icon-validation { + left: unset; + right: -$gap * 4; + } + + &--validation--phoneExt { + width: 18rem; + } + } + } +} diff --git a/styles/sections/_application_edit.scss b/styles/sections/_application_edit.scss index 8ff79b7b..8282cb64 100644 --- a/styles/sections/_application_edit.scss +++ b/styles/sections/_application_edit.scss @@ -23,66 +23,6 @@ } } -#modal--add-app-mem, -.form-content--app-mem { - text-align: left; - - .modal__body { - min-width: 75rem; - } - - input[type="checkbox"] + label::before { - margin-left: 0; - } - - .input__inline-fields { - text-align: left; - - .usa-input__choices label { - font-weight: $font-bold; - } - } - - .input__inline-fields { - padding: $gap * 2; - border: 1px solid $color-gray-lighter; - - &.checked { - border: 1px solid $color-blue; - } - - label { - font-weight: $font-bold; - } - - p.usa-input__help { - margin-bottom: 0; - padding-left: 3rem; - } - } - - .application-member__user-info { - .usa-input { - width: 45rem; - - input, - label, - .usa-input__message { - max-width: unset; - } - - label .icon-validation { - left: unset; - right: -$gap * 4; - } - - &--validation--phoneExt { - width: 18rem; - } - } - } -} - .environment-roles { padding: 0 ($gap * 3) ($gap * 3); diff --git a/templates/applications/fragments/member_form_fields.html b/templates/applications/fragments/member_form_fields.html index 3aa97687..707653fa 100644 --- a/templates/applications/fragments/member_form_fields.html +++ b/templates/applications/fragments/member_form_fields.html @@ -118,7 +118,7 @@ {% endmacro %} {% macro InfoFields(member_form) %} -