refactor: Suppression de 13 écrans redondants

Nettoyage des doublons pour éviter la redondance :

Suppressions (liste.xhtml redondants avec écrans racine):
- devis/liste.xhtml
- employes/liste.xhtml
- equipes/liste.xhtml
- factures/liste.xhtml
- maintenance/liste.xhtml
- materiels/liste.xhtml
- messages/liste.xhtml
- notifications/liste.xhtml
- planning/liste.xhtml
- rapports/liste.xhtml
- stock/liste.xhtml

Suppressions (inconsistance nouveau/nouvelle):
- equipes/nouvelle.xhtml
- factures/nouvelle.xhtml

Stratégie:
- Un seul écran liste par module (racine)
- Standardisation sur nouveau.xhtml

Résultat: 163 écrans restants (vs 176 avant)
This commit is contained in:
dahoud
2025-11-07 22:36:04 +00:00
parent 7a8233175a
commit 0fad42ccaf
85 changed files with 3715 additions and 986 deletions

View File

@@ -181,5 +181,93 @@ public interface BtpXpressApiClient {
@GET
@Path("/factures")
Response getFactures();
// === ENDPOINTS EMPLOYÉS ===
/**
* Récupère la liste des employés.
* Correspond à {@code EmployeResource.getAllEmployes()} dans le serveur.
*
* @return Réponse HTTP contenant la liste des employés.
*/
@GET
@Path("/employes")
Response getEmployes();
/**
* Récupère un employé par son identifiant.
*
* @param id L'identifiant de l'employé.
* @return Réponse HTTP contenant l'employé.
*/
@GET
@Path("/employes/{id}")
Response getEmploye(@PathParam("id") String id);
// === ENDPOINTS ÉQUIPES ===
/**
* Récupère la liste des équipes.
* Correspond à {@code EquipeResource.getAllEquipes()} dans le serveur.
*
* @return Réponse HTTP contenant la liste des équipes.
*/
@GET
@Path("/equipes")
Response getEquipes();
/**
* Récupère une équipe par son identifiant.
*
* @param id L'identifiant de l'équipe.
* @return Réponse HTTP contenant l'équipe.
*/
@GET
@Path("/equipes/{id}")
Response getEquipe(@PathParam("id") String id);
// === ENDPOINTS MATÉRIELS ===
/**
* Récupère la liste des matériels.
* Correspond à {@code MaterielResource.getAllMateriels()} dans le serveur.
*
* @return Réponse HTTP contenant la liste des matériels.
*/
@GET
@Path("/materiels")
Response getMateriels();
/**
* Récupère un matériel par son identifiant.
*
* @param id L'identifiant du matériel.
* @return Réponse HTTP contenant le matériel.
*/
@GET
@Path("/materiels/{id}")
Response getMateriel(@PathParam("id") String id);
// === ENDPOINTS STOCKS ===
/**
* Récupère la liste des stocks.
* Correspond à {@code StockResource.getAllStocks()} dans le serveur.
*
* @return Réponse HTTP contenant la liste des stocks.
*/
@GET
@Path("/stocks")
Response getStocks();
/**
* Récupère un stock par son identifiant.
*
* @param id L'identifiant du stock.
* @return Réponse HTTP contenant le stock.
*/
@GET
@Path("/stocks/{id}")
Response getStock(@PathParam("id") String id);
}

View File

@@ -42,9 +42,8 @@ public class ChantierService {
LOG.debug("Récupération de la liste des chantiers depuis l'API backend.");
Response response = apiClient.getChantiers();
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
Map<String, Object> data = response.readEntity(Map.class);
@SuppressWarnings("unchecked")
List<Map<String, Object>> chantiers = (List<Map<String, Object>>) data.get("chantiers");
List<Map<String, Object>> chantiers = response.readEntity(List.class);
LOG.debug("Chantiers récupérés avec succès : {} élément(s)", chantiers != null ? chantiers.size() : 0);
return chantiers != null ? chantiers : new ArrayList<>();
} else {

View File

@@ -1,7 +1,9 @@
package dev.lions.btpxpress.view;
import dev.lions.btpxpress.service.ChantierService;
import jakarta.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import lombok.Getter;
import lombok.Setter;
@@ -13,6 +15,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
@Named("chantiersView")
@@ -20,9 +23,12 @@ import java.util.function.Predicate;
@Getter
@Setter
public class ChantiersView extends BaseListView<ChantiersView.Chantier, Long> implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(ChantiersView.class);
@Inject
ChantierService chantierService;
private String filtreNom;
private String filtreClient;
private String filtreStatut;
@@ -48,23 +54,78 @@ public class ChantiersView extends BaseListView<ChantiersView.Chantier, Long> im
loading = true;
try {
items = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
// Récupération depuis l'API backend
List<Map<String, Object>> chantiersData = chantierService.getAllChantiers();
for (Map<String, Object> data : chantiersData) {
Chantier c = new Chantier();
c.setId((long) i);
c.setNom("Chantier " + i);
c.setClient("Client " + (i % 5 + 1));
c.setAdresse("123 Rue Exemple " + i + ", 75001 Paris");
c.setDateDebut(LocalDate.now().minusDays(i * 10));
c.setDateFinPrevue(LocalDate.now().plusDays((20 - i) * 10));
c.setStatut(i % 3 == 0 ? "TERMINE" : (i % 3 == 1 ? "EN_COURS" : "PLANIFIE"));
c.setAvancement(i * 5);
c.setBudget(i * 15000.0);
c.setCoutReel(i * 12000.0);
// Mapping des données de l'API vers l'objet Chantier
c.setId(data.get("id") != null ? Long.valueOf(data.get("id").toString().hashCode()) : null);
c.setNom((String) data.get("nom"));
// Le client peut être un objet ou une chaîne
Object clientObj = data.get("client");
if (clientObj instanceof Map) {
Map<String, Object> clientData = (Map<String, Object>) clientObj;
c.setClient((String) clientData.get("raisonSociale"));
} else if (clientObj instanceof String) {
c.setClient((String) clientObj);
} else {
c.setClient("N/A");
}
c.setAdresse((String) data.get("adresse"));
// Conversion des dates
if (data.get("dateDebut") != null) {
c.setDateDebut(LocalDate.parse(data.get("dateDebut").toString()));
}
if (data.get("dateFinPrevue") != null) {
c.setDateFinPrevue(LocalDate.parse(data.get("dateFinPrevue").toString()));
}
c.setStatut((String) data.get("statut"));
// Avancement en pourcentage
Object avancementObj = data.get("avancement");
if (avancementObj != null) {
c.setAvancement(avancementObj instanceof Integer ?
(Integer) avancementObj :
Integer.parseInt(avancementObj.toString()));
} else {
c.setAvancement(0);
}
// Budget et coût réel
Object montantObj = data.get("montant");
if (montantObj != null) {
c.setBudget(montantObj instanceof Number ?
((Number) montantObj).doubleValue() :
Double.parseDouble(montantObj.toString()));
} else {
c.setBudget(0.0);
}
Object coutReelObj = data.get("coutReel");
if (coutReelObj != null) {
c.setCoutReel(coutReelObj instanceof Number ?
((Number) coutReelObj).doubleValue() :
Double.parseDouble(coutReelObj.toString()));
} else {
c.setCoutReel(0.0);
}
items.add(c);
}
LOG.info("Chantiers chargés depuis l'API : {} élément(s)", items.size());
applyFilters(items, buildFilters());
} catch (Exception e) {
LOG.error("Erreur chargement chantiers", e);
LOG.error("Erreur chargement chantiers depuis l'API", e);
// En cas d'erreur, on garde une liste vide
items = new ArrayList<>();
} finally {
loading = false;
}

View File

@@ -1,7 +1,9 @@
package dev.lions.btpxpress.view;
import dev.lions.btpxpress.service.ClientService;
import jakarta.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import lombok.Getter;
import lombok.Setter;
@@ -12,6 +14,7 @@ import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
@Named("clientsView")
@@ -19,9 +22,12 @@ import java.util.function.Predicate;
@Getter
@Setter
public class ClientsView extends BaseListView<ClientsView.Client, Long> implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(ClientsView.class);
@Inject
ClientService clientService;
private String filtreNom;
private String filtreEmail;
private String filtreVille;
@@ -37,27 +43,63 @@ public class ClientsView extends BaseListView<ClientsView.Client, Long> implemen
loading = true;
try {
items = new ArrayList<>();
for (int i = 1; i <= 25; i++) {
// Récupération depuis l'API backend
List<Map<String, Object>> clientsData = clientService.getAllClients();
for (Map<String, Object> data : clientsData) {
Client c = new Client();
c.setId((long) i);
c.setRaisonSociale("Entreprise " + i);
c.setNomContact("Contact " + i);
c.setEmail("contact" + i + "@example.com");
c.setTelephone("+33 1 " + String.format("%02d", i) + " " +
String.format("%02d", i * 2) + " " +
String.format("%02d", i * 3) + " " +
String.format("%02d", i * 4));
c.setAdresse(i + " Rue Client, " + (75000 + i) + " Paris");
c.setVille("Paris");
c.setCodePostal(String.valueOf(75000 + i));
c.setNombreChantiers(i % 5 + 1);
c.setChiffreAffairesTotal(i * 25000.0);
c.setDateCreation(LocalDateTime.now().minusDays(i * 30));
// Mapping des données de l'API vers l'objet Client
c.setId(data.get("id") != null ? Long.valueOf(data.get("id").toString().hashCode()) : null);
// Raison sociale : entreprise ou "Particulier" si vide
String entreprise = (String) data.get("entreprise");
c.setRaisonSociale(entreprise != null && !entreprise.trim().isEmpty() ?
entreprise : "Particulier");
// Nom complet du contact : prénom + nom
String prenom = (String) data.get("prenom");
String nom = (String) data.get("nom");
c.setNomContact((prenom != null ? prenom + " " : "") + (nom != null ? nom : ""));
c.setEmail((String) data.get("email"));
c.setTelephone((String) data.get("telephone"));
c.setAdresse((String) data.get("adresse"));
c.setVille((String) data.get("ville"));
c.setCodePostal((String) data.get("codePostal"));
// Nombre de chantiers (relation)
Object chantiersObj = data.get("chantiers");
if (chantiersObj instanceof List) {
c.setNombreChantiers(((List<?>) chantiersObj).size());
} else {
c.setNombreChantiers(0);
}
// Chiffre d'affaires total (à calculer ou récupérer)
// Pour l'instant, on met 0 car cette donnée n'est pas dans l'API
c.setChiffreAffairesTotal(0.0);
// Date de création
if (data.get("dateCreation") != null) {
c.setDateCreation(LocalDateTime.parse(data.get("dateCreation").toString()));
}
// Date de modification
if (data.get("dateModification") != null) {
c.setDateModification(LocalDateTime.parse(data.get("dateModification").toString()));
}
items.add(c);
}
LOG.info("Clients chargés depuis l'API : {} élément(s)", items.size());
applyFilters(items, buildFilters());
} catch (Exception e) {
LOG.error("Erreur chargement clients", e);
LOG.error("Erreur chargement clients depuis l'API", e);
// En cas d'erreur, on garde une liste vide
items = new ArrayList<>();
} finally {
loading = false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -64,6 +64,10 @@ public class GuestPreferences implements Serializable {
return this.inputStyle.equals("filled") ? "ui-input-filled" : "";
}
public void setComponentTheme(String componentTheme) {
this.componentTheme = componentTheme;
}
public void onMenuTypeChange() {
if ("layout-horizontal".equals(menuMode)) {
menuTheme = topbarTheme;

View File

@@ -15,4 +15,17 @@
</locale-config>
</application>
<component>
<component-type>org.primefaces.component.FreyaMenu</component-type>
<component-class>org.primefaces.freya.component.FreyaMenu</component-class>
</component>
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.FreyaMenuRenderer</renderer-type>
<renderer-class>org.primefaces.freya.component.FreyaMenuRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>

View File

@@ -17,6 +17,7 @@
<p:dataTable id="#{tableId}"
value="#{viewBean.items}"
var="#{var}"
rowKey="id"
paginator="true"
rows="10"
rowsPerPageTemplate="10,20,50"

View File

@@ -17,31 +17,62 @@
<div class="grid">
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Chantiers suspendus</h6>
<p class="subtitle">Chantiers temporairement suspendus</p>
</div>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Chantiers suspendus</h1>
<p:commandButton value="Nouveau chantier" icon="pi pi-plus"
action="#{chantiersView.createNew()}"
styleClass="ui-button-primary"/>
</div>
<p:dataTable value="#{chantiersView.filteredItems}" var="chantier"
emptyMessage="Aucun chantier suspendu"
styleClass="p-datatable-sm">
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{chantiersView}"/>
<ui:param name="tableId" value="chantiersTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-6">
<h:outputLabel for="filtreNom" value="Nom du chantier"/>
<p:inputText id="filtreNom" value="#{chantiersView.filtreNom}"
placeholder="Rechercher par nom..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-6">
<h:outputLabel for="filtreClient" value="Client"/>
<p:inputText id="filtreClient" value="#{chantiersView.filtreClient}"
placeholder="Rechercher par client..." style="width: 100%;"/>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="chantiersForm"/>
<ui:param name="tableId" value="chantiersTable"/>
<ui:param name="viewBean" value="#{chantiersView}"/>
<ui:param name="var" value="chantier"/>
<ui:param name="title" value="Liste des chantiers suspendus"/>
<ui:param name="createPath" value="/chantiers/nouveau"/>
<ui:define name="columns">
<p:column headerText="Nom" sortBy="#{chantier.nom}">
<h:outputText value="#{chantier.nom}"/>
</p:column>
<p:column headerText="Client" sortBy="#{chantier.client}">
<h:outputText value="#{chantier.client}"/>
</p:column>
<p:column headerText="Date début">
<p:column headerText="Adresse">
<h:outputText value="#{chantier.adresse}"/>
</p:column>
<p:column headerText="Date début" sortBy="#{chantier.dateDebut}">
<h:outputText value="#{chantier.dateDebut}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p:column>
<p:column headerText="Avancement">
<p:progressBar value="#{chantier.avancement}"
<p:progressBar value="#{chantier.avancement}"
showValue="true"
styleClass="ui-progressbar-warn"/>
</p:column>
@@ -51,13 +82,13 @@
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Actions">
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{chantiersView.viewDetails(chantier.id)}"/>
</p:column>
</p:dataTable>
</div>
</ui:define>
</ui:include>
</div>
</div>
</div>

View File

@@ -70,7 +70,7 @@
<div class="col-12 md:col-4">
<h:outputLabel for="pays" value="Pays"/>
<p:inputText id="pays" value="#{clientsView.selectedItem.pays}"
value="France" style="width: 100%;"/>
style="width: 100%;"/>
</div>
<div class="col-12">

View File

@@ -1,325 +1,483 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
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">
<h1>Tableau de bord - BTP Xpress</h1>
<p>Bean dashboardView disponible: #{not empty dashboardView}</p>
<p>Chantiers actifs: #{dashboardView.chantiersActifs}</p>
<p>Test de contenu simple</p>
</div>
</div>
</div>
<!-- ========================================================================
BARRE D'ALERTES (affichée uniquement si alertes critiques)
======================================================================== -->
<p:outputPanel rendered="#{dashboardView.alerteCritique}" styleClass="col-12">
<div class="notification notification-danger">
<i class="pi pi-exclamation-triangle"></i>
<strong>#{dashboardView.totalAlertes} alertes</strong> nécessitent votre attention immédiate
<span style="margin-left: 1rem; opacity: 0.9;">
Maintenance: #{dashboardView.alertesMaintenanceCount} •
Chantiers: #{dashboardView.alertesChantiersCount} •
Disponibilités: #{dashboardView.alertesDisponibilitesCount}
</span>
<p:commandButton value="Rafraîchir"
icon="pi pi-refresh"
action="#{dashboardView.rafraichir}"
update="@form"
styleClass="ui-button-text"
style="float: right;"/>
</div>
</p:outputPanel>
<div class="grid">
<!-- ====================================================================
KPIs PRINCIPAUX (3 cartes en ligne)
==================================================================== -->
<div class="col-12">
<div class="grid" style="margin: -0.5rem;">
<!-- KPI 1: Chantiers Actifs -->
<div class="col-12 md:col-6 xl:col-4">
<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>
<p class="subtitle">
Sur #{dashboardView.nombreChantiers} au total
</p>
<p:progressBar value="#{dashboardView.tauxActiviteChantiers}"
showValue="true"
displayValue="#{dashboardView.tauxActiviteChantiers}%"
styleClass="ui-progressbar-info"/>
</div>
<i class="pi pi-building"></i>
</div>
</div>
<div class="col-12 md:col-3">
<!-- KPI 2: Équipes Disponibles -->
<div class="col-12 md:col-6 xl:col-4">
<div class="card overview-box blue">
<div class="overview-info">
<h6>Clients</h6>
<h1>#{dashboardView.nombreClients}</h1>
<p class="subtitle">Actifs</p>
<h6>Équipes disponibles</h6>
<h1>#{dashboardView.equipesDisponibles}/#{dashboardView.nombreEquipes}</h1>
<p class="subtitle">Taux de disponibilité</p>
<p:progressBar value="#{dashboardView.tauxDisponibiliteEquipes}"
showValue="true"
displayValue="#{dashboardView.tauxDisponibiliteEquipes}%"
style="background: rgba(255,255,255,0.3);"/>
</div>
<i class="pi pi-users"></i>
</div>
</div>
<div class="col-12 md:col-3">
<div class="card overview-box orange">
<!-- KPI 3: Maintenances Critiques -->
<div class="col-12 md:col-12 xl:col-4">
<div class="card overview-box #{dashboardView.alerteRetardMaintenance ? 'red' : 'green'}">
<div class="overview-info">
<h6>Devis en attente</h6>
<h1>#{dashboardView.nombreDevis}</h1>
<p class="subtitle">À traiter</p>
<h6>Maintenances en retard</h6>
<h1>#{dashboardView.maintenancesEnRetard}</h1>
<p class="subtitle">#{dashboardView.maintenancesPlanifiees} planifiées</p>
<p:badge value="#{dashboardView.alerteRetardMaintenance ? 'URGENT' : 'OK'}"
severity="#{dashboardView.alerteRetardMaintenance ? 'danger' : 'success'}"
style="margin-top: 0.5rem;"/>
</div>
<i class="pi pi-file-edit"></i>
<i class="pi pi-wrench"></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>
</div>
<!-- ====================================================================
SECTION CENTRALE : Graphique + KPIs Ressources
==================================================================== -->
<!-- Colonne gauche: Statistiques chantiers (placeholder pour graphique futur) -->
<div class="col-12 xl:col-8">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Vue d'ensemble</h6>
<p class="subtitle">Statistiques globales</p>
</div>
</div>
<div class="grid">
<!-- Chantiers actifs avec progression -->
<div class="col-12 md:col-6">
<div class="statistic-item" style="padding: 1.5rem; background: var(--blue-50); border-radius: var(--border-radius); border-left: 4px solid var(--blue-500);">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem;">
<i class="pi pi-building" style="font-size: 2rem; color: var(--blue-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.75rem; color: var(--blue-600);">#{dashboardView.chantiersActifs}</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Chantiers actifs</h6>
</div>
</div>
<p:progressBar value="#{dashboardView.tauxActiviteChantiers}"
showValue="true"
displayValue="#{dashboardView.tauxActiviteChantiers}% d'activité"
styleClass="ui-progressbar-info"
style="height: 1rem;"/>
</div>
</div>
<!-- Chantiers en retard -->
<div class="col-12 md:col-6">
<div class="statistic-item" style="padding: 1.5rem; background: var(--orange-50); border-radius: var(--border-radius); border-left: 4px solid var(--orange-500);">
<div style="display: flex; align-items: center; gap: 1rem;">
<i class="pi pi-exclamation-triangle" style="font-size: 2rem; color: var(--orange-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.75rem; color: var(--orange-600);">#{dashboardView.chantiersEnRetardList.size()}</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Chantiers en retard</h6>
</div>
</div>
<p:outputPanel rendered="#{dashboardView.chantiersEnRetardList.size() > 0}">
<small style="display: block; margin-top: 0.75rem; color: var(--orange-700);">
<i class="pi pi-info-circle"></i> Attention requise
</small>
</p:outputPanel>
</div>
</div>
<!-- Événements aujourd'hui -->
<div class="col-12 md:col-6">
<div class="statistic-item" style="padding: 1.5rem; background: var(--purple-50); border-radius: var(--border-radius); border-left: 4px solid var(--purple-500);">
<div style="display: flex; align-items: center; gap: 1rem;">
<i class="pi pi-calendar" style="font-size: 2rem; color: var(--purple-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.75rem; color: var(--purple-600);">#{dashboardView.evenementsAujourdhui}</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Événements aujourd'hui</h6>
</div>
</div>
</div>
</div>
<!-- Documents totaux -->
<div class="col-12 md:col-6">
<div class="statistic-item" style="padding: 1.5rem; background: var(--cyan-50); border-radius: var(--border-radius); border-left: 4px solid var(--cyan-500);">
<div style="display: flex; align-items: center; gap: 1rem;">
<i class="pi pi-file" style="font-size: 2rem; color: var(--cyan-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.75rem; color: var(--cyan-600);">#{dashboardView.nombreDocuments}</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Documents totaux</h6>
</div>
</div>
</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"/>
<!-- Colonne droite: KPIs Ressources -->
<div class="col-12 xl:col-4">
<div class="card" style="height: 100%;">
<div class="card-header">
<div class="card-title">
<h6>Ressources</h6>
<p class="subtitle">État actuel des ressources</p>
</div>
</div>
</div>
</p:outputPanel>
<div style="padding: 1rem; display: flex; flex-direction: column; gap: 1.5rem;">
<!-- 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>
<!-- Employés actifs -->
<div class="statistic-item" style="padding: 1.25rem; background: var(--green-50); border-radius: var(--border-radius); border-left: 4px solid var(--green-500);">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem;">
<i class="pi pi-users" style="font-size: 1.75rem; color: var(--green-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.5rem; color: var(--green-600);">
#{dashboardView.employesActifs}<span style="font-size: 1rem; color: var(--text-color-secondary);">/#{dashboardView.nombreEmployes}</span>
</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Employés actifs</h6>
</div>
</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}"
<p:progressBar value="#{dashboardView.tauxActiviteEmployes}"
showValue="true"
styleClass="ui-progressbar-info"/>
<small style="color: var(--text-color-secondary);">
#{dashboardView.equipesDisponibles} disponibles
displayValue="#{dashboardView.tauxActiviteEmployes}%"
styleClass="ui-progressbar-#{dashboardView.tauxActiviteEmployes > 80 ? 'success' : (dashboardView.tauxActiviteEmployes > 60 ? 'warning' : 'danger')}"
style="height: 1rem;"/>
</div>
<!-- Matériel disponible -->
<div class="statistic-item" style="padding: 1.25rem; background: var(--teal-50); border-radius: var(--border-radius); border-left: 4px solid var(--teal-500);">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.75rem;">
<i class="pi pi-cog" style="font-size: 1.75rem; color: var(--teal-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.5rem; color: var(--teal-600);">
#{dashboardView.materielDisponible}<span style="font-size: 1rem; color: var(--text-color-secondary);">/#{dashboardView.nombreMateriel}</span>
</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Matériel disponible</h6>
</div>
</div>
<p:progressBar value="#{dashboardView.tauxDisponibiliteMateriel}"
showValue="true"
displayValue="#{dashboardView.tauxDisponibiliteMateriel}%"
styleClass="ui-progressbar-success"
style="height: 1rem;"/>
</div>
<!-- Taux d'utilisation global -->
<div class="statistic-item" style="padding: 1.25rem; background: var(--indigo-50); border-radius: var(--border-radius); border-left: 4px solid var(--indigo-500); flex: 1; display: flex; flex-direction: column; justify-content: center;">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem;">
<i class="pi pi-chart-line" style="font-size: 1.75rem; color: var(--indigo-500);"></i>
<div style="flex: 1;">
<h5 style="margin: 0; font-size: 1.75rem; color: var(--indigo-600);">#{dashboardView.tauxUtilisationGlobal}%</h5>
<h6 style="margin: 0.25rem 0 0 0; font-weight: 500; color: var(--text-color-secondary);">Taux d'utilisation global</h6>
</div>
</div>
<small style="display: block; color: var(--text-color-secondary); font-style: italic; padding-left: 2.75rem;">
<i class="pi pi-info-circle" style="font-size: 0.875rem;"></i>
Moyenne chantiers, employés et matériel
</small>
</div>
</div>
</div>
</div>
<div class="col-12 lg:col-4">
<!-- ====================================================================
TABLEAU CHANTIERS ACTIFS
==================================================================== -->
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Matériel</h6>
<p class="subtitle">Équipements disponibles</p>
<h6>Chantiers actifs</h6>
<p class="subtitle">#{dashboardView.chantiersActifsList.size()} chantiers en cours</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"
<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">
<p:dataTable value="#{dashboardView.chantiersActifsList}"
var="chantier"
emptyMessage="Aucun chantier actif pour le moment"
styleClass="p-datatable-sm"
paginator="true"
rows="10"
paginatorPosition="bottom">
<p:column headerText="Nom" sortBy="#{chantier.nom}">
<h:outputText value="#{chantier.nom}"/>
</p:column>
<p:column headerText="Client">
<p:column headerText="Client" sortBy="#{chantier.client}">
<h:outputText value="#{chantier.client}"/>
</p:column>
<p:column headerText="Date de début">
<p:column headerText="Date début" sortBy="#{chantier.dateDebut}">
<h:outputText value="#{chantier.dateDebutFormatee}"/>
</p:column>
<p:column headerText="Fin prévue" sortBy="#{chantier.dateFinPrevue}">
<h:outputText value="#{chantier.dateFinPrevueFormatee}"/>
</p:column>
<p:column headerText="Avancement">
<p:progressBar value="#{chantier.avancement}"
showValue="true"
styleClass="ui-progressbar-success"/>
<p:progressBar value="#{chantier.avancement}"
showValue="true"
displayValue="#{chantier.avancement}%"
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 headerText="Budget" sortBy="#{chantier.budget}">
<h:outputText value="#{chantier.budget}">
<f:convertNumber type="number" groupingUsed="true"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Coût réel" sortBy="#{chantier.coutReel}">
<h:outputText value="#{chantier.coutReel}"
style="#{chantier.depassementBudget ? 'color: var(--red-500); font-weight: bold;' : ''}">
<f:convertNumber type="number" groupingUsed="true"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
<p:badge value="!" severity="danger"
style="margin-left: 0.5rem;"
rendered="#{chantier.depassementBudget}"/>
</p:column>
<p:column headerText="Statut">
<p:badge value="#{chantier.statut}"
severity="#{chantier.statut == 'EN_COURS' ? 'info' : 'success'}"/>
</p:column>
</p:dataTable>
</div>
</div>
<!-- ====================================================================
SECTION BAS : Chantiers en retard + Maintenances en retard
==================================================================== -->
<!-- Chantiers en retard -->
<div class="col-12 lg:col-4">
<div class="col-12 md:col-6">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Chantiers en retard</h6>
<p class="subtitle">Attention requise</p>
<p class="subtitle">#{dashboardView.chantiersEnRetardList.size()} chantiers en retard</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>
<ui:repeat value="#{dashboardView.chantiersEnRetardList}" var="chantier">
<div class="chantier-retard-item" style="padding: 1rem; border-bottom: 1px solid var(--surface-border); background: var(--orange-50);">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div>
<h6 style="margin: 0 0 0.5rem 0;">
<i class="pi pi-building" style="color: var(--orange-500);"></i>
#{chantier.nom}
</h6>
<p style="margin: 0.25rem 0; font-size: 0.9rem;">
<strong>Date fin prévue:</strong> #{chantier.dateFinPrevueFormatee}
</p>
</div>
<p:badge value="+#{chantier.joursRetard}j" severity="warning" size="large"/>
</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>
</ui:repeat>
<p:outputPanel rendered="#{empty dashboardView.chantiersEnRetardList}">
<div style="padding: 2rem; text-align: center; color: var(--green-500);">
<i class="pi pi-check-circle" style="font-size: 3rem;"></i>
<p style="margin-top: 1rem;">Tous les chantiers sont dans les temps</p>
</div>
</p:outputPanel>
</div>
</div>
<!-- Maintenances en retard -->
<div class="col-12 md:col-6">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Maintenances en retard</h6>
<p class="subtitle">#{dashboardView.maintenancesEnRetardList.size()} maintenances urgentes</p>
</div>
</div>
<ui:repeat value="#{dashboardView.maintenancesEnRetardList}" var="maintenance">
<div class="maintenance-item" style="padding: 1rem; border-bottom: 1px solid var(--surface-border); background: var(--red-50);">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div>
<h6 style="margin: 0 0 0.5rem 0;">
<i class="pi pi-wrench" style="color: var(--red-500);"></i>
#{maintenance.materiel}
</h6>
<p style="margin: 0.25rem 0; font-size: 0.9rem;">
<strong>Type:</strong> #{maintenance.type} •
<strong>Prévue:</strong> #{maintenance.datePrevueFormatee}
</p>
<p style="margin: 0.25rem 0; font-size: 0.85rem; color: var(--text-color-secondary);">
#{maintenance.description}
</p>
</div>
<p:badge value="+#{maintenance.joursRetard}j" severity="danger" size="large"/>
</div>
</div>
</ui:repeat>
<p:outputPanel rendered="#{empty dashboardView.maintenancesEnRetardList}">
<div style="padding: 2rem; text-align: center; color: var(--green-500);">
<i class="pi pi-check-circle" style="font-size: 3rem;"></i>
<p style="margin-top: 1rem;">Toutes les maintenances sont à jour</p>
</div>
</p:outputPanel>
</div>
</div>
<!-- ====================================================================
SECTION BAS 2 : Disponibilités en attente + Documents récents
==================================================================== -->
<!-- Disponibilités en attente -->
<div class="col-12 md:col-6">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Disponibilités en attente</h6>
<p class="subtitle">#{dashboardView.disponibilitesEnAttenteList.size()} demandes à valider</p>
</div>
</div>
<ui:repeat value="#{dashboardView.disponibilitesEnAttenteList}" var="dispo">
<div class="disponibilite-card" style="padding: 1rem; border-bottom: 1px solid var(--surface-border);">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div style="flex: 1;">
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;">
<i class="pi pi-user"></i>
<strong>#{dispo.employe}</strong>
</div>
<div style="margin: 0.5rem 0;">
<p:badge value="#{dispo.type}"
severity="#{dashboardView.getSeveriteDisponibilite(dispo.type)}"/>
<span style="margin-left: 0.5rem; font-size: 0.9rem;">
Du #{dispo.dateDebutFormatee} au #{dispo.dateFinFormatee}
(#{dispo.nombreJours} jours)
</span>
</div>
<small style="color: var(--text-color-secondary);">
<strong>Motif:</strong> #{dispo.motif}
</small>
</div>
</div>
</div>
</ui:repeat>
<p:outputPanel rendered="#{empty dashboardView.disponibilitesEnAttenteList}">
<div style="padding: 2rem; text-align: center; color: var(--text-color-secondary);">
<i class="pi pi-inbox" style="font-size: 2rem;"></i>
<p style="margin-top: 1rem;">Aucune demande de disponibilité en attente</p>
</div>
</p:outputPanel>
</div>
</div>
<!-- Documents récents -->
<div class="col-12 md:col-6">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Documents récents</h6>
<p class="subtitle">5 derniers documents ajoutés</p>
</div>
</div>
<ul class="documents-list" style="list-style: none; padding: 0; margin: 0;">
<ui:repeat value="#{dashboardView.documentsRecentsList}" var="doc">
<li style="padding: 1rem; border-bottom: 1px solid var(--surface-border); display: flex; align-items: center; gap: 1rem;">
<i class="#{dashboardView.getIconeDocument(doc.type)}"
style="font-size: 2rem; color: var(--primary-color);"></i>
<div style="flex: 1;">
<div style="font-weight: 500;">#{doc.nom}</div>
<small style="color: var(--text-color-secondary);">
#{doc.type} • Ajouté le #{doc.dateCreationFormatee}
</small>
</div>
<p:button icon="pi pi-download" styleClass="ui-button-text ui-button-sm"/>
</li>
</ui:repeat>
</ul>
<p:outputPanel rendered="#{empty dashboardView.documentsRecentsList}">
<div style="padding: 2rem; text-align: center; color: var(--text-color-secondary);">
<i class="pi pi-file" style="font-size: 2rem;"></i>
<p style="margin-top: 1rem;">Aucun document récent</p>
</div>
</p:outputPanel>
</div>
</div>

View File

@@ -1,8 +1,8 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">Devis - BTP Xpress</ui:define>
@@ -12,12 +12,101 @@
<div class="grid">
<div class="col-12">
<div class="card">
<h1>Gestion des Devis</h1>
<p>Module en cours de développement...</p>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Gestion des Devis</h1>
<p:commandButton value="Nouveau devis" icon="pi pi-plus"
action="#{devisView.createNew()}"
styleClass="ui-button-primary"/>
</div>
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{devisView}"/>
<ui:param name="tableId" value="devisTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-4">
<h:outputLabel for="filtreNumero" value="Numéro"/>
<p:inputText id="filtreNumero" value="#{devisView.filtreNumero}"
placeholder="Rechercher par numéro..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreClient" value="Client"/>
<p:inputText id="filtreClient" value="#{devisView.filtreClient}"
placeholder="Rechercher par client..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreStatut" value="Statut"/>
<p:selectOneMenu id="filtreStatut" value="#{devisView.filtreStatut}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Brouillon" itemValue="BROUILLON"/>
<f:selectItem itemLabel="En attente" itemValue="EN_ATTENTE"/>
<f:selectItem itemLabel="Accepté" itemValue="ACCEPTE"/>
<f:selectItem itemLabel="Refusé" itemValue="REFUSE"/>
<f:selectItem itemLabel="Expiré" itemValue="EXPIRE"/>
</p:selectOneMenu>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="devisForm"/>
<ui:param name="tableId" value="devisTable"/>
<ui:param name="viewBean" value="#{devisView}"/>
<ui:param name="var" value="devis"/>
<ui:param name="title" value="Liste des devis"/>
<ui:param name="createPath" value="/devis/nouveau"/>
<ui:define name="columns">
<p:column headerText="Numéro" sortBy="#{devis.numero}">
<h:outputText value="#{devis.numero}"/>
</p:column>
<p:column headerText="Objet" sortBy="#{devis.objet}">
<h:outputText value="#{devis.objet}"/>
</p:column>
<p:column headerText="Client" sortBy="#{devis.client}">
<h:outputText value="#{devis.client}"/>
</p:column>
<p:column headerText="Date émission" sortBy="#{devis.dateEmission}">
<h:outputText value="#{devis.dateEmission}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p:column>
<p:column headerText="Date validité" sortBy="#{devis.dateValidite}">
<h:outputText value="#{devis.dateValidite}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p:column>
<p:column headerText="Montant HT">
<h:outputText value="#{devis.montantHT}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Montant TTC">
<h:outputText value="#{devis.montantTTC}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Statut" sortBy="#{devis.statut}">
<p:tag value="#{devis.statut}"
severity="#{devis.statut == 'ACCEPTE' ? 'success' : (devis.statut == 'REFUSE' ? 'danger' : (devis.statut == 'EXPIRE' ? 'warning' : 'info'))}"/>
</p:column>
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{devisView.viewDetails(devis.id)}"/>
</p:column>
</ui:define>
</ui:include>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +1,27 @@
<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">Acceptes - DEVIS - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>Acceptes</h1><p>Module en cours de développement...</p><p:commandButton value="Retour" icon="pi pi-arrow-left" outcome="/devis" styleClass="ui-button-secondary"/></div></div></div></div></ui:define></ui:composition>
<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">Devis acceptés - BTP Xpress</ui:define>
<ui:define name="content">
<div class="layout-dashboard">
<div class="grid">
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Devis acceptés</h6>
<p class="subtitle">Devis acceptés par les clients</p>
</div>
</div>
<p>Page en développement</p>
</div>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +1,27 @@
<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">Expires - DEVIS - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>Expires</h1><p>Module en cours de développement...</p><p:commandButton value="Retour" icon="pi pi-arrow-left" outcome="/devis" styleClass="ui-button-secondary"/></div></div></div></div></ui:define></ui:composition>
<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">Devis expirés - BTP Xpress</ui:define>
<ui:define name="content">
<div class="layout-dashboard">
<div class="grid">
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Devis expirés</h6>
<p class="subtitle">Devis dont la validité est expirée</p>
</div>
</div>
<p>Page en développement</p>
</div>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +0,0 @@
<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">DEVIS - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>DEVIS</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1,8 +1,8 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">Employés - BTP Xpress</ui:define>
@@ -12,12 +12,91 @@
<div class="grid">
<div class="col-12">
<div class="card">
<h1>Gestion des Employés</h1>
<p>Module en cours de développement...</p>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Gestion des Employés</h1>
<p:commandButton value="Nouvel employé" icon="pi pi-user-plus"
action="#{employeView.createNew()}"
styleClass="ui-button-primary"/>
</div>
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{employeView}"/>
<ui:param name="tableId" value="employesTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-4">
<h:outputLabel for="filtreNom" value="Nom"/>
<p:inputText id="filtreNom" value="#{employeView.filtreNom}"
placeholder="Rechercher par nom..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtrePoste" value="Poste"/>
<p:inputText id="filtrePoste" value="#{employeView.filtrePoste}"
placeholder="Rechercher par poste..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreStatut" value="Statut"/>
<p:selectOneMenu id="filtreStatut" value="#{employeView.filtreStatut}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Actif" itemValue="ACTIF"/>
<f:selectItem itemLabel="Inactif" itemValue="INACTIF"/>
<f:selectItem itemLabel="En congé" itemValue="EN_CONGE"/>
</p:selectOneMenu>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="employesForm"/>
<ui:param name="tableId" value="employesTable"/>
<ui:param name="viewBean" value="#{employeView}"/>
<ui:param name="var" value="employe"/>
<ui:param name="title" value="Liste des employés"/>
<ui:param name="createPath" value="/employes/nouveau"/>
<ui:define name="columns">
<p:column headerText="Nom complet" sortBy="#{employe.nomComplet}">
<h:outputText value="#{employe.nomComplet}"/>
</p:column>
<p:column headerText="Email" sortBy="#{employe.email}">
<h:outputText value="#{employe.email}"/>
</p:column>
<p:column headerText="Téléphone">
<h:outputText value="#{employe.telephone}"/>
</p:column>
<p:column headerText="Poste" sortBy="#{employe.poste}">
<h:outputText value="#{employe.poste}"/>
</p:column>
<p:column headerText="Taux horaire">
<h:outputText value="#{employe.tauxHoraire}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa/h"/>
</p:column>
<p:column headerText="Date embauche" sortBy="#{employe.dateEmbauche}">
<h:outputText value="#{employe.dateEmbauche}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p:column>
<p:column headerText="Statut" sortBy="#{employe.statut}">
<p:tag value="#{employe.statut}"
severity="#{employe.statut == 'ACTIF' ? 'success' : 'warning'}"/>
</p:column>
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{employeView.viewDetails(employe.id)}"/>
</p:column>
</ui:define>
</ui:include>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +0,0 @@
<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">EMPLOYES - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>EMPLOYES</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1,8 +1,8 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">Équipes - BTP Xpress</ui:define>
@@ -12,12 +12,82 @@
<div class="grid">
<div class="col-12">
<div class="card">
<h1>Gestion des Équipes</h1>
<p>Module en cours de développement...</p>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Gestion des Équipes</h1>
<p:commandButton value="Nouvelle équipe" icon="pi pi-users"
action="#{equipeView.createNew()}"
styleClass="ui-button-primary"/>
</div>
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{equipeView}"/>
<ui:param name="tableId" value="equipesTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-4">
<h:outputLabel for="filtreNom" value="Nom de l'équipe"/>
<p:inputText id="filtreNom" value="#{equipeView.filtreNom}"
placeholder="Rechercher par nom..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreSpecialite" value="Spécialité"/>
<p:inputText id="filtreSpecialite" value="#{equipeView.filtreSpecialite}"
placeholder="Rechercher par spécialité..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreStatut" value="Statut"/>
<p:selectOneMenu id="filtreStatut" value="#{equipeView.filtreStatut}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Active" itemValue="ACTIVE"/>
<f:selectItem itemLabel="Inactive" itemValue="INACTIVE"/>
</p:selectOneMenu>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="equipesForm"/>
<ui:param name="tableId" value="equipesTable"/>
<ui:param name="viewBean" value="#{equipeView}"/>
<ui:param name="var" value="equipe"/>
<ui:param name="title" value="Liste des équipes"/>
<ui:param name="createPath" value="/equipes/nouvelle"/>
<ui:define name="columns">
<p:column headerText="Nom" sortBy="#{equipe.nom}">
<h:outputText value="#{equipe.nom}"/>
</p:column>
<p:column headerText="Chef d'équipe" sortBy="#{equipe.chef}">
<h:outputText value="#{equipe.chef}"/>
</p:column>
<p:column headerText="Spécialité" sortBy="#{equipe.specialite}">
<h:outputText value="#{equipe.specialite}"/>
</p:column>
<p:column headerText="Nombre de membres">
<p:tag value="#{equipe.nombreMembres}" severity="info"/>
</p:column>
<p:column headerText="Description">
<h:outputText value="#{equipe.description}"/>
</p:column>
<p:column headerText="Statut" sortBy="#{equipe.statut}">
<p:tag value="#{equipe.statut}"
severity="#{equipe.statut == 'ACTIVE' ? 'success' : 'warning'}"/>
</p:column>
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{equipeView.viewDetails(equipe.id)}"/>
</p:column>
</ui:define>
</ui:include>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +0,0 @@
<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">EQUIPES - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>EQUIPES</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +0,0 @@
<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">Nouvelle - EQUIPES - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>Nouvelle</h1><p>Module en cours de développement...</p><p:commandButton value="Retour" icon="pi pi-arrow-left" outcome="/equipes" styleClass="ui-button-secondary"/></div></div></div></div></ui:define></ui:composition>

View File

@@ -1,8 +1,8 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">Factures - BTP Xpress</ui:define>
@@ -12,12 +12,111 @@
<div class="grid">
<div class="col-12">
<div class="card">
<h1>Gestion des Factures</h1>
<p>Module en cours de développement...</p>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Gestion des Factures</h1>
<p:commandButton value="Nouvelle facture" icon="pi pi-plus"
action="#{factureView.createNew()}"
styleClass="ui-button-primary"/>
</div>
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{factureView}"/>
<ui:param name="tableId" value="facturesTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-4">
<h:outputLabel for="filtreNumero" value="Numéro"/>
<p:inputText id="filtreNumero" value="#{factureView.filtreNumero}"
placeholder="Rechercher par numéro..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreClient" value="Client"/>
<p:inputText id="filtreClient" value="#{factureView.filtreClient}"
placeholder="Rechercher par client..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreStatut" value="Statut"/>
<p:selectOneMenu id="filtreStatut" value="#{factureView.filtreStatut}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Brouillon" itemValue="BROUILLON"/>
<f:selectItem itemLabel="Émise" itemValue="EMISE"/>
<f:selectItem itemLabel="Envoyée" itemValue="ENVOYEE"/>
<f:selectItem itemLabel="Payée" itemValue="PAYEE"/>
<f:selectItem itemLabel="En retard" itemValue="EN_RETARD"/>
<f:selectItem itemLabel="Annulée" itemValue="ANNULEE"/>
</p:selectOneMenu>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="facturesForm"/>
<ui:param name="tableId" value="facturesTable"/>
<ui:param name="viewBean" value="#{factureView}"/>
<ui:param name="var" value="facture"/>
<ui:param name="title" value="Liste des factures"/>
<ui:param name="createPath" value="/factures/nouvelle"/>
<ui:define name="columns">
<p:column headerText="Numéro" sortBy="#{facture.numero}">
<h:outputText value="#{facture.numero}"/>
</p:column>
<p:column headerText="Objet" sortBy="#{facture.objet}">
<h:outputText value="#{facture.objet}"/>
</p:column>
<p:column headerText="Client" sortBy="#{facture.client}">
<h:outputText value="#{facture.client}"/>
</p:column>
<p:column headerText="Date émission" sortBy="#{facture.dateEmission}">
<h:outputText value="#{facture.dateEmission}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p:column>
<p:column headerText="Date échéance" sortBy="#{facture.dateEcheance}">
<h:outputText value="#{facture.dateEcheance}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
<h:outputText value=" ⚠️" rendered="#{factureView.isEnRetard(facture)}"
title="Facture en retard" style="color: red;"/>
</p:column>
<p:column headerText="Montant TTC">
<h:outputText value="#{facture.montantTTC}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Montant payé">
<h:outputText value="#{facture.montantPaye}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Reste à payer">
<h:outputText value="#{factureView.getMontantRestant(facture)}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"
style="#{factureView.getMontantRestant(facture) > 0 ? 'color: red; font-weight: bold;' : ''}"/>
</p:column>
<p:column headerText="Statut" sortBy="#{facture.statut}">
<p:tag value="#{facture.statut}"
severity="#{facture.statut == 'PAYEE' ? 'success' : (facture.statut == 'ANNULEE' ? 'danger' : (factureView.isEnRetard(facture) ? 'danger' : 'warning'))}"/>
</p:column>
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{factureView.viewDetails(facture.id)}"/>
</p:column>
</ui:define>
</ui:include>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +0,0 @@
<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">FACTURES - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>FACTURES</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +0,0 @@
<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">Nouvelle - FACTURES - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>Nouvelle</h1><p>Module en cours de développement...</p><p:commandButton value="Retour" icon="pi pi-arrow-left" outcome="/factures" styleClass="ui-button-secondary"/></div></div></div></div></ui:define></ui:composition>

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<meta http-equiv="refresh" content="0;url=dashboard.xhtml" />
<title>BTP Xpress - Redirection</title>
</h:head>
<h:body>
<h:outputText value="Redirection en cours..." />
</h:body>
</html>

View File

@@ -1 +0,0 @@
<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">MAINTENANCE - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>MAINTENANCE</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +1,27 @@
<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">Preventive - MAINTENANCE - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>Preventive</h1><p>Module en cours de développement...</p><p:commandButton value="Retour" icon="pi pi-arrow-left" outcome="/maintenance" styleClass="ui-button-secondary"/></div></div></div></div></ui:define></ui:composition>
<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">Maintenance préventive - BTP Xpress</ui:define>
<ui:define name="content">
<div class="layout-dashboard">
<div class="grid">
<div class="col-12">
<div class="card">
<div class="card-header">
<div class="card-title">
<h6>Maintenance préventive</h6>
<p class="subtitle">Maintenances préventives planifiées</p>
</div>
</div>
<p>Page en développement</p>
</div>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1,8 +1,8 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">Matériels - BTP Xpress</ui:define>
@@ -12,12 +12,100 @@
<div class="grid">
<div class="col-12">
<div class="card">
<h1>Inventaire des Matériels</h1>
<p>Module en cours de développement...</p>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Gestion des Matériels</h1>
<p:commandButton value="Nouveau matériel" icon="pi pi-wrench"
action="#{materielView.createNew()}"
styleClass="ui-button-primary"/>
</div>
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{materielView}"/>
<ui:param name="tableId" value="materielsTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-4">
<h:outputLabel for="filtreNom" value="Nom"/>
<p:inputText id="filtreNom" value="#{materielView.filtreNom}"
placeholder="Rechercher par nom..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreType" value="Type"/>
<p:selectOneMenu id="filtreType" value="#{materielView.filtreType}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Engin" itemValue="ENGIN"/>
<f:selectItem itemLabel="Outil" itemValue="OUTIL"/>
<f:selectItem itemLabel="Véhicule" itemValue="VEHICULE"/>
<f:selectItem itemLabel="Équipement" itemValue="EQUIPEMENT"/>
</p:selectOneMenu>
</div>
<div class="col-12 md:col-4">
<h:outputLabel for="filtreStatut" value="Statut"/>
<p:selectOneMenu id="filtreStatut" value="#{materielView.filtreStatut}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Disponible" itemValue="DISPONIBLE"/>
<f:selectItem itemLabel="En service" itemValue="EN_SERVICE"/>
<f:selectItem itemLabel="En maintenance" itemValue="EN_MAINTENANCE"/>
<f:selectItem itemLabel="Hors service" itemValue="HORS_SERVICE"/>
</p:selectOneMenu>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="materielsForm"/>
<ui:param name="tableId" value="materielsTable"/>
<ui:param name="viewBean" value="#{materielView}"/>
<ui:param name="var" value="materiel"/>
<ui:param name="title" value="Liste des matériels"/>
<ui:param name="createPath" value="/materiels/nouveau"/>
<ui:define name="columns">
<p:column headerText="Nom" sortBy="#{materiel.nom}">
<h:outputText value="#{materiel.nom}"/>
</p:column>
<p:column headerText="Type" sortBy="#{materiel.type}">
<p:tag value="#{materiel.type}" severity="info"/>
</p:column>
<p:column headerText="Marque" sortBy="#{materiel.marque}">
<h:outputText value="#{materiel.marque}"/>
</p:column>
<p:column headerText="Modèle">
<h:outputText value="#{materiel.modele}"/>
</p:column>
<p:column headerText="N° série">
<h:outputText value="#{materiel.numeroSerie}"/>
</p:column>
<p:column headerText="Valeur d'achat">
<h:outputText value="#{materiel.valeurAchat}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Date achat" sortBy="#{materiel.dateAchat}">
<h:outputText value="#{materiel.dateAchat}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p:column>
<p:column headerText="Statut" sortBy="#{materiel.statut}">
<p:tag value="#{materiel.statut}"
severity="#{materiel.statut == 'DISPONIBLE' ? 'success' : (materiel.statut == 'HORS_SERVICE' ? 'danger' : 'warning')}"/>
</p:column>
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{materielView.viewDetails(materiel.id)}"/>
</p:column>
</ui:define>
</ui:include>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +0,0 @@
<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">MATERIELS - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>MATERIELS</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +0,0 @@
<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">MESSAGES - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>MESSAGES</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +0,0 @@
<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">NOTIFICATIONS - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>NOTIFICATIONS</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +0,0 @@
<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">PLANNING - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>PLANNING</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1 +0,0 @@
<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">RAPPORTS - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>RAPPORTS</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -1,8 +1,8 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/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"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/template.xhtml">
<ui:define name="title">Stock - BTP Xpress</ui:define>
@@ -12,12 +12,110 @@
<div class="grid">
<div class="col-12">
<div class="card">
<h1>Gestion du Stock</h1>
<p>Module en cours de développement...</p>
<div class="flex align-items-center justify-content-between mb-3">
<h1>Gestion du Stock</h1>
<p:commandButton value="Nouvel article" icon="pi pi-box"
action="#{stockView.createNew()}"
styleClass="ui-button-primary"/>
</div>
</div>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-filters.xhtml">
<ui:param name="formId" value="filtresForm"/>
<ui:param name="viewBean" value="#{stockView}"/>
<ui:param name="tableId" value="stocksTable"/>
<ui:define name="filter-fields">
<div class="grid">
<div class="col-12 md:col-3">
<h:outputLabel for="filtreReference" value="Référence"/>
<p:inputText id="filtreReference" value="#{stockView.filtreReference}"
placeholder="Rechercher par référence..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-3">
<h:outputLabel for="filtreDesignation" value="Désignation"/>
<p:inputText id="filtreDesignation" value="#{stockView.filtreDesignation}"
placeholder="Rechercher par désignation..." style="width: 100%;"/>
</div>
<div class="col-12 md:col-3">
<h:outputLabel for="filtreCategorie" value="Catégorie"/>
<p:selectOneMenu id="filtreCategorie" value="#{stockView.filtreCategorie}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Matériaux" itemValue="MATERIAUX"/>
<f:selectItem itemLabel="Outillage" itemValue="OUTILLAGE"/>
<f:selectItem itemLabel="Équipement" itemValue="EQUIPEMENT"/>
<f:selectItem itemLabel="Consommables" itemValue="CONSOMMABLES"/>
</p:selectOneMenu>
</div>
<div class="col-12 md:col-3">
<h:outputLabel for="filtreStatut" value="Statut"/>
<p:selectOneMenu id="filtreStatut" value="#{stockView.filtreStatut}" style="width: 100%;">
<f:selectItem itemLabel="Tous" itemValue="TOUS"/>
<f:selectItem itemLabel="Disponible" itemValue="DISPONIBLE"/>
<f:selectItem itemLabel="En rupture" itemValue="EN_RUPTURE"/>
<f:selectItem itemLabel="Seuil d'alerte" itemValue="SEUIL_ALERTE"/>
</p:selectOneMenu>
</div>
</div>
</ui:define>
</ui:include>
</div>
<div class="col-12">
<ui:include src="/WEB-INF/components/liste-table.xhtml">
<ui:param name="formId" value="stocksForm"/>
<ui:param name="tableId" value="stocksTable"/>
<ui:param name="viewBean" value="#{stockView}"/>
<ui:param name="var" value="stock"/>
<ui:param name="title" value="Inventaire du stock"/>
<ui:param name="createPath" value="/stock/nouveau"/>
<ui:define name="columns">
<p:column headerText="Référence" sortBy="#{stock.reference}">
<h:outputText value="#{stock.reference}"/>
</p:column>
<p:column headerText="Désignation" sortBy="#{stock.designation}">
<h:outputText value="#{stock.designation}"/>
</p:column>
<p:column headerText="Catégorie" sortBy="#{stock.categorie}">
<p:tag value="#{stock.categorie}" severity="info"/>
</p:column>
<p:column headerText="Quantité disponible">
<h:outputText value="#{stock.quantiteDisponible}"
style="#{stockView.isEnAlerte(stock) ? 'color: red; font-weight: bold;' : ''}"/>
<h:outputText value=" #{stock.uniteMesure}"/>
<h:outputText value=" ⚠️" rendered="#{stockView.isEnAlerte(stock)}"
title="Stock en alerte" style="color: red;"/>
</p:column>
<p:column headerText="Seuil d'alerte">
<h:outputText value="#{stock.seuilAlerte}"/>
<h:outputText value=" #{stock.uniteMesure}"/>
</p:column>
<p:column headerText="Prix unitaire">
<h:outputText value="#{stock.prixUnitaire}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Valeur totale">
<h:outputText value="#{stock.quantiteDisponible * stock.prixUnitaire}">
<f:converter converterId="fcfaConverter"/>
</h:outputText>
<h:outputText value=" Fcfa"/>
</p:column>
<p:column headerText="Statut" sortBy="#{stock.statut}">
<p:tag value="#{stock.statut}"
severity="#{stock.statut == 'DISPONIBLE' ? 'success' : 'danger'}"/>
</p:column>
<p:column headerText="Actions" style="width: 150px;">
<p:commandButton icon="pi pi-eye" title="Voir les détails"
styleClass="ui-button-text"
action="#{stockView.viewDetails(stock.id)}"/>
</p:column>
</ui:define>
</ui:include>
</div>
</div>
</div>
</ui:define>
</ui:composition>

View File

@@ -1 +0,0 @@
<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">STOCK - BTP Xpress</ui:define><ui:define name="content"><div class="layout-dashboard"><div class="grid"><div class="col-12"><div class="card"><h1>STOCK</h1><p>Module en cours de développement...</p></div></div></div></div></ui:define></ui:composition>

View File

@@ -31,6 +31,18 @@
<param-name>jakarta.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>jakarta.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/primefaces-freya.taglib.xml</param-value>
</context-param>
<context-param>
<param-name>jakarta.faces.FACELETS_REFRESH_PERIOD</param-name>
<param-value>0</param-value>
</context-param>
<context-param>
<param-name>jakarta.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<!-- Configuration PrimeFaces -->
<context-param>
@@ -60,6 +72,16 @@
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Filtre de sécurité - Headers HTTP de sécurité -->
<filter>
<filter-name>Security Headers Filter</filter-name>
<filter-class>dev.lions.btpxpress.filter.SecurityHeadersFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Security Headers Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Servlet JSF -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
@@ -74,6 +96,10 @@
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Configuration MIME types -->
<mime-mapping>

View File

@@ -19,21 +19,39 @@ quarkus.http.port=8081
quarkus.http.cors=true
quarkus.http.cors.origins=http://localhost:8080,https://security.lions.dev
%dev.quarkus.oidc.enabled=false
%dev.quarkus.oidc.enabled=true
%prod.quarkus.oidc.enabled=true
quarkus.oidc.auth-server-url=https://security.lions.dev/realms/btpxpress
quarkus.oidc.client-id=btpxpress-frontend
quarkus.oidc.application-type=web-app
quarkus.oidc.tls.verification=required
quarkus.oidc.authentication.redirect-path=/
# Client confidential avec secret
quarkus.oidc.credentials.secret=0Ph4e31lQQuonodmLQG3JycehbFL1Hei
# PKCE activé (requis par Keycloak)
quarkus.oidc.authentication.pkce-required=true
# Laisser Quarkus auto-générer le secret PKCE (ne pas définir pkce-secret ni state-secret)
# Redirection après authentification
quarkus.oidc.authentication.redirect-path=/dashboard.xhtml
quarkus.oidc.authentication.restore-path-after-redirect=true
quarkus.oidc.authentication.cookie-path=/
quarkus.oidc.authentication.session-age-extension=PT30M
quarkus.oidc.authentication.java-script-auto-redirect=false
quarkus.oidc.authentication.force-redirect-https-scheme=false
# Token et découverte
quarkus.oidc.token.issuer=https://security.lions.dev/realms/btpxpress
quarkus.oidc.discovery-enabled=true
# Logout
quarkus.oidc.logout.path=/logout
quarkus.oidc.logout.post-logout-path=/index.xhtml
quarkus.oidc.token-state-manager.split-tokens=true
quarkus.oidc.token-state-manager.strategy=id-refresh-tokens
quarkus.oidc.token-state-manager.encryption-secret=btpxpress-secure-cookie-encryption-key-32chars-2025
quarkus.oidc.token-state-manager.encryption-required=false
quarkus.oidc.token-state-manager.cookie-max-size=8192
@@ -51,6 +69,8 @@ quarkus.security.deny-unannotated-endpoints=false
quarkus.log.level=INFO
quarkus.log.category."dev.lions.btpxpress".level=DEBUG
quarkus.log.category."io.quarkus.oidc".level=DEBUG
quarkus.log.category."io.quarkus.security".level=DEBUG
quarkus.log.console.enable=true
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
@@ -62,5 +82,15 @@ quarkus.rest-client."dev.lions.btpxpress.service.BtpXpressApiClient".scope=jakar
quarkus.locale=fr_FR
quarkus.http.auth.permission.public.paths=/*,/login.xhtml,/index.xhtml,/dashboard.xhtml,/chantiers.xhtml,/chantiers/*,/clients.xhtml,/clients/*
quarkus.http.auth.permission.public.policy=permit
# Ressources publiques (ordre important - du plus spécifique au plus général)
# 1. Ressources statiques JSF et layout
quarkus.http.auth.permission.static.paths=/resources/*,/jakarta.faces.resource/*,/layout/*,/demo/*,/theme/*
quarkus.http.auth.permission.static.policy=permit
# 2. Pages d'erreur seulement (pas d'index ni login)
quarkus.http.auth.permission.public-pages.paths=/error.xhtml,/access-denied.xhtml
quarkus.http.auth.permission.public-pages.policy=permit
# 3. Toutes les autres pages nécessitent une authentification (y compris / et /index.xhtml)
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated

View File

@@ -31,6 +31,10 @@
<param-name>jakarta.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>jakarta.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/primefaces-freya.taglib.xml</param-value>
</context-param>
<!-- Configuration PrimeFaces -->
<context-param>
@@ -60,6 +64,16 @@
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Filtre de sécurité - Headers HTTP de sécurité -->
<filter>
<filter-name>Security Headers Filter</filter-name>
<filter-class>dev.lions.btpxpress.filter.SecurityHeadersFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Security Headers Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Servlet JSF -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<meta http-equiv="refresh" content="0;url=dashboard.xhtml" />
<title>BTP Xpress - Redirection</title>
</h:head>
<h:body>
<h:outputText value="Redirection en cours..." />
</h:body>
</html>