Skip to content

Commit ab34584

Browse files
authored
Get Buildings Route (#196)
* backend for get buildings route * replace hardcoded BUIDLINGS var with get request * pr comments * fix edit resident so that the buildings show up * fix bug
1 parent ce3bf7c commit ab34584

14 files changed

+198
-43
lines changed

backend/app/rest/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def init_app(app):
88
log_records_routes,
99
residents_routes,
1010
tags_routes,
11+
buildings_routes,
1112
)
1213

1314
app.register_blueprint(user_routes.blueprint)
@@ -18,3 +19,4 @@ def init_app(app):
1819
app.register_blueprint(log_records_routes.blueprint)
1920
app.register_blueprint(residents_routes.blueprint)
2021
app.register_blueprint(tags_routes.blueprint)
22+
app.register_blueprint(buildings_routes.blueprint)

backend/app/rest/buildings_routes.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from flask import Blueprint, current_app, jsonify, request
2+
from ..middlewares.auth import require_authorization_by_role
3+
from ..services.implementations.buildings_service import BuildingsService
4+
5+
buildings_service = BuildingsService(current_app.logger)
6+
blueprint = Blueprint("buildings", __name__, url_prefix="/buildings")
7+
8+
9+
@blueprint.route("/", methods=["GET"], strict_slashes=False)
10+
@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"})
11+
def get_buildings():
12+
"""
13+
Get buildings.
14+
"""
15+
try:
16+
building_results = buildings_service.get_buildings()
17+
return jsonify(building_results), 201
18+
except Exception as e:
19+
error_message = getattr(e, "message", None)
20+
return jsonify({"error": (error_message if error_message else str(e))}), 500
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from flask import jsonify
2+
from ..interfaces.buildings_service import IBuildingsService
3+
from ...models.buildings import Buildings
4+
from ...models import db
5+
6+
7+
class BuildingsService(IBuildingsService):
8+
"""
9+
Buildings implementation
10+
"""
11+
12+
def __init__(self, logger):
13+
"""
14+
Create an instance of BuildingsService
15+
16+
:param logger: application's logger instance
17+
:type logger: logger
18+
"""
19+
self.logger = logger
20+
21+
def get_buildings(self):
22+
try:
23+
buildings_results = Buildings.query.all()
24+
25+
return {
26+
"buildings": list(
27+
map(lambda building: building.to_dict(), buildings_results)
28+
)
29+
}
30+
except Exception as postgres_error:
31+
raise postgres_error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from abc import ABC, abstractmethod
2+
3+
4+
class IBuildingsService(ABC):
5+
"""
6+
BuildingsService interface with buildings methods
7+
"""
8+
9+
@abstractmethod
10+
def get_buildings(self):
11+
"""
12+
Gets buildings in json format.
13+
"""
14+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import axios, { AxiosError } from "axios";
2+
import AUTHENTICATED_USER_KEY from "../constants/AuthConstants";
3+
import { getLocalStorageObjProperty } from "../utils/LocalStorageUtils";
4+
import baseAPIClient from "./BaseAPIClient";
5+
import { GetBuildingsResponse } from "../types/BuildingTypes";
6+
7+
const getBuildings = async (): Promise<GetBuildingsResponse> => {
8+
try {
9+
const bearerToken = `Bearer ${getLocalStorageObjProperty(
10+
AUTHENTICATED_USER_KEY,
11+
"accessToken",
12+
)}`;
13+
const { data } = await baseAPIClient.get<GetBuildingsResponse>(
14+
`/buildings`,
15+
{
16+
headers: { Authorization: bearerToken },
17+
},
18+
);
19+
return data;
20+
} catch (error) {
21+
return null;
22+
}
23+
};
24+
25+
export default {
26+
getBuildings,
27+
};

frontend/src/components/forms/CreateLog.tsx

+13-6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import ResidentAPIClient from "../../APIClients/ResidentAPIClient";
3434
import { getLocalStorageObj } from "../../utils/LocalStorageUtils";
3535
import AUTHENTICATED_USER_KEY from "../../constants/AuthConstants";
3636
import LogRecordAPIClient from "../../APIClients/LogRecordAPIClient";
37+
import BuildingAPIClient from "../../APIClients/BuildingAPIClient";
38+
import { BuildingLabel } from "../../types/BuildingTypes";
3739
import selectStyle from "../../theme/forms/selectStyles";
3840
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
3941
import { UserLabel } from "../../types/UserTypes";
@@ -56,11 +58,6 @@ type AlertDataOptions = {
5658
};
5759

5860
// Ideally we should be storing this information in the database
59-
const BUILDINGS = [
60-
{ label: "144", value: 1 },
61-
{ label: "362", value: 2 },
62-
{ label: "402", value: 3 },
63-
];
6461

6562
const ALERT_DATA: AlertDataOptions = {
6663
DEFAULT: {
@@ -128,6 +125,7 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
128125

129126
const [employeeOptions, setEmployeeOptions] = useState<UserLabel[]>([]);
130127
const [residentOptions, setResidentOptions] = useState<UserLabel[]>([]);
128+
const [buildingOptions, setBuildingOptions] = useState<BuildingLabel[]>([]);
131129

132130
const [isCreateOpen, setCreateOpen] = React.useState(false);
133131

@@ -210,6 +208,15 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
210208

211209
// fetch resident + employee data for log creation
212210
const getLogEntryOptions = async () => {
211+
const buildingsData = await BuildingAPIClient.getBuildings();
212+
213+
if (buildingsData && buildingsData.buildings.length !== 0) {
214+
const buildingLabels: BuildingLabel[] = buildingsData.buildings.map(
215+
(building) => ({ label: building.name!, value: building.id! }),
216+
);
217+
setBuildingOptions(buildingLabels);
218+
}
219+
213220
const residentsData = await ResidentAPIClient.getResidents({
214221
returnAll: true,
215222
});
@@ -390,7 +397,7 @@ const CreateLog = ({ getRecords, countRecords, setUserPageNum }: Props) => {
390397
<FormControl isRequired isInvalid={buildingError} mt={4}>
391398
<FormLabel>Building</FormLabel>
392399
<Select
393-
options={BUILDINGS}
400+
options={buildingOptions}
394401
placeholder="Building No."
395402
onChange={handleBuildingChange}
396403
styles={selectStyle}

frontend/src/components/forms/CreateResident.tsx

+17-8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { Col, Row } from "react-bootstrap";
2828
import selectStyle from "../../theme/forms/selectStyles";
2929
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
3030
import ResidentAPIClient from "../../APIClients/ResidentAPIClient";
31+
import BuildingAPIClient from "../../APIClients/BuildingAPIClient";
32+
import { BuildingLabel } from "../../types/BuildingTypes";
3133
import { convertToString } from "../../helper/dateHelpers";
3234

3335
type Props = {
@@ -36,18 +38,12 @@ type Props = {
3638
countResidents: () => Promise<void>;
3739
};
3840

39-
// TODO: Connect to Buidings table
40-
const BUILDINGS = [
41-
{ label: "144", value: 1 },
42-
{ label: "362", value: 2 },
43-
{ label: "402", value: 3 },
44-
];
45-
4641
const CreateResident = ({
4742
getRecords,
4843
setUserPageNum,
4944
countResidents,
5045
}: Props): React.ReactElement => {
46+
const [buildingOptions, setBuildingOptions] = useState<BuildingLabel[]>([]);
5147
const [initials, setInitials] = useState("");
5248
const [roomNumber, setRoomNumber] = useState("");
5349
const [moveInDate, setMoveInDate] = useState(new Date());
@@ -106,9 +102,22 @@ const CreateResident = ({
106102
}
107103
};
108104

105+
const getBuildingsOptions = async () => {
106+
const buildingsData = await BuildingAPIClient.getBuildings();
107+
108+
if (buildingsData && buildingsData.buildings.length !== 0) {
109+
const buildingLabels: BuildingLabel[] = buildingsData.buildings.map(
110+
(building) => ({ label: building.name!, value: building.id! }),
111+
);
112+
setBuildingOptions(buildingLabels);
113+
}
114+
};
115+
109116
const handleOpen = () => {
110117
setIsOpen(true);
111118

119+
getBuildingsOptions();
120+
112121
// Reset the input states
113122
setInitials("");
114123
setRoomNumber("");
@@ -223,7 +232,7 @@ const CreateResident = ({
223232
<FormControl isRequired isInvalid={buildingError}>
224233
<FormLabel>Building</FormLabel>
225234
<Select
226-
options={BUILDINGS}
235+
options={buildingOptions}
227236
placeholder="Select building"
228237
onChange={handleBuildingChange}
229238
styles={selectStyle}

frontend/src/components/forms/EditLog.tsx

+5-9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import AUTHENTICATED_USER_KEY from "../../constants/AuthConstants";
3333
import LogRecordAPIClient from "../../APIClients/LogRecordAPIClient";
3434
import selectStyle from "../../theme/forms/selectStyles";
3535
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
36+
import { BuildingLabel } from "../../types/BuildingTypes";
3637
import { UserLabel } from "../../types/UserTypes";
3738
import { ResidentLabel } from "../../types/ResidentTypes";
3839
import { LogRecord } from "../../types/LogRecordTypes";
@@ -48,6 +49,7 @@ type Props = {
4849
getRecords: (pageNumber: number) => Promise<void>;
4950
countRecords: () => Promise<void>;
5051
setUserPageNum: React.Dispatch<React.SetStateAction<number>>;
52+
buildingOptions: BuildingLabel[];
5153
};
5254

5355
type AlertData = {
@@ -59,13 +61,6 @@ type AlertDataOptions = {
5961
[key: string]: AlertData;
6062
};
6163

62-
// Ideally we should be storing this information in the database
63-
const BUILDINGS = [
64-
{ label: "144", value: 1 },
65-
{ label: "362", value: 2 },
66-
{ label: "402", value: 3 },
67-
];
68-
6964
const ALERT_DATA: AlertDataOptions = {
7065
DEFAULT: {
7166
status: "info",
@@ -110,6 +105,7 @@ const EditLog = ({
110105
getRecords,
111106
countRecords,
112107
setUserPageNum,
108+
buildingOptions,
113109
}: Props) => {
114110
// currently, the select for employees is locked and should default to current user. Need to check if admins/regular staff are allowed to change this
115111
const [employee, setEmployee] = useState<UserLabel>(getCurUserSelectOption());
@@ -348,11 +344,11 @@ const EditLog = ({
348344
<FormControl isRequired isInvalid={buildingError} mt={4}>
349345
<FormLabel>Building</FormLabel>
350346
<Select
351-
options={BUILDINGS}
347+
options={buildingOptions}
352348
placeholder="Building No."
353349
onChange={handleBuildingChange}
354350
styles={selectStyle}
355-
defaultValue={BUILDINGS.find(
351+
defaultValue={buildingOptions.find(
356352
(item) => item.value === buildingId,
357353
)}
358354
/>

frontend/src/components/forms/EditResident.tsx

+8-11
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,15 @@ import { SingleDatepicker } from "chakra-dayzed-datepicker";
2929
import { Card, Col, Row } from "react-bootstrap";
3030
import ResidentAPIClient from "../../APIClients/ResidentAPIClient";
3131
import { Resident } from "../../types/ResidentTypes";
32-
32+
import BuildingAPIClient from "../../APIClients/BuildingAPIClient";
33+
import { BuildingLabel } from "../../types/BuildingTypes";
3334
import selectStyle from "../../theme/forms/selectStyles";
3435
import { singleDatePickerStyle } from "../../theme/forms/datePickerStyles";
3536
import CreateToast from "../common/Toasts";
3637
import { convertToDate, convertToString } from "../../helper/dateHelpers";
3738

38-
// TODO: Connect to Buidings table
39-
const BUILDINGS = [
40-
{ label: "144", value: 1 },
41-
{ label: "362", value: 2 },
42-
{ label: "402", value: 3 },
43-
];
44-
4539
type Props = {
40+
buildingOptions: BuildingLabel[],
4641
resident: Resident;
4742
isOpen: boolean;
4843
userPageNum: number;
@@ -51,6 +46,7 @@ type Props = {
5146
};
5247

5348
const EditResident = ({
49+
buildingOptions,
5450
resident,
5551
isOpen,
5652
userPageNum,
@@ -60,7 +56,7 @@ const EditResident = ({
6056
const [initials, setInitials] = useState("");
6157
const [roomNumber, setRoomNumber] = useState(-1);
6258
const [moveInDate, setMoveInDate] = useState(new Date());
63-
const [buildingId, setBuildingId] = useState<number>(-1);
59+
const [buildingId, setBuildingId] = useState<number>(resident.building.id);
6460
const [moveOutDate, setMoveOutDate] = useState<Date | undefined>();
6561

6662
const [initialsError, setInitialsError] = useState(false);
@@ -100,6 +96,7 @@ const EditResident = ({
10096
setMoveOutDate(undefined);
10197
};
10298

99+
103100
const handleInitialsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
104101
const inputValue = e.target.value as string;
105102
if (/^[a-z]{0,2}$/i.test(inputValue)) {
@@ -247,8 +244,8 @@ const EditResident = ({
247244
<FormControl isRequired isInvalid={buildingError}>
248245
<FormLabel>Building</FormLabel>
249246
<Select
250-
options={BUILDINGS}
251-
defaultValue={BUILDINGS.find(
247+
options={buildingOptions}
248+
defaultValue={buildingOptions.find(
252249
(item) => item.value === buildingId,
253250
)}
254251
onChange={handleBuildingChange}

frontend/src/components/pages/HomePage/LogRecordsTable.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import EditLog from "../../forms/EditLog";
2929
import LogRecordAPIClient from "../../../APIClients/LogRecordAPIClient";
3030
import ResidentAPIClient from "../../../APIClients/ResidentAPIClient";
3131
import UserAPIClient from "../../../APIClients/UserAPIClient";
32+
import BuildingAPIClient from "../../../APIClients/BuildingAPIClient";
3233
import { UserLabel } from "../../../types/UserTypes";
34+
import { BuildingLabel } from "../../../types/BuildingTypes";
3335
import ConfirmationModal from "../../common/ConfirmationModal";
3436

3537
type Props = {
@@ -57,6 +59,8 @@ const LogRecordsTable = ({
5759

5860
const [showAlert, setShowAlert] = useState(false);
5961

62+
const [buildingOptions, setBuildingOptions] = useState<BuildingLabel[]>([]);
63+
6064
// Menu states
6165
const [deleteOpenMap, setDeleteOpenMap] = useState<{
6266
[key: number]: boolean;
@@ -100,6 +104,15 @@ const LogRecordsTable = ({
100104
setResidentOptions(residentLabels);
101105
}
102106

107+
const buildingsData = await BuildingAPIClient.getBuildings();
108+
109+
if (buildingsData && buildingsData.buildings.length !== 0) {
110+
const buildingLabels: BuildingLabel[] = buildingsData.buildings.map(
111+
(building) => ({ label: building.name!, value: building.id! }),
112+
);
113+
setBuildingOptions(buildingLabels);
114+
}
115+
103116
const usersData = await UserAPIClient.getUsers({ returnAll: true });
104117
if (usersData && usersData.users.length !== 0) {
105118
const userLabels: UserLabel[] = usersData.users
@@ -222,6 +235,7 @@ const LogRecordsTable = ({
222235
getRecords={getRecords}
223236
countRecords={countRecords}
224237
setUserPageNum={setUserPageNum}
238+
buildingOptions={buildingOptions}
225239
/>
226240

227241
<ConfirmationModal

0 commit comments

Comments
 (0)