Tocknerheld-Dashboard
-Select a menu item to view content.
+Bitte wähle einen Menüpunkt, um in den entsprechenden Bereich zu springen.
diff --git a/app/data/invoices.csv b/app/data/invoices.csv new file mode 100644 index 0000000..6d7aba2 --- /dev/null +++ b/app/data/invoices.csv @@ -0,0 +1,2 @@ +Rechnungsnummer;Email;Anrede +RE357891;isaak.buslovich@trocknerheld.de;Isaak Trocknerheld diff --git a/app/routes.py b/app/routes.py index 19683e8..379b62f 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,8 +1,30 @@ -from flask import render_template, redirect, url_for, current_app +from flask import render_template, redirect, url_for, current_app, render_template_string, request import os +import csv + from app import app, htmx +def read_invoices_from_csv(csv_file_path): + try: + with open(csv_file_path, mode='r', encoding='utf-8') as file: + return list(csv.DictReader(file, delimiter=';')) + except FileNotFoundError: + current_app.logger.error(f"CSV file not found at {csv_file_path}") + except csv.Error as e: + current_app.logger.error(f"Error reading CSV file: {e}") + return [] + + +@app.route('/load-invoices') +def load_invoices(): + csv_file_path = os.path.join(current_app.root_path, 'data', 'invoices.csv') + invoices = read_invoices_from_csv(csv_file_path) + if not invoices: + return "No invoice data available", 500 + return render_template('partials/invoice_table.html', invoices=invoices) + + def render_htmx_or_full(template_partial, template_full): try: if htmx: @@ -10,7 +32,6 @@ def render_htmx_or_full(template_partial, template_full): return render_template(template_full) except Exception as e: current_app.logger.error(f"Error rendering template: {e}") - # Optionally, you can return a custom error page here return "An error occurred", 500 @@ -24,12 +45,42 @@ def buchhaltung(): return render_htmx_or_full('partials/buchhaltung.html', 'index.html') +@app.route('/add-invoice', methods=['POST']) +def add_invoice(): + csv_file_path = os.path.join(current_app.root_path, 'data', 'invoices.csv') + new_invoice = { + 'Rechnungsnummer': request.form['rechnungsnummer'], + 'Email': request.form['email'], + 'Anrede': request.form['anrede'] + } + + try: + # Read the existing data + with open(csv_file_path, 'r', newline='', encoding='utf-8') as csvfile: + reader = csv.DictReader(csvfile, delimiter=';') + fieldnames = reader.fieldnames + existing_data = list(reader) + + # Add the new invoice at the beginning of the data + existing_data.insert(0, new_invoice) + + # Write the updated data back to the file + with open(csv_file_path, 'w', newline='', encoding='utf-8') as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=';') + writer.writeheader() + writer.writerows(existing_data) + + return redirect(url_for('buchhaltung')) + except Exception as e: + current_app.logger.error(f"Error updating CSV file: {e}") + return "An error occurred while adding the invoice", 500 + + @app.route('/flottenmanagement') def flottenmanagement(): return render_htmx_or_full('partials/flottenmanagement.html', 'index.html') -# Additional debug route to check the file structure @app.route('/debug/templates') def debug_templates(): template_dir = os.path.join(current_app.root_path, 'templates') diff --git a/app/static/css/style.css b/app/static/css/style.css index 19f96a7..b5af572 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -1,12 +1,26 @@ /* Import Google Fonts */ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); +/* Root and Variable Definitions */ +:root { + --sidebar-width-collapsed: 60px; + --sidebar-width-expanded: 280px; + --max-container-width: 1200px; + --primary-color: #007BFF; + --primary-color-hover: #0066cc; + --light-grey: #F8F9FA; + --dark-grey: #343A40; + --white: #FFFFFF; + --box-shadow-color: rgba(0, 0, 0, 0.1); + --box-shadow-hover-color: rgba(0, 0, 0, 0.15); +} + /* Body and Global Styles */ body { font-family: 'Roboto', sans-serif; margin: 0; padding: 0; - background: #F8F9FA; + background: var(--light-grey); display: flex; height: 100vh; overflow: hidden; @@ -16,67 +30,73 @@ body { .sidebar { display: flex; flex-direction: column; - background: #FFFFFF; - width: 60px; /* Collapsed width */ + background: var(--white); + width: var(--sidebar-width-collapsed); height: 100%; - transition: width 0.3s ease; /* Smooth transition for expanding */ - box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); + transition: width 0.3s ease; + box-shadow: 2px 0 5px var(--box-shadow-color); overflow: hidden; z-index: 20; } .sidebar:hover, .sidebar.sidebar-expanded { - width: 280px; + width: var(--sidebar-width-expanded); } .menu-item { display: flex; align-items: center; padding: 15px 20px; - color: #343A40; + color: var(--dark-grey); font-size: 16px; transition: background-color 0.3s, padding-left 0.3s; cursor: pointer; - overflow: hidden; /* Hide overflow when sidebar is collapsed */ + overflow: hidden; } .menu-item:hover { - background-color: #007BFF; - color: #FFFFFF; + background-color: var(--primary-color); + color: var(--white); } .menu-icon { margin-right: 10px; - transition: transform 0.3s ease; /* Smooth icon transition */ + transition: transform 0.3s ease; } .sidebar:hover .menu-icon, .sidebar.sidebar-expanded .menu-icon { - transform: rotate(360deg); /* Rotate icon on hover */ + transform: rotate(360deg); } .menu-label { white-space: nowrap; overflow: hidden; transition: opacity 0.3s ease, width 0.3s ease; - display: none; /* Hide labels initially */ + display: none; } .sidebar:hover .menu-label, .sidebar.sidebar-expanded .menu-label { - display: inline; /* Show labels when sidebar is expanded */ + display: inline; } -/* Adjust sidebar for touch devices (no hover) */ -@media (hover: none) { - .sidebar:hover .menu-label { - display: none; +@media (min-width: 1200px) { + .container { + max-width: var(--max-container-width); + margin: auto; + padding: 20px 40px; + } + + .card-grid { + grid-template-columns: repeat(3, 1fr); + gap: 30px; } } -/* Responsive Design */ +/* Responsive Design for Smaller Screens */ @media (max-width: 768px) { .sidebar { position: fixed; - width: 60px; + width: var(--sidebar-width-collapsed); } .sidebar-expanded { @@ -84,46 +104,68 @@ body { } .container { - margin-left: 60px; - width: calc(100% - 60px); + margin-left: var(--sidebar-width-collapsed); + width: calc(100% - var(--sidebar-width-collapsed)); } .sidebar-expanded + .container { margin-left: 200px; width: calc(100% - 200px); } + + .card-grid { + grid-template-columns: 1fr; + } } /* Main Content Styles */ .container { - margin-left: 60px; + margin-left: var(--sidebar-width-collapsed); transition: margin-left 0.3s ease; padding: 20px; - width: calc(100% - 60px); + width: calc(100% - var(--sidebar-width-collapsed)); overflow-y: auto; } .card-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-template-columns: 1fr; gap: 20px; } .card { - background-color: white; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: var(--white); + box-shadow: 0 4px 8px var(--box-shadow-color); border-radius: 10px; overflow: hidden; - transition: transform 0.3s ease, box-shadow 0.3s ease; + transition: transform 0.2s ease, box-shadow 0.2s ease; + display: flex; + flex-direction: column; } .card:hover { - transform: translateY(-5px); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); + transform: translateY(-2px); + box-shadow: 0 5px 10px var(--box-shadow-hover-color); } .card-container { padding: 20px; + display: flex; + flex-direction: column; + flex-grow: 1; +} + +/* Table Styles */ +.card table { + width: 100%; + border-collapse: collapse; + table-layout: fixed; +} + +.card th, .card td { + border: 1px solid #ddd; + padding: 8px; + word-wrap: break-word; } /* Form and Button Styles */ @@ -136,18 +178,17 @@ input, .button { } .button { - background-color: #007BFF; - color: white; + background-color: var(--primary-color); + color: var(--white); border: none; cursor: pointer; transition: background-color 0.3s; } .button:hover { - background-color: #003875; + background-color: var(--primary-color-hover); } -/* Login Overlay Styles */ .login-overlay { position: fixed; top: 0; @@ -168,7 +209,6 @@ input, .button { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); } -/* Tabbed Interface Styles */ .tab-group { list-style: none; padding: 0; @@ -201,7 +241,6 @@ input, .button { border-bottom: 2px solid white; } -/* Tab Content */ .tab-content { background: #fff; padding: 20px; diff --git a/app/templates/index.html b/app/templates/index.html index 95fe91a..95cebbd 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,9 +1,9 @@ - +
-Select a menu item to view content.
+Bitte wähle einen Menüpunkt, um in den entsprechenden Bereich zu springen.
This is a description for card 1.
+This is a description for card 2.
+Tabelle aller versendeter Rechnungen inklusive E-Mail und Anrede
++ +
| Rechnungsnummer | +Anrede | +|
|---|---|---|
| {{ invoice['Rechnungsnummer'] }} | +{{ invoice['Email'] }} | +{{ invoice['Anrede'] }} | +
No invoices available.
+{% endif %}