Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save nickalexej/30bed76bb33856fc55ec2cf923f51719 to your computer and use it in GitHub Desktop.

Select an option

Save nickalexej/30bed76bb33856fc55ec2cf923f51719 to your computer and use it in GitHub Desktop.
Entfernt Automatisch erstellte Workouts aus Garmin Connect Web #vibecoded --- 2026-02-11: Fix - Anpassungen im HTML von Garmin angepasst. Verbesserte Log Ausgabe im Terminal 2026-03-11: Fix -für Gamin Webanpassungen 2026-05-11: Fix - Garmin änderte Ellipsis-Button-Selector & Dropdown-Menü-Klasse
function deleteWorkouts() {
// Suche alle Zeilen in der Workout-Tabelle
const workoutRows = document.querySelectorAll('tr.SortableTableLayout_tableRow__iHLrR');
if (workoutRows.length === 0) {
console.log('Keine Workout-Zeilen gefunden.');
return;
}
// Finde das erste Workout mit "Bei Predictive Fitness bearbeiten"
let targetRow = null;
for (const row of workoutRows) {
// Prüfe die "Bearbeitet"-Spalte
const editedCell = row.querySelector('td[data-title="Bearbeitet"]');
if (editedCell) {
const text = editedCell.textContent.trim();
if (text === 'Bei Predictive Fitness bearbeiten') {
const workoutName = row.querySelector('td[data-title="Name"]')?.textContent;
targetRow = row;
console.log('✓ Predictive Fitness Workout gefunden:', workoutName);
break;
}
}
}
// Wenn kein passendes Training gefunden wurde → fertig
if (!targetRow) {
console.log('✓✓✓ Keine Predictive Fitness Workouts mehr gefunden. Fertig!');
return;
}
// Finde den Drei-Punkte-Button (Ellipsis)
const ellipsisButton = targetRow.querySelector('.WorkoutsList_ellipsisMenu__4RZ\\+p button')
|| targetRow.querySelector('button[aria-label="Optionen"]')
|| targetRow.querySelector('button[aria-label="Optionen"] i.icon-ellipsis-vertical')?.closest('button');
if (!ellipsisButton) {
console.log('⚠ Drei-Punkte-Menü nicht gefunden. Überspringe...');
setTimeout(deleteWorkouts, 1000);
return;
}
// Klicke auf den Button
ellipsisButton.click();
console.log('→ Menü geöffnet...');
setTimeout(() => {
findAndClickDelete(targetRow);
}, 500);
}
function clickLöschenInPopup(callback) {
// Primär: ActionMenu-Button direkt per Klasse
let found = null;
const actionMenuItems = document.querySelectorAll('.ActionMenuItem_actionMenuItem__kUf7T');
for (const el of actionMenuItems) {
const text = el.textContent.trim().toLowerCase();
if (text === 'löschen' || text === 'delete') {
found = el;
console.log('✓ ActionMenu-Löschen gefunden');
break;
}
}
// Fallback: sichtbares "Löschen"-Element außerhalb von <tr> und Confirm-Dialog
if (!found) {
const candidates = document.querySelectorAll('button, li, [role="menuitem"], [role="option"], a, span');
for (const el of candidates) {
if (el.closest('tr')) continue;
if (el.closest('.Dialog_dialog__fiyk-')) continue;
const text = el.textContent.trim().toLowerCase();
if (el.offsetParent !== null && (text === 'löschen' || text === 'delete')) {
found = el;
console.log('✓ Fallback-Löschen gefunden:', el.tagName, el.className);
break;
}
}
}
if (found) {
found.click();
setTimeout(callback, 800);
} else {
console.log('→ Kein Popup-Löschen gefunden, gehe weiter...');
callback();
}
}
function findAndClickDelete(row) {
// Schritt 1: Löschen im Dropdown-Menü klicken
// Schritt 2: falls Zwischen-Popup erscheint, dort nochmal Löschen klicken
// Schritt 3: finaler Confirm-Dialog
clickLöschenInPopup(() => {
clickLöschenInPopup(() => {
confirmDelete();
});
});
}
function confirmDelete() {
// Suche nach ALLEN Dialogen
const allDialogs = document.querySelectorAll('.Dialog_dialog__fiyk-');
console.log('→ Gefundene Dialoge:', allDialogs.length);
// Finde den Dialog mit "Training löschen" im Header (OHNE h3!)
let deleteDialog = null;
for (const dialog of allDialogs) {
// WICHTIG: Der Header ist ein DIV, kein H3!
const header = dialog.querySelector('.Dialog_dialogHeader__Guxgt');
if (header) {
const headerText = header.textContent.trim();
console.log(' → Dialog-Titel: "' + headerText + '"');
if (headerText === 'Training löschen' || headerText.toLowerCase().includes('training') && headerText.toLowerCase().includes('löschen')) {
deleteDialog = dialog;
console.log('✓ "Training löschen"-Dialog gefunden!');
break;
}
}
}
if (!deleteDialog) {
console.log('⚠ "Training löschen"-Dialog nicht gefunden! Warte länger...');
// Zähle Wiederholungen
if (!confirmDelete.retryCount) confirmDelete.retryCount = 0;
confirmDelete.retryCount++;
if (confirmDelete.retryCount < 5) {
setTimeout(confirmDelete, 500);
} else {
console.log('⚠ Dialog erscheint nicht. Schließe alle Dialoge und versuche nächstes Workout...');
confirmDelete.retryCount = 0;
// Schließe alle Dialoge
const closeBtns = document.querySelectorAll('.Dialog_dialogCloseBtn__EtPd-');
closeBtns.forEach(btn => btn.click());
setTimeout(deleteWorkouts, 2000);
}
return;
}
// Reset retry counter
confirmDelete.retryCount = 0;
// Suche den Footer in diesem spezifischen Dialog
const dialogFooter = deleteDialog.querySelector('.Dialog_dialogFooter__W9xGT');
if (!dialogFooter) {
console.log('⚠ Dialog-Footer nicht gefunden in diesem Dialog!');
setTimeout(confirmDelete, 500);
return;
}
console.log('✓ Dialog-Footer im richtigen Dialog gefunden!');
// Suche alle Buttons im Footer dieses spezifischen Dialogs
const footerButtons = dialogFooter.querySelectorAll('button');
console.log('→ Gefundene Footer-Buttons im Lösch-Dialog:', footerButtons.length);
let confirmButton = null;
for (const btn of footerButtons) {
const text = btn.textContent.toLowerCase().trim();
const classes = btn.className;
console.log(' → Button-Text: "' + text + '", Klassen:', classes);
// Suche nach dem "Löschen" Button
if (text === 'löschen' || text === 'delete') {
confirmButton = btn;
console.log('✓✓ Löschen-Button gefunden!');
break;
}
}
if (!confirmButton) {
// Fallback: Suche nach danger-Button
confirmButton = dialogFooter.querySelector('button.Button_danger__hwLq8, button[class*="danger"]');
if (confirmButton) {
console.log('✓✓ Löschen-Button via Danger-Klasse gefunden!');
}
}
if (!confirmButton) {
console.log('⚠ Kein Löschen-Button gefunden. Schließe Dialog...');
const closeBtn = deleteDialog.querySelector('.Dialog_dialogCloseBtn__EtPd-');
if (closeBtn) {
closeBtn.click();
console.log('→ Dialog geschlossen, versuche nächstes Workout...');
}
setTimeout(deleteWorkouts, 2000);
return;
}
console.log('→ Klicke Löschen-Button...');
confirmButton.click();
console.log('✓✓✓ Workout gelöscht!\n');
// Nächstes Workout nach kurzer Pause
setTimeout(deleteWorkouts, 2000);
}
// Start
console.log('╔════════════════════════════════════════════════════╗');
console.log('║ Predictive Fitness Workout Bulk-Löscher ║');
console.log('╚════════════════════════════════════════════════════╝');
console.log('Kriterium: "Bei Predictive Fitness bearbeiten"\n');
deleteWorkouts();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment