improving-week-36 #1
@@ -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>
|
||||
</>
|
||||
|
||||
88
my-app/src/components/BookingModal.jsx
Normal file
88
my-app/src/components/BookingModal.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}>
|
||||
@@ -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
|
||||
}))
|
||||
}));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user