diff --git a/backend/hotel_hacienda/src/app.js b/backend/hotel_hacienda/src/app.js
index 84f0350..b14f357 100644
--- a/backend/hotel_hacienda/src/app.js
+++ b/backend/hotel_hacienda/src/app.js
@@ -37,6 +37,7 @@ const incomehrxRoutes = require('./routes/incomehrx.routes');
const roomsRoutes = require('./routes/rooms.routes');
const dashboardRoutes = require('./routes/dashboard.routes');
const reservationsRoutes = require('./routes/reservations.routes');
+const guestsRoutes = require('./routes/guests.routes');
//Prefijo - Auth routes are public (no middleware)
app.use('/api/auth', authRoutes);
@@ -60,5 +61,6 @@ app.use('/api/restaurantpl', authMiddleware, restaurantRoutes);
app.use('/api/rooms', authMiddleware, roomsRoutes);
app.use('/api/dashboard', authMiddleware, dashboardRoutes);
app.use('/api/reservations', authMiddleware, reservationsRoutes);
+app.use('/api/guests', authMiddleware, guestsRoutes);
module.exports = app;
diff --git a/backend/hotel_hacienda/src/controllers/guests.controller.js b/backend/hotel_hacienda/src/controllers/guests.controller.js
new file mode 100644
index 0000000..61b6ca7
--- /dev/null
+++ b/backend/hotel_hacienda/src/controllers/guests.controller.js
@@ -0,0 +1,108 @@
+const pool = require('../db/connection');
+
+const getGuests = async (req, res) => {
+ try {
+ const { search, limit = 50, offset = 0 } = req.query;
+ let query = `
+ SELECT g.*,
+ r.room_id, rm.name_room,
+ r.check_in as current_check_in
+ FROM guests g
+ LEFT JOIN reservations r ON r.guest_id = g.id AND r.status = 'checked_in'
+ LEFT JOIN rooms rm ON rm.id_room = r.room_id
+ `;
+ const params = [];
+ let paramIndex = 1;
+
+ if (search) {
+ query += ` WHERE g.first_name ILIKE $${paramIndex} OR g.last_name ILIKE $${paramIndex} OR g.email ILIKE $${paramIndex} OR g.phone ILIKE $${paramIndex}`;
+ params.push(`%${search}%`);
+ paramIndex++;
+ }
+ query += ` ORDER BY g.created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
+ params.push(parseInt(limit), parseInt(offset));
+
+ const result = await pool.query(query, params);
+ const countQuery = search
+ ? await pool.query('SELECT COUNT(*) FROM guests WHERE first_name ILIKE $1 OR last_name ILIKE $1 OR email ILIKE $1 OR phone ILIKE $1', [`%${search}%`])
+ : await pool.query('SELECT COUNT(*) FROM guests');
+
+ res.json({ guests: result.rows, total: parseInt(countQuery.rows[0].count) });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: 'Error al obtener huespedes' });
+ }
+};
+
+const getGuestById = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const guest = await pool.query(`
+ SELECT g.*, r.room_id, rm.name_room, r.check_in as current_check_in, r.check_out as current_check_out
+ FROM guests g
+ LEFT JOIN reservations r ON r.guest_id = g.id AND r.status = 'checked_in'
+ LEFT JOIN rooms rm ON rm.id_room = r.room_id
+ WHERE g.id = $1
+ `, [id]);
+
+ if (guest.rows.length === 0) return res.status(404).json({ message: 'Huesped no encontrado' });
+ res.json({ guest: guest.rows[0] });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: 'Error al obtener huesped' });
+ }
+};
+
+const createGuest = async (req, res) => {
+ try {
+ const { first_name, last_name, email, phone, id_type, id_number, nationality, address, notes } = req.body;
+ const result = await pool.query(
+ `INSERT INTO guests (first_name, last_name, email, phone, id_type, id_number, nationality, address, notes)
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *`,
+ [first_name, last_name, email, phone, id_type, id_number, nationality, address, notes]
+ );
+ res.status(201).json({ guest: result.rows[0], message: 'Huesped creado correctamente' });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: 'Error al crear huesped' });
+ }
+};
+
+const updateGuest = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const { first_name, last_name, email, phone, id_type, id_number, nationality, address, notes } = req.body;
+ const result = await pool.query(
+ `UPDATE guests SET first_name = COALESCE($1, first_name), last_name = COALESCE($2, last_name),
+ email = COALESCE($3, email), phone = COALESCE($4, phone), id_type = COALESCE($5, id_type),
+ id_number = COALESCE($6, id_number), nationality = COALESCE($7, nationality),
+ address = COALESCE($8, address), notes = COALESCE($9, notes), updated_at = NOW()
+ WHERE id = $10 RETURNING *`,
+ [first_name, last_name, email, phone, id_type, id_number, nationality, address, notes, id]
+ );
+ if (result.rows.length === 0) return res.status(404).json({ message: 'Huesped no encontrado' });
+ res.json({ guest: result.rows[0] });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: 'Error al actualizar huesped' });
+ }
+};
+
+const getGuestStays = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const result = await pool.query(`
+ SELECT gs.*, rm.name_room, rm.bed_type
+ FROM guest_stays gs
+ LEFT JOIN rooms rm ON rm.id_room = gs.room_id
+ WHERE gs.guest_id = $1
+ ORDER BY gs.check_in DESC
+ `, [id]);
+ res.json({ stays: result.rows });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ message: 'Error al obtener estadias' });
+ }
+};
+
+module.exports = { getGuests, getGuestById, createGuest, updateGuest, getGuestStays };
diff --git a/backend/hotel_hacienda/src/routes/guests.routes.js b/backend/hotel_hacienda/src/routes/guests.routes.js
new file mode 100644
index 0000000..be3768f
--- /dev/null
+++ b/backend/hotel_hacienda/src/routes/guests.routes.js
@@ -0,0 +1,11 @@
+const express = require('express');
+const router = express.Router();
+const ctrl = require('../controllers/guests.controller');
+
+router.get('/', ctrl.getGuests);
+router.get('/:id', ctrl.getGuestById);
+router.get('/:id/stays', ctrl.getGuestStays);
+router.post('/', ctrl.createGuest);
+router.put('/:id', ctrl.updateGuest);
+
+module.exports = router;
diff --git a/frontend/Frontend-Hotel/src/App.jsx b/frontend/Frontend-Hotel/src/App.jsx
index f75cabb..68dfb3d 100644
--- a/frontend/Frontend-Hotel/src/App.jsx
+++ b/frontend/Frontend-Hotel/src/App.jsx
@@ -42,6 +42,8 @@ import HousekeeperOutcomes from "./pages/Inventory/HousekeeperOutcomes.jsx";
import RoomDashboard from "./pages/RoomDashboard/RoomDashboard.jsx";
import Reservations from "./pages/Reservations/Reservations.jsx";
import NewReservation from "./pages/Reservations/NewReservation.jsx";
+import Guests from "./pages/Guests/Guests.jsx";
+import GuestDetail from "./pages/Guests/GuestDetail.jsx";
import "./styles/global.css";
//SubmenĂș de Hotel
@@ -137,6 +139,8 @@ export default function App() {
{t("common.loading")}
+{t("common.noResults")}
+{guest.email}
+{guest.phone}
+{guest.id_type}
+{guest.id_number}
+{guest.address}
+{guest.notes}
+{t("common.noResults")}
+| {t("common.room")} | +{t("reservations.checkIn")} | +{t("reservations.checkOut")} | +{t("common.total")} | +Rating | +
|---|---|---|---|---|
| + {stay.name_room || "-"} + {stay.bed_type && ( + + {stay.bed_type} + + )} + | +{formatDate(stay.check_in)} | +{formatDate(stay.check_out)} | ++ ${Number(stay.total_charged || 0).toLocaleString()} + | +{renderStars(stay.rating)} | +
{t("common.loading")}
+{t("common.noResults")}
++ {guest.email} +
+ )} + {guest.phone && ( ++ {guest.phone} +
+ )} +