improving-week-36 #1

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

View File

@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
import Layout from './Layout';
import { RoomBooking } from './pages/RoomBooking';
import { NewBooking } from './pages/NewBooking';
import { CalendarSettings } from './pages/CalendarSettings';
import { BookingSettings } from './pages/BookingSettings';
import FullScreenLoader from './components/FullScreenLoader';
const AppRoutes = () => {
@@ -32,7 +32,7 @@ const AppRoutes = () => {
<Route path="/" element={<Layout />}>
<Route index element={<RoomBooking bookings={bookings} />} />
<Route path="new-booking" element={<NewBooking addBooking={addBooking} />} />
<Route path="calendar-settings" element={<CalendarSettings />} />
<Route path="booking-settings" element={<BookingSettings />} />
</Route>
</Routes>
</>

View File

@@ -0,0 +1,88 @@
import React, { useState } from 'react';
import { Button, Dialog, Heading, Modal } from 'react-aria-components';
import { convertDateObjectToString, getTimeFromIndex } from '../helpers';
import Dropdown from './Dropdown';
import { useBookingContext } from '../context/BookingContext';
import styles from './TimeCard.module.css';
export function BookingModal({
startTimeIndex,
hoursAvailable,
endTimeIndex,
setEndTimeIndex,
className
}) {
const booking = useBookingContext();
const bookingLengths = [
{ value: 1, label: "30 min" },
{ value: 2, label: "1 h" },
{ value: 3, label: "1.5 h" },
{ value: 4, label: "2 h" },
{ value: 5, label: "2.5 h" },
{ value: 6, label: "3 h" },
{ value: 7, label: "3.5 h" },
{ value: 8, label: "4 h" },
];
function getLabelFromAvailableHours(availableHours) {
return bookingLengths.find(option => option.value === availableHours)?.label || "Välj längd";
}
const disabledOptions = {
1: !(hoursAvailable > 0),
2: !(hoursAvailable > 1),
3: !(hoursAvailable > 2),
4: !(hoursAvailable > 3),
5: !(hoursAvailable > 4),
6: !(hoursAvailable > 5),
7: !(hoursAvailable > 6),
8: !(hoursAvailable > 7),
};
function handleChange(event) {
console.log(event.target.value);
setEndTimeIndex(startTimeIndex + parseInt(event.target.value));
booking.setSelectedEndIndex(startTimeIndex + parseInt(event.target.value));
}
return (
<Modal isDismissable className={className}>
<Dialog>
<form>
<Heading slot="title">{booking.title == "" ? "Jacobs bokning" : booking.title}</Heading>
<p>{convertDateObjectToString(booking.selectedDate)}</p>
<p className={styles.timeSpan}>{getTimeFromIndex(startTimeIndex)} - {getTimeFromIndex(endTimeIndex)}</p>
<div className={styles.sectionWithTitle}>
<label>Längd</label>
<Dropdown
options={bookingLengths}
disabledOptions={disabledOptions}
onChange={handleChange}
placeholder={{ value: hoursAvailable, label: getLabelFromAvailableHours(hoursAvailable) }}
/>
</div>
<div className={styles.sectionWithTitle}>
<label>Tilldelat rum</label>
<p>G5:12</p>
</div>
<div className={styles.sectionWithTitle}>
<label>Deltagare</label>
<p>{booking.participants.length > 0 ? booking.participants.map(p => p.name).join(", ") : "Inga deltagare"}</p>
</div>
<div className={styles.modalFooter}>
<Button className={styles.cancelButton} slot="close">
Avbryt
</Button>
<Button className={styles.saveButton} onClick={booking.handleSave}>
Boka
</Button>
</div>
</form>
</Dialog>
</Modal>
);
}

View File

@@ -31,7 +31,7 @@ const Header = () => {
<div className={styles.menu}>
{/* Menu items */}
<Link onClick={handleClick} to="/">Lokalbokning</Link>
<Link onClick={handleClick} to="/calendar-settings">Calendar Settings</Link>
<Link onClick={handleClick} to="/booking-settings">Booking Settings</Link>
</div>
)}
</header>

View File

@@ -1,17 +1,27 @@
import React from 'react';
import React, { useMemo } from 'react';
import Dropdown from './Dropdown';
import { SMALL_GROUP_ROOMS } from '../constants/bookingConstants';
import { useBookingContext } from '../context/BookingContext';
import { useSettingsContext } from '../context/SettingsContext';
import styles from './RoomSelectionField.module.css';
export function RoomSelectionField() {
const booking = useBookingContext();
const { settings } = useSettingsContext();
// Generate room options based on settings
const roomOptions = useMemo(() => {
return Array.from({ length: settings.numberOfRooms }, (_, i) => ({
value: `G5:${i + 1}`,
label: `G5:${i + 1}`,
}));
}, [settings.numberOfRooms]);
return (
<div>
<h3 className={styles.elementHeading}>Rum</h3>
<Dropdown
options={SMALL_GROUP_ROOMS}
options={roomOptions}
onChange={(e) => booking.handleRoomChange(e)}
placeholder={{
label: "Alla rum",

View File

@@ -1,12 +1,8 @@
import { Button, Dialog, DialogTrigger, Heading, Modal } from 'react-aria-components';
import { Button, DialogTrigger } from 'react-aria-components';
import React, { useState } from 'react';
import styles from './TimeCard.module.css';
import { convertDateObjectToString, getTimeFromIndex } from '../helpers';
import Dropdown from './Dropdown';
import { useBookingContext } from '../context/BookingContext';
import { BookingModal } from './BookingModal';
export default function TimeCard({
startTimeIndex,
@@ -47,37 +43,6 @@ export default function TimeCard({
const isEndState = ["availableEndTime", "selectedEnd"].includes(state);
const bookingLengths = [
{ value: 1, label: "30 min" },
{ value: 2, label: "1 h" },
{ value: 3, label: "1.5 h" },
{ value: 4, label: "2 h" },
{ value: 5, label: "2.5 h" },
{ value: 6, label: "3 h" },
{ value: 7, label: "3.5 h" },
{ value: 8, label: "4 h" },
];
function getLabelFromAvailableHours(availableHours) {
return bookingLengths.find(option => option.value === availableHours)?.label || "Välj längd";
}
const disabledOptions = {
1: !(hoursAvailable > 0),
2: !(hoursAvailable > 1),
3: !(hoursAvailable > 2),
4: !(hoursAvailable > 3),
5: !(hoursAvailable > 4),
6: !(hoursAvailable > 5),
7: !(hoursAvailable > 6),
8: !(hoursAvailable > 7),
};
function handleChange(event) {
console.log(event.target.value);
setEndTimeIndex(startTimeIndex + parseInt(event.target.value));
booking.setSelectedEndIndex(startTimeIndex + parseInt(event.target.value));
}
if (state === "availableSlot") {
@@ -99,43 +64,13 @@ export default function TimeCard({
</>
) : null}
</Button>
<Modal isDismissable className={styles.modalContainer}>
<Dialog>
<form>
<Heading slot="title">{booking.title == "" ? "Jacobs bokning" : booking.title}</Heading>
<p>{convertDateObjectToString(booking.selectedDate)}</p>
<p className={styles.timeSpan}>{getTimeFromIndex(startTimeIndex)} - {getTimeFromIndex(endTimeIndex)}</p>
<div className={styles.sectionWithTitle}>
<label>Längd</label>
<Dropdown
options={bookingLengths}
disabledOptions={disabledOptions}
onChange={handleChange}
placeholder={{ value: hoursAvailable, label: getLabelFromAvailableHours(hoursAvailable) }}
/>
</div>
<div className={styles.sectionWithTitle}>
<label>Tilldelat rum</label>
<p>G5:12</p>
</div>
<div className={styles.sectionWithTitle}>
<label>Deltagare</label>
<p>{booking.participants.join(", ")}</p>
</div>
<div className={styles.modalFooter}>
<Button className={styles.cancelButton} slot="close">
Avbryt
</Button>
<Button className={styles.saveButton} onClick={booking.handleSave}>
Boka
</Button>
</div>
</form>
</Dialog>
</Modal>
<BookingModal
startTimeIndex={startTimeIndex}
hoursAvailable={hoursAvailable}
endTimeIndex={endTimeIndex}
setEndTimeIndex={setEndTimeIndex}
className={styles.modalContainer}
/>
</DialogTrigger>
);
}

View File

@@ -8,10 +8,20 @@ import {
} from '../utils/bookingUtils';
import { DEFAULT_BOOKING_TITLE, PEOPLE } from '../constants/bookingConstants';
import { useDisabledOptions } from './useDisabledOptions';
import { useSettingsContext } from '../context/SettingsContext';
export function useBookingState(addBooking, initialDate = null) {
const { settings } = useSettingsContext();
// State hooks - simplified back to useState for stability
const [timeSlotsByRoom, setTimeSlotsByRoom] = useState(generateInitialRooms());
const [timeSlotsByRoom, setTimeSlotsByRoom] = useState(() =>
generateInitialRooms(
settings.roomAvailabilityChance,
settings.numberOfRooms,
settings.earliestTimeSlot,
settings.latestTimeSlot
)
);
const [currentRoom, setCurrentRoom] = useState(null);
const [selectedRoom, setSelectedRoom] = useState("allRooms");
const [selectedStartIndex, setSelectedStartIndex] = useState(null);
@@ -55,9 +65,14 @@ export function useBookingState(addBooking, initialDate = null) {
const handleDateChange = useCallback((date) => {
setSelectedDate(date);
setTimeSlotsByRoom(generateInitialRooms());
setTimeSlotsByRoom(generateInitialRooms(
settings.roomAvailabilityChance,
settings.numberOfRooms,
settings.earliestTimeSlot,
settings.latestTimeSlot
));
resetTimeSelections();
}, [resetTimeSelections]);
}, [resetTimeSelections, settings.roomAvailabilityChance, settings.numberOfRooms, settings.earliestTimeSlot, settings.latestTimeSlot]);
const handleRoomChange = useCallback((event) => {
const roomValue = event.target.value;

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { useSettingsContext } from '../context/SettingsContext';
import styles from './CalendarSettings.module.css';
import styles from './BookingSettings.module.css';
export function CalendarSettings() {
export function BookingSettings() {
const { settings, updateSettings, resetSettings, getEffectiveToday } = useSettingsContext();
const [tempDate, setTempDate] = useState('');
@@ -37,8 +37,8 @@ export function CalendarSettings() {
return (
<div className={styles.container}>
<div className={styles.header}>
<h1>Calendar Settings</h1>
<p>Configure calendar behavior for testing purposes</p>
<h1>Booking Settings</h1>
<p>Configure booking system behavior for testing purposes</p>
</div>
<div className={styles.content}>

View File

@@ -1,11 +1,12 @@
import { today, getLocalTimeZone } from '@internationalized/date';
import { NUMBER_OF_ROOMS, CHANCE_OF_AVAILABILITY } from '../constants/bookingConstants';
export const generateInitialRooms = (chanceOfAvailability = CHANCE_OF_AVAILABILITY, numberOfRooms = NUMBER_OF_ROOMS) => {
export const generateInitialRooms = (chanceOfAvailability = CHANCE_OF_AVAILABILITY, numberOfRooms = NUMBER_OF_ROOMS, earliestSlot = 0, latestSlot = 22) => {
return [...Array(numberOfRooms)].map((room, index) => ({
roomId: `G5:${index + 1}`,
times: Array.from({ length: 23 }, (_, i) => ({
available: Math.random() < chanceOfAvailability ? true : false
available: i >= earliestSlot && i <= latestSlot ?
(Math.random() < chanceOfAvailability) : false
}))
}));
};