diff --git a/logs/pickleball-error.log b/logs/pickleball-error.log index ceb6c2d..51db281 100644 --- a/logs/pickleball-error.log +++ b/logs/pickleball-error.log @@ -720,3 +720,4 @@ called `Result::unwrap()` on an `Err` value: Os { code: 48, kind: AddrInUse, mes note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Migration error: error returned from database: (code: 1) no such column: rating Migration error: error returned from database: (code: 1) no such column: rating +Migration error: error returned from database: (code: 1) no such column: rating diff --git a/logs/pickleball.log b/logs/pickleball.log index 22ee3bb..1fff33d 100644 --- a/logs/pickleball.log +++ b/logs/pickleball.log @@ -1081,3 +1081,16 @@ Starting Pickleball ELO Tracker Server on port 3000... ➕ Add Player: http://localhost:3000/players/new 🎾 Record Match: http://localhost:3000/matches/new +🏓 Pickleball ELO Tracker v3.0 +============================== + +Starting Pickleball ELO Tracker Server on port 3000... + +✅ Server running at http://localhost:3000 +📊 Leaderboard: http://localhost:3000/leaderboard +📜 Match History: http://localhost:3000/matches +👥 Players: http://localhost:3000/players +⚖️ Team Balancer: http://localhost:3000/balance +➕ Add Player: http://localhost:3000/players/new +🎾 Record Match: http://localhost:3000/matches/new + diff --git a/pickleball-elo b/pickleball-elo index 31c52a2..9100563 100755 Binary files a/pickleball-elo and b/pickleball-elo differ diff --git a/src/main.rs b/src/main.rs index ccf4ecf..8a6ef77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -205,6 +205,8 @@ struct PlayerData { email: String, rating_display: String, has_email: bool, + wins: i64, + losses: i64, } #[derive(Template, Clone, Debug)] @@ -1578,16 +1580,30 @@ async fn create_match( /// /// **Returns:** HTML page with players table async fn players_list_handler(State(state): State) -> impl IntoResponse { - // UNIFIED RATING: use rating field (merged singles/doubles) - let players: Vec<(i64, String, Option, f64)> = sqlx::query_as( - "SELECT id, name, email, rating FROM players ORDER BY rating DESC" + // UNIFIED RATING: use rating field (merged singles/doubles) with W-L record + let players: Vec<(i64, String, Option, f64, i64, i64)> = sqlx::query_as( + r#"SELECT + p.id, p.name, p.email, p.rating, + COALESCE(SUM(CASE WHEN + (mp.team = 1 AND m.team1_score > m.team2_score) OR + (mp.team = 2 AND m.team2_score > m.team1_score) + THEN 1 ELSE 0 END), 0) as wins, + COALESCE(SUM(CASE WHEN + (mp.team = 1 AND m.team1_score < m.team2_score) OR + (mp.team = 2 AND m.team2_score < m.team1_score) + THEN 1 ELSE 0 END), 0) as losses + FROM players p + LEFT JOIN match_participants mp ON p.id = mp.player_id + LEFT JOIN matches m ON mp.match_id = m.id + GROUP BY p.id + ORDER BY p.rating DESC"# ) .fetch_all(&state.pool) .await .unwrap_or_default(); let players = players.into_iter() - .map(|(id, name, email, rating)| { + .map(|(id, name, email, rating, wins, losses)| { let has_email = email.is_some(); PlayerData { id, @@ -1597,6 +1613,8 @@ async fn players_list_handler(State(state): State) -> impl IntoRespons email: email.unwrap_or_default(), rating_display: format!("{:.1}", rating), has_email, + wins, + losses, } }) .collect(); @@ -1614,11 +1632,22 @@ async fn players_list_handler(State(state): State) -> impl IntoRespons /// /// **Returns:** HTML page with unified leaderboard async fn leaderboard_handler(State(state): State) -> impl IntoResponse { - // UNIFIED RATING: Single leaderboard using rating field - let players: Vec<(i64, String, f64, Option)> = sqlx::query_as( - r#"SELECT DISTINCT p.id, p.name, p.rating, p.email + // UNIFIED RATING: Single leaderboard using rating field with W-L record + let players: Vec<(i64, String, f64, Option, i64, i64)> = sqlx::query_as( + r#"SELECT + p.id, p.name, p.rating, p.email, + COALESCE(SUM(CASE WHEN + (mp.team = 1 AND m.team1_score > m.team2_score) OR + (mp.team = 2 AND m.team2_score > m.team1_score) + THEN 1 ELSE 0 END), 0) as wins, + COALESCE(SUM(CASE WHEN + (mp.team = 1 AND m.team1_score < m.team2_score) OR + (mp.team = 2 AND m.team2_score < m.team1_score) + THEN 1 ELSE 0 END), 0) as losses FROM players p - JOIN match_participants mp ON p.id = mp.player_id + LEFT JOIN match_participants mp ON p.id = mp.player_id + LEFT JOIN matches m ON mp.match_id = m.id + GROUP BY p.id ORDER BY p.rating DESC"# ) .fetch_all(&state.pool) @@ -1626,7 +1655,7 @@ async fn leaderboard_handler(State(state): State) -> impl IntoResponse .unwrap_or_default(); let leaderboard = players.into_iter().enumerate() - .map(|(i, (id, name, rating, email))| { + .map(|(i, (id, name, rating, email, wins, losses))| { let has_email = email.is_some(); let player_data = PlayerData { id, @@ -1636,6 +1665,8 @@ async fn leaderboard_handler(State(state): State) -> impl IntoResponse email: email.unwrap_or_default(), rating_display: format!("{:.1}", rating), has_email, + wins, + losses, }; ((i + 1) as i32, player_data) }) diff --git a/templates/components/player_card.html b/templates/components/player_card.html index 90a33d7..06c8a15 100644 --- a/templates/components/player_card.html +++ b/templates/components/player_card.html @@ -2,12 +2,23 @@

{{ player.name }}

-
{{ player.singles_rating | round(1) }}
- {% if player.email %} -

📧 {{ player.email }}

+
+
+
Rating
+
{{ player.rating_display }}
+
+
+
Record
+
+ {{ player.wins }}-{{ player.losses }} +
+
+
+ {% if player.has_email %} +

📧 {{ player.email }}

{% endif %} - diff --git a/templates/pages/player.html b/templates/pages/player.html index 9d78c57..2dcef13 100644 --- a/templates/pages/player.html +++ b/templates/pages/player.html @@ -17,11 +17,17 @@ ✏️ Edit -
+
ELO Rating
{{ player.rating_display }}
+
+
Record
+
+ {{ player.wins }}-{{ player.losses }} +
+
Matches Played
{{ match_count }}
diff --git a/templates/pages/players.html b/templates/pages/players.html index fe4598d..f3461ed 100644 --- a/templates/pages/players.html +++ b/templates/pages/players.html @@ -18,13 +18,24 @@

{{ player.name }}

-
{{ player.rating_display }}
+
+
+
Rating
+
{{ player.rating_display }}
+
+
+
Record
+
+ {{ player.wins }}-{{ player.losses }} +
+
+
{% if player.has_email %} -

📧 {{ player.email }}

+

📧 {{ player.email }}

{% endif %} - {% endfor %}