219 lines
6.3 KiB
Go
219 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"mealprep/database"
|
|
"mealprep/handlers"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
// Print startup banner
|
|
printBanner()
|
|
|
|
// Initialize database
|
|
if err := database.InitDB("mealprep.db"); err != nil {
|
|
log.Fatalf("Failed to initialize database: %v", err)
|
|
}
|
|
defer database.DB.Close()
|
|
|
|
log.Println("✅ Database initialized successfully")
|
|
|
|
// Static files
|
|
fs := http.FileServer(http.Dir("static"))
|
|
http.Handle("/static/", http.StripPrefix("/static/", fs))
|
|
|
|
// Routes
|
|
http.HandleFunc("/", indexHandler)
|
|
|
|
// Ingredients
|
|
http.HandleFunc("/ingredients", func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case "GET":
|
|
handlers.IngredientsHandler(w, r)
|
|
case "POST":
|
|
handlers.AddIngredientHandler(w, r)
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
http.HandleFunc("/ingredients/", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == "DELETE" {
|
|
handlers.DeleteIngredientHandler(w, r)
|
|
} else {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
|
|
// Meals
|
|
http.HandleFunc("/meals", func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case "GET":
|
|
handlers.MealsHandler(w, r)
|
|
case "POST":
|
|
handlers.AddMealHandler(w, r)
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
http.HandleFunc("/meals/", func(w http.ResponseWriter, r *http.Request) {
|
|
path := r.URL.Path
|
|
if strings.Contains(path, "/ingredients") {
|
|
// Meal ingredients routes
|
|
if r.Method == "GET" {
|
|
handlers.GetMealIngredientsHandler(w, r)
|
|
} else if r.Method == "POST" {
|
|
handlers.AddMealIngredientHandler(w, r)
|
|
} else if r.Method == "DELETE" {
|
|
handlers.DeleteMealIngredientHandler(w, r)
|
|
} else {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
} else {
|
|
// Meal delete route
|
|
if r.Method == "DELETE" {
|
|
handlers.DeleteMealHandler(w, r)
|
|
} else {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
})
|
|
|
|
// Week Plan
|
|
http.HandleFunc("/week-plan", func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case "GET":
|
|
handlers.WeekPlanHandler(w, r)
|
|
case "POST":
|
|
handlers.AddWeekPlanEntryHandler(w, r)
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
http.HandleFunc("/week-plan/", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == "DELETE" {
|
|
handlers.DeleteWeekPlanEntryHandler(w, r)
|
|
} else {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
|
|
// Grocery List
|
|
http.HandleFunc("/grocery-list", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == "GET" {
|
|
handlers.GroceryListHandler(w, r)
|
|
} else {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
})
|
|
|
|
|
|
// Start server
|
|
port := "8080"
|
|
fmt.Println()
|
|
log.Printf("🚀 Server running on http://localhost:%s", port)
|
|
log.Println("📝 Press Ctrl+C to stop the server")
|
|
fmt.Println()
|
|
if err := http.ListenAndServe(":"+port, nil); err != nil {
|
|
log.Fatalf("❌ Failed to start server: %v", err)
|
|
}
|
|
}
|
|
|
|
func printBanner() {
|
|
banner := `
|
|
╔══════════════════════════════════════════════════════════════╗
|
|
║ ║
|
|
║ 🍽️ MEAL PREP PLANNER 🍽️ ║
|
|
║ ║
|
|
║ Plan your meals • Generate grocery lists ║
|
|
║ ║
|
|
╚══════════════════════════════════════════════════════════════╝
|
|
`
|
|
fmt.Println(banner)
|
|
log.Println("🔧 Initializing application...")
|
|
}
|
|
|
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
tmpl := `
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Meal Prep Planner</title>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="container">
|
|
<h1>🍽️ Meal Prep Planner</h1>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="container">
|
|
<div class="tabs">
|
|
<button class="tab active"
|
|
hx-get="/ingredients"
|
|
hx-target="#content"
|
|
hx-swap="innerHTML"
|
|
onclick="setActiveTab(this)">
|
|
Ingredients
|
|
</button>
|
|
<button class="tab"
|
|
hx-get="/meals"
|
|
hx-target="#content"
|
|
hx-swap="innerHTML"
|
|
onclick="setActiveTab(this)">
|
|
Meals
|
|
</button>
|
|
<button class="tab"
|
|
hx-get="/week-plan"
|
|
hx-target="#content"
|
|
hx-swap="innerHTML"
|
|
onclick="setActiveTab(this)">
|
|
Week Plan
|
|
</button>
|
|
<button class="tab"
|
|
hx-get="/grocery-list"
|
|
hx-target="#content"
|
|
hx-swap="innerHTML"
|
|
onclick="setActiveTab(this)">
|
|
Grocery List
|
|
</button>
|
|
</div>
|
|
|
|
<div id="content" class="content">
|
|
<div hx-get="/ingredients" hx-trigger="load"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function setActiveTab(clickedTab) {
|
|
// Remove active class from all tabs
|
|
document.querySelectorAll('.tab').forEach(tab => {
|
|
tab.classList.remove('active');
|
|
});
|
|
// Add active class to clicked tab
|
|
clickedTab.classList.add('active');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
`
|
|
|
|
t := template.Must(template.New("index").Parse(tmpl))
|
|
if err := t.Execute(w, nil); err != nil {
|
|
log.Printf("Error rendering template: %v", err)
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
}
|
|
}
|