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:
+172
-279
@@ -1,90 +1,28 @@
|
||||
let cachedDebugInfo = {};
|
||||
let pendingPlayerData = null;
|
||||
|
||||
function displayRatings(ratings) {
|
||||
const tableDiv = document.getElementById('ratings-table');
|
||||
|
||||
if (ratings.length === 0) {
|
||||
tableDiv.innerHTML = '<p>No ratings found.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let tableHTML = `
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="mobile-hide">Rank</th>
|
||||
<th>Player Name</th>
|
||||
<th class="mobile-hide">PDGA #</th>
|
||||
<th>Rating</th>
|
||||
<th class="mobile-hide">Change</th>
|
||||
<th class="mobile-hide">Predicted</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
ratings.forEach((player, index) => {
|
||||
const difference = player.predictedRating && player.rating ?
|
||||
player.predictedRating - player.rating : 0;
|
||||
const diffText = difference > 0 ? `+${difference}` : difference.toString();
|
||||
const diffClass = difference > 0 ? 'positive' : difference < 0 ? 'negative' : 'neutral';
|
||||
|
||||
const ratingChangeText = player.ratingChange ?
|
||||
(player.ratingChange > 0 ? `+${player.ratingChange}` : player.ratingChange.toString()) : 'N/A';
|
||||
const ratingChangeClass = player.ratingChange > 0 ? 'positive' :
|
||||
player.ratingChange < 0 ? 'negative' : 'neutral';
|
||||
|
||||
tableHTML += `
|
||||
<tr id="row-${player.pdgaNumber}" class="expandable-row" onclick="togglePlayerHistory(${player.pdgaNumber})">
|
||||
<td class="mobile-hide">${index + 1}</td>
|
||||
<td class="player-name">
|
||||
<a href="https://www.pdga.com/player/${player.pdgaNumber}" target="_blank" onclick="event.stopPropagation()">${player.name}</a>
|
||||
<div class="mobile-only pdga-number" style="font-size: 11px; color: #999; margin-top: 2px;">PDGA #${player.pdgaNumber}</div>
|
||||
</td>
|
||||
<td class="pdga-number mobile-hide">#${player.pdgaNumber}</td>
|
||||
<td class="rating">
|
||||
<div class="refresh-section">
|
||||
<span class="rating-value" data-rating="${player.rating || ''}" data-stddev="${player.stdDev || ''}" data-pdga="${player.pdgaNumber}" style="cursor: help;">${player.rating || '<span style="color: #999; font-style: italic;">Click refresh</span>'}</span>
|
||||
<i class="fas fa-sync-alt refresh-icon" onclick="refreshPlayer(${player.pdgaNumber})" title="Refresh player data"></i>
|
||||
</div>
|
||||
<div class="mobile-only rating-change ${ratingChangeClass}" style="font-size: 11px; margin-top: 2px;">${ratingChangeText}</div>
|
||||
<div class="std-dev-tooltip" id="tooltip-rating-${player.pdgaNumber}"></div>
|
||||
</td>
|
||||
<td class="rating-change ${ratingChangeClass} mobile-hide">${ratingChangeText}</td>
|
||||
<td class="predicted-rating mobile-hide" id="predicted-${player.pdgaNumber}">
|
||||
<div class="refresh-section">
|
||||
<span class="predicted-value" data-stddev="${player.stdDev || ''}" data-pdga="${player.pdgaNumber}" style="cursor: help;">${player.predictedRating || 'N/A'}</span>
|
||||
<i class="fas fa-question-circle debug-icon" onclick="showDebugInfo(${player.pdgaNumber})" title="Show calculation details" style="margin-left: 5px; color: #6c757d; cursor: pointer; opacity: 0.6;"></i>
|
||||
<i class="fas fa-sync-alt refresh-icon" onclick="refreshRoundHistory(${player.pdgaNumber})" title="Refresh prediction data"></i>
|
||||
</div>
|
||||
<div class="std-dev-tooltip" id="tooltip-stddev-${player.pdgaNumber}"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="history-${player.pdgaNumber}" class="expanded-content">
|
||||
<td colspan="6" class="expanded-cell">
|
||||
<div class="chart-title">
|
||||
<div class="refresh-section">
|
||||
Rating History for ${player.name}
|
||||
<i class="fas fa-sync-alt refresh-icon" onclick="refreshRatingHistory(${player.pdgaNumber})" title="Refresh rating history"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container" id="chart-${player.pdgaNumber}" style="position: relative;">
|
||||
<div class="loading-chart">Click to load rating history...</div>
|
||||
</div>
|
||||
<div class="chart-tooltip" id="tooltip-${player.pdgaNumber}"></div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
function setupTooltipsAfterSwap() {
|
||||
document.body.addEventListener('htmx:afterSwap', function(event) {
|
||||
if (event.detail.target.id === 'ratings-table') {
|
||||
initRatingsTooltips();
|
||||
}
|
||||
// After player history partial loads, render the chart
|
||||
const target = event.detail.target;
|
||||
if (target.id && target.id.startsWith('history-content-')) {
|
||||
const container = target.querySelector('.chart-container');
|
||||
if (container && container.dataset.history) {
|
||||
try {
|
||||
const history = JSON.parse(container.dataset.history);
|
||||
createRatingChart(container, history);
|
||||
} catch (e) {
|
||||
console.error('Error rendering chart:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tableHTML += `
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
tableDiv.innerHTML = tableHTML;
|
||||
}
|
||||
|
||||
function initRatingsTooltips() {
|
||||
document.querySelectorAll('.predicted-value').forEach(span => {
|
||||
const pdgaNumber = span.dataset.pdga;
|
||||
const stdDev = span.dataset.stddev;
|
||||
@@ -109,47 +47,30 @@ function displayRatings(ratings) {
|
||||
});
|
||||
}
|
||||
|
||||
async function togglePlayerHistory(pdgaNumber) {
|
||||
function togglePlayerHistory(pdgaNumber) {
|
||||
const historyRow = document.getElementById(`history-${pdgaNumber}`);
|
||||
const chartContainer = document.getElementById(`chart-${pdgaNumber}`);
|
||||
|
||||
const contentDiv = document.getElementById(`history-content-${pdgaNumber}`);
|
||||
|
||||
if (historyRow.style.display === 'table-row') {
|
||||
historyRow.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
historyRow.style.display = 'table-row';
|
||||
|
||||
if (chartContainer.dataset.loaded === 'true') {
|
||||
|
||||
if (contentDiv.dataset.loaded === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
chartContainer.innerHTML = '<div class="loading-chart">Loading rating history...</div>';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/rating-history/${pdgaNumber}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.history && data.history.length > 0) {
|
||||
createRatingChart(chartContainer, data.history);
|
||||
chartContainer.dataset.loaded = 'true';
|
||||
} else {
|
||||
chartContainer.innerHTML = '<div class="loading-chart">No rating history available</div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading rating history:', error);
|
||||
chartContainer.innerHTML = '<div class="loading-chart">Error loading rating history</div>';
|
||||
}
|
||||
|
||||
htmx.ajax('GET', `/partials/player-history/${pdgaNumber}`, {target: `#history-content-${pdgaNumber}`, swap: 'innerHTML'});
|
||||
contentDiv.dataset.loaded = 'true';
|
||||
}
|
||||
|
||||
async function clearCache() {
|
||||
try {
|
||||
const response = await fetch('/api/clear-cache', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const response = await fetch('/api/clear-cache', { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
if (data.success) {
|
||||
alert(data.message);
|
||||
if (confirm('Reload page to fetch fresh data?')) {
|
||||
@@ -167,27 +88,24 @@ async function clearCache() {
|
||||
async function refreshPlayer(pdgaNumber) {
|
||||
const icon = document.querySelector(`#row-${pdgaNumber} .rating .refresh-icon`);
|
||||
icon.classList.add('spinning');
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/refresh-player/${pdgaNumber}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const response = await fetch(`/api/refresh-player/${pdgaNumber}`, { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
if (data.success) {
|
||||
const row = document.getElementById(`row-${pdgaNumber}`);
|
||||
const ratingCell = row.querySelector('.rating');
|
||||
const ratingChangeCell = row.querySelector('.rating-change');
|
||||
|
||||
|
||||
const nameLink = row.querySelector('.player-name a');
|
||||
nameLink.textContent = data.player.name;
|
||||
|
||||
const ratingChangeText = data.player.ratingChange ?
|
||||
|
||||
const ratingChangeText = data.player.ratingChange ?
|
||||
(data.player.ratingChange > 0 ? `+${data.player.ratingChange}` : data.player.ratingChange.toString()) : 'N/A';
|
||||
const ratingChangeClass = data.player.ratingChange > 0 ? 'positive' :
|
||||
const ratingChangeClass = data.player.ratingChange > 0 ? 'positive' :
|
||||
data.player.ratingChange < 0 ? 'negative' : 'neutral';
|
||||
|
||||
|
||||
const ratingValue = ratingCell.querySelector('.rating-value');
|
||||
if (ratingValue) {
|
||||
ratingValue.textContent = data.player.rating || 'N/A';
|
||||
@@ -224,18 +142,15 @@ async function refreshPlayer(pdgaNumber) {
|
||||
async function refreshRoundHistory(pdgaNumber) {
|
||||
const icon = document.querySelector(`#predicted-${pdgaNumber} .refresh-icon`);
|
||||
icon.classList.add('spinning');
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/refresh-round-history/${pdgaNumber}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const response = await fetch(`/api/refresh-round-history/${pdgaNumber}`, { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(JSON.stringify(data));
|
||||
}
|
||||
|
||||
|
||||
if (data.success) {
|
||||
if (data.debugLog) {
|
||||
cachedDebugInfo[pdgaNumber] = data.debugLog;
|
||||
@@ -271,27 +186,6 @@ async function refreshRoundHistory(pdgaNumber) {
|
||||
replaceWithTooltip(ratingValue, ratingTooltip, () => `Rating Range: ${minRating} - ${maxRating} (\u00b1${stdDev})`);
|
||||
}
|
||||
}
|
||||
|
||||
const diffCell = document.getElementById(`diff-${pdgaNumber}`);
|
||||
if (diffCell) {
|
||||
const currentRatingElement = document.querySelector(`#row-${pdgaNumber} .rating .refresh-section`);
|
||||
if (currentRatingElement && currentRatingElement.firstChild) {
|
||||
const currentRatingText = currentRatingElement.firstChild.textContent;
|
||||
const currentRating = parseInt(currentRatingText);
|
||||
|
||||
if (data.predictedRating && currentRating && !isNaN(currentRating)) {
|
||||
const difference = data.predictedRating - currentRating;
|
||||
const diffText = difference > 0 ? `+${difference}` : difference.toString();
|
||||
const diffClass = difference > 0 ? 'positive' : difference < 0 ? 'negative' : 'neutral';
|
||||
|
||||
diffCell.className = `difference ${diffClass}`;
|
||||
diffCell.textContent = diffText;
|
||||
} else {
|
||||
diffCell.innerHTML = '<span style="color: #999; font-style: italic;">Use refresh</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error refreshing round history:', error);
|
||||
@@ -331,24 +225,16 @@ async function refreshRoundHistory(pdgaNumber) {
|
||||
async function refreshRatingHistory(pdgaNumber) {
|
||||
const icon = document.querySelector(`#history-${pdgaNumber} .chart-title .refresh-icon`);
|
||||
icon.classList.add('spinning');
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/refresh-rating-history/${pdgaNumber}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const response = await fetch(`/api/refresh-rating-history/${pdgaNumber}`, { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
if (data.success) {
|
||||
const chartContainer = document.getElementById(`chart-${pdgaNumber}`);
|
||||
chartContainer.dataset.loaded = 'false';
|
||||
|
||||
if (data.history && data.history.length > 0) {
|
||||
createRatingChart(chartContainer, data.history);
|
||||
chartContainer.dataset.loaded = 'true';
|
||||
} else {
|
||||
chartContainer.innerHTML = '<div class="loading-chart">No rating history available</div>';
|
||||
}
|
||||
const contentDiv = document.getElementById(`history-content-${pdgaNumber}`);
|
||||
contentDiv.dataset.loaded = 'false';
|
||||
htmx.ajax('GET', `/partials/player-history/${pdgaNumber}`, {target: `#history-content-${pdgaNumber}`, swap: 'innerHTML'});
|
||||
contentDiv.dataset.loaded = 'true';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error refreshing rating history:', error);
|
||||
@@ -358,61 +244,27 @@ async function refreshRatingHistory(pdgaNumber) {
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshAllPlayers() {
|
||||
const icons = document.querySelectorAll('th .refresh-icon');
|
||||
const ratingIcon = icons[0];
|
||||
ratingIcon.classList.add('spinning');
|
||||
|
||||
try {
|
||||
const ratings = await getAllPlayersFromDB();
|
||||
displayRatings(ratings);
|
||||
} catch (error) {
|
||||
console.error('Error refreshing all players:', error);
|
||||
alert('Failed to refresh player data');
|
||||
} finally {
|
||||
ratingIcon.classList.remove('spinning');
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshAllPredictions() {
|
||||
const icons = document.querySelectorAll('th .refresh-icon');
|
||||
const predictedIcon = icons[1];
|
||||
predictedIcon.classList.add('spinning');
|
||||
|
||||
try {
|
||||
alert('Bulk prediction refresh not implemented yet. Use individual refresh icons.');
|
||||
} catch (error) {
|
||||
console.error('Error refreshing all predictions:', error);
|
||||
alert('Failed to refresh predictions');
|
||||
} finally {
|
||||
predictedIcon.classList.remove('spinning');
|
||||
}
|
||||
}
|
||||
|
||||
async function showDebugInfo(pdgaNumber) {
|
||||
const modal = document.getElementById('debug-modal');
|
||||
const header = document.getElementById('debug-header');
|
||||
const log = document.getElementById('debug-log');
|
||||
|
||||
|
||||
const playerNameElement = document.querySelector(`#row-${pdgaNumber} .player-name a`);
|
||||
const playerName = playerNameElement ? playerNameElement.textContent : `PDGA #${pdgaNumber}`;
|
||||
|
||||
|
||||
header.textContent = `Prediction Calculation Details - ${playerName}`;
|
||||
log.textContent = 'Loading calculation details...';
|
||||
modal.style.display = 'flex';
|
||||
|
||||
|
||||
try {
|
||||
if (cachedDebugInfo[pdgaNumber]) {
|
||||
log.textContent = cachedDebugInfo[pdgaNumber].join('\n');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/refresh-round-history/${pdgaNumber}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
|
||||
const response = await fetch(`/api/refresh-round-history/${pdgaNumber}`, { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
if (data.success && data.debugLog) {
|
||||
cachedDebugInfo[pdgaNumber] = data.debugLog;
|
||||
log.textContent = data.debugLog.join('\n');
|
||||
@@ -426,8 +278,7 @@ async function showDebugInfo(pdgaNumber) {
|
||||
}
|
||||
|
||||
function closeDebugModal(event) {
|
||||
const modal = document.getElementById('debug-modal');
|
||||
modal.style.display = 'none';
|
||||
document.getElementById('debug-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
async function searchAndAddPlayer() {
|
||||
@@ -440,9 +291,9 @@ async function searchAndAddPlayer() {
|
||||
}
|
||||
|
||||
const button = document.querySelector('.btn-add');
|
||||
const originalHTML = button.innerHTML;
|
||||
const originalText = button.textContent;
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<i class="fas fa-sync-alt spinning"></i> Searching...';
|
||||
button.textContent = 'Searching...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/search-player/${pdgaNumber}`);
|
||||
@@ -466,70 +317,112 @@ async function searchAndAddPlayer() {
|
||||
showErrorModal('Failed to search for player. Please try again.');
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
button.innerHTML = originalHTML;
|
||||
button.textContent = originalText;
|
||||
}
|
||||
}
|
||||
|
||||
function showConfirmationModal(player) {
|
||||
const modal = document.getElementById('add-player-modal');
|
||||
const header = document.getElementById('add-player-modal-header');
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
document.getElementById('add-player-modal-header').textContent = 'Confirm Player';
|
||||
|
||||
header.textContent = 'Confirm Player';
|
||||
body.innerHTML = `
|
||||
<p>Is this the correct player you want to add?</p>
|
||||
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 4px; margin-top: 15px;">
|
||||
<strong style="font-size: 18px; color: #007bff;">${player.name}</strong><br>
|
||||
<span style="color: #6c757d;">PDGA #${player.pdgaNumber}</span><br>
|
||||
${player.rating ? `<span style="color: #28a745; font-weight: bold;">Current Rating: ${player.rating}</span>` : '<span style="color: #999;">No rating available</span>'}
|
||||
</div>
|
||||
`;
|
||||
footer.innerHTML = `
|
||||
<button class="btn btn-cancel" onclick="closeAddPlayerModal()">Cancel</button>
|
||||
<button class="btn btn-confirm" onclick="confirmAddPlayer()">Add Player</button>
|
||||
`;
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
body.textContent = '';
|
||||
|
||||
const question = document.createElement('p');
|
||||
question.textContent = 'Is this the correct player you want to add?';
|
||||
body.appendChild(question);
|
||||
|
||||
const info = document.createElement('div');
|
||||
info.style.cssText = 'background-color: #f8f9fa; padding: 15px; border-radius: 4px; margin-top: 15px;';
|
||||
|
||||
const name = document.createElement('strong');
|
||||
name.style.cssText = 'font-size: 18px; color: #007bff;';
|
||||
name.textContent = player.name;
|
||||
info.appendChild(name);
|
||||
info.appendChild(document.createElement('br'));
|
||||
|
||||
const pdga = document.createElement('span');
|
||||
pdga.style.color = '#6c757d';
|
||||
pdga.textContent = `PDGA #${player.pdgaNumber}`;
|
||||
info.appendChild(pdga);
|
||||
info.appendChild(document.createElement('br'));
|
||||
|
||||
const rating = document.createElement('span');
|
||||
if (player.rating) {
|
||||
rating.style.cssText = 'color: #28a745; font-weight: bold;';
|
||||
rating.textContent = `Current Rating: ${player.rating}`;
|
||||
} else {
|
||||
rating.style.color = '#999';
|
||||
rating.textContent = 'No rating available';
|
||||
}
|
||||
info.appendChild(rating);
|
||||
body.appendChild(info);
|
||||
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
footer.textContent = '';
|
||||
|
||||
const cancelBtn = document.createElement('button');
|
||||
cancelBtn.className = 'btn btn-cancel';
|
||||
cancelBtn.textContent = 'Cancel';
|
||||
cancelBtn.onclick = closeAddPlayerModal;
|
||||
footer.appendChild(cancelBtn);
|
||||
|
||||
const confirmBtn = document.createElement('button');
|
||||
confirmBtn.className = 'btn btn-confirm';
|
||||
confirmBtn.textContent = 'Add Player';
|
||||
confirmBtn.onclick = confirmAddPlayer;
|
||||
footer.appendChild(confirmBtn);
|
||||
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function showErrorModal(message) {
|
||||
const modal = document.getElementById('add-player-modal');
|
||||
const header = document.getElementById('add-player-modal-header');
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
document.getElementById('add-player-modal-header').textContent = 'Player Not Found';
|
||||
|
||||
header.textContent = 'Player Not Found';
|
||||
body.innerHTML = `
|
||||
<p style="color: #dc3545;">
|
||||
<i class="fas fa-exclamation-circle"></i> ${message}
|
||||
</p>
|
||||
<p style="margin-top: 10px; color: #6c757d; font-size: 14px;">
|
||||
Please check the PDGA number and try again.
|
||||
</p>
|
||||
`;
|
||||
footer.innerHTML = `
|
||||
<button class="btn btn-cancel" onclick="closeAddPlayerModal()">Close</button>
|
||||
`;
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
body.textContent = '';
|
||||
|
||||
const errorP = document.createElement('p');
|
||||
errorP.style.color = '#dc3545';
|
||||
errorP.textContent = message;
|
||||
body.appendChild(errorP);
|
||||
|
||||
const helpP = document.createElement('p');
|
||||
helpP.style.cssText = 'margin-top: 10px; color: #6c757d; font-size: 14px;';
|
||||
helpP.textContent = 'Please check the PDGA number and try again.';
|
||||
body.appendChild(helpP);
|
||||
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
footer.textContent = '';
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.className = 'btn btn-cancel';
|
||||
closeBtn.textContent = 'Close';
|
||||
closeBtn.onclick = closeAddPlayerModal;
|
||||
footer.appendChild(closeBtn);
|
||||
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function showInfoModal(message) {
|
||||
const modal = document.getElementById('add-player-modal');
|
||||
const header = document.getElementById('add-player-modal-header');
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
document.getElementById('add-player-modal-header').textContent = 'Information';
|
||||
|
||||
header.textContent = 'Information';
|
||||
body.innerHTML = `
|
||||
<p style="color: #007bff;">
|
||||
<i class="fas fa-info-circle"></i> ${message}
|
||||
</p>
|
||||
`;
|
||||
footer.innerHTML = `
|
||||
<button class="btn btn-cancel" onclick="closeAddPlayerModal()">Close</button>
|
||||
`;
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
body.textContent = '';
|
||||
|
||||
const infoP = document.createElement('p');
|
||||
infoP.style.color = '#007bff';
|
||||
infoP.textContent = message;
|
||||
body.appendChild(infoP);
|
||||
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
footer.textContent = '';
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.className = 'btn btn-cancel';
|
||||
closeBtn.textContent = 'Close';
|
||||
closeBtn.onclick = closeAddPlayerModal;
|
||||
footer.appendChild(closeBtn);
|
||||
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
@@ -540,19 +433,14 @@ async function confirmAddPlayer() {
|
||||
return;
|
||||
}
|
||||
|
||||
const modal = document.getElementById('add-player-modal');
|
||||
const body = document.getElementById('add-player-modal-body');
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
|
||||
body.innerHTML = '<p style="text-align: center;"><i class="fas fa-sync-alt spinning"></i> Adding player...</p>';
|
||||
footer.innerHTML = '';
|
||||
body.textContent = 'Adding player...';
|
||||
document.getElementById('add-player-modal-footer').textContent = '';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/add-player', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pdgaNumber: pendingPlayerData.pdgaNumber })
|
||||
});
|
||||
|
||||
@@ -562,34 +450,39 @@ async function confirmAddPlayer() {
|
||||
throw new Error(data.error || 'Failed to add player');
|
||||
}
|
||||
|
||||
body.innerHTML = `
|
||||
<p style="color: #28a745; text-align: center;">
|
||||
<i class="fas fa-check-circle" style="font-size: 48px; margin-bottom: 15px;"></i><br>
|
||||
<strong>${data.player.name}</strong> has been added successfully!
|
||||
</p>
|
||||
`;
|
||||
footer.innerHTML = `
|
||||
<button class="btn btn-confirm" onclick="closeAddPlayerModal(); location.reload();">OK</button>
|
||||
`;
|
||||
body.textContent = '';
|
||||
const successP = document.createElement('p');
|
||||
successP.style.cssText = 'color: #28a745; text-align: center;';
|
||||
successP.textContent = `${data.player.name} has been added successfully!`;
|
||||
body.appendChild(successP);
|
||||
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
footer.textContent = '';
|
||||
const okBtn = document.createElement('button');
|
||||
okBtn.className = 'btn btn-confirm';
|
||||
okBtn.textContent = 'OK';
|
||||
okBtn.onclick = function() { closeAddPlayerModal(); location.reload(); };
|
||||
footer.appendChild(okBtn);
|
||||
|
||||
document.getElementById('pdga-number-input').value = '';
|
||||
pendingPlayerData = null;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error adding player:', error);
|
||||
body.innerHTML = `
|
||||
<p style="color: #dc3545;">
|
||||
<i class="fas fa-exclamation-circle"></i> ${error.message}
|
||||
</p>
|
||||
`;
|
||||
footer.innerHTML = `
|
||||
<button class="btn btn-cancel" onclick="closeAddPlayerModal()">Close</button>
|
||||
`;
|
||||
body.textContent = error.message;
|
||||
body.style.color = '#dc3545';
|
||||
|
||||
const footer = document.getElementById('add-player-modal-footer');
|
||||
footer.textContent = '';
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.className = 'btn btn-cancel';
|
||||
closeBtn.textContent = 'Close';
|
||||
closeBtn.onclick = closeAddPlayerModal;
|
||||
footer.appendChild(closeBtn);
|
||||
}
|
||||
}
|
||||
|
||||
function closeAddPlayerModal(event) {
|
||||
const modal = document.getElementById('add-player-modal');
|
||||
modal.style.display = 'none';
|
||||
document.getElementById('add-player-modal').style.display = 'none';
|
||||
pendingPlayerData = null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user