-
Notifications
You must be signed in to change notification settings - Fork 0
feat: rooms list sort by button #210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
e9e752f
feat: New Rooms Layout
eric-kitagawa b508b36
feat: improve query param serialization for arrays
eric-kitagawa 99716f8
chore: style overhaul + more explicit component naming
eric-kitagawa bddd50c
feat: tag component
eric-kitagawa 1ab5401
format
eric-kitagawa 591a321
more styling
eric-kitagawa f3c0aac
fix rooms type
eric-kitagawa 2855aa5
chore: delete rooms mock data and lint
eric-kitagawa b6b919a
feat: dropdown component + container
eric-kitagawa 4c95952
feat: add sorting logic to rooms page
eric-kitagawa 19a69b1
formatting
eric-kitagawa 6bf77c3
first pass on filter tag
eric-kitagawa 5b2e08c
Merge branch 'main' into eric/rooms-list-sort-by-button
eric-kitagawa e3db6f6
merge main into eric/rooms-list-backend-integration
eric-kitagawa 253db20
I HATE YOU LINT
eric-kitagawa 21edf0d
sneaky merge conflict
eric-kitagawa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import { ChevronDown } from "lucide-react"; | ||
| import { useGetRoomsFloors } from "@shared"; | ||
| import { Button } from "../ui/Button"; | ||
| import { useDropdown } from "../../hooks/use-dropdown"; | ||
| import { FloorDropdownOptions } from "./FloorDropdownOptions"; | ||
| import { FloorDropdownSearch } from "./FloorDropdownSearch"; | ||
|
|
||
| type FloorDropdownProps = { | ||
| selected?: Array<number>; | ||
| onChangeSelectedFloors?: (floors: Array<number>) => void; | ||
| }; | ||
|
|
||
| function getFloorLabel(selected: Array<number>) { | ||
| switch (selected.length) { | ||
| case 0: | ||
| return "All Floors"; | ||
| case 1: | ||
| return `Floor ${selected[0]}`; | ||
| default: | ||
| return `${selected.length} floors selected`; | ||
| } | ||
| } | ||
|
|
||
| export function FloorDropdown({ | ||
| selected = [], | ||
| onChangeSelectedFloors, | ||
| }: FloorDropdownProps) { | ||
| const { data: floors = [] } = useGetRoomsFloors(); | ||
|
|
||
| const { | ||
| open, | ||
| search, | ||
| pending, | ||
| searchProps, | ||
| triggerProps, | ||
| toggle, | ||
| selectProps, | ||
| cancelProps, | ||
| } = useDropdown<number>({ | ||
| selected, | ||
| onChangeSelectedItems: onChangeSelectedFloors, | ||
| }); | ||
|
|
||
| const filtered = floors.filter((f) => | ||
| `floor ${f}`.includes(search.trim().toLowerCase()), | ||
| ); | ||
|
|
||
| return ( | ||
| <div className="relative min-w-0 w-full max-w-75 bg-bg-primary rounded-md"> | ||
| <button | ||
| type="button" | ||
| {...triggerProps} | ||
| className={`flex w-full min-w-0 items-center justify-between text-sm text-text-default px-4 py-3 ${open ? "rounded-t-md" : "rounded-md"}`} | ||
| > | ||
| <span className="truncate">{getFloorLabel(selected)}</span> | ||
| <ChevronDown | ||
| className={`h-5 w-5 shrink-0 transition-transform duration-200 ${open ? "rotate-180" : ""}`} | ||
| strokeWidth={2.5} | ||
| /> | ||
| </button> | ||
|
|
||
| {open && ( | ||
| <div className="absolute left-0 top-full z-10 w-full bg-bg-primary rounded-b-md shadow-md"> | ||
| <FloorDropdownSearch {...searchProps} /> | ||
|
|
||
| <FloorDropdownOptions | ||
| floors={filtered} | ||
| pending={pending} | ||
| search={search} | ||
| onToggle={toggle} | ||
| /> | ||
|
|
||
| <div className="flex justify-end gap-3 border-t border-stroke-subtle p-3"> | ||
| <Button variant="secondary" {...cancelProps}> | ||
| Cancel | ||
| </Button> | ||
| <Button variant="primary" {...selectProps}> | ||
| Select | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| type FloorDropdownOptionsProps = { | ||
| floors: Array<number>; | ||
| pending: Array<number>; | ||
| search: string; | ||
| onToggle: (floor: number) => void; | ||
| }; | ||
|
|
||
| export function FloorDropdownOptions({ | ||
| floors, | ||
| pending, | ||
| search, | ||
| onToggle, | ||
| }: FloorDropdownOptionsProps) { | ||
| return ( | ||
| <div className="flex flex-col max-h-28 overflow-y-auto"> | ||
| {floors.length === 0 ? ( | ||
| <p className="px-4 py-2 text-sm text-text-subtle"> | ||
| No floors match "{search}" | ||
| </p> | ||
| ) : ( | ||
| floors.map((floor) => ( | ||
| <label | ||
| key={floor} | ||
| className="flex items-center gap-3 px-4 py-2 text-sm cursor-pointer hover:bg-bg-selected" | ||
| > | ||
| <input | ||
| type="checkbox" | ||
| checked={pending.includes(floor)} | ||
| onChange={() => onToggle(floor)} | ||
| className="accent-black" | ||
| /> | ||
| Floor {floor} | ||
| </label> | ||
| )) | ||
| )} | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { Search, X } from "lucide-react"; | ||
|
|
||
| type FloorDropdownSearchProps = { | ||
| value: string; | ||
| onChange: (value: string) => void; | ||
| }; | ||
|
|
||
| export const FloorDropdownSearch = ({ | ||
| value, | ||
| onChange, | ||
| }: FloorDropdownSearchProps) => { | ||
| return ( | ||
| <div className="flex items-center gap-3 border-y border-stroke-subtle px-4 py-3"> | ||
| <Search className="h-5 w-5 shrink-0 text-text-subtle" strokeWidth={2.5} /> | ||
| <input | ||
| type="text" | ||
| value={value} | ||
| onChange={(e) => onChange(e.target.value)} | ||
| placeholder="Search..." | ||
| className="flex-1 bg-transparent text-sm outline-none placeholder:text-text-subtle" | ||
| /> | ||
| {value && ( | ||
| <button type="button" onClick={() => onChange("")}> | ||
| <X className="h-5 w-5 text-text-subtle hover:text-text-default" /> | ||
| </button> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import { ChevronDown } from "lucide-react"; | ||
| import { useState } from "react"; | ||
| import { cn } from "@/lib/utils"; | ||
|
|
||
| type OrderByDropdownProps = { | ||
| ascending: boolean; | ||
| setAscending: (ascending: boolean) => void; | ||
| }; | ||
|
|
||
| export function OrderByDropdown({ | ||
| ascending, | ||
| setAscending, | ||
| }: OrderByDropdownProps) { | ||
| const [open, setOpen] = useState(false); | ||
| const label = ascending ? "Ascending" : "Descending"; | ||
|
|
||
| return ( | ||
| <div | ||
| className="relative min-w-0 w-full max-w-31.5" | ||
| onBlur={(e) => { | ||
| if (!e.currentTarget.contains(e.relatedTarget)) setOpen(false); | ||
| }} | ||
| > | ||
| <button | ||
| type="button" | ||
| className={cn( | ||
| "flex w-full items-center justify-between gap-2 py-1 pl-2 pr-1 border border-stroke-subtle bg-bg-primary text-left", | ||
| open ? "rounded-t-md" : "rounded-md", | ||
| )} | ||
| onClick={() => setOpen((o) => !o)} | ||
| aria-expanded={open} | ||
| > | ||
| <span className="min-w-0 truncate text-md text-text-default"> | ||
| {label} | ||
| </span> | ||
| <ChevronDown | ||
| className={cn( | ||
| "h-4 w-4 shrink-0 transition-transform", | ||
| open && "rotate-180", | ||
| )} | ||
| /> | ||
| </button> | ||
|
|
||
| {open && ( | ||
| <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"> | ||
| <button | ||
| type="button" | ||
| className="block w-full pl-2 pr-1 py-1 text-left text-md text-text-default hover:bg-bg-selected" | ||
| onClick={() => { | ||
| setAscending(!ascending); | ||
| setOpen(false); | ||
| }} | ||
| > | ||
| {ascending ? "Descending" : "Ascending"} | ||
| </button> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,58 @@ | ||
| import type { Room } from "@/components/rooms/RoomsList"; | ||
| import { | ||
| BrushCleaning, | ||
| CircleAlert, | ||
| CircleUserRound, | ||
| UserRoundCheck, | ||
| } from "lucide-react"; | ||
| import { Tag } from "../ui/Tag"; | ||
| import type { RoomWithOptionalGuestBooking } from "@shared"; | ||
| import { cn } from "@/lib/utils"; | ||
|
|
||
| type RoomCardProps = { | ||
| room: Room; | ||
| room: RoomWithOptionalGuestBooking; | ||
| isSelected?: boolean; | ||
| onClick: () => void; | ||
| }; | ||
|
|
||
| function getGuestDisplay(room: RoomWithOptionalGuestBooking) { | ||
| const hasGuests = (room.guests?.length ?? 0) > 0; | ||
| const guestLabel = room.guests | ||
| ?.map((guest) => `${guest.first_name} ${guest.last_name}`) | ||
| .join(", "); | ||
|
|
||
| return { hasGuests, guestLabel }; | ||
| } | ||
|
|
||
| export function RoomCard({ room, isSelected = false, onClick }: RoomCardProps) { | ||
| const { hasGuests, guestLabel } = getGuestDisplay(room); | ||
|
|
||
| return ( | ||
| <button | ||
| type="button" | ||
| onClick={onClick} | ||
| className={cn( | ||
| "flex flex-col items-start gap-[1.5vh] flex-1 min-w-0 w-full min-h-[11vh] text-left rounded-md border px-[0.6vw] py-[0.9vh] transition-colors", | ||
| "flex flex-col items-start flex-1 min-w-0 w-full min-h-36.25 text-left rounded-md border gap-2 px-5 py-4 transition-colors", | ||
| isSelected | ||
| ? "border-stroke-subtle bg-white shadow-sm" | ||
| : "border-zinc-200 hover:bg-selected", | ||
| ? "border-stroke-subtle bg-bg-selected shadow-sm" | ||
| : "border-stroke-subtle hover:bg-bg-selected cursor-pointer", | ||
| )} | ||
| > | ||
| <span className="text-xl font-bold text-zinc-900 "> | ||
| <span className="text-xl font-bold text-text-default "> | ||
| Room {room.room_number} | ||
| </span> | ||
| <span className="text-sm font-light text-zinc-500">{room.room_type}</span> | ||
| <span className="text-xs font-medium text-zinc-900"> | ||
| Jane Doe, John Doe | ||
| </span> | ||
| {room.tags && room.tags.length > 0 && ( | ||
| <div className="flex flex-wrap gap-[0.4vh]"> | ||
| {room.tags.map((tag) => ( | ||
| <span | ||
| key={tag} | ||
| className="inline-flex items-center px-[2vw] py-[0.5vh] rounded text-xs font-medium bg-primary text-white" | ||
| > | ||
| {tag} | ||
| </span> | ||
| ))} | ||
| <span className="text-sm text-text-subtle">{room.suite_type}</span> | ||
| {hasGuests && ( | ||
| <div className="flex items-center gap-1 text-sm text-text-subtle pb-3"> | ||
| <CircleUserRound className="h-4.5 w-4.5 shrink-0" /> | ||
| <span className="truncate">{guestLabel}</span> | ||
| </div> | ||
| )} | ||
| {/* Hardcoded tag for now */} | ||
| <div className="flex flex-row gap-3 pt-1"> | ||
| <Tag icon={CircleAlert} label="Urgent Task" variant="danger" /> | ||
| <Tag icon={UserRoundCheck} label="Occupied" variant="default" /> | ||
| <Tag icon={BrushCleaning} label="Needs Cleaning" variant="default" /> | ||
| </div> | ||
| </button> | ||
| ); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good but def shadcn for future cases, you're the goat for cooking up from scratch though