Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
adbfe0aca2 | |||
efce228aed | |||
d87074b9dd | |||
efb87ce21b | |||
4797b363cb | |||
ecf4c0ee0c | |||
10aeef2283 | |||
d891ba62bc |
15
CHANGELOG.md
15
CHANGELOG.md
@@ -218,4 +218,17 @@ und dieses Projekt adhäriert zu [Semantic Versioning](https://semver.org/lang/d
|
|||||||
- Version auf 1.2.19 aktualisiert
|
- Version auf 1.2.19 aktualisiert
|
||||||
|
|
||||||
## [1.2.18] - 2024-03-19
|
## [1.2.18] - 2024-03-19
|
||||||
# ... existing code ...
|
# ... existing code ...
|
||||||
|
|
||||||
|
## [v1.2.20] - 2024-03-19
|
||||||
|
### Hinzugefügt
|
||||||
|
- Dunkles Theme für bessere Lesbarkeit bei schlechten Lichtverhältnissen
|
||||||
|
- Theme-Switcher im Hamburger-Menü
|
||||||
|
- Automatische Speicherung der Theme-Präferenz
|
||||||
|
|
||||||
|
## [1.2.21] - 2024-03-24
|
||||||
|
### Fixed
|
||||||
|
- Verbesserte Datenbankinitialisierung und CSV-Import
|
||||||
|
- Korrektur der Suchfunktionalität
|
||||||
|
|
||||||
|
## [1.2.20] - 2024-03-24
|
11
README.md
11
README.md
@@ -19,7 +19,7 @@ Eine einfache und effiziente Kundensuche für medisoftware Kunden.
|
|||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
Aktuelle Version: 1.2.19
|
Aktuelle Version: 1.2.21
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -144,15 +144,16 @@ Aktuelle Version: [v1.2.17](CHANGELOG.md#v1217---2024-03-19)
|
|||||||
|
|
||||||
Language|files|blank|comment|code
|
Language|files|blank|comment|code
|
||||||
:-------|-------:|-------:|-------:|-------:
|
:-------|-------:|-------:|-------:|-------:
|
||||||
|
HTML|4|13|37|436
|
||||||
JavaScript|1|67|28|420
|
JavaScript|1|67|28|420
|
||||||
HTML|4|16|0|382
|
CSS|1|72|16|353
|
||||||
CSS|1|63|0|324
|
Markdown|2|79|0|300
|
||||||
Markdown|2|77|0|295
|
Python|1|71|126|280
|
||||||
YAML|1|0|0|14
|
YAML|1|0|0|14
|
||||||
Dockerfile|1|8|9|11
|
Dockerfile|1|8|9|11
|
||||||
Text|1|0|0|6
|
Text|1|0|0|6
|
||||||
--------|--------|--------|--------|--------
|
--------|--------|--------|--------|--------
|
||||||
SUM:|12|302|163|1732
|
SUM:|12|310|216|1820
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
Alle Rechte vorbehalten. © 2025 medisoftware
|
Alle Rechte vorbehalten. © 2025 medisoftware
|
144
app.py
144
app.py
@@ -15,7 +15,7 @@ import markdown2
|
|||||||
app = Flask(__name__, static_folder='static')
|
app = Flask(__name__, static_folder='static')
|
||||||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev')
|
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev')
|
||||||
app.config['ALLOWED_IP_RANGES'] = os.getenv('ALLOWED_IP_RANGES', '192.168.0.0/16,10.0.0.0/8').split(',')
|
app.config['ALLOWED_IP_RANGES'] = os.getenv('ALLOWED_IP_RANGES', '192.168.0.0/16,10.0.0.0/8').split(',')
|
||||||
app.config['VERSION'] = '1.2.19'
|
app.config['VERSION'] = '1.2.21'
|
||||||
app.config['DATABASE'] = 'data/customers.db'
|
app.config['DATABASE'] = 'data/customers.db'
|
||||||
app.config['DATABASE_TIMEOUT'] = 20
|
app.config['DATABASE_TIMEOUT'] = 20
|
||||||
app.config['DATABASE_POOL_SIZE'] = 5
|
app.config['DATABASE_POOL_SIZE'] = 5
|
||||||
@@ -51,6 +51,12 @@ def init_db():
|
|||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Prüfe, ob die Tabelle bereits existiert
|
||||||
|
c.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='customers'")
|
||||||
|
if c.fetchone() is not None:
|
||||||
|
app.logger.info('Datenbank existiert bereits, überspringe Initialisierung')
|
||||||
|
return
|
||||||
|
|
||||||
# Erstelle die Kunden-Tabelle
|
# Erstelle die Kunden-Tabelle
|
||||||
c.execute('''
|
c.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS customers (
|
CREATE TABLE IF NOT EXISTS customers (
|
||||||
@@ -83,11 +89,59 @@ def init_db():
|
|||||||
# Zusammengesetzter Index für die häufigste Suchkombination
|
# Zusammengesetzter Index für die häufigste Suchkombination
|
||||||
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_search ON customers(name, ort, fachrichtung, tag)')
|
c.execute('CREATE INDEX IF NOT EXISTS idx_customers_search ON customers(name, ort, fachrichtung, tag)')
|
||||||
|
|
||||||
logger.info('Datenbank initialisiert')
|
app.logger.info('Datenbank initialisiert')
|
||||||
|
|
||||||
|
# Importiere die CSV-Daten
|
||||||
|
import_csv_data('data/medisoft.csv', 'MEDISOFT')
|
||||||
|
import_csv_data('data/mediconsult.csv', 'MEDICONSULT')
|
||||||
|
app.logger.info('CSV-Daten erfolgreich in die Datenbank importiert')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'Fehler bei der Datenbankinitialisierung: {str(e)}')
|
app.logger.error(f'Fehler bei der Datenbankinitialisierung: {str(e)}')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def import_csv_data(file_path, tag):
|
||||||
|
"""Importiert die CSV-Datei in die Datenbank."""
|
||||||
|
try:
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
app.logger.warning(f"CSV-Datei {file_path} nicht gefunden")
|
||||||
|
return
|
||||||
|
|
||||||
|
app.logger.info(f"Importiere {tag}-Daten aus {file_path}...")
|
||||||
|
|
||||||
|
with get_db() as conn:
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
# Lese die CSV-Datei
|
||||||
|
df = pd.read_csv(file_path, encoding='iso-8859-1')
|
||||||
|
df.columns = df.columns.str.strip().str.replace('"', '')
|
||||||
|
df = df.apply(lambda x: x.str.strip().str.replace('"', '') if x.dtype == "object" else x)
|
||||||
|
|
||||||
|
# Filtere Datensätze mit Fachrichtung "intern"
|
||||||
|
df = df[df['Fachrichtung'].str.lower() != 'intern']
|
||||||
|
|
||||||
|
# Bereite die Daten für den Batch-Insert vor
|
||||||
|
data = [(
|
||||||
|
row['VorNachname'], row['Nummer'], row['Strasse'], row['PLZ'], row['Ort'],
|
||||||
|
row['Tel'], row['Tel'], row['mail'], row['Fachrichtung'], tag,
|
||||||
|
row['Handy'], row['Tele Firma'], row['Kontakt1'], row['Kontakt2'], row['Kontakt3']
|
||||||
|
) for _, row in df.iterrows()]
|
||||||
|
|
||||||
|
# Führe Batch-Insert durch
|
||||||
|
c.executemany('''
|
||||||
|
INSERT INTO customers (
|
||||||
|
name, nummer, strasse, plz, ort, telefon, mobil, email,
|
||||||
|
fachrichtung, tag, handy, tele_firma, kontakt1, kontakt2, kontakt3
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
''', data)
|
||||||
|
|
||||||
|
app.logger.info(f"{tag}-Daten erfolgreich importiert")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Fehler beim Importieren der {tag}-Daten: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
def isIPInSubnet(ip, subnet):
|
def isIPInSubnet(ip, subnet):
|
||||||
"""Überprüft, ob eine IP-Adresse in einem Subnetz liegt."""
|
"""Überprüft, ob eine IP-Adresse in einem Subnetz liegt."""
|
||||||
try:
|
try:
|
||||||
@@ -109,76 +163,6 @@ def isIPInSubnet(ip, subnet):
|
|||||||
logger.error(f"Fehler bei der IP-Überprüfung: {str(e)}")
|
logger.error(f"Fehler bei der IP-Überprüfung: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def import_csv():
|
|
||||||
"""Importiert die CSV-Datei in die Datenbank"""
|
|
||||||
try:
|
|
||||||
with get_db() as conn:
|
|
||||||
c = conn.cursor()
|
|
||||||
|
|
||||||
# Lösche bestehende Daten
|
|
||||||
c.execute('DELETE FROM customers')
|
|
||||||
|
|
||||||
# Importiere MEDISOFT-Daten
|
|
||||||
if os.path.exists('data/customers.csv'):
|
|
||||||
logger.info("Importiere MEDISOFT-Daten...")
|
|
||||||
df = pd.read_csv('data/customers.csv', encoding='iso-8859-1')
|
|
||||||
df.columns = df.columns.str.strip().str.replace('"', '')
|
|
||||||
df = df.apply(lambda x: x.str.strip().str.replace('"', '') if x.dtype == "object" else x)
|
|
||||||
|
|
||||||
# Filtere Datensätze mit Fachrichtung "intern"
|
|
||||||
df = df[df['Fachrichtung'].str.lower() != 'intern']
|
|
||||||
|
|
||||||
# Bereite die Daten für den Batch-Insert vor
|
|
||||||
data = [(
|
|
||||||
row['VorNachname'], row['Nummer'], row['Strasse'], row['PLZ'], row['Ort'],
|
|
||||||
row['Tel'], row['Tel'], row['mail'], row['Fachrichtung'], 'medisoft',
|
|
||||||
row['Handy'], row['Tele Firma'], row['Kontakt1'], row['Kontakt2'], row['Kontakt3']
|
|
||||||
) for _, row in df.iterrows()]
|
|
||||||
|
|
||||||
# Führe Batch-Insert durch
|
|
||||||
c.executemany('''
|
|
||||||
INSERT INTO customers (
|
|
||||||
name, nummer, strasse, plz, ort, telefon, mobil, email,
|
|
||||||
fachrichtung, tag, handy, tele_firma, kontakt1, kontakt2, kontakt3
|
|
||||||
)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
''', data)
|
|
||||||
else:
|
|
||||||
logger.warning("MEDISOFT CSV-Datei nicht gefunden")
|
|
||||||
|
|
||||||
# Importiere MEDICONSULT-Daten
|
|
||||||
if os.path.exists('data/customers_snk.csv'):
|
|
||||||
logger.info("Importiere MEDICONSULT-Daten...")
|
|
||||||
df_snk = pd.read_csv('data/customers_snk.csv', encoding='iso-8859-1')
|
|
||||||
df_snk.columns = df_snk.columns.str.strip().str.replace('"', '')
|
|
||||||
df_snk = df_snk.apply(lambda x: x.str.strip().str.replace('"', '') if x.dtype == "object" else x)
|
|
||||||
|
|
||||||
# Filtere Datensätze mit Fachrichtung "intern"
|
|
||||||
df_snk = df_snk[df_snk['Fachrichtung'].str.lower() != 'intern']
|
|
||||||
|
|
||||||
# Bereite die Daten für den Batch-Insert vor
|
|
||||||
data = [(
|
|
||||||
row['VorNachname'], row['Nummer'], row['Strasse'], row['PLZ'], row['Ort'],
|
|
||||||
row['Tel'], row['Tel'], row['mail'], row['Fachrichtung'], 'mediconsult',
|
|
||||||
row['Handy'], row['Tele Firma'], row['Kontakt1'], row['Kontakt2'], row['Kontakt3']
|
|
||||||
) for _, row in df_snk.iterrows()]
|
|
||||||
|
|
||||||
# Führe Batch-Insert durch
|
|
||||||
c.executemany('''
|
|
||||||
INSERT INTO customers (
|
|
||||||
name, nummer, strasse, plz, ort, telefon, mobil, email,
|
|
||||||
fachrichtung, tag, handy, tele_firma, kontakt1, kontakt2, kontakt3
|
|
||||||
)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
''', data)
|
|
||||||
else:
|
|
||||||
logger.warning("MEDICONSULT CSV-Datei nicht gefunden")
|
|
||||||
|
|
||||||
logger.info("CSV-Daten erfolgreich in die Datenbank importiert")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Fehler beim Importieren der CSV-Datei: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
# Überprüfe, ob die Client-IP in einem der erlaubten Bereiche liegt
|
# Überprüfe, ob die Client-IP in einem der erlaubten Bereiche liegt
|
||||||
@@ -422,8 +406,8 @@ def upload():
|
|||||||
customers.save('data/customers.csv')
|
customers.save('data/customers.csv')
|
||||||
|
|
||||||
# Importiere die Daten in die Datenbank
|
# Importiere die Daten in die Datenbank
|
||||||
import_csv('data/customers_snk.csv', 'snk')
|
import_csv_data('data/customers_snk.csv', 'snk')
|
||||||
import_csv('data/customers.csv', 'medisoft')
|
import_csv_data('data/customers.csv', 'medisoft')
|
||||||
|
|
||||||
return render_template('upload.html', success="Dateien erfolgreich hochgeladen und importiert", version=app.config['VERSION'])
|
return render_template('upload.html', success="Dateien erfolgreich hochgeladen und importiert", version=app.config['VERSION'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -453,18 +437,8 @@ def init_app(app):
|
|||||||
# Stelle sicher, dass der data-Ordner existiert
|
# Stelle sicher, dass der data-Ordner existiert
|
||||||
os.makedirs('data', exist_ok=True)
|
os.makedirs('data', exist_ok=True)
|
||||||
|
|
||||||
# Lösche die alte Datenbank, falls sie existiert
|
|
||||||
if os.path.exists(app.config['DATABASE']):
|
|
||||||
try:
|
|
||||||
os.remove(app.config['DATABASE'])
|
|
||||||
logger.info(f"Alte Datenbank {app.config['DATABASE']} wurde gelöscht")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Fehler beim Löschen der alten Datenbank: {str(e)}")
|
|
||||||
|
|
||||||
# Initialisiere die Datenbank
|
# Initialisiere die Datenbank
|
||||||
init_db()
|
init_db()
|
||||||
# Importiere die CSV-Daten
|
|
||||||
import_csv()
|
|
||||||
logger.info("Anwendung erfolgreich initialisiert")
|
logger.info("Anwendung erfolgreich initialisiert")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Fehler bei der Initialisierung: {str(e)}")
|
logger.error(f"Fehler bei der Initialisierung: {str(e)}")
|
||||||
|
87
static/css/style.css
Normal file
87
static/css/style.css
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
:root {
|
||||||
|
--primary-color: #007bff;
|
||||||
|
--secondary-color: #6c757d;
|
||||||
|
--background-color: #ffffff;
|
||||||
|
--text-color: #212529;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--border-color: #dee2e6;
|
||||||
|
--hover-bg: #f8f9fa;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--primary-color: #0d6efd;
|
||||||
|
--secondary-color: #adb5bd;
|
||||||
|
--background-color: #212529;
|
||||||
|
--text-color: #f8f9fa;
|
||||||
|
--card-bg: #343a40;
|
||||||
|
--border-color: #495057;
|
||||||
|
--hover-bg: #495057;
|
||||||
|
--shadow-color: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: 0 2px 4px var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-bottom-color: var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-hover tbody tr:hover {
|
||||||
|
background-color: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
border-bottom-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
border-top-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary {
|
||||||
|
color: var(--text-color);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary:hover {
|
||||||
|
background-color: var(--hover-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
@@ -1,20 +1,121 @@
|
|||||||
|
/*
|
||||||
|
medisoftware Kundensuche - Hauptstyles
|
||||||
|
Version: 1.2.19
|
||||||
|
Entwickler: medisoftware GmbH
|
||||||
|
Letzte Änderung: 2024-03-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Allgemeine Styles */
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hauptcontainer */
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
margin-bottom: 4rem; /* Platz für die fixierte Fußzeile */
|
margin-bottom: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Suchcontainer */
|
||||||
.search-container {
|
.search-container {
|
||||||
max-width: 800px;
|
background-color: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suchergebnisse */
|
||||||
|
#searchResults {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kundenkarte */
|
||||||
|
.customer-card {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kundenname */
|
||||||
|
.customer-name {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kundeninformationen */
|
||||||
|
.customer-info {
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Klickbare Links */
|
||||||
|
.customer-info a {
|
||||||
|
color: #0d6efd;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-link {
|
||||||
|
color: #0d6efd;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Anpassungen */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.main-content {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-card {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-card {
|
.result-card {
|
||||||
@@ -73,19 +174,6 @@ body {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
|
||||||
flex-shrink: 0;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border-top: 1px solid #dee2e6;
|
|
||||||
width: 100%;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.share-feedback {
|
.share-feedback {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
@@ -209,14 +297,6 @@ body {
|
|||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customer-header {
|
.customer-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -224,12 +304,6 @@ body {
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-name {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customer-actions {
|
.customer-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -286,26 +360,6 @@ body {
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-content {
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border-top: 1px solid #dee2e6;
|
|
||||||
width: 100%;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
color: #0d6efd;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.general-search {
|
.general-search {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
@@ -5,6 +5,12 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="client-ip" content="{{ request.headers.get('X-Forwarded-For', request.remote_addr) }}">
|
<meta name="client-ip" content="{{ request.headers.get('X-Forwarded-For', request.remote_addr) }}">
|
||||||
<meta name="allowed-ip-ranges" content="{{ allowed_ip_ranges }}">
|
<meta name="allowed-ip-ranges" content="{{ allowed_ip_ranges }}">
|
||||||
|
<!--
|
||||||
|
medisoftware Kundensuche
|
||||||
|
Version: {{ version }}
|
||||||
|
Entwickler: medisoftware GmbH
|
||||||
|
Letzte Änderung: 2024-03-19
|
||||||
|
-->
|
||||||
<title>medisoftware Kundensuche</title>
|
<title>medisoftware Kundensuche</title>
|
||||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
<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://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
@@ -12,6 +18,10 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
|
/*
|
||||||
|
Inline-Styles für spezifische Anpassungen
|
||||||
|
Diese Styles sind nur für diese Seite gültig
|
||||||
|
*/
|
||||||
.logo {
|
.logo {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -28,23 +38,22 @@
|
|||||||
<button class="btn btn-link text-dark" type="button" id="menuButton" data-bs-toggle="dropdown" aria-expanded="false">
|
<button class="btn btn-link text-dark" type="button" id="menuButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-list fs-4"></i>
|
<i class="bi bi-list fs-4"></i>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu" aria-labelledby="menuButton">
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="{{ url_for('index') }}">
|
||||||
|
<i class="bi bi-house"></i> Home
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('upload') }}">
|
||||||
|
<i class="bi bi-cloud-upload"></i> CSV-Dateien hochladen
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('readme') }}">
|
||||||
|
<i class="bi bi-book"></i> README
|
||||||
|
</a>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="{{ url_for('index') }}">
|
<button class="dropdown-item theme-toggle" id="themeToggle">
|
||||||
<i class="bi bi-house"></i> Home
|
<i class="bi bi-moon-stars"></i> Theme wechseln
|
||||||
</a>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
</div>
|
||||||
<a class="dropdown-item" href="{{ url_for('upload') }}">
|
|
||||||
<i class="bi bi-cloud-upload"></i> CSV-Dateien hochladen
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="{{ url_for('readme') }}">
|
|
||||||
<i class="bi bi-book"></i> README
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a href="https://medisoftware.de" target="_blank" rel="noopener noreferrer">
|
<a href="https://medisoftware.de" target="_blank" rel="noopener noreferrer">
|
||||||
@@ -146,5 +155,28 @@
|
|||||||
|
|
||||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Theme Switcher
|
||||||
|
const themeToggle = document.getElementById('themeToggle');
|
||||||
|
const icon = themeToggle.querySelector('i');
|
||||||
|
|
||||||
|
// Theme aus dem localStorage laden
|
||||||
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
||||||
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||||
|
updateThemeIcon(savedTheme);
|
||||||
|
|
||||||
|
themeToggle.addEventListener('click', () => {
|
||||||
|
const currentTheme = document.documentElement.getAttribute('data-theme');
|
||||||
|
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
|
localStorage.setItem('theme', newTheme);
|
||||||
|
updateThemeIcon(newTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateThemeIcon(theme) {
|
||||||
|
icon.className = theme === 'light' ? 'bi bi-moon-stars' : 'bi bi-sun';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -3,56 +3,104 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!--
|
||||||
|
medisoftware Kundensuche - README
|
||||||
|
Version: {{ version }}
|
||||||
|
Entwickler: medisoftware GmbH
|
||||||
|
Letzte Änderung: 2024-03-19
|
||||||
|
-->
|
||||||
<title>medisoftware Kundensuche - README</title>
|
<title>medisoftware Kundensuche - README</title>
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<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">
|
<!-- Bootstrap Icons für die Menü-Symbole -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5.5.0/github-markdown.min.css">
|
|
||||||
<style>
|
<style>
|
||||||
|
/*
|
||||||
|
Inline-Styles für die README-Seite
|
||||||
|
Diese Styles sind nur für diese Seite gültig
|
||||||
|
*/
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
background-color: #f8f9fa;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1 0 auto;
|
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
}
|
||||||
.footer {
|
.logo {
|
||||||
flex-shrink: 0;
|
width: 200px;
|
||||||
text-align: center;
|
height: auto;
|
||||||
padding: 1rem;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border-top: 1px solid #dee2e6;
|
|
||||||
width: 100%;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
.markdown-body {
|
|
||||||
box-sizing: border-box;
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 980px;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 45px;
|
display: block;
|
||||||
}
|
}
|
||||||
@media (max-width: 767px) {
|
.readme-container {
|
||||||
.markdown-body {
|
background-color: white;
|
||||||
padding: 15px;
|
padding: 2rem;
|
||||||
}
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.readme-content {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.readme-content h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.readme-content h2 {
|
||||||
|
color: #444;
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.readme-content p {
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.readme-content code {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
.readme-content pre {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.readme-content ul, .readme-content ol {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
.readme-content li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.readme-content blockquote {
|
||||||
|
border-left: 4px solid #dee2e6;
|
||||||
|
padding-left: 1rem;
|
||||||
|
margin-left: 0;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
.readme-content table {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.readme-content th, .readme-content td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
.readme-content th {
|
||||||
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Hauptcontainer für den Inhalt -->
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<!-- Header mit Logo und Menü -->
|
||||||
<div class="dropdown">
|
<div class="position-relative mb-4">
|
||||||
|
<!-- Dropdown-Menü -->
|
||||||
|
<div class="dropdown position-absolute start-0">
|
||||||
<button class="btn btn-link text-dark" type="button" id="menuButton" data-bs-toggle="dropdown" aria-expanded="false">
|
<button class="btn btn-link text-dark" type="button" id="menuButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-list fs-4"></i>
|
<i class="bi bi-list fs-4"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -74,38 +122,22 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a href="https://medisoftware.de" target="_blank" rel="noopener noreferrer">
|
<!-- Logo -->
|
||||||
<img src="{{ url_for('static', filename='medisoftware_logo_rb_200.png') }}" alt="medisoftware Logo" class="img-fluid" style="max-width: 200px;">
|
<div class="text-center">
|
||||||
</a>
|
<a href="https://medisoftware.de" target="_blank" rel="noopener noreferrer">
|
||||||
</div>
|
<img src="{{ url_for('static', filename='medisoftware_logo_rb_200.png') }}" alt="medisoftware Logo" class="img-fluid logo">
|
||||||
|
</a>
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h2 class="text-center mb-0">README</h2>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
</div>
|
||||||
{% if error %}
|
<!-- README-Container -->
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="readme-container">
|
||||||
{{ error }}
|
<div class="readme-content">
|
||||||
</div>
|
{{ readme_content | safe }}
|
||||||
{% else %}
|
|
||||||
<div class="markdown-body">
|
|
||||||
{{ content | safe }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Bootstrap JavaScript -->
|
||||||
<footer class="footer">
|
|
||||||
<div class="footer-content">
|
|
||||||
Proudly made with ❤️ and 🍺 by <a href="https://www.medisoftware.de" target="_blank" class="footer-link">medisoftware</a>
|
|
||||||
<div style="font-size: 0.8em;">Version: {{ version }}</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -3,10 +3,22 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!--
|
||||||
|
medisoftware Kundensuche - CSV Upload
|
||||||
|
Version: {{ version }}
|
||||||
|
Entwickler: medisoftware GmbH
|
||||||
|
Letzte Änderung: 2024-03-19
|
||||||
|
-->
|
||||||
<title>medisoftware Kundensuche - CSV Upload</title>
|
<title>medisoftware Kundensuche - CSV Upload</title>
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Bootstrap Icons für die Menü-Symbole -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<style>
|
<style>
|
||||||
|
/*
|
||||||
|
Inline-Styles für die Upload-Seite
|
||||||
|
Diese Styles sind nur für diese Seite gültig
|
||||||
|
*/
|
||||||
body {
|
body {
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
@@ -34,9 +46,12 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Hauptcontainer für den Inhalt -->
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<!-- Header mit Logo und Menü -->
|
||||||
<div class="position-relative mb-4">
|
<div class="position-relative mb-4">
|
||||||
|
<!-- Dropdown-Menü -->
|
||||||
<div class="dropdown position-absolute start-0">
|
<div class="dropdown position-absolute start-0">
|
||||||
<button class="btn btn-link text-dark" type="button" id="menuButton" data-bs-toggle="dropdown" aria-expanded="false">
|
<button class="btn btn-link text-dark" type="button" id="menuButton" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-list fs-4"></i>
|
<i class="bi bi-list fs-4"></i>
|
||||||
@@ -59,22 +74,27 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Logo -->
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a href="https://medisoftware.de" target="_blank" rel="noopener noreferrer">
|
<a href="https://medisoftware.de" target="_blank" rel="noopener noreferrer">
|
||||||
<img src="{{ url_for('static', filename='medisoftware_logo_rb_200.png') }}" alt="medisoftware Logo" class="img-fluid logo">
|
<img src="{{ url_for('static', filename='medisoftware_logo_rb_200.png') }}" alt="medisoftware Logo" class="img-fluid logo">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Upload-Container -->
|
||||||
<div class="upload-container">
|
<div class="upload-container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h2 class="text-center mb-4">CSV-Dateien hochladen</h2>
|
<h2 class="text-center mb-4">CSV-Dateien hochladen</h2>
|
||||||
|
<!-- Fehlermeldungen -->
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<div class="alert alert-danger">{{ error }}</div>
|
<div class="alert alert-danger">{{ error }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- Erfolgsmeldungen -->
|
||||||
{% if success %}
|
{% if success %}
|
||||||
<div class="alert alert-success">{{ success }}</div>
|
<div class="alert alert-success">{{ success }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- Upload-Formular -->
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="medisoft_file" class="form-label">MEDISOFT CSV-Datei</label>
|
<label for="medisoft_file" class="form-label">MEDISOFT CSV-Datei</label>
|
||||||
@@ -97,6 +117,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Bootstrap JavaScript -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Reference in New Issue
Block a user