2026-07-26 14:39:10 +02:00
< 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"
2026-08-05 14:35:11 +02:00
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
2026-07-26 14:39:10 +02:00
template="/WEB-INF/layout/template.xhtml">
< f:metadata >
< f:viewParam name = "user1" value = "#{formUserBean.user1}" / >
< f:viewParam name = "canEdit" value = "#{formUserBean.modifier}" / >
< / f:metadata >
< ui:define name = "title" >
#{msg['distribution.page.user']}
< / ui:define >
< ui:define name = "content" >
#{formUserBean.init()}
2026-08-05 14:35:11 +02:00
< style >
.user-card-profile,
.user-card-right {
border-radius: 16px !important;
box-shadow: 0 1px 4px rgba(0,0,0,0.08) !important;
border: 1px solid #E5E7EB !important;
}
.user-card-profile .ui-panel-content,
.user-card-right .ui-panel-content {
border-radius: 16px !important;
height: 100%;
}
.user-cards-row {
display: flex;
align-items: stretch;
gap: 1.5%;
margin-bottom: 1rem;
margin-top: 1rem;
}
.user-card-profile-wrapper {
width: 33%;
}
.user-card-right-wrapper {
width: 65.5%;
}
.user-avatar {
width: 90px;
height: 90px;
border-radius: 50%;
background-color: #DBEAFE;
color: #0D9488;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
font-weight: 700;
font-family: 'Inter', sans-serif;
margin: 10px auto 18px auto;
}
.user-fullname {
text-align: center;
font-size: 1.15rem;
font-weight: 700;
color: #0D1B2E;
margin-bottom: 25px;
}
.info-block {
text-align: left;
margin-bottom: 5px;
}
.info-row {
display: flex;
align-items: center;
padding: 12px 4px;
}
.info-row i {
color: #0D9488 !important;
font-size: 1rem;
margin-right: 8px;
width: 16px;
flex-shrink: 0;
}
.info-label {
color: #0D9488 !important;
font-weight: 600 !important;
font-size: 0.98rem;
display: flex;
align-items: center;
width: 160px;
flex-shrink: 0;
}
.info-value {
color: #334155;
font-size: 1rem;
text-align: left;
}
.info-divider {
border: none;
border-top: 1px solid #E5E7EB;
margin: 10px 0;
}
.status-chip {
display: inline-block;
background-color: #0D9488;
color: #ffffff;
font-size: 0.78rem;
font-weight: 600;
padding: 3px 14px;
border-radius: 20px;
}
.status-chip.inactive {
background-color: #EF4444;
}
.btn-outline-gray,
.btn-outline-gray.ui-button {
background-color: #ffffff !important;
border: 1px solid #E2E8F0 !important;
color: #334155 !important;
border-radius: 10px !important;
padding: 8px 18px !important;
font-weight: 500 !important;
}
.btn-outline-gray:hover {
background-color: #F3F4F6 !important;
}
.btn-teal-pill,
.btn-teal-pill.ui-button {
background-color: #0D9488 !important;
border-color: #0D9488 !important;
color: #ffffff !important;
border-radius: 10px !important;
padding: 8px 18px !important;
font-weight: 500 !important;
}
.btn-teal-pill:hover {
background-color: #0b7d73 !important;
}
.btn-red-pill,
.btn-red-pill.ui-button {
background-color: #EF4444 !important;
border-color: #EF4444 !important;
color: #ffffff !important;
border-radius: 10px !important;
padding: 8px 18px !important;
font-weight: 500 !important;
}
.btn-red-pill:hover {
background-color: #DC2626 !important;
}
.form-tabs .ui-tabs-nav {
background-color: transparent !important;
border: none !important;
border-bottom: 1px solid #E5E7EB !important;
padding-left: 4px;
display: flex !important;
gap: 8px;
}
.form-tabs .ui-tabs-nav li {
margin-right: 0 !important;
padding: 0 !important;
display: flex !important;
}
.form-tabs .ui-tabs-nav li a {
background-color: transparent !important;
border: none !important;
border-radius: 8px 8px 0 0 !important;
color: #64748B !important;
font-weight: 500 !important;
padding: 10px 16px !important;
margin: 0 !important;
display: flex !important;
align-items: center !important;
height: 100% !important;
box-sizing: border-box !important;
transition: background-color 0.2s ease, color 0.2s ease;
}
.form-tabs .ui-tabs-nav li a i {
color: inherit !important;
margin-right: 6px;
}
.form-tabs .ui-tabs-nav li:not(.ui-state-active):not(.ui-tabs-selected) a:hover {
background-color: rgba(13, 148, 136, 0.1) !important;
color: #0D9488 !important;
}
.form-tabs .ui-tabs-nav li.ui-state-active a,
.form-tabs .ui-tabs-nav li.ui-tabs-selected a {
background-color: rgba(13, 148, 136, 0.12) !important;
color: #0D9488 !important;
border-bottom: 2px solid #0D9488 !important;
font-weight: 600 !important;
}
.form-tabs .ui-tabs-panel {
padding: 20px 4px !important;
}
.table-subheader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.table-subheader-title {
font-weight: 700;
font-size: 0.95rem;
color: #0D1B2E;
}
.btn-export {
background-color: #ffffff !important;
border: 1px solid #dee2e6 !important;
color: #0D9488 !important;
}
.btn-export:hover {
background-color: #e6f5f4 !important;
border-color: #0D9488 !important;
}
.user-card-right .ui-datatable-tablewrapper table td,
.user-card-right .ui-datatable-tablewrapper table th {
vertical-align: middle !important;
text-align: center !important;
}
.edit-field-row {
display: flex;
align-items: center;
margin-bottom: 18px;
}
.edit-field-label {
color: #0D9488 !important;
font-weight: 600 !important;
font-size: 0.98rem;
width: 160px;
flex-shrink: 0;
display: flex;
align-items: center;
}
.edit-field-label i {
margin-right: 8px;
color: #0D9488 !important;
}
.edit-field-input .ui-inputtext,
.edit-field-input .ui-selectonemenu,
.edit-field-input .ui-password {
width: 100% !important;
background-color: #F3F4F6 !important;
border: 1px solid #E5E7EB !important;
border-radius: 8px !important;
}
.edit-field-input {
flex: 1;
max-width: 380px;
}
/* MODIF : message d'erreur de confirmation MP */
.ui-message-error {
color: #EF4444 !important;
font-weight: 500;
}
< / style >
<!-- MODIF : id explicite ajouté au form pour fiabiliser le ciblage ajax/messages -->
< h:form id = "userForm" >
< div class = "user-cards-row" >
< div class = "user-card-profile-wrapper" >
< p:card id = "card" styleClass = "user-card-profile" style = "height: 100%;" >
< div style = "display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;" >
< div style = "width: 150px;" >
< p:commandButton value = "#{msg['distribution.page.etatVGlobal.retour']}" icon = "pi pi-arrow-left"
action="#{formUserBean.voirUser()}" ajax="false"
styleClass="btn-outline-gray" />
< / div >
< div style = " text-align: right; display: flex; gap: 8px;" >
< p:commandButton value = "#{msg['distribution.page.supprimer']}" icon = "pi pi-trash"
action="#{formUserBean.deleteUser()}"
rendered="#{utilControleAccess.isshowPrivilegeRemove('user') and formUserBean.canEdit}"
styleClass="btn-red-pill" />
< p:commandButton value = "#{msg['distribution.page.enregistrer']}" icon = "pi pi-save"
rendered="#{formUserBean.canEdit}"
action="#{formUserBean.saveUser()}"
update="@form"
styleClass="btn-teal-pill" />
< p:commandButton value = "#{msg['distribution.page.modifier']}" icon = "pi pi-pencil"
rendered="#{!formUserBean.canEdit and utilControleAccess.isshowPrivilegeUpdate('user')}"
action="#{formUserBean.editUser()}"
styleClass="btn-teal-pill" />
< / div >
2026-07-26 14:39:10 +02:00
< / div >
2026-08-05 14:35:11 +02:00
< div class = "user-avatar" >
#{fn:substring(formUserBean.userSelected.nom,0,1)}#{fn:substring(formUserBean.userSelected.prenom,0,1)}
< / div >
<!-- ===================== MODE CONSULTATION ===================== -->
< h:panelGroup rendered = "#{!formUserBean.canEdit}" >
< div class = "user-fullname" >
< h:outputText value = "#{formUserBean.userSelected.nom} #{formUserBean.userSelected.prenom}" / >
< / div >
< div class = "info-block" >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-user" > < / i > #{msg['distribution.page.user.nom']}< / span >
< span class = "info-value" > < h:outputText value = "#{formUserBean.userSelected.nom}" / > < / span >
< / div >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-users" > < / i > #{msg['distribution.page.user.prenom']}< / span >
< span class = "info-value" > < h:outputText value = "#{formUserBean.userSelected.prenom}" / > < / span >
< / div >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-phone" > < / i > #{msg['stock.page.fournisseur.Telephone']}< / span >
< span class = "info-value" > < h:outputText value = "#{formUserBean.userSelected.telephone}" / > < / span >
< / div >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-envelope" > < / i > #{msg['stock.page.fournisseur.mail']}< / span >
< span class = "info-value" > < h:outputText value = "#{formUserBean.userSelected.mail}" / > < / span >
< / div >
< / div >
< hr class = "info-divider" / >
< div class = "info-block" >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-check-circle" > < / i > #{msg['distribution.page.client.actif']}< / span >
< span class = "info-value" >
< span class = "status-chip #{formUserBean.userSelected.actif ne 'true' ? 'inactive' : ''}" >
#{formUserBean.userSelected.actif eq 'true' ? 'Actif' : 'Inactif'}
< / span >
< / span >
< / div >
< / div >
< hr class = "info-divider" / >
< div class = "info-block" >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-users" > < / i > #{msg['distribution.page.user.groupe']}< / span >
< span class = "info-value" > < h:outputText value = "#{formUserBean.member.fkGroupe.nomGroupe}" / > < / span >
< / div >
< div class = "info-row" >
< span class = "info-label" > < i class = "pi pi-user" > < / i > Nom d'utilisateur< / span >
< span class = "info-value" > < h:outputText value = "#{formUserBean.userSelected.userid}" / > < / span >
< / div >
< / div >
< / h:panelGroup >
<!-- ===================== MODE EDITION ===================== -->
< h:panelGroup rendered = "#{formUserBean.canEdit}" >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-user" > < / i > #{msg['distribution.page.user.nom']}< / span >
< div class = "edit-field-input" >
< p:inputText id = "nom" value = "#{formUserBean.userSelected.nom}" / >
< / div >
< / div >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-users" > < / i > #{msg['distribution.page.user.prenom']}< / span >
< div class = "edit-field-input" >
< p:inputText id = "prenom" value = "#{formUserBean.userSelected.prenom}" / >
< / div >
< / div >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-phone" > < / i > #{msg['stock.page.fournisseur.Telephone']}< / span >
< div class = "edit-field-input" >
< p:inputText id = "telephone" value = "#{formUserBean.userSelected.telephone}" / >
< / div >
< / div >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-envelope" > < / i > #{msg['stock.page.fournisseur.mail']}< / span >
< div class = "edit-field-input" >
< p:inputText id = "mail" value = "#{formUserBean.userSelected.mail}" / >
< / div >
< / div >
< hr class = "info-divider" / >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-check-circle" > < / i > #{msg['distribution.page.client.actif']}< / span >
< div class = "edit-field-input" >
< p:selectOneMenu value = "#{formUserBean.userSelected.actif}" >
< f:selectItem itemLabel = "Oui" itemValue = "true" / >
< f:selectItem itemLabel = "Non" itemValue = "false" / >
< / p:selectOneMenu >
< / div >
< / div >
< hr class = "info-divider" / >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-users" > < / i > #{msg['distribution.page.user.groupe']}< / span >
< div class = "edit-field-input" >
< p:selectOneMenu id = "groupe"
converter="groupeConverter"
value="#{formUserBean.member.fkGroupe}"
rendered="#{utilControleAccess.isshowPrivilegeUpdate('modifier_groupe')}">
< f:converter binding = "#{groupeConverter}" / >
< f:selectItem itemLabel = "#{msg['distribution.select']}" noSelectionOption = "true" / >
< f:selectItems value = "#{formUserBean.listeGroups}" var = "groupe"
itemLabel="#{groupe.nomGroupe}" itemValue="#{groupe}" />
< / p:selectOneMenu >
< / div >
< / div >
< div class = "edit-field-row" >
< span class = "edit-field-label" > < i class = "pi pi-user" > < / i > Nom d'utilisateur< / span >
< div class = "edit-field-input" >
< p:inputText id = "user" value = "#{formUserBean.userSelected.userid}" pt:autocomplete = "off" / >
< / div >
< / div >
< div class = "edit-field-row" rendered = "#{formUserBean.member.fkGroupe.id ne 'admin' or (utilContext.currentUser.userid eq formUserBean.userSelected.userid)}" >
< span class = "edit-field-label" > < i class = "pi pi-lock" > < / i > #{msg['distribution.page.user.password']}< / span >
< div class = "edit-field-input" >
< p:password id = "pw" value = "#{formUserBean.userSelected.userpw}"
toggleMask="true" redisplay="true" pt:autocomplete="off" />
< / div >
< / div >
<!-- MODIF : champ de confirmation du mot de passe, variable séparée (formUserBean.confirmationMp) -->
< div class = "edit-field-row" rendered = "#{formUserBean.member.fkGroupe.id ne 'admin' or (utilContext.currentUser.userid eq formUserBean.userSelected.userid)}" >
< span class = "edit-field-label" > < i class = "pi pi-lock" > < / i > Confirmation MP< / span >
< div class = "edit-field-input" >
< p:password id = "confirmPw" value = "#{formUserBean.confirmationMp}"
toggleMask="true" redisplay="true" pt:autocomplete="off">
<!-- MODIF : process inclut "pw" pour envoyer sa valeur actuelle au serveur -->
< p:ajax event = "change" process = "pw confirmPw"
listener="#{formUserBean.verifierConfirmationMp}"
update="confirmPwMsg confirmPw" />
< / p:password >
< p:message id = "confirmPwMsg" for = "confirmPw" style = "display:block; margin-top:4px; font-size:0.85rem;" / >
< / div >
< / div >
< script >
//< ![CDATA[
(function () {
function neutraliserAutofill() {
var userField = document.querySelector('input[id$=":user"]');
var pwField = document.querySelector('input[id$=":pw_input"]');
var confirmPwField = document.querySelector('input[id$=":confirmPw_input"]');
if (userField & & !userField.dataset.autofillFixed) {
userField.setAttribute('readonly', 'readonly');
userField.dataset.autofillFixed = 'true';
userField.addEventListener('focus', function () {
userField.removeAttribute('readonly');
});
}
if (pwField & & !pwField.dataset.autofillFixed) {
pwField.setAttribute('readonly', 'readonly');
pwField.dataset.autofillFixed = 'true';
pwField.addEventListener('focus', function () {
pwField.removeAttribute('readonly');
});
}
if (confirmPwField & & !confirmPwField.dataset.autofillFixed) {
confirmPwField.setAttribute('readonly', 'readonly');
confirmPwField.dataset.autofillFixed = 'true';
confirmPwField.addEventListener('focus', function () {
confirmPwField.removeAttribute('readonly');
});
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', neutraliserAutofill);
} else {
neutraliserAutofill();
}
setTimeout(neutraliserAutofill, 300);
})();
//]]>
< / script >
< / h:panelGroup >
2026-07-26 14:39:10 +02:00
< / p:card >
< / div >
2026-08-05 14:35:11 +02:00
< div class = "user-card-right-wrapper" >
< p:card styleClass = "user-card-right" style = "height: 100%;" >
< p:tabView scrollable = "true" styleClass = "form-tabs" >
< p:tab title = "#{msg['distribution.page.user.historique.connexion']}" >
< div class = "table-subheader" >
< span class = "table-subheader-title" > #{msg['distribution.page.user.historique.connexion']}< / span >
< div >
< p:commandButton styleClass = "mr-2 btn-export" icon = "pi pi-file-excel" title = "Excel" ajax = "false" >
< p:dataExporter type = "xls" target = "dataTable" fileName = "historique_connexion" / >
< / p:commandButton >
< p:commandButton styleClass = "btn-export" icon = "pi pi-file-pdf" title = "PDF" ajax = "false" >
< p:dataExporter type = "pdf" target = "dataTable" fileName = "historique_connexion" / >
< / p:commandButton >
< / div >
< / div >
< p:dataTable id = "dataTable" var = "histCnx"
rows="10" paginator="true" draggableColumns="true"
value="#{formUserBean.listeHistCnx}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="10,30,40,60,80,100"
emptyMessage="#{msg['distribution.page.produit.liste.historique.non'] }"
selectionMode="single"
rowKey="#{histCnx.id}">
< p:column style = " text-align: center;" headerText = "#{msg['stock.page.achat.date']}" filterBy = "#{histCnx.date}" filterMatchMode = "contains" sortBy = "#{histCnx.date}" >
< h:outputText value = "#{histCnx.date}" / >
< / p:column >
< p:column style = " text-align: center;" headerText = "#{msg['stock.page.achat.heur']}" filterBy = "#{histCnx.heur}" filterMatchMode = "contains" sortBy = "#{histCnx.heur}" >
< h:outputText value = "#{histCnx.heur}" / >
< / p:column >
< p:column style = " text-align: center;" headerText = "#{msg['stock.page.imprimante.adresseIp']}" filterBy = "#{histCnx.adresseIp}" filterMatchMode = "contains" sortBy = "#{histCnx.adresseIp}" >
< h:outputText value = "#{histCnx.adresseIp}" / >
< / p:column >
< / p:dataTable >
< / p:tab >
< p:tab title = "#{msg['distribution.page.consulter.historiqueManip']}" >
< div class = "table-subheader" >
< span class = "table-subheader-title" > #{msg['distribution.page.consulter.historiqueManip']}< / span >
< div >
< p:commandButton styleClass = "mr-2 btn-export" icon = "pi pi-file-excel" title = "Excel" ajax = "false" >
< p:dataExporter type = "xls" target = "dataTable2" fileName = "historique_manipulation" / >
< / p:commandButton >
< p:commandButton styleClass = "btn-export" icon = "pi pi-file-pdf" title = "PDF" ajax = "false" >
< p:dataExporter type = "pdf" target = "dataTable2" fileName = "historique_manipulation" / >
< / p:commandButton >
< / div >
< / div >
< p:dataTable id = "dataTable2" var = "histManip"
rows="10" paginator="true" draggableColumns="true"
value="#{formUserBean.listehistManipulation}"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="10,30,40,60,80,100"
emptyMessage="#{msg['distribution.page.produit.liste.historique.non'] }"
selectionMode="single"
rowKey="#{histManip.id}">
< p:column style = " text-align: center;" headerText = "#{msg['distribution.page.nomTable']}" filterBy = "#{histManip.nomTable}" filterMatchMode = "contains" sortBy = "#{histManip.nomTable}" >
< h:outputText value = "#{histManip.nomTable}" / >
2026-07-26 14:39:10 +02:00
< / p:column >
2026-08-05 14:35:11 +02:00
< p:column style = " text-align: center;" headerText = "#{msg['distribution.page.idLigne']}" filterBy = "#{histManip.idLigne}" filterMatchMode = "contains" sortBy = "#{histManip.idLigne}" >
< h:outputText value = "#{histManip.idLigne}" / >
< / p:column >
< p:column style = " text-align: center;" headerText = "#{msg['distribution.page.type.historiqueManip']}" filterBy = "#{histManip.typeManipulation}" filterMatchMode = "contains" sortBy = "#{histManip.typeManipulation}" >
< h:outputText value = "#{histManip.typeManipulation}" / >
< / p:column >
< p:column style = " text-align: center;" headerText = "#{msg['distribution.page.dateManip']}" filterBy = "#{histManip.dateManipulation}" filterMatchMode = "contains" sortBy = "#{histManip.dateManipulation}" >
< h:outputText value = "#{histManip.dateManipulation}" / >
< / p:column >
< p:column style = " text-align: center;" headerText = "#{msg['distribution.page.oldValeur']}" filterBy = "#{histManip.oldValeur}" filterMatchMode = "contains" sortBy = "#{histManip.oldValeur}" >
< h:outputText value = "#{histManip.oldValeur}" / >
< / p:column >
< p:column style = " text-align: center;" headerText = "#{msg['distribution.page.newValeur']}" filterBy = "#{histManip.newValeur}" filterMatchMode = "contains" sortBy = "#{histManip.newValeur}" >
< h:outputText value = "#{histManip.newValeur}" / >
< / p:column >
< / p:dataTable >
2026-07-26 14:39:10 +02:00
< / p:tab >
< / p:tabView >
< / p:card >
< / div >
< / div >
2026-08-05 14:35:11 +02:00
2026-07-26 14:39:10 +02:00
< / h:form >
< / ui:define >
< / ui:composition >