"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">
Enter final points (e.g., 10-7 or 12-10)