Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions handlers/reservation/repository/reservation_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,21 @@ func (r *ReservationRepository) GetUserReservations(userEmail string, filter str
return response, rows.Err()
}

func (r *ReservationRepository) GetItemName(itemID int) (string, error) {
query := "SELECT name FROM reservation_element WHERE id_reservation_element = $1"
row := r.DB.QueryRow(query, itemID)
var name string
if err := row.Scan(&name); err != nil {
if errors.Is(err, sql.ErrNoRows) {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Item with ID %d does not exist", itemID))
return "", fmt.Errorf("item with ID %d not found", itemID)
}
utils.LogMessage(utils.LevelError, fmt.Sprintf("Failed to get item name: %v", err))
return "", err
}
return name, nil
}

func (r *ReservationRepository) SearchItemsAndCategories(search string) ([]models.ReservationCategory, []models.ReservationItem, error) {
like := fmt.Sprintf("%%%s%%", strings.ToLower(search))

Expand Down
74 changes: 73 additions & 1 deletion handlers/reservation/reservation_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@
"github.com/gofiber/fiber/v2"
"github.com/plugimt/transat-backend/handlers/reservation/repository"
"github.com/plugimt/transat-backend/models"
"github.com/plugimt/transat-backend/services"
"github.com/plugimt/transat-backend/utils"
)

type ReservationHandler struct {
ReservationRepository *repository.ReservationRepository
DiscordService *services.DiscordService
}

func NewReservationHandler(db *sql.DB) *ReservationHandler {
func NewReservationHandler(db *sql.DB, discordService *services.DiscordService) *ReservationHandler {
return &ReservationHandler{
ReservationRepository: repository.NewReservationRepository(db),
DiscordService: discordService,
}
}

Expand Down Expand Up @@ -440,6 +443,24 @@
"error": "Failed to create reservation",
})
}

if h.DiscordService != nil && res.User != nil {
itemName, err := h.ReservationRepository.GetItemName(itemID)
if err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to get item name for Discord notification: %v", err))
itemName = "Unknown Item"
}

startDate := reservationItemTime.StartDate.Format("2006-01-02 15:04:05")
var endDate string
if reservationItemTime.EndDate != nil {
endDate = reservationItemTime.EndDate.Format("2006-01-02 15:04:05")
}

if err := h.DiscordService.SendReservationCreated(itemName, startDate, endDate, ItemPerSlot, *res.User); err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to send Discord notification for reservation creation: %v", err))
}
}
} else if reservationRequest.EndDate != nil {
res, err = h.ReservationRepository.EndReservation(reservationItemTime, itemID, ItemPerSlot, c.Locals("email").(string))
if err != nil {
Expand All @@ -456,6 +477,21 @@
"error": "Failed to end reservation",
})
}

if h.DiscordService != nil && res.User != nil {
itemName, err := h.ReservationRepository.GetItemName(itemID)
if err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to get item name for Discord notification: %v", err))
itemName = "Unknown Item"
}

startDate := reservationItemTime.StartDate.Format("2006-01-02 15:04:05")
endDate := reservationItemTime.EndDate.Format("2006-01-02 15:04:05")

if err := h.DiscordService.SendReservationCancelled(itemName, startDate, endDate, ItemPerSlot, *res.User); err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to send Discord notification for reservation cancellation: %v", err))
}
}
}

utils.LogMessage(utils.LevelInfo, "Reservation updated successfully")
Expand Down Expand Up @@ -670,6 +706,42 @@
})
}

if h.DiscordService != nil && deleted {
itemName, err := h.ReservationRepository.GetItemName(itemID)
if err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to get item name for Discord notification: %v", err))
itemName = "Unknown Item"
}

userEmail := c.Locals("email").(string)
userInfo := models.ReservationUser{
Email: userEmail,
}

// Try to get user details from database
query := "SELECT first_name, last_name, COALESCE(profile_picture, '') FROM newf WHERE email = $1"
row := h.ReservationRepository.DB.QueryRow(query, userEmail)
if err := row.Scan(&userInfo.FirstName, &userInfo.LastName, &userInfo.ProfilePicture); err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to get user details for Discord notification: %v", err))
userInfo.FirstName = "Unknown"
userInfo.LastName = "User"
}

startDate := reservationItemTime.StartDate.Format("2006-01-02 15:04:05")
var endDate string
if ItemPerSlot {
var slotDuration time.Duration = time.Hour // default to 1 hour if not set
if reservationItemTime.SlotDuration > 0 {

Check failure on line 734 in handlers/reservation/reservation_handler.go

View workflow job for this annotation

GitHub Actions / build

reservationItemTime.SlotDuration undefined (type models.ReservationManagementRequestTime has no field or method SlotDuration)
slotDuration = reservationItemTime.SlotDuration

Check failure on line 735 in handlers/reservation/reservation_handler.go

View workflow job for this annotation

GitHub Actions / build

reservationItemTime.SlotDuration undefined (type models.ReservationManagementRequestTime has no field or method SlotDuration)
}
endDate = reservationItemTime.StartDate.Add(slotDuration).Format("2006-01-02 15:04:05")
}

if err := h.DiscordService.SendReservationCancelled(itemName, startDate, endDate, ItemPerSlot, userInfo); err != nil {
utils.LogMessage(utils.LevelWarn, fmt.Sprintf("Failed to send Discord notification for reservation deletion: %v", err))
}
}

utils.LogMessage(utils.LevelInfo, "Reservation deleted successfully")
utils.LogFooter()
return c.JSON(fiber.Map{
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func main() {

// Discord webhook notifications
discordService := services.NewDiscordService(os.Getenv("DISCORD_WEBHOOK_URL"))
// Discord reservation-specific webhook (falls back to default if unset)
reservationDiscordService := services.NewDiscordService(os.Getenv("DISCORD_RESERVATION_ALERT_WEBHOOK"))

restHandler := restaurantHandler.NewRestaurantHandler(db, translationService, notificationService)

Expand Down Expand Up @@ -168,7 +170,7 @@ func main() {
routes.SetupWashingMachineRoutes(app)
routes.SetupWeatherRoutes(app, weatherHandler)
routes.SetupEventRoutes(app, eventHandler)
routes.SetupReservationRoutes(app, db)
routes.SetupReservationRoutes(app, db, reservationDiscordService)
routes.SetupBassineRoutes(app, db)
routes.SetupAdminRoutes(app, db)

Expand Down
5 changes: 3 additions & 2 deletions routes/reservation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/plugimt/transat-backend/handlers/reservation" // Import the reservation handlers
"github.com/plugimt/transat-backend/middlewares"
"github.com/plugimt/transat-backend/services"
)

func SetupReservationRoutes(router fiber.Router, db *sql.DB) {
func SetupReservationRoutes(router fiber.Router, db *sql.DB, discordService *services.DiscordService) {
// Initialize Reservation Handler
reservationHandler := reservation.NewReservationHandler(db)
reservationHandler := reservation.NewReservationHandler(db, discordService)

reservationGroup := router.Group("/reservation", middlewares.JWTMiddleware)

Expand Down
84 changes: 79 additions & 5 deletions services/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,21 @@ type discordEmbedField struct {
}

type discordEmbed struct {
Title string `json:"title"`
Description string `json:"description,omitempty"`
Color int `json:"color,omitempty"`
Fields []discordEmbedField `json:"fields,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Color int `json:"color,omitempty"`
Fields []discordEmbedField `json:"fields,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
Thumbnail *discordEmbedThumbnail `json:"thumbnail,omitempty"`
Image *discordEmbedImage `json:"image,omitempty"`
}

type discordEmbedThumbnail struct {
URL string `json:"url"`
}

type discordEmbedImage struct {
URL string `json:"url"`
}

type discordPayload struct {
Expand Down Expand Up @@ -111,3 +121,67 @@ func (ds *DiscordService) SendUserVerified(user models.Newf, numberOfAccounts in
}
return ds.sendEmbed(embed)
}

func (ds *DiscordService) SendReservationCreated(itemName, startDate, endDate string, isSlot bool, user models.ReservationUser) error {
var slotType string
if isSlot {
slotType = "Slot-based"
} else {
slotType = "Open-ended"
}

fields := []discordEmbedField{
{Name: "Item", Value: safe(itemName, "N/A"), Inline: false},
{Name: "Type", Value: slotType, Inline: true},
{Name: "Start Date", Value: safe(startDate, "N/A"), Inline: true},
{Name: "User", Value: fmt.Sprintf("%s %s", safe(user.FirstName, ""), safe(user.LastName, "")), Inline: true},
{Name: "Email", Value: safe(user.Email, "N/A"), Inline: true},
}

if endDate != "" {
fields = append(fields, discordEmbedField{Name: "End Date", Value: endDate, Inline: true})
}

embed := discordEmbed{
Title: "New Reservation Created",
Color: 0x4CAF50, // green
Timestamp: time.Now().UTC().Format(time.RFC3339),
Fields: fields,
}
if user.ProfilePicture != "" {
embed.Thumbnail = &discordEmbedThumbnail{URL: user.ProfilePicture}
}
return ds.sendEmbed(embed)
}

func (ds *DiscordService) SendReservationCancelled(itemName, startDate, endDate string, isSlot bool, user models.ReservationUser) error {
var slotType string
if isSlot {
slotType = "Slot-based"
} else {
slotType = "Open-ended"
}

fields := []discordEmbedField{
{Name: "Item", Value: safe(itemName, "N/A"), Inline: false},
{Name: "Type", Value: slotType, Inline: true},
{Name: "Start Date", Value: safe(startDate, "N/A"), Inline: true},
{Name: "User", Value: fmt.Sprintf("%s %s", safe(user.FirstName, ""), safe(user.LastName, "")), Inline: true},
{Name: "Email", Value: safe(user.Email, "N/A"), Inline: true},
}

if endDate != "" {
fields = append(fields, discordEmbedField{Name: "End Date", Value: endDate, Inline: true})
}

embed := discordEmbed{
Title: "Reservation Cancelled",
Color: 0xF44336, // red
Timestamp: time.Now().UTC().Format(time.RFC3339),
Fields: fields,
}
if user.ProfilePicture != "" {
embed.Thumbnail = &discordEmbedThumbnail{URL: user.ProfilePicture}
}
return ds.sendEmbed(embed)
}
Loading