feat: Module Devis professionnel avec écrans complets
Création de 2 écrans professionnels pour le module Devis:
1. devis/nouveau.xhtml:
- 4 sections: Informations générales, Détail du devis, Montants, Conditions
- Numéro auto-généré avec icône
- Statut avec 5 valeurs (BROUILLON, ATTENTE, ACCEPTE, REFUSE, EXPIRE)
- Dates d'émission et validité avec calendriers
- Client et objet du devis requis
- Placeholder pour lignes de devis (future développement)
- Calcul automatique TVA 18% et TTC
- Récapitulatif visuel HT/TVA/TTC avec composant monétaire
- Conditions de paiement et remarques (section collapsible)
- 3 boutons: Annuler, Brouillon, Envoyer
2. devis/details.xhtml:
- En-tête: numéro, statut, client, objet, dates
- Actions: Retour, Convertir en chantier, PDF, Modifier
- 4 KPI cards: Montant HT, TVA, TTC, Statut
- 6 onglets professionnels:
* Vue d'ensemble: infos + récap financier + actions rapides
* Détail des lignes: table lignes (placeholder)
* Conditions: paiement, délais, garanties
* Documents: GED associée (placeholder)
* Suivi: timeline actions
* Historique: modifications (placeholder)
Corrections:
- Fix navigation /factures/nouvelle -> /factures/nouveau (factures.xhtml)
- Fix menu /factures/nouvelle -> /factures/nouveau (menu.xhtml)
Tous les composants réutilisables utilisés (status-badge, monetary-display).
Validation complète côté client et serveur.
UI/UX professionnel adapté au métier BTP.
This commit is contained in:
187
src/main/java/dev/lions/btpxpress/view/EquipeView.java
Normal file
187
src/main/java/dev/lions/btpxpress/view/EquipeView.java
Normal file
@@ -0,0 +1,187 @@
|
||||
package dev.lions.btpxpress.view;
|
||||
|
||||
import dev.lions.btpxpress.service.EquipeService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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("equipeView")
|
||||
@ViewScoped
|
||||
@Getter
|
||||
@Setter
|
||||
public class EquipeView extends BaseListView<EquipeView.Equipe, Long> implements Serializable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EquipeView.class);
|
||||
|
||||
@Inject
|
||||
EquipeService equipeService;
|
||||
|
||||
private String filtreNom;
|
||||
private String filtreSpecialite;
|
||||
private String filtreStatut;
|
||||
private Long equipeId;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (filtreStatut == null) {
|
||||
filtreStatut = "TOUS";
|
||||
}
|
||||
loadItems();
|
||||
}
|
||||
|
||||
public void setFiltreStatut(String statut) {
|
||||
this.filtreStatut = statut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadItems() {
|
||||
loading = true;
|
||||
try {
|
||||
items = new ArrayList<>();
|
||||
|
||||
// Récupération depuis l'API backend
|
||||
List<Map<String, Object>> equipesData = equipeService.getAllEquipes();
|
||||
|
||||
for (Map<String, Object> data : equipesData) {
|
||||
Equipe eq = new Equipe();
|
||||
|
||||
eq.setId(data.get("id") != null ? Long.valueOf(data.get("id").toString().hashCode()) : null);
|
||||
eq.setNom((String) data.get("nom"));
|
||||
eq.setDescription((String) data.get("description"));
|
||||
eq.setSpecialite((String) data.get("specialite"));
|
||||
eq.setStatut((String) data.get("statut"));
|
||||
|
||||
// Chef d'équipe
|
||||
Object chefObj = data.get("chef");
|
||||
if (chefObj instanceof Map) {
|
||||
Map<String, Object> chefData = (Map<String, Object>) chefObj;
|
||||
String prenom = (String) chefData.get("prenom");
|
||||
String nom = (String) chefData.get("nom");
|
||||
eq.setChef((prenom != null ? prenom + " " : "") + (nom != null ? nom : ""));
|
||||
} else {
|
||||
eq.setChef("N/A");
|
||||
}
|
||||
|
||||
// Nombre de membres
|
||||
Object membresObj = data.get("membres");
|
||||
if (membresObj instanceof List) {
|
||||
eq.setNombreMembres(((List<?>) membresObj).size());
|
||||
} else {
|
||||
eq.setNombreMembres(0);
|
||||
}
|
||||
|
||||
if (data.get("dateCreation") != null) {
|
||||
eq.setDateCreation(LocalDateTime.parse(data.get("dateCreation").toString()));
|
||||
}
|
||||
|
||||
items.add(eq);
|
||||
}
|
||||
|
||||
LOG.info("Équipes chargées depuis l'API : {} élément(s)", items.size());
|
||||
applyFilters(items, buildFilters());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Erreur chargement équipes depuis l'API", e);
|
||||
items = new ArrayList<>();
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Predicate<Equipe>> buildFilters() {
|
||||
List<Predicate<Equipe>> filters = new ArrayList<>();
|
||||
if (filtreNom != null && !filtreNom.trim().isEmpty()) {
|
||||
filters.add(e -> e.getNom().toLowerCase().contains(filtreNom.toLowerCase()));
|
||||
}
|
||||
if (filtreSpecialite != null && !filtreSpecialite.trim().isEmpty()) {
|
||||
filters.add(e -> e.getSpecialite() != null &&
|
||||
e.getSpecialite().toLowerCase().contains(filtreSpecialite.toLowerCase()));
|
||||
}
|
||||
if (filtreStatut != null && !filtreStatut.trim().isEmpty() && !"TOUS".equals(filtreStatut)) {
|
||||
filters.add(e -> e.getStatut().equals(filtreStatut));
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetFilterFields() {
|
||||
filtreNom = null;
|
||||
filtreSpecialite = null;
|
||||
filtreStatut = "TOUS";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDetailsPath() {
|
||||
return "/equipes/";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreatePath() {
|
||||
return "/equipes/nouvelle";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performDelete() {
|
||||
LOG.info("Suppression équipe : {}", selectedItem.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Equipe createNewEntity() {
|
||||
Equipe equipe = new Equipe();
|
||||
equipe.setStatut("ACTIVE");
|
||||
equipe.setNombreMembres(0);
|
||||
equipe.setDateCreation(LocalDateTime.now());
|
||||
return equipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCreate() {
|
||||
if (selectedItem.getId() == null) {
|
||||
selectedItem.setId(System.currentTimeMillis());
|
||||
}
|
||||
selectedItem.setDateCreation(LocalDateTime.now());
|
||||
items.add(selectedItem);
|
||||
LOG.info("Created: {}", selectedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performUpdate() {
|
||||
LOG.info("Updated: {}", selectedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getEntityId(Equipe entity) {
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createNew() {
|
||||
selectedItem = new Equipe();
|
||||
selectedItem.setStatut("ACTIVE");
|
||||
return getCreatePath() + "?faces-redirect=true";
|
||||
}
|
||||
|
||||
@lombok.Getter
|
||||
@lombok.Setter
|
||||
public static class Equipe {
|
||||
private Long id;
|
||||
private String nom;
|
||||
private String description;
|
||||
private String chef;
|
||||
private String specialite;
|
||||
private String statut;
|
||||
private int nombreMembres;
|
||||
private LocalDateTime dateCreation;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user