From 7ff0ad55709afe070fb7ead9de42aae6f1c01524 Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Wed, 5 Aug 2026 02:07:05 +0200 Subject: [PATCH] web detail view --- src/api/main.py | 37 +++++ src/api/templates/contact.html | 289 +++++++++++++++++++++++++++++++++ src/api/templates/index.html | 13 +- 3 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 src/api/templates/contact.html diff --git a/src/api/main.py b/src/api/main.py index 37754a4..082c11e 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -186,3 +186,40 @@ def web_index( "search": search or "", }, ) + + +@app.get("/contacts/{contact_id}", response_class=HTMLResponse) +def web_contact( + request: Request, + contact_id: int, + current_user: str = Depends(get_current_user), +): + account_name, is_admin = resolve_account_for_user(current_user) + + with db.get_connection() as conn: + where_clause, params = _account_filter_clause(account_name) + id_clause = "AND id = %s" if where_clause else "WHERE id = %s" + with conn.cursor() as cur: + cur.execute( + f"""SELECT id, account, uid, full_name, given_name, family_name, organization, + job_title, birthday, notes, emails, phones, addresses, urls, categories, updated_at + FROM contacts {where_clause} {id_clause}""", + params + [contact_id], + ) + row = cur.fetchone() + + if not row: + from fastapi.responses import RedirectResponse + return RedirectResponse(url="/", status_code=303) + + contact = _row_to_contact_out(row) + + return templates.TemplateResponse( + "contact.html", + { + "request": request, + "current_user": current_user, + "is_admin": is_admin, + "contact": contact, + }, + ) diff --git a/src/api/templates/contact.html b/src/api/templates/contact.html new file mode 100644 index 0000000..ae19cb6 --- /dev/null +++ b/src/api/templates/contact.html @@ -0,0 +1,289 @@ + + + + + + {{ contact.full_name or 'Kontakt' }} – Kontakte + + + +
+ ← Zurück zur Übersicht + +
+
+
+
{{ contact.full_name or '(Kein Name)' }}
+ {% if contact.organization or contact.job_title %} +
+ {% if contact.job_title %}{{ contact.job_title }}{% endif %} + {% if contact.job_title and contact.organization %} bei {% endif %} + {% if contact.organization %}{{ contact.organization }}{% endif %} +
+ {% endif %} +
+ {% if is_admin and contact.account %} + + {% endif %} +
+ + {% if contact.birthday %} +
+
+ 🎂 {{ contact.birthday }} +
+
+ {% endif %} + + {% if contact.emails %} +
+
E-Mail
+
+ {% for email in contact.emails %} +
+ {{ email.type }} + {{ email.value }} +
+ {% endfor %} +
+
+ {% endif %} + + {% if contact.phones %} +
+
Telefon
+
+ {% for phone in contact.phones %} +
+ {{ phone.type }} + {{ phone.value }} +
+ {% endfor %} +
+
+ {% endif %} + + {% if contact.addresses %} +
+
Adresse
+
+ {% for addr in contact.addresses %} +
+ {{ addr.type }} + + {{ addr.street }}
+ {% if addr.zip %}{{ addr.zip }} {% endif %}{{ addr.city }}
+ {{ addr.region }}{% if addr.region and addr.country %}, {% endif %}{{ addr.country }} +
+
+ {% endfor %} +
+
+ {% endif %} + + {% if contact.urls %} +
+
Website
+
+ {% for url in contact.urls %} +
+ {{ url.type }} + {{ url.value }} +
+ {% endfor %} +
+
+ {% endif %} + + {% if contact.categories %} +
+
Kategorien
+
+ {% for cat in contact.categories %} + {{ cat }} + {% endfor %} +
+
+ {% endif %} + + {% if contact.notes %} +
+
Notizen
+
{{ contact.notes }}
+
+ {% endif %} +
+
+ + diff --git a/src/api/templates/index.html b/src/api/templates/index.html index f887886..181637e 100644 --- a/src/api/templates/index.html +++ b/src/api/templates/index.html @@ -90,10 +90,19 @@ } .contact-card { + display: block; background: #fff; border-radius: 8px; padding: 1rem 1.25rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); + text-decoration: none; + color: inherit; + transition: box-shadow 0.15s, transform 0.15s; + } + + .contact-card:hover { + box-shadow: 0 2px 8px rgba(0,0,0,0.15); + transform: translateY(-1px); } .contact-header { @@ -172,7 +181,7 @@
{% if contacts %} {% for c in contacts %} -