using Microsoft.AspNetCore.SignalR; namespace SkillManagementSL.EndPoints.Hubs; // Real-time push for the Skill Matrix and Employee Skill Profile screens — closes the // "no real-time push" gap noted in the Skill Management feature guide (previously fast-refresh/ // on-demand only). Hub is transient; no mutable state stored in instance fields. LoginDTO trust // model isn't needed here since group membership carries no identity/authorization semantics // (employee/tenant groups are pure "what am I watching," same tenant-scoping the underlying REST // endpoints already enforce on every fetch) — mirrors WiSL.EndPoints.Hubs.WiHub exactly. public class SkillHub(ILogger logger) : Hub { public override async Task OnConnectedAsync() { logger.LogInformation("SkillHub connected: {ConnectionId}", Context.ConnectionId); await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception? exception) { if (exception is not null) logger.LogWarning(exception, "SkillHub disconnected with error: {ConnectionId}", Context.ConnectionId); await base.OnDisconnectedAsync(exception); } // ── Hub methods (client-callable) ────────────────────────────────── public Task JoinEmployeeGroup(int employeeId) => Groups.AddToGroupAsync(Context.ConnectionId, EmployeeGroup(employeeId)); public Task LeaveEmployeeGroup(int employeeId) => Groups.RemoveFromGroupAsync(Context.ConnectionId, EmployeeGroup(employeeId)); public Task JoinMatrixGroup(int clientId) => Groups.AddToGroupAsync(Context.ConnectionId, MatrixGroup(clientId)); public Task LeaveMatrixGroup(int clientId) => Groups.RemoveFromGroupAsync(Context.ConnectionId, MatrixGroup(clientId)); // ── Group naming ──────────────────────────────────────────────────── public static string EmployeeGroup(int employeeId) => $"skill:employee:{employeeId}"; public static string MatrixGroup(int clientId) => $"skill:matrix:{clientId}"; }