Skip to content

Commit

Permalink
Log Record Deleting/Editing (#134)
Browse files Browse the repository at this point in the history
* Add kebab icon and menu

* add delete confirmation component

* fix id selection within map

* add backend api

* add success alert message

* add frontend for edit component

* fix api locations

* add api connection to database

* add default value to edit log fields

* run linter

* fix select dropdown states

* fix some resident issues

* remove extra api calls and fix reset states on close

* fix table heading styles

* swap menu option order

* add page reload after submit

* fix resident default value

* fix attnTo to make it optional

* fix flagged checkbox

* run linters

* add formatting changes and comments

* change deleteLogRecord return type to boolean

* add params type for editLogRecord

* run linter

* fix attnTo to make it optional

* add date/time selection for create log

* add date/time for edit log

* fix note and residentId errors

* refactor methods

* remove extra API calls

* fix handleNotesChange param type

* fix async api calls

---------

Co-authored-by: Connor Bechthold <[email protected]>
Co-authored-by: Safewaan <[email protected]>
  • Loading branch information
3 people authored Aug 1, 2023
1 parent c631c75 commit c9f873d
Show file tree
Hide file tree
Showing 12 changed files with 913 additions and 100 deletions.
13 changes: 2 additions & 11 deletions backend/app/services/implementations/log_records_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def __init__(self, logger):

def add_record(self, log_record):
new_log_record = log_record
new_log_record["datetime"] = datetime.now()

try:
new_log_record = LogRecords(**new_log_record)
Expand Down Expand Up @@ -212,16 +211,6 @@ def update_log_record(self, log_id, updated_log_record):
LogRecords.attn_to: None,
}
)
if "note" in updated_log_record:
LogRecords.query.filter_by(log_id=log_id).update(
{LogRecords.note: updated_log_record["note"]}
)
else:
LogRecords.query.filter_by(log_id=log_id).update(
{
LogRecords.note: None,
}
)
if "tags" in updated_log_record:
LogRecords.query.filter_by(log_id=log_id).update(
{LogRecords.tags: updated_log_record["tags"]}
Expand All @@ -238,6 +227,8 @@ def update_log_record(self, log_id, updated_log_record):
LogRecords.resident_id: updated_log_record["resident_id"],
LogRecords.flagged: updated_log_record["flagged"],
LogRecords.building: updated_log_record["building"],
LogRecords.note: updated_log_record["note"],
LogRecords.datetime: updated_log_record["datetime"],
}
)
if not updated_log_record:
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/APIClients/AuthAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const twoFaWithGoogle = async (
}
};

const logout = async (userId: string | undefined): Promise<boolean> => {
const logout = async (userId: number | undefined): Promise<boolean> => {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
Expand Down
80 changes: 69 additions & 11 deletions frontend/src/APIClients/LogRecordAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
GetLogRecordsReponse,
LogRecordFilters,
PostLogRecordsResponse,
EditLogRecordParams,
CreateLogRecordParams,
} from "../types/LogRecordTypes";

const countLogRecords = async ({
Expand Down Expand Up @@ -85,29 +87,32 @@ const filterLogRecords = async ({
}
};

const createLog = async (
userId: number,
residentId: number,
flagged: boolean,
note: string,
attentionTo: number,
building: string,
): Promise<PostLogRecordsResponse> => {
const createLog = async ({
employeeId,
residentId,
datetime,
flagged,
note,
tags,
building,
attnTo,
}: CreateLogRecordParams): Promise<PostLogRecordsResponse> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
)}`;

const { data } = await baseAPIClient.post<PostLogRecordsResponse>(
"/log_records/",
{
employeeId: userId,
employeeId,
residentId,
datetime,
flagged,
note,
attnTo: attentionTo,
tags,
building,
attnTo,
},
{ headers: { Authorization: bearerToken } },
);
Expand All @@ -117,8 +122,61 @@ const createLog = async (
}
};

const deleteLogRecord = async (logId: number): Promise<boolean> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
)}`;
await baseAPIClient.delete(`/log_records/${logId}`, {
headers: { Authorization: bearerToken },
});
return true;
} catch (error) {
return false;
}
};

const editLogRecord = async ({
logId,
employeeId,
residentId,
datetime,
flagged,
note,
tags,
building,
attnTo,
}: EditLogRecordParams): Promise<boolean> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
)}`;
await baseAPIClient.put(
`/log_records/${logId}`,
{
employeeId,
residentId,
datetime,
flagged,
note,
attnTo,
tags,
building,
},
{ headers: { Authorization: bearerToken } },
);
return true;
} catch (error) {
return false;
}
};

export default {
createLog,
countLogRecords,
filterLogRecords,
deleteLogRecord,
editLogRecord,
};
74 changes: 74 additions & 0 deletions frontend/src/components/common/DeleteConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { useState } from "react";
import {
Box,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
Text,
} from "@chakra-ui/react";

type Props = {
itemName: string;
itemId: number;
isOpen: boolean;
toggleClose: () => void;
deleteAPI: (itemId: number) => void;
};

const DeleteConfirmation = ({
itemName,
itemId,
isOpen,
toggleClose,
deleteAPI,
}: Props): React.ReactElement => {
const ITEM_NAME = itemName.toLowerCase();

const MESSAGE_HEADER = `Delete ${
ITEM_NAME.charAt(0).toUpperCase() + ITEM_NAME.slice(1)
}`;

const MESSAGE_TEXT = `Are you sure you want to delete this ${ITEM_NAME}? Deleting a \n\
${ITEM_NAME} will permanently remove it from your system.`;

const handleSubmit = async () => {
deleteAPI(itemId);
toggleClose();
};

return (
<>
<Box>
<Modal isOpen={isOpen} onClose={toggleClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>{MESSAGE_HEADER}</ModalHeader>
<ModalBody>
<Box marginBottom="12px">
<Text>{MESSAGE_TEXT}</Text>
</Box>
</ModalBody>
<ModalFooter>
<Button
onClick={toggleClose}
variant="tertiary"
marginRight="8px"
>
Cancel
</Button>
<Button onClick={handleSubmit} variant="primary" type="submit">
Yes, delete
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Box>
</>
);
};

export default DeleteConfirmation;
2 changes: 1 addition & 1 deletion frontend/src/components/common/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type Props = {
userPageNum: number;
setUserPageNum: React.Dispatch<React.SetStateAction<number>>;
setResultsPerPage: React.Dispatch<React.SetStateAction<number>>;
getRecords: (page_number: number) => Promise<void>;
getRecords: (pageNumber: number) => Promise<void>;
};

const RESULTS_PER_PAGE_OPTIONS = [10, 25, 50];
Expand Down
58 changes: 33 additions & 25 deletions frontend/src/components/forms/CreateLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ import selectStyle from "../../theme/forms/selectStyles";
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
import { UserLabel } from "../../types/UserTypes";
import { ResidentLabel } from "../../types/ResidentTypes";
import combineDateTime from "../../helper/combineDateTime";

type Props = {
getRecords: (page_number: number) => Promise<void>;
getRecords: (pageNumber: number) => Promise<void>;
countRecords: () => Promise<void>;
setUserPageNum: React.Dispatch<React.SetStateAction<number>>;
};
Expand All @@ -56,9 +57,9 @@ type AlertDataOptions = {

// Ideally we should be storing this information in the database
const BUILDINGS = [
{ label: "144", value: "144 Erb St. West" },
{ label: "362", value: "362 Erb St. West" },
{ label: "402", value: "402 Erb St. West" },
{ label: "144 Erb St. West", value: "144" },
{ label: "362 Erb St. West", value: "362" },
{ label: "402 Erb St. West", value: "402" },
];

const ALERT_DATA: AlertDataOptions = {
Expand Down Expand Up @@ -101,8 +102,8 @@ const getCurUserSelectOption = () => {
AUTHENTICATED_USER_KEY,
);
if (curUser && curUser.firstName && curUser.id) {
const userId = parseInt(curUser.id, 10);
return { label: curUser.firstName, value: userId };
const userId = curUser.id
return { label: curUser.firstName, value: userId }
}
return { label: "", value: -1 };
};
Expand Down Expand Up @@ -193,11 +194,13 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
) => {
if (selectedOption !== null) {
setAttnTo(selectedOption.value);
} else {
setAttnTo(-1);
}
};

const handleNotesChange = (e: { target: { value: unknown } }) => {
const inputValue: string = e.target.value as string;
const handleNotesChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const inputValue = e.target.value as string;
setNotes(inputValue);
setNotesError(inputValue === "");
};
Expand Down Expand Up @@ -263,7 +266,7 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
setCreateOpen(false);
};

const handleSubmit = () => {
const handleSubmit = async () => {
// Update error states
setEmployeeError(!employee.label);
setDateError(date === null);
Expand All @@ -287,24 +290,28 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
// Create a log in the db with this data
setCreateOpen(false);
// update the table with the new log
LogRecordAPIClient.createLog(
employee.value,
resident,
// NOTE: -1 is the default state for attnTo
const attentionTo = attnTo === -1 ? undefined : attnTo;
const res = await LogRecordAPIClient.createLog({
employeeId: employee.value,
residentId: resident,
datetime: combineDateTime(date, time),
flagged,
notes,
attnTo,
note: notes,
tags,
building,
).then((res) => {
if (res != null) {
setAlertData(ALERT_DATA.SUCCESS);
countRecords();
getRecords(1);
setUserPageNum(1);
} else {
setAlertData(ALERT_DATA.ERROR);
}
setShowAlert(true);
});
attnTo: attentionTo,
})
if (res != null) {
setAlertData(ALERT_DATA.SUCCESS)
countRecords();
getRecords(1);
setUserPageNum(1);
}
else {
setAlertData(ALERT_DATA.ERROR)
}
setShowAlert(true);
};

useEffect(() => {
Expand Down Expand Up @@ -423,6 +430,7 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
<FormControl mt={4}>
<FormLabel>Attention To</FormLabel>
<Select
isClearable
options={employeeOptions}
placeholder="Select Employee"
onChange={handleAttnToChange}
Expand Down
Loading

0 comments on commit c9f873d

Please sign in to comment.