From e7b35cd81f2e515371a30a59ea4173a31dbeefc5 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 6 Sep 2025 01:09:20 +0900 Subject: feat(backend): add admin page for game creation --- backend/admin/handler.go | 149 ++++++++++++++++++++++++--------- backend/admin/templates/game_edit.html | 8 +- backend/admin/templates/game_new.html | 40 +++++++++ backend/admin/templates/games.html | 3 + backend/db/query.sql.go | 27 ++++++ backend/query.sql | 5 ++ 6 files changed, 191 insertions(+), 41 deletions(-) create mode 100644 backend/admin/templates/game_new.html (limited to 'backend') diff --git a/backend/admin/handler.go b/backend/admin/handler.go index 5a0f0a1..19e44e6 100644 --- a/backend/admin/handler.go +++ b/backend/admin/handler.go @@ -62,6 +62,8 @@ func (h *Handler) RegisterHandlers(g *echo.Group) { g.POST("/users/:userID/fetch-icon", h.postUserFetchIcon) g.GET("/games", h.getGames) + g.GET("/games/new", h.getGameNew) + g.POST("/games/new", h.postGameNew) g.GET("/games/:gameID", h.getGameEdit) g.POST("/games/:gameID", h.postGameEdit) g.POST("/games/:gameID/start", h.postGameStart) @@ -80,6 +82,44 @@ func (h *Handler) getDashboard(c echo.Context) error { }) } +func (h *Handler) getOnlineQualifyingRanking(c echo.Context) error { + game1, err := strconv.Atoi(c.QueryParam("game_1")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid game_1") + } + game2, err := strconv.Atoi(c.QueryParam("game_2")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid game_2") + } + + rows, err := h.q.GetQualifyingRanking(c.Request().Context(), db.GetQualifyingRankingParams{ + GameID: int32(game1), + GameID_2: int32(game2), + }) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + entries := make([]echo.Map, len(rows)) + for i, r := range rows { + entries[i] = echo.Map{ + "Rank": i + 1, + "Username": r.Username, + "UserLabel": r.UserLabel, + "Score1": r.CodeSize1, + "Score2": r.CodeSize2, + "TotalScore": r.TotalCodeSize, + "SubmittedAt1": r.SubmittedAt1.Time.In(jst).Format("2006-01-02T15:04"), + "SubmittedAt2": r.SubmittedAt2.Time.In(jst).Format("2006-01-02T15:04"), + } + } + return c.Render(http.StatusOK, "online_qualifying_ranking", echo.Map{ + "BasePath": h.conf.BasePath, + "Title": "Online Qualifying Ranking", + "Entries": entries, + }) +} + func (h *Handler) getUsers(c echo.Context) error { rows, err := h.q.ListUsers(c.Request().Context()) if err != nil { @@ -215,6 +255,60 @@ func (h *Handler) getGames(c echo.Context) error { }) } +func (h *Handler) getGameNew(c echo.Context) error { + problemRows, err := h.q.ListProblems(c.Request().Context()) + if err != nil { + if !errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + } + var problems []echo.Map + for _, p := range problemRows { + problems = append(problems, echo.Map{ + "ProblemID": int(p.ProblemID), + "Title": p.Title, + }) + } + + return c.Render(http.StatusOK, "game_new", echo.Map{ + "BasePath": h.conf.BasePath, + "Title": "New Game", + "Problems": problems, + }) +} + +func (h *Handler) postGameNew(c echo.Context) error { + gameType := c.FormValue("game_type") + isPublic := (c.FormValue("is_public") != "") + displayName := c.FormValue("display_name") + durationSeconds, err := strconv.Atoi(c.FormValue("duration_seconds")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid duration_seconds") + } + var problemID int + { + problemIDRaw := c.FormValue("problem_id") + problemIDInt, err := strconv.Atoi(problemIDRaw) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid problem_id") + } + problemID = problemIDInt + } + + _, err = h.q.CreateGame(c.Request().Context(), db.CreateGameParams{ + GameType: gameType, + IsPublic: isPublic, + DisplayName: displayName, + DurationSeconds: int32(durationSeconds), + ProblemID: int32(problemID), + }) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/games") +} + func (h *Handler) getGameEdit(c echo.Context) error { gameID, err := strconv.Atoi(c.Param("gameID")) if err != nil { @@ -248,6 +342,20 @@ func (h *Handler) getGameEdit(c echo.Context) error { mainPlayer2 = int(mainPlayerRows[1].UserID) } + problemRows, err := h.q.ListProblems(c.Request().Context()) + if err != nil { + if !errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + } + var problems []echo.Map + for _, p := range problemRows { + problems = append(problems, echo.Map{ + "ProblemID": int(p.ProblemID), + "Title": p.Title, + }) + } + userRows, err := h.q.ListUsers(c.Request().Context()) if err != nil { if !errors.Is(err, pgx.ErrNoRows) { @@ -276,7 +384,8 @@ func (h *Handler) getGameEdit(c echo.Context) error { "MainPlayer1": mainPlayer1, "MainPlayer2": mainPlayer2, }, - "Users": users, + "Problems": problems, + "Users": users, }) } @@ -398,44 +507,6 @@ func (h *Handler) postGameStart(c echo.Context) error { return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/games") } -func (h *Handler) getOnlineQualifyingRanking(c echo.Context) error { - game1, err := strconv.Atoi(c.QueryParam("game_1")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid game_1") - } - game2, err := strconv.Atoi(c.QueryParam("game_2")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid game_2") - } - - rows, err := h.q.GetQualifyingRanking(c.Request().Context(), db.GetQualifyingRankingParams{ - GameID: int32(game1), - GameID_2: int32(game2), - }) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - - entries := make([]echo.Map, len(rows)) - for i, r := range rows { - entries[i] = echo.Map{ - "Rank": i + 1, - "Username": r.Username, - "UserLabel": r.UserLabel, - "Score1": r.CodeSize1, - "Score2": r.CodeSize2, - "TotalScore": r.TotalCodeSize, - "SubmittedAt1": r.SubmittedAt1.Time.In(jst).Format("2006-01-02T15:04"), - "SubmittedAt2": r.SubmittedAt2.Time.In(jst).Format("2006-01-02T15:04"), - } - } - return c.Render(http.StatusOK, "online_qualifying_ranking", echo.Map{ - "BasePath": h.conf.BasePath, - "Title": "Online Qualifying Ranking", - "Entries": entries, - }) -} - func (h *Handler) getProblems(c echo.Context) error { rows, err := h.q.ListProblems(c.Request().Context()) if err != nil { diff --git a/backend/admin/templates/game_edit.html b/backend/admin/templates/game_edit.html index 2d769c4..b171343 100644 --- a/backend/admin/templates/game_edit.html +++ b/backend/admin/templates/game_edit.html @@ -34,8 +34,12 @@
- - + +
diff --git a/backend/admin/templates/game_new.html b/backend/admin/templates/game_new.html new file mode 100644 index 0000000..3e3210a --- /dev/null +++ b/backend/admin/templates/game_new.html @@ -0,0 +1,40 @@ +{{ template "base.html" . }} + +{{ define "breadcrumb" }} +Dashboard | Games +{{ end }} + +{{ define "content" }} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+{{ end }} diff --git a/backend/admin/templates/games.html b/backend/admin/templates/games.html index b5c512a..402c702 100644 --- a/backend/admin/templates/games.html +++ b/backend/admin/templates/games.html @@ -5,6 +5,9 @@ {{ end }} {{ define "content" }} +
+ Create New Game +