let searchTimeout; let lastResults = []; let fachrichtungTimeout; let ortTimeout; function createPhoneLink(phone) { if (!phone) return ''; const clientIP = document.querySelector('meta[name="client-ip"]').content; const allowedIPRanges = document.querySelector('meta[name="allowed-ip-ranges"]').content.split(','); // Überprüfen, ob die Client-IP in einem der erlaubten Bereiche liegt const isAllowed = allowedIPRanges.some(range => isIPInSubnet(clientIP, range.trim())); // Entferne alle nicht-numerischen Zeichen let cleanNumber = phone.replace(/\D/g, ''); // Formatiere die Nummer let formattedNumber = cleanNumber; if (cleanNumber.length === 11) { formattedNumber = cleanNumber.replace(/(\d{4})(\d{7})/, '$1-$2'); } else if (cleanNumber.length === 10) { formattedNumber = cleanNumber.replace(/(\d{3})(\d{7})/, '$1-$2'); } // Erstelle den Link return `${formattedNumber}`; } function createEmailLink(email) { if (!email) return ''; return `${email}`; } function highlightText(text, searchTerm) { if (!searchTerm || !text) return text; // Escapen von Sonderzeichen im Suchbegriff const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Erstelle einen regulären Ausdruck ohne Wortgrenzen const regex = new RegExp(escapedSearchTerm, 'gi'); return text.replace(regex, '$&'); } function createAddressLink(street, plz, city) { if (!street || !plz || !city) return ''; const address = `${street}, ${plz} ${city}`; const searchQuery = encodeURIComponent(address); const routeQuery = encodeURIComponent(address); return `${address} `; } function adjustCustomerNumber(number) { return number - 12000; } function isIPInSubnet(ip, subnet) { // Teile die IP und das Subnetz in ihre Komponenten const [subnetIP, bits] = subnet.split('/'); const ipParts = ip.split('.').map(Number); const subnetParts = subnetIP.split('.').map(Number); // Konvertiere IPs in 32-bit Zahlen const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3]; const subnetNum = (subnetParts[0] << 24) | (subnetParts[1] << 16) | (subnetParts[2] << 8) | subnetParts[3]; // Erstelle die Subnetzmaske const mask = ~((1 << (32 - bits)) - 1); // Prüfe, ob die IP im Subnetz liegt return (ipNum & mask) === (subnetNum & mask); } function createCustomerLink(nummer) { const clientIP = document.querySelector('meta[name="client-ip"]').content; const allowedIPRanges = document.querySelector('meta[name="allowed-ip-ranges"]').content.split(','); // Überprüfe, ob die Client-IP in einem der erlaubten Bereiche liegt const isAllowed = allowedIPRanges.some(range => { const trimmedRange = range.trim(); return isIPInSubnet(clientIP, trimmedRange); }); if (isAllowed) { const adjustedNumber = adjustCustomerNumber(nummer); return `${nummer}`; } else { return nummer; } } function showCopyFeedback() { const feedback = document.getElementById('shareFeedback'); feedback.style.display = 'block'; feedback.style.opacity = '1'; feedback.addEventListener('animationend', () => { feedback.style.display = 'none'; }, { once: true }); } async function copyCustomerLink(customerNumber) { const url = new URL(window.location.href); url.searchParams.set('kundennummer', customerNumber); try { await navigator.clipboard.writeText(url.toString()); showCopyFeedback(); } catch (err) { // Fehlerbehandlung ohne console.log } } function updateResultCounts() { // Nur Gesamtzahl anzeigen const generalCount = lastResults.length; document.getElementById('resultCount').textContent = generalCount > 0 ? `${generalCount} Treffer gefunden` : ''; document.getElementById('resultCount').classList.toggle('visible', generalCount > 0); // Export-Button anzeigen/verstecken document.getElementById('exportButton').style.display = generalCount > 0 ? 'inline-block' : 'none'; } function exportToCSV() { if (!lastResults || lastResults.length === 0) return; // CSV-Header definieren const headers = [ 'Nummer', 'Name', 'Fachrichtung', 'Straße', 'PLZ', 'Ort', 'Telefon', 'Mobil', 'Handy', 'Telefon Firma', 'E-Mail', 'Kontakt 1', 'Kontakt 2', 'Kontakt 3', 'Tags' ]; // CSV-Daten erstellen const csvRows = [headers]; lastResults.forEach(customer => { const row = [ customer.nummer, customer.name, customer.fachrichtung, customer.strasse, customer.plz, customer.ort, customer.telefon, customer.mobil, customer.handy, customer.tele_firma, customer.email, customer.kontakt1, customer.kontakt2, customer.kontakt3, (customer.tags || []).join(';') ].map(value => { // Werte mit Kommas oder Anführungszeichen in Anführungszeichen setzen if (value && (value.includes(',') || value.includes('"') || value.includes('\n'))) { return `"${value.replace(/"/g, '""')}"`; } return value || ''; }); csvRows.push(row); }); // CSV-String erstellen const csvContent = csvRows.map(row => row.join(',')).join('\n'); // Blob erstellen und Download starten const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', `kundensuche_${new Date().toISOString().split('T')[0]}.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } function displayResults(results) { const resultsDiv = document.getElementById('results'); const resultCount = document.getElementById('resultCount'); const generalSearchTerm = document.getElementById('q').value; const nameSearchTerm = document.getElementById('nameInput').value; const fachrichtungSearchTerm = document.getElementById('fachrichtungInput').value; if (!results || results.length === 0) { resultsDiv.innerHTML = '

Keine Ergebnisse gefunden.

'; resultCount.textContent = '0 Ergebnisse'; return; } resultCount.textContent = `${results.length} Ergebnisse`; lastResults = results; const resultsHTML = results.map(customer => { const highlightedName = highlightText(customer.name, nameSearchTerm); const highlightedFachrichtung = highlightText(customer.fachrichtung, fachrichtungSearchTerm); const highlightedGeneral = highlightText(customer.name, generalSearchTerm) || highlightText(customer.fachrichtung, generalSearchTerm) || highlightText(customer.ort, generalSearchTerm); // Hilfsfunktion zum Erstellen von Feldern nur wenn sie Werte haben const createFieldIfValue = (label, value, formatter = (v) => v) => { if (!value || value === 'N/A' || value === 'n/a' || value === 'N/a' || (typeof value === 'string' && value.trim() === '')) return ''; const formattedValue = formatter(value); return `

${label}: ${formattedValue}

`; }; return `

${highlightedName || highlightedGeneral}

${(customer.tag || 'medisoft').toUpperCase()}
${createFieldIfValue('Nummer', highlightText(customer.nummer, generalSearchTerm), createCustomerLink)} ${createFieldIfValue('Adresse', (customer.strasse && customer.plz && customer.ort) ? true : false, () => createAddressLink( highlightText(customer.strasse, generalSearchTerm), highlightText(customer.plz, generalSearchTerm), highlightText(customer.ort, generalSearchTerm) ))} ${createFieldIfValue('Telefon', highlightText(customer.telefon, generalSearchTerm), createPhoneLink)} ${createFieldIfValue('Mobil', highlightText(customer.mobil, generalSearchTerm), createPhoneLink)} ${createFieldIfValue('Handy', highlightText(customer.handy, generalSearchTerm), createPhoneLink)} ${createFieldIfValue('Telefon Firma', highlightText(customer.tele_firma, generalSearchTerm), createPhoneLink)} ${createFieldIfValue('E-Mail', highlightText(customer.email, generalSearchTerm), createEmailLink)} ${createFieldIfValue('Fachrichtung', highlightText(customer.fachrichtung, generalSearchTerm || fachrichtungSearchTerm))} ${createFieldIfValue('Kontakt 1', highlightText(customer.kontakt1, generalSearchTerm), createPhoneLink)} ${createFieldIfValue('Kontakt 2', highlightText(customer.kontakt2, generalSearchTerm), createPhoneLink)} ${createFieldIfValue('Kontakt 3', highlightText(customer.kontakt3, generalSearchTerm), createPhoneLink)} ${customer.tags && customer.tags.length > 0 ? `

Tags: ${customer.tags.map(tag => `${tag}`).join('')}

` : ''}
`; }).join(''); resultsDiv.innerHTML = resultsHTML; updateResultCounts(); } function clearInput(inputId) { document.getElementById(inputId).value = ''; searchCustomers(); } async function searchCustomers() { const loading = document.getElementById('loading'); const results = document.getElementById('results'); const generalSearch = document.getElementById('q').value; const nameSearch = document.getElementById('nameInput').value; const ortSearch = document.getElementById('ortInput').value; const nummerSearch = document.getElementById('nummerInput').value; const plzSearch = document.getElementById('plzInput').value; const fachrichtungSearch = document.getElementById('fachrichtungInput').value; const tagFilter = document.getElementById('tagFilter').value; // Zeige Ladeanimation loading.style.display = 'block'; results.innerHTML = ''; // Setze Timeout zurück clearTimeout(searchTimeout); // Verzögerte Suche searchTimeout = setTimeout(async () => { try { // Baue die Suchanfrage const params = new URLSearchParams(); if (generalSearch) params.append('q', generalSearch); if (nameSearch) params.append('name', nameSearch); if (ortSearch) params.append('ort', ortSearch); if (nummerSearch) params.append('nummer', nummerSearch); if (plzSearch) params.append('plz', plzSearch); if (fachrichtungSearch) params.append('fachrichtung', fachrichtungSearch); if (tagFilter) params.append('tag', tagFilter); const response = await fetch('/search?' + params.toString()); if (!response.ok) { throw new Error('Netzwerkantwort war nicht ok'); } const data = await response.json(); displayResults(data); } catch (error) { results.innerHTML = '

Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.

'; } finally { loading.style.display = 'none'; } }, 300); } function setupFachrichtungAutocomplete() { const fachrichtungInput = document.getElementById('fachrichtungInput'); const autocompleteList = document.createElement('div'); autocompleteList.className = 'autocomplete-items'; fachrichtungInput.parentNode.appendChild(autocompleteList); fachrichtungInput.addEventListener('input', function() { clearTimeout(fachrichtungTimeout); const searchTerm = this.value; if (searchTerm.length < 2) { autocompleteList.style.display = 'none'; return; } fachrichtungTimeout = setTimeout(() => { fetch(`/api/fachrichtungen?q=${encodeURIComponent(searchTerm)}`) .then(response => response.json()) .then(data => { autocompleteList.innerHTML = ''; if (data.length > 0) { data.forEach(item => { const div = document.createElement('div'); div.textContent = item; div.addEventListener('click', () => { fachrichtungInput.value = item; autocompleteList.style.display = 'none'; searchCustomers(); }); autocompleteList.appendChild(div); }); autocompleteList.style.display = 'block'; } else { autocompleteList.style.display = 'none'; } }); }, 300); }); document.addEventListener('click', function(e) { if (!fachrichtungInput.contains(e.target) && !autocompleteList.contains(e.target)) { autocompleteList.style.display = 'none'; } }); } function setupOrtAutocomplete() { const ortInput = document.getElementById('ortInput'); const autocompleteList = document.createElement('div'); autocompleteList.className = 'autocomplete-items'; ortInput.parentNode.appendChild(autocompleteList); ortInput.addEventListener('input', function() { clearTimeout(ortTimeout); const searchTerm = this.value; if (searchTerm.length < 2) { autocompleteList.style.display = 'none'; return; } ortTimeout = setTimeout(() => { fetch(`/api/orte?q=${encodeURIComponent(searchTerm)}`) .then(response => response.json()) .then(data => { autocompleteList.innerHTML = ''; if (data.length > 0) { data.forEach(item => { const div = document.createElement('div'); div.textContent = item; div.addEventListener('click', () => { ortInput.value = item; autocompleteList.style.display = 'none'; searchCustomers(); }); autocompleteList.appendChild(div); }); autocompleteList.style.display = 'block'; } else { autocompleteList.style.display = 'none'; } }); }, 300); }); document.addEventListener('click', function(e) { if (!ortInput.contains(e.target) && !autocompleteList.contains(e.target)) { autocompleteList.style.display = 'none'; } }); } // Event-Listener für die URL-Parameter und Autocomplete-Setup document.addEventListener('DOMContentLoaded', function() { const urlParams = new URLSearchParams(window.location.search); const kundennummer = urlParams.get('kundennummer'); const name = urlParams.get('name'); const ort = urlParams.get('ort'); const plz = urlParams.get('plz'); if (kundennummer) { document.getElementById('nummerInput').value = kundennummer; searchCustomers(); } if (name) { document.getElementById('nameInput').value = name; searchCustomers(); } if (ort) { document.getElementById('ortInput').value = ort; searchCustomers(); } if (plz) { document.getElementById('plzInput').value = plz; searchCustomers(); } // Setup Autocomplete setupFachrichtungAutocomplete(); setupOrtAutocomplete(); });