From ed9bf5f06242b9797d025ef287588a9dfa6fc509 Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Wed, 5 Aug 2026 20:24:09 +0200 Subject: [PATCH] web custom links --- config/accounts.json.example | 9 +- src/api/main.py | 10 ++ src/api/templates/contact.html | 287 ++++++++++++++++++++------------- src/config.py | 7 +- 4 files changed, 198 insertions(+), 115 deletions(-) diff --git a/config/accounts.json.example b/config/accounts.json.example index 4286559..8410a87 100644 --- a/config/accounts.json.example +++ b/config/accounts.json.example @@ -4,13 +4,18 @@ "name": "markus", "apple_email": "markus@icloud.com", "apple_app_password": "xxxx-xxxx-xxxx-xxxx", - "authelia_user": "mmustermann" + "authelia_user": "mmustermann", + "custom_links": [ + {"label": "Google Suche", "url": "https://www.google.com/search?q=[fullname]"}, + {"label": "LinkedIn", "url": "https://www.linkedin.com/search/results/all/?keywords=[fullname]"} + ] }, { "name": "partner", "apple_email": "partner@icloud.com", "apple_app_password": "yyyy-yyyy-yyyy-yyyy", - "authelia_user": "partner.user" + "authelia_user": "partner.user", + "custom_links": [] } ], "admins": [ diff --git a/src/api/main.py b/src/api/main.py index 139637e..2842cc4 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -354,6 +354,15 @@ def web_contact( contact = _row_to_contact_out(row) + custom_links = [] + contact_account = contact.get("account") + if contact_account: + accounts = Config.load_accounts() + for acc in accounts: + if acc.name == contact_account: + custom_links = acc.custom_links + break + return templates.TemplateResponse( "contact.html", { @@ -363,5 +372,6 @@ def web_contact( "show_all": show_all, "contact": contact, "search": search or "", + "custom_links": custom_links, }, ) diff --git a/src/api/templates/contact.html b/src/api/templates/contact.html index 8f8c8d0..28dec30 100644 --- a/src/api/templates/contact.html +++ b/src/api/templates/contact.html @@ -48,11 +48,48 @@ text-decoration: underline; } + .contact-layout { + display: flex; + gap: 1.5rem; + align-items: flex-start; + } + .contact-card { background: #fff; border-radius: 8px; padding: 1rem 1.25rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); + flex: 1; + min-width: 0; + } + + .custom-links-box { + width: 280px; + flex-shrink: 0; + background: #fff; + border-radius: 8px; + padding: 1rem 1.25rem; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + } + + .links-spacer { + flex: 1; + } + + .quicklink-item { + display: block; + padding: 0.75rem; + background: #f8f9fa; + border-radius: 6px; + color: #0066cc; + text-decoration: none; + font-size: 0.875rem; + transition: background 0.15s ease; + } + + .quicklink-item:hover { + background: #e7f3ff; + text-decoration: underline; } .contact-header { @@ -224,6 +261,17 @@ .contact-photo:hover { transform: scale(1.03); } + + @media (max-width: 1100px) { + .custom-links-box, + .links-spacer { + display: none; + } + + .contact-layout { + display: block; + } + } @@ -231,127 +279,144 @@

Kontakte

← Zurück zur Liste -
-
-
-
{{ 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 %} +
+ + +
+
+
+
{{ 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 contact.photo_url %} + Foto + {% endif %} + {% if show_all and contact.account %} + Account: {{ contact.account }} + {% endif %}
- {% endif %}
-
- {% if contact.photo_url %} - Foto - {% endif %} - {% if show_all and contact.account %} - Account: {{ 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.social_profiles %} +
+
Soziale Profile
+
+ {% for sp in contact.social_profiles %} +
+ {{ sp.type }}{% if sp.username %} ({{ sp.username }}){% endif %} + {{ sp.url }} +
+ {% endfor %} +
+
+ {% endif %} + + {% if contact.categories %} +
+
Kategorien
+
+ {% for cat in contact.categories %} + {{ cat }} + {% endfor %} +
+
+ {% endif %} + + {% if contact.notes %} +
+
Notizen
+
{{ contact.notes }}
+
+ {% endif %}
- {% if contact.birthday %} -
-
- 🎂 {{ contact.birthday }} -
-
- {% endif %} - - {% if contact.emails %} -
-
E-Mail
+ {% if custom_links %} + {% 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.social_profiles %} -
-
Soziale Profile
-
- {% for sp in contact.social_profiles %} -
- {{ sp.type }}{% if sp.username %} ({{ sp.username }}){% endif %} - {{ sp.url }} -
- {% 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/config.py b/src/config.py index fab57cf..989ac61 100644 --- a/src/config.py +++ b/src/config.py @@ -11,11 +11,13 @@ ICLOUD_BASE_URL = "https://contacts.icloud.com/" class Account: - def __init__(self, name: str, apple_email: str, apple_app_password: str, authelia_user: str | None): + def __init__(self, name: str, apple_email: str, apple_app_password: str, authelia_user: str | None, + custom_links: list[dict] | None = None): self.name = name self.apple_email = apple_email self.apple_app_password = apple_app_password self.authelia_user = authelia_user + self.custom_links = custom_links or [] class Config: @@ -95,7 +97,8 @@ class Config: if name in seen_names: raise RuntimeError(f"Account-Name '{name}' ist nicht eindeutig") seen_names.add(name) - accounts.append(Account(name, email, pwd, authelia_user)) + custom_links = entry.get("custom_links", []) + accounts.append(Account(name, email, pwd, authelia_user, custom_links)) return accounts @classmethod