Create KoReview component to add multiple LOA items

This commit is contained in:
leigh-mil 2019-02-18 16:52:18 -05:00
parent 070ccec23f
commit 3325e4c219
6 changed files with 188 additions and 37 deletions

View File

@ -1,7 +1,8 @@
from flask_wtf.file import FileAllowed
from flask_wtf import FlaskForm
from wtforms.fields.html5 import DateField
from wtforms.fields import StringField, TextAreaField, FileField
from wtforms.fields import StringField, TextAreaField, FileField, FieldList
from wtforms.validators import Optional, Length
from .forms import CacheableForm
@ -11,6 +12,8 @@ from atst.utils.localization import translate
class KOReviewForm(CacheableForm):
EMPTY_LOA = ["", None]
start_date = DateField(
translate("forms.ko_review.start_date_label"), format="%m/%d/%Y"
)
@ -26,11 +29,19 @@ class KOReviewForm(CacheableForm):
number = StringField(
translate("forms.ko_review.to_number"), validators=[Length(min=10)]
)
loa = StringField(
translate("forms.ko_review.loa"), validators=[Length(min=10), IsNumber()]
loa = FieldList(
StringField(
translate("forms.ko_review.loa"), validators=[Length(min=10), IsNumber()]
)
)
custom_clauses = TextAreaField(
translate("forms.ko_review.custom_clauses_label"),
description=translate("forms.ko_review.custom_clauses_description"),
validators=[Optional()],
)
@property
def data(self):
_data = super(FlaskForm, self).data
_data["loa"] = [n for n in _data["loa"] if n not in self.EMPTY_LOA]
return _data

View File

@ -0,0 +1,40 @@
import textinput from '../text_input'
import DateSelector from '../date_selector'
import uploadinput from '../upload_input'
import inputValidations from '../../lib/input_validations'
const createLOA = number => ({ number })
export default {
name: 'ko-review',
components: {
textinput,
DateSelector,
uploadinput,
},
props: {
initialData: {
type: Object,
default: () => ({}),
},
modalName: String,
},
data: function() {
const { loa } = this.initialData
const loas =
typeof loa === 'array' && loa.length > 0 ? this.initialValue : ['']
return {
loas,
}
},
methods: {
addLOA: function(event) {
this.loas.push(createLOA(''))
},
},
}

View File

@ -31,6 +31,7 @@ import ConfirmationPopover from './components/confirmation_popover'
import { isNotInVerticalViewport } from './lib/viewport'
import DateSelector from './components/date_selector'
import SidenavToggler from './components/sidenav_toggler'
import KoReview from './components/forms/ko_review'
Vue.config.productionTip = false
@ -64,6 +65,7 @@ const app = new Vue({
DateSelector,
EditOfficerForm,
SidenavToggler,
KoReview,
},
mounted: function() {

View File

@ -323,6 +323,37 @@
margin: 0;
}
}
.task-order__loa-list {
ul {
padding-left: 0;
}
.task-order__loa-add-item {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
max-width: 30em;
.icon-link {
&:first-child {
margin-right: -$gap;
}
}
}
}
.task-order__loa-list-item {
display: flex;
flex-direction: row;
align-items: flex-end;
.usa-input {
flex-grow: 1;
}
}
}
.task-order-invitations {

View File

@ -11,37 +11,29 @@
{% from "components/review_field.html" import ReviewField %}
{% from "components/upload_input.html" import UploadInput %}
{% block content %}
<ko-review inline-template v-bind:initial-data='{{ form.data|tojson }}'>
<div class="col task-order-form">
<div class="col task-order-form">
{% include "fragments/flash.html" %}
{% include "fragments/flash.html" %}
{% block form_action %}
<form method='POST' action="{{ url_for('portfolios.submit_ko_review', portfolio_id=portfolio.id, task_order_id=task_order.id, form=form) }}" autocomplete="off" enctype="multipart/form-data">
{% endblock %}
{% block form_action %}
<form method='POST' action="{{ url_for('portfolios.submit_ko_review', portfolio_id=portfolio.id, task_order_id=task_order.id, form=form) }}" autocomplete="off" enctype="multipart/form-data">
{% endblock %}
{{ form.csrf_token }}
{{ form.csrf_token }}
{% block form %}
{% block form %}
<div class="top-message">
<h1 class="subheading title">
{{ "task_orders.ko_review.title" | translate }}
</h1>
{% include "fragments/ko_review_message.html" %}
</div>
<div class="panel">
<div class="panel__heading">
<h1 class="task-order-form__heading subheading">
<div class="h2">{{ "task_orders.ko_review.review_title" | translate }}</div>
{{ "task_orders.new.review.section_title"| translate }}
<div class="top-message">
<h1 class="subheading title">
{{ "task_orders.ko_review.title" | translate }}
</h1>
{% include "fragments/ko_review_message.html" %}
</div>
<div class="panel__content">
<div class="panel">
<div class="h2">
{{ "task_orders.new.review.app_info"| translate }}
</div>
@ -74,24 +66,64 @@
{% include "fragments/task_order_review/oversight.html" %}
<hr>
<div class="h2">{{ "task_orders.ko_review.task_order_information"| translate }}</div>
<div class="panel__content">
<div class="h2">{{ "task_orders.new.review.app_info"| translate }}</div>
{% include "fragments/task_order_review/app_info.html" %}
<hr>
<div class="h2">{{ "task_orders.new.review.reporting"| translate }}</div>
{% include "fragments/task_order_review/reporting.html" %}
<hr>
<div class="h2">{{ "task_orders.new.review.funding"| translate }}</div>
{% include "fragments/task_order_review/funding.html" %}
<div class="form__sub-fields">
{{ DatePicker(form.start_date) }}
{{ DatePicker(form.end_date) }}
</div>
<hr>
<div class="h2">{{ "task_orders.new.review.oversight"| translate }}</div>
{% include "fragments/task_order_review/oversight.html" %}
<hr>
<div class="h2">{{ "task_orders.ko_review.task_order_information"| translate }}</div>
<div class="form__sub-fields">
{{ UploadInput(form.pdf, show_label=True) }}
{{ TextInput(form.number, placeholder='1234567890') }}
<div class="task-order__loa-list">
<ul>
<li v-for="(loa, i) in loas" class="task-order__loa-list-item">
<div class="usa-input usa-input--validation--anything">
<label :for="'loa-' + i">
<div class="usa-input__title">Line of Accounting (LOA) #</div>
</label>
<input type="text" :id="'loa-' + i" placeholder="1234567890"/>
<input type="hidden" :name="'environment_names-' + i"/>
</div>
</li>
</ul>
<div class="task-order__loa-add-item">
<button v-on:click="addLOA" class="icon-link" tabindex="0" type="button">{{ Icon('plus') }} Add another LOA</button>
</div>
</div>
{{ TextInput(form.custom_clauses, paragraph=True) }}
</div>
<div class="form__sub-fields">
{{ UploadInput(form.pdf, show_label=True) }}
{{ TextInput(form.number, placeholder='1234567890') }}
{{ TextInput(form.loa, placeholder='1234567890') }}
{{ TextInput(form.custom_clauses, paragraph=True) }}
</div>
</div>
</div>
{% endblock %}
{% endblock %}
<div class='action-group'>
<input type='submit' class='usa-button usa-button-primary' value='Continue' />
</div>
</form>
</form>
</div>
</div>
</ko-review>
{% endblock %}

View File

@ -358,7 +358,42 @@ def test_submit_completed_ko_review_page_as_cor(client, user_session, pdf_upload
"start_date": "02/10/2019",
"end_date": "03/10/2019",
"number": "1938745981",
"loa": "0813458013405",
"loa": ["0813458013405"],
"custom_clauses": "hi im a custom clause",
"pdf": pdf_upload,
}
response = client.post(
url_for(
"portfolios.ko_review",
portfolio_id=portfolio.id,
task_order_id=task_order.id,
),
data=form_data,
)
assert task_order.pdf
assert response.headers["Location"] == url_for(
"task_orders.signature_requested", task_order_id=task_order.id, _external=True
)
def test_submit_to_with_multiple_loas(client, user_session, pdf_upload):
portfolio = PortfolioFactory.create()
ko = UserFactory.create()
PortfolioRoleFactory.create(
role=Roles.get("officer"),
portfolio=portfolio,
user=ko,
status=PortfolioStatus.ACTIVE,
)
task_order = TaskOrderFactory.create(portfolio=portfolio, contracting_officer=ko)
user_session(ko)
form_data = {
"start_date": "02/10/2019",
"end_date": "03/10/2019",
"number": "1938745981",
"loa": ["0813458013405", "1234567890", "5678901234"],
"custom_clauses": "hi im a custom clause",
"pdf": pdf_upload,
}