mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
Add start.gg tournament and player import integration
This commit is contained in:
@@ -4,7 +4,7 @@ import { useHead } from '@unhead/vue';
|
||||
defineOptions({ name: 'PlayersView' });
|
||||
|
||||
import type { QTableColumn } from 'quasar';
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { countryOptions, getCountryLabel } from '../../../shared/countries';
|
||||
import type { Schemas } from '../../../types';
|
||||
import { usePlayersStore } from '../stores/players';
|
||||
@@ -18,6 +18,19 @@ interface PlayerRow extends Player {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface StartGGTournament {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
startAt: number | null;
|
||||
}
|
||||
|
||||
interface StartGGImportedPlayer extends Player {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const STARTGG_TOKEN_STORAGE_KEY = 'scoreko-dev.startgg-token';
|
||||
|
||||
const playersStore = usePlayersStore();
|
||||
const rows = computed<PlayerRow[]>(() => playersStore.rows);
|
||||
|
||||
@@ -73,6 +86,98 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const startGGToken = ref(localStorage.getItem(STARTGG_TOKEN_STORAGE_KEY) ?? '');
|
||||
const recentTournaments = ref<StartGGTournament[]>([]);
|
||||
const loadingTournaments = ref(false);
|
||||
const tournamentsError = ref('');
|
||||
const isImportDialogOpen = ref(false);
|
||||
const loadingTournamentPlayers = ref(false);
|
||||
const selectedTournament = ref<StartGGTournament | null>(null);
|
||||
const startGGPlayers = ref<StartGGImportedPlayer[]>([]);
|
||||
const selectedStartGGPlayerIds = ref<string[]>([]);
|
||||
|
||||
watch(startGGToken, (value) => {
|
||||
localStorage.setItem(STARTGG_TOKEN_STORAGE_KEY, value);
|
||||
});
|
||||
|
||||
const sendNodeCGMessage = <T>(messageName: string, payload: unknown): Promise<T> =>
|
||||
new Promise((resolve, reject) => {
|
||||
nodecg.sendMessage(messageName, payload, (error, response) => {
|
||||
if (error) {
|
||||
reject(new Error(String(error)));
|
||||
return;
|
||||
}
|
||||
resolve(response as T);
|
||||
});
|
||||
});
|
||||
|
||||
const loadRecentTournaments = async () => {
|
||||
const token = startGGToken.value.trim();
|
||||
if (!token) {
|
||||
tournamentsError.value = 'Añade tu token de start.gg para cargar torneos.';
|
||||
recentTournaments.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
tournamentsError.value = '';
|
||||
loadingTournaments.value = true;
|
||||
try {
|
||||
const tournaments = await sendNodeCGMessage<StartGGTournament[]>('startgg:fetchRecentTournaments', {
|
||||
token,
|
||||
});
|
||||
recentTournaments.value = tournaments;
|
||||
if (!tournaments.length) {
|
||||
tournamentsError.value = 'No hay torneos recientes para esta cuenta.';
|
||||
}
|
||||
} catch (error) {
|
||||
tournamentsError.value = error instanceof Error ? error.message : 'No se pudieron cargar torneos.';
|
||||
recentTournaments.value = [];
|
||||
} finally {
|
||||
loadingTournaments.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openStartGGImportDialog = async (tournament: StartGGTournament) => {
|
||||
selectedTournament.value = tournament;
|
||||
isImportDialogOpen.value = true;
|
||||
loadingTournamentPlayers.value = true;
|
||||
selectedStartGGPlayerIds.value = [];
|
||||
startGGPlayers.value = [];
|
||||
|
||||
try {
|
||||
const importedPlayers = await sendNodeCGMessage<StartGGImportedPlayer[]>('startgg:fetchTournamentPlayers', {
|
||||
token: startGGToken.value.trim(),
|
||||
slug: tournament.slug,
|
||||
});
|
||||
startGGPlayers.value = importedPlayers;
|
||||
selectedStartGGPlayerIds.value = importedPlayers.map((player) => player.id);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'No se pudieron cargar jugadores';
|
||||
window.alert(message);
|
||||
isImportDialogOpen.value = false;
|
||||
} finally {
|
||||
loadingTournamentPlayers.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const importSelectedStartGGPlayers = () => {
|
||||
const selectedPlayers = startGGPlayers.value.filter((player) =>
|
||||
selectedStartGGPlayerIds.value.includes(player.id),
|
||||
);
|
||||
|
||||
selectedPlayers.forEach((player) => {
|
||||
playersStore.upsertPlayer(player.id, {
|
||||
gamertag: player.gamertag,
|
||||
name: player.name,
|
||||
team: player.team,
|
||||
country: player.country,
|
||||
twitter: player.twitter,
|
||||
});
|
||||
});
|
||||
|
||||
isImportDialogOpen.value = false;
|
||||
};
|
||||
|
||||
const generateId = () => {
|
||||
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
||||
return crypto.randomUUID();
|
||||
@@ -141,6 +246,12 @@ const handleImport = async (event: Event) => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (startGGToken.value.trim()) {
|
||||
void loadRecentTournaments();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -159,6 +270,72 @@ const handleImport = async (event: Event) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<QCard
|
||||
flat
|
||||
bordered
|
||||
class="q-pa-md"
|
||||
>
|
||||
<div class="text-h6 q-mb-sm">
|
||||
Integración start.gg
|
||||
</div>
|
||||
<div class="text-caption q-mb-md">
|
||||
Pega tu token personal de start.gg para cargar automáticamente tus torneos recientes y sus inscritos.
|
||||
</div>
|
||||
<div class="row q-col-gutter-sm items-center">
|
||||
<div class="col-12 col-md-7">
|
||||
<QInput
|
||||
v-model="startGGToken"
|
||||
label="start.gg API Token"
|
||||
dense
|
||||
outlined
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<QBtn
|
||||
color="secondary"
|
||||
icon="sync"
|
||||
label="Cargar torneos"
|
||||
:loading="loadingTournaments"
|
||||
@click="loadRecentTournaments"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="tournamentsError"
|
||||
class="text-negative q-mt-sm"
|
||||
>
|
||||
{{ tournamentsError }}
|
||||
</div>
|
||||
<QList
|
||||
v-if="recentTournaments.length"
|
||||
bordered
|
||||
separator
|
||||
class="q-mt-md startgg-tournaments-list"
|
||||
>
|
||||
<QItem
|
||||
v-for="tournament in recentTournaments"
|
||||
:key="tournament.id"
|
||||
clickable
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ tournament.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ tournament.slug }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection side>
|
||||
<QBtn
|
||||
color="primary"
|
||||
unelevated
|
||||
label="Importar jugadores"
|
||||
@click.stop="openStartGGImportDialog(tournament)"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCard>
|
||||
|
||||
<div class="row items-center q-gutter-sm q-mb-md">
|
||||
<QInput
|
||||
v-model="filter"
|
||||
@@ -222,6 +399,51 @@ const handleImport = async (event: Event) => {
|
||||
</template>
|
||||
</QTable>
|
||||
|
||||
<QDialog v-model="isImportDialogOpen">
|
||||
<QCard class="players-dialog">
|
||||
<QCardSection>
|
||||
<div class="text-h6">
|
||||
Importar desde {{ selectedTournament?.name || 'start.gg' }}
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QSeparator />
|
||||
<QCardSection>
|
||||
<div
|
||||
v-if="loadingTournamentPlayers"
|
||||
class="row items-center q-gutter-sm"
|
||||
>
|
||||
<QSpinner />
|
||||
<span>Cargando inscritos...</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<QOptionGroup
|
||||
v-model="selectedStartGGPlayerIds"
|
||||
type="checkbox"
|
||||
:options="startGGPlayers.map((player) => ({
|
||||
label: `${player.gamertag}${player.team ? ` (${player.team})` : ''}${player.country ? ` - ${getCountryLabel(player.country)}` : ''}`,
|
||||
value: player.id,
|
||||
}))"
|
||||
/>
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QSeparator />
|
||||
<QCardActions align="right">
|
||||
<QBtn
|
||||
flat
|
||||
label="Cancelar"
|
||||
color="secondary"
|
||||
@click="isImportDialogOpen = false"
|
||||
/>
|
||||
<QBtn
|
||||
color="primary"
|
||||
label="Importar seleccionados"
|
||||
:disable="!selectedStartGGPlayerIds.length"
|
||||
@click="importSelectedStartGGPlayers"
|
||||
/>
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
|
||||
<QDialog v-model="isDialogOpen">
|
||||
<QCard class="players-dialog">
|
||||
<QCardSection>
|
||||
@@ -324,6 +546,11 @@ const handleImport = async (event: Event) => {
|
||||
width: min(720px, 90vw);
|
||||
}
|
||||
|
||||
.startgg-tournaments-list {
|
||||
max-height: 280px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.players-underlined-field :deep(.q-field__control) {
|
||||
min-height: 28px;
|
||||
padding: 0;
|
||||
|
||||
Reference in New Issue
Block a user