🎨 Improve code readability by reformatting and updating function definitions and comments
This commit is contained in:
65
code/websites/pokedex.online/src/utilities/constants.js
Normal file
65
code/websites/pokedex.online/src/utilities/constants.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Application Constants
|
||||
* Centralized configuration values for the Pokedex Online application
|
||||
*/
|
||||
|
||||
export const API_CONFIG = {
|
||||
CHALLONGE_BASE_URL: 'https://api.challonge.com/v1/',
|
||||
TIMEOUT: 10000,
|
||||
RETRY_ATTEMPTS: 3
|
||||
};
|
||||
|
||||
export const UI_CONFIG = {
|
||||
TOAST_DURATION: 5000,
|
||||
DEBOUNCE_DELAY: 300,
|
||||
ITEMS_PER_PAGE: 50
|
||||
};
|
||||
|
||||
export const TOURNAMENT_TYPES = {
|
||||
SINGLE_ELIMINATION: 'single_elimination',
|
||||
DOUBLE_ELIMINATION: 'double_elimination',
|
||||
ROUND_ROBIN: 'round_robin',
|
||||
SWISS: 'swiss'
|
||||
};
|
||||
|
||||
export const TOURNAMENT_STATES = {
|
||||
PENDING: 'pending',
|
||||
CHECKING_IN: 'checking_in',
|
||||
CHECKED_IN: 'checked_in',
|
||||
UNDERWAY: 'underway',
|
||||
COMPLETE: 'complete'
|
||||
};
|
||||
|
||||
export const POKEMON_TYPES = {
|
||||
NORMAL: 'POKEMON_TYPE_NORMAL',
|
||||
FIRE: 'POKEMON_TYPE_FIRE',
|
||||
WATER: 'POKEMON_TYPE_WATER',
|
||||
ELECTRIC: 'POKEMON_TYPE_ELECTRIC',
|
||||
GRASS: 'POKEMON_TYPE_GRASS',
|
||||
ICE: 'POKEMON_TYPE_ICE',
|
||||
FIGHTING: 'POKEMON_TYPE_FIGHTING',
|
||||
POISON: 'POKEMON_TYPE_POISON',
|
||||
GROUND: 'POKEMON_TYPE_GROUND',
|
||||
FLYING: 'POKEMON_TYPE_FLYING',
|
||||
PSYCHIC: 'POKEMON_TYPE_PSYCHIC',
|
||||
BUG: 'POKEMON_TYPE_BUG',
|
||||
ROCK: 'POKEMON_TYPE_ROCK',
|
||||
GHOST: 'POKEMON_TYPE_GHOST',
|
||||
DRAGON: 'POKEMON_TYPE_DRAGON',
|
||||
DARK: 'POKEMON_TYPE_DARK',
|
||||
STEEL: 'POKEMON_TYPE_STEEL',
|
||||
FAIRY: 'POKEMON_TYPE_FAIRY'
|
||||
};
|
||||
|
||||
export const CSV_HEADERS = {
|
||||
PLAYER_ID: 'player_id',
|
||||
FIRST_NAME: 'first_name',
|
||||
LAST_NAME: 'last_name',
|
||||
COUNTRY_CODE: 'country_code',
|
||||
DIVISION: 'division',
|
||||
SCREENNAME: 'screenname',
|
||||
EMAIL: 'email',
|
||||
TOURNAMENT_ID: 'tournament_id'
|
||||
};
|
||||
|
||||
export const EXPECTED_CSV_HEADERS = Object.values(CSV_HEADERS);
|
||||
140
code/websites/pokedex.online/src/utilities/csv-utils.js
Normal file
140
code/websites/pokedex.online/src/utilities/csv-utils.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* CSV Parsing Utilities
|
||||
* Functions for parsing and validating CSV files (RK9 player registrations)
|
||||
*/
|
||||
|
||||
import { EXPECTED_CSV_HEADERS, CSV_HEADERS } from './constants.js';
|
||||
|
||||
/**
|
||||
* Validate CSV headers against expected format
|
||||
* @param {string[]} headers - Array of header names from CSV
|
||||
* @throws {Error} If headers are invalid or missing required fields
|
||||
*/
|
||||
export function validateCsvHeaders(headers) {
|
||||
if (!headers || headers.length === 0) {
|
||||
throw new Error('CSV file is missing headers');
|
||||
}
|
||||
|
||||
if (headers.length !== EXPECTED_CSV_HEADERS.length) {
|
||||
throw new Error(
|
||||
`Invalid CSV file headers: Expected ${EXPECTED_CSV_HEADERS.length} headers but found ${headers.length}`
|
||||
);
|
||||
}
|
||||
|
||||
const missingHeaders = EXPECTED_CSV_HEADERS.filter(
|
||||
expectedHeader => !headers.includes(expectedHeader)
|
||||
);
|
||||
|
||||
if (missingHeaders.length > 0) {
|
||||
throw new Error(
|
||||
`Invalid CSV file headers: Missing the following headers: ${missingHeaders.join(', ')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CSV text content into structured player data
|
||||
* @param {string} csvData - Raw CSV file content
|
||||
* @returns {Object} Object keyed by screenname with player data
|
||||
* @throws {Error} If CSV format is invalid
|
||||
*/
|
||||
export function parseCsv(csvData) {
|
||||
const rows = csvData
|
||||
.split('\n')
|
||||
.map(row => row.split(','))
|
||||
.filter(row => row.some(cell => cell.trim() !== ''));
|
||||
|
||||
if (rows.length === 0) {
|
||||
throw new Error('CSV file is empty');
|
||||
}
|
||||
|
||||
const headers = rows[0].map(header => header.trim());
|
||||
validateCsvHeaders(headers);
|
||||
|
||||
// Validate row format
|
||||
for (let i = 1; i < rows.length; i++) {
|
||||
if (rows[i].length !== EXPECTED_CSV_HEADERS.length) {
|
||||
throw new Error(`Invalid row format at line ${i + 1}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse rows into objects
|
||||
return rows.slice(1).reduce((acc, row) => {
|
||||
const participant = {};
|
||||
EXPECTED_CSV_HEADERS.forEach((header, idx) => {
|
||||
participant[header] = row[idx]?.trim();
|
||||
});
|
||||
acc[participant[CSV_HEADERS.SCREENNAME]] = participant;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CSV file from browser File API
|
||||
* @param {File} file - File object from input[type=file]
|
||||
* @returns {Promise<Object>} Parsed player data
|
||||
*/
|
||||
export async function parsePlayerCsvFile(file) {
|
||||
if (!file) {
|
||||
throw new Error('No file provided');
|
||||
}
|
||||
|
||||
if (!file.name.endsWith('.csv')) {
|
||||
throw new Error('File must be a CSV file');
|
||||
}
|
||||
|
||||
const text = await file.text();
|
||||
return parseCsv(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert parsed CSV data to array format
|
||||
* @param {Object} csvObject - Object from parseCsv
|
||||
* @returns {Array} Array of player objects with screenname included
|
||||
*/
|
||||
export function csvObjectToArray(csvObject) {
|
||||
return Object.entries(csvObject).map(([screenname, data]) => ({
|
||||
...data,
|
||||
screenname
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate individual player data
|
||||
* @param {Object} player - Player data object
|
||||
* @returns {Object} Validation result {valid: boolean, errors: string[]}
|
||||
*/
|
||||
export function validatePlayerData(player) {
|
||||
const errors = [];
|
||||
|
||||
if (!player[CSV_HEADERS.PLAYER_ID]) {
|
||||
errors.push('Missing player_id');
|
||||
}
|
||||
|
||||
if (!player[CSV_HEADERS.SCREENNAME]) {
|
||||
errors.push('Missing screenname');
|
||||
}
|
||||
|
||||
if (!player[CSV_HEADERS.DIVISION]) {
|
||||
errors.push('Missing division');
|
||||
}
|
||||
|
||||
const email = player[CSV_HEADERS.EMAIL];
|
||||
if (email && !isValidEmail(email)) {
|
||||
errors.push('Invalid email format');
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple email validation
|
||||
* @param {string} email - Email address to validate
|
||||
* @returns {boolean} True if email format is valid
|
||||
*/
|
||||
function isValidEmail(email) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
}
|
||||
51
code/websites/pokedex.online/src/utilities/debug.js
Normal file
51
code/websites/pokedex.online/src/utilities/debug.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Debug Logging Utility
|
||||
*
|
||||
* Provides simple debug logging that can be toggled via environment variable
|
||||
* or browser console: localStorage.setItem('DEBUG', '1')
|
||||
*
|
||||
* Usage:
|
||||
* import { debug } from '../utilities/debug.js'
|
||||
* debug('info', 'message', data)
|
||||
* debug('error', 'message', error)
|
||||
*/
|
||||
|
||||
const DEBUG_ENABLED = () => {
|
||||
// Check environment variable
|
||||
if (import.meta.env.VITE_DEBUG === 'true') {
|
||||
return true;
|
||||
}
|
||||
// Check localStorage for quick toggle in browser
|
||||
try {
|
||||
return localStorage.getItem('DEBUG') === '1';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export function debug(level, message, data = null) {
|
||||
if (!DEBUG_ENABLED()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const prefix = `[${timestamp}] ${level.toUpperCase()}:`;
|
||||
|
||||
if (data) {
|
||||
console[level === 'error' ? 'error' : 'log'](prefix, message, data);
|
||||
} else {
|
||||
console[level === 'error' ? 'error' : 'log'](prefix, message);
|
||||
}
|
||||
}
|
||||
|
||||
export function debugInfo(message, data = null) {
|
||||
debug('info', message, data);
|
||||
}
|
||||
|
||||
export function debugError(message, error = null) {
|
||||
debug('error', message, error);
|
||||
}
|
||||
|
||||
export function debugWarn(message, data = null) {
|
||||
debug('warn', message, data);
|
||||
}
|
||||
139
code/websites/pokedex.online/src/utilities/gamemaster-utils.js
Normal file
139
code/websites/pokedex.online/src/utilities/gamemaster-utils.js
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Gamemaster Utilities
|
||||
* Functions for fetching and processing PokeMiners gamemaster data
|
||||
*/
|
||||
|
||||
const POKEMINERS_GAMEMASTER_URL =
|
||||
'https://raw.githubusercontent.com/PokeMiners/game_masters/master/latest/latest.json';
|
||||
|
||||
/**
|
||||
* Fetch latest gamemaster data from PokeMiners GitHub
|
||||
* @returns {Promise<Array>} Gamemaster data array
|
||||
*/
|
||||
export async function fetchLatestGamemaster() {
|
||||
try {
|
||||
const response = await fetch(POKEMINERS_GAMEMASTER_URL);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch gamemaster: ${response.statusText}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error fetching gamemaster:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Break up gamemaster into separate categories
|
||||
* @param {Array} gamemaster - Full gamemaster data
|
||||
* @returns {Object} Separated data {pokemon, pokemonAllForms, moves}
|
||||
*/
|
||||
export function breakUpGamemaster(gamemaster) {
|
||||
const regionCheck = ['alola', 'galarian', 'hisuian', 'paldea'];
|
||||
|
||||
const result = gamemaster.reduce(
|
||||
(acc, item) => {
|
||||
const templateId = item.templateId;
|
||||
|
||||
// POKEMON FILTER
|
||||
// If the templateId begins with 'V' AND includes 'pokemon'
|
||||
if (
|
||||
templateId.startsWith('V') &&
|
||||
templateId.toLowerCase().includes('pokemon')
|
||||
) {
|
||||
const pokemonSettings = item.data?.pokemonSettings;
|
||||
const pokemonId = pokemonSettings?.pokemonId;
|
||||
|
||||
// Add to allFormsCostumes (includes everything)
|
||||
acc.pokemonAllForms.push(item);
|
||||
|
||||
// Add to pokemon (filtered - first occurrence OR regional forms)
|
||||
if (
|
||||
!acc.pokemonSeen.has(pokemonId) ||
|
||||
(acc.pokemonSeen.has(pokemonId) &&
|
||||
regionCheck.includes(
|
||||
pokemonSettings?.form?.split('_')[1]?.toLowerCase()
|
||||
))
|
||||
) {
|
||||
acc.pokemonSeen.add(pokemonId);
|
||||
acc.pokemon.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// POKEMON MOVE FILTER
|
||||
if (
|
||||
templateId.startsWith('V') &&
|
||||
templateId.toLowerCase().includes('move')
|
||||
) {
|
||||
const moveSettings = item.data?.moveSettings;
|
||||
const moveId = moveSettings?.movementId;
|
||||
if (!acc.moveSeen.has(moveId)) {
|
||||
acc.moveSeen.add(moveId);
|
||||
acc.moves.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
pokemon: [],
|
||||
pokemonAllForms: [],
|
||||
moves: [],
|
||||
pokemonSeen: new Set(),
|
||||
moveSeen: new Set()
|
||||
}
|
||||
);
|
||||
|
||||
// Clean up the Sets before returning
|
||||
delete result.pokemonSeen;
|
||||
delete result.moveSeen;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download JSON data as a file
|
||||
* @param {Object|Array} data - Data to download
|
||||
* @param {string} filename - Filename for download
|
||||
*/
|
||||
export function downloadJson(data, filename) {
|
||||
const json = JSON.stringify(data, null, 2);
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate file size in MB
|
||||
* @param {Object|Array} data - Data to measure
|
||||
* @returns {string} Size in MB formatted
|
||||
*/
|
||||
export function calculateFileSize(data) {
|
||||
const json = JSON.stringify(data);
|
||||
const bytes = new Blob([json]).size;
|
||||
const mb = bytes / (1024 * 1024);
|
||||
return `${mb.toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statistics about gamemaster data
|
||||
* @param {Object} brokenUpData - Result from breakUpGamemaster
|
||||
* @returns {Object} Statistics
|
||||
*/
|
||||
export function getGamemasterStats(brokenUpData) {
|
||||
return {
|
||||
pokemonCount: brokenUpData.pokemon.length,
|
||||
allFormsCount: brokenUpData.pokemonAllForms.length,
|
||||
movesCount: brokenUpData.moves.length,
|
||||
pokemonSize: calculateFileSize(brokenUpData.pokemon),
|
||||
allFormsSize: calculateFileSize(brokenUpData.pokemonAllForms),
|
||||
movesSize: calculateFileSize(brokenUpData.moves)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Participant Model
|
||||
* Represents a tournament participant with Challonge and RK9 data
|
||||
*/
|
||||
|
||||
export class ParticipantModel {
|
||||
constructor(participant = {}) {
|
||||
// Challonge Data
|
||||
this.id = participant.id;
|
||||
this.tournamentId = participant.tournament_id;
|
||||
this.name = participant.name;
|
||||
this.seed = participant.seed || 0;
|
||||
this.misc = participant.misc || ''; // Can store player ID or notes
|
||||
|
||||
// Status
|
||||
this.active = participant.active !== false;
|
||||
this.checkedIn = !!participant.checked_in_at;
|
||||
this.checkedInAt = participant.checked_in_at
|
||||
? new Date(participant.checked_in_at)
|
||||
: null;
|
||||
this.createdAt = participant.created_at
|
||||
? new Date(participant.created_at)
|
||||
: null;
|
||||
this.updatedAt = participant.updated_at
|
||||
? new Date(participant.updated_at)
|
||||
: null;
|
||||
|
||||
// Tournament Performance
|
||||
this.finalRank = participant.final_rank || null;
|
||||
this.wins = participant.wins || 0;
|
||||
this.losses = participant.losses || 0;
|
||||
this.ties = participant.ties || 0;
|
||||
|
||||
// RK9 Integration Data (merged after CSV import)
|
||||
this.rk9Data = participant.rk9Data || null;
|
||||
this.printIndex = participant.printIndex || null;
|
||||
|
||||
// Group/Pool assignment (for swiss/round robin)
|
||||
this.groupPlayerId = participant.group_player_ids?.[0] || null;
|
||||
|
||||
// Custom Data
|
||||
this.customFieldResponses = participant.custom_field_responses || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full player name from RK9 data if available
|
||||
* @returns {string}
|
||||
*/
|
||||
getFullName() {
|
||||
if (this.rk9Data?.first_name && this.rk9Data?.last_name) {
|
||||
return `${this.rk9Data.first_name} ${this.rk9Data.last_name}`;
|
||||
}
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get player division from RK9 data
|
||||
* @returns {string}
|
||||
*/
|
||||
getDivision() {
|
||||
return this.rk9Data?.division || 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get player email from RK9 data
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getEmail() {
|
||||
return this.rk9Data?.email || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get player ID from RK9 data
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getPlayerId() {
|
||||
return this.rk9Data?.player_id || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate win rate
|
||||
* @returns {number} Win rate as decimal (0-1)
|
||||
*/
|
||||
getWinRate() {
|
||||
const total = this.wins + this.losses + this.ties;
|
||||
return total > 0 ? this.wins / total : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total matches played
|
||||
* @returns {number}
|
||||
*/
|
||||
getMatchesPlayed() {
|
||||
return this.wins + this.losses + this.ties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if participant has RK9 registration data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasRegistrationData() {
|
||||
return !!this.rk9Data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if participant is checked in
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isCheckedIn() {
|
||||
return this.checkedIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if participant is still active in tournament
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isActive() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get match record string (W-L-T)
|
||||
* @returns {string}
|
||||
*/
|
||||
getRecord() {
|
||||
return `${this.wins}-${this.losses}-${this.ties}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format participant data for display
|
||||
* @returns {Object}
|
||||
*/
|
||||
toDisplayFormat() {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
fullName: this.getFullName(),
|
||||
seed: this.seed,
|
||||
division: this.getDivision(),
|
||||
record: this.getRecord(),
|
||||
winRate: Math.round(this.getWinRate() * 100),
|
||||
rank: this.finalRank,
|
||||
checkedIn: this.checkedIn,
|
||||
hasRegistration: this.hasRegistrationData()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate participant data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isValid() {
|
||||
return !!(this.id && this.name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Pokemon Model
|
||||
* Represents Pokemon data from PokeMiners gamemaster files
|
||||
*/
|
||||
|
||||
import {
|
||||
extractPokedexNumber,
|
||||
pokemonIdToDisplayName,
|
||||
formatPokemonType
|
||||
} from '../string-utils.js';
|
||||
|
||||
export class PokemonModel {
|
||||
constructor(pokemonData = {}) {
|
||||
const settings = pokemonData.data?.pokemonSettings || {};
|
||||
|
||||
// Identity
|
||||
this.templateId = pokemonData.templateId;
|
||||
this.pokemonId = settings.pokemonId;
|
||||
this.form = settings.form;
|
||||
this.dexNumber = extractPokedexNumber(this.templateId);
|
||||
|
||||
// Types
|
||||
this.type = settings.type;
|
||||
this.type2 = settings.type2 || null;
|
||||
|
||||
// Base Stats
|
||||
this.stats = {
|
||||
hp: settings.stats?.baseStamina || 0,
|
||||
atk: settings.stats?.baseAttack || 0,
|
||||
def: settings.stats?.baseDefense || 0
|
||||
};
|
||||
|
||||
// Moves
|
||||
this.quickMoves = settings.quickMoves || [];
|
||||
this.cinematicMoves = settings.cinematicMoves || [];
|
||||
this.eliteQuickMoves = settings.eliteQuickMove || [];
|
||||
this.eliteCinematicMoves = settings.eliteCinematicMove || [];
|
||||
|
||||
// Evolution
|
||||
this.evolutionIds = settings.evolutionIds || [];
|
||||
this.evolutionBranch = settings.evolutionBranch || [];
|
||||
this.candyToEvolve = settings.candyToEvolve || 0;
|
||||
this.familyId = settings.familyId;
|
||||
|
||||
// Pokedex Info
|
||||
this.heightM = settings.pokedexHeightM || 0;
|
||||
this.weightKg = settings.pokedexWeightKg || 0;
|
||||
|
||||
// Buddy System
|
||||
this.kmBuddyDistance = settings.kmBuddyDistance || 0;
|
||||
this.buddyScale = settings.buddyScale || 1;
|
||||
this.buddyPortraitOffset = settings.buddyPortraitOffset || [0, 0, 0];
|
||||
|
||||
// Camera Settings
|
||||
this.camera = {
|
||||
diskRadius: settings.camera?.diskRadiusM || 0,
|
||||
cylinderRadius: settings.camera?.cylinderRadiusM || 0,
|
||||
cylinderHeight: settings.camera?.cylinderHeightM || 0
|
||||
};
|
||||
|
||||
// Encounter Settings
|
||||
this.encounter = {
|
||||
baseCaptureRate: settings.encounter?.baseCaptureRate || 0,
|
||||
baseFleeRate: settings.encounter?.baseFleeRate || 0,
|
||||
collisionRadius: settings.encounter?.collisionRadiusM || 0,
|
||||
collisionHeight: settings.encounter?.collisionHeightM || 0,
|
||||
movementType: settings.encounter?.movementType || 'MOVEMENT_WALK'
|
||||
};
|
||||
|
||||
// Shadow Pokemon
|
||||
this.shadow = settings.shadow
|
||||
? {
|
||||
purificationStardustNeeded:
|
||||
settings.shadow.purificationStardustNeeded || 0,
|
||||
purificationCandyNeeded: settings.shadow.purificationCandyNeeded || 0,
|
||||
purifiedChargeMove: settings.shadow.purifiedChargeMove || null,
|
||||
shadowChargeMove: settings.shadow.shadowChargeMove || null
|
||||
}
|
||||
: null;
|
||||
|
||||
// Flags
|
||||
this.isTransferable = settings.isTransferable !== false;
|
||||
this.isTradable = settings.isTradable !== false;
|
||||
this.isDeployable = settings.isDeployable !== false;
|
||||
this.isMega = !!settings.tempEvoOverrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get display-friendly name
|
||||
* @returns {string}
|
||||
*/
|
||||
get displayName() {
|
||||
return pokemonIdToDisplayName(this.pokemonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted types for display
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get displayTypes() {
|
||||
const types = [formatPokemonType(this.type)];
|
||||
if (this.type2) {
|
||||
types.push(formatPokemonType(this.type2));
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total base stats
|
||||
* @returns {number}
|
||||
*/
|
||||
get totalStats() {
|
||||
return this.stats.hp + this.stats.atk + this.stats.def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Pokemon has an evolution
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasEvolution() {
|
||||
return this.evolutionBranch.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Pokemon is a shadow form
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isShadow() {
|
||||
return !!this.shadow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Pokemon has elite moves
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasEliteMoves() {
|
||||
return (
|
||||
this.eliteQuickMoves.length > 0 || this.eliteCinematicMoves.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available moves (quick + charged)
|
||||
* @returns {Object}
|
||||
*/
|
||||
getAllMoves() {
|
||||
return {
|
||||
quick: [...this.quickMoves, ...this.eliteQuickMoves],
|
||||
charged: [...this.cinematicMoves, ...this.eliteCinematicMoves]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Pokemon can mega evolve
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canMegaEvolve() {
|
||||
return this.isMega;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get evolution details
|
||||
* @returns {Array}
|
||||
*/
|
||||
getEvolutions() {
|
||||
return this.evolutionBranch.map(evo => ({
|
||||
evolution: evo.evolution,
|
||||
candyCost: evo.candyCost || 0,
|
||||
form: evo.form,
|
||||
itemRequirement: evo.evolutionItemRequirement || null
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format for display/export
|
||||
* @returns {Object}
|
||||
*/
|
||||
toDisplayFormat() {
|
||||
return {
|
||||
dexNumber: this.dexNumber,
|
||||
name: this.displayName,
|
||||
types: this.displayTypes,
|
||||
stats: this.stats,
|
||||
totalStats: this.totalStats,
|
||||
canEvolve: this.hasEvolution(),
|
||||
isShadow: this.isShadow(),
|
||||
hasEliteMoves: this.hasEliteMoves()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Pokemon data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isValid() {
|
||||
return !!(this.templateId && this.pokemonId && this.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Tournament Model
|
||||
* Normalizes Challonge tournament data into a structured object
|
||||
*/
|
||||
|
||||
import { TOURNAMENT_TYPES, TOURNAMENT_STATES } from '../constants.js';
|
||||
|
||||
export class TournamentModel {
|
||||
constructor(tournament = {}) {
|
||||
// Core Properties
|
||||
this.id = tournament.id;
|
||||
this.name = tournament.name;
|
||||
this.url = tournament.url;
|
||||
this.tournamentType =
|
||||
tournament.tournament_type || TOURNAMENT_TYPES.SINGLE_ELIMINATION;
|
||||
this.state = tournament.state || TOURNAMENT_STATES.PENDING;
|
||||
|
||||
// Scheduling
|
||||
this.startDate = tournament.start_at ? new Date(tournament.start_at) : null;
|
||||
this.startedAt = tournament.started_at
|
||||
? new Date(tournament.started_at)
|
||||
: null;
|
||||
this.completedAt = tournament.completed_at
|
||||
? new Date(tournament.completed_at)
|
||||
: null;
|
||||
this.checkInDuration = tournament.check_in_duration || 0;
|
||||
this.startedCheckingInAt = tournament.started_checking_in_at
|
||||
? new Date(tournament.started_checking_in_at)
|
||||
: null;
|
||||
|
||||
// Scoring Configuration
|
||||
this.pointsForMatchWin = parseFloat(tournament.pts_for_match_win) || 1.0;
|
||||
this.pointsForMatchTie = parseFloat(tournament.pts_for_match_tie) || 0.5;
|
||||
this.pointsForGameWin = parseFloat(tournament.pts_for_game_win) || 0.0;
|
||||
this.pointsForGameTie = parseFloat(tournament.pts_for_game_tie) || 0.0;
|
||||
this.pointsForBye = parseFloat(tournament.pts_for_bye) || 1.0;
|
||||
|
||||
// Swiss/Round Robin Settings
|
||||
this.swissRounds = tournament.swiss_rounds || 0;
|
||||
this.rankedBy = tournament.ranked_by || 'match wins';
|
||||
|
||||
// Participants
|
||||
this.participantsCount = tournament.participants_count || 0;
|
||||
this.signupCap = tournament.signup_cap || null;
|
||||
this.participants = tournament.participants || [];
|
||||
|
||||
// Matches
|
||||
this.matches = tournament.matches || [];
|
||||
|
||||
// Settings
|
||||
this.openSignup = tournament.open_signup || false;
|
||||
this.private = tournament.private || false;
|
||||
this.showRounds = tournament.show_rounds || false;
|
||||
this.sequentialPairings = tournament.sequential_pairings || false;
|
||||
this.acceptAttachments = tournament.accept_attachments || false;
|
||||
this.hideForum = tournament.hide_forum || false;
|
||||
this.notifyUsersWhenMatchesOpen =
|
||||
tournament.notify_users_when_matches_open || false;
|
||||
this.notifyUsersWhenTournamentEnds =
|
||||
tournament.notify_users_when_the_tournament_ends || false;
|
||||
|
||||
// Grand Finals
|
||||
this.grandFinalsModifier = tournament.grand_finals_modifier || null;
|
||||
this.holdThirdPlaceMatch = tournament.hold_third_place_match || false;
|
||||
|
||||
// Description
|
||||
this.description = tournament.description || '';
|
||||
this.subdomain = tournament.subdomain || null;
|
||||
|
||||
// Full tournament URL
|
||||
this.fullUrl = this.subdomain
|
||||
? `https://${this.subdomain}.challonge.com/${this.url}`
|
||||
: `https://challonge.com/${this.url}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tournament is currently active
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isActive() {
|
||||
return (
|
||||
this.state === TOURNAMENT_STATES.UNDERWAY ||
|
||||
this.state === TOURNAMENT_STATES.CHECKING_IN ||
|
||||
this.state === TOURNAMENT_STATES.CHECKED_IN
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tournament is complete
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isComplete() {
|
||||
return this.state === TOURNAMENT_STATES.COMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tournament is accepting signups
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isAcceptingSignups() {
|
||||
return this.openSignup && this.state === TOURNAMENT_STATES.PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tournament has reached signup cap
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isAtCapacity() {
|
||||
if (!this.signupCap) return false;
|
||||
return this.participantsCount >= this.signupCap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tournament duration in milliseconds
|
||||
* @returns {number|null}
|
||||
*/
|
||||
getDuration() {
|
||||
if (!this.startedAt || !this.completedAt) return null;
|
||||
return this.completedAt.getTime() - this.startedAt.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format duration as human-readable string
|
||||
* @returns {string}
|
||||
*/
|
||||
getFormattedDuration() {
|
||||
const duration = this.getDuration();
|
||||
if (!duration) return 'N/A';
|
||||
|
||||
const hours = Math.floor(duration / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes}m`;
|
||||
}
|
||||
return `${minutes}m`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate tournament data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isValid() {
|
||||
return !!(this.id && this.name && this.tournamentType);
|
||||
}
|
||||
}
|
||||
129
code/websites/pokedex.online/src/utilities/participant-utils.js
Normal file
129
code/websites/pokedex.online/src/utilities/participant-utils.js
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Participant Utility Functions
|
||||
* Functions for merging and managing tournament participant data
|
||||
*/
|
||||
|
||||
import { normalizeScreenname } from './string-utils.js';
|
||||
|
||||
/**
|
||||
* Merge RK9 registration data with Challonge tournament participants
|
||||
* Matches participants by normalized screenname
|
||||
* @param {Object} rk9Participants - Object of RK9 players keyed by screenname
|
||||
* @param {Object} participantsById - Object of Challonge participants keyed by ID
|
||||
* @returns {Object} {participantsById: merged data, issues: unmatched names}
|
||||
*/
|
||||
export function mergeRK9Participants(rk9Participants, participantsById) {
|
||||
// Create normalized lookup map for RK9 data
|
||||
const normalizedRK9 = Object.fromEntries(
|
||||
Object.entries(rk9Participants).map(([key, value]) => [
|
||||
normalizeScreenname(key),
|
||||
value
|
||||
])
|
||||
);
|
||||
|
||||
// Match Challonge participants to RK9 data
|
||||
Object.values(participantsById).forEach(participant => {
|
||||
const normalized = normalizeScreenname(participant.name);
|
||||
const rk9Participant = normalizedRK9[normalized];
|
||||
|
||||
if (rk9Participant) {
|
||||
participant.rk9Data = rk9Participant;
|
||||
// Track print order based on RK9 registration order
|
||||
participant.printIndex =
|
||||
Object.keys(normalizedRK9).indexOf(normalized) + 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Collect participants without rk9Data for reporting issues
|
||||
const issues = Object.values(participantsById).reduce((acc, participant) => {
|
||||
if (!participant.rk9Data) {
|
||||
acc.push(participant.name);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return { participantsById, issues };
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort participants by seed number
|
||||
* @param {Array} participants - Array of participant objects
|
||||
* @returns {Array} Sorted array
|
||||
*/
|
||||
export function sortParticipantsBySeed(participants) {
|
||||
return [...participants].sort((a, b) => a.seed - b.seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort participants by final rank
|
||||
* @param {Array} participants - Array of participant objects
|
||||
* @returns {Array} Sorted array
|
||||
*/
|
||||
export function sortParticipantsByRank(participants) {
|
||||
return [...participants].sort((a, b) => {
|
||||
// Handle null ranks (participants who didn't finish)
|
||||
if (a.final_rank === null) return 1;
|
||||
if (b.final_rank === null) return -1;
|
||||
return a.final_rank - b.final_rank;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Group participants by division (from RK9 data)
|
||||
* @param {Array} participants - Array of participant objects with rk9Data
|
||||
* @returns {Object} Object keyed by division name
|
||||
*/
|
||||
export function groupParticipantsByDivision(participants) {
|
||||
return participants.reduce((acc, participant) => {
|
||||
const division = participant.rk9Data?.division || 'Unknown';
|
||||
if (!acc[division]) {
|
||||
acc[division] = [];
|
||||
}
|
||||
acc[division].push(participant);
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate participant statistics
|
||||
* @param {Object} participant - Participant object with match history
|
||||
* @returns {Object} Statistics {wins, losses, ties, winRate, matchesPlayed}
|
||||
*/
|
||||
export function calculateParticipantStats(participant) {
|
||||
const wins = participant.wins || 0;
|
||||
const losses = participant.losses || 0;
|
||||
const ties = participant.ties || 0;
|
||||
const matchesPlayed = wins + losses + ties;
|
||||
const winRate = matchesPlayed > 0 ? wins / matchesPlayed : 0;
|
||||
|
||||
return {
|
||||
wins,
|
||||
losses,
|
||||
ties,
|
||||
matchesPlayed,
|
||||
winRate: Math.round(winRate * 100) / 100 // Round to 2 decimals
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find participant by name (case-insensitive, normalized)
|
||||
* @param {Array} participants - Array of participant objects
|
||||
* @param {string} searchName - Name to search for
|
||||
* @returns {Object|null} Found participant or null
|
||||
*/
|
||||
export function findParticipantByName(participants, searchName) {
|
||||
const normalizedSearch = normalizeScreenname(searchName);
|
||||
return participants.find(
|
||||
p => normalizeScreenname(p.name) === normalizedSearch
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter participants by check-in status
|
||||
* @param {Array} participants - Array of participant objects
|
||||
* @param {boolean} checkedIn - Filter for checked-in (true) or not checked-in (false)
|
||||
* @returns {Array} Filtered participants
|
||||
*/
|
||||
export function filterByCheckInStatus(participants, checkedIn) {
|
||||
return participants.filter(p => !!p.checked_in_at === checkedIn);
|
||||
}
|
||||
75
code/websites/pokedex.online/src/utilities/string-utils.js
Normal file
75
code/websites/pokedex.online/src/utilities/string-utils.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* String Utility Functions
|
||||
* Common string manipulation and normalization utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Normalize a screenname for reliable matching
|
||||
* Removes all non-alphanumeric characters and converts to lowercase
|
||||
* @param {string} name - The screenname to normalize
|
||||
* @returns {string} Normalized screenname
|
||||
*/
|
||||
export function normalizeScreenname(name) {
|
||||
if (!name) return '';
|
||||
return name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Pokemon ID format to display name
|
||||
* Example: "BULBASAUR" -> "Bulbasaur"
|
||||
* Example: "IVYSAUR_NORMAL" -> "Ivysaur"
|
||||
* @param {string} pokemonId - Pokemon ID from gamemaster
|
||||
* @returns {string} Display-friendly name
|
||||
*/
|
||||
export function pokemonIdToDisplayName(pokemonId) {
|
||||
if (!pokemonId) return '';
|
||||
|
||||
// Remove form suffix (e.g., "_NORMAL", "_ALOLA")
|
||||
const baseName = pokemonId.split('_')[0];
|
||||
|
||||
return baseName.toLowerCase().replace(/\b\w/g, char => char.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract Pokedex number from template ID
|
||||
* Example: "V0001_POKEMON_BULBASAUR" -> 1
|
||||
* @param {string} templateId - Template ID from gamemaster
|
||||
* @returns {number|null} Pokedex number or null if not found
|
||||
*/
|
||||
export function extractPokedexNumber(templateId) {
|
||||
if (!templateId) return null;
|
||||
const match = templateId.match(/V(\d{4})/);
|
||||
return match ? parseInt(match[1], 10) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Pokemon type for display
|
||||
* Example: "POKEMON_TYPE_GRASS" -> "Grass"
|
||||
* @param {string} type - Type from gamemaster
|
||||
* @returns {string} Display-friendly type name
|
||||
*/
|
||||
export function formatPokemonType(type) {
|
||||
if (!type) return '';
|
||||
return type
|
||||
.replace('POKEMON_TYPE_', '')
|
||||
.toLowerCase()
|
||||
.replace(/\b\w/g, char => char.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Debounce function execution
|
||||
* @param {Function} func - Function to debounce
|
||||
* @param {number} wait - Milliseconds to wait
|
||||
* @returns {Function} Debounced function
|
||||
*/
|
||||
export function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
@@ -15,15 +15,15 @@
|
||||
* Makes three parallel API calls (pending, in_progress, ended) and combines
|
||||
* the results while deduplicating by tournament ID.
|
||||
*
|
||||
* @param {Object} client - Challonge API client (from createChallongeV2Client)
|
||||
* @param {Object} [options] - Query options
|
||||
* @param {string} [options.scopeType] - USER, COMMUNITY, or APPLICATION scope (default: USER)
|
||||
* @param {string} [options.communityId] - Community ID (if using COMMUNITY scope)
|
||||
* @param {number} [options.page] - Page number (default: 1)
|
||||
* @param {number} [options.per_page] - Results per page (default: 25)
|
||||
* @param {string[]} [options.states] - States to query (default: ['pending', 'in_progress', 'ended'])
|
||||
* @param {boolean} [options.includeCommunities] - Also query community tournaments (default: false)
|
||||
* @returns {Promise<any[]>} Combined and deduplicated tournament list
|
||||
* @param client - Challonge API client (from createChallongeV2Client)
|
||||
* @param options - Query options
|
||||
* @param options.scopeType - USER, COMMUNITY, or APPLICATION scope (default: USER)
|
||||
* @param options.communityId - Community ID (if using COMMUNITY scope)
|
||||
* @param options.page - Page number (default: 1)
|
||||
* @param options.per_page - Results per page (default: 25)
|
||||
* @param options.states - States to query (default: full Challonge state list)
|
||||
* @param options.includeCommunities - Also query community tournaments (default: false)
|
||||
* @returns Combined and deduplicated tournament list
|
||||
*
|
||||
* @example
|
||||
* import { queryAllTournaments } from '../utilities/tournament-query.js'
|
||||
@@ -39,7 +39,17 @@ export async function queryAllTournaments(client, options = {}) {
|
||||
communityId,
|
||||
page = 1,
|
||||
per_page = 25,
|
||||
states = ['pending', 'in_progress', 'ended'],
|
||||
states = [
|
||||
'pending',
|
||||
'checking_in',
|
||||
'checked_in',
|
||||
'accepting_predictions',
|
||||
'group_stages_underway',
|
||||
'group_stages_finalized',
|
||||
'underway',
|
||||
'awaiting_review',
|
||||
'complete'
|
||||
],
|
||||
includeCommunities = false
|
||||
} = options;
|
||||
|
||||
@@ -53,13 +63,15 @@ export async function queryAllTournaments(client, options = {}) {
|
||||
|
||||
// Query all states in parallel
|
||||
const promises = states.map(state =>
|
||||
client.tournaments.list({
|
||||
...baseOptions,
|
||||
state
|
||||
}).catch((err) => {
|
||||
console.error(`Error querying ${state} tournaments:`, err);
|
||||
return [];
|
||||
})
|
||||
client.tournaments
|
||||
.list({
|
||||
...baseOptions,
|
||||
state
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(`Error querying ${state} tournaments:`, err);
|
||||
return [];
|
||||
})
|
||||
);
|
||||
|
||||
// Wait for all requests
|
||||
@@ -69,7 +81,7 @@ export async function queryAllTournaments(client, options = {}) {
|
||||
const tournamentMap = new Map();
|
||||
results.forEach(tournamentArray => {
|
||||
if (Array.isArray(tournamentArray)) {
|
||||
tournamentArray.forEach((tournament) => {
|
||||
tournamentArray.forEach(tournament => {
|
||||
// Handle both v1 and v2.1 response formats
|
||||
const id = tournament.id || tournament.tournament?.id;
|
||||
if (id && !tournamentMap.has(id)) {
|
||||
@@ -88,9 +100,9 @@ export async function queryAllTournaments(client, options = {}) {
|
||||
* For the USER scope, the Challonge API returns both created and admin tournaments,
|
||||
* but optionally query across all states for completeness.
|
||||
*
|
||||
* @param {Object} client - Challonge API client
|
||||
* @param {Object} [options] - Query options (same as queryAllTournaments)
|
||||
* @returns {Promise<any[]>} User's created and admin tournaments
|
||||
* @param client - Challonge API client
|
||||
* @param options - Query options (same as queryAllTournaments)
|
||||
* @returns User's created and admin tournaments
|
||||
*/
|
||||
export async function queryUserTournaments(client, options = {}) {
|
||||
return queryAllTournaments(client, {
|
||||
@@ -102,12 +114,16 @@ export async function queryUserTournaments(client, options = {}) {
|
||||
/**
|
||||
* Query all tournaments in a community (all states)
|
||||
*
|
||||
* @param {Object} client - Challonge API client
|
||||
* @param {string} communityId - Community numeric ID
|
||||
* @param {Object} [options] - Query options
|
||||
* @returns {Promise<any[]>} Community tournaments across all states
|
||||
* @param client - Challonge API client
|
||||
* @param communityId - Community numeric ID
|
||||
* @param options - Query options
|
||||
* @returns Community tournaments across all states
|
||||
*/
|
||||
export async function queryCommunityTournaments(client, communityId, options = {}) {
|
||||
export async function queryCommunityTournaments(
|
||||
client,
|
||||
communityId,
|
||||
options = {}
|
||||
) {
|
||||
return queryAllTournaments(client, {
|
||||
...options,
|
||||
scopeType: 'COMMUNITY',
|
||||
@@ -121,10 +137,10 @@ export async function queryCommunityTournaments(client, communityId, options = {
|
||||
* Useful if you only care about specific states or want to use
|
||||
* a different set of states than the default.
|
||||
*
|
||||
* @param {Object} client - Challonge API client
|
||||
* @param {string[]} states - States to query (e.g., ['pending', 'in_progress'])
|
||||
* @param {Object} [options] - Query options
|
||||
* @returns {Promise<any[]>} Tournaments matching the given states
|
||||
* @param client - Challonge API client
|
||||
* @param states - States to query (e.g., ['pending', 'in_progress'])
|
||||
* @param options - Query options
|
||||
* @returns Tournaments matching the given states
|
||||
*/
|
||||
export async function queryTournamentsByStates(client, states, options = {}) {
|
||||
return queryAllTournaments(client, {
|
||||
@@ -136,9 +152,9 @@ export async function queryTournamentsByStates(client, states, options = {}) {
|
||||
/**
|
||||
* Query active tournaments only (pending + in_progress)
|
||||
*
|
||||
* @param {Object} client - Challonge API client
|
||||
* @param {Object} [options] - Query options
|
||||
* @returns {Promise<any[]>} Active tournaments
|
||||
* @param client - Challonge API client
|
||||
* @param options - Query options
|
||||
* @returns Active tournaments
|
||||
*/
|
||||
export async function queryActiveTournaments(client, options = {}) {
|
||||
return queryTournamentsByStates(client, ['pending', 'in_progress'], options);
|
||||
@@ -147,9 +163,9 @@ export async function queryActiveTournaments(client, options = {}) {
|
||||
/**
|
||||
* Query completed tournaments only (ended)
|
||||
*
|
||||
* @param {Object} client - Challonge API client
|
||||
* @param {Object} [options] - Query options
|
||||
* @returns {Promise<any[]>} Completed tournaments
|
||||
* @param client - Challonge API client
|
||||
* @param options - Query options
|
||||
* @returns Completed tournaments
|
||||
*/
|
||||
export async function queryCompletedTournaments(client, options = {}) {
|
||||
return queryTournamentsByStates(client, ['ended'], options);
|
||||
|
||||
Reference in New Issue
Block a user