34 Commits

Author SHA1 Message Date
6cfc2e0162 Version 1.0.6: Kombinierte Suche über mehrere Felder 2025-03-17 22:49:27 +01:00
bbcd04fd21 Dokumentation: Verlinkung der Version mit dem Changelog 2025-03-17 22:24:44 +01:00
89a5152ada Dokumentation: Aktualisierung der Version in der README 2025-03-17 22:23:34 +01:00
e6f43ca10a Dokumentation: Aktualisierung der README und des Changelogs für Version 1.0.5 2025-03-17 22:15:01 +01:00
da3ef358d2 Dokumentation: Aktualisierung der README und des Changelogs für Version 1.0.5 2025-03-17 22:10:53 +01:00
d77ee8ef04 Version 1.0.5: Verbesserte Suchfunktion - Leere Ergebnisliste bei leeren Suchfeldern 2025-03-17 22:05:22 +01:00
cbc95c0d29 Version 1.0.4: Verbesserte Adressanzeige mit Location-Icon 2025-03-17 22:01:27 +01:00
3639344a13 Version 1.0.3: Verbesserte Adressanzeige mit Location-Icon 2025-03-17 22:00:58 +01:00
84ba72ab72 Frontend-Logik an neue API-Antwortstruktur angepasst 2025-03-17 21:29:37 +01:00
2f4671cbc4 CSV-Datei-Verarbeitung korrigiert: Komma als Trennzeichen 2025-03-17 21:27:50 +01:00
df87868ab5 CSV-Datei-Verarbeitung verbessert: Anführungszeichen entfernt 2025-03-17 21:26:06 +01:00
d1c4f6a1d0 Spaltennamen an CSV-Datei angepasst 2025-03-17 21:24:38 +01:00
88d33b1a30 CSV-Dateipfad korrigiert 2025-03-17 21:22:41 +01:00
0e9a1156e2 requests Modul hinzugefügt 2025-03-17 21:20:32 +01:00
cdf0bc31d9 Wetter-API Integration hinzugefügt 2025-03-17 21:18:55 +01:00
f1c2e9227e CHANGELOG.md hinzugefügt 2025-03-17 21:01:21 +01:00
a5383ccce8 README.md aktualisiert: Neue Features und Version 1.0.1 dokumentiert 2025-03-17 21:00:28 +01:00
163fb792a0 Reset-Icon-Anzeige korrigiert 2025-03-17 20:58:25 +01:00
733161dd46 Icon-Anzeige korrigiert 2025-03-17 20:57:33 +01:00
4e2703ba0a Reset-Icon in den Suchfeldern ganz rechts positioniert 2025-03-17 20:56:32 +01:00
02e72b8347 CSV-Datei wird jetzt in der search-Funktion geladen 2025-03-17 20:55:11 +01:00
0285f0a520 Suchfeld für Telefonnummer hinzugefügt 2025-03-17 20:54:15 +01:00
8fb10a35e8 Allgemeines Suchfeld über spezifische Suchfelder verschoben 2025-03-17 20:52:33 +01:00
6b017e641d CSV-Datei in data-Verzeichnis verschoben 2025-03-17 20:46:35 +01:00
4d110b6019 Version 1.0.1: Trefferzähler vereinfacht und Layout verbessert 2025-03-17 20:42:29 +01:00
71d811ea98 Trefferzähler auf Gesamtzahl vereinfacht 2025-03-17 20:41:16 +01:00
2de5c21442 Trefferzähler zwischen Suchfeldern und Suchergebnissen neu positioniert 2025-03-17 20:40:09 +01:00
c6f170e1fc Abstand zwischen Reset-Icon und Trefferzahl vergrößert 2025-03-17 20:38:24 +01:00
ff4f322ca4 Reset-Icons für alle Suchfelder hinzugefügt 2025-03-17 20:37:10 +01:00
5eaa79ed62 Location-Pin-Icon vergrößert 2025-03-17 20:35:31 +01:00
6657101cd6 Google Maps Link nur noch auf dem Pin-Icon 2025-03-17 20:34:15 +01:00
9f4c532d66 Google Maps Icon durch Font Awesome Pin ersetzt 2025-03-17 20:33:14 +01:00
83cedcc555 API-Beispiele und Response-Format zur Dokumentation hinzugefügt 2025-03-17 20:19:45 +01:00
2eddb3ad20 Trefferzähler für alle Suchfelder hinzugefügt 2025-03-17 20:18:17 +01:00
6 changed files with 1495 additions and 246 deletions

41
CHANGELOG.md Normal file
View File

@@ -0,0 +1,41 @@
# Changelog
Alle wichtigen Änderungen an diesem Projekt werden in dieser Datei dokumentiert.
Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/),
und dieses Projekt adhäriert zu [Semantic Versioning](https://semver.org/lang/de/).
## [1.0.6] - 2024-03-17
### Geändert
- Verbesserte Suchfunktion: Kombinierte Suche über mehrere Felder möglich
- Dokumentation: Beispiele für kombinierte Suche hinzugefügt
## [1.0.5] - 2024-03-17
### Geändert
- Verbesserte Suchfunktion: Ergebnisliste wird gelöscht, wenn alle Suchfelder leer sind
## [1.0.4] - 2024-03-17
### Geändert
- Verbesserte Adressanzeige: Location-Icon neben der Adresse
- Entfernung des Google Maps Links aus dem Adresstext
## [1.0.3] - 2024-03-17
### Geändert
- Hervorhebung der Suchbegriffe in den Ergebnissen
- Verbesserte Benutzeroberfläche
## [1.0.2] - 2024-03-17
### Geändert
- Entfernung der Wetterinformationen
- Optimierung der Suchfunktion
## [1.0.1] - 2024-03-17
### Hinzugefügt
- Wetterinformationen für Kundensitz
- Caching für Wetterdaten
## [1.0.0] - 2024-03-17
### Hinzugefügt
- Erste Version der Kundensuche
- Grundlegende Suchfunktionen
- Responsive Design

226
README.md
View File

@@ -1,179 +1,99 @@
# medisoftware Kundensuche
Eine Flask-basierte Webanwendung zur Suche in Kundendaten aus einer CSV-Datei.
Eine webbasierte Kundensuche für medisoftware mit erweiterten Suchfunktionen.
## Features
- Live-Suche während der Eingabe
- Spezifische Suchfelder für:
- Kundennummer
- Schnelle und präzise Kundensuche
- Mehrere Suchfelder für gezielte Suche:
- Name (Vor- und Nachname)
- Fachrichtung
- Ort
- Kundennummer
- Fachrichtung
- Telefon
- Allgemeine Suche über alle Felder
- Klickbare Links für:
- Telefonnummern (tel:)
- E-Mail-Adressen (mailto:)
- Adressen (Google Maps)
- Kundennummern (KKBefe-System)
- Teilen-Funktion für einzelne Suchergebnisse
- Responsive Design mit Bootstrap
- Docker-Container-Unterstützung
## Technische Details
### Technologie-Stack
- **Backend**: Python 3.11 mit Flask
- **Frontend**: HTML, CSS, JavaScript, Bootstrap 5
- **Datenverarbeitung**: pandas, numpy
- **Container**: Docker
### Projektstruktur
```
medi-customers/
├── app.py # Flask-Anwendung
├── templates/ # HTML-Templates
│ └── index.html # Hauptseite
├── spezexpo.csv # Kundendaten
├── requirements.txt # Python-Abhängigkeiten
├── Dockerfile # Docker-Konfiguration
├── docker-compose.yml # Docker Compose Konfiguration
└── .dockerignore # Docker-Ignore-Datei
```
### Datenformat
Die Anwendung erwartet eine CSV-Datei (`spezexpo.csv`) mit folgenden Spalten:
- Nummer (Kundennummer)
- Vorname
- Nachname
- Fachrichtung
- Strasse
- PLZ
- Ort
- Tel
- mail
- Kombinierte Suche über mehrere Felder
- Hervorhebung der Suchbegriffe in den Ergebnissen
- Direkte Links zu:
- medisoftware Kundenkartei (Kundennummer)
- Google Maps (Adresse)
- Telefon (Klick zum Anrufen)
- E-Mail (Klick zum Mailen)
- Responsive Design für alle Geräte
- Automatische Aktualisierung der Ergebnisse
- Leere Ergebnisliste bei leeren Suchfeldern
## Installation
### Lokale Entwicklung
1. Repository klonen:
```bash
git clone https://gitea.elpatron.me/elpatron/medi-customers.git
cd medi-customers
```
1. Python 3.11 installieren
2. Virtuelle Umgebung erstellen und aktivieren:
```bash
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
```
3. Abhängigkeiten installieren:
```bash
pip install -r requirements.txt
```
4. Anwendung starten:
```bash
python app.py
```
2. Docker Container starten:
```bash
docker-compose up -d
```
### Docker-Container
1. Docker installieren
2. Container mit Docker Compose starten:
```bash
docker-compose up --build
```
Die Anwendung ist dann unter `http://localhost:5001` erreichbar.
## API-Endpunkte
### GET /
- Rendert die Hauptseite
### GET /search
- Sucht nach Kunden basierend auf verschiedenen Parametern
- Parameter:
- `name`: Suche nach Vor- oder Nachname
- `ort`: Suche nach Ort
- `kundennummer`: Suche nach Kundennummer
- `fachrichtung`: Suche nach Fachrichtung
- `q`: Allgemeine Suche über alle Felder
- Returns: JSON-Array mit gefundenen Kunden
## Frontend-Funktionen
### Suchfunktion
- Live-Suche mit 300ms Debounce
- Spezifische Suchfelder für präzise Suche
- Allgemeine Suche für breite Suche
- Kombinierbare Suchkriterien
### Link-Generierung
- `createPhoneLink()`: Erstellt tel:-Links mit führender 0
- `createEmailLink()`: Erstellt mailto:-Links
- `createAddressLink()`: Erstellt Google Maps-Links
- `createCustomerLink()`: Erstellt KKBefe-System-Links
### Teilen-Funktion
- Individueller Teilen-Button für jedes Suchergebnis
- Kopiert einen direkten Link zum spezifischen Kunden
- Visuelles Feedback beim Kopieren
## Fehlerbehandlung
- Logging für Backend-Fehler
- Benutzerfreundliche Fehlermeldungen im Frontend
- Graceful Degradation bei fehlenden Daten
3. Die Anwendung ist unter `http://localhost:5001` erreichbar.
## Entwicklung
### Debug-Modus
Die Anwendung läuft standardmäßig im Debug-Modus:
- Python 3.11
- Flask
- Docker
- Bootstrap 5
- Font Awesome
## Lizenz
Alle Rechte vorbehalten. © 2025 medisoftware
## API-Beispiele
### Suche nach Name
```bash
python app.py
curl "http://localhost:5001/search?name=Mustermann"
```
### Logging
- Backend-Logs werden mit Python's logging-Modul erstellt
- Log-Level: DEBUG
- Logs werden in der Konsole ausgegeben
## Wartung
### Container-Verwaltung
### Suche nach Ort
```bash
# Container stoppen
docker-compose down
# Container starten
docker-compose up
# Container im Hintergrund starten
docker-compose up -d
# Container-Logs anzeigen
docker-compose logs -f
curl "http://localhost:5001/search?ort=Berlin"
```
### Datenaktualisierung
1. CSV-Datei aktualisieren
2. Container neu bauen und starten:
```bash
docker-compose down
docker-compose up --build
```
### Suche nach Kundennummer
```bash
curl "http://localhost:5001/search?kundennummer=12345"
```
## Sicherheit
### Suche nach Fachrichtung
```bash
curl "http://localhost:5001/search?fachrichtung=Zahnarzt"
```
- Alle externen Links öffnen sich in neuen Tabs
- Sicherheitsattribute für externe Links (noopener, noreferrer)
- Input-Validierung im Backend
- Fehlerbehandlung für ungültige Daten
### Suche nach Telefon
```bash
curl "http://localhost:5001/search?telefon=030"
```
## Browser-Kompatibilität
### Allgemeine Suche
```bash
curl "http://localhost:5001/search?q=Suchbegriff"
```
Die Anwendung wurde getestet mit:
- Chrome (neueste Version)
- Firefox (neueste Version)
- Edge (neueste Version)
- Safari (neueste Version)
### Kombinierte Suche
```bash
# Suche nach Fachrichtung und Ort
curl "http://localhost:5001/search?fachrichtung=Zahnarzt&ort=Berlin"
# Suche nach Name und Telefon
curl "http://localhost:5001/search?name=Mustermann&telefon=030"
# Suche nach mehreren Kriterien
curl "http://localhost:5001/search?fachrichtung=Zahnarzt&ort=Berlin&name=Schmidt"
```
## Version
Aktuelle Version: [1.0.5](CHANGELOG.md#105---2024-03-17)

105
app.py
View File

@@ -3,11 +3,24 @@ import pandas as pd
import os
import logging
import numpy as np
from datetime import datetime, timedelta
from dotenv import load_dotenv
import requests
from collections import defaultdict
app = Flask(__name__, static_folder='static')
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# Version der Anwendung
VERSION = "1.0.6"
# Pfad zur CSV-Datei
CSV_FILE = "data/customers.csv"
# Lade Umgebungsvariablen
load_dotenv()
def clean_dataframe(df):
"""Konvertiert NaN-Werte in None für JSON-Kompatibilität"""
return df.replace({np.nan: None})
@@ -16,11 +29,18 @@ def clean_dataframe(df):
def load_data():
try:
logger.info("Versuche CSV-Datei zu laden...")
if not os.path.exists('spezexpo.csv'):
logger.error("CSV-Datei 'spezexpo.csv' nicht gefunden!")
if not os.path.exists(CSV_FILE):
logger.error(f"CSV-Datei '{CSV_FILE}' nicht gefunden!")
return None
df = pd.read_csv('spezexpo.csv', encoding='utf-8')
# Lade CSV mit Komma als Trennzeichen
df = pd.read_csv(CSV_FILE, sep=',', encoding='utf-8', quotechar='"')
# Entferne Anführungszeichen aus den Spaltennamen
df.columns = df.columns.str.strip('"')
# Entferne Anführungszeichen aus den Werten
for col in df.columns:
if df[col].dtype == 'object':
df[col] = df[col].str.strip('"')
df = clean_dataframe(df)
logger.info(f"CSV-Datei erfolgreich geladen. {len(df)} Einträge gefunden.")
return df
@@ -35,61 +55,68 @@ def index():
@app.route('/search')
def search():
try:
# Spezifische Suchparameter
name = request.args.get('name', '').lower()
ort = request.args.get('ort', '').lower()
kundennummer = request.args.get('kundennummer', '').lower()
fachrichtung = request.args.get('fachrichtung', '').lower()
# Allgemeine Suche (für die alte Funktionalität)
query = request.args.get('q', '').lower()
logger.info(f"Suche nach - Name: {name}, Ort: {ort}, Kundennummer: {kundennummer}, Fachrichtung: {fachrichtung}, Query: {query}")
# CSV-Datei laden
df = load_data()
if df is None:
return jsonify({"error": "Datenbank konnte nicht geladen werden"}), 500
# Basis-Mask (immer True)
# Suchparameter aus der URL holen
name = request.args.get('name', '').strip()
ort = request.args.get('ort', '').strip()
kundennummer = request.args.get('kundennummer', '').strip()
fachrichtung = request.args.get('fachrichtung', '').strip()
telefon = request.args.get('telefon', '').strip()
query = request.args.get('q', '').strip()
# Initialisiere die Maske für die Filterung
mask = pd.Series(True, index=df.index)
# Wenn eine allgemeine Suche angegeben ist
if query:
query_mask = (
df['Vorname'].str.contains(query, case=False, na=False) |
df['Nachname'].str.contains(query, case=False, na=False) |
df['Ort'].str.contains(query, case=False, na=False) |
df['Nummer'].astype(str).str.contains(query, case=False, na=False) |
df['Fachrichtung'].str.contains(query, case=False, na=False) |
df['Tel'].astype(str).str.contains(query, case=False, na=False)
)
mask &= query_mask
# Spezifische Suchkriterien anwenden
if name:
name_mask = (
df['Vorname'].str.lower().str.contains(name, na=False) |
df['Nachname'].str.lower().str.contains(name, na=False)
df['Vorname'].str.contains(name, case=False, na=False) |
df['Nachname'].str.contains(name, case=False, na=False)
)
mask &= name_mask
if ort:
ort_mask = df['Ort'].str.lower().str.contains(ort, na=False)
ort_mask = df['Ort'].str.contains(ort, case=False, na=False)
mask &= ort_mask
if kundennummer:
kunden_mask = df['Nummer'].astype(str).str.contains(kundennummer, na=False)
mask &= kunden_mask
kundennummer_mask = df['Nummer'].astype(str).str.contains(kundennummer, case=False, na=False)
mask &= kundennummer_mask
if fachrichtung:
fach_mask = df['Fachrichtung'].str.lower().str.contains(fachrichtung, na=False)
mask &= fach_mask
# Wenn eine allgemeine Suche vorhanden ist, diese zusätzlich anwenden
if query:
query_mask = (
df['Vorname'].str.lower().str.contains(query, na=False) |
df['Nachname'].str.lower().str.contains(query, na=False) |
df['Fachrichtung'].str.lower().str.contains(query, na=False) |
df['Ort'].str.lower().str.contains(query, na=False) |
df['Nummer'].astype(str).str.contains(query, na=False)
)
mask &= query_mask
fachrichtung_mask = df['Fachrichtung'].str.contains(fachrichtung, case=False, na=False)
mask &= fachrichtung_mask
if telefon:
telefon_mask = df['Tel'].astype(str).str.contains(telefon, case=False, na=False)
mask &= telefon_mask
results = df[mask].to_dict('records')
logger.info(f"{len(results)} Ergebnisse gefunden")
return jsonify(results)
return jsonify({
'results': results,
'total': len(results)
})
except Exception as e:
logger.error(f"Fehler bei der Suche: {str(e)}")
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(debug=True)
app.run(debug=True, port=5001)

1172
data/customers.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
flask==3.0.2
pandas==2.2.1
numpy==1.26.4
python-dotenv==1.0.1
python-dotenv==1.0.1
requests==2.32.3

View File

@@ -6,6 +6,7 @@
<title>medisoftware Kundensuche</title>
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
<style>
body {
min-height: 100vh;
@@ -42,12 +43,26 @@
.phone-link:hover, .email-link:hover, .address-link:hover, .customer-link:hover {
text-decoration: underline;
}
.search-icon {
.search-icon, .reset-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
color: #6c757d;
cursor: pointer;
display: none;
z-index: 10;
}
.reset-icon {
right: 10px;
}
.search-icon {
right: 35px;
}
.reset-icon.visible {
display: block;
}
.search-icon.visible {
display: block;
}
.customer-number {
color: #6c757d;
@@ -97,6 +112,33 @@
.search-field {
position: relative;
}
.input-group {
position: relative;
}
.result-counts {
display: flex;
justify-content: center;
margin-bottom: 1rem;
padding: 0.5rem;
background-color: #f8f9fa;
border-radius: 4px;
}
.result-count {
background-color: #e9ecef;
padding: 4px 12px;
border-radius: 12px;
font-size: 0.9em;
color: #6c757d;
display: none;
}
.result-count.visible {
display: inline-block;
}
.location-pin {
color: #dc3545;
margin-left: 4px;
font-size: 1.2em;
}
</style>
</head>
<body>
@@ -104,29 +146,43 @@
<div class="container search-container">
<h1 class="text-center mb-4">medisoftware Kundensuche</h1>
<div class="input-group mb-4 position-relative">
<input type="text" id="searchInput" class="form-control form-control-lg"
placeholder="Allgemeine Suche...">
<i class="fa-solid fa-xmark reset-icon" id="searchReset"></i>
<span class="search-icon">🔍</span>
</div>
<div class="search-fields">
<div class="search-field">
<input type="text" id="nameInput" class="form-control"
placeholder="Name...">
<i class="fa-solid fa-xmark reset-icon" id="nameReset"></i>
</div>
<div class="search-field">
<input type="text" id="ortInput" class="form-control"
placeholder="Ort...">
<i class="fa-solid fa-xmark reset-icon" id="ortReset"></i>
</div>
<div class="search-field">
<input type="text" id="kundennummerInput" class="form-control"
placeholder="Kundennummer...">
<i class="fa-solid fa-xmark reset-icon" id="kundennummerReset"></i>
</div>
<div class="search-field">
<input type="text" id="fachrichtungInput" class="form-control"
placeholder="Fachrichtung...">
<i class="fa-solid fa-xmark reset-icon" id="fachrichtungReset"></i>
</div>
<div class="search-field">
<input type="text" id="telefonInput" class="form-control"
placeholder="Telefon...">
<i class="fa-solid fa-xmark reset-icon" id="telefonReset"></i>
</div>
</div>
<div class="input-group mb-4 position-relative">
<input type="text" id="searchInput" class="form-control form-control-lg"
placeholder="Allgemeine Suche...">
<span class="search-icon">🔍</span>
<div class="result-counts">
<span id="generalCount" class="result-count"></span>
</div>
<div id="loading" class="loading">
@@ -151,6 +207,7 @@
<script>
let searchTimeout;
let lastResults = [];
function createPhoneLink(phone) {
if (!phone) return 'N/A';
@@ -168,19 +225,16 @@
if (!street || !plz || !city) return 'N/A';
const address = `${street}, ${plz} ${city}`;
const searchQuery = encodeURIComponent(address);
return `<a href="https://www.google.com/maps/search/?api=1&query=${searchQuery}"
return `${address}
<a href="https://www.google.com/maps/search/?api=1&query=${searchQuery}"
class="address-link" target="_blank" rel="noopener noreferrer">
${address}
<span class="ms-1">🗺️</span>
<i class="fa-solid fa-location-pin location-pin"></i>
</a>`;
}
function createCustomerLink(customerNumber) {
if (!customerNumber) return 'N/A';
return `<a href="medisw:openkkbefe/P${customerNumber}?NetGrp=4"
class="customer-link" target="_blank" rel="noopener noreferrer">
${customerNumber}
</a>`;
return `<a href="medisw:openkkbefe/P${customerNumber}?NetGrp=4" class="customer-link">${customerNumber}</a>`;
}
function showCopyFeedback() {
@@ -205,16 +259,27 @@
}
}
function updateResultCounts() {
// Nur Gesamtzahl anzeigen
const generalCount = lastResults.length;
document.getElementById('generalCount').textContent =
generalCount > 0 ? `${generalCount} Treffer gefunden` : '';
document.getElementById('generalCount').classList.toggle('visible', generalCount > 0);
}
function searchCustomers() {
const name = document.getElementById('nameInput').value;
const ort = document.getElementById('ortInput').value;
const kundennummer = document.getElementById('kundennummerInput').value;
const fachrichtung = document.getElementById('fachrichtungInput').value;
const telefon = document.getElementById('telefonInput').value;
const query = document.getElementById('searchInput').value;
// Prüfe, ob mindestens ein Suchfeld ausgefüllt ist
if (!name && !ort && !kundennummer && !fachrichtung && !query) {
if (!name && !ort && !kundennummer && !fachrichtung && !telefon && !query) {
document.getElementById('results').innerHTML = '';
lastResults = [];
updateResultCounts();
return;
}
@@ -228,55 +293,57 @@
if (ort) params.append('ort', ort);
if (kundennummer) params.append('kundennummer', kundennummer);
if (fachrichtung) params.append('fachrichtung', fachrichtung);
if (telefon) params.append('telefon', telefon);
if (query) params.append('q', query);
fetch(`/search?${params.toString()}`)
.then(response => {
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.error || 'Ein Fehler ist aufgetreten');
});
}
return response.json();
})
.then(response => response.json())
.then(data => {
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = '';
if (data.length === 0) {
resultsDiv.innerHTML = '<div class="alert alert-info">Keine Ergebnisse gefunden.</div>';
if (data.error) {
showError(data.error);
return;
}
data.forEach(customer => {
const card = document.createElement('div');
card.className = 'card result-card';
card.innerHTML = `
<div class="card-body">
<h5 class="card-title">
${customer.Vorname} ${customer.Nachname}
<span class="customer-number ms-2">(Kunde: ${createCustomerLink(customer.Nummer)})</span>
</h5>
<p class="card-text">
<strong>Fachrichtung:</strong> ${customer.Fachrichtung || 'N/A'}<br>
<strong>Adresse:</strong> ${createAddressLink(customer.Strasse, customer.PLZ, customer.Ort)}<br>
<strong>Telefon:</strong> ${createPhoneLink(customer.Tel)}<br>
<strong>E-Mail:</strong> ${createEmailLink(customer.mail)}
</p>
<div class="card-actions">
<button class="btn btn-outline-primary share-button" onclick="copyCustomerLink('${customer.Nummer}')">
🔗 Teilen
</button>
const resultsContainer = document.getElementById('results');
resultsContainer.innerHTML = '';
if (data.results && data.results.length > 0) {
data.results.forEach(customer => {
const card = document.createElement('div');
card.className = 'card mb-3';
const customerLink = createCustomerLink(customer.Nummer);
console.log('Customer:', customer); // Debug-Ausgabe
console.log('Customer link:', customerLink); // Debug-Ausgabe
card.innerHTML = `
<div class="card-body">
<h5 class="card-title">${customer.Vorname} ${customer.Nachname}</h5>
<p class="card-text">
<strong>Kundennummer:</strong> ${customerLink}<br>
<strong>Fachrichtung:</strong> ${customer.Fachrichtung || 'N/A'}<br>
<strong>Adresse:</strong> ${createAddressLink(customer.Strasse, customer.PLZ, customer.Ort)}<br>
<strong>Telefon:</strong> ${createPhoneLink(customer.Tel)}<br>
<strong>E-Mail:</strong> ${createEmailLink(customer.mail)}
</p>
</div>
</div>
`;
resultsDiv.appendChild(card);
});
`;
resultsContainer.appendChild(card);
});
// Zeige die Anzahl der Treffer an
const totalResults = document.getElementById('total-results');
if (totalResults) {
totalResults.textContent = `${data.total} Treffer gefunden`;
}
} else {
resultsContainer.innerHTML = '<div class="alert alert-info">Keine Ergebnisse gefunden.</div>';
}
})
.catch(error => {
console.error('Fehler:', error);
document.getElementById('results').innerHTML =
`<div class="alert alert-danger">${error.message}</div>`;
lastResults = [];
updateResultCounts();
})
.finally(() => {
document.getElementById('loading').style.display = 'none';
@@ -289,13 +356,32 @@
document.getElementById('ortInput'),
document.getElementById('kundennummerInput'),
document.getElementById('fachrichtungInput'),
document.getElementById('telefonInput'),
document.getElementById('searchInput')
];
searchInputs.forEach(input => {
const resetIcons = [
document.getElementById('nameReset'),
document.getElementById('ortReset'),
document.getElementById('kundennummerReset'),
document.getElementById('fachrichtungReset'),
document.getElementById('telefonReset'),
document.getElementById('searchReset')
];
searchInputs.forEach((input, index) => {
input.addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(searchCustomers, 300);
// Reset-Icon anzeigen/verstecken
resetIcons[index].classList.toggle('visible', this.value.length > 0);
});
// Reset-Funktionalität
resetIcons[index].addEventListener('click', function() {
searchInputs[index].value = '';
searchCustomers();
});
});
@@ -306,15 +392,17 @@
const ort = urlParams.get('ort');
const kundennummer = urlParams.get('kundennummer');
const fachrichtung = urlParams.get('fachrichtung');
const telefon = urlParams.get('telefon');
const query = urlParams.get('q');
if (name) document.getElementById('nameInput').value = name;
if (ort) document.getElementById('ortInput').value = ort;
if (kundennummer) document.getElementById('kundennummerInput').value = kundennummer;
if (fachrichtung) document.getElementById('fachrichtungInput').value = fachrichtung;
if (telefon) document.getElementById('telefonInput').value = telefon;
if (query) document.getElementById('searchInput').value = query;
if (name || ort || kundennummer || fachrichtung || query) {
if (name || ort || kundennummer || fachrichtung || telefon || query) {
searchCustomers();
}
});