diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index 9409b13..0b5b472 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -13,7 +13,14 @@ const montserrat = Montserrat({ export default function RootLayout({ children }: { children: ReactNode }) { return ( - + {children} diff --git a/client/src/app/test/card/page.tsx b/client/src/app/test/card/page.tsx new file mode 100644 index 0000000..f74fd58 --- /dev/null +++ b/client/src/app/test/card/page.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { + AdminRoomCard, + BookingRoomCard, + RoomCard, +} from "@/components/ui/room-card"; +import { roomsMock } from "@/types/card"; + +export default function RoomsList() { + return ( +
+

Booking Rooms Display

+ +
+ {roomsMock.map((room) => ( + alert("Book")} + /> + ))} +
+ +
+ +

+ Admin Meeting Rooms Display +

+ +
+ {roomsMock.map((room) => ( + alert("View")} + onEdit={() => alert("Edit")} + onRemove={() => alert("Remove")} + /> + ))} +
+
+ +

General Rooms Display

+ +
+ {roomsMock.map((room) => ( + + ))} +
+
+ ); +} diff --git a/client/src/components/alert-dialog.tsx b/client/src/components/alert-dialog.tsx index bf2fdde..3c3018c 100644 --- a/client/src/components/alert-dialog.tsx +++ b/client/src/components/alert-dialog.tsx @@ -25,7 +25,7 @@ import { cn } from "@/lib/utils"; * * @param title Optional title displayed at the top of the dialog * @param successText Text displayed when the operation is complete - * @param color Optional color for the success icon and button (default: `#006DD5`) + * @param color Optional color for the success icon and button (default: `var(--bloom-blue)`) * @param showIcon Whether to show the icon above the content (default: `true`) * @param children React node used as the DialogTrigger * @@ -33,7 +33,7 @@ import { cn } from "@/lib/utils"; * * * @@ -42,7 +42,7 @@ function AlertDialog({ title, successText, children, - color = "#006DD5", + color = "var(--bloom-blue)", showIcon = true, }: { title?: string; @@ -80,7 +80,7 @@ function AlertDialog({ + * + * + */ +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +}; diff --git a/client/src/components/ui/room-card.tsx b/client/src/components/ui/room-card.tsx new file mode 100644 index 0000000..83bd896 --- /dev/null +++ b/client/src/components/ui/room-card.tsx @@ -0,0 +1,301 @@ +"use client"; + +import Image from "next/image"; +import { FaMicrophone } from "react-icons/fa"; +import { GrStatusUnknown } from "react-icons/gr"; +import { IoMdVideocam } from "react-icons/io"; +import { LuHdmiPort } from "react-icons/lu"; +import { MdPhonelinkRing } from "react-icons/md"; +import { RiArtboardLine } from "react-icons/ri"; + +import { Card } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import { Room } from "@/types/card"; + +import { Button } from "./button"; + +// Mapping amenity string -> React icon component +const amenityIcons: Record = { + Default: , + Audio: , + Video: , + HDMI: , + "White Board": , + "Sound System": , +}; + +/** + * Renders a list of room amenities using icons or plain text. + * + * @param {Object} props + * @param {string[]} props.amenities List of amenities to display. + * @param {boolean} [props.hideIcon=false] If true, displays amenities as plain text. + * @param {string} [props.className] Optional extra class names for styling. + * + * @example + * + * + */ +const Amenities = ({ + amenities, + hideIcon = false, + className, +}: { + amenities: string[]; + hideIcon?: boolean; + className?: string; +}) => { + if (hideIcon) { + return {amenities.join(", ")}; + } + + return ( +
+ {amenities.map((amenity) => ( +
+ {amenityIcons[amenity] || amenityIcons["Default"]} +
+ ))} +
+ ); +}; + +/** + * Displays a room with details in a label-value grid format. + * + * Shows image, room name, location, seats, amenities and availability. + * Intended for general / admin use as an info card. + * + * @param {Object} props + * @param {Room} props.room Room data object. + * + * @example + * const room = { + * title: "Meeting Room A", + * image: "/rooms/a.jpg", + * location: "Level 2 Block B", + * seats: 10, + * amenities: ["Audio", "HDMI"], + * available: true, + * }; + * + * + */ +function RoomCard({ room }: { room: Room; hideIcon?: boolean }) { + const roomDetailsGroups = [ + { label: "Room Name", value: room.title }, + { label: "Location", value: room.location }, + { label: "No of Seats", value: room.seats }, + { + label: "Amenities", + value: , + }, + { label: "Availability", value: room.availablility }, + ]; + + return ( +
+
+
+ {room.title} +
+
+ + {/* Info grid ensures perfect alignment */} +
+ {roomDetailsGroups.map((item) => ( +
+ + {item.label} + + {item.value} +
+ ))} +
+
+ ); +} + +type BookingRoomProps = { + room: Room; + hideIcon?: boolean; + onBook?: () => void; +}; + +/** + * User-facing booking card for displaying a room. + * + * Responsive: shows compact card on mobile, detailed card on desktop. + * Includes image, title, location, facilities, availability, and a Book button. + * + * @param {Object} props + * @param {Room} props.room Room data object. + * @param {boolean} [props.hideIcon=false] Hide icons for amenities if true. + * @param {() => void} [props.onBook] Callback when the Book button is clicked. + * + * @example + * const room = { + * title: "Training Room B", + * image: "/rooms/b.jpg", + * location: "Level 3 Block D", + * seats: 12, + * amenities: ["Audio", "HDMI", "White Board"], + * available: true, + * }; + * + * console.log("Booked!")} /> + */ +export function BookingRoomCard({ room, onBook }: BookingRoomProps) { + // Default: User card view + return ( + +
+ {room.title} +
+ +
+ {room.title} + + {room.location} + +
+ +
+ +
+
+ ); +} + +type AdminRoomCardProps = { + room: Room; + hideIcon?: boolean; + onView?: () => void; + onEdit?: () => void; + onRemove?: () => void; +}; + +/** + * Admin card for managing room details. + * + * Displays full room info: name, location, seats, amenities, and bookings. + * Includes admin action buttons: View Bookings, Edit, Remove. + * Supports disabling the Remove button if room is already removed. + * + * @param {Object} props + * @param {Room} props.room Room data object. + * @param {boolean} [props.hideIcon=false] Hide amenity icons if true. + * @param {() => void} [props.onView] Callback for View Bookings button. + * @param {() => void} [props.onEdit] Callback for Edit button. + * @param {() => void} [props.onRemove] Callback for Remove button. + * + * @example + * const room = { + * title: "Studio Room C", + * image: "/rooms/c.jpg", + * location: "Level 1 Block E", + * seats: 20, + * amenities: ["White Board", "Audio"], + * bookings: 10, + * removed: false, + * }; + * + * console.log("View")} + * onEdit={() => console.log("Edit")} + * onRemove={() => console.log("Remove")} + * /> + */ +function AdminRoomCard({ + room, + hideIcon, + onView, + onEdit, + onRemove, +}: AdminRoomCardProps) { + const roomDetails = [ + { label: "Room Name", value: room.title }, + { label: "Location", value: room.location }, + { label: "No of Seats", value: room.seats }, + { + label: "Amenities", + value: , + }, + { label: "Bookings", value: room.bookings }, + ]; + + return ( +
+ {/* Room Image */} +
+ {room.title} +
+ + {/* Room Details */} +
+ {roomDetails.map((item) => ( +
+ {item.label} + {item.value} +
+ ))} +
+ + {/* Action Buttons */} +
+ + + +
+
+ ); +} + +export { AdminRoomCard, RoomCard }; diff --git a/client/src/types/card.ts b/client/src/types/card.ts new file mode 100644 index 0000000..3150232 --- /dev/null +++ b/client/src/types/card.ts @@ -0,0 +1,86 @@ +export type Room = { + id: number; + title: string; + image: string; + location: string; + available: boolean; + availablility: string; + seats: number; + amenities: string[]; + bookings: number; + removed?: boolean; +}; + +const defaultImage = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z/C/HwAFgwJ/lYpukQAAAABJRU5ErkJggg=="; + +export const roomsMock: Room[] = [ + { + id: 1, + title: "BHAGIRATHI", + image: defaultImage, + seats: 4, + location: "Pune Hinjewadi", + available: true, + availablility: "8:00am - 5:00pm, Mon - Fri", + amenities: ["Audio", "Video", "HDMI", "White Board", "Sound System"], + bookings: 10, + removed: true, + }, + { + id: 2, + title: "GHATAPRABHA", + image: defaultImage, + seats: 8, + location: "St Catherine's college, 2 Park Road", + available: false, + availablility: "8:00am - 7:00pm, Mon - Fri", + amenities: ["Audio", "Video", "HDMI", "White Board", "Sound System"], + bookings: 5, + }, + { + id: 3, + title: "BHIMA", + image: defaultImage, + seats: 8, + location: "UNIT-1B PNQ- HJ", + available: true, + availablility: "9:00am - 5:00pm, Mon - Fri", + amenities: ["Audio", "Video", "HDMI", "White Board", "Sound System"], + bookings: 3, + }, + { + id: 4, + title: "TUNGBHADRA", + image: defaultImage, + seats: 4, + location: "St Catherine's college, 2 Park Road", + available: true, + availablility: "12;00pm - 7:00pm, Wed - Fri", + amenities: ["Audio", "Video", "HDMI", "White Board", "Sound System"], + bookings: 8, + removed: true, + }, + { + id: 5, + title: "BRAMHAPUTRA", + image: defaultImage, + seats: 4, + location: "UNIT-1B PNQ- HJ", + available: false, + availablility: "10:00am - 4:00pm, Mon - Tues", + amenities: ["Audio", "Video", "HDMI", "White Board", "Sound System"], + bookings: 7, + }, + { + id: 6, + title: "SINDU", + image: defaultImage, + seats: 4, + location: "Pune Hinjewadi", + available: true, + availablility: "9:00am - 3:00pm, Mon - Thurs", + amenities: ["Audio", "Video", "HDMI", "White Board", "Sound System"], + bookings: 5, + }, +];