Added the first page.

Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
2025-08-27 16:10:45 -04:00
parent 1d5757576d
commit 309d17bb65
4 changed files with 198 additions and 20 deletions

View File

@@ -15,8 +15,10 @@
"@types/node": "^16.7.13",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/react-router-dom": "^5.3.3",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.8.2",
"react-scripts": "5.0.1",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"

View File

@@ -1,26 +1,16 @@
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import LandingPage from "./pages/LandingPage";
function App() {
const App: React.FC = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
<Router>
<Routes>
<Route path="/" element={<LandingPage />} />
{/* Add more routes here, e.g. for the booking page */}
</Routes>
</Router>
);
}
};
export default App;

View File

@@ -0,0 +1,132 @@
import React, { useEffect, useState } from "react";
import {
Container,
Typography,
Box,
Button,
Grid,
Card,
CardContent,
CircularProgress,
AppBar,
Toolbar,
} from "@mui/material";
import { useNavigate } from "react-router-dom";
interface Room {
id: number;
name: string;
capacity: number;
}
interface Booking {
id: number;
room_id: number;
start_time: string;
end_time: string;
user: string;
}
const LandingPage: React.FC = () => {
const [rooms, setRooms] = useState<Room[]>([]);
const [bookings, setBookings] = useState<Booking[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const navigate = useNavigate();
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const roomsRes = await fetch("/api/rooms");
const bookingsRes = await fetch("/api/bookings/today");
if (!roomsRes.ok || !bookingsRes.ok)
throw new Error("Failed to fetch data");
setRooms(await roomsRes.json());
setBookings(await bookingsRes.json());
} catch (err: any) {
setError(err.message || "Unknown error");
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return (
<Box>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Conference Room Booking
</Typography>
<Button color="inherit" onClick={() => navigate("/bookings")}>
Bookings
</Button>
</Toolbar>
</AppBar>
<Container maxWidth="md" sx={{ mt: 4 }}>
<Typography variant="h4" gutterBottom>
Available Conference Rooms
</Typography>
{loading ? (
<CircularProgress />
) : error ? (
<Typography color="error">{error}</Typography>
) : (
<Grid container spacing={2}>
{rooms.map((room) => (
<Grid item xs={12} sm={6} md={4} key={room.id}>
<Card>
<CardContent>
<Typography variant="h6">{room.name}</Typography>
<Typography variant="body2">
Capacity: {room.capacity}
</Typography>
</CardContent>
</Card>
</Grid>
))}
</Grid>
)}
<Box mt={4}>
<Typography variant="h5" gutterBottom>
Today's Bookings
</Typography>
{loading ? (
<CircularProgress />
) : error ? (
<Typography color="error">{error}</Typography>
) : bookings.length === 0 ? (
<Typography>No bookings for today.</Typography>
) : (
<Grid container spacing={2}>
{bookings.map((booking) => (
<Grid item xs={12} sm={6} md={4} key={booking.id}>
<Card>
<CardContent>
<Typography variant="subtitle1">
Room:{" "}
{rooms.find((r) => r.id === booking.room_id)?.name ||
"Unknown"}
</Typography>
<Typography variant="body2">
{booking.start_time} - {booking.end_time}
</Typography>
<Typography variant="body2">
Booked by: {booking.user}
</Typography>
</CardContent>
</Card>
</Grid>
))}
</Grid>
)}
</Box>
</Container>
</Box>
);
};
export default LandingPage;

View File

@@ -2168,6 +2168,11 @@
dependencies:
"@types/node" "*"
"@types/history@^4.7.11":
version "4.7.11"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@@ -2281,11 +2286,35 @@
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.1.7.tgz#2863f2aa89e023592b981204ef92c5221b286410"
integrity sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==
"@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
dependencies:
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.20"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c"
integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==
dependencies:
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-transition-group@^4.4.12":
version "4.4.12"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044"
integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==
"@types/react@*":
version "19.1.12"
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.12.tgz#7bfaa76aabbb0b4fe0493c21a3a7a93d33e8937b"
integrity sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==
dependencies:
csstype "^3.0.2"
"@types/react@^19.0.0":
version "19.1.10"
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.10.tgz#a05015952ef328e1b85579c839a71304b07d21d9"
@@ -3556,6 +3585,11 @@ cookie@0.7.1:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
cookie@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610"
integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==
core-js-compat@^3.43.0:
version "3.45.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.45.1.tgz#424f3f4af30bf676fd1b67a579465104f64e9c7a"
@@ -8059,6 +8093,21 @@ react-refresh@^0.11.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
react-router-dom@^7.8.2:
version "7.8.2"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.8.2.tgz#25a8fc36588189baf3bbb5e360c8ffffbd2beabc"
integrity sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==
dependencies:
react-router "7.8.2"
react-router@7.8.2:
version "7.8.2"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.8.2.tgz#9d2d4147ca72832c550acc60ed688062d18f70b8"
integrity sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==
dependencies:
cookie "^1.0.1"
set-cookie-parser "^2.6.0"
react-scripts@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003"
@@ -8564,6 +8613,11 @@ serve-static@1.16.2:
parseurl "~1.3.3"
send "0.19.0"
set-cookie-parser@^2.6.0:
version "2.7.1"
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943"
integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==
set-function-length@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"