From 787de0503499e5790b7209bcebcf64a24790ef25 Mon Sep 17 00:00:00 2001 From: Pandipipas Date: Tue, 19 May 2026 03:21:50 +0200 Subject: [PATCH] feat: enhance settings view with integration options for start.gg and Challonge, add manual token dialogs, and improve keyboard shortcut management --- src/dashboard/scoreko-dev/i18n.ts | 133 +++-- src/dashboard/scoreko-dev/views/Players.vue | 528 ++++++------------ src/dashboard/scoreko-dev/views/Settings.vue | 548 ++++++++++++++----- 3 files changed, 653 insertions(+), 556 deletions(-) diff --git a/src/dashboard/scoreko-dev/i18n.ts b/src/dashboard/scoreko-dev/i18n.ts index f7bab9f..c060067 100644 --- a/src/dashboard/scoreko-dev/i18n.ts +++ b/src/dashboard/scoreko-dev/i18n.ts @@ -24,6 +24,14 @@ type Translations = { settingsShortcutRightDecrementHint: string; settingsShortcutReset: string; settingsShortcutRecordingHint: string; + settingsShortcutConflictWarning: string; + settingsShortcutStartRecording: string; + settingsShortcutStopRecording: string; + settingsShortcutResetSingle: string; + settingsIntegrationsTitle: string; + settingsIntegrationsDescription: string; + settingsDisconnect: string; + settingsNotConnected: string; languageEnglish: string; languageSpanish: string; scoreboardUnassigned: string; @@ -53,6 +61,8 @@ type Translations = { aboutElectronNote: string; aboutUnknownReleaseError: string; aboutGitHubStatusError: string; + aboutChangelog: string; + aboutTechStackTitle: string; graphicsTitle: string; graphicsDescription: string; graphicsNoConfigured: string; @@ -61,10 +71,16 @@ type Translations = { graphicsScoreboard: string; graphicsCommentary: string; graphicsSkinLabel: string; + graphicsCopied: string; + graphicsOpenBrowser: string; commentaryTitle: string; commentaryCommentator1: string; commentaryCommentator2: string; commentaryTwitterText: string; + commentaryTwitterMaxLength: string; + commentaryTwitterInvalidChars: string; + commentarySwap: string; + commentaryClear: string; bracketTitle: string; bracketStage: string; bracketSide: string; @@ -85,18 +101,8 @@ type Translations = { playersSearchPlaceholder: string; playersImport: string; playersExport: string; - commentaryTwitterMaxLength: string; - commentaryTwitterInvalidChars: string; - commentarySwap: string; - commentaryClear: string; - aboutChangelog : string; - aboutTechStackTitle : string; - settingsShortcutConflictWarning : string; - settingsShortcutStartRecording: string; - settingsShortcutStopRecording: string; - settingsShortcutResetSingle: string; - graphicsCopied : string; - graphicsOpenBrowser : string; + playersConnectInSettings: string; + playersConnectInSettingsSuffix: string; }; const STORAGE_KEY = 'scoreko-dev.language'; @@ -108,6 +114,8 @@ const messages: Record = { menuGraphics: 'Graphics', menuSettings: 'Settings', menuAbout: 'About', + + // ── Settings ──────────────────────────────────────────────────────────── settingsTitle: 'Settings', settingsDescription: 'Dashboard and bundle configuration.', settingsLanguageLabel: 'Language', @@ -124,8 +132,20 @@ const messages: Record = { settingsShortcutRightDecrementHint: 'Decreases right player score by one.', settingsShortcutReset: 'Reset shortcuts', settingsShortcutRecordingHint: 'Press the desired shortcut now (example: Alt+1).', + settingsShortcutConflictWarning: 'This shortcut is already assigned to another action.', + settingsShortcutStartRecording: 'Start recording shortcut', + settingsShortcutStopRecording: 'Stop recording shortcut', + settingsShortcutResetSingle: 'Reset this shortcut', + settingsIntegrationsTitle: 'Integrations', + settingsIntegrationsDescription: 'Connect your tournament platform accounts to import players directly from brackets.', + settingsDisconnect: 'Disconnect', + settingsNotConnected: 'Not connected', + + // ── Language ───────────────────────────────────────────────────────────── languageEnglish: 'English', languageSpanish: 'Spanish', + + // ── Scoreboard ─────────────────────────────────────────────────────────── scoreboardUnassigned: '(Unassigned)', scoreboardLeft: 'Left', scoreboardRight: 'Right', @@ -137,6 +157,8 @@ const messages: Record = { scoreboardLabelTeam: 'Team', scoreboardLabelCountry: 'Country', scoreboardLabelGame: 'Game', + + // ── About ──────────────────────────────────────────────────────────────── aboutTitle: 'About', aboutVersion: 'Version', aboutDescription: 'Dashboard for producing fighting game overlays using NodeCG, Vue, and Quasar.', @@ -153,6 +175,10 @@ const messages: Record = { aboutElectronNote: 'Note for Electron: this panel only implements detection and notification. For real automatic desktop updates, you need to integrate autoUpdater into Electron\'s main process and publish signed artifacts per platform.', aboutUnknownReleaseError: 'Unknown error while checking releases.', aboutGitHubStatusError: 'GitHub responded with status', + aboutChangelog: 'Changelog', + aboutTechStackTitle: 'Tech stack', + + // ── Graphics ───────────────────────────────────────────────────────────── graphicsTitle: 'Graphics', graphicsDescription: 'Bundle graphics controls and status.', graphicsNoConfigured: 'There are no graphics configured in this bundle.', @@ -161,19 +187,31 @@ const messages: Record = { graphicsScoreboard: 'Scoreboard', graphicsCommentary: 'Commentary', graphicsSkinLabel: 'Skin', + graphicsCopied: 'URL copied to clipboard', + graphicsOpenBrowser: 'Open in browser', + + // ── Commentary ─────────────────────────────────────────────────────────── commentaryTitle: 'Commentary', commentaryCommentator1: 'Commentator #1', commentaryCommentator2: 'Commentator #2', commentaryTwitterText: '@Twitter / Text', + commentaryTwitterMaxLength: 'Twitter character limit exceeded', + commentaryTwitterInvalidChars: 'Invalid characters in Twitter text', + commentarySwap: 'Swap commentators', + commentaryClear: 'Clear commentary', + + // ── Bracket ────────────────────────────────────────────────────────────── bracketTitle: 'Bracket', bracketStage: 'Stage', bracketSide: 'Bracket side', bracketCustomProgress: 'Custom progress', bracketPreview: 'Preview', + + // ── Players ────────────────────────────────────────────────────────────── playersLabelTeam: 'Team', playersLabelCountry: 'Country', playersLabelActions: 'Actions', - playersStartggHelp: 'Connect via OAuth (recommended) or paste your personal token to load tournaments you created or administrate. If you see "Client authentication failed", verify your config uses the Client ID/Secret from a start.gg OAuth App.', + playersStartggHelp: 'Connect via OAuth (recommended) or paste your personal token to load tournaments you created or administrate.', playersConnectStartgg: 'Connect with start.gg', playersConnected: 'Connected', playersUsePersonalApi: 'Use personal API', @@ -185,25 +223,18 @@ const messages: Record = { playersSearchPlaceholder: 'Search...', playersImport: 'Import', playersExport: 'Export', - commentaryTwitterMaxLength: 'Twitter character limit exceeded', - commentaryTwitterInvalidChars: 'Invalid characters in Twitter text', - commentarySwap: 'Swap commentators', - commentaryClear: 'Clear commentary', - aboutChangelog: 'Changelog', - aboutTechStackTitle: 'Tech stack', - settingsShortcutConflictWarning: 'This shortcut is already assigned to', - settingsShortcutStartRecording: 'Start recording shortcut', - settingsShortcutStopRecording: 'Stop recording shortcut', - settingsShortcutResetSingle: 'Reset single player score shortcut', - graphicsCopied: 'URL copied to clipboard', - graphicsOpenBrowser: 'Open in browser', + playersConnectInSettings: 'Connect your account in', + playersConnectInSettingsSuffix: 'to import players from tournaments.', }, + es: { menuDashboard: 'Panel', menuPlayers: 'Jugadores', menuGraphics: 'Gráficos', menuSettings: 'Configuración', menuAbout: 'Acerca de', + + // ── Settings ──────────────────────────────────────────────────────────── settingsTitle: 'Configuración', settingsDescription: 'Configuración del dashboard y del bundle.', settingsLanguageLabel: 'Idioma', @@ -220,8 +251,20 @@ const messages: Record = { settingsShortcutRightDecrementHint: 'Reduce en uno el score del jugador derecho.', settingsShortcutReset: 'Restablecer atajos', settingsShortcutRecordingHint: 'Pulsa ahora el atajo deseado (ejemplo: Alt+1).', + settingsShortcutConflictWarning: 'Este atajo ya está asignado a otra acción.', + settingsShortcutStartRecording: 'Iniciar grabación de atajo', + settingsShortcutStopRecording: 'Detener grabación de atajo', + settingsShortcutResetSingle: 'Restablecer este atajo', + settingsIntegrationsTitle: 'Integraciones', + settingsIntegrationsDescription: 'Conecta tus cuentas de plataformas de torneos para importar jugadores directamente desde los brackets.', + settingsDisconnect: 'Desconectar', + settingsNotConnected: 'No conectado', + + // ── Language ───────────────────────────────────────────────────────────── languageEnglish: 'Inglés', languageSpanish: 'Castellano', + + // ── Scoreboard ─────────────────────────────────────────────────────────── scoreboardUnassigned: '(Sin asignar)', scoreboardLeft: 'Izquierda', scoreboardRight: 'Derecha', @@ -233,6 +276,8 @@ const messages: Record = { scoreboardLabelTeam: 'Equipo', scoreboardLabelCountry: 'País', scoreboardLabelGame: 'Juego', + + // ── About ──────────────────────────────────────────────────────────────── aboutTitle: 'Acerca de', aboutVersion: 'Versión', aboutDescription: 'Dashboard para producir overlays de juegos de lucha usando NodeCG, Vue y Quasar.', @@ -249,6 +294,10 @@ const messages: Record = { aboutElectronNote: 'Nota para Electron: este panel solo implementa detección y notificación. Para actualizaciones automáticas reales de escritorio, debes integrar autoUpdater en el proceso principal de Electron y publicar artefactos firmados por plataforma.', aboutUnknownReleaseError: 'Error desconocido al consultar releases.', aboutGitHubStatusError: 'GitHub respondió con estado', + aboutChangelog: 'Changelog', + aboutTechStackTitle: 'Tech stack', + + // ── Graphics ───────────────────────────────────────────────────────────── graphicsTitle: 'Gráficos', graphicsDescription: 'Controles y estado de los gráficos del bundle.', graphicsNoConfigured: 'No hay gráficos configurados en este bundle.', @@ -257,19 +306,31 @@ const messages: Record = { graphicsScoreboard: 'Scoreboard', graphicsCommentary: 'Comentario', graphicsSkinLabel: 'Skin', + graphicsCopied: 'URL copiada al portapapeles', + graphicsOpenBrowser: 'Abrir en el navegador', + + // ── Commentary ─────────────────────────────────────────────────────────── commentaryTitle: 'Comentario', commentaryCommentator1: 'Comentarista #1', commentaryCommentator2: 'Comentarista #2', commentaryTwitterText: '@Twitter / Texto', + commentaryTwitterMaxLength: 'Se excedió el límite de caracteres de Twitter', + commentaryTwitterInvalidChars: 'Caracteres inválidos en el texto de Twitter', + commentarySwap: 'Intercambiar comentaristas', + commentaryClear: 'Limpiar comentario', + + // ── Bracket ────────────────────────────────────────────────────────────── bracketTitle: 'Bracket', bracketStage: 'Etapa', bracketSide: 'Lado del bracket', bracketCustomProgress: 'Progreso personalizado', bracketPreview: 'Vista previa', + + // ── Players ────────────────────────────────────────────────────────────── playersLabelTeam: 'Equipo', playersLabelCountry: 'País', playersLabelActions: 'Acciones', - playersStartggHelp: 'Conéctate por OAuth (recomendado) o pega tu token personal para cargar torneos que creaste o administras. Si ves "Client authentication failed", revisa que tu configuración use el Client ID/Secret de una app OAuth de start.gg.', + playersStartggHelp: 'Conéctate por OAuth (recomendado) o pega tu token personal para cargar torneos que creaste o administras.', playersConnectStartgg: 'Conectar con start.gg', playersConnected: 'Conectado', playersUsePersonalApi: 'Usar API personal', @@ -281,28 +342,15 @@ const messages: Record = { playersSearchPlaceholder: 'Buscar...', playersImport: 'Importar', playersExport: 'Exportar', - commentaryTwitterMaxLength: 'Se excedió el límite de caracteres de Twitter', - commentaryTwitterInvalidChars: 'Caracteres inválidos en el texto de Twitter', - commentarySwap: 'Intercambiar comentaristas', - commentaryClear: 'Limpiar comentario', - aboutChangelog: 'Changelog', - aboutTechStackTitle: 'Tech stack', - settingsShortcutConflictWarning: 'This shortcut is already assigned to', - settingsShortcutStartRecording: 'Start recording shortcut', - settingsShortcutStopRecording: 'Stop recording shortcut', - settingsShortcutResetSingle: 'Reset single player score shortcut', - graphicsCopied: 'URL copiada al portapapeles', - graphicsOpenBrowser: 'Abrir en el navegador', + playersConnectInSettings: 'Conecta tu cuenta en', + playersConnectInSettingsSuffix: 'para importar jugadores desde torneos.', }, }; const normalizeLocale = (value: unknown): Locale => (value === 'es' ? 'es' : 'en'); const getStoredLocale = (): Locale => { - if (typeof window === 'undefined') { - return 'en'; - } - + if (typeof window === 'undefined') return 'en'; return normalizeLocale(localStorage.getItem(STORAGE_KEY)); }; @@ -310,7 +358,6 @@ export const locale = ref(getStoredLocale()); export const setLocale = (value: Locale) => { locale.value = normalizeLocale(value); - if (typeof window !== 'undefined') { localStorage.setItem(STORAGE_KEY, locale.value); } diff --git a/src/dashboard/scoreko-dev/views/Players.vue b/src/dashboard/scoreko-dev/views/Players.vue index 69e24ab..f8800f2 100644 --- a/src/dashboard/scoreko-dev/views/Players.vue +++ b/src/dashboard/scoreko-dev/views/Players.vue @@ -35,7 +35,7 @@ const playersStore = usePlayersStore(); const $q = useQuasar(); const rows = computed(() => playersStore.rows); -// ─── Integraciones ───────────────────────────────────────────────────────────── +// ─── Integraciones (solo se usa para importar torneos) ───────────────────────── const startgg = useIntegration({ messagePrefix: 'startgg', @@ -57,7 +57,6 @@ const challonge = useIntegration({ playersStore, }); -// Notifica errores de apertura del diálogo de importación (sustituye window.alert) watch(() => startgg.importDialogError, (msg) => { if (msg) $q.notify({ type: 'negative', message: msg }); }); @@ -83,12 +82,6 @@ const formatExpiresAt = (ts: number): string => year: 'numeric', }); -// ─── Label de conexión de Challonge ─────────────────────────────────────────── - -const challongeConnectionLabel = computed(() => - challonge.hasValidatedToken ? t('playersConnected') : 'Token set', -); - // ─── Tabla de jugadores ──────────────────────────────────────────────────────── const filter = ref(''); @@ -152,7 +145,6 @@ const openCreateDialog = () => { const openEditDialog = (row: PlayerRow) => { editingId.value = row.id; - // CORRECCIÓN: evitar el patrón `void id` usando un alias _id const { id: _id, ...playerData } = row; Object.assign(form, playerData); isDialogOpen.value = true; @@ -169,34 +161,6 @@ const deletePlayer = (row: PlayerRow) => { } }; -// ─── Diálogos de token manual ────────────────────────────────────────────────── - -const isStartggManualDialogOpen = ref(false); -const startggManualDraft = ref(''); - -const openStartggManualDialog = () => { - startggManualDraft.value = startgg.token; - isStartggManualDialogOpen.value = true; -}; - -const saveStartggManualToken = () => { - startgg.token = startggManualDraft.value.trim(); - isStartggManualDialogOpen.value = false; -}; - -const isChallongeManualDialogOpen = ref(false); -const challongeManualDraft = ref(''); - -const openChallongeManualDialog = () => { - challongeManualDraft.value = challonge.token; - isChallongeManualDialogOpen.value = true; -}; - -const saveChallongeManualToken = () => { - challonge.token = challongeManualDraft.value.trim(); - isChallongeManualDialogOpen.value = false; -}; - // ─── Exportar / importar JSON ────────────────────────────────────────────────── const fileInput = ref(null); @@ -232,6 +196,8 @@ const handleImport = async (event: Event) => { {{ rows.length }} players + @@ -286,9 +249,7 @@ const handleImport = async (event: Event) => { @change="handleImport" > - -
{
-
+
{{ row.name }}
- -
+ +
- + -
-
+ - start.gg -
-
- {{ t('playersStartggHelp') }} -
-
-
- - -
-
- -
-
-
- {{ startgg.tournamentsError }} -
-
- start.gg + + -
- - - -
-
- - {{ t('playersImportPlayers') }} - -
+ > + {{ t('playersConnected') }} +
+ + {{ t('settingsNotConnected') || 'Not connected' }} +
+ + +
+ {{ t('playersConnectInSettings') || 'Connect your start.gg account in' }} + Settings + {{ t('playersConnectInSettingsSuffix') || 'to import players from tournaments.' }} +
+ + + - + -
- Challonge - Challonge -
-
- {{ t('playersChallongeHelp') }} -
-
-
- - -
-
- -
-
-
- {{ challonge.tournamentsError }} -
-
- + Challonge + Challonge + + -
- - - -
-
- - {{ t('playersImportPlayers') }} - -
+ > + {{ challonge.hasValidatedToken ? t('playersConnected') : 'Token set' }} +
+ + {{ t('settingsNotConnected') || 'Not connected' }} +
+ + +
+ {{ t('playersConnectInSettings') || 'Connect your Challonge account in' }} + Settings + {{ t('playersConnectInSettingsSuffix') || 'to import players from tournaments.' }} +
+ + +
@@ -570,9 +468,7 @@ const handleImport = async (event: Event) => { -
- Import from {{ startgg.importingTournament?.name || 'start.gg' }} -
+
Import from {{ startgg.importingTournament?.name || 'start.gg' }}
@@ -617,9 +513,7 @@ const handleImport = async (event: Event) => { -
- Import from {{ challonge.importingTournament?.name || 'Challonge' }} -
+
Import from {{ challonge.importingTournament?.name || 'Challonge' }}
@@ -660,65 +554,6 @@ const handleImport = async (event: Event) => {
- - - - -
Personal start.gg API
-
- - -
- If OAuth fails, you can create your personal token manually with these steps: -
-
    -
  1. Go to https://start.gg/admin/profile/developer
  2. -
  3. Sign in with your account
  4. -
  5. From the 3 access tokens, click Third Party
  6. -
  7. Create a new one and fill the description with any name you want
  8. -
  9. Copy the generated token and paste it into Scoreko
  10. -
- -
- - - - - - -
-
- - - - - -
Personal Challonge API
-
- - -
- If OAuth fails, paste a personal Challonge API token. -
- -
- - - - - - -
-
- @@ -750,18 +585,11 @@ const handleImport = async (event: Event) => { v-model="form.country" v-model:input-value="countryInput" :options="filteredCountryOptions" - option-value="value" - option-label="label" - emit-value - map-options - use-input - input-debounce="0" - hide-selected - fill-input - clearable + option-value="value" option-label="label" + emit-value map-options use-input input-debounce="0" + hide-selected fill-input clearable :label="t('playersLabelCountry')" - dense - class="players-underlined-field" + dense class="players-underlined-field" @filter="filterCountries" />
@@ -781,6 +609,7 @@ const handleImport = async (event: Event) => { + @@ -804,8 +633,8 @@ const handleImport = async (event: Event) => { flex: 1 1 auto; } -.players-startgg-column { - min-width: 320px; +.players-import-column { + min-width: 300px; } .players-integrations-stack { @@ -819,35 +648,14 @@ const handleImport = async (event: Event) => { width: min(720px, 90vw); } -.startgg-heading { - display: inline-flex; - align-items: center; - gap: 8px; -} - -.startgg-heading__icon { - width: 20px; - height: 20px; -} - -.challonge-heading__icon { - width: 20px; - height: 20px; - border-radius: 4px; -} - -.startgg-tournament-row { +.players-tournament-row { gap: 4px; } -.startgg-refresh-btn:hover { +.players-refresh-btn:hover { background: rgba(255, 255, 255, 0.12); } -.startgg-connected-btn { - font-weight: 600; -} - .players-underlined-field :deep(.q-field__control) { min-height: 28px; padding: 0; @@ -861,10 +669,6 @@ const handleImport = async (event: Event) => { border-bottom: 1px solid rgba(255, 255, 255, 0.34); } -.manual-token-steps { - line-height: 1.5; -} - .visually-hidden { position: absolute; width: 1px; @@ -875,4 +679,4 @@ const handleImport = async (event: Event) => { clip: rect(0, 0, 0, 0); border: 0; } - \ No newline at end of file + diff --git a/src/dashboard/scoreko-dev/views/Settings.vue b/src/dashboard/scoreko-dev/views/Settings.vue index 9c63653..2654b91 100644 --- a/src/dashboard/scoreko-dev/views/Settings.vue +++ b/src/dashboard/scoreko-dev/views/Settings.vue @@ -1,8 +1,11 @@