commit
b7677018c2
@ -2,6 +2,7 @@ import os
|
||||
import re
|
||||
import pathlib
|
||||
from configparser import ConfigParser
|
||||
from datetime import datetime
|
||||
from flask import Flask, request, g, session
|
||||
from flask_session import Session
|
||||
import redis
|
||||
@ -175,6 +176,12 @@ def map_config(config):
|
||||
# with a Beat job once a day)
|
||||
"CELERY_RESULT_EXPIRES": 0,
|
||||
"CELERY_RESULT_EXTENDED": True,
|
||||
"CONTRACT_START_DATE": datetime.strptime(
|
||||
config.get("default", "CONTRACT_START_DATE"), "%Y-%m-%d"
|
||||
).date(),
|
||||
"CONTRACT_END_DATE": datetime.strptime(
|
||||
config.get("default", "CONTRACT_END_DATE"), "%Y-%m-%d"
|
||||
).date(),
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@ from wtforms.fields import (
|
||||
from wtforms.fields.html5 import DateField
|
||||
from wtforms.validators import Required, Optional, Length, NumberRange, ValidationError
|
||||
from flask_wtf import FlaskForm
|
||||
from datetime import datetime
|
||||
from numbers import Number
|
||||
|
||||
from .data import JEDI_CLIN_TYPES
|
||||
@ -85,12 +84,8 @@ class CLINForm(FlaskForm):
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
valid = super().validate(*args, **kwargs)
|
||||
contract_start = datetime.strptime(
|
||||
app.config.get("CONTRACT_START_DATE"), "%Y-%m-%d"
|
||||
).date()
|
||||
contract_end = datetime.strptime(
|
||||
app.config.get("CONTRACT_END_DATE"), "%Y-%m-%d"
|
||||
).date()
|
||||
contract_start = app.config.get("CONTRACT_START_DATE")
|
||||
contract_end = app.config.get("CONTRACT_END_DATE")
|
||||
|
||||
if (
|
||||
self.start_date.data
|
||||
|
@ -163,6 +163,20 @@ describe('DateSelector', () => {
|
||||
component.year = new Date().getFullYear()
|
||||
expect(component.isYearValid).toEqual(true)
|
||||
})
|
||||
|
||||
it('returns true when year is between min and max years', () => {
|
||||
component.year = new Date('2019-01-01').getFullYear()
|
||||
component.mindate = new Date('2018-01-01')
|
||||
component.maxdate = new Date('2019-12-31')
|
||||
expect(component.isYearValid).toEqual(true)
|
||||
})
|
||||
|
||||
it('returns false when year is outside of min and max years', () => {
|
||||
component.year = new Date('2020-01-01').getFullYear()
|
||||
component.mindate = new Date('2018-01-01')
|
||||
component.maxdate = new Date('2019-01-01')
|
||||
expect(component.isYearValid).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('formattedDate', () => {
|
||||
@ -184,4 +198,56 @@ describe('DateSelector', () => {
|
||||
expect(component.formattedDate).toEqual('01/22/1988')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isDateComplete', () => {
|
||||
it('returns true if all fields are completed', () => {
|
||||
component.day = 22
|
||||
component.month = 1
|
||||
component.year = 1988
|
||||
expect(component.isDateComplete).toEqual(true)
|
||||
})
|
||||
|
||||
it('returns false if all fields are not completed', () => {
|
||||
component.day = 22
|
||||
component.month = 1
|
||||
component.year = 19
|
||||
expect(component.isDateComplete).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('minError', () => {
|
||||
it('returns true if the date is before mindate', () => {
|
||||
component.mindate = new Date('2020-01-01')
|
||||
component.day = 1
|
||||
component.month = 1
|
||||
component.year = 2000
|
||||
expect(component.minError).toEqual(true)
|
||||
})
|
||||
|
||||
it('returns fals if the date is after mindate', () => {
|
||||
component.mindate = new Date('2020-01-01')
|
||||
component.day = 1
|
||||
component.month = 1
|
||||
component.year = 2025
|
||||
expect(component.minError).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('maxError', () => {
|
||||
it('returns true if the date is after maxdate', () => {
|
||||
component.maxdate = new Date('2020-01-01')
|
||||
component.day = 1
|
||||
component.month = 1
|
||||
component.year = 2025
|
||||
expect(component.maxError).toEqual(true)
|
||||
})
|
||||
|
||||
it('returns false if the date is before maxdate', () => {
|
||||
component.maxdate = new Date('2020-01-01')
|
||||
component.day = 1
|
||||
component.month = 1
|
||||
component.year = 2005
|
||||
expect(component.maxError).toEqual(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
105
js/components/__tests__/pop_date_range.test.js
Normal file
105
js/components/__tests__/pop_date_range.test.js
Normal file
@ -0,0 +1,105 @@
|
||||
import Vue from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
import PopDateRange from '../pop_date_range'
|
||||
|
||||
import { makeTestWrapper } from '../../test_utils/component_test_helpers'
|
||||
|
||||
const PopDateRangeWrapper = makeTestWrapper({
|
||||
components: { PopDateRange },
|
||||
templatePath: 'pop_date_range.html',
|
||||
data: function() {
|
||||
return {
|
||||
initialMinStartDate: '2019-09-14',
|
||||
initialMaxEndDate: '2022-09-14',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
describe('PopDateRange Test', () => {
|
||||
const component = new Vue(PopDateRange)
|
||||
|
||||
it('should calculate the max start date', () => {
|
||||
component.maxStartDate = new Date('2020-01-01')
|
||||
const date = new Date('2019-12-31')
|
||||
expect(component.calcMaxStartDate(date)).toEqual(date)
|
||||
})
|
||||
|
||||
it('should calculate the min end date', () => {
|
||||
component.minEndDate = new Date('2020-01-01')
|
||||
const date = new Date('2020-02-02')
|
||||
expect(component.calcMinEndDate(date)).toEqual(date)
|
||||
})
|
||||
|
||||
it('should add an error to the start date if it is out of range', () => {
|
||||
const wrapper = mount(PopDateRangeWrapper, {
|
||||
propsData: {
|
||||
initialData: {},
|
||||
},
|
||||
})
|
||||
|
||||
const error = ['usa-input--error']
|
||||
var startDateField = wrapper.find('fieldset[name="start_date"]')
|
||||
var endDateField = wrapper.find('fieldset[name="end_date"]')
|
||||
|
||||
// set valid date range
|
||||
startDateField.find('input[name="date-month"]').setValue('01')
|
||||
startDateField.find('input[name="date-day"]').setValue('01')
|
||||
startDateField.find('input[name="date-year"]').setValue('2020')
|
||||
|
||||
endDateField.find('input[name="date-month"]').setValue('01')
|
||||
endDateField.find('input[name="date-day"]').setValue('01')
|
||||
endDateField.find('input[name="date-year"]').setValue('2021')
|
||||
|
||||
// manually trigger the change event in the hidden fields
|
||||
startDateField.find('input[name="start_date"]').trigger('change')
|
||||
endDateField.find('input[name="end_date"]').trigger('change')
|
||||
|
||||
// check that both dates do not have error class
|
||||
expect(startDateField.classes()).toEqual(expect.not.arrayContaining(error))
|
||||
expect(endDateField.classes()).toEqual(expect.not.arrayContaining(error))
|
||||
|
||||
// update start date to be after end date and trigger change event
|
||||
startDateField.find('input[name="date-year"]').setValue('2022')
|
||||
startDateField.find('input[name="start_date"]').trigger('change')
|
||||
|
||||
expect(startDateField.classes()).toEqual(expect.arrayContaining(error))
|
||||
expect(endDateField.classes()).toEqual(expect.not.arrayContaining(error))
|
||||
})
|
||||
|
||||
it('should add an error to the end date if it is out of range', () => {
|
||||
const wrapper = mount(PopDateRangeWrapper, {
|
||||
propsData: {
|
||||
initialData: {},
|
||||
},
|
||||
})
|
||||
|
||||
const error = ['usa-input--error']
|
||||
var startDateField = wrapper.find('fieldset[name="start_date"]')
|
||||
var endDateField = wrapper.find('fieldset[name="end_date"]')
|
||||
|
||||
// set valid date range
|
||||
startDateField.find('input[name="date-month"]').setValue('01')
|
||||
startDateField.find('input[name="date-day"]').setValue('01')
|
||||
startDateField.find('input[name="date-year"]').setValue('2020')
|
||||
|
||||
endDateField.find('input[name="date-month"]').setValue('01')
|
||||
endDateField.find('input[name="date-day"]').setValue('01')
|
||||
endDateField.find('input[name="date-year"]').setValue('2021')
|
||||
|
||||
// manually trigger the change event in the hidden fields
|
||||
startDateField.find('input[name="start_date"]').trigger('change')
|
||||
endDateField.find('input[name="end_date"]').trigger('change')
|
||||
|
||||
// check that both dates do not have error class
|
||||
expect(startDateField.classes()).toEqual(expect.not.arrayContaining(error))
|
||||
expect(endDateField.classes()).toEqual(expect.not.arrayContaining(error))
|
||||
|
||||
// update end date to be before end date and trigger change event
|
||||
endDateField.find('input[name="date-year"]').setValue('2019')
|
||||
endDateField.find('input[name="end_date"]').trigger('change')
|
||||
|
||||
expect(startDateField.classes()).toEqual(expect.not.arrayContaining(error))
|
||||
expect(endDateField.classes()).toEqual(expect.arrayContaining(error))
|
||||
})
|
||||
})
|
@ -1,28 +1,22 @@
|
||||
import * as R from 'ramda'
|
||||
import { format } from 'date-fns'
|
||||
|
||||
import DateSelector from './date_selector'
|
||||
import { emitEvent } from '../lib/emitters'
|
||||
import Modal from '../mixins/modal'
|
||||
import optionsinput from './options_input'
|
||||
import textinput from './text_input'
|
||||
import clindollaramount from './clin_dollar_amount'
|
||||
import PopDateRange from './pop_date_range'
|
||||
|
||||
const TOTAL_AMOUNT = 'total_amount'
|
||||
const OBLIGATED_AMOUNT = 'obligated_amount'
|
||||
const START_DATE = 'start_date'
|
||||
const END_DATE = 'end_date'
|
||||
const POP = 'period_of_performance'
|
||||
const NUMBER = 'number'
|
||||
|
||||
export default {
|
||||
name: 'clin-fields',
|
||||
|
||||
components: {
|
||||
DateSelector,
|
||||
optionsinput,
|
||||
textinput,
|
||||
clindollaramount,
|
||||
PopDateRange,
|
||||
},
|
||||
|
||||
mixins: [Modal],
|
||||
@ -37,79 +31,26 @@ export default {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
initialStartDate: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
initialEndDate: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
initialClinNumber: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
contractStart: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
contractEnd: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data: function() {
|
||||
const start = !!this.initialStartDate
|
||||
? new Date(this.initialStartDate)
|
||||
: undefined
|
||||
const end = !!this.initialEndDate
|
||||
? new Date(this.initialEndDate)
|
||||
: undefined
|
||||
const fundingValidation =
|
||||
this.initialObligated && this.initialTotal
|
||||
? this.initialObligated <= this.initialTotal
|
||||
: true
|
||||
const popValidation = !this.initialStartDate ? false : start < end
|
||||
const clinNumber = !!this.initialClinNumber
|
||||
? this.initialClinNumber
|
||||
: undefined
|
||||
const contractStartDate = new Date(this.contractStart)
|
||||
const contractEndDate = new Date(this.contractEnd)
|
||||
|
||||
return {
|
||||
clinIndex: this.initialClinIndex,
|
||||
clinNumber: clinNumber,
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
popValid: popValidation,
|
||||
startDateValid: false,
|
||||
endDateValid: false,
|
||||
contractStartDate: contractStartDate,
|
||||
contractEndDate: contractEndDate,
|
||||
clinNumber: clinNumber,
|
||||
showClin: true,
|
||||
popErrors: [],
|
||||
validations: [
|
||||
{
|
||||
func: this.popDateOrder,
|
||||
message: 'PoP start date must be before end date.',
|
||||
},
|
||||
{
|
||||
func: this.popStartsAfterContract,
|
||||
message: `PoP start date must be on or after ${format(
|
||||
contractStartDate,
|
||||
'MMM D, YYYY'
|
||||
)}.`,
|
||||
},
|
||||
{
|
||||
func: this.popEndsBeforeContract,
|
||||
message: `PoP end date must be before or on ${format(
|
||||
contractEndDate,
|
||||
'MMM D, YYYY'
|
||||
)}.`,
|
||||
},
|
||||
],
|
||||
totalAmount: this.initialTotal || 0,
|
||||
obligatedAmount: this.initialObligated || 0,
|
||||
fundingValid: fundingValidation,
|
||||
@ -127,11 +68,6 @@ export default {
|
||||
obligatedAmount: this.initialObligated,
|
||||
totalAmount: this.initialTotal,
|
||||
})
|
||||
emitEvent('field-mount', this, {
|
||||
optional: false,
|
||||
name: 'clins-' + this.clinIndex + '-' + POP,
|
||||
valid: this.checkPopValid(),
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
@ -143,50 +79,6 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
checkPopValid: function() {
|
||||
return (
|
||||
this.popDateOrder() &&
|
||||
this.popStartsAfterContract() &&
|
||||
this.popEndsBeforeContract()
|
||||
)
|
||||
},
|
||||
|
||||
validatePop: function() {
|
||||
this.popValid = this.checkPopValid()
|
||||
emitEvent('field-change', this, {
|
||||
name: 'clins-' + this.clinIndex + '-' + POP,
|
||||
valid: this.popValid,
|
||||
})
|
||||
|
||||
this.popErrors = R.pipe(
|
||||
R.map(validation =>
|
||||
!validation.func() ? validation.message : undefined
|
||||
),
|
||||
R.filter(Boolean)
|
||||
)(this.validations)
|
||||
},
|
||||
|
||||
popStartsAfterContract: function() {
|
||||
if (this.startDateValid) {
|
||||
return this.startDate >= this.contractStartDate
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
popEndsBeforeContract: function() {
|
||||
if (this.endDateValid) {
|
||||
return this.endDate <= this.contractEndDate
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
popDateOrder: function() {
|
||||
if (!!this.startDate && !!this.endDate) {
|
||||
return this.startDate < this.endDate
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
checkFundingValid: function() {
|
||||
return this.obligatedAmount <= this.totalAmount
|
||||
},
|
||||
@ -205,14 +97,6 @@ export default {
|
||||
} else if (event.name.includes(OBLIGATED_AMOUNT)) {
|
||||
this.obligatedAmount = parseFloat(event.value)
|
||||
this.validateFunding()
|
||||
} else if (event.name.includes(START_DATE)) {
|
||||
if (!!event.value) this.startDate = new Date(event.value)
|
||||
if (!!event.valid) this.startDateValid = event.valid
|
||||
this.validatePop()
|
||||
} else if (event.name.includes(END_DATE)) {
|
||||
if (!!event.value) this.endDate = new Date(event.value)
|
||||
if (!!event.valid) this.endDateValid = event.valid
|
||||
this.validatePop()
|
||||
} else if (event.name.includes(NUMBER)) {
|
||||
this.clinNumber = event.value
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import Vue from 'vue'
|
||||
import { getDaysInMonth } from 'date-fns'
|
||||
import { emitEvent } from '../lib/emitters'
|
||||
|
||||
var paddedNumber = function(number) {
|
||||
let paddedNumber = function(number) {
|
||||
if ((number + '').length === 1) {
|
||||
return `0${number}`
|
||||
} else {
|
||||
@ -20,10 +20,6 @@ export default {
|
||||
mindate: { type: String },
|
||||
maxdate: { type: String },
|
||||
nameTag: { type: String },
|
||||
watch: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
optional: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@ -36,7 +32,6 @@ export default {
|
||||
month: this.initialmonth,
|
||||
year: this.initialyear,
|
||||
name: this.nameTag,
|
||||
showValidation: false,
|
||||
}
|
||||
},
|
||||
|
||||
@ -87,23 +82,29 @@ export default {
|
||||
},
|
||||
|
||||
isMonthValid: function() {
|
||||
var _month = parseInt(this.month)
|
||||
var valid = _month >= 0 && _month <= 12
|
||||
this._emitChange('month', this.month, valid)
|
||||
let _month = parseInt(this.month)
|
||||
let valid = _month >= 0 && _month <= 12
|
||||
return valid
|
||||
},
|
||||
|
||||
isDayValid: function() {
|
||||
var _day = parseInt(this.day)
|
||||
var valid = _day >= 0 && _day <= this.daysMaxCalculation
|
||||
this._emitChange('day', this.day, valid)
|
||||
let _day = parseInt(this.day)
|
||||
let valid = _day >= 0 && _day <= this.daysMaxCalculation
|
||||
return valid
|
||||
},
|
||||
|
||||
isYearValid: function() {
|
||||
// Emit a change event
|
||||
var valid = parseInt(this.year) >= 1
|
||||
this._emitChange('year', this.year, valid)
|
||||
let valid
|
||||
let minYear = this.mindate ? this.minDateParsed.getFullYear() : null
|
||||
let maxYear = this.maxdate ? this.maxDateParsed.getFullYear() : null
|
||||
|
||||
if (minYear && maxYear) {
|
||||
valid = this.year >= minYear && this.year <= maxYear
|
||||
} else {
|
||||
valid = parseInt(this.year) >= 1
|
||||
}
|
||||
|
||||
return valid
|
||||
},
|
||||
|
||||
@ -135,6 +136,10 @@ export default {
|
||||
)
|
||||
},
|
||||
|
||||
isDateComplete: function() {
|
||||
return !!this.day && !!this.month && !!this.year && this.year > 999
|
||||
},
|
||||
|
||||
daysMaxCalculation: function() {
|
||||
switch (parseInt(this.month)) {
|
||||
case 2: // February
|
||||
@ -157,22 +162,49 @@ export default {
|
||||
return 31
|
||||
}
|
||||
},
|
||||
|
||||
minError: function() {
|
||||
if (this.isDateComplete) {
|
||||
return this.minDateParsed > this.dateParsed
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
maxError: function() {
|
||||
if (this.isDateComplete) {
|
||||
return this.maxDateParsed < this.dateParsed
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
maxDateParsed: function() {
|
||||
return new Date(this.maxdate)
|
||||
},
|
||||
|
||||
minDateParsed: function() {
|
||||
return new Date(this.mindate)
|
||||
},
|
||||
|
||||
dateParsed: function() {
|
||||
return new Date(this.formattedDate)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onInput: function(e) {
|
||||
this.showValidation = true
|
||||
|
||||
emitEvent('field-change', this, {
|
||||
value: this.formattedDate,
|
||||
name: this.name,
|
||||
watch: this.watch,
|
||||
valid: this.isDateValid,
|
||||
})
|
||||
},
|
||||
|
||||
_emitChange: function(name, value, valid) {
|
||||
emitEvent('field-change', this, { value, name, valid })
|
||||
this.$emit('date-change', {
|
||||
value: this.formattedDate,
|
||||
name: this.name,
|
||||
valid: this.isDateValid,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
|
88
js/components/pop_date_range.js
Normal file
88
js/components/pop_date_range.js
Normal file
@ -0,0 +1,88 @@
|
||||
import { format } from 'date-fns'
|
||||
|
||||
import DateSelector from './date_selector'
|
||||
|
||||
const START_DATE = 'start_date'
|
||||
const END_DATE = 'end_date'
|
||||
|
||||
export default {
|
||||
name: 'pop-date-range',
|
||||
|
||||
components: {
|
||||
DateSelector,
|
||||
},
|
||||
|
||||
props: {
|
||||
initialMinStartDate: String,
|
||||
initialMaxEndDate: String,
|
||||
initialStartDate: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
initialEndDate: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
clinIndex: Number,
|
||||
},
|
||||
|
||||
data: function() {
|
||||
let start = !!this.initialStartDate
|
||||
? new Date(this.initialStartDate)
|
||||
: false
|
||||
let contractStart = new Date(this.initialMinStartDate)
|
||||
let minEndDate = start && start > contractStart ? start : contractStart
|
||||
|
||||
let end = !!this.initialEndDate ? new Date(this.initialEndDate) : false
|
||||
let contractEnd = new Date(this.initialMaxEndDate)
|
||||
let maxStartDate = end && end < contractEnd ? end : contractEnd
|
||||
|
||||
// the maxStartDate and minEndDate change based on user input:
|
||||
// the latest date the start can be is the PoP end date
|
||||
// the earliest date the end can be is the PoP start date
|
||||
// if the form is initialized with out a PoP, the maxStartDate and minEndDate
|
||||
// default to the contract dates
|
||||
return {
|
||||
maxStartDate: maxStartDate,
|
||||
minEndDate: minEndDate,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleDateChange: function(event) {
|
||||
if (event.name.includes(START_DATE) && event.valid) {
|
||||
let date = new Date(event.value)
|
||||
this.minEndDate = this.calcMinEndDate(date)
|
||||
} else if (event.name.includes(END_DATE) && event.valid) {
|
||||
let date = new Date(event.value)
|
||||
this.maxStartDate = this.calcMaxStartDate(date)
|
||||
}
|
||||
},
|
||||
|
||||
calcMaxStartDate: function(date) {
|
||||
if (!!date && date < this.maxStartDate) {
|
||||
return date
|
||||
} else {
|
||||
return this.maxStartDate
|
||||
}
|
||||
},
|
||||
|
||||
calcMinEndDate: function(date) {
|
||||
if (!!date && date > this.minEndDate) {
|
||||
return date
|
||||
} else {
|
||||
return this.minEndDate
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
maxStartProp: function() {
|
||||
return format(this.maxStartDate, 'YYYY-MM-DD')
|
||||
},
|
||||
|
||||
minEndProp: function() {
|
||||
return format(this.minEndDate, 'YYYY-MM-DD')
|
||||
},
|
||||
},
|
||||
}
|
@ -32,6 +32,7 @@ import NewEnvironment from './components/forms/new_environment'
|
||||
import SemiCollapsibleText from './components/semi_collapsible_text'
|
||||
import ToForm from './components/forms/to_form'
|
||||
import ClinFields from './components/clin_fields'
|
||||
import PopDateRange from './components/pop_date_range'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
@ -65,6 +66,7 @@ const app = new Vue({
|
||||
SemiCollapsibleText,
|
||||
ToForm,
|
||||
ClinFields,
|
||||
PopDateRange,
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
|
@ -312,36 +312,56 @@
|
||||
</div>
|
||||
|
||||
|
||||
<pop-date-range
|
||||
initial-min-start-date="2019-09-14"
|
||||
initial-max-end-date="2022-09-14"
|
||||
|
||||
v-bind:clin-index="clinIndex"
|
||||
|
||||
|
||||
initial-start-date="None"
|
||||
|
||||
|
||||
initial-end-date="None"
|
||||
|
||||
inline-template>
|
||||
|
||||
<div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector
|
||||
|
||||
:mindate="initialMinStartDate"
|
||||
:maxdate="maxStartProp"
|
||||
|
||||
name-tag='start_date'
|
||||
initialmonth=""
|
||||
initialday=""
|
||||
initialyear=""
|
||||
v-bind:watch='true'
|
||||
|
||||
:optional='false'
|
||||
v-on:date-change='handleDateChange'
|
||||
inline-template>
|
||||
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
Start Date
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<p class='usa-input__help'>
|
||||
For example: 07 04 1776
|
||||
</p>
|
||||
|
||||
<div v-if='minError' class="usa-input-error-message">
|
||||
PoP start date must be on or after September 14, 2019.
|
||||
</div>
|
||||
<div v-if='maxError' class="usa-input-error-message">
|
||||
PoP start date must be before end date.
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input name="start_date" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>Month</label>
|
||||
@ -374,18 +394,18 @@
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>Year</label>
|
||||
<input
|
||||
id="date-year"
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
|
||||
|
||||
max="2022"
|
||||
min="2019"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="showValidation">
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
|
||||
<span class="icon icon--ok icon--green" aria-hidden="true"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg></span>
|
||||
@ -405,20 +425,23 @@
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector
|
||||
|
||||
:mindate="minEndProp"
|
||||
:maxdate="initialMaxEndDate"
|
||||
|
||||
name-tag='end_date'
|
||||
initialmonth=""
|
||||
initialday=""
|
||||
initialyear=""
|
||||
v-bind:watch='true'
|
||||
|
||||
:optional='false'
|
||||
v-on:date-change='handleDateChange'
|
||||
inline-template>
|
||||
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
End Date
|
||||
@ -427,8 +450,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class='usa-alert usa-alert-info' role='alert' aria-live='polite'>
|
||||
|
||||
<div class='usa-alert-body'>
|
||||
@ -448,17 +469,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<p class='usa-input__help'>
|
||||
For example: 07 04 1776
|
||||
</p>
|
||||
|
||||
<div v-if='minError' class="usa-input-error-message">
|
||||
PoP end date must be after start date.
|
||||
</div>
|
||||
<div v-if='maxError' class="usa-input-error-message">
|
||||
PoP end date must be on or after September 14, 2022.
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input name="end_date" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>Month</label>
|
||||
@ -491,18 +515,15 @@
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>Year</label>
|
||||
<input
|
||||
id="date-year"
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
|
||||
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="showValidation">
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
|
||||
<span class="icon icon--ok icon--green" aria-hidden="true"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg></span>
|
||||
@ -522,12 +543,10 @@
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</pop-date-range>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="usa-input-error-message form-has-errors">
|
||||
<p v-for="error in popErrors" :key="error" v-html='error'></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="$root.activeModal === removeModalId" v-cloak>
|
||||
|
234
js/test_templates/pop_date_range.html
Normal file
234
js/test_templates/pop_date_range.html
Normal file
@ -0,0 +1,234 @@
|
||||
|
||||
<pop-date-range
|
||||
initial-min-start-date="2019-09-14"
|
||||
initial-max-end-date="2022-09-14"
|
||||
|
||||
v-bind:clin-index="1"
|
||||
|
||||
|
||||
initial-start-date="None"
|
||||
|
||||
|
||||
initial-end-date="None"
|
||||
|
||||
inline-template>
|
||||
|
||||
<div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector
|
||||
:mindate="initialMinStartDate"
|
||||
:maxdate="maxStartProp"
|
||||
|
||||
name-tag='start_date'
|
||||
initialmonth=""
|
||||
initialday=""
|
||||
initialyear=""
|
||||
|
||||
:optional='true'
|
||||
v-on:date-change='handleDateChange'
|
||||
inline-template>
|
||||
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
Start Date
|
||||
</div>
|
||||
|
||||
<p class='usa-input__help'>
|
||||
For example: 07 04 1776
|
||||
</p>
|
||||
|
||||
<div v-if='minError' class="usa-input-error-message">
|
||||
PoP start date must be on or after September 14, 2019.
|
||||
</div>
|
||||
<div v-if='maxError' class="usa-input-error-message">
|
||||
PoP start date must be before end date.
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>Month</label>
|
||||
<input
|
||||
name="date-month"
|
||||
max="12"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (month && !isMonthValid) }"
|
||||
v-model="month"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-day">
|
||||
<label>Day</label>
|
||||
<input
|
||||
name="date-day"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (day && !isDayValid) }"
|
||||
v-bind:max="daysMaxCalculation"
|
||||
v-model="day"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>Year</label>
|
||||
<input
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
max="2022"
|
||||
min="2019"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
|
||||
<span class="icon icon--ok icon--green" aria-hidden="true"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg></span>
|
||||
|
||||
</div>
|
||||
<div class="usa-form-group-date-ok" v-else>
|
||||
|
||||
<span class="icon icon--alert icon--red" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#fdb81e">
|
||||
<path d="M8 16c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zM8 2C4.691 2 2 4.691 2 8s2.691 6 6 6 6-2.691 6-6-2.691-6-6-6zm0 8c-.552 0-1-.447-1-1V4c0-.552.448-1 1-1s1 .448 1 1v5c0 .553-.448 1-1 1zm0 3c-.26 0-.52-.11-.71-.29-.18-.19-.29-.45-.29-.71 0-.271.11-.521.29-.71.38-.37 1.05-.37 1.42 0 .18.189.29.45.29.71s-.11.52-.29.71c-.19.18-.45.29-.71.29z"/>
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector
|
||||
:mindate="minEndProp"
|
||||
:maxdate="initialMaxEndDate"
|
||||
|
||||
name-tag='end_date'
|
||||
initialmonth=""
|
||||
initialday=""
|
||||
initialyear=""
|
||||
|
||||
:optional='true'
|
||||
v-on:date-change='handleDateChange'
|
||||
inline-template>
|
||||
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
End Date
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class='usa-alert usa-alert-info' role='alert' aria-live='polite'>
|
||||
|
||||
<div class='usa-alert-body'>
|
||||
|
||||
|
||||
|
||||
<p class='usa-alert-text'>
|
||||
A CLIN's period of performance must end before September 14, 2022.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class='usa-input__help'>
|
||||
For example: 07 04 1776
|
||||
</p>
|
||||
|
||||
<div v-if='minError' class="usa-input-error-message">
|
||||
PoP end date must be after start date.
|
||||
</div>
|
||||
<div v-if='maxError' class="usa-input-error-message">
|
||||
PoP end date must be on or after September 14, 2022.
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>Month</label>
|
||||
<input
|
||||
name="date-month"
|
||||
max="12"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (month && !isMonthValid) }"
|
||||
v-model="month"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-day">
|
||||
<label>Day</label>
|
||||
<input
|
||||
name="date-day"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (day && !isDayValid) }"
|
||||
v-bind:max="daysMaxCalculation"
|
||||
v-model="day"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>Year</label>
|
||||
<input
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
|
||||
<span class="icon icon--ok icon--green" aria-hidden="true"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg></span>
|
||||
|
||||
</div>
|
||||
<div class="usa-form-group-date-ok" v-else>
|
||||
|
||||
<span class="icon icon--alert icon--red" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#fdb81e">
|
||||
<path d="M8 16c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zM8 2C4.691 2 2 4.691 2 8s2.691 6 6 6 6-2.691 6-6-2.691-6-6-6zm0 8c-.552 0-1-.447-1-1V4c0-.552.448-1 1-1s1 .448 1 1v5c0 .553-.448 1-1 1zm0 3c-.26 0-.52-.11-.71-.29-.18-.19-.29-.45-.29-.71 0-.271.11-.521.29-.71.38-.37 1.05-.37 1.42 0 .18.189.29.45.29.71s-.11.52-.29.71c-.19.18-.45.29-.71.29z"/>
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</pop-date-range>
|
@ -199,10 +199,12 @@
|
||||
Period of Performance
|
||||
</div>
|
||||
</div>
|
||||
<pop-date-range initial-max-end-date="2022-09-14" initial-min-start-date="2019-09-14" inline-template="" v-bind:clin-index="clinIndex">
|
||||
<div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector :name-tag="'clins-' + clinIndex + '-start_date'" :optional="false" :watch="true" inline-template="">
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<date-selector :maxdate="maxStartProp" :mindate="initialMinStartDate" :name-tag="'clins-' + clinIndex + '-start_date'" :optional="false" inline-template="" v-on:date-change="handleDateChange">
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
Start Date
|
||||
@ -210,6 +212,12 @@
|
||||
<p class="usa-input__help">
|
||||
For example: 07 04 1776
|
||||
</p>
|
||||
<div class="usa-input-error-message" v-if="minError">
|
||||
PoP start date must be on or after September 14, 2019.
|
||||
</div>
|
||||
<div class="usa-input-error-message" v-if="maxError">
|
||||
PoP start date must be before end date.
|
||||
</div>
|
||||
</legend>
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" type="hidden" v-bind:value="formattedDate" v-on:change="onInput"/>
|
||||
@ -223,11 +231,19 @@
|
||||
</div>
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>Year</label>
|
||||
<input maxlength="4" name="date-year" type="number" v-model="year" v-on:change="onInput"/>
|
||||
<input max="2022" maxlength="4" min="2019" name="date-year" type="number" v-model="year" v-on:change="onInput"/>
|
||||
</div>
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
<span aria-hidden="true" class="icon icon--ok icon--green"><svg aria-hidden="true" class="svg-inline--fa fa-check-circle fa-w-16" data-icon="check-circle" data-prefix="fas" focusable="false" role="img" viewbox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" fill="currentColor"></path></svg></span>
|
||||
</div>
|
||||
<div class="usa-form-group-date-ok" v-else="">
|
||||
<span aria-hidden="true" class="icon icon--alert icon--red"><svg fill="#fdb81e" viewbox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 16c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zM8 2C4.691 2 2 4.691 2 8s2.691 6 6 6 6-2.691 6-6-2.691-6-6-6zm0 8c-.552 0-1-.447-1-1V4c0-.552.448-1 1-1s1 .448 1 1v5c0 .553-.448 1-1 1zm0 3c-.26 0-.52-.11-.71-.29-.18-.19-.29-.45-.29-.71 0-.271.11-.521.29-.71.38-.37 1.05-.37 1.42 0 .18.189.29.45.29.71s-.11.52-.29.71c-.19.18-.45.29-.71.29z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
@ -235,8 +251,8 @@
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector :name-tag="'clins-' + clinIndex + '-end_date'" :optional="false" :watch="true" inline-template="">
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<date-selector :maxdate="initialMaxEndDate" :mindate="minEndProp" :name-tag="'clins-' + clinIndex + '-end_date'" :optional="false" inline-template="" v-on:date-change="handleDateChange">
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
End Date
|
||||
@ -251,6 +267,12 @@
|
||||
<p class="usa-input__help">
|
||||
For example: 07 04 1776
|
||||
</p>
|
||||
<div class="usa-input-error-message" v-if="minError">
|
||||
PoP end date must be after start date.
|
||||
</div>
|
||||
<div class="usa-input-error-message" v-if="maxError">
|
||||
PoP end date must be on or after September 14, 2022.
|
||||
</div>
|
||||
</legend>
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" type="hidden" v-bind:value="formattedDate" v-on:change="onInput"/>
|
||||
@ -266,19 +288,24 @@
|
||||
<label>Year</label>
|
||||
<input maxlength="4" name="date-year" type="number" v-model="year" v-on:change="onInput"/>
|
||||
</div>
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
<span aria-hidden="true" class="icon icon--ok icon--green"><svg aria-hidden="true" class="svg-inline--fa fa-check-circle fa-w-16" data-icon="check-circle" data-prefix="fas" focusable="false" role="img" viewbox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" fill="currentColor"></path></svg></span>
|
||||
</div>
|
||||
<div class="usa-form-group-date-ok" v-else="">
|
||||
<span aria-hidden="true" class="icon icon--alert icon--red"><svg fill="#fdb81e" viewbox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 16c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8zM8 2C4.691 2 2 4.691 2 8s2.691 6 6 6 6-2.691 6-6-2.691-6-6-6zm0 8c-.552 0-1-.447-1-1V4c0-.552.448-1 1-1s1 .448 1 1v5c0 .553-.448 1-1 1zm0 3c-.26 0-.52-.11-.71-.29-.18-.19-.29-.45-.29-.71 0-.271.11-.521.29-.71.38-.37 1.05-.37 1.42 0 .18.189.29.45.29.71s-.11.52-.29.71c-.19.18-.45.29-.71.29z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="usa-input-error-message form-has-errors">
|
||||
<p :key="error" v-for="error in popErrors" v-html="error"></p>
|
||||
</div>
|
||||
</div>
|
||||
</pop-date-range>
|
||||
</div>
|
||||
<div v-cloak="" v-show="$root.activeModal === removeModalId">
|
||||
<div :id='"modal--" + removeModalId' class="modal modal--dismissable">
|
||||
|
@ -1,9 +1,9 @@
|
||||
{% from "components/clin_dollar_amount.html" import CLINDollarAmount %}
|
||||
{% from 'components/alert.html' import Alert %}
|
||||
{% from 'components/date_picker.html' import DatePicker %}
|
||||
{% from 'components/icon.html' import Icon %}
|
||||
{% from 'components/options_input.html' import OptionsInput %}
|
||||
{% from 'components/text_input.html' import TextInput %}
|
||||
{% from 'components/pop_date_range.html' import PopDateRange %}
|
||||
|
||||
{% macro CLINFields(contract_start, contract_end, fields=None, index=None) %}
|
||||
<clin-fields
|
||||
@ -128,156 +128,11 @@
|
||||
{{ 'task_orders.form.pop' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
{% set contract_end_formatted = contract_end | dateFromString(formatter="%Y-%m-%d") | formattedDate(formatter="%B %d, %Y") %}
|
||||
{% if fields %}
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
{{ DatePicker(fields.start_date, watch=True, optional=False) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
{% call DatePicker(fields.end_date, watch=True, optional=False) %}
|
||||
{{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': contract_end_formatted})) }}
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
{{ PopDateRange(start_field=fields.start_date, end_field=fields.end_date, optional=False, mindate=contract_start, maxdate=contract_end) }}
|
||||
{% else %}
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector :name-tag="'clins-' + clinIndex + '-start_date'" :watch='true' :optional='false' inline-template>
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
{{ 'task_orders.form.pop_start' | translate }}
|
||||
</div>
|
||||
<p class='usa-input__help'>
|
||||
{{ 'task_orders.form.pop_example' | translate }}
|
||||
</p>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>{{ 'components.date_selector.month' | translate }}</label>
|
||||
<input
|
||||
name="date-month"
|
||||
max="12"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (month && !isMonthValid) }"
|
||||
v-model="month"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-day">
|
||||
<label>{{ 'components.date_selector.day' | translate }}</label>
|
||||
<input
|
||||
name="date-day"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (day && !isDayValid) }"
|
||||
v-bind:max="daysMaxCalculation"
|
||||
v-model="day"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>{{ 'components.date_selector.year' | translate }}</label>
|
||||
<input
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
{{ Icon("ok", classes="icon--green") }}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector :name-tag="'clins-' + clinIndex + '-end_date'" :watch='true' :optional='false' inline-template>
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
{{ 'task_orders.form.pop_end' | translate }}
|
||||
</div>
|
||||
|
||||
{{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': contract_end_formatted})) }}
|
||||
|
||||
<p class='usa-input__help'>
|
||||
{{ 'task_orders.form.pop_example' | translate }}
|
||||
</p>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>{{ 'components.date_selector.month' | translate }}</label>
|
||||
<input
|
||||
name="date-month"
|
||||
max="12"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (month && !isMonthValid) }"
|
||||
v-model="month"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-day">
|
||||
<label>{{ 'components.date_selector.day' | translate }}</label>
|
||||
<input
|
||||
name="date-day"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (day && !isDayValid) }"
|
||||
v-bind:max="daysMaxCalculation"
|
||||
v-model="day"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>{{ 'components.date_selector.year' | translate }}</label>
|
||||
<input
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
{{ Icon("ok", classes="icon--green") }}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
{{ PopDateRange(optional=False, mindate=contract_start, maxdate=contract_end) }}
|
||||
{% endif %}
|
||||
<div class="form-row">
|
||||
<div class="usa-input-error-message form-has-errors">
|
||||
<p v-for="error in popErrors" :key="error" v-html='error'></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="$root.activeModal === removeModalId" v-cloak>
|
||||
|
@ -7,7 +7,6 @@
|
||||
description=field.description,
|
||||
mindate=None,
|
||||
maxdate=None,
|
||||
watch=False,
|
||||
optional=True) -%}
|
||||
|
||||
<date-selector
|
||||
@ -17,11 +16,10 @@
|
||||
initialmonth="{{ field.data.month }}"
|
||||
initialday="{{ field.data.day }}"
|
||||
initialyear="{{ field.data.year }}"
|
||||
v-bind:watch='{{ watch | string | lower }}'
|
||||
:optional='{{ optional | string | lower }}'
|
||||
inline-template>
|
||||
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && showValidation }">
|
||||
<fieldset class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
{{ label }}
|
||||
@ -83,7 +81,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="showValidation">
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
{{ Icon("ok", classes="icon--green") }}
|
||||
</div>
|
||||
|
210
templates/components/pop_date_range.html
Normal file
210
templates/components/pop_date_range.html
Normal file
@ -0,0 +1,210 @@
|
||||
{% from 'components/alert.html' import Alert %}
|
||||
{% from 'components/icon.html' import Icon %}
|
||||
|
||||
{% macro PopDateRange(start_field=None, end_field=None, mindate=mindate, maxdate=maxdate, optional=True, index=None) %}
|
||||
<pop-date-range
|
||||
initial-min-start-date="{{ mindate }}"
|
||||
initial-max-end-date="{{ maxdate }}"
|
||||
{% if index %}
|
||||
v-bind:clin-index="{{ index }}"
|
||||
{% else %}
|
||||
v-bind:clin-index="clinIndex"
|
||||
{% endif %}
|
||||
{% if start_field %}
|
||||
initial-start-date="{{ start_field.data }}"
|
||||
{% endif %}
|
||||
{% if end_field %}
|
||||
initial-end-date="{{ end_field.data }}"
|
||||
{% endif %}
|
||||
inline-template>
|
||||
|
||||
<div>
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector
|
||||
:mindate="initialMinStartDate"
|
||||
:maxdate="maxStartProp"
|
||||
{% if start_field %}
|
||||
name-tag='{{ start_field.name }}'
|
||||
initialmonth="{{ start_field.data.month }}"
|
||||
initialday="{{ start_field.data.day }}"
|
||||
initialyear="{{ start_field.data.year }}"
|
||||
{% else %}
|
||||
:name-tag="'clins-' + clinIndex + '-start_date'"
|
||||
{% endif %}
|
||||
:optional='{{ optional | string | lower }}'
|
||||
v-on:date-change='handleDateChange'
|
||||
inline-template>
|
||||
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
{{ "task_orders.form.pop_start" | translate }}
|
||||
</div>
|
||||
|
||||
<p class='usa-input__help'>
|
||||
{{ "task_orders.form.pop_example" | translate | safe }}
|
||||
</p>
|
||||
|
||||
<div v-if='minError' class="usa-input-error-message">
|
||||
PoP start date must be on or after {{ mindate | formattedDate(formatter="%B %d, %Y") }}.
|
||||
</div>
|
||||
<div v-if='maxError' class="usa-input-error-message">
|
||||
PoP start date must be before end date.
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>{{ 'components.date_selector.month' | translate }}</label>
|
||||
<input
|
||||
name="date-month"
|
||||
max="12"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (month && !isMonthValid) }"
|
||||
v-model="month"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-day">
|
||||
<label>{{ 'components.date_selector.day' | translate }}</label>
|
||||
<input
|
||||
name="date-day"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (day && !isDayValid) }"
|
||||
v-bind:max="daysMaxCalculation"
|
||||
v-model="day"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>{{ 'components.date_selector.year' | translate }}</label>
|
||||
<input
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
{% if maxdate %}max="{{ maxdate.year }}"{% endif %}
|
||||
{% if mindate %}min="{{ mindate.year }}"{% endif %}
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
{{ Icon("ok", classes="icon--green") }}
|
||||
</div>
|
||||
<div class="usa-form-group-date-ok" v-else>
|
||||
{{ Icon("alert", classes="icon--red")}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-col">
|
||||
<date-selector
|
||||
:mindate="minEndProp"
|
||||
:maxdate="initialMaxEndDate"
|
||||
{% if end_field %}
|
||||
name-tag='{{ end_field.name }}'
|
||||
initialmonth="{{ end_field.data.month }}"
|
||||
initialday="{{ end_field.data.day }}"
|
||||
initialyear="{{ end_field.data.year }}"
|
||||
{% else %}
|
||||
:name-tag="'clins-' + clinIndex + '-end_date'"
|
||||
{% endif %}
|
||||
:optional='{{ optional | string | lower }}'
|
||||
v-on:date-change='handleDateChange'
|
||||
inline-template>
|
||||
|
||||
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid && isDateComplete, 'usa-input--error': !isDateValid && isDateComplete }">
|
||||
<legend>
|
||||
<div class="usa-input__title">
|
||||
{{ 'task_orders.form.pop_end' | translate }}
|
||||
</div>
|
||||
{% set formatted_end_date = maxdate | formattedDate(formatter="%B %d, %Y") %}
|
||||
{{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': formatted_end_date })) }}
|
||||
|
||||
<p class='usa-input__help'>
|
||||
{{ 'task_orders.form.pop_example' | translate }}
|
||||
</p>
|
||||
|
||||
<div v-if='minError' class="usa-input-error-message">
|
||||
PoP end date must be after start date.
|
||||
</div>
|
||||
<div v-if='maxError' class="usa-input-error-message">
|
||||
PoP end date must be on or after {{ formatted_end_date }}.
|
||||
</div>
|
||||
</legend>
|
||||
|
||||
<div class="date-picker-component">
|
||||
<input :name="name" v-bind:value="formattedDate" v-on:change="onInput" type="hidden" />
|
||||
|
||||
<div class="usa-form-group usa-form-group-month">
|
||||
<label>{{ 'components.date_selector.month' | translate }}</label>
|
||||
<input
|
||||
name="date-month"
|
||||
max="12"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (month && !isMonthValid) }"
|
||||
v-model="month"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-day">
|
||||
<label>{{ 'components.date_selector.day' | translate }}</label>
|
||||
<input
|
||||
name="date-day"
|
||||
maxlength="2"
|
||||
min="1"
|
||||
type="number"
|
||||
v-bind:class="{ 'usa-input-error': (day && !isDayValid) }"
|
||||
v-bind:max="daysMaxCalculation"
|
||||
v-model="day"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="usa-form-group usa-form-group-year">
|
||||
<label>{{ 'components.date_selector.year' | translate }}</label>
|
||||
<input
|
||||
name="date-year"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
v-model="year"
|
||||
v-on:change="onInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="isDateComplete">
|
||||
<div class="usa-form-group-date-ok" v-if="isDateValid">
|
||||
{{ Icon("ok", classes="icon--green") }}
|
||||
</div>
|
||||
<div class="usa-form-group-date-ok" v-else>
|
||||
{{ Icon("alert", classes="icon--red")}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</date-selector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</pop-date-range>
|
||||
{% endmacro %}
|
@ -38,12 +38,8 @@ def test_clin_form_start_date_before_end_date():
|
||||
|
||||
|
||||
def test_clin_form_pop_dates_within_contract_dates():
|
||||
CONTRACT_START_DATE = datetime.datetime.strptime(
|
||||
app.config.get("CONTRACT_START_DATE"), "%Y-%m-%d"
|
||||
).date()
|
||||
CONTRACT_END_DATE = datetime.datetime.strptime(
|
||||
app.config.get("CONTRACT_END_DATE"), "%Y-%m-%d"
|
||||
).date()
|
||||
CONTRACT_START_DATE = app.config.get("CONTRACT_START_DATE")
|
||||
CONTRACT_END_DATE = app.config.get("CONTRACT_END_DATE")
|
||||
|
||||
invalid_start = CONTRACT_START_DATE - relativedelta(months=1)
|
||||
invalid_end = CONTRACT_END_DATE + relativedelta(months=1)
|
||||
|
@ -135,3 +135,17 @@ def test_make_clin_fields(env, app):
|
||||
0,
|
||||
)
|
||||
write_template(clin_fields, "clin_fields.html")
|
||||
|
||||
|
||||
def test_make_pop_date_range(env, app):
|
||||
pop_date_range_template = env.get_template("components/pop_date_range.html")
|
||||
pop_date_range_macro = getattr(pop_date_range_template.module, "PopDateRange")
|
||||
form = CLINForm()
|
||||
pop_date_range = pop_date_range_macro(
|
||||
start_field=form.start_date,
|
||||
end_field=form.end_date,
|
||||
mindate=app.config.get("CONTRACT_START_DATE"),
|
||||
maxdate=app.config.get("CONTRACT_END_DATE"),
|
||||
index=1,
|
||||
)
|
||||
write_template(pop_date_range, "pop_date_range.html")
|
||||
|
Loading…
x
Reference in New Issue
Block a user