- VCardBuilder: Titel (N-Präfix), Abteilung (ORG), Privat-E-Mail, Mobil, Zentrale, Fax, Website, Geschäfts-/Privatadresse, Foto (PHOTO URI) - Profilseite: Foto über echte URL, Kontakt-Sektion (alle Nummern/Mails/Web), Adresse geschäftlich+privat, Branding via BrandingService (Reseller-Vererbung) - Mitarbeiter-Formular nutzt das breite Modal (kein Overflow mehr) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
225 lines
11 KiB
Twig
225 lines
11 KiB
Twig
{% extends 'base.html.twig' %}
|
||
|
||
{% set fullName = ((e.title ? e.title ~ ' ' : '') ~ e.firstName ~ ' ' ~ e.lastName)|trim %}
|
||
{% set reseller = e.company.reseller %}
|
||
|
||
{# White-Label-Branding (Firma → Reseller → Standard), aus dem BrandingService #}
|
||
{% set primary = branding.primaryColor %}
|
||
{% set primaryDark = branding.primaryColorDark %}
|
||
{% set logo = branding.logoUrl %}
|
||
|
||
{% macro addressBlock(a, label) %}
|
||
{% set s = a.street|default('') %}
|
||
{% set l2 = a.addressLine2|default('') %}
|
||
{% set zip = a.zip|default('') %}
|
||
{% set city = a.city|default('') %}
|
||
{% set st = a.state|default('') %}
|
||
{% set co = a.country|default('') %}
|
||
<div class="addr">
|
||
{% if label %}<span class="at">{{ label }}</span>{% endif %}
|
||
{% if s %}{{ s }} {{ a.houseNumber|default('') }}<br>{% endif %}
|
||
{% if l2 %}{{ l2 }}<br>{% endif %}
|
||
{% if zip or city %}{{ zip }} {{ city }}<br>{% endif %}
|
||
{% if st %}{{ st }}<br>{% endif %}
|
||
{% if co %}{{ co }}{% endif %}
|
||
</div>
|
||
{% endmacro %}
|
||
|
||
{% block title %}{{ fullName }} – {{ e.company.name }}{% endblock %}
|
||
|
||
{% block meta %}
|
||
<meta name="description" content="{{ (e.position ? e.position ~ ' · ' : '') ~ e.company.name }}">
|
||
<meta property="og:title" content="{{ fullName }}">
|
||
<meta property="og:description" content="{{ (e.position ? e.position ~ ' · ' : '') ~ e.company.name }}">
|
||
<meta property="og:type" content="profile">
|
||
{% endblock %}
|
||
|
||
{% block stylesheets %}
|
||
{% if primary %}
|
||
<style>
|
||
:root {
|
||
--psc-orange: {{ primary }};
|
||
--psc-orange-dark: {{ primaryDark }};
|
||
--psc-orange-soft: color-mix(in srgb, {{ primary }} 14%, white);
|
||
--psc-orange-soft-2: color-mix(in srgb, {{ primary }} 7%, white);
|
||
--psc-border: color-mix(in srgb, {{ primary }} 32%, white);
|
||
}
|
||
</style>
|
||
{% endif %}
|
||
<style>
|
||
.wrap { max-width: 480px; margin: 0 auto; padding: 1.5rem 1rem 3rem; }
|
||
.vc__logo { display: block; max-height: 38px; margin: 0 auto .4rem; }
|
||
.vc {
|
||
background: var(--white);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
overflow: hidden;
|
||
border: 1px solid #f0f0f0;
|
||
}
|
||
.vc__cover {
|
||
height: 120px;
|
||
background: linear-gradient(135deg, var(--psc-orange) 0%, var(--psc-orange-dark) 100%);
|
||
display: flex; align-items: center; justify-content: center;
|
||
}
|
||
.vc__cover-logo { max-height: 46px; max-width: 70%; filter: brightness(0) invert(1); opacity: .95; }
|
||
.vc__head { padding: 0 1.6rem 1.4rem; margin-top: -56px; text-align: center; }
|
||
.vc__avatar {
|
||
width: 112px; height: 112px; border-radius: 50%;
|
||
border: 5px solid var(--white);
|
||
background: var(--psc-orange-soft);
|
||
color: var(--psc-orange-dark);
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
font-size: 2.4rem; font-weight: 700; object-fit: cover;
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
.vc__name { font-size: 1.6rem; margin: .7rem 0 .15rem; }
|
||
.vc__role { color: var(--muted); font-size: 1rem; }
|
||
.vc__org {
|
||
display: inline-block; margin-top: .6rem;
|
||
background: var(--psc-orange-soft); color: var(--psc-orange-dark);
|
||
padding: .25rem .9rem; border-radius: 999px; font-weight: 600; font-size: .85rem;
|
||
}
|
||
.vc__actions { display: grid; gap: .6rem; padding: 0 1.6rem 1.4rem; }
|
||
.vc__row { display: flex; gap: .6rem; }
|
||
.vc__row .btn { flex: 1; justify-content: center; }
|
||
.btn-block { width: 100%; justify-content: center; }
|
||
|
||
.vc__section { padding: 1.2rem 1.6rem; border-top: 1px solid #f2f2f2; }
|
||
.vc__label { font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); font-weight: 700; margin-bottom: .7rem; }
|
||
.links { display: grid; gap: .5rem; }
|
||
.link {
|
||
display: flex; align-items: center; gap: .7rem;
|
||
padding: .7rem .9rem; border: 1px solid #eee; border-radius: var(--radius-sm);
|
||
color: var(--text); font-weight: 600; font-size: .92rem;
|
||
}
|
||
.link:hover { border-color: var(--psc-border); background: var(--psc-orange-soft-2); text-decoration: none; }
|
||
.link .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--psc-orange); }
|
||
.bio { color: var(--text); font-size: .95rem; }
|
||
|
||
.contact { display: grid; gap: .45rem; }
|
||
.crow {
|
||
display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
|
||
padding: .6rem .9rem; border: 1px solid #eee; border-radius: var(--radius-sm);
|
||
color: var(--text);
|
||
}
|
||
.crow:hover { border-color: var(--psc-border); background: var(--psc-orange-soft-2); text-decoration: none; }
|
||
.crow .ck { color: var(--muted); font-size: .82rem; font-weight: 600; white-space: nowrap; }
|
||
.crow .cv { font-weight: 600; font-size: .92rem; text-align: right; word-break: break-word; }
|
||
.addr { font-size: .95rem; line-height: 1.55; color: var(--text); }
|
||
.addr + .addr { margin-top: .9rem; }
|
||
.addr .at { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 700; margin-bottom: .25rem; }
|
||
|
||
.qr { text-align: center; }
|
||
.qr img { width: 190px; height: 190px; border-radius: var(--radius-sm); border: 1px solid #eee; }
|
||
.qr p { color: var(--muted); font-size: .82rem; margin: .6rem 0 0; }
|
||
|
||
.foot { text-align: center; margin-top: 1.6rem; color: var(--muted); font-size: .8rem; }
|
||
.foot .brand-logo { font-size: .95rem; }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block body %}
|
||
<div class="wrap">
|
||
<div class="vc">
|
||
<div class="vc__cover">
|
||
{% if logo %}<img class="vc__cover-logo" src="{{ logo }}" alt="{{ e.company.name }}">{% endif %}
|
||
</div>
|
||
<div class="vc__head">
|
||
{% if e.photoPath %}
|
||
<img class="vc__avatar" src="{{ path('employee_photo_public', {id: e.id}) }}" alt="{{ fullName }}">
|
||
{% else %}
|
||
<div class="vc__avatar">{{ (e.firstName|first ~ e.lastName|first)|upper }}</div>
|
||
{% endif %}
|
||
<h1 class="vc__name">{{ fullName }}</h1>
|
||
{% if e.position or e.department %}
|
||
<div class="vc__role">{{ e.position }}{% if e.position and e.department %} · {% endif %}{{ e.department }}</div>
|
||
{% endif %}
|
||
<div class="vc__org">{{ e.company.name }}</div>
|
||
</div>
|
||
|
||
<div class="vc__actions">
|
||
<a class="btn btn-primary btn-block" href="{{ path('public_profile_vcard', {companySlug: e.company.slug, slug: e.slug}) }}">
|
||
⬇ Kontakt speichern (vCard)
|
||
</a>
|
||
<div class="vc__row">
|
||
{% if e.phone %}<a class="btn btn-soft" href="tel:{{ e.phone }}">Anrufen</a>{% endif %}
|
||
{% if e.mobile %}<a class="btn btn-soft" href="tel:{{ e.mobile }}">Mobil</a>{% endif %}
|
||
{% if e.email %}<a class="btn btn-soft" href="mailto:{{ e.email }}">E-Mail</a>{% endif %}
|
||
{% if e.website %}<a class="btn btn-soft" href="{{ e.website }}" target="_blank" rel="noopener">Web</a>{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
{% if e.bio %}
|
||
<div class="vc__section">
|
||
<div class="vc__label">Über mich</div>
|
||
<p class="bio">{{ e.bio }}</p>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% set hasContact = e.phone or e.mobile or e.phoneCentral or e.fax or e.email or e.emailPrivate or e.website %}
|
||
{% if hasContact %}
|
||
<div class="vc__section">
|
||
<div class="vc__label">Kontakt</div>
|
||
<div class="contact">
|
||
{% if e.phone %}<a class="crow" href="tel:{{ e.phone }}"><span class="ck">Telefon</span><span class="cv">{{ e.phone }}</span></a>{% endif %}
|
||
{% if e.mobile %}<a class="crow" href="tel:{{ e.mobile }}"><span class="ck">Mobil</span><span class="cv">{{ e.mobile }}</span></a>{% endif %}
|
||
{% if e.phoneCentral %}<a class="crow" href="tel:{{ e.phoneCentral }}"><span class="ck">Zentrale</span><span class="cv">{{ e.phoneCentral }}</span></a>{% endif %}
|
||
{% if e.fax %}<div class="crow"><span class="ck">Fax</span><span class="cv">{{ e.fax }}</span></div>{% endif %}
|
||
{% if e.email %}<a class="crow" href="mailto:{{ e.email }}"><span class="ck">E-Mail</span><span class="cv">{{ e.email }}</span></a>{% endif %}
|
||
{% if e.emailPrivate %}<a class="crow" href="mailto:{{ e.emailPrivate }}"><span class="ck">E-Mail privat</span><span class="cv">{{ e.emailPrivate }}</span></a>{% endif %}
|
||
{% if e.website %}<a class="crow" href="{{ e.website }}" target="_blank" rel="noopener"><span class="ck">Website</span><span class="cv">{{ e.website }}</span></a>{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% set ab = e.addressBusiness %}
|
||
{% set ap = e.addressPrivate %}
|
||
{% if ab or ap %}
|
||
<div class="vc__section">
|
||
<div class="vc__label">Adresse</div>
|
||
{% if ab %}
|
||
{{ _self.addressBlock(ab, ap ? 'Geschäftlich' : null) }}
|
||
{% endif %}
|
||
{% if ap %}
|
||
{{ _self.addressBlock(ap, 'Privat') }}
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if e.contactLinks|length %}
|
||
<div class="vc__section">
|
||
<div class="vc__label">Links</div>
|
||
<div class="links">
|
||
{% for link in e.contactLinks %}
|
||
<a class="link" href="{{ link.url }}" target="_blank" rel="noopener">
|
||
<span class="dot"></span>{{ link.label ?: link.type|capitalize }}
|
||
</a>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="vc__section qr">
|
||
<div class="vc__label">Profil teilen</div>
|
||
<img src="{{ path('public_profile_qr', {companySlug: e.company.slug, slug: e.slug}) }}" alt="QR-Code zum Profil">
|
||
<p>QR-Code scannen, um dieses Profil zu öffnen</p>
|
||
</div>
|
||
|
||
{% if walletEnabled %}
|
||
<div class="vc__section qr">
|
||
<div class="vc__label">Zur Wallet hinzufügen</div>
|
||
<a href="{{ path('wallet_landing', {code: e.shortCode}) }}">
|
||
<img src="{{ path('wallet_qr', {code: e.shortCode}) }}" alt="QR-Code: Karte zu Apple/Google Wallet hinzufügen">
|
||
</a>
|
||
<p>Scannen, um die Karte in Apple / Google Wallet zu speichern</p>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="foot">
|
||
bereitgestellt über
|
||
<span class="brand-logo">{{ reseller ? reseller.name : 'vcard4' }}<span class="tag">reseller</span></span>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|