From 9268e1cbba70e74047d93a7adb238c383bcc797f Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Sun, 22 Sep 2024 19:50:57 +0200 Subject: [PATCH 01/24] add go-echarts for SSR charts --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 337d2ff..aea327d 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.22.1 require ( github.com/dustin/go-humanize v1.0.1 // indirect + github.com/go-echarts/go-echarts/v2 v2.4.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.3.0 // indirect diff --git a/go.sum b/go.sum index 055817f..90a6084 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg= github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4= 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/go-echarts/go-echarts/v2 v2.4.2 h1:1FC3tGzsLSgdeO4Ltc3OAtcIiRomfEKxKX9oocIL68g= +github.com/go-echarts/go-echarts/v2 v2.4.2/go.mod h1:56YlvzhW/a+du15f3S2qUGNDfKnFOeJSThBIrVFHDtI= 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/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= From 02812ba0c7a61d2255781d1c284280f784e7ee2d Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Sun, 22 Sep 2024 19:51:27 +0200 Subject: [PATCH 02/24] separate the handlers --- internal/api_handlers.go | 51 ++++++++++++++++++++++++++++++++++++++++ internal/handlers.go | 9 +++++++ main.go | 4 ++++ 3 files changed, 64 insertions(+) create mode 100644 internal/api_handlers.go diff --git a/internal/api_handlers.go b/internal/api_handlers.go new file mode 100644 index 0000000..fe9a2cc --- /dev/null +++ b/internal/api_handlers.go @@ -0,0 +1,51 @@ +package internal + +import ( + "html/template" + "math/rand" + "minesweeper/internal/db" + "net/http" + + "github.com/go-echarts/go-echarts/v2/charts" + "github.com/go-echarts/go-echarts/v2/opts" + "github.com/gorilla/sessions" +) + +type ApiHandler struct { + Templates *template.Template + Store *sessions.CookieStore + Queries *db.Queries +} + +func NewApiHandler(templates *template.Template, store *sessions.CookieStore, queries *db.Queries) *ApiHandler { + return &ApiHandler{templates, store, queries} +} + +func (h *ApiHandler) PieWinsLossesIncompleteChart(w http.ResponseWriter, r *http.Request) { + var ( + itemCntPie = 3 + options = []string{"Wins", "Losses", "Incomplete"} + colors = []string{"#28a745", "#dc3545", "#ffc107"} + ) + + items := make([]opts.PieData, 0) + for i := 0; i < itemCntPie; i++ { + items = append(items, opts.PieData{Name: options[i], Value: rand.Intn(100), ItemStyle: &opts.ItemStyle{Color: colors[i]}}) + } + + pie := charts.NewPie() + + pie.SetGlobalOptions(charts.WithTitleOpts(opts.Title{ + Title: "Wins vs Losses vs Incomplete", + Subtitle: "Minesweeper Global Statistics", + })) + + pie.AddSeries("Game Status", items). + SetSeriesOptions( + charts.WithLabelOpts(opts.Label{ + Formatter: "{b}: {d}%", // Label formatter to show percentage + }), + ) + + pie.Render(w) +} diff --git a/internal/handlers.go b/internal/handlers.go index ed31601..7578afd 100644 --- a/internal/handlers.go +++ b/internal/handlers.go @@ -360,3 +360,12 @@ func (h *Handler) SessionGamesInfo(w http.ResponseWriter, r *http.Request) { } } + +func (h *Handler) Charts(w http.ResponseWriter, r *http.Request) { + err := h.Templates.ExecuteTemplate(w, "charts_page", nil) + + if err != nil { + http.Error(w, fmt.Sprintf("Error rendering template: %v", err), http.StatusInternalServerError) + return + } +} diff --git a/main.go b/main.go index 325816e..876e34c 100644 --- a/main.go +++ b/main.go @@ -109,6 +109,7 @@ func main() { queries := db.New(dbConn) handler := internal.NewHandler(templates, globalStore, queries) + apiHandler := internal.NewApiHandler(templates, globalStore, queries) mux.HandleFunc("/", handler.Index) mux.HandleFunc("/load-game", handler.LoadGame) @@ -116,6 +117,9 @@ func main() { mux.HandleFunc("/handle-grid-action", handler.HandleGridAction) mux.HandleFunc("/games", handler.IndexGames) mux.HandleFunc("/session-games-info", handler.SessionGamesInfo) + mux.HandleFunc("/charts", handler.Charts) + + mux.HandleFunc("/api/charts/pie/wins-losses-incomplete", apiHandler.PieWinsLossesIncompleteChart) port := cmp.Or(os.Getenv("APP_PORT"), "8080") From a65b46580f16cc869f7a41d4792e59f2544dfcd1 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Sun, 22 Sep 2024 19:51:43 +0200 Subject: [PATCH 03/24] add charts page --- templates/admin/base_layout.html | 4 ++-- templates/admin/charts_page.html | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 templates/admin/charts_page.html diff --git a/templates/admin/base_layout.html b/templates/admin/base_layout.html index c8ba16e..f682c12 100644 --- a/templates/admin/base_layout.html +++ b/templates/admin/base_layout.html @@ -7,7 +7,7 @@ name="viewport" content="width=device-width, initial-scale=1.0" /> - Minesweeper! + Minesweeper in Numbers - + {{ template "navbar" }} {{ block "content" . }}{{ end }} diff --git a/templates/admin/charts_page.html b/templates/admin/charts_page.html new file mode 100644 index 0000000..c393b7d --- /dev/null +++ b/templates/admin/charts_page.html @@ -0,0 +1,16 @@ +{{ define "charts_page" }} + {{ template "base_layout" . }} +
+

Game Statistics

+ +
+ +
+
+{{ end }} From 0b359b783de0e4c3d69787661bc32e8f095ea290 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Tue, 24 Sep 2024 21:17:14 +0200 Subject: [PATCH 04/24] rm pointless

tag --- templates/index.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/templates/index.html b/templates/index.html index 4b4af45..796d4d8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -27,21 +27,19 @@ {{ if .HasGameUuid }} -
-

+

Loading existing game...

{{ else }} -
Date: Tue, 24 Sep 2024 21:33:54 +0200 Subject: [PATCH 05/24] new endpoint retuning escaped html --- internal/api_handlers.go | 36 +++++++++++++++++++++++++++++++++++- main.go | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/internal/api_handlers.go b/internal/api_handlers.go index fe9a2cc..6f61000 100644 --- a/internal/api_handlers.go +++ b/internal/api_handlers.go @@ -47,5 +47,39 @@ func (h *ApiHandler) PieWinsLossesIncompleteChart(w http.ResponseWriter, r *http }), ) - pie.Render(w) +func (h *ApiHandler) GridSizeBar(w http.ResponseWriter, r *http.Request) { + // TODO remove this placeholder data + gridSizes := []string{"3x3", "4x4", "5x5", "6x6", "7x7"} + gamesPlayed := []int{15, 40, 25, 10, 5} + + bar := charts.NewBar() + bar.SetGlobalOptions(charts.WithTitleOpts(opts.Title{ + Title: "Grid Size Popularity", + Subtitle: "Games played per grid size", + }), charts.WithDataZoomOpts(opts.DataZoom{ + Type: "slider", + Start: 10, + End: 50, + }), + ) + + items := make([]opts.BarData, 0) + for _, v := range gamesPlayed { + items = append(items, opts.BarData{Value: v}) + } + + bar.SetXAxis(gridSizes). + AddSeries("Games Played", items). + SetSeriesOptions( + charts.WithLabelOpts(opts.Label{Show: opts.Bool(true)}), + ) + + htmlBarSnippet, err := renderToHtml(bar) + + if err != nil { + http.Error(w, fmt.Sprintf("Error rendering chart: %v", err), http.StatusInternalServerError) + return + } + + w.Write([]byte(htmlBarSnippet)) } diff --git a/main.go b/main.go index 876e34c..6248530 100644 --- a/main.go +++ b/main.go @@ -119,7 +119,7 @@ func main() { mux.HandleFunc("/session-games-info", handler.SessionGamesInfo) mux.HandleFunc("/charts", handler.Charts) - mux.HandleFunc("/api/charts/pie/wins-losses-incomplete", apiHandler.PieWinsLossesIncompleteChart) + mux.HandleFunc("/api/charts/bar/grid-size", apiHandler.GridSizeBar) port := cmp.Or(os.Getenv("APP_PORT"), "8080") From 2f64a63fdb5ee0580db6aab92f0873838916f7db Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Tue, 24 Sep 2024 21:34:34 +0200 Subject: [PATCH 06/24] custom Renderer to get escaped html --- internal/api_handlers.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/internal/api_handlers.go b/internal/api_handlers.go index 6f61000..54c01ca 100644 --- a/internal/api_handlers.go +++ b/internal/api_handlers.go @@ -1,6 +1,8 @@ package internal import ( + "bytes" + "fmt" "html/template" "math/rand" "minesweeper/internal/db" @@ -9,6 +11,8 @@ import ( "github.com/go-echarts/go-echarts/v2/charts" "github.com/go-echarts/go-echarts/v2/opts" "github.com/gorilla/sessions" + + chartrender "github.com/go-echarts/go-echarts/v2/render" ) type ApiHandler struct { @@ -21,7 +25,30 @@ func NewApiHandler(templates *template.Template, store *sessions.CookieStore, qu return &ApiHandler{templates, store, queries} } -func (h *ApiHandler) PieWinsLossesIncompleteChart(w http.ResponseWriter, r *http.Request) { +// renderToHtml renders a chart as a template.HTML value. +// +// The argument should be a go-echarts chart that implements the Renderer interface. +// The rendered chart is returned as a template.HTML value. +// +// If the chart fails to render, an error is returned. +func renderToHtml(c interface{}) (template.HTML, error) { + r, ok := c.(chartrender.Renderer) + if !ok { + return "", fmt.Errorf("provided chart does not implement the Renderer interface") + } + + var buf bytes.Buffer + + err := r.Render(&buf) + if err != nil { + return "", fmt.Errorf("failed to render chart: %v", err) + + } + + return template.HTML(buf.String()), nil +} + +func pieWinsLossesIncompleteChart() *charts.Pie { var ( itemCntPie = 3 options = []string{"Wins", "Losses", "Incomplete"} From b4b2dd53bba1ea67e010455640cac39cbb3c7256 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Tue, 24 Sep 2024 21:35:38 +0200 Subject: [PATCH 07/24] working lazy loading chart with spinner --- templates/admin/charts_page.html | 44 +++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/templates/admin/charts_page.html b/templates/admin/charts_page.html index c393b7d..f4c915f 100644 --- a/templates/admin/charts_page.html +++ b/templates/admin/charts_page.html @@ -1,16 +1,42 @@ {{ define "charts_page" }} + {{ template "base_layout" . }} -
+

Game Statistics

-
- +
+
+
+
+ + + + +

+ Loading chart... +

+
+
+
{{ end }} From 593a3e9663fca3c82d26763695182053302672a8 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Tue, 24 Sep 2024 21:36:14 +0200 Subject: [PATCH 08/24] container ad margin on top, useless --- templates/admin/navbar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/admin/navbar.html b/templates/admin/navbar.html index f092ded..469fef0 100644 --- a/templates/admin/navbar.html +++ b/templates/admin/navbar.html @@ -1,7 +1,7 @@ {{ define "navbar" }}
{{ end }} From 0029a55c17d6ae7241277792b16db523e7c0f5b4 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Sun, 29 Sep 2024 11:13:53 +0200 Subject: [PATCH 19/24] rm placeholder data from pie chart --- db/queries/queries.sql | 9 +++++++++ internal/api_handlers.go | 26 ++++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/db/queries/queries.sql b/db/queries/queries.sql index 864ae10..7d9fcc6 100644 --- a/db/queries/queries.sql +++ b/db/queries/queries.sql @@ -62,6 +62,15 @@ FROM WHERE uuid IN (sqlc.slice('uuids')); +-- name: GetGamesInfo :one +SELECT + COUNT(*) AS total_games, + COUNT(*) FILTER (WHERE game_won = TRUE) AS won_games, + COUNT(*) FILTER (WHERE game_failed = TRUE AND game_won = FALSE) AS lost_games, + COUNT(*) FILTER (WHERE game_failed = FALSE AND game_won = FALSE) AS not_finished_games +FROM + games; + -- name: GetMovesByGameId :many SELECT * diff --git a/internal/api_handlers.go b/internal/api_handlers.go index 9da017b..9f84a52 100644 --- a/internal/api_handlers.go +++ b/internal/api_handlers.go @@ -5,10 +5,10 @@ import ( "database/sql" "fmt" "html/template" - "math/rand" "minesweeper/internal/db" "net/http" "strings" + "time" "github.com/go-echarts/go-echarts/v2/charts" "github.com/go-echarts/go-echarts/v2/opts" @@ -60,26 +60,28 @@ func renderToHtml(c interface{}) (template.HTML, error) { } func (h *ApiHandler) PieWinsLossesIncompleteChart(w http.ResponseWriter, r *http.Request) { - var ( - itemCntPie = 3 - options = []string{"Wins", "Losses", "Incomplete"} - colors = []string{"#28a745", "#dc3545", "#ffc107"} - ) + rawData, err := h.Queries.GetGamesInfo(r.Context()) - // TODO remove this placeholder data - items := make([]opts.PieData, 0) - for i := 0; i < itemCntPie; i++ { - items = append(items, opts.PieData{Name: options[i], Value: rand.Intn(100), ItemStyle: &opts.ItemStyle{Color: colors[i]}}) + if err != nil { + http.Error(w, fmt.Sprintf("Error fetching DB information: %v", err), http.StatusInternalServerError) + return } + parsedData := make([]opts.PieData, 0) + var colors = []string{"#28a745", "#dc3545", "#ffc107"} + + parsedData = append(parsedData, opts.PieData{Name: "Wins", Value: rawData.WonGames, ItemStyle: &opts.ItemStyle{Color: colors[0]}}) + parsedData = append(parsedData, opts.PieData{Name: "Losses", Value: rawData.LostGames, ItemStyle: &opts.ItemStyle{Color: colors[1]}}) + parsedData = append(parsedData, opts.PieData{Name: "Incomplete", Value: rawData.NotFinishedGames, ItemStyle: &opts.ItemStyle{Color: colors[2]}}) + pie := charts.NewPie() pie.SetGlobalOptions(charts.WithTitleOpts(opts.Title{ Title: "Wins vs Losses vs Incomplete", - Subtitle: "Minesweeper Global Statistics", + Subtitle: fmt.Sprintf("Total games: %v", rawData.TotalGames), })) - pie.AddSeries("Game Status", items). + pie.AddSeries("Game Status", parsedData). SetSeriesOptions( charts.WithLabelOpts(opts.Label{ Formatter: "{b}: {d}%", // Label formatter to show percentage From 2fb96c3e8534c86a1156f892063047b8278aafd8 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Sun, 29 Sep 2024 11:51:35 +0200 Subject: [PATCH 20/24] rm placeholder data with real one --- db/queries/queries.sql | 14 +++++- internal/api_handlers.go | 99 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 9 deletions(-) diff --git a/db/queries/queries.sql b/db/queries/queries.sql index 7d9fcc6..f339fa5 100644 --- a/db/queries/queries.sql +++ b/db/queries/queries.sql @@ -86,4 +86,16 @@ SELECT FROM games WHERE created_at >= ? AND created_at < ? GROUP BY day -ORDER BY day; \ No newline at end of file +ORDER BY day; + +-- name: GetGamesPlayedPerGridSize :many +SELECT grid_size, COUNT(*) AS games_played +FROM games +GROUP BY grid_size +ORDER BY grid_size; + +-- name: GetMinesPopularity :many +SELECT mines_amount, COUNT(*) AS mines_count +FROM games +GROUP BY mines_amount +ORDER BY mines_amount; \ No newline at end of file diff --git a/internal/api_handlers.go b/internal/api_handlers.go index 9f84a52..5749adc 100644 --- a/internal/api_handlers.go +++ b/internal/api_handlers.go @@ -7,6 +7,7 @@ import ( "html/template" "minesweeper/internal/db" "net/http" + "strconv" "strings" "time" @@ -99,9 +100,34 @@ func (h *ApiHandler) PieWinsLossesIncompleteChart(w http.ResponseWriter, r *http } func (h *ApiHandler) GridSizeBar(w http.ResponseWriter, r *http.Request) { - // TODO remove this placeholder data - gridSizes := []string{"3x3", "4x4", "5x5", "6x6", "7x7"} - gamesPlayed := []int{15, 40, 25, 10, 5} + + rawData, err := h.Queries.GetGamesPlayedPerGridSize(r.Context()) + + if err != nil { + http.Error(w, fmt.Sprintf("Error fetching grid size data: %v", err), http.StatusInternalServerError) + return + } + + gridSizes := make([]string, len(rawData)) + parsedBarData := make([]opts.BarData, len(rawData)) + + // find the most popular grid size + maxGamesPlayed := int64(0) + for _, dbData := range rawData { + if dbData.GamesPlayed > maxGamesPlayed { + maxGamesPlayed = dbData.GamesPlayed + } + } + + for i, dbData := range rawData { + gridSizes[i] = fmt.Sprintf("%vx%v", dbData.GridSize, dbData.GridSize) + + if dbData.GamesPlayed == maxGamesPlayed { + parsedBarData[i] = opts.BarData{Value: dbData.GamesPlayed, ItemStyle: &opts.ItemStyle{Color: "#ffa500"}} + } else { + parsedBarData[i] = opts.BarData{Value: dbData.GamesPlayed} + } + } bar := charts.NewBar() bar.SetGlobalOptions( @@ -118,13 +144,69 @@ func (h *ApiHandler) GridSizeBar(w http.ResponseWriter, r *http.Request) { }), ) - items := make([]opts.BarData, 0) - for _, v := range gamesPlayed { - items = append(items, opts.BarData{Value: v}) + bar.SetXAxis(gridSizes). + AddSeries("Games Played", parsedBarData). + SetSeriesOptions( + charts.WithLabelOpts(opts.Label{Show: opts.Bool(true)}), + ) + + htmlBarSnippet, err := renderToHtml(bar) + + if err != nil { + http.Error(w, fmt.Sprintf("Error rendering chart: %v", err), http.StatusInternalServerError) + return } - bar.SetXAxis(gridSizes). - AddSeries("Games Played", items). + w.Write([]byte(htmlBarSnippet)) +} + +func (h *ApiHandler) MinesAmountBarChart(w http.ResponseWriter, r *http.Request) { + rawDbData, err := h.Queries.GetMinesPopularity(r.Context()) + + if err != nil { + http.Error(w, fmt.Sprintf("Error fetching mines popularity data: %v", err), http.StatusInternalServerError) + return + } + + // find the most popular amount of mines + maxAmount := int64(0) + for _, dbData := range rawDbData { + if dbData.MinesCount > maxAmount { + maxAmount = dbData.MinesCount + } + } + + minesPopularity := make([]string, len(rawDbData)) + parsedBarData := make([]opts.BarData, len(rawDbData)) + + for i, dbData := range rawDbData { + minesPopularity[i] = strconv.FormatInt(dbData.MinesAmount, 10) + + if dbData.MinesCount == maxAmount { + parsedBarData[i] = opts.BarData{Value: dbData.MinesCount, ItemStyle: &opts.ItemStyle{Color: "#ffa500"}} + } else { + parsedBarData[i] = opts.BarData{Value: dbData.MinesCount} + } + } + + bar := charts.NewBar() + bar.SetGlobalOptions( + charts.WithTitleOpts(opts.Title{ + Title: "Amount of Mines Popularity", + Subtitle: "Games with particular amount of mines", + }), + charts.WithDataZoomOpts(opts.DataZoom{ + Type: "slider", + Start: 10, + End: 75, + }), + charts.WithLegendOpts(opts.Legend{ + Show: opts.Bool(false), + }), + ) + + bar.SetXAxis(minesPopularity). + AddSeries("Mines Amount", parsedBarData). SetSeriesOptions( charts.WithLabelOpts(opts.Label{Show: opts.Bool(true)}), ) @@ -138,6 +220,7 @@ func (h *ApiHandler) GridSizeBar(w http.ResponseWriter, r *http.Request) { w.Write([]byte(htmlBarSnippet)) } + func (h *ApiHandler) PlayedGamesInMonthBarChart(w http.ResponseWriter, r *http.Request) { pickedDate := r.URL.Query().Get("picked-date-range") From 66307736279c5f950c70f7af54e00882fc3af829 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Sun, 29 Sep 2024 19:21:47 +0200 Subject: [PATCH 21/24] total home page UI refactor --- templates/game/game_settings_form.html | 4 +- templates/game/session_games_info.html | 6 +- templates/index.html | 114 ++++++++++++++++++------- 3 files changed, 89 insertions(+), 35 deletions(-) diff --git a/templates/game/game_settings_form.html b/templates/game/game_settings_form.html index a48b3fd..20e4fe5 100644 --- a/templates/game/game_settings_form.html +++ b/templates/game/game_settings_form.html @@ -2,11 +2,11 @@
+
{{ if .HasGames }}
-

Not Finished

+

Incompleted

{{ .NotFinishedGames }} @@ -43,7 +43,7 @@
{{ else }}

No diff --git a/templates/index.html b/templates/index.html index 796d4d8..6e967bf 100644 --- a/templates/index.html +++ b/templates/index.html @@ -23,38 +23,92 @@ crossorigin="anonymous" referrerpolicy="no-referrer" /> - - - {{ if .HasGameUuid }} -

-

- Loading existing game... -

-
- {{ else }} - -
-

- Loading session games info... -

-
+ +
{{ end }} From 6fcd27d09740450eedbed1b1778700426b648a41 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Mon, 30 Sep 2024 11:50:22 +0200 Subject: [PATCH 22/24] fix displaying errors for game-settings-form just mount hx-ext in parent element --- templates/game/game_settings_form.html | 14 +------------- templates/index.html | 3 ++- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/templates/game/game_settings_form.html b/templates/game/game_settings_form.html index 20e4fe5..a76c116 100644 --- a/templates/game/game_settings_form.html +++ b/templates/game/game_settings_form.html @@ -9,7 +9,7 @@ hx-target="#home-page" hx-target-4*="#error-section" hx-swap="outerHTML" - class="p-8 rounded shadow-md w-96" + class="w-full max-w-sm p-6 rounded-lg shadow-md" > @@ -115,17 +115,5 @@ field.style.display = "block"; } } - - document.body.addEventListener("htmx:beforeOnLoad", function (evt) { - const xhr = evt.detail.xhr; - - if (xhr.getResponseHeader("HX-Trigger") === "gameStarted") { - const sessionGamesInfo = - document.getElementById("session-games-info"); - if (sessionGamesInfo) { - sessionGamesInfo.remove(); - } - } - }); {{ end }} diff --git a/templates/index.html b/templates/index.html index 6e967bf..8ac88aa 100644 --- a/templates/index.html +++ b/templates/index.html @@ -26,10 +26,11 @@
-
+

Game Settings

From 4ef40b79f49f1c87af867306f62a1e4c60595902 Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Mon, 30 Sep 2024 15:11:45 +0200 Subject: [PATCH 23/24] after finished game => failed | won display whole grid --- dist/main.css | 1 + templates/game/game_grid.html | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dist/main.css b/dist/main.css index e9f634d..c05d367 100644 --- a/dist/main.css +++ b/dist/main.css @@ -2,6 +2,7 @@ background-color: rgba(0, 0, 0, 0.088); transition: background-color 0.2s ease; cursor: pointer; + font-weight: bold; } .cell-revealed { diff --git a/templates/game/game_grid.html b/templates/game/game_grid.html index 297758c..08d8dc2 100644 --- a/templates/game/game_grid.html +++ b/templates/game/game_grid.html @@ -14,19 +14,21 @@
- {{ if $cell.IsFlagged }} - 🚩 - {{ else if $cell.IsRevealed }} + {{ if or $cell.IsRevealed $.GameFailed $.GameWon }} {{ if $cell.HasMine }} 💣 - {{ else if - gt $cell.AdjacentMines 0 - }} + {{ else if gt $cell.AdjacentMines 0 }} {{ $cell.AdjacentMines }} {{ end }} + {{ else if $cell.IsFlagged }} + 🚩 {{ end }}
{{ end }} From aa641e741ef54693a24481d183ef04e15016fc5b Mon Sep 17 00:00:00 2001 From: Oskarowski Date: Mon, 30 Sep 2024 19:51:12 +0200 Subject: [PATCH 24/24] upgrade the / page and add favicon --- dist/icon.png | Bin 0 -> 5731 bytes templates/admin/base_layout.html | 1 + templates/game/game_layout.html | 14 +++-- templates/game/game_settings_form.html | 68 ++++++++++++------------- templates/index.html | 62 +++++++++++++++++++--- 5 files changed, 98 insertions(+), 47 deletions(-) create mode 100644 dist/icon.png diff --git a/dist/icon.png b/dist/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..89d2d412fd8b5f27414731eb50a9fe0a4cc196f2 GIT binary patch literal 5731 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 literal 0 HcmV?d00001 diff --git a/templates/admin/base_layout.html b/templates/admin/base_layout.html index f682c12..8a27db2 100644 --- a/templates/admin/base_layout.html +++ b/templates/admin/base_layout.html @@ -8,6 +8,7 @@ content="width=device-width, initial-scale=1.0" /> Minesweeper in Numbers + {{ end }} diff --git a/templates/index.html b/templates/index.html index 8ac88aa..12a821c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -8,6 +8,7 @@ content="width=device-width, initial-scale=1.0" /> Minesweeper! +