package dev.lions.unionflow.client.view; import dev.lions.unionflow.client.service.AuditTrailRestClient; import dev.lions.unionflow.server.api.dto.audit.response.AuditTrailOperationResponse; import jakarta.faces.application.FacesMessage; import jakarta.faces.context.FacesContext; import jakarta.faces.view.ViewScoped; import jakarta.inject.Inject; import jakarta.inject.Named; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.UUID; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.logging.Logger; /** * Bean Live Activity Feed (Sprint 15 — transparency opérationnelle). * *

Polling 10s sur {@code /api/audit-trail/recent} avec choix du scope : *

* *

L'auto-refresh est piloté côté UI via {@code <p:poll>} qui appelle {@link #rafraichir()}. */ @Named @ViewScoped public class LiveFeedBean implements Serializable { private static final long serialVersionUID = 1L; private static final Logger LOG = Logger.getLogger(LiveFeedBean.class); @Inject @RestClient AuditTrailRestClient client; /** SELF (défaut, n'importe quel rôle), ORG (admin/officer), ALL (compliance/contrôleur). */ private String scope = "SELF"; private UUID orgId; private UUID userId; private int limit = 50; private List operations = Collections.emptyList(); private long compteur; private String erreur; public void rafraichir() { erreur = null; try { operations = client.recent(scope, orgId, userId, limit); compteur++; LOG.debugf("LiveFeed (%s) refresh #%d : %d ops", scope, compteur, operations == null ? 0 : operations.size()); } catch (Exception e) { LOG.warnf("LiveFeed refresh échoué : %s", e.getMessage()); erreur = "Échec rafraîchissement : " + e.getMessage(); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Erreur LiveFeed", erreur)); operations = Collections.emptyList(); } } public String getCouleurAction(String actionType) { if (actionType == null) return "secondary"; return switch (actionType) { case "DELETE", "PAYMENT_FAILED" -> "danger"; case "VALIDATE", "PAYMENT_CONFIRMED", "AID_REQUEST_APPROVED" -> "success"; case "UPDATE", "PAYMENT_INITIATED", "BUDGET_APPROVED" -> "info"; case "CREATE" -> "primary"; case "EXPORT" -> "warning"; default -> "secondary"; }; } public String getCouleurSod(Boolean sodCheckPassed) { if (sodCheckPassed == null) return "secondary"; return sodCheckPassed ? "success" : "danger"; } /** Pour l'affichage relatif "il y a Xs". */ public String tempsRelatif(java.time.LocalDateTime dt) { if (dt == null) return "—"; long secondes = java.time.Duration.between(dt, java.time.LocalDateTime.now()).getSeconds(); if (secondes < 0) return "à l'instant"; if (secondes < 60) return "il y a " + secondes + "s"; if (secondes < 3600) return "il y a " + (secondes / 60) + "m"; if (secondes < 86400) return "il y a " + (secondes / 3600) + "h"; return "il y a " + (secondes / 86400) + "j"; } // Getters / setters public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public UUID getOrgId() { return orgId; } public void setOrgId(UUID orgId) { this.orgId = orgId; } public UUID getUserId() { return userId; } public void setUserId(UUID userId) { this.userId = userId; } public int getLimit() { return limit; } public void setLimit(int limit) { this.limit = Math.max(1, Math.min(limit, 500)); } public List getOperations() { return operations; } public long getCompteur() { return compteur; } public String getErreur() { return erreur; } }