2026-04-22 15:22:26 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
use MintyPHP\I18n;
|
|
|
|
|
|
|
|
|
|
$values = $user ?? [];
|
2026-04-22 15:49:10 +02:00
|
|
|
$profileKey = trim((string) ($profileKey ?? 'user-profile'));
|
|
|
|
|
if ($profileKey === '') {
|
|
|
|
|
$profileKey = 'user-profile';
|
|
|
|
|
}
|
2026-04-22 15:22:26 +02:00
|
|
|
$name = trim(($values['first_name'] ?? '') . ' ' . ($values['last_name'] ?? ''));
|
|
|
|
|
$displayName = $name !== '' ? $name : ($values['email'] ?? t('Address book'));
|
|
|
|
|
$email = trim((string) ($values['email'] ?? ''));
|
|
|
|
|
$phone = trim((string) ($values['phone'] ?? ''));
|
|
|
|
|
$mobile = trim((string) ($values['mobile'] ?? ''));
|
|
|
|
|
$shortDial = trim((string) ($values['short_dial'] ?? ''));
|
|
|
|
|
$address = trim((string) ($values['address'] ?? ''));
|
|
|
|
|
$postalCode = trim((string) ($values['postal_code'] ?? ''));
|
|
|
|
|
$city = trim((string) ($values['city'] ?? ''));
|
|
|
|
|
$region = trim((string) ($values['region'] ?? ''));
|
|
|
|
|
$country = trim((string) ($values['country'] ?? ''));
|
|
|
|
|
$profileDescription = trim((string) ($values['profile_description'] ?? ''));
|
|
|
|
|
$jobTitle = trim((string) ($values['job_title'] ?? ''));
|
|
|
|
|
$hireDate = trim((string) ($values['hire_date'] ?? ''));
|
|
|
|
|
$hireDateLabel = '';
|
|
|
|
|
if ($hireDate !== '') {
|
|
|
|
|
$format = (strpos((string) (I18n::$locale ?? ''), 'de') === 0) ? 'd.m.Y' : 'Y-m-d';
|
|
|
|
|
try {
|
|
|
|
|
$hireDateLabel = (new \DateTimeImmutable($hireDate))->format($format);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$hireDateLabel = $hireDate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$avatarUuid = (string) ($values['uuid'] ?? '');
|
|
|
|
|
$hasAvatar = !empty($values['has_avatar']) && $avatarUuid !== '';
|
|
|
|
|
|
|
|
|
|
$initials = '';
|
|
|
|
|
if ($displayName !== '') {
|
|
|
|
|
$parts = array_filter(array_map('trim', preg_split('/\s+/', $displayName) ?: []));
|
|
|
|
|
$chars = '';
|
|
|
|
|
foreach ($parts as $part) {
|
|
|
|
|
if (function_exists('mb_substr')) {
|
|
|
|
|
$chars .= mb_substr($part, 0, 1);
|
|
|
|
|
} else {
|
|
|
|
|
$chars .= substr($part, 0, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$initials = strtoupper($chars !== '' ? $chars : '?');
|
|
|
|
|
}
|
|
|
|
|
$tenantGroups = $values['tenant_groups'] ?? [];
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
$tenantGroups = is_array($tenantGroups) ? $tenantGroups : [];
|
|
|
|
|
$primaryTenantLabel = '';
|
|
|
|
|
$primaryTenantDepartments = [];
|
|
|
|
|
foreach ($tenantGroups as $group) {
|
|
|
|
|
if (!empty($group['is_primary'])) {
|
|
|
|
|
$primaryTenantLabel = trim((string) ($group['label'] ?? ''));
|
|
|
|
|
$primaryDepts = $group['departments'] ?? [];
|
|
|
|
|
$primaryTenantDepartments = is_array($primaryDepts) ? $primaryDepts : [];
|
|
|
|
|
break;
|
2026-04-22 15:22:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
$hasContact = ($email !== '' || $phone !== '' || $mobile !== '');
|
|
|
|
|
$hasAddress = ($address !== '' || $postalCode !== '' || $city !== '' || $region !== '' || $country !== '');
|
|
|
|
|
// Organization tab only appears for multi-tenant users — single-tenant info
|
|
|
|
|
// is already communicated through the header chips, and redundancy hurts more
|
|
|
|
|
// than a minor asymmetry.
|
|
|
|
|
$hasOrganization = count($tenantGroups) > 1;
|
|
|
|
|
$hasAbout = ($profileDescription !== '' || $hireDateLabel !== '' || $shortDial !== '');
|
|
|
|
|
$hasAnyTab = ($hasAddress || $hasAbout || $hasOrganization);
|
|
|
|
|
|
|
|
|
|
$copyEmailLabel = t('Copy email address');
|
|
|
|
|
$copyPhoneLabel = t('Copy phone number');
|
|
|
|
|
$copyMobileLabel = t('Copy mobile number');
|
|
|
|
|
|
2026-04-22 15:22:26 +02:00
|
|
|
?>
|
|
|
|
|
|
|
|
|
|
<div class="app-details-container">
|
|
|
|
|
<section>
|
|
|
|
|
<div class="app-profile-card-container">
|
|
|
|
|
<div class="app-profile-card">
|
|
|
|
|
<div class="app-profile-banner"></div>
|
|
|
|
|
<div class="app-profile-header">
|
|
|
|
|
<div class="user-avatar-block avatar-round app-profile-avatar">
|
|
|
|
|
<?php if ($hasAvatar): ?>
|
2026-04-22 15:49:10 +02:00
|
|
|
<a data-fslightbox="<?php e($profileKey); ?>-avatar"
|
2026-04-22 15:22:26 +02:00
|
|
|
href="admin/users/avatar-file?uuid=<?php e($avatarUuid); ?>&size=256">
|
|
|
|
|
<img class="user-avatar-image" src="admin/users/avatar-file?uuid=<?php e($avatarUuid); ?>&size=160"
|
|
|
|
|
alt="">
|
|
|
|
|
</a>
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
<span class="user-avatar-placeholder"><?php e($initials ?: '?'); ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
|
|
|
|
|
<div class="app-profile-identity">
|
|
|
|
|
<hgroup class="app-profile-identity-name">
|
2026-04-22 15:22:26 +02:00
|
|
|
<h2><?php e($displayName); ?></h2>
|
|
|
|
|
<?php if ($jobTitle !== ''): ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<p><?php e($jobTitle); ?></p>
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php endif; ?>
|
|
|
|
|
</hgroup>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
|
|
|
|
|
<?php if ($primaryTenantLabel !== '' || !empty($primaryTenantDepartments) || $hireDateLabel !== ''): ?>
|
|
|
|
|
<div class="app-profile-chips" aria-label="<?php e(t('Summary')); ?>">
|
|
|
|
|
<?php if ($primaryTenantLabel !== ''): ?>
|
|
|
|
|
<span class="badge" data-variant="neutral">
|
|
|
|
|
<i class="bi bi-building" aria-hidden="true"></i>
|
|
|
|
|
<?php e($primaryTenantLabel); ?>
|
|
|
|
|
<small>· <?php e(t('Primary')); ?></small>
|
|
|
|
|
</span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php foreach (array_slice($primaryTenantDepartments, 0, 3) as $dept): ?>
|
|
|
|
|
<span class="badge" data-variant="neutral">
|
|
|
|
|
<i class="bi bi-tag" aria-hidden="true"></i>
|
|
|
|
|
<?php e($dept); ?>
|
|
|
|
|
</span>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
<?php if ($hireDateLabel !== ''): ?>
|
|
|
|
|
<span class="badge" data-variant="neutral">
|
|
|
|
|
<i class="bi bi-calendar-event" aria-hidden="true"></i>
|
|
|
|
|
<?php e(t('Hired')); ?> <?php e($hireDateLabel); ?>
|
|
|
|
|
</span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
2026-04-22 15:22:26 +02:00
|
|
|
</div>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
|
|
|
|
|
<?php if ($hasContact): ?>
|
|
|
|
|
<div class="app-profile-actions">
|
|
|
|
|
<?php if ($email !== ''): ?>
|
|
|
|
|
<a role="button" class="secondary outline small app-profile-action-button"
|
|
|
|
|
href="mailto:<?php e($email); ?>">
|
|
|
|
|
<i class="bi bi-envelope" aria-hidden="true"></i>
|
|
|
|
|
<span><?php e(t('Send email')); ?></span>
|
|
|
|
|
</a>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if ($phone !== '' || $mobile !== ''): ?>
|
|
|
|
|
<?php $callTarget = $phone !== '' ? $phone : $mobile; ?>
|
|
|
|
|
<a role="button" class="secondary outline small app-profile-action-button"
|
|
|
|
|
href="tel:<?php e($callTarget); ?>">
|
|
|
|
|
<i class="bi bi-telephone" aria-hidden="true"></i>
|
|
|
|
|
<span><?php e(t('Call')); ?></span>
|
|
|
|
|
</a>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
2026-04-22 15:22:26 +02:00
|
|
|
</div>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
|
|
|
|
|
<?php if ($hasContact): ?>
|
|
|
|
|
<ul class="app-profile-contact-list" aria-label="<?php e(t('Contact')); ?>">
|
|
|
|
|
<?php if ($email !== ''): ?>
|
|
|
|
|
<li class="app-profile-contact-row">
|
|
|
|
|
<a class="app-profile-contact-value" href="mailto:<?php e($email); ?>" aria-label="<?php e(t('Email')); ?>: <?php e($email); ?>">
|
|
|
|
|
<span class="app-profile-contact-icon" aria-hidden="true"
|
|
|
|
|
data-tooltip="<?php e(t('Email')); ?>" data-tooltip-pos="right">
|
|
|
|
|
<i class="bi bi-envelope"></i>
|
|
|
|
|
</span>
|
|
|
|
|
<span class="app-profile-contact-text"><?php e($email); ?></span>
|
|
|
|
|
</a>
|
|
|
|
|
<button type="button" class="app-copy-button" data-copy-button
|
|
|
|
|
data-copy-value="<?php e($email); ?>"
|
|
|
|
|
aria-label="<?php e($copyEmailLabel); ?>"
|
|
|
|
|
data-tooltip="<?php e($copyEmailLabel); ?>" data-tooltip-pos="left">
|
|
|
|
|
<i class="bi bi-copy" data-copy-icon-idle aria-hidden="true"></i>
|
|
|
|
|
<i class="bi bi-check2" data-copy-icon-done aria-hidden="true"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</li>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if ($phone !== ''): ?>
|
|
|
|
|
<li class="app-profile-contact-row">
|
|
|
|
|
<a class="app-profile-contact-value" href="tel:<?php e($phone); ?>" aria-label="<?php e(t('Phone')); ?>: <?php e($phone); ?>">
|
|
|
|
|
<span class="app-profile-contact-icon" aria-hidden="true"
|
|
|
|
|
data-tooltip="<?php e(t('Phone')); ?>" data-tooltip-pos="right">
|
|
|
|
|
<i class="bi bi-telephone"></i>
|
|
|
|
|
</span>
|
|
|
|
|
<span class="app-profile-contact-text"><?php e($phone); ?></span>
|
|
|
|
|
</a>
|
|
|
|
|
<button type="button" class="app-copy-button" data-copy-button
|
|
|
|
|
data-copy-value="<?php e($phone); ?>"
|
|
|
|
|
aria-label="<?php e($copyPhoneLabel); ?>"
|
|
|
|
|
data-tooltip="<?php e($copyPhoneLabel); ?>" data-tooltip-pos="left">
|
|
|
|
|
<i class="bi bi-copy" data-copy-icon-idle aria-hidden="true"></i>
|
|
|
|
|
<i class="bi bi-check2" data-copy-icon-done aria-hidden="true"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</li>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if ($mobile !== ''): ?>
|
|
|
|
|
<li class="app-profile-contact-row">
|
|
|
|
|
<a class="app-profile-contact-value" href="tel:<?php e($mobile); ?>" aria-label="<?php e(t('Mobile')); ?>: <?php e($mobile); ?>">
|
|
|
|
|
<span class="app-profile-contact-icon" aria-hidden="true"
|
|
|
|
|
data-tooltip="<?php e(t('Mobile')); ?>" data-tooltip-pos="right">
|
|
|
|
|
<i class="bi bi-phone"></i>
|
|
|
|
|
</span>
|
|
|
|
|
<span class="app-profile-contact-text"><?php e($mobile); ?></span>
|
|
|
|
|
</a>
|
|
|
|
|
<button type="button" class="app-copy-button" data-copy-button
|
|
|
|
|
data-copy-value="<?php e($mobile); ?>"
|
|
|
|
|
aria-label="<?php e($copyMobileLabel); ?>"
|
|
|
|
|
data-tooltip="<?php e($copyMobileLabel); ?>" data-tooltip-pos="left">
|
|
|
|
|
<i class="bi bi-copy" data-copy-icon-idle aria-hidden="true"></i>
|
|
|
|
|
<i class="bi bi-check2" data-copy-icon-done aria-hidden="true"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</li>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</ul>
|
|
|
|
|
<?php endif; ?>
|
2026-04-22 15:22:26 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
|
|
|
|
|
<?php if ($hasAnyTab): ?>
|
|
|
|
|
<div class="app-profile-body-container">
|
|
|
|
|
<div class="app-profile-body">
|
|
|
|
|
<div class="app-tabs app-profile-tabs" data-tabs data-app-component="tabs"
|
|
|
|
|
data-tabs-param="tab" data-tabs-storage-key="<?php e($profileKey); ?>">
|
|
|
|
|
<div class="app-tabs-nav">
|
|
|
|
|
<?php
|
|
|
|
|
$firstTab = true;
|
|
|
|
|
$renderTabButton = function (string $key, string $label) use (&$firstTab): void {
|
|
|
|
|
$default = $firstTab ? ' data-tab-default' : '';
|
|
|
|
|
$firstTab = false;
|
|
|
|
|
echo '<button type="button" data-tab="' . htmlspecialchars($key, ENT_QUOTES, 'UTF-8') . '"' . $default . '>'
|
|
|
|
|
. htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</button>';
|
|
|
|
|
};
|
|
|
|
|
?>
|
|
|
|
|
<?php if ($hasAddress) { $renderTabButton('address', t('Address')); } ?>
|
|
|
|
|
<?php if ($hasOrganization) { $renderTabButton('organization', t('Organization')); } ?>
|
|
|
|
|
<?php if ($hasAbout) { $renderTabButton('about', t('About')); } ?>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php if ($hasAddress): ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<div data-tab-panel="address" class="app-profile-panel">
|
|
|
|
|
<address class="app-profile-address">
|
|
|
|
|
<?php if ($address !== ''): ?>
|
|
|
|
|
<span><?php e($address); ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php $line2 = trim($postalCode . ' ' . $city); ?>
|
|
|
|
|
<?php if ($line2 !== ''): ?>
|
|
|
|
|
<span><?php e($line2); ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if ($region !== ''): ?>
|
|
|
|
|
<span><?php e($region); ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if ($country !== ''): ?>
|
|
|
|
|
<span><?php e($country); ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</address>
|
|
|
|
|
</div>
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php endif; ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php if ($hasOrganization): ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<div data-tab-panel="organization" class="app-profile-panel">
|
|
|
|
|
<ul class="app-profile-tenant-list">
|
|
|
|
|
<?php foreach ($tenantGroups as $group): ?>
|
|
|
|
|
<?php
|
|
|
|
|
$label = trim((string) ($group['label'] ?? ''));
|
|
|
|
|
if ($label === '') { continue; }
|
|
|
|
|
$departments = $group['departments'] ?? [];
|
|
|
|
|
$departments = is_array($departments) ? $departments : [];
|
|
|
|
|
$isPrimary = !empty($group['is_primary']);
|
|
|
|
|
?>
|
|
|
|
|
<li class="app-profile-tenant-item">
|
|
|
|
|
<div class="app-profile-tenant-label">
|
|
|
|
|
<i class="bi bi-building" aria-hidden="true"></i>
|
|
|
|
|
<strong><?php e($label); ?></strong>
|
|
|
|
|
<?php if ($isPrimary): ?>
|
|
|
|
|
<small class="app-profile-tenant-primary">· <?php e(t('Primary')); ?></small>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php if (!empty($departments)): ?>
|
|
|
|
|
<div class="app-profile-tenant-departments">
|
|
|
|
|
<?php e(implode(', ', $departments)); ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</li>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</ul>
|
2026-04-22 15:22:26 +02:00
|
|
|
</div>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
<?php if ($hasAbout): ?>
|
|
|
|
|
<div data-tab-panel="about" class="app-profile-panel">
|
|
|
|
|
<?php if ($hireDateLabel !== ''): ?>
|
|
|
|
|
<div class="app-profile-key-value">
|
|
|
|
|
<small><?php e(t('Hire date')); ?></small>
|
|
|
|
|
<p><?php e($hireDateLabel); ?></p>
|
2026-04-22 15:22:26 +02:00
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if ($shortDial !== ''): ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<div class="app-profile-key-value">
|
2026-04-22 15:22:26 +02:00
|
|
|
<small><?php e(t('Short dial')); ?></small>
|
|
|
|
|
<p><?php e($shortDial); ?></p>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<?php if ($profileDescription !== ''): ?>
|
|
|
|
|
<div class="app-profile-about-description">
|
|
|
|
|
<?php foreach (preg_split('/\\r\\n|\\r|\\n/', $profileDescription) as $line): ?>
|
|
|
|
|
<?php if (trim($line) !== ''): ?>
|
|
|
|
|
<p><?php e($line); ?></p>
|
|
|
|
|
<?php endif; ?>
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php endforeach; ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
</div>
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php endif; ?>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
</div>
|
2026-04-22 15:22:26 +02:00
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
feat(addressbook): Stripe-style detail profile — meta chips, always-visible contact stack, copy affordance
Refactors the address-book detail partial (templates/partials/app-user-profile.phtml)
toward a Stripe/Linear aesthetic: the banner + avatar still carry identity,
but the header now also carries meta chips (primary tenant, department,
hire date) and primary CTAs (Send email, Call). Email/phone/mobile move
out of a tab into an always-visible contact stack directly under the
identity block, with icon-led rows and a hover-revealed copy button.
Tabs reduce to Address / About / Organization, with Organization only
appearing for multi-tenant users — single-tenant assignments are fully
communicated through chips. The detail aside is gone; main content is
single-column and more focused.
Introduces web/js/components/app-copy-field.js — a generic, server-
markup-driven copy-to-clipboard button component wired from app-init.js.
The markup is rendered by the PHP partial (role-compatible, SSR-safe);
the component only attaches the click handler and drives icon-swap
feedback via an is-copied class.
Address-book index page now loads the 'address-book' CSS group alongside
'address-book-index' so the detail drawer (which mounts the same profile
partial inline) gets the correct styles.
Contact rows use inline-block + vertical-align centering inside the
clickable link; the field-label tooltip lives on a <span> wrapper rather
than the <i> itself (Bootstrap icons render their glyph via ::before, and
the tooltip rule would otherwise cap it). Hover reveals the copy button
on pointer devices; @media (hover: none) shows it permanently on touch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:40:55 +02:00
|
|
|
<?php endif; ?>
|
2026-04-22 15:22:26 +02:00
|
|
|
</section>
|
|
|
|
|
</div>
|