From 90240c19af0778d9cdcbd5b738a58da663ee26f7 Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 10:52:27 -0400 Subject: [PATCH 01/14] Emit field change events from root --- js/components/text_input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/components/text_input.js b/js/components/text_input.js index 60e9021f..88334115 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -89,7 +89,7 @@ export default { this.showValid = valid // Emit a change event - this.$emit('fieldChange', { + this.$root.$emit('field-change', { value, valid, name: this.name From 687af77e339e00b129ac0366e0d729de8c352bc4 Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 10:53:13 -0400 Subject: [PATCH 02/14] Emit field change event for options inputs --- js/components/options_input.js | 16 ++++++++++ js/index.js | 4 ++- templates/components/options_input.html | 41 +++++++++++++------------ 3 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 js/components/options_input.js diff --git a/js/components/options_input.js b/js/components/options_input.js new file mode 100644 index 00000000..eb16f706 --- /dev/null +++ b/js/components/options_input.js @@ -0,0 +1,16 @@ +export default { + name: 'optionsinput', + + props: { + name: String + }, + + methods: { + onInput: function (e) { + this.$root.$emit('field-change', { + value: e.target.value, + name: this.name + }) + } + } +} diff --git a/js/index.js b/js/index.js index b13d1a6a..1259a285 100644 --- a/js/index.js +++ b/js/index.js @@ -1,12 +1,14 @@ import classes from '../styles/atat.scss' import Vue from 'vue/dist/vue' +import optionsinput from './components/options_input' import textinput from './components/text_input' const app = new Vue({ el: '#app-root', components: { - textinput + optionsinput, + textinput, }, methods: { closeModal: function(name) { diff --git a/templates/components/options_input.html b/templates/components/options_input.html index dbd01947..f183cca4 100644 --- a/templates/components/options_input.html +++ b/templates/components/options_input.html @@ -1,32 +1,33 @@ {% from "components/icon.html" import Icon %} {% macro OptionsInput(field, inline=False) -%} -
+ +
-
- - {{ field.label | striptags}} +
+ + {{ field.label | striptags}} - {% if field.description %} - {{ field.description | safe }} - {% endif %} + {% if field.description %} + {{ field.description | safe }} + {% endif %} + + {% if field.errors %} + {{ Icon('alert') }} + {% endif %} + + + {{ field() }} {% if field.errors %} - {{ Icon('alert') }} + {% for error in field.errors %} + {{ error }} + {% endfor %} {% endif %} - - - {{ field() }} - - {% if field.errors %} - {% for error in field.errors %} - {{ error }} - {% endfor %} - {% endif %} - -
-
+ +
+ {%- endmacro %} From 86391e4ab55ed7c808fa50b615e7c402c4bc3d60 Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 10:53:46 -0400 Subject: [PATCH 03/14] Hook up details of use form to Vue to show/hide migration fields --- js/components/forms/details_of_use.js | 43 +++++++++++++++++++++++ js/index.js | 2 ++ templates/requests/screen-1.html | 50 +++++++++++++++------------ 3 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 js/components/forms/details_of_use.js diff --git a/js/components/forms/details_of_use.js b/js/components/forms/details_of_use.js new file mode 100644 index 00000000..bcd06361 --- /dev/null +++ b/js/components/forms/details_of_use.js @@ -0,0 +1,43 @@ +import textinput from '../text_input' +import optionsinput from '../options_input' + +export default { + name: 'details-of-use', + + components: { + textinput, + optionsinput, + }, + + props: { + initialData: { + type: Object, + default: () => ({}) + } + }, + + data: function () { + return { + jedi_migration: this.initialData.jedi_migration + } + }, + + mounted: function () { + this.$root.$on('field-change', this.handleFieldChange) + }, + + computed: { + isJediMigration: function () { + return this.jedi_migration === 'yes' + } + }, + + methods: { + handleFieldChange: function (event) { + const { value, name } = event + if (typeof this[name] !== undefined) { + this[name] = value + } + }, + } +} diff --git a/js/index.js b/js/index.js index 1259a285..7e833bc0 100644 --- a/js/index.js +++ b/js/index.js @@ -3,12 +3,14 @@ import Vue from 'vue/dist/vue' import optionsinput from './components/options_input' import textinput from './components/text_input' +import DetailsOfUse from './components/forms/details_of_use' const app = new Vue({ el: '#app-root', components: { optionsinput, textinput, + DetailsOfUse, }, methods: { closeModal: function(name) { diff --git a/templates/requests/screen-1.html b/templates/requests/screen-1.html index 5f6bbf9f..971a738b 100644 --- a/templates/requests/screen-1.html +++ b/templates/requests/screen-1.html @@ -17,32 +17,38 @@ ) }} {% endif %} + +
-

We’d like to know a little about how you plan to use JEDI Cloud services to process your request. Please answer the following questions to the best of your ability. Note that the CCPO does not directly help with migrating systems to JEDI Cloud. These questions are for learning about your cloud readiness and financial usage of the JEDI Cloud; your estimates will not be used for any department level reporting.

-

All fields are required, unless specified optional.

+

We’d like to know a little about how you plan to use JEDI Cloud services to process your request. Please answer the following questions to the best of your ability. Note that the CCPO does not directly help with migrating systems to JEDI Cloud. These questions are for learning about your cloud readiness and financial usage of the JEDI Cloud; your estimates will not be used for any department level reporting.

+

All fields are required, unless specified optional.

-

General

-{{ OptionsInput(f.dod_component) }} -{{ TextInput(f.jedi_usage, paragraph=True, placeholder="e.g. We are migrating XYZ application to the cloud so that...") }} +

General

+ {{ OptionsInput(f.dod_component) }} + {{ TextInput(f.jedi_usage, paragraph=True, placeholder="e.g. We are migrating XYZ application to the cloud so that...") }} -

Cloud Readiness

-{{ TextInput(f.num_software_systems, validation='integer') }} -{{ OptionsInput(f.jedi_migration) }} -{{ OptionsInput(f.rationalization_software_systems) }} -{{ OptionsInput(f.technical_support_team) }} -{{ OptionsInput(f.organization_providing_assistance) }} -{{ OptionsInput(f.engineering_assessment) }} -{{ OptionsInput(f.data_transfers) }} -{{ OptionsInput(f.expected_completion_date) }} -{{ OptionsInput(f.cloud_native) }} +

Cloud Readiness

+ {{ TextInput(f.num_software_systems, validation='integer') }} + {{ OptionsInput(f.jedi_migration) }} + -

Financial Usage

-{{ TextInput(f.estimated_monthly_spend, validation='dollars') }} -

So this means you are spending approximately $X annually

-{{ TextInput(f.dollar_value, validation='dollars') }} -{{ TextInput(f.number_user_sessions, validation='integer') }} -{{ TextInput(f.average_daily_traffic, placeholder='Gigabytes per day', validation='gigabytes') }} -{{ TextInput(f.start_date, validation='date', placeholder='MM / DD / YYYY') }} +

Financial Usage

+ {{ TextInput(f.estimated_monthly_spend, validation='dollars') }} +

So this means you are spending approximately $X annually

+ {{ TextInput(f.dollar_value, validation='dollars') }} + {{ TextInput(f.number_user_sessions, validation='integer') }} + {{ TextInput(f.average_daily_traffic, placeholder='Gigabytes per day', validation='gigabytes') }} + {{ TextInput(f.start_date, validation='date', placeholder='MM / DD / YYYY') }} +
+
{% endblock %} From 88ede0c696f7963d0597158be8e09ff7de104998 Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 11:20:35 -0400 Subject: [PATCH 04/14] Emit raw field value instead of masked value --- js/components/text_input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/components/text_input.js b/js/components/text_input.js index 88334115..d45570ed 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -90,7 +90,7 @@ export default { // Emit a change event this.$root.$emit('field-change', { - value, + value: this._rawValue(value), valid, name: this.name }) From 855857217f4172e510428d84f3959b2b300d21bc Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 11:20:56 -0400 Subject: [PATCH 05/14] Set custom delimiters for Vue Normally, in Vue.js, you could use `{{ variable }}` to render the value of some variable. In our setup however, Jinja expects to handle the `{{}}` as a template. This change allows us to use `!{ variable }` to render the value in our Vue templates. --- js/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/index.js b/js/index.js index 7e833bc0..02d24503 100644 --- a/js/index.js +++ b/js/index.js @@ -35,5 +35,6 @@ const app = new Vue({ const modal = modalOpen.getAttribute("data-modal"); this.modals[modal] = true; } - } + }, + delimiters: ['!{', '}'] }) From 8859ca238e9dfd7a92733d54e404ee624833710c Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 11:23:22 -0400 Subject: [PATCH 06/14] Show annual spend calculated from monthly spend --- js/components/forms/details_of_use.js | 12 +++++++++++- templates/requests/screen-1.html | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/js/components/forms/details_of_use.js b/js/components/forms/details_of_use.js index bcd06361..94b0f5f0 100644 --- a/js/components/forms/details_of_use.js +++ b/js/components/forms/details_of_use.js @@ -17,8 +17,14 @@ export default { }, data: function () { + const { + estimated_monthly_spend = 0, + jedi_migration = '' + } = this.initialData + return { - jedi_migration: this.initialData.jedi_migration + estimated_monthly_spend, + jedi_migration } }, @@ -27,6 +33,10 @@ export default { }, computed: { + annualSpend: function () { + const monthlySpend = this.estimated_monthly_spend || 0 + return monthlySpend * 12 + }, isJediMigration: function () { return this.jedi_migration === 'yes' } diff --git a/templates/requests/screen-1.html b/templates/requests/screen-1.html index 971a738b..d0acf98d 100644 --- a/templates/requests/screen-1.html +++ b/templates/requests/screen-1.html @@ -42,7 +42,7 @@

Financial Usage

{{ TextInput(f.estimated_monthly_spend, validation='dollars') }} -

So this means you are spending approximately $X annually

+

So this means you are spending approximately $!{ annualSpend } annually

{{ TextInput(f.dollar_value, validation='dollars') }} {{ TextInput(f.number_user_sessions, validation='integer') }} {{ TextInput(f.average_daily_traffic, placeholder='Gigabytes per day', validation='gigabytes') }} From 7b01e5b8a34cee13bba217b6d0a520ded7d0743f Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 14:31:26 -0400 Subject: [PATCH 07/14] Add keys to inputs to prevent Vue from re-using elements --- templates/components/options_input.html | 2 +- templates/components/text_input.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/components/options_input.html b/templates/components/options_input.html index f183cca4..f2cf87b6 100644 --- a/templates/components/options_input.html +++ b/templates/components/options_input.html @@ -1,7 +1,7 @@ {% from "components/icon.html" import Icon %} {% macro OptionsInput(field, inline=False) -%} - +
diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 33dfb29e..9a79bac3 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -6,6 +6,7 @@ validation='{{ validation }}' {% if field.data %}initial-value='{{ field.data }}'{% endif %} {% if field.errors %}v-bind:initial-errors='{{ field.errors }}'{% endif %} + key='{{ field.name }}' inline-template>
Date: Fri, 10 Aug 2018 14:46:32 -0400 Subject: [PATCH 08/14] Set default value for radio fields If we don't set the default, the default gets set to `"None"`, which is an unintuitive value. An empty string is equivalent but more usuable. --- atst/forms/request.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/atst/forms/request.py b/atst/forms/request.py index a59aaf19..a7e5214a 100644 --- a/atst/forms/request.py +++ b/atst/forms/request.py @@ -37,16 +37,19 @@ class RequestForm(ValidatedForm): jedi_migration = RadioField( "Are you using the JEDI Cloud to migrate existing systems?", choices=[("yes", "Yes"), ("no", "No")], + default="", ) rationalization_software_systems = RadioField( "Have you completed a “rationalization” of your software systems to move to the cloud?", choices=[("yes", "Yes"), ("no", "No"), ("in_progress", "In Progress")], + default="", ) technical_support_team = RadioField( "Are you working with a technical support team experienced in cloud migrations?", choices=[("yes", "Yes"), ("no", "No")], + default="", ) organization_providing_assistance = RadioField( # this needs to be updated to use checkboxes instead of radio @@ -56,11 +59,13 @@ class RequestForm(ValidatedForm): ("contractor", "Contractor"), ("other_dod_organization", "Other DoD organization"), ], + default="", ) engineering_assessment = RadioField( "Have you completed an engineering assessment of your software systems for cloud readiness?", choices=[("yes", "Yes"), ("no", "No"), ("in_progress", "In Progress")], + default="", ) data_transfers = SelectField( @@ -94,6 +99,7 @@ class RequestForm(ValidatedForm): cloud_native = RadioField( "Are your software systems being developed cloud native?", choices=[("yes", "Yes"), ("no", "No")], + default="", ) # Details of Use: Financial Usage From f53e656475d4a556ef7f015bcfb0ca958422550a Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 14:47:52 -0400 Subject: [PATCH 09/14] Don't display cloud migration questions until an answer is selected --- js/components/forms/details_of_use.js | 3 +++ templates/requests/screen-1.html | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/js/components/forms/details_of_use.js b/js/components/forms/details_of_use.js index 94b0f5f0..edc9cacf 100644 --- a/js/components/forms/details_of_use.js +++ b/js/components/forms/details_of_use.js @@ -37,6 +37,9 @@ export default { const monthlySpend = this.estimated_monthly_spend || 0 return monthlySpend * 12 }, + jediMigrationOptionSelected: function () { + return this.jedi_migration !== '' + }, isJediMigration: function () { return this.jedi_migration === 'yes' } diff --git a/templates/requests/screen-1.html b/templates/requests/screen-1.html index d0acf98d..23f078ab 100644 --- a/templates/requests/screen-1.html +++ b/templates/requests/screen-1.html @@ -17,7 +17,7 @@ ) }} {% endif %} - +

We’d like to know a little about how you plan to use JEDI Cloud services to process your request. Please answer the following questions to the best of your ability. Note that the CCPO does not directly help with migrating systems to JEDI Cloud. These questions are for learning about your cloud readiness and financial usage of the JEDI Cloud; your estimates will not be used for any department level reporting.

@@ -30,14 +30,18 @@

Cloud Readiness

{{ TextInput(f.num_software_systems, validation='integer') }} {{ OptionsInput(f.jedi_migration) }} -