PageSourceSearch

https://app.medi-one.ai/modules/contacts.js?v=20260807-noshow2-soa…l1-nsl2-impauth1-tdup2-dnd1-kf1-kf3-kt3-it1

js medi-one.ai collected 2026-10-05 14:15:00 UTC 383,299 bytes, 8,027 lines download raw bytes

1// // phase8-contacts-users-migrated
2import { usersApi } from './usersApi.js';
3// Adapter: usersApi.me() in Firestore snapshot shape
4async function _phase8UserSnap(uid) {
5  try {
6    const me = await usersApi.me();
7    return { exists: () => !!me, data: () => me, id: (me && me.uid) || uid };
8  } catch (e) {
9    return { exists: () => false, data: () => null };
10  }
11}
12import { db, auth } from '../firebase-init.js';
13import UI from './ui.js?v=20260820-dd1-imperr1-bann1-impauth1';
14import { Pages } from './pages.js';
15import { ContactImportUI } from './contactImportUI.js?v=20260731-csvimport-merk1-impauth1-kteam1-kt3';
16import { loadCustomLeadStatuses } from '../app.js'; // Hinzugefügt
17import { 
18    collection, 
19    query, 
20    where, 
21    orderBy, 
22    limit, 
23    getDocs, 
24    doc, 
25    getDoc, 
26    addDoc, 
27    updateDoc, 
28    deleteDoc, 
29    serverTimestamp,
30    writeBatch,
31    arrayUnion,  // HINZUGEFÜGT
32    arrayRemove,  // HINZUGEFÜGT
33    startAfter  // HINZUGEFÜGT für Pagination
34} from 'https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js';
35// phase8c-contacts-migrated-v1
36import SmartListsApi from './smartListsApi.js';
37import AutoCallRulesApi from './autoCallRulesApi.js';
38import ContactsApi from './contactsApi.js';
39import AgentsApi from './agentsApi.js';
40import { AUFGABE_OPTIONS, DEFAULT_INDUSTRIES, DEFAULT_LEAD_STATUSES } from './constants.js';
41import { Calendar } from './calendar.js?v=20260826-tdup2-badge1-dnd1-kalrw2'; // Calendar importieren
42import { Auth } from './auth.js';
43import { Language } from './language.js';
44import ContactDetail from './contactDetail.js?v=20260807-noshow2-soap1-p5f1-rrev1-bill1-intake1-alarm1-tokfix1-dat1-mod1b-nam1-merk1--kal1-kont1--kd1-prax1-nsl1-nsl2-impauth1-tdup2-kf1-kf3-kteam1-kt3'; // ContactDetail für mobile Ansicht
45import { ladeKundenfelder, renderKundenfelder, sammleKundenfelder } from './customFields.js?v=kf3'; // kundenfelder-v1
46import * as KTeam from './kontaktTeam.js?v=kteam3'; // kontakt-team-v1
47
48// phase8c helpers
49async function _c8c_authedFetch(path, opts = {}) {
50  const tok = await Auth.getIdToken();
51  const headers = Object.assign({ 'Authorization': 'Bearer ' + tok, 'Content-Type': 'application/json' }, opts.headers || {});
52  return fetch(path, Object.assign({}, opts, { headers }));
53}
54
55async function _c8c_userMe() {
56  try { const r = await _c8c_authedFetch('/api/users/me'); if (r.ok) return await r.json(); } catch (e) {}
57  return null;
58}
59
60async function _c8c_listMulticallTemplates() {
61  const r = await _c8c_authedFetch('/api/multicall/list');
62  if (!r.ok) return [];
63  const j = await r.json();
64  const all = j.multicalls || [];
65  return all.filter(d => {
66    const isVoice = (d.task || '').startsWith('Sprachbefehl:');
67    const isTemplate = d.status === 'template' || (d.greeting && d.message && !d.phone_number);
68    return !isVoice && isTemplate;
69  });
70}
71
72async function _c8c_listAgents() {
73  try { const a = await AgentsApi.list(); return Array.isArray(a) ? a : []; } catch (e) { return []; }
74}
75
76async function _c8c_getCalendarTypes() {
77  const r = await _c8c_authedFetch('/api/calendar/types');
78  if (!r.ok) return [];
79  const j = await r.json();
80  return j.types || j || [];
81}
82
83
84// Kurze Übersetzungsfunktion für einfacheren Zugriff
85function t(text) {
86    // Fallback für iPhone/Safari falls Language nicht verfügbar ist
87    let result = text;
88    if (typeof Language !== 'undefined' && Language.translate) {
89        result = Language.translate(text);
90    }
91    // Physio Edition: Kontakte -> Patienten
92    // praxis-modus-v1 (13.08.2026): auch bei Betriebsart 'praxis'.
93    if (document.body.classList.contains('physio-edition')
94        || document.body.classList.contains('physio-menu')
95        || document.documentElement.classList.contains('physio-menu')) {
96        result = result.replace(/Kontakte/g, 'Patienten').replace(/Kontakt(?!e)/g, 'Patient').replace(/Kontakten/g, 'Patienten');
97    }
98    return result;
99}
100
101// Helper-Funktion für lokalisierte Datumsformatierung
102function getLocale() {
103    return Language.currentLanguage || 'de-DE';
104}
105
106// Helper-Funktion für Bootstrap Datepicker Konfiguration
107function getDatepickerConfig() {
108    const currentLang = Language.currentLanguage || 'de-DE';
109    const config = {
110        'de-DE': { format: 'dd.mm.yyyy', language: 'de' },
111        'en-US': { format: 'mm/dd/yyyy', language: 'en' },
112        'es-ES': { format: 'dd/mm/yyyy', language: 'es' },
113        'fr-FR': { format: 'dd/mm/yyyy', language: 'fr' },
114        'ar-SA': { format: 'dd/mm/yyyy', language: 'ar' },
115        'it-IT': { format: 'dd/mm/yyyy', language: 'it' } // app-i18n-it-v1
116    };
117    return config[currentLang] || config['de-DE'];
118}
119
120// Globale Variable für Ladeindikator
121let contactsLoadingIndicator = null;
122
123// CSS für den Ladeindikator hinzufügen, wenn es noch nicht existiert
124function ensureLoadingIndicatorStyles() {
125    if (!document.getElementById('loading-indicator-styles')) {
126        const style = document.createElement('style');
127        style.id = 'loading-indicator-styles';
128        style.textContent = `
129            .loading-overlay {
130                position: fixed; /* Geändert zu fixed */
131                top: 0;
132                left: 0;
133                width: 100%;
134                height: 100%;
135                background-color: rgba(255, 255, 255, 0.8); /* Leicht transparent */
136                display: flex;
137                justify-content: center;
138                align-items: center;
139                z-index: 1056; /* Höher als Bootstrap Modals (1055) */
140                opacity: 1; /* Start mit voller Sichtbarkeit */
141                transition: opacity 0.3s ease; /* Weiches Ausblenden */
142            }
143            .loading-overlay.hidden {
144                opacity: 0;
145                pointer-events: none; /* Verhindert Interaktion, wenn ausgeblendet */
146            }
147            .spinner-container {
148                text-align: center;
149                background-color: white;
150                padding: 20px;
151                border-radius: 8px;
152                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
153            }
154        `;
155        document.head.appendChild(style);
156    }
157}
158
159// Ladeindikator anzeigen
160function showContactsLoadingIndicator() {
161            // console.log("[showContactsLoadingIndicator] Called"); // Logging
162    ensureLoadingIndicatorStyles(); // Stelle sicher, dass Styles vorhanden sind
163
164    // Ziel ist jetzt immer document.body
165    const targetContainer = document.body;
166
167    // Prüfe, ob bereits ein Ladeindikator existiert
168    let loadingIndicator = document.getElementById('contactsLoadingIndicator');
169
170    if (!loadingIndicator) {
171        // Erstelle den Ladeindikator, wenn er noch nicht existiert
172            // console.log("[showContactsLoadingIndicator] Creating indicator"); // Logging
173        loadingIndicator = document.createElement('div');
174        loadingIndicator.id = 'contactsLoadingIndicator';
175        loadingIndicator.className = 'loading-overlay'; // Startet sichtbar
176        loadingIndicator.innerHTML = `
177            <div class="spinner-container">
178                <div class="spinner-border text-primary" role="status">
179                    <span class="visually-hidden">${t('Wird geladen...')}</span>
180                </div>
181                <div class="mt-2">${t('Kontakte werden geladen...')}</div>
182        `;
183
184        targetContainer.appendChild(loadingIndicator);
185    } else {
186        // Wenn er existiert, stelle sicher, dass er sichtbar ist
187            // console.log("[showContactsLoadingIndicator] Making existing indicator visible"); // Logging
188        loadingIndicator.classList.remove('hidden');
189    }
190            // console.log("Loading indicator shown for contacts.");
191}
192
193// Ladeindikator ausblenden
194function hideContactsLoadingIndicator() {
195            // console.log("[hideContactsLoadingIndicator] Called"); // Logging
196    const loadingIndicator = document.getElementById('contactsLoadingIndicator');
197    if (loadingIndicator) {
198            // console.log("[hideContactsLoadingIndicator] Hiding indicator"); // Logging
199        // Füge die 'hidden'-Klasse hinzu, um die Ausblend-Animation zu starten
200        loadingIndicator.classList.add('hidden');
201
202        // Entferne das Element nach der Animation
203        setTimeout(() => {
204            if (loadingIndicator.parentNode) {
205            // console.log("[hideContactsLoadingIndicator] Removing indicator from DOM"); // Logging
206                loadingIndicator.parentNode.removeChild(loadingIndicator);
207            }
208        }, 300); // Wartezeit entspricht der CSS-Transition-Dauer
209    } else {
210            // console.log("[hideContactsLoadingIndicator] Indicator not found"); // Logging
211    }
212            // console.log("Loading indicator hidden for contacts.");
213}
214
215// Verbesserte Cache-Funktion mit dynamischer Ablaufzeit und Kompression
216function getCachedData(cacheKey, fetchFunction, cacheDuration = 15 * 60 * 1000) { // Erhöht auf 15 Minuten
217    try {
218        const cached = localStorage.getItem(cacheKey);
219        if (cached) {
220            try {
221                const parsed = JSON.parse(cached);
222                if (Date.now() - parsed.timestamp < cacheDuration) {
223            // console.log(`Rückgabe von Cache-Daten für Schlüssel: ${cacheKey}`);
224                    return Promise.resolve(parsed.data);
225                } else {
226            // console.log(`Cache abgelaufen für Schlüssel: ${cacheKey}`);
227                    // Aktualisiere die Daten im Hintergrund, während wir abgelaufene Daten zurückgeben
228                    setTimeout(() => {
229                        fetchFunction().then(data => {
230                            try {
231                                localStorage.setItem(cacheKey, JSON.stringify({
232                                    timestamp: Date.now(),
233                                    data: data
234                                }));
235            // console.log(`Cache im Hintergrund aktualisiert für: ${cacheKey}`);
236                            } catch (e) {
237                                console.warn('Fehler beim Aktualisieren des Cache:', e);
238                            }
239                        });
240                    }, 100);
241                    
242                    // Gib trotzdem die abgelaufenen Daten zurück für schnellere Anzeige
243                    if (parsed.data) {
244                        return Promise.resolve(parsed.data);
245                    }
246                }
247            } catch (e) {
248                console.error('Fehler beim Parsen der Cache-Daten für Schlüssel:', cacheKey, e);
249            }
250        }
251    } catch (storageError) {
252        console.warn('localStorage-Fehler, Cache wird übersprungen:', storageError);
253        return fetchFunction();
254    }
255    
256    // Daten neu laden
257    return fetchFunction().then(data => {
258        cacheData(cacheKey, data);
259        return data;
260    });
261}
262
263// Separate Funktion zum Speichern von Daten im Cache
264function cacheData(cacheKey, data) {
265    try {
266        // Speichere mit Zeitstempel
267        localStorage.setItem(cacheKey, JSON.stringify({
268            timestamp: Date.now(),
269            data: data
270        }));
271    } catch (storageError) {
272        console.warn('Fehler beim Speichern im Cache:', storageError);
273        
274        // Intelligente Cache-Bereinigung
275        try {
276            // 1. Prüfe verfügbaren Speicher
277            const totalItems = localStorage.length;
278            
279            // 2. Wenn mehr als 20 Einträge, lösche die ältesten Kontakt-Caches
280            if (totalItems > 20) {
281                const cacheEntries = [];
282                
283                // Sammle alle Kontakt-Caches mit Zeitstempeln
284                for (let i = 0; i < localStorage.length; i++) {
285                    const key = localStorage.key(i);
286                    if (key && key.startsWith('contacts_')) {
287                        try {
288                            const item = JSON.parse(localStorage.getItem(key));
289                            cacheEntries.push({
290                                key: key,
291                                timestamp: item.timestamp || 0
292                            });
293                        } catch (e) {
294                            // Ungültiger Cache-Eintrag, direkt löschen
295                            localStorage.removeItem(key);
296                        }
297                    }
298                }
299                
300                // Sortiere nach Alter (älteste zuerst)
301                cacheEntries.sort((a, b) => a.timestamp - b.timestamp);
302                
303                // Lösche die ältesten 30% der Einträge
304                const deleteCount = Math.ceil(cacheEntries.length * 0.3);
305                for (let i = 0; i < deleteCount; i++) {
306                    if (cacheEntries[i]) {
307                        localStorage.removeItem(cacheEntries[i].key);
308            // console.log(`Gelöschter alter Cache: ${cacheEntries[i].key}`);
309                    }
310                }
311                
312                // Versuche erneut zu speichern
313                try {
314                    localStorage.setItem(cacheKey, JSON.stringify({
315                        timestamp: Date.now(),
316                        data: data
317                    }));
318            // console.log('Cache nach Bereinigung erfolgreich gespeichert');
319                } catch (retryError) {
320                    console.warn('Cache konnte nach Bereinigung nicht gespeichert werden');
321                }
322            }
323        } catch (cleanupError) {
324            console.error('Fehler bei der Cache-Bereinigung:', cleanupError);
325        }
326    }
327}
328
329// Optimierte Funktion für das Cachen gefilterter Kontakte
330function setCachedFilteredContacts(key, contacts) {
331    try {
332        // Cache ALLE Kontakte ohne Limit
333        const contactsToCache = contacts;
334        
335        // Kontakte sind bereits minimal (von loadContacts), keine weitere Reduzierung nötig
336        const minimalContacts = contactsToCache;
337        
338        // Speichere mit TTL (Time-To-Live)
339        const cacheData = {
340            timestamp: Date.now(),
341            expires: Date.now() + (15 * 60 * 1000), // 15 Minuten TTL
342            count: contacts.length, // Speichere Gesamtanzahl
343            data: minimalContacts
344        };
345        
346        localStorage.setItem(key, JSON.stringify(cacheData));
347        
348        // Speichere den letzten Cache-Key für schnelles Wiederherstellen
349        localStorage.setItem('last_contacts_cache_key', key);
350    } catch (error) {
351        console.warn('Cache-Fehler:', error);
352    }
353}
354
355// Neue Funktion zur regelmäßigen Cache-Bereinigung
356function scheduleCacheCleanup() {
357    // Führe die Bereinigung alle 30 Minuten durch
358    setInterval(() => {
359        try {
360            // console.log('Starte geplante Cache-Bereinigung...');
361            const now = Date.now();
362            let cleanedCount = 0;
363            
364            // Durchlaufe alle Cache-Einträge
365            for (let i = 0; i < localStorage.length; i++) {
366                const key = localStorage.key(i);
367                
368                // Fokussiere auf Kontakt-bezogene Caches
369                if (key && (key.startsWith('contacts_') || key.startsWith('filtered_contacts_'))) {
370                    try {
371                        const cacheData = JSON.parse(localStorage.getItem(key));
372                        
373                        // Lösche abgelaufene oder alte Caches (älter als 2 Stunden)
374                        if (cacheData.expires && cacheData.expires < now || 
375                            cacheData.timestamp && (now - cacheData.timestamp > 2 * 60 * 60 * 1000)) {
376                            localStorage.removeItem(key);
377                            cleanedCount++;
378                        }
379                    } catch (e) {
380                        // Ungültiger Cache-Eintrag, direkt löschen
381                        localStorage.removeItem(key);
382                        cleanedCount++;
383                    }
384                }
385            }
386            
387            // console.log(`Cache-Bereinigung abgeschlossen. ${cleanedCount} Einträge gelöscht.`);
388        } catch (error) {
389            console.error('Fehler bei der geplanten Cache-Bereinigung:', error);
390        }
391    }, 30 * 60 * 1000); // Alle 30 Minuten
392}
393
394// Starte die Cache-Bereinigung beim Laden
395scheduleCacheCleanup();
396
397// Lazy Loading für Detaildaten mit verbessertem Caching
398async function loadContactDetails(contactId) {
399    const cachedDetails = localStorage.getItem(`contact_details_${contactId}`);
400    if (cachedDetails) {
401        try {
402            const parsed = JSON.parse(cachedDetails);
403            // Erhöhe Cache-Zeit auf 15 Minuten
404            if (Date.now() - parsed.timestamp < 15 * 60 * 1000) {
405            // console.log(`Kontaktdetails aus Cache geladen: ${contactId}`);
406                return parsed.data;
407            }
408        } catch (e) {
409            console.error('Error parsing cached contact details:', e);
410        }
411    }
412    
413    // Lade Details nur bei Bedarf
414    const response = await fetch(`/api/contacts/${contactId}`);
415    const details = await response.json();
416    
417    try {
418        localStorage.setItem(`contact_details_${contactId}`, JSON.stringify({
419            timestamp: Date.now(),
420            data: details
421        }));
422    } catch (e) {
423        console.warn('Error caching contact details:', e);
424    }
425    
426    return details;
427}
428
429// ---- EXPORTS ----
430// Exportiere die Cache-Funktionen, damit sie von anderen Modulen verwendet werden können
431export { getCachedData, cacheData };
432
433// kontakte-filter-v1: Ein Kontaktfeld ist nicht zwingend eine Zeichenkette.
434// In den Echtdaten liegen phone_number und email bei einigen Kontakten als
435// Objekt vor ({type, phone, country, phone_formatted}). Der frueher direkte
436// Aufruf von .toLowerCase() warf dort einen TypeError und brach das Filtern
437// fuer ALLE Kontakte des Mandanten ab ("Error filtering contacts").
438// Objekte werden flach zu ihren Werten aufgeloest, damit die enthaltene
439// Nummer weiterhin auffindbar bleibt.
440function suchText(wert) {
441    if (wert === null || wert === undefined) return '';
442    if (typeof wert === 'string') return wert.toLowerCase();
443    if (typeof wert === 'number' || typeof wert === 'boolean') return String(wert).toLowerCase();
444    if (Array.isArray(wert)) return wert.map(suchText).join(' ');
445    if (typeof wert === 'object') {
446        try { return Object.values(wert).map(suchText).join(' '); } catch (e) { return ''; }
447    }
448    return String(wert).toLowerCase();
449}
450
451// Contacts class for managing contacts in the application
452export class Contacts {
453    constructor() {
454        this.db = db;
455        this.searchTimeout = null;
456        this.eventsBound = false;  // Track if events are already bound
457        this.contacts = [];  // Store all contacts
458        this.contactImportUI = new ContactImportUI();  // Initialize contact import UI
459        this.addContactModal = null;
460        this.smartListModal = null;
461
462        // Listen for language changes
463        document.addEventListener('languageChanged', () => {
464            this.translateAllModals();
465        });
466        this.currentPage = 1;
467        this.itemsPerPage = 50; // Anzahl pro Seite für Pagination
468        this.renderDebounceTimer = null;
469        this.lastVisible = null; // Für Firestore Pagination
470        this.hasMore = true; // Gibt es noch mehr Kontakte zu laden?
471        this.isLoadingMore = false; // Verhindert mehrfaches Laden
472        this.initialLoadSize = 200; // 200 Kontakte initial laden für bessere Suche
473        this.scrollObserver = null; // Für Infinite Scrolling
474        this.allContactsLoaded = false; // Flag ob alle Kontakte geladen sind
475        this.backgroundLoadingComplete = false; // Flag für Hintergrund-Laden
476        this.smartLists = [];
477        this.smartListEventsBound = false;
478        this.currentFilteredContacts = null; // Add this line to store filtered contacts
479        this.callSummaryCache = new Map(); // Cache for call summaries
480        this.userDoc = null; // Add this line to store user document
481        this.filterTimeout = null; // Add this line for filter timeout
482        this.leadStatuses = []; // Hinzufügen, um Lead-Status zu speichern
483        this.smartListBodyClickListener = null; // Hinzufügen für Body-Listener
484        this.activeSmartListId = null; // Hinzufügen, um aktive Smart List zu speichern
485        this.autoCallRulesModal = null; // Property für das neue Modal
486        this.autoCallRules = []; // Zum Speichern der geladenen Regeln
487        this.multiCallTemplates = []; // Zum Speichern der Vorlagen für das Dropdown
488        this.allFilteredContactsSelected = false; // Flag für "Alle gefilterten Kontakte" Auswahl
489        this.selectedFilteredContactIds = []; // Speichert IDs aller gefilterten Kontakte bei Auswahl
490        
491        // Speicher für vollständige Kontaktdaten (bei Bedarf geladen)
492        this.fullContactsCache = new Map(); // Vollständige Daten einzelner Kontakte
493
494        // DEBOUNCE FIX: Verhindert mehrfaches gleichzeitiges Laden
495        this.isLoadingContacts = false; // Flag ob gerade geladen wird
496        this.pendingLoadPromise = null; // Promise für laufende Requests
497
498        // SIP-Account Check
499        this.hasSipAccount = false; // Flag ob SIP-Account vorhanden ist
500
501        // Entferne diese Aufrufe aus dem Konstruktor, sie kommen zu früh
502        // this.loadLastCachedContacts();
503        // this.scheduleBackgroundSync();
504    }
505
506    /**
507     * Speichere den aktuellen Zustand der Kontaktliste für spätere Wiederherstellung
508     */
509    storeContactsState() {
510        try {
511            const currentState = {
512                currentPage: this.currentPage,
513                searchTerm: document.querySelector('#searchInput')?.value || '',
514                leadStatus: document.querySelector('#leadStatusFilter')?.value || '',
515                task: document.querySelector('#aufgabeFilter')?.value || '',
516                branche: document.querySelector('#brancheFilter')?.value || '',
517                dateFrom: document.querySelector('#dateFromFilter')?.value || '',
518                dateTo: document.querySelector('#dateToFilter')?.value || '',
519                activeSmartListId: this.activeSmartListId || localStorage.getItem('activeSmartListId'),
520                hasFilters: false, // Wird unten berechnet
521                timestamp: Date.now()
522            };
523
524            // Prüfe, ob Filter aktiv sind
525            currentState.hasFilters = !!(
526                currentState.searchTerm || 
527                currentState.leadStatus || 
528                currentState.task || 
529                currentState.branche ||
530                currentState.dateFrom ||
531                currentState.dateTo
532            );
533
534            // console.log('[storeContactsState] Speichere Kontaktlisten-Zustand:', currentState);
535            sessionStorage.setItem('contactsListState', JSON.stringify(currentState));
536            
537            // NEUE OPTIMIERUNG: Speichere den kompletten DOM-Zustand
538            const contactsContainer = document.getElementById('contactsContainer');
539            if (contactsContainer) {
540                try {
541                    // Speichere HTML und Scroll-Position
542                    const domState = {
543                        html: contactsContainer.innerHTML,
544                        scrollTop: window.pageYOffset || document.documentElement.scrollTop,
545                        timestamp: Date.now()
546                    };
547                    sessionStorage.setItem('contactsListDOM', JSON.stringify(domState));
548                    
549                    // Speichere auch die Kontakte-Daten für mehrfache Navigation
550                    if (this.contacts && this.contacts.length > 0) {
551                        try {
552                            sessionStorage.setItem('contactsData', JSON.stringify({
553                                contacts: this.contacts,
554                                currentFilteredContacts: this.currentFilteredContacts,
555                                timestamp: Date.now()
556                            }));
557                        } catch (e) {
558                            // Falls zu groß für sessionStorage, speichere nur IDs
559                            console.warn('Kontakte zu groß für sessionStorage, speichere nur Basis-Daten');
560                        }
561                    }
562                    
563            // console.log('[storeContactsState] DOM-Zustand und Kontakte gespeichert');
564                } catch (e) {
565                    console.warn('[storeContactsState] Konnte DOM nicht speichern:', e);
566                }
567            }
568        } catch (error) {
569            console.error('Fehler beim Speichern des Kontaktlisten-Zustands:', error);
570        }
571    }
572
573    // Neue Methode zum Löschen des Kontakt-Caches
574    async clearContactsCache() {
575        try {
576            // console.log('Lösche Kontakt-Cache...');
577            
578            // Aktuelle Benutzer-ID und Tenant-ID ermitteln
579            const user = auth.currentUser;
580            if (!user) {
581                console.warn('Kein Benutzer angemeldet. Cache kann nicht gelöscht werden.');
582                return;
583            }
584
585            // Benutzer-Dokument abrufen, um die Tenant-ID zu bekommen
586                        const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
587            
588            if (!userDocSnap.exists()) {
589                console.warn('Benutzerprofil nicht gefunden.');
590                return;
591            }
592            
593            const userData = userDocSnap.data();
594            const tenantId = userData.tenantID || userData.tenant_id;
595            
596            if (!tenantId) {
597                console.warn('Keine Tenant-ID gefunden.');
598                return;
599            }
600
601            // Lösche ALLE Kontakte-Caches für diesen Benutzer/Tenant (mit und ohne Suffix)
602            const mainCacheKey = `contacts_${user.uid}_${tenantId}`;
603            localStorage.removeItem(mainCacheKey);
604            localStorage.removeItem(`${mainCacheKey}_desktop`);
605            localStorage.removeItem(`${mainCacheKey}_mobile`);
606            console.log('[clearContactsCache] Alle Cache-Keys gelöscht:', mainCacheKey);
607            
608            // Lösche alle gefilterten Kontaktlisten-Caches
609            const keysToRemove = [];
610            for (let i = 0; i < localStorage.length; i++) {
611                const key = localStorage.key(i);
612                if (key && (key.startsWith('filtered_contacts_') || 
613                            key.startsWith('contact_details_'))) {
614                    keysToRemove.push(key);
615                }
616            }
617            
618            // Lösche die gesammelten Schlüssel
619            keysToRemove.forEach(key => {
620                localStorage.removeItem(key);
621            });
622            
623            // Leere auch das interne contacts-Array
624            this.contacts = [];
625            this.currentFilteredContacts = null;
626            
627            // console.log(`Cache gelöscht: ${mainCacheKey} und ${keysToRemove.length} weitere Einträge`);
628        } catch (error) {
629            console.error('Fehler beim Löschen des Kontakt-Caches:', error);
630        }
631    }
632
633    async init() {
634        // showContactsLoadingIndicator(); // Ladeindikator deaktiviert für bessere Performance
635        try {
636            // console.log("Contacts.init gestartet");
637            // Ladeindikator anzeigen <-- Entfernt
638
639            // Prüfe, ob Benutzer angemeldet ist
640            const user = auth.currentUser;
641            if (!user) {
642            // console.log('Benutzer nicht angemeldet. Kontakt-Initialisierung wird übersprungen.');
643                // hideContactsLoadingIndicator(); // Ladeindikator deaktiviert
644                return;
645            }
646            
647            // Lade das User-Dokument
648                        const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
649            if (userDocSnap.exists()) {
650                this.userDoc = userDocSnap.data();
651            }
652            
653            // Prüfe, ob der Kontakt-Container existiert
654            const container = document.getElementById('contactsContainer');
655            if (!container) {
656                console.log('Kontakt-Container nicht gefunden. Seite wird vermutlich nicht angezeigt.');
657                // hideContactsLoadingIndicator(); // Ladeindikator deaktiviert
658                return;
659            }
660            
661            // Setze sofort einen Platzhalter, damit etwas sichtbar ist
662            console.log('[init] Setze Platzhalter in Container');
663            container.innerHTML = `
664                <div class="text-center p-4">
665                    <div class="spinner-border text-primary" role="status">
666                        <span class="visually-hidden">${t('Lade Kontakte...')}</span>
667                    </div>
668                    <p class="mt-2">${t('Kontakte werden geladen...')}</p>
669                </div>
670            `;
671
672            // Prüfe ob wir von der Detailseite zurückkommen
673            const savedState = sessionStorage.getItem('contactsListState');
674            // VERBESSERT: Prüfe ob DOM-Cache existiert, nicht nur Timestamp
675            const domStateStr = sessionStorage.getItem('contactsListDOM');
676            const fromDetailPage = !!(savedState && domStateStr); // True wenn beide Caches existieren
677
678            // Lead-Status nur laden wenn noch nicht vorhanden oder bei normaler Initialisierung
679            if (!this.leadStatuses || this.leadStatuses.length === 0 || !fromDetailPage) {
680                await this.loadLeadStatuses(); // Lade Lead-Status
681                this.updateFilterDropdowns(); // Aktualisiere Filter-Dropdowns mit Lead-Status
682            }
683
684            // SIP-Account prüfen
685            await this.checkSipAccount();
686
687            // Optimierte Ladelogik
688            if (fromDetailPage) {
689                // Wenn wir von der Detailseite zurückkommen
690            // console.log('Kehre von Detailseite zurück - prüfe gespeicherten DOM');
691                
692                // domStateStr wurde schon oben geholt
693                let domRestored = false;
694                
695                if (domStateStr) {
696                    try {
697                        const domState = JSON.parse(domStateStr);
698                        // Prüfe ob DOM-Cache noch frisch ist (max 5 Minuten alt)
699                        if (domState.timestamp > Date.now() - 300000) {
700                            const contactsContainer = document.getElementById('contactsContainer');
701                            if (contactsContainer) {
702            // console.log('Stelle DOM-Zustand wieder her');
703                                contactsContainer.innerHTML = domState.html;
704                                domRestored = true;
705                                
706                                // Stelle Kontakte-Daten wieder her
707                                const contactsDataStr = sessionStorage.getItem('contactsData');
708                                if (contactsDataStr) {
709                                    try {
710                                        const contactsData = JSON.parse(contactsDataStr);
711                                        this.contacts = contactsData.contacts || [];
712                                        this.currentFilteredContacts = contactsData.currentFilteredContacts;
713            // console.log(`Kontakte-Daten wiederhergestellt: ${this.contacts.length} Kontakte`);
714                                    } catch (e) {
715                                        console.warn('Konnte Kontakte-Daten nicht wiederherstellen:', e);
716                                        // Fallback: Lade Kontakte aus localStorage Cache
717                                        if (!this.contacts || this.contacts.length === 0) {
718                                            await this.loadLastCachedContacts();
719                                        }
720                                    }
721                                } else {
722                                    // Keine Kontakte in SessionStorage, versuche localStorage
723                                    if (!this.contacts || this.contacts.length === 0) {
724                                        await this.loadLastCachedContacts();
725                                    }
726                                }
727                                
728                                // Stelle Scroll-Position wieder her
729                                setTimeout(() => {
730                                    window.scrollTo(0, domState.scrollTop);
731                                }, 0);
732                                
733                                // Stelle Filter-Werte wieder her
734                                if (savedState) {
735                                    const state = JSON.parse(savedState);
736                                    if (state.searchTerm) document.querySelector('#searchInput').value = state.searchTerm;
737                                    if (state.leadStatus) document.querySelector('#leadStatusFilter').value = state.leadStatus;
738                                    if (state.task) document.querySelector('#aufgabeFilter').value = state.task;
739                                    if (state.branche) document.querySelector('#brancheFilter').value = state.branche;
740                                    if (state.dateFrom) document.querySelector('#dateFromFilter').value = state.dateFrom;
741                                    if (state.dateTo) document.querySelector('#dateToFilter').value = state.dateTo;
742                                    this.currentPage = state.currentPage || 1;
743                                }
744                                
745                                // Binde Event-Listener neu
746                                this.bindPaginationEvents();
747                                this.setupContactEventListeners();
748                                this.updateSmartListsUI();
749                                this.bindPageControls(true);
750                            }
751                        }
752                    } catch (e) {
753                        console.warn('Konnte DOM nicht wiederherstellen:', e);
754                    }
755                }
756                
757                // Fallback nur wenn DOM nicht wiederhergestellt wurde
758                if (!domRestored) {
759            // console.log('DOM-Wiederherstellung fehlgeschlagen, verwende Fallback');
760                    if (this.contacts && this.contacts.length > 0) {
761            // console.log(`Verwende ${this.contacts.length}
761 Kontakte aus dem Speicher`);
762                        await this.renderContacts(this.contacts);
763                    } else {
764                        const cachedLoaded = await this.loadLastCachedContacts();
765                        if (!cachedLoaded) {
766                            await this.loadContacts();
767                        }
768                    }
769                }
770                
771                // Initialisierung nur wenn noch nicht vorhanden
772                if (!this.smartListModal) {
773                    this.initializeModal(); // Für neuen Kontakt Modal  
774                    this.initializeAutoCallRulesModal(); // NEU: AutoCall Regel Modal initialisieren
775                }
776                
777                // Smart Lists sind bereits geladen, überspringe initializeSmartLists
778            // console.log('Überspringe Smart Lists Laden - verwende vorhandene Daten');
779                
780                // Hintergrund-Sync nur nach einer Weile
781                setTimeout(() => this.scheduleBackgroundSync(), 5000);
782            } else {
783                // Normale Initialisierung - lösche alten DOM-Cache
784                sessionStorage.removeItem('contactsListDOM');
785                sessionStorage.removeItem('contactsListState');
786                sessionStorage.removeItem('contactsData');
787            // console.log('Normale Initialisierung - DOM-Cache gelöscht');
788                
789                await this.loadContacts(); // Rendert Kontakte UND den Platzhalter
790                this.scheduleBackgroundSync();
791                
792                // Initialisiere Modals und Smart List Daten
793                this.initializeModal(); // Für neuen Kontakt Modal
794                this.initializeAutoCallRulesModal(); // NEU: AutoCall Regel Modal initialisieren
795                await this.initializeSmartLists(); // Lädt Smart List Daten (ohne UI Update)
796            }
797
798            // Prüfe auf gespeicherte aktive Smart List und wende sie an - NUR bei normaler Initialisierung
799            if (!fromDetailPage) {
800                const storedActiveSmartListId = localStorage.getItem('activeSmartListId');
801                let smartListApplied = false; // Flag, ob eine Smart List angewendet wurde
802                if (storedActiveSmartListId) {
803            // console.log("Found active smart list ID in storage:", storedActiveSmartListId);
804                    const exists = this.smartLists.some(list => list.id === storedActiveSmartListId);
805                    if (exists) {
806            // console.log("Applying stored active smart list:", storedActiveSmartListId);
807                        // Wende die Smart List an, bevor die UI für Smart Lists erstellt wird
808                        await this.applySmartList(storedActiveSmartListId);
809                        smartListApplied = true; // Setze das Flag
810                    } else {
811            // console.log("Stored active smart list ID not found in current lists, removing from storage.");
812                        localStorage.removeItem('activeSmartListId');
813                    }
814                }
815            }
816
817            // Jetzt, da der Platzhalter existiert UND die Smart List Daten geladen sind:
818            // Update Smart Lists UI *nach* potentiellem Anwenden einer gespeicherten Liste
819            // Aber NUR wenn der Platzhalter existiert
820            if (document.getElementById('smart-lists-placeholder')) {
821                this.updateSmartListsUI();
822            } else {
823            // console.log('[init] Smart-Listen Platzhalter noch nicht vorhanden, überspringe updateSmartListsUI');
824            }
825 
826            // Binde Listener für die dynamisch gerenderten Listenelemente (Buttons, Tabelle etc.)
827            // NACHDEM loadContacts die Tabelle initial gerendert hat (oder applySmartList)
828            // Wird bereits in renderContactsImmediate aufgerufen, daher hier nur als Fallback
829            if (!fromDetailPage) {
830                this.setupContactEventListeners();
831            } 
832
833            // Initialisiere die Tooltips
834            const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
835            tooltipTriggerList.map(function (tooltipTriggerEl) {
836                return new bootstrap.Tooltip(tooltipTriggerEl);
837            });
838            
839            // Binde Listener für statische Seitenelemente (Suche, Filter)
840            // Erzwinge das Neubinden, indem wir den Flag explizit zurücksetzen
841            // console.log("[init] Calling bindPageControls...");
842            this.bindPageControls(true); // Force rebind parameter
843            // console.log("[init] bindPageControls completed.");
844            
845            // Ladeindikator ausblenden <-- HIER GANZ AM ENDE DES TRY-BLOCKS
846            // hideContactsLoadingIndicator(); // Ladeindikator deaktiviert
847
848        } catch (error) {
849            console.error('Error initializing contacts:', error);
850            UI.showError(t('Fehler beim Laden der Kontakte'));
851            // hideContactsLoadingIndicator(); // Ladeindikator deaktiviert
852        } finally {
853            // this.setupEventDelegation(); // Entfernt, da unnötig/potenziell störend
854            // console.log("Contacts.init abgeschlossen");
855        }
856    }
857
858    // Bindet Listener für statische Seitenelemente (Suche, Filter) - Nur einmal aufrufen
859    bindPageControls(forceRebind = false) {
860        if (this.pageControlsBound && !forceRebind) {
861            // console.log("[bindPageControls] Already bound, skipping.");
862            return;
863        }
864        
865        if (forceRebind) {
866            // console.log("[bindPageControls] Force rebinding event listeners...");
867            this.pageControlsBound = false;
868        }
869            // console.log("[bindPageControls] Binding search and filter listeners.");
870
871        // Entferne vorherigen Clear-Button-Listener falls vorhanden
872        if (this.clearButtonListener) {
873            // console.log("[bindPageControls] Removing existing clear button listener");
874            document.removeEventListener('click', this.clearButtonListener);
875            this.clearButtonListener = null;
876        }
877
878        // Verwende Event-Delegation für robuste Clear-Button-Unterstützung
879        this.clearButtonListener = (e) => {
880            // Clear Search Button (verschiedene mögliche Selektoren)
881            if (e.target.closest('#clearSearchBtn') || 
882                e.target.closest('.clear-search-btn') || 
883                e.target.closest('[data-action="clear-search"]') ||
884                (e.target.closest('.input-group') && e.target.closest('button') && e.target.closest('#searchInput')?.parentElement)) {
885                
886            // console.log("[bindPageControls] Clear search button clicked.");
887                const searchInput = document.querySelector('#searchInput');
888                if (searchInput) {
889                    searchInput.value = '';
890                    // Wenn Suche gelöscht wird, ist keine Smart List mehr aktiv
891                    localStorage.removeItem('activeSmartListId');
892                    this.activeSmartListId = null;
893                    this.applyFilters(); // Wende leere Filter an, um alle Kontakte zu zeigen
894                    // Speichere den aktualisierten Zustand
895                    this.storeContactsState();
896                }
897                e.preventDefault();
898                e.stopPropagation();
899            }
900
901                         // Clear Lead Status Filter Button
902             if (e.target.closest('#clearLeadStatusBtn') || 
903                 e.target.closest('.clear-lead-status-btn') || 
904                 e.target.closest('[data-action="clear-lead-status"]')) {
905                 
906            // console.log("[bindPageControls] Clear lead status filter button clicked.");
907                 const leadStatusFilter = document.querySelector('#leadStatusFilter');
908                 if (leadStatusFilter) leadStatusFilter.value = '';
909                 this.applyFilters();
910                 this.storeContactsState();
911                 e.preventDefault();
912                 e.stopPropagation();
913             }
914
915             // Clear Branche Filter Button
916             if (e.target.closest('#clearBrancheBtn') || 
917                 e.target.closest('#clearIndustryBtn') ||
918                 e.target.closest('.clear-branche-btn') || 
919                 e.target.closest('.clear-industry-btn') ||
920                 e.target.closest('[data-action="clear-branche"]') ||
921                 e.target.closest('[data-action="clear-industry"]')) {
922                 
923            // console.log("[bindPageControls] Clear branche filter button clicked.");
924                 const brancheFilter = document.querySelector('#brancheFilter');
925                 if (brancheFilter) brancheFilter.value = '';
926                 this.applyFilters();
927                 this.storeContactsState();
928                 e.preventDefault();
929                 e.stopPropagation();
930             }
931
932             // Clear Task/Aufgabe Filter Button
933             if (e.target.closest('#clearTaskBtn') || 
934                 e.target.closest('#clearAufgabeBtn') ||
935                 e.target.closest('.clear-task-btn') || 
936                 e.target.closest('.clear-aufgabe-btn') ||
937                 e.target.closest('[data-action="clear-task"]') ||
938                 e.target.closest('[data-action="clear-aufgabe"]')) {
939                 
940            // console.log("[bindPageControls] Clear task filter button clicked.");
941                 const taskFilter = document.querySelector('#aufgabeFilter');
942                 if (taskFilter) taskFilter.value = '';
943                 this.applyFilters();
944                 this.storeContactsState();
945                 e.preventDefault();
946                 e.stopPropagation();
947             }
948
949             // Clear Date Filter Button (verschiedene mögliche Selektoren)
950             if (e.target.closest('#clearDateFilterBtn') || 
951                 e.target.closest('.clear-date-filter-btn') || 
952                 e.target.closest('[data-action="clear-date-filter"]')) {
953                 
954            // console.log("[bindPageControls] Clear date filter button clicked.");
955                 const dateFromFilter = document.querySelector('#dateFromFilter');
956                 const dateToFilter = document.querySelector('#dateToFilter');
957                 if (dateFromFilter) dateFromFilter.value = '';
958                 if (dateToFilter) dateToFilter.value = '';
959                 this.applyFilters();
960                 // Speichere den aktualisierten Zustand
961                 this.storeContactsState();
962                 e.preventDefault();
963                 e.stopPropagation();
964             }
965
966             // GENERIC Clear Filter Button - fängt alle anderen Clear-Buttons ab
967             if (e.target.closest('.clear-filter') || 
968                 e.target.closest('.filter-clear') ||
969                 e.target.closest('.btn-clear-filter') ||
970                 (e.target.closest('button') && (
971                     e.target.closest('button').textContent.includes('×') ||
972                     e.target.closest('button').innerHTML.includes('times') ||
973                     e.target.closest('button').innerHTML.includes('fa-times') ||
974                     e.target.closest('button').innerHTML.includes('fa-close')
975                 ))) {
976                 
977            // console.log("[bindPageControls] Generic clear filter button clicked.");
978                 
979                 // Versuche herauszufinden, welcher Filter gelöscht werden soll
980                 const button = e.target.closest('button');
981                 const filterContainer = button?.closest('.form-group') || button?.closest('.filter-group') || button?.closest('.input-group');
982                 
983                 if (filterContainer) {
984                     const select = filterContainer.querySelector('select');
985                     const input = filterContainer.querySelector('input');
986                     
987                     if (select) {
988            // console.log("[bindPageControls] Clearing select element:", select.id);
989                         select.value = '';
990                     }
991                     if (input) {
992            // console.log("[bindPageControls] Clearing input element:", input.id);
993                         input.value = '';
994                     }
995                     
996                     this.applyFilters();
997                     this.storeContactsState();
998                 }
999                 
1000                 e.preventDefault();
1001                 e.stopPropagation();
1002             }
1003
1004            // Clear All Filters Button (neuer Button)
1005            if (e.target.closest('#clearAllFiltersBtn') || 
1006                e.target.closest('.clear-all-filters-btn') || 
1007                e.target.closest('[data-action="clear-all-filters"]')) {
1008                
1009            // console.log("[bindPageControls] Clear all filters button clicked.");
1010                this.clearAllFilters();
1011                                 e.preventDefault();
1012                 e.stopPropagation();
1013             }
1014         };
1015         
1016         // Füge den Event-Listener hinzu
1017         document.addEventListener('click', this.clearButtonListener);
1018            // console.log("[bindPageControls] Clear button listener added to document");
1019
1020        // --- Suche --- 
1021        const searchInput = document.querySelector('#searchInput');
1022        if (searchInput) {
1023            // Verwende debounceSearch direkt als Listener
1024            searchInput.addEventListener('input', (e) => this.debounceSearch(e));
1025        } else {
1026             console.warn("[bindPageControls] Search input not found.");
1027        }
1028
1029        // --- Filter Dropdowns ---
1030        const leadStatusFilter = document.querySelector('#leadStatusFilter');
1031        const taskFilter = document.querySelector('#aufgabeFilter');
1032        const brancheFilter = document.querySelector('#brancheFilter');
1033        const dateFromFilter = document.querySelector('#dateFromFilter');
1034        const dateToFilter = document.querySelector('#dateToFilter');
1035
1036        // Entferne vorherige Filter-Listener falls vorhanden
1037        if (this.filterChangeHandler) {
1038            [leadStatusFilter, taskFilter, brancheFilter, dateFromFilter, dateToFilter].forEach(filter => {
1039                if (filter) {
1040                    filter.removeEventListener('change', this.filterChangeHandler);
1041                }
1042            });
1043        }
1044
1045        // Einziger Filter-Handler
1046        this.filterChangeHandler = () => {
1047            // console.log("[filterChangeHandler] Filter change detected");
1048            if (this.filterTimeout) {
1049                clearTimeout(this.filterTimeout);
1050            }
1051            this.filterTimeout = setTimeout(() => {
1052            // console.log("[filterChangeHandler] Applying filters after timeout");
1053                this.applyFilters();
1054                // Speichere den neuen Zustand nach Filteranwendung
1055                this.storeContactsState();
1056            }, 300);
1057        };
1058
1059        // Binde Filter-Listener
1060        [leadStatusFilter, taskFilter, brancheFilter, dateFromFilter, dateToFilter].forEach((filter, index) => {
1061            if (filter) {
1062                filter.addEventListener('change', this.filterChangeHandler);
1063            // console.log(`[bindPageControls] Filter listener bound to element ${index}:`, filter.id || filter.className);
1064            } else {
1065                console.warn(`[bindPageControls] Filter element ${index} not found.`);
1066            }
1067        });
1068
1069        this.pageControlsBound = true;
1070            // console.log("[bindPageControls] All listeners bound successfully - Search, Filter, and Clear buttons ready.");
1071    }
1072
1073    // Neue Hilfsmethode zum Löschen aller Filter
1074    clearAllFilters() {
1075            // console.log("[clearAllFilters] Clearing all filters and search.");
1076        
1077        // Suche leeren
1078        const searchInput = document.querySelector('#searchInput');
1079        if (searchInput) {
1080            searchInput.value = '';
1081        }
1082
1083        // Alle Filter-Dropdowns zurücksetzen
1084        const filters = [
1085            '#leadStatusFilter',
1086            '#aufgabeFilter', 
1087            '#brancheFilter'
1088        ];
1089        
1090        filters.forEach(selector => {
1091            const element = document.querySelector(selector);
1092            if (element) {
1093                element.value = '';
1094            }
1095        });
1096
1097        // Datumsfilter leeren
1098        const dateFromFilter = document.querySelector('#dateFromFilter');
1099        const dateToFilter = document.querySelector('#dateToFilter');
1100        if (dateFromFilter) dateFromFilter.value = '';
1101        if (dateToFilter) dateToFilter.value = '';
1102
1103        // Smart List deaktivieren
1104        localStorage.removeItem('activeSmartListId');
1105        this.activeSmartListId = null;
1106
1107        // Filter anwenden (zeigt alle Kontakte)
1108        this.applyFilters();
1109        
1110        // Zustand speichern
1111        this.storeContactsState();
1112
1113        UI.showSuccess(t('Alle Filter wurden geleert.'));
1114    }
1115
1116    // Neue Methode zum Prüfen ob SIP-Account vorhanden ist
1117    async checkSipAccount() {
1118        try {
1119            const user = auth.currentUser;
1120            if (!user) {
1121                this.hasSipAccount = false;
1122                return;
1123            }
1124
1125            // Lade User-Dokument falls noch nicht vorhanden
1126            if (!this.userDoc) {
1127                                const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1128                if (userDocSnap.exists()) {
1129                    this.userDoc = userDocSnap.data();
1130                }
1131            }
1132
1133            const tenantId = this.userDoc?.tenantID || this.userDoc?.tenant_id;
1134            if (!tenantId) {
1135                this.hasSipAccount = false;
1136                return;
1137            }
1138
1139            // Lade Tenant-Dokument und prüfe auf SIP-Accounts
1140            /* phase8c */ const _me = await _c8c_userMe(); const tenantDocSnap = { exists: () => !!_me, data: () => ({ name: (_me && (_me.companyName || _me.company)) || '', company: (_me && _me.company) || '', country: (_me && _me.country) || 'DE' }) };
1141
1142            if (tenantDocSnap.exists()) {
1143                const tenantData = tenantDocSnap.data();
1144                // Prüfe ob telephonySettings.sipTrunks existiert und nicht leer ist
1145                const sipTrunks = tenantData?.telephonySettings?.sipTrunks;
1146                this.hasSipAccount = sipTrunks && Object.keys(sipTrunks).length > 0;
1147                console.log('[checkSipAccount] SIP-Account gefunden:', this.hasSipAccount);
1148            } else {
1149                this.hasSipAccount = false;
1150            }
1151        } catch (error) {
1152            console.error('[checkSipAccount] Fehler beim Prüfen des SIP-Accounts:', error);
1153            this.hasSipAccount = false;
1154        }
1155    }
1156
1157    // Neue Methode zum Laden der Lead-Status (Standard + Custom über app.js)
1158    async loadLeadStatuses() {
1159        try {
1160            const user = auth.currentUser;
1161            if (!user) {
1162                console.warn("Kein Benutzer angemeldet, Standard-Lead-Status werden verwendet.");
1163                // Fallback zu einem minimalen Set oder leer lassen?
1164                this.leadStatuses = DEFAULT_LEAD_STATUSES; 
1165                return;
1166            }
1167            
1168            // Stelle sicher, dass userDoc geladen ist (sollte durch init geschehen sein)
1169            if (!this.userDoc) {
1170                console.warn("Benutzerdokument nicht geladen, versuche es jetzt zu laden...");
1171                                const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1172                if (userDocSnap.exists()) {
1173                    this.userDoc = userDocSnap.data();
1174                } else {
1175                    console.error("Benutzerdokument konnte nicht geladen werden.");
1176                    this.leadStatuses = DEFAULT_LEAD_STATUSES; // Fallback
1177                    return;
1178                }
1179            }
1180
1181            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
1182
1183            if (!tenantId) {
1184                console.error("Keine Tenant-ID im Benutzerdokument gefunden.");
1185                this.leadStatuses = DEFAULT_LEAD_STATUSES; // Fallback
1186                return;
1187            }
1188
1189            // Rufe die zentrale Funktion aus app.js auf
1190            this.leadStatuses = await loadCustomLeadStatuses(tenantId);
1191            // console.log('Geladene Lead-Status (über app.js):', this.leadStatuses);
1192
1193        } catch (error) {
1194            console.error('Fehler beim Laden der Lead-Status über loadCustomLeadStatuses:', error);
1195            // Fallback zu Standard-Status bei Fehlern
1196            this.leadStatuses = DEFAULT_LEAD_STATUSES;
1197        }
1198    }
1199
1200    initializeModal() {
1201        const modalElement = document.getElementById('addContactModal');
1202        if (modalElement && !this.addContactModal) {
1203            // Initialize modal with Bootstrap options
1204            this.addContactModal = new bootstrap.Modal(modalElement);
1205
1206            // Add form submit handler
1207            const form = document.getElementById('addContactForm');
1208            if (form) {
1209                form.onsubmit = async (e) => {
1210                    e.preventDefault();
1211                    await this.createNewContact();
1212                };
1213            }
1214
1215            // Listen for modal hidden event to reset form
1216            modalElement.addEventListener('hidden.bs.modal', () => {
1217                const form = document.getElementById('addContactForm');
1218                if (form) {
1219                    form.reset();
1220                }
1221            });
1222
1223            // Listen for modal shown event to focus first input
1224            modalElement.addEventListener('shown.bs.modal', () => {
1225                const firstInput = modalElement.querySelector('input[name="name"]');
1226                if (firstInput) {
1227                    firstInput.focus();
1228                }
1229            });
1230        }
1231    }
1232
1233    // NEU: Modal für AutoCall-Regeln initialisieren
1234    initializeAutoCallRulesModal() {
1235        // HTML für das Modal (wird später dynamisch hinzugefügt oder muss in index.html sein)
1236        const modalHtml = `
1237        <div class="modal fade" id="autoCallRulesModal" tabindex="-1" aria-labelledby="autoCallRulesModalLabel" aria-hidden="true">
1238            <div class="modal-dialog modal-xl"> <!-- Geändert zu modal-xl für mehr Breite -->
1239                <div class="modal-content">
1240                    <div class="modal-header">
1241                        <h5 class="modal-title" id="autoCallRulesModalLabel">${t('AutoCall Regeln verwalten')}</h5>
1242                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1243                    </div>
1244                    <div class="modal-body">
1245                        <h6>${t('Bestehende Regeln')}</h6>
1246                        <div class="table-responsive mb-4">
1247                            <table class="table table-sm">
1248                                <thead>
1249                                    <tr>
1250                                        <th>${t('Trigger Status')}</th>
1251                                        <th>${t('Vorlage')}</th>
1252                                        <th>${t('Aktiv')}</th>
1253                                        <th>${t('Aktionen')}</th>
1254                                    </tr>
1255                                </thead>
1256                                <tbody id="autoCallRulesTableBody">
1257                                    
1258                                    <tr><td colspan="4" class="text-center">${t('Lade Regeln...')}</td></tr>
1259                                </tbody>
1260                            </table>
1261                        </div>
1262
1263                        <h6>${t('Neue Regel hinzufügen')}</h6>
1264                        <form id="newAutoCallRuleForm">
1265                            <div class="row g-3 align-items-end">
1266                                <div class="col-md-4">
1267                                    <label for="ruleLeadStatus" class="form-label">${t('Wenn Lead-Status:')}</label>
1268                                    <select class="form-select" id="ruleLeadStatus" required>
1269                                        <option value="">${t('Wählen...')}</option>
1270                                        
1271                                    </select>
1272                                </div>
1273                                <div class="col-md-4">
1274                                    <label for="ruleTemplate" class="form-label">${t('Verwende Vorlage:')}</label>
1275                                    <select class="form-select" id="ruleTemplate" required>
1276                                        <option value="">${t('Wählen...')}</option>
1277                                        
1278                                    </select>
1279                                </div>
1280                                <div class="col-md-2">
1281                                    <div class="form-check mb-2">
1282                                        <input class="form-check-input" type="checkbox" id="ruleActive" checked />
1283                                        <label class="form-check-label" for="ruleActive">
1284                                            ${t('Aktiv')}
1285                                        </label>
1286                                    </div>
1287                                </div>
1288                                <div class="col-md-2">
1289                                    <button type="submit" class="btn btn-primary w-100">${t('Regel hinzufügen')}</button>
1290                                </div>
1291                            </div>
1292                        </form>
1293                    </div>
1294                    <div class="modal-footer">
1295                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">${t('Schließen')}</button>
1296                    </div>
1297                </div>
1298            </div>
1299        </div>
1300        `;
1301
1302        // Füge das Modal zum Body hinzu, wenn es noch nicht existiert
1303        if (!document.getElementById('autoCallRulesModal')) {
1304            document.body.insertAdjacentHTML('beforeend', modalHtml);
1305        }
1306
1307        const modalElement = document.getElementById('autoCallRulesModal');
1308        if (modalElement) {
1309            this.autoCallRulesModal = new bootstrap.Modal(modalElement);
1310
1311            // Event Listener für das Formular (wird nur einmal hinzugefügt)
1312            const form = document.getElementById('newAutoCallRuleForm');
1313            if (form && !form.dataset.listenerAttached) {
1314                 form.addEventListener('submit', (e) => this.handleSaveAutoCallRule(e));
1315                 form.dataset.listenerAttached = 'true'; // Markieren, dass Listener angehängt ist
1316            }
1317             // Event Listener für die Tabelle (Event Delegation)
1318             const tableBody = document.getElementById('autoCallRulesTableBody');
1319             if (tableBody && !tableBody.dataset.listenerAttached) {
1320                 tableBody.addEventListener('click', (e) => {
1321                     if (e.target.classList.contains('toggle-rule-active')) {
1322                         const ruleId = e.target.dataset.ruleId;
1323                         const currentStatus = e.target.checked;
1324                         this.toggleAutoCallRuleActive(ruleId, currentStatus);
1325                     } else if (e.target.closest('.delete-rule')) {
1326                         const ruleId = e.target.closest('.delete-rule').dataset.ruleId;
1327                         this.deleteAutoCallRule(ruleId);
1328                     }
1329                 });
1330                 tableBody.dataset.listenerAttached = 'true'; // Markieren
1331             }
1332        } else {
1333            console.error("AutoCall Rules Modal Element konnte nicht gefunden werden.");
1334        }
1335    }
1336
1337    // NEU: Methode zum Aktualisieren der AutoCall-Modal-Texte nach Sprachwechsel
1338    updateAutoCallModalTexts() {
1339        const modal = document.getElementById('autoCallRulesModal');
1340        if (!modal) return;
1341
1342        // Modal-Titel aktualisieren
1343        const modalTitle = modal.querySelector('#autoCallRulesModalLabel');
1344        if (modalTitle) modalTitle.textContent = t('AutoCall Regeln verwalten');
1345
1346        // Tabellen-Header aktualisieren
1347        const headers = modal.querySelectorAll('thead th');
1348        if (headers.length >= 4) {
1349            headers[0].textContent = t('Trigger Status');
1350            headers[1].textContent = t('Vorlage');
1351            headers[2].textContent = t('Aktiv');
1352            headers[3].textContent = t('Aktionen');
1353        }
1354
1355        // Überschriften aktualisieren
1356        const headings = modal.querySelectorAll('h6');
1357        if (headings.length >= 2) {
1358            headings[0].textContent = t('Bestehende Regeln');
1359            headings[1].textContent = t('Neue Regel hinzufügen');
1360        }
1361
1362        // Formular-Labels aktualisieren
1363        const leadStatusLabel = modal.querySelector('label[for="ruleLeadStatus"]');
1364        if (leadStatusLabel) leadStatusLabel.textContent = t('Wenn Lead-Status:');
1365
1366        const templateLabel = modal.querySelector('label[for="ruleTemplate"]');
1367        if (templateLabel) templateLabel.textContent = t('Verwende Vorlage:');
1368
1369        const activeLabel = modal.querySelector('label[for="ruleActive"]');
1370        if (activeLabel) activeLabel.textContent = t('Aktiv');
1371
1372        // Button-Texte aktualisieren
1373        const submitButton = modal.querySelector('button[type="submit"]');
1374        if (submitButton) submitButton.textContent = t('Regel hinzufügen');
1375
1376        const closeButton = modal.querySelector('.modal-footer .btn-secondary');
1377        if (closeButton) closeButton.textContent = t('Schließen');
1378
1379        // Dropdown-Optionen aktualisieren
1380        const leadStatusSelect = modal.querySelector('#ruleLeadStatus');
1381        if (leadStatusSelect && leadStatusSelect.children.length > 0) {
1382            leadStatusSelect.children[0].textContent = t('Wählen...');
1383        }
1384
1385        const templateSelect = modal.querySelector('#ruleTemplate');
1386        if (templateSelect && templateSelect.children.length > 0) {
1387            templateSelect.children[0].textContent = t('Wählen...');
1388        }
1389
1390        // Tabelle neu rendern, falls Regeln vorhanden sind
1391        if (this.autoCallRules && this.autoCallRules.length > 0) {
1392            this.renderAutoCallRulesTable(this.autoCallRules);
1393        }
1394    }
1395
1396    // NEU: Methode zum Anzeigen des Modals und Laden der Daten
1397    async showAutoCallRulesModal() {
1398        if (!this.autoCallRulesModal) {
1399            console.error("AutoCall Rules Modal ist nicht initialisiert.");
1400            UI.showError("Fehler beim Öffnen der Regeleinstellungen.");
1401            return;
1402        }
1403
1404        // Ladeindikator in Tabelle anzeigen
1405        const tableBody = document.getElementById('autoCallRulesTableBody');
1406        if(tableBody) tableBody.innerHTML = `<tr><td colspan="4" class="text-center"><div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">${t('Lade Regeln...')}</span></div></td></tr>`;
1407
1408        // Fülle Lead Status Dropdown (verwende geladene Status)
1409        const leadStatusSelect = document.getElementById('ruleLeadStatus');
1410        if (leadStatusSelect) {
1411            leadStatusSelect.innerHTML = `<option value="">${t('Wählen...')}</option>`;
1412            // Stelle sicher, dass leadStatuses geladen ist
1413            // if (!this.leadStatuses || this.leadStatuses.length === 0) {
1414            //     await this.loadLeadStatuses(); // Lade Status, falls noch nicht geschehen
1415            // }
1416            // Entferne die Schleife über alle Status
1417            // this.leadStatuses.forEach(status => {
1418            //     leadStatusSelect.innerHTML += `<option value="${status}">${t(status)}</option>`;
1419            // });
1420
1421            // Füge nur die Option "Anrufen" hinzu
1422            leadStatusSelect.innerHTML += `<option value="Anrufen">${t('Anrufen')}</option>`;
1423
1424        } else {
1425            console.error("Lead Status Select Element ('ruleLeadStatus') nicht gefunden.");
1426        }
1427
1428        // Lade Vorlagen und Regeln parallel
1429        try {
1430            await Promise.all([
1431                this.populateTemplateDropdown(), // Lädt Vorlagen und füllt Dropdown
1432                this.loadAutoCallRules()         // Lädt Regeln und rendert Tabelle
1433            ]);
1434        } catch (error) {
1435            console.error("Fehler beim Laden der AutoCall-Regeldaten:", error);
1436            UI.showError(t('Fehler beim Laden der Regeleinstellungen.'));
1437             if(tableBody) tableBody.innerHTML = `<tr><td colspan="4" class="text-center text-danger">${t('Fehler beim Laden.')}</td></tr>`;
1438        }
1439
1440        // Übersetze das Modal vor dem Anzeigen
1441        this.updateAutoCallModalTexts();
1442
1443        this.autoCallRulesModal.show();
1444    }
1445
1446    // NEU: Methode zum Laden der MultiCall-Vorlagen und Befüllen des Dropdowns
1447    async populateTemplateDropdown() {
1448        const templateSelect = document.getElementById('ruleTemplate');
1449        if (!templateSelect) {
1450            console.error("Template Select Element ('ruleTemplate') nicht gefunden.");
1451            return;
1452        }
1453        templateSelect.innerHTML = `<option value="">${t('Lade Vorlagen...')}</option>`;
1454        templateSelect.disabled = true;
1455
1456        try {
1457            const user = auth.currentUser;
1458            if (!user) throw new Error(t('Benutzer nicht angemeldet.'));
1459
1460            // Tenant ID ermitteln (aus userDoc, wie in anderen Methoden)
1461            if (!this.userDoc) {
1462                 console.warn("UserDoc nicht gefunden in populateTemplateDropdown, versuche neu zu laden.");
1463                                const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1464                if (userDocSnap.exists()) this.userDoc = userDocSnap.data();
1465                else throw new Error(t('Benutzerprofil nicht gefunden.'));
1466            }
1467            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
1468            if (!tenantId) throw new Error(t('Keine Tenant-ID gefunden.'));
1469
1470            // Korrekter Pfad zur 'multicall'-Sammlung auf Root-Ebene
1471/* phase8c */ this.multiCallTemplates = (await _c8c_listMulticallTemplates()).filter(d => d.id && (d.task || d.name));
1472            // console.log("Geladene MultiCall-Vorlagen:", this.multiCallTemplates); // Debug Log
1473
1474            // Dropdown befüllen
1475            templateSelect.innerHTML = `<option value="">${t('Wählen...')}</option>`;
1476            if (this.multiCallTemplates.length > 0) {
1477                this.multiCallTemplates.forEach(template => {
1478                    // Verwende 'task' oder 'name' für die Anzeige, mit Fallback
1479                    const templateName = template.task || template.name || `Vorlage ${template.id.substring(0, 5)}...`;
1480                    templateSelect.innerHTML += `<option value="${template.id}">${templateName}</option>`;
1481                });
1482                templateSelect.disabled = false;
1483            } else {
1484                 templateSelect.innerHTML = '<option value="">Keine Vorlagen gefunden</option>';
1485                 templateSelect.disabled = true; // Deaktivieren, wenn keine Vorlagen da sind
1486            }
1487
1488        } catch (error) {
1489            console.error("Fehler beim Laden der MultiCall-Vorlagen:", error);
1490            templateSelect.innerHTML = '<option value="">Fehler beim Laden</option>';
1491            templateSelect.disabled = true;
1492        }
1493    }
1494
1495    // NEU: Methode zum Laden der AutoCall-Regeln
1496    async loadAutoCallRules() {
1497        const tableBody = document.getElementById('autoCallRulesTableBody');
1498         if (!tableBody) {
1499             console.error("AutoCall Rules Table Body ('autoCallRulesTableBody') nicht gefunden.");
1500             return; // Frühzeitiger Ausstieg, wenn das Element fehlt
1501         }
1502         tableBody.innerHTML = '<tr><td colspan="4" class="text-center">Lade Regeln...</td></tr>'; // Ladezustand anzeigen
1503
1504        try {
1505            const user = auth.currentUser;
1506            if (!user) throw new Error("Benutzer nicht angemeldet.");
1507
1508             // Stelle sicher, dass userDoc geladen ist
1509             if (!this.userDoc) {
1510                 console.warn("UserDoc nicht gefunden in loadAutoCallRules, versuche neu zu laden.");
1511                                  const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1512                 if (userDocSnap.exists()) this.userDoc = userDocSnap.data();
1513                 else throw new Error("Benutzerprofil nicht gefunden.");
1514             }
1515
1516            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
1517            if (!tenantId) throw new Error("Keine Tenant-ID gefunden.");
1518
1519            // Pfad zu den Regeln (Tenant-Ebene) - KORREKT
1520/* phase8c */ this.autoCallRules = await AutoCallRulesApi.list(true);
1521            // console.log("Geladene AutoCall-Regeln:", this.autoCallRules); // Debug Log
1522
1523            this.renderAutoCallRulesTable(this.autoCallRules);
1524
1525        } catch (error) {
1526            console.error("Fehler beim Laden der AutoCall-Regeln:", error);
1527             if(tableBody) tableBody.innerHTML = '<tr><td colspan="4" class="text-center text-danger">Fehler beim Laden der Regeln.</td></tr>';
1528             // Fehler weitergeben, damit showAutoCallRulesModal ihn fangen kann (optional, je nach gewünschtem Verhalten)
1529             // throw error;
1530        }
1531    }
1532
1533     // NEU: Methode zum Rendern der Regel-Tabelle
1534     renderAutoCallRulesTable(rules) {
1535        const tableBody = document.getElementById('autoCallRulesTableBody');
1536        if (!tableBody) return;
1537
1538        if (!rules || rules.length === 0) {
1539            tableBody.innerHTML = `<tr><td colspan="4" class="text-center">${t('Keine Regeln vorhanden')}</td></tr>`;
1540            return;
1541        }
1542
1543        // Stelle sicher, dass Vorlagen geladen sind, bevor gerendert wird
1544        if (!this.multiCallTemplates || this.multiCallTemplates.length === 0) {
1545             console.warn("Vorlagen sind noch nicht geladen, Regel-Tabelle kann unvollständig sein.");
1546             // Optional: Erneut laden oder warten? Vorerst nur warnen.
1547        }
1548
1549        tableBody.innerHTML = rules.map(rule => {
1550            // Finde den Vorlagennamen (Fallback, falls Vorlage gelöscht wurde oder noch nicht geladen)
1551            const template = this.multiCallTemplates?.find(t => t.id === rule.templateId);
1552            const templateName = template
1553                ? (template.task || template.name || `${t('Vorlage')} ${template.id.substring(0,5)}...`)
1554                : (rule.templateId ? `${t('Unbekannt')} (${rule.templateId.substring(0,5)}...)` : t('Keine Vorlage'));
1555
1556            return `
1557                <tr>
1558                    <td>${rule.triggerLeadStatus || 'N/A'}</td>
1559                    <td>${templateName}</td>
1560                    <td>
1561                        <div class="form-check form-switch">
1562                            <input class="form-check-input toggle-rule-active" type="checkbox" role="switch"
1563                                   id="ruleSwitch-${rule.id}" data-rule-id="${rule.id}" ${rule.active ? 'checked' : ''} />
1564                            <label class="form-check-label" for="ruleSwitch-${rule.id}">${rule.active ? t('Aktiv') : t('Inaktiv')}</label>
1565                        </div>
1566                    </td>
1567                    <td>
1568                        <button class="btn btn-outline-danger btn-sm delete-rule" data-rule-id="${rule.id}" title="Regel löschen">
1569                            <i class="fas fa-trash"></i>
1570                        </button>
1571                    </td>
1572                </tr>
1573            `;
1574        }).join('');
1575    }
1576
1577    // NEU: Handler zum Speichern einer neuen Regel
1578    async handleSaveAutoCallRule(e) {
1579        e.preventDefault();
1580        const form = e.target;
1581        const submitButton = form.querySelector('button[type="submit"]');
1582        if (!submitButton) return; // Sicherstellen, dass der Button existiert
1583        const originalButtonText = submitButton.innerHTML;
1584        submitButton.disabled = true;
1585        submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Speichern...';
1586
1587        try {
1588            const user = auth.currentUser;
1589            if (!user) throw new Error("Benutzer nicht angemeldet.");
1590
1591            // Stelle sicher, dass userDoc geladen ist
1592            if (!this.userDoc) {
1593                 console.warn("UserDoc nicht gefunden in handleSaveAutoCallRule, versuche neu zu laden.");
1594                                  const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1595                 if (userDocSnap.exists()) this.userDoc = userDocSnap.data();
1596                 else throw new Error("Benutzerprofil nicht gefunden.");
1597            }
1598
1599            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
1600            if (!tenantId) throw new Error("Keine Tenant-ID gefunden.");
1601
1602            const leadStatusSelect = document.getElementById('ruleLeadStatus');
1603            const templateSelect = document.getElementById('ruleTemplate');
1604            const activeCheckbox = document.getElementById('ruleActive');
1605
1606            // Prüfe, ob die Elemente gefunden wurden
1607            if (!leadStatusSelect || !templateSelect || !activeCheckbox) {
1608                 throw new Error("Formular-Elemente für neue Regel nicht gefunden.");
1609            }
1610
1611            const leadStatus = leadStatusSelect.value;
1612            const templateId = templateSelect.value;
1613            const active = activeCheckbox.checked;
1614
1615            if (!leadStatus || !templateId) {
1616                UI.showWarning("Bitte wählen Sie einen Status und eine Vorlage aus."); // Geändert zu Warning
1617                return; // Frühzeitiger Ausstieg
1618            }
1619
1620            // Prüfen, ob Regel für diesen Status schon existiert
1621            const existingRule = this.autoCallRules?.find(r => r.triggerLeadStatus === leadStatus);
1622            if (existingRule) {
1623                // Hinweis geben, ohne zu blockieren oder zu fragen
1624                 UI.showWarning(`Es existiert bereits eine Regel für den Status '${leadStatus}'. Sie wird überschrieben.`);
1625                 // Lösche die alte Regel, bevor die neue hinzugefügt wird
1626                 /* phase8c: backend overwrites by status */
1627            }
1628
1629/* phase8c */ await AutoCallRulesApi.create({ triggerLeadStatus: leadStatus, templateId: templateId, active: active });
1630
1631            UI.showSuccess("AutoCall Regel erfolgreich gespeichert.");
1632            form.reset(); // Formular zurücksetzen
1633            activeCheckbox.checked = true; // Checkbox standardmäßig aktivieren
1634
1635            // Regeln neu laden und Tabelle aktualisieren
1636            await this.loadAutoCallRules(); // Dies rendert die Tabelle neu
1637
1638        } catch (error) {
1639            console.error("Fehler beim Speichern der AutoCall Regel:", error);
1640            UI.showError("Fehler beim Speichern der Regel: " + error.message);
1641        } finally {
1642             submitButton.disabled = false;
1643             submitButton.innerHTML = originalButtonText;
1644        }
1645    }
1646
1647     // NEU: Handler zum Aktivieren/Deaktivieren einer Regel
1648    async toggleAutoCallRuleActive(ruleId, newActiveState) {
1649            // console.log(`Toggle rule ${ruleId} to active: ${newActiveState}`);
1650        const label = document.querySelector(`label[for="ruleSwitch-${ruleId}"]`);
1651        const checkbox = document.getElementById(`ruleSwitch-${ruleId}`);
1652        if (label) label.textContent = newActiveState ? 'Aktiviere...' : 'Deaktiviere...';
1653        if (checkbox) checkbox.disabled = true; // Deaktivieren während der Aktualisierung
1654
1655        try {
1656             const user = auth.currentUser;
1657            if (!user) throw new Error("Benutzer nicht angemeldet.");
1658
1659            // Stelle sicher, dass userDoc geladen ist
1660            if (!this.userDoc) {
1661                 console.warn("UserDoc nicht gefunden in toggleAutoCallRuleActive, versuche neu zu laden.");
1662                                  const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1663                 if (userDocSnap.exists()) this.userDoc = userDocSnap.data();
1664                 else throw new Error("Benutzerprofil nicht gefunden.");
1665            }
1666
1667            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
1668            if (!tenantId) throw new Error("Keine Tenant-ID gefunden.");
1669
1670             /* phase8c */ await AutoCallRulesApi.update(ruleId, { active: newActiveState });
1671
1672             UI.showSuccess(`Regel ${newActiveState ? 'aktiviert' : 'deaktiviert'}.`);
1673
1674             // Update lokalen Cache und UI direkt, anstatt alles neu zu laden
1675             const ruleIndex = this.autoCallRules.findIndex(rule => rule.id === ruleId);
1676             if (ruleIndex > -1) {
1677                 this.autoCallRules[ruleIndex].active = newActiveState;
1678             }
1679             if (label) label.textContent = newActiveState ? 'Aktiv' : 'Inaktiv';
1680
1681
1682             // Alternativ: Neu laden und rendern (wie vorher)
1683             // await this.loadAutoCallRules();
1684
1685        } catch (error) {
1686             console.error("Fehler beim Ändern des Regelstatus:", error);
1687             UI.showError("Fehler beim Ändern des Status.");
1688             // Status im UI zurücksetzen bei Fehler
1689             if (checkbox) checkbox.checked = !newActiveState; // Alten Zustand wiederherstellen
1690              if(label) label.textContent = !newActiveState ? 'Aktiv' : 'Inaktiv'; // Alten Text wiederherstellen
1691        } finally {
1692             if (checkbox) checkbox.disabled = false; // Checkbox wieder aktivieren
1693        }
1694    }
1695
1696    // NEU: Handler zum Löschen einer Regel
1697    async deleteAutoCallRule(ruleId) {
1698        // Verwende ein benutzerdefiniertes Bestätigungsmodal oder die Standard-Confirm-Box
1699        // Beispiel mit standard confirm:
1700        if (!confirm(t('Möchten Sie diese AutoCall Regel wirklich löschen?'))) {
1701            return;
1702        }
1703
1704         // Optional: Zeige einen Ladezustand oder deaktiviere den Button
1705         const deleteButton = document.querySelector(`.delete-rule[data-rule-id="${ruleId}"]`);
1706         if (deleteButton) deleteButton.disabled = true;
1707
1708        try {
1709            const user = auth.currentUser;
1710            if (!user) throw new Error("Benutzer nicht angemeldet.");
1711
1712             // Stelle sicher, dass userDoc geladen ist
1713            if (!this.userDoc) {
1714                 console.warn("UserDoc nicht gefunden in deleteAutoCallRule, versuche neu zu laden.");
1715                                  const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
1716                 if (userDocSnap.exists()) this.userDoc = userDocSnap.data();
1717                 else throw new Error("Benutzerprofil nicht gefunden.");
1718            }
1719
1720            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
1721            if (!tenantId) throw new Error("Keine Tenant-ID gefunden.");
1722
1723/* phase8c */ await AutoCallRulesApi.delete(ruleId);
1724
1725            UI.showSuccess("AutoCall Regel gelöscht.");
1726
1727             // Update lokalen Cache und UI direkt
1728             this.autoCallRules = this.autoCallRules.filter(rule => rule.id !== ruleId);
1729             this.renderAutoCallRulesTable(this.autoCallRules); // Tabelle neu rendern
1730
1731             // Alternativ: Neu laden und rendern (wie vorher)
1732             // await this.loadAutoCallRules();
1733
1734        } catch (error) {
1735            console.error("Fehler beim Löschen der AutoCall Regel:", error);
1736            UI.showError("Fehler beim Löschen der Regel.");
1737             // Optional: Button wieder aktivieren bei Fehler
1738             if (deleteButton) deleteButton.disabled = false;
1739        }
1740    }
1741
1742    async initializeSmartLists() {
1743        // Smart List Modal initialisieren
1744        const modalElement = document.getElementById('smartListModal');
1745        if (modalElement && !this.smartListModal) {
1746            this.smartListModal = new bootstrap.Modal(modalElement);
1747            
1748            if (!this.smartListEventsBound) {
1749                // Event Listener für das Formular
1750                const form = document.getElementById('smartListForm');
1751                if (form) {
1752                    // Stelle sicher, dass die Ort-Option im Filter-Feld vorhanden ist
1753                    const filterFieldSelects = form.querySelectorAll('.filter-field');
1754                    filterFieldSelects.forEach(select => {
1755                        if (!select.querySelector('option[value="city"]')) {
1756                            const cityOption = document.createElement('option');
1757                            cityOption.value = 'city';
1758                            cityOption.textContent = 'Ort';
1759                            select.appendChild(cityOption);
1760                        }
1761                    });
1762                    
1763                    // Entferne vorherige Event-Listener
1764                    const newForm = form.cloneNode(true);
1765                    form.parentNode.replaceChild(newForm, form);
1766                    newForm.addEventListener('submit', (e) => this.handleSmartListSubmit(e));
1767
1768                    // Initialisiere die erste Filter-Zeile
1769                    const firstRow = newForm.querySelector('.filter-row');
1770                    if (firstRow) {
1771                        this.initializeFilterRow(firstRow);
1772                    }
1773                }
1774                
1775                // Event Listener für Filter hinzufügen/entfernen
1776                const addFilterBtn = document.getElementById('addFilterBtn');
1777                if (addFilterBtn) {
1778                    // Entferne vorherige Event-Listener
1779                    const newBtn = addFilterBtn.cloneNode(true);
1780                    addFilterBtn.parentNode.replaceChild(newBtn, addFilterBtn);
1781                    newBtn.addEventListener('click', () => this.addFilterRow());
1782                }
1783
1784                const filterContainer = document.getElementById('filterContainer');
1785                if (filterContainer) {
1786                    // Entferne vorherige Event-Listener
1787                    const newContainer = filterContainer.cloneNode(true);
1788                    filterContainer.parentNode.replaceChild(newContainer, filterContainer);
1789                    newContainer.addEventListener('click', (e) => {
1790                        if (e.target.closest('.remove-filter')) {
1791                            e.target.closest('.filter-row').remove();
1792                        }
1793                    });
1794                }
1795
1796                // Event Listener für Modal-Events
1797                modalElement.addEventListener('hidden.bs.modal', () => {
1798                    // Entferne die Backdrop manuell
1799                    const backdrop = document.querySelector('.modal-backdrop');
1800                    if (backdrop) {
1801                        backdrop.remove();
1802                    }
1803                    // Entferne die Modal-Klassen vom Body
1804                    document.body.classList.remove('modal-open');
1805                    document.body.style.removeProperty('padding-right');
1806                });
1807
1808                this.smartListEventsBound = true;
1809            }
1810        }
1811
1812        // Lade gespeicherte Smart Lists
1813        await this.loadSmartLists(false); // Hier UI-Update unterdrücken
1814    }
1815
1816    async loadSmartLists(updateUI = true) { // Parameter hinzugefügt
1817        try {
1818            const user = auth.currentUser;
1819            if (!user) return;
1820
1821/* phase8c */ this.smartLists = await SmartListsApi.list(true);
1822            // console.log("[loadSmartLists] Smart lists loaded:", this.smartLists); // DEBUG LOG
1823
1824            if (updateUI) { // UI nur aktualisieren, wenn explizit gewünscht
1825                this.updateSmartListsUI();
1826            }
1827        } catch (error) {
1828            console.error('Error loading smart lists:', error);
1829            UI.showError(t('Fehler beim Laden der Smart-Listen'));
1830        }
1831    }
1832
1833    updateSmartListsUI() {
1834            // console.log("[updateSmartListsUI] Updating UI for smart lists."); // DEBUG LOG
1835        const container = document.getElementById('smart-lists-placeholder'); // Ziel ist jetzt der Platzhalter
1836        if (!container) {
1837            console.error("[updateSmartListsUI] Placeholder container not found!"); // DEBUG LOG
1838            return;
1839        }
1840
1841        const smartListsDropdown = document.createElement('div');
1842        smartListsDropdown.className = 'smart-lists-dropdown dropdown mt-3'; // Füge Bootstrap Dropdown Klassen hinzu
1843
1844        // Dropdown-Button
1845        const dropdownButton = document.createElement('button');
1846        dropdownButton.className = 'btn btn-outline-primary btn-sm dropdown-toggle';
1847        dropdownButton.type = 'button';
1848        dropdownButton.id = 'smartListsDropdownMenuButton';
1849        dropdownButton.setAttribute('data-bs-toggle', 'dropdown');
1850        dropdownButton.setAttribute('aria-expanded', 'false');
1851        dropdownButton.textContent = t('Smart-Listen') + ' ';
1852
1853        // Dropdown-Menü
1854        const dropdownMenu = document.createElement('ul');
1855        dropdownMenu.className = 'dropdown-menu';
1856        dropdownMenu.setAttribute('aria-labelledby', 'smartListsDropdownMenuButton');
1857
1858        // Erstelle Dropdown-Items für jede Liste
1859        if (this.smartLists.length > 0) {
1860            this.smartLists.forEach(list => {
1861                const listItem = document.createElement('li');
1862                listItem.className = 'd-flex justify-content-between align-items-center dropdown-item-container'; // Eigene Klasse für Styling
1863
1864                const listLink = document.createElement('a');
1865                listLink.className = 'dropdown-item smart-list-link';
1866                listLink.href = '#';
1867                listLink.dataset.listId = list.id;
1868                listLink.innerHTML = `
1869                    ${list.name}
1870                `;
1871
1872                const deleteButton = document.createElement('button');
1873                deleteButton.className = 'btn btn-outline-danger btn-sm delete-smart-list ms-2'; // Kleinere Größe und Abstand
1874                deleteButton.dataset.listId = list.id;
1875                deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
1876                deleteButton.style.padding = '0.1rem 0.3rem'; // Padding anpassen für kleines Icon
1877                deleteButton.style.lineHeight = '1'; // Zeilenhöhe anpassen
1878
1879                listItem.appendChild(listLink);
1880                listItem.appendChild(deleteButton);
1881                dropdownMenu.appendChild(listItem);
1882            });
1883        } else {
1884            dropdownMenu.innerHTML = `<li><span class="dropdown-item disabled">${t('Keine Smart-Listen vorhanden')}</span></li>`;
1885        }
1886
1887        smartListsDropdown.appendChild(dropdownButton);
1888        smartListsDropdown.appendChild(dropdownMenu);
1889
1890        // Ersetze existierende Smart-Listen oder füge neue hinzu
1891        const existingSmartLists = container.querySelector('.smart-lists-dropdown, .smart-lists'); // Auch alte Klasse suchen
1892        if (existingSmartLists) {
1893            existingSmartLists.replaceWith(smartListsDropdown);
1894        } else {
1895            container.appendChild(smartListsDropdown);
1896        }
1897
1898        // === NEU: Event Delegation über document.body ===
1899            // console.log("[updateSmartListsUI] Attaching delegated listener to document.body for smart lists.");
1900
1901        // Entferne alten Listener, falls vorhanden
1902        if (this.smartListBodyClickListener) {
1903            // console.log("[updateSmartListsUI] Removing existing body listener.");
1904            document.body.removeEventListener('click', this.smartListBodyClickListener);
1905            this.smartListBodyClickListener = null;
1906        }
1907
1908        // Definiere die Listener-Funktion und speichere sie
1909        this.smartListBodyClickListener = (e) => {
1910            const placeholder = document.getElementById('smart-lists-placeholder');
1911
1912            // Nur fortfahren, wenn der Klick im Placeholder stattfindet
1913            if (!placeholder || !placeholder.contains(e.target)) {
1914                return;
1915            }
1916
1917            // console.log("[updateSmartListsUI] Delegated BODY click detected inside placeholder.");
1918
1919            const link = e.target.closest('.smart-list-link');
1920            const deleteBtn = e.target.closest('.delete-smart-list');
1921
1922            if (link) {
1923                e.preventDefault();
1924                const listId = link.dataset.listId;
1925            // console.log(`[updateSmartListsUI] Delegated BODY click matched smart list link: ${listId}`);
1926                // Rufe applySmartList erst nach kurzer Verzögerung auf, um sicherzustellen, dass andere UI-Updates abgeschlossen sind
1927                setTimeout(() => this.applySmartList(listId), 0);
1928            } else if (deleteBtn) {
1929                e.stopPropagation(); // Verhindert Schließen des Dropdowns
1930                const listId = deleteBtn.dataset.listId;
1931            // console.log(`[updateSmartListsUI] Delegated BODY click matched delete button: ${listId}`);
1932                 // Rufe deleteSmartList erst nach kurzer Verzögerung auf
1933                setTimeout(() => this.deleteSmartList(e, listId), 0);
1934            } else {
1935            // console.log("[updateSmartListsUI] Delegated BODY click did not match link or delete button within placeholder.");
1936            }
1937        };
1938
1939        // Hänge den neuen Listener an body an
1940        document.body.addEventListener('click', this.smartListBodyClickListener);
1941            // console.log("[updateSmartListsUI] Attached new body listener.");
1942        // === Event Delegation ENDE ===
1943
1944        /* === ALT: Listener am Dropdown-Container (auskommentiert) ===
1945        // const newDropdown = smartListsDropdown.cloneNode(true);
1946        // smartListsDropdown.parentNode.replaceChild(newDropdown, smartListsDropdown);
1947        // console.log("[updateSmartListsUI] Adding delegated click listener to dropdown container.");
1948        // newDropdown.addEventListener('click', (e) => { ... });
1949        */
1950
1951        /* === ALT: Individuelle Listener (auskommentiert/entfernt) === ... */
1952
1953        // Style für den Container der Dropdown-Items
1954        const style = document.createElement('style');
1955        style.textContent = `
1956            /* Stelle sicher, dass geöffnete Dropdown-Menüs einen hohen z-index haben */
1957            .dropdown-menu.show {
1958                z-index: 1060 !important; /* Erzwinge hohen z-index */
1959            }
1960            .dropdown-item-container {
1961                padding: 0.25rem 1rem; /* Standard padding für Dropdown-Items */
1962            }
1963            .dropdown-item-container .smart-list-link {
1964                flex-grow: 1; /* Link nimmt verfügbaren Platz ein */
1965                white-space: normal; /* Erlaube Umbruch für längere Namen */
1966                padding: 0; /* Kein extra Padding für den Link selbst */
1967                background-color: transparent !important; /* Kein Hover-Effekt vom Dropdown-Item */
1968                color: inherit !important; /* Standardtextfarbe erben */
1969            }
1970             .dropdown-item-container .smart-list-link:hover,
1971             .dropdown-item-container .smart-list-link:focus {
1972                 text-decoration: underline; /* Unterstreichen bei Hover/Fokus */
1973             }
1974
1975            .dropdown-item-container .delete-smart-list {
1976                 flex-shrink: 0; /* Button schrumpft nicht */
1977            }
1978        `;
1979        // Append style only once to the head
1980        if (!document.getElementById('smartlist-dropdown-style')) {
1981            style.id = 'smartlist-dropdown-style';
1982            document.head.appendChild(style);
1983        }
1984    }
1985
1986    async deleteSmartList(e, listId) {
1987        e.stopPropagation(); // Verhindert das Triggern des Filter-Events
1988        
1989        try {
1990            const user = auth.currentUser;
1991            if (!user) return;
1992
1993            // Bestätigungsdialog
1994            if (!confirm(t('Möchten Sie diese Smart-Liste wirklich löschen?'))) {
1995                return;
1996            }
1997
1998            // Lösche die Smart-Liste aus Firestore
1999/* phase8c */ await SmartListsApi.delete(listId);
2000
2001            // Aktualisiere die UI
2002            await this.loadSmartLists();
2003            
2004            UI.showSuccess('Smart-Liste wurde gelöscht');
2005        } catch (error) {
2006            console.error('Error deleting smart list:', error);
2007            UI.showError('Fehler beim Löschen der Smart-Liste');
2008        }
2009    }
2010
2011    async loadAllContactsInBackground() {
2012        // Lade alle restlichen Kontakte im Hintergrund für vollständige Suche
2013        if (this.backgroundLoadingComplete || !this.lastVisible) {
2014            return;
2015        }
2016
2017            // console.log('Starte Hintergrund-Laden aller Kontakte...');
2018        
2019        try {
2020            const user = auth.currentUser;
2021            if (!user) return;
2022
2023            const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
2024            const tenantId = userDoc.data()?.tenantID || userDoc.data()?.tenant_id;
2025            
2026            let currentLastVisible = this.lastVisible;
2027            let hasMoreToLoad = true;
2028            let totalLoaded = this.contacts.length;
2029
2030            while (hasMoreToLoad) {
2031                /* phase8c bg-pagination noop */ const snapshot = { empty: true, forEach: () => {}, docs: [], size: 0 };
2032                
2033                if (snapshot.empty) {
2034                    hasMoreToLoad = false;
2035                    this.backgroundLoadingComplete = true;
2036                    this.allContactsLoaded = true;
2037            // console.log(`Hintergrund-Laden abgeschlossen. Total: ${this.contacts.length} Kontakte`);
2038                    break;
2039                }
2040
2041                const newContacts = [];
2042                snapshot.forEach(doc => {
2043                    const contact = { id: doc.id, ...doc.data() };
2044                    if (!this.contacts.find(c =>
2044 c.id === contact.id)) {
2045                        newContacts.push(contact);
2046                    }
2047                });
2048
2049                // Füge neue Kontakte zur Liste hinzu (ohne zu rendern)
2050                this.contacts = [...this.contacts, ...newContacts];
2051                totalLoaded += newContacts.length;
2052                
2053                currentLastVisible = snapshot.docs[snapshot.docs.length - 1];
2054                hasMoreToLoad = snapshot.docs.length === 500;
2055                
2056                // Cache aktualisieren
2057                const cacheKey = `contacts_${user.uid}_${tenantId}`;
2058                try {
2059                    localStorage.setItem(cacheKey, JSON.stringify({
2060                        timestamp: Date.now(),
2061                        data: this.contacts
2062                    }));
2063                } catch (e) {
2064                    console.warn('Cache-Speichern fehlgeschlagen:', e);
2065                }
2066                
2067            // console.log(`Hintergrund: ${totalLoaded} Kontakte geladen...`);
2068                
2069                // Kleine Pause zwischen Batches
2070                await new Promise(resolve => setTimeout(resolve, 100));
2071            }
2072            
2073        } catch (error) {
2074            console.error('Fehler beim Hintergrund-Laden:', error);
2075        }
2076    }
2077
2078    async loadMoreContacts() {
2079        if (this.isLoadingMore || !this.hasMore || !this.lastVisible) {
2080            // console.log('LoadMore abgebrochen: isLoading=', this.isLoadingMore, 'hasMore=', this.hasMore);
2081            return;
2082        }
2083
2084        this.isLoadingMore = true;
2085            // console.log('Lade weitere Kontakte...');
2086        
2087        // Zeige Loading Indicator
2088        const loadIndicator = document.getElementById('loadMoreIndicator');
2089        if (loadIndicator) {
2090            loadIndicator.style.display = 'block';
2091        }
2092        
2093        // Für Mobile: Zeige auch einen Hinweis in der Pagination
2094        const isMobile = window.innerWidth <= 768;
2095        if (isMobile) {
2096            const paginationInfo = document.querySelector('.mobile-pagination-info');
2097            if (paginationInfo) {
2098                paginationInfo.textContent = t('Lädt...');
2099            }
2100        }
2101
2102        try {
2103            const user = auth.currentUser;
2104            if (!user) return;
2105
2106            const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
2107            const tenantId = userDoc.data()?.tenantID || userDoc.data()?.tenant_id;
2108
2109            /* phase8c loadMore noop */ const snapshot = { empty: true, forEach: () => {}, docs: [], size: 0 };
2110            
2111            if (snapshot.empty) {
2112                this.hasMore = false;
2113            // console.log('Keine weiteren Kontakte verfügbar');
2114                this.showEndOfListIndicator();
2115                return;
2116            }
2117
2118            const newContacts = [];
2119            snapshot.forEach(doc => {
2120                newContacts.push({ id: doc.id, ...doc.data() });
2121            });
2122
2123            // Füge neue Kontakte zur Liste hinzu
2124            this.contacts = [...this.contacts, ...newContacts];
2125            
2126            // Update last visible
2127            this.lastVisible = snapshot.docs[snapshot.docs.length - 1];
2128            this.hasMore = snapshot.docs.length === 50; // Prüfe ob es noch mehr gibt
2129
2130            // Rendere nur die neuen Kontakte (append)
2131            this.appendContactsToTable(newContacts);
2132            
2133            // console.log(`${newContacts.length} weitere Kontakte geladen. Total: ${this.contacts.length}`);
2134
2135        } catch (error) {
2136            console.error('Fehler beim Laden weiterer Kontakte:', error);
2137        } finally {
2138            this.isLoadingMore = false;
2139            // Verstecke Loading Indicator
2140            const loadIndicator = document.getElementById('loadMoreIndicator');
2141            if (loadIndicator) {
2142                loadIndicator.style.display = 'none';
2143            }
2144        }
2145    }
2146
2147    appendContactsToTable(newContacts) {
2148        const tbody = document.getElementById('contactsTableBody');
2149        if (!tbody) return;
2150
2151        // Verwende DocumentFragment für bessere Performance
2152        const fragment = document.createDocumentFragment();
2153        
2154        /* contacts-list-v1: EINE Quelle fuer die Zeilen-Darstellung. Vorher baute dieser
2155           Pfad eigenes HTML mit nur 6 von 9 Zellen - Nachladen erzeugte kaputte Zeilen. */
2156        const wrap = document.createElement('tbody');
2157        wrap.innerHTML = newContacts.map(contact => this.renderContactRow(contact)).join('');
2158        while (wrap.firstElementChild) fragment.appendChild(wrap.firstElementChild);
2159
2160        tbody.appendChild(fragment);
2161        
2162        // Re-bind Event-Listener für neue Zeilen
2163        this.bindNewRowEventListeners();
2164    }
2165
2166    setupInfiniteScroll() {
2167        // Cleanup previous observer
2168        if (this.scrollObserver) {
2169            this.scrollObserver.disconnect();
2170        }
2171        
2172        const isMobile = window.innerWidth <= 768;
2173        
2174        // Für Mobile nutze einen einfacheren Scroll-Ansatz
2175        if (isMobile) {
2176            // Entferne vorherige Scroll-Listener
2177            if (this.scrollHandler) {
2178                window.removeEventListener('scroll', this.scrollHandler);
2179            }
2180            
2181            let isScrolling = false;
2182            this.scrollHandler = () => {
2183                if (isScrolling) return;
2184                
2185                // Prüfe ob wir am Ende der Seite sind
2186                const scrollPosition = window.innerHeight + window.scrollY;
2187                const threshold = document.body.offsetHeight - 300; // 300px vor dem Ende
2188                
2189                if (scrollPosition >= threshold && this.hasMore && !this.isLoadingMore) {
2190                    isScrolling = true;
2191                    // console.log('Mobile: Triggering load more contacts');
2192                    this.loadMoreContacts().then(() => {
2193                        isScrolling = false;
2194                    });
2195                }
2196            };
2197            
2198            // Throttle scroll events für bessere Performance
2199            let scrollTimeout;
2200            const throttledScrollHandler = () => {
2201                if (!scrollTimeout) {
2202                    scrollTimeout = setTimeout(() => {
2203                        scrollTimeout = null;
2204                        this.scrollHandler();
2205                    }, 150); // 150ms throttle
2206                }
2207            };
2208            
2209            window.addEventListener('scroll', throttledScrollHandler, { passive: true });
2210            
2211        } else {
2212            // Desktop: Verwende Intersection Observer
2213            const sentinel = document.createElement('div');
2214            sentinel.id = 'scrollSentinel';
2215            sentinel.style.height = '1px';
2216            
2217            const container = document.getElementById('contactsContainer');
2218            if (container) {
2219                container.appendChild(sentinel);
2220            }
2221
2222            // Setup Intersection Observer
2223            const options = {
2224                root: null,
2225                rootMargin: '200px',
2226                threshold: 0.1
2227            };
2228
2229            this.scrollObserver = new IntersectionObserver((entries) => {
2230                entries.forEach(entry => {
2231                    if (entry.isIntersecting && this.hasMore && !this.isLoadingMore) {
2232                        this.loadMoreContacts();
2233                    }
2234                });
2235            }, options);
2236
2237            if (sentinel) {
2238                this.scrollObserver.observe(sentinel);
2239            }
2240        }
2241    }
2242
2243    showEndOfListIndicator() {
2244        const container = document.getElementById('contactsContainer');
2245        if (container) {
2246            const indicator = document.createElement('div');
2247            indicator.className = 'text-center py-3 text-muted';
2248            indicator.innerHTML = t('Alle Kontakte geladen');
2249            container.appendChild(indicator);
2250        }
2251    }
2252
2253    getLeadStatusColor(status) {
2254        const colors = {
2255            'Neu': 'primary',
2256            'Kontaktiert': 'info',
2257            'Qualifiziert': 'warning',
2258            'Angebot': 'secondary',
2259            'Gewonnen': 'success',
2260            'Verloren': 'danger'
2261        };
2262        return colors[status] || 'secondary';
2263    }
2264
2265    bindNewRowEventListeners() {
2266        // Binde Event-Listener nur für neue Zeilen
2267        const newRows = document.querySelectorAll('.contact-row:not([data-events-bound])');
2268        newRows.forEach(row => {
2269            row.addEventListener('click', async (e) => {
2270                if (e.target.closest('.checkbox-column')) return;
2271                
2272                e.preventDefault();
2273                const contactId = row.dataset.id;
2274                if (contactId) {
2275                    this.storeContactsState();
2276                    window.history.pushState({}, '', `?page=contact-detail&id=${contactId}`);
2277                    await Pages.loadPage('contact-detail');
2278                }
2279            });
2280            row.setAttribute('data-events-bound', 'true');
2281        });
2282    }
2283
2284    async loadContacts(forceRefresh = false) {
2285        // DEBOUNCE FIX: Wenn bereits ein Request läuft, gib das existierende Promise zurück
2286        if (this.isLoadingContacts && this.pendingLoadPromise && !forceRefresh) {
2287            console.log('⏭️ loadContacts bereits in Arbeit, warte auf existierenden Request...');
2288            return this.pendingLoadPromise;
2289        }
2290
2291        // Setze Loading-Flag
2292        this.isLoadingContacts = true;
2293
2294        // Erstelle ein Promise für diesen Request
2295        this.pendingLoadPromise = this._loadContactsInternal(forceRefresh);
2296
2297        try {
2298            const result = await this.pendingLoadPromise;
2299            return result;
2300        } finally {
2301            // Cleanup nach Abschluss
2302            this.isLoadingContacts = false;
2303            this.pendingLoadPromise = null;
2304        }
2305    }
2306
2307    async _loadContactsInternal(forceRefresh = false) {
2308        // showContactsLoadingIndicator(); // Ladeindikator anzeigen <-- Entfernt
2309            // console.log("Lade Kontakte...");
2310        // this.showLoadingIndicator('contacts'); // <-- Entfernt
2311
2312        try {
2313            const user = auth.currentUser;
2314            if (!user) {
2315                console.error('No user logged in');
2316                return;
2317            }
2318
2319            // console.log('Loading contacts for user:', user.uid);
2320            
2321            // Get user's tenant ID from their profile
2322            const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
2323            if (!userDoc.exists()) {
2324                console.error('User profile not found');
2325                return;
2326            }
2327            
2328            const userData = userDoc.data();
2329            const tenantId = userData.tenantID || userData.tenant_id;
2330            if (!tenantId) {
2331                UI.showError('No tenant ID found for user');
2332                console.error('No tenant ID found for user');
2333                return;
2334            }
2335
2336            // Prüfe Kontakt-Berechtigungen für Mitarbeiter
2337            const userRole = userData.role || 'user';
2338            const contactPermissions = userData.contact_permissions || {};
2339            const visibilityMode = contactPermissions.visibility_mode || 'all';
2340            const onlyOwnContacts = (userRole === 'employee' && visibilityMode === 'own');
2341            
2342            if (onlyOwnContacts) {
2343                console.log('[Contacts] Mitarbeiter mit nur eigene Kontakte - filtere nach uid:', user.uid);
2344            }
2345
2346            // console.log('Using tenant ID:', tenantId);
2347
2348            // Define fetch function for caching - Optimiert für minimale Daten
2349            const fetchContacts = async () => {
2350                const contactsRef = null; /* phase8c stub for dead code below */
2351                /* phase8c initial-load via API */ const _allC = await ContactsApi.list({ limit: 5000 }).catch(()=>[]); const snapshot = { forEach: (cb) => _allC.forEach(d => cb({ id: d.id, data: () => d })), docs: _allC.map(d => ({ id: d.id, data: () => d })), size: _allC.length, empty: _allC.length === 0 };
2352                    const contacts = [];
2353                    
2354                    // Speichere nur minimale Daten für die Liste
2355                    snapshot.forEach(doc => {
2356                        const fullData = doc.data();
2357                        
2358                        // Speichere vollständige Daten in separatem Cache für späteren Zugriff
2359                        this.fullContactsCache.set(doc.id, { id: doc.id, ...fullData });
2360                        
2361                        // Für die Liste nur benötigte Felder
2362                        contacts.push({
2363                            id: doc.id,
2364                            name: fullData.name || '',
2365                            email: fullData.email || '',
2366                            phone_number: fullData.phone_number || '',
2367                            company: fullData.company || '',
2368                            updated_at: fullData.updated_at,
2369                            lead_status: fullData.lead_status || '',
2370                            task: fullData.task || '',
2371                            tenantID: fullData.tenantID || '',
2372                            // Für Filter benötigt
2373                            industry: fullData.industry || '',
2374                            team: fullData.team || [], team_uids: fullData.team_uids || [], liste: fullData.liste || '', // kontakt-team-v1
2375                            created_at: fullData.created_at
2376                        });
2377                    });
2378                    
2379                    // Speichere last visible document für späteres Laden
2380                    if (snapshot.docs.length > 0) {
2381                        this.lastVisible = snapshot.docs[snapshot.docs.length - 1];
2382                        this.hasMore = snapshot.docs.length === 100;
2383                    } else {
2384                        this.hasMore = false;
2385                    }
2386                    
2387                    this.allContactsLoaded = !this.hasMore;
2388                    this.backgroundLoadingComplete = !this.hasMore;
2389                    
2390                    // console.log(`Mobile: ${contacts.length} Kontakte initial geladen`);
2391                    return contacts;
2392                
2393                if (false) {
2394                    // DESKTOP: dead code (phase8c — ContactsApi.list now returns full set)
2395                    let q;
2396                    if (onlyOwnContacts) {
2397                        // Mitarbeiter sieht nur eigene Kontakte
2398                        q = query(contactsRef, 
2399                            where('tenantID', '==', tenantId.toString()),
2400                            where('uid', '==', user.uid),
2401                            orderBy('updated_at', 'desc')
2402                        );
2403                    } else {
2404                        q = query(contactsRef, 
2405                            where('tenantID', '==', tenantId.toString()),
2406                            orderBy('updated_at', 'desc')
2407                        );
2408                    }
2409                    const snapshot = await getDocs(q);
2410                    const contacts = [];
2411                    
2412                    // Speichere nur minimale Daten für die Liste
2413                    snapshot.forEach(doc => {
2414                        const fullData = doc.data();
2415                        
2416                        // Speichere vollständige Daten in separatem Cache für späteren Zugriff
2417                        this.fullContactsCache.set(doc.id, { id: doc.id, ...fullData });
2418                        
2419                        // Für die Liste nur benötigte Felder
2420                        contacts.push({
2421                            id: doc.id,
2422                            name: fullData.name || '',
2423                            email: fullData.email || '',
2424                            phone_number: fullData.phone_number || '',
2425                            company: fullData.company || '',
2426                            updated_at: fullData.updated_at,
2427                            lead_status: fullData.lead_status || '',
2428                            task: fullData.task || '',
2429                            tenantID: fullData.tenantID || '',
2430                            // Für Filter benötigt
2431                            industry: fullData.industry || '',
2432                            team: fullData.team || [], team_uids: fullData.team_uids || [], liste: fullData.liste || '', // kontakt-team-v1
2433                            created_at: fullData.created_at
2434                        });
2435                    });
2436                    
2437                    this.allContactsLoaded = true;
2438                    this.backgroundLoadingComplete = true;
2439                    this.hasMore = false;
2440                    
2441                    // console.log(`Desktop: ${contacts.length} Kontakte vollständig geladen`);
2442                    return contacts;
2443                }
2444            };
2445
2446            // Get contacts with caching - Unterschiedlicher Cache für Mobile/Desktop
2447            const isMobile = window.innerWidth <= 768;
2448            const cacheKey = `contacts_${user.uid}_${tenantId}_${isMobile ? 'mobile' : 'desktop'}`;
2449            
2450            // Bei forceRefresh Cache komplett umgehen
2451            if (forceRefresh) {
2452                console.log('[loadContacts] forceRefresh aktiviert - lösche alle Caches');
2453                // Lösche alle Cache-Keys
2454                localStorage.removeItem(cacheKey);
2455                localStorage.removeItem(`contacts_${user.uid}_${tenantId}_desktop`);
2456                localStorage.removeItem(`contacts_${user.uid}_${tenantId}_mobile`);
2457                localStorage.removeItem(`contacts_${user.uid}_${tenantId}`);
2458                // Lösche auch den Memory-Cache
2459                if (this.fullContactsCache) {
2460                    this.fullContactsCache.clear();
2461                }
2462                console.log('[loadContacts] Lade Daten direkt von Firebase...');
2463                // Lade Daten direkt ohne Cache
2464                this.contacts = await fetchContacts();
2465                console.log(`[loadContacts] ${this.contacts.length} Kontakte von Firebase geladen`);
2466                
2467                // Debug: Zeige Lead-Status der ersten 5 Kontakte
2468                if (this.contacts.length > 0) {
2469                    console.log('[loadContacts] Lead-Status Beispiele:');
2470                    this.contacts.slice(0, 5).forEach(contact => {
2471                        console.log(`  - ${contact.name || contact.email}: ${contact.lead_status || 'KEIN STATUS'}`);
2472                    });
2473                }
2474                
2475                // Speichere die neuen Daten im Cache
2476                try {
2477                    localStorage.setItem(cacheKey, JSON.stringify({
2478                        timestamp: Date.now(),
2479                        data: this.contacts
2480                    }));
2481                } catch (e) {
2482                    console.warn('Fehler beim Speichern im Cache:', e);
2483                }
2484            } else {
2485                // Cache für eine Minute invalidieren um sicherzustellen dass alle Kontakte neu geladen werden
2486                const cached = localStorage.getItem(cacheKey);
2487                if (cached) {
2488                    try {
2489                        const parsed = JSON.parse(cached);
2490                        if (parsed.data && parsed.data.length < 50) {
2491                // console.log('Alter Cache mit nur', parsed.data.length, 'Kontakten gefunden - lösche Cache...');
2492                            localStorage.removeItem(cacheKey);
2493                        }
2494                    } catch (e) {
2495                        console.error('Fehler beim Prüfen des Cache:', e);
2496                    }
2497                }
2498                
2499                this.contacts = await getCachedData(cacheKey, fetchContacts, 15 * 60 * 1000); // 15 Minuten Cache
2500            }
2501
2502            // console.log('Loaded contacts count:', this.contacts.length);
2503            if (this.contacts.length > 0) {
2504            // console.log('Sample contact fields:', Object.keys(this.contacts[0]));
2505            // console.log('Sample contact lead status:', this.contacts[0].lead_status);
2506            }
2507            
2508            // Reset event binding state
2509            this.eventsBound = false;
2510            await this.renderContacts(this.contacts);
2511                 // await this.bindEvents(); // Entfernt
2512                 // this.setupContactEventListeners(); // Entfernt von hier
2513                 // this.updateSmartListsUI(); // Wird jetzt in renderContactsImmediate aufgerufen
2514            
2515            // Aktiviere Infinite Scrolling nach dem ersten Render
2516            // setTimeout(() => {
2517            //     this.setupInfiniteScroll();
2518            // }, 100);
2519            // Infinite Scrolling deaktiviert - alle Kontakte sind bereits geladen
2520            
2521            return this.contacts;
2522        } catch (error) {
2523            console.error('Error loading contacts:', error);
2524            UI.showError('Error loading contacts. Please try again.');
2525            // hideContactsLoadingIndicator(); // Ladeindikator deaktiviert
2526        } finally {
2527            // hideContactsLoadingIndicator(); // <-- Entfernt
2528            // this.hideLoadingIndicator('contacts'); // <-- Entfernt
2529            // console.log("Kontakte Ladevorgang abgeschlossen.");
2530        }
2531    }
2532
2533    /**
2534     * Lädt vollständige Kontaktdaten bei Bedarf (z.B. für Detailansicht)
2535     * Nutzt Cache falls vorhanden, sonst lädt von Firestore
2536     */
2537    async getFullContactData(contactId) {
2538        try {
2539            // Prüfe zuerst den Cache
2540            if (this.fullContactsCache.has(contactId)) {
2541            // console.log(`Vollständige Daten für Kontakt ${contactId} aus Cache geladen`);
2542                return this.fullContactsCache.get(contactId);
2543            }
2544            
2545            // Falls nicht im Cache, lade von Firestore
2546            // console.log(`Lade vollständige Daten für Kontakt ${contactId} von Firestore`);
2547            /* phase8: contacts/${contactId} via API */
2548            const __ctRes = await fetch(`/api/contacts/${contactId}`, { headers: { 'Authorization': 'Bearer ' + (await Auth.getIdToken()) } });
2549            const __ctData = __ctRes.ok ? await __ctRes.json() : null;
2550            const contactDoc = { exists: () => !!__ctData, data: () => __ctData, id: __ctData && __ctData.id };
2551            
2552            if (contactDoc.exists()) {
2553                const fullData = { id: contactDoc.id, ...contactDoc.data() };
2554                // Speichere im Cache für zukünftige Verwendung
2555                this.fullContactsCache.set(contactId, fullData);
2556                return fullData;
2557            } else {
2558                console.error(`Kontakt ${contactId} nicht gefunden`);
2559                return null;
2560            }
2561        } catch (error) {
2562            console.error(`Fehler beim Laden vollständiger Daten für Kontakt ${contactId}:`, error);
2563            return null;
2564        }
2565    }
2566
2567    setupContactEventListeners() {
2568        // Remove any existing event listeners first
2569        const buttons = [
2570            'newContactBtn',
2571            'importContactsBtn',
2572            'createSmartListBtn',
2573            'sendGroupEmailBtn',
2574            'newMultiCallBtn',
2575            'sofortAnrufBtn',
2576            'autoCallBtn'
2577        ];
2578        
2579        // Mobile Load More Button
2580        const mobileLoadMoreBtn = document.getElementById('mobileLoadMoreBtn');
2581        if (mobileLoadMoreBtn) {
2582            mobileLoadMoreBtn.addEventListener('click', async () => {
2583                mobileLoadMoreBtn.disabled = true;
2584                mobileLoadMoreBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>' + t('Lädt...');
2585                
2586                await this.loadMoreContacts();
2587                
2588                // Neu rendern nach dem Laden
2589                await this.renderContacts(this.contacts);
2590                this.setupContactEventListeners(); // Event-Listener neu binden
2591            });
2592        }
2593        
2594        // Setup Infinite Scroll für Mobile
2595        const isMobile = window.innerWidth <= 768;
2596        if (isMobile && this.hasMore) {
2597            this.setupInfiniteScroll();
2598        }
2599        
2600        buttons.forEach(btnId => {
2601            const btn = document.getElementById(btnId);
2602            if (btn) {
2603                // Clone and replace the button to remove old event listeners
2604                const newBtn = btn.cloneNode(true);
2605                btn.parentNode.replaceChild(newBtn, btn);
2606            }
2607        });
2608
2609        // Now add the new event listeners
2610        const newContactBtn = document.getElementById('newContactBtn');
2611        if (newContactBtn) {
2612            newContactBtn.addEventListener('click', () => this.showNewContactModal());
2613        }
2614
2615        const importBtn = document.getElementById('importContactsBtn');
2616        if (importBtn) {
2617            importBtn.addEventListener('click', (e) => {
2618                e.stopPropagation();
2619                this.contactImportUI.show();
2620            });
2621        }
2622
2623        const createSmartListBtn = document.getElementById('createSmartListBtn');
2624        if (createSmartListBtn) {
2625            createSmartListBtn.addEventListener('click', () => {
2626                // Übersetze das Modal vor dem Anzeigen
2627                this.translateSmartListModal();
2628                if (this.smartListModal) {
2629                    this.smartListModal.show();
2630                }
2631            });
2632        }
2633
2634        const sendGroupEmailBtn = document.getElementById('sendGroupEmailBtn');
2635        if (sendGroupEmailBtn) {
2636            sendGroupEmailBtn.addEventListener('click', () => this.handleSendGroupEmail());
2637        }
2638
2639        const newMultiCallBtn = document.getElementById('newMultiCallBtn');
2640        if (newMultiCallBtn) {
2641            // Clone button to remove old event listeners
2642            const newBtn = newMultiCallBtn.cloneNode(true);
2643            newMultiCallBtn.parentNode.replaceChild(newBtn, newMultiCallBtn);
2644            newBtn.addEventListener('click', () => {
2645            // console.log('MultiCall Button clicked!'); // Debug log
2646                this.handleSipOutboundCall();
2647            });
2648        }
2649
2650        const sofortAnrufBtn = document.getElementById('sofortAnrufBtn');
2651        if (sofortAnrufBtn) {
2652            sofortAnrufBtn.addEventListener('click', () => this.handleSofortAnruf());
2653        }
2654
2655        const autoCallBtn = document.getElementById('autoCallBtn');
2656        if (autoCallBtn) {
2657            autoCallBtn.addEventListener('click', () => this.handleAutoCall());
2658        }
2659
2660        // === HINZUGEFÜGT: Listener für Bulk Action Buttons ===
2661        const bulkUpdateBtn = document.getElementById('bulkUpdateBtn');
2662        if (bulkUpdateBtn) {
2663            // console.log("[setupContactEventListeners] Adding listener to #bulkUpdateBtn.");
2664            const newBulkUpdateBtn = bulkUpdateBtn.cloneNode(true);
2665            bulkUpdateBtn.parentNode.replaceChild(newBulkUpdateBtn, bulkUpdateBtn);
2666            newBulkUpdateBtn.addEventListener('click', () => this.handleBulkUpdate());
2667        } else {
2668             // Dieser Log sollte jetzt nicht mehr erscheinen, da der Button im gerenderten HTML ist
2669            console.warn("[setupContactEventListeners] #bulkUpdateBtn not found during setup.");
2670        }
2671
2672        const bulkDeleteBtn = document.getElementById('bulkDeleteBtn');
2673        if (bulkDeleteBtn) {
2674            // console.log("[setupContactEventListeners] Adding listener to #bulkDeleteBtn.");
2675            const newBulkDeleteBtn = bulkDeleteBtn.cloneNode(true);
2676            bulkDeleteBtn.parentNode.replaceChild(newBulkDeleteBtn, bulkDeleteBtn);
2677            newBulkDeleteBtn.addEventListener('click', () => this.handleBulkDelete());
2678        } else {
2679            console.warn("[setupContactEventListeners] #bulkDeleteBtn not found during setup.");
2680        }
2681        // === HINZUGEFÜGT ENDE ===
2682
2683        // Event-Listener für Zeilen-Klicks (ZUERST klonen, damit Delete-Buttons danach Handler bekommen)
2684        document.querySelectorAll('.contact-link').forEach(link => {
2685            const newLink = link.cloneNode(true);
2686            link.parentNode.replaceChild(newLink, link);
2687
2688            // Füge den Event-Listener zum neuen Element hinzu
2689            newLink.addEventListener('click', async (e) => {
2690                // Wenn auf die Checkbox oder Delete-Button geklickt wurde, nicht zur Detail-Ansicht navigieren
2691                if (e.target.closest('.checkbox-column') || e.target.closest('.delete-contact') || (e.target.closest('.contact-noclick') && !e.target.closest('.contact-open-item'))) {
2692                    return;
2693                }
2694
2695                e.preventDefault();
2696                const contactId = e.currentTarget.dataset.id;
2697                if (contactId) {
2698                    // Speichere den aktuellen Zustand vor der Navigation
2699            // console.log('[setupContactEventListeners] Speichere State vor Navigation zu Kontakt:', contactId);
2700                    this.storeContactsState();
2701
2702                    // Update URL without page reload
2703                    window.history.pushState({}, '', `?page=contact-detail&id=${contactId}`);
2704                    // Load the contact detail page
2705                    await Pages.loadPage('contact-detail');
2706                }
2707            });
2708        });
2709
2710        // === Delete-Buttons in jeder Zeile (NACH dem Zeilen-Klonen, damit Handler erhalten bleiben) ===
2711        document.querySelectorAll('.delete-contact').forEach(button => {
2712            button.addEventListener('click', async (e) => {
2713                e.stopPropagation();
2714                e.preventDefault();
2715                const contactId = button.dataset.id;
2716                if (confirm(t('Sind Sie sicher, dass Sie diesen Kontakt löschen möchten?'))) {
2717                    await this.deleteContact(contactId);
2718                }
2719            });
2720        });
2721
2722        // Event-Listener für Checkboxen
2723        document.querySelectorAll('.contact-checkbox').forEach(checkbox => {
2724            const newCheckbox = checkbox.cloneNode(true);
2725            checkbox.parentNode.replaceChild(newCheckbox, checkbox);
2726            
2727            // Checkbox-Event separat behandeln
2728            newCheckbox.addEventListener('change', (e) => {
2729                e.stopPropagation(); // Verhindert Bubble-up zum Row-Click
2730                this.updateBulkActionsVisibility();
2731            });
2732        });
2733
2734        // Event-Listener für "Alle auswählen" Dropdown
2735        const selectAll = document.getElementById('selectAll');
2736        if (selectAll) {
2737            // Verhindere das Standard-Checkbox-Verhalten beim Klick
2738            selectAll.addEventListener('click', (e) => {
2739                e.preventDefault();
2740            });
2741        }
2742        
2743        // Event-Listener für "Alle auf dieser Seite"
2744        const selectAllPage = document.getElementById('selectAllPage');
2745        if (selectAllPage) {
2746            selectAllPage.addEventListener('click', (e) => {
2747                e.preventDefault();
2748                // Nur sichtbare Checkboxen auf der aktuellen Seite auswählen
2749                document.querySelectorAll('.contact-row .contact-checkbox').forEach(checkbox => {
2750                    checkbox.checked = true;
2751                });
2752                // Schließe Dropdown
2753                const dropdown = bootstrap.Dropdown.getInstance(selectAll);
2754                if (dropdown) dropdown.hide();
2755                this.updateBulkActionsVisibility();
2756            });
2757        }
2758        
2759        // Event-Listener für "Alle gefilterten Kontakte"
2760        const selectAllFiltered = document.getElementById('selectAllFiltered');
2761        if (selectAllFiltered) {
2762            selectAllFiltered.addEventListener('click', async (e) => {
2763                e.preventDefault();
2764                
2765                // Check if currentFilteredContacts exists and has contacts
2766                if (!this.currentFilteredContacts || this.currentFilteredContacts.length === 0) {
2767                    UI.showInfo('Keine gefilterten Kontakte vorhanden');
2768                    return;
2769                }
2770                
2771                // Bestätige bei großen Mengen
2772                const filteredCount = this.currentFilteredContacts.length;
2773                if (filteredCount > 100) {
2774                    if (!confirm(`${t('Möchten Sie wirklich alle')} ${filteredCount}
2774 ${t('gefilterten Kontakte auswählen?')}`)) {
2775                        return;
2776                    }
2777                }
2778                
2779                // Markiere alle gefilterten Kontakte als ausgewählt
2780                this.allFilteredContactsSelected = true;
2781                this.selectedFilteredContactIds = this.currentFilteredContacts.map(c => c.id);
2782                
2783                // Wähle alle sichtbaren Checkboxen aus
2784                document.querySelectorAll('.contact-row .contact-checkbox').forEach(checkbox => {
2785                    checkbox.checked = true;
2786                });
2787                
2788                // Zeige Hinweis
2789                UI.showInfo(`${filteredCount} gefilterte Kontakte ausgewählt`);
2790                
2791                // Schließe Dropdown
2792                const dropdown = bootstrap.Dropdown.getInstance(selectAll);
2793                if (dropdown) dropdown.hide();
2794                
2795                this.updateBulkActionsVisibility();
2796            });
2797        }
2798
2799        // Event-Listener für mobilen "Alle auswählen"-Button
2800        const selectAllMobileBtn = document.getElementById('selectAllMobile');
2801        if (selectAllMobileBtn) {
2802            // Entferne alte Listener
2803            const newSelectAllMobileBtn = selectAllMobileBtn.cloneNode(true);
2804            selectAllMobileBtn.parentNode.replaceChild(newSelectAllMobileBtn, selectAllMobileBtn);
2805
2806            newSelectAllMobileBtn.addEventListener('click', (e) => {
2807                // Bestimme, ob alle bereits ausgewählt sind
2808                const allCheckboxes = document.querySelectorAll('.contact-checkbox');
2809                const checkedCheckboxes = document.querySelectorAll('.contact-checkbox:checked');
2810                const shouldSelectAll = checkedCheckboxes.length < allCheckboxes.length;
2811
2812                allCheckboxes.forEach(checkbox => {
2813                    checkbox.checked = shouldSelectAll;
2814                });
2815
2816                // Ändere den Button-Text entsprechend
2817                if (shouldSelectAll) {
2818                    newSelectAllMobileBtn.innerHTML = `<i class="fas fa-minus-square me-1"></i>${t('Alle abwählen')}`;
2819                } else {
2820                    newSelectAllMobileBtn.innerHTML = `<i class="fas fa-check-square me-1"></i>${t('Alle auswählen')}`;
2821                }
2822
2823                this.updateBulkActionsVisibility();
2824             });
2825        }
2826    }
2827
2828    debounceSearch(event) {
2829        // Clear the previous timeout
2830        if (this.searchTimeout) {
2831            clearTimeout(this.searchTimeout);
2832        }
2833
2834        // Set a new timeout
2835        this.searchTimeout = setTimeout(() => {
2836            this.applyFilters();
2837        }, 300); // Wait 300ms after the user stops typing
2838    }
2839
2840    async renderContacts(filteredContacts) {
2841        // Debounce rendering für bessere Performance
2842        if (this.renderDebounceTimer) {
2843            clearTimeout(this.renderDebounceTimer);
2844        }
2845        
2846        this.renderDebounceTimer = setTimeout(async () => {
2847            await this.renderContactsImmediate(filteredContacts);
2848        }, 50);
2849    }
2850    
2851    async renderContactsImmediate(filteredContacts) {
2852        // kontakt-team-v1: Filter/Knopf nachruesten; beim ersten Laden der
2853        // Teamdaten einmal neu zeichnen, damit die Zuweisungen in den Zeilen stehen.
2854        setTimeout(async () => {
2855            const vorher = !!this._kteamInfo;
2856            await this._kteamFilterEinbauen();
2857            if (!vorher && this._kteamInfo && (filteredContacts || []).some(c => Array.isArray(c.team) && c.team.length)) {
2858                this.renderContacts(filteredContacts);
2859            }
2860        }, 0);
2861        console.log('[renderContactsImmediate] Start');
2862        console.log('[renderContactsImmediate] Anzahl Kontakte zu rendern:', filteredContacts ? filteredContacts.length : 'keine');
2863        
2864        // Debug: Zeige Lead-Status der ersten 5 Kontakte
2865        if (filteredContacts && filteredContacts.length > 0) {
2866            console.log('[renderContactsImmediate] Lead-Status der ersten 5:');
2867            filteredContacts.slice(0, 5).forEach(contact => {
2868                console.log(`  - ${contact.name || contact.email}: ${contact.lead_status || 'KEIN STATUS'}`);
2869            });
2870        }
2871        
2872        // Prüfe, ob der Container existiert
2873        const contactsContainer = document.getElementById('contactsContainer');
2874        if (!contactsContainer) {
2875            console.error('[renderContactsImmediate] Container nicht gefunden!');
2876            return;
2877        }
2878        
2879        console.log('[renderContactsImmediate] Container gefunden');
2880
2881        // Stelle sicher, dass Lead-Status geladen sind
2882        await this.getLeadStatuses();
2883
2884        // Sonst normale Rendering-Logik...
2885        // ... vorhandener Code ...
2886        
2887        // Verwende ein leeres Array wenn keine Kontakte vorhanden sind,
2888        // damit die Tabelle trotzdem gerendert wird
2889        const contactsToRender = this.currentFilteredContacts || filteredContacts || [];
2890        
2891        // Calculate pagination
2892        const totalPages = Math.ceil(contactsToRender.length / this.itemsPerPage);
2893        const startIndex = (this.currentPage - 1) * this.itemsPerPage;
2894        const endIndex = startIndex + this.itemsPerPage;
2895        const contactsToShow = contactsToRender.slice(startIndex, endIndex);
2896
2897        let html = `
2898            <div class="container-fluid">
2899                <style id="contacts-list-v1-style">
2900                    .contact-avatar{display:inline-flex;align-items:center;justify-content:center;
2901                        width:34px;height:34px;min-width:34px;border-radius:50%;color:#fff;
2902                        font-size:.78rem;font-weight:600;letter-spacing:.02em;}
2903                    .contact-identity{display:flex;align-items:center;gap:.6rem;min-width:0;}
2904                    .contact-identity .contact-identity-text{min-width:0;}
2905                    .contact-name-text,.contact-subline{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
2906                    .contact-subline{font-size:.8rem;}
2907                    .contact-task-chip{font-size:.7rem;font-weight:500;}
2908                    /* contacts-menu-fix-v1: das Menue wird per JS auf position:fixed gesetzt; z-index muss ueber der Tabelle liegen. */
2909                    .contact-quick-actions .dropdown-menu{z-index:1080;}
2910                    /* contacts-menu-fix-v1b: styles.css:3841 legt auf JEDES tbody tr eine
2911                       Einblendung mit fill-mode "both" - dadurch bleibt dauerhaft ein
2912                       transform (Einheitsmatrix) an der Zeile stehen. Ein transformierter
2913                       Vorfahr wird zum Bezugsrahmen fuer position:fixed: das Menue war
2914                       dadurch WIEDER an die Zeile gebunden und wurde erneut abgeschnitten.
2915                       Nur fuer die Kontakttabelle abgeschaltet - nimmt nebenbei das
2916                       Flackern bei jedem Neuaufbau der Liste (Suche/Blaettern) weg. */
2917                    .contacts-table-wrap tbody tr{animation:none !important;transform:none !important;}
2918                    .contact-quick-actions{display:inline-flex;gap:.25rem;align-items:center;}
2919                    .contact-quick-actions .btn{--bs-btn-padding-y:.15rem;--bs-btn-padding-x:.45rem;}
2920                </style>
2921                <!-- Platzhalter für Smart-Listen Dropdown -->
2922                <div id="smart-lists-placeholder" class="mb-3"></div>
2923
2924                <div class="d-flex justify-content-between align-items-center mb-4">
2925                    <div class="d-flex flex-column flex-md-row gap-2 contact-actions-container">
2926                        <button type="button" class="btn btn-primary" id="newContactBtn">
2927                            <i class="fas fa-plus me-1"></i>${t('Neu')}
2928                        </button>
2929                        <button type="button" class="btn btn-outline-secondary d-md-none" id="selectAllMobile">
2930                            <i class="fas fa-check-square me-1"></i>${t('Alle auswählen')}
2931                        </button>
2932                        <button type="button" class="btn btn-outline-secondary" id="importContactsBtn">
2933                            <i class="fas fa-file-import me-1"></i>${t('Kontakte importieren')}
2934                        </button>
2935                        <button type="button" class="btn btn-outline-secondary" id="createSmartListBtn">
2936                            <i class="fas fa-list me-1"></i>${t('Smart-Liste erstellen')}
2937                        </button>
2938                        <button type="button" class="btn btn-outline-secondary" id="sendGroupEmailBtn">
2939                            <i class="fas fa-envelope me-1"></i>${t('Gruppen-E-Mail')}
2940                        </button>
2941                        ${this.hasSipAccount ? `
2942                        <button type="button"
2943                                class="btn btn-outline-secondary ${!this.hasMultiCallPermission() ? 'disabled opacity-50' : ''}"
2944                                id="newMultiCallBtn"
2945                                data-bs-toggle="tooltip"
2946                                data-bs-placement="top"
2947                                title="${!this.hasMultiCallPermission() ? t('MultiCall Abo erforderlich') : ''}">
2948                            <i class="fas fa-phone me-1"></i>${t('Sofort Anruf')}
2949                        </button>
2950                        ` : `
2951                        <button type="button"
2952                                class="btn btn-outline-secondary ${!this.hasMultiCallPermission() ? 'disabled opacity-50' : ''}"
2953                                id="sofortAnrufBtn"
2954                                data-bs-toggle="tooltip"
2955                                data-bs-placement="top"
2956                                title="${!this.hasMultiCallPermission() ? t('MultiCall Abo erforderlich') : ''}">
2957                            <i class="fas fa-phone me-1"></i>${t('Sofort Anruf')}
2958                        </button>
2959                        `}
2960                        <button type="button"
2961                                class="btn btn-outline-secondary ${!this.hasPersonalAssistantPermission() ? 'disabled opacity-50' : ''}"
2962                                id="autoCallBtn"
2963                                data-bs-toggle="tooltip"
2964                                data-bs-placement="top"
2965                                title="${!this.hasPersonalAssistantPermission() ? t('Personal Assistant Abo erforderlich') : ''}">
2966                            <i class="fas fa-phone-volume me-1"></i>${t('AutoCall')}
2967                        </button>
2968                    </div>
2969                </div>
2970
2971
2972                <!-- MultiCall Template Modal -->
2973                <div class="modal fade" id="multiCallTemplateModal" tabindex="-1" aria-labelledby="multiCallTemplateModalLabel" aria-hidden="true">
2974                    <div class="modal-dialog">
2975                        <div class="modal-content">
2976                            <div class="modal-header">
2977                                <h5 class="modal-title" id="multiCallTemplateModalLabel">${t('MultiCall Vorlage auswählen')}</h5>
2978                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
2979                            </div>
2980                            <div class="modal-body">
2981                                <div class="mb-3">
2982                                    <label for="multiCallTemplateSelect" class="form-label">${t('Vorlage')}</label>
2983                                    <select class="form-select" id="multiCallTemplateSelect" required>
2984                                    </select>
2985                                </div>
2986                                <div class="mb-3">
2987                                    <label for="multiCallCalendarSelect" class="form-label">${t('Kalender')}</label>
2988                                    <select class="form-select" id="multiCallCalendarSelect">
2989                                        <option value="">${t('Kein Kalender')}</option>
2990                                        ${await this.getCalendarOptionsHtml()}
2991                                    </select>
2992                                </div>
2993                                <div id="selectedContactsCountMultiCall" class="text-muted small"></div>
2994                            </div>
2995                            <div class="modal-footer">
2996                                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="Abbrechen">${t('Abbrechen')}</button>
2997                                <button type="button" class="btn btn-primary" id="createMultiCallTasksBtn" data-i18n="MultiCall starten">${t('MultiCall starten')}</button>
2998                            </div>
2999                        </div>
3000                    </div>
3001                </div>
3002
3003                <!-- Sofort Anruf Template Modal -->
3004                <div class="modal fade" id="sofortAnrufTemplateModal" tabindex="-1" aria-labelledby="sofortAnrufTemplateModalLabel" aria-hidden="true">
3005                    <div class="modal-dialog">
3006                        <div class="modal-content">
3007                            <div class="modal-header">
3008                                <h5 class="modal-title" id="sofortAnrufTemplateModalLabel">${t('Sofort Anruf - Vorlage auswählen')}</h5>
3009                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
3010                            </div>
3011                            <div class="modal-body">
3012                                <div class="mb-3">
3013                                    <label for="sofortAnrufTemplateSelect" class="form-label">${t('Vorlage')}</label>
3014                                    <select class="form-select" id="sofortAnrufTemplateSelect" required>
3015                                    </select>
3016                                </div>
3017                                <div class="mb-3">
3018                                    <label for="sofortAnrufAgentSelect" class="form-label">${t('Agent')}</label>
3019                                    <select class="form-select" id="sofortAnrufAgentSelect">
3020                                        <option value="">${t('Wird geladen...')}</option>
3021                                    </select>
3022                                </div>
3023                                <div class="mb-3">
3024                                    <label for="sofortAnrufCalendarSelect" class="form-label">${t('Kalender')}</label>
3025                                    <select class="form-select" id="sofortAnrufCalendarSelect">
3026                                        <option value="">${t('Kein Kalender')}</option>
3027                                        ${await this.getCalendarOptionsHtml()}
3028                                    </select>
3029                                </div>
3030                                <div id="selectedContactsCountSofortAnruf" class="text-muted small"></div>
3031                            </div>
3032                            <div class="modal-footer">
3033                                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="Abbrechen">${t('Abbrechen')}</button>
3034                                <button type="button" class="btn btn-warning" id="startSofortAnrufBtn" data-i18n="Sofort anrufen"><i class="fas fa-phone me-1"></i>${t('Sofort anrufen')}</button>
3035                            </div>
3036                        </div>
3037                    </div>
3038                </div>
3039
3040                <!-- Bulk Actions -->
3041                <div class="bulk-actions-container mb-3" style="display: none;">
3042                    <div class="card">
3043                        <div class="card-body">
3044                            <div class="row g-3 align-items-end">
3045                                <div class="col-12 col-md-3">
3046                                    <label class="form-label" for="bulkLeadStatus">${t('Lead-Status')}</label>
3047                                    <select class="form-select" id="bulkLeadStatus" name="bulkLeadStatus">
3048                                        <option value="">${t('Status wählen')}</option>
3049                                        ${(this.leadStatuses || DEFAULT_LEAD_STATUSES).map(status =>
3050                                            `<option value="${status}">${t(status)}</option>`
3051                                        ).join('')}
3052                                    </select>
3053                                </div>
3054                                <div class="col-12 col-md-3">
3055                                    <label class="form-label" for="bulkAufgabe">${t('Aufgabe')}</label>
3056                                    <select class="form-select" id="bulkAufgabe" name="bulkAufgabe">
3057                                        <option value="">${t('Aufgabe wählen')}</option>
3058                                        ${AUFGABE_OPTIONS.map(option =>
3059                                            `<option value="${option}">${t(option)}</option>`
3060                                        ).join('')}
3061                                    </select>
3062                                </div>
3063                                <div class="col-12 col-md-3">
3064                                    <label class="form-label" for="bulkIndustry">${t('Branche')}</label>
3065                                    <select class="form-select" id="bulkIndustry" name="bulkIndustry">
3066                                        <option value="">${t('Branche wählen')}</option>
3067                                        ${DEFAULT_INDUSTRIES.map(industry =>
3068                                            `<option value="${industry}">${t(industry)}</option>`
3069                                        ).join('')}
3070                                    </select>
3071                                </div>
3072                                <div class="col-12 col-md-2">
3073                                    <label class="form-label" style="visibility: hidden;">${t('Aktion')}</label>
3074                                    <button type="button" class="btn btn-primary w-100" id="bulkUpdateBtn">${t('Ändern')}</button>
3075                                </div>
3076                                <div class="col-12 col-md-auto kteam-bulk-col ${this._kteamInfo?.sicht?.darf_zuweisen ? '' : 'd-none'}">
3077                                    <label class="form-label" style="visibility: hidden;">${t('Zuweisen')}</label>
3078                                    <button type="button" class="btn btn-outline-primary w-100" id="bulkAssignBtn" title="${t('Ausgewählte Kontakte einer Person zuweisen')}"><i class="fas fa-user-tag me-1"></i>${t('Zuweisen')}</button>
3079                                </div>
3080                                <div class="col-12 col-md-1">
3081                                    <label class="form-label" style="visibility: hidden;">${t('Löschen')}</label>
3082                                    <button type="button" class="btn btn-danger w-100" id="bulkDeleteBtn">
3083                                        <i class="fas fa-trash"></i>
3084                                    </button>
3085                                </div>
3086                            </div>
3087                        </div>
3088                    </div>
3089                </div>
3090                <div class="table-responsive contacts-table-wrap">
3091                    <table class="table email-table mb-0">
3092                        <thead class="table-light">
3093                            <tr>
3094                                <th class="checkbox-column text-center" style="width: 40px;">
3095                                    <div class="dropdown">
3096                                        <input type="checkbox" class="form-check-input dropdown-toggle" id="selectAll" 
3097                                               data-bs-toggle="dropdown" aria-expanded="false" 
3098                                               style="transform: scale(1.2); cursor: pointer;" />
3099                                        <ul class="dropdown-menu" aria-labelledby="selectAll">
3100                                            <li><a class="dropdown-item" href="#" id="selectAllPage">
3101                                                <i class="fas fa-check-square me-2"></i>${t('Alle auf dieser Seite')}
3102                                            </a></li>
3103                                            <li><a class="dropdown-item" href="#" id="selectAllFiltered">
3104                                                <i class="fas fa-filter me-2"></i>${t('Alle gefilterten Kontakte')}
3105                                            </a></li>
3106                                        </ul>
3107                                    </div>
3108                                </th>
3109                                <th class="name-column">${t('Kontakt')}</th>
3110                                <th class="phone-column d-none d-md-table-cell" style="width: 190px;">${t('Telefonnummer')}</th>
3111                                <th class="date-column d-none d-lg-table-cell" style="width: 150px;">${t('Aktualisiert')}</th>
3112                                <th class="lead-status-column" style="width: 130px;">${t('Lead-Status')}</th>
3113                                <th class="action-column text-end" style="width: 110px;">${t('Aktionen')}</th>
3114                            </tr>
3115                        </thead>
3116                        <tbody id="contactsTableBody">
3117        `;
3118
3119        // OPTIMIERUNG: Synchrones Rendering ohne await
3120        let rowsHtml = '';
3121        if (contactsToShow.length === 0) {
3122            // Zeige eine Nachricht wenn keine Kontakte vorhanden sind
3123            rowsHtml = `
3124                <tr>
3125                    <td colspan="6" class="text-center py-4">
3126                        <div class="text-muted">
3127                            <i class="fas fa-users fa-3x mb-3"></i>
3128                            <p>${contactsToRender.length === 0 ? t('Keine Kontakte vorhanden') : t('Keine Kontakte auf dieser Seite')}</p>
3129                        </div>
3130                    </td>
3131                </tr>
3132            `;
3133        } else {
3134            for (const contact of contactsToShow) {
3135                rowsHtml += this.renderContactRow(contact);
3136            }
3137        }
3138        html += rowsHtml;
3139
3140        html += `
3141                    </tbody>
3142                </table>
3143            </div>
3144
3145            <!-- Mobile Contacts Container -->
3146            <div class="mobile-contacts-container">
3147                ${this.renderMobileContacts(contactsToShow, contactsToRender)}
3148            </div>
3149
3150            <!-- Mobile Floating Action Button -->
3151            <button type="button" class="mobile-fab" id="mobileNewContactBtn">
3152                <i class="fas fa-plus"></i>
3153            </button>
3154        `;
3155
3156        // Add pagination controls if needed
3157        if (totalPages > 1) {
3158            html += `
3159                <style>
3160                    .pagination .page-link {
3161                        color: #6c757d;
3162                        background-color: #fff;
3163                        border-color: #dee2e6;
3164                    }
3165                    .pagination .page-link:hover {
3166                        color: #495057;
3167                        background-color: #e9ecef;
3168                        border-color: #dee2e6;
3169                    }
3170                    .pagination .page-item.active .page-link {
3171                        color: #fff;
3172                        background-color: #6c757d;
3173                        border-color: #6c757d;
3174                    }
3175                    .pagination .page-item.disabled .page-link {
3176                        color: #adb5bd;
3177                        background-color: #fff;
3178                        border-color: #dee2e6;
3179                    }
3180                </style>
3181                <div class="d-flex flex-column align-items-start flex-md-row justify-content-md-between align-items-md-center mt-3">
3182                    <div class="text-muted mb-2 mb-md-0">
3183                        ${t('Zeige')} ${startIndex + 1} ${t('bis')} ${Math.min(endIndex, contactsToRender.length)} ${t('von')} ${contactsToRender.length}
3184                    </div>
3185                    <nav aria-label="${t('Kontakte Navigation')}" class="contacts-pagination">
3186                        <ul class="pagination mb-0">
3187                            <li class="page-item ${this.currentPage === 1 ? 'disabled' : ''}">
3188                                <a class="page-link" href="#" data-page="${this.currentPage - 1}">
3189                                    <i class="fas fa-chevron-left"></i>
3190                                </a>
3191                            </li>
3192            `;
3193
3194            // Calculate which pages to show
3195            const maxVisiblePages = window.innerWidth >= 768 ? 20 : 5; // Desktop: 20, Mobile: 5
3196            let startPage = Math.max(1, this.currentPage - Math.floor(maxVisiblePages / 2));
3197            let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
3198            
3199            // Adjust startPage if we're near the end to keep maxVisiblePages
3200            if (endPage - startPage + 1 < maxVisiblePages) {
3201                startPage = Math.max(1, endPage - maxVisiblePages + 1);
3202            }
3203
3204            // Add first page if we're not starting from 1
3205            if (startPage > 1) {
3206                html += `
3207                    <li class="page-item">
3208                        <a class="page-link" href="#" data-page="1">1</a>
3209                    </li>
3210                `;
3211                if (startPage > 2) {
3212                    html += `
3213                        <li class="page-item disabled">
3214                            <span class="page-link">...</span>
3215                        </li>
3216                    `;
3217                }
3218            }
3219
3220            // Add page numbers
3221            for (let i = startPage; i <= endPage; i++) {
3222                html += `
3223                    <li class="page-item ${i === this.currentPage ? 'active' : ''}">
3224                        <a class="page-link" href="#" data-page="${i}">${i}</a>
3225                    </li>
3226                `;
3227            }
3228
3229            // Add last page if we're not ending at totalPages
3230            if (endPage < totalPages) {
3231                if (endPage < totalPages - 1) {
3232                    html += `
3233                        <li class="page-item disabled">
3234                            <span class="page-link">...</span>
3235                        </li>
3236                    `;
3237                }
3238                html += `
3239                    <li class="page-item">
3240                        <a class="page-link" href="#" data-page="${totalPages}">${totalPages}</a>
3241                    </li>
3242                `;
3243            }
3244
3245            html += `
3246                            <li class="page-item ${this.currentPage === totalPages ? 'disabled' : ''}">
3247                                <a class="page-link" href="#" data-page="${this.currentPage + 1}">
3248                                    <i class="fas fa-chevron-right"></i>
3249                                </a>
3250                            </li>
3251                        </ul>
3252                    </nav>
3253                </div>
3254            `;
3255        }
3256
3257        // Mobile Pagination und Load More Button
3258        const isMobile = window.innerWidth <= 768;
3259        if (isMobile) {
3260            // Zeige "Mehr laden" Button statt Pagination auf Mobile
3261            if (this.hasMore && !this.allContactsLoaded) {
3262                html += `
3263                    <div class="mobile-load-more-container text-center py-3">
3264                        <button class="btn btn-primary" id="mobileLoadMoreBtn">
3265                            <i class="fas fa-plus-circle me-2"></i>${t('Mehr Kontakte laden')}
3266                        </button>
3267                        <div class="text-muted small mt-2">
3268                            ${contactsToRender.length} ${t('von')} ${this.hasMore ? t('vielen') : contactsToRender.length} ${t('Kontakten geladen')}
3269                        </div>
3270                    </div>
3271                `;
3272            } else if (!this.hasMore) {
3273                html += `
3274                    <div class="text-center py-3 text-muted">
3275                        <i class="fas fa-check-circle me-2"></i>${t('Alle')} ${contactsToRender.length} ${t('Kontakte geladen')}
3276                    </div>
3277                `;
3278            }
3279        } else if (totalPages > 1) {
3280            // Desktop Pagination wie gehabt
3281            html += `
3282                <div class="mobile-pagination">
3283                    <button class="mobile-pagination-btn" ${this.currentPage === 1 ? 'disabled' : ''} data-page="${this.currentPage - 1}">
3284                        <i class="fas fa-chevron-left"></i>
3285                    </button>
3286                    <span class="mobile-pagination-info">
3287                        ${this.currentPage} / ${totalPages}
3288                    </span>
3289                    <button class="mobile-pagination-btn" ${this.currentPage === totalPages ? 'disabled' : ''} data-page="${this.currentPage + 1}">
3290                        <i class="fas fa-chevron-right"></i>
3291                    </button>
3292                </div>
3293            `;
3294        }
3295
3296        // Mobile Bulk Actions Bar
3297        html += `
3298            <div class="mobile-bulk-actions" id="mobileBulkActions">
3299                <div class="mobile-bulk-actions-content">
3300                    <span class="mobile-bulk-count">
3301                        <span id="mobileSelectedCount">0</span> ${t('ausgewählt')}
3302                    </span>
3303                    <button class="btn btn-sm btn-primary" id="mobileBulkEdit">
3304                        <i class="fas fa-edit"></i>
3305                    </button>
3306                    <button class="btn btn-sm btn-danger" id="mobileBulkDelete">
3307                        <i class="fas fa-trash"></i>
3308                    </button>
3309                    <button class="btn btn-sm btn-secondary" id="mobileCancelSelection">
3310                        ${t('Abbrechen')}
3311                    </button>
3312                </div>
3313            </div>
3314        `;
3315
3316        const container = document.getElementById('contactsContainer');
3317        if (!container) {
3318            console.error('Contacts container not found');
3319            return;
3320        }
3321
3322        // Debug-Ausgabe für iPhone
3323        console.log('[renderContacts] Container gefunden, füge HTML ein');
3324        console.log('[renderContacts] HTML-Länge:', html.length);
3325        
3326        // Verwende requestAnimationFrame für bessere Performance
3327        requestAnimationFrame(() => {
3328            try {
3329                container.innerHTML = html;
3330                console.log('[renderContacts] HTML erfolgreich eingefügt');
3331            } catch (e) {
3332                console.error('[renderContacts] Fehler beim Einfügen des HTML:', e);
3333                // Fallback: Einfache Nachricht anzeigen
3334                container.innerHTML = '<div class="alert alert-warning m-3">Fehler beim Laden der Kontakte. Bitte Seite neu laden.</div>';
3335            }
3336
3337            // Binde Paginierungs-Event-Handler neu
3338            this.bindPaginationEvents();
3339            
3340            // Binde Event-Listener für Kontakt-Zeilen neu
3341            this.setupContactEventListeners();
3342            
3343            // Stelle Smart-Listen-UI wieder her
3344            this.updateSmartListsUI();
3345        });
3346    }
3347
3348    // Neue Methode für Paginierungs-Event-Handler
3349    bindPaginationEvents() {
3350        const paginationContainer = document.querySelector('.contacts-pagination');
3351        if (paginationContainer) {
3352            // Entferne alte Event-Listener durch Klonen
3353            const newPaginationContainer = paginationContainer.cloneNode(true);
3354            paginationContainer.parentNode.replaceChild(newPaginationContainer, paginationContainer);
3355
3356            // Füge neue Event-Listener hinzu
3357            newPaginationContainer.addEventListener('click', async (e) => {
3358                e.preventDefault();
3359                const pageLink = e.target.closest('.page-link');
3360                if (pageLink) {
3361                    const newPage = parseInt(pageLink.dataset.page);
3362                    if (!isNaN(newPage) && newPage !== this.currentPage) {
3363                        this.currentPage = newPage;
3364                        // Wenn eine Smart List aktiv ist, rendere die gefilterten Kontakte
3365                        if (this.currentFilteredContacts) {
3366                            await this.renderContacts(this.currentFilteredContacts);
3367                        } else {
3368                            await this.renderContacts(this.contacts);
3369                        }
3370                        // Binde Event-Handler neu
3371                        this.setupContactEventListeners();
3372                        // Stelle Smart-Listen-UI wieder her
3373                        this.updateSmartListsUI();
3374                        // Speichere den aktualisierten Zustand
3375                        this.storeContactsState();
3376                        // Scrolle nach oben
3377                        window.scrollTo(0, 0);
3378                    }
3379                }
3380            });
3381        }
3382    }
3383
3384    /* contacts-list-v1: HTML-Escaping fuer alle Nutzerwerte (CSV-Import + KI-Erfassung am Telefon). */
3385    escC(v) {
3386        if (v === null || v === undefined) return '';
3387        return String(v)
3388            .replace(/&/g, '&amp;')
3389            .replace(/</g, '&lt;')
3390            .replace(/>/g, '&gt;')
3391            .replace(/"/g, '&quot;')
3392            .replace(/'/g, '&#39;');
3393    }
3394
3395    /* contacts-list-v1: Anzeige-Identitaet eines Kontakts. Ein Viertel der Kontakte
3396       heisst "Unbekannt" - dort ist die Rufnummer die eigentliche Identitaet. */
3397    contactIdentity(contact) {
3398        const rawName = String(contact.name || '').trim();
3399        const phone = String(contact.phone_number || '').trim();
3400        const company = String(contact.company || '').trim();
3401        const email = String(contact.email || '').trim();
3402        const unknown = !rawName || rawName === '-' || /^unbekannt/i.test(rawName);
3403
3404        const primary = unknown ? (phone || t('Unbekannt')) : rawName;
3405        const sub = [];
3406        if (unknown && phone) sub.push(t('Unbekannter Anrufer'));
3407        if (company) sub.push(company);
3408        if (email) sub.push(email);
3409
3410        let initials = '';
3411        if (!unknown) {
3412            const parts = rawName.split(/\s+/).filter(Boolean);
3413            initials = parts.slice(0, 2).map(p => p.charAt(0)).join('').toUpperCase();
3414        }
3415        // Deterministische Farbe, damit derselbe Kontakt immer gleich aussieht
3416        const key = (rawName || phone || String(contact.id || ''));
3417        let h = 0;
3418        for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) >>> 0;
3419        const palette = ['#4C6EF5', '#12B886', '#F76707', '#7048E8', '#0CA678', '#E8590C', '#1C7ED6', '#AE3EC9'];
3420
3421        return {
3422            unknown, phone, company, email,
3423            primary,
3424            sub: sub.join(' \u00b7 '),
3425            initials,
3426            color: palette[h % palette.length]
3427        };
3428    }
3429
3430    /* contacts-menu-fix-v1: Das Aktionsmenue lag in einem td mit overflow:hidden und wurde
3431       dadurch abgeschnitten - lesbar war nur der obere Rand von "Oeffnen". DREI Vorfahren
3432       klippen: .table td {overflow:hidden}, .v2-layout .table {overflow:hidden !important}
3433       und #mainContent .table-responsive {overflow-x:auto !important} aus erp.css. Genau
3434       daran ist meine fruehere Regel .contacts-table-wrap{overflow:visible} gescheitert
3435       (id-Selektor + !important schlagen sie). Diese Regeln zu ueberschreiben traefe JEDE
3436       Tabelle der App (Textkuerzung, runde Ecken, Querscrollen). Stattdessen holt
3437       position:fixed das Menue aus dem Klipp-Bereich - es bleibt dabei an seinem Platz im
3438       DOM, damit alle bestehenden Klick-Bindungen (Oeffnen/Loeschen) unveraendert laufen.
3439       data-bs-display="static" am Knopf schaltet Popper ab, das sonst dagegen positioniert. */
3440    ensureMenuPositioning() {
3441        if (window.__clMenuFixBound) return;
3442        window.__clMenuFixBound = true;
3443
3444        const SEL = '.contact-quick-actions [data-bs-toggle="dropdown"]';
3445
3446        const place = (toggle) => {
3447            const menu = toggle.parentElement && toggle.parentElement.querySelector('.dropdown-menu');
3448            if (!menu) return;
3449            menu.style.position = 'fixed';
3450            menu.style.transform = 'none';
3451            menu.style.margin = '0';
3452            menu.style.inset = 'auto';
3453            const r = toggle.getBoundingClientRect();
3454            const w = menu.offsetWidth || 180;
3455            const h = menu.offsetHeight || 0;
3456            const gap = 4;
3457            let top = r.bottom + gap;
3458            if (h && top + h > window.innerHeight - 8) {
3459                const above = r.top - gap - h;
3460                top = above > 8 ? above : Math.max(8, window.innerHeight - h - 8);
3461            }
3462            let left = r.right - w;
3463            left = Math.max(8, Math.min(left, window.innerWidth - w - 8));
3464            menu.style.top = top + 'px';
3465            menu.style.left = left + 'px';
3466        };
3467
3468        const closeAll = () => {
3469            document.querySelectorAll('.contact-quick-actions .dropdown-menu.show').forEach((menu) => {
3470                const toggle = menu.parentElement && menu.parentElement.querySelector('[data-bs-toggle="dropdown"]');
3471                const inst = (toggle && window.bootstrap && window.bootstrap.Dropdown)
3472                    ? window.bootstrap.Dropdown.getInstance(toggle) : null;
3473                if (inst) { inst.hide(); return; }
3474                menu.classList.remove('show');
3475                if (toggle) toggle.setAttribute('aria-expanded', 'false');
3476            });
3477        };
3478
3479        document.addEventListener('shown.bs.dropdown', (e) => {
3480            const toggle = (e.target && e.target.closest) ? e.target.closest(SEL) : null;
3481            if (toggle) place(toggle);
3482        });
3483        /* Beim Scrollen wandert der Knopf, ein festes Menue nicht -> schliessen statt verrutschen. */
3484        window.addEventListener('scroll', closeAll, true);
3485        window.addEventListener('resize', closeAll);
3486    }
3487
3488    renderContactRow(contact) {
3489        this.ensureMenuPositioning();
3490        /* contacts-list-v1: Firma/Aufgabe wandern in die Unterzeile (waren zu 84%/92% leer),
3491           die tote Status-Spalte ist entfallen, alle Nutzerwerte sind escaped. */
3492        const id = this.escC(contact.id);
3493        const idt = this.contactIdentity(contact);
3494        const task = String(contact.task || '').trim();
3495
3496        const avatar = idt.initials
3497            ? `<span class="contact-avatar" style="background:${idt.color}">${this.escC(idt.initials)}</span>`
3498            : `<span class="contact-avatar" style="background:#adb5bd"><i class="fas fa-user"></i></span>`;
3499
3500        const callBtn = idt.phone
3501            ? `<a href="tel:${this.escC(idt.phone)}" class="btn btn-sm btn-outline-secondary contact-call-btn" title="${t('Anrufen')}"><i class="fas fa-phone"></i></a>`
3502            : '';
3503
3504        return `
3505            <tr data-contact-id="${id}" class="contact-row contact-link" style="cursor: pointer;" data-id="${id}" data-contact-name="${this.escC(contact.name || '')}" data-contact-email="${this.escC(contact.email || '')}" data-contact-phone="${this.escC(contact.phone_number || '')}">
3506                <td class="checkbox-column text-center" style="width: 40px;">
3507                    <input type="checkbox" name="contactSelect" class="form-check-input contact-checkbox" value="${id}" id="contact-${id}" style="transform: scale(1.2);" />
3508                </td>
3509                <td class="name-column">
3510                    <div class="contact-identity">
3511                        ${avatar}
3512                        <div class="contact-identity-text">
3513                            <div class="fw-semibold contact-name-text" title="${this.escC(idt.primary)}">${this.escC(idt.primary)}</div>
3514                            ${idt.sub ? `<div class="text-muted contact-subline" title="${this.escC(idt.sub)}">${this.escC(idt.sub)}</div>` : ''}
3515                            <div class="small text-muted d-block d-md-none">
3516                                ${idt.phone && !idt.unknown ? `<i class="fas fa-phone me-1"></i>${this.escC(idt.phone)}` : ''}
3517                            </div>
3518                            ${task ? `<span class="badge bg-light text-dark border contact-task-chip mt-1"><i class="fas fa-clipboard-check me-1"></i>${this.escC(task)}</span>` : ''}
3519                            ${KTeam.teamChips(contact, this._kteamInfo) /* kontakt-team-v1 */}
3520                        </div>
3521                    </div>
3522                </td>
3523                <td class="phone-column d-none d-md-table-cell">${idt.phone ? this.escC(idt.phone) : '<span class="text-muted">-</span>'}</td>
3524                <td class="date-column d-none d-lg-table-cell">${/* contacts-list-v1b: formatDate liefert bewusst HTML (Datum + Uhrzeit in 2 Zeilen) - hier NICHT escapen */ this.formatDate(contact.updated_at) || '-'}</td>
3525                <td class="lead-status-column">
3526                    <span class="badge ${this.getStatusClass(contact.lead_status)}">${this.escC(t(contact.lead_status || 'Neu'))}</span>
3527                </td>
3528                <td class="text-end action-column contact-noclick">
3529                    <div class="contact-quick-actions">
3530                        ${callBtn}
3531                        <div class="dropdown">
3532                            <button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="dropdown" data-bs-display="static" aria-expanded="false" title="${t('Weitere Aktionen')}">
3533                                <i class="fas fa-ellipsis-h"></i>
3534                            </button>
3535                            <ul class="dropdown-menu dropdown-menu-end">
3536                                <li><a class="dropdown-item contact-open-item" href="#" data-id="${id}"><i class="fas fa-up-right-from-square fa-fw me-2"></i>${t('Öffnen')}</a></li>
3537                                ${idt.email ? `<li><a class="dropdown-item" href="mailto:${this.escC(idt.email)}"><i class="fas fa-envelope fa-fw me-2"></i>${t('E-Mail schreiben')}</a></li>` : ''}
3538                                <li><hr class="dropdown-divider"></li>
3539                                <li><a class="dropdown-item text-danger delete-contact" href="#" data-id="${id}"><i class="fas fa-trash-alt fa-fw me-2"></i>${t('Löschen')}</a></li>
3540                            </ul>
3541                        </div>
3542                    </div>
3543                </td>
3544            </tr>
3545        `;
3546    }
3547
3548    renderMobileContacts(contactsToShow, allContacts) {
3549        if (!contactsToShow || contactsToShow.length === 0) {
3550            return `
3551                <div class="mobile-no-contacts">
3552                    <i class="fas fa-users"></i>
3553                    <p>
3553${allContacts && allContacts.length === 0 ? t('Keine Kontakte vorhanden') : t('Keine Kontakte auf dieser Seite')}</p>
3554                </div>
3555            `;
3556        }
3557
3558        let html = '';
3559        
3560        for (const contact of contactsToShow) {
3561            const latestCallSummary = null; // Performance-Optimierung
3562            
3563            html += `
3564                <div class="mobile-contact-card" data-contact-id="${this.escC(contact.id)}" data-contact-name="${this.escC(contact.name || '')}" data-contact-email="${this.escC(contact.email || '')}" data-contact-phone="${this.escC(contact.phone_number || '')}">
3565                    <div class="mobile-contact-header" data-id="${contact.id}">
3566                        <div class="mobile-contact-checkbox">
3567                            <input type="checkbox" 
3568                                   class="form-check-input contact-checkbox" 
3569                                   value="${contact.id}" 
3570                                   id="mobile-contact-${contact.id}">
3571                        </div>
3572                        
3573                        <div class="mobile-contact-info">
3574                            <div class="mobile-contact-name" data-field="name">${this.escC(this.contactIdentity(contact).primary)}</div>
3575                            ${contact.company ? `<div class="mobile-contact-company" data-field="company">${this.escC(contact.company)}</div>` : ''}
3576                            
3577                            <div class="mobile-contact-details">
3578                                ${contact.email ? `
3579                                    <div class="mobile-contact-detail-item">
3580                                        <i class="fas fa-envelope"></i>
3581                                        <span class="mobile-contact-email" data-field="email">${this.escC(contact.email)}</span>
3582                                    </div>
3583                                ` : ''}
3584                                ${contact.phone_number ? `
3585                                    <div class="mobile-contact-detail-item">
3586                                        <i class="fas fa-phone"></i>
3587                                        <span class="mobile-contact-phone" data-field="phone">${this.escC(contact.phone_number)}</span>
3588                                    </div>
3589                                ` : ''}
3590                            </div>
3591                        </div>
3592                        
3593                        <div class="mobile-contact-badges">
3594                            <span class="mobile-lead-badge ${this.getStatusClass(contact.lead_status)}">
3595                                ${this.escC(t(contact.lead_status || 'Neu'))}
3596                            </span>
3597                        </div>
3598                        
3599                        <div class="mobile-contact-actions">
3600                            <button class="mobile-delete-btn delete-contact" data-id="${contact.id}">
3601                                <i class="fas fa-trash"></i>
3602                            </button>
3603                        </div>
3604                    </div>
3605                </div>
3606            `;
3607        }
3608        
3609        return html;
3610    }
3611
3612    getStatusClass(status) {
3613        const statusClasses = {
3614            'Neu': 'bg-info',
3615            'Anrufen': 'bg-warning',
3616            'Angerufen': 'bg-primary',
3617            'In Bearbeitung': 'bg-warning',
3618            'Qualifiziert': 'bg-success',
3619            'Nicht Qualifiziert': 'bg-danger',
3620            'Kein Bedarf': 'bg-secondary',
3621            'Kunde': 'bg-success',
3622            'Wiedervorlage': 'bg-info'
3623        };
3624        return statusClasses[status] || 'bg-secondary';
3625    }
3626
3627    parseContactDate(timestamp) {
3628        // Hilfsmethode zum Parsen von Kontakt-Datumsfeldern für Filter
3629        if (!timestamp) return null;
3630
3631        try {
3632            let date;
3633            if (timestamp?.toDate) {
3634                // Firebase Timestamp
3635                date = timestamp.toDate();
3636            } else if (typeof timestamp === 'object' && timestamp.seconds) {
3637                // Firebase Timestamp-ähnliches Objekt
3638                date = new Date(timestamp.seconds * 1000);
3639            } else if (typeof timestamp === 'number') {
3640                // Unix Timestamp in Millisekunden
3641                date = new Date(timestamp);
3642            } else if (typeof timestamp === 'string') {
3643                // ISO String oder anderes Datumsformat
3644                if (timestamp.includes('T')) {
3645                    // ISO String
3646                    date = new Date(timestamp);
3647                } else {
3648                    // Versuche verschiedene Datumsformate
3649                    const parts = timestamp.split(/[./-]/);
3650                    if (parts.length === 3) {
3651                        // Annahme: DD.MM.YYYY oder YYYY-MM-DD
3652                        if (parts[0].length === 4) {
3653                            // YYYY-MM-DD
3654                            date = new Date(parts[0], parts[1] - 1, parts[2]);
3655                        } else {
3656                            // DD.MM.YYYY
3657                            date = new Date(parts[2], parts[1] - 1, parts[0]);
3658                        }
3659                    } else {
3660                        date = new Date(timestamp);
3661                    }
3662                }
3663            } else {
3664                return null;
3665            }
3666
3667            // Überprüfe ob das Datum gültig ist
3668            if (isNaN(date.getTime())) {
3669                return null;
3670            }
3671
3672            return date;
3673        } catch (error) {
3674            console.error('Error parsing contact date:', error, 'Timestamp:', timestamp);
3675            return null;
3676        }
3677    }
3678
3679    formatDate(timestamp) {
3680        // Wenn kein Timestamp vorhanden ist, return '-'
3681        if (!timestamp) return '-';
3682
3683        try {
3684            // Debug-Ausgabe
3685            // console.log('Original timestamp:', timestamp);
3686            // console.log('Timestamp type:', typeof timestamp);
3687            if (timestamp?.toDate) {
3688            // console.log('Is Firestore timestamp');
3689            }
3690
3691            // Konvertiere Firebase Timestamp oder String zu Date Object
3692            let date;
3693            if (timestamp?.toDate) {
3694                // Firebase Timestamp
3695                date = timestamp.toDate();
3696            } else if (typeof timestamp === 'object' && timestamp.seconds) {
3697                // Firebase Timestamp-ähnliches Objekt
3698                date = new Date(timestamp.seconds * 1000);
3699            } else if (typeof timestamp === 'number') {
3700                // Unix Timestamp in Millisekunden
3701                date = new Date(timestamp);
3702            } else if (typeof timestamp === 'string') {
3703                // ISO String oder anderes Datumsformat
3704                if (timestamp.includes('T')) {
3705                    // ISO String
3706                    date = new Date(timestamp);
3707                } else {
3708                    // Versuche verschiedene Datumsformate
3709                    const parts = timestamp.split(/[./-]/);
3710                    if (parts.length === 3) {
3711                        // Annahme: DD.MM.YYYY oder YYYY-MM-DD
3712                        if (parts[0].length === 4) {
3713                            // YYYY-MM-DD
3714                            date = new Date(parts[0], parts[1] - 1, parts[2]);
3715                        } else {
3716                            // DD.MM.YYYY
3717                            date = new Date(parts[2], parts[1] - 1, parts[0]);
3718                        }
3719                    } else {
3720                        date = new Date(timestamp);
3721                    }
3722                }
3723            } else {
3724                console.error('Unbekanntes Datumsformat:', timestamp);
3725                return '-';
3726            }
3727
3728            // console.log('Parsed date:', date);
3729
3730            // Überprüfe ob das Datum gültig ist
3731            if (isNaN(date.getTime())) {
3732                console.error('Ungültiges Datum:', timestamp);
3733                return '-';
3734            }
3735
3736            // console.log('Parsed date:', date);
3737
3738            // Formatiere das Datum in 2 Zeilen: Datum + Uhrzeit
3739            const datePart = date.toLocaleDateString(getLocale(), {
3740                year: 'numeric',
3741                month: '2-digit',
3742                day: '2-digit'
3743            });
3744            const timePart = date.toLocaleTimeString(getLocale(), {
3745                hour: '2-digit',
3746                minute: '2-digit'
3747            });
3748
3749            // console.log('Formatted date:', datePart, timePart);
3750            return `${datePart}<br><span class="text-muted small">${timePart}</span>`;
3751
3752        } catch (error) {
3753            console.error('Error formatting date:', error, 'Timestamp:', timestamp);
3754            return '-';
3755        }
3756    }
3757
3758    // kontakt-team-v1: Filter "Zuweisung"/"Liste" und Knopf "Zuweisen".
3759    // Die Filterleiste selbst entsteht in pages.js; die beiden Auswahlfelder
3760    // werden hier nachgeruestet, damit die Aenderung auf diese Datei begrenzt bleibt.
3761    async _kteamFilterEinbauen() {
3762        try {
3763            if (!this._kteamInfo) {
3764                this._kteamInfo = await KTeam.ladeInfo();
3765            }
3766        } catch (e) {
3767            console.warn('[kontakt-team] Info nicht ladbar:', e);
3768            return;
3769        }
3770        const info = this._kteamInfo;
3771        const anker = document.querySelector('#aufgabeFilter');
3772        if (anker && !document.querySelector('#zuweisungFilter')) {
3773            const sel = document.createElement('select');
3774            sel.className = 'form-select';
3775            sel.id = 'zuweisungFilter';
3776            sel.title = t('Zuweisung');
3777            sel.innerHTML = `<option value="">${t('Zuweisung: Alle')}</option>
3778                <option value="__mir">${t('Mir zugewiesen')}</option>
3779                <option value="__keine">${t('Nicht zugewiesen')}</option>
3780                ${(info.mitglieder || []).map(m =>
3780 `<option value="${this.escC(m.uid)}">${this.escC(m.name)}</option>`).join('')}`;
3781            anker.insertAdjacentElement('afterend', sel);
3782            sel.addEventListener('change', () => this.applyFilters());
3783        }
3784        const listen = [...new Set((this.contacts || []).map(c => c.liste).filter(Boolean))].sort();
3785        let lsel = document.querySelector('#listeFilter');
3786        if (listen.length && anker && !lsel) {
3787            lsel = document.createElement('select');
3788            lsel.className = 'form-select';
3789            lsel.id = 'listeFilter';
3790            lsel.title = t('Liste');
3791            (document.querySelector('#zuweisungFilter') || anker).insertAdjacentElement('afterend', lsel);
3792            lsel.addEventListener('change', () => this.applyFilters());
3793        }
3794        if (lsel) {
3795            const aktuell = lsel.value;
3796            lsel.innerHTML = `<option value="">${t('Liste: Alle')}</option>`
3797                + listen.map(l => `<option value="${this.escC(l)}">${this.escC(l)}</option>`).join('');
3798            if (listen.includes(aktuell)) lsel.value = aktuell;
3799        }
3800        const btn = document.getElementById('bulkAssignBtn');
3801        if (btn) btn.closest('.kteam-bulk-col')?.classList.toggle('d-none', !info.sicht?.darf_zuweisen);
3802        if (!window.__kteamBulkListener) {
3803            window.__kteamBulkListener = true;
3804            document.addEventListener('click', (e) => {
3805                if (!e.target.closest('#bulkAssignBtn')) return;
3806                e.preventDefault();
3807                const inst = window.__kteamContacts;
3808                if (!inst) return;
3809                const ids = inst.getSelectedContactIds();
3810                if (!ids.length) { UI.showError(t('Keine Kontakte ausgewählt')); return; }
3811                KTeam.oeffneZuweisenDialog(ids, async (r) => {
3812                    if (r?.meldung) UI.showSuccess(r.meldung);
3813                    await inst.clearContactsCache();
3814                    await inst.loadContacts(true);
3815                    await inst.applyFilters();
3816                });
3817            });
3818        }
3819        window.__kteamContacts = this;
3820    }
3821
3822    async applyFilters() {
3823        // showContactsLoadingIndicator(); // Ladeindikator deaktiviert für bessere Performance
3824        const filterButton = document.getElementById('apply-filters-btn');
3825        let originalButtonText = ''; // Standardwert definieren
3826
3827        // Wenn Filter manuell angewendet werden, ist keine Smart List mehr aktiv
3828        localStorage.removeItem('activeSmartListId');
3829        this.activeSmartListId = null;
3830            // console.log("[applyFilters] Removed activeSmartListId from localStorage due to manual filter change.");
3831
3832        // Prüfe, ob der Button existiert, bevor darauf zugegriffen wird
3833        if (filterButton) {
3834            originalButtonText = filterButton.innerHTML;
3835            filterButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Laden...';
3836            filterButton.disabled = true;
3837        }
3838
3839        try {
3840            const searchTerm = document.querySelector('#searchInput')?.value?.toLowerCase() || '';
3841            const leadStatus = document.querySelector('#leadStatusFilter')?.value || '';
3842            const task = document.querySelector('#aufgabeFilter')?.value || '';
3843            const branche = document.querySelector('#brancheFilter')?.value || '';
3844            const dateFrom = document.querySelector('#dateFromFilter')?.value || '';
3845            const dateTo = document.querySelector('#dateToFilter')?.value || '';
3846            // kontakt-team-v1: Zuweisung und Importliste
3847            await this._kteamFilterEinbauen();
3848            const zuweisung = document.querySelector('#zuweisungFilter')?.value || '';
3849            const liste = document.querySelector('#listeFilter')?.value || '';
3850            const eigeneUid = auth.currentUser?.uid || '';
3851
3852            // Filter the existing contacts instead of loading them again
3853            let filteredContacts = [...this.contacts];
3854
3855            // Wenn nicht alle Kontakte geladen sind und es eine Suche gibt, zeige Hinweis
3856            if (searchTerm && !this.backgroundLoadingComplete) {
3857            // console.log(`Suche in ${this.contacts.length} geladenen Kontakten. Hintergrund-Laden läuft noch...`);
3858                // Optional: Zeige einen Hinweis an den Benutzer
3859                const searchInfo = document.getElementById('searchInfo');
3860                if (searchInfo) {
3861                    searchInfo.innerHTML = `<small class="text-muted">Durchsuche ${this.contacts.length} Kontakte... (weitere werden geladen)</small>`;
3862                    searchInfo.style.display = 'block';
3863                }
3864            }
3865
3866            // Apply filters
3867            filteredContacts = filteredContacts.filter(contact => {
3868                const searchFields = [
3869                    contact.name,
3870                    contact.email,
3871                    contact.company,
3872                    contact.phone_number,
3873                    contact.industry
3874                ].map(suchText).filter(Boolean);  // kontakte-filter-v1
3875
3876                const matchesSearch = searchTerm === '' || searchFields.some(field => field.includes(searchTerm));
3877                const matchesStatus = !leadStatus || contact.lead_status === leadStatus;
3878                const matchesTask = !task || contact.task === task;
3879                const matchesBranche = !branche || contact.industry === branche;
3880
3881                // Datumsbereich-Filter für updated_at
3882                let matchesDateRange = true;
3883                if (dateFrom || dateTo) {
3884                    const contactDate = this.parseContactDate(contact.updated_at);
3885                    
3886                    if (contactDate) {
3887                        if (dateFrom) {
3888                            const fromDate = new Date(dateFrom);
3889                            fromDate.setHours(0, 0, 0, 0); // Start des Tages
3890                            matchesDateRange = matchesDateRange && contactDate >= fromDate;
3891                        }
3892                        
3893                        if (dateTo) {
3894                            const toDate = new Date(dateTo);
3895                            toDate.setHours(23, 59, 59, 999); // Ende des Tages
3896                            matchesDateRange = matchesDateRange && contactDate <= toDate;
3897                        }
3898                    } else {
3899                        // Wenn kein Datum vorhanden ist, Filter nicht erfüllt
3900                        matchesDateRange = false;
3901                    }
3902                }
3903
3904                const matchesTeam = KTeam.passtZuweisung(contact, zuweisung, eigeneUid);  // kontakt-team-v1
3905                const matchesListe = !liste || (contact.liste || '') === liste;
3906                return matchesSearch && matchesStatus && matchesTask && matchesBranche && matchesDateRange && matchesTeam && matchesListe;
3907            });
3908
3909            // Store the filtered contacts
3910            this.currentFilteredContacts = filteredContacts;
3911
3912            // Render the filtered contacts - this removes old DOM listeners implicitly
3913            await this.renderContacts(filteredContacts);
3914            
3915            // Set up event listeners for the new DOM elements
3916            this.setupContactEventListeners(); // Zurück geändert
3917
3918            // Restore the Smart Lists UI as renderContacts replaced the placeholder
3919            this.updateSmartListsUI();
3920
3921        } catch (error) {
3922            console.error('Error applying filters:', error);
3923            UI.showError('Error filtering contacts');
3924        } finally {
3925            // Stelle den Button-Text nur wieder her, wenn der Button existiert
3926            if (filterButton) {
3927                filterButton.innerHTML = originalButtonText;
3928                filterButton.disabled = false;
3929            }
3930            // hideContactsLoadingIndicator(); // Ladeindikator deaktiviert
3931        }
3932
3933        // Nach dem Filtern die gefilterten Kontakte im Cache speichern
3934        // kontakte-cache-v1: Der Schluessel enthielt Date.now(), also legte JEDER
3935        // Filtervorgang einen NEUEN localStorage-Eintrag mit der kompletten Liste an
3936        // (bei einem mittleren Mandanten ~230 KB pro Eintrag). Nach wenigen Suchen
3937        // war das 5-MB-Kontingent erschoepft, der QuotaExceeded-Fehler wurde still
3938        // verschluckt und danach funktionierte gar kein Cache mehr. Fe
3938ster
3939        // Schluessel: der letzte Stand ueberschreibt den vorigen.
3940        const cacheKey = `filtered_contacts_${this.currentUserUID}`;
3941        setCachedFilteredContacts(cacheKey, this.currentFilteredContacts);
3942    }
3943
3944    async bindEvents() {
3945            // console.log("[bindEvents] Function called."); // NEUER LOG AM ANFANG
3946        // Wait for DOM to be ready
3947        if (!document.getElementById('contactsContainer')) {
3948            console.warn('Contacts container not found, skipping event binding');
3949            return;
3950        }
3951
3952        // Initialize modal if not already done
3953        this.initializeModal();
3954
3955        // New contact button handler
3956        const newContactBtn = document.getElementById('newContactBtn');
3957        if (newContactBtn) {
3958            newContactBtn.addEventListener('click', () => this.showNewContactModal());
3959        }
3960        
3961        // Mobile New contact floating button handler
3962        const mobileNewContactBtn = document.getElementById('mobileNewContactBtn');
3963        if (mobileNewContactBtn) {
3964            mobileNewContactBtn.addEventListener('click', () => this.showNewContactModal());
3965        }
3966
3967        // Mobile contact card click handlers mit Event-Delegation
3968        const mobileContactsContainer = document.querySelector('.mobile-contacts-container');
3969        console.log('[Mobile Click Setup] Container gefunden:', !!mobileContactsContainer);
3970        
3971        if (mobileContactsContainer) {
3972            // Zähle mobile Kontakt-Karten
3973            const mobileCards = mobileContactsContainer.querySelectorAll('.mobile-contact-card');
3974            console.log('[Mobile Click Setup] Anzahl mobile Karten:', mobileCards.length);
3975            
3976            // Entferne alte Listener falls vorhanden
3977            if (this.mobileContactClickHandler) {
3978                mobileContactsContainer.removeEventListener('click', this.mobileContactClickHandler);
3979            }
3980            
3981            // Definiere neuen Handler
3982            this.mobileContactClickHandler = async (e) => {
3983                console.log('[Mobile Click] Click-Event ausgelöst auf:', e.target);
3984                
3985                // Finde den geklickten Header oder die Karte selbst
3986                const card = e.target.closest('.mobile-contact-card');
3987                const header = e.target.closest('.mobile-contact-header');
3988                
3989                console.log('[Mobile Click] Card gefunden:', !!card, 'Header gefunden:', !!header);
3990                
3991                if (!card && !header) return;
3992                
3993                // Verhindere Click wenn Checkbox oder Delete-Button geklickt wurde
3994                if (e.target.closest('.mobile-contact-checkbox') || 
3995                    e.target.closest('.mobile-delete-btn') ||
3996                    e.target.closest('input[type="checkbox"]') ||
3997                    e.target.closest('button.delete-contact')) {
3998                    console.log('[Mobile Click] Click auf Checkbox oder Delete-Button - ignoriert');
3999                    return;
4000                }
4001                
4002                // Hole contactId von Header oder Card
4003                const contactId = (header && header.dataset.id) || (card && card.dataset.contactId);
4004                console.log('[Mobile Click] Contact ID:', contactId);
4005                
4006                if (contactId) {
4007                    console.log('[Mobile Click] Öffne Kontakt-Details für ID:', contactId);
4008                    
4009                    // Prüfe ob ContactDetail verfügbar ist
4010                    if (typeof ContactDetail !== 'undefined' && ContactDetail.show) {
4011                        console.log('[Mobile Click] ContactDetail.show() wird aufgerufen');
4012                        ContactDetail.show(contactId);
4013                    } else {
4014                        console.log('[Mobile Click] ContactDetail nicht gefunden, lade dynamisch...');
4015                        // Fallback: Lade ContactDetail dynamisch
4016                        try {
4017                            const module = await import('./contactDetail.js?v=20260807-noshow2-soap1-p5f1-rrev1-bill1-intake1-alarm1-tokfix1-dat1-mod1b-nam1-merk1--kal1-kont1--kd1-prax1-nsl1-nsl2-impauth1-tdup2-kf1-kf3-kteam1-kt3');
4018                            const ContactDetailModule = module.default || module.ContactDetail;
4019                            if (ContactDetailModule && ContactDetailModule.show) {
4020                                console.log('[Mobile Click] ContactDetail dynamisch geladen und aufgerufen');
4021                                ContactDetailModule.show(contactId);
4022                            } else {
4023                                console.error('[Mobile Click] ContactDetail.show nicht gefunden im Modul');
4024                            }
4025                        } catch (error) {
4026                            console.error('[Mobile Click] Fehler beim Laden von ContactDetail:', error);
4027                        }
4028                    }
4029                } else {
4030                    console.warn('[Mobile Click] Keine Contact ID gefunden');
4031                }
4032            };
4033            
4034            // Füge Event-Listener hinzu
4035            mobileContactsContainer.addEventListener('click', this.mobileContactClickHandler);
4036            console.log('[Mobile Click Setup] Event-Listener hinzugefügt');
4037        } else {
4038            console.warn('[Mobile Click Setup] Mobile Container nicht gefunden - vermutlich Desktop-Ansicht');
4039        }
4040
4041        // Mobile pagination handlers
4042        document.querySelectorAll('.mobile-pagination-btn').forEach(btn => {
4043            btn.addEventListener('click', (e) => {
4044                e.preventDefault();
4045                const page = parseInt(btn.dataset.page);
4046                if (!btn.disabled && page) {
4047                    this.currentPage = page;
4048                    this.renderContacts(this.currentFilteredContacts || this.contacts);
4049                }
4050            });
4051        });
4052
4053        // Mobile bulk actions handlers
4054        const mobileBulkEdit = document.getElementById('mobileBulkEdit');
4055        if (mobileBulkEdit) {
4056            mobileBulkEdit.addEventListener('click', () => this.handleBulkUpdate());
4057        }
4058
4059        const mobileBulkDelete = document.getElementById('mobileBulkDelete');
4060        if (mobileBulkDelete) {
4061            mobileBulkDelete.addEventListener('click', () => this.handleBulkDelete());
4062        }
4063
4064        const mobileCancelSelection = document.getElementById('mobileCancelSelection');
4065        if (mobileCancelSelection) {
4066            mobileCancelSelection.addEventListener('click', () => {
4067                this.deselectAllContacts();
4068                this.updateBulkActionsVisibility();
4069            });
4070        }
4071
4072        // Import button handler
4073        const importBtn = document.getElementById('importContactsBtn');
4074        if (importBtn) {
4075            importBtn.addEventListener('click', (e) => {
4076                e.stopPropagation();
4077                this.contactImportUI.show();
4078            });
4079        }
4080
4081        // Smart List button handler
4082        const createSmartListBtn = document.getElementById('createSmartListBtn');
4083        if (createSmartListBtn) {
4084            createSmartListBtn.addEventListener('click', () => {
4085                // Übersetze das Modal vor dem Anzeigen
4086                this.translateSmartListModal();
4087                if (this.smartListModal) {
4088                    this.smartListModal.show();
4089                }
4090            });
4091        }
4092
4093        // Add event listener for group email sending
4094        const sendGroupEmailBtn = document.getElementById('sendGroupEmailBtn');
4095        if (sendGroupEmailBtn) {
4096            sendGroupEmailBtn.addEventListener('click', () => this.handleSendGroupEmail());
4097        }
4098
4099        // Add event listener for Sofort Anruf button
4100        const sofortAnrufBtn = document.getElementById('sofortAnrufBtn');
4101        if (sofortAnrufBtn) {
4102            sofortAnrufBtn.addEventListener('click', () => this.handleSofortAnruf());
4103        }
4104
4105        // Add event listener for AutoCall button
4106        const autoCallBtn = document.getElementById('autoCallBtn');
4107        if (autoCallBtn) {
4108            autoCallBtn.addEventListener('click', () => this.handleAutoCall());
4109        }
4110
4111        // Clear any existing handlers
4112        if (this.checkboxHandler) {
4113            const oldCheckboxes = document.querySelectorAll('.contact-checkbox');
4114            oldCheckboxes.forEach(checkbox => {
4115                checkbox.removeEventListener('change', this.checkboxHandler);
4116            });
4117        }
4118
4119        // Store event handler references
4120        this.bulkUpdateHandler = () => this.handleBulkUpdate();
4121        this.bulkDeleteHandler = () => this.handleBulkDelete();
4122        this.checkboxHandler = () => this.updateBulkActionsVisibility();
4123
4124        // Bind checkboxes
4125        const checkboxes = document.querySelectorAll('.contact-checkbox');
4126        checkboxes.forEach(checkbox => {
4127            checkbox.addEventListener('change', this.checkboxHandler);
4128        });
4129
4130        // Bind select all checkbox
4131        const selectAllCheckbox = document.getElementById('selectAll');
4132        if (selectAllCheckbox) {
4133            this.selectAllHandler = (e) => {
4134                const isChecked = e.target.checked;
4135                const visibleCheckboxes = document.querySelectorAll('.contact-row:not([style*="display: none"]) .contact-checkbox');
4136                visibleCheckboxes.forEach(checkbox => {
4137                    checkbox.checked = isChecked;
4138                });
4139                this.updateBulkActionsVisibility();
4140            };
4141            selectAllCheckbox.removeEventListener('change', this.selectAllHandler);
4142            selectAllCheckbox.addEventListener('change', this.selectAllHandler);
4143        }
4144        
4145        // Search
4146        const searchInput = document.querySelector('#searchInput');
4147        if (searchInput) {
4148            searchInput.removeEventListener('input', e => this.debounceSearch(e));
4149            searchInput.addEventListener('input', e => this.debounceSearch(e));
4150        }
4151
4152        // Clear search button
4153        const clearSearchBtn = document.querySelector('#clearSearchBtn');
4154        if (clearSearchBtn) {
4155            this.clearSearchHandler = async () => {
4156                const searchInput = document.querySelector('#searchInput');
4157                if (searchInput) {
4158                    searchInput.value = '';
4159                    // Wenn Suche gelöscht wird, ist keine Smart List mehr aktiv
4160                    localStorage.removeItem('activeSmartListId');
4161                    this.activeSmartListId = null;
4162                    this.applyFilters(); // Wende leere Filter an, um alle Kontakte zu zeigen
4163                }
4164            };
4165            clearSearchBtn.removeEventListener('click', this.clearSearchHandler);
4166            clearSearchBtn.addEventListener('click', this.clearSearchHandler);
4167        }
4168
4169        // Bind all filter dropdowns
4170        const leadStatusFilter = document.querySelector('#leadStatusFilter');
4171        const taskFilter = document.querySelector('#aufgabeFilter');
4172        const brancheFilter = document.querySelector('#brancheFilter');
4173        const dateFromFilter = document.querySelector('#dateFromFilter');
4174        const dateToFilter = document.querySelector('#dateToFilter');
4175        const clearDateFilterBtn = document.querySelector('#clearDateFilterBtn');
4176
4177        // Erstelle einen einzigen Filter-Handler für alle Filteränderungen
4178        this.filterChangeHandler = () => {
4179            // Füge eine kleine Verzögerung hinzu, falls mehrere Filter schnell geändert werden
4180            if (this.filterTimeout) {
4181                clearTimeout(this.filterTimeout);
4182            }
4183            this.filterTimeout = setTimeout(() => {
4184                this.applyFilters();
4185            }, 300); // 300ms Verzögerung
4186        };
4187
4188        [leadStatusFilter, taskFilter, brancheFilter, dateFromFilter, dateToFilter].forEach(filter => {
4189            if (filter) {
4190                // Entferne zuerst alle vorherigen Listener für diesen Handler
4191                filter.removeEventListener('change', this.filterChangeHandler);
4192                // Füge den neuen Handler hinzu
4193                filter.addEventListener('change', this.filterChangeHandler);
4194            }
4195        });
4196
4197        // Clear Date Filter Button
4198        if (clearDateFilterBtn) {
4199            this.clearDateFilterHandler = () => {
4200                const dateFromFilter = document.querySelector('#dateFromFilter');
4201                const dateToFilter = document.querySelector('#dateToFilter');
4202                if (dateFromFilter) dateFromFilter.value = '';
4203                if (dateToFilter) dateToFilter.value = '';
4204                this.applyFilters();
4205            };
4206            clearDateFilterBtn.removeEventListener('click', this.clearDateFilterHandler);
4207            clearDateFilterBtn.addEventListener('click', this.clearDateFilterHandler);
4208        }
4209
4210        // --- ÄNDERUNG: Zurück zur Logik aus contacts.js2 ---
4211        // Die Handler werden *nicht* hier definiert, sondern sind die Funktionen selbst.
4212        // Entferne die vorherigen Definitionen von this.bulkUpdateHandler und this.bulkDeleteHandler
4213
4214        // Bind bulk action buttons (ruft direkt die implementierenden Funktionen auf)
4215        const bulkUpdateBtn = document.getElementById('bulkUpdateBtn');
4216        const bulkDeleteBtn = document.getElementById('bulkDeleteBtn');
4217
4218        if (bulkUpdateBtn) {
4219            // console.log("[bindEvents] Trying to attach listener to #bulkUpdateBtn. Element found:", bulkUpdateBtn);
4220            // console.log("[bindEvents] #bulkUpdateBtn disabled status:", bulkUpdateBtn.disabled);
4221            // Stelle sicher, dass alte Listener entfernt werden (cloneNode ist eine robuste Methode)
4222            const newBulkUpdateBtn = bulkUpdateBtn.cloneNode(true);
4223            bulkUpdateBtn.parentNode.replaceChild(newBulkUpdateBtn, bulkUpdateBtn);
4224            // Füge den Listener hinzu, der direkt die Funktion aufruft
4225            newBulkUpdateBtn.addEventListener('click', () => {
4226                // console.log("[bindEvents] #bulkUpdateBtn clicked! Calling handleBulkUpdate..."); // Log wird jetzt in handleBulkUpdate gemacht
4227                this.handleBulkUpdate();
4228            });
4229            // console.log("[bindEvents] Listener ATTACHED to #bulkUpdateBtn.");
4230        } else {
4231            console.error("[bindEvents] #bulkUpdateBtn element NOT FOUND!");
4232        }
4233
4234        if (bulkDeleteBtn) {
4235            // console.log("[bindEvents] Trying to attach listener to #bulkDeleteBtn. Element found:", bulkDeleteBtn);
4236            // Stelle sicher, dass alte Listener entfernt werden
4237            const newBulkDeleteBtn = bulkDeleteBtn.cloneNode(true);
4238            bulkDeleteBtn.parentNode.replaceChild(newBulkDeleteBtn, bulkDeleteBtn);
4239            // Füge den Listener hinzu, der direkt die Funktion aufruft
4240            newBulkDeleteBtn.addEventListener('click', () => {
4241                // console.log("[bindEvents] #bulkDeleteBtn clicked! Calling handleBulkDelete..."); // Log wird jetzt in handleBulkDelete gemacht
4242                this.handleBulkDelete();
4243            });
4244            // console.log("[bindEvents] Listener ATTACHED to #bulkDeleteBtn.");
4245        } else {
4246            console.error("[bindEvents] #bulkDeleteBtn element NOT FOUND!");
4247        }
4248        // --- ÄNDERUNG ENDE ---
4249
4250        // Bind row click handlers
4251        const contactRows = document.querySelectorAll('.contact-row');
4252        contactRows.forEach(row => {
4253            row.addEventListener('click', async (e) => {
4254                // Don't navigate if clicking checkbox or delete button
4255                if (e.target.closest('.contact-checkbox') ||
4256                    (e.target.closest('.contact-noclick') && !e.target.closest('.contact-open-item')) ||
4257                    e.target.closest('.delete-contact')) {
4258                    return;
4259                }
4260                
4261                e.preventDefault();
4262                const contactId = row.dataset.contactId;
4263                
4264                // NEUE FUNKTION: Speichere den aktuellen Zustand vor der Navigation
4265                this.storeContactsState();
4266                
4267                const url = new URL(window.location.href);
4268                url.searchParams.set('page', 'contact-detail');
4269                url.searchParams.set('id', contactId);
4270                window.history.pushState({}, '', url);
4271                await Pages.loadPage('contact-detail');
4272            });
4273        });
4274
4275        // Bind delete buttons
4276        const deleteButtons = document.querySelectorAll('.delete-contact');
4277        deleteButtons.forEach(button => {
4278            button.addEventListener('click', async (e) => {
4279                e.stopPropagation(); // Prevent row click
4280                const contactId = button.dataset.id;
4281                if (confirm(t('Sind Sie sicher, dass Sie diesen Kontakt löschen möchten?'))) {
4282                    await this.deleteContact(contactId);
4283                }
4284            });
4285        });
4286
4287        // Add pagination event listeners
4288        document.addEventListener('click', (e) => {
4289            if (e.target.closest('.pagination')) {
4290                e.preventDefault();
4291                const pageLink = e.target.closest('.page-link');
4292                if (pageLink) {
4293                    const newPage = parseInt(pageLink.dataset.page);
4294                    if (!isNaN(newPage) && newPage !== this.currentPage) {
4295                        this.currentPage = newPage;
4296                        this.applyFilters(); // This will trigger a re-render with the new page
4297                    }
4298                }
4299            }
4300        });
4301
4302        // Mark events as bound
4303        this.eventsBound = true;
4304
4305        // ✅ FEHLENDER EVENT-LISTENER FÜR EDIT-CONTACT-MODAL HINZUFÜGEN
4306        const editContactForm = document.getElementById('editContactForm');
4307        if (editContactForm) {
4308            editContactForm.addEventListener('submit', async (e) => {
4309                e.preventDefault();
4310            // console.log('Edit contact form submitted');
4311                
4312                try {
4313                    const contactId = document.getElementById('editContactId').value;
4314                    if (!contactId) {
4315                        throw new Error('Keine Kontakt-ID gefunden');
4316                    }
4317                    
4318                    // Sammle alle Formulardaten
4319                    const formData = new FormData(editContactForm);
4320                    const contactData = {};
4321                    
4322                    // Konvertiere FormData zu Objekt
4323                    formData.forEach((value, key) => {
4324                        contactData[key] = value;
4325                    });
4326                    
4327            // console.log('Updating contact with data:', contactData);
4328                    
4329                    // Rufe die Update-Funktion auf
4330                    await this.updateContact(contactId, contactData);
4331                    
4332                    // Schließe das Modal
4333                    const modal = bootstrap.Modal.getInstance(document.getElementById('editContactModal'));
4334                    if (modal) {
4335                        modal.hide();
4336                    }
4337                    
4338                    // Aktualisiere die Kontaktliste
4339                    await this.loadContacts();
4340                    
4341                    UI.showSuccess('Kontakt erfolgreich aktualisiert');
4342                    
4343                } catch (error) {
4344                    console.error('Error updating contact:', error);
4345                    UI.showError('Fehler beim Aktualisieren des Kontakts: ' + error.message);
4346                }
4347            });
4348        }
4349    }
4350
4351    updateBulkActionsVisibility() {
4352        const checkedBoxes = document.querySelectorAll('.contact-checkbox:checked');
4353        const bulkActionsSection = document.querySelector('.bulk-actions-container'); // Klasse verwenden statt ID
4354        
4355        if (bulkActionsSection) {
4356            if (checkedBoxes.length > 0) {
4357                bulkActionsSection.style.display = 'block';
4358            } else {
4359                bulkActionsSection.style.display = 'none';
4360            }
4361        }
4362
4363        // Mobile Bulk Actions Bar
4364        const mobileBulkActions = document.getElementById('mobileBulkActions');
4365        if (mobileBulkActions) {
4366            if (checkedBoxes.length > 0) {
4367                mobileBulkActions.classList.add('show');
4368                const mobileSelectedCount = document.getElementById('mobileSelectedCount');
4369                if (mobileSelectedCount) {
4370                    mobileSelectedCount.textContent = checkedBoxes.length;
4371                }
4372            } else {
4373                mobileBulkActions.classList.remove('show');
4374            }
4375        }
4376
4377        // Mobile contact cards selection state
4378        document.querySelectorAll('.mobile-contact-card').forEach(card => {
4379            const checkbox = card.querySelector('.contact-checkbox');
4380            if (checkbox && checkbox.checked) {
4381                card.classList.add('selected');
4382            } else {
4383                card.classList.remove('selected');
4384            }
4385        });
4386
4387        // Aktualisiere den Zustand des mobilen "Alle auswählen"-Buttons
4388        const selectAllMobileBtn = document.getElementById('selectAllMobile');
4389        if (selectAllMobileBtn) {
4390            const allCheckboxes = document.querySelectorAll('.contact-checkbox');
4391            const allSelected = checkedBoxes.length === allCheckboxes.length && allCheckboxes.length > 0;
4392            if (allSelected) {
4393                 selectAllMobileBtn.innerHTML = `<i class="fas fa-minus-square me-1"></i>${t('Alle abwählen')}`;
4394            } else {
4395                 selectAllMobileBtn.innerHTML = `<i class="fas fa-check-square me-1"></i>${t('Alle auswählen')}`;
4396            }
4397        }
4398    }
4399
4400    removeEventListeners() {
4401        // Remove new contact button handler
4402        const newContactBtn = document.querySelector('#newContactBtn');
4403        if (newContactBtn && this.newContactHandler) {
4404            newContactBtn.removeEventListener('click', this.newContactHandler);
4405        }
4406
4407        // Remove checkbox listeners
4408        const checkboxes = document.querySelectorAll('.contact-checkbox');
4409        if (this.checkboxHandler) {
4410            checkboxes.forEach(checkbox => {
4411                checkbox.removeEventListener('change', this.checkboxHandler);
4412            });
4413        }
4414
4415        // Remove select all listener
4416        const selectAll = document.getElementById('selectAll');
4417        if (selectAll && this.selectAllHandler) {
4418            selectAll.removeEventListener('change', this.selectAllHandler);
4419        }
4420
4421        // Remove bulk action listeners
4422        const bulkUpdateBtn = document.querySelector('#bulkUpdateBtn');
4423        if (bulkUpdateBtn && this.bulkUpdateHandler) {
4424            bulkUpdateBtn.removeEventListener('click', this.bulkUpdateHandler);
4425        }
4426
4427        const bulkDeleteBtn = document.querySelector('#bulkDeleteBtn');
4428        if (bulkDeleteBtn && this.bulkDeleteHandler) {
4429            bulkDeleteBtn.removeEventListener('click', this.bulkDeleteHandler);
4430        }
4431
4432        // Remove clear search handler
4433        const clearSearchBtn = document.querySelector('#clearSearchBtn');
4434        if (clearSearchBtn && this.clearSearchHandler) {
4435            clearSearchBtn.removeEventListener('click', this.clearSearchHandler);
4436        }
4437
4438        // Remove row click handlers
4439        const contactRows = document.querySelectorAll('.contact-row');
4440        contactRows.forEach(row => {
4441            row.removeEventListener('click', this.rowClickHandler);
4442        });
4443
4444        // Remove delete button handlers
4445        const deleteButtons = document.querySelectorAll('.delete-contact');
4446        deleteButtons.forEach(button => {
4447            button.removeEventListener('click', this.deleteHandler);
4448        });
4449
4450        // Remove group email button handler
4451        const sendGroupEmailBtn = document.getElementById('sendGroupEmailBtn');
4452        if (sendGroupEmailBtn && this.sendGroupEmailHandler) {
4453            sendGroupEmailBtn.removeEventListener('click', this.sendGroupEmailHandler);
4454        }
4455    }
4456
4457    async createNewContact() {
4458        const form = document.getElementById('addContactForm');
4459        if (!form) return;
4460
4461        if (!form.checkValidity()) {
4462            form.classList.add('was-validated');
4463            return;
4464        }
4465
4466        try {
4467            // Get form data using form elements
4468            const formData = new FormData(form);
4469            const contactData = {
4470                name: formData.get('name'),
4471                email: formData.get('email'),
4472                company: formData.get('company'),
4473                phone_number: formData.get('phone'),
4474                industry: formData.get('industry'),
4475                lead_status: formData.get('lead_status'),
4476                task: formData.get('aufgabe'),
4477                street: formData.get('street'),
4478                zip: formData.get('zip'),
4479                city: formData.get('city'),
4480                website: formData.get('website'),
4481                created_at: serverTimestamp(),
4482                updated_at: serverTimestamp(),
4483                customer_id: Math.floor(Math.random() * 10000000000).toString(), // Generate customer_id as string
4484            };
4485
4486            // kundenfelder-v1: eigene Felder aus dem Anlage-Modal uebernehmen
4487            try {
4488                const cfDefs = this._cfDefs || await ladeKundenfelder();
4489                const cfWerte = sammleKundenfelder(form, cfDefs);
4490                if (Object.keys(cfWerte).length) contactData.custom = cfWerte;
4491            } catch (e) { console.warn('Kundenfelder nicht uebernommen:', e); }
4492
4493            const user = auth.currentUser;
4494            if (!user) throw new Error('No user logged in');
4495
4496            // Add user ID to contact data
4497            contactData.uid = user.uid;
4498
4499            const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
4500            if (!userDoc.exists()) throw new Error('User profile not found');
4501
4502            const userData = userDoc.data();
4503            const tenantId = userData.tenantID || userData.tenant_id;
4504            if (!tenantId) throw new Error('No tenant ID found for user');
4505
4506            contactData.tenantID = tenantId.toString();
4507
4508            await this.createContact(contactData);
4509            
4510            // Hide modal first
4511            if (this.addContactModal) {
4512                this.addContactModal.hide();
4513            }
4514
4515            // Wait for modal to finish hiding
4516            await new Promise(resolve => setTimeout(resolve, 150));
4517            
4518            // Reload contacts and show success message
4519            await this.loadContacts();
4520            UI.showSuccess('Kontakt erfolgreich erstellt');
4521        } catch (error) {
4522            console.error('Error creating contact:', error);
4523            UI.showError('Fehler beim Erstellen des Kontakts');
4524        }
4525    }
4526
4527    async restoreContactOrder(originalOrder) {
4528        // Stelle die ursprüngliche Reihenfolge der Kontakte wieder her
4529        try {
4530            console.log('[restoreContactOrder] Stelle Reihenfolge wieder her für', originalOrder.length, 'Kontakte');
4531            
4532            // Erstelle eine Map für schnelles Lookup
4533            const orderMap = new Map();
4534            originalOrder.forEach((id, index) => {
4535                orderMap.set(id, index);
4536            });
4537            
4538            // Sortiere die aktuell gefilterten Kontakte oder alle Kontakte
4539            const contactsToSort = this.currentFilteredContacts || this.contacts;
4540            
4541            // Sortiere basierend auf der ursprünglichen Reihenfolge
4542            const sortedContacts = [...contactsToSort].sort((a, b) => {
4543                const aIndex = orderMap.get(a.id);
4544                const bIndex = orderMap.get(b.id);
4545                
4546                // Wenn beide in der ursprünglichen Reihenfolge waren
4547                if (aIndex !== undefined && bIndex !== undefined) {
4548                    return aIndex - bIndex;
4549                }
4550                
4551                // Neue Kontakte (nicht in der ursprünglichen Reihenfolge) ans Ende
4552                if (aIndex === undefined && bIndex !== undefined) return 1;
4553                if (aIndex !== undefined && bIndex === undefined) return -1;
4554                
4555                // Beide neu - behalte ihre aktuelle Reihenfolge
4556                return 0;
4557            });
4558            
4559            // Aktualisiere die entsprechende Liste
4560            if (this.currentFilteredContacts) {
4561                this.currentFilteredContacts = sortedContacts;
4562            } else {
4563                this.contacts = sortedContacts;
4564            }
4565            
4566            // Rendere die Kontakte neu mit der wiederhergestellten Sortierung
4567            await this.renderContacts(this.currentFilteredContacts || this.contacts);
4568            console.log('[restoreContactOrder] Sortierung wiederhergestellt und gerendert');
4569            
4570        } catch (error) {
4571            console.error('[restoreContactOrder] Fehler beim Wiederherstellen der Sortierung:', error);
4572        }
4573    }
4574
4575    async handleBulkUpdate() { // Erwartet keine Argumente mehr
4576            // console.log('[handleBulkUpdate] Click detected and function called.'); // NEUER LOG GANZ AM ANFANG
4577            // console.log('[handleBulkUpdate] Funktion aufgerufen.'); // LOGGING
4578        try {
4579            const selectedContactIds = this.getSelectedContactIds(); // Holt IDs innerhalb der Funktion
4580            // console.log('[handleBulkUpdate] Ausgewählte IDs:', selectedContactIds); // LOGGING
4581            if (!selectedContactIds.length) {
4582                UI.showError('Keine Kontakte ausgewählt');
4583                return;
4584            }
4585
4586            // Holt Werte aus dem DOM innerhalb der Funktion
4587            const leadStatusElement = document.getElementById('bulkLeadStatus');
4588            const taskElement = document.getElementById('bulkAufgabe'); // Korrigierte ID
4589            const industryElement = document.getElementById('bulkIndustry');
4590
4591            const leadStatus = leadStatusElement ? leadStatusElement.value : null;
4592            const task = taskElement ? taskElement.value : null;
4593            const industry = industryElement ? industryElement.value : null;
4594            // console.log('[handleBulkUpdate] Gesammelte Werte:', { leadStatus, task, industry }); // LOGGING
4595
4596            const updateData = {};
4597            if (leadStatus) updateData.lead_status = leadStatus;
4598            if (task) updateData.task = task;
4599            if (industry) updateData.industry = industry;
4600            // console.log('[handleBulkUpdate] Update Data Objekt:', updateData); // LOGGING
4601
4602            if (Object.keys(updateData).length === 0) {
4603            // console.log('[handleBulkUpdate] Keine Änderungen ausgewählt.'); // LOGGING
4604                UI.showError('Bitte wählen Sie mindestens eine Änderung aus (Lead-Status, Aufgabe oder Branche).');
4605                return;
4606            }
4607
4608             // Bestätigungsdialog
4609            const changes = Object.entries(updateData).map(([key, value]) => {
4610                const fieldName = key.replace('_', ' ');
4611                // Übersetze den Wert, falls es ein Lead-Status ist
4612                const translatedValue = key === 'lead_status' ? t(value) : value;
4613                return `${fieldName} ${t('zu')} '${translatedValue}'`;
4614            }).join(', ');
4615            // console.log('[handleBulkUpdate] Zeige Bestätigungsdialog...'); // LOGGING
4616            if (!confirm(`${t('Möchten Sie für')} ${selectedContactIds.length} ${t('Kontakte wirklich die folgenden Änderungen vornehmen')}: ${changes}?`)) {
4617            // console.log('[handleBulkUpdate] Bestätigungsdialog abgelehnt.'); // LOGGING
4618                 return; // Abbruch, wenn nicht bestätigt
4619            }
4620            // console.log('[handleBulkUpdate] Bestätigt. Starte Batch-Verarbeitung...'); // LOGGING
4621
4622            // --- Zeige prominenten Ladeindikator ---
4623            const updateLoadingOverlay = document.createElement('div');
4624            updateLoadingOverlay.id = 'bulkUpdateLoadingOverlay';
4625            updateLoadingOverlay.style.cssText = `
4626                position: fixed;
4627                top: 0;
4628                left: 0;
4629                width: 100%;
4630                height: 100%;
4631                background: rgba(0, 0, 0, 0.7);
4632                display: flex;
4633                justify-content: center;
4634                align-items: center;
4635                z-index: 99999;
4636            `;
4637            updateLoadingOverlay.innerHTML = `
4638                <div style="background: white; padding: 30px; border-radius: 10px; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
4639                    <div class="spinner-border text-primary mb-3" style="width: 3rem; height: 3rem;" role="status">
4640                        <span class="visually-hidden">Wird geladen...</span>
4641                    </div>
4642                    <h5 id="bulkUpdateStatus">
4642Aktualisiere ${selectedContactIds.length} Kontakte...</h5>
4643                    <div id="bulkUpdateProgress" style="margin-top: 10px; color: #666;"></div>
4644                </div>
4645            `;
4646            document.body.appendChild(updateLoadingOverlay);
4647
4648            // --- API-basierte Verarbeitung statt Firestore-Batch ---
4649
4650            const user = auth.currentUser; // From firebase-init.js
4651            if (!user) {
4652                throw new Error("User not authenticated for bulk update.");
4653            }
4654            const idToken = await user.getIdToken();
4655
4656            let updatedCount = 0;
4657            let errorCount = 0;
4658            const errors = [];
4659
4660            // console.log('[handleBulkUpdate] Starte API-Aufrufe für alle Kontakte...');
4661
4662            // Funktion zum Verarbeiten von Kontakten in Chunks
4663            const processInChunks = async (ids, chunkSize = 50) => {
4664                const results = [];
4665                for (let i = 0; i < ids.length; i += chunkSize) {
4666                    const chunk = ids.slice(i, i + chunkSize);
4667            // console.log(`[handleBulkUpdate] Verarbeite Chunk ${Math.floor(i/chunkSize) + 1}/${Math.ceil(ids.length/chunkSize)} (${chunk.length} Kontakte)`);
4668                    
4669                    // Update UI progress
4670                    const progressElement = document.getElementById('bulkUpdateProgress');
4671                    if (progressElement) {
4672                        progressElement.textContent = `Verarbeite... ${i + chunk.length} von ${ids.length}`;
4673                    }
4674                    
4675                    // Verarbeite diesen Chunk parallel
4676                    const chunkPromises = chunk.map(async (contactId) => {
4677                        if (!contactId) {
4678                            console.warn('[handleBulkUpdate] Ungültige Kontakt-ID übersprungen:', contactId);
4679                            return { status: 'rejected', reason: 'Invalid contact ID' };
4680                        }
4681                        try {
4682                            const response = await fetch(`/api/contacts/${contactId}`, {
4683                                method: 'PUT',
4684                                headers: {
4685                                    'Content-Type': 'application/json',
4686                                    'Authorization': `Bearer ${idToken}`
4687                                },
4688                                body: JSON.stringify(updateData)
4689                            });
4690                            if (!response.ok) {
4691                                const errorData = await response.json().catch(() => ({ message: response.statusText }));
4692                                throw new Error(`API Error ${response.status}: ${errorData.message || response.statusText}`);
4693                            }
4694                            return { status: 'fulfilled', contactId };
4695                        } catch (error) {
4696                            console.error(`[handleBulkUpdate] Fehler beim API-Aufruf für Kontakt ${contactId}:`, error);
4697                            return { status: 'rejected', contactId, reason: error.message };
4698                        }
4699                    });
4700                    
4701                    // Warte auf diesen Chunk
4702                    const chunkResults = await Promise.allSettled(chunkPromises);
4703                    results.push(...chunkResults);
4704                    
4705                    // Kleine Pause zwischen Chunks, um Server nicht zu überlasten
4706                    if (i + chunkSize < ids.length) {
4707                        await new Promise(resolve => setTimeout(resolve, 100));
4708                    }
4709                }
4710                return results;
4711            };
4712            
4713            // Verarbeite alle Kontakte in Chunks
4714            const results = await processInChunks(selectedContactIds);
4715            
4716            // Verarbeite die Ergebnisse
4717            results.forEach((promiseResult, index) => {
4718                if (promiseResult.status === 'fulfilled') {
4719                    const result = promiseResult.value;
4720                    if (result.status === 'fulfilled') {
4721                        updatedCount++;
4722                    } else if (result.status === 'rejected') {
4723                        errorCount++;
4724                        errors.push(`${result.contactId}: ${result.reason}`);
4725                    }
4726                } else {
4727                    errorCount++;
4728                    errors.push(`Kontakt ${selectedContactIds[index]}: ${promiseResult.reason}`);
4729                }
4730            });
4731
4732            // Zeige Ergebnisse an (Ladeindikator bleibt während Refresh aktiv)
4733            if (updatedCount > 0) {
4734                UI.showSuccess(`${updatedCount} von ${selectedContactIds.length} Kontakten erfolgreich aktualisiert.`);
4735            // console.log(`[handleBulkUpdate] Erfolgreich aktualisiert: ${updatedCount}/${selectedContactIds.length}`);
4736            }
4737            if (errorCount > 0) {
4738                console.error('[handleBulkUpdate] Fehler bei folgenden Kontakten:', errors);
4739                UI.showError(`${errorCount} Kontakte konnten nicht aktualisiert werden. Siehe Konsole für Details.`);
4740            }
4741            
4742            // Update Ladeindikator-Text für Refresh
4743            const statusElement = document.getElementById('bulkUpdateStatus');
4744            if (statusElement) {
4745                statusElement.textContent = 'Aktualisiere Ansicht...';
4746            }
4747            const progressElement = document.getElementById('bulkUpdateProgress');
4748            if (progressElement) {
4749                progressElement.textContent = 'Lade aktualisierte Kontakte...';
4750            }
4751
4752            // Speichere die aktuelle Sortierung vor dem Neuladen
4753            const currentContactOrder = this.currentFilteredContacts 
4754                ? this.currentFilteredContacts.map(c => c.id) 
4755                : this.contacts.map(c => c.id);
4756            console.log('[handleBulkUpdate] Aktuelle Sortierung gespeichert:', currentContactOrder.length, 'Kontakte');
4757
4758            console.log('[handleBulkUpdate] Cache leeren und Kontakte neu laden...');
4759            console.log('[handleBulkUpdate] Update Data war:', updateData);
4760            
4761            // Kleine Verzögerung, damit Firebase die Änderungen propagieren kann
4762            console.log('[handleBulkUpdate] Warte 500ms für Firebase-Propagierung...');
4763            await new Promise(resolve => setTimeout(resolve, 500));
4764            
4765            await this.clearContactsCache();
4766            console.log('[handleBulkUpdate] Starte loadContacts mit forceRefresh=true');
4767            await this.loadContacts(true); // Lädt alle Kontakte neu mit forceRefresh
4768            console.log('[handleBulkUpdate] Kontakte wurden neu geladen');
4769
4770            // Wende die aktive Smart List erneut an, falls eine aktiv war
4771            if (this.activeSmartListId) {
4772            // console.log(`[handleBulkUpdate] Wende aktive Smart List (${this.activeSmartListId}) erneut an...`);
4773                await this.applySmartList(this.activeSmartListId);
4774            } else {
4775            console.log('[handleBulkUpdate] Keine aktive Smart List, zeige alle Kontakte.');
4776                // Filter anwenden falls vorhanden
4777                await this.applyFilters();
4778                console.log('[handleBulkUpdate] Filter angewendet');
4779            }
4780            
4781            // Stelle die ursprüngliche Sortierung wieder her
4782            if (currentContactOrder.length > 0) {
4783                console.log('[handleBulkUpdate] Stelle ursprüngliche Sortierung wieder her...');
4784                await this.restoreContactOrder(currentContactOrder);
4785            }
4786
4787            this.deselectAllContacts();
4788            this.updateBulkActionsVisibility();
4789            this.updateSmartListsUI(); // Hinzugefügt: Smart-Listen UI wiederherstellen
4790            
4791            // Entferne Ladeindikator erst jetzt, nachdem alles fertig ist
4792            const updateOverlay = document.getElementById('bulkUpdateLoadingOverlay');
4793            if (updateOverlay) {
4794                updateOverlay.remove();
4795            }
4796            
4797            console.log('[handleBulkUpdate] Abgeschlossen. Rendering sollte aktualisiert sein.');
4798
4799        } catch (error) { // Fängt Fehler beim Holen der IDs oder Werte ab
4800             console.error('[handleBulkUpdate] Unerwarteter Fehler in der Funktion:', error);
4801             UI.showError('Ein unerwarteter Fehler ist bei der MassenÃ
4801¤nderung aufgetreten.');
4802             // Entferne Ladeindikator im Fehlerfall
4803             const errorOverlay = document.getElementById('bulkUpdateLoadingOverlay');
4804             if (errorOverlay) {
4805                 errorOverlay.remove();
4806             }
4807        }
4808    }
4809
4810    async handleBulkDelete() { // Erwartet keine Argumente mehr
4811            // console.log('[handleBulkDelete] Funktion aufgerufen.'); // LOGGING
4812        try { // Füge try-catch hinzu
4813            const selectedContactIds = this.getSelectedContactIds(); // Holt IDs innerhalb der Funktion
4814            // console.log('[handleBulkDelete] Ausgewählte IDs:', selectedContactIds); // LOGGING
4815            if (!selectedContactIds.length) {
4816                UI.showError('Keine Kontakte zum Löschen ausgewählt');
4817                return;
4818            }
4819
4820            // Bestätigungsdialog
4821            // console.log('[handleBulkDelete] Zeige Bestätigungsdialog...'); // LOGGING
4822            if (!confirm(`${t('Sind Sie absolut sicher, dass Sie')} ${selectedContactIds.length} ${t('Kontakte unwiderruflich löschen möchten?')}`)) {
4823            // console.log('[handleBulkDelete] Löschen abgebrochen.'); // LOGGING
4824                return;
4825            }
4826            // console.log('[handleBulkDelete] Bestätigt. Starte Batch-Verarbeitung...'); // LOGGING
4827
4828            // --- Zeige prominenten Ladeindikator für Delete ---
4829            const deleteLoadingOverlay = document.createElement('div');
4830            deleteLoadingOverlay.id = 'bulkDeleteLoadingOverlay';
4831            deleteLoadingOverlay.style.cssText = `
4832                position: fixed;
4833                top: 0;
4834                left: 0;
4835                width: 100%;
4836                height: 100%;
4837                background: rgba(0, 0, 0, 0.7);
4838                display: flex;
4839                justify-content: center;
4840                align-items: center;
4841                z-index: 99999;
4842            `;
4843            deleteLoadingOverlay.innerHTML = `
4844                <div style="background: white; padding: 30px; border-radius: 10px; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
4845                    <div class="spinner-border text-danger mb-3" style="width: 3rem; height: 3rem;" role="status">
4846                        <span class="visually-hidden">Wird geladen...</span>
4847                    </div>
4848                    <h5 id="bulkDeleteStatus">Lösche ${selectedContactIds.length} Kontakte...</h5>
4849                    <div id="bulkDeleteProgress" style="margin-top: 10px; color: #666;"></div>
4850                </div>
4851            `;
4852            document.body.appendChild(deleteLoadingOverlay);
4853
4854            // Welle 4: Backend-API statt direkter Firestore-Batch (siehe routes/contacts.py:bulk_delete_contacts)
4855            let deletedCount = 0;
4856            let errorCount = 0;
4857            const validIds = selectedContactIds.filter(id => !!id);
4858            errorCount = selectedContactIds.length - validIds.length;
4859            try {
4860                const idToken = await auth.currentUser.getIdToken();
4861                const CHUNK_SIZE = 500;
4862                for (let i = 0; i < validIds.length; i += CHUNK_SIZE) {
4863                    const chunk = validIds.slice(i, i + CHUNK_SIZE);
4864                    const resp = await fetch('/api/contacts/bulk-delete', {
4865                        method: 'POST',
4866                        headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${idToken}` },
4867                        body: JSON.stringify({ contact_ids: chunk }),
4868                    });
4869                    if (!resp.ok) {
4870                        const text = await resp.text().catch(() => `HTTP ${resp.status}`);
4871                        throw new Error(text || `HTTP ${resp.status}`);
4872                    }
4873                    const data = await resp.json();
4874                    deletedCount += data.deleted || 0;
4875                    errorCount += (data.errors || []).length + (data.forbidden || 0);
4876                }
4877            } catch (error) {
4878                console.error('[handleBulkDelete] API error:', error);
4879                UI.showError(`Fehler beim Löschen der Kontakte: ${error.message}`);
4880                errorCount += (validIds.length - deletedCount);
4881                UI.hideLoading();
4882                await this.loadContacts();
4883                this.deselectAllContacts();
4884                this.updateBulkActionsVisibility();
4885                this.updateSmartListsUI();
4886                return;
4887            }
4888
4889            // Zeige Ergebnisse an (Ladeindikator bleibt während Refresh aktiv)
4890            if (deletedCount > 0) {
4891                UI.showSuccess(`${deletedCount} von ${selectedContactIds.length} Kontakten erfolgreich gelöscht.`);
4892            }
4893            if (errorCount > 0) {
4894                 const invalidIdErrors = selectedContactIds.filter(id => !id).length;
4895                 const commitErrors = errorCount - invalidIdErrors;
4896                 let errorMsg = '';
4897                 if (invalidIdErrors > 0) errorMsg += `${invalidIdErrors} ungültige Kontakt-IDs übersprungen. `;
4898                 if (commitErrors > 0) errorMsg += `${commitErrors} Kontakte konnten aufgrund eines Fehlers nicht gelöscht werden.`;
4899                 UI.showError(errorMsg.trim());
4900            }
4901            
4902            // Update Ladeindikator-Text für Refresh
4903            const deleteStatusElement = document.getElementById('bulkDeleteStatus');
4904            if (deleteStatusElement) {
4905                deleteStatusElement.textContent = 'Aktualisiere Ansicht...';
4906            }
4907            const deleteProgressElement = document.getElementById('bulkDeleteProgress');
4908            if (deleteProgressElement) {
4909                deleteProgressElement.textContent = 'Lade aktualisierte Kontaktliste...';
4910            }
4911
4912            // Speichere die aktuelle Sortierung vor dem Neuladen (ohne die gelöschten Kontakte)
4913            const deletedIds = new Set(selectedContactIds);
4914            const currentContactOrder = (this.currentFilteredContacts || this.contacts)
4915                .filter(c => !deletedIds.has(c.id))
4916                .map(c => c.id);
4917            console.log('[handleBulkDelete] Aktuelle Sortierung gespeichert (ohne gel
4917öschte):', currentContactOrder.length, 'Kontakte');
4918
4919            // console.log('[handleBulkDelete] Cache leeren und Kontakte neu laden...');
4920            await this.clearContactsCache();
4921            await this.loadContacts(true); // Lädt alle Kontakte neu mit forceRefresh
4922
4923            // Wende die aktive Smart List erneut an, falls eine aktiv war
4924            if (this.activeSmartListId) {
4925            // console.log(`[handleBulkDelete] Wende aktive Smart List (${this.activeSmartListId}) erneut an...`);
4926                await this.applySmartList(this.activeSmartListId);
4927            } else {
4928            // console.log('[handleBulkDelete] Keine aktive Smart List, zeige alle Kontakte.');
4929                // Filter anwenden falls vorhanden
4930                await this.applyFilters();
4931            }
4932            
4933            // Stelle die ursprüngliche Sortierung wieder her
4934            if (currentContactOrder.length > 0) {
4935                console.log('[handleBulkDelete] Stelle ursprüngliche Sortierung wieder her...');
4936                await this.restoreContactOrder(currentContactOrder);
4937            }
4938
4939            this.deselectAllContacts();
4940            this.updateBulkActionsVisibility();
4941            this.updateSmartListsUI(); // Hinzugefügt: Smart-Listen UI wiederherstellen
4942            
4943            // Entferne Ladeindikator erst jetzt, nachdem alles fertig ist
4944            const deleteOverlay = document.getElementById('bulkDeleteLoadingOverlay');
4945            if (deleteOverlay) {
4946                deleteOverlay.remove();
4947            }
4948            
4949            // console.log('[handleBulkDelete] Abgeschlossen.');
4950
4951        } catch (error) { // Fängt Fehler beim Holen der IDs ab
4952            console.error('[handleBulkDelete] Unerwarteter Fehler in der Funktion:', error);
4953            UI.showError('Ein unerwarteter Fehler ist bei der Massenlöschung aufgetreten.');
4954            // Entferne Ladeindikator im Fehlerfall
4955            const deleteErrorOverlay = document.getElementById('bulkDeleteLoadingOverlay');
4956            if (deleteErrorOverlay) {
4957                deleteErrorOverlay.remove();
4958            }
4959        }
4960    }
4961
4962    async deleteContact(id) {
4963        try {
4964            /* phase8c */ await ContactsApi.delete(id);
4965            // Clear contacts cache (both mobile and desktop versions)
4966            const user = auth.currentUser;
4967            if (user) {
4968                const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
4969                const userData = userDoc.data();
4970                const tenantId = userData.tenantID || userData.tenant_id;
4971                if (tenantId) {
4972                    // Clear both mobile and desktop cache keys
4973                    const desktopCacheKey = `contacts_${user.uid}_${tenantId}_desktop`;
4974                    const mobileCacheKey = `contacts_${user.uid}_${tenantId}_mobile`;
4975                    const oldCacheKey = `contacts_${user.uid}_${tenantId}`; // Legacy cache key
4976                    
4977                    localStorage.removeItem(desktopCacheKey);
4978                    localStorage.removeItem(mobileCacheKey);
4979                    localStorage.removeItem(oldCacheKey);
4980                    
4981                    // Also clear last cache key
4982                    const lastCacheKey = localStorage.getItem('last_contacts_cache_key');
4983                    if (lastCacheKey) {
4984                        localStorage.removeItem(lastCacheKey);
4985                    }
4986                }
4987            }
4988            await this.loadContacts();
4989            UI.showSuccess('Kontakt wurde gelöscht');
4990        } catch (error) {
4991            console.error('Error deleting contact:', error);
4992            UI.showError('Error deleting contact. Please try again.');
4993        }
4994    }
4995
4996    async createContact(contactData) {
4997        try {
4998            /* phase8c */ const _newC_phase8c = await ContactsApi.create({...contactData}); const docRef = { id: _newC_phase8c.id };
4999
5000            // Clear contacts cache (both mobile and desktop versions)
5001            const user = auth.currentUser;
5002            if (user) {
5003                const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
5004                const userData = userDoc.data();
5005                const tenantId = userData.tenantID || userData.tenant_id;
5006                if (tenantId) {
5007                    // Clear both mobile and desktop cache keys
5008                    const desktopCacheKey = `contacts_${user.uid}_${tenantId}_desktop`;
5009                    const mobileCacheKey = `contacts_${user.uid}_${tenantId}_mobile`;
5010                    const oldCacheKey = `contacts_${user.uid}_${tenantId}`; // Legacy cache key
5011                    
5012                    localStorage.removeItem(desktopCacheKey);
5013                    localStorage.removeItem(mobileCacheKey);
5014                    localStorage.removeItem(oldCacheKey);
5015                    
5016                    // Also clear last cache key
5017                    const lastCacheKey = localStorage.getItem('last_contacts_cache_key');
5018                    if (lastCacheKey) {
5019                        localStorage.removeItem(lastCacheKey);
5020                    }
5021                }
5022            }
5023
5024            return docRef.id;
5025        } catch (error) {
5026            console.error('Error creating contact:', error);
5027            throw error;
5028        }
5029    }
5030
5031    async updateContact(contactId, contactData) {
5032        try {
5033            /* phase8c */ await ContactsApi.update(contactId, {...contactData});
5034
5035            // Clear contacts cache after successful update
5036            const user = auth.currentUser;
5037            if (user) {
5038                // Need to fetch userDoc again or ensure it's available
5039                // For simplicity, let's fetch it if not present on 'this'
5040                let tenantId = this.userDoc?.tenantID || this.userDoc?.tenant_id;
5041                if (!tenantId) {
5042                    const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 */
5043                    if (userDocSnap.exists()) {
5044                        const userData = userDocSnap.data();
5045                        tenantId = userData.tenantID || userData.tenant_id;
5046                    }
5047                }
5048
5049                if (tenantId) {
5050                    const cacheKey = `contacts_${user.uid}_${tenantId}`;
5051                    localStorage.removeItem(cacheKey);
5052            // console.log('Contacts cache cleared due to update for key:', cacheKey);
5053                    // Zusätzlich den In-Memory Cache leeren
5054                    this.contacts = [];
5055                    this.currentFilteredContacts = null;
5056            // console.log('In-memory contacts array cleared.');
5057                } else {
5058                    console.warn('Could not clear contacts cache after update: Tenant ID not found.');
5059                }
5060            } else {
5061                 console.warn('Could not clear contacts cache after update: User not logged in.');
5062            }
5063
5064        } catch (error) {
5065            console.error('Error updating contact:', error);
5066            throw error;
5067        }
5068    }
5069
5070    async updateLeadStatus(contactId, newStatus) {
5071        try {
5072            const user = auth.currentUser; // From firebase-init.js
5073            if (!user) {
5074                throw new Error("User not authenticated for updating lead status.");
5075            }
5076            const idToken = await user.getIdToken();
5077
5078            const response = await fetch(`/api/contacts/${contactId}`, {
5079                method: 'PUT',
5080                headers: {
5081                    'Content-Type': 'application/json',
5082                    'Authorization': `Bearer ${idToken}`
5083                },
5084                body: JSON.stringify({ lead_status: newStatus })
5085            });
5086
5087            if (!response.ok) {
5088                const errorData = await response.json().catch(() => ({ message: response.statusText }));
5089                throw new Error(`Failed to update lead status: ${errorData.message || response.statusText}`);
5090            }
5091            
5092            // Optional: Die UI könnte hier direkt mit den Antwortdaten aktualisiert werden,
5093            // oder man verlässt sich auf ein Neuladen/Firestore-Listener.
5094            // const updatedContact = await response.json();
5095            // console.log('Lead status updated via API for contact:', contactId);
5096
5097            // Die lokale Datenhaltung (this.contacts, this.currentFilteredContacts) 
5098            // muss möglicherweise manuell aktualisiert oder neu geladen werden, 
5099            // um die Änderungen in der UI sofort widerzuspiegeln, wenn man sich nicht auf 
5100            // ein komplettes Neuladen der Kontaktliste verlässt.
5101            // Ein gängiges Muster ist es, this.loadContacts() oder eine ähnliche Methode aufzurufen,
5102            // nachdem eine solche Operation abgeschlossen ist, oder zumindest den betroffenen Kontakt
5103            // in den lokalen Arrays zu aktualisieren.
5104
5105        } catch (error) {
5106            console.error('Error updating lead status via API:', error);
5107            // Zeige dem Benutzer einen Fehler an, falls UI-Modul vorhanden
5108            if (typeof UI !== 'undefined' && UI.showError) {
5109                UI.showError(t('Fehler beim Aktualisieren des Lead-Status: ') + error.message);
5110            }
5111            throw error; // Fehler weiterwerfen, damit der Aufrufer ihn behandeln kann
5112        }
5113    }
5114
5115    async addConversation(contactId, conversationData) {
5116        if (!contactId || !conversationData) {
5117            console.error('Ungültige Eingabe für addConversation');
5118            UI.showError('Ungültige Eingabe zum Hinzufügen des Gesprächs.');
5119            return;
5120        }
5121        try {
5122            /* phase8c addConversation */ const _existing = (await ContactsApi.get(contactId).catch(()=>({}))).conversations || [];
5123            const _newConv = { ...conversationData, id: Date.now().toString(), createdAt: new Date().toISOString() };
5124            await ContactsApi.update(contactId, { conversations: [..._existing, _newConv] });
5125            // console.log('Gespräch hinzugefügt für Kontakt:', contactId);
5126            UI.showSuccess('Gespräch erfolgreich hinzugefügt');
5127            // Optional: Lokale Daten aktualisieren oder neu laden
5128        } catch (error) {
5129            console.error('Error adding conversation:', error);
5130            UI.showError('Fehler beim Hinzufügen des Gesprächs: ' + error.message);
5131            throw error;
5132        }
5133    }
5134
5135    async deleteConversation(contactId, conversationToDelete) {
5136         if (!contactId || !conversationToDelete || !conversationToDelete.id) { // Prüfe auf conversationToDelete.id
5137             console.error('Ungültige Daten zum Löschen des Gesprächs (ID fehlt?).');
5138             UI.showError('Ungültige Daten zum Löschen des Gesprächs.');
5139             return;
5140         }
5141         try {
5142             /* phase8c deleteConversation */
5143             const _cd_full = await ContactsApi.get(contactId).catch(()=>null);
5144             const contactSnap = { exists: () => !!_cd_full, data: () => _cd_full || {} };
5145             if (contactSnap.exists()) {
5146                 const conversations = (_cd_full && _cd_full.conversations) || [];
5147                 const conversationToRemove = conversations.find(conv => conv.id === conversationToDelete.id);
5148                 if (conversationToRemove) {
5149                     await ContactsApi.update(contactId, { conversations: conversations.filter(c => c.id !== conversationToDelete.id) });
5150            // console.log('Gespräch gelöscht für Kontakt:', contactId);
5151                     UI.showSuccess('Gespräch erfolgreich gelöscht');
5152                 } else {
5153                     console.warn('Zu löschendes Gespräch nicht im Array gefunden (ID:', conversationToDelete.id, ').');
5154                     UI.showWarning('Gespräch konnte nicht gefunden werden.')
5155                 }
5156             }
5157         } catch (error) {
5158             console.error('Error deleting conversation:', error);
5159             UI.showError('Fehler beim Löschen des Gesprächs: ' + error.message);
5160             throw error;
5161         }
5162    }
5163
5164    async addNote(contactId, noteText) { // noteData in noteText geändert
5165        if (!contactId || !noteText || typeof noteText !== 'string' || noteText.trim() === '') { // Überprüfe noteText
5166            console.error('Ungültige Eingabe für addNote');
5167            UI.showError('Notiz darf nicht leer sein.');
5168            return;
5169        }
5170        try {
5171/* phase8c */ await ContactsApi.addNote(contactId, noteText);
5172            // console.log('Notiz hinzugefügt für Kontakt:', contactId);
5173            UI.showSuccess('Notiz erfolgreich hinzugefügt');
5174        } catch (error) {
5175            console.error('Error adding note:', error);
5176            UI.showError('Fehler beim Hinzufügen der Notiz: ' + error.message);
5177            throw error;
5178        }
5179    }
5180
5181    async deleteNote(contactId, noteToDelete) { // noteId zu noteToDelete geändert
5182        if (!contactId || !noteToDelete || !noteToDelete.id) { // Prüfe auf noteToDelete.id
5183             console.error('Ungültige Daten zum Löschen der Notiz (ID fehlt?).');
5184             UI.showError('Ungültige Daten zum Löschen der Notiz.');
5185             return;
5186         }
5187        try {
5188            /* phase8c */
5189            await ContactsApi.deleteNote(contactId, noteToDelete.id);
5190             const noteToRemove = noteToDelete; const contactSnap = { exists: () => true, data: () => ({ notes: [] }) };
5191             if (contactSnap.exists()) {
5192                 const noteToRemove2 = noteToRemove;
5193                 if (noteToRemove2) {
5194            // console.log('Notiz gelöscht für Kontakt:', contactId);
5195                     UI.showSuccess('Notiz erfolgreich gelöscht');
5196                 } else {
5197                     console.warn('Zu löschende Notiz nicht im Array gefunden (ID:', noteToDelete.id, ').');
5198                     UI.showWarning('Notiz konnte nicht gefunden werden.');
5199                 }
5200             }
5201        } catch (error) {
5202            console.error('Error deleting note:', error);
5203            UI.showError('Fehler beim Löschen der Notiz: ' + error.message);
5204            throw error;
5205        }
5206    }
5207
5208    async getContact(contactId) {
5209        try {
5210            /* phase8c */ const _c_phase8c = await ContactsApi.get(contactId).catch(()=>null); const docSnap = { exists: () => !!_c_phase8c, data: () => _c_phase8c || {} };
5211            
5212            if (docSnap.exists()) {
5213                return { id: docSnap.id, ...docSnap.data() };
5214            } else {
5215                throw new Error('Contact not found');
5216            }
5217        } catch (error) {
5218            console.error('Error getting contact:', error);
5219            throw error;
5220        }
5221    }
5222
5223    async createTasksHandler(selectedTemplateId, selectedContacts) {
5224        const user = auth.currentUser;
5225        if (!user) {
5226            throw new Error('Benutzer nicht eingeloggt.');
5227        }
5228
5229        // Get user's tenant ID
5230        const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
5231        if (!userDoc.exists()) {
5232            throw new Error('Benutzerprofil nicht gefunden.');
5233        }
5234        
5235        const userData = userDoc.data();
5236        const tenantId = userData.tenantID || userData.tenant_id;
5237        if (!tenantId) {
5238            throw new Error('Keine Tenant ID gefunden.');
5239        }
5240
5241        // Get the selected template
5242        const templateDoc = await getDoc(doc(db, 'multicall', selectedTemplateId));
5243        if (!templateDoc.exists()) {
5244            throw new Error('Ausgewählte Vorlage nicht gefunden.');
5245        }
5246
5247        const selectedTemplate = templateDoc.data();
5248
5249        // Get ID token for API calls
5250        const idToken = await user.getIdToken();
5251        const headers = {
5252            'Content-Type': 'application/json',
5253            'Authorization': `Bearer ${idToken}`
5254        };
5255
5256        // Welle 4: Backend-API statt direkter Firestore-Batch (siehe routes/contacts.py:multicall_start)
5257        const validContacts = selectedContacts.filter(c => c && c.customer_id);
5258        const contactIds = validContacts.map(c => c.id);
5259        let multicallResp;
5260        try {
5261            const startResp = await fetch('/api/contacts/multicall-start', {
5262                method: 'POST',
5263                headers,
5264                body: JSON.stringify({
5265                    contact_ids: contactIds,
5266                    template_id: selectedTemplateId,
5267                }),
5268            });
5269            if (!startResp.ok) {
5270                const txt = await startResp.text().catch(() => `HTTP ${startResp.status}`);
5271                throw new Error(txt || `HTTP ${startResp.status}`);
5272            }
5273            multicallResp = await startResp.json();
5274            console.log(`[multicall] processed=${multicallResp.processed} skipped=${multicallResp.skipped} errors=${(multicallResp.errors || []).length}`);
5275        } catch (err) {
5276            console.error('[multicall] backend error:', err);
5277            throw new Error('Fehler beim Anlegen der Multicall-Aufgaben: ' + (err.message || err));
5278        }
5279        const taskByContactId = {};
5280        for (const t of (multicallResp.tasks || [])) {
5281            taskByContactId[t.contact_id] = t.task_id;
5282        }
5283        const taskRefs = validContacts
5284            .filter(c => taskByContactId[c.id])
5285            .map(c => ({
5286                contact: c,
5287                taskRef: { id: taskByContactId[c.id] },
5288                contactRef: { id: c.id },
5289            }));
5290
5291        // Konfiguration für Twilio API-Limits
5292        const CONCURRENT_CALLS = 1; // Maximale Anzahl gleichzeitiger Anrufe
5293        const CALL_DELAY = 30000; // 30 Sekunden Pause zwischen den Anrufen
5294        
5295        // Starte die Anrufe sequentiell
5296        const results = [];
5297        const failedCalls = [];
5298        const maxRetries = 5;
5299
5300        // Verarbeite die Anrufe sequentiell mit begrenzter Parallelität
5301        for (let i = 0; i < taskRefs.length; i += CONCURRENT_CALLS) {
5302            const currentBatch = taskRefs.slice(i, i + CONCURRENT_CALLS);
5303            // console.log(`Processing calls ${i + 1} to ${Math.min(i + CONCURRENT_CALLS, taskRefs.length)} of ${taskRefs.length}`);
5304            
5305            // Starte maximal CONCURRENT_CALLS Anrufe parallel
5306            const batchPromises = currentBatch.map(async ({ contact, taskRef, contactRef }) => {
5307                try {
5308                    const requestBody = {
5309                        destinationNumber: contact.phone,
5310                        task_id: taskRef.id,
5311                        calendar_id: document.getElementById('multiCallCalendarSelect')?.value || "1"
5312                        // agent_id und tenant_id werden vom Server aus dem Token ermittelt
5313                    };
5314                    
5315            // console.log('Sending request to /api/start_ultravox_out with body:', requestBody);
5316                    
5317                    const response = await fetch('/api/start_ultravox_out', {
5318                        method: 'POST',
5319                        headers: headers,
5320                        body: JSON.stringify(requestBody)
5321                    });
5322
5323                    if (!response.ok) {
5324                        const errorData = await response.json();
5325                        throw new Error(errorData.message || 'Fehler beim Starten des Anrufs');
5326                    }
5327
5328                    return { status: 'fulfilled', value: await response.json() };
5329                } catch (error) {
5330                    console.error('Error making call:', error);
5331                    failedCalls.push({ contact, taskRef, contactRef, retries: 0 });
5332                    return { status: 'rejected', reason: error };
5333                }
5334            });
5335
5336            // Warte auf alle Anrufe im aktuellen Batch
5337            const batchResults = await Promise.all(batchPromises);
5338            results.push(...batchResults);
5339
5340            // Warte CALL_DELAY Millisekunden vor dem nächsten Batch
5341            if (i + CONCURRENT_CALLS < taskRefs.length) {
5342                await new Promise(resolve => setTimeout(resolve, CALL_DELAY));
5343            }
5344        }
5345
5346        // Wiederhole fehlgeschlagene Anrufe
5347        let currentRetry = 0;
5348        while (failedCalls.length > 0 && currentRetry < maxRetries) {
5349            currentRetry++;
5350            // console.log(`Retry attempt ${currentRetry} for ${failedCalls.length} failed calls`);
5351            
5352            // Verarbeite die fehlgeschlagenen Anrufe auch mit begrenzter Parallelität
5353            for (let i = 0; i < failedCalls.length; i += CONCURRENT_CALLS) {
5354                const retryBatch = failedCalls.slice(i, i + CONCURRENT_CALLS);
5355                const retryPromises = retryBatch.map(async (failedCall) => {
5356                    try {
5357                        const requestBody = {
5358                            destinationNumber: failedCall.contact.phone,
5359                            task_id: failedCall.taskRef.id
5360                            // agent_id und tenant_id werden vom Server aus dem Token ermittelt
5361                        };
5362                        
5363            // console.log(`Retry ${currentRetry} for call to ${failedCall.contact.phone}`);
5364                        
5365                        const response = await fetch('/api/start_ultravox_out', {
5366                            method: 'POST',
5367                            headers: headers,
5368                            body: JSON.stringify(requestBody)
5369                        });
5370
5371                        if (!response.ok) {
5372                            const errorData = await response.json();
5373                            throw new Error(errorData.message || 'Fehler beim Starten des Anrufs');
5374                        }
5375
5376                        // Entferne erfolgreichen Anruf aus failedCalls
5377                        const index = failedCalls.indexOf(failedCall);
5378                        if (index > -1) {
5379                            failedCalls.splice(index, 1);
5380                        }
5381
5382                        return { status: 'fulfilled', value: await response.json() };
5383                    } catch (error) {
5384                        console.error(`Retry ${currentRetry} failed:`, error);
5385                        failedCall.retries++;
5386                        return { status: 'rejected', reason: error };
5387                    }
5388                });
5389
5390                const retryResults = await Promise.all(retryPromises);
5391                results.push(...retryResults);
5392
5393                // Warte CALL_DELAY Millisekunden vor dem nächsten Batch
5394                if (i + CONCURRENT_CALLS < failedCalls.length) {
5395                    await new Promise(resolve => setTimeout(resolve, CALL_DELAY));
5396                }
5397            }
5398
5399            // Warte 10 Sekunden vor dem nächsten Retry-Versuch
5400            if (failedCalls.length > 0 && currentRetry < maxRetries) {
5401                await new Promise(resolve => setTimeout(resolve, 10000));
5402            }
5403        }
5404
5405        // Zähle erfolgreiche und fehlgeschlagene Anrufe
5406        const successful = results.filter(r => r.status === 'fulfilled').length;
5407        const failed = results.filter(r => r.status === 'rejected').length;
5408
5409        UI.showSuccess(`${successful} Anrufe wurden erfolgreich gestartet. Die Anrufe werden mit maximal ${CONCURRENT_CALLS} gleichzeitigen Anrufen und ${CALL_DELAY/1000} Sekunden Pause ausgeführt. Es können maximal 50 Anrufe gleichzeit ausgeführt werden.`);
5410    }
5411
5412    // Übersetze Email Template Modal
5413    translateEmailTemplateModal() {
5414        const modal = document.getElementById('emailTemplateModal');
5415        if (!modal) return;
5416
5417        // Übersetze Modal-Titel
5418        const modalTitle = modal.querySelector('.modal-title');
5419        if (modalTitle) {
5420            const titleKey = modalTitle.getAttribute('data-i18n');
5421            if (titleKey) modalTitle.textContent = t(titleKey);
5422        }
5423
5424        // Übersetze Labels
5425        const labels = modal.querySelectorAll('label[data-i18n]');
5426        labels.forEach(label => {
5427            const key = label.getAttribute('data-i18n');
5428            if (key) label.textContent = t(key);
5429        });
5430
5431        // Übersetze Buttons
5432        const buttons = modal.querySelectorAll('button[data-i18n]');
5433        buttons.forEach(button => {
5434            const key = button.getAttribute('data-i18n');
5435            if (key) button.textContent = t(key);
5436        });
5437    }
5438
5439    async handleSendGroupEmail() {
5440        const selectedContacts = this.getSelectedContacts();
5441
5442        if (selectedContacts.length === 0) {
5443            UI.showError(t('Bitte wählen Sie mindestens einen Kontakt aus.'));
5444            return;
5445        }
5446
5447        const button = document.getElementById('sendGroupEmailBtn');
5448        const originalContent = button.innerHTML;
5449        button.innerHTML = `<i class="fas fa-spinner fa-spin me-1"></i>${t('Vorbereitung...')}`;
5450        button.disabled = true;
5451
5452        try {
5453            // Load email templates
5454            const templates = await this.loadEmailTemplates();
5455
5456            // Show template selection modal
5457            const modalElement = document.getElementById('emailTemplateModal');
5458            if (!modalElement) {
5459                throw new Error(t('Email Template Modal nicht gefunden.'));
5460            }
5461
5462            // Translate modal content
5463            this.translateEmailTemplateModal();
5464
5465            // Ensure the modal is properly initialized with Bootstrap
5466            let modal = bootstrap.Modal.getInstance(modalElement);
5467            if (!modal) {
5468                modal = new bootstrap.Modal(modalElement);
5469            }
5470
5471            // Update template select
5472            const templateSelect = document.getElementById('emailTemplateSelect');
5473            templateSelect.innerHTML = templates.map(template =>
5474                `<option value="${template.id}">${template.name}</option>`
5475            ).join('');
5476
5477            // Update selected contacts count
5478            const selectedCount = document.getElementById('selectedContactsCount');
5479            if (selectedCount) {
5480                selectedCount.textContent = `${selectedContacts.length} ${t('Kontakte ausgewählt')}`;
5481            }
5482
5483            // Set up send button handler
5484            const sendButton = document.getElementById('sendGroupEmailConfirm');
5485            if (sendButton) {
5486                // Clone the button to remove previous listeners
5487                const newSendButton = sendButton.cloneNode(true);
5488                sendButton.parentNode.replaceChild(newSendButton, sendButton);
5489
5490                // Add the new event listener
5491                newSendButton.addEventListener('click', async () => {
5492                    const sendBtn = newSendButton; // Use the new button reference
5493                    const btnContent = sendBtn.innerHTML;
5494                    sendBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Sende...';
5495                    sendBtn.disabled = true;
5496
5497                    let totalSent = 0;
5498                    let totalFailed = 0;
5499                    const allFailedEmails = [];
5500                    const batchSize = 10; // Batch size set to 10
5501
5502                    try {
5503                        const templateId = templateSelect.value;
5504                        if (!templateId) {
5505                            throw new Error('Bitte wählen Sie eine E-Mail-Vorlage aus.');
5506                        }
5507
5508                        const user = auth.currentUser;
5509                        if (!user) {
5510                            throw new Error('Benutzer nicht eingeloggt.');
5511                        }
5512                        const idToken = await user.getIdToken(); // Get token once
5513
5514                        // Process contacts in batches
5515                        for (let i = 0; i < selectedContacts.length; i += batchSize) {
5516                            const batchContacts = selectedContacts.slice(i, i + batchSize);
5517            // console.log(`Sending batch ${i / batchSize + 1}: ${batchContacts.length} contacts`);
5518
5519                            // Prepare the email data for the current batch
5520                            const emailData = {
5521                                templateId: templateId,
5522                                contacts: batchContacts.map(contact => ({
5523                                    id: contact.id,
5524                                    customer_id: contact.customer_id,
5525                                    email: contact.email,
5526                                    name: contact.name || '',
5527                                    phone: contact.phone,
5528                                    tenant_id: contact.tenant_id,
5529                                    website: contact.website || '',
5530                                    company: contact.company || '',
5531                                    street: contact.street || '',
5532                                    zip: contact.zip || '',
5533                                    city: contact.city || ''
5534                                })),
5535                                userId: user.uid
5536                            };
5537
5538                            // Send the request to the server for the current batch
5539                            const response = await fetch('/api/sendGroupEmail', {
5540                                method: 'POST',
5541                                headers: {
5542                                    'Content-Type': 'application/json',
5543                                    'Authorization': `Bearer ${idToken}`
5544                                },
5545                                body: JSON.stringify(emailData)
5546                            });
5547
5548                            if (!response.ok) {
5549                                const errorData = await response.json().catch(() => ({ error: 'Serverfehler ohne Details' }));
5550                                console.error(`Fehler bei Batch ${i / batchSize + 1}:`, errorData);
5551                                // Assume all in batch failed if response is not ok
5552                                totalFailed += batchContacts.length;
5553                                batchContacts.forEach(c => allFailedEmails.push({ email: c.email, error: errorData.error || 'Serverfehler' }));
5554                                // Optionally add a small delay before the next batch on error
5555                                // await new Promise(resolve => setTimeout(resolve, 500));
5556                                continue; // Continue to the next batch
5557                            }
5558
5559                            const result = await response.json();
5560                            const batchSuccessCount = batchContacts.length - (result.failedEmails?.length || 0);
5561                            totalSent += batchSuccessCount;
5562                            if (result.failedEmails && result.failedEmails.length > 0) {
5563                                totalFailed += result.failedEmails.length;
5564                                allFailedEmails.push(...result.failedEmails);
5565                            }
5566            // console.log(`Batch ${i / batchSize + 1} result: ${batchSuccessCount} sent, ${result.failedEmails?.length || 0} failed.`);
5567
5568                            // Optional: Add a small delay between batches
5569                            // await new Promise(resolve => setTimeout(resolve, 200));
5570                        }
5571
5572                        // Close modal and show summary message
5573                        modal.hide();
5574                        if (totalFailed === 0) {
5575                            UI.showSuccess(`E-Mails wurden erfolgreich an ${totalSent} Kontakte gesendet.`);
5576                        } else {
5577                            const errors = allFailedEmails.map(fail =>
5578                                `${fail.email}: ${fail.error}`
5579                            ).join('\n'); // Corrected join with '\n'
5580                            UI.showWarning(`${totalSent} E-Mails gesendet, ${totalFailed} fehlgeschlagen.\nDetails:\n${errors}`); // Corrected template literal for UI.showWarning
5581                        }
5582
5583                    } catch (error) {
5584                        console.error('Error sending group email batches:', error);
5585                        UI.showError('Fehler beim Senden der E-Mails: ' + error.message);
5586                    } finally {
5587                        sendBtn.innerHTML = btnContent;
5588                        sendBtn.disabled = false;
5589                    }
5590                });
5591            }
5592
5593            modal.show();
5594        } catch (error) {
5595            console.error('Error preparing group email:', error);
5596            UI.showError('Fehler beim Vorbereiten der Gruppen-E-Mail: ' + error.message);
5597        } finally {
5598            button.innerHTML = originalContent;
5599            button.disabled = false;
5600        }
5601    }
5602
5603    async loadEmailTemplates() {
5604        try {
5605            const userId = auth.currentUser.uid;
5606            const templatesRef = collection(db, 'users', userId, 'emailTemplates');
5607            const querySnapshot = await getDocs(templatesRef);
5608            
5609            const templates = [];
5610            querySnapshot.forEach(doc => {
5611                templates.push({ id: doc.id, ...doc.data() });
5612            });
5613            
5614            return templates;
5615        } catch (error) {
5616            console.error('Fehler beim Laden der E-Mail-Vorlagen:', error);
5617            return [];
5618        }
5619    }
5620
5621    getSelectedContacts() {
5622        const checkboxes = document.querySelectorAll('input[name="contactSelect"]:checked');
5623        const selectedContacts = Array.from(checkboxes).map(checkbox => {
5624            const contactId = checkbox.value;
5625            const contact = this.contacts.find(contact => contact.id === contactId);
5626            if (!contact) {
5627                console.warn(`Contact not found for ID: ${contactId}`);
5628                return null;
5629            }
5630        
5631            return {
5632                id: contact.id,
5633                customer_id: contact.customer_id,
5634                email: contact.email,
5635                name: contact.name || '',
5636                phone: contact.phone_number, // Use phone_number field
5637                tenant_id: contact.tenantID || contact.tenant_id,
5638                website: contact.website || '', // Include website field
5639                company: contact.company || '', // Include company field
5640                street: contact.street || '',
5641                zip: contact.zip || '',
5642                city: contact.city || ''
5643            };
5644        }).filter(contact => contact);
5645
5646            // console.log('Selected contacts:', selectedContacts); // Debug log
5647        return selectedContacts;
5648    }
5649
5650    async handleSofortAnruf() {
5651        const selectedContacts = this.getSelectedContacts();
5652        
5653        if (selectedContacts.length === 0) {
5654            UI.showError(t('Bitte wählen Sie mindestens einen Kontakt aus.'));
5655            return;
5656        }
5657
5658        if (selectedContacts.length > 50) {
5659            UI.showError('Es können maximal 50 Kontakte auf einmal ausgewählt werden.');
5660            return;
5661        }
5662
5663        // Prüfe, ob alle ausgewählten Kontakte eine Telefonnummer haben
5664        const contactsWithoutPhone = selectedContacts.filter(contact => !contact.phone);
5665        if (contactsWithoutPhone.length > 0) {
5666            UI.showError(`${contactsWithoutPhone.length} ausgewählte Kontakte haben keine Telefonnummer.`);
5667            return;
5668        }
5669
5670        try {
5671            UI.showLoading('Lade MultiCall Vorlagen...');
5672            
5673            const currentUser = auth.currentUser;
5674            if (!currentUser) {
5675                throw new Error('Nicht eingeloggt');
5676            }
5677
5678            const idToken = await currentUser.getIdToken();
5679            
5680            const userDoc = await _phase8UserSnap(currentUser.uid); /* phase8 */
5681            if (!userDoc.exists()) {
5682                throw new Error('Benutzerprofil nicht gefunden');
5683                return;
5684            }
5685            
5686            const userData = userDoc.data();
5687            const tenantId = userData.tenantID || userData.tenant_id;
5688            if (!tenantId) {
5689                throw new Error('Keine Mandanten-ID gefunden');
5690                return;
5691            }
5692
5693/* phase8c */ const templates = await _c8c_listMulticallTemplates();
5694
5695            if (templates.length === 0) {
5696                UI.showError('Keine MultiCall Vorlagen gefunden.');
5697                return;
5698            }
5699
5700            UI.hideLoading();
5701
5702            // Update the modal HTML to include calendar selection
5703            const modalHtml = `
5704                <div class="modal fade" id="sofortAnrufTemplateModal" tabindex="-1" aria-labelledby="sofortAnrufTemplateModalLabel" aria-hidden="true">
5705                    <div class="modal-dialog">
5706                        <div class="modal-content">
5707                            <div class="modal-header">
5708                                <h5 class="modal-title" id="sofortAnrufTemplateModalLabel">${t('Sofort Anruf - Vorlage auswählen')}</h5>
5709                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
5710                            </div>
5711                            <div class="modal-body">
5712                                <div class="mb-3">
5713                                    <label for="sofortAnrufTemplateSelect" class="form-label">${t('Vorlage')}</label>
5714                                    <select class="form-select" id="sofortAnrufTemplateSelect" required>
5715                                    </select>
5716                                </div>
5717                                <div class="mb-3">
5718                                    <label for="sofortAnrufAgentSelect" class="form-label">${t('Agent')}</label>
5719                                    <select class="form-select" id="sofortAnrufAgentSelect">
5720                                        <option value="">${t('Wird geladen...')}</option>
5721                                    </select>
5722                                </div>
5723                                <div class="mb-3">
5724                                    <label for="sofortAnrufCalendarSelect" class="form-label">${t('Kalender')}</label>
5725                                    <select class="form-select" id="sofortAnrufCalendarSelect">
5726                                        <option value="">${t('Kein Kalender')}</option>
5727                                        ${await this.getCalendarOptionsHtml()}
5728                                    </select>
5729                                </div>
5730                                <div id="selectedContactsCountSofortAnruf" class="text-muted small"></div>
5731                            </div>
5732                            <div class="modal-footer">
5733                                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="Abbrechen">${t('Abbrechen')}</button>
5734                                <button type="button" class="btn btn-warning" id="startSofortAnrufBtn" data-i18n="Sofort anrufen"><i class="fas fa-phone me-1"></i>${t('Sofort anrufen')}</button>
5735                            </div>
5736                        </div>
5737                    </div>
5738                </div>
5739            `;
5740
5741            // Add modal to document if it doesn't exist
5742            if (!document.getElementById('sofortAnrufTemplateModal')) {
5743                document.body.insertAdjacentHTML('beforeend', modalHtml);
5744            }
5745
5746            const modalElement = document.getElementById('sofortAnrufTemplateModal');
5747            const modal = new bootstrap.Modal(modalElement);
5748
5749            // Initialize modal before using it
5750            modal.show();
5751            modal.hide();
5752
5753            // Update template select
5754            const templateSelect = document.getElementById('sofortAnrufTemplateSelect');
5755            templateSelect.innerHTML = templates.map(template =>
5756                `<option value="${template.id}">${template.task || 'Unbenannte Vorlage'}</option>`
5757            ).join('');
5758
5759            // Load agents for dropdown
5760            const agentSelect = document.getElementById('sofortAnrufAgentSelect');
5761            try {
5762                /* phase8c */ const _ag = await _c8c_listAgents();
5762 const agentsSnap = { forEach: (cb) => _ag.forEach(d => cb({ id: d.id, data: () => d })) };
5763
5764                const agents = [];
5765                agentsSnap.forEach(docSnap => {
5766                    const data = docSnap.data();
5767                    if (data.name) {
5768                        agents.push({ id: docSnap.id, name: data.name, selfHosted: !!data.selfHosted });
5769                    }
5770                });
5771
5772                // Sort: selfHosted first, then alphabetically
5773                agents.sort((a, b) => {
5774                    if (a.selfHosted && !b.selfHosted) return -1;
5775                    if (!a.selfHosted && b.selfHosted) return 1;
5776                    return (a.name || '').localeCompare(b.name || '');
5777                });
5778
5779                if (agentSelect) {
5780                    agentSelect.innerHTML = agents.map(agent =>
5781                        `<option value="${agent.id}">${agent.name}${agent.selfHosted ? ' (Gemini)' : ''}</option>`
5782                    ).join('');
5783                }
5784            } catch (err) {
5785                console.error('Error loading agents for Sofort-Anruf:', err);
5786                if (agentSelect) {
5787                    agentSelect.innerHTML = `<option value="">${t('Fehler beim Laden')}</option>`;
5788                }
5789            }
5790
5791            // Update selected contacts count
5792            const selectedCount = document.getElementById('selectedContactsCountSofortAnruf');
5793            if (selectedCount) {
5794                selectedCount.textContent = `${selectedContacts.length} Kontakte ausgewählt`;
5795            }
5796
5797            // Set up send button handler
5798            const startSofortAnrufBtn = document.getElementById('startSofortAnrufBtn');
5799            if (startSofortAnrufBtn) {
5800                const sendHandler = async () => {
5801                    const sendBtn = document.getElementById('startSofortAnrufBtn');
5802                    if (!sendBtn) {
5803                        console.error('startSofortAnrufBtn not found');
5804                        return;
5805                    }
5806                    const btnContent = sendBtn.innerHTML;
5807                    sendBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Anrufe starten...';
5808                    sendBtn.disabled = true;
5809
5810                    try {
5811                        const templateId = templateSelect.value;
5812                        if (!templateId) {
5813                            throw new Error('Bitte wählen Sie eine Vorlage aus.');
5814                        }
5815
5816                        const selectedTemplate = templates.find(t => t.id === templateId);
5817
5818                        if (!selectedTemplate) {
5819                            modal.hide();
5820                            UI.showError('Ausgewählte Vorlage nicht gefunden.');
5821                            sendBtn.innerHTML = btnContent;
5822                            sendBtn.disabled = false;
5823                            return;
5824                        }
5825
5826            // console.log('Selected Template:', selectedTemplate);
5827
5828                        // Headers for API calls
5829                        const headers = {
5830                            'Content-Type': 'application/json',
5831                            'Authorization': `Bearer ${idToken}`
5832                        };
5833            // console.log('Request headers:', headers);
5834                        
5835                        // phase8c sofort-anruf via ContactsApi
5836                        const taskRefs = [];
5837                        for (const contact of selectedContacts) {
5838                            try {
5839                                await ContactsApi.update(contact.id, { lead_status: 'Anrufen', task: selectedTemplate.task || '' });
5840                                const taskData = {
5841                                    attempt: 0,
5842                                    customer_id: parseInt(contact.customer_id),
5843                                    message: selectedTemplate.message,
5844                                    message2: selectedTemplate.message2,
5845                                    greeting: (selectedTemplate.greeting || '').replace('{name}', contact.name || ''),
5846                                    status: 'Erledigt',
5847                                    task: selectedTemplate.task || '',
5848                                    task_id: Date.now(),
5849                                    tenant_id: tenantId.toString(),
5850                                    uid: currentUser.uid,
5851                                    templateId: selectedTemplate.id,
5852                                    calendar_id: document.getElementById('sofortAnrufCalendarSelect')?.value || "1"
5853                                };
5854                                const created = await ContactsApi.createTask(contact.id, taskData);
5855                                taskRefs.push({ contact, taskRef: { id: created.id || created.task_id }
5855, contactRef: { id: contact.id } });
5856                            } catch (e) { console.error('phase8c sofort-anruf failed for ' + contact.id, e); }
5857                        }
5858
5859                        // Konfiguration für Ultravox API-Limits
5860                        const CONCURRENT_CALLS = 1; // Maximale Anzahl gleichzeitiger Anrufe
5861                        const CALL_DELAY = 30000; // 30 Sekunden Pause zwischen den Anrufen
5862                        
5863                        // Starte die Anrufe sequentiell
5864                        const results = [];
5865                        const failedCalls = [];
5866                        const maxRetries = 5;
5867
5868                        // Verarbeite die Anrufe sequentiell mit begrenzter Parallelität
5869                        for (let i = 0; i < taskRefs.length; i += CONCURRENT_CALLS) {
5870                            const currentBatch = taskRefs.slice(i, i + CONCURRENT_CALLS);
5871            // console.log(`Processing calls ${i + 1} to ${Math.min(i + CONCURRENT_CALLS, taskRefs.length)} of ${taskRefs.length}`);
5872                            
5873                            // Starte maximal CONCURRENT_CALLS Anrufe parallel
5874                            const batchPromises = currentBatch.map(async ({ contact, taskRef, contactRef }) => {
5875                                try {
5876                                    const selectedAgentId = document.getElementById('sofortAnrufAgentSelect')?.value || '';
5877                                    const requestBody = {
5878                                        destinationNumber: contact.phone,
5879                                        task_id: taskRef.id,
5880                                        calendar_id: document.getElementById('sofortAnrufCalendarSelect')?.value || "1",
5881                                        ...(selectedAgentId && { agent_id: selectedAgentId })
5882                                    };
5883
5884                                    const response = await fetch('/api/start_ultravox_out', {
5885                                        method: 'POST',
5886                                        headers: headers,
5887                                        body: JSON.stringify(requestBody)
5888                                    });
5889
5890                                    if (!response.ok) {
5891                                        const errorData = await response.json();
5892                                        
5893                                        // Spezielle Behandlung für "Keine verfügbare ausgehende Nummer"
5894                                        if (errorData.message && errorData.message.includes('Keine verfügbare ausgehende Nummer')) {
5895                                            throw new Error('Keine ausgehende Telefonnummer konfiguriert. Bitte kontaktieren Sie den Administrator.');
5896                                        }
5897                                        
5898                                        throw new Error(errorData.message || 'Fehler beim Starten des Anrufs');
5899                                    }
5900
5901                                    // Anruf erfolgreich gestartet - jetzt den Lead-Status aktualisieren
5902                                    /* phase8c */ await ContactsApi.update(contact.id, { lead_status: 'Angerufen' });
5903
5904                                    return { status: 'fulfilled', value: await response.json() };
5905                                } catch (error) {
5906                                    console.error('Error making call:', error);
5907                                    failedCalls.push({ contact, taskRef, contactRef, retries: 0 });
5908                                    return { status: 'rejected', reason: error };
5909                                }
5910                            });
5911
5912                            // Warte auf alle Anrufe im aktuellen Batch
5913                            const batchResults = await Promise.all(batchPromises);
5914                            results.push(...batchResults);
5915
5916                            // Warte CALL_DELAY Millisekunden vor dem nächsten Batch
5917                            if (i + CONCURRENT_CALLS < taskRefs.length) {
5918                                await new Promise(resolve => setTimeout(resolve, CALL_DELAY));
5919                            }
5920                        }
5921
5922                        // Wiederhole fehlgeschlagene Anrufe
5923                        let currentRetry = 0;
5924                        while (failedCalls.length > 0 && currentRetry < maxRetries) {
5925                            currentRetry++;
5926            // console.log(`Retry attempt ${currentRetry} for ${failedCalls.length} failed calls`);
5927                            
5928                            // Verarbeite die fehlgeschlagenen Anrufe auch mit begrenzter Parallelität
5929                            for (let i = 0; i < failedCalls.length; i += CONCURRENT_CALLS) {
5930                                const retryBatch = failedCalls.slice(i, i + CONCURRENT_CALLS);
5931                                const retryPromises = retryBatch.map(async (failedCall) => {
5932                                    try {
5933                                        const requestBody = {
5934                                            destinationNumber: failedCall.contact.phone,
5935                                            task_id: failedCall.taskRef.id
5936                                            // agent_id und tenant_id werden vom Server aus dem Token ermittelt
5937                                        };
5938                                        
5939            // console.log(`Retry ${currentRetry} for call to ${failedCall.contact.phone}`);
5940                                        
5941                                        const response = await fetch('/api/start_ultravox_out', {
5942                                            method: 'POST',
5943                                            headers: headers,
5944                                            body: JSON.stringify(requestBody)
5945                                        });
5946
5947                                        if (!response.ok) {
5948                                            const errorData = await response.json();
5949                                            throw new Error(errorData.message || 'Fehler beim Starten des Anrufs');
5950                                        }
5951
5952                                        // Anruf erfolgreich gestartet - jetzt den Lead-Status aktualisieren
5953                                        /* phase8c */ await ContactsApi.update(failedCall.contact.
5953id, { lead_status: 'Angerufen' });
5954
5955                                        // Entferne erfolgreichen Anruf aus failedCalls
5956                                        const index = failedCalls.indexOf(failedCall);
5957                                        if (index > -1) {
5958                                            failedCalls.splice(index, 1);
5959                                        }
5960
5961                                        return { status: 'fulfilled', value: await response.json() };
5962                                    } catch (error) {
5963                                        console.error(`Retry ${currentRetry} failed:`, error);
5964                                        failedCall.retries++;
5965                                        return { status: 'rejected', reason: error };
5966                                    }
5967                                });
5968
5969                                const retryResults = await Promise.all(retryPromises);
5970                                results.push(...retryResults);
5971
5972                                // Warte CALL_DELAY Millisekunden vor dem nächsten Batch
5973                                if (i + CONCURRENT_CALLS < failedCalls.length) {
5974                                    await new Promise(resolve => setTimeout(resolve, CALL_DELAY));
5975                                }
5976                            }
5977
5978                            // Warte 10 Sekunden vor dem nächsten Retry-Versuch
5979                            if (failedCalls.length > 0 && currentRetry < maxRetries) {
5980                                await new Promise(resolve => setTimeout(resolve, 10000));
5981                            }
5982                        }
5983
5984                        // Zähle erfolgreiche und fehlgeschlagene Anrufe
5985                        const successful = results.filter(r => r.status === 'fulfilled').length;
5986                        const failed = results.filter(r => r.status === 'rejected').length;
5987
5988                        modal.hide();
5989                        if (failed === 0) {
5990                            UI.showSuccess(`${successful} Anrufe wurden erfolgreich gestartet. Die Anrufe werden mit maximal ${CONCURRENT_CALLS} gleichzeitigen Anrufen und ${CALL_DELAY/1000} Sekunden Pause ausgeführt.`);
5991                        } else {
5992                            const retriedMessage = currentRetry > 0 ? ` Nach ${currentRetry} Wiederholungsversuchen` : '';
5993                            UI.showWarning(`${successful} Anrufe erfolgreich gestartet, ${failed} fehlgeschlagen.${retriedMessage} Die Anrufe werden mit maximal ${CONCURRENT_CALLS} gleichzeitigen Anrufen und ${CALL_DELAY/1000} Sekunden Pause ausgeführt.`);
5994                        }
5995                    } catch (error) {
5996                        console.error('Error starting calls:', error);
5997                        UI.showError('Fehler beim Starten der Anrufe: ' + error.message);
5998                        modal.hide(); // Stelle sicher, dass das Modal geschlossen wird
5999                    } finally {
6000                        // Stelle sicher, dass der Button wieder aktiviert wird
6001                        const finalBtn = document.getElementById('startSofortAnrufBtn');
6002                        if (finalBtn) {
6003                            finalBtn.innerHTML = btnContent || '<i class="fas fa-phone me-1"></i>Sofort anrufen';
6004                            finalBtn.disabled = false;
6005                        }
6006                    }
6007                };
6008
6009                // Remove existing click listeners and add new one
6010                const newSendButton = startSofortAnrufBtn.cloneNode(true);
6011                startSofortAnrufBtn.parentNode.replaceChild(newSendButton, startSofortAnrufBtn);
6012                newSendButton.addEventListener('click', sendHandler);
6013            }
6014
6015            modal.show();
6016
6017        } catch (error) {
6018            console.error('Error in handleSofortAnruf:', error);
6019            UI.showError('Fehler: ' + error.message);
6020            UI.hideLoading();
6021        }
6022    }
6023
6024    async handleNewMultiCall() {
6025            // console.log('handleNewMultiCall() called'); // Debug log
6026        
6027        // Check permissions first
6028        if (!this.hasMultiCallPermission()) {
6029            // console.log('MultiCall permission denied'); // Debug log
6030            UI.showError('Sie haben keine Berechtigung für MultiCall. Bitte wenden Sie sich an den Administrator.');
6031            return;
6032        }
6033        
6034        const selectedContacts = this.getSelectedContacts();
6035            // console.log('Selected contacts:', selectedContacts.length); // Debug log
6036        
6037        if (selectedContacts.length === 0) {
6038            UI.showError(t('Bitte wählen Sie mindestens einen Kontakt aus.'));
6039            return;
6040        }
6041
6042        const selectedContact = selectedContacts[0];
6043        if (!selectedContact.phone) {
6044            UI.showError('Der ausgewählte Kontakt hat keine Telefonnummer.');
6045            return;
6046        }
6047
6048        try {
6049            UI.showLoading('Lade MultiCall Vorlagen...');
6050            
6051            // Get current user's ID token
6052            const currentUser = auth.currentUser;
6053            // console.log('Current user:', currentUser?.email);
6054            
6055            if (!currentUser) {
6056                throw new Error('Nicht eingeloggt');
6057            }
6058
6059            // Get ID token for API calls
6060            // console.log('Getting ID token...');
6061            const idToken = await currentUser.getIdToken();
6062            // console.log('Got ID token:', idToken.substring(0, 20) + '...');
6063
6064            // Get user's tenant ID
6065            const userDoc = await _phase8UserSnap(currentUser.uid); /* phase8 */
6066            if (!userDoc.exists()) {
6067                console.error('User profile not found');
6068                return;
6069            }
6070            
6071            const userData = userDoc.data();
6072            const tenantId = userData.tenantID || userData.tenant_id;
6073            if (!tenantId) {
6074                console.error('No tenant ID found for user');
6075                return;
6076            }
6077
6078            // console.log('Using tenant ID:', tenantId);
6079/* phase8c */ const templates = await _c8c_listMulticallTemplates();
6080
6081            if (templates.length === 0) {
6082                UI.showError('Keine MultiCall Vorlagen gefunden.');
6083                return;
6084            }
6085
6086            UI.hideLoading();
6087
6088            // Show template selection modal
6089            // console.log('Looking for multiCallTemplateModal...'); // Debug log
6090            const modalElement = document.getElementById('multiCallTemplateModal');
6091            if (!modalElement) {
6092                console.error('MultiCall Template Modal element not found!'); // Debug log
6093                UI.showError('MultiCall Template Modal nicht gefunden.');
6094                return;
6095            }
6096            // console.log('Modal element found, creating Bootstrap modal...'); // Debug log
6097
6098            const modal = new bootstrap.Modal(modalElement);
6099
6100            // Update template select
6101            // console.log('Updating template select...'); // Debug log
6102            const templateSelect = document.getElementById('multiCallTemplateSelect');
6103            if (!templateSelect) {
6104                console.error('multiCallTemplateSelect not found!'); // Debug log
6105                UI.showError('Template Select Element nicht gefunden.');
6106                return;
6107            }
6108            templateSelect.innerHTML = templates.map(template => 
6109                `<option value="${template.id}">${template.task || 'Unbenannte Vorlage'}</option>`
6110            ).join('');
6111
6112            // Update selected contacts count
6113            // console.log('Updating selected contacts count...'); // Debug log
6114            const selectedCount = document.getElementById('selectedContactsCountMultiCall');
6115            if (selectedCount) {
6116                selectedCount.textContent = `${selectedContacts.length} Kontakte ausgewählt`;
6117            // console.log(`Set contact count to: ${selectedContacts.length}`); // Debug log
6118            } else {
6119                console.warn('selectedContactsCountMultiCall element not found'); // Debug log
6120            }
6121
6122            // Set up send button handler
6123            // console.log('Setting up button handler...'); // Debug log
6124            const createTasksBtn = document.getElementById('createMultiCallTasksBtn');
6125            if (createTasksBtn) {
6126            // console.log('createMultiCallTasksBtn found, adding event listener'); // Debug log
6127                const sendHandler = async () => {
6128                    const templateId = templateSelect.value;
6129                    if (!templateId) {
6130                        UI.showError('Bitte wählen Sie eine Vorlage aus.');
6131                        return;
6132                    }
6133
6134                    const selectedTemplate = templates.find(t => t.id === templateId);
6135
6136                    if (!selectedTemplate) {
6137                        modal.hide();
6138                        UI.showError('Ausgewählte Vorlage nicht gefunden.');
6139                        return;
6140                    }
6141
6142                    try {
6143                        UI.showLoading('Erstelle Tasks...');
6144                        await this.createTasksHandler(templateId, selectedContacts);
6145                        modal.hide();
6146                        UI.showSuccess('Tasks erfolgreich erstellt');
6147                    } catch (error) {
6148                        console.error('Error creating tasks:', error);
6149                        UI.showError('Fehler beim Erstellen der Tasks: ' + error.message);
6150                    } finally {
6151                        UI.hideLoading();
6152                    }
6153                };
6154
6155                // Remove existing click listeners and add new one
6156                const newSendButton = createTasksBtn.cloneNode(true);
6157                createTasksBtn.parentNode.replaceChild(newSendButton, createTasksBtn);
6158                newSendButton.addEventListener('click', sendHandler);
6159            } else {
6160                console.error('createMultiCallTasksBtn not found!'); // Debug log
6161                UI.showError('MultiCall Button nicht gefunden.');
6162                return;
6163            }
6164
6165            // console.log('Showing modal...'); // Debug log
6166            modal.show();
6167
6168        } catch (error) {
6169            console.error('Error in handleNewMultiCall:', error);
6170            UI.showError('Fehler: ' + error.message);
6171            UI.hideLoading();
6172        }
6173    }
6174
6175
6176
6177    async handleAutoCall() {
6178        // Öffne das AutoCall Rules Modal
6179        await this.showAutoCallRulesModal();
6180    }
6181
6182    async handleSmartListSubmit(e) {
6183        e.preventDefault();
6184
6185        try {
6186            const user = auth.currentUser;
6187            if (!user) return;
6188
6189            const formData = new FormData(e.target);
6190            const name = formData.get('listName');
6191
6192            if (!name) {
6193                UI.showError(t('Bitte geben Sie einen Namen für die Liste ein'));
6194                return;
6195            }
6196
6197            // Sammle alle Filter
6198            const filters = [];
6199            document.querySelectorAll('.filter-row').forEach(row => {
6200                const field = row.querySelector('.filter-field')?.value;
6201                const operator = row.querySelector('.filter-operator')?.value;
6202                const value = row.querySelector('.filter-value')?.value;
6203
6204                if (field && operator && value) {
6205                    filters.push({ field, operator, value });
6206                }
6207            });
6208
6209            if (filters.length === 0) {
6210                UI.showError(t('Bitte fügen Sie mindestens einen Filter hinzu'));
6211                return;
6212            }
6213
6214/* phase8c */ await SmartListsApi.create({ name, filters });
6215
6216            // Verstecke das Modal
6217            if (this.smartListModal) {
6218                this.smartListModal.hide();
6219            }
6220
6221            // Warte kurz, bis das Modal versteckt ist
6222            await new Promise(resolve => setTimeout(resolve, 150));
6223
6224            // Force rebinding of events
6225            this.eventsBound = false;
6226            this.smartListEventsBound = false;
6227
6228            // Aktualisiere die UI und binde Events neu
6229            await this.loadSmartLists();
6230            await this.bindEvents();
6231            this.setupContactEventListeners();
6232
6233            UI.showSuccess(t('Smart-Liste erfolgreich erstellt'));
6234
6235            // Reset form
6236            e.target.reset();
6237            const filterContainer = document.getElementById('filterContainer');
6238            if (filterContainer) {
6239                filterContainer.innerHTML = '';
6240                this.addFilterRow(); // Add one empty filter row
6241            }
6242
6243        } catch (error) {
6244            console.error('Error creating smart list:', error);
6245            UI.showError(t('Fehler beim Erstellen der Smart-Liste'));
6246        }
6247    }
6248
6249    addFilterRow() {
6250        const filterContainer = document.getElementById('filterContainer');
6251        const template = filterContainer.querySelector('.filter-row')?.cloneNode(true);
6252        
6253        if (template) {
6254            // Setze die Werte zurück
6255            template.querySelectorAll('select, input').forEach(el => {
6256                if (el.tagName === 'SELECT') {
6257                    el.selectedIndex = 0;
6258                } else {
6259                    el.value = '';
6260                }
6261            });
6262
6263            // Stelle sicher, dass die Ort-Option im Filter-Feld vorhanden ist
6264            const fieldSelect = template.querySelector('.filter-field');
6265            if (fieldSelect && !fieldSelect.querySelector('option[value="city"]')) {
6266                const cityOption = document.createElement('option');
6267                cityOption.value = 'city';
6268                cityOption.textContent = t('Ort');
6269                fieldSelect.appendChild(cityOption);
6270            }
6271
6272            const operatorSelect = template.querySelector('.filter-operator');
6273            const valueContainer = template.querySelector('.filter-value-container');
6274
6275            if (fieldSelect && operatorSelect && valueContainer) {
6276                fieldSelect.addEventListener('change', () => this.updateFilterOperators(fieldSelect, operatorSelect, valueContainer));
6277                operatorSelect.addEventListener('change', () => this.updateFilterValueInput(fieldSelect, operatorSelect, valueContainer));
6278            }
6279
6280            filterContainer.appendChild(template);
6281            
6282            // Initialisiere die Operatoren für das ausgewählte Feld
6283            this.updateFilterOperators(fieldSelect, operatorSelect, valueContainer);
6284        } else {
6285            console.error('Filter template not found');
6286            UI.showError('Fehler beim Hinzufügen eines neuen Filters');
6287        }
6288    }
6289
6290    updateFilterOperators(fieldSelect, operatorSelect, valueContainer) {
6291        const dateFields = ['lastContact', 'createdAt', 'updatedAt'];
6292        const statusFields = ['status'];
6293        const industryFields = ['industry'];
6294        
6295        // Verstecke alle Operatoren
6296        operatorSelect.querySelectorAll('option').forEach(option => {
6297            option.style.display = 'none';
6298        });
6299
6300        if (dateFields.includes(fieldSelect.value)) {
6301            // Zeige nur Datums-Operatoren
6302            operatorSelect.querySelectorAll('option[value="before"], option[value="after"], option[value="equals"]').forEach(option => {
6303                option.style.display = '';
6304            });
6305            operatorSelect.value = 'before';
6306        } else if (statusFields.includes(fieldSelect.value)) {
6307            // Zeige nur Gleichheits-Operatoren
6308            operatorSelect.querySelectorAll('option[value="equals"], option[value="notEquals"]').forEach(option => {
6309                option.style.display = '';
6310            });
6311            operatorSelect.value = 'equals';
6312        } else {
6313            // Zeige Standard-Operatoren
6314            operatorSelect.querySelectorAll('option:not([value="before"]):not([value="after"])').forEach(option => {
6315                option.style.display = '';
6316            });
6317            operatorSelect.value = 'contains';
6318        }
6319
6320        // Aktualisiere das Wertefeld
6321        this.updateFilterValueInput(fieldSelect, operatorSelect, valueContainer);
6322    }
6323
6324    updateFilterValueInput(fieldSelect, operatorSelect, valueContainer) {
6325        const dateFields = ['lastContact', 'createdAt', 'updatedAt'];
6326        const statusFields = ['status'];
6327        const industryFields = ['industry'];
6328
6329        // Erstelle das passende Eingabefeld
6330        let input;
6331        if (dateFields.includes(fieldSelect.value)) {
6332            const inputGroup = document.createElement('div');
6333            inputGroup.className = 'input-group date';
6334            
6335            input = document.createElement('input');
6336            input.type = 'text';
6337            input.className = 'form-control filter-value';
6338            input.required = true;
6339            input.readOnly = true;
6340            
6341            const inputGroupAppend = document.createElement('span');
6342            inputGroupAppend.className = 'input-group-text';
6343            inputGroupAppend.innerHTML = '<i class="fas fa-calendar"></i>';
6344            
6345            inputGroup.appendChild(input);
6346            inputGroup.appendChild(inputGroupAppend);
6347            
6348            // Ersetze das alte Eingabefeld
6349            const oldInput = valueContainer.querySelector('.filter-value, .input-group');
6350            if (oldInput) {
6351                valueContainer.replaceChild(inputGroup, oldInput);
6352            } else {
6353                valueContainer.appendChild(inputGroup);
6354            }
6355            
6356            // Initialisiere den Datepicker mit lokalisierter Konfiguration
6357            const dpConfig = getDatepickerConfig();
6358            (window.loadLib ? window.loadLib('datepicker') : Promise.resolve()).then(() => $(input).datepicker({
6359                format: dpConfig.format,
6360                language: dpConfig.language,
6361                autoclose: true,
6362                todayHighlight: true,
6363                clearBtn: true
6364            }));
6365            
6366            return;
6367        } else if (statusFields.includes(fieldSelect.value)) {
6368            input = document.createElement('select');
6369            input.className = 'form-select filter-value';
6370            input.innerHTML = `
6371                <option value="">Bitte wählen</option>
6372                ${(this.leadStatuses || DEFAULT_LEAD_STATUSES).map(status => 
6373                    `<option value="${status}">${t(status)}</option>`
6374                ).join('')}
6375            `;
6376        } else if (industryFields.includes(fieldSelect.value)) {
6377            input = document.createElement('select');
6378            input.className = 'form-select filter-value';
6379            input.innerHTML = `
6380                <option value="">Bitte wählen</option>
6381                ${DEFAULT_INDUSTRIES.map(industry => 
6382                    `<option value="${industry}">${t(industry)}</option>`
6383                ).join('')}
6384            `;
6385        } else {
6386            input = document.createElement('input');
6387            input.type = 'text';
6388            input.className = 'form-control filter-value';
6389            input.placeholder = 'Wert eingeben...';
6390        }
6391
6392        // Ersetze das alte Eingabefeld
6393        const oldInput = valueContainer.querySelector('.filter-value, .input-group');
6394        if (oldInput) {
6395            valueContainer.replaceChild(input, oldInput);
6396        } else {
6397            valueContainer.appendChild(input);
6398        }
6399    }
6400
6401    initializeFilterRow(row) {
6402        const fieldSelect = row.querySelector('.filter-field');
6403        const operatorSelect = row.querySelector('.filter-operator');
6404        const valueContainer = row.querySelector('.filter-value-container');
6405
6406        // Stelle sicher, dass die Ort-Option im Feld-Dropdown vorhanden ist
6407        if (fieldSelect && !fieldSelect.querySelector('option[value="city"]')) {
6408            // Füge "Ort" zur Auswahl hinzu, nach "industry" Option
6409            const industryOption = fieldSelect.querySelector('option[value="industry"]');
6410            if (industryOption) {
6411                const cityOption = document.createElement('option');
6412                cityOption.value = 'city';
6413                cityOption.textContent = 'Ort';
6414                industryOption.insertAdjacentElement('afterend', cityOption);
6415            } else {
6416                // Falls "industry" nicht gefunden wird, füge einfach am Ende hinzu
6417                const cityOption = document.createElement('option');
6418                cityOption.value = 'city';
6419                cityOption.textContent = 'Ort';
6420                fieldSelect.appendChild(cityOption);
6421            }
6422        }
6423
6424        if (fieldSelect && operatorSelect && valueContainer) {
6425            // Event-Listener für Änderungen am Feld-Typ
6426            fieldSelect.addEventListener('change', () => {
6427                this.updateFilterOperators(fieldSelect, operatorSelect, valueContainer);
6428                
6429                // Sofort den richtigen Wert-Input initialisieren, damit die Branchenauswahl
6430                // korrekt erstellt wird, wenn "Branche" ausgewählt ist
6431                if (fieldSelect.value === 'industry') {
6432                    const input = document.createElement('select');
6433                    input.className = 'form-select filter-value';
6434                    input.innerHTML = `
6435                        <option value="">Bitte wählen</option>
6436                        ${DEFAULT_INDUSTRIES.map(industry => 
6437                            `<option value="${industry}">${t(industry)}</option>`
6438                        ).join('')}
6439                    `;
6440                    
6441                    // Ersetze das alte Eingabefeld
6442                    const oldInput = valueContainer.querySelector('.filter-value, .input-group');
6443                    if (oldInput) {
6444                        valueContainer.replaceChild(input, oldInput);
6445                    } else {
6446                        valueContainer.appendChild(input);
6447                    }
6448                }
6449            });
6450            
6451            // Bestehender Event-Listener für Operator-Änderungen
6452            operatorSelect.addEventListener('change', () => this.updateFilterValueInput(fieldSelect, operatorSelect, valueContainer));
6453            
6454            // Initialisiere die Operatoren für das ausgewählte Feld
6455            this.updateFilterOperators(fieldSelect, operatorSelect, valueContainer);
6456        }
6457    }
6458
6459    async applySmartList(listId) {
6460            // console.log(`[applySmartList] Attempting to apply smart list with ID: ${listId}`); // DEBUG LOG
6461        try {
6462            UI.showLoading('Smart List wird geladen...');
6463
6464            const user = auth.currentUser;
6465            if (!user) {
6466                console.error("[applySmartList] User not logged in."); // DEBUG LOG
6467                throw new Error('Benutzer nicht eingeloggt');
6468            }
6469
6470            // Speichere die aktive Smart-Listen-ID im localStorage
6471            localStorage.setItem('activeSmartListId', listId);
6472            // console.log(`[applySmartList] Saved active smart list ID to localStorage: ${listId}`);
6473
6474            // Get the smart list document from the user's subcollection
6475            /* phase8c */ const _smData = await SmartListsApi.get(listId).catch(()=>null); const smartListDoc = { exists: () => !!_smData, data: () => _smData || {} };
6476            if (!smartListDoc.exists()) {
6477                 console.error(`[applySmartList] Smart list document not found for ID: ${listId}`); // DEBUG LOG
6478                throw new Error('Smart List nicht gefunden');
6479            }
6480
6481            const smartList = { id: smartListDoc.id, ...smartListDoc.data() };
6482            // console.log('[applySmartList] Applying smart list definition:', smartList); // DEBUG LOG
6483
6484            // Speichere die ID der aktiven Smart List INTERN
6485            this.activeSmartListId = listId;
6486
6487            // Filter contacts based on smart list criteria
6488            this.currentFilteredContacts = this.contacts.filter(contact => {
6489            // console.log(`\n[applySmartList] Checking contact ID: ${contact.id}, Name: ${contact.name}`); // DEBUG LOG
6490                return smartList.filters.every(filter => {
6491            // console.log(`  [applySmartList] Applying filter - Field: ${filter.field}, Operator: ${filter.operator}, Value: "${filter.value}"`); // DEBUG LOG
6492
6493                    // Get the correct field value based on field mapping
6494                    let value;
6495                    switch(filter.field) {
6496                        case 'status':
6497                            value = contact.lead_status;
6498                            break;
6499                        case 'updatedAt':
6500                            value = contact.updated_at;
6501                            break;
6502                        case 'createdAt':
6503                            value = contact.created_at;
6504                            break;
6505                        case 'industry':
6506                            value = contact.industry;
6507                            break;
6508                        case 'city':
6509                            value = contact.city;
6510                            break;
6511                        default:
6512                            value = contact[filter.field];
6513                    }
6514
6515            // console.log(`  [applySmartList] Contact value for field "${filter.field}":`, value); // DEBUG LOG
6516
6517                    if (!value && value !== 0 && value !== false) {
6518            // console.log('  [applySmartList] -> Filter failed (value is missing/null/undefined)'); // DEBUG LOG
6519                        return false;
6520                    }
6521
6522                    // Spezielle Behandlung für Datumsfelder
6523                    if (['lastContact', 'createdAt', 'updatedAt'].includes(filter.field)) {
6524                        if (!filter.value) {
6525                            return false;
6526                        }
6527                        
6528                        // Verbesserte Datumsumwandlung für Firestore Timestamp
6529                        let contactDate;
6530                        if (value?.toDate) {
6531                            // Firestore Timestamp
6532                            contactDate = value.toDate();
6533            // console.log('Converted from Firestore timestamp:', contactDate);
6534                        } else if (value instanceof Date) {
6535                            contactDate = value;
6536                        } else if (typeof value === 'object' && value.seconds) {
6537                            // Manuelle Umwandlung von Timestamp-ähnlichem Objekt
6538                            contactDate = new Date(value.seconds * 1000);
6539            // console.log('Converted from seconds timestamp:', contactDate);
6540                        } else if (typeof value === 'number') {
6541                            contactDate = new Date(value);
6542                        } else if (typeof value === 'string') {
6543                            contactDate = new Date(value);
6544                        } else {
6545            // console.log('Unbekannter Datumstyp:', typeof value);
6546                            return false;
6547                        }
6548
6549                        // Filtern nach Datum
6550            // console.log('Contact date before parsing:', contactDate);
6551                        
6552                        // Filter-Datum parsen - erwartet Format: DD.MM.YYYY
6553                        const parts = filter.value.split('.');
6554                        if (parts.length !== 3) {
6555            // console.log('Invalid date format in filter:', filter.value);
6556                            return false;
6557                        }
6558                        
6559                        const day = parseInt(parts[0], 10);
6560                        const month = parseInt(parts[1], 10) - 1; // JS-Monate beginnen bei 0
6561                        const year = parseInt(parts[2], 10);
6562                        
6563                        if (isNaN(day) || isNaN(month) || isNaN(year)) {
6564            // console.log('Invalid date parts:', { day, month, year });
6565                            return false;
6566                        }
6567                        
6568                        const filterDate = new Date(year, month, day);
6569            // console.log('Filter date:', filterDate);
6570
6571                        if (isNaN(contactDate.getTime()) || isNaN(filterDate.getTime())) {
6572            // console.log('Invalid date conversion');
6573                            return false;
6574                        }
6575
6576                        // Setze die Uhrzeiten auf Mitternacht für den reinen Datumsvergleich
6577                        contactDate = new Date(contactDate.getFullYear(), contactDate.getMonth(), contactDate.getDate());
6578                        
6579            // console.log('Comparing dates:', {
6580            //                 contactDate,
6581            //                 filterDate,
6582            //                 operator: filter.operator
6583            //             });
6584
6585                        switch (filter.operator) {
6586                            case 'equals':
6587                                return contactDate.getTime() === filterDate.getTime();
6588                            case 'notEquals':
6589                                return contactDate.getTime() !== filterDate.getTime();
6590                            case 'before':
6591                                return contactDate.getTime() < filterDate.getTime();
6592                            case 'after':
6593                                return contactDate.getTime() > filterDate.getTime();
6594                            case 'onOrBefore':
6595                                return contactDate.getTime() <= filterDate.getTime();
6596                            case 'onOrAfter':
6597                                return contactDate.getTime() >= filterDate.getTime();
6598                            default:
6599                                return false;
6600                        }
6601                    }
6602
6603                    // Behandlung für String-Felder
6604                    const contactValue = String(value).toLowerCase();
6605                    const filterValue = String(filter.value).toLowerCase();
6606
6607            // console.log(`  [applySmartList] Comparing string values: "${contactValue}" ${filter.operator} "${filterValue}"`); // DEBUG LOG
6608
6609                    let result = false; // DEBUG LOG
6610                    switch (filter.operator) {
6611                        case 'contains':
6612                            result = contactValue.includes(filterValue); // DEBUG LOG
6613                            break; // DEBUG LOG
6614                        case 'equals':
6615                            result = contactValue === filterValue; // DEBUG LOG
6616                             break; // DEBUG LOG
6617                        case 'notEquals':
6618                            result = contactValue !== filterValue; // DEBUG LOG
6619                             break; // DEBUG LOG
6620                        case 'startsWith':
6621                            result = contactValue.startsWith(filterValue); // DEBUG LOG
6622                             break; // DEBUG LOG
6623                        case 'endsWith':
6624                             result = contactValue.endsWith(filterValue); // DEBUG LOG
6625                             break; // DEBUG LOG
6626                        default:
6627                            result = false; // DEBUG LOG
6628                    }
6629            // console.log('  [applySmartList] -> Filter result (string/other):', result); // DEBUG LOG
6630                    return result; // DEBUG LOG
6631                });
6632            });
6633
6634            // console.log('[applySmartList] Filtered contacts count:', this.currentFilteredContacts.length); // DEBUG LOG
6635            // console.log('[applySmartList] Filtered contacts sample:', this.currentFilteredContacts.slice(0, 5)); // DEBUG LOG
6636
6637            // Aktualisiere die Kontaktliste
6638            // WICHTIG: renderContacts erstellt den Placeholder neu!
6639            await this.renderContacts(this.currentFilteredContacts);
6640            
6641            // Binde Listener neu, NACHDEM die gefilterte Liste gerendert wurde
6642            this.setupContactEventListeners();
6643            
6644            // Stelle das Smart-Listen-Dropdown wieder her, da renderContacts es entfernt hat
6645            this.updateSmartListsUI();
6646            
6647            // Aktualisiere den Badge-Counter
6648            const clickedButton = document.querySelector(`button[data-list-id="${listId}"]`);
6649            const badge = clickedButton?.querySelector('.badge');
6650            if (badge) {
6651                badge.textContent = this.currentFilteredContacts.length;
6652            }
6653
6654            // Zeige Feedback
6655            UI.showSuccess(`Smart List "${smartList.name}" wurde angewendet. ${this.currentFilteredContacts.length} Kontakte gefunden.`);
6656
6657            // Speichere den aktualisierten Zustand nach Smart List Anwendung
6658            this.storeContactsState();
6659
6660        } catch (error) {
6661            console.error('[applySmartList] Error applying smart list:', error); // DEBUG LOG
6662            UI.showError('Fehler beim Anwenden der Smart List: ' + error.message);
6663        } finally {
6664            UI.hideLoading();
6665        }
6666    }
6667
6668    async showNewContactModal() {
6669        // Initialize modal if not already done
6670        this.initializeModal();
6671
6672        // Apply translations to modal elements
6673        const modalTitle = document.querySelector('#addContactModal .modal-title');
6674        if (modalTitle) {
6675            modalTitle.textContent = t('Neuer Kontakt');
6676        }
6677
6678        // Translate all labels
6679        const labels = document.querySelectorAll('#addContactModal label');
6680        labels.forEach(label => {
6681            const i18nKey = label.getAttribute('data-i18n');
6682            if (i18nKey) {
6683                label.textContent = t(i18nKey);
6684            }
6685        });
6686
6687        // Translate buttons in modal footer
6688        const modalFooterButtons = document.querySelectorAll('#addContactModal .modal-footer button');
6689        modalFooterButtons.forEach(button => {
6690            const i18nKey = button.getAttribute('data-i18n');
6691            if (i18nKey) {
6692                button.textContent = t(i18nKey);
6693            }
6694        });
6695
6696        // Translate "Bitte wählen" options in all selects
6697        const brancheSelect = document.querySelector('#addContactModal select[name="industry"]');
6698        if (brancheSelect) {
6699            const firstOption = brancheSelect.querySelector('option[value=""]');
6700            if (firstOption) {
6701                firstOption.textContent = t('Bitte wählen');
6702            }
6703        }
6704
6705        // Dynamically populate the aufgabe dropdown
6706        const aufgabeSelect = document.querySelector('#addContactModal select[name="aufgabe"]');
6707        if (aufgabeSelect) {
6708            aufgabeSelect.innerHTML = `<option value="">${t('Bitte wählen')}</option>`;
6709            AUFGABE_OPTIONS.forEach(option => {
6710                const optionElement = document.createElement('option');
6711                optionElement.value = option;
6712                optionElement.textContent = t(option); // Translate aufgabe option
6713                aufgabeSelect.appendChild(optionElement);
6714            });
6715        }
6716
6717        // Dynamically populate the lead status dropdown
6718        const leadStatusSelect = document.querySelector('#addContactModal select[name="lead_status"]');
6719        if (leadStatusSelect && this.leadStatuses && this.leadStatuses.length > 0) {
6720            leadStatusSelect.innerHTML = `<option value="">${t('Bitte wählen...')}</option>`; // Add default option with translation
6721            this.leadStatuses.forEach(status => {
6722                const option = document.createElement('option');
6723                option.value = status;
6724                option.textContent = t(status); // Translate lead status
6725                leadStatusSelect.appendChild(option);
6726            });
6727        } else if (leadStatusSelect) {
6728            // Fallback if leadStatuses are not loaded
6729            leadStatusSelect.innerHTML = `<option value="">${t('Laden...')}</option>`;
6730            console.warn("Lead statuses not loaded yet for add contact modal.");
6731            // Attempt to load them again if needed, or provide a default set
6732        }
6733
6734        // kundenfelder-v1: eigene Felder ins Anlage-Modal einblenden
6735        try {
6736            const cfDefs = await ladeKundenfelder();
6737            this._cfDefs = cfDefs;
6738            const mb = document.querySelector('#addContactModal .modal-body');
6739            if (mb) {
6740                let host = mb.querySelector('#addContactCustomFields');
6741                if (!host) {
6742                    host = document.createElement('div');
6743                    host.id = 'addContactCustomFields';
6744                    mb.appendChild(host);
6745                }
6746                host.innerHTML = cfDefs.length
6747                    ? `<hr><p class="fw-bold small mb-2">${t('Eigene Felder')}</p><div class="row g-2">${renderKundenfelder(cfDefs, {}, { colClass: 'col-12' })}</div>`
6748                    : '';
6749            }
6750        } catch (e) { console.warn('Kundenfelder im Anlage-Modal nicht geladen:', e); }
6751
6752        // Show modal
6753        if (this.addContactModal) {
6754            this.addContactModal.show();
6755        }
6756    }
6757
6758    getCallStatusEmoji(callSummary) {
6759        if (!callSummary) {
6760            return '<i class="fas fa-question text-secondary" style="font-size: 0.8em;"></i>';
6761        }
6762
6763        // Anrufbeantworter check
6764        const isAnrufbeantworter = callSummary.anrufbeantworter === true;
6765        
6766        // Prüfe zuerst Anrufbeantworter
6767        if (isAnrufbeantworter) {
6768            return '<i class="fas fa-meh text-warning" style="font-size: 1.2em;"></i>';
6769        }
6770        
6771        if (callSummary.call_successful === true && callSummary.duration >= 15) {
6772            // Suche nach laufenden Tasks für diesen Kontakt und setze sie auf "Erledigt"
6773            if (callSummary.contactId) {  // Only proceed if we have a contact ID
6774                /* phase8c */ ContactsApi.listTasks(callSummary.contactId).then(tasks => {
6775                    const running = (tasks || []).filter(t => (t.status || '') === 'Läuft' || (t.status || '').toLowerCase() === 'läuft');
6776                    return Promise.all(running.map(t => ContactsApi.updateTask(callSummary.contactId, t.id, { status: 'Erledigt' })));
6777                }).catch(err => console.error('Fehler beim Aktualisieren der Tasks:', err));
6778            }
6779            
6780            return '<i class="fas fa-smile text-success" style="font-size: 1.2em;"></i>';
6781        }
6782
6783        // Wenn der Anruf eine Dauer > 0 hat, aber nicht erfolgreich oder zu kurz war
6784        if (callSummary.duration > 0) {
6785            return '<i class="fas fa-frown text-info" style="font-size: 1.2em;"></i>';
6786        }
6787        
6788        return '<i class="fas fa-times-circle text-danger" style="font-size: 0.8em;"></i>';
6789    }
6790
6791    getCallStatusTitle(callSummary) {
6792        if (!callSummary) {
6793            return t('Kein Anrufstatus verfügbar');
6794        }
6795
6796        const isAnrufbeantworter = callSummary.anrufbeantworter === true;
6797
6798        // Prüfe zuerst Anrufbeantworter
6799        if (isAnrufbeantworter) {
6800            return t('Anrufbeantworter erreicht');
6801        }
6802
6803        // Prüfe ob der Anruf erfolgreich UND lang genug war
6804        if (callSummary.call_successful === true && callSummary.duration >= 15) {
6805            return t('Anruf erfolgreich');
6806        }
6807
6808        // Wenn der Anruf eine Dauer > 0 hat
6809        if (callSummary.duration > 0) {
6810            return t('Anruf nicht erfolgreich');
6811        }
6812
6813        return t('Kein Kontakt');
6814    }
6815
6816    getCallStatusColor(callSummary) {
6817        if (!callSummary) {
6818            return 'text-secondary';
6819        }
6820
6821        const isAnrufbeantworter = callSummary.anrufbeantworter === true;
6822
6823        // Prüfe zuerst Anrufbeantworter
6824        if (isAnrufbeantworter) {
6825            return 'text-warning';
6826        }
6827
6828        // Prüfe ob der Anruf erfolgreich UND lang genug war
6829        if (callSummary.call_successful === true && callSummary.duration >= 15) {
6830            return 'text-success';
6831        }
6832
6833        // Wenn der Anruf eine Dauer > 0 hat
6834        if (callSummary.duration > 0) {
6835            return 'text-info';
6836        }
6837
6838        return 'text-danger';
6839    }
6840
6841    async getLatestCallSummary(contactId) {
6842        try {
6843            const user = auth.currentUser;
6844            if (!user) return null;
6845
6846            /* phase8c */ const _c = await ContactsApi.get(contactId).catch(()=>null);
6847            if (!_c) return null;
6848            const customerId = String(_c.customer_id || '');
6849            const _csResp = await _c8c_authedFetch('/api/call-summary/list', { method: 'POST', body: JSON.stringify({ customer_id: customerId, limit: 1 }) });
6850            if (!_csResp.ok) return null;
6851            const _csJ = await _csResp.json();
6852            const _csList = _csJ.summaries || [];
6853            return _csList.length > 0 ? _csList[0] : null;
6854            
6855        } catch (error) {
6856            console.error('Error getting latest call summary:', error);
6857            return null;
6858        }
6859    }
6860
6861    hasMultiCallPermission() {
6862        const user = auth.currentUser;
6863        if (!user) return false;
6864
6865        // Hole das User-Dokument aus dem Cache wenn möglich
6866        const userDoc = this.userDoc;
6867        if (!userDoc) return false;
6868
6869        // Prüfe ob multicall aktiviert ist
6870        return userDoc.multicall === true;
6871    }
6872
6873    hasPersonalAssistantPermission() {
6874        const user = auth.currentUser;
6875        if (!user) return false;
6876
6877        const userDoc = this.userDoc;
6878        if (!userDoc) return false;
6879
6880        return userDoc.personal_assistent === true;
6881    }
6882
6883    // Vollständige Implementierung der Kalenderoptions-Funktion
6884    async getCalendarOptionsHtml() {
6885        try {
6886            const user = auth.currentUser;
6887            if (!user) {
6888            // console.log('No user logged in');
6889                return ''; // Gib einen leeren String zurück, wenn kein Benutzer angemeldet ist
6890            }
6891            
6892            // Kalendertypen aus der Datenbank laden
6893            const tenantId = this.userDoc?.tenant_id || this.userDoc?.tenantId || window.currentTenantId;
6894            
6895            if (!tenantId) {
6896            // console.log('No tenant ID available');
6897                return '';
6898            }
6899            
6900            // phase8c calendar-types via API
6901            const _ct3 = await _c8c_getCalendarTypes();
6902            const querySnapshot = { empty: _ct3.length === 0, forEach: (cb) => _ct3.forEach(d => cb({ id: d.id || d.name, data: () => d })) };
6903            let calendarOptions = '';
6904            
6905            // Erstelle die Optionen
6906            if (!querySnapshot.empty) {
6907                calendarOptions += '<option value="">Kalender auswählen...</option>';
6908                
6909                querySnapshot.forEach((doc) => {
6910                    const calendarData = doc.data();
6911                    calendarOptions += `<option value="${doc.id}" data-color="${calendarData.color || '#3788d8'}">${calendarData.name}</option>`;
6912                });
6913            } else {
6914                // Wenn keine Kalender gefunden, zeige einen Platzhalter
6915                calendarOptions += '<option value="">Kein Kalender verfügbar</option>';
6916            }
6917            
6918            return calendarOptions;
6919        } catch (error) {
6920            console.error('Error loading calendar options:', error);
6921            return '<option value="">Fehler beim Laden der Kalender</option>';
6922        }
6923    }
6924
6925    // Übersetze alle Modals bei Sprachwechsel
6926    translateAllModals() {
6927        this.translateSmartListModal();
6928        this.translateEmailTemplateModal();
6929        this.updateAutoCallModalTexts(); // AutoCall Rules Modal übersetzen
6930
6931        // Übersetze auch das "Neuer Kontakt" Modal wenn es existiert
6932        const addContactModal = document.getElementById('addContactModal');
6933        if (addContactModal) {
6934            // Modal-Titel
6935            const modalTitle = addContactModal.querySelector('.modal-title');
6936            if (modalTitle) {
6937                const titleKey = modalTitle.getAttribute('data-i18n');
6938                if (titleKey) modalTitle.textContent = t(titleKey);
6939            }
6940
6941            // Alle Labels
6942            const labels = addContactModal.querySelectorAll('label[data-i18n]');
6943            labels.forEach(label => {
6944                const key = label.getAttribute('data-i18n');
6945                if (key) label.textContent = t(key);
6946            });
6947
6948            // Buttons
6949            const buttons = addContactModal.querySelectorAll('button[data-i18n]');
6950            buttons.forEach(button => {
6951                const key = button.getAttribute('data-i18n');
6952                if (key) button.textContent = t(key);
6953            });
6954        }
6955    }
6956
6957    // Übersetze Smart List Modal
6958    translateSmartListModal() {
6959        console.log('translateSmartListModal aufgerufen - Version 2024-10-30-14:00');
6960        const modal = document.getElementById('smartListModal');
6961        if (!modal) {
6962            console.log('SmartList Modal nicht gefunden!');
6963            return;
6964        }
6965        console.log('SmartList Modal gefunden, starte Übersetzung...');
6966
6967        // Übersetze Modal-Titel
6968        const modalTitle = modal.querySelector('.modal-title');
6969        if (modalTitle) {
6970            const titleKey = modalTitle.getAttribute('data-i18n');
6971            console.log('Modal Title Key:', titleKey, '-> Übersetzung:', t(titleKey));
6972            if (titleKey) modalTitle.textContent = t(titleKey);
6973        }
6974
6975        // Übersetze alle Labels
6976        const labels = modal.querySelectorAll('label[data-i18n]');
6977        console.log('Gefundene Labels:', labels.length);
6978        labels.forEach(label => {
6979            const key = label.getAttribute('data-i18n');
6980            if (key) {
6981                console.log('Label:', key, '-> Übersetzung:', t(key));
6982                label.textContent = t(key);
6983            }
6984        });
6985
6986        // Übersetze alle Options in Selects
6987        const options = modal.querySelectorAll('option[data-i18n]');
6988        console.log('Gefundene Options:', options.length);
6989        options.forEach(option => {
6990            const key = option.getAttribute('data-i18n');
6991            if (key) option.textContent = t(key);
6992        });
6993
6994        // Übersetze Placeholder - ALLE inputs mit placeholder
6995        const placeholderInputs = modal.querySelectorAll('input[placeholder]');
6996        console.log('Gefundene Inputs mit Placeholder:', placeholderInputs.length);
6997        placeholderInputs.forEach(input => {
6998            const placeholderKey = input.getAttribute('data-i18n-placeholder');
6999            if (placeholderKey) {
7000                console.log('Placeholder:', placeholderKey, '-> Übersetzung:', t(placeholderKey));
7001                input.placeholder = t(placeholderKey);
7002            } else if (input.placeholder) {
7003                // Versuche den Placeholder-Text direkt zu übersetzen
7004                const translated = t(input.placeholder);
7005                console.log('Direkter Placeholder:', input.placeholder, '-> Übersetzung:', translated);
7006                if (translated !== input.placeholder) {
7007                    input.placeholder = translated;
7008                }
7009            }
7010        });
7011
7012        // Übersetze Buttons
7013        const buttons = modal.querySelectorAll('button[data-i18n], button[data-i18n-html]');
7014        console.log('Gefundene Buttons:', buttons.length);
7015        buttons.forEach(button => {
7016            const key = button.getAttribute('data-i18n') || button.getAttribute('data-i18n-html');
7017            console.log('Button Key:', key, '-> Übersetzung:', t(key));
7018            if (key) {
7019                // Für Buttons mit Icons behalte das Icon
7020                if (button.querySelector('i')) {
7021                    const icon = button.querySelector('i').outerHTML;
7022                    button.innerHTML = icon + ' ' + t(key);
7023                } else {
7024                    button.textContent = t(key);
7025                }
7026            }
7027        });
7028
7029        console.log('SmartList Modal Übersetzung abgeschlossen');
7030    }
7031
7032    // Smart List Modal vorbereiten und anzeigen
7033    createSmartListModal() {
7034        // Stelle sicher, dass das Modal-Element existiert
7035        let modalElement = document.getElementById('smartListModal');
7036
7037        // Wenn Modal existiert, übersetze alle Elemente
7038        if (modalElement) {
7039            this.translateSmartListModal();
7040        }
7041
7042        // Falls nicht vorhanden, erstelle es
7043        if (!modalElement) {
7044            const modalHtml = `
7045                <div class="modal fade" id="smartListModal" tabindex="-1" role="dialog" aria-labelledby="smartListModalLabel" aria-hidden="true">
7046                    <div class="modal-dialog modal-lg" role="document">
7047                        <div class="modal-content">
7048                            <div class="modal-header">
7049                                <h5 class="modal-title" id="smartListModalLabel">${t('Smart-Liste erstellen')}</h5>
7050                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
7051                            </div>
7052                            <div class="modal-body">
7053                                <form id="smartListForm">
7054                                    <div class="mb-3">
7055                                        <label for="listName" class="form-label">${t('Name der Liste')}</label>
7056                                        <input type="text" class="form-control" id="listName" name="listName" required />
7057                                    </div>
7058                                    <div class="mb-3">
7059                                        <label class="form-label">${t('Filter')}</label>
7060                                        <div id="filterContainer">
7061                                            <div class="filter-row mb-3 d-flex">
7062                                                <div class="flex-grow-1 me-2">
7063                                                    <select class="form-select filter-field">
7064                                                        <option value="name">${t('Name')}</option>
7065                                                        <option value="email">${t('E-Mail')}</option>
7066                                                        <option value="company">${t('Firma')}</option>
7067                                                        <option value="phone_number">${t('Telefon')}</option>
7068                                                        <option value="industry">${t('Branche')}</option>
7069                                                        <option value="city">${t('Ort')}</option>
7070                                                        <option value="status">${t('Lead-Status')}</option>
7071                                                        <option value="lastContact">${t('Letzter Kontakt')}</option>
7072                                                        <option value="createdAt">${t('Erstellt am')}</option>
7073                                                        <option value="updatedAt">${t('Aktualisiert am')}</option>
7074                                                    </select>
7075                                                </div>
7076                                                <div class="flex-grow-1 me-2">
7077                                                    <select class="form-select filter-operator">
7078                                                        <option value="contains">${t('enthält')}</option>
7079                                                        <option value="equals">${t('ist gleich')}</option>
7080                                                        <option value="notEquals">${t('ist nicht gleich')}</option>
7081                                                        <option value="startsWith">${t('beginnt mit')}</option>
7082                                                        <option value="endsWith">${t('endet mit')}</option>
7083                                                        <option value="before">${t('vor dem')}</option>
7084                                                        <option value="after">${t('nach dem')}</option>
7085                                                    </select>
7086                                                </div>
7087                                                <div class="flex-grow-1 me-2 filter-value-container">
7088                                                    <input type="text" class="form-control filter-value" placeholder="${t('Wert eingeben...')}" />
7089                                                </div>
7090                                                <button type="button" class="btn btn-outline-danger remove-filter">
7091                                                    <i class="fas fa-times"></i>
7092                                                </button>
7093                                            </div>
7094                                        </div>
7095                                        <button type="button" class="btn btn-outline-secondary" id="addFilterBtn">
7096                                            <i class="fas fa-plus"></i> ${t('Filter hinzufügen')}
7097                                        </button>
7098                                    </div>
7099                                    <div class="modal-footer">
7100                                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="Abbrechen">${t('Abbrechen')}</button>
7101                                        <button type="submit" class="btn btn-primary" data-i18n="Liste erstellen">${t('Liste erstellen')}</button>
7102                                    </div>
7103                                </form>
7104                            </div>
7105                        </div>
7106                    </div>
7107                </div>
7108            `;
7109
7110            document.body.insertAdjacentHTML('beforeend', modalHtml);
7111            modalElement = document.getElementById('smartListModal');
7112        }
7113        
7114        // Initialisiere das Modal
7115        if (!this.smartListModal) {
7116            this.smartListModal = new bootstrap.Modal(modalElement);
7117        }
7118        
7119        // Event-Listener für das Formular
7120        const form = document.getElementById('smartListForm');
7121        if (form) {
7122            // Entferne vorherige Event-Listener
7123            const newForm = form.cloneNode(true);
7124            form.parentNode.replaceChild(newForm, form);
7125            newForm.addEventListener('submit', (e) => this.handleSmartListSubmit(e));
7126            
7127            // Initialisiere die erste Filter-Zeile
7128            const firstRow = newForm.querySelector('.filter-row');
7129            if (firstRow) {
7130                this.initializeFilterRow(firstRow);
7131            }
7132        }
7133        
7134        // Event-Listener für Filter hinzufügen
7135        const addFilterBtn = document.getElementById('addFilterBtn');
7136        if (addFilterBtn) {
7137            // Entferne vorherige Event-Listener
7138            const newBtn = addFilterBtn.cloneNode(true);
7139            addFilterBtn.parentNode.replaceChild(newBtn, addFilterBtn);
7140            newBtn.addEventListener('click', () => this.addFilterRow());
7141        }
7142        
7143        // Event-Listener für Filter entfernen
7144        const filterContainer = document.getElementById('filterContainer');
7145        if (filterContainer) {
7146            // Entferne vorherige Event-Listener
7147            const newContainer = filterContainer.cloneNode(true);
7148            filterContainer.parentNode.replaceChild(newContainer, filterContainer);
7149            newContainer.addEventListener('click', (e) => {
7150                if (e.target.closest('.remove-filter')) {
7151                    e.target.closest('.filter-row').remove();
7152                }
7153            });
7154        }
7155        
7156        // Zeige das Modal an
7157        this.smartListModal.show();
7158    }
7159
7160    renderVirtualizedContacts() {
7161        const container = document.getElementById('contactsContainer');
7162        const viewportHeight = container.clientHeight;
7163        const rowHeight = 50; // Angenommene Höhe einer Zeile
7164        
7165        const visibleRowsCount = Math.ceil(viewportHeight / rowHeight);
7166        const bufferRowsCount = 5; // Zusätzliche Reihen oben und unten
7167        
7168        const scrollTop = container.scrollTop;
7169        const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - bufferRowsCount);
7170        const endIndex = Math.min(
7171            this.filteredContacts.length, 
7172            Math.ceil((scrollTop + viewportHeight) / rowHeight) + bufferRowsCount
7173        );
7174        
7175        // Nur die sichtbaren Kontakte rendern
7176        const visibleContacts = this.filteredContacts.slice(startIndex, endIndex);
7177        
7178        // Container für virtuelle Liste setzen
7179        const totalHeight = this.filteredContacts.length * rowHeight;
7180        const topPadding = startIndex * rowHeight;
7181        
7182        container.innerHTML = `
7183            <div style="height: ${topPadding}px"></div>
7184            <div id="visibleRows"></div>
7185            <div style="height: ${totalHeight - topPadding - (endIndex - startIndex) * rowHeight}px"></div>
7186        `;
7187        
7188        // Sichtbare Reihen rendern
7189        const visibleRowsContainer = document.getElementById('visibleRows');
7190        visibleRowsContainer.innerHTML = visibleContacts.map(contact => this.createContactRow(contact)).join('');
7191        
7192        // Nur einen Event-Listener für den Container mit Event-Delegation
7193        this.setupEventDelegation();
7194    }
7195
7196    setupEventDelegation() {
7197        const container = document.getElementById('contactsContainer');
7198        
7199        // Entferne vorherige Event-Listener, falls vorhanden
7200        const newContainer = container.cloneNode(true);
7201        container.parentNode.replaceChild(newContainer, container);
7202        
7203        // Nur ein Event-Listener für den Container
7204        newContainer.addEventListener('click', (e) => {
7205            const contactLink = e.target.closest('.contact-link');
7206            if (contactLink) {
7207                // Prüfen ob Checkbox geklickt wurde
7208                if (e.target.closest('.checkbox-column') || (e.target.closest('.contact-noclick') && !e.target.closest('.contact-open-item'))) {
7209                    return;
7210                }
7211                
7212                e.preventDefault();
7213                const contactId = contactLink.dataset.id;
7214                if (contactId) {
7215                    // NEUE FUNKTION: Speichere den aktuellen Zustand vor der Navigation
7216                    this.storeContactsState();
7217                    
7218                    // Update URL without page reload
7219                    window.history.pushState({}, '', `?page=contact-detail&id=${contactId}`);
7220                    // Load the contact detail page
7221                    Pages.loadPage('contact-detail');
7222                }
7223            }
7224        });
7225        
7226        // Ebenso für Checkboxen
7227        newContainer.addEventListener('change', (e) => {
7228            if (e.target.matches('.contact-checkbox')) {
7229                e.stopPropagation();
7230                this.updateBulkActionsVisibility();
7231            }
7232        });
7233    }
7234
7235    // Verbesserte Ladestrategie in Chunks
7236    async loadAllContacts() {
7237        this.isLoading = true;
7238        this.showLoadingIndicator();
7239        
7240        try {
7241            // Hole zunächst eine kleine Menge für schnelle Anzeige
7242            await this.loadContactsPage(1, 50);
7243            this.renderContacts();
7244            
7245            // Lade dann den Rest im Hintergrund
7246            this.loadRemainingContactsInBackground();
7247        } catch (error) {
7248            console.error('Fehler beim Laden der Kontakte:', error);
7249            this.showError('Fehler beim Laden der Kontakte');
7250        } finally {
7251            this.isLoading = false;
7252            this.hideLoadingIndicator();
7253        }
7254    }
7255
7256    // Laden im Hintergrund (nur für Desktop)
7257    async loadRemainingContactsInBackground(tenantId) {
7258        if (!tenantId || !this.lastVisible || !this.hasMore) {
7259            return;
7260        }
7261        
7262        // Starte das Laden im Hintergrund nach kurzer Verzögerung
7263        setTimeout(async () => {
7264            let currentLastVisible = this.lastVisible;
7265            let hasMore = this.hasMore;
7266            
7267            while (hasMore) {
7268                try {
7269                    /* phase8c bg-pagination noop */ const snapshot = { empty: true, forEach: () => {}, docs: [], size: 0 };
7270                    
7271                    if (snapshot.empty) {
7272                        hasMore = false;
7273                        this.allContactsLoaded = true;
7274                        this.backgroundLoadingComplete = true;
7275                        break;
7276                    }
7277                    
7278                    // Füge neue Kontakte zum Array hinzu
7279                    const newContacts = [];
7280                    snapshot.forEach(doc => {
7281                        const fullData = doc.data();
7282                        
7283                        // Speichere vollständige Daten im Cache
7284                        this.fullContactsCache.set(doc.id, { id: doc.id, ...fullData });
7285                        
7286                        // Minimale Daten für die Liste
7287                        newContacts.push({
7288                            id: doc.id,
7289                            name: fullData.name || '',
7290                            email: fullData.email || '',
7291                            phone_number: fullData.phone_number || '',
7292                            company: fullData.company || '',
7293                            updated_at: fullData.updated_at,
7294                            lead_status: fullData.lead_status || '',
7295                            task: fullData.task || '',
7296                            tenantID: fullData.tenantID || '',
7297                            industry: fullData.industry || '',
7298                            team: fullData.team || [], team_uids: fullData.team_uids || [], liste: fullData.liste || '', // kontakt-team-v1
7299                            created_at: fullData.created_at
7300                        });
7301                    });
7302                    
7303                    // Füge neue Kontakte hinzu
7304                    this.contacts = [...this.contacts, ...newContacts];
7305                    
7306                    // Aktualisiere last visible
7307                    currentLastVisible = snapshot.docs[snapshot.docs.length - 1];
7308                    hasMore = snapshot.docs.length === 500;
7309                    
7310                    // console.log(`Weitere ${newContacts.length} Kontakte im Hintergrund geladen. Total: ${this.contacts.length}`);
7311                    
7312                    // Kurze Pause zwischen den Batches
7313                    await new Promise(resolve => setTimeout(resolve, 200));
7314                    
7315                } catch (error) {
7316                    console.error('Fehler beim Hintergrundladen:', error);
7317                    break;
7318                }
7319            }
7320            
7321            this.allContactsLoaded = true;
7322            this.backgroundLoadingComplete = true;
7323            // console.log('Hintergrundladen abgeschlossen. Total Kontakte:', this.contacts.length);
7324            
7325            // Aktualisiere Cache mit allen Kontakten
7326            const user = auth.currentUser;
7327            if (user) {
7328                // Desktop cache - nur für Desktop speichern
7329                const cacheKey = `contacts_${user.uid}_${tenantId}_desktop`;
7330                localStorage.setItem(cacheKey, JSON.stringify({
7331                    data: this.contacts,
7332                    timestamp: Date.now()
7333                }));
7334            }
7335        }, 1000); // Starte nach 1 Sekunde
7336    }
7337
7338    // Effizienteres Filtern und Sortieren
7339    filterAndSortContacts(searchQuery) {
7340        // Verzögerung für Eingabeeffizienz
7341        clearTimeout(this.filterTimeout);
7342        
7343        this.filterTimeout = setTimeout(() => {
7344            console.time('filterAndSort');
7345            
7346            // Arbeite mit einer Teilmenge, wenn wir viele Kontakte haben
7347            const contactsToProcess = this.contacts.length > 1000 
7348                ? this.contacts.slice(0, 1000) // Verarbeite zunächst nur die ersten 1000
7349                : this.contacts;
7350            
7351            let filtered = contactsToProcess;
7352            
7353            // Filter nur anwenden, wenn eine Suchanfrage vorhanden ist
7354            if (searchQuery && searchQuery.trim()) {
7355                const query = searchQuery.toLowerCase().trim();
7356                filtered = contactsToProcess.filter(contact => {
7357                    // Optimierte Suche mit frühem Abbruch
7358                    // kontakte-filter-v1: optional chaining schuetzt nur gegen
7359                    // null/undefined, nicht gegen Objekt-Felder.
7360                    return (suchText(contact.name).includes(query) ||
7361                            suchText(contact.email).includes(query) ||
7362                            suchText(contact.phone_number).includes(query) ||
7363                            suchText(contact.company).includes(query));
7364                });
7365            }
7366            
7367            // Effizienter sortieren durch Caching der Sortierwerte
7368            if (this.sortField && this.sortDirection) {
7369                filtered.sort((a, b) => {
7370                    const aValue = (a[this.sortField] || '').toString().toLowerCase();
7371                    const bValue = (b[this.sortField] || '').toString().toLowerCase();
7372                    
7373                    return this.sortDirection === 'asc' 
7374                        ? aValue.localeCompare(bValue)
7375                        : bValue.localeCompare(aValue);
7376                });
7377            }
7378            
7379            this.filteredContacts = filtered;
7380            console.timeEnd('filterAndSort');
7381            
7382            // Virtuelle Liste aktualisieren
7383            if (this.contacts.length > 100) {
7384                this.renderVirtualizedContacts();
7385            } else {
7386                this.renderContacts();
7387            }
7388            
7389            // Weitere Kontakte im Hintergrund verarbeiten, wenn wir viele haben
7390            if (this.contacts.length > 1000 && searchQuery && searchQuery.trim()) {
7391                this.processRemainingContactsInBackground(searchQuery);
7392            }
7393        }, 200);
7394    }
7395
7396    // Neue Methode zum schnellen Laden von gecachten Kontakten
7397    async loadLastCachedContacts() {
7398        // this.showLoadingIndicator(); // <-- Entfernt
7399        
7400        try {
7401            // Versuche zuerst den spezifischen Cache mit User und Tenant ID
7402            const user = auth.currentUser;
7403            if (!user) return false;
7404            
7405            // Verwende gespeicherte Tenant-ID falls vorhanden, um Firestore-Abfrage zu vermeiden
7406            let tenantId = this.userDoc?.tenantID || this.userDoc?.tenant_id;
7407            
7408            // Nur wenn noch keine Tenant-ID vorhanden, lade sie von Firestore
7409            if (!tenantId) {
7410                const userDoc = await _phase8UserSnap(user.uid); /* phase8 */
7411                tenantId = userDoc.data()?.tenantID || userDoc.data()?.tenant_id;
7412            }
7413            
7414            const primaryCacheKey = `contacts_${user.uid}_${tenantId}`;
7415            
7416            // Versuche primären Cache
7417            let cachedData = localStorage.getItem(primaryCacheKey);
7418            
7419            // Falls nicht vorhanden, versuche letzten Cache-Key
7420            if (!cachedData) {
7421                const lastCacheKey = localStorage.getItem('last_contacts_cache_key');
7422                if (lastCacheKey) {
7423                    cachedData = localStorage.getItem(lastCacheKey);
7424                }
7425            }
7426            
7427            if (cachedData) {
7428                const parsed = JSON.parse(cachedData);
7429                if (parsed.data && parsed.data.length > 0) {
7430                    // Prüfe ob Cache noch gültig ist (15 Minuten)
7431                    const cacheAge = Date.now() - parsed.timestamp;
7432                    if (cacheAge < 15 * 60 * 1000) {
7433            // console.log(`Lade ${parsed.data.length} Kontakte aus Cache (Alter: ${Math.round(cacheAge/1000)}s)`);
7434                        this.contacts = parsed.data;
7435                        
7436                        // Sortiere Kontakte konsistent (updated_at desc)
7437                        this.contacts.sort((a, b) => {
7438                            const dateA = this.parseContactDate(a.updated_at);
7439                            const dateB = this.parseContactDate(b.updated_at);
7440                            return dateB - dateA; // Neueste zuerst
7441                        });
7442                        
7443                        // Zeige die gecachten Kontakte sofort an
7444                        await this.renderContacts(this.contacts);
7445                        
7446                        // KEIN automatisches Neuladen im Hintergrund mehr!
7447                        // Das wird jetzt von init() gesteuert
7448                        
7449                        return true;
7450                    }
7451                }
7452            }
7453            // Indikator hier nicht ausblenden
7454            return false;
7455        } catch (error) {
7456            console.warn('Error loading cached contacts:', error); // Log error
7457            // Indikator hier nicht ausblenden
7458            return false;
7459        }
7460    }
7461
7462    // Hintergrund-Synchronisation für Kontakte
7463    scheduleBackgroundSync() {
7464        // Synchronisiere Kontakte alle 10 Minuten im Hintergrund
7465        setInterval(() => {
7466            // Nur synchronisieren, wenn die Seite aktiv ist und die Kontakte angezeigt werden
7467            if (document.visibilityState === 'visible' && 
7468                document.getElementById('contactsContainer')) {
7469            // console.log('Starte Hintergrund-Synchronisation der Kontakte...');
7470                this.silentUpdate();
7471            }
7472        }, 10 * 60 * 1000);
7473    }
7474
7475    // Stille Aktualisierung ohne UI-Unterbrechung
7476    async silentUpdate() {
7477        try {
7478            const freshContacts = await this.fetchContactsFromServer();
7479            if (freshContacts && freshContacts.length > 0) {
7480                // Aktualisiere die Kontakte im Hintergrund
7481                this.contacts = freshContacts;
7482                
7483                // Aktualisiere die UI nur, wenn sich die Anzahl der Kontakte geändert hat
7484                const currentlyDisplayed = document.querySelectorAll('.contact-row').length;
7485                if (currentlyDisplayed !== freshContacts.length) {
7486            // console.log('Kontakte im Hintergrund aktualisiert, UI wird aktualisiert');
7487                    this.renderContacts(freshContacts);
7488                } else {
7489            // console.log('Kontakte im Hintergrund aktualisiert, UI unverändert');
7490                }
7491            }
7492        } catch (error) {
7493            console.warn('Fehler bei der stillen Aktualisierung:', error);
7494        }
7495    }
7496
7497    // Stelle sicher, dass die renderInstantCallModal-Methode die Kalenderoptionen korrekt lädt
7498    async renderInstantCallModal(contactId) {
7499        try {
7500            // Bisheriger Code zum Abrufen der Kontaktdaten...
7501            
7502            // Hier die Kalenderoptionen laden
7503            const calendarOptionsHtml = await this.getCalendarOptionsHtml();
7504            
7505            // Modal-Inhalt aktualisieren und sicherstellen, dass das Kalender-Dropdown korrekt befüllt wird
7506            const calendarSelect = document.getElementById('eventCalendar');
7507            if (calendarSelect) {
7508                calendarSelect.innerHTML = calendarOptionsHtml;
7509            }
7510            
7511            // Rest des Codes...
7512        } catch (error) {
7513            console.error('Error rendering instant call modal:', error);
7514        }
7515    }
7516
7517    // KORRIGIERTE/NEUE Bulk Action Funktionen
7518    getSelectedContactIds() {
7519        // Wenn alle gefilterten Kontakte ausgewählt wurden
7520        if (this.allFilteredContactsSelected) {
7521            // console.log("Alle gefilterten Kontakte ausgewählt:", this.selectedFilteredContactIds.length);
7522            return this.selectedFilteredContactIds;
7523        }
7524        
7525        // Ansonsten nur die manuell ausgewählten Checkboxen
7526        const checkboxes = document.querySelectorAll('input[name="contactSelect"]:checked');
7527        const selectedContactIds = Array.from(checkboxes).map(checkbox => checkbox.value);
7528            // console.log("Ausgewählte Kontakt-IDs:", selectedContactIds);
7529        return selectedContactIds;
7530    }
7531
7532    async executeBulkAction() {
7533        const selectedContactIds = this.getSelectedContactIds();
7534        if (selectedContactIds.length === 0) {
7535            UI.showError('Keine Kontakte ausgewählt.');
7536            return;
7537        }
7538
7539        const actionSelect = document.getElementById('bulkActionSelect'); // Annahme: Es gibt ein Select-Element mit dieser ID
7540        if (!actionSelect) {
7541             console.error("Bulk Action Select Element ('bulkActionSelect') nicht gefunden.");
7542             UI.showError("Bulk Action Steuerelement fehlt.");
7543             return;
7544        }
7545        const selectedAction = actionSelect.value;
7546            // console.log("Ausgewählte Bulk Action:", selectedAction);
7547
7548        if (selectedAction === 'update') {
7549            const fieldSelect = document.getElementById('bulkFieldSelect'); // Annahme: Feld-Auswahl
7550             if (!fieldSelect) {
7551                 console.error("Bulk Field Select Element ('bulkFieldSelect') nicht gefunden.");
7552                 UI.showError("Bulk Action Feld-Auswahl fehlt.");
7553                 return;
7554             }
7555            const selectedField = fieldSelect.value;
7556            let updateData = {};
7557            let newValueDescription = ''; // Für Bestätigungsdialog
7558
7559            // console.log("Ausgewähltes Feld für Update:", selectedField);
7560
7561            if (selectedField === 'lead_status') {
7562                const statusSelect = document.getElementById('bulkLeadStatus'); // ID aus vorherigem Code
7563                if (!statusSelect) { UI.showError("Lead Status Auswahl nicht gefunden."); return; }
7564                const newValue = statusSelect.value;
7565                if (!newValue) {
7566                    UI.showError('Bitte einen neuen Lead-Status auswählen.');
7567                    return;
7568                }
7569                updateData.lead_status = newValue;
7570                newValueDescription = `Lead-Status auf '${newValue}'`;
7571            }
7571 else if (selectedField === 'industry') {
7572                const industrySelect = document.getElementById('bulkIndustry'); // ID aus vorherigem Code
7573                 if (!industrySelect) { UI.showError("Branchen Auswahl nicht gefunden."); return; }
7574                const newValue = industrySelect.value;
7575                 if (!newValue) {
7576                    UI.showError('Bitte eine Branche auswählen.');
7577                    return;
7578                }
7579                updateData.industry = newValue;
7580                 newValueDescription = `Branche auf '${newValue}'`;
7581            } else if (selectedField === 'task') { // Aufgabe hinzufügen
7582                const taskSelect = document.getElementById('bulkTask'); // ID aus vorherigem Code
7583                 if (!taskSelect) { UI.showError("Aufgaben Auswahl nicht gefunden."); return; }
7584                 const newValue = taskSelect.value;
7585                 if (!newValue) {
7586                     UI.showError('Bitte eine Aufgabe auswählen.');
7587                     return;
7588                 }
7589                 updateData.task = newValue;
7590                 newValueDescription = `Aufgabe auf '${newValue}'`;
7591            }
7592            // HINWEIS: Bulk Tag Add/Remove wird aktuell nicht über diese UI gesteuert, daher hier entfernt.
7593            // Kann bei Bedarf wieder hinzugefügt werden, wenn die UI Elemente (bulkTags) existieren.
7594            /* else if (selectedField === 'tags_add') { ... }
7595               else if (selectedField === 'tags_remove') { ... } */
7596            else {
7597                UI.showError('Ungültiges Feld für Update ausgewählt.');
7598                return;
7599            }
7600
7601            // Bestätigungsdialog
7602            if (confirm(`${t('Möchten Sie für')} ${selectedContactIds.length} ${t('Kontakte wirklich')} ${newValueDescription} ${t('setzen')}?`)) {
7603                await this.handleBulkUpdate(selectedContactIds, updateData); // Verwende die korrigierte handleBulkUpdate
7604            }
7605
7606        } else if (selectedAction === 'delete') {
7607            // Bestätigungsdialog
7608            if (confirm(`${t('Möchten Sie die ausgewählten')} ${selectedContactIds.length} ${t('Kontakte wirklich löschen? Dieser Vorgang kann nicht rückgängig gemacht werden!')}`)) {
7609                await this.handleBulkDelete(selectedContactIds); // Verwende die korrigierte handleBulkDelete
7610            }
7611        } else {
7612            UI.showError('Bitte wählen Sie eine Aktion aus.');
7613        }
7614    }
7615
7616    // HINZUGEFÜGTE Hilfsfunktion
7617    deselectAllContacts() {
7618        // Setze die Flags für gefilterte Auswahl zurück
7619        this.allFilteredContactsSelected = false;
7620        this.selectedFilteredContactIds = [];
7621        
7622        const selectAllCheckbox = document.getElementById('selectAll'); // Desktop Checkbox
7623        if (selectAllCheckbox) {
7624            selectAllCheckbox.checked = false;
7625        }
7626        // Mobile Button Text zurücksetzen (falls vorhanden)
7627        const selectAllMobileBtn = document.getElementById('selectAllMobile');
7628        if (selectAllMobileBtn) {
7629             selectAllMobileBtn.innerHTML = '<i class="fas fa-check-square me-1"></i>Alle auswählen';
7630        }
7631        // Alle einzelnen Checkboxen abwählen
7632        const checkboxes = document.querySelectorAll('input[name="contactSelect"]');
7633        checkboxes.forEach(checkbox => checkbox.checked = false);
7634        // Sichtbarkeit der Bulk Actions aktualisieren
7635        this.updateBulkActionsVisibility();
7636    }
7637
7638    // NEU: Methode zum Abrufen der Lead-Status (lädt sie bei Bedarf)
7639    async getLeadStatuses() {
7640        // Prüfe, ob Status bereits geladen sind und gültig erscheinen
7641        if (this.leadStatuses && Array.isArray(this.leadStatuses) && this.leadStatuses.length > 0) {
7642            // console.log("[getLeadStatuses] Returning already loaded statuses:", this.leadStatuses);
7643            return this.leadStatuses;
7644        }
7645        
7646        // Wenn nicht geladen, rufe die Ladefunktion auf
7647            // console.log("[getLeadStatuses] Statuses not loaded, calling loadLeadStatuses...");
7648        await this.loadLeadStatuses(); // Diese Funktion speichert das Ergebnis in this.leadStatuses
7649        
7650        // Gib die frisch geladenen Status zurück
7651            // console.log("[getLeadStatuses] Returning newly loaded statuses:", this.leadStatuses);
7652        return this.leadStatuses;
7653    }
7654
7655    // Neue Funktion zum Aktualisieren der Filter-Dropdowns mit dynamischen Lead-Status
7656    updateFilterDropdowns() {
7657        const leadStatusFilter = document.getElementById('leadStatusFilter');
7658        if (leadStatusFilter && this.leadStatuses) {
7659            // Aktueller ausgewählter Wert speichern
7660            const currentValue = leadStatusFilter.value;
7661            
7662            // Dropdown leeren und neu füllen
7663            leadStatusFilter.innerHTML = `
7664                <option value="">${t('Lead-Status: Alle')}</option>
7665                ${this.leadStatuses.map(status => 
7666                    `<option value="${status}">${t(status)}</option>`
7667                ).join('')}
7668            `;
7669            
7670            // Vorherigen Wert wiederherstellen, wenn möglich
7671            if (currentValue && this.leadStatuses.includes(currentValue)) {
7672                leadStatusFilter.value = currentValue;
7673            }
7674        }
7675    }
7676
7677    async loadLeadStatuses() {
7678        try {
7679            const user = auth.currentUser;
7680            if (!user) {
7681                console.warn("Kein Benutzer angemeldet, Standard-Lead-Status werden verwendet.");
7682                // Fallback zu einem minimalen Set oder leer lassen?
7683                this.leadStatuses = DEFAULT_LEAD_STATUSES; 
7684                return;
7685            }
7686            
7687            // Stelle sicher, dass userDoc geladen ist (sollte durch init geschehen sein)
7688            if (!this.userDoc) {
7689                console.warn("Benutzerdokument nicht geladen, versuche es jetzt zu laden...");
7690                                const userDocSnap = await _phase8UserSnap(user.uid); /* phase8 (was userDocRef + getDoc) */
7691                if (userDocSnap.exists()) {
7692                    this.userDoc = userDocSnap.data();
7693                } else {
7694                    console.error("Benutzerdokument konnte nicht geladen werden.");
7695                    this.leadStatuses = DEFAULT_LEAD_STATUSES; // Fallback
7696                    return;
7697                }
7698            }
7699
7700            const tenantId = this.userDoc.tenantID || this.userDoc.tenant_id;
7701
7702            if (!tenantId) {
7703                console.error("Keine Tenant-ID im Benutzerdokument gefunden.");
7704                this.leadStatuses = DEFAULT_LEAD_STATUSES; // Fallback
7705                return;
7706            }
7707
7708            // Rufe die zentrale Funktion aus app.js auf
7709            this.leadStatuses = await loadCustomLeadStatuses(tenantId);
7710            // console.log('Geladene Lead-Status (über app.js):', this.leadStatuses);
7711
7712        } catch (error) {
7713            console.error('Fehler beim Laden der Lead-Status über loadCustomLeadStatuses:', error);
7714            // Fallback zu Standard-Status bei Fehlern
7715            this.leadStatuses = DEFAULT_LEAD_STATUSES;
7716        }
7717    }
7718
7719    // SIP Outbound Call Handler
7720    async handleSipOutboundCall() {
7721        if (!this.hasMultiCallPermission()) {
7722            UI.showError('Sie haben keine Berechtigung für Voice AI Outbound Calls. Bitte wenden Sie sich an den Administrator.');
7723            return;
7724        }
7725
7726        const selectedContacts = this.getSelectedContacts();
7727
7728        if (selectedContacts.length === 0) {
7729            UI.showError(t('Bitte wählen Sie mindestens einen Kontakt aus.'));
7730            return;
7731        }
7732
7733        if (selectedContacts.length > 50) {
7734            UI.showError('Es können maximal 50 Kontakte auf einmal ausgewählt werden.');
7735            return;
7736        }
7737
7738        const contactsWithoutPhone = selectedContacts.filter(contact => !contact.phone);
7739        if (contactsWithoutPhone.length > 0) {
7740            UI.showError(`${contactsWithoutPhone.length} ausgewählte Kontakte haben keine Telefonnummer.`);
7741            return;
7742        }
7743
7744        try {
7745            // Get user's tenant ID
7746            const currentUser = auth.currentUser;
7747            if (!currentUser) {
7748                throw new Error('Nicht eingeloggt');
7749            }
7750
7751            const userDoc = await _phase8UserSnap(currentUser.uid); /* phase8 */
7752            if (!userDoc.exists()) {
7753                throw new Error('Benutzerprofil nicht gefunden');
7754            }
7755
7756            const userData = userDoc.data();
7757            const tenantId = userData.tenantID || userData.tenant_id;
7758            if (!tenantId) {
7759                throw new Error('Keine Mandanten-ID gefunden');
7760            }
7761
7762            // Templates laden (wie bei Sofort Anruf)
7763/* phase8c */ const templates = await _c8c_listMulticallTemplates();
7764
7765            if (templates.length === 0) {
7766                throw new Error('Keine Voice AI Templates gefunden. Bitte erstellen Sie zuerst eine MultiCall-Vorlage.');
7767            }
7768
7769            // Calendar Options laden
7770            const calendarOptionsHtml = await this.getCalendarOptionsHtml();
7771
7772            // Modal HTML erstellen (wie bei Sofort Anruf)
7773            const modalHtml = `
7774                <div class="modal fade" id="voiceAiOutboundModal" tabindex="-1" aria-labelledby="voiceAiOutboundModalLabel" aria-hidden="true">
7775                    <div class="modal-dialog">
7776                        <div class="modal-content">
7777                            <div class="modal-header">
7778                                <h5 class="modal-title" id="voiceAiOutboundModalLabel">${t('Voice AI Outbound Call - Vorlage auswählen')}</h5>
7779                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
7780                            </div>
7781                            <div class="modal-body">
7782                                <div class="mb-3">
7783                                    <label for="voiceAiTemplateSelect" class="form-label">${t('MultiCall Vorlage')}</label>
7784                                    <select class="form-select" id="voiceAiTemplateSelect" required>
7785                                    </select>
7786                                </div>
7787                                <div class="mb-3">
7788                                    <label for="voiceAiCalendarSelect" class="form-label">${t('Kalender')}</label>
7789                                    <select class="form-select" id="voiceAiCalendarSelect">
7790                                        <option value="">${t('Kein Kalender')}</option>
7791                                        ${calendarOptionsHtml}
7792                                    </select>
7793                                </div>
7794                                <div id="selectedContactsCountVoiceAi" class="text-muted small"></div>
7795                            </div>
7796                            <div class="modal-footer">
7797                                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="Abbrechen">${t('Abbrechen')}</button>
7798                                <button type="button" class="btn btn-primary" id="startVoiceAiCallBtn" data-i18n="Voice AI Anrufe starten"><i class="fas fa-phone-volume me-1"></i>${t('Voice AI Anrufe starten')}</button>
7799                            </div>
7800                        </div>
7801                    </div>
7802                </div>
7803            `;
7804
7805            // Entferne existierendes Modal falls vorhanden
7806            const existingModal = document.getElementById('voiceAiOutboundModal');
7807            if (existingModal) {
7808                existingModal.remove();
7809            }
7810
7811            // Modal zum DOM hinzufügen
7812            document.body.insertAdjacentHTML('beforeend', modalHtml);
7813
7814            const modalElement = document.getElementById('voiceAiOutboundModal');
7815            const modal = new bootstrap.Modal(modalElement);
7816
7817            // Template Select füllen
7818            const templateSelect = document.getElementById('voiceAiTemplateSelect');
7819            templateSelect.innerHTML = templates.map(template =>
7820                `<option value="${template.id}">${template.task || template.name || 'Unbenannte Vorlage'}</option>`
7821            ).join('');
7822
7823            // Kontakt-Anzahl anzeigen
7824            const selectedCount = document.getElementById('selectedContactsCountVoiceAi');
7825            if (selectedCount) {
7826                selectedCount.textContent = `${selectedContacts.length}
7826 Kontakte ausgewählt für Voice AI Calls`;
7827            }
7828
7829            // Start Button Handler
7830            const startVoiceAiCallBtn = document.getElementById('startVoiceAiCallBtn');
7831            if (startVoiceAiCallBtn) {
7832                const callHandler = async () => {
7833                    const startBtn = document.getElementById('startVoiceAiCallBtn');
7834                    if (!startBtn) return;
7835
7836                    const btnContent = startBtn.innerHTML;
7837                    startBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Voice AI Calls starten...';
7838                    startBtn.disabled = true;
7839
7840                    try {
7841                        const templateId = templateSelect.value;
7842                        const calendarId = document.getElementById('voiceAiCalendarSelect').value;
7843
7844                        if (!templateId) {
7845                            throw new Error('Bitte wählen Sie eine MultiCall-Vorlage aus.');
7846                        }
7847
7848                        const selectedTemplate = templates.find(t => t.id === templateId);
7849                        if (!selectedTemplate) {
7850                            modal.hide();
7851                            UI.showError('Ausgewählte Vorlage nicht gefunden.');
7852                            return;
7853                        }
7854
7855                        modal.hide();
7856
7857                        // Voice AI Calls starten (wie bei Sofort Anruf Success Message)
7858                        UI.showLoading('Starte Voice AI Outbound Calls...');
7859
7860                        const callPromises = selectedContacts.map(contact =>
7861                            this.initiateVoiceAiCall(contact.id, contact.phone, selectedTemplate, calendarId)
7862                        );
7863
7864                        await Promise.all(callPromises);
7865
7866                        // Success-Meldung wie bei Sofort Anruf
7867                        UI.showSuccess(`Voice AI Outbound Calls werden an ${selectedContacts.length} Kontakte gestartet.`);
7868
7869                        this.deselectAllContacts();
7870                        this.updateBulkActionsVisibility();
7871
7872                    } catch (error) {
7873                        console.error('Error starting Voice AI calls:', error);
7874                        UI.showError('Fehler: ' + error.message);
7875                    } finally {
7876                        const finalBtn = document.getElementById('startVoiceAiCallBtn');
7877                        if (finalBtn) {
7878                            finalBtn.innerHTML = btnContent;
7879                            finalBtn.disabled = false;
7880                        }
7881                        UI.hideLoading();
7882                    }
7883                };
7884
7885                // Event Listener hinzufügen
7886                const newCallButton = startVoiceAiCallBtn.cloneNode(true);
7887                startVoiceAiCallBtn.parentNode.replaceChild(newCallButton, startVoiceAiCallBtn);
7888                newCallButton.addEventListener('click', callHandler);
7889            }
7890
7891            modal.show();
7892
7893        } catch (error) {
7894            console.error('Error in handleSipOutboundCall:', error);
7895            UI.showError('Fehler: ' + error.message);
7896            UI.hideLoading();
7897        }
7898    }
7899
7900    // Voice AI Call Function mit Template und Kalender
7901    async initiateVoiceAiCall(contactId, phoneNumber, template, calendarId) {
7902        try {
7903            // Get current user and auth token
7904            const currentUser = auth.currentUser;
7905            if (!currentUser) {
7906                throw new Error('Nicht eingeloggt');
7907            }
7908            const idToken = await currentUser.getIdToken();
7909
7910            // Get user's tenant ID
7911            const userDoc = await _phase8UserSnap(currentUser.uid); /* phase8 */
7912            if (!userDoc.exists()) {
7913                throw new Error('Benutzerprofil nicht gefunden');
7914            }
7915
7916            const userData = userDoc.data();
7917            const tenantId = userData.tenantID || userData.tenant_id;
7918            if (!tenantId) {
7919                throw new Error('Keine Mandanten-ID gefunden');
7920            }
7921
7922            // Agent-Konfiguration mit Template-Daten (wie bei Sofort Anruf)
7923            const agentConfig = {
7924                template: template,
7925                calendarId: calendarId,
7926                prompt: template.message || template.prompt || template.description,
7927                greeting: template.greeting || "Guten Tag, hier ist Ihr VoiceOne Assistent",
7928                message: template.message || "",
7929                message2: template.message2 || "",
7930                agent_id: template.agent_id || template.agentId,  // Agent-ID aus Template
7931                purpose: "multicall_outbound",
7932                voice: template.voice || "Susi-German"
7933            };
7934
7935            const response = await fetch('/api/outbound/initiate-call', {
7936                method: 'POST',
7937                headers: {
7938                    'Content-Type': 'application/json',
7939                    'Authorization': `Bearer ${idToken}`
7940                },
7941                body: JSON.stringify({
7942                    contactId: contactId,
7943                    phoneNumber: phoneNumber,
7944                    tenantId: tenantId,
7945                    callType: 'voice_ai_outbound',
7946                    agentConfig: agentConfig,
7947                    templateId: template.id,
7948                    calendarId: calendarId
7949                })
7950            });
7951
7952            const result = await response.json();
7953
7954            if (result.success) {
7955                // Success-Meldung wie bei Sofort Anruf
7956                console.log(`Voice AI Call zu ${phoneNumber} wird gestartet (Template: ${template.name})`);
7957                this.showCallStatus(result.callId, phoneNumber, template.name);
7958            } else {
7959                throw new Error(result.error || 'Unbekannter Fehler');
7960            }
7961        } catch (error) {
7962            console.error('Fehler beim Voice AI Call:', error);
7963            throw error; // Propagate error for Promise.all handling
7964        }
7965    }
7966
7967    // Legacy Instant Call Function (für Kompatibilität)
7968    async initiateInstantCall(contactId, phoneNumber) {
7969        try {
7970            // Get current user and auth token
7971            const currentUser = auth.currentUser;
7972            if (!currentUser) {
7973                throw new Error('Nicht eingeloggt');
7974            }
7975            const idToken = await currentUser.getIdToken();
7976
7977            // Get user's tenant ID
7978            const userDoc = await _phase8UserSnap(currentUser.uid); /* phase8 */
7979            if (!userDoc.exists()) {
7980                throw new Error('Benutzerprofil nicht gefunden');
7981            }
7982
7983            const userData = userDoc.data();
7984            const tenantId = userData.tenantID || userData.tenant_id;
7985            if (!tenantId) {
7986                throw new Error('Keine Mandanten-ID gefunden');
7987            }
7988
7989            const response = await fetch('/api/outbound/initiate-call', {
7990                method: 'POST',
7991                headers: {
7992                    'Content-Type': 'application/json',
7993                    'Authorization': `Bearer ${idToken}`
7994                },
7995                body: JSON.stringify({
7996                    contactId: contactId,
7997                    phoneNumber: phoneNumber,
7998                    tenantId: tenantId,
7999                    callType: 'outbound_voice_ai',
8000                    agentConfig: {
8001                        greeting: "Hallo, hier ist Ihr VoiceOne Assistent...",
8002                        purpose: "customer_outreach"
8003                    }
8004                })
8005            });
8006
8007            const result = await response.json();
8008
8009            if (result.success) {
8010                UI.showSuccess(`Voice KI-Anruf zu ${phoneNumber} wird initiiert...`);
8011                this.showCallStatus(result.callId, phoneNumber);
8012            } else {
8013                UI.showError(`Anruf fehlgeschlagen: ${result.error}`);
8014            }
8015        } catch (error) {
8016            console.error('Fehler beim Initiieren des Anrufs:', error);
8017            UI.showError('Fehler beim Initiieren des Anrufs');
8018        }
8019    }
8020
8021    showCallStatus(callId, phoneNumber) {
8022        console.log(`Call ${callId} zu ${phoneNumber} initiiert`);
8023    }
8024}
8025
8026export const contacts = new Contacts();
8027window.contacts = contacts;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.