Skip to content

Commit

Permalink
FEAT-577: Finalise All Rooms page (#584)
Browse files Browse the repository at this point in the history
* Restyled room list and dropdown

* Updated styling

* Add start time filtering

* Fix failing test

* Update filter

* Fix test
  • Loading branch information
JessicaF authored Nov 14, 2024
1 parent 0a5e0b8 commit 0f304a0
Show file tree
Hide file tree
Showing 16 changed files with 215 additions and 331 deletions.
9 changes: 5 additions & 4 deletions backend/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ export const getBookingsForDate = async (
return Object.fromEntries(res.rooms.map((room) => [room.id, room]));
};

export const getBookingsForTimeRange = async (
startTime: Date,
endTime: Date
) => {
export const getBookingsFromStartTime = async (startTime: Date) => {
// The date is in UTC time, convert to AEST first to manipulate hours
const base = utcToZonedTime(startTime, "Australia/Sydney");
base.setHours(23, 59);
const endTime = zonedTimeToUtc(base, "Australia/Sydney");
const res = await queryBookingsInRange(startTime, endTime);
return Object.fromEntries(res.rooms.map((room) => [room.id, room]));
};
Expand Down
3 changes: 2 additions & 1 deletion backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ app.get(
app.get(
"/api/rooms/search",
asyncHandler(async (req: Request, res: Response, next: NextFunction) => {
const datetime = parseStatusDatetime(req);
const filters = parseSearchFilters(req);
const data = await searchAllRoom(filters);
const data = await searchAllRoom(datetime, filters);
res.send(data);
next();
})
Expand Down
29 changes: 7 additions & 22 deletions backend/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { queryBookingsForRoom } from "./dbInterface";
import {
calculateStatus,
getBookingsForDate,
getBookingsForTimeRange,
getBookingsFromStartTime,
getBuildingRoomData,
} from "./helpers";
import { SearchFilters, StatusFilters } from "./types";
Expand Down Expand Up @@ -89,18 +89,10 @@ export const getAllRoomStatus = async (
};

export const searchAllRoom = async (
date: Date,
filters: SearchFilters
): Promise<SearchResponse> => {
let bookings;

if (filters.startTime && filters.endTime) {
bookings = await getBookingsForTimeRange(
filters.startTime,
filters.endTime
);
} else {
bookings = await getBookingsForDate(new Date());
}
const bookings = await getBookingsFromStartTime(date);

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

const status = calculateStatus(
filters.startTime ?? new Date(),
date,
bookings[roomData.id].bookings,
filters.duration || 0
);
Expand All @@ -138,16 +130,9 @@ export const searchAllRoom = async (
}
}

if (filters.startTime && filters.endTime) {
for (const roomId of Object.keys(result)) {
if (
result[roomId].status === "busy" ||
(result[roomId].status === "soon" &&
new Date(result[roomId].endtime).getTime() <
new Date(filters.endTime).getTime())
) {
delete result[roomId];
}
for (const roomId of Object.keys(result)) {
if (result[roomId].status === "busy") {
delete result[roomId];
}
}

Expand Down
29 changes: 16 additions & 13 deletions frontend/__tests__/AllRoomsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,38 @@ describe("AllRooms page", () => {
});

it("renders AllRoomsSearchBar", () => {
render(<AllRoomsSearchBar />);

const building = screen.getByText("Building");
const capacity = screen.getByText("Capacity");
const when = screen.getByText("When");
const duration = screen.getByText("Duration");
render(
<Provider store={store}>
<AllRoomsSearchBar />
</Provider>
);

expect(building).toBeInTheDocument();
expect(capacity).toBeInTheDocument();
expect(when).toBeInTheDocument();
expect(duration).toBeInTheDocument();
expect(screen.getByTestId("CalendarIcon")).toBeInTheDocument();
expect(screen.getByTestId("ClockIcon")).toBeInTheDocument();
});

it("renders AllRoomsFilter", () => {
render(
<Provider store={store}>
<ThemeProvider theme={createTheme({})}>
<AllRoomsFilter filters={{}} />
<AllRoomsFilter
filters={{
usage: "Tutorial Room",
location: "Lower Campus",
duration: "1+ hours",
}}
/>
</ThemeProvider>
</Provider>
);

const roomType = screen.getByText("Room Type");
const location = screen.getByText("Location");
const idRequired = screen.getByText("ID Required");
const duration = screen.getByText("Duration Free");

expect(roomType).toBeInTheDocument();
expect(location).toBeInTheDocument();
expect(idRequired).toBeInTheDocument();
expect(duration).toBeInTheDocument();
});

describe("renders AllRoomsRoom", () => {
Expand Down
74 changes: 34 additions & 40 deletions frontend/app/allRooms/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import SearchIcon from "@mui/icons-material/Search";
import { Button } from "@mui/material";
import Container from "@mui/material/Container";
import { Alert, Button, Typography } from "@mui/material";
import Stack from "@mui/material/Stack";
import { styled } from "@mui/system";
import AllRoomsSearchBar from "components/AllRoomsSearchBar";
import useAllRooms from "hooks/useAllRooms";
import { useMemo, useState } from "react";
import { useSelector } from "react-redux";
Expand All @@ -13,18 +12,28 @@ import { selectFilters } from "redux/filtersSlice";
import AllRoomsFilter from "../../components/AllRoomsFilter";
import Room from "../../components/AllRoomsRoom";
import RoomList from "../../components/AllRoomsRoomList";
import AllRoomsSearchBar from "../../components/AllRoomsSearchBar";

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

const roomsDisplay = useMemo(() => {
if (!rooms) return;
const roomEntries = Object.entries(rooms).slice(0, visibleRooms);
const availableRooms = Object.entries(rooms).filter(
(room) => room[1].status !== "busy"
);

if (availableRooms.length === 0) {
return (
<Alert severity="error" sx={{ marginTop: 2 }}>
No rooms satisfying current filters
</Alert>
);
}

const roomEntries = availableRooms.slice(0, visibleRooms);

return roomEntries.map(([roomId, { name, status, endtime }]) => {
return (
<Room
Expand All @@ -45,57 +54,42 @@ export default function Page() {
const totalRooms = rooms ? Object.keys(rooms).length : 0;

return (
<Container>
<Stack>
<StyledSearchBar>
<AllRoomsSearchBar />
<SearchIcon />
</StyledSearchBar>
<Stack alignItems="center">
<Stack marginTop={4} paddingX={1}>
<Typography fontWeight="bold" variant="h4">
All Free Rooms
</Typography>
<Typography marginTop={1} variant="body1">
Not looking for a specific building? See all free rooms in this easy
to search list!
</Typography>
<StyledBody>
<AllRoomsFilter filters={filters} />
<RoomList isValidating={isValidating}>
<AllRoomsSearchBar />
{roomsDisplay}
{visibleRooms < totalRooms && (
<Button variant="contained" onClick={handleLoadMore}>
<Button
variant="outlined"
onClick={handleLoadMore}
sx={{ marginY: 1 }}
>
Load More Rooms
</Button>
)}
</RoomList>
</StyledBody>
</Stack>
</Container>
</Stack>
);
}

const StyledSearchBar = styled(Stack)(({ theme }) => ({
flexDirection: "row",
minWidth: "332px",
borderRadius: 8,
borderStyle: "solid",
borderWidth: "thin",
borderColor: theme.palette.text.secondary,
margin: theme.spacing(6, 6.25, 3.75),
padding: theme.spacing(1.25),
justifyContent: "space-between",
alignItems: "center",
[theme.breakpoints.down("xs")]: {
spacing: 1,
margin: theme.spacing(3, 6.25, 2),
},
[theme.breakpoints.up("xs")]: {
spacing: 2,
},
}));

const StyledBody = styled(Stack)(({ theme }) => ({
flexDirection: "row",
// flexWrap: "wrap",
margin: theme.spacing(0, 4.25),
padding: theme.spacing(2),
justifyContent: "space-between",
gap: theme.spacing(2),
[theme.breakpoints.down("md")]: {
marginTop: "30px",
[theme.breakpoints.down("sm")]: {
flexDirection: "column",
padding: theme.spacing(0, 2),
},
}));
41 changes: 30 additions & 11 deletions frontend/components/AllRoomsFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
import { Button } from "@mui/material";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { styled } from "@mui/system";
import { clearFilters } from "redux/filtersSlice";
import { useDispatch } from "redux/hooks";
import { Filters } from "types";

import FilterSideBar from "./FilterSideBar";

const AllRoomsFilter: React.FC<{ filters: Filters }> = ({ filters }) => {
const dispatch = useDispatch();

return (
<StyledMainFilter>
<Typography
sx={{
color: "primary.main",
width: "fit-content",
marginRight: 2,
fontWeight: 500,
fontSize: "0.85rem",
paddingBottom: "4px",
}}
<Stack
alignContent="center"
alignItems="center"
direction="row"
justifyContent="space-between"
>
FILTER OPTIONS
</Typography>
<Typography
sx={{
color: "primary.main",
width: "fit-content",
marginRight: 2,
fontWeight: 500,
fontSize: "0.85rem",
paddingBottom: "4px",
}}
>
FILTER
</Typography>
<Button
size="small"
sx={{ position: "relative", bottom: 3 }}
onClick={() => dispatch(clearFilters())}
>
RESET
</Button>
</Stack>
<FilterSideBar filters={filters} />
</StyledMainFilter>
);
Expand Down
81 changes: 0 additions & 81 deletions frontend/components/AllRoomsFilterMobile.tsx

This file was deleted.

Loading

0 comments on commit 0f304a0

Please sign in to comment.