TrizRis/src/main/webapp/views/user/form.xhtml
2026-08-05 13:35:11 +01:00

606 lines
31 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"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
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()}
<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>
</div>
<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>
</p:card>
</div>
<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}" />
</p:column>
<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>
</p:tab>
</p:tabView>
</p:card>
</div>
</div>
</h:form>
</ui:define>
</ui:composition>