Skip to content

Commit

Permalink
Merge pull request #770 from bcgov/fix/report-off-by-one
Browse files Browse the repository at this point in the history
Fix: off-by-one report dates bug
  • Loading branch information
matthieu-foucault authored Jun 23, 2022
2 parents 13218c5 + 34d5c45 commit d4fc967
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 125 deletions.
14 changes: 6 additions & 8 deletions app/components/Form/ProjectAnnualReportForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import ReportDueIndicator from "components/ReportingRequirement/ReportDueIndicat
import Status from "components/ReportingRequirement/Status";
import projectReportingRequirementSchema from "data/jsonSchemaForm/projectReportingRequirementSchema";
import { JSONSchema7 } from "json-schema";
import { getReportingStatus, isOverdue } from "lib/helpers/reportStatusHelpers";
import FormBorder from "lib/theme/components/FormBorder";
import EmptyObjectFieldTemplate from "lib/theme/EmptyObjectFieldTemplate";
import { useAddReportingRequirementToRevision } from "mutations/ProjectReportingRequirement/addReportingRequirementToRevision";
Expand Down Expand Up @@ -119,21 +118,16 @@ const ProjectAnnualReportForm: React.FC<Props> = (props) => {
);
}, [projectRevision.projectAnnualReportFormChanges]);

const reportDueDate =
const upcomingReportDueDate =
projectRevision.upcomingAnnualReportFormChange?.asReportingRequirement
.reportDueDate;
const overdue = useMemo(() => {
return isOverdue(reportDueDate);
}, [reportDueDate]);

const reportSubmittedDates = useMemo(() => {
return projectRevision.projectAnnualReportFormChanges.edges.map(
({ node }) => node.newFormData.submittedDate
);
}, [projectRevision.projectAnnualReportFormChanges.edges]);

const variant = getReportingStatus(reportSubmittedDates, overdue);

return (
<div>
<header>
Expand All @@ -142,7 +136,11 @@ const ProjectAnnualReportForm: React.FC<Props> = (props) => {
<SavingIndicator isSaved={!isUpdating && !isAdding} />
</header>
<h3>Status</h3>
<Status variant={variant} reportType={"Annual"} />
<Status
upcomingReportDueDate={upcomingReportDueDate}
reportSubmittedDates={reportSubmittedDates}
reportType={"Annual"}
/>

<ReportDueIndicator
reportTitle="Annual Report"
Expand Down
14 changes: 6 additions & 8 deletions app/components/Form/ProjectMilestoneReportForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
projectMilestoneSchema,
} from "data/jsonSchemaForm/projectMilestoneSchema";
import { JSONSchema7, JSONSchema7Definition } from "json-schema";
import { getReportingStatus, isOverdue } from "lib/helpers/reportStatusHelpers";
import FormBorder from "lib/theme/components/FormBorder";
import EmptyObjectFieldTemplate from "lib/theme/EmptyObjectFieldTemplate";
import { useAddReportingRequirementToRevision } from "mutations/ProjectReportingRequirement/addReportingRequirementToRevision";
Expand Down Expand Up @@ -171,29 +170,28 @@ const ProjectMilestoneReportForm: React.FC<Props> = (props) => {
);
}, [projectRevision.projectMilestoneReportFormChanges]);

const reportDueDate =
const upcomingReportDueDate =
projectRevision.upcomingMilestoneReportFormChange?.asReportingRequirement
.reportDueDate;
const overdue = useMemo(() => {
return isOverdue(reportDueDate);
}, [reportDueDate]);

const reportSubmittedDates = useMemo(() => {
return projectRevision.projectMilestoneReportFormChanges.edges.map(
({ node }) => node.newFormData.submittedDate
);
}, [projectRevision.projectMilestoneReportFormChanges.edges]);

const variant = getReportingStatus(reportSubmittedDates, overdue);

return (
<div>
<header id={`Milestone0`}>
<h2>Milestone Reports</h2>
<UndoChangesButton formChangeIds={formChangeIds} formRefs={formRefs} />
<SavingIndicator isSaved={!isUpdating && !isAdding} />
</header>
<Status variant={variant} reportType={"Milestone"} />
<Status
upcomingReportDueDate={upcomingReportDueDate}
reportSubmittedDates={reportSubmittedDates}
reportType={"Milestone"}
/>
<ReportDueIndicator
reportTitle="Milestone Report"
reportDueFormChange={projectRevision.upcomingMilestoneReportFormChange}
Expand Down
18 changes: 8 additions & 10 deletions app/components/Form/ProjectQuarterlyReportForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Button } from "@button-inc/bcgov-theme";
import { faPlusCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import CollapsibleReport from "components/ReportingRequirement/CollapsibleReport";
import ReportDueIndicator from "components/ReportingRequirement/ReportDueIndicator";
import Status from "components/ReportingRequirement/Status";
import projectReportingRequirementSchema from "data/jsonSchemaForm/projectReportingRequirementSchema";
import { JSONSchema7 } from "json-schema";
import { getReportingStatus, isOverdue } from "lib/helpers/reportStatusHelpers";
import FormBorder from "lib/theme/components/FormBorder";
import EmptyObjectFieldTemplate from "lib/theme/EmptyObjectFieldTemplate";
import { useAddReportingRequirementToRevision } from "mutations/ProjectReportingRequirement/addReportingRequirementToRevision";
Expand All @@ -23,8 +24,6 @@ import {
} from "./reportingRequirementFormChangeFunctions";
import SavingIndicator from "./SavingIndicator";
import UndoChangesButton from "./UndoChangesButton";
import CollapsibleReport from "components/ReportingRequirement/CollapsibleReport";
import Status from "components/ReportingRequirement/Status";

interface Props {
onSubmit: () => void;
Expand Down Expand Up @@ -111,21 +110,16 @@ const ProjectQuarterlyReportForm: React.FC<Props> = (props) => {
);
}, [projectRevision.projectQuarterlyReportFormChanges]);

const reportDueDate =
const upcomingReportDueDate =
projectRevision.upcomingQuarterlyReportFormChange?.asReportingRequirement
.reportDueDate;
const overdue = useMemo(() => {
return isOverdue(reportDueDate);
}, [reportDueDate]);

const reportSubmittedDates = useMemo(() => {
return projectRevision.projectQuarterlyReportFormChanges.edges.map(
({ node }) => node.newFormData.submittedDate
);
}, [projectRevision.projectQuarterlyReportFormChanges.edges]);

const variant = getReportingStatus(reportSubmittedDates, overdue);

return (
<div>
<header>
Expand All @@ -134,7 +128,11 @@ const ProjectQuarterlyReportForm: React.FC<Props> = (props) => {
<SavingIndicator isSaved={!isUpdating && !isAdding} />
</header>
<h3>Status</h3>
<Status variant={variant} reportType={"Quarterly"} />
<Status
upcomingReportDueDate={upcomingReportDueDate}
reportSubmittedDates={reportSubmittedDates}
reportType={"Quarterly"}
/>
<ReportDueIndicator
reportTitle="Quarterly Report"
reportDueFormChange={projectRevision.upcomingQuarterlyReportFormChange}
Expand Down
7 changes: 5 additions & 2 deletions app/components/ReportingRequirement/CollapsibleReport.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { getBadgeForDates } from "lib/helpers/reportStatusHelpers";
import { getBadgeForIndividualReportStatus } from "lib/helpers/reportStatusHelpers";
import { useState } from "react";
import { graphql, useFragment } from "react-relay";
import { CollapsibleReport_reportingRequirement$key } from "__generated__/CollapsibleReport_reportingRequirement.graphql";
Expand Down Expand Up @@ -38,7 +38,10 @@ const CollapsibleReport: React.FC<Props> = ({
<header className="reportHeader" onClick={() => setIsOpen(!isOpen)}>
<h3>{title}</h3>
<div className="reportStatus">
{getBadgeForDates(data.reportDueDate, data.submittedDate)}
{getBadgeForIndividualReportStatus(
data.reportDueDate,
data.submittedDate
)}
</div>
<div className="toggleIcon">
<FontAwesomeIcon icon={isOpen ? faChevronUp : faChevronDown} />
Expand Down
9 changes: 6 additions & 3 deletions app/components/ReportingRequirement/ReportDueIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { faCalendarAlt, faCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { daysUntilReportDue, isOverdue } from "lib/helpers/reportStatusHelpers";
import {
getDaysUntilDue,
parseStringDate,
} from "lib/helpers/reportStatusHelpers";
import FormBorder from "lib/theme/components/FormBorder";
import { DateTime } from "luxon";
import Link from "next/link";
Expand Down Expand Up @@ -39,8 +42,8 @@ const ReportDueIndicator: React.FC<Props> = ({
const reportingRequirement = formChange?.reportingRequirement;
const hasValidReportDueDate = reportingRequirement?.reportDueDate;

const reportDueIn = daysUntilReportDue(hasValidReportDueDate);
const overdue = isOverdue(hasValidReportDueDate);
const reportDueIn = getDaysUntilDue(parseStringDate(hasValidReportDueDate));
const overdue = reportDueIn < 0 ? true : false;

return (
<>
Expand Down
17 changes: 13 additions & 4 deletions app/components/ReportingRequirement/Status.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import StatusBadge from "components/StatusBadge";
import { getBadgeForOverallReportStatus } from "lib/helpers/reportStatusHelpers";

interface Props {
reportType: "Quarterly" | "Annual" | "Milestone";
variant: "complete" | "late" | "onTrack" | "inReview" | "none";
upcomingReportDueDate: string;
reportSubmittedDates: string[];
}

const Status: React.FC<Props> = ({ reportType, variant }) => {
const Status: React.FC<Props> = ({
reportType,
upcomingReportDueDate,
reportSubmittedDates,
}) => {
return (
<div>
Status of {reportType} Reports <StatusBadge variant={variant} />
Status of {reportType} Reports{" "}
{getBadgeForOverallReportStatus(
upcomingReportDueDate,
reportSubmittedDates
)}
<style jsx>{`
div {
margin-bottom: 1em;
Expand Down
74 changes: 35 additions & 39 deletions app/lib/helpers/reportStatusHelpers.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
import StatusBadge from "components/StatusBadge";
import { DateTime } from "luxon";

export const daysUntilReportDue = (reportDueDate: string) => {
const diff = DateTime.fromISO(reportDueDate, {
setZone: true,
locale: "en-CA",
}).diff(
// Current date without time information
DateTime.now().setZone("America/Vancouver").startOf("day"),
"days"
export const parseStringDate = (stringDate: string) => {
if (!stringDate) return null;
return (
stringDate &&
DateTime.fromISO(stringDate, {
setZone: true,
locale: "en-CA",
}).startOf("day")
);

return Math.ceil(diff.days);
};

export const isOverdue = (reportDueDate: string | undefined) => {
return reportDueDate ? daysUntilReportDue(reportDueDate) < 0 : false;
export const getDaysUntilDue = (reportDueDate: DateTime) => {
if (!reportDueDate) {
return null;
}
return Math.floor(
reportDueDate.diff(
// Current date without time information
DateTime.now().setZone("America/Vancouver").startOf("day"),
"days"
).days
);
};

export const getReportingStatus = (
reportSubmittedDates: string[],
isReportOverdue: boolean
export const getBadgeForOverallReportStatus = (
upcomingReportDueDateString: string,
reportSubmittedDates: string[]
) => {
const parsedDueDate = parseStringDate(upcomingReportDueDateString);
const reportsExist = reportSubmittedDates?.length !== 0;

if (!reportsExist) {
return "none";
return <StatusBadge variant="none" />;
} else if (reportsExist && !reportSubmittedDates.includes(undefined)) {
return "complete";
} else if (isReportOverdue) {
return "late";
return <StatusBadge variant="complete" />;
} else {
return "onTrack";
const dueIn = getDaysUntilDue(parsedDueDate);
return dueIn < 0 ? (
<StatusBadge variant="late" />
) : (
<StatusBadge variant="onTrack" />
);
}
};

export const getBadgeForDates = (
export const getBadgeForIndividualReportStatus = (
reportDueDateString: string,
submittedDateString: string
) => {
const parsedDueDate =
reportDueDateString &&
DateTime.fromISO(reportDueDateString, {
setZone: true,
locale: "en-CA",
}).startOf("day");
const parsedSubmittedDate =
submittedDateString &&
DateTime.fromISO(submittedDateString, {
setZone: true,
locale: "en-CA",
}).startOf("day");
const parsedDueDate = parseStringDate(reportDueDateString);
const parsedSubmittedDate = parseStringDate(submittedDateString);

if (parsedDueDate && parsedSubmittedDate) {
{
Expand All @@ -64,11 +64,7 @@ export const getBadgeForDates = (
);
}
} else if (parsedDueDate && !parsedSubmittedDate) {
const dueIn = parsedDueDate.diff(
// Current date without time information
DateTime.now().setZone("America/Vancouver").startOf("day"),
"days"
).days;
const dueIn = getDaysUntilDue(parsedDueDate);

return dueIn < 0 ? (
<StatusBadge variant="late" />
Expand Down
51 changes: 0 additions & 51 deletions app/tests/unit/lib/helpers/reportStatusHelpers.test.ts

This file was deleted.

Loading

0 comments on commit d4fc967

Please sign in to comment.