commit 0159e5e49f810ceb7f365809892dd210599066b4 Author: Your Name Date: Thu Feb 19 22:36:03 2026 +0100 yeet diff --git a/.aider.chat.history.md b/.aider.chat.history.md new file mode 100644 index 0000000..205e36e --- /dev/null +++ b/.aider.chat.history.md @@ -0,0 +1,6 @@ + +# aider chat started at 2026-02-19 21:35:37 + +> Update git name with: git config user.name "Your Name" +> Update git email with: git config user.email "you@example.com" +> You can skip this check with --no-gitignore diff --git a/birthdays.db b/birthdays.db new file mode 100644 index 0000000..c139d50 Binary files /dev/null and b/birthdays.db differ diff --git a/db/birthdays.db b/db/birthdays.db new file mode 100644 index 0000000..22be88f Binary files /dev/null and b/db/birthdays.db differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3d89e89 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module main.go + +go 1.25.7 + +require github.com/mattn/go-sqlite3 v1.14.34 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..684933a --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= +github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= diff --git a/main.go b/main.go new file mode 100644 index 0000000..391b2fe --- /dev/null +++ b/main.go @@ -0,0 +1,143 @@ +package main + +import ( + "database/sql" + "html/template" + "log" + "net/http" + "time" + + _ "github.com/mattn/go-sqlite3" +) + +type Birthday struct { + ID int `db:"id"` + Name string `db:"name"` + Birthday time.Time `db:"birthday"` + GiftPurchased bool `db:"gift_purchased"` + DaysUntil int `db:"days_until"` +} + +var db *sql.DB + +func initDB() { + var err error + db, err = sql.Open("sqlite3", "./birthdays.db") + if err != nil { + log.Fatal(err) + } + + // Create table if it doesn't exist + createTableSQL := ` + CREATE TABLE IF NOT EXISTS birthdays ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + birthday DATE NOT NULL, + gift_purchased BOOLEAN DEFAULT FALSE + );` + + _, err = db.Exec(createTableSQL) + if err != nil { + log.Fatal(err) + } +} + +func getBirthdays() ([]Birthday, error) { + rows, err := db.Query("SELECT id, name, birthday, gift_purchased FROM birthdays ORDER BY birthday") + if err != nil { + return nil, err + } + defer rows.Close() + + var birthdays []Birthday + for rows.Next() { + var b Birthday + err := rows.Scan(&b.ID, &b.Name, &b.Birthday, &b.GiftPurchased) + if err != nil { + return nil, err + } + birthdays = append(birthdays, b) + } + + return birthdays, nil +} + +func addBirthday(name string, birthday time.Time, giftPurchased bool) error { + _, err := db.Exec("INSERT INTO birthdays (name, birthday, gift_purchased) VALUES (?, ?, ?)", name, birthday, giftPurchased) + return err +} + +func homeHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + name := r.FormValue("name") + birthdayStr := r.FormValue("birthday") + giftPurchased := r.FormValue("giftPurchased") == "on" + + // Parse the birthday date + birthday, err := time.Parse("2006-01-02", birthdayStr) + if err != nil { + log.Printf("Error parsing birthday: %v", err) + http.Error(w, "Invalid birthday date", http.StatusBadRequest) + return + } + + err = addBirthday(name, birthday, giftPurchased) + if err != nil { + log.Printf("Error adding birthday: %v", err) + http.Error(w, "Error adding birthday", http.StatusInternalServerError) + return + } + + // Redirect to avoid resubmission + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + + birthdays, err := getBirthdays() + if err != nil { + log.Printf("Error getting birthdays: %v", err) + http.Error(w, "Error getting birthdays", http.StatusInternalServerError) + return + } + + // Calculate days until each birthday + for i, b := range birthdays { + nextBirthday := time.Date(time.Now().Year(), b.Birthday.Month(), b.Birthday.Day(), 0, 0, 0, 0, time.Now().Location()) + if nextBirthday.Before(time.Now()) { + nextBirthday = nextBirthday.AddDate(1, 0, 0) + } + daysUntil := int(nextBirthday.Sub(time.Now()).Hours() / 24) + birthdays[i].DaysUntil = daysUntil + } + + data := struct { + UpcomingBirthdays []Birthday + }{ + UpcomingBirthdays: birthdays, + } + + // Parse template from template directory + tmpl, err := template.ParseFiles("template/index.html") + if err != nil { + log.Printf("Error parsing template: %v", err) + http.Error(w, "Template error", http.StatusInternalServerError) + return + } + + err = tmpl.Execute(w, data) + if err != nil { + log.Printf("Error executing template: %v", err) + http.Error(w, "Template execution error", http.StatusInternalServerError) + return + } +} + +func main() { + initDB() + defer db.Close() + + http.HandleFunc("/", homeHandler) + + log.Println("Server starting on http://localhost:8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/template/index.html b/template/index.html new file mode 100644 index 0000000..acd2a90 --- /dev/null +++ b/template/index.html @@ -0,0 +1,139 @@ + + + + + + Birthday Reminder + + + + + +
+
+
+
+
+ Upcoming Birthdays +
+
+
+
+
Today
+
    + {{range .UpcomingBirthdays}} + {{if eq .DaysUntil 0}} +
  • + {{.Name}} + TODAY! +
  • + {{end}} + {{end}} +
+
+
+
Coming Up
+
    + {{range .UpcomingBirthdays}} + {{if ne .DaysUntil 0}} +
  • + {{.Name}} + {{.DaysUntil}} days +
  • + {{end}} + {{end}} +
+
+
+
+
+
+
+
+
+ Add New Birthday +
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+ + + + + +