774 lines
20 KiB
Go
774 lines
20 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"reservation-system/backend/internal/auth"
|
|
"reservation-system/backend/internal/db"
|
|
"reservation-system/backend/internal/models"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type Handler struct {
|
|
db *db.DB
|
|
}
|
|
|
|
func NewHandler(database *db.DB) *Handler {
|
|
return &Handler{db: database}
|
|
}
|
|
|
|
func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
|
// Auth
|
|
mux.HandleFunc("/api/auth/login", h.handleLogin)
|
|
mux.HandleFunc("/api/auth/refresh", h.handleRefresh)
|
|
|
|
// Rooms (protected)
|
|
mux.HandleFunc("/api/rooms", h.authMiddleware(h.handleRooms))
|
|
mux.HandleFunc("/api/rooms/", h.authMiddleware(h.handleRoomDetail))
|
|
|
|
// Tables (protected)
|
|
mux.HandleFunc("/api/tables", h.authMiddleware(h.handleTables))
|
|
mux.HandleFunc("/api/tables/", h.authMiddleware(h.handleTableDetail))
|
|
mux.HandleFunc("/api/tables/merge", h.authMiddleware(h.handleMergeTables))
|
|
|
|
// NEW: Room tables endpoint
|
|
mux.HandleFunc("/api/rooms/", h.authMiddleware(h.handleRoomTables))
|
|
|
|
// Reservations (protected)
|
|
mux.HandleFunc("/api/reservations", h.authMiddleware(h.handleReservations))
|
|
mux.HandleFunc("/api/reservations/", h.authMiddleware(h.handleReservationDetail))
|
|
mux.HandleFunc("/api/reservations/date/", h.authMiddleware(h.handleReservationsByDate))
|
|
|
|
// NEW: Room Bookings
|
|
mux.HandleFunc("/api/room-bookings", h.authMiddleware(h.handleRoomBookings))
|
|
mux.HandleFunc("/api/room-bookings/", h.authMiddleware(h.handleRoomBookingDetail))
|
|
|
|
// Availability
|
|
mux.HandleFunc("/api/availability", h.authMiddleware(h.handleAvailability))
|
|
mux.HandleFunc("/api/availability/room", h.authMiddleware(h.handleRoomAvailability))
|
|
|
|
// Email config
|
|
mux.HandleFunc("/api/email-config", h.authMiddleware(h.handleEmailConfig))
|
|
|
|
// Dashboard stats
|
|
mux.HandleFunc("/api/dashboard", h.authMiddleware(h.handleDashboard))
|
|
}
|
|
|
|
func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
user, err := h.db.GetUserByUsername(req.Username)
|
|
if err != nil {
|
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if user == nil || bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)) != nil {
|
|
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
token, err := auth.GenerateToken(user.Username, user.IsAdmin)
|
|
if err != nil {
|
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"token": token,
|
|
"user": user.Username,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) handleRefresh(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
http.Error(w, "Missing token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
|
claims, err := auth.ValidateToken(tokenString)
|
|
if err != nil {
|
|
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
newToken, err := auth.GenerateToken(claims.Username, claims.IsAdmin)
|
|
if err != nil {
|
|
http.Error(w, "Server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"token": newToken,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) authMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
http.Error(w, "Missing token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
|
if _, err := auth.ValidateToken(tokenString); err != nil {
|
|
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
next(w, r)
|
|
}
|
|
}
|
|
|
|
// Rooms Handler
|
|
func (h *Handler) handleRooms(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
rooms, err := h.db.GetRooms()
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(rooms)
|
|
|
|
case http.MethodPost:
|
|
var room models.Room
|
|
if err := json.NewDecoder(r.Body).Decode(&room); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if room.Name == "" {
|
|
http.Error(w, "Name required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := h.db.CreateRoom(&room); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(room)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleRoomDetail(w http.ResponseWriter, r *http.Request) {
|
|
// Check if it's /api/rooms/:id/tables
|
|
path := r.URL.Path
|
|
if strings.HasSuffix(path, "/tables") {
|
|
h.handleRoomTables(w, r)
|
|
return
|
|
}
|
|
|
|
idStr := strings.TrimPrefix(path, "/api/rooms/")
|
|
id, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
http.Error(w, "Invalid ID", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
room, err := h.db.GetRoom(id)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if room == nil {
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(room)
|
|
|
|
case http.MethodPut:
|
|
var room models.Room
|
|
if err := json.NewDecoder(r.Body).Decode(&room); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
room.ID = id
|
|
if err := h.db.UpdateRoom(&room); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(room)
|
|
|
|
case http.MethodDelete:
|
|
if err := h.db.DeleteRoom(id); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
// NEW: Room Tables Handler - GET /api/rooms/:id/tables
|
|
func (h *Handler) handleRoomTables(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
path := r.URL.Path
|
|
idStr := strings.TrimSuffix(strings.TrimPrefix(path, "/api/rooms/"), "/tables")
|
|
roomID, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
http.Error(w, "Invalid room ID", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if r.Method == http.MethodGet {
|
|
tables, err := h.db.GetTablesByRoom(roomID)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(tables)
|
|
} else {
|
|
// POST - create table for room
|
|
var table models.Table
|
|
if err := json.NewDecoder(r.Body).Decode(&table); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
table.RoomID = roomID
|
|
if table.MaxGuests == 0 {
|
|
http.Error(w, "Max guests required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := h.db.CreateTable(&table); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(table)
|
|
}
|
|
}
|
|
|
|
// Tables Handler
|
|
func (h *Handler) handleTables(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
roomID := r.URL.Query().Get("room_id")
|
|
var tables []models.Table
|
|
var err error
|
|
|
|
if roomID != "" {
|
|
rid, _ := strconv.Atoi(roomID)
|
|
tables, err = h.db.GetTablesByRoom(rid)
|
|
} else {
|
|
tables, err = h.db.GetAllTables()
|
|
}
|
|
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(tables)
|
|
|
|
case http.MethodPost:
|
|
var table models.Table
|
|
if err := json.NewDecoder(r.Body).Decode(&table); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if table.MaxGuests == 0 {
|
|
http.Error(w, "Max guests required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := h.db.CreateTable(&table); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(table)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleTableDetail(w http.ResponseWriter, r *http.Request) {
|
|
idStr := strings.TrimPrefix(r.URL.Path, "/api/tables/")
|
|
id, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
http.Error(w, "Invalid ID", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
table, err := h.db.GetTable(id)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if table == nil {
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(table)
|
|
|
|
case http.MethodPut:
|
|
var table models.Table
|
|
if err := json.NewDecoder(r.Body).Decode(&table); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
table.ID = id
|
|
if err := h.db.UpdateTable(&table); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(table)
|
|
|
|
case http.MethodDelete:
|
|
if err := h.db.DeleteTable(id); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
// Reservations Handler
|
|
func (h *Handler) handleReservations(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
date := r.URL.Query().Get("date")
|
|
if date == "" {
|
|
date = time.Now().Format("2006-01-02")
|
|
}
|
|
reservations, err := h.db.GetReservationsByDate(date)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(reservations)
|
|
|
|
case http.MethodPost:
|
|
var res models.Reservation
|
|
if err := json.NewDecoder(r.Body).Decode(&res); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Check availability
|
|
available, err := h.db.CheckAvailability(res.TableID, res.Date, res.TimeFrom, res.TimeTo)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !available {
|
|
http.Error(w, "Table not available at this time", http.StatusConflict)
|
|
return
|
|
}
|
|
|
|
// Check table capacity
|
|
table, err := h.db.GetTable(res.TableID)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if table != nil && res.Guests > table.MaxGuests {
|
|
http.Error(w, "Guest count exceeds table capacity", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if res.Source == "" {
|
|
res.Source = "manual"
|
|
}
|
|
|
|
if err := h.db.CreateReservation(&res); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleReservationDetail(w http.ResponseWriter, r *http.Request) {
|
|
idStr := strings.TrimPrefix(r.URL.Path, "/api/reservations/")
|
|
id, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
http.Error(w, "Invalid ID", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
res, err := h.db.GetReservation(id)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if res == nil {
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
case http.MethodPut:
|
|
var res models.Reservation
|
|
if err := json.NewDecoder(r.Body).Decode(&res); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
res.ID = id
|
|
if err := h.db.UpdateReservation(&res); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
case http.MethodDelete:
|
|
if err := h.db.DeleteReservation(id); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleReservationsByDate(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
date := strings.TrimPrefix(r.URL.Path, "/api/reservations/date/")
|
|
if date == "" {
|
|
http.Error(w, "Date required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
reservations, err := h.db.GetReservationsByDate(date)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(reservations)
|
|
}
|
|
|
|
// NEW: Room Bookings Handlers
|
|
func (h *Handler) handleRoomBookings(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
roomID := r.URL.Query().Get("room_id")
|
|
date := r.URL.Query().Get("date")
|
|
var bookings []models.RoomBooking
|
|
var err error
|
|
|
|
if roomID != "" {
|
|
rid, _ := strconv.Atoi(roomID)
|
|
bookings, err = h.db.GetRoomBookingsByRoom(rid)
|
|
} else if date != "" {
|
|
bookings, err = h.db.GetRoomBookingsByDate(date)
|
|
} else {
|
|
// Get all bookings for today
|
|
bookings, err = h.db.GetRoomBookingsByDate(time.Now().Format("2006-01-02"))
|
|
}
|
|
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(bookings)
|
|
|
|
case http.MethodPost:
|
|
var rb models.RoomBooking
|
|
if err := json.NewDecoder(r.Body).Decode(&rb); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Check room availability
|
|
available, err := h.db.CheckRoomAvailability(rb.RoomID, rb.Date, rb.TimeFrom, rb.TimeTo)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !available {
|
|
http.Error(w, "Room not available at this time", http.StatusConflict)
|
|
return
|
|
}
|
|
|
|
// Check room capacity
|
|
room, err := h.db.GetRoom(rb.RoomID)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if room != nil && rb.Guests > room.Capacity {
|
|
http.Error(w, "Guest count exceeds room capacity", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if rb.Status == "" {
|
|
rb.Status = "confirmed"
|
|
}
|
|
|
|
if err := h.db.CreateRoomBooking(&rb); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(rb)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleRoomBookingDetail(w http.ResponseWriter, r *http.Request) {
|
|
idStr := strings.TrimPrefix(r.URL.Path, "/api/room-bookings/")
|
|
id, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
http.Error(w, "Invalid ID", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
rb, err := h.db.GetRoomBooking(id)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if rb == nil {
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(rb)
|
|
|
|
case http.MethodPut:
|
|
var rb models.RoomBooking
|
|
if err := json.NewDecoder(r.Body).Decode(&rb); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
rb.ID = id
|
|
if err := h.db.UpdateRoomBooking(&rb); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(rb)
|
|
|
|
case http.MethodDelete:
|
|
if err := h.db.DeleteRoomBooking(id); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
// Availability Handler
|
|
func (h *Handler) handleAvailability(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
date := r.URL.Query().Get("date")
|
|
if date == "" {
|
|
date = time.Now().Format("2006-01-02")
|
|
}
|
|
|
|
tables, err := h.db.GetAllTables()
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var availability []map[string]interface{}
|
|
for _, table := range tables {
|
|
reservations, _ := h.db.GetReservationsByTable(table.ID, date)
|
|
availability = append(availability, map[string]interface{}{
|
|
"table_id": table.ID,
|
|
"table_name": table.RoomName + " - Tisch " + strconv.Itoa(table.ID),
|
|
"max_guests": table.MaxGuests,
|
|
"reservations": reservations,
|
|
})
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(availability)
|
|
}
|
|
|
|
// NEW: Room Availability Handler
|
|
func (h *Handler) handleRoomAvailability(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
date := r.URL.Query().Get("date")
|
|
if date == "" {
|
|
date = time.Now().Format("2006-01-02")
|
|
}
|
|
|
|
rooms, err := h.db.GetRooms()
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var availability []map[string]interface{}
|
|
for _, room := range rooms {
|
|
bookings, _ := h.db.GetRoomBookingsByRoom(room.ID)
|
|
availability = append(availability, map[string]interface{}{
|
|
"room_id": room.ID,
|
|
"room_name": room.Name,
|
|
"capacity": room.Capacity,
|
|
"bookings": bookings,
|
|
})
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(availability)
|
|
}
|
|
|
|
func (h *Handler) handleMergeTables(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
ParentTableID int `json:"parent_table_id"`
|
|
ChildTableID int `json:"child_table_id"`
|
|
MergedName string `json:"merged_name"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
parentTable, err := h.db.GetTable(req.ParentTableID)
|
|
if err != nil || parentTable == nil {
|
|
http.Error(w, "Parent table not found", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
childTable, err := h.db.GetTable(req.ChildTableID)
|
|
if err != nil || childTable == nil {
|
|
http.Error(w, "Child table not found", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if parentTable.RoomID != childTable.RoomID {
|
|
http.Error(w, "Tables must be in the same room", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
merge := models.TableMerge{
|
|
ParentTableID: req.ParentTableID,
|
|
ChildTableID: req.ChildTableID,
|
|
MergedName: req.MergedName,
|
|
Active: true,
|
|
}
|
|
|
|
if err := h.db.CreateTableMerge(&merge); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(merge)
|
|
}
|
|
|
|
func (h *Handler) handleEmailConfig(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
cfg, err := h.db.GetEmailConfig()
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if cfg != nil {
|
|
cfg.Password = "***"
|
|
}
|
|
json.NewEncoder(w).Encode(cfg)
|
|
|
|
case http.MethodPost, http.MethodPut:
|
|
var cfg models.EmailConfig
|
|
if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
http.Error(w, "Invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := h.db.SaveEmailConfig(&cfg); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(cfg)
|
|
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleDashboard(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
today := time.Now().Format("2006-01-02")
|
|
totalGuests, _ := h.db.GetTotalGuestsForDate(today)
|
|
reservations, _ := h.db.GetReservationsByDate(today)
|
|
roomBookings, _ := h.db.GetRoomBookingsByDate(today)
|
|
|
|
dashboard := map[string]interface{}{
|
|
"today": today,
|
|
"total_guests": totalGuests,
|
|
"reservation_count": len(reservations),
|
|
"room_booking_count": len(roomBookings),
|
|
"reservations": reservations,
|
|
"room_bookings": roomBookings,
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(dashboard)
|
|
}
|