Files
pdga-rating/public/js/courses.js
T
Samuel Enocsson eb77b1f32b feat: add Tjing course import
Search and import courses with layouts from Tjing's GraphQL API.
Total par is calculated from individual hole data. Courses are saved
with a tjing.se link as unique identifier to prevent duplicates.
2026-03-20 08:45:16 +01:00

170 lines
5.6 KiB
JavaScript

function toggleAccordion(accordionId) {
const content = document.getElementById(accordionId);
const icon = document.getElementById(`${accordionId}-icon`);
if (content.classList.contains('expanded')) {
content.classList.remove('expanded');
icon.classList.remove('expanded');
} else {
content.classList.add('expanded');
icon.classList.add('expanded');
}
}
function toggleCourseLayouts(courseId) {
const layoutsRow = document.getElementById(`layouts-${courseId}`);
const layoutsContainer = document.getElementById(`layouts-container-${courseId}`);
if (layoutsRow.style.display === 'table-row') {
layoutsRow.style.display = 'none';
return;
}
layoutsRow.style.display = 'table-row';
if (layoutsContainer.dataset.loaded === 'true') {
return;
}
htmx.ajax('GET', `/partials/course-layouts/${courseId}`, {target: `#layouts-container-${courseId}`, swap: 'innerHTML'});
layoutsContainer.dataset.loaded = 'true';
}
async function scrapeCourses() {
const btn = document.getElementById('scrape-courses-btn');
btn.disabled = true;
btn.textContent = 'Scraping...';
try {
const response = await fetch('/api/scrape-courses', { method: 'POST' });
const data = await response.json();
if (data.success) {
alert(data.message);
htmx.ajax('GET', '/partials/course-table', '#courses-table');
} else {
alert('Failed to scrape courses');
}
} catch (error) {
console.error('Error scraping courses:', error);
alert('Error scraping courses');
} finally {
btn.disabled = false;
btn.textContent = 'Scrape Courses';
}
}
async function searchTjing() {
var input = document.getElementById('tjing-search');
var query = input.value.trim();
if (query.length < 2) return;
var btn = document.getElementById('tjing-search-btn');
btn.disabled = true;
try {
var response = await fetch('/api/tjing/search?q=' + encodeURIComponent(query));
var courses = await response.json();
var container = document.getElementById('tjing-results');
container.textContent = '';
if (courses.length === 0) {
var p = document.createElement('p');
p.className = 'no-layouts';
p.textContent = 'No courses found on Tjing';
container.appendChild(p);
return;
}
courses.forEach(function(course) {
var item = document.createElement('div');
item.className = 'tjing-result';
var info = document.createElement('div');
info.className = 'tjing-result-info';
var name = document.createElement('span');
name.className = 'tjing-result-name';
name.textContent = course.name;
var addr = document.createElement('span');
addr.className = 'tjing-result-address';
addr.textContent = course.address || '';
info.appendChild(name);
info.appendChild(addr);
var importBtn = document.createElement('button');
importBtn.className = 'btn';
importBtn.textContent = 'Import';
importBtn.addEventListener('click', function() { importFromTjing(course.id, importBtn); });
item.appendChild(info);
item.appendChild(importBtn);
container.appendChild(item);
});
} catch (error) {
console.error('Error searching Tjing:', error);
alert('Failed to search Tjing');
} finally {
btn.disabled = false;
}
}
async function importFromTjing(tjingId, btn) {
btn.disabled = true;
btn.textContent = 'Importing...';
try {
var response = await fetch('/api/tjing/import/' + tjingId, { method: 'POST' });
var data = await response.json();
if (data.success) {
btn.textContent = 'Imported!';
btn.style.background = 'var(--green)';
htmx.ajax('GET', '/partials/course-table', '#courses-table');
} else {
alert(data.error || 'Failed to import');
btn.disabled = false;
btn.textContent = 'Import';
}
} catch (error) {
console.error('Error importing from Tjing:', error);
alert('Failed to import course');
btn.disabled = false;
btn.textContent = 'Import';
}
}
async function scrapeLayouts(courseId, courseName) {
const icon = document.querySelector(`#row-${courseId} .refresh-icon`);
icon.classList.add('spinning');
try {
const response = await fetch(`/api/scrape-layouts/${courseId}`, { method: 'POST' });
const data = await response.json();
if (response.status === 409) {
alert(data.message || 'Scrape already in progress for this course. Please wait.');
} else if (data.success) {
const layoutsContainer = document.getElementById(`layouts-container-${courseId}`);
layoutsContainer.dataset.loaded = 'false';
const layoutsRow = document.getElementById(`layouts-${courseId}`);
if (layoutsRow.style.display === 'table-row') {
htmx.ajax('GET', `/partials/course-layouts/${courseId}`, {target: `#layouts-container-${courseId}`, swap: 'innerHTML'});
layoutsContainer.dataset.loaded = 'true';
}
alert(data.message);
} else {
alert('Failed to scrape layouts');
}
} catch (error) {
console.error('Error scraping layouts:', error);
alert('Error scraping layouts');
} finally {
icon.classList.remove('spinning');
}
}