Fügt Unterstützung für die neue nuscore API hinzu. Aktualisiert die Backend-Routen zur Verarbeitung von Anfragen an die nuscore API und integriert die neuen Dialogkomponenten im Frontend. Ermöglicht das Erstellen lokaler Kopien von nuscore-Daten und verbessert die Benutzeroberfläche durch neue Schaltflächen und Dialoge. Entfernt veraltete Konsolenausgaben und optimiert die Logik zur PIN-Verwaltung.
This commit is contained in:
7
frontend/package-lock.json
generated
7
frontend/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.3",
|
||||
"core-js": "^3.8.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^2.5.2",
|
||||
"jspdf-autotable": "^5.0.2",
|
||||
@@ -1375,6 +1376,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.3",
|
||||
"core-js": "^3.8.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^2.5.2",
|
||||
"jspdf-autotable": "^5.0.2",
|
||||
|
||||
@@ -54,11 +54,13 @@
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import MatchReportDialog from './MatchReportDialog.vue';
|
||||
import MatchReportApiDialog from './MatchReportApiDialog.vue';
|
||||
import MatchReportHeaderActions from './MatchReportHeaderActions.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MatchReportDialog,
|
||||
MatchReportApiDialog,
|
||||
MatchReportHeaderActions
|
||||
},
|
||||
name: 'DialogManager',
|
||||
@@ -69,11 +71,15 @@ export default {
|
||||
...mapActions(['closeDialog', 'minimizeDialog', 'restoreDialog', 'bringDialogToFront']),
|
||||
|
||||
getDialogComponent(componentName) {
|
||||
console.log('🔍 Suche Komponente:', componentName);
|
||||
const components = {
|
||||
'MatchReportDialog': MatchReportDialog,
|
||||
'MatchReportApiDialog': MatchReportApiDialog,
|
||||
'MatchReportHeaderActions': MatchReportHeaderActions
|
||||
};
|
||||
return components[componentName] || null;
|
||||
const component = components[componentName] || null;
|
||||
console.log('🔍 Gefundene Komponente:', component ? component.name : 'null');
|
||||
return component;
|
||||
},
|
||||
|
||||
bringToFront(dialogId) {
|
||||
@@ -93,101 +99,31 @@ export default {
|
||||
|
||||
// Behandle die verschiedenen Action-Types
|
||||
if (action.type === 'insertPin') {
|
||||
// PIN ins iframe einfügen
|
||||
// PIN via postMessage an das zuletzt geöffnete MatchReport-iframe senden
|
||||
this.insertPinIntoIframe(action.match);
|
||||
}
|
||||
},
|
||||
|
||||
insertPinIntoIframe(match) {
|
||||
console.log('🔍 PIN-Einfügen gestartet für Match:', match);
|
||||
console.log('📌 Verfügbare PINs:', {
|
||||
homePin: match.homePin,
|
||||
guestPin: match.guestPin
|
||||
});
|
||||
|
||||
// Versuche direkten Zugriff auf die MatchReportDialog-Komponente
|
||||
const matchReportDialogs = document.querySelectorAll('.match-report-dialog');
|
||||
console.log('🖼️ Gefundene MatchReportDialogs:', matchReportDialogs.length);
|
||||
|
||||
if (matchReportDialogs.length > 0) {
|
||||
// Versuche die insertPinManually Methode aufzurufen
|
||||
console.log('🎯 Versuche direkten Zugriff auf MatchReportDialog');
|
||||
|
||||
// Finde das iframe im aktuellen Dialog
|
||||
const iframe = matchReportDialogs[matchReportDialogs.length - 1].querySelector('iframe');
|
||||
if (iframe) {
|
||||
console.log('✅ Iframe gefunden, versuche PIN-Einfügung');
|
||||
|
||||
try {
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (iframeDoc) {
|
||||
console.log('✅ Direkter DOM-Zugriff möglich');
|
||||
|
||||
// Suche nach PIN-Feldern
|
||||
const pinSelectors = [
|
||||
'input[type="password"][placeholder*="Vereins"]',
|
||||
'input[type="password"][placeholder*="Spiel-Pin"]',
|
||||
'input[type="password"][placeholder*="PIN"]',
|
||||
'input[type="password"]',
|
||||
'input[placeholder*="Vereins"]',
|
||||
'input[placeholder*="Spiel-Pin"]'
|
||||
];
|
||||
|
||||
let pinField = null;
|
||||
for (const selector of pinSelectors) {
|
||||
pinField = iframeDoc.querySelector(selector);
|
||||
if (pinField) {
|
||||
console.log(`✅ PIN-Feld gefunden mit Selektor: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinField && match.homePin) {
|
||||
console.log('📝 Füge PIN ein:', match.homePin);
|
||||
pinField.value = match.homePin;
|
||||
pinField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
console.log('✅ PIN erfolgreich eingefügt');
|
||||
return;
|
||||
} else {
|
||||
console.log('❌ PIN-Feld nicht gefunden');
|
||||
console.log('🔍 Verfügbare Input-Felder:', iframeDoc.querySelectorAll('input'));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('🚫 Cross-Origin-Zugriff blockiert (erwartet)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: PostMessage verwenden
|
||||
console.log('🔍 PIN-Einfügen gestartet (postMessage)');
|
||||
const iframes = document.querySelectorAll('iframe');
|
||||
if (iframes.length > 0) {
|
||||
const iframe = iframes[iframes.length - 1]; // Neuestes iframe
|
||||
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: match.homePin,
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
|
||||
console.log('📤 Sende PostMessage:', message);
|
||||
|
||||
const origins = ['https://ttde-apps.liga.nu', 'https://liga.nu', '*'];
|
||||
origins.forEach(origin => {
|
||||
try {
|
||||
iframe.contentWindow.postMessage(message, origin);
|
||||
console.log(`📤 PostMessage an ${origin} gesendet`);
|
||||
} catch (e) {
|
||||
console.log(`❌ PostMessage an ${origin} fehlgeschlagen:`, e.message);
|
||||
}
|
||||
});
|
||||
if (iframes.length === 0) {
|
||||
console.log('❌ Kein iframe gefunden');
|
||||
return;
|
||||
}
|
||||
const iframe = iframes[iframes.length - 1];
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: match.homePin || match.guestPin || '',
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
try {
|
||||
iframe.contentWindow.postMessage(message, 'http://localhost:3000');
|
||||
console.log('📤 PostMessage an http://localhost:3000 gesendet');
|
||||
} catch (e) {
|
||||
console.log('PostMessage Fehler (nicht kritisch):', e.message);
|
||||
}
|
||||
|
||||
console.log('💡 Alternative: Verwenden Sie den "📋 PIN kopieren" Button');
|
||||
console.log('📋 PIN zum Kopieren:', match.homePin || match.guestPin);
|
||||
},
|
||||
|
||||
handlePostMessage(event) {
|
||||
@@ -195,8 +131,8 @@ export default {
|
||||
console.log('- Origin:', event.origin);
|
||||
console.log('- Data:', event.data);
|
||||
|
||||
// Nur Nachrichten von nuscore verarbeiten
|
||||
if (event.origin !== 'https://ttde-apps.liga.nu' && event.origin !== 'https://liga.nu') {
|
||||
// Nur Nachrichten von unserem Proxy verarbeiten
|
||||
if (event.origin !== 'http://localhost:3000') {
|
||||
console.log('🚫 Nachricht von unbekannter Origin ignoriert');
|
||||
return;
|
||||
}
|
||||
@@ -306,7 +242,7 @@ export default {
|
||||
|
||||
.dialog-content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
1704
frontend/src/components/MatchReportApiDialog.vue
Normal file
1704
frontend/src/components/MatchReportApiDialog.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,18 @@
|
||||
<template>
|
||||
<div class="match-report-dialog">
|
||||
<!-- nuscore Analyzer für lokale Kopie -->
|
||||
<NuscoreAnalyzer v-if="showAnalyzer" @close="showAnalyzer = false" />
|
||||
|
||||
<!-- Toggle für Analyzer -->
|
||||
<div class="analyzer-toggle">
|
||||
<button @click="showAnalyzer = !showAnalyzer" class="toggle-btn">
|
||||
{{ showAnalyzer ? '🔼 Analyzer verstecken' : '🔍 nuscore analysieren' }}
|
||||
</button>
|
||||
<button @click="createLocalCopy" class="toggle-btn" style="margin-left: 10px;">
|
||||
💾 Lokale Kopie erstellen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="report-content">
|
||||
<iframe
|
||||
ref="reportIframe"
|
||||
@@ -15,27 +28,29 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NuscoreAnalyzer from './NuscoreAnalyzer.vue';
|
||||
import { generateSimpleNuscoreHTML } from '../utils/simpleNuscoreHTMLGenerator.js';
|
||||
|
||||
export default {
|
||||
name: 'MatchReportDialog',
|
||||
components: {
|
||||
NuscoreAnalyzer
|
||||
},
|
||||
props: {
|
||||
match: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAnalyzer: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
reportUrl() {
|
||||
// Verschiedene URL-Parameter versuchen, die nuscore möglicherweise unterstützt
|
||||
const baseUrl = 'https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list';
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// Verschiedene Parameter-Namen versuchen
|
||||
params.set('code', this.match.code);
|
||||
params.set('gamecode', this.match.code);
|
||||
params.set('spielcode', this.match.code);
|
||||
params.set('matchcode', this.match.code);
|
||||
|
||||
return `${baseUrl}?${params.toString()}`;
|
||||
// Verwende die neue API-basierte Lösung
|
||||
return `http://localhost:3000/api/nuscore/meetinginfo/${this.match.code}`;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -64,158 +79,24 @@ export default {
|
||||
|
||||
onIframeLoad() {
|
||||
console.log('🔄 Iframe geladen, URL:', this.$refs.reportIframe?.src);
|
||||
console.log('📊 Iframe-Inhalt:', this.$refs.reportIframe?.contentDocument?.title || 'Kein Titel');
|
||||
|
||||
// Warte kurz, damit das iframe vollständig geladen ist
|
||||
setTimeout(() => {
|
||||
this.injectContentScript();
|
||||
}, 2000);
|
||||
|
||||
// Überwache URL-Änderungen im iframe
|
||||
// Sende den Code an das iframe über PostMessage
|
||||
this.tryPostMessage();
|
||||
|
||||
// Überwache URL-Änderungen im iframe (nur Logging, da kein direkter Zugriff möglich)
|
||||
this.startUrlMonitoring();
|
||||
},
|
||||
|
||||
injectContentScript() {
|
||||
try {
|
||||
const iframe = this.$refs.reportIframe;
|
||||
if (!iframe || !iframe.contentWindow) {
|
||||
console.log('Iframe noch nicht bereit für Content Script');
|
||||
return;
|
||||
}
|
||||
|
||||
// Content Script als String definieren
|
||||
const contentScript = `
|
||||
(function() {
|
||||
console.log('Content Script geladen');
|
||||
|
||||
// Warte bis die Seite vollständig geladen ist
|
||||
function waitForElement(selector, callback) {
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
callback(element);
|
||||
} else {
|
||||
setTimeout(() => waitForElement(selector, callback), 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Suche nach dem Input-Feld
|
||||
waitForElement('#gamecode', function(input) {
|
||||
console.log('Input-Feld gefunden:', input);
|
||||
|
||||
// Code einfügen
|
||||
input.value = '${this.match.code}';
|
||||
|
||||
// Events auslösen
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
|
||||
console.log('Code eingefügt:', '${this.match.code}');
|
||||
|
||||
// Suche nach dem Button und klicke ihn
|
||||
setTimeout(() => {
|
||||
const button = document.querySelector('button.btn-primary');
|
||||
if (button) {
|
||||
console.log('Button gefunden, klicke ihn');
|
||||
button.click();
|
||||
} else {
|
||||
console.log('Button nicht gefunden');
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
})();
|
||||
`;
|
||||
|
||||
// Script in das iframe injizieren
|
||||
const script = iframe.contentDocument.createElement('script');
|
||||
script.textContent = contentScript;
|
||||
iframe.contentDocument.head.appendChild(script);
|
||||
|
||||
console.log('Content Script injiziert');
|
||||
|
||||
} catch (error) {
|
||||
console.log('Fehler beim Injizieren des Content Scripts:', error);
|
||||
// Fallback zu PostMessage
|
||||
this.tryPostMessage();
|
||||
}
|
||||
},
|
||||
|
||||
fillGameCode() {
|
||||
try {
|
||||
const iframe = this.$refs.reportIframe;
|
||||
if (!iframe || !iframe.contentWindow) {
|
||||
console.log('Iframe noch nicht bereit');
|
||||
return;
|
||||
}
|
||||
|
||||
// Versuche, das Input-Feld zu finden und zu füllen
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
|
||||
if (iframeDoc) {
|
||||
const gameCodeInput = iframeDoc.getElementById('gamecode');
|
||||
if (gameCodeInput) {
|
||||
// Code in das Input-Feld einfügen
|
||||
gameCodeInput.value = this.match.code;
|
||||
|
||||
// Event auslösen, damit Angular die Änderung erkennt
|
||||
gameCodeInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
gameCodeInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
console.log('Spielcode erfolgreich eingefügt:', this.match.code);
|
||||
|
||||
// Optional: Automatisch den Button klicken
|
||||
setTimeout(() => {
|
||||
this.clickLoadButton(iframeDoc);
|
||||
}, 500);
|
||||
} else {
|
||||
console.log('Input-Feld mit ID "gamecode" nicht gefunden');
|
||||
}
|
||||
} else {
|
||||
console.log('Kein Zugriff auf iframe-Dokument (Cross-Origin)');
|
||||
// Fallback: PostMessage verwenden
|
||||
this.tryPostMessage();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Fehler beim Zugriff auf iframe:', error);
|
||||
// Fallback: PostMessage verwenden
|
||||
this.tryPostMessage();
|
||||
}
|
||||
},
|
||||
|
||||
clickLoadButton(iframeDoc) {
|
||||
try {
|
||||
// Suche nach dem Button (verschiedene Selektoren)
|
||||
const buttonSelectors = [
|
||||
'button.btn-primary',
|
||||
'button[type="button"]',
|
||||
'button:contains("Laden")'
|
||||
];
|
||||
|
||||
let loadButton = null;
|
||||
for (const selector of buttonSelectors) {
|
||||
loadButton = iframeDoc.querySelector(selector);
|
||||
if (loadButton) break;
|
||||
}
|
||||
|
||||
if (loadButton) {
|
||||
loadButton.click();
|
||||
console.log('Laden-Button erfolgreich geklickt');
|
||||
} else {
|
||||
console.log('Laden-Button nicht gefunden');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Fehler beim Klicken des Buttons:', error);
|
||||
}
|
||||
},
|
||||
|
||||
tryPostMessage() {
|
||||
try {
|
||||
const iframe = this.$refs.reportIframe;
|
||||
if (iframe && iframe.contentWindow) {
|
||||
// Sende Nachricht an das iframe
|
||||
// Sende Nachricht an das iframe (jetzt localhost:3000)
|
||||
iframe.contentWindow.postMessage({
|
||||
action: 'fillGameCode',
|
||||
code: this.match.code
|
||||
}, 'https://ttde-apps.liga.nu');
|
||||
}, 'http://localhost:3000');
|
||||
|
||||
console.log('PostMessage gesendet mit Code:', this.match.code);
|
||||
}
|
||||
@@ -260,52 +141,18 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kein DOM-Zugriff mehr; ausschließlich postMessage senden
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: this.match.homePin,
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
console.log('📤 Sende PostMessage:', message);
|
||||
try {
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
|
||||
// Suche nach PIN-Feldern auf der Meeting-Seite
|
||||
const pinSelectors = [
|
||||
'input[type="password"][placeholder*="Vereins"]',
|
||||
'input[type="password"][placeholder*="Spiel-Pin"]',
|
||||
'input[type="password"][placeholder*="PIN"]',
|
||||
'input[type="password"]',
|
||||
'input[placeholder*="Vereins"]',
|
||||
'input[placeholder*="Spiel-Pin"]'
|
||||
];
|
||||
|
||||
let pinField = null;
|
||||
for (const selector of pinSelectors) {
|
||||
pinField = iframeDoc.querySelector(selector);
|
||||
if (pinField) {
|
||||
console.log(`✅ PIN-Feld gefunden mit Selektor: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinField) {
|
||||
console.log('📝 Füge PIN ein:', this.match.homePin);
|
||||
pinField.value = this.match.homePin;
|
||||
pinField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
console.log('✅ PIN erfolgreich eingefügt');
|
||||
} else {
|
||||
console.log('❌ PIN-Feld nicht gefunden');
|
||||
console.log('🔍 Verfügbare Input-Felder:', iframeDoc.querySelectorAll('input'));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log('🚫 Cross-Origin-Zugriff blockiert (erwartet)');
|
||||
|
||||
// Fallback: PostMessage
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: this.match.homePin,
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
|
||||
console.log('📤 Sende PostMessage:', message);
|
||||
iframe.contentWindow.postMessage(message, 'https://ttde-apps.liga.nu');
|
||||
} catch (e) {
|
||||
console.log('PostMessage Fehler (nicht kritisch):', e.message);
|
||||
iframe.contentWindow.postMessage(message, '*');
|
||||
}
|
||||
},
|
||||
@@ -316,9 +163,28 @@ export default {
|
||||
this.attemptPinInsertionAfterRedirect();
|
||||
},
|
||||
|
||||
async createLocalCopy() {
|
||||
console.log('🏗️ Erstelle lokale Kopie mit PIN...');
|
||||
|
||||
// Erstelle eine einfache HTML-Datei, die die ursprüngliche nuscore-Seite in einem iframe lädt
|
||||
const html = generateSimpleNuscoreHTML(this.match);
|
||||
|
||||
const blob = new Blob([html], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'nuscore-local.html';
|
||||
link.click();
|
||||
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
console.log('✅ Lokale Kopie erstellt (iframe-basiert)');
|
||||
},
|
||||
|
||||
handlePostMessage(event) {
|
||||
// Nur Nachrichten von nuscore verarbeiten
|
||||
if (event.origin !== 'https://ttde-apps.liga.nu') {
|
||||
// Nur Nachrichten von unserem Proxy verarbeiten
|
||||
if (event.origin !== 'http://localhost:3000') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,6 +208,27 @@ export default {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.analyzer-toggle {
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.match-info {
|
||||
background: #f8f9fa;
|
||||
padding: 16px;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="match-report-header-actions">
|
||||
<!-- <button @click="insertPin" class="header-action-btn" title="PIN automatisch einfügen">
|
||||
<button @click="insertPin" class="header-action-btn" title="PIN automatisch einfügen">
|
||||
📌 PIN einfügen
|
||||
</button>-->
|
||||
</button>
|
||||
<button @click="copyPin" class="header-action-btn copy-button" title="PIN in Zwischenablage kopieren">
|
||||
📋 PIN kopieren
|
||||
</button>
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
async copyPin() {
|
||||
async copyPin(event) {
|
||||
const pin = this.match.homePin || this.match.guestPin;
|
||||
if (!pin) {
|
||||
console.warn('⚠️ Keine PIN verfügbar zum Kopieren');
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
console.log('✅ PIN erfolgreich kopiert:', pin);
|
||||
|
||||
// Visuelles Feedback
|
||||
const button = event.target;
|
||||
const button = event?.target;
|
||||
const originalText = button.textContent;
|
||||
button.textContent = '✅ Kopiert!';
|
||||
button.style.backgroundColor = '#28a745';
|
||||
|
||||
528
frontend/src/components/NuscoreAnalyzer.vue
Normal file
528
frontend/src/components/NuscoreAnalyzer.vue
Normal file
@@ -0,0 +1,528 @@
|
||||
<template>
|
||||
<div class="nuscore-analyzer">
|
||||
<div class="analyzer-header">
|
||||
<h3>🔍 nuscore Analyzer</h3>
|
||||
<p>Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter</p>
|
||||
</div>
|
||||
|
||||
<div class="analyzer-controls">
|
||||
<button @click="analyzeNuscore" :disabled="isAnalyzing" class="analyze-btn">
|
||||
{{ isAnalyzing ? '🔄 Analysiere...' : '🔍 nuscore analysieren' }}
|
||||
</button>
|
||||
|
||||
<button @click="downloadResources" :disabled="!resources.length || isDownloading" class="download-btn">
|
||||
{{ isDownloading ? '⬇️ Lade herunter...' : `⬇️ ${resources.length} Ressourcen herunterladen` }}
|
||||
</button>
|
||||
|
||||
<button @click="createLocalCopy" :disabled="!hasDownloadedResources || isCreating" class="create-btn">
|
||||
{{ isCreating ? '🏗️ Erstelle...' : '🏗️ Lokale Kopie erstellen' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="analyzer-results" v-if="resources.length > 0">
|
||||
<h4>📋 Gefundene Ressourcen:</h4>
|
||||
<div class="resource-list">
|
||||
<div
|
||||
v-for="resource in resources"
|
||||
:key="resource.url"
|
||||
class="resource-item"
|
||||
:class="{ 'downloaded': resource.downloaded }"
|
||||
>
|
||||
<span class="resource-type">{{ resource.type }}</span>
|
||||
<span class="resource-url">{{ resource.url }}</span>
|
||||
<span class="resource-size">{{ formatSize(resource.size) }}</span>
|
||||
<span v-if="resource.downloaded" class="status">✅</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="analyzer-log" v-if="logs.length > 0">
|
||||
<h4>📝 Log:</h4>
|
||||
<div class="log-content">
|
||||
<div v-for="(log, index) in logs" :key="index" class="log-entry">
|
||||
{{ log }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateSimpleNuscoreHTML } from '../utils/simpleNuscoreHTMLGenerator.js';
|
||||
|
||||
export default {
|
||||
name: 'NuscoreAnalyzer',
|
||||
data() {
|
||||
return {
|
||||
isAnalyzing: false,
|
||||
isDownloading: false,
|
||||
isCreating: false,
|
||||
resources: [],
|
||||
downloadedResources: [],
|
||||
logs: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasDownloadedResources() {
|
||||
return this.resources.some(resource => resource.downloaded) || this.downloadedResources.length > 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async analyzeNuscore() {
|
||||
this.isAnalyzing = true;
|
||||
this.logs = [];
|
||||
this.resources = [];
|
||||
|
||||
this.addLog('🔍 Starte nuscore-Analyse...');
|
||||
|
||||
try {
|
||||
// Öffne nuscore in einem versteckten iframe
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = 'https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list';
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
// Warte auf das Laden
|
||||
await new Promise((resolve) => {
|
||||
iframe.onload = resolve;
|
||||
setTimeout(resolve, 5000); // Timeout nach 5 Sekunden
|
||||
});
|
||||
|
||||
this.addLog('✅ nuscore-Seite geladen');
|
||||
|
||||
// Analysiere die Seite
|
||||
await this.analyzePage(iframe);
|
||||
|
||||
// Entferne das iframe
|
||||
document.body.removeChild(iframe);
|
||||
|
||||
this.addLog(`✅ Analyse abgeschlossen: ${this.resources.length} Ressourcen gefunden`);
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Fehler bei der Analyse: ${error.message}`);
|
||||
} finally {
|
||||
this.isAnalyzing = false;
|
||||
}
|
||||
},
|
||||
|
||||
async analyzePage(iframe) {
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
|
||||
// Analysiere Script-Tags
|
||||
const scripts = doc.querySelectorAll('script[src]');
|
||||
this.addLog(`📜 ${scripts.length} Script-Tags gefunden`);
|
||||
|
||||
scripts.forEach(script => {
|
||||
this.resources.push({
|
||||
type: 'script',
|
||||
url: script.src,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
// Analysiere Link-Tags (CSS)
|
||||
const stylesheets = doc.querySelectorAll('link[rel="stylesheet"][href]');
|
||||
this.addLog(`🎨 ${stylesheets.length} Stylesheets gefunden`);
|
||||
|
||||
stylesheets.forEach(link => {
|
||||
this.resources.push({
|
||||
type: 'stylesheet',
|
||||
url: link.href,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
// Analysiere andere Ressourcen
|
||||
const images = doc.querySelectorAll('img[src]');
|
||||
this.addLog(`🖼️ ${images.length} Bilder gefunden`);
|
||||
|
||||
images.forEach(img => {
|
||||
this.resources.push({
|
||||
type: 'image',
|
||||
url: img.src,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`⚠️ Cross-Origin-Zugriff blockiert: ${error.message}`);
|
||||
|
||||
// Fallback: Analysiere die ursprüngliche URL
|
||||
await this.analyzeUrlDirectly();
|
||||
}
|
||||
},
|
||||
|
||||
async analyzeUrlDirectly() {
|
||||
this.addLog('🔄 Fallback: Bekannte nuscore-Ressourcen verwenden');
|
||||
|
||||
try {
|
||||
// Bekannte nuscore-Ressourcen basierend auf der URL-Struktur
|
||||
const baseUrl = 'https://ttde-apps.liga.nu';
|
||||
|
||||
// Typische nuscore-Ressourcen
|
||||
const knownResources = [
|
||||
// JavaScript-Bundles (typische Angular/React-Bundles)
|
||||
`${baseUrl}/nuliga/nuscore-tt/main.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/runtime.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/vendor.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/polyfills.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/styles.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/main.6c3da9dbb4871026b9a1.js`, // Aus den Logs
|
||||
|
||||
// CSS-Dateien
|
||||
`${baseUrl}/nuliga/nuscore-tt/styles.css`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/main.css`,
|
||||
|
||||
// Weitere mögliche Ressourcen
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/main.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/runtime.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/vendor.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/styles.css`,
|
||||
|
||||
// Angular-spezifische Ressourcen
|
||||
`${baseUrl}/nuliga/nuscore-tt/angular.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/angular.min.js`,
|
||||
|
||||
// Bootstrap/CSS-Frameworks
|
||||
`${baseUrl}/nuliga/nuscore-tt/bootstrap.css`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/bootstrap.min.css`,
|
||||
|
||||
// Fonts
|
||||
`${baseUrl}/nuliga/nuscore-tt/fonts/`,
|
||||
|
||||
// Bilder/Assets
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/images/`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/icons/`
|
||||
];
|
||||
|
||||
// Füge bekannte Ressourcen hinzu
|
||||
knownResources.forEach(url => {
|
||||
this.resources.push({
|
||||
type: url.includes('.css') ? 'stylesheet' :
|
||||
url.includes('.js') ? 'script' :
|
||||
url.includes('fonts') ? 'font' : 'asset',
|
||||
url: url,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
this.addLog(`✅ ${this.resources.length} bekannte Ressourcen hinzugefügt`);
|
||||
|
||||
// Versuche zusätzlich, Ressourcen aus dem aktuellen iframe zu extrahieren
|
||||
await this.extractResourcesFromCurrentPage();
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Bekannte Ressourcen-Analyse fehlgeschlagen: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async extractResourcesFromCurrentPage() {
|
||||
this.addLog('🔍 Versuche Ressourcen-Extraktion von der aktuellen Seite');
|
||||
|
||||
try {
|
||||
// Analysiere die aktuelle Seite nach Ressourcen
|
||||
const scripts = document.querySelectorAll('script[src]');
|
||||
const stylesheets = document.querySelectorAll('link[rel="stylesheet"][href]');
|
||||
|
||||
this.addLog(`📜 ${scripts.length} Scripts auf aktueller Seite gefunden`);
|
||||
this.addLog(`🎨 ${stylesheets.length} Stylesheets auf aktueller Seite gefunden`);
|
||||
|
||||
// Füge gefundene Ressourcen hinzu (falls sie nuscore-relevant sind)
|
||||
scripts.forEach(script => {
|
||||
if (script.src.includes('nuscore') || script.src.includes('liga.nu')) {
|
||||
this.resources.push({
|
||||
type: 'script',
|
||||
url: script.src,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
stylesheets.forEach(link => {
|
||||
if (link.href.includes('nuscore') || link.href.includes('liga.nu')) {
|
||||
this.resources.push({
|
||||
type: 'stylesheet',
|
||||
url: link.href,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.addLog(`✅ ${this.resources.length} nuscore-relevante Ressourcen gefunden`);
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`⚠️ Ressourcen-Extraktion fehlgeschlagen: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async downloadResources() {
|
||||
this.isDownloading = true;
|
||||
this.downloadedResources = [];
|
||||
|
||||
this.addLog('⬇️ Starte Download der Ressourcen...');
|
||||
this.addLog('⚠️ Hinweis: CORS-Beschränkungen können Downloads blockieren');
|
||||
|
||||
for (const resource of this.resources) {
|
||||
try {
|
||||
this.addLog(`⬇️ Lade herunter: ${resource.url}`);
|
||||
|
||||
// Versuche verschiedene Download-Methoden
|
||||
let blob = null;
|
||||
|
||||
// Methode 1: Direkter fetch
|
||||
try {
|
||||
const response = await fetch(resource.url, {
|
||||
mode: 'cors',
|
||||
credentials: 'omit'
|
||||
});
|
||||
blob = await response.blob();
|
||||
} catch (corsError) {
|
||||
this.addLog(`⚠️ CORS-Fehler für ${resource.url}, versuche Alternative...`);
|
||||
|
||||
// Methode 2: Proxy über iframe
|
||||
blob = await this.downloadViaIframe(resource.url);
|
||||
}
|
||||
|
||||
if (blob && blob.size > 0) {
|
||||
resource.downloaded = true;
|
||||
resource.size = blob.size;
|
||||
resource.blob = blob;
|
||||
resource.localUrl = URL.createObjectURL(blob);
|
||||
|
||||
this.downloadedResources.push(resource);
|
||||
|
||||
this.addLog(`✅ Heruntergeladen: ${this.formatSize(blob.size)}`);
|
||||
} else {
|
||||
this.addLog(`⚠️ Leere oder ungültige Ressource: ${resource.url}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Download fehlgeschlagen: ${resource.url} - ${error.message}`);
|
||||
|
||||
// Fallback: Markiere als "verfügbar aber nicht heruntergeladen"
|
||||
resource.available = true;
|
||||
resource.downloaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.addLog(`✅ Download abgeschlossen: ${this.downloadedResources.length}/${this.resources.length} erfolgreich`);
|
||||
this.addLog(`💡 Tipp: Verwenden Sie die lokale Kopie auch mit externen Ressourcen`);
|
||||
this.isDownloading = false;
|
||||
},
|
||||
|
||||
async downloadViaIframe(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = url;
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
iframe.onload = () => {
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const content = doc.documentElement.outerHTML;
|
||||
const blob = new Blob([content], { type: 'text/html' });
|
||||
document.body.removeChild(iframe);
|
||||
resolve(blob);
|
||||
} catch (error) {
|
||||
document.body.removeChild(iframe);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
iframe.onerror = () => {
|
||||
document.body.removeChild(iframe);
|
||||
reject(new Error('Iframe load failed'));
|
||||
};
|
||||
|
||||
// Timeout nach 10 Sekunden
|
||||
setTimeout(() => {
|
||||
if (document.body.contains(iframe)) {
|
||||
document.body.removeChild(iframe);
|
||||
reject(new Error('Download timeout'));
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async createLocalCopy() {
|
||||
this.isCreating = true;
|
||||
this.addLog('🏗️ Erstelle lokale Kopie...');
|
||||
|
||||
try {
|
||||
// Erstelle eine einfache HTML-Datei, die die ursprüngliche nuscore-Seite in einem iframe lädt
|
||||
const html = generateSimpleNuscoreHTML({ homePin: '', guestPin: '', code: '' });
|
||||
|
||||
const blob = new Blob([html], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'nuscore-local.html';
|
||||
link.click();
|
||||
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
this.addLog('✅ Lokale Kopie erstellt (iframe-basiert)');
|
||||
this.addLog('💡 Die lokale Kopie lädt die ursprüngliche nuscore-Seite in einem iframe');
|
||||
this.addLog('💡 PIN-Einfügung funktioniert über PostMessage');
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Fehler beim Erstellen der lokalen Kopie: ${error.message}`);
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
},
|
||||
|
||||
addLog(message) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
this.logs.push(`[${timestamp}] ${message}`);
|
||||
},
|
||||
|
||||
formatSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nuscore-analyzer {
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.analyzer-header h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.analyzer-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.analyze-btn, .download-btn, .create-btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.analyze-btn {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.analyze-btn:hover:not(:disabled) {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
background: var(--secondary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.download-btn:hover:not(:disabled) {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.create-btn:hover:not(:disabled) {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.analyze-btn:disabled, .download-btn:disabled, .create-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.resource-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.resource-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.resource-item.downloaded {
|
||||
background: #d4edda;
|
||||
}
|
||||
|
||||
.resource-type {
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.resource-url {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.resource-size {
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
background: #000;
|
||||
color: #0f0;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
85
frontend/src/utils/nuscoreHTMLGenerator.js
Normal file
85
frontend/src/utils/nuscoreHTMLGenerator.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// HTML-Generator für nuscore lokale Kopie
|
||||
export function generateNuscoreHTML(scripts, stylesheets, useExternalUrls = false) {
|
||||
const parts = [];
|
||||
|
||||
parts.push('<!DOCTYPE html>');
|
||||
parts.push('<html lang="de">');
|
||||
|
||||
// Head
|
||||
parts.push('<head>');
|
||||
parts.push(' <meta charset="UTF-8">');
|
||||
parts.push(' <meta name="viewport" content="width=device-width, initial-scale=1.0">');
|
||||
parts.push(' <title>nuscore - Lokale Kopie</title>');
|
||||
parts.push(' <base href="/">');
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- Stylesheets -->');
|
||||
|
||||
stylesheets.forEach(sheet => {
|
||||
const href = useExternalUrls ? sheet.url : sheet.localUrl;
|
||||
parts.push(` <link rel="stylesheet" href="${href}">`);
|
||||
});
|
||||
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- PIN-Einfügung Script -->');
|
||||
parts.push(' <script>');
|
||||
parts.push(' window.addEventListener("message", function(event) {');
|
||||
parts.push(' if (event.data && event.data.action === "fillPin") {');
|
||||
parts.push(' console.log("PIN-Einfügung empfangen:", event.data.pin);');
|
||||
parts.push(' ');
|
||||
parts.push(' const pinSelectors = [');
|
||||
parts.push(' "input[type=\\"password\\"][placeholder*=\\"Vereins\\"]",');
|
||||
parts.push(' "input[type=\\"password\\"][placeholder*=\\"Spiel-Pin\\"]",');
|
||||
parts.push(' "input[type=\\"password\\"][placeholder*=\\"PIN\\"]",');
|
||||
parts.push(' "input[type=\\"password\\"]"');
|
||||
parts.push(' ];');
|
||||
parts.push(' ');
|
||||
parts.push(' let pinField = null;');
|
||||
parts.push(' for (const selector of pinSelectors) {');
|
||||
parts.push(' pinField = document.querySelector(selector);');
|
||||
parts.push(' if (pinField) break;');
|
||||
parts.push(' }');
|
||||
parts.push(' ');
|
||||
parts.push(' if (pinField) {');
|
||||
parts.push(' pinField.value = event.data.pin;');
|
||||
parts.push(' pinField.dispatchEvent(new Event("input", { bubbles: true }));');
|
||||
parts.push(' pinField.dispatchEvent(new Event("change", { bubbles: true }));');
|
||||
parts.push(' console.log("PIN erfolgreich eingefügt:", event.data.pin);');
|
||||
parts.push(' }');
|
||||
parts.push(' }');
|
||||
parts.push(' });');
|
||||
parts.push(' </script>');
|
||||
parts.push('</head>');
|
||||
|
||||
// Body
|
||||
parts.push('<body>');
|
||||
parts.push(' <app-root></app-root>');
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- Scripts -->');
|
||||
|
||||
scripts.forEach(script => {
|
||||
const src = useExternalUrls ? script.url : script.localUrl;
|
||||
parts.push(` <script src="${src}"></script>`);
|
||||
});
|
||||
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- Initialisierung -->');
|
||||
parts.push(' <script>');
|
||||
parts.push(' console.log("nuscore lokale Kopie gestartet");');
|
||||
parts.push(' console.log("PIN-Einfügung bereit");');
|
||||
parts.push(' ');
|
||||
parts.push(' const urlParams = new URLSearchParams(window.location.search);');
|
||||
parts.push(' const code = urlParams.get("code");');
|
||||
parts.push(' if (code) {');
|
||||
parts.push(' console.log("Code aus URL gefunden:", code);');
|
||||
parts.push(' }');
|
||||
parts.push(' ');
|
||||
parts.push(' // Angular Router Konfiguration für lokale Kopie');
|
||||
parts.push(' if (window.ng && window.ng.core) {');
|
||||
parts.push(' console.log("Angular gefunden, konfiguriere Router...");');
|
||||
parts.push(' }');
|
||||
parts.push(' </script>');
|
||||
parts.push('</body>');
|
||||
parts.push('</html>');
|
||||
|
||||
return parts.join('\n');
|
||||
}
|
||||
111
frontend/src/utils/simpleNuscoreHTMLGenerator.js
Normal file
111
frontend/src/utils/simpleNuscoreHTMLGenerator.js
Normal file
@@ -0,0 +1,111 @@
|
||||
// Einfacher HTML-Generator für nuscore lokale Kopie (iframe-basiert)
|
||||
export function generateSimpleNuscoreHTML(match) {
|
||||
const pin = match.homePin || match.guestPin || '';
|
||||
const code = match.code || '';
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>nuscore - Lokale Kopie</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
|
||||
.header { background: #f0f0f0; padding: 10px; border-bottom: 1px solid #ccc; }
|
||||
.controls { margin: 10px 0; }
|
||||
.controls button { margin-right: 10px; padding: 5px 10px; }
|
||||
.iframe-container { width: 100%; height: calc(100vh - 100px); }
|
||||
.iframe-container iframe { width: 100%; height: 100%; border: none; }
|
||||
.status { padding: 10px; background: #e8f5e8; border: 1px solid #4caf50; margin: 10px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>nuscore - Lokale Kopie</h1>
|
||||
<div class="controls">
|
||||
<button onclick="insertPin()">📌 PIN einfügen</button>
|
||||
<button onclick="copyPin()">📋 PIN kopieren</button>
|
||||
<input type="password" id="pinInput" placeholder="PIN eingeben" style="margin-left: 10px;">
|
||||
</div>
|
||||
<div class="status" id="status">Bereit für PIN-Einfügung</div>
|
||||
</div>
|
||||
<div class="iframe-container">
|
||||
<iframe id="nuscoreFrame" src="https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list"></iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentPin = "";
|
||||
|
||||
// PIN aus Match-Objekt laden
|
||||
const pin = "${pin}";
|
||||
const code = "${code}";
|
||||
|
||||
if (pin) {
|
||||
currentPin = pin;
|
||||
document.getElementById("pinInput").value = pin;
|
||||
document.getElementById("status").textContent = "PIN aus Match geladen: " + pin;
|
||||
}
|
||||
|
||||
function insertPin() {
|
||||
const pin = document.getElementById("pinInput").value || currentPin;
|
||||
if (!pin) {
|
||||
document.getElementById("status").textContent = "Keine PIN eingegeben";
|
||||
return;
|
||||
}
|
||||
|
||||
const iframe = document.getElementById("nuscoreFrame");
|
||||
const message = {
|
||||
action: "fillPin",
|
||||
pin: pin,
|
||||
timestamp: Date.now(),
|
||||
source: "nuscore-local"
|
||||
};
|
||||
|
||||
try {
|
||||
iframe.contentWindow.postMessage(message, "https://ttde-apps.liga.nu");
|
||||
document.getElementById("status").textContent = "PIN gesendet: " + pin;
|
||||
} catch (error) {
|
||||
document.getElementById("status").textContent = "Fehler beim Senden der PIN: " + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
function copyPin() {
|
||||
const pin = document.getElementById("pinInput").value || currentPin;
|
||||
if (!pin) {
|
||||
document.getElementById("status").textContent = "Keine PIN zum Kopieren";
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(pin).then(() => {
|
||||
document.getElementById("status").textContent = "PIN kopiert: " + pin;
|
||||
}).catch(() => {
|
||||
// Fallback
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = pin;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textArea);
|
||||
document.getElementById("status").textContent = "PIN kopiert (Fallback): " + pin;
|
||||
});
|
||||
}
|
||||
|
||||
// PostMessage Listener für Antworten vom iframe
|
||||
window.addEventListener("message", function(event) {
|
||||
if (event.origin !== "https://ttde-apps.liga.nu") return;
|
||||
|
||||
if (event.data && event.data.action === "pinInserted") {
|
||||
document.getElementById("status").textContent = "PIN erfolgreich eingefügt!";
|
||||
} else if (event.data && event.data.action === "pinError") {
|
||||
document.getElementById("status").textContent = "PIN-Einfügung fehlgeschlagen: " + event.data.error;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("nuscore lokale Kopie gestartet");
|
||||
if (code) console.log("Code aus Match:", code);
|
||||
if (pin) console.log("PIN aus Match:", pin);
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
@@ -93,13 +93,13 @@ import { mapGetters, mapActions } from 'vuex';
|
||||
import apiClient from '../apiClient.js';
|
||||
import PDFGenerator from '../components/PDFGenerator.js';
|
||||
import SeasonSelector from '../components/SeasonSelector.vue';
|
||||
import MatchReportDialog from '../components/MatchReportDialog.vue';
|
||||
import MatchReportApiDialog from '../components/MatchReportApiDialog.vue';
|
||||
|
||||
export default {
|
||||
name: 'ScheduleView',
|
||||
components: {
|
||||
SeasonSelector,
|
||||
MatchReportDialog
|
||||
MatchReportApiDialog
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'currentClub', 'clubs', 'currentClubName']),
|
||||
@@ -377,15 +377,9 @@ export default {
|
||||
const title = `${match.homeTeam?.name || 'N/A'} vs ${match.guestTeam?.name || 'N/A'} - ${this.selectedLeague}`;
|
||||
this.openDialog({
|
||||
title,
|
||||
component: 'MatchReportDialog',
|
||||
component: 'MatchReportApiDialog',
|
||||
props: {
|
||||
match
|
||||
},
|
||||
headerActions: {
|
||||
component: 'MatchReportHeaderActions',
|
||||
props: {
|
||||
match
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user