Skip to content

Commit

Permalink
#3024: Extract date and duration fields from report form
Browse files Browse the repository at this point in the history
Move those fields to a dedicated component in order to implement
all-day related features in isolation.
  • Loading branch information
ysf-simsoft committed Jun 22, 2020
1 parent 73f5e0b commit 9ca8def
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 42 deletions.
21 changes: 20 additions & 1 deletion client/src/models/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,26 @@ export default class Report extends Model {
.nullable()
.required("You must provide the Date of Engagement")
.default(null),
duration: yup.number().nullable().default(null),
duration: yup
.number()
.nullable()
.test(
"duration",
"Duration must be defined when engagement time is set!",
function(duration) {
const { engagementDate } = this.parent
if (
!engagementDate ||
moment(engagementDate).isSame(
moment(engagementDate).startOf("day")
)
) {
return true
}
return !!duration
}
)
.default(null),
// not actually in the database, but used for validation:
cancelled: yup
.boolean()
Expand Down
81 changes: 81 additions & 0 deletions client/src/pages/reports/EngagementDateFormPartial.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import CustomDateInput from "components/CustomDateInput"
import * as FieldHelper from "components/FieldHelper"
import { FastField } from "formik"
import { Report } from "models"
import PropTypes from "prop-types"
import React from "react"
import { HelpBlock } from "react-bootstrap"
import Settings from "settings"

const futureEngagementHint = (
<HelpBlock>
<span className="text-success">This will create a planned engagement</span>
</HelpBlock>
)

const EngagementDateFormPartial = ({
setFieldValue,
setFieldTouched,
validateFieldDebounced,
initialValues,
edit,
values
}) => {
if (!Settings.engagementsIncludeTimeAndDuration) {
return (
<FastField
name="engagementDate"
component={FieldHelper.SpecialField}
onChange={value => {
setFieldTouched("engagementDate", true, false) // onBlur doesn't work when selecting a date
setFieldValue("engagementDate", value, true)
}}
onBlur={() => setFieldTouched("engagementDate")}
widget={<CustomDateInput id="engagementDate" />}
>
{Report.isFuture(values.engagementDate) && futureEngagementHint}
</FastField>
)
}

return (
<>
<FastField
name="engagementDate"
component={FieldHelper.SpecialField}
onChange={value => {
setFieldTouched("engagementDate", true, false) // onBlur doesn't work when selecting a date
setFieldValue("engagementDate", value, true)
}}
onBlur={() => setFieldTouched("engagementDate")}
widget={<CustomDateInput id="engagementDate" withTime />}
>
{Report.isFuture(values.engagementDate) && futureEngagementHint}
</FastField>

<FastField
name="duration"
label="Duration (minutes)"
component={FieldHelper.InputField}
onChange={event => {
const safeVal =
(event.target.value || "").replace(/[^0-9]+/g, "") || null
setFieldTouched("duration", true, false)
setFieldValue("duration", safeVal, false)
validateFieldDebounced("duration")
}}
/>
</>
)
}

EngagementDateFormPartial.propTypes = {
setFieldValue: PropTypes.func.isRequired,
setFieldTouched: PropTypes.func.isRequired,
validateFieldDebounced: PropTypes.func.isRequired,
values: PropTypes.object.isRequired,
initialValues: PropTypes.instanceOf(Report).isRequired,
edit: PropTypes.bool.isRequired
}

export default EngagementDateFormPartial
51 changes: 10 additions & 41 deletions client/src/pages/reports/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import AdvancedSingleSelect from "components/advancedSelectWidget/AdvancedSingleSelect"
import AppContext from "components/AppContext"
import ConfirmDelete from "components/ConfirmDelete"
import CustomDateInput from "components/CustomDateInput"
import {
CustomFieldsContainer,
customFieldsJSONString
Expand All @@ -39,10 +38,11 @@ import _isEmpty from "lodash/isEmpty"
import _upperFirst from "lodash/upperFirst"
import { AuthorizationGroup, Location, Person, Report, Task } from "models"
import moment from "moment"
import EngagementDateFormPartial from "pages/reports/EngagementDateFormPartial"
import pluralize from "pluralize"
import PropTypes from "prop-types"
import React, { useEffect, useState } from "react"
import { Button, Checkbox, Collapse, HelpBlock } from "react-bootstrap"
import { Button, Checkbox, Collapse } from "react-bootstrap"
import { connect } from "react-redux"
import { useHistory } from "react-router-dom"
import { toast } from "react-toastify"
Expand Down Expand Up @@ -566,45 +566,14 @@ const BaseReportForm = ({
className="meeting-goal"
/>

<FastField
name="engagementDate"
component={FieldHelper.SpecialField}
onChange={value => {
setFieldTouched("engagementDate", true, false) // onBlur doesn't work when selecting a date
setFieldValue("engagementDate", value, true)
}}
onBlur={() => setFieldTouched("engagementDate")}
widget={
<CustomDateInput
id="engagementDate"
withTime={Settings.engagementsIncludeTimeAndDuration}
/>
}
>
{isFutureEngagement && (
<HelpBlock>
<span className="text-success">
This will create a planned engagement
</span>
</HelpBlock>
)}
</FastField>

{Settings.engagementsIncludeTimeAndDuration && (
<FastField
name="duration"
label="Duration (minutes)"
component={FieldHelper.InputField}
onChange={event => {
const safeVal =
(event.target.value || "").replace(/[^0-9]+/g, "") ||
null
setFieldTouched("duration", true, false)
setFieldValue("duration", safeVal, false)
validateFieldDebounced("duration")
}}
/>
)}
<EngagementDateFormPartial
setFieldValue={setFieldValue}
setFieldTouched={setFieldTouched}
validateFieldDebounced={validateFieldDebounced}
values={values}
initialValues={initialValues}
edit={edit}
/>

<FastField
name="location"
Expand Down
3 changes: 3 additions & 0 deletions client/tests/e2e/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ test.serial("Draft and submit a report", async t => {
const $intent = await $("#intent")
await $intent.click() // click intent to make sure the date picker is being closed

await pageHelpers.writeInForm("#duration", "30")
await t.context.driver.sleep(shortWaitMs) // wait for the datepicker to pop up

const $locationAdvancedSelect = await pageHelpers.chooseAdvancedSelectOption(
"#location",
"general hospit"
Expand Down

0 comments on commit 9ca8def

Please sign in to comment.