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:
dahoud
2025-11-08 10:49:19 +00:00
parent 0fad42ccaf
commit ec38f6a23a
192 changed files with 12029 additions and 271 deletions

View File

@@ -0,0 +1,354 @@
<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">Détails du devis - BTP Xpress</ui:define>
<f:metadata>
<f:viewParam name="id" value="#{devisView.devisId}"/>
<f:event type="preRenderView" listener="#{devisView.loadDevisById()}"/>
</f:metadata>
<ui:define name="content">
<div class="layout-dashboard">
<div class="grid">
<div class="col-12">
<!-- En-tête avec actions -->
<div class="card mb-3">
<div class="flex align-items-start justify-content-between flex-wrap gap-3">
<div class="flex-grow-1">
<div class="flex align-items-center gap-3 mb-2">
<h2 class="text-900 font-bold m-0">Devis #{devisView.selectedItem.numero}</h2>
<ui:include src="/WEB-INF/components/status-badge.xhtml">
<ui:param name="value" value="#{devisView.selectedItem.statut}"/>
</ui:include>
</div>
<p class="text-600 mt-0 mb-2">
<i class="pi pi-building mr-2"></i>#{devisView.selectedItem.client}
</p>
<p class="text-sm text-600 mt-0 mb-0">#{devisView.selectedItem.objet}</p>
<div class="flex align-items-center gap-3 text-sm mt-2">
<span class="text-600">
<i class="pi pi-calendar mr-1"></i>
Émis le: <h:outputText value="#{devisView.selectedItem.dateEmission}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</span>
<span class="text-600">
<i class="pi pi-calendar-times mr-1"></i>
Valide jusqu'au: <h:outputText value="#{devisView.selectedItem.dateValidite}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</span>
</div>
</div>
<div class="flex gap-2">
<p:commandButton value="Retour"
icon="pi pi-arrow-left"
outcome="/devis"
styleClass="ui-button-secondary ui-button-outlined"/>
<p:commandButton value="Convertir en chantier"
icon="pi pi-arrow-right"
rendered="#{devisView.selectedItem.statut eq 'ACCEPTE'}"
styleClass="ui-button-success"/>
<p:commandButton value="Télécharger PDF"
icon="pi pi-file-pdf"
styleClass="ui-button-danger ui-button-outlined"/>
<p:splitButton value="Modifier"
icon="pi pi-pencil"
styleClass="ui-button-primary">
</p:splitButton>
</div>
</div>
</div>
<!-- KPI Cards -->
<div class="grid mb-3">
<div class="col-12 md:col-6 lg:col-3">
<div class="card mb-0">
<div class="flex flex-column">
<span class="text-600 font-medium text-sm mb-2">Montant HT</span>
<div class="text-900 font-bold text-2xl mb-2">
<ui:include src="/WEB-INF/components/monetary-display.xhtml">
<ui:param name="amount" value="#{devisView.selectedItem.montantHT}"/>
<ui:param name="size" value="normal"/>
<ui:param name="bold" value="true"/>
</ui:include>
</div>
<span class="text-500 text-xs">Hors taxes</span>
</div>
</div>
</div>
<div class="col-12 md:col-6 lg:col-3">
<div class="card mb-0">
<div class="flex flex-column">
<span class="text-600 font-medium text-sm mb-2">TVA (18%)</span>
<div class="text-orange-600 font-bold text-2xl mb-2">
<ui:include src="/WEB-INF/components/monetary-display.xhtml">
<ui:param name="amount" value="#{devisView.selectedItem.montantHT * 0.18}"/>
<ui:param name="size" value="normal"/>
<ui:param name="bold" value="true"/>
</ui:include>
</div>
<span class="text-500 text-xs">Taxe sur la valeur ajoutée</span>
</div>
</div>
</div>
<div class="col-12 md:col-6 lg:col-3">
<div class="card mb-0">
<div class="flex flex-column">
<span class="text-600 font-medium text-sm mb-2">Montant TTC</span>
<div class="text-primary font-bold text-2xl mb-2">
<ui:include src="/WEB-INF/components/monetary-display.xhtml">
<ui:param name="amount" value="#{devisView.selectedItem.montantHT * 1.18}"/>
<ui:param name="size" value="normal"/>
<ui:param name="bold" value="true"/>
</ui:include>
</div>
<span class="text-500 text-xs">Toutes taxes comprises</span>
</div>
</div>
</div>
<div class="col-12 md:col-6 lg:col-3">
<div class="card mb-0">
<div class="flex flex-column">
<span class="text-600 font-medium text-sm mb-2">Statut</span>
<div class="mt-2 mb-2">
<ui:include src="/WEB-INF/components/status-badge.xhtml">
<ui:param name="value" value="#{devisView.selectedItem.statut}"/>
</ui:include>
</div>
<span class="text-500 text-xs">
<h:outputText value="Valide" rendered="#{devisView.selectedItem.statut ne 'EXPIRE'}"/>
<h:outputText value="Expiré" rendered="#{devisView.selectedItem.statut eq 'EXPIRE'}"/>
</span>
</div>
</div>
</div>
</div>
<!-- Onglets détaillés -->
<div class="card">
<p:tabView dynamic="true" cache="false">
<!-- ONGLET 1: Vue d'ensemble -->
<p:tab title="Vue d'ensemble" icon="pi pi-home">
<div class="grid">
<!-- Informations du devis -->
<div class="col-12 lg:col-6">
<h5 class="text-900 font-bold mb-3">Informations du devis</h5>
<div class="surface-50 border-round p-3 mb-3">
<div class="grid">
<div class="col-6">
<span class="text-600 text-sm">Numéro</span>
<p class="text-900 font-bold mt-1 mb-0">#{devisView.selectedItem.numero}</p>
</div>
<div class="col-6">
<span class="text-600 text-sm">Client</span>
<p class="text-900 font-medium mt-1 mb-0">#{devisView.selectedItem.client}</p>
</div>
<div class="col-12">
<span class="text-600 text-sm">Objet</span>
<p class="text-900 font-medium mt-1 mb-0">#{devisView.selectedItem.objet}</p>
</div>
<div class="col-6">
<span class="text-600 text-sm">Date d'émission</span>
<p class="text-900 font-medium mt-1 mb-0">
<h:outputText value="#{devisView.selectedItem.dateEmission}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p>
</div>
<div class="col-6">
<span class="text-600 text-sm">Date de validité</span>
<p class="text-900 font-medium mt-1 mb-0">
<h:outputText value="#{devisView.selectedItem.dateValidite}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
</p>
</div>
<div class="col-12">
<span class="text-600 text-sm">Statut</span>
<div class="mt-1">
<ui:include src="/WEB-INF/components/status-badge.xhtml">
<ui:param name="value" value="#{devisView.selectedItem.statut}"/>
</ui:include>
</div>
</div>
</div>
</div>
</div>
<!-- Récapitulatif financier -->
<div class="col-12 lg:col-6">
<h5 class="text-900 font-bold mb-3">Récapitulatif financier</h5>
<div class="surface-50 border-round p-3 mb-3">
<div class="grid">
<div class="col-12">
<div class="flex justify-content-between align-items-center mb-2">
<span class="text-600">Montant HT</span>
<span class="text-900 font-bold">
<ui:include src="/WEB-INF/components/monetary-display.xhtml">
<ui:param name="amount" value="#{devisView.selectedItem.montantHT}"/>
</ui:include>
</span>
</div>
<div class="flex justify-content-between align-items-center mb-2">
<span class="text-600">TVA (18%)</span>
<span class="text-orange-600 font-medium">
<ui:include src="/WEB-INF/components/monetary-display.xhtml">
<ui:param name="amount" value="#{devisView.selectedItem.montantHT * 0.18}"/>
</ui:include>
</span>
</div>
<div class="border-top-1 surface-border pt-2 mt-2">
<div class="flex justify-content-between align-items-center">
<span class="text-900 font-bold text-lg">Total TTC</span>
<span class="text-primary font-bold text-xl">
<ui:include src="/WEB-INF/components/monetary-display.xhtml">
<ui:param name="amount" value="#{devisView.selectedItem.montantHT * 1.18}"/>
<ui:param name="size" value="large"/>
<ui:param name="bold" value="true"/>
</ui:include>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Actions rapides -->
<div class="col-12">
<h5 class="text-900 font-bold mb-3">Actions rapides</h5>
<div class="surface-50 border-round p-3">
<div class="flex flex-wrap gap-2">
<p:commandButton value="Accepter le devis"
icon="pi pi-check"
rendered="#{devisView.selectedItem.statut eq 'ATTENTE'}"
styleClass="ui-button-success"/>
<p:commandButton value="Refuser"
icon="pi pi-times"
rendered="#{devisView.selectedItem.statut eq 'ATTENTE'}"
styleClass="ui-button-danger ui-button-outlined"/>
<p:commandButton value="Convertir en chantier"
icon="pi pi-arrow-right"
rendered="#{devisView.selectedItem.statut eq 'ACCEPTE'}"
styleClass="ui-button-primary"/>
<p:commandButton value="Dupliquer"
icon="pi pi-copy"
styleClass="ui-button-secondary ui-button-outlined"/>
<p:commandButton value="Envoyer par email"
icon="pi pi-send"
styleClass="ui-button-info ui-button-outlined"/>
<p:commandButton value="Télécharger PDF"
icon="pi pi-file-pdf"
styleClass="ui-button-danger ui-button-outlined"/>
</div>
</div>
</div>
</div>
</p:tab>
<!-- ONGLET 2: Lignes du devis -->
<p:tab title="Détail des lignes" icon="pi pi-list">
<div class="p-3">
<div class="flex align-items-center justify-content-between mb-3">
<h5 class="text-900 font-bold m-0">Lignes du devis</h5>
<p:commandButton value="Ajouter une ligne"
icon="pi pi-plus"
styleClass="ui-button-success ui-button-sm"/>
</div>
<p:message severity="info" text="Fonctionnalité de gestion des lignes de devis en cours de développement"/>
<div class="surface-50 border-round p-4 text-center mt-3">
<i class="pi pi-list text-400 mb-3" style="font-size: 3rem;"></i>
<p class="text-600">Bientôt disponible: tableau des prestations avec quantités, prix unitaires, sous-totaux</p>
</div>
</div>
</p:tab>
<!-- ONGLET 3: Conditions -->
<p:tab title="Conditions" icon="pi pi-file-edit">
<div class="p-3">
<h5 class="text-900 font-bold mb-3">Conditions commerciales</h5>
<div class="surface-50 border-round p-3 mb-3">
<h6 class="text-900 font-medium mb-2">Conditions de paiement</h6>
<p class="text-600 text-sm">
Les conditions de paiement seront affichées ici (exemple: paiement en 3 fois, 30% à la commande, etc.)
</p>
</div>
<div class="surface-50 border-round p-3 mb-3">
<h6 class="text-900 font-medium mb-2">Délais de livraison</h6>
<p class="text-600 text-sm">
Information sur les délais de réalisation du projet
</p>
</div>
<div class="surface-50 border-round p-3">
<h6 class="text-900 font-medium mb-2">Garanties</h6>
<p class="text-600 text-sm">
Conditions de garantie et assurances
</p>
</div>
</div>
</p:tab>
<!-- ONGLET 4: Documents -->
<p:tab title="Documents" icon="pi pi-folder">
<div class="p-3">
<div class="flex align-items-center justify-content-between mb-3">
<h5 class="text-900 font-bold m-0">Documents associés</h5>
<p:commandButton value="Ajouter un document"
icon="pi pi-upload"
styleClass="ui-button-success ui-button-sm"/>
</div>
<p:message severity="info" text="Fonctionnalité de gestion documentaire en cours de développement"/>
</div>
</p:tab>
<!-- ONGLET 5: Suivi -->
<p:tab title="Suivi" icon="pi pi-chart-line">
<div class="p-3">
<h5 class="text-900 font-bold mb-3">Suivi du devis</h5>
<div class="surface-50 border-round p-3">
<p:timeline align="alternate">
<p:templateSlot name="marker">
<i class="pi pi-circle-fill text-primary"></i>
</p:templateSlot>
<p:templateSlot name="content">
<small class="text-600">Historique des actions (création, envoi, acceptation, etc.)</small>
</p:templateSlot>
</p:timeline>
</div>
</div>
</p:tab>
<!-- ONGLET 6: Historique -->
<p:tab title="Historique" icon="pi pi-history">
<div class="p-3">
<h5 class="text-900 font-bold mb-3">Historique des modifications</h5>
<p:timeline align="alternate">
<p:templateSlot name="marker">
<i class="pi pi-circle-fill text-primary"></i>
</p:templateSlot>
<p:templateSlot name="content">
<small class="text-600">Fonctionnalité en cours de développement</small>
</p:templateSlot>
</p:timeline>
</div>
</p:tab>
</p:tabView>
</div>
</div>
</div>
</div>
</ui:define>
</ui:composition>