diff --git a/code/websites/pokedex.online/src/composables/useChallongeTests.js b/code/websites/pokedex.online/src/composables/useChallongeTests.js new file mode 100644 index 0000000..1070e11 --- /dev/null +++ b/code/websites/pokedex.online/src/composables/useChallongeTests.js @@ -0,0 +1,268 @@ +/** + * Challonge Tests Composable + * + * Manages tournament testing operations including: + * - Loading tournament lists with pagination + * - Tournament detail fetching + * - Search/filtering + * - Helper utilities for tournament data access + * + * @example + * ```js + * const { + * tournaments, + * loading, + * error, + * testListTournaments, + * loadMoreTournaments, + * toggleTournamentDetails + * } = useChallongeTests(client, apiVersion, tournamentScope); + * + * await testListTournaments(); + * ``` + */ + +import { ref, computed } from 'vue'; +import { useAsyncState } from './useAsyncState.js'; +import { queryAllTournaments } from '../utilities/tournament-query.js'; + +export function useChallongeTests(client, apiVersion, tournamentScope) { + // Async state management + const tournamentListState = useAsyncState(); + const loadMoreState = useAsyncState(); + const tournamentDetailsState = useAsyncState(); + + // Destructure for easier access + const { data: tournaments, isLoading: loading, error } = tournamentListState; + const { isLoading: loadingMore } = loadMoreState; + + // Search and filter + const searchQuery = ref(''); + const expandedTournamentId = ref(null); + + // Pagination state + const currentPage = ref(1); + const perPage = ref(100); + const totalTournaments = ref(0); + const hasNextPage = ref(false); + + /** + * Pagination info string + */ + const paginationInfo = computed(() => { + if (!tournaments.value) return ''; + const start = (currentPage.value - 1) * perPage.value + 1; + const end = Math.min( + start + tournaments.value.length - 1, + totalTournaments.value || tournaments.value.length + ); + const total = totalTournaments.value || tournaments.value.length; + return `Showing ${start}-${end} of ${total}`; + }); + + /** + * Filtered tournaments based on search query + */ + const filteredTournaments = computed(() => { + if (!tournaments.value) return null; + if (!searchQuery.value.trim()) return tournaments.value; + + const query = searchQuery.value.toLowerCase(); + return tournaments.value.filter(t => { + const name = getTournamentName(t).toLowerCase(); + return name.includes(query); + }); + }); + + /** + * Tournament details from async state + */ + const tournamentDetails = computed(() => tournamentDetailsState.data.value); + + /** + * Helper to get tournament name (handles both v1 and v2.1 structures) + */ + function getTournamentName(tournament) { + return tournament.tournament?.name || tournament.name || ''; + } + + /** + * Helper to get tournament ID + */ + function getTournamentId(tournament) { + return tournament.tournament?.id || tournament.id; + } + + /** + * Helper to get tournament property + */ + function getTournamentProp(tournament, prop) { + return tournament.tournament?.[prop] || tournament[prop]; + } + + /** + * Test listing tournaments with pagination support + */ + async function testListTournaments(resetPagination = true) { + if (!client.value) { + console.error('No API client available'); + return; + } + + if (resetPagination) { + currentPage.value = 1; + searchQuery.value = ''; + expandedTournamentId.value = null; + tournamentDetailsState.reset(); + } + + await tournamentListState.execute(async () => { + if (apiVersion.value === 'v1') { + // v1 doesn't support pagination + const result = await client.value.tournaments.list(); + totalTournaments.value = result.length; + hasNextPage.value = false; + return result; + } else { + // v2.1 - Query all tournament states in parallel + const result = await queryAllTournaments(client.value, { + page: currentPage.value, + per_page: perPage.value, + scopeType: tournamentScope.value + }); + + console.log('📊 Tournament API Response:', { + page: currentPage.value, + perPage: perPage.value, + scope: tournamentScope.value, + resultsCount: result.length + }); + + totalTournaments.value = result.length; + hasNextPage.value = result.length >= perPage.value; + return result; + } + }); + } + + /** + * Load more tournaments (pagination) + */ + async function loadMoreTournaments() { + if (apiVersion.value === 'v1') return; // v1 doesn't support pagination + if (!client.value) return; + + currentPage.value++; + + const result = await loadMoreState.execute(async () => { + const newResults = await queryAllTournaments(client.value, { + page: currentPage.value, + per_page: perPage.value, + scopeType: tournamentScope.value + }); + + hasNextPage.value = newResults.length === perPage.value; + return newResults; + }); + + if (result) { + // Append new results to existing tournaments + tournaments.value = [...tournaments.value, ...result]; + } else { + // Revert page increment on error + currentPage.value--; + } + } + + /** + * Change results per page + */ + async function changePerPage(newLimit) { + perPage.value = newLimit; + await testListTournaments(true); + } + + /** + * Toggle tournament details view + */ + async function toggleTournamentDetails(tournamentId) { + if (!client.value) return; + + if (expandedTournamentId.value === tournamentId) { + expandedTournamentId.value = null; + tournamentDetailsState.reset(); + return; + } + + expandedTournamentId.value = tournamentId; + + await tournamentDetailsState.execute(async () => { + if (apiVersion.value === 'v1') { + return await client.value.tournaments.get(tournamentId, { + includeParticipants: true, + includeMatches: true + }); + } else { + // v2.1 get tournament + return await client.value.tournaments.get(tournamentId); + } + }); + + // Reset expanded state if there was an error + if (tournamentDetailsState.error.value) { + expandedTournamentId.value = null; + } + } + + /** + * Reset all test state + */ + function resetState() { + tournamentListState.reset(); + loadMoreState.reset(); + tournamentDetailsState.reset(); + searchQuery.value = ''; + expandedTournamentId.value = null; + currentPage.value = 1; + } + + /** + * Format date string + */ + function formatDate(dateString) { + if (!dateString) return ''; + return new Date(dateString).toLocaleString(); + } + + return { + // State + tournaments, + loading, + loadingMore, + error, + searchQuery, + expandedTournamentId, + currentPage, + perPage, + totalTournaments, + hasNextPage, + tournamentDetails, + + // Computed + paginationInfo, + filteredTournaments, + + // Methods + testListTournaments, + loadMoreTournaments, + changePerPage, + toggleTournamentDetails, + resetState, + + // Helpers + getTournamentName, + getTournamentId, + getTournamentProp, + formatDate + }; +}