Venus / src /ui /rooms_ui.py
MetiMiester's picture
Update src/ui/rooms_ui.py
5c0f8dc verified
import gradio as gr
from src.db.rooms import (
list_rooms,
get_room_detail,
change_room_status,
)
from src.services.pricing_service import calculate_room_rate
from src.services.auth_service import is_admin
STATUS_COLORS = {
"free": "#ffffff",
"occupied": "#8e44ad",
"need_cleaning": "#e74c3c",
"out_of_service": "#95a5a6",
}
STATUS_TEXT_COLORS = {
"free": "#000000",
"occupied": "#ffffff",
"need_cleaning": "#ffffff",
"out_of_service": "#000000",
}
def render_rooms_tab(
db_path: str,
auth_state: gr.State,
selected_room_state: gr.State,
):
"""
Rooms tab:
- Excel-like grid by floor
- Click room to view details
- Status change with mandatory note
- History display
"""
gr.Markdown("## 🧱 Rooms")
with gr.Row():
grid_col = gr.Column(scale=3)
detail_col = gr.Column(scale=2)
# ==========================================================
# ROOM GRID
# ==========================================================
with grid_col:
rooms_md = gr.Markdown()
def build_grid():
rooms = list_rooms(db_path)
floors = {}
for r in rooms:
floor = str(r["room_no"])[0]
floors.setdefault(floor, []).append(r)
html = ""
for floor in sorted(floors.keys()):
html += f"<h4>Floor {floor}</h4>"
html += "<div style='display:grid;grid-template-columns:repeat(10,1fr);gap:6px;'>"
for r in floors[floor]:
bg = STATUS_COLORS[r["status"]]
fg = STATUS_TEXT_COLORS[r["status"]]
warn = "⚠️" if r["has_problem"] else ""
html += f"""
<div
style="
background:{bg};
color:{fg};
padding:10px;
border-radius:6px;
text-align:center;
font-weight:600;
cursor:pointer;
border:1px solid #ccc;
"
onclick="window.gradioApp().dispatchEvent(
new CustomEvent('select-room', {{ detail: '{r["room_no"]}' }})
)"
>
{r["room_no"]} {warn}<br/>
<small>{r["status"]}</small>
</div>
"""
html += "</div><br/>"
return html
rooms_md.value = build_grid()
# ==========================================================
# ROOM DETAIL PANEL
# ==========================================================
with detail_col:
gr.Markdown("### Room Details")
room_info = gr.Markdown("Select a room from the grid")
rate_md = gr.Markdown()
status_dd = gr.Dropdown(
choices=["free", "occupied", "need_cleaning", "out_of_service"],
label="Change status",
)
note_tb = gr.Textbox(label="Mandatory note", lines=3)
apply_btn = gr.Button("Apply Status Change", variant="primary")
history_md = gr.Markdown()
def load_room(room_no):
if not room_no:
return (
"Select a room from the grid",
"",
None,
"",
)
room, problems, history = get_room_detail(int(room_no), db_path)
rate = calculate_room_rate(
room_type=room["room_type"],
has_problem=len(problems) > 0,
)
info = f"""
**Room:** {room["room_no"]}
**Type:** {room["room_type"]}
**Status:** {room["status"]}
**Sellable:** {"Yes" if room["sellable"] else "No"}
"""
if problems:
info += "\n\n**Active problems:**\n"
for p in problems:
info += f"- {p['problem_type']}\n"
hist = "### Recent History\n"
if history:
for h in history:
hist += f"""
**{h['changed_at_utc']}**
{h['from_status']}{h['to_status']}
_{h['note']}_
by **{h['changed_by']}**
---
"""
else:
hist += "_No history_"
return (
info,
f"**Current Rate:** {rate if rate is not None else '—'} RO",
room["status"],
hist,
)
# Custom JS event listener (safe, no duplicate blocks)
gr.HTML(
"""
<script>
document.addEventListener("select-room", (e) => {
const room = e.detail;
window.gradioApp().dispatchEvent(
new CustomEvent("gradio-select-room", { detail: room })
);
});
</script>
"""
)
select_evt = gr.HTML(visible=False)
select_evt.change(
load_room,
inputs=[selected_room_state],
outputs=[room_info, rate_md, status_dd, history_md],
)
def apply_change(auth, new_status, note):
if not auth:
return gr.Warning("Login required")
if not note or not note.strip():
return gr.Warning("Note is required")
if not selected_room_state.value:
return gr.Warning("No room selected")
change_room_status(
room_no=int(selected_room_state.value),
new_status=new_status,
note=note.strip(),
changed_by=auth["username"],
db_path=db_path,
)
rooms_md.value = build_grid()
return gr.Markdown("✅ Status updated")
apply_btn.click(
apply_change,
inputs=[auth_state, status_dd, note_tb],
outputs=[history_md],
)