Skip to content

Commit 0f304a0

Browse files
authored
FEAT-577: Finalise All Rooms page (#584)
* Restyled room list and dropdown * Updated styling * Add start time filtering * Fix failing test * Update filter * Fix test
1 parent 0a5e0b8 commit 0f304a0

16 files changed

+215
-331
lines changed

backend/src/helpers.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ export const getBookingsForDate = async (
2121
return Object.fromEntries(res.rooms.map((room) => [room.id, room]));
2222
};
2323

24-
export const getBookingsForTimeRange = async (
25-
startTime: Date,
26-
endTime: Date
27-
) => {
24+
export const getBookingsFromStartTime = async (startTime: Date) => {
25+
// The date is in UTC time, convert to AEST first to manipulate hours
26+
const base = utcToZonedTime(startTime, "Australia/Sydney");
27+
base.setHours(23, 59);
28+
const endTime = zonedTimeToUtc(base, "Australia/Sydney");
2829
const res = await queryBookingsInRange(startTime, endTime);
2930
return Object.fromEntries(res.rooms.map((room) => [room.id, room]));
3031
};

backend/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ app.get(
7474
app.get(
7575
"/api/rooms/search",
7676
asyncHandler(async (req: Request, res: Response, next: NextFunction) => {
77+
const datetime = parseStatusDatetime(req);
7778
const filters = parseSearchFilters(req);
78-
const data = await searchAllRoom(filters);
79+
const data = await searchAllRoom(datetime, filters);
7980
res.send(data);
8081
next();
8182
})

backend/src/service.ts

+7-22
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { queryBookingsForRoom } from "./dbInterface";
1010
import {
1111
calculateStatus,
1212
getBookingsForDate,
13-
getBookingsForTimeRange,
13+
getBookingsFromStartTime,
1414
getBuildingRoomData,
1515
} from "./helpers";
1616
import { SearchFilters, StatusFilters } from "./types";
@@ -89,18 +89,10 @@ export const getAllRoomStatus = async (
8989
};
9090

9191
export const searchAllRoom = async (
92+
date: Date,
9293
filters: SearchFilters
9394
): Promise<SearchResponse> => {
94-
let bookings;
95-
96-
if (filters.startTime && filters.endTime) {
97-
bookings = await getBookingsForTimeRange(
98-
filters.startTime,
99-
filters.endTime
100-
);
101-
} else {
102-
bookings = await getBookingsForDate(new Date());
103-
}
95+
const bookings = await getBookingsFromStartTime(date);
10496

10597
const buildingData = await getBuildingRoomData();
10698
const result: SearchResponse = {};
@@ -122,7 +114,7 @@ export const searchAllRoom = async (
122114
continue;
123115

124116
const status = calculateStatus(
125-
filters.startTime ?? new Date(),
117+
date,
126118
bookings[roomData.id].bookings,
127119
filters.duration || 0
128120
);
@@ -138,16 +130,9 @@ export const searchAllRoom = async (
138130
}
139131
}
140132

141-
if (filters.startTime && filters.endTime) {
142-
for (const roomId of Object.keys(result)) {
143-
if (
144-
result[roomId].status === "busy" ||
145-
(result[roomId].status === "soon" &&
146-
new Date(result[roomId].endtime).getTime() <
147-
new Date(filters.endTime).getTime())
148-
) {
149-
delete result[roomId];
150-
}
133+
for (const roomId of Object.keys(result)) {
134+
if (result[roomId].status === "busy") {
135+
delete result[roomId];
151136
}
152137
}
153138

frontend/__tests__/AllRoomsPage.test.tsx

+16-13
Original file line numberDiff line numberDiff line change
@@ -26,35 +26,38 @@ describe("AllRooms page", () => {
2626
});
2727

2828
it("renders AllRoomsSearchBar", () => {
29-
render(<AllRoomsSearchBar />);
30-
31-
const building = screen.getByText("Building");
32-
const capacity = screen.getByText("Capacity");
33-
const when = screen.getByText("When");
34-
const duration = screen.getByText("Duration");
29+
render(
30+
<Provider store={store}>
31+
<AllRoomsSearchBar />
32+
</Provider>
33+
);
3534

36-
expect(building).toBeInTheDocument();
37-
expect(capacity).toBeInTheDocument();
38-
expect(when).toBeInTheDocument();
39-
expect(duration).toBeInTheDocument();
35+
expect(screen.getByTestId("CalendarIcon")).toBeInTheDocument();
36+
expect(screen.getByTestId("ClockIcon")).toBeInTheDocument();
4037
});
4138

4239
it("renders AllRoomsFilter", () => {
4340
render(
4441
<Provider store={store}>
4542
<ThemeProvider theme={createTheme({})}>
46-
<AllRoomsFilter filters={{}} />
43+
<AllRoomsFilter
44+
filters={{
45+
usage: "Tutorial Room",
46+
location: "Lower Campus",
47+
duration: "1+ hours",
48+
}}
49+
/>
4750
</ThemeProvider>
4851
</Provider>
4952
);
5053

5154
const roomType = screen.getByText("Room Type");
5255
const location = screen.getByText("Location");
53-
const idRequired = screen.getByText("ID Required");
56+
const duration = screen.getByText("Duration Free");
5457

5558
expect(roomType).toBeInTheDocument();
5659
expect(location).toBeInTheDocument();
57-
expect(idRequired).toBeInTheDocument();
60+
expect(duration).toBeInTheDocument();
5861
});
5962

6063
describe("renders AllRoomsRoom", () => {

frontend/app/allRooms/page.tsx

+34-40
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
"use client";
22

3-
import SearchIcon from "@mui/icons-material/Search";
4-
import { Button } from "@mui/material";
5-
import Container from "@mui/material/Container";
3+
import { Alert, Button, Typography } from "@mui/material";
64
import Stack from "@mui/material/Stack";
75
import { styled } from "@mui/system";
6+
import AllRoomsSearchBar from "components/AllRoomsSearchBar";
87
import useAllRooms from "hooks/useAllRooms";
98
import { useMemo, useState } from "react";
109
import { useSelector } from "react-redux";
@@ -13,18 +12,28 @@ import { selectFilters } from "redux/filtersSlice";
1312
import AllRoomsFilter from "../../components/AllRoomsFilter";
1413
import Room from "../../components/AllRoomsRoom";
1514
import RoomList from "../../components/AllRoomsRoomList";
16-
import AllRoomsSearchBar from "../../components/AllRoomsSearchBar";
1715

1816
export default function Page() {
1917
const filters = useSelector(selectFilters);
2018
const { rooms, isValidating } = useAllRooms(filters);
21-
const centred = isValidating ? "center" : "default";
22-
// const displayMobile = useMediaQuery(useTheme().breakpoints.down("md"));
2319
const [visibleRooms, setVisibleRooms] = useState(20);
2420

2521
const roomsDisplay = useMemo(() => {
2622
if (!rooms) return;
27-
const roomEntries = Object.entries(rooms).slice(0, visibleRooms);
23+
const availableRooms = Object.entries(rooms).filter(
24+
(room) => room[1].status !== "busy"
25+
);
26+
27+
if (availableRooms.length === 0) {
28+
return (
29+
<Alert severity="error" sx={{ marginTop: 2 }}>
30+
No rooms satisfying current filters
31+
</Alert>
32+
);
33+
}
34+
35+
const roomEntries = availableRooms.slice(0, visibleRooms);
36+
2837
return roomEntries.map(([roomId, { name, status, endtime }]) => {
2938
return (
3039
<Room
@@ -45,57 +54,42 @@ export default function Page() {
4554
const totalRooms = rooms ? Object.keys(rooms).length : 0;
4655

4756
return (
48-
<Container>
49-
<Stack>
50-
<StyledSearchBar>
51-
<AllRoomsSearchBar />
52-
<SearchIcon />
53-
</StyledSearchBar>
57+
<Stack alignItems="center">
58+
<Stack marginTop={4} paddingX={1}>
59+
<Typography fontWeight="bold" variant="h4">
60+
All Free Rooms
61+
</Typography>
62+
<Typography marginTop={1} variant="body1">
63+
Not looking for a specific building? See all free rooms in this easy
64+
to search list!
65+
</Typography>
5466
<StyledBody>
5567
<AllRoomsFilter filters={filters} />
5668
<RoomList isValidating={isValidating}>
69+
<AllRoomsSearchBar />
5770
{roomsDisplay}
5871
{visibleRooms < totalRooms && (
59-
<Button variant="contained" onClick={handleLoadMore}>
72+
<Button
73+
variant="outlined"
74+
onClick={handleLoadMore}
75+
sx={{ marginY: 1 }}
76+
>
6077
Load More Rooms
6178
</Button>
6279
)}
6380
</RoomList>
6481
</StyledBody>
6582
</Stack>
66-
</Container>
83+
</Stack>
6784
);
6885
}
6986

70-
const StyledSearchBar = styled(Stack)(({ theme }) => ({
71-
flexDirection: "row",
72-
minWidth: "332px",
73-
borderRadius: 8,
74-
borderStyle: "solid",
75-
borderWidth: "thin",
76-
borderColor: theme.palette.text.secondary,
77-
margin: theme.spacing(6, 6.25, 3.75),
78-
padding: theme.spacing(1.25),
79-
justifyContent: "space-between",
80-
alignItems: "center",
81-
[theme.breakpoints.down("xs")]: {
82-
spacing: 1,
83-
margin: theme.spacing(3, 6.25, 2),
84-
},
85-
[theme.breakpoints.up("xs")]: {
86-
spacing: 2,
87-
},
88-
}));
89-
9087
const StyledBody = styled(Stack)(({ theme }) => ({
9188
flexDirection: "row",
92-
// flexWrap: "wrap",
93-
margin: theme.spacing(0, 4.25),
94-
padding: theme.spacing(2),
9589
justifyContent: "space-between",
9690
gap: theme.spacing(2),
97-
[theme.breakpoints.down("md")]: {
91+
marginTop: "30px",
92+
[theme.breakpoints.down("sm")]: {
9893
flexDirection: "column",
99-
padding: theme.spacing(0, 2),
10094
},
10195
}));

frontend/components/AllRoomsFilter.tsx

+30-11
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,44 @@
1+
import { Button } from "@mui/material";
12
import Stack from "@mui/material/Stack";
23
import Typography from "@mui/material/Typography";
34
import { styled } from "@mui/system";
5+
import { clearFilters } from "redux/filtersSlice";
6+
import { useDispatch } from "redux/hooks";
47
import { Filters } from "types";
58

69
import FilterSideBar from "./FilterSideBar";
710

811
const AllRoomsFilter: React.FC<{ filters: Filters }> = ({ filters }) => {
12+
const dispatch = useDispatch();
13+
914
return (
1015
<StyledMainFilter>
11-
<Typography
12-
sx={{
13-
color: "primary.main",
14-
width: "fit-content",
15-
marginRight: 2,
16-
fontWeight: 500,
17-
fontSize: "0.85rem",
18-
paddingBottom: "4px",
19-
}}
16+
<Stack
17+
alignContent="center"
18+
alignItems="center"
19+
direction="row"
20+
justifyContent="space-between"
2021
>
21-
FILTER OPTIONS
22-
</Typography>
22+
<Typography
23+
sx={{
24+
color: "primary.main",
25+
width: "fit-content",
26+
marginRight: 2,
27+
fontWeight: 500,
28+
fontSize: "0.85rem",
29+
paddingBottom: "4px",
30+
}}
31+
>
32+
FILTER
33+
</Typography>
34+
<Button
35+
size="small"
36+
sx={{ position: "relative", bottom: 3 }}
37+
onClick={() => dispatch(clearFilters())}
38+
>
39+
RESET
40+
</Button>
41+
</Stack>
2342
<FilterSideBar filters={filters} />
2443
</StyledMainFilter>
2544
);

frontend/components/AllRoomsFilterMobile.tsx

-81
This file was deleted.

0 commit comments

Comments
 (0)