Feat: stats [WIP]
This commit is contained in:
		| @@ -43,7 +43,7 @@ | |||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
| <div id="ancestor" hx-ext="sse" sse-connect="/sub/sse"> | <div id="ancestor" hx-ext="sse" sse-connect="/sub/sse"> | ||||||
| 	{{template "main" .}} |         {{template "main" .}} | ||||||
| </div> | </div> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -5,9 +5,11 @@ | |||||||
| 		{{template "linklogin" .LinkLogin}} | 		{{template "linklogin" .LinkLogin}} | ||||||
| 	{{ else if not .State.RoomID }} | 	{{ else if not .State.RoomID }} | ||||||
| 		<div id="hello-user" class="grid grid-cols-3 items-center text-xl py-2"> | 		<div id="hello-user" class="grid grid-cols-3 items-center text-xl py-2"> | ||||||
| 			<div></div> |  | ||||||
| 			<p class="text-center">Hello {{.State.Username}}</p> | 			<p class="text-center">Hello {{.State.Username}}</p> | ||||||
| 			<div class="text-right"> | 			<div class="text-right"> | ||||||
|  | 				<a href="/stats" class="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent rounded"> | ||||||
|  | 					Stats | ||||||
|  | 				</a> | ||||||
| 				<a href="/signout"><button class="bg-indigo-600 text-white font-semibold text-sm px-4 py-2 rounded hover:bg-indigo-500">signout</button></a> | 				<a href="/signout"><button class="bg-indigo-600 text-white font-semibold text-sm px-4 py-2 rounded hover:bg-indigo-500">signout</button></a> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								components/stats.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								components/stats.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | {{define "stats"}} | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  | 	<title>Alias</title> | ||||||
|  | 	<script src="/assets/tailwind.css"></script> | ||||||
|  | 	<link rel="stylesheet" href="/assets/style.css"/> | ||||||
|  | 	<script src="/assets/htmx.min.js"></script> | ||||||
|  | 	<script src="/assets/htmx.sse.js"></script> | ||||||
|  | 	<script src="/assets/helpers.js"></script> | ||||||
|  | 	<meta charset="utf-8" name="viewport" content="width=device-width,initial-scale=1"/> | ||||||
|  | 	<link rel="icon" sizes="64x64" href="/assets/favicon/wolfhead_negated.ico"/> | ||||||
|  | 	<style type="text/css"> | ||||||
|  | 		body{ | ||||||
|  |             background-color: #0C1616FF; | ||||||
|  |             color: #8896b2; | ||||||
|  |             max-width: 1000px; | ||||||
|  |             min-width: 0; | ||||||
|  |             margin: 2em auto !important; | ||||||
|  |             margin-left: auto; | ||||||
|  |             margin-right: auto; | ||||||
|  |             line-height: 1.5; | ||||||
|  |             font-size: 16px; | ||||||
|  |             font-family: Open Sans,Arial; | ||||||
|  |             text-align: center; | ||||||
|  |             display: block; | ||||||
|  |         } | ||||||
|  |         a{ | ||||||
|  |             color: #00a2e7; | ||||||
|  |         } | ||||||
|  |         a:visited{ | ||||||
|  |             color: #ca1a70; | ||||||
|  |         } | ||||||
|  |         table { | ||||||
|  |           border-collapse: separate !important; | ||||||
|  |           border-spacing: 10px 10px; | ||||||
|  |           border: 1px solid white; | ||||||
|  |         } | ||||||
|  |         tr{ | ||||||
|  |             border: 1px solid white; | ||||||
|  |         } | ||||||
|  | 	</style> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | <div id="ancestor"> | ||||||
|  | <div class="container mx-auto p-4"> | ||||||
|  |     <h1 class="text-2xl font-bold mb-4">Player Leaderboard</h1> | ||||||
|  |     <div class="mb-4"> | ||||||
|  |         <a href="/" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> | ||||||
|  |             Home | ||||||
|  |         </a> | ||||||
|  |     </div> | ||||||
|  |     <div class="overflow-x-auto"> | ||||||
|  |         <table class="min-w-full bg-white"> | ||||||
|  |             <thead class="bg-gray-800 text-white"> | ||||||
|  |                 <tr> | ||||||
|  |                     <th class="py-2 px-4">Player</th> | ||||||
|  |                     <th class="py-2 px-4">Games Played</th> | ||||||
|  |                     <th class="py-2 px-4">Games Won</th> | ||||||
|  |                     <th class="py-2 px-4">Games Lost</th> | ||||||
|  |                     <th class="py-2 px-4">Mime Winrate</th> | ||||||
|  |                     <th class="py-2 px-4">Guesser Winrate</th> | ||||||
|  |                 </tr> | ||||||
|  |             </thead> | ||||||
|  |             <tbody class="text-gray-700"> | ||||||
|  |                 {{range .}} | ||||||
|  |                 <tr> | ||||||
|  |                     <td class="py-2 px-4 border">{{.PlayerUsername}}</td> | ||||||
|  |                     <td class="py-2 px-4 border">{{.GamesPlayed}}</td> | ||||||
|  |                     <td class="py-2 px-4 border">{{.GamesWon}}</td> | ||||||
|  |                     <td class="py-2 px-4 border">{{.GamesLost}}</td> | ||||||
|  |                     <td class="py-2 px-4 border">{{printf "%.2f" .MimeWinrate}}</td> | ||||||
|  |                     <td class="py-2 px-4 border">{{printf "%.2f" .GuesserWinrate}}</td> | ||||||
|  |                 </tr> | ||||||
|  |                 {{end}} | ||||||
|  |             </tbody> | ||||||
|  |         </table> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  | </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
|  | {{end}} | ||||||
| @@ -113,3 +113,42 @@ func HandleExit(w http.ResponseWriter, r *http.Request) { | |||||||
| 		log.Error("failed to exec templ;", "error", err, "templ", "base") | 		log.Error("failed to exec templ;", "error", err, "templ", "base") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func HandleStats(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	log.Debug("got stats call") | ||||||
|  | 	tmpl, err := template.ParseGlob("components/*.html") | ||||||
|  | 	if err != nil { | ||||||
|  | 		abortWithError(w, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	stats, err := repo.GetAllPlayerStats(r.Context()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("failed to get all player stats", "error", err) | ||||||
|  | 		abortWithError(w, "failed to retrieve player stats") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	fi, err := getFullInfoByCtx(r.Context()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("failed to fetch fi", "error", err) | ||||||
|  | 	} | ||||||
|  | 	// there must be a better way | ||||||
|  | 	if fi != nil && fi.Room != nil && fi.Room.ID != "" && fi.State != nil { | ||||||
|  | 		fi.Room.UpdateCounter() | ||||||
|  | 		if fi.State.Role == "mime" { | ||||||
|  | 			fi.Room.MimeView() // there must be a better way | ||||||
|  | 		} else { | ||||||
|  | 			fi.Room.GuesserView() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if fi != nil && fi.Room == nil { | ||||||
|  | 		rooms, err := repo.RoomList(r.Context()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("failed to list rooms;", "error", err) | ||||||
|  | 		} | ||||||
|  | 		fi.List = rooms | ||||||
|  | 	} | ||||||
|  | 	fi.List = nil | ||||||
|  | 	if err := tmpl.ExecuteTemplate(w, "stats", stats); err != nil { | ||||||
|  | 		log.Error("failed to exec templ;", "error", err, "templ", "base") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								main.go
									
									
									
									
									
								
							| @@ -68,6 +68,7 @@ func ListenToRequests(port string) *http.Server { | |||||||
| 	// | 	// | ||||||
| 	mux.HandleFunc("GET /ping", handlers.HandlePing) | 	mux.HandleFunc("GET /ping", handlers.HandlePing) | ||||||
| 	mux.HandleFunc("GET /", handlers.HandleHome) | 	mux.HandleFunc("GET /", handlers.HandleHome) | ||||||
|  | 	mux.HandleFunc("GET /stats", handlers.HandleStats) | ||||||
| 	mux.HandleFunc("POST /login", handlers.HandleFrontLogin) | 	mux.HandleFunc("POST /login", handlers.HandleFrontLogin) | ||||||
| 	mux.HandleFunc("GET /signout", handlers.HandleSignout) | 	mux.HandleFunc("GET /signout", handlers.HandleSignout) | ||||||
| 	mux.HandleFunc("POST /join-team", handlers.HandleJoinTeam) | 	mux.HandleFunc("POST /join-team", handlers.HandleJoinTeam) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
|  |  | ||||||
| type PlayerStatsRepo interface { | type PlayerStatsRepo interface { | ||||||
| 	GetPlayerStats(ctx context.Context, username string) (*models.PlayerStats, error) | 	GetPlayerStats(ctx context.Context, username string) (*models.PlayerStats, error) | ||||||
|  | 	GetAllPlayerStats(ctx context.Context) ([]*models.PlayerStats, error) | ||||||
| 	UpdatePlayerStats(ctx context.Context, stats *models.PlayerStats) error | 	UpdatePlayerStats(ctx context.Context, stats *models.PlayerStats) error | ||||||
| 	CreatePlayerStats(ctx context.Context, username string) error | 	CreatePlayerStats(ctx context.Context, username string) error | ||||||
| } | } | ||||||
| @@ -19,6 +20,12 @@ func (p *RepoProvider) GetPlayerStats(ctx context.Context, username string) (*mo | |||||||
| 	return stats, err | 	return stats, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (p *RepoProvider) GetAllPlayerStats(ctx context.Context) ([]*models.PlayerStats, error) { | ||||||
|  | 	var stats []*models.PlayerStats | ||||||
|  | 	err := sqlx.SelectContext(ctx, p.DB, &stats, "SELECT * FROM player_stats ORDER BY games_won DESC") | ||||||
|  | 	return stats, err | ||||||
|  | } | ||||||
|  |  | ||||||
| func (p *RepoProvider) UpdatePlayerStats(ctx context.Context, stats *models.PlayerStats) error { | func (p *RepoProvider) UpdatePlayerStats(ctx context.Context, stats *models.PlayerStats) error { | ||||||
| 	_, err := p.DB.NamedExecContext(ctx, `UPDATE player_stats SET | 	_, err := p.DB.NamedExecContext(ctx, `UPDATE player_stats SET | ||||||
| 		games_played = :games_played, | 		games_played = :games_played, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder