You're exploring a live demo Start Your Free Ladder →
"S / J" return name.split(' / ').map(n => n.split(' ')[0]).join(' / '); } return name.split(' ')[0]; // First name only }, isTiebreaker(set) { const p1 = parseInt(set.p1) || 0; const p2 = parseInt(set.p2) || 0; return (p1 === 7 && p2 === 6) || (p1 === 6 && p2 === 7); }, // Validate a regular set score (first to 6, win by 2, or 7-6 tiebreaker) isValidSetScore(p1, p2) { if (p1 === '' || p2 === '') return { valid: true, error: null }; // Empty is ok (incomplete) const s1 = parseInt(p1) || 0; const s2 = parseInt(p2) || 0; const high = Math.max(s1, s2); const low = Math.min(s1, s2); const diff = high - low; // Neither player has won yet (score too low) if (high < 6) { return { valid: false, error: 'Set incomplete - need at least 6 games to win' }; } // Standard win: 6-0, 6-1, 6-2, 6-3, 6-4 if (high === 6 && low <= 4) { return { valid: true, error: null }; } // Must win by 2 at 6-5 → should be 7-5 if (high === 6 && low === 5) { return { valid: false, error: '6-5 is not final - must win by 2 (play to 7-5 or tiebreaker)' }; } // Tiebreaker: 7-6 if (high === 7 && low === 6) { return { valid: true, error: null }; } // 7-5 win if (high === 7 && low === 5) { return { valid: true, error: null }; } // 7-4 or lower is invalid (can't get to 7 unless opponent had 5 or 6) if (high === 7 && low < 5) { return { valid: false, error: '7-' + low + ' is invalid - if you reach 7, opponent had at least 5' }; } // Extended games (no tiebreaker format): 8-6, 9-7, 10-8, etc. - must win by 2 if (high >= 8) { if (diff >= 2 && low >= 6) { return { valid: true, error: null }; } if (diff < 2) { return { valid: false, error: high + '-' + low + ' is not final - must win by 2' }; } if (low < 6) { return { valid: false, error: 'Invalid score - both players must have 6+ games in extended play' }; } } return { valid: false, error: 'Invalid set score' }; }, // Validate tiebreaker points (first to 7, win by 2) isValidTiebreakerScore(tb1, tb2) { if (tb1 === '' || tb2 === '') return { valid: true, error: null }; // Empty is ok const t1 = parseInt(tb1) || 0; const t2 = parseInt(tb2) || 0; const high = Math.max(t1, t2); const low = Math.min(t1, t2); const diff = high - low; // Neither has won yet if (high < 7) { return { valid: false, error: 'Tiebreaker incomplete - need at least 7 points to win' }; } // Standard tiebreaker win: 7-0 through 7-5 if (high === 7 && low <= 5 && diff >= 2) { return { valid: true, error: null }; } // At 6-6 or higher, must win by 2 if (high >= 7 && low >= 6) { if (diff >= 2) { return { valid: true, error: null }; } return { valid: false, error: high + '-' + low + ' tiebreaker not final - must win by 2' }; } // 7-6 in tiebreaker is invalid (must win by 2) if (high === 7 && low === 6) { return { valid: false, error: '7-6 tiebreaker invalid - must win by 2' }; } return { valid: false, error: 'Invalid tiebreaker score' }; }, // Validate pro set (first to 8, win by 2) isValidProSetScore(p1, p2) { if (p1 === '' || p2 === '') return { valid: true, error: null }; const s1 = parseInt(p1) || 0; const s2 = parseInt(p2) || 0; const high = Math.max(s1, s2); const low = Math.min(s1, s2); const diff = high - low; // Neither has won yet if (high < 8) { return { valid: false, error: 'Pro set incomplete - need 8 games to win' }; } // Standard win: 8-0 through 8-6 if (high === 8 && low <= 6 && diff >= 2) { return { valid: true, error: null }; } // At 7-7 or higher, must win by 2 if (high >= 8 && low >= 7) { if (diff >= 2) { return { valid: true, error: null }; } return { valid: false, error: high + '-' + low + ' not final - must win by 2' }; } // 8-7 is invalid if (high === 8 && low === 7) { return { valid: false, error: '8-7 invalid - must win by 2' }; } return { valid: false, error: 'Invalid pro set score' }; }, // Validate match tiebreaker (first to 10, win by 2) isValidMatchTiebreakerScore(p1, p2) { if (p1 === '' || p2 === '') return { valid: true, error: null }; const s1 = parseInt(p1) || 0; const s2 = parseInt(p2) || 0; const high = Math.max(s1, s2); const low = Math.min(s1, s2); const diff = high - low; // Neither has won yet if (high < 10) { return { valid: false, error: 'Match tiebreaker incomplete - need 10 points to win' }; } // Standard win: 10-0 through 10-8 if (high === 10 && low <= 8 && diff >= 2) { return { valid: true, error: null }; } // At 9-9 or higher, must win by 2 if (high >= 10 && low >= 9) { if (diff >= 2) { return { valid: true, error: null }; } return { valid: false, error: high + '-' + low + ' not final - must win by 2' }; } // 10-9 is invalid if (high === 10 && low === 9) { return { valid: false, error: '10-9 invalid - must win by 2' }; } return { valid: false, error: 'Invalid match tiebreaker score' }; }, // Get validation error for a specific set getSetError(set, idx) { if (this.matchFormat === 'tiebreaker') { return this.isValidMatchTiebreakerScore(set.p1, set.p2).error; } if (this.matchFormat === 'proset') { return this.isValidProSetScore(set.p1, set.p2).error; } // Regular set const setValidation = this.isValidSetScore(set.p1, set.p2); if (!setValidation.valid) return setValidation.error; // Check tiebreaker score if it's a 7-6 set if (this.isTiebreaker(set) && (set.tb1 !== '' || set.tb2 !== '')) { const tbValidation = this.isValidTiebreakerScore(set.tb1, set.tb2); if (!tbValidation.valid) return tbValidation.error; } return null; }, // Check if a set has a valid complete score isSetComplete(set) { if (set.p1 === '' || set.p2 === '') return false; if (this.matchFormat === 'tiebreaker') { return this.isValidMatchTiebreakerScore(set.p1, set.p2).valid; } if (this.matchFormat === 'proset') { return this.isValidProSetScore(set.p1, set.p2).valid; } const setValid = this.isValidSetScore(set.p1, set.p2).valid; if (!setValid) return false; // For 7-6 sets, tiebreaker score is optional but if entered must be valid if (this.isTiebreaker(set) && (set.tb1 !== '' || set.tb2 !== '')) { return this.isValidTiebreakerScore(set.tb1, set.tb2).valid; } return true; }, getSetWinner(set) { const p1 = parseInt(set.p1) || 0; const p2 = parseInt(set.p2) || 0; // For match tiebreaker format (single set to 10+) if (this.matchFormat === 'tiebreaker') { if (p1 >= 10 && p1 - p2 >= 2) return 'p1'; if (p2 >= 10 && p2 - p1 >= 2) return 'p2'; return null; } // For pro set (first to 8, win by 2) if (this.matchFormat === 'proset') { if (p1 >= 8 && p1 - p2 >= 2) return 'p1'; if (p2 >= 8 && p2 - p1 >= 2) return 'p2'; // Handle extended games (9-7, 10-8, etc) if (p1 >= 8 && p2 >= 8) { if (p1 - p2 >= 2) return 'p1'; if (p2 - p1 >= 2) return 'p2'; } return null; } // Regular set // Standard 6-x wins (if opponent has 4 or less) if (p1 >= 6 && p2 <= 4) return 'p1'; if (p2 >= 6 && p1 <= 4) return 'p2'; // 7-5 win if (p1 === 7 && p2 === 5) return 'p1'; if (p2 === 7 && p1 === 5) return 'p2'; // Tiebreaker win (7-6) if (p1 === 7 && p2 === 6) return 'p1'; if (p2 === 7 && p1 === 6) return 'p2'; // Extended games (8-6, 9-7, etc. for no-tiebreaker sets) if (p1 >= 6 && p2 >= 6 && p1 - p2 >= 2) return 'p1'; if (p1 >= 6 && p2 >= 6 && p2 - p1 >= 2) return 'p2'; return null; }, countSetsWon() { let p1Sets = 0, p2Sets = 0; this.matchForm.sets.forEach(set => { const winner = this.getSetWinner(set); if (winner === 'p1') p1Sets++; else if (winner === 'p2') p2Sets++; }); return { p1: p1Sets, p2: p2Sets }; }, getAutoWinner() { const sets = this.countSetsWon(); if (sets.p1 === 0 && sets.p2 === 0) return null; if (sets.p1 > sets.p2) return this.user?.id; if (sets.p2 > sets.p1) return this.matchForm.opponent_id; return null; }, getScoreDisplay() { return this.matchForm.sets .filter(s => s.p1 !== '' || s.p2 !== '') .map(s => { let score = (s.p1 || 0) + '-' + (s.p2 || 0); // Add tiebreaker if present if (this.isTiebreaker(s) && s.tb1 && s.tb2) { const tbWinner = parseInt(s.tb1) > parseInt(s.tb2) ? s.tb1 : s.tb2; const tbLoser = parseInt(s.tb1) > parseInt(s.tb2) ? s.tb2 : s.tb1; score += ' (' + tbLoser + ')'; } return score; }) .join(', '); }, hasValidScore() { // Check all non-empty sets are valid and complete const completeSets = this.matchForm.sets.filter(s => s.p1 !== '' || s.p2 !== ''); if (completeSets.length === 0) return false; // All entered sets must be valid for (const set of completeSets) { if (!this.isSetComplete(set)) return false; } // Must have a winner return this.getAutoWinner() !== null; }, // Get all current validation errors getAllErrors() { const errors = []; this.matchForm.sets.forEach((set, idx) => { const error = this.getSetError(set, idx); if (error) errors.push({ set: idx + 1, error }); }); return errors; }, reviewMatch() { this.matchForm.winner_id = this.getAutoWinner(); this.showConfirm = true; }, formatDate(dateStr) { if (!dateStr) return 'Today'; const date = new Date(dateStr + 'T00:00:00'); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }, setFormat(format) { this.matchFormat = format; // Reset to appropriate number of sets if (format === 'tiebreaker' || format === 'proset') { this.matchForm.sets = [{ p1: '', p2: '', tb1: '', tb2: '' }]; } else { this.matchForm.sets = [{ p1: '', p2: '', tb1: '', tb2: '' }, { p1: '', p2: '', tb1: '', tb2: '' }]; } }, initSets() { // Ensure sets have tiebreaker fields this.matchForm.sets = this.matchForm.sets.map(s => ({ ...s, tb1: s.tb1 || '', tb2: s.tb2 || '' })); }, autoAdvance(setIdx, field) { // Determine next input in sequence: p1 -> p2 -> (tb1 -> tb2 if 7-6) -> next set p1 -> ... let nextField, nextSetIdx; const set = this.matchForm.sets[setIdx]; if (field === 'p1') { // Move to opponent score in same set nextField = 'p2'; nextSetIdx = setIdx; } else if (field === 'p2') { // Check if this is a tiebreaker set (7-6) in regular format if (this.matchFormat === 'regular' && this.isTiebreaker(set)) { // Move to tiebreaker input nextField = 'tb1'; nextSetIdx = setIdx; } else { // Move to your score in next set (if exists) nextField = 'p1'; nextSetIdx = setIdx + 1; } } else if (field === 'tb1') { // Move to opponent tiebreaker nextField = 'tb2'; nextSetIdx = setIdx; } else if (field === 'tb2') { // Move to next set p1 (if exists) nextField = 'p1'; nextSetIdx = setIdx + 1; } // Focus next input after a brief delay setTimeout(() => { const nextInput = document.getElementById(`score-${nextSetIdx}-${nextField}`); if (nextInput) { nextInput.focus(); nextInput.select(); } }, 50); } }" x-init="initSets()" class="fixed inset-0 z-50 overflow-y-auto" style="display: none;" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0">

Report Match Result

Enter final points (e.g., 10-7 or 12-10)

Winner

Confirm Match Result

vs
Played on