From 1bc434be8cbd394173736e7fde0d33c7998e5b71 Mon Sep 17 00:00:00 2001 From: dandds Date: Fri, 22 Mar 2019 13:36:21 -0400 Subject: [PATCH] multi-step form modal with basic implementation for adding new member --- atst/forms/portfolio_member.py | 10 +++- atst/routes/portfolios/index.py | 5 +- js/components/forms/multi_step_modal_form.js | 43 ++++++++++++++ js/index.js | 2 + .../components/multi_step_modal_form.html | 45 +++++++++++++++ templates/portfolios/admin.html | 57 +++++++++++++++++++ translations.yaml | 2 + 7 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 js/components/forms/multi_step_modal_form.js create mode 100644 templates/components/multi_step_modal_form.html diff --git a/atst/forms/portfolio_member.py b/atst/forms/portfolio_member.py index 492777a7..2293deed 100644 --- a/atst/forms/portfolio_member.py +++ b/atst/forms/portfolio_member.py @@ -1,10 +1,10 @@ from wtforms.fields import StringField, FormField, FieldList -from wtforms.fields.html5 import EmailField -from wtforms.validators import Required, Email, Length +from wtforms.fields.html5 import EmailField, TelField +from wtforms.validators import Required, Email, Length, Optional from atst.domain.permission_sets import PermissionSets from .forms import BaseForm -from atst.forms.validators import IsNumber +from atst.forms.validators import IsNumber, PhoneNumber from atst.forms.fields import SelectField from atst.utils.localization import translate @@ -71,6 +71,10 @@ class NewForm(PermissionsForm): email = EmailField( translate("forms.new_member.email_label"), validators=[Required(), Email()] ) + phone_number = TelField( + translate("forms.new_member.phone_number_label"), + validators=[Optional(), PhoneNumber()], + ) dod_id = StringField( translate("forms.new_member.dod_id_label"), validators=[Required(), Length(min=10), IsNumber()], diff --git a/atst/routes/portfolios/index.py b/atst/routes/portfolios/index.py index b87763d4..78f0ba68 100644 --- a/atst/routes/portfolios/index.py +++ b/atst/routes/portfolios/index.py @@ -8,7 +8,7 @@ from atst.domain.portfolios import Portfolios from atst.domain.audit_log import AuditLog from atst.domain.common import Paginator from atst.forms.portfolio import PortfolioForm -from atst.forms.portfolio_member import MembersPermissionsForm +import atst.forms.portfolio_member as member_forms from atst.models.permissions import Permissions from atst.domain.permission_sets import PermissionSets from atst.domain.authz.decorator import user_can_access_decorator as user_can @@ -63,7 +63,7 @@ def render_admin_page(portfolio, form=None): members_data = [serialize_member_form_data(member) for member in portfolio.members] portfolio_form = PortfolioForm(data={"name": portfolio.name}) - member_perms_form = MembersPermissionsForm( + member_perms_form = member_forms.MembersPermissionsForm( data={"members_permissions": members_data} ) return render_template( @@ -71,6 +71,7 @@ def render_admin_page(portfolio, form=None): form=form, portfolio_form=portfolio_form, member_perms_form=member_perms_form, + member_form=member_forms.NewForm(), portfolio=portfolio, audit_events=audit_events, user=g.current_user, diff --git a/js/components/forms/multi_step_modal_form.js b/js/components/forms/multi_step_modal_form.js new file mode 100644 index 00000000..0fa2f0d0 --- /dev/null +++ b/js/components/forms/multi_step_modal_form.js @@ -0,0 +1,43 @@ +import FormMixin from '../../mixins/form' +import textinput from '../text_input' +import optionsinput from '../options_input' +import Selector from '../selector' +import Modal from '../../mixins/modal' +import toggler from '../toggler' + +export default { + name: 'multi-step-modal-form', + + mixins: [FormMixin, Modal], + + components: { + toggler, + Modal, + Selector, + textinput, + optionsinput, + }, + + props: {}, + + data: function() { + return { + step: 0, + } + }, + + mounted: function() { + return {} + }, + + methods: { + next: function() { + this.step += 1 + }, + goToStep: function(step) { + this.step = step + }, + }, + + computed: {}, +} diff --git a/js/index.js b/js/index.js index 654be95c..a5e58f45 100644 --- a/js/index.js +++ b/js/index.js @@ -18,6 +18,7 @@ import toggler from './components/toggler' import NewApplication from './components/forms/new_application' import EditEnvironmentRole from './components/forms/edit_environment_role' import EditApplicationRoles from './components/forms/edit_application_roles' +import MultiStepModalForm from './components/forms/multi_step_modal_form' import funding from './components/forms/funding' import uploadinput from './components/upload_input' import Modal from './mixins/modal' @@ -59,6 +60,7 @@ const app = new Vue({ LocalDatetime, EditEnvironmentRole, EditApplicationRoles, + MultiStepModalForm, ConfirmationPopover, funding, uploadinput, diff --git a/templates/components/multi_step_modal_form.html b/templates/components/multi_step_modal_form.html new file mode 100644 index 00000000..f31323b8 --- /dev/null +++ b/templates/components/multi_step_modal_form.html @@ -0,0 +1,45 @@ +{% from "components/modal.html" import Modal %} + +{% set numbers = ['one', 'two', 'three', 'four', 'five'] %} + +{% macro FormSteps(step_count, current_step) -%} + {% set count = numbers[step_count - 1] %} +
+ +
+{% endmacro %} + +{% macro MultiStepModalForm(name, form, form_action, steps, button_text="", dismissable=False) -%} + {% set step_count = steps|length %} + +
+ +
+ {{ form.csrf_token }} + {% call Modal(name=name, dismissable=dismissable) %} + {% for step in steps %} +
+ {{ FormSteps(step_count, loop.index) }} + {{ step }} +
+ {% endfor %} + {% endcall %} +
+
+
+{% endmacro %} diff --git a/templates/portfolios/admin.html b/templates/portfolios/admin.html index 2f0614a8..b8cc7942 100644 --- a/templates/portfolios/admin.html +++ b/templates/portfolios/admin.html @@ -3,6 +3,8 @@ {% from "components/pagination.html" import Pagination %} {% from "components/icon.html" import Icon %} {% from "components/text_input.html" import TextInput %} +{% from "components/multi_step_modal_form.html" import MultiStepModalForm %} +{% from "components/options_input.html" import OptionsInput %} {% set secondary_breadcrumb = "navigation.portfolio_navigation.portfolio_admin" | translate %} @@ -50,6 +52,61 @@ {% include "fragments/admin/portfolio_members.html" %} {% endif %} + {% set step_one %} +

Invite New Portfolio Member

+
+
+ {{ TextInput(member_form.first_name, validation='requiredField') }} +
+
+ {{ TextInput(member_form.last_name, validation='requiredField') }} +
+
+
+
+ {{ TextInput(member_form.email, validation='email') }} +
+
+ {{ TextInput(member_form.phone_number, validation='usPhone') }} +
+
+
+
+ {{ TextInput(member_form.dod_id, validation='dodId') }} +
+
+
+
+
+ Next Step + Cancel +
+ {% endset %} + {% set step_two %} +

Assign Member Permissions

+ + {{ Icon('info') }} + {{ "portfolios.admin.permissions_info" | translate }} + + {{ OptionsInput(member_form.perms_app_mgmt) }} + {{ OptionsInput(member_form.perms_funding) }} + {{ OptionsInput(member_form.perms_reporting) }} + {{ OptionsInput(member_form.perms_portfolio_mgmt) }} +
+ + Cancel +
+ {% endset %} + {{ MultiStepModalForm( + 'add-port-mem', + member_form, + url_for("portfolios.create_member", portfolio_id=portfolio.id), + [step_one, step_two], + button_text="add new member") + }} + + {% include "fragments/audit_events_log.html" %} + {% if user_can(permissions.VIEW_PORTFOLIO_ACTIVITY_LOG) %} {% include "fragments/audit_events_log.html" %} {{ Pagination(audit_events, 'portfolios.portfolio_admin', portfolio_id=portfolio.id) }} diff --git a/translations.yaml b/translations.yaml index 1577f78c..364ced82 100644 --- a/translations.yaml +++ b/translations.yaml @@ -134,6 +134,7 @@ forms: new_member: dod_id_label: DOD ID email_label: Email Address + phone_number_label: Phone Number (Optional) first_name_label: First Name last_name_label: Last Name portfolio_role_description: 'The portfolio role controls whether a member is permitted to organize a portfolio into applications and environments, add members to this portfolio, and view billing information.' @@ -568,6 +569,7 @@ portfolios: portfolio_members_subheading: These members have different levels of access to the portfolio. settings_info: Learn more about these settings add_member: Add a New Member + permissions_info: Learn more about these permissions activity_log_title: Activity Log members: archive_button: Archive User