diff --git a/public/css/shared.css b/public/css/shared.css index fc70220..98c4fdf 100644 --- a/public/css/shared.css +++ b/public/css/shared.css @@ -72,6 +72,9 @@ --shadow-overlay: 0 20px 60px rgba(15, 23, 42, 0.15), 0 4px 12px rgba(15, 23, 42, 0.08); --transition: 150ms cubic-bezier(0.4, 0, 0.2, 1); + + /* Topbar dimensions (used by sticky thead) */ + --topbar-height: 64px; } /* ── Reset & Base ─────────────────────────────── */ @@ -279,6 +282,7 @@ body { } @media (max-width: 880px) { + :root { --topbar-height: 56px; } .topbar__inner { padding: 10px 16px; gap: 10px; } .topbar__meta-item, .topbar__divider { display: none; } .topbar__refresh-label { display: none; } @@ -322,7 +326,7 @@ table { thead { position: sticky; - top: 56px; + top: var(--topbar-height); z-index: 10; } @@ -547,7 +551,7 @@ a:hover { } thead { - top: 56px; + top: var(--topbar-height); } } diff --git a/server.js b/server.js index 7747255..db7d620 100644 --- a/server.js +++ b/server.js @@ -14,6 +14,7 @@ app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views/pages')); app.use(express.static('public')); app.use(express.json()); +app.use(express.urlencoded({ extended: false })); app.use(playerRoutes); app.use(courseRoutes); diff --git a/src/models/player.js b/src/models/player.js index 9de3049..758061c 100644 --- a/src/models/player.js +++ b/src/models/player.js @@ -185,15 +185,17 @@ function savePredictedRatingToDB(pdgaNumber, predictedRating, stdDev = null) { }); } -function getLastRefresh(callback) { - db.get( - 'SELECT MAX(last_updated) AS lastRefresh FROM players', - [], - (err, row) => { - if (err) return callback(err); - callback(null, row ? row.lastRefresh : null); - } - ); +function getLastRefresh() { + return new Promise((resolve, reject) => { + db.get( + 'SELECT MAX(last_updated) AS lastRefresh FROM players', + [], + (err, row) => { + if (err) reject(err); + else resolve(row ? row.lastRefresh : null); + } + ); + }); } module.exports = { diff --git a/src/routes/players.js b/src/routes/players.js index b9e3c86..59abd72 100644 --- a/src/routes/players.js +++ b/src/routes/players.js @@ -12,21 +12,26 @@ const logger = require('../logger'); let refreshInProgress = false; -router.post('/api/refresh-all', async (req, res) => { +router.post('/api/refresh-all', async (req, res, next) => { if (refreshInProgress) { logger.info('refresh-all already in progress, rejecting'); return res.status(409).json({ error: 'Refresh already in progress' }); } refreshInProgress = true; try { - await refreshAllPlayersInDB(); + try { + await refreshAllPlayersInDB(); + } catch (err) { + logger.error({ err }, 'refresh-all failed'); + } + const page = req.body?.page === 'courses' ? 'courses' : 'players'; + const locals = await getTopbarLocals(); + res.render('../partials/topbar', { activePage: page, ...locals }); } catch (err) { - logger.error({ err }, 'refresh-all failed'); + next(err); } finally { refreshInProgress = false; } - const locals = await getTopbarLocals(); - res.render('../partials/topbar', { activePage: req.query.page || 'players', ...locals }); }); router.get('/partials/ratings-table', async (req, res) => { diff --git a/src/services/topbar-service.js b/src/services/topbar-service.js index b1a98b7..e050667 100644 --- a/src/services/topbar-service.js +++ b/src/services/topbar-service.js @@ -32,9 +32,7 @@ function computeNextUpdate(now = new Date()) { async function getTopbarLocals() { try { - const lastIso = await new Promise((resolve, reject) => { - getLastRefresh((err, val) => (err ? reject(err) : resolve(val))); - }); + const lastIso = await getLastRefresh(); return { lastRefresh: formatRelative(lastIso), nextUpdate: computeNextUpdate() }; } catch (err) { logger.warn({ err }, 'topbar locals fallback'); @@ -42,4 +40,4 @@ async function getTopbarLocals() { } } -module.exports = { getTopbarLocals, formatRelative, computeNextUpdate }; +module.exports = { getTopbarLocals }; diff --git a/views/pages/courses.ejs b/views/pages/courses.ejs index 0fe9cc3..c994b10 100644 --- a/views/pages/courses.ejs +++ b/views/pages/courses.ejs @@ -25,7 +25,6 @@ <%- include('../partials/layout', { title: 'PDGA Courses - Sweden', - heading: 'PDGA Courses - Sweden', activePage: 'courses', cssFiles: ['courses.css'], jsFiles: ['courses.js'], diff --git a/views/pages/index.ejs b/views/pages/index.ejs index 9b1ab80..fba9b26 100644 --- a/views/pages/index.ejs +++ b/views/pages/index.ejs @@ -56,7 +56,6 @@ <%- include('../partials/layout', { title: 'PDGA Ratings', - heading: 'PDGA Player Ratings', activePage: 'players', cssFiles: ['players.css'], jsFiles: ['tooltips.js', 'chart.js', 'progress.js', 'players.js'], diff --git a/views/partials/topbar.ejs b/views/partials/topbar.ejs index f726414..81ed594 100644 --- a/views/partials/topbar.ejs +++ b/views/partials/topbar.ejs @@ -1,45 +1,46 @@
-
- - - - Rating Tracker - Disc golf · unofficial - - +
+ + + + Rating Tracker + Disc golf · unofficial + + - + -
-
- Next update - <%= nextUpdate %> -
-
- Last refresh - <%= lastRefresh %> -
- - +
+
+ Next update + <%= nextUpdate %> +
+
+ Last refresh + <%= lastRefresh %> +
+ + +
-