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.file import FileAllowed
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
from wtforms.fields.html5 import DateField
|
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 wtforms.validators import Optional, Length
|
||||||
|
|
||||||
from .forms import CacheableForm
|
from .forms import CacheableForm
|
||||||
@ -11,6 +12,8 @@ from atst.utils.localization import translate
|
|||||||
|
|
||||||
|
|
||||||
class KOReviewForm(CacheableForm):
|
class KOReviewForm(CacheableForm):
|
||||||
|
EMPTY_LOA = ["", None]
|
||||||
|
|
||||||
start_date = DateField(
|
start_date = DateField(
|
||||||
translate("forms.ko_review.start_date_label"), format="%m/%d/%Y"
|
translate("forms.ko_review.start_date_label"), format="%m/%d/%Y"
|
||||||
)
|
)
|
||||||
@ -26,11 +29,19 @@ class KOReviewForm(CacheableForm):
|
|||||||
number = StringField(
|
number = StringField(
|
||||||
translate("forms.ko_review.to_number"), validators=[Length(min=10)]
|
translate("forms.ko_review.to_number"), validators=[Length(min=10)]
|
||||||
)
|
)
|
||||||
loa = StringField(
|
loa = FieldList(
|
||||||
|
StringField(
|
||||||
translate("forms.ko_review.loa"), validators=[Length(min=10), IsNumber()]
|
translate("forms.ko_review.loa"), validators=[Length(min=10), IsNumber()]
|
||||||
)
|
)
|
||||||
|
)
|
||||||
custom_clauses = TextAreaField(
|
custom_clauses = TextAreaField(
|
||||||
translate("forms.ko_review.custom_clauses_label"),
|
translate("forms.ko_review.custom_clauses_label"),
|
||||||
description=translate("forms.ko_review.custom_clauses_description"),
|
description=translate("forms.ko_review.custom_clauses_description"),
|
||||||
validators=[Optional()],
|
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 { isNotInVerticalViewport } from './lib/viewport'
|
||||||
import DateSelector from './components/date_selector'
|
import DateSelector from './components/date_selector'
|
||||||
import SidenavToggler from './components/sidenav_toggler'
|
import SidenavToggler from './components/sidenav_toggler'
|
||||||
|
import KoReview from './components/forms/ko_review'
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ const app = new Vue({
|
|||||||
DateSelector,
|
DateSelector,
|
||||||
EditOfficerForm,
|
EditOfficerForm,
|
||||||
SidenavToggler,
|
SidenavToggler,
|
||||||
|
KoReview,
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
|
@ -323,6 +323,37 @@
|
|||||||
margin: 0;
|
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 {
|
.task-order-invitations {
|
||||||
|
@ -11,9 +11,10 @@
|
|||||||
{% from "components/review_field.html" import ReviewField %}
|
{% from "components/review_field.html" import ReviewField %}
|
||||||
{% from "components/upload_input.html" import UploadInput %}
|
{% from "components/upload_input.html" import UploadInput %}
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="col task-order-form">
|
{% block content %}
|
||||||
|
<ko-review inline-template v-bind:initial-data='{{ form.data|tojson }}'>
|
||||||
|
<div class="col task-order-form">
|
||||||
|
|
||||||
{% include "fragments/flash.html" %}
|
{% include "fragments/flash.html" %}
|
||||||
|
|
||||||
@ -33,15 +34,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel">
|
<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 }}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel__content">
|
|
||||||
<div class="h2">
|
<div class="h2">
|
||||||
{{ "task_orders.new.review.app_info"| translate }}
|
{{ "task_orders.new.review.app_info"| translate }}
|
||||||
</div>
|
</div>
|
||||||
@ -74,12 +66,51 @@
|
|||||||
{% include "fragments/task_order_review/oversight.html" %}
|
{% include "fragments/task_order_review/oversight.html" %}
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
<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="h2">{{ "task_orders.ko_review.task_order_information"| translate }}</div>
|
||||||
|
|
||||||
<div class="form__sub-fields">
|
<div class="form__sub-fields">
|
||||||
{{ UploadInput(form.pdf, show_label=True) }}
|
{{ UploadInput(form.pdf, show_label=True) }}
|
||||||
{{ TextInput(form.number, placeholder='1234567890') }}
|
{{ TextInput(form.number, placeholder='1234567890') }}
|
||||||
{{ TextInput(form.loa, 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) }}
|
{{ TextInput(form.custom_clauses, paragraph=True) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -93,5 +124,6 @@
|
|||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</ko-review>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -358,7 +358,42 @@ def test_submit_completed_ko_review_page_as_cor(client, user_session, pdf_upload
|
|||||||
"start_date": "02/10/2019",
|
"start_date": "02/10/2019",
|
||||||
"end_date": "03/10/2019",
|
"end_date": "03/10/2019",
|
||||||
"number": "1938745981",
|
"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",
|
"custom_clauses": "hi im a custom clause",
|
||||||
"pdf": pdf_upload,
|
"pdf": pdf_upload,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user