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.
237 lines
16 KiB
HTML
237 lines
16 KiB
HTML
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
|
xmlns:h="http://java.sun.com/jsf/html"
|
|
xmlns:f="http://java.sun.com/jsf/core"
|
|
xmlns:ui="http://java.sun.com/jsf/facelets"
|
|
xmlns:p="http://primefaces.org/ui"
|
|
template="/WEB-INF/template.xhtml">
|
|
|
|
<ui:define name="title">Nouveau chantier - BTP Xpress</ui:define>
|
|
|
|
<ui:define name="content">
|
|
<div class="layout-dashboard">
|
|
<div class="grid">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<!-- En-tête avec breadcrumb -->
|
|
<div class="flex align-items-center justify-content-between mb-4">
|
|
<div>
|
|
<h2 class="text-900 font-bold mb-2">Créer un nouveau chantier</h2>
|
|
<p class="text-600 mt-0">Remplissez les informations du chantier à créer</p>
|
|
</div>
|
|
<p:commandButton value="Retour à la liste"
|
|
icon="pi pi-arrow-left"
|
|
outcome="/chantiers"
|
|
styleClass="ui-button-secondary ui-button-outlined"/>
|
|
</div>
|
|
|
|
<p:messages id="messages" showDetail="true" closable="true"/>
|
|
|
|
<h:form id="nouveauChantierForm" styleClass="p-fluid">
|
|
|
|
<!-- SECTION 1: Informations générales -->
|
|
<p:panel header="Informations générales" toggleable="true" collapsed="false" class="mb-4">
|
|
<div class="formgrid grid">
|
|
<!-- Nom du chantier -->
|
|
<div class="field col-12 md:col-6">
|
|
<label for="nom" class="font-bold">Nom du chantier <span class="text-red-500">*</span></label>
|
|
<p:inputText id="nom"
|
|
value="#{chantiersView.entity.nom}"
|
|
required="true"
|
|
requiredMessage="Le nom du chantier est obligatoire"
|
|
placeholder="Ex: Construction Immeuble R+3">
|
|
<f:validateLength minimum="3" maximum="200"/>
|
|
</p:inputText>
|
|
<small class="text-600">Nom descriptif du projet de construction</small>
|
|
</div>
|
|
|
|
<!-- Client -->
|
|
<div class="field col-12 md:col-6">
|
|
<label for="client" class="font-bold">Client <span class="text-red-500">*</span></label>
|
|
<p:inputText id="client"
|
|
value="#{chantiersView.entity.client}"
|
|
required="true"
|
|
requiredMessage="Le client est obligatoire"
|
|
placeholder="Ex: Société ABC">
|
|
<f:validateLength minimum="2" maximum="200"/>
|
|
</p:inputText>
|
|
<small class="text-600">Nom du client ou de l'entreprise</small>
|
|
</div>
|
|
|
|
<!-- Adresse complète -->
|
|
<div class="field col-12">
|
|
<label for="adresse" class="font-bold">Adresse du chantier</label>
|
|
<p:inputTextarea id="adresse"
|
|
value="#{chantiersView.entity.adresse}"
|
|
rows="3"
|
|
placeholder="Ex: Quartier Résidentiel, Avenue de la Paix, Lot 245"
|
|
autoResize="false">
|
|
<f:validateLength maximum="500"/>
|
|
</p:inputTextarea>
|
|
<small class="text-600">Localisation précise du chantier</small>
|
|
</div>
|
|
|
|
<!-- Statut -->
|
|
<div class="field col-12 md:col-4">
|
|
<label for="statut" class="font-bold">Statut <span class="text-red-500">*</span></label>
|
|
<p:selectOneMenu id="statut"
|
|
value="#{chantiersView.entity.statut}"
|
|
required="true">
|
|
<f:selectItem itemLabel="Sélectionner..." itemValue="" noSelectionOption="true"/>
|
|
<f:selectItem itemLabel="Planifié" itemValue="PLANIFIE"/>
|
|
<f:selectItem itemLabel="En cours" itemValue="EN_COURS"/>
|
|
<f:selectItem itemLabel="Suspendu" itemValue="SUSPENDU"/>
|
|
<f:selectItem itemLabel="Terminé" itemValue="TERMINE"/>
|
|
</p:selectOneMenu>
|
|
</div>
|
|
|
|
<!-- Avancement initial -->
|
|
<div class="field col-12 md:col-4">
|
|
<label for="avancement" class="font-bold">Avancement (%)</label>
|
|
<p:inputNumber id="avancement"
|
|
value="#{chantiersView.entity.avancement}"
|
|
minValue="0"
|
|
maxValue="100"
|
|
suffix=" %"
|
|
decimalPlaces="0">
|
|
</p:inputNumber>
|
|
<small class="text-600">Pourcentage de réalisation (0-100%)</small>
|
|
</div>
|
|
</div>
|
|
</p:panel>
|
|
|
|
<!-- SECTION 2: Planification -->
|
|
<p:panel header="Planification" toggleable="true" collapsed="false" class="mb-4">
|
|
<div class="formgrid grid">
|
|
<!-- Date de début -->
|
|
<div class="field col-12 md:col-4">
|
|
<label for="dateDebut" class="font-bold">Date de début <span class="text-red-500">*</span></label>
|
|
<p:calendar id="dateDebut"
|
|
value="#{chantiersView.entity.dateDebut}"
|
|
pattern="dd/MM/yyyy"
|
|
locale="fr"
|
|
required="true"
|
|
requiredMessage="La date de début est obligatoire"
|
|
showIcon="true"
|
|
showButtonBar="true"
|
|
monthNavigator="true"
|
|
yearNavigator="true"
|
|
yearRange="2020:2030"
|
|
placeholder="Sélectionner une date">
|
|
</p:calendar>
|
|
</div>
|
|
|
|
<!-- Date de fin prévue -->
|
|
<div class="field col-12 md:col-4">
|
|
<label for="dateFinPrevue" class="font-bold">Date de fin prévue <span class="text-red-500">*</span></label>
|
|
<p:calendar id="dateFinPrevue"
|
|
value="#{chantiersView.entity.dateFinPrevue}"
|
|
pattern="dd/MM/yyyy"
|
|
locale="fr"
|
|
required="true"
|
|
requiredMessage="La date de fin est obligatoire"
|
|
showIcon="true"
|
|
showButtonBar="true"
|
|
monthNavigator="true"
|
|
yearNavigator="true"
|
|
yearRange="2020:2035"
|
|
mindate="#{chantiersView.entity.dateDebut}"
|
|
placeholder="Sélectionner une date">
|
|
</p:calendar>
|
|
<small class="text-600">Doit être postérieure à la date de début</small>
|
|
</div>
|
|
|
|
<!-- Durée estimée (calculée automatiquement) -->
|
|
<div class="field col-12 md:col-4">
|
|
<label class="font-bold">Durée estimée</label>
|
|
<div class="p-inputgroup">
|
|
<span class="p-inputgroup-addon">
|
|
<i class="pi pi-calendar"></i>
|
|
</span>
|
|
<p:inputText value="Calculée automatiquement"
|
|
disabled="true"
|
|
styleClass="text-center font-bold"/>
|
|
</div>
|
|
<small class="text-600">Basé sur dates début et fin</small>
|
|
</div>
|
|
</div>
|
|
</p:panel>
|
|
|
|
<!-- SECTION 3: Budget -->
|
|
<p:panel header="Budget et coûts" toggleable="true" collapsed="false" class="mb-4">
|
|
<div class="formgrid grid">
|
|
<!-- Budget total -->
|
|
<div class="field col-12 md:col-6">
|
|
<label for="budget" class="font-bold">Budget total (FCFA) <span class="text-red-500">*</span></label>
|
|
<p:inputNumber id="budget"
|
|
value="#{chantiersView.entity.budget}"
|
|
required="true"
|
|
requiredMessage="Le budget est obligatoire"
|
|
minValue="0"
|
|
decimalPlaces="0"
|
|
thousandSeparator=" "
|
|
suffix=" FCFA"
|
|
placeholder="0">
|
|
</p:inputNumber>
|
|
<small class="text-600">Budget total alloué au chantier</small>
|
|
</div>
|
|
|
|
<!-- Coût réel (initialement 0) -->
|
|
<div class="field col-12 md:col-6">
|
|
<label for="coutReel" class="font-bold">Coût réel (FCFA)</label>
|
|
<p:inputNumber id="coutReel"
|
|
value="#{chantiersView.entity.coutReel}"
|
|
minValue="0"
|
|
decimalPlaces="0"
|
|
thousandSeparator=" "
|
|
suffix=" FCFA"
|
|
placeholder="0">
|
|
</p:inputNumber>
|
|
<small class="text-600">Coût réel dépensé (actualisé régulièrement)</small>
|
|
</div>
|
|
|
|
<!-- Indicateur budgétaire visuel -->
|
|
<div class="field col-12">
|
|
<div class="surface-100 border-round p-3">
|
|
<div class="flex align-items-center justify-content-between mb-2">
|
|
<span class="text-900 font-medium">État budgétaire</span>
|
|
<span class="text-600 text-sm">Budget: #{chantiersView.entity.budget} FCFA | Dépensé: #{chantiersView.entity.coutReel} FCFA</span>
|
|
</div>
|
|
<p:progressBar value="#{chantiersView.entity.coutReel / chantiersView.entity.budget * 100}"
|
|
displayValue="true"
|
|
labelTemplate="{value}% du budget utilisé"
|
|
styleClass="#{chantiersView.entity.coutReel > chantiersView.entity.budget ? 'bg-red-500' : 'bg-green-500'}"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</p:panel>
|
|
|
|
<!-- Boutons d'action -->
|
|
<div class="flex align-items-center justify-content-between pt-4 border-top-1 surface-border">
|
|
<div>
|
|
<span class="text-600 text-sm">Les champs marqués d'un </span>
|
|
<span class="text-red-500 font-bold">*</span>
|
|
<span class="text-600 text-sm"> sont obligatoires</span>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<p:commandButton value="Annuler"
|
|
icon="pi pi-times"
|
|
action="/chantiers?faces-redirect=true"
|
|
styleClass="ui-button-secondary"
|
|
immediate="true"/>
|
|
<p:commandButton value="Enregistrer le chantier"
|
|
icon="pi pi-save"
|
|
action="#{chantiersView.save}"
|
|
update="@form messages"
|
|
oncomplete="if (args && !args.validationFailed) window.location.href='/chantiers.xhtml';"
|
|
styleClass="ui-button-primary"/>
|
|
</div>
|
|
</div>
|
|
|
|
</h:form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ui:define>
|
|
</ui:composition>
|