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; // Teile den Suchbegriff in einzelne Wörter const searchWords = searchTerm.split(/\s+/).filter(word => word.length > 0); // Wenn keine Wörter gefunden wurden, gebe den ursprünglichen Text zurück if (searchWords.length === 0) return text; // Erstelle einen regulären Ausdruck für alle Suchwörter const regexPattern = searchWords .map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) .join('|'); // Erstelle den regulären Ausdruck const regex = new RegExp(`(${regexPattern})`, 'gi'); // Ersetze alle Übereinstimmungen mit mark-Tags return text.replace(regex, '$1'); } 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() { const resultCount = document.getElementById('result-count'); const exportButton = document.getElementById('exportButton'); if (lastResults && lastResults.length > 0) { resultCount.textContent = `${lastResults.length} Ergebnisse gefunden`; resultCount.style.display = 'inline'; exportButton.style.display = 'inline-block'; } else { resultCount.textContent = ''; resultCount.style.display = 'none'; exportButton.style.display = '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 exportToVCF(customer) { if (!customer) return; const vcfData = [ 'BEGIN:VCARD', 'VERSION:3.0', `FN:${customer.vorname || ''} ${customer.nachname || ''}`, `N:${customer.nachname || ''};${customer.vorname || ''};;`, `TEL;TYPE=CELL:${customer.telefon || ''}`, `TEL;TYPE=HOME:${customer.telefon_2 || ''}`, `EMAIL:${customer.email || ''}`, `ADR;TYPE=HOME:;;${customer.strasse || ''};${customer.plz || ''};${customer.ort || ''};${customer.land || ''}`, `ORG:${customer.firma || ''}`, `NOTE:${customer.notizen || ''}`, 'END:VCARD' ].join('\n'); const blob = new Blob([vcfData], { type: 'text/vcard;charset=utf-8' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `kontakt_${customer.vorname || ''}_${customer.nachname || ''}_${new Date().toISOString().split('T')[0]}.vcf`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); } function displayResults(results) { const resultsDiv = document.getElementById('results'); const resultCount = document.getElementById('result-count'); 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 = ''; return; } resultCount.textContent = `${results.length} Ergebnisse`; lastResults = results; const resultsHTML = results.map(customer => { // 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}

`; }; // Highlighting für alle Felder const highlightField = (value) => { if (!value) return value; let highlighted = value; if (nameSearchTerm) { highlighted = highlightText(highlighted, nameSearchTerm); } if (fachrichtungSearchTerm) { highlighted = highlightText(highlighted, fachrichtungSearchTerm); } if (generalSearchTerm) { highlighted = highlightText(highlighted, generalSearchTerm); } return highlighted; }; return `

${highlightField(customer.name)}

${(customer.tag || 'medisoft').toUpperCase()}
${createFieldIfValue('Nummer', highlightField(customer.nummer), createCustomerLink)} ${createFieldIfValue('Adresse', (customer.strasse && customer.plz && customer.ort) ? true : false, () => createAddressLink( customer.strasse, highlightField(customer.plz), highlightField(customer.ort) ))} ${createFieldIfValue('Telefon', highlightField(customer.telefon), createPhoneLink)} ${createFieldIfValue('Mobil', highlightField(customer.mobil), createPhoneLink)} ${createFieldIfValue('Handy', highlightField(customer.handy), createPhoneLink)} ${createFieldIfValue('E-Mail', highlightField(customer.email), createEmailLink)} ${createFieldIfValue('Fachrichtung', highlightField(customer.fachrichtung))} ${createFieldIfValue('Kontakt 1', highlightField(customer.kontakt1), createPhoneLink)} ${createFieldIfValue('Kontakt 2', highlightField(customer.kontakt2), createPhoneLink)} ${createFieldIfValue('Kontakt 3', highlightField(customer.kontakt3), 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(); });