Migration du frontend React/Next.js vers Quarkus + PrimeFaces Freya 5.0.0 Dashboard: - Extension de BtpXpressApiClient avec tous les endpoints dashboard - Création de DashboardService pour récupérer les données API - Refactorisation DashboardView : uniquement données réelles de l'API - Restructuration dashboard.xhtml avec tous les aspects métiers BTP - Suppression complète de toutes les données fictives Topbar: - Amélioration du menu profil utilisateur avec header professionnel - Ajout UserSessionBean pour gérer les informations utilisateur - Styles CSS personnalisés pour une disposition raffinée - Badges de notifications conditionnels Configuration: - Intégration du thème Freya 5.0.0-jakarta - Configuration OIDC pour Keycloak (security.lions.dev) - Gestion des erreurs HTTP 431 (headers size) - Support du format Fcfa avec séparateurs d'espaces Converters: - Création de FcfaConverter pour formater les montants en Fcfa avec espaces (x xxx xxx format) Code Quality: - Code entièrement documenté en français avec Javadoc exemplaire - Respect du principe Java 'Write once, use many times' - Logging complet pour le débogage - Gestion d'erreurs robuste
331 lines
17 KiB
HTML
331 lines
17 KiB
HTML
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
|
xmlns:h="http://java.sun.com/jsf/html"
|
|
xmlns:f="http://java.sun.com/jsf/core"
|
|
xmlns:ui="http://java.sun.com/jsf/facelets"
|
|
xmlns:p="http://primefaces.org/ui"
|
|
template="/WEB-INF/template.xhtml">
|
|
|
|
<ui:define name="title">Tableau de bord - BTP Xpress</ui:define>
|
|
|
|
<ui:define name="head">
|
|
<h:outputScript name="chartjs/chart.js" library="demo" />
|
|
<script>
|
|
//<![CDATA[
|
|
$(function(){
|
|
var ctx1 = document.getElementById("chartChantiers");
|
|
if (ctx1) {
|
|
var chartChantiers = new Chart(ctx1.getContext('2d'), {
|
|
type: 'line',
|
|
data: {
|
|
labels: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin'],
|
|
datasets: [{
|
|
label: 'Chantiers',
|
|
data: [12, 19, 15, 25, 22, 28],
|
|
borderColor: '#464DF2',
|
|
borderWidth: 3,
|
|
fill: true,
|
|
backgroundColor: 'rgba(70, 77, 242, 0.1)',
|
|
tension: 0.4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: { y: { beginAtZero: true } }
|
|
}
|
|
});
|
|
}
|
|
});
|
|
//]]>
|
|
</script>
|
|
</ui:define>
|
|
|
|
<ui:define name="content">
|
|
<div class="layout-dashboard">
|
|
<div class="grid">
|
|
|
|
<!-- KPI Cards - Vue d'ensemble -->
|
|
<div class="col-12">
|
|
<div class="grid" style="margin: -1rem;">
|
|
<div class="col-12 md:col-3">
|
|
<div class="card overview-box white">
|
|
<div class="overview-info">
|
|
<h6>Chantiers actifs</h6>
|
|
<h1>#{dashboardView.chantiersActifs}</h1>
|
|
<p class="subtitle">Sur #{dashboardView.nombreChantiers} au total</p>
|
|
</div>
|
|
<i class="pi pi-building"></i>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 md:col-3">
|
|
<div class="card overview-box blue">
|
|
<div class="overview-info">
|
|
<h6>Clients</h6>
|
|
<h1>#{dashboardView.nombreClients}</h1>
|
|
<p class="subtitle">Actifs</p>
|
|
</div>
|
|
<i class="pi pi-users"></i>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 md:col-3">
|
|
<div class="card overview-box orange">
|
|
<div class="overview-info">
|
|
<h6>Devis en attente</h6>
|
|
<h1>#{dashboardView.nombreDevis}</h1>
|
|
<p class="subtitle">À traiter</p>
|
|
</div>
|
|
<i class="pi pi-file-edit"></i>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 md:col-3">
|
|
<div class="card overview-box red">
|
|
<div class="overview-info">
|
|
<h6>Factures impayées</h6>
|
|
<h1>#{dashboardView.facturesImpayees}</h1>
|
|
<p class="subtitle">Attention requise</p>
|
|
</div>
|
|
<i class="pi pi-exclamation-triangle"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alertes critiques -->
|
|
<p:outputPanel rendered="#{dashboardView.alerteCritique}" styleClass="col-12">
|
|
<div class="card" style="background: #fff3cd; border-left: 4px solid #ffc107;">
|
|
<div class="grid align-items-center">
|
|
<div class="col">
|
|
<h5 style="margin: 0; color: #856404;">
|
|
<i class="pi pi-exclamation-triangle"></i>
|
|
Alertes critiques : #{dashboardView.totalAlertes}
|
|
</h5>
|
|
<p style="margin: 0.5rem 0 0 0; color: #856404;">
|
|
Des actions nécessitent votre attention immédiate
|
|
</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<p:commandButton value="Voir les alertes" icon="pi pi-bell"
|
|
outcome="/dashboard/alertes"
|
|
styleClass="ui-button-warning"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</p:outputPanel>
|
|
|
|
<!-- Graphiques et métriques financières -->
|
|
<div class="col-12 lg:col-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Évolution des chantiers</h6>
|
|
<p class="subtitle">Sur 6 mois</p>
|
|
</div>
|
|
</div>
|
|
<canvas id="chartChantiers" style="max-height: 300px;"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 lg:col-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Chiffre d'affaires</h6>
|
|
<p class="subtitle">Ce mois</p>
|
|
</div>
|
|
</div>
|
|
<div class="statistic-item">
|
|
<h1 style="margin: 0;">
|
|
<h:outputText value="#{dashboardView.chiffreAffairesMois}">
|
|
<f:converter converterId="fcfaConverter"/>
|
|
</h:outputText>
|
|
<h:outputText value=" Fcfa"/>
|
|
</h1>
|
|
<p style="color: var(--text-color-secondary); margin-top: 0.5rem;">
|
|
<i class="pi pi-info-circle"></i>
|
|
Données réelles de l'API
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" style="margin-top: 1rem;">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Budget consommé</h6>
|
|
<p class="subtitle">Sur #{dashboardView.budgetTotal} Fcfa</p>
|
|
</div>
|
|
</div>
|
|
<div class="statistic-item">
|
|
<p:progressBar value="#{dashboardView.tauxConsommationBudget}"
|
|
showValue="true"
|
|
styleClass="ui-progressbar-#{dashboardView.tauxConsommationBudget > 80 ? 'warn' : 'success'}"/>
|
|
<p style="color: var(--text-color-secondary); margin-top: 0.5rem;">
|
|
<h:outputText value="#{dashboardView.budgetConsomme}">
|
|
<f:converter converterId="fcfaConverter"/>
|
|
</h:outputText>
|
|
<h:outputText value=" Fcfa consommés"/>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ressources : Employés, Équipes, Matériel -->
|
|
<div class="col-12 lg:col-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Ressources humaines</h6>
|
|
<p class="subtitle">Employés et équipes</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid" style="gap: 1rem;">
|
|
<div class="col-12">
|
|
<div class="flex align-items-center justify-content-between">
|
|
<span><i class="pi pi-users"></i> Employés</span>
|
|
<strong>#{dashboardView.nombreEmployes}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="flex align-items-center justify-content-between">
|
|
<span><i class="pi pi-users"></i> Équipes</span>
|
|
<strong>#{dashboardView.nombreEquipes}</strong>
|
|
</div>
|
|
<p:progressBar value="#{dashboardView.nombreEquipes > 0 ? (dashboardView.equipesDisponibles * 100 / dashboardView.nombreEquipes) : 0}"
|
|
showValue="true"
|
|
styleClass="ui-progressbar-info"/>
|
|
<small style="color: var(--text-color-secondary);">
|
|
#{dashboardView.equipesDisponibles} disponibles
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 lg:col-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Matériel</h6>
|
|
<p class="subtitle">Équipements disponibles</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid" style="gap: 1rem;">
|
|
<div class="col-12">
|
|
<div class="flex align-items-center justify-content-between">
|
|
<span><i class="pi pi-wrench"></i> Total matériel</span>
|
|
<strong>#{dashboardView.nombreMateriel}</strong>
|
|
</div>
|
|
<p:progressBar value="#{dashboardView.nombreMateriel > 0 ? (dashboardView.materielDisponible * 100 / dashboardView.nombreMateriel) : 0}"
|
|
showValue="true"
|
|
styleClass="ui-progressbar-success"/>
|
|
<small style="color: var(--text-color-secondary);">
|
|
#{dashboardView.materielDisponible} disponibles
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 lg:col-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Maintenance</h6>
|
|
<p class="subtitle">État des maintenances</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid" style="gap: 1rem;">
|
|
<div class="col-12">
|
|
<div class="flex align-items-center justify-content-between">
|
|
<span><i class="pi pi-exclamation-circle" style="color: red;"></i> En retard</span>
|
|
<strong style="color: red;">#{dashboardView.maintenancesEnRetard}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="flex align-items-center justify-content-between">
|
|
<span><i class="pi pi-calendar"></i> Planifiées</span>
|
|
<strong>#{dashboardView.maintenancesPlanifiees}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<p:commandButton value="Voir les maintenances" icon="pi pi-cog"
|
|
outcome="/maintenance"
|
|
styleClass="ui-button-text" style="width: 100%;"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chantiers récents -->
|
|
<div class="col-12 lg:col-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Chantiers récents</h6>
|
|
<p class="subtitle">Derniers chantiers actifs</p>
|
|
</div>
|
|
<p:commandButton value="Voir tout" icon="pi pi-arrow-right"
|
|
outcome="/chantiers"
|
|
styleClass="ui-button-text"/>
|
|
</div>
|
|
<p:dataTable value="#{dashboardView.chantiersRecents}" var="chantier"
|
|
emptyMessage="Aucun chantier récent">
|
|
<p:column headerText="Nom">
|
|
<h:outputText value="#{chantier.nom}"/>
|
|
</p:column>
|
|
<p:column headerText="Client">
|
|
<h:outputText value="#{chantier.client}"/>
|
|
</p:column>
|
|
<p:column headerText="Date de début">
|
|
<h:outputText value="#{chantier.dateDebutFormatee}"/>
|
|
</p:column>
|
|
<p:column headerText="Avancement">
|
|
<p:progressBar value="#{chantier.avancement}"
|
|
showValue="true"
|
|
styleClass="ui-progressbar-success"/>
|
|
</p:column>
|
|
<p:column headerText="Actions">
|
|
<p:commandButton icon="pi pi-eye" title="Voir les détails"
|
|
styleClass="ui-button-text"
|
|
outcome="/chantiers/details?id=#{chantier.id}"/>
|
|
</p:column>
|
|
</p:dataTable>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chantiers en retard -->
|
|
<div class="col-12 lg:col-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h6>Chantiers en retard</h6>
|
|
<p class="subtitle">Attention requise</p>
|
|
</div>
|
|
</div>
|
|
<p:dataList value="#{dashboardView.chantiersEnRetard}" var="chantier"
|
|
emptyMessage="Aucun chantier en retard">
|
|
<div class="flex align-items-center justify-content-between" style="padding: 0.75rem; border-bottom: 1px solid var(--surface-border);">
|
|
<div>
|
|
<strong>#{chantier.nom}</strong>
|
|
<br/>
|
|
<small style="color: var(--text-color-secondary);">
|
|
#{chantier.dateFinPrevueFormatee}
|
|
</small>
|
|
</div>
|
|
<p:tag value="+#{chantier.joursRetard}j" severity="danger"/>
|
|
</div>
|
|
</p:dataList>
|
|
<p:outputPanel rendered="#{empty dashboardView.chantiersEnRetard}">
|
|
<p style="text-align: center; padding: 1rem; color: var(--text-color-secondary);">
|
|
<i class="pi pi-check-circle" style="color: green;"></i>
|
|
Aucun chantier en retard
|
|
</p>
|
|
</p:outputPanel>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</ui:define>
|
|
</ui:composition>
|