Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: CE-1260 add Outcome Date filter #794

Merged
merged 4 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ export interface ComplaintFilterParameters {
complaintMethod?: string;
actionTaken?: string;
outcomeAnimal?: string;
outcomeAnimalStartDate?: Date;
outcomeAnimalEndDate?: Date;
}
22 changes: 17 additions & 5 deletions backend/src/v1/complaint/complaint.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,11 @@ export class ComplaintService {
private readonly _getComplaintsByOutcomeAnimal = async (
token: string,
outcomeAnimalCode: string,
startDate: Date | undefined,
endDate: Date | undefined,
): Promise<string[]> => {
const { data, errors } = await get(token, {
query: `{getLeadsByOutcomeAnimal (outcomeAnimalCode: "${outcomeAnimalCode}")}`,
query: `{getLeadsByOutcomeAnimal (outcomeAnimalCode: "${outcomeAnimalCode}", startDate: "${startDate}" , endDate: "${endDate}")}`,
});
if (errors) {
this.logger.error("GraphQL errors:", errors);
Expand Down Expand Up @@ -975,8 +977,13 @@ export class ComplaintService {
}

// -- filter by complaint identifiers returned by case management if outcome animal filter is present
if (agency === "COS" && filters.outcomeAnimal) {
const complaintIdentifiers = await this._getComplaintsByOutcomeAnimal(token, filters.outcomeAnimal);
if (agency === "COS" && (filters.outcomeAnimal || filters.outcomeAnimalStartDate)) {
const complaintIdentifiers = await this._getComplaintsByOutcomeAnimal(
token,
filters.outcomeAnimal,
filters.outcomeAnimalStartDate,
filters.outcomeAnimalEndDate,
);

builder.andWhere("complaint.complaint_identifier IN(:...complaint_identifiers)", {
complaint_identifiers: complaintIdentifiers,
Expand Down Expand Up @@ -1144,8 +1151,13 @@ export class ComplaintService {
}

// -- filter by complaint identifiers returned by case management if outcome animal filter is present
if (agency === "COS" && filters.outcomeAnimal) {
const complaintIdentifiers = await this._getComplaintsByOutcomeAnimal(token, filters.outcomeAnimal);
if (agency === "COS" && (filters.outcomeAnimal || filters.outcomeAnimalStartDate)) {
const complaintIdentifiers = await this._getComplaintsByOutcomeAnimal(
token,
filters.outcomeAnimal,
filters.outcomeAnimalStartDate,
filters.outcomeAnimalEndDate,
);
complaintBuilder.andWhere("complaint.complaint_identifier IN(:...complaint_identifiers)", {
complaint_identifiers: complaintIdentifiers,
});
Expand Down
97 changes: 97 additions & 0 deletions frontend/src/app/components/common/filter-date.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { FC } from "react";
import DatePicker from "react-datepicker";

interface Props {
id: string;
label: string;
startDate: Date | undefined;
endDate: Date | undefined;
handleDateChange: (dates: [Date, Date]) => void;
}

export const FilterDate: FC<Props> = ({ id, label, startDate, endDate, handleDateChange }) => {
// manual entry of date change listener. Looks for a date range format of {yyyy-mm-dd} - {yyyy-mm-dd}
const handleManualDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e?.target?.value?.includes(" - ")) {
const [startDateStr, endDateStr] = e.target.value.split(" - ");
const startDate = new Date(startDateStr);
const endDate = new Date(endDateStr);

if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
// Invalid date format
return [null, null];
} else {
// add 1 to date because days start at 0
startDate.setDate(startDate.getDate() + 1);
endDate.setDate(endDate.getDate() + 1);

handleDateChange([startDate, endDate]);
}
}
return [null, null];
};

return (
<div id={id}>
<label htmlFor="date-range-picker-id">{label}</label>
<div className="filter-select-padding">
<DatePicker
id={`date-range-picker-${id}`}
showIcon={true}
renderCustomHeader={({ monthDate, customHeaderCount, decreaseMonth, increaseMonth }) => (
<div>
<button
aria-label="Previous Month"
className={`react-datepicker__navigation react-datepicker__navigation--previous ${
customHeaderCount === 1 ? "datepicker-nav-hidden" : "datepicker-nav-visible"
}`}
onClick={decreaseMonth}
>
<span
className={
"react-datepicker__navigation-icon react-datepicker__navigation-icon--previous datepicker-nav-icon"
}
>
{"<"}
</span>
</button>
<span className="react-datepicker__current-month">
{monthDate.toLocaleString("en-US", {
month: "long",
year: "numeric",
})}
</span>
<button
aria-label="Next Month"
className={`react-datepicker__navigation react-datepicker__navigation--next ${
customHeaderCount === 1 ? "datepicker-nav-hidden" : "datepicker-nav-visible"
}`}
onClick={increaseMonth}
>
<span
className={
"react-datepicker__navigation-icon react-datepicker__navigation-icon--next datepicker-nav-icon"
}
>
{">"}
</span>
</button>
</div>
)}
selected={startDate}
onChange={handleDateChange}
onChangeRaw={handleManualDateChange}
startDate={startDate}
endDate={endDate}
dateFormat="yyyy-MM-dd"
monthsShown={2}
selectsRange={true}
isClearable={true}
wrapperClassName="comp-filter-calendar-input"
showPreviousMonths
maxDate={new Date()}
/>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ export const ComplaintFilterBar: FC<Props> = ({
complaintMethod,
actionTaken,
outcomeAnimal,
outcomeAnimalStartDate,
outcomeAnimalEndDate,
} = state;

const dateRangeLabel = (): string | undefined => {
const dateRangeLabel = (startDate: Date | undefined | null, endDate: Date | undefined | null): string | undefined => {
const currentDate = new Date().toLocaleDateString();
if (startDate !== null && endDate !== null) {
return `${startDate?.toLocaleDateString()} - ${endDate?.toLocaleDateString()}`;
Expand All @@ -58,7 +60,7 @@ export const ComplaintFilterBar: FC<Props> = ({
}
};

const hasDate = () => {
const hasDate = (startDate: Date | undefined | null, endDate: Date | undefined | null) => {
if ((startDate === undefined || startDate === null) && (endDate === undefined || endDate === null)) {
return false;
}
Expand All @@ -78,6 +80,10 @@ export const ComplaintFilterBar: FC<Props> = ({
dispatch(clearFilter("startDate"));
dispatch(clearFilter("endDate"));
break;
case "outcomeAnimalDateRange":
dispatch(clearFilter("outcomeAnimalStartDate"));
dispatch(clearFilter("outcomeAnimalEndDate"));
break;
default:
dispatch(clearFilter(name));
break;
Expand Down Expand Up @@ -133,10 +139,10 @@ export const ComplaintFilterBar: FC<Props> = ({
/>
)}

{hasDate() && (
{hasDate(startDate, endDate) && (
<FilterButton
id="comp-date-range-filter"
label={dateRangeLabel()}
label={dateRangeLabel(startDate, endDate)}
name="dateRange"
clear={removeFilter}
/>
Expand Down Expand Up @@ -240,6 +246,15 @@ export const ComplaintFilterBar: FC<Props> = ({
clear={removeFilter}
/>
)}

{hasDate(outcomeAnimalStartDate, outcomeAnimalEndDate) && (
<FilterButton
id="comp-complaint-method-filter"
label={dateRangeLabel(outcomeAnimalStartDate, outcomeAnimalEndDate)}
name="outcomeAnimalDateRange"
clear={removeFilter}
/>
)}
</div>
</div>
);
Expand Down
114 changes: 34 additions & 80 deletions frontend/src/app/components/containers/complaints/complaint-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import {
import { selectOfficersByAgencyDropdownUsingPersonGuid } from "@store/reducers/officer";
import { selectDecisionTypeDropdown } from "@store/reducers/code-table-selectors";
import COMPLAINT_TYPES from "@apptypes/app/complaint-types";
import DatePicker from "react-datepicker";
import { CompSelect } from "@components/common/comp-select";
import { ComplaintFilterContext } from "@providers/complaint-filter-provider";
import { ComplaintFilterPayload, updateFilter } from "@store/reducers/complaint-filters";
import Option from "@apptypes/app/option";
import { listActiveFilters } from "@store/reducers/app";
import UserService from "@service/user-service";
import Roles from "@apptypes/app/roles";
import { FilterDate } from "../../common/filter-date";

type Props = {
type: string;
Expand All @@ -47,6 +47,8 @@ export const ComplaintFilter: FC<Props> = ({ type }) => {
complaintMethod,
actionTaken,
outcomeAnimal,
outcomeAnimalStartDate,
outcomeAnimalEndDate,
},
dispatch,
} = useContext(ComplaintFilterContext);
Expand Down Expand Up @@ -96,24 +98,20 @@ export const ComplaintFilter: FC<Props> = ({ type }) => {
setFilter("endDate", end);
};

// manual entry of date change listener. Looks for a date range format of {yyyy-mm-dd} - {yyyy-mm-dd}
const handleManualDateRangeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e?.target?.value?.includes(" - ")) {
const [startDateStr, endDateStr] = e.target.value.split(" - ");
const startDate = new Date(startDateStr);
const endDate = new Date(endDateStr);

if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
// Invalid date format
return [null, null];
} else {
// add 1 to date because days start at 0
startDate.setDate(startDate.getDate() + 1);
endDate.setDate(endDate.getDate() + 1);
handleDateRangeChange([startDate, endDate]);
}
const handleOutcomeDateRangeChange = (dates: [Date, Date]) => {
const [start, end] = dates;
//set the time to be end of day to ensure that we also search for records after the beginning of the selected day.
if (start) {
start.setHours(0, 0, 0);
start.setMilliseconds(0);
}
if (end) {
end.setHours(23, 59, 59);
end.setMilliseconds(999);
}
return [null, null];

setFilter("outcomeAnimalStartDate", start);
setFilter("outcomeAnimalEndDate", end);
};

///--
Expand Down Expand Up @@ -221,67 +219,13 @@ export const ComplaintFilter: FC<Props> = ({ type }) => {
)}

{activeFilters.showDateFilter && (
<div id="comp-filter-date-id">
<label htmlFor="date-range-picker-id">Date Logged</label>
<div className="filter-select-padding">
<DatePicker
id="date-range-picker-id"
showIcon={true}
renderCustomHeader={({ monthDate, customHeaderCount, decreaseMonth, increaseMonth }) => (
<div>
<button
aria-label="Previous Month"
className={`react-datepicker__navigation react-datepicker__navigation--previous ${
customHeaderCount === 1 ? "datepicker-nav-hidden" : "datepicker-nav-visible"
}`}
onClick={decreaseMonth}
>
<span
className={
"react-datepicker__navigation-icon react-datepicker__navigation-icon--previous datepicker-nav-icon"
}
>
{"<"}
</span>
</button>
<span className="react-datepicker__current-month">
{monthDate.toLocaleString("en-US", {
month: "long",
year: "numeric",
})}
</span>
<button
aria-label="Next Month"
className={`react-datepicker__navigation react-datepicker__navigation--next ${
customHeaderCount === 1 ? "datepicker-nav-hidden" : "datepicker-nav-visible"
}`}
onClick={increaseMonth}
>
<span
className={
"react-datepicker__navigation-icon react-datepicker__navigation-icon--next datepicker-nav-icon"
}
>
{">"}
</span>
</button>
</div>
)}
selected={startDate}
onChange={handleDateRangeChange}
onChangeRaw={handleManualDateRangeChange}
startDate={startDate}
endDate={endDate}
dateFormat="yyyy-MM-dd"
monthsShown={2}
selectsRange={true}
isClearable={true}
wrapperClassName="comp-filter-calendar-input"
showPreviousMonths
maxDate={new Date()}
/>
</div>
</div>
<FilterDate
id="comp-filter-date-id"
label="Date Logged"
startDate={startDate}
endDate={endDate}
handleDateChange={handleDateRangeChange}
/>
)}

{activeFilters.showStatusFilter && (
Expand Down Expand Up @@ -364,7 +308,7 @@ export const ComplaintFilter: FC<Props> = ({ type }) => {
setFilter("outcomeAnimal", option);
}}
classNames={{
menu: () => "top-layer-select",
menu: () => "top-layer-select outcome-animal-select",
}}
options={outcomeAnimalTypes}
placeholder="Select"
Expand All @@ -375,6 +319,16 @@ export const ComplaintFilter: FC<Props> = ({ type }) => {
</div>
</div>
)}

{COMPLAINT_TYPES.HWCR === type && activeFilters.showOutcomeAnimalDateFilter && (
<FilterDate
id="comp-filter-outcome-date-id"
label="Outcome Date"
startDate={outcomeAnimalStartDate}
endDate={outcomeAnimalEndDate}
handleDateChange={handleOutcomeDateRangeChange}
/>
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export const generateComplaintRequestPayload = (
complaintMethod,
actionTaken,
outcomeAnimal,
outcomeAnimalStartDate,
outcomeAnimalEndDate,
} = filters;

const common = {
Expand Down Expand Up @@ -92,6 +94,8 @@ export const generateComplaintRequestPayload = (
speciesCodeFilter: species,
natureOfComplaintFilter: natureOfComplaint,
outcomeAnimalFilter: outcomeAnimal,
outcomeAnimalStartDateFilter: outcomeAnimalStartDate,
outcomeAnimalEndDateFilter: outcomeAnimalEndDate,
} as ComplaintRequestPayload;
}
};
Expand Down
Loading
Loading