Skip to content

Commit

Permalink
feature/CE-32-Add-Attachments-When-Creating-New-Complaint (#223)
Browse files Browse the repository at this point in the history
Co-authored-by: afwilcox <[email protected]>
  • Loading branch information
barrfalk and afwilcox authored Dec 11, 2023
1 parent c4e94b5 commit a032b82
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 87 deletions.
12 changes: 6 additions & 6 deletions frontend/cypress/e2e/allegation-details-create.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,22 @@ describe("Complaint Create Page spec - Create View", () => {
cy.navigateToCreateScreen();

cy.selectItemById("complaint-type-select-id", "Enforcement");
cy.get("#caller-name-id").clear().type(createCallerInformation.name);
cy.get("#complaint-address-id")
cy.get("#caller-name-id").click({ force: true }).clear().type(createCallerInformation.name);
cy.get("#complaint-address-id").click({ force: true })
.clear()
.type(createCallerInformation.address);
cy.get("#complaint-email-id").clear().type(createCallerInformation.email);
cy.get("#complaint-email-id").click({ force: true }).clear().type(createCallerInformation.email);

cy.get("#caller-primary-phone-id").click({ force: true });
cy.get("#caller-primary-phone-id").clear();
cy.get("#caller-primary-phone-id").typeAndTriggerChange(
createCallerInformation.phoneInput,
);

cy.get("#caller-info-secondary-phone-id")
cy.get("#caller-info-secondary-phone-id").click({ force: true })
.clear()
.typeAndTriggerChange(createCallerInformation.secondaryInput);
cy.get("#caller-info-alternate-phone-id")
cy.get("#caller-info-alternate-phone-id").click({ force: true })
.clear()
.typeAndTriggerChange(createCallerInformation.alternateInput);

Expand All @@ -80,7 +80,7 @@ describe("Complaint Create Page spec - Create View", () => {
cy.get("#complaint-location-description-textarea-id").click({
force: true,
});
cy.get("#complaint-location-description-textarea-id")
cy.get("#complaint-location-description-textarea-id").click({ force: true })
.clear()
.type(createCallDetails.locationDescription, { delay: 0 });
cy.get("#complaint-description-textarea-id").click({ force: true });
Expand Down
7 changes: 6 additions & 1 deletion frontend/cypress/e2e/complaint-attachments.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe("Complaint Attachments", () => {
});

Cypress._.times(complaintTypes.length, (index) => {
it("Verifies that upload option exists ", () => {
it("Verifies that upload option exists on edit page", () => {
if ("#hwcr-tab".includes(complaintTypes[index])) {
cy.navigateToEditScreen(COMPLAINT_TYPES.HWCR, "23-000076");
} else {
Expand All @@ -60,4 +60,9 @@ describe("Complaint Attachments", () => {
});

});

it("Verifies that upload option exists on the create page", () => {
cy.navigateToCreateScreen();
cy.get("button.coms-carousel-upload-container").should("exist");
});
});
65 changes: 65 additions & 0 deletions frontend/src/app/common/attachment-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
deleteAttachments,
getAttachments,
saveAttachments,
} from "../store/reducers/attachments";
import { COMSObject } from "../types/coms/object";

// used to update the state of attachments that are to be added to a complaint
export const handleAddAttachments = (
setAttachmentsToAdd: React.Dispatch<React.SetStateAction<File[] | null>>,
selectedFiles: File[]
) => {
setAttachmentsToAdd((prevFiles) =>
prevFiles ? [...prevFiles, ...selectedFiles] : selectedFiles
);
};

// used to update the state of attachments that are to be deleted from a complaint
export const handleDeleteAttachments = (
attachmentsToAdd: File[] | null,
setAttachmentsToAdd: React.Dispatch<React.SetStateAction<File[] | null>>,
setAttachmentsToDelete: React.Dispatch<
React.SetStateAction<COMSObject[] | null>
>,
fileToDelete: COMSObject
) => {
if (!fileToDelete.pendingUpload) {
// a user is wanting to delete a previously uploaded attachment
setAttachmentsToDelete((prevFiles) =>
prevFiles ? [...prevFiles, fileToDelete] : [fileToDelete]
);
} else if (attachmentsToAdd) {
// a user has added an attachment and deleted it, before the complaint was saved. Let's make sure this file isn't uploaded, so remove it from the "attachmentsToAdd" state
setAttachmentsToAdd((prevAttachments) =>
prevAttachments
? prevAttachments.filter((file) => decodeURIComponent(file.name) !== decodeURIComponent(fileToDelete.name))
: null
);
}
};

// Given a list of attachments to add/delete, call COMS to add/delete those attachments
export async function handlePersistAttachments(
dispatch: any,
attachmentsToAdd: File[] | null,
attachmentsToDelete: COMSObject[] | null,
complaintIdentifier: string,
setAttachmentsToAdd: any,
setAttachmentsToDelete: any
) {
if (attachmentsToDelete) {
await dispatch(deleteAttachments(attachmentsToDelete));
}

if (attachmentsToAdd) {
await dispatch(saveAttachments(attachmentsToAdd, complaintIdentifier));
}

// refresh store
await dispatch(getAttachments(complaintIdentifier));

// Clear the attachments since they've been added or saved.
setAttachmentsToAdd(null);
setAttachmentsToDelete(null);
}
18 changes: 15 additions & 3 deletions frontend/src/app/components/common/attachments-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { selectMaxFileSize } from "../../store/reducers/app";
import { v4 as uuidv4 } from 'uuid';

type Props = {
complaintIdentifier: string;
complaintIdentifier?: string;
allowUpload?: boolean;
allowDelete?: boolean;
onFilesSelected?: (attachments: File[]) => void;
Expand Down Expand Up @@ -53,15 +53,17 @@ export const AttachmentsCarousel: FC<Props> = ({
// when the carousel data updates (from the selector, on load), populate the carousel slides
useEffect(() => {
if (carouselData) {
setSlides(carouselData);
setSlides(sortAttachmentsByName(carouselData));
} else {
setSlides([])
}
}, [carouselData]);

// get the attachments when the complaint loads
useEffect(() => {
dispatch(getAttachments(complaintIdentifier));
if (complaintIdentifier) {
dispatch(getAttachments(complaintIdentifier));
}
}, [complaintIdentifier, dispatch]);

//-- when the component unmounts clear the attachments from redux
Expand All @@ -71,6 +73,16 @@ export const AttachmentsCarousel: FC<Props> = ({
};
}, [dispatch]);

function sortAttachmentsByName(comsObjects: COMSObject[]): COMSObject[] {
// Create a copy of the array using slice() or spread syntax
const copy = [...comsObjects];

// Sort the copy based on the name property
copy.sort((a, b) => a.name.localeCompare(b.name));

return copy;
}

// when a user selects files (via the file browser that pops up when clicking the upload slide) then add them to the carousel
const onFileSelect = (newFiles: FileList) => {
const selectedFilesArray = Array.from(newFiles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { useNavigate } from "react-router-dom";
import { ComplaintLocation } from "./complaint-location";
import { AttachmentsCarousel } from "../../../common/attachments-carousel";
import { COMSObject } from "../../../../types/coms/object";
import { handleAddAttachments, handleDeleteAttachments, handlePersistAttachments } from "../../../../common/attachment-utils";

export const CreateComplaint: FC = () => {
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -225,6 +228,21 @@ export const CreateComplaint: FC = () => {
HwcrComplaint | AllegationComplaint
>(newEmptyComplaint);

// files to add to COMS when complaint is saved
const [attachmentsToAdd, setAttachmentsToAdd] = useState<File[] | null>(null);

// files to remove from COMS when complaint is saved
const [attachmentsToDelete, setAttachmentsToDelete] = useState<COMSObject[] | null>(null);

const onHandleAddAttachments = (selectedFiles: File[]) => {
handleAddAttachments(setAttachmentsToAdd, selectedFiles);
};

const onHandleDeleteAttachment = (fileToDelete: COMSObject) => {
handleDeleteAttachments(attachmentsToAdd, setAttachmentsToAdd, setAttachmentsToDelete, fileToDelete);
};


function noErrors() {
let noErrors = false;
if (
Expand Down Expand Up @@ -955,7 +973,20 @@ export const CreateComplaint: FC = () => {
if (!createComplaint) {
return;
}

let complaint = createComplaint;
setComplaintToOpenStatus(complaint);

const noError = await setErrors(complaint);

if (noError && noErrors()) {
await handleComplaintProcessing(complaint);
} else {
handleFormErrors();
}
};

const setComplaintToOpenStatus = (complaint: HwcrComplaint | AllegationComplaint) => {
const openStatus = {
short_description: "OPEN",
long_description: "Open",
Expand All @@ -967,63 +998,86 @@ export const CreateComplaint: FC = () => {
update_user_id: "",
update_utc_timestamp: null,
};

complaint.complaint_identifier.complaint_status_code = openStatus; //force OPEN

const noError = await setErrors(complaint);

if (noError && noErrors()) {
complaint.complaint_identifier.create_utc_timestamp =
complaint.complaint_identifier.update_utc_timestamp =
new Date().toDateString();
complaint.complaint_identifier.create_user_id =
complaint.complaint_identifier.update_user_id = userid;
complaint.complaint_identifier.location_geometry_point.type = "Point";
if (
complaint.complaint_identifier.location_geometry_point.coordinates
.length === 0
) {
complaint.complaint_identifier.location_geometry_point.coordinates = [
0, 0,
];
}
setCreateComplaint(complaint);
if (complaintType === COMPLAINT_TYPES.HWCR) {
const complaintId = await dispatch(
createWildlifeComplaint(complaint as HwcrComplaint),
);
if (complaintId) {
await dispatch(
getWildlifeComplaintByComplaintIdentifierSetUpdate(
complaintId,
setCreateComplaint,
),
);

navigate("/complaint/" + complaintType + "/" + complaintId);
}
}
else if (complaintType === COMPLAINT_TYPES.ERS) {
const complaintId = await dispatch(
createAllegationComplaint(complaint as AllegationComplaint),
);
if (complaintId) {
await dispatch(
getAllegationComplaintByComplaintIdentifierSetUpdate(
complaintId,
setCreateComplaint,
),
);

navigate("/complaint/" + complaintType + "/" + complaintId);
}
}
setErrorNotificationClass("comp-complaint-error display-none");
} else {
ToggleError("Errors in form");
setErrorNotificationClass("comp-complaint-error");
complaint.complaint_identifier.complaint_status_code = openStatus;
};

const handleComplaintProcessing = async (complaint: HwcrComplaint | AllegationComplaint) => {
updateComplaintDetails(complaint);
setCreateComplaint(complaint);

let complaintId = await processComplaintBasedOnType(complaint);
if (complaintId) {
handlePersistAttachments(dispatch, attachmentsToAdd, attachmentsToDelete, complaintId, setAttachmentsToAdd, setAttachmentsToDelete);
}

setErrorNotificationClass("comp-complaint-error display-none");
};

const updateComplaintDetails = (complaint: HwcrComplaint | AllegationComplaint) => {
const now = new Date().toDateString();
complaint.complaint_identifier.create_utc_timestamp = now;
complaint.complaint_identifier.update_utc_timestamp = now;
complaint.complaint_identifier.create_user_id = userid;
complaint.complaint_identifier.update_user_id = userid;
complaint.complaint_identifier.location_geometry_point.type = "Point";

if (
complaint.complaint_identifier.location_geometry_point.coordinates.length === 0
) {
complaint.complaint_identifier.location_geometry_point.coordinates = [0, 0];
}
};

const processComplaintBasedOnType = async (complaint: HwcrComplaint | AllegationComplaint) => {
switch (complaintType) {
case COMPLAINT_TYPES.HWCR:
return handleHwcrComplaint(complaint);
case COMPLAINT_TYPES.ERS:
return handleErsComplaint(complaint);
default:
return null;
}
};

const handleHwcrComplaint = async (complaint: HwcrComplaint | AllegationComplaint) => {
const complaintId = await dispatch(
createWildlifeComplaint(complaint as HwcrComplaint)
);
if (complaintId) {
await dispatch(
getWildlifeComplaintByComplaintIdentifierSetUpdate(
complaintId,
setCreateComplaint
)
);

navigate("/complaint/" + complaintType + "/" + complaintId);
}
return complaintId;
};

const handleErsComplaint = async (complaint: HwcrComplaint | AllegationComplaint) => {
const complaintId = await dispatch(
createAllegationComplaint(complaint as AllegationComplaint)
);
if (complaintId) {
await dispatch(
getAllegationComplaintByComplaintIdentifierSetUpdate(
complaintId,
setCreateComplaint
)
);

navigate("/complaint/" + complaintType + "/" + complaintId);
}
return complaintId;
};

const handleFormErrors = () => {
ToggleError("Errors in form");
setErrorNotificationClass("comp-complaint-error");
};


const maxDate = new Date();

Expand Down Expand Up @@ -1573,6 +1627,12 @@ export const CreateComplaint: FC = () => {
</div>
</div>
)}
<AttachmentsCarousel
allowUpload={true}
allowDelete={true}
onFilesSelected={onHandleAddAttachments}
onFileDeleted={onHandleDeleteAttachment}
/>
</div>
);
};
Loading

0 comments on commit a032b82

Please sign in to comment.