Add MyTischtennis fetch log functionality and new endpoints
Enhance MyTischtennis integration by introducing fetch log capabilities. Implement new controller methods to retrieve fetch logs and latest successful fetches for users. Update routes to include these new endpoints. Modify the MyTischtennis model to support fetch logs and ensure proper logging of fetch operations in various services. Update frontend components to display fetch statistics, improving user experience and data visibility.
This commit is contained in:
@@ -51,6 +51,58 @@
|
||||
<button class="btn-danger" @click="deleteAccount">Account trennen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fetch Statistics Section -->
|
||||
<div class="info-section fetch-stats-section" v-if="account">
|
||||
<h2>Datenabruf-Statistiken</h2>
|
||||
|
||||
<div v-if="loadingStats" class="loading-stats">Lade Statistiken...</div>
|
||||
|
||||
<div v-else-if="latestFetches" class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">📊</div>
|
||||
<div class="stat-content">
|
||||
<h3>Spielerwertungen</h3>
|
||||
<div v-if="latestFetches.ratings">
|
||||
<p class="stat-date">{{ formatDateRelative(latestFetches.ratings.lastFetch) }}</p>
|
||||
<p class="stat-detail">{{ latestFetches.ratings.recordsProcessed }} Spieler aktualisiert</p>
|
||||
<p class="stat-time" v-if="latestFetches.ratings.executionTime">{{ latestFetches.ratings.executionTime }}ms</p>
|
||||
</div>
|
||||
<p v-else class="stat-never">Noch nie abgerufen</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">🏓</div>
|
||||
<div class="stat-content">
|
||||
<h3>Spielergebnisse</h3>
|
||||
<div v-if="latestFetches.match_results">
|
||||
<p class="stat-date">{{ formatDateRelative(latestFetches.match_results.lastFetch) }}</p>
|
||||
<p class="stat-detail">{{ latestFetches.match_results.recordsProcessed }} Ergebnisse</p>
|
||||
<p class="stat-time" v-if="latestFetches.match_results.executionTime">{{ latestFetches.match_results.executionTime }}ms</p>
|
||||
</div>
|
||||
<p v-else class="stat-never">Noch nie abgerufen</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">📋</div>
|
||||
<div class="stat-content">
|
||||
<h3>Ligatabellen</h3>
|
||||
<div v-if="latestFetches.league_table">
|
||||
<p class="stat-date">{{ formatDateRelative(latestFetches.league_table.lastFetch) }}</p>
|
||||
<p class="stat-detail">{{ latestFetches.league_table.recordsProcessed }} Teams</p>
|
||||
<p class="stat-time" v-if="latestFetches.league_table.executionTime">{{ latestFetches.league_table.executionTime }}ms</p>
|
||||
</div>
|
||||
<p v-else class="stat-never">Noch nie abgerufen</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn-secondary refresh-stats-btn" @click="loadLatestFetches">
|
||||
🔄 Statistiken aktualisieren
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-account">
|
||||
@@ -141,13 +193,16 @@ export default {
|
||||
resolveCallback: null
|
||||
},
|
||||
loading: true,
|
||||
loadingStats: false,
|
||||
account: null,
|
||||
latestFetches: null,
|
||||
showDialog: false,
|
||||
showHistoryDialog: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadAccount();
|
||||
this.loadLatestFetches();
|
||||
},
|
||||
methods: {
|
||||
// Dialog Helper Methods
|
||||
@@ -247,23 +302,34 @@ export default {
|
||||
},
|
||||
|
||||
async deleteAccount() {
|
||||
if (!confirm('Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?')) {
|
||||
return;
|
||||
}
|
||||
const confirmed = await this.showConfirm(
|
||||
'Account trennen',
|
||||
'Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?',
|
||||
'',
|
||||
'danger'
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await apiClient.delete('/mytischtennis/account');
|
||||
this.account = null;
|
||||
this.$store.dispatch('showMessage', {
|
||||
text: 'myTischtennis-Account erfolgreich getrennt',
|
||||
type: 'success'
|
||||
});
|
||||
this.showInfo('Erfolg', 'myTischtennis-Account erfolgreich getrennt', '', 'success');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen des Accounts:', error);
|
||||
this.$store.dispatch('showMessage', {
|
||||
text: 'Fehler beim Trennen des Accounts',
|
||||
type: 'error'
|
||||
});
|
||||
this.showInfo('Fehler', 'Fehler beim Trennen des Accounts', error.message, 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async loadLatestFetches() {
|
||||
this.loadingStats = true;
|
||||
try {
|
||||
const response = await apiClient.get('/mytischtennis/latest-fetches');
|
||||
this.latestFetches = response.data.latestFetches;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Fetch-Statistiken:', error);
|
||||
} finally {
|
||||
this.loadingStats = false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -277,6 +343,31 @@ export default {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
},
|
||||
|
||||
formatDateRelative(dateString) {
|
||||
if (!dateString) return 'Nie';
|
||||
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffMs = now - date;
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
const diffDays = Math.floor(diffMs / 86400000);
|
||||
|
||||
if (diffMins < 1) return 'Gerade eben';
|
||||
if (diffMins < 60) return `vor ${diffMins} Min.`;
|
||||
if (diffHours < 24) return `vor ${diffHours} Std.`;
|
||||
if (diffDays === 1) return 'Gestern';
|
||||
if (diffDays < 7) return `vor ${diffDays} Tagen`;
|
||||
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -403,6 +494,85 @@ h1 {
|
||||
background-color: #545b62;
|
||||
}
|
||||
|
||||
/* Fetch Statistics */
|
||||
.fetch-stats-section {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.loading-stats {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size: 2.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-content h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1rem;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stat-date {
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.stat-detail {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.stat-time {
|
||||
font-size: 0.8rem;
|
||||
color: #999;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.stat-never {
|
||||
font-style: italic;
|
||||
color: #999;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.refresh-stats-btn {
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
|
||||
Reference in New Issue
Block a user