{% extends 'base.html.twig' %} {% block title %}{{ user.fullName }} - {{ isAdmin|default(true) ? 'Admin' : 'Utilisateurs' }}{% endblock %} {% block body %} {# Couleurs outils : pilotées par les globals tool_colors et tool_color_hex (source : Tool entity) #} {# ================================================================ BREADCRUMB ================================================================ #}
{% include '_molecules/breadcrumb/breadcrumb.html.twig' with { homeHref: path('app_dashboard'), items: [ { label: 'Utilisateurs', href: path('admin_user_list') }, { label: user.fullName } ] } only %}
{# ================================================================ HEADER UTILISATEUR ================================================================ #}
{# GAUCHE — Carte abonnement (admin uniquement) #} {% if isAdmin %} {% set sub = user.subscription %} {% set plan = sub ? sub.plan : null %} {% if plan and not plan.isFree() and sub.isActive() %} {% set gradient = plan.gradientCss ?? 'linear-gradient(135deg, #0d1b3e 0%, #0C81E4 100%)' %} {% elseif user.isB2B() %} {% set gradient = plan_gradients['system-b2b']|replace({'background: ': ''}) %} {% else %} {% set gradient = plan_gradients['freemium']|replace({'background: ': ''}) %} {% endif %}

Abonnement

{% if plan and not plan.isFree() and sub.isActive() %}

{{ plan.name }}

{{ sub.isAnnual() ? 'Annuel' : 'Mensuel' }} · {{ sub.isAnnual() ? plan.priceAnnual|number_format(2, '.', '') : plan.priceMonthly|number_format(2, '.', '') }}€/mois

{% elseif user.isB2B() %}

Entreprise

{{ user.company ? user.company.name : 'B2B' }}

{% else %}

Freemium

Plan gratuit

{% endif %}
{% endif %} {# DROITE — Infos utilisateur + boutons #}
{% include '_atoms/avatar/avatar.html.twig' with { name: user.fullName, size: 'xl', class: not user.isActive ? 'opacity-50' : '' } only %}

{{ user.fullName }}

{% include '_atoms/badge/badge.html.twig' with { preset: user.isActive ? 'user-active' : 'user-inactive', size: 'sm' } only %} {% if user.isBetaTester or 'ROLE_BETA_TESTER' in user.roles %} {% include '_atoms/badge/badge.html.twig' with { label: 'Beta testeur', variant: 'warning', icon: 'star', size: 'sm' } only %} {% endif %} {% if user.isEmailVerified %} {% include '_atoms/badge/badge.html.twig' with { label: 'Email vérifié', variant: 'success', icon: 'check-circle', size: 'sm' } only %} {% else %} {% include '_atoms/badge/badge.html.twig' with { label: 'Email non vérifié', variant: 'warning', icon: 'alert', size: 'sm' } only %} {% endif %}
{% include '_atoms/icon/icon.html.twig' with { name: 'mail', size: 'sm' } only %} {{ user.email }} {% if user.company %} {% include '_atoms/icon/icon.html.twig' with { name: 'buildings', size: 'sm' } only %} {{ user.company.name }} {% endif %} {% include '_atoms/icon/icon.html.twig' with { name: 'calendar', size: 'sm' } only %} Membre depuis {{ user.createdAt|date('d/m/Y') }}
{% include '_atoms/button/button.html.twig' with { label: 'Retour', variant: 'ghost', size: 'lg', icon: 'arrow-left', href: path('admin_user_list') } only %} {% set canEdit = isAdmin or not ('ROLE_ADMIN' in user.roles) %} {% if canEdit %} {% include '_atoms/button/button.html.twig' with { label: 'Modifier', variant: 'primary', size: 'lg', icon: 'edit', href: path('admin_user_edit', {uuid: user.uuid}) } only %} {% endif %}
{# ================================================================ STATISTIQUES TOKENS ================================================================ #} {% if userToken %} {% set percentAvailable = userToken.tokenUsable > 0 ? ((userToken.tokensRemaining / userToken.tokenUsable) * 100)|round(1) : 0 %} {% set percentUsed = userToken.tokenUsable > 0 ? ((userToken.tokenUsed / userToken.tokenUsable) * 100)|round(1) : 0 %} {% set co2 = userToken.energyConsumptionWh * 0.0004 %}
{% include '_molecules/stat-card/stat-card.html.twig' with { label: 'Tokens restants', value: userToken.tokensRemaining|number_format(0, ',', ' '), icon: 'token', variant: 'primary', size: 'lg', layout: 'vertical', editModalId: isAdmin ? 'modal-edit-remaining-tokens' : null } only %} {% include '_molecules/stat-card/stat-card.html.twig' with { label: 'Tokens consommés', value: userToken.tokenUsed|number_format(0, ',', ' '), icon: 'chart', variant: 'warning', size: 'lg', layout: 'vertical' } only %} {% include '_molecules/stat-card/stat-card.html.twig' with { label: 'Tokens mensuels', value: userMonthlyAllocation|number_format(0, ',', ' '), detail: userContracts|length ~ ' contrat' ~ (userContracts|length > 1 ? 's' : '') ~ ' actif' ~ (userContracts|length > 1 ? 's' : ''), icon: 'token', variant: 'success', size: 'lg', layout: 'vertical' } only %} {% include '_molecules/stat-card/stat-card.html.twig' with { label: 'Énergie consommée', value: userToken.energyConsumptionWh|number_format(1, ',', ' ') ~ ' Wh', detail: '≈ ' ~ co2|number_format(2, ',', ' ') ~ ' kg CO2', icon: 'bolt', variant: 'warning', size: 'lg', layout: 'vertical' } only %}
{% endif %} {# ================================================================ SECTION: INFORMATIONS DU COMPTE (Grille 2 colonnes) ================================================================ #}
{% include '_molecules/section-divider/section-divider.html.twig' with { title: 'Informations du compte', icon: 'user', iconColor: 'primary' } only %}
{# Colonne 1: Profil #} {% embed '_molecules/card/card.html.twig' with { title: 'Profil', icon: 'user', iconColor: 'primary' } %} {% block body %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Prénom', value: user.firstName, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Nom', value: user.lastName, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Email', value: user.email, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Entreprise', action: user.company ? { label: user.company.name, href: path('admin_company_show', {uuid: user.company.uuid}) } : null, value: not user.company ? 'Aucune' : null, valueColor: not user.company ? 'muted' : null, size: 'lg' } only %}
{% set roleConfig = 'ROLE_ADMIN' in user.roles ? { label: 'Administrateur', color: 'error' } : ('ROLE_COMPANY_MANAGER' in user.roles ? { label: 'Gestionnaire', color: 'info' } : ('ROLE_COMMERCIAL' in user.roles ? { label: 'Commercial', color: 'success' } : { label: 'Utilisateur', color: 'primary' })) %} {% include '_molecules/data-row/data-row.html.twig' with { label: 'Rôle', tag: roleConfig, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Inscription', value: user.createdAt|date('d/m/Y à H:i'), size: 'lg' } only %} {# Source d'acquisition (« Comment nous avez-vous connu ? ») #} {% set srcChoices = constant('App\\Config\\ReferralSourceConfig::CHOICES') %} {% set srcLabel = user.referralSource ? (srcChoices[user.referralSource]|default(user.referralSource)) : null %} {% if srcLabel and user.referralSourceDetail %}{% set srcLabel = srcLabel ~ ' — ' ~ user.referralSourceDetail %}{% endif %} {% include '_molecules/data-row/data-row.html.twig' with { label: 'Comment connu', value: srcLabel ?? '—', valueColor: srcLabel ? null : 'muted', size: 'lg' } only %} {% if user.recruitedBy %} {% include '_molecules/data-row/data-row.html.twig' with { label: 'Parrainé par', action: { label: user.recruitedBy.referralCode ~ (user.recruitedBy.user ? ' · ' ~ user.recruitedBy.user.fullName : ''), href: path('admin_commercial_show', {id: user.recruitedBy.id}) }, size: 'lg' } only %} {% endif %}
{% endblock %} {% endembed %} {# Colonne 2: Sécurité & Historique #} {% embed '_molecules/card/card.html.twig' with { title: 'Sécurité & Historique', icon: 'shield', iconColor: 'info' } %} {% block body %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Authentification', tag: user.googleId ? { label: 'Google SSO', color: 'info' } : { label: 'Email / Mot de passe', color: 'primary' }, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Email vérifié', tag: user.isEmailVerified ? { label: 'Vérifié', color: 'success' } : { label: 'Non vérifié', color: 'warning' }, value: user.emailVerifiedAt ? user.emailVerifiedAt|date('d/m/Y à H:i') : null, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'CGU acceptées', value: user.cgaAcceptedAt ? user.cgaAcceptedAt|date('d/m/Y à H:i') : 'Non acceptées', valueColor: not user.cgaAcceptedAt ? 'muted' : null, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Beta testeur', tag: user.isBetaTester ? { label: 'Oui', color: 'warning' } : { label: 'Non', color: 'neutral' }, size: 'lg' } only %}
{% include '_molecules/data-row/data-row.html.twig' with { label: 'Dernière mise à jour', value: user.updatedAt ? user.updatedAt|date('d/m/Y à H:i') : 'Jamais', valueColor: not user.updatedAt ? 'muted' : null, size: 'lg' } only %}
{% endblock %} {% endembed %}
{# ================================================================ SECTION: UTILISATION PAR OUTILS ================================================================ #} {% if tokensByTool is defined and tokensByTool|length > 0 %}
{% include '_molecules/section-divider/section-divider.html.twig' with { title: 'Utilisation par outils', icon: 'chart', iconColor: 'warning' } only %}
{# Graphique Donut #} {% set chartData = [] %} {% for toolData in tokensByTool %} {% set chartData = chartData|merge([toolData]) %} {% endfor %}
{# Détails par outil - couleurs cohérentes avec le dashboard #}
{% for toolData in tokensByTool %} {% set tToken = tool_colors[toolData.code] ?? 'primary' %} {% set tHex = tool_color_hex[toolData.code] ?? '12, 129, 228' %} {% set tHexFull = (tool_color_details[toolData.code] ?? {}).primary ?? null %} {% set tHexSecondary = (tool_color_details[toolData.code] ?? {}).secondary ?? null %}
{# Icône de l'outil #}
{% include '_atoms/icon/icon.html.twig' with { name: 'bubul-' ~ toolData.code, size: 'xl', color: tToken, colorPrimary: tHexFull, colorSecondary: tHexSecondary } only %}
{# Nom + Tokens #}
{{ toolData.name }} {{ toolData.tokens|number_format(0, ',', ' ') }} tokens
{# Énergie #}
{{ toolData.energy|number_format(2, ',', ' ') }} Wh
{% endfor %}
{% endif %} {# ================================================================ SECTION: EMPREINTE ÉCOLOGIQUE ================================================================ #}
{% include '_molecules/section-divider/section-divider.html.twig' with { title: 'Empreinte écologique', subtitle: 'Consommation globale', icon: 'earth', iconColor: 'success' } only %} {% include '_organisms/eco-footprint/eco-footprint.html.twig' with { totalWh: ecoData.totalWh, period: ecoData.period, weeklyData: ecoData.weeklyData, weekDelta: ecoData.weekDelta } only %}
{# ================================================================ SECTION: ACTIVITÉS & CONTRATS ================================================================ #}
{% include '_molecules/section-divider/section-divider.html.twig' with { title: 'Activités & Contrats', icon: 'calendar', iconColor: 'info' } only %}
{# Activités récentes #} {% include '_organisms/activity-list/activity-list.html.twig' with { activities: recentActivities|default([]), title: 'Activités récentes', emptyTitle: 'Aucune activité récente', emptyDesc: 'Les activités de cet utilisateur apparaîtront ici.' } only %} {# Contrats actifs #} {% include '_organisms/contract-list/contract-list.html.twig' with { contracts: userContracts|default([]), title: 'Contrats actifs', emptyTitle: 'Aucun contrat', emptyDesc: "Cet utilisateur n'a pas de contrat associé." } only %}
{# ================================================================ SECTION: GESTION DU COMPTE ================================================================ #} {% if user != app.user %}
{% include '_molecules/section-divider/section-divider.html.twig' with { title: 'Gestion du compte', icon: 'settings', iconColor: 'error' } only %}
{# Vérification email — renvoi d'invitation #} {% if not user.isEmailVerified %} {% include '_molecules/status-zone/status-zone.html.twig' with { title: 'Vérification email', description: "L'utilisateur n'a pas encore vérifié son adresse email et activé son compte. Vous pouvez lui renvoyer l'email d'invitation.", icon: 'mail', variant: 'warning', buttonLabel: 'Renvoyer l\'invitation', buttonIcon: 'send', modalId: 'modal-resend-verification' } only %} {% endif %} {# Réinitialisation du mot de passe #} {% include '_molecules/status-zone/status-zone.html.twig' with { title: 'Mot de passe', description: "Générer un nouveau mot de passe aléatoire et l'envoyer par email à l'utilisateur.", icon: 'key', variant: 'neutral', buttonLabel: 'Réinitialiser', buttonIcon: 'refresh', modalId: 'modal-reset-password' } only %} {# Statut du compte #} {% include '_molecules/status-zone/status-zone.html.twig' with { title: 'Statut du compte', description: user.isActive ? "Ce compte est actuellement activé. L'utilisateur peut accéder à la plateforme." : "Ce compte est actuellement désactivé. L'utilisateur ne peut pas se connecter.", icon: 'power', variant: user.isActive ? 'success' : 'warning', buttonLabel: user.isActive ? 'Désactiver' : 'Activer', buttonIcon: 'power', buttonVariant: user.isActive ? 'warning' : 'success', modalId: 'modal-toggle-status' } only %} {# Suppression du compte — admin uniquement #} {% if isAdmin %} {% include '_molecules/status-zone/status-zone.html.twig' with { title: 'Suppression du compte', description: "Supprimer définitivement ce compte. Cette action est irréversible.", icon: 'trash', variant: 'error', buttonLabel: 'Supprimer', buttonIcon: 'trash', buttonVariant: 'error', modalId: 'modal-delete-user' } only %} {% endif %}
{% endif %}
{# ================================================================ MODAL: RÉINITIALISER MOT DE PASSE ================================================================ #} {% if user != app.user %} {% embed '_molecules/modal/modal.html.twig' with { id: 'modal-reset-password', title: 'Réinitialiser le mot de passe', subtitle: user.fullName, variant: 'primary', size: 'md' } %} {% block body %}

Un nouveau mot de passe aléatoire sera généré et envoyé par email à {{ user.email }}.

L'ancien mot de passe ne sera plus valide.

{% endblock %} {% block footer %}
{% include '_atoms/button/button.html.twig' with { label: 'Annuler', variant: 'ghost', size: 'lg', type: 'button', attr: { onclick: "closeModal('modal-reset-password')" } } only %} {% include '_atoms/button/button.html.twig' with { label: 'Réinitialiser', variant: 'primary', size: 'lg', icon: 'refresh', type: 'submit' } only %}
{% endblock %} {% endembed %} {# MODAL: RENVOYER EMAIL DE VÉRIFICATION #} {% if not user.isEmailVerified %} {% embed '_molecules/modal/modal.html.twig' with { id: 'modal-resend-verification', title: 'Renvoyer l\'invitation', subtitle: user.fullName, variant: 'primary', size: 'md' } %} {% block body %}

Un nouvel email d'invitation sera envoyé à {{ user.email }} avec un lien valable 24 heures pour activer son compte.

{% endblock %} {% block footer %}
{% include '_atoms/button/button.html.twig' with { label: 'Annuler', variant: 'ghost', size: 'lg', type: 'button', attr: { onclick: "closeModal('modal-resend-verification')" } } only %} {% include '_atoms/button/button.html.twig' with { label: 'Renvoyer', variant: 'primary', size: 'lg', icon: 'send', type: 'submit' } only %}
{% endblock %} {% endembed %} {% endif %} {# MODAL: TOGGLE STATUT #} {% embed '_molecules/modal/modal.html.twig' with { id: 'modal-toggle-status', title: user.isActive ? 'Désactiver le compte' : 'Activer le compte', subtitle: user.fullName, variant: user.isActive ? 'warning' : 'success', size: 'md' } %} {% block body %}

{% if user.isActive %} L'utilisateur {{ user.fullName }} ne pourra plus se connecter à la plateforme. {% else %} L'utilisateur {{ user.fullName }} pourra à nouveau se connecter à la plateforme. {% endif %}

Cette action prendra effet immédiatement.

{% endblock %} {% block footer %}
{% include '_atoms/button/button.html.twig' with { label: 'Annuler', variant: 'ghost', size: 'lg', type: 'button', attr: { onclick: "closeModal('modal-toggle-status')" } } only %} {% include '_atoms/button/button.html.twig' with { label: user.isActive ? 'Désactiver' : 'Activer', variant: user.isActive ? 'warning' : 'success', size: 'lg', icon: 'power', type: 'submit' } only %}
{% endblock %} {% endembed %} {# MODAL: SUPPRESSION DU COMPTE — admin uniquement #} {% if isAdmin %} {% include 'admin/users/_modal_delete.html.twig' with { user: user, modal_id: 'modal-delete-user' } only %} {% endif %} {% endif %} {# ================================================================ MODAL: ÉDITER LES TOKENS RESTANTS ================================================================ #} {% if isAdmin and userToken %} {% embed '_molecules/modal/modal.html.twig' with { id: 'modal-edit-remaining-tokens', title: 'Tokens restants', subtitle: user.fullName, variant: 'primary', size: 'md' } %} {% block body %}

Solde actuel : {{ userToken.tokensRemaining|number_format(0, ',', ' ') }} tokens ({{ userToken.tokenUsed|number_format(0, ',', ' ') }} consommés)

{% endblock %} {% block footer %}
{% include '_atoms/button/button.html.twig' with { label: 'Annuler', variant: 'ghost', size: 'lg', type: 'button', attr: { onclick: "closeModal('modal-edit-remaining-tokens')" } } only %} {% include '_atoms/button/button.html.twig' with { label: 'Enregistrer', variant: 'primary', size: 'lg', icon: 'check', type: 'submit' } only %}
{% endblock %} {% endembed %} {% endif %} {# Historique des actions admin (admin uniquement) #} {% if isAdmin %}
{% include '_organisms/activity-log/activity-log-section.html.twig' with { logs: logs ?? [] } only %}
{% endif %} {% endblock %}