Skip to content

Commit af5f260

Browse files
feat: rooms list sort by button (#210)
* feat: add sorting logic to rooms page * formatting
1 parent 2ae1f63 commit af5f260

File tree

6 files changed

+134
-3
lines changed

6 files changed

+134
-3
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { X } from "lucide-react";
2+
import { cn } from "@/lib/utils";
3+
4+
type FilterTagProps = {
5+
label: string;
6+
onRemove: () => void;
7+
className?: string;
8+
};
9+
10+
export function FilterTag({ label, onRemove, className }: FilterTagProps) {
11+
return (
12+
<div
13+
className={cn(
14+
className,
15+
"inline-flex items-center gap-1 rounded-md border border-stroke-default pl-4 pr-3 py-1.5 text-sm text-text-default transition-colors",
16+
)}
17+
>
18+
{label}
19+
<button
20+
type="button"
21+
onClick={onRemove}
22+
className="text-text-subtle hover:text-text-default"
23+
>
24+
<X className="h-4 w-4" />
25+
</button>
26+
</div>
27+
);
28+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { ChevronDown } from "lucide-react";
2+
import { useState } from "react";
3+
import { cn } from "@/lib/utils";
4+
5+
type OrderByDropdownProps = {
6+
ascending: boolean;
7+
setAscending: (ascending: boolean) => void;
8+
};
9+
10+
export function OrderByDropdown({
11+
ascending,
12+
setAscending,
13+
}: OrderByDropdownProps) {
14+
const [open, setOpen] = useState(false);
15+
const label = ascending ? "Ascending" : "Descending";
16+
17+
return (
18+
<div
19+
className="relative min-w-0 w-full max-w-31.5"
20+
onBlur={(e) => {
21+
if (!e.currentTarget.contains(e.relatedTarget)) setOpen(false);
22+
}}
23+
>
24+
<button
25+
type="button"
26+
className={cn(
27+
"flex w-full items-center justify-between gap-2 py-1 pl-2 pr-1 border border-stroke-subtle bg-bg-primary text-left",
28+
open ? "rounded-t-md" : "rounded-md",
29+
)}
30+
onClick={() => setOpen((o) => !o)}
31+
aria-expanded={open}
32+
>
33+
<span className="min-w-0 truncate text-md text-text-default">
34+
{label}
35+
</span>
36+
<ChevronDown
37+
className={cn(
38+
"h-4 w-4 shrink-0 transition-transform",
39+
open && "rotate-180",
40+
)}
41+
/>
42+
</button>
43+
44+
{open && (
45+
<div className="absolute left-0 top-full z-10 w-full rounded-b-md border border-t-0 border-stroke-subtle bg-bg-primary shadow-md">
46+
<button
47+
type="button"
48+
className="block w-full pl-2 pr-1 py-1 text-left text-md text-text-default hover:bg-bg-selected"
49+
onClick={() => {
50+
setAscending(!ascending);
51+
setOpen(false);
52+
}}
53+
>
54+
{ascending ? "Descending" : "Ascending"}
55+
</button>
56+
</div>
57+
)}
58+
</div>
59+
);
60+
}

clients/web/src/components/rooms/RoomsList.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
1+
import { useMemo } from "react";
12
import type { RoomWithOptionalGuestBooking } from "@shared";
23
import { RoomCard } from "@/components/rooms/RoomCard";
34

45
type RoomsListProps = {
56
rooms: Array<RoomWithOptionalGuestBooking>;
67
onRoomSelect: (room: RoomWithOptionalGuestBooking) => void;
8+
ascending: boolean;
79
selectedRoomNumber?: number | null;
810
};
911

12+
function sortRoomsByRoomNumber(
13+
rooms: Array<RoomWithOptionalGuestBooking>,
14+
ascending: boolean,
15+
): Array<RoomWithOptionalGuestBooking> {
16+
return [...rooms].sort((a, b) => {
17+
const an = a.room_number ?? 0;
18+
const bn = b.room_number ?? 0;
19+
return ascending ? an - bn : bn - an;
20+
});
21+
}
22+
1023
export function RoomsList({
1124
rooms,
1225
onRoomSelect,
26+
ascending,
1327
selectedRoomNumber = null,
1428
}: RoomsListProps) {
29+
const sortedRooms = useMemo(
30+
() => sortRoomsByRoomNumber(rooms, ascending),
31+
[rooms, ascending],
32+
);
33+
1534
return (
1635
<section className="flex-1 min-h-0 flex flex-col overflow-hidden py-4">
1736
<nav className="flex-1 min-h-0">
1837
<ul className="flex flex-col gap-4 h-full overflow-y-auto min-h-0 [&::-webkit-scrollbar]:hidden">
19-
{rooms.map((room) => (
38+
{sortedRooms.map((room) => (
2039
<li key={room.room_number} className="min-w-0">
2140
<RoomCard
2241
room={room}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { OrderByDropdown } from "./OrderByDropdown";
2+
3+
type SortByContainerProps = {
4+
ascending: boolean;
5+
setAscending: (ascending: boolean) => void;
6+
};
7+
8+
export function SortByContainer({
9+
ascending,
10+
setAscending,
11+
}: SortByContainerProps) {
12+
return (
13+
<span className="text-sm text-text-subtle flex items-center gap-1 pt-6">
14+
Sort by:{" "}
15+
<OrderByDropdown ascending={ascending} setAscending={setAscending} />
16+
</span>
17+
);
18+
}

clients/web/src/routes/_protected/rooms.index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useState } from "react";
33
import { usePostRoomsHook } from "@shared/api/generated/endpoints/rooms/rooms";
44
import { useQuery } from "@tanstack/react-query";
55
import type { RoomWithOptionalGuestBooking } from "@shared";
6+
import { SortByContainer } from "@/components/rooms/SortByContainer";
67
import { PageShell } from "@/components/ui/PageShell";
78
import { RoomsHeader } from "@/components/rooms/RoomsHeader";
89
import { RoomsList } from "@/components/rooms/RoomsList";
@@ -16,10 +17,11 @@ function RoomsPage() {
1617
const [selectedFloors, setSelectedFloors] = useState<Array<number>>([]);
1718
const [selectedRoom, setSelectedRoom] =
1819
useState<RoomWithOptionalGuestBooking | null>(null);
20+
const [ascending, setAscending] = useState(true);
1921

2022
const postRooms = usePostRoomsHook();
2123

22-
const { data } = useQuery({
24+
const { data: rooms } = useQuery({
2325
queryKey: ["rooms", selectedFloors],
2426
queryFn: () =>
2527
postRooms({
@@ -44,8 +46,11 @@ function RoomsPage() {
4446
/>
4547
}
4648
>
49+
<SortByContainer ascending={ascending} setAscending={setAscending} />
50+
4751
<RoomsList
48-
rooms={data?.items ?? []}
52+
rooms={rooms?.items ?? []}
53+
ascending={ascending}
4954
onRoomSelect={setSelectedRoom}
5055
selectedRoomNumber={selectedRoom?.room_number ?? null}
5156
/>

clients/web/src/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
--color-shadow: #e7e7e7;
4949
--color-shadow-strong: #dcdcdc;
5050
--color-bg-container: #f8f8f8;
51+
--color-bg-disabled: #bababa;
5152
--color-bg-primary: #ffffff;
5253
--color-bg-secondary: #5d5d5d;
5354
--color-bg-high-priority: #ffeded;

0 commit comments

Comments
 (0)