Create KoReview component to add multiple LOA items
This commit is contained in:
parent
070ccec23f
commit
3325e4c219
@ -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
|
||||
|
40
js/components/forms/ko_review.js
Normal file
40
js/components/forms/ko_review.js
Normal 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(''))
|
||||
},
|
||||
},
|
||||
}
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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 %}
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user