diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index ba7378c5..0c87c42c 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -1,6 +1,7 @@ -from wtforms.fields import BooleanField, DecimalField, StringField +from wtforms.fields import BooleanField, DecimalField, FileField, StringField from wtforms.fields.html5 import DateField from wtforms.validators import Required, Optional +from flask_wtf.file import FileAllowed from .forms import BaseForm from atst.utils.localization import translate @@ -12,6 +13,13 @@ class TaskOrderForm(BaseForm): description=translate("forms.task_order.number_description"), validators=[Required()], ) + pdf = FileField( + None, + validators=[ + FileAllowed(["pdf"], translate("forms.task_order.file_format_not_allowed")) + ], + render_kw={"accept": ".pdf,application/pdf"}, + ) class FundingForm(BaseForm): diff --git a/js/components/forms/base_form.js b/js/components/forms/base_form.js index 86612659..ecb47bc4 100644 --- a/js/components/forms/base_form.js +++ b/js/components/forms/base_form.js @@ -9,6 +9,7 @@ import levelofwarrant from '../levelofwarrant' import multicheckboxinput from '../multi_checkbox_input' import optionsinput from '../options_input' import textinput from '../text_input' +import uploadinput from '../upload_input' import toggler from '../toggler' export default { @@ -23,6 +24,7 @@ export default { optionsinput, textinput, toggler, + uploadinput, }, mixins: [FormMixin], } diff --git a/js/components/upload_input.js b/js/components/upload_input.js index a9b31460..9fb71553 100644 --- a/js/components/upload_input.js +++ b/js/components/upload_input.js @@ -29,7 +29,7 @@ export default { const pdf = this.initialData return { - showUpload: !pdf || this.uploadErrors.length > 0, + attachment: pdf || null, } }, @@ -37,5 +37,24 @@ export default { showUploadInput: function() { this.showUpload = true }, + addAttachment: function(e) { + this.attachment = e.target.value + }, + removeAttachment: function(e) { + e.preventDefault() + this.attachment = null + this.$refs.attachmentInput.value = null + }, + }, + + computed: { + baseName: function() { + if (this.attachment) { + return this.attachment.split(/[\\/]/).pop() + } + }, + hasAttachment: function() { + return !!this.attachment + }, }, } diff --git a/static/icons/check-circle-solid.svg b/static/icons/check-circle-solid.svg new file mode 100644 index 00000000..6aaa9742 --- /dev/null +++ b/static/icons/check-circle-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/styles/atat.scss b/styles/atat.scss index 5d41ed96..65c4c6a8 100644 --- a/styles/atat.scss +++ b/styles/atat.scss @@ -23,6 +23,7 @@ @import "elements/graphs"; @import "elements/menu"; @import "elements/card"; +@import "elements/uploader"; @import "components/accordion_table"; @import "components/topbar"; diff --git a/styles/elements/_uploader.scss b/styles/elements/_uploader.scss new file mode 100644 index 00000000..bfca1e96 --- /dev/null +++ b/styles/elements/_uploader.scss @@ -0,0 +1,48 @@ +.upload-widget { + label.upload-label { + text-align: right; + border: 1px solid black; + padding: 0; + display: block; + + .upload-button { + padding: 1rem 1.5rem; + display: inline-block; + background-color: $color-blue; + border: 1px solid $color-blue; + margin: -1px; + color: white; + + &:hover { + background-color: $color-blue-darker; + } + } + } + + input { + opacity: 0; + } +} + +.uploaded-file { + .icon { + vertical-align: middle; + + svg * { + fill: $color-green; + } + } + + .uploaded-file__name { + vertical-align: middle; + margin-left: 0.5rem; + font-weight: $font-bold; + text-decoration: underline; + } + + .uploaded-file__remove { + vertical-align: middle; + margin-left: 2rem; + font-size: $small-font-size; + } +} diff --git a/templates/components/upload_input.html b/templates/components/upload_input.html index b6835484..d7d71cc3 100644 --- a/templates/components/upload_input.html +++ b/templates/components/upload_input.html @@ -1,24 +1,37 @@ +{% from "components/icon.html" import Icon %} + {% macro UploadInput(field, show_label=False) -%} - - - {% if show_label %} - {{ field.label }} - {% endif %} - {{ field.description }} - {{ field }} - {% for error in field.errors %} - {{error}} - {% endfor %} + + {{ Icon("check-circle-solid") }} + + Remove + + + {% if show_label %} + {{ field.label }} + {% endif %} + {{ field.description }} + + + + Browse + + + - - - Uploaded {{ field.data.filename }} - - Change - - + {% for error in field.errors %} + {{error}} + {% endfor %} + {%- endmacro %} diff --git a/templates/task_orders/edit.html b/templates/task_orders/edit.html index f67c42ea..fb3f1c6c 100644 --- a/templates/task_orders/edit.html +++ b/templates/task_orders/edit.html @@ -2,6 +2,7 @@ {% from 'components/save_button.html' import SaveButton %} {% from 'components/text_input.html' import TextInput %} +{% from 'components/upload_input.html' import UploadInput %} {% block content %} @@ -11,7 +12,7 @@ {% include "portfolios/header.html" %} {% endblock %} - + {{ form.csrf_token }} @@ -28,6 +29,7 @@ {{ "task_orders.new.form_help_text" | translate }} {{ TextInput(form.number, validation='taskOrderNumber') }} + {{ UploadInput(form.pdf) }}
Uploaded {{ field.data.filename }}