Add HTMX migration for server-rendered tables and lazy loading
- Add HTMX CDN to layout - Replace client-side table rendering (displayRatings, displayCourses) with server-rendered EJS partials via hx-get - Add server-side course search with debounced hx-trigger - Lazy-load player history and course layouts via htmx.ajax() - Render rating chart via htmx:afterSwap with data attributes - Add partial routes: ratings-table, course-table, player-history, course-layouts
This commit is contained in:
@@ -8,6 +8,36 @@ const { layoutEventCache, scrapeCourseDirectory, scrapeCourseLayouts, scrapeEven
|
||||
// Request locking to prevent concurrent scrapes of the same resource
|
||||
const activeScrapes = new Map();
|
||||
|
||||
router.get('/partials/course-table', async (req, res) => {
|
||||
try {
|
||||
const allCourses = await getAllCoursesFromDB();
|
||||
const query = req.query.q || '';
|
||||
let courses = allCourses;
|
||||
|
||||
if (query) {
|
||||
const q = query.toLowerCase();
|
||||
courses = allCourses.filter(c =>
|
||||
c.name.toLowerCase().includes(q) || c.city.toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
|
||||
res.render('../partials/course-table', { courses, query, total: allCourses.length });
|
||||
} catch (error) {
|
||||
res.status(500).send('<p>Error loading courses. Please try again.</p>');
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/partials/course-layouts/:courseId', async (req, res) => {
|
||||
try {
|
||||
const { courseId } = req.params;
|
||||
const layouts = await getLayoutsForCourse(courseId);
|
||||
res.render('../partials/course-layouts', { layouts, courseId });
|
||||
} catch (error) {
|
||||
console.error('Error loading course layouts:', error.message);
|
||||
res.status(500).send('<div class="no-layouts">Error loading layouts</div>');
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/api/courses', async (req, res) => {
|
||||
try {
|
||||
const courses = await getAllCoursesFromDB();
|
||||
|
||||
@@ -8,6 +8,43 @@ const { launchBrowser } = require('../scrapers/browser');
|
||||
const { getPlayerDataFromDB, scrapePDGARating, getAllRatingsFromDB, refreshAllPlayersInDB, getPredictedRatingFromDB } = require('../services/player-service');
|
||||
const { calculatePredictedRating } = require('../services/rating-calculator');
|
||||
|
||||
router.get('/partials/ratings-table', async (req, res) => {
|
||||
try {
|
||||
const ratings = await getAllRatingsFromDB();
|
||||
res.render('../partials/ratings-table', { ratings });
|
||||
} catch (error) {
|
||||
res.status(500).send('<p>Error loading ratings. Please try again.</p>');
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/partials/player-history/:pdgaNumber', async (req, res) => {
|
||||
try {
|
||||
const { pdgaNumber } = req.params;
|
||||
|
||||
let history = await getRatingHistoryFromDB(pdgaNumber);
|
||||
if (!history || history.length === 0) {
|
||||
const html = await fetchRatingHistory(pdgaNumber);
|
||||
history = parseRatingHistory(html);
|
||||
try {
|
||||
await saveRatingHistoryToDB(pdgaNumber, history);
|
||||
} catch (dbErr) {
|
||||
console.error('Failed to save rating history:', dbErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
const formattedHistory = (history || []).map(row => ({
|
||||
date: row.date,
|
||||
rating: row.rating,
|
||||
displayDate: new Date(row.date).toLocaleDateString('en-US', { day: '2-digit', month: 'short', year: 'numeric' })
|
||||
}));
|
||||
|
||||
res.render('../partials/player-history', { pdgaNumber, history: formattedHistory });
|
||||
} catch (error) {
|
||||
console.error('Error loading player history:', error.message);
|
||||
res.status(500).send('<div class="loading-chart">Error loading rating history</div>');
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/api/ratings', async (req, res) => {
|
||||
try {
|
||||
const ratings = await getAllRatingsFromDB();
|
||||
|
||||
Reference in New Issue
Block a user