Add data models
This commit is contained in:
107
.github/used-prompts/deploying.md
vendored
107
.github/used-prompts/deploying.md
vendored
@@ -1,107 +0,0 @@
|
|||||||
## Overview
|
|
||||||
|
|
||||||
Deploy `estus-shots` to the personal Fedora server manually—no CI/CD required. The application runs behind Nginx via a reverse proxy and is supervised by `systemd`.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Fedora (server) with SSH access for a user that can `sudo` without a passwordless setup.
|
|
||||||
- Nim toolchain installed locally (for `nim`/`nimble`).
|
|
||||||
- Server already provisioned with Nim runtime dependencies.
|
|
||||||
- Existing Nginx site definition that proxies traffic to the `estus-shots` service port (defaults to `127.0.0.1:9000`).
|
|
||||||
|
|
||||||
## One-time server setup
|
|
||||||
|
|
||||||
1. Create deployment directories on the server:
|
|
||||||
```fish
|
|
||||||
ssh user@server 'sudo mkdir -p /opt/estus-shots/bin /opt/estus-shots/config && sudo chown ${USER}:${USER} /opt/estus-shots -R'
|
|
||||||
```
|
|
||||||
2. Copy the `systemd` unit and enable it:
|
|
||||||
```fish
|
|
||||||
printf '%s\n' "[Unit]" \
|
|
||||||
"Description=estus-shots web service" \
|
|
||||||
"After=network.target" \
|
|
||||||
"" \
|
|
||||||
"[Service]" \
|
|
||||||
"Type=simple" \
|
|
||||||
"ExecStart=/opt/estus-shots/bin/estus-shots" \
|
|
||||||
"Restart=on-failure" \
|
|
||||||
"RestartSec=5" \
|
|
||||||
"User=www-data" \
|
|
||||||
"WorkingDirectory=/opt/estus-shots" \
|
|
||||||
"Environment=LOG_LEVEL=info" \
|
|
||||||
"" \
|
|
||||||
"[Install]" \
|
|
||||||
"WantedBy=multi-user.target" \
|
|
||||||
| ssh user@server 'sudo tee /etc/systemd/system/estus-shots.service'
|
|
||||||
|
|
||||||
ssh user@server 'sudo systemctl daemon-reload && sudo systemctl enable estus-shots.service'
|
|
||||||
```
|
|
||||||
3. Ensure Nginx reverse proxy points to `127.0.0.1:9000` and reload it once configured:
|
|
||||||
```fish
|
|
||||||
ssh user@server 'sudo systemctl restart nginx'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deployment script
|
|
||||||
|
|
||||||
Save the following as `scripts/deploy.fish` (or another preferred location) and make it executable with `chmod +x scripts/deploy.fish`.
|
|
||||||
|
|
||||||
```fish
|
|
||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
set SERVER user@server
|
|
||||||
set TARGET_DIR /opt/estus-shots
|
|
||||||
set BIN_NAME estus-shots
|
|
||||||
|
|
||||||
echo 'Building project locally (release)...'
|
|
||||||
nimble build -y
|
|
||||||
|
|
||||||
echo 'Uploading binary...'
|
|
||||||
scp bin/$BIN_NAME $SERVER:$TARGET_DIR/bin/
|
|
||||||
|
|
||||||
echo 'Uploading static assets...'
|
|
||||||
scp -r src/static $SERVER:$TARGET_DIR/
|
|
||||||
|
|
||||||
echo 'Restarting services...'
|
|
||||||
ssh $SERVER 'sudo systemctl restart estus-shots.service'
|
|
||||||
ssh $SERVER 'sudo systemctl restart nginx'
|
|
||||||
|
|
||||||
echo 'Deployment finished.'
|
|
||||||
```
|
|
||||||
|
|
||||||
> The script prompts for the SSH password and any required `sudo` password on the server.
|
|
||||||
|
|
||||||
## Manual deployment steps
|
|
||||||
|
|
||||||
1. Build the project locally:
|
|
||||||
```fish
|
|
||||||
nimble build -y
|
|
||||||
```
|
|
||||||
2. Copy the new build output and static assets:
|
|
||||||
```fish
|
|
||||||
scp bin/estus-shots user@server:/opt/estus-shots/bin/
|
|
||||||
scp -r src/static user@server:/opt/estus-shots/
|
|
||||||
```
|
|
||||||
3. Restart the service and, if needed, Nginx:
|
|
||||||
```fish
|
|
||||||
ssh user@server 'sudo systemctl restart estus-shots.service'
|
|
||||||
ssh user@server 'sudo systemctl restart nginx'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Check service status:
|
|
||||||
```fish
|
|
||||||
ssh user@server 'systemctl status estus-shots.service --no-pager'
|
|
||||||
```
|
|
||||||
- Review recent logs:
|
|
||||||
```fish
|
|
||||||
ssh user@server 'journalctl -u estus-shots.service -n 50 --no-pager'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting tips
|
|
||||||
|
|
||||||
- If the service fails, run it manually on the server to capture stdout/stderr: `bin/estus-shots`.
|
|
||||||
- Ensure SELinux contexts allow Nginx to proxy (`setsebool -P httpd_can_network_connect 1`).
|
|
||||||
- Validate reverse proxy config: `sudo nginx -t`.
|
|
||||||
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Estus Shots
|
||||||
|
Tracking tool for Dark Souls drinking games.
|
||||||
|
|
||||||
|
## Game Rules
|
||||||
|
- One player actively plays the game.
|
||||||
|
- When a player dies, all must take a drink.
|
||||||
|
- The next player takes over.
|
||||||
|
- If a player beats a boss, the controller passes on, no drinks.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Track game progress across multiple games.
|
||||||
|
- Progress is tracked per game sessions.
|
||||||
|
- A Session is compoesed of multiple Events
|
||||||
|
- Events can be start, end, boss defeated, death, etc.
|
||||||
|
- Statistics are can be viewed for players, games and bosses.
|
||||||
|
- Players can be added, removed and edited.
|
||||||
|
- Games can be added, removed and edited.
|
||||||
|
- Bosses can be added, removed and edited.
|
||||||
|
- A penalty event happens when a player dies.
|
||||||
|
- The penalty event assigns a a dink to a player
|
||||||
|
- A typical penaly would look like this:
|
||||||
|
- 20:24 Player A dies to Boss B
|
||||||
|
- Player A drinks a shot of Drink A
|
||||||
|
- Player B drinks a shot of Drinnk B
|
||||||
|
- Player C drinks a shot of Dink A
|
||||||
|
|
||||||
79
internal/models/models.go
Normal file
79
internal/models/models.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Player represents a participant in the drinking game.
|
||||||
|
type Player struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game represents a Dark Souls game variant.
|
||||||
|
type Game struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boss represents a boss in a Dark Souls game.
|
||||||
|
type Boss struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
GameID int `json:"game_id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drink represents a type of alcoholic beverage.
|
||||||
|
type Drink struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"` // e.g., "beer", "shot", "cocktail"
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventType represents the type of event that occurred during a session.
|
||||||
|
type EventType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventTypeStart EventType = "start"
|
||||||
|
EventTypeEnd EventType = "end"
|
||||||
|
EventTypeBossDefeated EventType = "boss_defeated"
|
||||||
|
EventTypeDeath EventType = "death"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Session represents a game session with multiple players.
|
||||||
|
type Session struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
GameID int `json:"game_id"`
|
||||||
|
StartedAt time.Time `json:"started_at"`
|
||||||
|
EndedAt *time.Time `json:"ended_at,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event represents an event that occurred during a session.
|
||||||
|
type Event struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
SessionID int `json:"session_id"`
|
||||||
|
EventType EventType `json:"event_type"`
|
||||||
|
PlayerID int `json:"player_id"`
|
||||||
|
BossID *int `json:"boss_id,omitempty"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
Notes string `json:"notes,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PenaltyDrink represents a drink consumed as a penalty.
|
||||||
|
type PenaltyDrink struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
EventID int `json:"event_id"`
|
||||||
|
PlayerID int `json:"player_id"`
|
||||||
|
DrinkID int `json:"drink_id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
@@ -111,6 +111,30 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
data := AdminMenuBar()
|
data := AdminMenuBar()
|
||||||
data.Title = "Admin Panel"
|
data.Title = "Admin Panel"
|
||||||
s.renderTemplate(w, http.StatusOK, "admin.html", data)
|
s.renderTemplate(w, http.StatusOK, "admin.html", data)
|
||||||
|
case "/players":
|
||||||
|
data := DefaultMenuBar()
|
||||||
|
data.Title = "Players"
|
||||||
|
s.renderTemplate(w, http.StatusOK, "players.html", data)
|
||||||
|
case "/games":
|
||||||
|
data := DefaultMenuBar()
|
||||||
|
data.Title = "Games"
|
||||||
|
s.renderTemplate(w, http.StatusOK, "games.html", data)
|
||||||
|
case "/bosses":
|
||||||
|
data := DefaultMenuBar()
|
||||||
|
data.Title = "Bosses"
|
||||||
|
s.renderTemplate(w, http.StatusOK, "bosses.html", data)
|
||||||
|
case "/sessions":
|
||||||
|
data := DefaultMenuBar()
|
||||||
|
data.Title = "Sessions"
|
||||||
|
s.renderTemplate(w, http.StatusOK, "sessions.html", data)
|
||||||
|
case "/statistics":
|
||||||
|
data := DefaultMenuBar()
|
||||||
|
data.Title = "Statistics"
|
||||||
|
s.renderTemplate(w, http.StatusOK, "statistics.html", data)
|
||||||
|
case "/drinks":
|
||||||
|
data := DefaultMenuBar()
|
||||||
|
data.Title = "Drinks"
|
||||||
|
s.renderTemplate(w, http.StatusOK, "drinks.html", data)
|
||||||
case "/counter":
|
case "/counter":
|
||||||
s.handleCounter(w, r)
|
s.handleCounter(w, r)
|
||||||
case "/time":
|
case "/time":
|
||||||
|
|||||||
@@ -40,22 +40,22 @@ func DefaultMenuBar() PageData {
|
|||||||
ShowClock: true,
|
ShowClock: true,
|
||||||
MenuGroups: []MenuGroup{
|
MenuGroups: []MenuGroup{
|
||||||
{
|
{
|
||||||
Label: "File",
|
Label: "Game",
|
||||||
Items: []MenuItem{
|
Items: []MenuItem{
|
||||||
{Label: "New", URL: "#!"},
|
{Label: "Home", URL: "/"},
|
||||||
{Label: "Open", URL: "#!"},
|
{Label: "Sessions", URL: "/sessions"},
|
||||||
{Label: "Save", URL: "#!"},
|
{Label: "Statistics", URL: "/statistics"},
|
||||||
{Label: "Save As", URL: "#!"},
|
|
||||||
{IsDivider: true},
|
{IsDivider: true},
|
||||||
{Label: "Exit", URL: "#!"},
|
{Label: "Exit", URL: "#!"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Label: "Edit",
|
Label: "Manage",
|
||||||
Items: []MenuItem{
|
Items: []MenuItem{
|
||||||
{Label: "Cut", URL: "#!"},
|
{Label: "Players", URL: "/players"},
|
||||||
{Label: "Copy", URL: "#!"},
|
{Label: "Games", URL: "/games"},
|
||||||
{Label: "Paste", URL: "#!"},
|
{Label: "Bosses", URL: "/bosses"},
|
||||||
|
{Label: "Drinks", URL: "/drinks"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
41
web/templates/bosses.html
Normal file
41
web/templates/bosses.html
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{{template "layout" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Bosses · Estus Shots{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<fieldset class="tui-fieldset" style="padding: 2rem;">
|
||||||
|
<legend>Bosses Management</legend>
|
||||||
|
<p class="tui-text-white">
|
||||||
|
Manage bosses from Dark Souls games.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Boss List</h2>
|
||||||
|
<div
|
||||||
|
id="boss-list"
|
||||||
|
hx-get="/bosses/list"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading bosses...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Add New Boss</h2>
|
||||||
|
<form hx-post="/bosses" hx-target="#boss-list" hx-swap="innerHTML">
|
||||||
|
<div class="tui-fieldset">
|
||||||
|
<label for="boss-name">Boss Name:</label>
|
||||||
|
<input type="text" id="boss-name" name="name" required class="tui-input" />
|
||||||
|
|
||||||
|
<label for="boss-game" style="margin-top: 1rem;">Game:</label>
|
||||||
|
<select id="boss-game" name="game_id" required class="tui-input">
|
||||||
|
<option value="">Select a game...</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button type="submit" class="tui-button" style="margin-top: 1rem;">Add Boss</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
{{end}}
|
||||||
46
web/templates/drinks.html
Normal file
46
web/templates/drinks.html
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{{template "layout" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Drinks · Estus Shots{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<fieldset class="tui-fieldset" style="padding: 2rem;">
|
||||||
|
<legend>Drinks Management</legend>
|
||||||
|
<p class="tui-text-white">
|
||||||
|
Manage the types of drinks consumed during the game.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Drink List</h2>
|
||||||
|
<div
|
||||||
|
id="drink-list"
|
||||||
|
hx-get="/drinks/list"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading drinks...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Add New Drink</h2>
|
||||||
|
<form hx-post="/drinks" hx-target="#drink-list" hx-swap="innerHTML">
|
||||||
|
<div class="tui-fieldset">
|
||||||
|
<label for="drink-name">Drink Name:</label>
|
||||||
|
<input type="text" id="drink-name" name="name" required class="tui-input" />
|
||||||
|
|
||||||
|
<label for="drink-type" style="margin-top: 1rem;">Type:</label>
|
||||||
|
<select id="drink-type" name="type" required class="tui-input">
|
||||||
|
<option value="">Select type...</option>
|
||||||
|
<option value="beer">Beer</option>
|
||||||
|
<option value="shot">Shot</option>
|
||||||
|
<option value="cocktail">Cocktail</option>
|
||||||
|
<option value="wine">Wine</option>
|
||||||
|
<option value="other">Other</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button type="submit" class="tui-button" style="margin-top: 1rem;">Add Drink</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
{{end}}
|
||||||
35
web/templates/games.html
Normal file
35
web/templates/games.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{{template "layout" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Games · Estus Shots{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<fieldset class="tui-fieldset" style="padding: 2rem;">
|
||||||
|
<legend>Games Management</legend>
|
||||||
|
<p class="tui-text-white">
|
||||||
|
Manage Dark Souls game variants tracked by the system.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Game List</h2>
|
||||||
|
<div
|
||||||
|
id="game-list"
|
||||||
|
hx-get="/games/list"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading games...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Add New Game</h2>
|
||||||
|
<form hx-post="/games" hx-target="#game-list" hx-swap="innerHTML">
|
||||||
|
<div class="tui-fieldset">
|
||||||
|
<label for="game-name">Game Name:</label>
|
||||||
|
<input type="text" id="game-name" name="name" required class="tui-input" />
|
||||||
|
<button type="submit" class="tui-button" style="margin-top: 1rem;">Add Game</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
{{end}}
|
||||||
35
web/templates/players.html
Normal file
35
web/templates/players.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{{template "layout" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Players · Estus Shots{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<fieldset class="tui-fieldset" style="padding: 2rem;">
|
||||||
|
<legend>Players Management</legend>
|
||||||
|
<p class="tui-text-white">
|
||||||
|
Manage players who participate in the drinking game.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Player List</h2>
|
||||||
|
<div
|
||||||
|
id="player-list"
|
||||||
|
hx-get="/players/list"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading players...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Add New Player</h2>
|
||||||
|
<form hx-post="/players" hx-target="#player-list" hx-swap="innerHTML">
|
||||||
|
<div class="tui-fieldset">
|
||||||
|
<label for="player-name">Name:</label>
|
||||||
|
<input type="text" id="player-name" name="name" required class="tui-input" />
|
||||||
|
<button type="submit" class="tui-button" style="margin-top: 1rem;">Add Player</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
{{end}}
|
||||||
50
web/templates/sessions.html
Normal file
50
web/templates/sessions.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{{template "layout" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Sessions · Estus Shots{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<fieldset class="tui-fieldset" style="padding: 2rem;">
|
||||||
|
<legend>Sessions Management</legend>
|
||||||
|
<p class="tui-text-white">
|
||||||
|
View and manage game sessions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Active Sessions</h2>
|
||||||
|
<div
|
||||||
|
id="active-sessions"
|
||||||
|
hx-get="/sessions/active"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading active sessions...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Session History</h2>
|
||||||
|
<div
|
||||||
|
id="session-history"
|
||||||
|
hx-get="/sessions/history"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading session history...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Start New Session</h2>
|
||||||
|
<form hx-post="/sessions" hx-target="#active-sessions" hx-swap="innerHTML">
|
||||||
|
<div class="tui-fieldset">
|
||||||
|
<label for="session-game">Game:</label>
|
||||||
|
<select id="session-game" name="game_id" required class="tui-input">
|
||||||
|
<option value="">Select a game...</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button type="submit" class="tui-button" style="margin-top: 1rem;">Start Session</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
{{end}}
|
||||||
48
web/templates/statistics.html
Normal file
48
web/templates/statistics.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{{template "layout" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Statistics · Estus Shots{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<fieldset class="tui-fieldset" style="padding: 2rem;">
|
||||||
|
<legend>Statistics</legend>
|
||||||
|
<p class="tui-text-white">
|
||||||
|
View statistics for players, games, and bosses.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Player Statistics</h2>
|
||||||
|
<div
|
||||||
|
id="player-stats"
|
||||||
|
hx-get="/statistics/players"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading player statistics...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Boss Statistics</h2>
|
||||||
|
<div
|
||||||
|
id="boss-stats"
|
||||||
|
hx-get="/statistics/bosses"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading boss statistics...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 1.5rem;">
|
||||||
|
<h2 class="tui-text-green">Game Statistics</h2>
|
||||||
|
<div
|
||||||
|
id="game-stats"
|
||||||
|
hx-get="/statistics/games"
|
||||||
|
hx-trigger="load"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<div class="tui-panel">Loading game statistics...</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
{{end}}
|
||||||
Reference in New Issue
Block a user