2ccb018bdf
Players can create tours with selected courses/layouts and a date range. Others join via a 6-character tour code, play the courses, and report their total strokes. Live leaderboard with points and +/- par display. Includes: database schema, model, service, routes, views, and styling.
169 lines
5.0 KiB
JavaScript
169 lines
5.0 KiB
JavaScript
var coursesData = [];
|
|
|
|
async function loadCourses() {
|
|
try {
|
|
var res = await fetch('/api/tours/courses-with-layouts');
|
|
coursesData = await res.json();
|
|
updateCourseDropdowns();
|
|
} catch (err) {
|
|
console.error('Failed to load courses:', err);
|
|
}
|
|
}
|
|
|
|
function populateSelectWithCourses(select) {
|
|
select.textContent = '';
|
|
var defaultOpt = document.createElement('option');
|
|
defaultOpt.value = '';
|
|
defaultOpt.textContent = 'Select a course...';
|
|
select.appendChild(defaultOpt);
|
|
|
|
coursesData.forEach(function(course) {
|
|
var opt = document.createElement('option');
|
|
opt.value = course.id;
|
|
opt.textContent = course.name + ' (' + course.city + ')';
|
|
select.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
function updateCourseDropdowns() {
|
|
document.querySelectorAll('.course-select').forEach(function(select) {
|
|
var currentValue = select.value;
|
|
populateSelectWithCourses(select);
|
|
select.value = currentValue;
|
|
});
|
|
}
|
|
|
|
function onCourseChange(courseSelect) {
|
|
var index = courseSelect.dataset.index;
|
|
var layoutSelect = document.querySelector('.layout-select[data-index="' + index + '"]');
|
|
var courseId = parseInt(courseSelect.value);
|
|
|
|
layoutSelect.textContent = '';
|
|
layoutSelect.disabled = true;
|
|
|
|
var defaultOpt = document.createElement('option');
|
|
defaultOpt.value = '';
|
|
defaultOpt.textContent = 'Select layout...';
|
|
layoutSelect.appendChild(defaultOpt);
|
|
|
|
if (!courseId) return;
|
|
|
|
var course = coursesData.find(function(c) { return c.id === courseId; });
|
|
if (!course) return;
|
|
|
|
course.layouts.forEach(function(layout) {
|
|
var opt = document.createElement('option');
|
|
opt.value = layout.id;
|
|
opt.textContent = layout.name + ' (Par ' + layout.par + ')';
|
|
layoutSelect.appendChild(opt);
|
|
});
|
|
layoutSelect.disabled = false;
|
|
}
|
|
|
|
var courseIndex = 1;
|
|
|
|
function addCourseEntry() {
|
|
var container = document.getElementById('course-selector');
|
|
var entry = document.createElement('div');
|
|
entry.className = 'course-entry';
|
|
|
|
var courseSelect = document.createElement('select');
|
|
courseSelect.className = 'input course-select';
|
|
courseSelect.dataset.index = courseIndex;
|
|
populateSelectWithCourses(courseSelect);
|
|
courseSelect.addEventListener('change', function() { onCourseChange(courseSelect); });
|
|
|
|
var layoutSelect = document.createElement('select');
|
|
layoutSelect.className = 'input layout-select';
|
|
layoutSelect.dataset.index = courseIndex;
|
|
layoutSelect.disabled = true;
|
|
var defaultOpt = document.createElement('option');
|
|
defaultOpt.value = '';
|
|
defaultOpt.textContent = 'Select course first';
|
|
layoutSelect.appendChild(defaultOpt);
|
|
|
|
var removeBtn = document.createElement('button');
|
|
removeBtn.className = 'btn-remove';
|
|
removeBtn.type = 'button';
|
|
var icon = document.createElement('i');
|
|
icon.className = 'fas fa-times';
|
|
removeBtn.appendChild(icon);
|
|
removeBtn.addEventListener('click', function() { entry.remove(); });
|
|
|
|
entry.appendChild(courseSelect);
|
|
entry.appendChild(layoutSelect);
|
|
entry.appendChild(removeBtn);
|
|
container.appendChild(entry);
|
|
|
|
courseIndex++;
|
|
}
|
|
|
|
async function createTour() {
|
|
var name = document.getElementById('tour-name').value.trim();
|
|
var startDate = document.getElementById('tour-start').value;
|
|
var endDate = document.getElementById('tour-end').value;
|
|
|
|
if (!name || !startDate || !endDate) {
|
|
alert('Please fill in name and dates');
|
|
return;
|
|
}
|
|
|
|
var courses = [];
|
|
document.querySelectorAll('.course-entry').forEach(function(entry) {
|
|
var courseId = entry.querySelector('.course-select').value;
|
|
var layoutId = entry.querySelector('.layout-select').value;
|
|
if (courseId && layoutId) {
|
|
courses.push({ courseId: parseInt(courseId), layoutId: parseInt(layoutId) });
|
|
}
|
|
});
|
|
|
|
if (courses.length === 0) {
|
|
alert('Please select at least one course with a layout');
|
|
return;
|
|
}
|
|
|
|
var btn = document.getElementById('create-tour-btn');
|
|
btn.disabled = true;
|
|
|
|
try {
|
|
var res = await fetch('/api/tours', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: name, startDate: startDate, endDate: endDate, courses: courses })
|
|
});
|
|
|
|
var data = await res.json();
|
|
if (data.success) {
|
|
var link = document.getElementById('tour-link');
|
|
link.href = '/tours/' + data.code;
|
|
link.textContent = window.location.origin + '/tours/' + data.code;
|
|
document.getElementById('tour-created').style.display = '';
|
|
} else {
|
|
alert(data.error || 'Failed to create tour');
|
|
}
|
|
} catch (err) {
|
|
console.error('Error creating tour:', err);
|
|
alert('Failed to create tour');
|
|
} finally {
|
|
btn.disabled = false;
|
|
}
|
|
}
|
|
|
|
function goToTour() {
|
|
var code = document.getElementById('tour-code').value.trim().toUpperCase();
|
|
if (code) {
|
|
window.location.href = '/tours/' + code;
|
|
}
|
|
}
|
|
|
|
// Initialize
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadCourses();
|
|
|
|
// Delegate change events for course selects
|
|
document.getElementById('course-selector').addEventListener('change', function(e) {
|
|
if (e.target.classList.contains('course-select')) {
|
|
onCourseChange(e.target);
|
|
}
|
|
});
|
|
}); |