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 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",
|
||||||
|
|||||||
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() {
|
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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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 { 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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
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