basic room display grid + buttons

This commit is contained in:
2025-12-04 12:27:31 +01:00
parent 61a7ab64d1
commit 787a248e6f
10 changed files with 241 additions and 12 deletions

View File

@@ -3,9 +3,12 @@ import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg' import viteLogo from '/vite.svg'
import './App.css' import './App.css'
import { API_BASE } from './config.js'
import Login from './components/login.jsx' import Login from './components/login.jsx'
import MainApp from './components/MainApp.jsx' import MainApp from './components/MainApp.jsx'
export default function App() { export default function App() {
const [loading, setLoading] = useState(true); // true while checking auth const [loading, setLoading] = useState(true); // true while checking auth
const [loggedIn, setLoggedIn] = useState(false); const [loggedIn, setLoggedIn] = useState(false);
@@ -14,7 +17,7 @@ export default function App() {
async function checkAuth() { async function checkAuth() {
try { try {
// This endpoint should validate the refresh token cookie and return an access token // This endpoint should validate the refresh token cookie and return an access token
const res = await fetch("http://localhost:7080/auth/login_refresh_token", { const res = await fetch(`${API_BASE}/auth/login_refresh_token`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",

View File

@@ -0,0 +1,94 @@
import { createContext, useContext, useState, useEffect } from "react";
import { API_BASE } from "../config";
const HotelContext = createContext(null);
export function useHotel() {
return useContext(HotelContext);
}
export function HotelProvider({ accessToken, children }) {
const [rooms, setRooms] = useState([]);
const [conversations, setConversations] = useState([]);
const [users, setUsers] = useState([]);
const tokens = JSON.parse(accessToken);
const accessTokenSingle = tokens[0];
// --- API FUNCTIONS ---
async function fetchRooms() {
const res = await fetch( `${API_BASE}/rooms/rooms`, {
headers: { Authorization: `Bearer ${accessTokenSingle}` },
});
return res.json();
}
async function updateRoomStatus(roomId, status) {
await fetch(`${API_BASE}/rooms/clean_db_update/${roomId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${accessTokenSingle}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ status }),
});
// refresh cached rooms
const updated = await fetchRooms();
setRooms(updated);
}
async function fetchConversations() {
const res = await fetch(`${API_BASE}/chat/get_conv`, {
method: "POST",
headers: {
Authorization: `Bearer ${accessTokenSingle}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
conv_id: 4,
timestamp: "2025-09-25 11:05:33",
}),
});
return res.json();
}
async function fetchHotelUsers() {
const res = await fetch(`${API_BASE}/chat/hotel_users`, {
headers: { Authorization: `Bearer ${accessTokenSingle}` },
});
return res.json();
}
// --- INITIAL DATA LOADING ---
useEffect(() => {
if (!accessToken) return;
async function load() {
const [roomsData, convData, usersData] = await Promise.all([
fetchRooms(),
fetchConversations(),
//fetchHotelUsers(),
]);
setRooms(roomsData);
setConversations(convData);
//setUsers(usersData);
}
load();
}, [accessToken]);
return (
<HotelContext.Provider
value={{
rooms,
conversations,
users,
updateRoomStatus,
}}
>
{children}
</HotelContext.Provider>
);
}

View File

@@ -1,24 +1,41 @@
import { HotelProvider, useHotel } from "./HotelContext";
import RoomWidget from "./widget/roomWidget"
export default function MainApp() { export default function MainAppWrapper() {
// You could fetch your short-lived access token from localStorage
const accessToken = localStorage.getItem("access_tokens"); const accessToken = localStorage.getItem("access_tokens");
console.log("the accesToken in Mainapp is :" + accessToken);
return (
<HotelProvider accessToken={accessToken}>
<MainApp />
</HotelProvider>
);
}
function MainApp() {
const accessToken = localStorage.getItem("access_tokens");
const { rooms, conversations, users } = useHotel();
return ( return (
<div style={{ padding: "2rem" }}> <div style={{ padding: "2rem" }}>
<h1>Welcome to MyHotel!</h1> <h1>Welcome to MyHotel!</h1>
<p>Your access token (short-lived) is:</p>
<h2>Access token</h2>
<pre>{accessToken}</pre> <pre>{accessToken}</pre>
<section> <section>
<h2>Dashboard</h2> <h2>Dashboard</h2>
<ul> <ul>
<li>Rooms</li> <li>Rooms: {rooms.length}</li>
<li>Bookings</li> <RoomWidget roomlist={rooms}/>
<li>Guests</li> <li>Conversations: {conversations.length}</li>
<li>Reports</li> <li>Users: {users.length}</li>
</ul> </ul>
</section> </section>
</div> </div>
); );
} }

View File

@@ -0,0 +1,16 @@
import { useHotel } from "./HotelContext";
export default function RoomManagement() {
const { rooms } = useHotel();
return (
<div>
<h2>Rooms</h2>
{rooms.map(r => (
<div key={r.id}>
Room {r.number} {r.status}
</div>
))}
</div>
);
}

View File

@@ -1,3 +1,6 @@
import { API_BASE } from "../config";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Login from "./components/Login"; import Login from "./components/Login";
import MainApp from "./MainApp"; import MainApp from "./MainApp";
@@ -10,7 +13,7 @@ export default function App() {
async function checkLogin() { async function checkLogin() {
try { try {
// Call the endpoint that validates the refresh token cookie // Call the endpoint that validates the refresh token cookie
const res = await fetch("http://localhost:7080/auth/login_refresh_token", { const res = await fetch(`${API_BASE}/auth/login_refresh_token`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",

View File

@@ -1,3 +1,5 @@
import { API_BASE } from "../config";
import { useState } from "react"; import { useState } from "react";
export default function Login({ onLogin }) { export default function Login({ onLogin }) {
@@ -9,10 +11,10 @@ export default function Login({ onLogin }) {
e.preventDefault(); e.preventDefault();
setError(""); setError("");
const device_id = "147ac10b-58cc-4372-a567-0e02b2c3d479" ; const device_id = "147ac10b-58cc-4372-a567-0e02b2c3d479";
try { try {
const res = await fetch("http://localhost:7080/auth/create_refresh", { const res = await fetch(`${API_BASE}/auth/create_refresh`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",

View File

View File

@@ -0,0 +1,25 @@
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1px;
background-color: #777777;
}
.card {
gap: 1px;
background-color: #a3a3a3;
padding: 5px;
}
.card input {
width: 90%;
box-sizing: border-box; /* important */
max-width: 100%; /* prevents overflow */
}
.actions {
display: flex;
}

View File

@@ -0,0 +1,68 @@
import "./RoomWidget.css";
import { useHotel } from "../HotelContext";
import { useState } from "react";
export default function RoomWidget({ roomlist }) {
return (
<div className="grid">
{roomlist.map(room => (
<RoomCard
key={room.id}
id = {room.id}
number={room.number}
status={room.status}
/>
))}
</div>
);
}
function RoomCard({ number, status ,id}) {
const { updateRoomStatus } = useHotel();
const [editing, setEditing] = useState(false);
const [value, SetValue] = useState(status);
function submit() {
updateRoomStatus(id,value);
setEditing(false);
}
return (
<div className="card">
<h3>Room {number}</h3>
<h4>Id {id}</h4>
{!editing ?(
<p onClick={() => setEditing(true)} style={{ cursor: "pointer" }}>
Status: {status}
</p>
): (
<div>
<input
type="text"
value={value}
autoFocus
onChange={e => SetValue(e.target.value)}
onKeyDown={e => e.key === "Enter" && submit()}
/>
</div>
)}
<div className="actions">
<button onClick={() => updateRoomStatus(id, "clean")}>
Mark Clean
</button>
<button onClick={() => updateRoomStatus(id, "dirty")}>
Mark Dirty
</button>
</div>
</div>
);
}
//export default roomWidget

1
src/config.js Normal file
View File

@@ -0,0 +1 @@
export const API_BASE = "http://localhost:7080";