init
This commit is contained in:
17
go.mod
Normal file
17
go.mod
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module synlotto-website
|
||||||
|
|
||||||
|
go 1.24.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect
|
||||||
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
modernc.org/libc v1.61.13 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.8.2 // indirect
|
||||||
|
modernc.org/sqlite v1.36.1 // indirect
|
||||||
|
)
|
||||||
23
go.sum
Normal file
23
go.sum
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo=
|
||||||
|
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
|
||||||
|
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
|
||||||
|
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
|
||||||
|
modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ=
|
||||||
|
modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
|
||||||
61
handlers/draw_handler.go
Normal file
61
handlers/draw_handler.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"synlotto-website/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tmpl = template.Must(template.ParseFiles(
|
||||||
|
"templates/layout.html",
|
||||||
|
"templates/index.html",
|
||||||
|
"templates/new_draw.html",
|
||||||
|
))
|
||||||
|
|
||||||
|
func Home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("✅ Home hit")
|
||||||
|
|
||||||
|
var draws []models.Draw
|
||||||
|
|
||||||
|
err := tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
||||||
|
"Page": "index",
|
||||||
|
"Data": draws,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("❌ Template error:", err)
|
||||||
|
http.Error(w, "Error rendering homepage", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDraw(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("➡️ New draw form opened")
|
||||||
|
|
||||||
|
err := tmpl.ExecuteTemplate(w, "layout", map[string]interface{}{
|
||||||
|
"Page": "new_draw",
|
||||||
|
"Data": nil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("❌ Template error:", err)
|
||||||
|
http.Error(w, "Error rendering form", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Submit(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("📝 Form submission received")
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid form", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
date := r.FormValue("date")
|
||||||
|
numbers := r.FormValue("numbers")
|
||||||
|
spend := r.FormValue("spend")
|
||||||
|
ret := r.FormValue("return")
|
||||||
|
|
||||||
|
log.Printf("📅 Date: %s | 🔢 Numbers: %s | 💸 Spend: %s | 💰 Return: %s\n", date, numbers, spend, ret)
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
}
|
||||||
16
main.go
Normal file
16
main.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"synlotto-website/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", handlers.Home)
|
||||||
|
http.HandleFunc("/new", handlers.NewDraw)
|
||||||
|
http.HandleFunc("/submit", handlers.Submit)
|
||||||
|
|
||||||
|
log.Println("🚀 Lotto Tracker running on http://localhost:8080")
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
||||||
10
models/draw.go
Normal file
10
models/draw.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Draw struct {
|
||||||
|
ID int
|
||||||
|
Date string
|
||||||
|
Numbers string
|
||||||
|
Spend float64
|
||||||
|
Return float64
|
||||||
|
ProfitLoss float64
|
||||||
|
}
|
||||||
31
storage/db.go
Normal file
31
storage/db.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitDB(filepath string) *sql.DB {
|
||||||
|
db, err := sql.Open("sqlite", filepath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("❌ Failed to open DB:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
createTable := `
|
||||||
|
CREATE TABLE IF NOT EXISTS draws (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
date TEXT,
|
||||||
|
numbers TEXT,
|
||||||
|
spend REAL,
|
||||||
|
return REAL
|
||||||
|
);`
|
||||||
|
|
||||||
|
_, err = db.Exec(createTable)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("❌ Failed to create table:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
29
templates/index.html
Normal file
29
templates/index.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{{ define "index" }}
|
||||||
|
<a href="/new">+ Add New Draw</a>
|
||||||
|
|
||||||
|
{{ if . }}
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Numbers</th>
|
||||||
|
<th>Spend (£)</th>
|
||||||
|
<th>Return (£)</th>
|
||||||
|
<th>Profit/Loss (£)</th>
|
||||||
|
</tr>
|
||||||
|
{{ range . }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ .Date }}</td>
|
||||||
|
<td>{{ .Numbers }}</td>
|
||||||
|
<td>{{ printf "%.2f" .Spend }}</td>
|
||||||
|
<td>{{ printf "%.2f" .Return }}</td>
|
||||||
|
<td>{{ printf "%.2f" .ProfitLoss }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ else }}
|
||||||
|
<tr><td colspan="5">No draws recorded yet.</td></tr>
|
||||||
|
{{ end }}
|
||||||
|
</table>
|
||||||
|
{{ else }}
|
||||||
|
<p>No draws recorded yet. <a href="/new">Add your first draw</a></p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
26
templates/layout.html
Normal file
26
templates/layout.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{{ define "layout" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Lotto Tracker</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; margin: 40px; }
|
||||||
|
table { border-collapse: collapse; width: 100%; margin-top: 20px; }
|
||||||
|
th, td { padding: 8px 12px; border: 1px solid #ddd; text-align: center; }
|
||||||
|
th { background-color: #f5f5f5; }
|
||||||
|
.form-section { margin-bottom: 20px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Lotto Tracker</h1>
|
||||||
|
|
||||||
|
{{ if eq .Page "index" }}
|
||||||
|
{{ template "index" .Data }}
|
||||||
|
{{ else if eq .Page "new_draw" }}
|
||||||
|
{{ template "new_draw" .Data }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
19
templates/new_draw.html
Normal file
19
templates/new_draw.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{ define "new_draw" }}
|
||||||
|
<a href="/">← Back to Overview</a>
|
||||||
|
<h2>New Draw Entry</h2>
|
||||||
|
<form action="/submit" method="POST">
|
||||||
|
<div class="form-section">
|
||||||
|
<label>Date: <input type="date" name="date" required></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label>Numbers: <input type="text" name="numbers" required></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label>Spend (£): <input type="number" step="0.01" name="spend" required></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label>Return (£): <input type="number" step="0.01" name="return" required></label>
|
||||||
|
</div>
|
||||||
|
<button type="submit">Save</button>
|
||||||
|
</form>
|
||||||
|
{{ end }}
|
||||||
BIN
tracker.db
Normal file
BIN
tracker.db
Normal file
Binary file not shown.
Reference in New Issue
Block a user