improving-week-36 #1

Merged
jare2473 merged 41 commits from improving-week-36 into main 2025-09-04 10:49:05 +02:00
10 changed files with 234 additions and 10 deletions
Showing only changes of commit 3ec26f789a - Show all commits

View File

@@ -6,6 +6,7 @@ import Layout from './Layout';
import { RoomBooking } from './pages/RoomBooking';
import { NewBooking } from './pages/NewBooking';
import { BookingSettings } from './pages/BookingSettings';
import { TestSession } from './pages/TestSession';
import FullScreenLoader from './components/FullScreenLoader';
const AppRoutes = () => {
@@ -105,6 +106,9 @@ const AppRoutes = () => {
<Routes>
{/* Fullscreen route outside of Layout */}
<Route path="test-session" element={<TestSession />} />
<Route path="/" element={<Layout />}>
<Route index element={<RoomBooking bookings={bookings} showSuccessBanner={showSuccessBanner} lastCreatedBooking={lastCreatedBooking} onDismissBanner={() => setShowSuccessBanner(false)} onBookingUpdate={updateBooking} onBookingDelete={deleteBooking} showDeleteBanner={showDeleteBanner} lastDeletedBooking={lastDeletedBooking} onDismissDeleteBanner={() => setShowDeleteBanner(false)} />} />
<Route path="new-booking" element={<NewBooking addBooking={addBooking} />} />

View File

@@ -16,7 +16,7 @@ export function BookingModal({
isOpen = true
}) {
const booking = useBookingContext();
const { getCurrentUser } = useSettingsContext();
const { getCurrentUser, getDefaultBookingTitle } = useSettingsContext();
// Initialize with pre-selected booking length if available, or auto-select if only 30 min available
const initialLength = booking.selectedBookingLength > 0 ? booking.selectedBookingLength :
@@ -98,7 +98,7 @@ export function BookingModal({
>
<Dialog style={{overflow: 'hidden'}}>
<form>
<Heading slot="title">{booking.title == "" ? "Jacobs bokning" : booking.title}</Heading>
<Heading slot="title">{booking.title == "" ? getDefaultBookingTitle() : booking.title}</Heading>
<p>{convertDateObjectToString(booking.selectedDate)}</p>
<div className={styles.timeDisplay}>
<div className={styles.timeRange}>

View File

@@ -1,10 +1,11 @@
import React from 'react';
import { DEFAULT_BOOKING_TITLE } from '../constants/bookingConstants';
import { useBookingContext } from '../context/BookingContext';
import { useSettingsContext } from '../context/SettingsContext';
import styles from './BookingTitleField.module.css';
export function BookingTitleField({ compact = false }) {
const booking = useBookingContext();
const { getDefaultBookingTitle } = useSettingsContext();
return (
<>
@@ -13,7 +14,7 @@ export function BookingTitleField({ compact = false }) {
type="text"
value={booking.title}
onChange={(event) => booking.setTitle(event.target.value)}
placeholder={DEFAULT_BOOKING_TITLE}
placeholder={getDefaultBookingTitle()}
className={compact ? styles.compactTextInput : styles.textInput}
/>
</>

View File

@@ -1,10 +1,12 @@
import React, { useState, useRef, useEffect } from 'react';
import { PEOPLE, USER } from '../constants/bookingConstants';
import { useBookingContext } from '../context/BookingContext';
import { useSettingsContext } from '../context/SettingsContext';
import styles from './ParticipantsSelector.module.css';
export function ParticipantsSelector({ compact = false }) {
const booking = useBookingContext();
const { getCurrentUser } = useSettingsContext();
const [searchTerm, setSearchTerm] = useState('');
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [focusedIndex, setFocusedIndex] = useState(-1);
@@ -266,7 +268,7 @@ export function ParticipantsSelector({ compact = false }) {
<div className={styles.selectedParticipants}>
{/* Default User (Non-deletable) */}
<div className={`${styles.participantChip} ${styles.defaultUserChip}`}>
<span className={styles.participantName}>{USER.name}</span>
<span className={styles.participantName}>{getCurrentUser().name}</span>
</div>
{/* Additional Participants (Deletable) */}

View File

@@ -106,7 +106,7 @@ export const SettingsProvider = ({ children }) => {
numberOfRooms: 5,
earliestTimeSlot: 0,
latestTimeSlot: 23,
currentUserName: USER.name,
currentUserName: USER.name, // This will reset to "Jacob Reinikainen"
showDevelopmentBanner: false,
showBookingConfirmationBanner: false,
showBookingDeleteBanner: false,
@@ -125,6 +125,12 @@ export const SettingsProvider = ({ children }) => {
};
};
// Get dynamic default booking title
const getDefaultBookingTitle = () => {
const firstName = settings.currentUserName.split(' ')[0];
return `${firstName}s bokning`;
};
return (
<SettingsContext.Provider value={{
settings,
@@ -132,6 +138,7 @@ export const SettingsProvider = ({ children }) => {
resetSettings,
getEffectiveToday,
getCurrentUser,
getDefaultBookingTitle,
}}>
{children}
</SettingsContext.Provider>

View File

@@ -6,7 +6,7 @@ import {
generateId,
findObjectById
} from '../utils/bookingUtils';
import { DEFAULT_BOOKING_TITLE, PEOPLE, USER } from '../constants/bookingConstants';
import { PEOPLE, USER } from '../constants/bookingConstants';
import { useDisabledOptions } from './useDisabledOptions';
import { useSettingsContext } from '../context/SettingsContext';
@@ -25,7 +25,7 @@ function getRoomCategory(roomName) {
}
export function useBookingState(addBooking, initialDate = null) {
const { settings } = useSettingsContext();
const { settings, getDefaultBookingTitle } = useSettingsContext();
// State hooks - simplified back to useState for stability
const [timeSlotsByRoom, setTimeSlotsByRoom] = useState(() =>
@@ -131,14 +131,14 @@ export function useBookingState(addBooking, initialDate = null) {
endTime: selectedEndIndex,
room: roomToBook,
roomCategory: getRoomCategory(roomToBook),
title: title !== "" ? title : DEFAULT_BOOKING_TITLE,
title: title !== "" ? title : getDefaultBookingTitle(),
participants: allParticipants
});
resetSelections();
navigate('/');
window.scrollTo(0, 0);
}, [addBooking, selectedDate, selectedStartIndex, selectedEndIndex, selectedRoom, assignedRoom, title, participants, resetSelections, navigate]);
}, [addBooking, selectedDate, selectedStartIndex, selectedEndIndex, selectedRoom, assignedRoom, title, participants, resetSelections, navigate, getDefaultBookingTitle]);
const handleTimeCardExit = useCallback(() => {
if (!selectedEndIndex) {

View File

@@ -305,6 +305,12 @@ export function BookingSettings() {
<button onClick={resetSettings} className={styles.resetButton}>
Reset to Defaults
</button>
<button
onClick={() => window.location.href = '/test-session'}
className={styles.testSessionButton}
>
🧪 Start Test Session
</button>
<div className={styles.info}>
Settings are automatically saved and will persist between sessions
</div>

View File

@@ -258,6 +258,23 @@
background: #b91c1c;
}
.testSessionButton {
padding: 0.75rem 2rem;
background: #2563eb;
color: white;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s;
margin-left: 1rem;
}
.testSessionButton:hover {
background: #1d4ed8;
}
.info {
font-size: 0.875rem;
color: #6b7280;

View File

@@ -0,0 +1,84 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useSettingsContext } from '../context/SettingsContext';
import styles from './TestSession.module.css';
const NORMAL_NAMES = [
"Nigel Twittlebottom",
"Percival Crumplebottom",
"Reginald Puddingworth",
"Algernon Snodgrass",
"Bartholomew Wigglesworth"
];
export function TestSession() {
const [userName, setUserName] = useState('');
const [isUsingNormalName, setIsUsingNormalName] = useState(false);
const { updateSettings } = useSettingsContext();
const navigate = useNavigate();
const handleNormalNameSelect = (name) => {
setUserName(name);
setIsUsingNormalName(true);
};
const handleCustomNameChange = (e) => {
setUserName(e.target.value);
setIsUsingNormalName(false);
};
const handleStart = () => {
if (userName.trim()) {
// Update the user name in settings
updateSettings({ currentUserName: userName.trim() });
// Navigate to the main app
navigate('/');
}
};
const canStart = userName.trim().length > 0;
return (
<div className={styles.container}>
<div className={styles.content}>
<div className={styles.header}>
<h1>Test Session</h1>
<p>Enter your name to begin:</p>
</div>
<div className={styles.nameSection}>
<input
type="text"
value={userName}
onChange={handleCustomNameChange}
placeholder="Your name"
className={styles.nameInput}
/>
<p>Or choose one:</p>
<div className={styles.normalNames}>
{NORMAL_NAMES.map((name, index) => (
<button
key={index}
onClick={() => handleNormalNameSelect(name)}
className={`${styles.normalNameButton} ${userName === name ? styles.selected : ''}`}
>
{name}
</button>
))}
</div>
</div>
{canStart && (
<button
onClick={handleStart}
className={styles.startButton}
>
Start
</button>
)}
</div>
</div>
);
}

View File

@@ -0,0 +1,103 @@
.container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.content {
max-width: 500px;
width: 100%;
text-align: center;
}
.header h1 {
font-size: 2rem;
color: #1f2937;
margin-bottom: 0.5rem;
font-weight: 600;
}
.header p {
font-size: 1rem;
color: #6b7280;
margin-bottom: 2rem;
}
.nameSection {
margin: 2rem 0;
}
.nameInput {
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border: 1px solid #d1d5db;
border-radius: 6px;
outline: none;
margin-bottom: 1.5rem;
}
.nameInput:focus {
border-color: #2563eb;
}
.normalNames {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
justify-content: center;
}
.normalNameButton {
padding: 0.5rem 1rem;
background: white;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 0.9rem;
color: #374151;
cursor: pointer;
margin: 0.25rem;
}
.normalNameButton:hover {
background: #f9fafb;
}
.normalNameButton.selected {
background: #2563eb;
border-color: #2563eb;
color: white;
}
.startButton {
padding: 0.75rem 2rem;
background: #2563eb;
color: white;
border: none;
border-radius: 6px;
font-size: 1rem;
cursor: pointer;
margin-top: 1.5rem;
}
.startButton:hover {
background: #1d4ed8;
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.normalNames {
flex-direction: column;
align-items: center;
}
.normalNameButton {
width: 100%;
max-width: 200px;
}
}