454 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | |
| <html lang="de">
 | |
| <head>
 | |
|     <meta charset="UTF-8">
 | |
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | |
|     <title>DSGVO-Export Hilfe | PonyWave Tools</title>
 | |
|     <meta name="description" content="Informationen zum Beantragen von DSGVO-Exporten bei verschiedenen Diensten">
 | |
|     <meta property="og:title" content="DSGVO-Export Hilfe">
 | |
|     <meta property="og:description" content="Informationen zum Beantragen von DSGVO-Exporten bei verschiedenen Diensten">
 | |
|     <meta property="og:url" content="https://tools.ponywave.de/dsgvo_helper">
 | |
|     <meta property="og:type" content="website">
 | |
|     <link rel="icon" href="../favicon.png">
 | |
|     <script defer src="https://stats.ponywave.de/script" data-website-id="9ef713d2-adb9-4906-9df5-708d8a8b9131" data-tag="dsgvo_helper"></script>
 | |
|     <style>
 | |
|         :root {
 | |
|             --bg-color: #f5f5f5;
 | |
|             --text-color: #333;
 | |
|             --primary-color: #6200ea;
 | |
|             --secondary-color: #b388ff;
 | |
|             --accent-color: #3700b3;
 | |
|             --card-bg: #ffffff;
 | |
|             --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 | |
|             --table-header: #f0f0f0;
 | |
|             --table-row-odd: #ffffff;
 | |
|             --table-row-even: #f9f9f9;
 | |
|             --table-border: #e0e0e0;
 | |
|         }
 | |
| 
 | |
|         [data-theme="dark"] {
 | |
|             --bg-color: #121212;
 | |
|             --text-color: #e0e0e0;
 | |
|             --primary-color: #bb86fc;
 | |
|             --secondary-color: #03dac6;
 | |
|             --accent-color: #cf6679;
 | |
|             --card-bg: #1e1e1e;
 | |
|             --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
 | |
|             --table-header: #2d2d2d;
 | |
|             --table-row-odd: #1e1e1e;
 | |
|             --table-row-even: #252525;
 | |
|             --table-border: #333333;
 | |
|         }
 | |
| 
 | |
|         * {
 | |
|             margin: 0;
 | |
|             padding: 0;
 | |
|             box-sizing: border-box;
 | |
|             transition: background-color 0.3s, color 0.3s;
 | |
|         }
 | |
| 
 | |
|         body {
 | |
|             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 | |
|             background-color: var(--bg-color);
 | |
|             color: var(--text-color);
 | |
|             line-height: 1.6;
 | |
|             padding: 0;
 | |
|             margin: 0;
 | |
|         }
 | |
| 
 | |
|         header {
 | |
|             background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
 | |
|             color: white;
 | |
|             text-align: center;
 | |
|             padding: 2rem 1rem;
 | |
|             margin-bottom: 2rem;
 | |
|             box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 | |
|         }
 | |
| 
 | |
|         h1 {
 | |
|             margin-bottom: 0.5rem;
 | |
|             font-size: 2.5rem;
 | |
|         }
 | |
| 
 | |
|         .container {
 | |
|             max-width: 1200px;
 | |
|             margin: 0 auto;
 | |
|             padding: 0 1rem;
 | |
|         }
 | |
| 
 | |
|         .card {
 | |
|             background-color: var(--card-bg);
 | |
|             border-radius: 8px;
 | |
|             box-shadow: var(--card-shadow);
 | |
|             padding: 2rem;
 | |
|             margin-bottom: 2rem;
 | |
|         }
 | |
| 
 | |
|         .intro {
 | |
|             margin-bottom: 1.5rem;
 | |
|         }
 | |
| 
 | |
|         /* Tabellenstile */
 | |
|         .table-container {
 | |
|             overflow-x: auto;
 | |
|         }
 | |
| 
 | |
|         table {
 | |
|             width: 100%;
 | |
|             border-collapse: collapse;
 | |
|             margin: 1rem 0;
 | |
|             font-size: 0.95rem;
 | |
|         }
 | |
| 
 | |
|         th, td {
 | |
|             padding: 12px 15px;
 | |
|             text-align: left;
 | |
|             border-bottom: 1px solid var(--table-border);
 | |
|         }
 | |
| 
 | |
|         th {
 | |
|             background-color: var(--table-header);
 | |
|             font-weight: bold;
 | |
|         }
 | |
| 
 | |
|         tr:nth-child(odd) {
 | |
|             background-color: var(--table-row-odd);
 | |
|         }
 | |
| 
 | |
|         tr:nth-child(even) {
 | |
|             background-color: var(--table-row-even);
 | |
|         }
 | |
| 
 | |
|         td a {
 | |
|             color: var(--primary-color);
 | |
|             text-decoration: none;
 | |
|         }
 | |
| 
 | |
|         td a:hover {
 | |
|             text-decoration: underline;
 | |
|         }
 | |
| 
 | |
|         /* Themenschalter */
 | |
|         .theme-switch {
 | |
|             position: fixed;
 | |
|             top: 20px;
 | |
|             right: 20px;
 | |
|             z-index: 999;
 | |
|         }
 | |
| 
 | |
|         .theme-switch button {
 | |
|             background-color: var(--card-bg);
 | |
|             color: var(--text-color);
 | |
|             border: 2px solid var(--primary-color);
 | |
|             border-radius: 30px;
 | |
|             padding: 8px 15px;
 | |
|             cursor: pointer;
 | |
|             font-weight: bold;
 | |
|             display: flex;
 | |
|             align-items: center;
 | |
|             gap: 8px;
 | |
|             transition: all 0.3s ease;
 | |
|         }
 | |
| 
 | |
|         .theme-switch button:hover {
 | |
|             background-color: var(--primary-color);
 | |
|             color: white;
 | |
|         }
 | |
| 
 | |
|         .theme-icon {
 | |
|             font-size: 18px;
 | |
|         }
 | |
| 
 | |
|         /* Footer */
 | |
|         footer {
 | |
|             text-align: center;
 | |
|             padding: 2rem 1rem;
 | |
|             margin-top: 2rem;
 | |
|             background-color: var(--card-bg);
 | |
|             border-top: 1px solid var(--table-border);
 | |
|         }
 | |
| 
 | |
|         footer a {
 | |
|             color: var(--primary-color);
 | |
|             text-decoration: none;
 | |
|         }
 | |
| 
 | |
|         footer a:hover {
 | |
|             text-decoration: underline;
 | |
|         }
 | |
| 
 | |
|         .heart {
 | |
|             color: var(--accent-color);
 | |
|             animation: heartbeat 1.5s infinite;
 | |
|         }
 | |
| 
 | |
|         @keyframes heartbeat {
 | |
|             0%, 100% { transform: scale(1); }
 | |
|             50% { transform: scale(1.1); }
 | |
|         }
 | |
| 
 | |
|         /* Responsive Design */
 | |
|         @media (max-width: 768px) {
 | |
|             h1 {
 | |
|                 font-size: 2rem;
 | |
|             }
 | |
|             
 | |
|             .card {
 | |
|                 padding: 1.5rem;
 | |
|             }
 | |
|             
 | |
|             th, td {
 | |
|                 padding: 8px 10px;
 | |
|                 font-size: 0.9rem;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* Sortierfunktion Stile */
 | |
|         .sortable th {
 | |
|             cursor: pointer;
 | |
|             position: relative;
 | |
|         }
 | |
| 
 | |
|         .sortable th::after {
 | |
|             content: '↕';
 | |
|             position: absolute;
 | |
|             right: 8px;
 | |
|             color: #999;
 | |
|         }
 | |
| 
 | |
|         .sortable th.asc::after {
 | |
|             content: '↓';
 | |
|             color: var(--primary-color);
 | |
|         }
 | |
| 
 | |
|         .sortable th.desc::after {
 | |
|             content: '↑';
 | |
|             color: var(--primary-color);
 | |
|         }
 | |
| 
 | |
|         /* Suchfeld Stile */
 | |
|         .search-container {
 | |
|             margin-bottom: 1.5rem;
 | |
|         }
 | |
| 
 | |
|         .search-input {
 | |
|             width: 100%;
 | |
|             padding: 10px 15px;
 | |
|             border: 1px solid var(--table-border);
 | |
|             border-radius: 4px;
 | |
|             font-size: 1rem;
 | |
|             background-color: var(--card-bg);
 | |
|             color: var(--text-color);
 | |
|         }
 | |
| 
 | |
|         .search-input:focus {
 | |
|             outline: none;
 | |
|             border-color: var(--primary-color);
 | |
|         }
 | |
|     </style>
 | |
| </head>
 | |
| <body>
 | |
|     <div class="theme-switch">
 | |
|         <button id="theme-toggle">
 | |
|             <span class="theme-icon">🌙</span>
 | |
|         </button>
 | |
|     </div>
 | |
| 
 | |
|     <header>
 | |
|         <div class="container">
 | |
|             <h1>DSGVO-Export Hilfe</h1>
 | |
|             <p>Informationen zum Beantragen von DSGVO-Exporten bei verschiedenen Diensten</p>
 | |
|         </div>
 | |
|     </header>
 | |
| 
 | |
|     <div class="container">
 | |
|         <div class="card intro">
 | |
|             <h2>DSGVO-Exports einfach erklärt</h2>
 | |
|             <p>Nach der Datenschutz-Grundverordnung (DSGVO) haben Sie das Recht, eine Kopie Ihrer persönlichen Daten von Unternehmen anzufordern, die diese verarbeiten. Diese Seite hilft Ihnen dabei, Informationen über die Beantragung von DSGVO-Exporten bei verschiedenen Diensten zu finden.</p>
 | |
|             <p>Fehler oder fehlende Dienste können Sie als <a href="https://github.com/Akamaru/issues" target="_blank" rel="noopener noreferrer">Issue auf GitHub</a> melden.</p>
 | |
|         </div>
 | |
| 
 | |
|         <div class="card">
 | |
|             <div class="search-container">
 | |
|                 <input type="text" id="searchInput" class="search-input" placeholder="Suche nach Diensten, URLs, ..." aria-label="Suche">
 | |
|             </div>
 | |
|             <div class="table-container">
 | |
|                 <table id="dsgvoTable" class="sortable">
 | |
|                     <thead>
 | |
|                         <tr>
 | |
|                             <th data-sort="dienst" class="asc">Dienst</th>
 | |
|                             <th data-sort="url">URL</th>
 | |
|                             <th data-sort="anleitung">Anleitung</th>
 | |
|                         </tr>
 | |
|                     </thead>
 | |
|                     <tbody>
 | |
|                         <!-- Tabellendaten werden per JavaScript geladen -->
 | |
|                     </tbody>
 | |
|                 </table>
 | |
|             </div>
 | |
|         </div>
 | |
|     </div>
 | |
| 
 | |
|     <footer>
 | |
|         <div class="container">
 | |
|             <p><a href="https://tools.ponywave.de/">Zurück zur Startseite</a> | © <span id="current-year"></span> Akamaru | Made with <span class="heart">❤️</span> by Claude</p>
 | |
|         </div>
 | |
|     </footer>
 | |
| 
 | |
|     <script>
 | |
|         // DOM-Elemente
 | |
|         const themeToggle = document.getElementById('theme-toggle');
 | |
|         const themeIcon = themeToggle.querySelector('.theme-icon');
 | |
|         const table = document.getElementById('dsgvoTable');
 | |
|         const tableBody = table.querySelector('tbody');
 | |
|         const searchInput = document.getElementById('searchInput');
 | |
|         const currentYearSpan = document.getElementById('current-year');
 | |
| 
 | |
|         // Aktuelles Jahr im Footer setzen
 | |
|         currentYearSpan.textContent = new Date().getFullYear();
 | |
| 
 | |
|         // Theme-Erkennung und Einstellung
 | |
|         function setTheme(isDark) {
 | |
|             document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
 | |
|             themeIcon.textContent = isDark ? '☀️' : '🌙';
 | |
|             localStorage.setItem('dsgvoHelperTheme', isDark ? 'dark' : 'light');
 | |
|         }
 | |
| 
 | |
|         // Bevorzugtes Theme des Nutzers erkennen
 | |
|         function detectPreferredTheme() {
 | |
|             const savedTheme = localStorage.getItem('dsgvoHelperTheme');
 | |
|             if (savedTheme) {
 | |
|                 return savedTheme === 'dark';
 | |
|             }
 | |
|             return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
 | |
|         }
 | |
| 
 | |
|         // Theme beim Laden setzen
 | |
|         setTheme(detectPreferredTheme());
 | |
| 
 | |
|         // Theme-Wechsel
 | |
|         themeToggle.addEventListener('click', () => {
 | |
|             const currentTheme = document.documentElement.getAttribute('data-theme');
 | |
|             setTheme(currentTheme !== 'dark');
 | |
|         });
 | |
| 
 | |
|         // Daten aus JSON-Datei laden
 | |
|         let dsgvoData = [];
 | |
|         
 | |
|         fetch('data.json')
 | |
|             .then(response => response.json())
 | |
|             .then(data => {
 | |
|                 dsgvoData = data;
 | |
|                 // Initial nach Dienst sortieren
 | |
|                 currentSort = { column: 'dienst', direction: 'asc' };
 | |
|                 renderTableData(dsgvoData);
 | |
|             })
 | |
|             .catch(error => {
 | |
|                 console.error('Fehler beim Laden der Daten:', error);
 | |
|                 // Fallback-Daten anzeigen, falls JSON-Datei nicht geladen werden kann
 | |
|                 tableBody.innerHTML = '<tr><td colspan="3" style="text-align: center;">Fehler beim Laden der Daten. Bitte versuchen Sie es später erneut.</td></tr>';
 | |
|             });
 | |
| 
 | |
|         // Tabellendaten rendern
 | |
|         function renderTableData(data) {
 | |
|             tableBody.innerHTML = '';
 | |
|             
 | |
|             if (data.length === 0) {
 | |
|                 const row = document.createElement('tr');
 | |
|                 const cell = document.createElement('td');
 | |
|                 cell.setAttribute('colspan', '3');
 | |
|                 cell.textContent = 'Keine Ergebnisse gefunden';
 | |
|                 cell.style.textAlign = 'center';
 | |
|                 row.appendChild(cell);
 | |
|                 tableBody.appendChild(row);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             data.forEach(item => {
 | |
|                 const row = document.createElement('tr');
 | |
|                 
 | |
|                 const dienstCell = document.createElement('td');
 | |
|                 dienstCell.textContent = item.dienst;
 | |
|                 row.appendChild(dienstCell);
 | |
|                 
 | |
|                 const urlCell = document.createElement('td');
 | |
|                 const urlLink = document.createElement('a');
 | |
|                 urlLink.href = item.url;
 | |
|                 urlLink.textContent = item.url;
 | |
|                 urlLink.target = '_blank';
 | |
|                 urlLink.rel = 'noopener noreferrer';
 | |
|                 urlCell.appendChild(urlLink);
 | |
|                 row.appendChild(urlCell);
 | |
|                 
 | |
|                 const anleitungCell = document.createElement('td');
 | |
|                 anleitungCell.textContent = item.anleitung;
 | |
|                 row.appendChild(anleitungCell);
 | |
|                 
 | |
|                 tableBody.appendChild(row);
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         // Suchfunktion
 | |
|         searchInput.addEventListener('input', function() {
 | |
|             const searchTerm = this.value.toLowerCase();
 | |
|             const filteredData = dsgvoData.filter(item => {
 | |
|                 return Object.values(item).some(value => 
 | |
|                     value.toLowerCase().includes(searchTerm)
 | |
|                 );
 | |
|             });
 | |
|             renderTableData(filteredData);
 | |
|         });
 | |
| 
 | |
|         // Sortierfunktion
 | |
|         let currentSort = { column: null, direction: 'asc' };
 | |
| 
 | |
|         function sortTable(column) {
 | |
|             const headers = table.querySelectorAll('th');
 | |
|             
 | |
|             // Reset alle Header-Klassen
 | |
|             headers.forEach(header => {
 | |
|                 header.classList.remove('asc', 'desc');
 | |
|             });
 | |
|             
 | |
|             const header = table.querySelector(`th[data-sort="${column}"]`);
 | |
|             
 | |
|             // Sortierdirection festlegen
 | |
|             let direction = 'asc';
 | |
|             if (currentSort.column === column && currentSort.direction === 'asc') {
 | |
|                 direction = 'desc';
 | |
|             }
 | |
|             
 | |
|             // Header-Klasse aktualisieren
 | |
|             header.classList.add(direction);
 | |
|             
 | |
|             // Daten sortieren
 | |
|             const sortedData = [...dsgvoData].sort((a, b) => {
 | |
|                 const valueA = a[column].toLowerCase();
 | |
|                 const valueB = b[column].toLowerCase();
 | |
|                 
 | |
|                 if (valueA < valueB) {
 | |
|                     return direction === 'asc' ? -1 : 1;
 | |
|                 }
 | |
|                 if (valueA > valueB) {
 | |
|                     return direction === 'asc' ? 1 : -1;
 | |
|                 }
 | |
|                 return 0;
 | |
|             });
 | |
|             
 | |
|             // Sortierungsstatusvariable aktualisieren
 | |
|             currentSort = { column, direction };
 | |
|             
 | |
|             // Neu rendern
 | |
|             renderTableData(sortedData);
 | |
|         }
 | |
| 
 | |
|         // Event-Listener für Tabellensortierung
 | |
|         table.querySelectorAll('th').forEach(header => {
 | |
|             const column = header.getAttribute('data-sort');
 | |
|             if (column) {
 | |
|                 header.addEventListener('click', () => sortTable(column));
 | |
|             }
 | |
|         });
 | |
|     </script>
 | |
| </body>
 | |
| </html>  |