vcard4reseller/backend/templates/public/profile.html.twig
Thomas Peterson fa321fb6a5 Profil + vCard mit neuen Mitarbeiterfeldern; Modal breiter
- 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>
2026-06-09 19:33:46 +02:00

225 lines
11 KiB
Twig
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.

{% 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&nbsp;/&nbsp;Google&nbsp;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 %}