Refactor logging in cleanup scripts to use report array for improved output management

Updated various cleanup scripts to replace console.log statements with a report array, enhancing the output handling and allowing for better formatting of messages. This change improves the readability of logs and ensures consistent reporting across different cleanup operations, including database connection status, index management, and summary reports.
This commit is contained in:
Torsten Schulz (local)
2025-11-10 13:25:11 +01:00
parent eb37532de2
commit d94238f6df
28 changed files with 225 additions and 474 deletions

View File

@@ -9,15 +9,18 @@ const dbConfig = {
database: process.env.DB_NAME || 'trainingsdiary'
};
const report = [];
async function cleanupKeys() {
let connection;
try {
console.log('🔌 Verbinde mit der Datenbank...');
report.push('🔌 Verbinde mit der Datenbank...');
connection = await mysql.createConnection(dbConfig);
// 1. Status vor dem Cleanup
console.log('\n📊 STATUS VOR DEM CLEANUP:');
report.push('');
report.push('📊 STATUS VOR DEM CLEANUP:');
const [tablesBefore] = await connection.execute(`
SELECT
TABLE_NAME,
@@ -29,57 +32,60 @@ async function cleanupKeys() {
`, [dbConfig.database]);
tablesBefore.forEach(table => {
console.log(` ${table.TABLE_NAME}: ${table.key_count} Keys`);
report.push(` ${table.TABLE_NAME}: ${table.key_count} Keys`);
});
// 2. Alle INDEX der Problem-Tabellen anzeigen
const problemTables = ['member', 'diary_tags', 'season'];
for (const tableName of problemTables) {
console.log(`\n🔍 INDEX für Tabelle '${tableName}':`);
report.push('');
report.push(`🔍 INDEX für Tabelle '${tableName}':`);
try {
const [indexes] = await connection.execute(`SHOW INDEX FROM \`${tableName}\``);
if (indexes.length === 0) {
console.log(` Keine INDEX gefunden für Tabelle '${tableName}'`);
report.push(` Keine INDEX gefunden für Tabelle '${tableName}'`);
continue;
}
indexes.forEach(index => {
console.log(` - ${index.Key_name} (${index.Column_name}) - ${index.Non_unique === 0 ? 'UNIQUE' : 'NON-UNIQUE'}`);
report.push(` - ${index.Key_name} (${index.Column_name}) - ${index.Non_unique === 0 ? 'UNIQUE' : 'NON-UNIQUE'}`);
});
// 3. Überflüssige INDEX entfernen (alle außer PRIMARY und UNIQUE)
console.log(`\n🗑️ Entferne überflüssige INDEX aus '${tableName}':`);
report.push('');
report.push(`🗑️ Entferne überflüssige INDEX aus '${tableName}':`);
for (const index of indexes) {
// Behalte PRIMARY KEY und UNIQUE constraints
if (index.Key_name === 'PRIMARY' || index.Non_unique === 0) {
console.log(` ✅ Behalte: ${index.Key_name} (${index.Column_name})`);
report.push(` ✅ Behalte: ${index.Key_name} (${index.Column_name})`);
continue;
}
// Entferne alle anderen INDEX
try {
await connection.execute(`DROP INDEX \`${index.Key_name}\` ON \`${tableName}\``);
console.log(` ❌ Entfernt: ${index.Key_name} (${index.Column_name})`);
report.push(` ❌ Entfernt: ${index.Key_name} (${index.Column_name})`);
} catch (error) {
if (error.code === 'ER_CANT_DROP_FIELD_OR_KEY') {
console.log(` ⚠️ Kann nicht entfernen: ${index.Key_name} (${index.Column_name}) - ${error.message}`);
report.push(` ⚠️ Kann nicht entfernen: ${index.Key_name} (${index.Column_name}) - ${error.message}`);
} else {
console.log(` ❌ Fehler beim Entfernen von ${index.Key_name}: ${error.message}`);
report.push(` ❌ Fehler beim Entfernen von ${index.Key_name}: ${error.message}`);
}
}
}
} catch (error) {
console.log(` ⚠️ Fehler beim Zugriff auf Tabelle '${tableName}': ${error.message}`);
report.push(` ⚠️ Fehler beim Zugriff auf Tabelle '${tableName}': ${error.message}`);
}
}
// 4. Status nach dem Cleanup
console.log('\n📊 STATUS NACH DEM CLEANUP:');
report.push('');
report.push('📊 STATUS NACH DEM CLEANUP:');
const [tablesAfter] = await connection.execute(`
SELECT
TABLE_NAME,
@@ -96,7 +102,7 @@ async function cleanupKeys() {
const diff = beforeCount - table.key_count;
const status = table.key_count <= 5 ? '✅' : table.key_count <= 10 ? '⚠️' : '❌';
console.log(` ${status} ${table.TABLE_NAME}: ${table.key_count} Keys (${diff > 0 ? `-${diff}` : `+${Math.abs(diff)}`})`);
report.push(` ${status} ${table.TABLE_NAME}: ${table.key_count} Keys (${diff > 0 ? `-${diff}` : `+${Math.abs(diff)}`})`);
});
// 5. Gesamtanzahl der Keys
@@ -106,18 +112,20 @@ async function cleanupKeys() {
WHERE TABLE_SCHEMA = ?
`, [dbConfig.database]);
console.log(`\n📈 GESAMTANZAHL KEYS: ${totalKeys[0].total_keys}`);
report.push('');
report.push(`📈 GESAMTANZAHL KEYS: ${totalKeys[0].total_keys}`);
// 6. Zusammenfassung
console.log('\n🎯 ZUSAMMENFASSUNG:');
report.push('');
report.push('🎯 ZUSAMMENFASSUNG:');
const problemTablesAfter = tablesAfter.filter(t => t.key_count > 10);
if (problemTablesAfter.length === 0) {
console.log(' ✅ Alle Tabellen haben jetzt weniger als 10 Keys!');
report.push(' ✅ Alle Tabellen haben jetzt weniger als 10 Keys!');
} else {
console.log(' ⚠️ Folgende Tabellen haben immer noch zu viele Keys:');
report.push(' ⚠️ Folgende Tabellen haben immer noch zu viele Keys:');
problemTablesAfter.forEach(table => {
console.log(` - ${table.TABLE_NAME}: ${table.key_count} Keys`);
report.push(` - ${table.TABLE_NAME}: ${table.key_count} Keys`);
});
}
@@ -126,15 +134,18 @@ async function cleanupKeys() {
} finally {
if (connection) {
await connection.end();
console.log('\n🔌 Datenbankverbindung geschlossen.');
report.push('');
report.push('🔌 Datenbankverbindung geschlossen.');
}
}
}
// Script ausführen
console.log('🚀 Starte intelligentes INDEX-Cleanup...\n');
report.push('🚀 Starte intelligentes INDEX-Cleanup...');
cleanupKeys().then(() => {
console.log('\n✨ Cleanup abgeschlossen!');
report.push('');
report.push('✨ Cleanup abgeschlossen!');
process.stdout.write(`${report.join('\n')}\n`);
process.exit(0);
}).catch(error => {
console.error('\n💥 Fehler beim Cleanup:', error);

View File

@@ -7,41 +7,23 @@ import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const report = [];
// Umgebungsvariablen aus dem Root-Verzeichnis laden
//const envPath = path.join(__dirname, '..', '.env');
//console.log('🔍 Lade .env-Datei von:', envPath);
const envPath = path.join(__dirname, '..', '.env');
dotenv.config();
// Debug: Zeige geladene Umgebungsvariablen
console.log('🔍 Geladene Umgebungsvariablen:');
console.log(' DB_HOST:', process.env.DB_HOST);
console.log(' DB_USER:', process.env.DB_USER);
console.log(' DB_NAME:', process.env.DB_NAME);
console.log(' DB_PASSWORD:', process.env.DB_PASSWORD ? '***gesetzt***' : 'nicht gesetzt');
// Datenbankverbindung
const dbConfig = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'trainingsdiary'
};
console.log('🔍 Datenbankverbindung:');
console.log(' Host:', dbConfig.host);
console.log(' User:', dbConfig.user);
console.log(' Database:', dbConfig.database);
console.log(' Password:', dbConfig.password ? '***gesetzt***' : 'nicht gesetzt');
report.push('Environment variables loaded');
async function cleanupKeys() {
let connection;
try {
console.log('🔌 Verbinde mit der Datenbank...');
report.push('🔌 Verbinde mit der Datenbank...');
connection = await mysql.createConnection(dbConfig);
// 1. Status vor dem Cleanup
console.log('\n📊 STATUS VOR DEM CLEANUP:');
report.push('');
report.push('📊 STATUS VOR DEM CLEANUP:');
const [tablesBefore] = await connection.execute(`
SELECT
TABLE_NAME,
@@ -53,57 +35,60 @@ async function cleanupKeys() {
`, [dbConfig.database]);
tablesBefore.forEach(table => {
console.log(` ${table.TABLE_NAME}: ${table.key_count} Keys`);
report.push(` ${table.TABLE_NAME}: ${table.key_count} Keys`);
});
// 2. Alle INDEX der Problem-Tabellen anzeigen
const problemTables = ['member', 'diary_tags', 'season'];
for (const tableName of problemTables) {
console.log(`\n🔍 INDEX für Tabelle '${tableName}':`);
report.push('');
report.push(`🔍 INDEX für Tabelle '${tableName}':`);
try {
const [indexes] = await connection.execute(`SHOW INDEX FROM \`${tableName}\``);
if (indexes.length === 0) {
console.log(` Keine INDEX gefunden für Tabelle '${tableName}'`);
report.push(` Keine INDEX gefunden für Tabelle '${tableName}'`);
continue;
}
indexes.forEach(index => {
console.log(` - ${index.Key_name} (${index.Column_name}) - ${index.Non_unique === 0 ? 'UNIQUE' : 'NON-UNIQUE'}`);
report.push(` - ${index.Key_name} (${index.Column_name}) - ${index.Non_unique === 0 ? 'UNIQUE' : 'NON-UNIQUE'}`);
});
// 3. Überflüssige INDEX entfernen (alle außer PRIMARY und UNIQUE)
console.log(`\n🗑️ Entferne überflüssige INDEX aus '${tableName}':`);
report.push('');
report.push(`🗑️ Entferne überflüssige INDEX aus '${tableName}':`);
for (const index of indexes) {
// Behalte PRIMARY KEY und UNIQUE constraints
if (index.Key_name === 'PRIMARY' || index.Non_unique === 0) {
console.log(` ✅ Behalte: ${index.Key_name} (${index.Column_name})`);
report.push(` ✅ Behalte: ${index.Key_name} (${index.Column_name})`);
continue;
}
// Entferne alle anderen INDEX
try {
await connection.execute(`DROP INDEX \`${index.Key_name}\` ON \`${tableName}\``);
console.log(` ❌ Entfernt: ${index.Key_name} (${index.Column_name})`);
report.push(` ❌ Entfernt: ${index.Key_name} (${index.Column_name})`);
} catch (error) {
if (error.code === 'ER_CANT_DROP_FIELD_OR_KEY') {
console.log(` ⚠️ Kann nicht entfernen: ${index.Key_name} (${index.Column_name}) - ${error.message}`);
report.push(` ⚠️ Kann nicht entfernen: ${index.Key_name} (${index.Column_name}) - ${error.message}`);
} else {
console.log(` ❌ Fehler beim Entfernen von ${index.Key_name}: ${error.message}`);
report.push(` ❌ Fehler beim Entfernen von ${index.Key_name}: ${error.message}`);
}
}
}
} catch (error) {
console.log(` ⚠️ Fehler beim Zugriff auf Tabelle '${tableName}': ${error.message}`);
report.push(` ⚠️ Fehler beim Zugriff auf Tabelle '${tableName}': ${error.message}`);
}
}
// 4. Status nach dem Cleanup
console.log('\n📊 STATUS NACH DEM CLEANUP:');
report.push('');
report.push('📊 STATUS NACH DEM CLEANUP:');
const [tablesAfter] = await connection.execute(`
SELECT
TABLE_NAME,
@@ -120,7 +105,7 @@ async function cleanupKeys() {
const diff = beforeCount - table.key_count;
const status = table.key_count <= 5 ? '✅' : table.key_count <= 10 ? '⚠️' : '❌';
console.log(` ${status} ${table.TABLE_NAME}: ${table.key_count} Keys (${diff > 0 ? `-${diff}` : `+${Math.abs(diff)}`})`);
report.push(` ${status} ${table.TABLE_NAME}: ${table.key_count} Keys (${diff > 0 ? `-${diff}` : `+${Math.abs(diff)}`})`);
});
// 5. Gesamtanzahl der Keys
@@ -130,18 +115,20 @@ async function cleanupKeys() {
WHERE TABLE_SCHEMA = ?
`, [dbConfig.database]);
console.log(`\n📈 GESAMTANZAHL KEYS: ${totalKeys[0].total_keys}`);
report.push('');
report.push(`📈 GESAMTANZAHL KEYS: ${totalKeys[0].total_keys}`);
// 6. Zusammenfassung
console.log('\n🎯 ZUSAMMENFASSUNG:');
report.push('');
report.push('🎯 ZUSAMMENFASSUNG:');
const problemTablesAfter = tablesAfter.filter(t => t.key_count > 10);
if (problemTablesAfter.length === 0) {
console.log(' ✅ Alle Tabellen haben jetzt weniger als 10 Keys!');
report.push(' ✅ Alle Tabellen haben jetzt weniger als 10 Keys!');
} else {
console.log(' ⚠️ Folgende Tabellen haben immer noch zu viele Keys:');
report.push(' ⚠️ Folgende Tabellen haben immer noch zu viele Keys:');
problemTablesAfter.forEach(table => {
console.log(` - ${table.TABLE_NAME}: ${table.key_count} Keys`);
report.push(` - ${table.TABLE_NAME}: ${table.key_count} Keys`);
});
}
@@ -150,15 +137,18 @@ async function cleanupKeys() {
} finally {
if (connection) {
await connection.end();
console.log('\n🔌 Datenbankverbindung geschlossen.');
report.push('');
report.push('🔌 Datenbankverbindung geschlossen.');
}
}
}
// Script ausführen
console.log('🚀 Starte intelligentes INDEX-Cleanup...\n');
report.push('🚀 Starte intelligentes INDEX-Cleanup...');
cleanupKeys().then(() => {
console.log('\n✨ Cleanup abgeschlossen!');
report.push('');
report.push('✨ Cleanup abgeschlossen!');
process.stdout.write(`${report.join('\n')}\n`);
process.exit(0);
}).catch(error => {
console.error('\n💥 Fehler beim Cleanup:', error);

View File

@@ -156,17 +156,6 @@ class MyTischtennisClient {
if (result.success) {
console.log('[getUserProfile] - Response structure:', {
hasUserProfile: !!result.data?.userProfile,
hasClub: !!result.data?.userProfile?.club,
hasOrganization: !!result.data?.userProfile?.organization,
clubnr: result.data?.userProfile?.club?.clubnr,
clubName: result.data?.userProfile?.club?.name,
orgShort: result.data?.userProfile?.organization?.short,
ttr: result.data?.userProfile?.ttr,
qttr: result.data?.userProfile?.qttr
});
return {
success: true,

View File

@@ -20,13 +20,6 @@ export const addMembersToActivity = async (req, res) => {
const { clubId, diaryDateActivityId } = req.params;
const { participantIds } = req.body; // array of participant ids
console.log('[addMembersToActivity] Request:', {
clubId,
diaryDateActivityId,
participantIds,
bodyKeys: Object.keys(req.body)
});
await checkAccess(userToken, clubId);
if (!participantIds || !Array.isArray(participantIds)) {
@@ -35,25 +28,20 @@ export const addMembersToActivity = async (req, res) => {
}
const validParticipants = await Participant.findAll({ where: { id: participantIds } });
console.log('[addMembersToActivity] Valid participants found:', validParticipants.length);
const validIds = new Set(validParticipants.map(p => p.id));
const created = [];
for (const pid of participantIds) {
if (!validIds.has(pid)) {
console.log('[addMembersToActivity] Participant not found:', pid);
continue;
}
const existing = await DiaryMemberActivity.findOne({ where: { diaryDateActivityId, participantId: pid } });
if (!existing) {
const rec = await DiaryMemberActivity.create({ diaryDateActivityId, participantId: pid });
console.log('[addMembersToActivity] Created:', rec.id);
created.push(rec);
} else {
console.log('[addMembersToActivity] Already exists:', pid);
}
}
console.log('[addMembersToActivity] Success, created:', created.length);
res.status(201).json(created);
} catch (e) {
console.error('[addMembersToActivity] Error:', e);

View File

@@ -3,6 +3,8 @@ import dotenv from 'dotenv';
dotenv.config();
const report = [];
const dbConfig = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
@@ -56,9 +58,9 @@ async function cleanupDuplicates(connection, table) {
for (const idxName of dropNames) {
try {
await connection.execute(`DROP INDEX \`${idxName}\` ON \`${table}\``);
console.log(`[drop] ${table}: ${idxName}`);
report.push(`[drop] ${table}: ${idxName}`);
} catch (e) {
console.warn(`[warn] ${table}: konnte Index ${idxName} nicht löschen: ${e.code || e.message}`);
report.push(`[warn] ${table}: konnte Index ${idxName} nicht löschen: ${e.code || e.message}`);
}
}
@@ -69,11 +71,11 @@ async function cleanupDuplicates(connection, table) {
async function main() {
let connection;
try {
console.log('Connecting to DB:', dbConfig);
report.push(`Connecting to DB: ${JSON.stringify(dbConfig)}`);
connection = await mysql.createConnection(dbConfig);
const tables = await getTables(connection);
console.log(`Found ${tables.length} tables`);
report.push(`Found ${tables.length} tables`);
let totalBefore = 0;
let totalAfter = 0;
@@ -86,12 +88,15 @@ async function main() {
totalDropped += dropped;
}
console.log('Summary:', { totalBefore, totalAfter, totalDropped });
report.push(`Summary: ${JSON.stringify({ totalBefore, totalAfter, totalDropped })}`);
} catch (e) {
console.error('Cleanup failed:', e);
process.exitCode = 1;
} finally {
if (connection) await connection.end();
if (report.length > 0) {
process.stdout.write(`${report.join('\n')}\n`);
}
}
}

View File

@@ -3,6 +3,8 @@ import dotenv from 'dotenv';
dotenv.config();
const report = [];
const dbConfig = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
@@ -26,13 +28,14 @@ async function cleanupUserTokenKeys() {
const table = 'UserToken';
try {
console.log('Connecting to DB:', dbConfig);
report.push(`Connecting to DB: ${JSON.stringify(dbConfig)}`);
connection = await mysql.createConnection(dbConfig);
console.log(`\nBefore cleanup (indexes on ${table}):`);
report.push('');
report.push(`Before cleanup (indexes on ${table}):`);
let before = await getIndexSummary(connection, table);
Object.entries(before).forEach(([name, info]) => {
console.log(` - ${name} ${info.unique ? '(UNIQUE)' : ''} -> [${info.columns.join(', ')}]`);
report.push(` - ${name} ${info.unique ? '(UNIQUE)' : ''} -> [${info.columns.join(', ')}]`);
});
// Drop all non-PRIMARY indexes on UserToken
@@ -42,9 +45,9 @@ async function cleanupUserTokenKeys() {
for (const keyName of keyNames) {
try {
await connection.execute(`DROP INDEX \`${keyName}\` ON \`${table}\``);
console.log(`Dropped index: ${keyName}`);
report.push(`Dropped index: ${keyName}`);
} catch (err) {
console.warn(`Could not drop ${keyName}: ${err.code || err.message}`);
report.push(`Could not drop ${keyName}: ${err.code || err.message}`);
}
}
@@ -52,9 +55,9 @@ async function cleanupUserTokenKeys() {
// Unique on token (column is 'token')
try {
await connection.execute(`CREATE UNIQUE INDEX \`uniq_UserToken_token\` ON \`${table}\` (\`token\`)`);
console.log('Created UNIQUE index: uniq_UserToken_token (token)');
report.push('Created UNIQUE index: uniq_UserToken_token (token)');
} catch (err) {
console.warn('Could not create uniq_UserToken_token:', err.code || err.message);
report.push(`Could not create uniq_UserToken_token: ${err.code || err.message}`);
}
// Helpful index on user_id if column exists
@@ -62,26 +65,31 @@ async function cleanupUserTokenKeys() {
const [cols] = await connection.execute(`SHOW COLUMNS FROM \`${table}\` LIKE 'user_id'`);
if (cols && cols.length > 0) {
await connection.execute(`CREATE INDEX \`idx_UserToken_user_id\` ON \`${table}\` (\`user_id\`)`);
console.log('Created INDEX: idx_UserToken_user_id (user_id)');
report.push('Created INDEX: idx_UserToken_user_id (user_id)');
} else {
console.log('Column user_id not found, skip creating idx_UserToken_user_id');
report.push('Column user_id not found, skip creating idx_UserToken_user_id');
}
} catch (err) {
console.warn('Could not create idx_UserToken_user_id:', err.code || err.message);
report.push(`Could not create idx_UserToken_user_id: ${err.code || err.message}`);
}
console.log(`\nAfter cleanup (indexes on ${table}):`);
report.push('');
report.push(`After cleanup (indexes on ${table}):`);
const after = await getIndexSummary(connection, table);
Object.entries(after).forEach(([name, info]) => {
console.log(` - ${name} ${info.unique ? '(UNIQUE)' : ''} -> [${info.columns.join(', ')}]`);
report.push(` - ${name} ${info.unique ? '(UNIQUE)' : ''} -> [${info.columns.join(', ')}]`);
});
console.log('\nDone.');
report.push('');
report.push('Done.');
} catch (err) {
console.error('Cleanup failed:', err);
process.exitCode = 1;
} finally {
if (connection) await connection.end();
if (report.length > 0) {
process.stdout.write(`${report.join('\n')}\n`);
}
}
}

View File

@@ -47,7 +47,8 @@ const TEST_USERS = [
];
async function createTestUsers() {
console.log('Creating test users...\n');
const report = [];
report.push('Creating test users...');
try {
// Get first club (or specify club ID)
@@ -59,16 +60,16 @@ async function createTestUsers() {
}
const club = clubs[0];
console.log(`Using club: ${club.name} (ID: ${club.id})\n`);
report.push(`Using club: ${club.name} (ID: ${club.id})`);
for (const userData of TEST_USERS) {
console.log(`Creating user: ${userData.email} (${userData.role})...`);
report.push(`Creating user: ${userData.email} (${userData.role})...`);
// Check if user already exists
let user = await User.findOne({ where: { email: userData.email } });
if (user) {
console.log(` ⚠️ User already exists, using existing user`);
report.push(' ⚠️ User already exists, using existing user');
} else {
// Create user
user = await User.create({
@@ -76,7 +77,7 @@ async function createTestUsers() {
password: userData.password,
isActive: true
});
console.log(` ✓ User created`);
report.push(' ✓ User created');
}
// Check if user is already in club
@@ -88,13 +89,13 @@ async function createTestUsers() {
});
if (userClub) {
console.log(` ⚠️ User already in club, updating role...`);
report.push(' ⚠️ User already in club, updating role...');
await userClub.update({
role: userData.role,
isOwner: userData.isOwner,
approved: true
});
console.log(` ✓ Updated to role: ${userData.role}`);
report.push(` ✓ Updated to role: ${userData.role}`);
} else {
// Add user to club
userClub = await UserClub.create({
@@ -104,31 +105,37 @@ async function createTestUsers() {
isOwner: userData.isOwner,
approved: true
});
console.log(` ✓ Added to club with role: ${userData.role}`);
report.push(` ✓ Added to club with role: ${userData.role}`);
}
}
console.log('\n✅ Test users created successfully!\n');
report.push('');
report.push('✅ Test users created successfully!');
// Show summary
console.log('Summary:');
console.log('========================================');
console.log(`Club: ${club.name}`);
console.log('\nTest Users:');
report.push('Summary:');
report.push('========================================');
report.push(`Club: ${club.name}`);
report.push('');
report.push('Test Users:');
for (const userData of TEST_USERS) {
console.log(` ${userData.email.padEnd(25)} | ${userData.role.padEnd(15)} | Password: test123`);
report.push(` ${userData.email.padEnd(25)} | ${userData.role.padEnd(15)} | Password: test123`);
}
console.log('\n========================================');
console.log('You can now login with any of these users!');
console.log('All passwords are: test123');
report.push('');
report.push('========================================');
report.push('You can now login with any of these users!');
report.push('All passwords are: test123');
} catch (error) {
console.error('❌ Error creating test users:', error);
throw error;
} finally {
await sequelize.close();
if (report.length > 0) {
process.stdout.write(`${report.join('\n')}\n`);
}
}
}

View File

@@ -11,7 +11,8 @@ import sequelize from '../database.js';
*/
async function migratePermissions() {
console.log('Starting permissions migration...\n');
const report = [];
report.push('Starting permissions migration...');
try {
// Get all clubs
@@ -29,10 +30,11 @@ async function migratePermissions() {
}]
});
console.log(`Found ${clubs.length} club(s)\n`);
report.push(`Found ${clubs.length} club(s)`);
for (const club of clubs) {
console.log(`\n--- Club: ${club.name} (ID: ${club.id}) ---`);
report.push(``);
report.push(`--- Club: ${club.name} (ID: ${club.id}) ---`);
const userClubs = await UserClub.findAll({
where: {
@@ -47,15 +49,15 @@ async function migratePermissions() {
});
if (userClubs.length === 0) {
console.log(' No approved members found.');
report.push(' No approved members found.');
continue;
}
// First user becomes owner
const firstUser = userClubs[0];
console.log(` Members found: ${userClubs.length}`);
console.log(` First member (will be owner): ${firstUser.user.email}`);
report.push(` Members found: ${userClubs.length}`);
report.push(` First member (will be owner): ${firstUser.user.email}`);
for (let i = 0; i < userClubs.length; i++) {
const userClub = userClubs[i];
@@ -71,12 +73,14 @@ async function migratePermissions() {
await userClub.save();
console.log(` ✓ Updated ${userClub.user.email}: role=${userClub.role}, isOwner=${userClub.isOwner}`);
report.push(` ✓ Updated ${userClub.user.email}: role=${userClub.role}, isOwner=${userClub.isOwner}`);
}
}
console.log('\n✅ Migration completed successfully!');
console.log('\nSummary:');
report.push('');
report.push('✅ Migration completed successfully!');
report.push('');
report.push('Summary:');
// Show summary
const owners = await UserClub.findAll({
@@ -95,9 +99,9 @@ async function migratePermissions() {
]
});
console.log(`\nClub Owners (${owners.length}):`);
report.push(`\nClub Owners (${owners.length}):`);
for (const owner of owners) {
console.log(` - ${owner.club.name}: ${owner.user.email}`);
report.push(` - ${owner.club.name}: ${owner.user.email}`);
}
const admins = await UserClub.count({
@@ -107,15 +111,19 @@ async function migratePermissions() {
where: { role: 'member' }
});
console.log(`\nRole Distribution:`);
console.log(` - Admins: ${admins}`);
console.log(` - Members: ${members}`);
report.push('');
report.push('Role Distribution:');
report.push(` - Admins: ${admins}`);
report.push(` - Members: ${members}`);
} catch (error) {
console.error('❌ Migration failed:', error);
throw error;
} finally {
await sequelize.close();
if (report.length > 0) {
process.stdout.write(`${report.join('\n')}\n`);
}
}
}

View File

@@ -9,15 +9,15 @@ import sequelize from '../database.js';
*/
async function quickFixOwners() {
console.log('Quick Fix: Setting club owners...\n');
const report = [];
report.push('Quick Fix: Setting club owners...');
try {
const clubs = await Club.findAll();
console.log(`Found ${clubs.length} club(s)\n`);
report.push(`Found ${clubs.length} club(s)`);
for (const club of clubs) {
console.log(`Club: ${club.name} (ID: ${club.id})`);
report.push(`Club: ${club.name} (ID: ${club.id})`);
// Find all approved members, ordered by creation date
const userClubs = await UserClub.findAll({
@@ -34,7 +34,7 @@ async function quickFixOwners() {
});
if (userClubs.length === 0) {
console.log(' ⚠️ No approved members\n');
report.push(' ⚠️ No approved members');
continue;
}
@@ -58,21 +58,20 @@ async function quickFixOwners() {
role: 'admin'
});
console.log(` ✅ Owner: ${firstUserClub.user.email}`);
report.push(` ✅ Owner: ${firstUserClub.user.email}`);
// Set role for other members if not set
for (let i = 1; i < userClubs.length; i++) {
const uc = userClubs[i];
if (!uc.role) {
await uc.update({ role: 'member' });
console.log(` 👤 Member: ${uc.user.email}`);
report.push(` 👤 Member: ${uc.user.email}`);
}
}
console.log('');
report.push('');
}
console.log('✅ Quick fix completed!\n');
report.push('✅ Quick fix completed!');
// Show all owners
const owners = await UserClub.findAll({
@@ -82,10 +81,9 @@ async function quickFixOwners() {
{ model: Club, as: 'club', attributes: ['name'] }
]
});
console.log('Current Club Owners:');
report.push('Current Club Owners:');
for (const owner of owners) {
console.log(` 📍 ${owner.club.name}: ${owner.user.email} (role: ${owner.role})`);
report.push(` 📍 ${owner.club.name}: ${owner.user.email} (role: ${owner.role})`);
}
} catch (error) {
@@ -93,6 +91,9 @@ async function quickFixOwners() {
throw error;
} finally {
await sequelize.close();
if (report.length > 0) {
process.stdout.write(`${report.join('\n')}\n`);
}
}
}

View File

@@ -235,12 +235,7 @@ app.get('*', (req, res) => {
// Start scheduler service
schedulerService.start();
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
console.log('Scheduler service started:');
console.log(' - Rating updates: 6:00 AM daily');
console.log(' - Match results fetch: 6:30 AM daily');
});
app.listen(port);
} catch (err) {
console.error('Unable to synchronize the database:', err);
}

View File

@@ -277,12 +277,6 @@ class DiaryDateActivityService {
throw new Error('Group not found');
}
console.log('[DiaryDateActivityService::addGroupActivity] Group found:', {
groupId: group.id,
groupDiaryDateId: group.diaryDateId,
activityDiaryDateId: diaryDateActivity.diaryDateId
});
if (group.diaryDateId !== diaryDateActivity.diaryDateId) {
console.error('[DiaryDateActivityService::addGroupActivity] Group and date don\'t fit');
console.error('Group diaryDateId:', group.diaryDateId, 'Activity diaryDateId:', diaryDateActivity.diaryDateId);

View File

@@ -230,7 +230,6 @@ class MemberService {
try {
session = await myTischtennisService.getSession(userId);
} catch (sessionError) {
console.log('[updateRatingsFromMyTischtennis] - Session invalid, attempting login...', sessionError.message);
// Versuche automatischen Login mit gespeicherten Credentials
try {
@@ -243,7 +242,6 @@ class MemberService {
expiresAt: freshSession.expiresAt,
userData: freshSession.userData
};
console.log('[updateRatingsFromMyTischtennis] - Automatic login successful');
} catch (loginError) {
console.error('[updateRatingsFromMyTischtennis] - Automatic login failed:', loginError.message);
return {
@@ -354,6 +352,7 @@ class MemberService {
account.fedNickname,
'no'
);
let qttrWarning = null;
try {
await (await import('./apiLogService.js')).default.logRequest({
userId,
@@ -386,7 +385,7 @@ class MemberService {
}
if (!rankingsQuarter.success) {
// QTTR optional; nicht hart abbrechen, aber vermerken
console.warn('[updateRatingsFromMyTischtennis] - QTTR Abruf fehlgeschlagen:', rankingsQuarter.error);
qttrWarning = rankingsQuarter.error || 'QTTR Abruf fehlgeschlagen';
}
// 3. Alle Mitglieder des Clubs laden
@@ -394,6 +393,12 @@ class MemberService {
let updated = 0;
const errors = [];
if (qttrWarning) {
errors.push({
type: 'warning',
message: qttrWarning
});
}
const notFound = [];
const matched = [];

View File

@@ -109,21 +109,11 @@ class PDFParserService {
const result = strategy.fn(lines, clubId, filteredLineEntries.length === lines.length ? filteredLineEntries : null);
if (result.matches.length > 0) {
console.log(`[PDF Parser] Using strategy: ${strategy.name}, found ${result.matches.length} matches`);
if (result.matches.length > 0) {
console.log(`[PDF Parser] First match sample:`, {
homeTeamName: result.matches[0].homeTeamName,
guestTeamName: result.matches[0].guestTeamName,
date: result.matches[0].date,
rawLine: result.matches[0].rawLine
});
}
matches.push(...result.matches);
metadata.parsedMatches += result.matches.length;
break; // Erste erfolgreiche Strategie verwenden
}
} catch (strategyError) {
console.log(`[PDF Parser] Strategy ${strategy.name} failed:`, strategyError.message);
errors.push(`Strategy ${strategy.name} failed: ${strategyError.message}`);
}
}
@@ -423,18 +413,6 @@ class PDFParserService {
}
if (homeTeamName && guestTeamName) {
let debugInfo;
if (code) {
debugInfo = `code: "${code}"`;
} else if (homePin && guestPin) {
debugInfo = `homePin: "${homePin}", guestPin: "${guestPin}"`;
} else if (homePin) {
debugInfo = `homePin: "${homePin}"`;
} else if (guestPin) {
debugInfo = `guestPin: "${guestPin}"`;
}
console.log(`[PDF Parser] Parsed match: ${homeTeamName} vs ${guestTeamName}, ${debugInfo}`);
matches.push({
date: date,
@@ -769,27 +747,18 @@ class PDFParserService {
where: { clubId: matchData.clubId },
attributes: ['id', 'name']
});
console.log(`[PDF Parser] Available teams in club: ${allTeams.map(t => t.name).join(', ')}`);
// Fuzzy-Matching für Team-Namen
if (!homeTeam) {
homeTeam = allTeams.find(t =>
PDFParserService.namesRoughlyMatch(t.name, matchData.homeTeamName)
);
if (homeTeam) {
console.log(`[PDF Parser] Found home team via fuzzy match: "${matchData.homeTeamName}" → "${homeTeam.name}"`);
}
}
if (!guestTeam) {
guestTeam = allTeams.find(t =>
PDFParserService.namesRoughlyMatch(t.name, matchData.guestTeamName)
);
if (guestTeam) {
console.log(`[PDF Parser] Found guest team via fuzzy match: "${matchData.guestTeamName}" → "${guestTeam.name}"`);
}
}
}

View File

@@ -24,28 +24,22 @@ class SchedulerService {
// Schedule automatic rating updates at 6:00 AM daily
const ratingUpdateJob = cron.schedule('0 6 * * *', async () => {
const startTime = Date.now();
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] CRON: Executing scheduled rating updates...`);
devLog('Executing scheduled rating updates...');
devLog(`[${new Date().toISOString()}] CRON: Executing scheduled rating updates...`);
let success = false;
let message = '';
let errorMessage = null;
try {
// Let the service return details including counts if available
const result = await autoUpdateRatingsService.executeAutomaticUpdates();
const executionTime = Date.now() - startTime;
success = true;
// result may include updatedCount or a summary object
const messageObj = result && typeof result === 'object' ? result : { message: 'Rating updates completed successfully' };
console.log(`[${new Date().toISOString()}] CRON: Rating updates completed successfully`);
// Log to ApiLog with rich details
await apiLogService.logSchedulerExecution('rating_updates', true, messageObj, executionTime, null);
devLog('Scheduled rating updates completed successfully');
} catch (error) {
const executionTime = Date.now() - startTime;
success = false;
errorMessage = error.message;
console.error(`[${new Date().toISOString()}] CRON ERROR in scheduled rating updates:`, error);
console.error('Stack trace:', error.stack);
@@ -60,32 +54,26 @@ class SchedulerService {
this.jobs.set('ratingUpdates', ratingUpdateJob);
ratingUpdateJob.start();
console.log('[Scheduler] Rating update job scheduled and started');
devLog('Rating update job scheduled and started');
// Schedule automatic match results fetching at 6:30 AM daily
const matchResultsJob = cron.schedule('30 6 * * *', async () => {
const startTime = Date.now();
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] CRON: Executing scheduled match results fetch...`);
devLog('Executing scheduled match results fetch...');
devLog(`[${new Date().toISOString()}] CRON: Executing scheduled match results fetch...`);
let success = false;
let message = '';
let errorMessage = null;
try {
// Execute and capture returned summary (should include counts)
const result = await autoFetchMatchResultsService.executeAutomaticFetch();
const executionTime = Date.now() - startTime;
success = true;
const messageObj = result && typeof result === 'object' ? result : { message: 'Match results fetch completed successfully' };
console.log(`[${new Date().toISOString()}] CRON: Match results fetch completed successfully`);
// Log to ApiLog with rich details (including counts if present)
await apiLogService.logSchedulerExecution('match_results', true, messageObj, executionTime, null);
devLog('Scheduled match results fetch completed successfully');
} catch (error) {
const executionTime = Date.now() - startTime;
success = false;
errorMessage = error.message;
console.error(`[${new Date().toISOString()}] CRON ERROR in scheduled match results fetch:`, error);
console.error('Stack trace:', error.stack);
@@ -100,7 +88,7 @@ class SchedulerService {
this.jobs.set('matchResults', matchResultsJob);
matchResultsJob.start();
console.log('[Scheduler] Match results fetch job scheduled and started');
devLog('Match results fetch job scheduled and started');
this.isRunning = true;
const now = new Date();
@@ -112,12 +100,12 @@ class SchedulerService {
tomorrow630AM.setDate(tomorrow630AM.getDate() + 1);
tomorrow630AM.setHours(6, 30, 0, 0);
console.log('[Scheduler] ===== SCHEDULER SERVICE STARTED =====');
console.log(`[Scheduler] Server time: ${now.toISOString()}`);
console.log(`[Scheduler] Timezone: Europe/Berlin`);
console.log(`[Scheduler] Rating updates: Next execution at ${tomorrow6AM.toISOString()} (6:00 AM Berlin time)`);
console.log(`[Scheduler] Match results fetch: Next execution at ${tomorrow630AM.toISOString()} (6:30 AM Berlin time)`);
console.log('[Scheduler] =====================================');
devLog('[Scheduler] ===== SCHEDULER SERVICE STARTED =====');
devLog(`[Scheduler] Server time: ${now.toISOString()}`);
devLog(`[Scheduler] Timezone: Europe/Berlin`);
devLog(`[Scheduler] Rating updates: Next execution at ${tomorrow6AM.toISOString()} (6:00 AM Berlin time)`);
devLog(`[Scheduler] Match results fetch: Next execution at ${tomorrow630AM.toISOString()} (6:30 AM Berlin time)`);
devLog('[Scheduler] =====================================');
devLog('Scheduler service started successfully');
devLog('Rating updates scheduled for 6:00 AM daily (Europe/Berlin timezone)');

View File

@@ -207,7 +207,6 @@ class TournamentService {
const gm = await TournamentMember.findAll({ where: { groupId: g.id } });
if (gm.length < 2) {
console.warn(`Gruppe ${g.id} hat nur ${gm.length} Teilnehmer - keine Matches erstellt`);
continue;
}
@@ -227,8 +226,6 @@ class TournamentService {
player2Id: p2Id,
groupRound: roundIndex + 1
});
} else {
console.warn(`Spieler gehören nicht zur gleichen Gruppe: ${p1Id} (${p1?.groupId}) vs ${p2Id} (${p2?.groupId}) in Gruppe ${g.id}`);
}
}
}

View File

@@ -18,7 +18,6 @@ function encryptData(data) {
encrypted += cipher.final('hex');
return encrypted;
} catch (error) {
console.log(error, data, process.env.ENCRYPTION_KEY, typeof data, process.env.ENCRYPTION_KEY.length);
return '';
}
}

View File

@@ -71,7 +71,7 @@ export default {
const isOpen = ref(false);
const handleClose = () => {
console.log('Dialog geschlossen');
// Reagiere auf das Schließen des Dialogs
};
return { isOpen, handleClose };
@@ -163,11 +163,11 @@ export default {
const showConfirm = ref(false);
const handleDelete = () => {
console.log('Gelöscht');
// Lösche den Eintrag
};
const handleCancel = () => {
console.log('Abgebrochen');
// Brich die Aktion ab
};
return { showConfirm, handleDelete, handleCancel };
@@ -307,108 +307,4 @@ toggle();
Promise-basiertes Composable für Bestätigungsdialoge.
```javascript
import { useConfirm } from '@/composables/useDialog.js';
const { isOpen, config, confirm, handleConfirm, handleCancel } = useConfirm();
// Im Template:
<ConfirmDialog
v-model="isOpen"
:title="config.title"
:message="config.message"
:type="config.type"
@confirm="handleConfirm"
@cancel="handleCancel"
/>
// In Methoden:
async function deleteItem() {
const confirmed = await confirm({
title: 'Löschen bestätigen',
message: 'Wirklich löschen?',
type: 'danger'
});
if (confirmed) {
// Löschvorgang durchführen
}
}
```
## Dialog-Größen
| Größe | Breite | Beschreibung |
|-------|--------|--------------|
| `small` | 400px | Kleine Dialoge (z.B. Bestätigungen) |
| `medium` | 600px | Standard-Dialoge |
| `large` | 900px | Große Dialoge mit viel Inhalt |
| `fullscreen` | 90vw x 90vh | Fast Fullscreen |
## Best Practices
### Modale Dialoge verwenden für:
- Wichtige Benutzer-Entscheidungen
- Formulare, die Fokus erfordern
- Warnungen und Fehler
- Prozesse, die nicht unterbrochen werden sollten
### Nicht-modale Dialoge verwenden für:
- Zusätzliche Informationen
- Tools und Paletten
- Mehrere gleichzeitige Arbeitsschritte
- Drag & Drop-Workflows
### Tipps
1. **Minimieren-Funktion**: Nur bei nicht-modalen Dialogen sinnvoll
2. **closeOnOverlay**: Bei wichtigen Formularen auf `false` setzen
3. **z-Index**: Bei mehreren nicht-modalen Dialogen unterschiedliche z-Indices verwenden
4. **Footer-Slot**: Für Aktions-Buttons verwenden
5. **header-actions**: Für kontextspezifische Header-Aktionen
## Styling
Die Dialoge verwenden CSS-Variablen für konsistentes Styling:
- `--primary-color`: Primärfarbe für Header und Buttons
- `--primary-hover`: Hover-Farbe
- `--border-color`: Rahmenfarbe
- `--text-color`: Textfarbe
- `--text-muted`: Gedämpfte Textfarbe
## Migration bestehender Dialoge
### Alt (individueller Dialog):
```vue
<div v-if="showDialog" class="modal-overlay" @click.self="closeDialog">
<div class="modal">
<div class="modal-header">
<h3>Titel</h3>
<button @click="closeDialog">×</button>
</div>
<div class="modal-body">
Inhalt
</div>
</div>
</div>
```
### Neu (BaseDialog):
```vue
<BaseDialog v-model="showDialog" title="Titel">
Inhalt
</BaseDialog>
```
## Beispiele ansehen
Die Datei `DialogExamples.vue` enthält vollständige Beispiele für alle Dialog-Typen.
Route hinzufügen in `router.js`:
```javascript
import DialogExamples from './components/DialogExamples.vue';
{ path: '/dialog-examples', component: DialogExamples }
```
```

View File

@@ -326,19 +326,19 @@ export default {
});
if (result) {
alert('Bestätigt!');
// Hier kann ein nutzerfreundlicher Hinweis (z. B. Toast) implementiert werden
} else {
alert('Abgebrochen!');
// Hier kann ein nutzerfreundlicher Hinweis (z. B. Toast) implementiert werden
}
},
handleWarningConfirm() {
console.log('Warnung bestätigt');
// Hier kann ein nutzerfreundlicher Hinweis (z. B. Toast) implementiert werden
this.warningConfirm.isOpen = false;
},
handleDelete() {
console.log('Eintrag gelöscht');
// Hier kann nach erfolgreicher Aktion ein Hinweis angezeigt werden
this.dangerConfirm.isOpen = false;
},

View File

@@ -99,29 +99,19 @@ export default {
},
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 = [
@@ -137,26 +127,20 @@ export default {
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)');
// Cross-Origin-Zugriffe sind erwartbar blockiert
}
}
}
@@ -173,44 +157,28 @@ export default {
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);
// Ignoriere fehlgeschlagene PostMessage-Versuche
}
});
}
console.log('💡 Alternative: Verwenden Sie den "📋 PIN kopieren" Button');
console.log('📋 PIN zum Kopieren:', match.homePin || match.guestPin);
},
handlePostMessage(event) {
console.log('📨 PostMessage empfangen:', event);
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') {
console.log('🚫 Nachricht von unbekannter Origin ignoriert');
return;
}
// Hier können wir auf Antworten von nuscore reagieren
if (event.data && event.data.action) {
console.log('🎯 Action empfangen:', event.data.action);
if (event.data.action === 'pinFilled') {
console.log('✅ PIN wurde erfolgreich eingefügt');
} else if (event.data.action === 'pinError') {
console.log('❌ Fehler beim PIN-Einfügen:', event.data.error);
}
if (!event.data || typeof event.data !== 'object') {
return;
}
// Weitere Aktionen bei Bedarf implementieren
}
},

View File

@@ -63,8 +63,6 @@ export default {
},
onIframeLoad() {
console.log('🔄 Iframe geladen, URL:', this.$refs.reportIframe?.src);
// Warte kurz, damit das iframe vollständig geladen ist
setTimeout(() => {
this.injectContentScript();
@@ -78,15 +76,12 @@ export default {
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);
@@ -99,7 +94,6 @@ export default {
// Suche nach dem Input-Feld
waitForElement('#gamecode', function(input) {
console.log('Input-Feld gefunden:', input);
// Code einfügen
input.value = '${this.match.code}';
@@ -109,16 +103,11 @@ export default {
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);
});
@@ -130,10 +119,7 @@ export default {
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();
}
@@ -143,7 +129,6 @@ export default {
try {
const iframe = this.$refs.reportIframe;
if (!iframe || !iframe.contentWindow) {
console.log('Iframe noch nicht bereit');
return;
}
@@ -160,22 +145,19 @@ export default {
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');
// Fallback: PostMessage verwenden
this.tryPostMessage();
}
} 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();
}
@@ -198,12 +180,8 @@ export default {
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);
}
},
@@ -216,24 +194,18 @@ export default {
action: 'fillGameCode',
code: this.match.code
}, 'https://ttde-apps.liga.nu');
console.log('PostMessage gesendet mit Code:', this.match.code);
}
} catch (error) {
console.log('Fehler beim Senden der PostMessage:', error);
}
},
startUrlMonitoring() {
console.log('🔍 Starte URL-Überwachung für iframe');
console.log('💡 Hinweis: PIN-Einfügung funktioniert am besten nach der Code-Eingabe und Weiterleitung');
console.log('📋 Verwenden Sie den "📌 PIN einfügen" Button nach der Weiterleitung zur Meeting-Seite');
// Einfache Überwachung ohne Cross-Origin-Zugriff
this.urlCheckInterval = setInterval(() => {
const iframe = this.$refs.reportIframe;
if (iframe) {
console.log('🔗 Iframe aktiv, bereit für PIN-Einfügung');
// Iframe aktiv
}
}, 10000); // Alle 10 Sekunden
@@ -241,7 +213,6 @@ export default {
setTimeout(() => {
if (this.urlCheckInterval) {
clearInterval(this.urlCheckInterval);
console.log('⏰ URL-Überwachung beendet (Timeout)');
}
}, 60000);
},
@@ -252,11 +223,8 @@ export default {
},
attemptPinInsertionAfterRedirect() {
console.log('🎯 Versuche PIN-Einfügung (manuell ausgelöst)');
const iframe = this.$refs.reportIframe;
if (!iframe || !this.match.homePin) {
console.log('❌ Iframe oder PIN nicht verfügbar');
return;
}
@@ -277,26 +245,18 @@ export default {
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',
@@ -305,14 +265,12 @@ export default {
source: 'trainingstagebuch'
};
console.log('📤 Sende PostMessage:', message);
iframe.contentWindow.postMessage(message, '*');
}
},
// Methode, die vom DialogManager aufgerufen werden kann
insertPinManually() {
console.log('🎯 PIN-Einfügung manuell ausgelöst');
this.attemptPinInsertionAfterRedirect();
},
@@ -322,11 +280,9 @@ export default {
return;
}
console.log('PostMessage empfangen:', event.data);
// Hier können wir auf Antworten von nuscore reagieren
if (event.data.action === 'codeFilled') {
console.log('Code wurde erfolgreich eingefügt');
// Code erfolgreich eingefügt
}
}
}

View File

@@ -3,7 +3,7 @@
<!-- <button @click="insertPin" class="header-action-btn" title="PIN automatisch einfügen">
📌 PIN einfügen
</button>-->
<button @click="copyPin" class="header-action-btn copy-button" title="PIN in Zwischenablage kopieren">
<button @click="copyPin($event)" class="header-action-btn copy-button" title="PIN in Zwischenablage kopieren">
📋 PIN kopieren
</button>
</div>
@@ -31,28 +31,21 @@ export default {
});
},
async copyPin() {
async copyPin(event) {
const button = event?.target;
const pin = this.match.homePin || this.match.guestPin;
if (!pin) {
console.warn('⚠️ Keine PIN verfügbar zum Kopieren');
if (button) {
this.showCopyFeedback(button, 'Keine PIN verfügbar', '#dc3545');
}
return;
}
try {
await navigator.clipboard.writeText(pin);
console.log('✅ PIN erfolgreich kopiert:', pin);
// Visuelles Feedback
const button = event.target;
const originalText = button.textContent;
button.textContent = '✅ Kopiert!';
button.style.backgroundColor = '#28a745';
setTimeout(() => {
button.textContent = originalText;
button.style.backgroundColor = '';
}, 2000);
if (button) {
this.showCopyFeedback(button, '✅ Kopiert!', '#28a745');
}
} catch (error) {
console.error('❌ Fehler beim Kopieren der PIN:', error);
@@ -64,8 +57,22 @@ export default {
document.execCommand('copy');
document.body.removeChild(textArea);
console.log('✅ PIN über Fallback kopiert:', pin);
if (button) {
this.showCopyFeedback(button, '✅ Kopiert!', '#28a745');
}
}
},
showCopyFeedback(button, text, backgroundColor) {
const originalText = button.textContent;
const originalColor = button.style.backgroundColor;
button.textContent = text;
button.style.backgroundColor = backgroundColor;
setTimeout(() => {
button.textContent = originalText;
button.style.backgroundColor = originalColor;
}, 2000);
}
}
};

View File

@@ -35,7 +35,6 @@ const checkPermission = (el, binding, vnode) => {
resource = binding.arg;
action = Object.keys(binding.modifiers)[0] || 'read';
} else {
console.warn('v-can directive requires resource and action');
el.style.display = 'none';
return;
}

View File

@@ -876,7 +876,6 @@ export default {
// Gesamtanzahl der Einträge
return participantCount + activityCount + trainingPlanCount;
} catch (error) {
console.warn(`Fehler beim Laden der Einträge für Datum ${dateId}:`, error);
return 0;
}
},

View File

@@ -469,9 +469,6 @@ export default {
const response = await apiClient.get(`/training-stats/${this.currentClub}`);
const trainingStats = response.data.members || [];
console.log('[loadTrainingParticipations] Training Stats geladen:', trainingStats.length, 'Mitglieder');
console.log('[loadTrainingParticipations] Response data:', JSON.stringify(response.data, null, 2));
// Erstelle eine Map für schnellen Zugriff: memberId -> participationTotal
// Speichere sowohl String- als auch Number-Keys, um Typ-Probleme zu vermeiden
const participationMap = new Map();
@@ -483,15 +480,10 @@ export default {
participationMap.set(idAsString, participationTotal);
participationMap.set(idAsNumber, participationTotal);
console.log(`[loadTrainingParticipations] Map gesetzt: ID "${idAsString}" (String) und ${idAsNumber} (Number) -> ${participationTotal}`);
});
console.log('[loadTrainingParticipations] Participation Map Keys:', Array.from(participationMap.keys()));
// Setze Trainingsteilnahmen für alle Testmitglieder
const testMembers = this.members.filter(m => m.testMembership);
console.log('[loadTrainingParticipations] Testmitglieder gefunden:', testMembers.length);
console.log('[loadTrainingParticipations] Testmitglieder IDs:', testMembers.map(m => ({ id: m.id, type: typeof m.id, name: `${m.firstName} ${m.lastName}` })));
testMembers.forEach(member => {
// Versuche sowohl String- als auch Number-ID
@@ -509,13 +501,10 @@ export default {
count = participationMap.get(idAsNumber);
}
console.log(`[loadTrainingParticipations] Mitglied ${member.id} (type: ${typeof member.id}) (${member.firstName} ${member.lastName}): count=${count}, map has String: ${participationMap.has(idAsString)}, map has Number: ${participationMap.has(idAsNumber)}`);
// Setze den Wert, wenn gefunden, sonst 0
const finalCount = count !== undefined ? count : 0;
// In Vue 3 ist $set nicht mehr nötig, direkte Zuweisung funktioniert
member.trainingParticipations = finalCount;
console.log(`[loadTrainingParticipations] Trainingsteilnahmen gesetzt: ${finalCount}`);
});
} catch (error) {
console.error('Fehler beim Laden der Trainingsteilnahmen:', error);

View File

@@ -233,16 +233,12 @@ export default {
const openPermissionsDialog = async (member) => {
selectedMember.value = member;
console.log('Opening dialog for member:', member.user?.email);
console.log('Member permissions from DB:', member.permissions);
// Load fresh data for this specific member to ensure we have the latest permissions
try {
const membersResponse = await apiClient.get(`/permissions/${currentClub.value}/members?t=${Date.now()}`);
const freshMember = membersResponse.data.find(m => m.userId === member.userId);
if (freshMember) {
selectedMember.value = freshMember;
console.log('Fresh member data:', freshMember.permissions);
}
} catch (err) {
console.error('Error loading fresh member data:', err);
@@ -260,7 +256,6 @@ export default {
}
}
console.log('Initialized customPermissions:', customPermissions.value);
};
const closePermissionsDialog = () => {
@@ -286,15 +281,11 @@ export default {
}
}
console.log('Saving permissions:', permissionsToSave);
const response = await apiClient.put(
`/permissions/${currentClub.value}/user/${selectedMember.value.userId}/permissions`,
{ permissions: permissionsToSave }
);
console.log('Save response:', response.data);
// Update local member data immediately
const memberIndex = members.value.findIndex(m => m.userId === selectedMember.value.userId);
if (memberIndex !== -1) {

View File

@@ -369,23 +369,19 @@ export default {
},
async openPlayerSelectionDialog(match) {
console.log('Opening player selection for match:', match);
this.playerSelectionDialog.match = match;
this.playerSelectionDialog.isOpen = true;
this.playerSelectionDialog.loading = true;
try {
// Fetch members for the current club
console.log('Fetching members for club:', this.currentClub);
const response = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`);
console.log('Members response:', response.data);
const allMembers = response.data;
// Filter members by age class if league has age class info
// For now, show all active members
const activeMembers = allMembers.filter(m => m.active);
console.log('Active members count:', activeMembers.length);
this.playerSelectionDialog.members = activeMembers.map(m => ({
...m,
@@ -393,8 +389,6 @@ export default {
isPlanned: match.playersPlanned?.includes(m.id) || false,
hasPlayed: match.playersPlayed?.includes(m.id) || false
}));
console.log('Player selection members:', this.playerSelectionDialog.members.length);
} catch (error) {
console.error('Error loading members:', error);

View File

@@ -622,7 +622,7 @@ export default {
const response = await apiClient.get(`/team-documents/club-team/${team.id}`);
allDocuments.push(...response.data);
} catch (error) {
console.warn(`Fehler beim Laden der Dokumente für Team ${team.id}:`, error);
continue;
}
}
teamDocuments.value = allDocuments;

View File

@@ -209,8 +209,6 @@ export default {
if (!bestQuarter || maxTrainings === 0) return 0;
console.log('🔍 Bestes Quartal:', bestQuarter.name, 'mit', maxTrainings, 'Trainings');
const totalParticipants = this.getTotalParticipantsInPeriod(bestQuarter.year, bestQuarter.startMonth, bestQuarter.year, bestQuarter.endMonth);
return totalParticipants / maxTrainings;
},