ソースを参照

Chat und Docx-Export

gc-server3 1 ヶ月 前
コミット
b55baebab3
8 ファイル変更1785 行追加427 行削除
  1. 0 1
      app/models.py
  2. 34 0
      app/routes.py
  3. 1 0
      pyproject.toml
  4. 141 7
      templates/base.html
  5. 1172 0
      templates/chat.html
  6. BIN
      templates/docx/Mahnung_AHR.docx
  7. 14 13
      templates/login.html
  8. 423 406
      uv.lock

+ 0 - 1
app/models.py

@@ -11,7 +11,6 @@ from sqlalchemy import (
     Text,
 )
 from sqlalchemy.orm import relationship
-from sqlalchemy.orm.relationships import _RelationshipDeclared
 
 from .db import Base
 

+ 34 - 0
app/routes.py

@@ -11,6 +11,7 @@ from .auth import ldap_authenticate
 from .db import get_session
 from .models import Bemerkung, Forderung
 from .schemas import BemerkungIn
+from docxtpl import DocxTemplate
 
 router = APIRouter()
 templates = Jinja2Templates(directory="templates")
@@ -43,6 +44,12 @@ def forderungsliste(request: Request, db: Session = Depends(get_session), limit:
     return templates.TemplateResponse("list.html", {"request": request, "forderungen": q})
 
 
+@router.get("/chat", response_class=HTMLResponse)
+def chat(request: Request):
+    # q = db.query(Forderung).order_by(Forderung.faelligkeit.asc()).limit(limit).all()
+    return templates.TemplateResponse(request, "chat.html")
+
+
 @router.get("/export/csv")
 def export_csv(db: Session = Depends(get_session)):
     q = db.query(Forderung).all()
@@ -131,3 +138,30 @@ def export_docx(id: int, db: Session = Depends(get_session)):
         media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
         headers={"Content-Disposition": f"attachment; filename=rechnung_{f.rechnungsnummer}.docx"},
     )
+
+
+@router.get("/export/docx")
+def export_docx2(id: int, db: Session = Depends(get_session)):
+    doc = DocxTemplate("templates\\docx\\Mahnung_AHR.docx")
+    context = {
+        "Kunde_Name" : "Robert Burghard",
+        "Kunde_Adresse" : "Im Waldhof 14a",
+        "Kunde_PLZ" : "61476",
+        "Kunde_Ort" : "Kronberg",
+        "Datum_heute" : "25.03.2026",
+        "Kunde_Nr" : "23121983",
+        "Fahrzeug_Kennzeichen" : "OF-RB 512",
+        "Rechnung_Nr" : "2600007",
+        "Betrag_SB" : "250,00",
+        "Betrag_USt" : "380,00",
+    }
+    doc.render(context)
+    # doc.save("export\\Mahnung_AHR_Burghard.docx")
+    buf = io.BytesIO()
+    doc.save(buf)
+    buf.seek(0)
+    return StreamingResponse(
+        buf,
+        media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+        headers={"Content-Disposition": "attachment; filename=Mahnung_Burghard.docx"},
+    )

+ 1 - 0
pyproject.toml

@@ -18,4 +18,5 @@ dependencies = [
 	"pyodbc>=4.0",
 	"aiofiles>=23.1",
 	"python-dotenv>=1.2.1",
+	"docxtpl>=0.20.2",
 ]

+ 141 - 7
templates/base.html

@@ -1,18 +1,152 @@
-<!doctype html>
+<!DOCTYPE html>
 <html lang="de">
   <head>
+    <base href="./">
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="description" content="Global Cube">
+    <meta name="author" content="Global Cube GmbH - Robert Burghard">
+    <meta name="keyword" content="Bootstrap,Admin,Template,Open,Source,jQuery,CSS,HTML,RWD,Dashboard">    
     <title>Forderungen</title>
     <script src="https://unpkg.com/htmx.org@1.9.3"></script>
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
+
+    <link href="https://cdn.jsdelivr.net/npm/@coreui/coreui@5.4.3/dist/css/coreui.min.css" rel="stylesheet" integrity="sha384-oMIIhJL1T5s+PxJr6+Qb0pO1IRFB6OGMM+J57UBT3UQKxSVsb++MkXpu9cLqaJxu" crossorigin="anonymous">
+    <script src="https://cdn.jsdelivr.net/npm/@coreui/coreui@5.4.3/dist/js/coreui.bundle.min.js" integrity="sha384-SWhFOmxmv1pfTLKVBW7q8uossvuaWNeQFdmaWi6xdldiUjyqG9F6V2R2BOC8gkxx" crossorigin="anonymous"></script>
   </head>
   <body class="container py-3">
-    <header class="mb-3">
-      <h1>Forderungen</h1>
-    </header>
-    <main>
+    <!--<nav class="navbar navbar-expand-lg bg-primary">
+    <div class="container-fluid">
+        <a class="navbar-brand" href="#">Navbar</a>
+        <button class="navbar-toggler" type="button" data-coreui-toggle="collapse" data-coreui-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
+        <span class="navbar-toggler-icon"></span>
+        </button>
+        <div class="collapse navbar-collapse" id="navbarNav">
+        <ul class="navbar-nav">
+            <li class="nav-item">
+            <a class="nav-link active" aria-current="page" href="#">Home</a>
+            </li>
+            <li class="nav-item">
+            <a class="nav-link" href="#">Features</a>
+            </li>
+            <li class="nav-item">
+            <a class="nav-link" href="#">Pricing</a>
+            </li>
+            <li class="nav-item">
+            <a class="nav-link disabled" aria-disabled="true">Disabled</a>
+            </li>
+        </ul>
+        </div>
+    </div>
+    </nav>-->
+        <div class="wrapper d-flex flex-column min-vh-100">
+      <header class="header header-sticky p-0 mb-4">
+        <div class="container-fluid border-bottom px-4">
+          <button class="header-toggler" type="button" onclick="coreui.Sidebar.getInstance(document.querySelector('#sidebar')).toggle()" style="margin-inline-start: -14px;">
+            <svg class="icon icon-lg">
+              <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-menu"></use>
+            </svg>
+          </button>
+          <ul class="header-nav d-none d-lg-flex">
+            <li class="nav-item"><a class="nav-link" href="#">Dashboard</a></li>
+            <li class="nav-item"><a class="nav-link" href="#">Users</a></li>
+            <li class="nav-item"><a class="nav-link" href="#">Settings</a></li>
+          </ul>
+          <ul class="header-nav ms-auto">
+            <li class="nav-item"><a class="nav-link" href="#">
+                <svg class="icon icon-lg">
+                  <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-bell"></use>
+                </svg></a></li>
+            <li class="nav-item"><a class="nav-link" href="#">
+                <svg class="icon icon-lg">
+                  <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-list-rich"></use>
+                </svg></a></li>
+            <li class="nav-item"><a class="nav-link" href="#">
+                <svg class="icon icon-lg">
+                  <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-envelope-open"></use>
+                </svg></a></li>
+          </ul>
+          <ul class="header-nav">
+            <li class="nav-item py-1">
+              <div class="vr h-100 mx-2 text-body text-opacity-75"></div>
+            </li>
+            <li class="nav-item dropdown">
+              <button class="btn btn-link nav-link py-2 px-2 d-flex align-items-center" type="button" aria-expanded="false" data-coreui-toggle="dropdown">
+                <svg class="icon icon-lg theme-icon-active">
+                  <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-contrast"></use>
+                </svg>
+              </button>
+              <ul class="dropdown-menu dropdown-menu-end" style="--cui-dropdown-min-width: 8rem;">
+                <li>
+                  <button class="dropdown-item d-flex align-items-center" type="button" data-coreui-theme-value="light">
+                    <svg class="icon icon-lg me-3">
+                      <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-sun"></use>
+                    </svg>Light
+                  </button>
+                </li>
+                <li>
+                  <button class="dropdown-item d-flex align-items-center" type="button" data-coreui-theme-value="dark">
+                    <svg class="icon icon-lg me-3">
+                      <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-moon"></use>
+                    </svg>Dark
+                  </button>
+                </li>
+                <li>
+                  <button class="dropdown-item d-flex align-items-center active" type="button" data-coreui-theme-value="auto">
+                    <svg class="icon icon-lg me-3">
+                      <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-contrast"></use>
+                    </svg>Auto
+                  </button>
+                </li>
+              </ul>
+            </li>
+            <li class="nav-item py-1">
+              <div class="vr h-100 mx-2 text-body text-opacity-75"></div>
+            </li>
+            <li class="nav-item dropdown"><a class="nav-link py-0 pe-0" data-coreui-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
+                <div class="avatar avatar-md"><img class="avatar-img" src="assets/img/avatars/8.jpg" alt="user@email.com"></div>
+              </a>
+              <div class="dropdown-menu dropdown-menu-end pt-0">
+                <div class="dropdown-header bg-body-tertiary text-body-secondary fw-semibold rounded-top mb-2">Account</div><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-bell"></use>
+                  </svg> Updates<span class="badge badge-sm bg-info ms-2">42</span></a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-envelope-open"></use>
+                  </svg> Messages<span class="badge badge-sm bg-success ms-2">42</span></a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-task"></use>
+                  </svg> Tasks<span class="badge badge-sm bg-danger ms-2">42</span></a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-comment-square"></use>
+                  </svg> Comments<span class="badge badge-sm bg-warning ms-2">42</span></a>
+                <div class="dropdown-header bg-body-tertiary text-body-secondary fw-semibold my-2">
+                  <div class="fw-semibold">Settings</div>
+                </div><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-user"></use>
+                  </svg> Profile</a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-settings"></use>
+                  </svg> Settings</a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-credit-card"></use>
+                  </svg> Payments<span class="badge badge-sm bg-secondary ms-2">42</span></a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-file"></use>
+                  </svg> Projects<span class="badge badge-sm bg-primary ms-2">42</span></a>
+                <div class="dropdown-divider"></div><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-lock-locked"></use>
+                  </svg> Lock Account</a><a class="dropdown-item" href="#">
+                  <svg class="icon me-2">
+                    <use xlink:href="vendors/@coreui/icons/svg/free.svg#cil-account-logout"></use>
+                  </svg> Logout</a>
+              </div>
+            </li>
+          </ul>
+        </div>
+    <div class="container-fluid px-4">
       {% block content %}{% endblock %}
-    </main>
+    </div>
   </body>
 </html>

+ 1172 - 0
templates/chat.html

@@ -0,0 +1,1172 @@
+<html>
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Chat Forderung</title>
+    <style>
+        :root {
+            --primary: #5468FF;
+            --primary-light: #8590FF;
+            --primary-dark: #3A4DB2;
+            --student: #E9EFFF;
+            --teacher: #EBFAEF;
+            --teacher-accent: #4CAF50;
+            --text-dark: #1A2138;
+            --text-light: #5F6A8A;
+            --background: #F9FAFC;
+            --panel: #FFFFFF;
+            --border: #E1E5EE;
+            --shadow: 0 4px 12px rgba(84, 104, 255, 0.10);
+            --hover-shadow: 0 6px 16px rgba(84, 104, 255, 0.18);
+            --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+        }
+
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+        }
+
+        body {
+            background-color: var(--background);
+            color: var(--text-dark);
+            width: 100%;
+            height: 100vh;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            overflow: hidden;
+        }
+
+        .chat-container {
+            width: 700px;
+            height: 700px;
+            display: flex;
+            flex-direction: column;
+            border-radius: 16px;
+            background-color: var(--panel);
+            box-shadow: var(--shadow);
+            overflow: hidden;
+            position: relative;
+        }
+
+        .header {
+            padding: 16px 24px;
+            background-color: var(--panel);
+            border-bottom: 1px solid var(--border);
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+            position: relative;
+            z-index: 10;
+        }
+
+        .header-left {
+            display: flex;
+            align-items: center;
+            gap: 12px;
+        }
+
+        .course-info {
+            display: flex;
+            flex-direction: column;
+        }
+
+        .course-title {
+            font-weight: 600;
+            font-size: 16px;
+            color: var(--text-dark);
+        }
+
+        .course-participants {
+            font-size: 12px;
+            color: var(--text-light);
+        }
+
+        .header-actions {
+            display: flex;
+            gap: 12px;
+        }
+
+        .icon-button {
+            width: 36px;
+            height: 36px;
+            border-radius: 50%;
+            border: none;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            background-color: var(--background);
+            color: var(--text-light);
+            cursor: pointer;
+            transition: var(--transition);
+        }
+
+        .icon-button:hover {
+            background-color: var(--primary-light);
+            color: white;
+            transform: translateY(-2px);
+            box-shadow: var(--hover-shadow);
+        }
+
+        .icon-button svg {
+            width: 18px;
+            height: 18px;
+        }
+
+        .tabs {
+            display: flex;
+            padding: 0 20px;
+            background-color: var(--panel);
+            border-bottom: 1px solid var(--border);
+            position: relative;
+            z-index: 5;
+        }
+
+        .tab {
+            padding: 12px 16px;
+            font-size: 14px;
+            font-weight: 500;
+            color: var(--text-light);
+            cursor: pointer;
+            border-bottom: 2px solid transparent;
+            transition: var(--transition);
+            position: relative;
+        }
+
+        .tab.active {
+            color: var(--primary);
+            border-bottom: 2px solid var(--primary);
+        }
+
+        .tab:hover:not(.active) {
+            color: var(--text-dark);
+        }
+
+        .badge {
+            position: absolute;
+            top: 8px;
+            right: 8px;
+            background-color: var(--primary);
+            color: white;
+            font-size: 10px;
+            font-weight: 500;
+            padding: 1px 6px;
+            border-radius: 10px;
+        }
+
+        .chat-content {
+            flex: 1;
+            overflow-y: auto;
+            padding: 24px;
+            display: flex;
+            flex-direction: column;
+            gap: 16px;
+            scroll-behavior: smooth;
+        }
+
+        .chat-content::-webkit-scrollbar {
+            width: 6px;
+        }
+
+        .chat-content::-webkit-scrollbar-track {
+            background: transparent;
+        }
+
+        .chat-content::-webkit-scrollbar-thumb {
+            background-color: var(--border);
+            border-radius: 6px;
+        }
+
+        .message {
+            display: flex;
+            flex-direction: column;
+            max-width: 80%;
+            animation: fadeIn 0.3s ease-out;
+        }
+
+        @keyframes fadeIn {
+            from {
+                opacity: 0;
+                transform: translateY(10px);
+            }
+            to {
+                opacity: 1;
+                transform: translateY(0);
+            }
+        }
+
+        .message.student {
+            align-self: flex-end;
+        }
+
+        .message.teacher {
+            align-self: flex-start;
+        }
+
+        .message-header {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            margin-bottom: 4px;
+        }
+
+        .avatar {
+            width: 28px;
+            height: 28px;
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-weight: 600;
+            font-size: 12px;
+            color: white;
+            flex-shrink: 0;
+        }
+
+        .student .avatar {
+            background-color: var(--primary);
+        }
+
+        .teacher .avatar {
+            background-color: var(--teacher-accent);
+        }
+
+        .sender-name {
+            font-size: 13px;
+            font-weight: 600;
+        }
+
+        .student .sender-name {
+            color: var(--primary-dark);
+        }
+
+        .teacher .sender-name {
+            color: var(--teacher-accent);
+        }
+
+        .timestamp {
+            font-size: 11px;
+            color: var(--text-light);
+            margin-left: auto;
+        }
+
+        .message-bubble {
+            padding: 12px 16px;
+            border-radius: 16px;
+            font-size: 14px;
+            line-height: 1.5;
+            position: relative;
+            transition: var(--transition);
+        }
+
+        .student .message-bubble {
+            background-color: var(--student);
+            border-top-right-radius: 4px;
+        }
+
+        .teacher .message-bubble {
+            background-color: var(--teacher);
+            border-top-left-radius: 4px;
+        }
+
+        .message-bubble:hover {
+            transform: translateY(-2px);
+            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
+        }
+
+        .message-actions {
+            display: flex;
+            gap: 8px;
+            margin-top: 6px;
+            justify-content: flex-end;
+            opacity: 0;
+            transition: var(--transition);
+        }
+
+        .message:hover .message-actions {
+            opacity: 1;
+        }
+
+        .action-button {
+            background: none;
+            border: none;
+            font-size: 12px;
+            color: var(--text-light);
+            cursor: pointer;
+            padding: 2px 6px;
+            border-radius: 4px;
+            transition: var(--transition);
+        }
+
+        .action-button:hover {
+            background-color: rgba(84, 104, 255, 0.1);
+            color: var(--primary);
+        }
+
+        .embed-content {
+            margin-top: 10px;
+            border-radius: 8px;
+            overflow: hidden;
+            border: 1px solid var(--border);
+            background-color: white;
+            transition: var(--transition);
+        }
+
+        .embed-content:hover {
+            box-shadow: var(--shadow);
+            transform: translateY(-2px);
+        }
+
+        .embed-header {
+            padding: 10px 12px;
+            background-color: rgba(84, 104, 255, 0.05);
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            border-bottom: 1px solid var(--border);
+        }
+
+        .embed-icon {
+            width: 20px;
+            height: 20px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: var(--primary);
+        }
+
+        .embed-title {
+            font-size: 12px;
+            font-weight: 500;
+            flex: 1;
+        }
+
+        .embed-body {
+            padding: 12px;
+        }
+
+        .embed-image {
+            width: 100%;
+            border-radius: 6px;
+            height: auto;
+            object-fit: cover;
+        }
+
+        .embed-text {
+            font-size: 13px;
+            color: var(--text-light);
+            margin-top: 8px;
+            line-height: 1.4;
+        }
+
+        .embed-footer {
+            padding: 8px 12px;
+            border-top: 1px solid var(--border);
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+        }
+
+        .embed-button {
+            background-color: var(--primary-light);
+            color: white;
+            font-size: 12px;
+            font-weight: 500;
+            padding: 6px 12px;
+            border-radius: 6px;
+            border: none;
+            cursor: pointer;
+            transition: var(--transition);
+        }
+
+        .embed-button:hover {
+            background-color: var(--primary);
+            transform: translateY(-1px);
+            box-shadow: 0 3px 8px rgba(84, 104, 255, 0.2);
+        }
+
+        .date-divider {
+            display: flex;
+            align-items: center;
+            gap: 12px;
+            margin: 16px 0;
+            opacity: 0.7;
+        }
+
+        .divider-line {
+            flex: 1;
+            height: 1px;
+            background-color: var(--border);
+        }
+
+        .divider-text {
+            font-size: 12px;
+            color: var(--text-light);
+            white-space: nowrap;
+        }
+
+        .typing-indicator {
+            display: flex;
+            align-items: center;
+            gap: 4px;
+            font-size: 12px;
+            color: var(--text-light);
+            padding: 8px 12px;
+            margin-bottom: 8px;
+        }
+
+        .typing-dot {
+            width: 5px;
+            height: 5px;
+            background-color: var(--text-light);
+            border-radius: 50%;
+            animation: typingAnimation 1.4s infinite ease-in-out;
+        }
+
+        .typing-dot:nth-child(1) {
+            animation-delay: 0s;
+        }
+
+        .typing-dot:nth-child(2) {
+            animation-delay: 0.2s;
+        }
+
+        .typing-dot:nth-child(3) {
+            animation-delay: 0.4s;
+        }
+
+        @keyframes typingAnimation {
+            0%, 100% {
+                transform: translateY(0);
+                opacity: 0.5;
+            }
+            50% {
+                transform: translateY(-4px);
+                opacity: 1;
+            }
+        }
+
+        .input-area {
+            padding: 16px 24px 24px;
+            background-color: var(--panel);
+            border-top: 1px solid var(--border);
+            z-index: 10;
+        }
+
+        .toolbar {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            margin-bottom: 12px;
+        }
+
+        .tool-button {
+            background: none;
+            border: none;
+            cursor: pointer;
+            width: 32px;
+            height: 32px;
+            border-radius: 4px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: var(--text-light);
+            transition: var(--transition);
+        }
+
+        .tool-button:hover {
+            background-color: rgba(84, 104, 255, 0.1);
+            color: var(--primary);
+        }
+
+        .tool-button svg {
+            width: 18px;
+            height: 18px;
+        }
+
+        .input-container {
+            display: flex;
+            gap: 12px;
+            align-items: center;
+        }
+
+        .message-input {
+            flex: 1;
+            padding: 14px 16px;
+            border-radius: 10px;
+            border: 1px solid var(--border);
+            background-color: var(--background);
+            font-size: 14px;
+            resize: none;
+            transition: var(--transition);
+            height: 50px;
+            outline: none;
+        }
+
+        .message-input:focus {
+            border-color: var(--primary-light);
+            box-shadow: 0 0 0 2px rgba(84, 104, 255, 0.1);
+        }
+
+        .send-button {
+            background-color: var(--primary);
+            color: white;
+            border: none;
+            width: 50px;
+            height: 50px;
+            border-radius: 12px;
+            cursor: pointer;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            transition: var(--transition);
+        }
+
+        .send-button:hover {
+            background-color: var(--primary-dark);
+            transform: translateY(-2px);
+            box-shadow: 0 4px 12px rgba(84, 104, 255, 0.2);
+        }
+
+        .poll-container {
+            padding: 12px;
+            background-color: rgba(84, 104, 255, 0.05);
+            border-radius: 8px;
+            margin-top: 8px;
+        }
+
+        .poll-question {
+            font-weight: 500;
+            margin-bottom: 10px;
+            font-size: 13px;
+        }
+
+        .poll-options {
+            display: flex;
+            flex-direction: column;
+            gap: 8px;
+        }
+
+        .poll-option {
+            background-color: white;
+            border: 1px solid var(--border);
+            padding: 8px 12px;
+            border-radius: 6px;
+            font-size: 13px;
+            position: relative;
+            cursor: pointer;
+            transition: var(--transition);
+            display: flex;
+            justify-content: space-between;
+        }
+
+        .poll-option:hover {
+            border-color: var(--primary-light);
+            background-color: rgba(84, 104, 255, 0.03);
+        }
+
+        .poll-option.selected {
+            border-color: var(--primary);
+            background-color: rgba(84, 104, 255, 0.1);
+        }
+
+        .poll-option.selected::before {
+            content: '';
+            position: absolute;
+            left: 0;
+            top: 0;
+            height: 100%;
+            width: 3px;
+            background-color: var(--primary);
+            border-top-left-radius: 6px;
+            border-bottom-left-radius: 6px;
+        }
+
+        .poll-percent {
+            font-size: 12px;
+            font-weight: 500;
+            color: var(--primary);
+        }
+
+        .poll-votes {
+            margin-top: 10px;
+            font-size: 12px;
+            color: var(--text-light);
+            text-align: right;
+        }
+
+        .resource-link {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            padding: 10px;
+            background-color: var(--background);
+            border-radius: 6px;
+            cursor: pointer;
+            transition: var(--transition);
+            text-decoration: none;
+            color: var(--text-dark);
+            border: 1px solid var(--border);
+        }
+
+        .resource-link:hover {
+            background-color: rgba(84, 104, 255, 0.05);
+            border-color: var(--primary-light);
+            transform: translateY(-2px);
+            box-shadow: var(--shadow);
+        }
+
+        .resource-icon {
+            width: 36px;
+            height: 36px;
+            background-color: rgba(84, 104, 255, 0.1);
+            border-radius: 6px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: var(--primary);
+        }
+
+        .resource-info {
+            flex: 1;
+        }
+
+        .resource-title {
+            font-size: 13px;
+            font-weight: 500;
+        }
+
+        .resource-desc {
+            font-size: 11px;
+            color: var(--text-light);
+        }
+
+        /* Responsive Styles */
+        @media (max-width: 700px) {
+            .chat-container {
+                width: 100%;
+                height: 100%;
+                border-radius: 0;
+            }
+            
+            .message {
+                max-width: 90%;
+            }
+            
+            .header-actions {
+                gap: 8px;
+            }
+            
+            .icon-button {
+                width: 32px;
+                height: 32px;
+            }
+            
+            .tabs {
+                padding: 0 10px;
+            }
+            
+            .tab {
+                padding: 12px 10px;
+                font-size: 13px;
+            }
+        }
+
+        /* Animation for new messages */
+        @keyframes popIn {
+            0% {
+                transform: scale(0.8);
+                opacity: 0;
+            }
+            50% {
+                transform: scale(1.05);
+            }
+            100% {
+                transform: scale(1);
+                opacity: 1;
+            }
+        }
+
+        .pop-in {
+            animation: popIn 0.3s forwards;
+        }
+
+        /* Resource expansion animation */
+        .resource-expanded {
+            max-height: 0;
+            overflow: hidden;
+            transition: max-height 0.3s ease;
+        }
+
+        .resource-expanded.active {
+            max-height: 200px;
+        }
+
+        /* Tooltip Styles */
+        .tooltip {
+            position: relative;
+        }
+
+        .tooltip::after {
+            content: attr(data-tooltip);
+            position: absolute;
+            bottom: 130%;
+            left: 50%;
+            transform: translateX(-50%);
+            padding: 5px 10px;
+            border-radius: 4px;
+            background-color: rgba(26, 33, 56, 0.9);
+            color: white;
+            font-size: 11px;
+            white-space: nowrap;
+            opacity: 0;
+            visibility: hidden;
+            transition: all 0.2s ease;
+            pointer-events: none;
+            z-index: 100;
+        }
+
+        .tooltip::before {
+            content: '';
+            position: absolute;
+            bottom: 120%;
+            left: 50%;
+            transform: translateX(-50%);
+            border-width: 4px;
+            border-style: solid;
+            border-color: rgba(26, 33, 56, 0.9) transparent transparent transparent;
+            opacity: 0;
+            visibility: hidden;
+            transition: all 0.2s ease;
+            pointer-events: none;
+            z-index: 100;
+        }
+
+        .tooltip:hover::after,
+        .tooltip:hover::before {
+            opacity: 1;
+            visibility: visible;
+        }
+
+        /* Reaction Styles */
+        .reactions {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 4px;
+            margin-top: 8px;
+        }
+
+        .reaction {
+            font-size: 12px;
+            padding: 2px 6px 2px 4px;
+            background-color: rgba(84, 104, 255, 0.06);
+            border-radius: 12px;
+            display: flex;
+            align-items: center;
+            gap: 3px;
+            cursor: pointer;
+            transition: var(--transition);
+        }
+
+        .reaction:hover {
+            background-color: rgba(84, 104, 255, 0.15);
+        }
+
+        .reaction.active {
+            background-color: rgba(84, 104, 255, 0.2);
+            font-weight: 500;
+        }
+    </style>
+</head>
+<body>
+    <div class="chat-container">
+        <div class="header">
+            <div class="header-left">
+                <div class="avatar" style="background-color: #5468FF;">
+                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg>
+                </div>
+                <div class="course-info">
+                    <div class="course-title">993413 - Elvis Selimovic / WRG25400108</div>
+                    <div class="course-participants">4 Mitarbeiter involviert</div>
+                </div>
+            </div>
+            <div class="header-actions">
+                <button class="icon-button tooltip" data-tooltip="Search">
+                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
+                </button>
+                <button class="icon-button tooltip" data-tooltip="Resource Library">
+                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
+                </button>
+                <button class="icon-button tooltip" data-tooltip="Settings">
+                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
+                </button>
+            </div>
+        </div>
+        <div class="tabs">
+            <div class="tab active">Diese Rechnung</div>
+            <div class="tab">Alle offenen Rechnungen</div>
+            <div class="tab">Dieses Fahrzeug</div>
+            <div class="tab">Gesamte Historie</div>
+        </div>
+        <div class="chat-content" id="chatContent">
+            <div class="date-divider">
+                <div class="divider-line"></div>
+                <div class="divider-text">Q1/2025</div>
+                <div class="divider-line"></div>
+            </div>
+            
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">DKI</div>
+                    <div class="sender-name">Daniel Klingenstein</div>
+                    <div class="timestamp">04.01.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Korrigierte Werkstattrechnung
+                </div>
+                <div class="embed-content">
+                    <div class="embed-header">
+                        <div class="embed-icon">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line></svg>
+                        </div>
+                        <div class="embed-title">Rechnung WRG25400108</div>
+                    </div>
+                    <div class="embed-body">
+                        <img src="img.jpg" alt="Rechnung WRG25400108" class="embed-image">
+                        <div class="embed-text">
+                            ...
+                        </div>
+                    </div>
+                    <div class="embed-footer">
+                        <button class="embed-button" id="openResourceBtn">Öffnen</button>
+                    </div>
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">22.01.2025</div>
+                </div>
+                <div class="message-bubble">
+                    AZ 3120/24 HP Schaden Rep RE kann nicht eingereicht werden. ESW Zusatzprodukte fehlen
+                </div>
+            </div>
+            
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">30.01.2025</div>
+                </div>
+                <div class="message-bubble">
+                    AZ 3120/24 HP Schaden Rep RE an RAW gesendet
+                </div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">11.03.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="date-divider">
+                <div class="divider-line"></div>
+                <div class="divider-text">Q2/2025</div>
+                <div class="divider-line"></div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">01.04.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Akteneinsicht liegt nun vor, im Diktat
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">14.04.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">22.04.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Nachgefordert
+                </div>
+                <div class="embed-content">
+                    <div class="embed-header">
+                        <div class="embed-icon">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line></svg>
+                        </div>
+                        <div class="embed-title">Mahnung</div>
+                    </div>
+                    <div class="embed-body">
+                        <img src="img.jpg" alt="Mahnung 993413" class="embed-image">
+                        <div class="embed-text">
+                            ...
+                        </div>
+                    </div>
+                    <div class="embed-footer">
+                        <button class="embed-button" id="openResourceBtn">Öffnen</button>
+                    </div>
+                </div>
+            </div>
+            
+
+
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">09.05.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">03.06.2025</div>
+                </div>
+                <div class="message-bubble">
+                    RA schreibt KD an ob geklagt werden soll und RS besteht
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">23.06.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">23.06.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Akte im Diktat
+                </div>
+            </div>
+            <div class="date-divider">
+                <div class="divider-line"></div>
+                <div class="divider-text">Q3/2025</div>
+                <div class="divider-line"></div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">01.07.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Warten auf RÜ Polizei
+                </div>
+            </div>
+
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">15.07.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">22.07.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Warten auf Rückmeldung Mdt wg Rechtschutz
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">11.08.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">19.08.2025</div>
+                </div>
+                <div class="message-bubble">
+                    ST bez. Mit STN S+W wird nachgefordert 
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">08.09.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="date-divider">
+                <div class="divider-line"></div>
+                <div class="divider-text">Q4/2025</div>
+                <div class="divider-line"></div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">14.10.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Klage in Vorbereitung WV 18.11.2025
+                </div>
+            </div>
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">25.11.2025</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">MDE</div>
+                    <div class="sender-name">Maria Dietrich</div>
+                    <div class="timestamp">30.11.2025</div>
+                </div>
+                <div class="message-bubble">
+                    EWB
+                </div>
+                <div class="embed-content">
+                    <div class="embed-header">
+                        <div class="embed-icon">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line></svg>
+                        </div>
+                        <div class="embed-title">EWB 20% 11/2025</div>
+                    </div>
+                    <div class="embed-body">
+                        <img src="img.jpg" alt="Buchung" class="embed-image">
+                        <div class="embed-text">
+                            ...
+                        </div>
+                    </div>
+                    <div class="embed-footer">
+                        <button class="embed-button" id="openResourceBtn">Öffnen</button>
+                    </div>
+                </div>
+            </div>  
+
+            <div class="message student">
+                <div class="message-header">
+                    <div class="avatar">CVE</div>
+                    <div class="sender-name">Carolin Vetter</div>
+                    <div class="timestamp">04.12.2025</div>
+                </div>
+                <div class="message-bubble">
+                    gerichtliches Verfahren läuft WV 02/26
+                </div>
+            </div>
+
+            <div class="date-divider">
+                <div class="divider-line"></div>
+                <div class="divider-text">Q1/2026</div>
+                <div class="divider-line"></div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">19.02.2026</div>
+                </div>
+                <div class="message-bubble">
+                    Stand RA
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">09.03.2026</div>
+                </div>
+                <div class="message-bubble">
+                    Schreiben von RAW erhalten. Sehr verwirrend, nochmals nachgefragt
+                </div>
+            </div>
+
+            <div class="message teacher">
+                <div class="message-header">
+                    <div class="avatar">JAK</div>
+                    <div class="sender-name">Jennifer Walk</div>
+                    <div class="timestamp">10.03.2026</div>
+                </div>
+                <div class="message-bubble">
+                    Vergleich mit HP Versicherung nun KD angeschrieben ob bei seiner Kasko eingereicht wird oder nicht
+                </div>
+            </div>
+            
+        </div>
+        <div class="input-area">
+            <div class="toolbar">
+                <button class="tool-button tooltip" data-tooltip="Attach File">
+                </button>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

BIN
templates/docx/Mahnung_AHR.docx


+ 14 - 13
templates/login.html

@@ -1,16 +1,17 @@
-{% extends "base.html" %}
+{% extends 'base.html' %}
 {% block content %}
-<div class="col-md-6">
-  <form method="post" action="/login">
-    <div class="mb-3">
-      <label class="form-label">Benutzer</label>
-      <input name="username" class="form-control" required>
-    </div>
-    <div class="mb-3">
-      <label class="form-label">Passwort</label>
-      <input type="password" name="password" class="form-control" required>
-    </div>
-    <button class="btn btn-primary" type="submit">Login</button>
-  </form>
+<div class="c-login">
+    <h1>Login</h1>
+    <form method="post" action="/login">
+        <div class="form-group">
+            <label for="username">Username</label>
+            <input type="text" class="form-control" id="username" name="username" required>
+        </div>
+        <div class="form-group">
+            <label for="password">Password</label>
+            <input type="password" class="form-control" id="password" name="password" required>
+        </div>
+        <button type="submit" class="btn btn-primary">Login</button>
+    </form>
 </div>
 {% endblock %}

ファイルの差分が大きいため隠しています
+ 423 - 406
uv.lock


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません