some css fix and websocket fix for item update

This commit is contained in:
2026-02-20 01:38:37 +01:00
parent e7a283b39e
commit aecad87a9a
19 changed files with 997 additions and 633 deletions

698
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"baseline-browser-mapping": "^2.9.19",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",

View File

@@ -1,42 +1,36 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
padding: .5rem;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
.app {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.user-btn {
position: fixed;
top: 12px;
right: 12px;
z-index: 1000;
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
.log-menu {
position: fixed;
top: 50px;
right: 12px;
width: 280px;
background: #111;
border: 1px solid #333;
padding: 12px;
z-index: 1001;
box-shadow: 0 8px 30px rgba(0,0,0,.4);
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
.log-menu button,
.log-menu input {
width: 100%;
margin-bottom: 8px;
}

View File

@@ -8,6 +8,8 @@ import { API_BASE } from './config.js'
import Login from './components/login.jsx'
import MainApp from './components/MainApp.jsx'
import AdminWidget from './components/widget/adminWidget.jsx'
export const ClientContext = createContext(null);
@@ -22,9 +24,8 @@ export default function App() {
const [updateCurrentPassword, setUpdateCurrentPassword] = useState("");
const [updateNewPassword, setUpdateNewPassword] = useState("");
const accessToken = localStorage.getItem("access_tokens");
const tokens = JSON.parse(accessToken)
const accessTokenSingle = tokens[0];
const [logMenuOpen, setLogMenuOpen] = useState(false)
useEffect(() => {
@@ -60,6 +61,7 @@ async function checkAuth() {
}
}
async function logoutUser() {
try {
@@ -87,6 +89,7 @@ async function logoutUser() {
}
async function logoutAllSessions() {
if (!accessTokenSingle) return;
try {
const res = await fetch(`${API_BASE}/auth/logout_all_devices`, {
@@ -113,10 +116,68 @@ async function logoutAllSessions() {
}
}
function RegisterUser() {
let [username, setUserName] = useState("");
let [password, setPassword] = useState("");
let [displayName, setDisplayName] = useState("");
let hotel_ids = [1];
function registerUser(username, password, hotel_ids, displayname) {
const payload = {
username,
password,
hotel_ids,
displayname
}
console.log("Reg")
const res = fetch(`${API_BASE}/auth/register`, {
method: "PUT",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(payload),
}
)
}
return(
<div className="CreateUserDiv">
<input
value={username}
onChange={e => setUserName(e.target.value)}
placeholder="NomDeCompte"
type="text" />
<input
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Mot de Passe"
type="text" />
<input
value={displayName}
onChange={e => setDisplayName(e.target.value)}
placeholder="Nome afficher"
type="text" />
<button
onClick={() =>
registerUser(username, password, hotel_ids, displayName)
}
>
Créer l'utilisateur
</button>
</div>
);
}
function updatePassword(e) {
async function updatePassword(e) {
e.preventDefault();
//try
@@ -126,7 +187,7 @@ function updatePassword(e) {
newpassword : updateNewPassword
}
const res = fetch(`${API_BASE}/auth/update_password`, {
const res = await fetch(`${API_BASE}/auth/update_password`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
@@ -146,33 +207,46 @@ function updatePassword(e) {
return (
<>
<button onClick={logoutUser}>disconnect</button>
<button onClick={logoutAllSessions}>disconnect all session</button>
<button className="user-btn" onClick={() => setLogMenuOpen(o => !o)}>
⚙️
</button>
<form onSubmit={updatePassword}>
<input
type="text"
placeholder="Username"
value={updateUsername}
onChange={(e) => setUpdateUsername(e.target.value)}
/>
{logMenuOpen && (
<div className='log-menu'>
<button onClick={logoutUser}>disconnect</button>
<button onClick={logoutAllSessions}>disconnect all session</button>
<input
type="text"
placeholder="MDP actuel"
value={updateCurrentPassword}
onChange={(e) => setUpdateCurrentPassword(e.target.value)}
/>
<form onSubmit={updatePassword}>
<h1>Update Password</h1>
<input
type="text"
placeholder="Username"
value={updateUsername}
onChange={(e) => setUpdateUsername(e.target.value)}
/>
<input
type="text"
placeholder="Nouveau MDP"
value={updateNewPassword}
onChange={(e) => setUpdateNewPassword(e.target.value)}
/>
<input
type="text"
placeholder="MDP actuel"
value={updateCurrentPassword}
onChange={(e) => setUpdateCurrentPassword(e.target.value)}
/>
<button type="submit">Uppdate</button>
</form>
<input
type="text"
placeholder="Nouveau MDP"
value={updateNewPassword}
onChange={(e) => setUpdateNewPassword(e.target.value)}
/>
<button type="submit">Uppdate</button>
</form>
<RegisterUser/>
</div>
)}
{loggedIn ? (
<MainApp resClientId={clientId} />

View File

@@ -1,13 +1,16 @@
import { HotelProvider, useHotel } from "./HotelContext";
import { HotelProvider, useHotel } from "./context/HotelContext";
import RoomWidget from "./widget/roomWidget"
import ChatWidget from "./widget/chatWidget"
import InventoryWidget from "./widget/inventoryWidget";
import { useEffect, useState } from "react";
import "./MainApp.css"
import AdminWidget from "./widget/adminWidget";
export default function MainAppWrapper({resClientId}) {
const accessToken = localStorage.getItem("access_tokens");
const accessToken = localStorage.getItem("access_tokens");
return (
<HotelProvider accessToken={accessToken} resClientId={resClientId}>
@@ -18,25 +21,29 @@ export default function MainAppWrapper({resClientId}) {
function MainApp() {
const accessToken = localStorage.getItem("access_tokens");
const { rooms, conversations, users } = useHotel();
const VIEW = {
ROOMS: "rooms",
INVENTORY: "inventory",
ADMIN: "admin",
};
const { rooms } = useHotel();
const { conversations } = useHotel();
const { users } = useHotel();
const [view, setView] = useState(VIEW.ROOMS)
return (
<div style={{ padding: "2rem" }}>
<h1>Welcome to MyHotel!</h1>
<div>
<div className="toolbar">
<button className="toolbutton" onClick={() => setView(VIEW.ROOMS)}>Rooms</button>
<button className="toolbutton" onClick={() => setView(VIEW.INVENTORY)}>Inventory</button>
</div>
<h2>Access token</h2>
<pre>{accessToken}</pre>
<h3>Rooms: {rooms.length}</h3>
<h3>Conversations: {conversations.length}</h3>
<h3>Users: {users.length}</h3>
<h2>Dashboard</h2>
<section className="main">
<RoomWidget roomlist={rooms}/>
<ChatWidget convlist={conversations}/>
<InventoryWidget/>
<AdminWidget/>
{view === VIEW.ROOMS && <RoomWidget />}
{view === VIEW.INVENTORY && <InventoryWidget />}
<ChatWidget convlist={conversations} />
</section>
</div>
);

View File

@@ -1,7 +1,23 @@
.main {
display: grid;
grid-template-columns: 70% 30%;
height: 80%;
}
display: flex;
flex-direction: column;
justify-content: space-around;
background-color: rgb(160, 149, 199);
}
.main > * {
min-width: 0; /* prevents overflow bugs */
}
.toolbar {
justify-content: space-around;
height: 10vh;
height: 5vh;
}
.toolbutton {
margin: .5rem;
height: 80%;
width: 20%;
}

View File

@@ -2,7 +2,7 @@ import {createContext, useContext,
useState, useEffect,
useRef
} from "react";
import { API_BASE } from "../config";
import { API_BASE } from "../../config";
const HotelContext = createContext(null);
@@ -16,6 +16,9 @@ export function HotelProvider({ accessToken, children, resClientId}) {
const [rooms, setRooms] = useState([]);
const [conversations, setConversations] = useState([]);
const [messagesByConvId, setMessagesByConvId] = useState({});
const [items, SetItems] = useState([]);
const [users, setUsers] = useState([]);
const [usersById, setUsersById] = useState({});
@@ -55,7 +58,6 @@ export function HotelProvider({ accessToken, children, resClientId}) {
// CHAT
async function createConversation(name) {
const payload = {
@@ -278,11 +280,24 @@ export function HotelProvider({ accessToken, children, resClientId}) {
break;
}
case "item_update": {
SetItems(prev =>
prev.map(i =>
i.id === msg.item_id
? { ...i, amount: msg.number }
: i
)
);
break;
}
case "chat_message": {
appendMessage(msg);
break;
}
default:
console.warn("Unhandled WS message bruuuuh:", msg);
}
@@ -290,7 +305,7 @@ export function HotelProvider({ accessToken, children, resClientId}) {
// --- ADMIN PORTAL ----
function registerUser(username,password,hotel_ids, displayname) {
function registerUser(username, password, hotel_ids, displayname) {
const payload = {
username,
@@ -351,10 +366,11 @@ async function addHotelUser(user_id,hotel_ids) {
if (!accessToken) return;
async function load() {
const [roomsData, convData, usersData] = await Promise.all([
const [roomsData, convData, usersData, itemsData] = await Promise.all([
fetchRooms(),
fetchConversations(),
fetchHotelUsers(),
fetchHotelInventory(),
]);
@@ -362,7 +378,7 @@ async function addHotelUser(user_id,hotel_ids) {
setRooms(roomsData);
setConversations(convData);
setUsers(usersData);
SetItems(itemsData);
//getHotelList();
}
@@ -370,7 +386,7 @@ async function addHotelUser(user_id,hotel_ids) {
load();
//console.log("USERS 2 =",usersById)
console.log("here are hte items" + items)
}, []);
useEffect(() => {
@@ -383,7 +399,8 @@ async function addHotelUser(user_id,hotel_ids) {
//if (!accessTokenSingle || !clientId) return;
const ws = new WebSocket(
`http://localhost:7080/auth/ws/${accessTokenSingle}`
`ws://localhost:7080/auth/ws/${accessTokenSingle}`
//`wss://79.137.75.155/hotel-demo/api/auth/ws/${accessTokenSingle}`
);
wsRef.current = ws;
@@ -423,6 +440,7 @@ async function addHotelUser(user_id,hotel_ids) {
users,
usersById,
clientId,
items,
fetchHotelUsers,
updateRoomStatus,

View File

@@ -0,0 +1,65 @@
import {createContext, useContext,
useState, useEffect,
useRef } from "react";
import { API_BASE } from "../../config";
const RoomContext = createContext(null);
export function UseRoom() {
return useContext(RoomContext);
}
export function RoomProvider({ accessToken, children, resclientid}) {
const [rooms, setRooms] = useState([]);
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);
}
//TODO: update room status function
useEffect(() => {
if (!accessToken) return;
async function load() {
const [roomsData] = await Promise.all([
fetchRooms()
]);
setRooms(roomsData);
}
load();
})
return(
<RoomContext.Provider
value={{
rooms,
updateRoomStatus
}}
></RoomContext.Provider>
);
}

View File

@@ -40,22 +40,104 @@ export default function Login({ onLogin }) {
}
}
//TODO: add login button for test1 and test2
async function LoginTest1() {
//e.preventDefault();
//setError("");
const device_id = "147ac10b-58cc-4372-a567-0e02b2c3d479";
let username = "test1"
let password = "test1"
try {
const res = await fetch(`${API_BASE}/auth/create_refresh`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": "react-app",
},
credentials: "include",
body: JSON.stringify({ username, password, device_id }),
});
if (!res.ok) {
setError("Invalid credentials or server error.");
return;
}
//const data = await res.json(); // server returns short-lived token
//localStorage.setItem("access_token", data.access_token);
if (onLogin) await onLogin(); // notify App to show MainApp
} catch (err) {
console.error(err);
setError("Network error.");
}
}
async function LoginTest2() {
//e.preventDefault();
//setError("");
const device_id = "147ac10b-58cc-4372-a567-0e02b2c3d479";
let username = "test2"
let password = "test2"
try {
const res = await fetch(`${API_BASE}/auth/create_refresh`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": "react-app",
},
credentials: "include",
body: JSON.stringify({ username, password, device_id }),
});
if (!res.ok) {
setError("Invalid credentials or server error.");
return;
}
//const data = await res.json(); // server returns short-lived token
//localStorage.setItem("access_token", data.access_token);
if (onLogin) await onLogin(); // notify App to show MainApp
} catch (err) {
console.error(err);
setError("Network error.");
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Login</button>
{error && <p style={{ color: "red" }}>{error}</p>}
</form>
<>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Login</button>
{error && <p style={{ color: "red" }}>{error}</p>}
</form>
<div>
<button onClick={LoginTest1} >login test 1</button>
<button onClick={LoginTest2} >login test 2</button>
</div>
</>
);
}

View File

@@ -1,46 +1,34 @@
.messagebox{
/* Room grid */
.roomGrid {
margin: 5px;
height: 90vh;
overflow-y: auto;
border-radius: 10px;
background-color: var(--card) ;
display: grid;
padding: 16px;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1rem;
}
/* Room card */
.card {
background-color: var(--panel) ;
padding: 1rem;
border-radius: 10px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
/* your messages */
.msg--mine {
align-self: flex-end;
background-color: #464b43;
text-align: right;
}
/* others */
.msg--theirs {
align-self: flex-start;
background-color: #473f3f;
}
.grid {
display: flex;
justify-items: space-around;
gap: 1px;
background-color: #777777;
}
.card {
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 1px;
background-color: #a3a3a3;
padding: 5px;
}
.card input {
width: 90%;
box-sizing: border-box; /* important */
max-width: 100%; /* prevents overflow */
.roomCard input {
width: 100%;
}
.actions {
display: flex;
justify-content: space-between;
gap: 0.5rem;
}

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from "react";
import { useHotel } from "../HotelContext";
import { useHotel } from "../context/HotelContext";
export default function AdminWidget() {

View File

@@ -1,16 +1,89 @@
.chatWidget{
display: flex;
flex-direction: column;
}
.convcard {
background-color: brown;
.chatWidget {
margin :5px;
display: flex;
justify-content: space-between;
flex-direction: column;
background: var(--card);
border-radius: 10px;
overflow: hidden;
}
.convlist {
display: flex;
flex-direction: row;
align-items: right;
background-color: darkcyan;
display: flex;
flex-direction: row;
gap: 0.5rem;
padding: 0.75rem;
overflow-y: auto;
height: 20%;
}
.convcard {
padding: 0.5rem 0.75rem;
background: var(--panel);
border-radius: 6px;
cursor: pointer;
}
.convcard:hover {
background: var(--border);
}
.senderBox{
display: flex;
height: 10%;
}
.senderBox * {
border: 0px;
padding: 0px;
}
.textbox{
width: 70%;
}
.sendbutton{
width: 30%;
}
.messageCard {
height: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
background: var(--messageCard);
overflow-y: auto;
}
/* Messages */
.msg {
max-width: 70%;
padding: 0.5rem 0.75rem;
border-radius: 10px;
font-size: 0.9rem;
}
.msg--mine {
align-self: flex-end;
background: var(--accent);
color: white;
}
.msg--theirs {
align-self: flex-start;
background: var(--panel);
}
.createConvBox {
display: flex;
justify-content: space-around;
}
.createConvBox > * {
}

View File

@@ -1,4 +1,5 @@
import { useHotel } from "../HotelContext";
import { useHotel } from "../context/HotelContext";
import { useState, useEffect } from "react";
import { useRef } from "react";
@@ -110,17 +111,63 @@ function SenderBox({ conv_id, onSend, disabled }) {
};
return (
<div hidden={disabled} className="senderbox">
<div hidden={disabled} className="senderBox">
<input
className="textbox"
value={text}
onChange={e => setText(e.target.value)}
onKeyDown={e => e.key === "Enter" && handleSend() }
placeholder="Type a message…"
/>
<button onClick={handleSend}>Send</button>
<button className="sendbutton" onClick={handleSend}>Send</button>
</div>
);
}
function ConvCard({id, title, onOpenConv}) {
return(
<div className="convcard" onClick={() => onOpenConv(id)} >
<h3>ConvId : {id} </h3>
<h3>Name : {title}</h3>
</div>
)
}
function MessagesBox({ conv_id }) {
const { messagesByConvId, usersById, clientId } = useHotel();
const messages = messagesByConvId[conv_id] ?? [];
console.log(messagesByConvId);
console.log(usersById);
if (messages.length === 0) {
return <div className="messagesbox empty">No messages</div>;
}
return (
<div className="messageCard">
{messages
.slice()
.sort((a, b) => new Date(a.sent_at) - new Date(b.sent_at))
.map(msg => {
const isMine = msg.sender_id === clientId;
return (
<div key={msg.sent_at}
className={`msg ${isMine ? "msg--mine" : "msg--theirs"}`}>
<p>
<strong>Sender:</strong>{" "}
{usersById[msg.sender_id] ?? "Unknown user"}
</p>
<p>{msg.content}</p>
</div>
);
})}
</div>
);
}
function AddUsersMenu({ convId, usersById, fetchConvUsers, onValidate, onClose }) {
const [selected, setSelected] = useState(new Set());
@@ -197,53 +244,10 @@ function CreateConvMenu({onSend}) {
<input
value= {nameText}
onChange= {element => setNameText(element.target.value)}
onKeyDown={e => e.key === "Enter" && handleSend() }
placeholder="Ma Conversation"
/>
<button onClick= {handleSend}>Send</button>
</div>
)
}
function ConvCard({id, title, onOpenConv}) {
return(
<div className="convcard">
<h3>ConvId : {id} </h3>
<h3>Name : {title}</h3>
<p onClick={() => onOpenConv(id)}> GET MESSAAGE</p>
</div>
)
}
function MessagesBox({ conv_id }) {
const { messagesByConvId, usersById, clientId } = useHotel();
const messages = messagesByConvId[conv_id] ?? [];
console.log(messagesByConvId);
console.log(usersById);
if (messages.length === 0) {
return <div className="messagesbox empty">No messages</div>;
}
return (
<div className="messagebox">
{messages
.slice()
.sort((a, b) => new Date(a.sent_at) - new Date(b.sent_at))
.map(msg => {
const isMine = msg.sender_id === clientId;
return (
<div key={msg.sent_at}
className={`msg ${isMine ? "msg--mine" : "msg--theirs"}`}>
<p>
<strong>Sender:</strong>{" "}
{usersById[msg.sender_id] ?? "Unknown user"}
</p>
<p>{msg.content}</p>
</div>
);
})}
</div>
);
}

View File

@@ -1,2 +1,23 @@
.grid {
margin: 5px ;
align-items: start;
grid-auto-rows: max-content;
height: 90vh;
overflow-y: auto;
border-radius: 10px;
background-color: var(--card) ;
padding: 16px;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
display: grid;
}
.itemcard {
background-color: var(--panel) ;
padding: 1rem;
border-radius: 10px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}

View File

@@ -1,54 +1,46 @@
import { useEffect, useState } from "react";
import {HotelProvider, useHotel} from "../HotelContext";
import {HotelProvider, useHotel} from "../context/HotelContext";
import "./inventoryWidget.css";
export default function InventoryWidget({}) {
const {
fetchHotelInventory
} = useHotel();
//const { fetchHotelInventory } = useHotel();
//const [items, setItems] = useState([]);
const [items, setItems] = useState([]);
/*
useEffect(() => {
async function loadInventory() {
const data = await fetchHotelInventory()
setItems(data);
}
loadInventory()
console.log ("loaded inventory")
}, [])
*/
const { items } = useHotel();
return (
<div>
<div className="grid">
{items.map(item => (
<ItemCard
key={item.id}
id={item.id}
name={item.name}
amount={item.amount}
/>
))}
</div>
<div>
<CreateItemMenu/>
</div>
</div>
<div className="grid">
{items.map(item => (
<ItemCard
key={item.id}
id={item.id}
name={item.name}
amount={item.amount}
/>
))}
<CreateItemMenu/>
</div>
</div>
)
}
function ItemCard({id, name, amount}) {
const [itemAmount, setItemAmount] = useState(amount);
const [editing, setEditing] = useState(false);
const {updateItemAmount} = useHotel();
function submit(item_id, item_amount) {
@@ -59,10 +51,12 @@ function ItemCard({id, name, amount}) {
return (
<div className="itemcard">
<div>{name}</div>
<h3>{name}</h3>
{!editing ?(
<p onClick={() => setEditing(true)} /*style={ cursor: "pointer" }*/>Amount :{amount}</p>
<h5 onClick={() => setEditing(true)} >
Amount :{amount}
</h5>
):(
<div>
<input
@@ -74,10 +68,8 @@ function ItemCard({id, name, amount}) {
/>
</div>
)}
</div>
)
}
//TODO: same pb as convlist, update when typing in the create menu
@@ -86,25 +78,23 @@ function CreateItemMenu() {
const [itemAmount, setItemAmount] = useState(0);
const {createItem} = useHotel();
const handleSubmit = () => {
if (!itemName.trim() | !itemAmount.trim() ) return;
createItem(itemName, itemAmount);
}
return(
<div>
<div className="itemcard" >
<input
type="text"
onChange={element => setItemName(element.target.value)}
placeholder="Nom de l'objet"
type="text"
onChange={element => setItemName(element.target.value)}
placeholder="Nom de l'objet"
/>
<input
type="text"
onChange={element => setItemAmount(element.target.value)}
placeholder="Montant d'objet(s)"
type="text"
onChange={element => setItemAmount(element.target.value)}
placeholder="Montant d'objet(s)"
/>
<button onClick={handleSubmit}>Creer</button>
</div>

View File

@@ -1,12 +1,17 @@
import "./RoomWidget.css";
import { useHotel } from "../HotelContext";
import { useHotel } from "../context/HotelContext";
import { useState } from "react";
export default function RoomWidget({ roomlist }) {
export default function RoomWidget({}) {
const { rooms } = useHotel();
return (
<div className="grid">
{roomlist.map(room => (
<div className="roomGrid">
{rooms.map(room => (
<RoomCard
key={room.id}
id = {room.id}
@@ -19,8 +24,6 @@ export default function RoomWidget({ roomlist }) {
}
function RoomCard({ number, status ,id}) {
//FIXME: this shouldn't use hotel context, instead : set the state once on load
const { updateRoomStatus } = useHotel();
const [editing, setEdtiting] = useState(false);

View File

@@ -1,2 +1,5 @@
//export const API_BASE = "http://79.137.75.155:8080";
export const API_BASE = "http://localhost:7080";
//export const API_BASE = "https://79.137.75.155/hotel-demo/api/";
export const API_BASE = "http://localhost:7080";
//export const API_BASE = "https://79.137.75.155:5090";

View File

@@ -1,68 +1,45 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
--bg: #0f172a;
--panel: #111827;
--card: #1f2933;
--messageCard: #2f3f4e;
--border: #2d3748;
--text: #e5e7eb;
--muted: #9ca3af;
--accent: #6366f1;
--danger: #ef4444;
--success: #22c55e;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
padding: 0px;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
background-color: var(--border);
color: var(--text);
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
background: var(--accent);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
button:hover {
opacity: 0.9;
}
input {
width: 80%;
background: var(--panel);
border: 1px solid var(--border);
color: var(--text);
padding: 0.5rem;
border-radius: 6px;
}

View File

@@ -3,5 +3,7 @@ import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
base: './',
plugins: [react()],
})