design changes, refactor

This commit is contained in:
Radek Davidek 2025-12-28 19:16:00 +01:00
parent 356cd757f4
commit 4a00eeb3a4
7 changed files with 107 additions and 46 deletions

View File

@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.darts.stats</groupId> <groupId>cz.kamma.darts</groupId>
<artifactId>darts-stats-web</artifactId> <artifactId>darts-stats-web</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
@ -55,7 +55,7 @@
</descriptorRefs> </descriptorRefs>
<archive> <archive>
<manifest> <manifest>
<mainClass>com.darts.stats.App</mainClass> <mainClass>cz.kamma.darts.App</mainClass>
</manifest> </manifest>
</archive> </archive>
</configuration> </configuration>

View File

@ -1,4 +1,4 @@
package com.darts.stats; package cz.kamma.darts;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;

View File

@ -1,4 +1,4 @@
package com.darts.stats; package cz.kamma.darts;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;

View File

@ -1,4 +1,4 @@
package com.darts.stats; package cz.kamma.darts;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
@ -16,6 +16,13 @@ public class RawGame {
private List<RawThrow> throwsList; private List<RawThrow> throwsList;
} }
@Data
public static class RawPlayerThrow {
private int value;
private boolean isBust;
private String label;
}
@Data @Data
public static class RawThrow { public static class RawThrow {
private int value; private int value;

View File

@ -1,4 +1,4 @@
package com.darts.stats; package cz.kamma.darts;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;

View File

@ -7,12 +7,13 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style> <style>
:root { :root {
--primary: #2563eb; --primary: #22c55e; /* Green */
--bg: #f8fafc; --primary-hover: #16a34a;
--card-bg: #ffffff; --bg: #0f172a; /* Dark slate */
--text-main: #1e293b; --card-bg: #1e293b;
--text-muted: #64748b; --text-main: #f8fafc;
--border: #e2e8f0; --text-muted: #94a3b8;
--border: #334155;
} }
body { body {
font-family: 'Inter', system-ui, -apple-system, sans-serif; font-family: 'Inter', system-ui, -apple-system, sans-serif;
@ -23,13 +24,13 @@
line-height: 1.5; line-height: 1.5;
} }
.container { max-width: 1200px; margin: auto; } .container { max-width: 1200px; margin: auto; }
h1 { font-size: 2rem; font-weight: 800; margin-bottom: 2rem; display: flex; align-items: center; gap: 10px; } h1 { font-size: 2rem; font-weight: 800; margin-bottom: 2rem; display: flex; align-items: center; gap: 10px; color: var(--primary); }
.card { .card {
background: var(--card-bg); background: var(--card-bg);
padding: 24px; padding: 24px;
border-radius: 12px; border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1); box-shadow: 0 4px 6px -1px rgba(0,0,0,0.2);
margin-bottom: 24px; margin-bottom: 24px;
border: 1px solid var(--border); border: 1px solid var(--border);
} }
@ -39,23 +40,24 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
background: #eff6ff; background: #14532d33; /* Dark green tint */
border: 1px dashed var(--primary); border: 1px dashed var(--primary);
} }
input[type="file"] { input[type="file"] {
font-size: 0.9rem; font-size: 0.9rem;
color: var(--text-muted);
} }
button { button {
background: var(--primary); background: var(--primary);
color: white; color: #052e16; /* Dark green text for contrast */
border: none; border: none;
padding: 8px 16px; padding: 8px 16px;
border-radius: 6px; border-radius: 6px;
font-weight: 600; font-weight: 700;
cursor: pointer; cursor: pointer;
transition: opacity 0.2s; transition: all 0.2s;
} }
button:hover { opacity: 0.9; } button:hover { background: var(--primary-hover); transform: translateY(-1px); }
.stats-grid { .stats-grid {
display: grid; display: grid;
@ -70,9 +72,9 @@
gap: 16px; gap: 16px;
} }
.player-header { .player-header {
font-size: 1.25rem; font-size: 1.4rem;
font-weight: 700; font-weight: 800;
border-bottom: 2px solid var(--bg); border-bottom: 2px solid var(--border);
padding-bottom: 8px; padding-bottom: 8px;
color: var(--primary); color: var(--primary);
} }
@ -81,23 +83,26 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 8px 0; padding: 8px 0;
border-bottom: 1px solid #33415544;
} }
.stat-label { color: var(--text-muted); font-size: 0.9rem; font-weight: 500; } .stat-label { color: var(--text-muted); font-size: 0.9rem; font-weight: 500; }
.stat-value { font-size: 1.1rem; font-weight: 700; color: var(--text-main); } .stat-value { font-size: 1.1rem; font-weight: 700; color: var(--text-main); }
table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 0.95rem; } table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 0.95rem; }
th { th {
background-color: var(--bg); background-color: #0f172a;
padding: 12px; padding: 12px;
text-align: left; text-align: left;
font-weight: 600; font-weight: 600;
color: var(--text-muted); color: var(--primary);
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
transition: background 0.2s; transition: background 0.2s;
border-bottom: 2px solid var(--border);
} }
th:hover { background-color: var(--border); } th:hover { background-color: var(--border); }
td { padding: 12px; border-bottom: 1px solid var(--border); } td { padding: 12px; border-bottom: 1px solid var(--border); color: var(--text-main); }
tr:hover td { background-color: #ffffff05; }
tr:last-child td { border-bottom: none; } tr:last-child td { border-bottom: none; }
.badge { .badge {
@ -107,10 +112,40 @@
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
} }
.badge-win { background: #dcfce7; color: #166534; } .badge-win { background: #064e3b; color: #34d399; }
.badge-loss { background: #fee2e2; color: #991b1b; } .badge-loss { background: #7f1d1d; color: #fca5a5; }
.chart-container { position: relative; height: 350px; width: 100%; } .chart-container { position: relative; height: 350px; width: 100%; }
/* Responsive adjustments */
@media (max-width: 600px) {
body { padding: 12px; }
h1 { font-size: 1.5rem; margin-bottom: 1.5rem; }
.card { padding: 16px; margin-bottom: 16px; }
.upload-section {
flex-direction: column;
align-items: stretch;
text-align: center;
}
.stats-grid {
grid-template-columns: 1fr;
}
.table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
margin: 0 -16px;
padding: 0 16px;
}
table {
min-width: 600px; /* Force scroll on small screens */
}
.chart-container { height: 250px; }
}
</style> </style>
</head> </head>
<body> <body>
@ -137,20 +172,22 @@
<div class="card"> <div class="card">
<h2>Recent Games</h2> <h2>Recent Games</h2>
<table id="games-table"> <div class="table-container">
<thead> <table id="games-table">
<tr> <thead>
<th onclick="sortTable(0)">Date ↕</th> <tr>
<th onclick="sortTable(1)">Player ↕</th> <th onclick="sortTable(0)">Date ↕</th>
<th onclick="sortTable(2)">Opponent ↕</th> <th onclick="sortTable(1)">Player ↕</th>
<th onclick="sortTable(3)">Avg ↕</th> <th onclick="sortTable(2)">Opponent ↕</th>
<th onclick="sortTable(4)">Darts ↕</th> <th onclick="sortTable(3)">Avg ↕</th>
<th onclick="sortTable(5)">Round ↕</th> <th onclick="sortTable(4)">Darts ↕</th>
<th onclick="sortTable(6)">Result ↕</th> <th onclick="sortTable(5)">Round ↕</th>
</tr> <th onclick="sortTable(6)">Result ↕</th>
</thead> </tr>
<tbody></tbody> </thead>
</table> <tbody></tbody>
</table>
</div>
</div> </div>
</div> </div>
@ -230,19 +267,23 @@
const players = [...new Set(games.map(g => g.player))]; const players = [...new Set(games.map(g => g.player))];
const dates = [...new Set(games.map(g => g.date))].sort((a, b) => new Date(a) - new Date(b)); const dates = [...new Set(games.map(g => g.date))].sort((a, b) => new Date(a) - new Date(b));
const datasets = players.map(player => { const datasets = players.map((player, index) => {
const playerGames = games.filter(g => g.player === player); const playerGames = games.filter(g => g.player === player);
const data = dates.map(date => { const data = dates.map(date => {
const game = playerGames.find(g => g.date === date); const game = playerGames.find(g => g.date === date);
return game ? game.average : null; return game ? game.average : null;
}); });
const colors = ['#22c55e', '#3b82f6', '#a855f7', '#f59e0b'];
const color = colors[index % colors.length];
return { return {
label: player, label: player,
data: data, data: data,
borderColor: '#' + Math.floor(Math.random()*16777215).toString(16), borderColor: color,
fill: false, backgroundColor: color + '22',
tension: 0.1, fill: true,
tension: 0.3,
spanGaps: true spanGaps: true
}; };
}); });
@ -256,8 +297,20 @@
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
plugins: {
legend: { labels: { color: '#f8fafc', font: { weight: '600' } } }
},
scales: { scales: {
y: { beginAtZero: false, title: { display: true, text: '3-Dart Average' } } y: {
beginAtZero: false,
grid: { color: '#334155' },
ticks: { color: '#94a3b8' },
title: { display: true, text: '3-Dart Average', color: '#94a3b8' }
},
x: {
grid: { color: '#334155' },
ticks: { color: '#94a3b8' }
}
} }
} }
}); });

1
stats.json Normal file

File diff suppressed because one or more lines are too long