improving-week-36 #1
@@ -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} />} />
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -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) */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
84
my-app/src/pages/TestSession.jsx
Normal file
84
my-app/src/pages/TestSession.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
103
my-app/src/pages/TestSession.module.css
Normal file
103
my-app/src/pages/TestSession.module.css
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user