basic room display grid + buttons
This commit is contained in:
@@ -3,9 +3,12 @@ import reactLogo from './assets/react.svg'
|
||||
import viteLogo from '/vite.svg'
|
||||
import './App.css'
|
||||
|
||||
import { API_BASE } from './config.js'
|
||||
|
||||
import Login from './components/login.jsx'
|
||||
import MainApp from './components/MainApp.jsx'
|
||||
|
||||
|
||||
export default function App() {
|
||||
const [loading, setLoading] = useState(true); // true while checking auth
|
||||
const [loggedIn, setLoggedIn] = useState(false);
|
||||
@@ -14,7 +17,7 @@ export default function App() {
|
||||
async function checkAuth() {
|
||||
try {
|
||||
// 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",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
94
src/components/HotelContext.jsx
Normal file
94
src/components/HotelContext.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +1,41 @@
|
||||
import { HotelProvider, useHotel } from "./HotelContext";
|
||||
import RoomWidget from "./widget/roomWidget"
|
||||
|
||||
|
||||
export default function MainApp() {
|
||||
// You could fetch your short-lived access token from localStorage
|
||||
export default function MainAppWrapper() {
|
||||
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 (
|
||||
<div style={{ padding: "2rem" }}>
|
||||
<h1>Welcome to MyHotel!</h1>
|
||||
<p>Your access token (short-lived) is:</p>
|
||||
|
||||
<h2>Access token</h2>
|
||||
<pre>{accessToken}</pre>
|
||||
|
||||
<section>
|
||||
<h2>Dashboard</h2>
|
||||
<ul>
|
||||
<li>Rooms</li>
|
||||
<li>Bookings</li>
|
||||
<li>Guests</li>
|
||||
<li>Reports</li>
|
||||
<li>Rooms: {rooms.length}</li>
|
||||
<RoomWidget roomlist={rooms}/>
|
||||
<li>Conversations: {conversations.length}</li>
|
||||
<li>Users: {users.length}</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
16
src/components/RoomManagement.jsx
Normal file
16
src/components/RoomManagement.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
import { API_BASE } from "../config";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Login from "./components/Login";
|
||||
import MainApp from "./MainApp";
|
||||
@@ -10,7 +13,7 @@ export default function App() {
|
||||
async function checkLogin() {
|
||||
try {
|
||||
// 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",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { API_BASE } from "../config";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export default function Login({ onLogin }) {
|
||||
@@ -12,7 +14,7 @@ export default function Login({ onLogin }) {
|
||||
const device_id = "147ac10b-58cc-4372-a567-0e02b2c3d479";
|
||||
|
||||
try {
|
||||
const res = await fetch("http://localhost:7080/auth/create_refresh", {
|
||||
const res = await fetch(`${API_BASE}/auth/create_refresh`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
0
src/components/widget/ConvWiget.jsx
Normal file
0
src/components/widget/ConvWiget.jsx
Normal file
25
src/components/widget/RoomWidget.css
Normal file
25
src/components/widget/RoomWidget.css
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
68
src/components/widget/roomWidget.jsx
Normal file
68
src/components/widget/roomWidget.jsx
Normal 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
1
src/config.js
Normal file
@@ -0,0 +1 @@
|
||||
export const API_BASE = "http://localhost:7080";
|
||||
Reference in New Issue
Block a user