Versions stable (inachevée mais prête à un déploiement en prod)

This commit is contained in:
DahoudG
2024-12-15 16:35:50 +00:00
parent a276ac2318
commit d2cb9da730
126 changed files with 13559 additions and 631 deletions

View File

@@ -0,0 +1,171 @@
package dev.lions.components;
import jakarta.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.primefaces.model.charts.*;
import org.primefaces.model.charts.bar.*;
import org.primefaces.model.charts.line.*;
import org.primefaces.model.charts.pie.*;
import org.primefaces.model.charts.optionconfig.title.Title;
import org.primefaces.model.charts.optionconfig.legend.Legend;
import java.io.Serializable;
import java.util.*;
/**
* Composant de gestion des graphiques.
* Fournit des modèles pour les graphiques linéaires, en barres et circulaires.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@Named
@ViewScoped
public class ChartComponent implements Serializable {
private static final long serialVersionUID = 1L;
private static final List<String> CHART_COLORS = Arrays.asList(
"rgba(33, 150, 243, 0.8)",
"rgba(255, 64, 129, 0.8)",
"rgba(255, 193, 7, 0.8)",
"rgba(76, 175, 80, 0.8)",
"rgba(156, 39, 176, 0.8)"
);
@Getter @Setter
private LineChartModel lineModel;
@Getter @Setter
private BarChartModel barModel;
@Getter @Setter
private PieChartModel pieModel;
/**
* Initialise les modèles de graphiques lors de la construction du composant.
*/
@PostConstruct
public void init() {
log.info("Initialisation des modèles de graphiques.");
createLineModel();
createBarModel();
createPieModel();
}
/**
* Crée un modèle de graphique linéaire.
*/
private void createLineModel() {
lineModel = new LineChartModel();
ChartData data = new ChartData();
LineChartDataSet dataSet = new LineChartDataSet();
dataSet.setLabel("Évolution des ventes");
dataSet.setData(new ArrayList<>(generateRandomData(6)));
dataSet.setBorderColor(CHART_COLORS.get(0));
dataSet.setFill(false);
dataSet.setTension(0.4);
data.addChartDataSet(dataSet);
data.setLabels(generateLabels(6, "Mois"));
lineModel.setData(data);
addChartOptions(lineModel, "Évolution temporelle");
log.debug("Modèle de graphique linéaire créé.");
}
/**
* Crée un modèle de graphique en barres.
*/
private void createBarModel() {
barModel = new BarChartModel();
ChartData data = new ChartData();
BarChartDataSet dataSet = new BarChartDataSet();
dataSet.setLabel("Performance par trimestre");
dataSet.setData(new ArrayList<>(generateRandomData(4)));
dataSet.setBackgroundColor(CHART_COLORS.get(1));
data.addChartDataSet(dataSet);
data.setLabels(generateLabels(4, "T"));
barModel.setData(data);
addChartOptions(barModel, "Performance trimestrielle");
log.debug("Modèle de graphique en barres créé.");
}
/**
* Crée un modèle de graphique circulaire.
*/
private void createPieModel() {
pieModel = new PieChartModel();
ChartData data = new ChartData();
PieChartDataSet dataSet = new PieChartDataSet();
dataSet.setData(Arrays.asList(25, 35, 40));
dataSet.setBackgroundColor(CHART_COLORS);
data.addChartDataSet(dataSet);
data.setLabels(Arrays.asList("Développement", "Marketing", "Infrastructure"));
pieModel.setData(data);
addChartOptions(pieModel, "Répartition des activités");
log.debug("Modèle de graphique circulaire créé.");
}
/**
* Ajoute des options telles que le titre et la légende aux graphiques.
*
* @param model Le modèle de graphique.
* @param title Titre du graphique.
*/
private void addChartOptions(ChartModel model, String title) {
Title chartTitle = new Title();
chartTitle.setDisplay(true);
chartTitle.setText(title);
Legend legend = new Legend();
legend.setDisplay(true);
legend.setPosition("bottom");
if (model instanceof LineChartModel) {
LineChartModel lineChart = (LineChartModel) model;
lineChart.setExtender((String) chartTitle.getText());
} else if (model instanceof BarChartModel) {
BarChartModel barChart = (BarChartModel) model;
barChart.setExtender((String) chartTitle.getText());
} else if (model instanceof PieChartModel) {
PieChartModel pieChart = (PieChartModel) model;
pieChart.setExtender((String) chartTitle.getText());
}
}
private List<Number> generateRandomData(int size) {
Random random = new Random();
List<Number> data = new ArrayList<>();
for (int i = 0; i < size; i++) {
data.add(random.nextInt(100));
}
return data;
}
private List<String> generateLabels(int size, String prefix) {
List<String> labels = new ArrayList<>();
for (int i = 1; i <= size; i++) {
labels.add(prefix + " " + i);
}
return labels;
}
}

View File

@@ -0,0 +1,4 @@
package dev.lions.components;
public class DataTableView {
}

View File

@@ -0,0 +1,295 @@
package dev.lions.components;
import jakarta.enterprise.context.Dependent;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.io.Serial;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.primefaces.model.FilterMeta;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortMeta;
import org.primefaces.event.data.PageEvent;
import dev.lions.utils.Column;
import dev.lions.utils.FilterCriteria;
import dev.lions.exceptions.DataTableException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Composant de tableau de données dynamique avec support de pagination, tri et filtrage.
* Fournit une interface riche et performante pour l'affichage et la manipulation des données
* tabulaires dans l'application.
*
* @author Lions Dev Team
* @version 2.1
*/
@Named
@Dependent
@Slf4j
public class DynamicDataTable<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final int DEFAULT_PAGE_SIZE = 10;
private static final int MAX_PAGE_SIZE = 100;
private static final String DEFAULT_EMPTY_MESSAGE = "Aucune donnée disponible";
@Getter @Setter
private List<T> data;
@Getter @Setter
private List<Column> columns;
@Getter @Setter
private String emptyMessage = DEFAULT_EMPTY_MESSAGE;
@Getter @Setter
@Min(1)
private int pageSize = DEFAULT_PAGE_SIZE;
@Getter
private LazyDataModel<T> lazyModel;
@Getter
private final Map<String, FilterCriteria> activeFilters = new ConcurrentHashMap<>();
private final Map<String, Comparator<T>> customSorters = new HashMap<>();
private final Map<String, PropertyAccessor<T>> propertyAccessors = new HashMap<>();
/**
* Initialise le tableau avec les données et les colonnes spécifiées.
*
* @param data Données à afficher
* @param columns Configuration des colonnes
*/
public void initialize(@NotNull List<T> data, @NotNull List<Column> columns) {
log.info("Initialisation du tableau dynamique avec {} enregistrements", data.size());
validateInitializationParameters(data, columns);
this.data = new ArrayList<>(data);
this.columns = new ArrayList<>(columns);
initializePropertyAccessors();
initializeLazyLoading();
}
/**
* Configure le modèle de chargement paresseux des données.
*/
private void initializeLazyLoading() {
lazyModel = new LazyDataModel<T>() {
@Override
public List<T> load(int first, int pageSize, Map<String, SortMeta> sortBy, Map<String, FilterMeta> filterBy) {
try {
return loadDataPage(first, pageSize, sortBy, filterBy);
} catch (Exception e) {
log.error("Erreur lors du chargement des données", e);
throw new DataTableException("Échec du chargement des données", e);
}
}
@Override
public int count(Map<String, FilterMeta> filterBy) {
return data == null ? 0 : data.size();
}
};
lazyModel.setRowCount(data.size());
}
/**
* Charge une page de données selon les critères spécifiés.
*/
protected List<T> loadDataPage(int first, int pageSize, Map<String, SortMeta> sortBy, Map<String, FilterMeta> filterBy) {
return data.stream()
.filter(item -> applyFilters(item, filterBy))
.sorted((a, b) -> applySorting(a, b, sortBy))
.skip(first)
.limit(pageSize)
.collect(Collectors.toList());
}
/**
* Applique les filtres sur un élément.
*/
private boolean applyFilters(T item, Map<String, FilterMeta> filterBy) {
if (filterBy == null || filterBy.isEmpty()) return true;
return filterBy.entrySet().stream().allMatch(entry -> {
Object filterValue = entry.getValue().getFilterValue();
if (filterValue == null) return true;
try {
Object value = getPropertyValue(item, entry.getKey());
return value != null && value.toString().toLowerCase().contains(filterValue.toString().toLowerCase());
} catch (Exception e) {
log.warn("Erreur lors du filtrage", e);
return false;
}
});
}
/**
* Vérifie si un élément correspond à un critère de filtrage.
*/
private boolean matchesFilter(T item, String property, Object filterValue) {
if (filterValue == null) {
return true;
}
try {
Object value = getPropertyValue(item, property);
return compareValues(value, filterValue);
} catch (Exception e) {
log.warn("Erreur lors du filtrage de la propriété: {}", property, e);
return false;
}
}
/**
* Compare deux valeurs pour le filtrage.
*/
private boolean compareValues(Object value, Object filterValue) {
if (value == null) {
return filterValue == null;
}
String valueStr = value.toString().toLowerCase();
String filterStr = filterValue.toString().toLowerCase();
return valueStr.contains(filterStr);
}
/**
* Applique le tri sur les données.
*/
private int applySorting(T a, T b, Map<String, SortMeta> sortBy) {
for (Map.Entry<String, SortMeta> entry : sortBy.entrySet()) {
String property = entry.getKey();
try {
Comparable valueA = (Comparable) getPropertyValue(a, property);
Comparable valueB = (Comparable) getPropertyValue(b, property);
int result = valueA.compareTo(valueB);
return entry.getValue().getOrder().isAscending() ? result : -result;
} catch (Exception e) {
log.warn("Erreur lors du tri", e);
}
}
return 0;
}
/**
* Compare les valeurs de deux propriétés pour le tri.
*/
@SuppressWarnings("unchecked")
private int compareProperties(T a, T b, String property) {
try {
Comparable valueA = (Comparable) getPropertyValue(a, property);
Comparable valueB = (Comparable) getPropertyValue(b, property);
if (valueA == null && valueB == null) return 0;
if (valueA == null) return -1;
if (valueB == null) return 1;
return valueA.compareTo(valueB);
} catch (Exception e) {
log.warn("Erreur lors de la comparaison de la propriété: {}", property, e);
return 0;
}
}
/**
* Initialise les accesseurs de propriétés pour optimiser les performances.
*/
private void initializePropertyAccessors() {
columns.forEach(column -> {
String property = column.getField();
try {
Method getter = findGetter(property);
propertyAccessors.put(property, item -> getter.invoke(item));
} catch (Exception e) {
log.warn("Impossible de créer l'accesseur pour la propriété: {}", property, e);
}
});
}
/**
* Trouve la méthode getter pour une propriété.
*/
private Method findGetter(String property) throws NoSuchMethodException {
String getterName = "get" + property.substring(0, 1).toUpperCase() + property.substring(1);
return data.get(0).getClass().getMethod(getterName);
}
/**
* Interface fonctionnelle pour l'accès aux propriétés.
*/
@FunctionalInterface
private interface PropertyAccessor<T> {
Object access(T item) throws Exception;
}
/**
* Ajoute un trieur personnalisé pour une colonne.
*/
public void addCustomSorter(String property, Comparator<T> comparator) {
customSorters.put(property, comparator);
}
/**
* Met à jour le nombre total de lignes.
*/
private void updateRowCount() {
if (lazyModel != null) {
lazyModel.setRowCount(data.size());
}
}
/**
* Gère l'événement de changement de page.
*/
public void onPageChange(PageEvent event) {
log.debug("Changement de page: {}", event.getPage());
}
/**
* Valide les paramètres d'initialisation.
*/
private void validateInitializationParameters(List<T> data, List<Column> columns) {
if (data == null || data.isEmpty()) {
throw new DataTableException("Les données ne peuvent pas être nulles ou vides");
}
if (columns == null || columns.isEmpty()) {
throw new DataTableException("La configuration des colonnes est requise");
}
}
/**
* Rafraîchit les données du tableau.
*/
public void refresh() {
log.debug("Rafraîchissement du tableau");
updateRowCount();
}
private Object getPropertyValue(T item, String property) throws Exception {
PropertyAccessor<T> accessor = propertyAccessors.get(property);
if (accessor != null) {
return accessor.access(item);
}
throw new NoSuchFieldException("Propriété inaccessible : " + property);
}
}

View File

@@ -0,0 +1,226 @@
package dev.lions.components;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotNull;
import java.io.Serial;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.file.UploadedFile;
import dev.lions.config.ApplicationConfig;
import dev.lions.exceptions.FileUploadException;
import dev.lions.services.FileStorageService;
import dev.lions.utils.FileValidator;
import dev.lions.utils.SecurityUtils;
import java.io.Serializable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Composant de gestion des téléchargements de fichiers.
* Fournit une interface sécurisée et performante pour le téléchargement,
* la validation et la gestion des fichiers dans l'application.
*
* @author Lions Dev Team
* @version 2.1
*/
@Named
@ViewScoped
@Slf4j
public class FileUploadComponent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final int MAX_FILES = 10; // Limite de fichiers autorisés
private static final String TEMP_DIR_PREFIX = "upload_"; // Préfixe pour répertoire temporaire
@Inject
ApplicationConfig appConfig;
@Inject
FileStorageService storageService;
@Inject
FileValidator fileValidator;
@Inject
SecurityUtils securityUtils;
@Getter
private final List<UploadedFileInfo> uploadedFiles = new ArrayList<>();
@Getter
@Setter
private String uploadDirectory;
@Getter
@Setter
private boolean multiple = false;
@Getter
@Setter
private String acceptedTypes;
@Getter
@Setter
private long maxFileSize;
/**
* Initialisation du composant.
*/
@PostConstruct
public void init() {
this.maxFileSize = appConfig.getMaxFileSize();
this.acceptedTypes = appConfig.getAllowedFileTypes();
this.uploadDirectory = createTempUploadDirectory();
log.info("Composant de téléchargement initialisé. Taille max: {}, Types acceptés: {}",
maxFileSize, acceptedTypes);
}
/**
* Gère l'événement de téléchargement de fichier.
*
* @param event L'événement PrimeFaces contenant le fichier téléchargé.
*/
public void handleFileUpload(@NotNull FileUploadEvent event) {
UploadedFile file = event.getFile();
log.info("Téléchargement de fichier : {}", file.getFileName());
try {
validateUploadRequest(file);
UploadedFileInfo fileInfo = processUploadedFile(file);
uploadedFiles.add(fileInfo);
addSuccessMessage("Fichier téléchargé avec succès : " + fileInfo.getFileName());
} catch (FileUploadException e) {
log.error("Erreur de validation du fichier : {}", file.getFileName(), e);
addErrorMessage(e.getMessage());
} catch (IOException e) {
log.error("Erreur lors du traitement du fichier : {}", file.getFileName(), e);
addErrorMessage("Une erreur est survenue lors du traitement du fichier.");
}
}
/**
* Valide la requête de téléchargement.
*
* @param file Le fichier téléchargé.
*/
private void validateUploadRequest(UploadedFile file) {
if (uploadedFiles.size() >= MAX_FILES) {
throw new FileUploadException("Vous avez atteint le nombre maximum de fichiers autorisés.");
}
fileValidator.validateFile(file, acceptedTypes, maxFileSize);
}
/**
* Traite et stocke le fichier téléchargé.
*
* @param file Le fichier téléchargé.
* @return Les informations du fichier.
* @throws IOException En cas d'erreur de stockage.
*/
private UploadedFileInfo processUploadedFile(UploadedFile file) throws IOException {
String secureFileName = generateSecureFileName(file.getFileName());
Path destinationPath = storageService.storeFile(file.getInputStream(), uploadDirectory, secureFileName);
return UploadedFileInfo.builder()
.id(UUID.randomUUID().toString())
.fileName(file.getFileName())
.contentType(file.getContentType())
.size(file.getSize())
.path(destinationPath)
.build();
}
/**
* Génère un nom de fichier sécurisé.
*
* @param originalFileName Nom original.
* @return Nom sécurisé.
*/
private String generateSecureFileName(String originalFileName) {
String extension = getFileExtension(originalFileName);
return securityUtils.sanitizeFileName(UUID.randomUUID().toString() + "." + extension);
}
/**
* Récupère l'extension d'un fichier.
*
* @param fileName Nom du fichier.
* @return Extension.
*/
private String getFileExtension(String fileName) {
return fileName.substring(fileName.lastIndexOf('.') + 1);
}
/**
* Crée un répertoire temporaire pour les téléchargements.
*
* @return Le chemin du répertoire temporaire.
*/
private String createTempUploadDirectory() {
return storageService.createTempDirectory(TEMP_DIR_PREFIX + UUID.randomUUID());
}
/**
* Nettoie les ressources lors de la destruction du composant.
*/
@PreDestroy
public void cleanup() {
try {
storageService.deleteDirectory(uploadDirectory);
log.info("Répertoire temporaire supprimé : {}", uploadDirectory);
} catch (Exception e) {
log.error("Erreur lors du nettoyage des ressources : {}", uploadDirectory, e);
}
}
/**
* Ajoute un message de succès dans l'interface utilisateur.
*
* @param message Message à afficher.
*/
private void addSuccessMessage(String message) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", message));
}
/**
* Ajoute un message d'erreur dans l'interface utilisateur.
*
* @param message Message à afficher.
*/
private void addErrorMessage(String message) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", message));
}
/**
* Classe interne représentant un fichier téléchargé.
*/
@Getter
@Builder
public static class UploadedFileInfo {
private final String id;
private final String fileName;
private final String contentType;
private final long size;
private final Path path;
}
}

View File

@@ -0,0 +1,225 @@
package dev.lions.components;
import jakarta.annotation.PostConstruct;
import jakarta.faces.model.SelectItem;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import jakarta.faces.context.FacesContext;
import jakarta.faces.application.FacesMessage;
import jakarta.validation.constraints.NotNull;
import java.io.Serial;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import dev.lions.utils.FilterOperator;
import dev.lions.utils.FilterCriteria;
import dev.lions.exceptions.FilterException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.*;
/**
* Composant de gestion des filtres dynamiques.
* Permet la création, la validation et l'application de filtres
* pour des tableaux de données.
*
* <p>Fonctionnalités incluses :
* <ul>
* <li>Ajout de filtres avec validation des entrées</li>
* <li>Suppression de filtres</li>
* <li>Application des filtres sur des listes d'objets</li>
* <li>Interface utilisateur avec feedback via les messages JSF</li>
* </ul>
*
* @author Lions Dev Team
* @version 2.2
*/
@Slf4j
@Named
@ViewScoped
public class FilterComponent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final int MAX_FILTERS = 10;
@Getter @Setter
private List<FilterCriteria> criteria = new ArrayList<>();
@Getter @Setter
private String selectedField;
@Getter @Setter
private FilterOperator selectedOperator;
@Getter @Setter
private String filterValue;
@Getter
private List<SelectItem> availableFields;
@Getter
private List<SelectItem> availableOperators;
private final Map<String, String> fieldConfigurations = new LinkedHashMap<>();
@PostConstruct
public void init() {
log.debug("Initialisation du composant de filtrage");
initializeFieldConfigurations();
initializeAvailableFields();
initializeAvailableOperators();
}
/**
* Initialise la configuration des champs disponibles.
*/
private void initializeFieldConfigurations() {
fieldConfigurations.put("name", "Nom");
fieldConfigurations.put("date", "Date");
fieldConfigurations.put("status", "Statut");
fieldConfigurations.put("category", "Catégorie");
fieldConfigurations.put("price", "Prix");
log.info("Champs disponibles pour le filtrage : {}", fieldConfigurations.keySet());
}
/**
* Remplit la liste des champs disponibles.
*/
private void initializeAvailableFields() {
availableFields = new ArrayList<>();
fieldConfigurations.forEach((key, value) ->
availableFields.add(new SelectItem(key, value))
);
}
/**
* Remplit la liste des opérateurs disponibles.
*/
private void initializeAvailableOperators() {
availableOperators = new ArrayList<>();
for (FilterOperator operator : FilterOperator.values()) {
availableOperators.add(new SelectItem(operator, operator.getLabel()));
}
}
/**
* Ajoute un critère de filtrage après validation.
*/
public void addFilter() {
log.debug("Ajout d'un filtre : Champ = {}, Opérateur = {}, Valeur = {}",
selectedField, selectedOperator, filterValue);
try {
validateFilterInput();
validateFilterLimit();
FilterCriteria newCriteria = new FilterCriteria(selectedField, selectedOperator, filterValue);
criteria.add(newCriteria);
addMessage(FacesMessage.SEVERITY_INFO, "Filtre ajouté", "Filtre appliqué avec succès.");
log.info("Filtre ajouté avec succès : {}", newCriteria);
resetForm();
} catch (FilterException e) {
log.warn("Erreur de validation du filtre", e);
addMessage(FacesMessage.SEVERITY_ERROR, "Erreur", e.getMessage());
}
}
/**
* Supprime un critère de filtrage.
*
* @param filter Le critère à supprimer.
*/
public void removeFilter(@NotNull FilterCriteria filter) {
log.debug("Suppression du filtre : {}", filter);
criteria.remove(filter);
addMessage(FacesMessage.SEVERITY_INFO, "Filtre supprimé", "Le filtre a été retiré.");
}
/**
* Efface tous les filtres existants.
*/
public void clearAllFilters() {
log.info("Suppression de tous les filtres ({})", criteria.size());
criteria.clear();
addMessage(FacesMessage.SEVERITY_INFO, "Filtres effacés", "Tous les filtres ont été supprimés.");
}
/**
* Applique les filtres sur une liste de données.
*
* @param data Liste des objets à filtrer.
* @return Liste filtrée.
*/
public List<Object> applyFilters(List<Object> data) {
if (criteria.isEmpty()) {
return data;
}
log.debug("Application des filtres sur {} éléments", data.size());
return data.stream().filter(this::matchesAllCriteria).toList();
}
/**
* Valide les entrées du filtre.
*/
private void validateFilterInput() {
if (selectedField == null || selectedOperator == null || filterValue == null) {
throw new FilterException("Tous les champs du filtre doivent être remplis.");
}
if (selectedOperator.isNumericComparison()) {
try {
Double.parseDouble(filterValue);
} catch (NumberFormatException e) {
throw new FilterException("La valeur doit être numérique pour cet opérateur.");
}
}
}
private void validateFilterLimit() {
if (criteria.size() >= MAX_FILTERS) {
throw new FilterException("Nombre maximum de filtres atteint (" + MAX_FILTERS + ")");
}
}
private boolean matchesAllCriteria(Object item) {
return criteria.stream().allMatch(filter -> matchesCriteria(item, filter));
}
private boolean matchesCriteria(Object item, FilterCriteria filter) {
try {
Object value = getPropertyValue(item, filter.getField());
return filter.getOperator().apply(value, (String) filter.getValue());
} catch (Exception e) {
log.warn("Erreur d'accès à la propriété : {}", filter.getField(), e);
return false;
}
}
private Object getPropertyValue(Object item, String property) {
try {
Method getter = item.getClass().getMethod("get" + capitalize(property));
return getter.invoke(item);
} catch (Exception e) {
throw new FilterException("Propriété inaccessible : " + property);
}
}
private String capitalize(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
private void resetForm() {
selectedField = null;
selectedOperator = null;
filterValue = null;
}
private void addMessage(FacesMessage.Severity severity, String summary, String detail) {
FacesContext.getCurrentInstance()
.addMessage(null, new FacesMessage(severity, summary, detail));
}
}

View File

@@ -0,0 +1,84 @@
package dev.lions.components;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotBlank;
import java.io.Serial;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.util.ResourceBundle;
/**
* Composant gérant l'affichage des notifications dans l'interface utilisateur.
*/
@Slf4j
@Named
@ViewScoped
public class NotificationComponent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final String MESSAGE_BUNDLE = "messages";
@Inject
FacesContext facesContext;
@Inject
transient ResourceBundle messageBundle;
/**
* Affiche un message de succès.
*/
public void showSuccess(@NotBlank String key) {
log.debug("Affichage message succès: {}", key);
addMessage(FacesMessage.SEVERITY_INFO,
getMessage(key + ".title", "Succès"),
getMessage(key + ".detail"));
}
/**
* Affiche un message d'erreur.
*/
public void showError(@NotBlank String key) {
log.debug("Affichage message erreur: {}", key);
addMessage(FacesMessage.SEVERITY_ERROR,
getMessage(key + ".title", "Erreur"),
getMessage(key + ".detail"));
}
/**
* Affiche un message d'avertissement.
*/
public void showWarning(@NotBlank String key) {
log.debug("Affichage message avertissement: {}", key);
addMessage(FacesMessage.SEVERITY_WARN,
getMessage(key + ".title", "Attention"),
getMessage(key + ".detail"));
}
/**
* Récupère un message localisé avec fallback.
*/
private String getMessage(String key, String defaultValue) {
try {
return messageBundle.getString(key);
} catch (Exception e) {
log.warn("Message non trouvé: {}", key);
return defaultValue;
}
}
private String getMessage(String key) {
return getMessage(key, key);
}
private void addMessage(FacesMessage.Severity severity, String summary, String detail) {
facesContext.addMessage(null, new FacesMessage(severity, summary, detail));
log.debug("Message ajouté: {} - {}", summary, detail);
}
}