Files
icloud-contacts-sync/src/api/templates/contact.html
T
2026-08-10 23:20:42 +02:00

506 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de) -->
<!-- Licensed under the MIT License. See LICENSE file in project root for details. -->
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<title>{{ contact.full_name or 'Kontakt' }} Kontakte</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f5f5f5;
color: #333;
line-height: 1.6;
overflow-y: scroll;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem 1rem;
}
h1 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: #222;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: #0066cc;
text-decoration: none;
font-size: 0.875rem;
margin-bottom: 1.5rem;
}
.back-link:hover {
text-decoration: underline;
}
.contact-card {
background: #fff;
border-radius: 8px;
padding: 1rem 1.25rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.custom-links-box {
position: fixed;
top: 136px;
right: 2rem;
width: 380px;
background: #fff;
border-radius: 8px;
padding: 1rem 1.25rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
max-height: calc(100vh - 4rem);
overflow-y: auto;
}
.quicklink-item {
display: block;
padding: 0.25rem 0.6rem;
background: #e7f3ff;
color: #0066cc;
border-radius: 20px;
text-decoration: none;
font-size: 0.8rem;
transition: background 0.15s ease;
}
.quicklink-item:hover {
background: #d0e8ff;
text-decoration: underline;
}
.contact-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1rem;
margin-bottom: 1.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid #eee;
}
.contact-photo {
width: 160px;
height: 160px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.contact-name {
font-size: 1.5rem;
font-weight: 600;
color: #222;
}
.contact-subtitle {
font-size: 0.875rem;
color: #666;
margin-top: 0.25rem;
}
.badge {
display: inline-block;
padding: 0.1rem 0.4rem;
border-radius: 3px;
font-size: 0.6rem;
font-weight: 400;
text-transform: none;
letter-spacing: 0;
white-space: nowrap;
color: #888;
background: #f0f0f0;
}
.section {
margin-bottom: 1.5rem;
}
.section:last-child {
margin-bottom: 0;
}
.section-title {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #888;
margin-bottom: 0.75rem;
}
.section-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.section-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem;
background: #f8f9fa;
border-radius: 6px;
}
.section-item-label {
font-size: 0.75rem;
color: #888;
text-transform: capitalize;
}
.section-item-value {
font-size: 0.875rem;
color: #333;
}
.section-item-value a {
color: #0066cc;
text-decoration: none;
}
.section-item-value a:hover {
text-decoration: underline;
}
.tiles-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
}
.tile {
padding: 0.75rem;
background: #f8f9fa;
border-radius: 6px;
}
.tile-label {
font-size: 0.7rem;
color: #888;
text-transform: capitalize;
margin-bottom: 0.25rem;
line-height: 1.3;
}
.tile-value {
font-size: 0.875rem;
color: #333;
word-break: break-word;
}
.maps-link {
display: inline-block;
vertical-align: middle;
margin-left: 0.3rem;
color: #0066cc;
opacity: 0.6;
transition: opacity 0.15s ease;
}
.maps-link:hover {
opacity: 1;
}
.tile-value a {
color: #0066cc;
text-decoration: none;
}
.tile-value a:hover {
text-decoration: underline;
}
.birthday-highlight {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
background: #fff3cd;
border-radius: 6px;
font-size: 0.875rem;
color: #856404;
}
.notes-content {
padding: 0.75rem;
background: #f8f9fa;
border-radius: 6px;
font-size: 0.875rem;
color: #333;
white-space: pre-wrap;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tag {
display: inline-block;
padding: 0.25rem 0.6rem;
background: #e7f3ff;
color: #0066cc;
border-radius: 20px;
font-size: 0.8rem;
text-decoration: none;
transition: background 0.15s ease;
}
.tag:hover {
background: #d0e8ff;
text-decoration: underline;
}
.no-data {
color: #888;
font-style: italic;
font-size: 0.875rem;
}
.photo-dialog {
border: none;
border-radius: 12px;
padding: 10px;
max-width: 90vw;
max-height: 90vh;
background: transparent;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.photo-dialog::backdrop {
background: rgba(0, 0, 0, 0.85);
}
.photo-dialog img {
display: block;
max-width: 90vw;
max-height: 85vh;
border-radius: 8px;
object-fit: contain;
}
.contact-photo {
cursor: pointer;
transition: transform 0.15s ease;
}
.contact-photo:hover {
transform: scale(1.03);
}
@media (max-width: 600px) {
.tiles-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 1450px) {
.custom-links-box {
display: none;
}
}
</style>
</head>
<body>
<div class="container">
<h1><a href="/" style="text-decoration:none;color:inherit;display:inline-flex;align-items:center;gap:0.4rem"><img src="/static/favicon.svg" alt="" style="height:1.5em;width:1.5em;vertical-align:middle">Kontakte</a></h1>
<a href="/search{% if search %}?search={{ search }}{% endif %}" class="back-link">← Zurück zur Liste</a>
<div class="contact-card">
<div class="contact-header">
<div>
<div class="contact-name">{{ contact.full_name or '(Kein Name)' }}</div>
{% if contact.organization or contact.job_title %}
<div class="contact-subtitle">
{% if contact.job_title %}{{ contact.job_title }}{% endif %}
{% if contact.job_title and contact.organization %} bei {% endif %}
{% if contact.organization %}{{ contact.organization }}{% endif %}
</div>
{% endif %}
</div>
<div style="display:flex;flex-direction:column;align-items:center;gap:0.3rem">
{% if contact.photo_url %}
<img src="{{ contact.photo_url }}" alt="Foto" class="contact-photo" onerror="this.style.display='none'" onclick="document.getElementById('photo-dialog').showModal()">
{% endif %}
{% if show_all and contact.account %}
<span class="badge">Account: {{ contact.account }}</span>
{% endif %}
</div>
</div>
{% if contact.birthday %}
<div class="section">
<div class="birthday-highlight">
🎂 {{ contact.birthday|fmt_birthday }}
</div>
</div>
{% endif %}
{% if contact.emails %}
<div class="section">
<div class="section-title">E-Mail</div>
<div class="tiles-grid">
{% for email in contact.emails %}
<div class="tile">
<div class="tile-label">{{ email.type }}</div>
<div class="tile-value"><a href="mailto:{{ email.value }}">{{ email.value }}</a></div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.phones %}
<div class="section">
<div class="section-title">Telefon</div>
<div class="tiles-grid">
{% for phone in contact.phones %}
<div class="tile">
<div class="tile-label">{{ phone.type }}</div>
<div class="tile-value"><a href="tel:{{ phone.value }}">{{ phone.value }}</a></div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.addresses %}
<div class="section">
<div class="section-title">Adresse</div>
<div class="tiles-grid">
{% for addr in contact.addresses %}
<div class="tile">
<div class="tile-label">{{ addr.type }}</div>
<div class="tile-value">
{{ addr.street }}<br>
{% if addr.zip %}{{ addr.zip }} {% endif %}{{ addr.city }}<br>
{{ addr.region }}{% if addr.region and addr.country %}, {% endif %}{{ addr.country }}
{% if addr.street and addr.city %}
<a href="https://www.google.com/maps/search/?api=1&query={{ addr.street|urlquote }}, {% if addr.zip %}{{ addr.zip|urlquote }} {% endif %}{{ addr.city|urlquote }}" target="_blank" rel="noopener" class="maps-link" title="In Maps öffnen">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 0 1 0-5 2.5 2.5 0 0 1 0 5z"/></svg>
</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.urls %}
<div class="section">
<div class="section-title">Website</div>
<div class="tiles-grid">
{% for url in contact.urls %}
<div class="tile">
<div class="tile-label">{{ url.type }}</div>
<div class="tile-value"><a href="{{ url.value }}" target="_blank">{{ url.value }}</a></div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.social_profiles %}
<div class="section">
<div class="section-title">Soziale Profile</div>
<div class="tiles-grid">
{% for sp in contact.social_profiles %}
<div class="tile">
<div class="tile-label">{{ sp.type }}{% if sp.username %} ({{ sp.username }}){% endif %}</div>
<div class="tile-value"><a href="{{ sp.url }}" target="_blank">{{ sp.url }}</a></div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.related_names %}
<div class="section">
<div class="section-title">Beziehungen</div>
<div class="tiles-grid">
{% for rn in contact.related_names %}
<div class="tile">
<div class="tile-label">{{ rn.type }}</div>
<div class="tile-value">
{% if rn.id %}<a href="/contacts/{{ rn.id }}">{{ rn.name }}</a>{% else %}{{ rn.name }}{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.categories %}
<div class="section">
<div class="section-title">Kategorien</div>
<div class="tags">
{% for cat in contact.categories %}
<span class="tag">{{ cat }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% if groups %}
<div class="section">
<div class="section-title">Gruppen</div>
<div class="tags">
{% for g in groups %}
<a href="/search?group={{ g.uid|urlquote }}" class="tag">{{ g.name or '(Ohne Name)' }}</a>
{% endfor %}
</div>
</div>
{% endif %}
{% if contact.notes %}
<div class="section">
<div class="section-title">Notizen</div>
<div class="notes-content">{{ contact.notes }}</div>
</div>
{% endif %}
</div>
{% if custom_links %}
<div class="custom-links-box">
<div class="section-title">Schnelllinks</div>
<div class="section-list">
{% for link in custom_links %}
<a href="{{ link.url }}" target="_blank" class="quicklink-item">
{{ link.label }}
</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% if contact.photo_url %}
<dialog id="photo-dialog" class="photo-dialog" onclick="if(event.target===this)this.close()">
<img src="{{ contact.photo_url }}" alt="Foto" onerror="this.parentElement.close()">
</dialog>
{% endif %}
</body>
</html>