{# # ============================================================================ # MOLECULE: STAT CARD # ============================================================================ # # Carte de statistique pour dashboards et tableaux de bord. # Affiche une valeur clé avec icône, label et progression optionnelle. # # ============================================================================ # COMPOSITION (Atomes utilisés) # ============================================================================ # # - _atoms/icon/icon.html.twig # - _atoms/typography/typography.html.twig # - _atoms/progress/progress.html.twig # # ============================================================================ # PARAMETRES # ============================================================================ # # | Paramètre | Type | Défaut | Description | # |----------------|---------|-----------|------------------------------------------| # | label | string | (requis) | Titre/label de la statistique | # | value | string | (requis) | Valeur principale | # | icon | string | null | Nom de l'icone | # | detail | string | null | Texte secondaire | # | variant | string | 'default' | default, primary, success, warning, error| # | trend | string | null | up, down, neutral | # | trendValue | string | null | Valeur du trend (ex: '+12%') | # | progress | number | null | Pourcentage 0-100 | # | progressLabel | string | null | Label de la progression | # | previousValue | string | null | Valeur période précédente (hover reveal) | # | delta | string | null | Variation vs précédent (ex: '+20%') | # | deltaDown | bool | false | true si la variation est négative | # | href | string | null | Rend la carte cliquable | # | size | string | 'md' | sm, md, lg | # | layout | string | 'vertical'| vertical (icone au-dessus), horizontal | # | class | string | '' | Classes additionnelles | # # ============================================================================ # EXEMPLES D'UTILISATION # ============================================================================ # # Stat simple (layout vertical par défaut) : # {% include '_molecules/stat-card/stat-card.html.twig' with { # label: 'Utilisateurs', # value: '1,234', # icon: 'users' # } %} # # Layout horizontal (icône à côté) : # {% include '_molecules/stat-card/stat-card.html.twig' with { # label: 'Utilisateurs', # value: '1,234', # icon: 'users', # layout: 'horizontal' # } %} # # Avec trend et progression: # {% include '_molecules/stat-card/stat-card.html.twig' with { # label: 'Tokens restants', # value: '12,500', # icon: 'token', # variant: 'primary', # progress: 75, # progressLabel: 'Disponibles' # } %} # # ============================================================================ #} {# ============================================================================ CONFIGURATION DES VALEURS PAR DÉFAUT ============================================================================ #} {% set label = label ?? 'Statistique' %} {% set value = value ?? '0' %} {% set icon = icon ?? null %} {% set detail = detail ?? null %} {% set variant = variant ?? 'default' %} {% set trend = trend ?? null %} {% set trendValue = trendValue ?? null %} {% set progress = progress ?? null %} {% set progressLabel = progressLabel ?? null %} {% set previousValue = previousValue ?? null %} {% set delta = delta ?? null %} {% set deltaDown = deltaDown ?? false %} {% set href = href ?? null %} {% set size = size ?? 'md' %} {% set valueAttr = valueAttr ?? null %} {% set layout = layout ?? 'vertical' %} {% set class = class ?? '' %} {% set editModalId = editModalId ?? null %} {# ============================================================================ CLASSES TAILWIND ============================================================================ #} {% set variantConfig = { 'default': { 'bg': 'bg-white', 'iconBg': 'bg-gradient-to-br from-gray-100 to-gray-50', 'iconColor': 'muted', 'border': 'border-primary/10', 'hoverBar': 'from-primary to-blue-400', 'progressColor': 'primary' }, 'primary': { 'bg': 'bg-white', 'iconBg': 'bg-gradient-to-br from-blue-100 to-blue-50', 'iconColor': 'primary', 'border': 'border-primary/10', 'hoverBar': 'from-primary to-blue-400', 'progressColor': 'primary' }, 'success': { 'bg': 'bg-white', 'iconBg': 'bg-success/10', 'iconColor': 'success', 'border': 'border-success/10', 'hoverBar': 'from-success to-success/80', 'progressColor': 'success' }, 'warning': { 'bg': 'bg-white', 'iconBg': 'bg-warning/10', 'iconColor': 'warning', 'border': 'border-warning/10', 'hoverBar': 'from-warning to-warning/80', 'progressColor': 'warning' }, 'error': { 'bg': 'bg-white', 'iconBg': 'bg-error/10', 'iconColor': 'error', 'border': 'border-error/10', 'hoverBar': 'from-error to-error/80', 'progressColor': 'error' }, 'info': { 'bg': 'bg-white', 'iconBg': 'bg-info/10', 'iconColor': 'info', 'border': 'border-info/10', 'hoverBar': 'from-info to-info/80', 'progressColor': 'info' }, 'cohorts': { 'bg': 'bg-white', 'iconBg': 'bg-cohorts/10', 'iconColor': 'cohorts', 'border': 'border-cohorts/10', 'hoverBar': 'from-cohorts to-cohorts/80', 'progressColor': 'cohorts' } } %} {% set sizeConfig = { 'sm': { 'padding': 'p-6', 'iconContainer': 'w-14 h-14', 'iconSize': 'xl', 'labelSize': 'text-xs', 'valueSize': 'text-3xl', 'detailSize': 'text-xs', 'gap': 'gap-4', 'iconMargin': 'mb-5', 'valueMargin': 'mt-1', 'progressMargin': 'mt-5' }, 'md': { 'padding': 'p-8', 'iconContainer': 'w-16 h-16', 'iconSize': '2xl', 'labelSize': 'text-sm', 'valueSize': 'text-4xl', 'detailSize': 'text-sm', 'gap': 'gap-5', 'iconMargin': 'mb-6', 'valueMargin': 'mt-2', 'progressMargin': 'mt-6' }, 'lg': { 'padding': 'p-10', 'iconContainer': 'w-20 h-20', 'iconSize': '3xl', 'labelSize': 'text-sm', 'valueSize': 'text-5xl', 'detailSize': 'text-base', 'gap': 'gap-6', 'iconMargin': 'mb-8', 'valueMargin': 'mt-3', 'progressMargin': 'mt-8' } } %} {% set trendConfig = { 'up': { 'icon': 'arrow-up', 'color': 'text-green-600', 'bg': 'bg-green-50' }, 'down': { 'icon': 'arrow-down', 'color': 'text-red-600', 'bg': 'bg-red-50' }, 'neutral': { 'icon': 'minus', 'color': 'text-gray-500', 'bg': 'bg-gray-50' } } %} {% set vConfig = variantConfig[variant] ?? variantConfig['default'] %} {% set sConfig = sizeConfig[size] ?? sizeConfig['md'] %} {% set baseClasses = [ 'bg-white/80 backdrop-blur-sm', 'rounded-2xl border', 'border-gray-200/60', 'shadow-[0_2px_8px_rgba(12,129,228,0.04)]', sConfig.padding, 'relative overflow-hidden', 'transition-all duration-[400ms] ease-[cubic-bezier(0.16,1,0.3,1)]', 'hover:-translate-y-1.5 hover:shadow-[0_16px_32px_rgba(12,129,228,0.12),0_0_0_1px_rgba(12,129,228,0.06)] hover:border-primary/20', 'group' ]|join(' ') %} {% set containerClasses = [ baseClasses, href ? 'block cursor-pointer' : '', class ]|join(' ')|trim %} {# Tag a utiliser #} {% set tag = href ? 'a' : 'div' %} {# ============================================================================ RENDU HTML ============================================================================ #} <{{ tag }}{% if href %} href="{{ href }}"{% endif %} class="{{ containerClasses }}"> {# Barre colorée au hover #}
{# Bouton d'édition (optionnel) #} {% if editModalId %}{{ label }}
{{ value }}
{% if trend and trendValue %} {% set tConfig = trendConfig[trend] ?? trendConfig['neutral'] %} {% include '_atoms/icon/icon.html.twig' with { name: tConfig.icon, size: 'xs', color: trend == 'up' ? 'success' : (trend == 'down' ? 'error' : 'muted') } only %} {{ trendValue }} {% endif %}{{ detail }}
{% endif %}{{ label }}
{# Valeur + Trend #}{{ value }}
{% if trend and trendValue %} {% set tConfig = trendConfig[trend] ?? trendConfig['neutral'] %} {% include '_atoms/icon/icon.html.twig' with { name: tConfig.icon, size: 'xs', color: trend == 'up' ? 'success' : (trend == 'down' ? 'error' : 'muted') } only %} {{ trendValue }} {% endif %}{{ detail }}
{% endif %} {% endif %} {# Barre de progression (commune aux deux layouts) #} {% if progress is not null %}