Working on organizing the CSS better.

Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
2025-10-08 20:13:21 -04:00
parent 06da55443d
commit f7054bd1da
11 changed files with 217 additions and 249 deletions

View File

@@ -13,6 +13,7 @@
// External imports
import React from "react";
import { Box, Button } from "@mui/material";
import "../styles/BookingConfirmation.css";
// Internal imports
@@ -45,17 +46,7 @@ const BookingConfirmation: React.FC<BookingConfirmationProps> = ({
return null;
}
return (
<Box
sx={{
maxWidth: 400,
margin: "40px auto",
padding: 4,
borderRadius: 3,
boxShadow: 2,
background: "var(--color-white)",
textAlign: "center",
}}
>
<Box className="booking-confirmation-container">
{deleted ? (
<>
<h2>Booking Deleted</h2>

View File

@@ -27,6 +27,7 @@ import {
Select,
TextField,
} from "@mui/material";
import "../styles/BookingForm.css";
import { SelectChangeEvent } from "@mui/material/Select";
// Internal imports
@@ -376,22 +377,8 @@ const BookingForm: React.FC<BookingFormProps> = ({
}
fullWidth
margin="normal"
InputProps={{
readOnly: true,
sx: {
color: "rgba(34,34,34,0.8) !important",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
inputProps: {
style: {
color: "rgba(34,34,34,0.8)",
background: "#f0f0f0",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
},
}}
InputProps={{ readOnly: true }}
className="bookingform-viewfield"
disabled
/>
) : (
@@ -419,26 +406,8 @@ const BookingForm: React.FC<BookingFormProps> = ({
onChange={(e) => setTitle(e.target.value)}
fullWidth
margin="normal"
InputProps={
isViewMode
? {
readOnly: true,
sx: {
color: "rgba(34,34,34,0.8) !important",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
inputProps: {
style: {
color: "rgba(34,34,34,0.8)",
background: "#f0f0f0",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
},
}
: {}
}
InputProps={isViewMode ? { readOnly: true } : {}}
className={isViewMode ? "bookingform-viewfield" : undefined}
disabled={isViewMode}
/>
{/* Start time */}
@@ -463,26 +432,8 @@ const BookingForm: React.FC<BookingFormProps> = ({
fullWidth
margin="normal"
{...(!isViewMode && { required: true })}
InputProps={
isViewMode
? {
readOnly: true,
sx: {
color: "rgba(34,34,34,0.8) !important",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
inputProps: {
style: {
color: "rgba(34,34,34,0.8)",
background: "#f0f0f0",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
},
}
: {}
}
InputProps={isViewMode ? { readOnly: true } : {}}
className={isViewMode ? "bookingform-viewfield" : undefined}
disabled={isViewMode}
/>
<Box color="error.main" fontSize={13} mb={1}>
@@ -510,26 +461,8 @@ const BookingForm: React.FC<BookingFormProps> = ({
fullWidth
margin="normal"
{...(!isViewMode && { required: true })}
InputProps={
isViewMode
? {
readOnly: true,
sx: {
color: "rgba(34,34,34,0.8) !important",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
inputProps: {
style: {
color: "rgba(34,34,34,0.8)",
background: "#f0f0f0",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
},
}
: {}
}
InputProps={isViewMode ? { readOnly: true } : {}}
className={isViewMode ? "bookingform-viewfield" : undefined}
disabled={isViewMode}
/>
<Box color="error.main" fontSize={13} mb={1}>
@@ -557,31 +490,14 @@ const BookingForm: React.FC<BookingFormProps> = ({
}
fullWidth
margin="normal"
InputProps={{
readOnly: true,
sx: {
color: "rgba(34,34,34,0.8) !important",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
inputProps: {
style: {
color: "rgba(34,34,34,0.8)",
background: "#f0f0f0",
opacity: 0.8,
WebkitTextFillColor: "rgba(34,34,34,0.8)",
},
},
}}
InputProps={{ readOnly: true }}
className="bookingform-viewfield"
disabled
placeholder={invitees.length === 0 ? "No invitees" : undefined}
/>
) : (
<FormControl fullWidth margin="normal" error={remainingSlots < 0}>
<InputLabel
id="invitees-label"
sx={{ backgroundColor: "#fff", opacity: 1, px: 0.5 }}
>
<InputLabel id="invitees-label" className="bookingform-label-bg">
Invitees
</InputLabel>
<Select
@@ -591,7 +507,7 @@ const BookingForm: React.FC<BookingFormProps> = ({
onChange={handleInviteeChange}
input={<OutlinedInput label="Invitees" readOnly={isViewMode} />}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
<Box className="bookingform-invitee-list">
{(selected as string[]).map((value, idx) => {
const user = users.find(
(u: import("../interfaces").User) => u.email === value
@@ -609,6 +525,7 @@ const BookingForm: React.FC<BookingFormProps> = ({
key={value}
label={label}
color={errors[`invitee_${idx}`] ? "error" : undefined}
className="bookingform-invitee-chip"
/>
);
})}
@@ -618,13 +535,11 @@ const BookingForm: React.FC<BookingFormProps> = ({
>
<MenuItem
disabled
sx={{
color: remainingSlots < 0 ? "error.main" : "#222",
fontWeight: 600,
fontSize: 15,
pointerEvents: "none",
background: "inherit",
}}
className={
remainingSlots < 0
? "bookingform-invitee-over"
: "bookingform-invitee-remaining"
}
>
{remainingSlots >= 0
? `${remainingSlots} invitee slot${
@@ -663,33 +578,16 @@ const BookingForm: React.FC<BookingFormProps> = ({
value={email}
selected={isSelected}
disabled={disableUnselected}
sx={
className={
disableUnselected
? {
color: "#444",
opacity: 1,
fontWeight: "normal",
bgcolor: "inherit",
cursor: "not-allowed",
textDecoration: "line-through",
}
: {
fontWeight: isSelected ? "bold" : "normal",
bgcolor: isSelected
? "rgba(25, 118, 210, 0.08)"
: "inherit",
}
? "bookingform-invitee-unavailable"
: isSelected
? "bookingform-invitee-selected"
: "bookingform-invitee"
}
>
{isSelected && (
<span
style={{
color: "var(--color-primary)",
marginRight: 8,
}}
>
</span>
<span className="bookingform-invitee-check"></span>
)}
{label}
</MenuItem>

View File

@@ -6,6 +6,7 @@ import React from "react";
// MUI imports
import { List, ListItemButton, Typography } from "@mui/material";
import "../styles/BookingList.css";
// Internal helper imports
import {
@@ -44,7 +45,7 @@ const BookingList: React.FC<BookingListProps> = ({ bookings, onSelect }) => {
if (!bookings || bookings.length === 0) {
return (
<List sx={{ mt: 0.5 }}>
<List className="booking-list">
<ListItemButton className="bookinglist-empty" disabled>
<Typography className="bookinglist-item-title">
No bookings found.
@@ -67,45 +68,29 @@ const BookingList: React.FC<BookingListProps> = ({ bookings, onSelect }) => {
return user ? user.name : email;
};
return (
<List sx={{ mt: 0.5 }}>
<List className="booking-list">
{sortedBookings.map((booking) => {
let inviteeNames: string[] = [];
if (Array.isArray(booking.invitees)) {
inviteeNames = booking.invitees.map(getInviteeDisplay);
}
// Room color logic (use class for exact match)
const roomId = booking.room_id ? Number(booking.room_id) : 0;
// Corrected: room-color-((roomId-1)%20+1)
const roomColorIdx = ((roomId - 1) % 20) + 1;
const roomColorClass = `room-color-${roomColorIdx}`;
return (
<ListItemButton
key={booking.id}
onClick={() => onSelect && onSelect(booking)}
className={`bookinglist-item ${roomColorClass}`}
sx={{
flexDirection: "column",
alignItems: "flex-start",
borderRadius: 4,
mb: 1,
}}
className={`booking-list-item ${roomColorClass}`}
>
<span className="bookinglist-item-title">
<span className="booking-list-time">
{booking.start_time && booking.end_time
? `${formatBookingTime(
booking.start_time
)} - ${formatBookingTime(booking.end_time)}`
: "Time not set"}
</span>
<span
className="bookinglist-invitees"
style={{
marginTop: 4,
fontSize: "0.92em",
fontWeight: 400,
opacity: 0.85,
}}
>
<span className="booking-list-invitees">
{inviteeNames.length > 0
? `Invitees: ${inviteeNames.join(", ")}`
: "No invitees"}

View File

@@ -22,6 +22,7 @@ import {
DialogTitle,
Typography,
} from "@mui/material";
import "../styles/RoomDetailsModal.css";
// Type-only imports
import type { RoomDetailsModalProps } from "../interfaces";
@@ -48,7 +49,7 @@ const RoomDetailsModal: React.FC<RoomDetailsModalProps> = ({
Capacity: {room.capacity}
</Typography>
</Box>
<Typography variant="subtitle1" sx={{ mt: 2 }}>
<Typography variant="subtitle1" className="room-details-modal-title">
Equipment
</Typography>
<Typography variant="body2">

View File

@@ -15,10 +15,11 @@ import {
Typography,
ListItemButton,
} from "@mui/material";
import "../styles/RoomList.css";
import MeetingRoomIcon from "@mui/icons-material/MeetingRoom";
// Internal helper imports
import { getRoomListItemStyles, getRoomSecondaryText } from "../helpers/room";
import { getRoomSecondaryText } from "../helpers/room";
// Type-only imports
import type { RoomListProps } from "../interfaces";
@@ -46,90 +47,36 @@ const RoomList: FC<RoomListProps> = ({
logger.debug("[RoomList] rooms updated", rooms);
}, [rooms]);
return (
<Card
sx={{
height: "100%",
display: "flex",
flexDirection: "column",
border: `1px solid var(--color-primary)`,
borderRadius: 6,
}}
>
<CardContent
sx={{
flex: "0 0 auto",
borderBottom: "1px solid #e0e0e0",
backgroundColor: "var(--color-primary)",
mb: 0,
borderTopLeftRadius: 6,
borderTopRightRadius: 6,
}}
>
<Typography
variant="h6"
gutterBottom
sx={{
display: "flex",
alignItems: "center",
fontWeight: 600,
color: "var(--color-white)",
}}
>
<MeetingRoomIcon
sx={{ verticalAlign: "middle", mr: 1, color: "var(--color-white)" }}
/>
<Card className="room-list-card">
<CardContent className="room-list-header">
<Typography variant="h6" gutterBottom className="room-list-title">
<MeetingRoomIcon className="room-list-icon" />
Available Rooms
</Typography>
</CardContent>
<List
aria-label="Available conference rooms"
sx={{
flex: "1 1 auto",
overflowY: "auto",
minHeight: 0,
backgroundColor: "var(--color-background)",
color: "var(--color-text-primary)",
borderBottomLeftRadius: 6,
borderBottomRightRadius: 6,
}}
>
<List aria-label="Available conference rooms" className="room-list">
{rooms.length > 0 ? (
rooms.map((room) =>
onSelectRoom ? (
rooms.map((room) => {
const isSelected = room.id === selectedRoomId;
return onSelectRoom ? (
<ListItemButton
key={room.id}
selected={room.id === selectedRoomId}
selected={isSelected}
onClick={(e) => {
// ...removed debug log...
e.preventDefault();
e.stopPropagation();
onSelectRoom(room.id);
setTimeout(() => {
// ...removed debug log...
}, 0);
}}
aria-label={`Select room ${room.name}`}
data-testid={`room-item-${room.id}`}
sx={getRoomListItemStyles(room.id, selectedRoomId)}
className={
isSelected
? "room-list-item-selected room-list-item"
: "room-list-item"
}
>
<ListItemText
primary={
<span
style={{
color:
room.id === selectedRoomId
? "#000"
: `var(--room-color-${
((Number(room.id) - 1) % 20) + 1
})`,
fontWeight: room.id === selectedRoomId ? 700 : 500,
fontSize: room.id === selectedRoomId ? "1.08em" : "1em",
letterSpacing: 0.5,
}}
>
{room.name}
</span>
}
primary={room.name}
secondary={getRoomSecondaryText(room)}
/>
</ListItemButton>
@@ -138,14 +85,15 @@ const RoomList: FC<RoomListProps> = ({
key={room.id}
aria-label={`Select room ${room.name}`}
data-testid={`room-item-${room.id}`}
className="room-list-item"
>
<ListItemText
primary={room.name}
secondary={getRoomSecondaryText(room)}
/>
</ListItem>
)
)
);
})
) : (
<Typography>No rooms available.</Typography>
)}

View File

@@ -6,6 +6,7 @@
*/
import React from "react";
import { FormControl, InputLabel, Select, MenuItem } from "@mui/material";
import "../styles/RoomSelect.css";
import { useRooms } from "../context/RoomContext";
import { logger } from "../utils/logger";
@@ -57,11 +58,12 @@ const RoomSelect: React.FC<RoomSelectProps> = ({
}
}
return (
<FormControl size="small" sx={{ minWidth }}>
<InputLabel
id="room-select-label"
sx={{ backgroundColor: "#fff", opacity: 1, px: 0.5 }}
>
<FormControl
size="small"
className="room-select-form"
style={minWidth ? { minWidth } : undefined}
>
<InputLabel id="room-select-label" className="room-select-label-bg">
{label}
</InputLabel>
<Select
@@ -72,26 +74,40 @@ const RoomSelect: React.FC<RoomSelectProps> = ({
logger.info(`[RoomSelect] Room changed to ${e.target.value}`);
onChange(e.target.value as string | number);
}}
sx={{
backgroundColor: selectBgColor,
color: "#fff",
transition: "background-color 0.2s",
}}
className="room-select-dropdown"
style={
selectBgColor
? {
backgroundColor: selectBgColor,
color: "#fff",
transition: "background-color 0.2s",
}
: undefined
}
>
{rooms.map((room) => {
// Compute room index directly from room.id
const roomIdx = ((Number(room.id) - 1) % 20) + 1;
const bgColor = `var(--room-color-${roomIdx})`;
const isSelected = String(selectedRoomId) === String(room.id);
// For selected: background white, foreground = bgColor
// For unselected: background = bgColor, foreground = white
// For hover: handled by CSS
return (
<MenuItem
key={room.id}
value={room.id}
sx={{
backgroundColor: bgColor,
color: isSelected ? "#000" : "#fff",
fontWeight: 500,
}}
className={
"room-select-menu-item" + (isSelected ? " selected" : "")
}
style={
isSelected
? {
backgroundColor: "#fff",
color: bgColor,
fontWeight: "bold",
}
: { backgroundColor: bgColor, color: "#fff", fontWeight: 500 }
}
>
{room.name}
</MenuItem>

View File

@@ -0,0 +1,10 @@
/* BookingConfirmation component styles */
.booking-confirmation-container {
background: var(--color-white, #fff);
padding: 1.5em;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
max-width: 400px;
margin: 40px auto;
text-align: center;
}

View File

@@ -1,3 +1,77 @@
/* View mode field styles */
.bookingform-viewfield {
color: var(--view-mode-color, rgba(34, 34, 34, 0.8)) !important;
background: var(--view-mode-bg-color, #f0f0f0);
opacity: 0.8;
-webkit-text-fill-color: var(--view-mode-color, rgba(34, 34, 34, 0.8));
}
/* Label background for InputLabel */
.bookingform-label-bg {
background-color: var(--color-white, #fff);
opacity: 1;
padding-left: 0.5em;
padding-right: 0.5em;
}
/* Invitee list container */
.bookingform-invitee-list {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
}
/* Invitee chip */
.bookingform-invitee-chip {
color: var(--color-primary, #1976d2);
margin-right: 8px;
}
/* Invitee menu item: remaining slots */
.bookingform-invitee-remaining {
color: var(--color-darker-gray, #222);
font-weight: 600;
font-size: 15px;
pointer-events: none;
background: inherit;
}
/* Invitee menu item: over capacity */
.bookingform-invitee-over {
color: var(--color-light-red, #d32f2f);
font-weight: 600;
font-size: 15px;
pointer-events: none;
background: inherit;
}
/* Invitee menu item: unavailable */
.bookingform-invitee-unavailable {
color: var(--color-dark-gray, #444);
opacity: 1;
font-weight: normal;
background: inherit;
cursor: not-allowed;
text-decoration: line-through;
}
/* Invitee menu item: selected */
.bookingform-invitee-selected {
font-weight: bold;
background-color: var(--invitee-selected, rgba(25, 118, 210, 0.08));
}
/* Invitee menu item: normal */
.bookingform-invitee {
font-weight: normal;
background: inherit;
}
/* Invitee checkmark */
.bookingform-invitee-check {
color: var(--color-primary, #1976d2);
margin-right: 8px;
}
/* Room label styles */
.booking-form-room-label {
display: flex;
@@ -7,7 +81,7 @@
/* Room icon styles */
.booking-form-room-icon {
padding: 0.5em;
color: rgba(0, 0, 0, 0.54);
color: var(--room-icon, rgba(0, 0, 0, 0.54));
margin-left: 0;
}

View File

@@ -0,0 +1,4 @@
/* RoomDetailsModal component styles */
.room-details-modal-title {
margin-top: 2em;
}

View File

@@ -0,0 +1,33 @@
/* RoomSelect component styles */
.room-select-label-bg {
background-color: var(--color-white, #fff);
opacity: 1;
padding-left: 0.5em;
padding-right: 0.5em;
}
.room-select-dropdown {
background-color: var(--room-select-bg, #fff);
color: var(--room-select-color, #222);
}
.room-select-menu-item {
background-color: var(--room-menu-bg, inherit);
color: var(--room-menu-color, #fff);
}
.room-select-menu-item.selected {
font-weight: bold;
/* background and color set inline for selected */
}
.room-select-menu-item:hover,
.room-select-menu-item.Mui-focusVisible {
background-color: #f0f0f0 !important;
color: #111 !important;
}
.room-select-menu-item.unavailable {
color: var(--color-dark-gray, #444);
opacity: 1;
font-weight: normal;
background-color: inherit;
cursor: not-allowed;
text-decoration: line-through;
}

View File

@@ -11,7 +11,10 @@
--color-on-surface: #23272f;
--color-on-surface-secondary: #555;
--color-white: #fff;
--color-dark-gray: #444;
--color-darker-gray: #222;
--color-disabled: #bdbdbd;
--color-light-red: #d32f2f;
/* Room color variables */
--room-color-1: #1976d2;
--room-color-2: #388e3c;
@@ -33,4 +36,9 @@
--room-color-18: #607d8b;
--room-color-19: #795548;
--room-color-20: #b71c1c;
--view-mode-color: rgba(34, 34, 34, 0.8);
--view-mode-bg-color: #f0f0f0;
--invitee-selected: rgba(25, 118, 210, 0.08);
--room-icon: rgba(0, 0, 0, 0.54);
}