ContextService
Descriere Generală
ContextService
este un serviciu esențial pentru gestionarea contextelor utilizatorilor în aplicațiile FOD. Permite utilizatorilor să comute între contexte personale (Individual) și organizaționale, gestionând persistența selecției și oferind evenimente pentru schimbări de context. Este fundamental pentru aplicațiile care permit acțiuni atât în nume personal, cât și în numele organizațiilor.
Înregistrare
// În Program.cs sau Startup.cs
builder.Services.AddScoped<IContextService, ContextService>();
// Servicii dependente necesare
builder.Services.AddScoped<ICurrentUserContextService, CurrentUserContextService>();
builder.Services.AddScoped<IApplicationModelLoader, ApplicationModelLoader>();
Interfața IContextService
public interface IContextService
{
EventHandler<UserContext> ContextChanged { get; set; }
EventHandler ChangeContextRequested { get; set; }
EventHandler ChangeContextRequestedCard { get; set; }
Task<IEnumerable<UserContext>> Get();
Task<UserContext> GetCurrent();
Task<UserContext> GetIndividualContext();
Task<bool> HasCurrentContext();
Task<bool> HasContexts();
Task<bool> GetContextSelectionRequired();
Task SetCurrent(UserContext context, bool selected = false);
Task SetCurrent(string idn);
Task ClearData();
Task RequestContextChange();
Task RequestContextChangeCard();
}
Model UserContext
public class UserContext
{
public string ContextId { get; set; }
public string ContextName { get; set; }
public UserContextType ContextType { get; set; }
public bool IsSelected { get; set; }
}
public enum UserContextType
{
Individual,
Organization
}
Exemple de Utilizare
Obținere Context Curent
@inject IContextService ContextService
<div class="current-context">
@if (currentContext != null)
{
<p>Context activ: @currentContext.ContextName</p>
<p>Tip: @currentContext.ContextType</p>
}
else
{
<p>Niciun context selectat</p>
}
</div>
@code {
private UserContext currentContext;
protected override async Task OnInitializedAsync()
{
currentContext = await ContextService.GetCurrent();
// Abonare la schimbări de context
ContextService.ContextChanged += OnContextChanged;
}
private void OnContextChanged(object sender, UserContext newContext)
{
currentContext = newContext;
InvokeAsync(StateHasChanged);
}
public void Dispose()
{
ContextService.ContextChanged -= OnContextChanged;
}
}
Selector de Context
@inject IContextService ContextService
<div class="context-selector">
<h4>Selectați contextul de lucru:</h4>
@if (contexts != null)
{
<div class="context-list">
@foreach (var context in contexts)
{
<div class="context-item @(IsCurrentContext(context) ? "active" : "")"
@onclick="() => SelectContext(context)">
<FodIcon Icon="@GetContextIcon(context)" />
<span>@context.ContextName</span>
@if (IsCurrentContext(context))
{
<FodBadge Color="FodColor.Success">Activ</FodBadge>
}
</div>
}
</div>
}
</div>
@code {
private IEnumerable<UserContext> contexts;
private UserContext currentContext;
protected override async Task OnInitializedAsync()
{
contexts = await ContextService.Get();
currentContext = await ContextService.GetCurrent();
}
private async Task SelectContext(UserContext context)
{
await ContextService.SetCurrent(context, true);
currentContext = context;
}
private bool IsCurrentContext(UserContext context)
{
return currentContext?.ContextId == context.ContextId;
}
private string GetContextIcon(UserContext context)
{
return context.ContextType == UserContextType.Individual
? FodIcons.Material.Filled.Person
: FodIcons.Material.Filled.Business;
}
}
Verificare Selecție Context Necesară
@inject IContextService ContextService
@inject NavigationManager Navigation
@if (contextSelectionRequired)
{
<div class="context-selection-required">
<FodAlert Severity="FodSeverity.Warning">
Vă rugăm selectați un context pentru a continua.
</FodAlert>
<FodButton @onclick="OpenContextSelector" Color="FodColor.Primary">
Selectează Context
</FodButton>
</div>
}
@code {
private bool contextSelectionRequired;
protected override async Task OnInitializedAsync()
{
contextSelectionRequired = await ContextService.GetContextSelectionRequired();
if (contextSelectionRequired)
{
// Redirecționează către pagina de selecție context
Navigation.NavigateTo("/select-context");
}
}
private void OpenContextSelector()
{
ContextService.RequestContextChange();
}
}
Context în Header-ul Aplicației
@inject IContextService ContextService
<div class="app-header">
<div class="context-display">
@if (currentContext != null)
{
<div class="context-info" @onclick="RequestContextChange">
<FodIcon Icon="@GetIcon()" Size="FodSize.Small" />
<span>@GetContextDisplayName()</span>
<FodIcon Icon="@FodIcons.Material.Filled.ArrowDropDown" Size="FodSize.Small" />
</div>
}
</div>
</div>
@code {
private UserContext currentContext;
protected override async Task OnInitializedAsync()
{
currentContext = await ContextService.GetCurrent();
ContextService.ContextChanged += HandleContextChanged;
}
private void HandleContextChanged(object sender, UserContext context)
{
currentContext = context;
InvokeAsync(StateHasChanged);
}
private string GetIcon()
{
return currentContext.ContextType == UserContextType.Individual
? FodIcons.Material.Filled.Person
: FodIcons.Material.Filled.Business;
}
private string GetContextDisplayName()
{
if (currentContext.ContextType == UserContextType.Individual)
return "Personal";
return currentContext.ContextName;
}
private void RequestContextChange()
{
ContextService.RequestContextChange();
}
}
Context Switch cu Confirmare
@inject IContextService ContextService
@inject IFodNotificationService NotificationService
<div class="context-switcher">
@foreach (var context in availableContexts)
{
<FodButton @onclick="() => SwitchContext(context)"
Disabled="@(IsCurrentContext(context))">
Comută la @context.ContextName
</FodButton>
}
</div>
<FodModal @bind-Visible="showConfirmDialog" Title="Confirmare schimbare context">
<BodyTemplate>
<p>Sunteți sigur că doriți să schimbați contextul la <strong>@pendingContext?.ContextName</strong>?</p>
<p class="text-muted">Această acțiune poate afecta datele afișate și operațiunile disponibile.</p>
</BodyTemplate>
<FooterTemplate>
<FodButton @onclick="CancelSwitch">Anulează</FodButton>
<FodButton Color="FodColor.Primary" @onclick="ConfirmSwitch">Confirmă</FodButton>
</FooterTemplate>
</FodModal>
@code {
private IEnumerable<UserContext> availableContexts = new List<UserContext>();
private UserContext currentContext;
private UserContext pendingContext;
private bool showConfirmDialog;
protected override async Task OnInitializedAsync()
{
availableContexts = await ContextService.Get();
currentContext = await ContextService.GetCurrent();
}
private void SwitchContext(UserContext context)
{
if (IsCurrentContext(context)) return;
pendingContext = context;
showConfirmDialog = true;
}
private async Task ConfirmSwitch()
{
await ContextService.SetCurrent(pendingContext, true);
NotificationService.Add($"Context schimbat la {pendingContext.ContextName}", FodSeverity.Success);
currentContext = pendingContext;
pendingContext = null;
showConfirmDialog = false;
}
private void CancelSwitch()
{
pendingContext = null;
showConfirmDialog = false;
}
private bool IsCurrentContext(UserContext context)
{
return currentContext?.ContextId == context.ContextId;
}
}
Context Card Display
@inject IContextService ContextService
<div class="context-cards">
@foreach (var context in contexts)
{
<div class="context-card @(IsActive(context) ? "active" : "")"
@onclick="() => SelectContext(context)">
<div class="card-header">
<FodIcon Icon="@GetIcon(context)" Size="FodSize.Large" />
</div>
<div class="card-body">
<h5>@context.ContextName</h5>
<p class="text-muted">
@(context.ContextType == UserContextType.Individual
? "Acțiuni personale"
: "Acțiuni în numele organizației")
</p>
</div>
@if (IsActive(context))
{
<div class="card-footer">
<FodBadge Color="FodColor.Success">Context Activ</FodBadge>
</div>
}
</div>
}
</div>
@code {
private IEnumerable<UserContext> contexts;
private UserContext activeContext;
protected override async Task OnInitializedAsync()
{
contexts = await ContextService.Get();
activeContext = await ContextService.GetCurrent();
// Răspunde la cereri de schimbare card
ContextService.ChangeContextRequestedCard += HandleCardChangeRequest;
}
private void HandleCardChangeRequest(object sender, EventArgs e)
{
// Afișează selector card-uri
StateHasChanged();
}
private async Task SelectContext(UserContext context)
{
await ContextService.SetCurrent(context, true);
activeContext = context;
}
private bool IsActive(UserContext context) => activeContext?.ContextId == context.ContextId;
private string GetIcon(UserContext context)
{
return context.ContextType == UserContextType.Individual
? FodIcons.Material.Filled.AccountCircle
: FodIcons.Material.Filled.CorporateFare;
}
}
Gestionare Context pentru Formulare
@inject IContextService ContextService
<EditForm Model="@model" OnValidSubmit="@HandleSubmit">
<div class="form-context-info">
<FodAlert Severity="FodSeverity.Info">
Completați formularul în contextul: <strong>@currentContext?.ContextName</strong>
</FodAlert>
</div>
<!-- Câmpuri formular bazate pe context -->
@if (currentContext?.ContextType == UserContextType.Individual)
{
<FODInputText @bind-Value="model.PersonalIdnp" Label="IDNP" />
<FODInputText @bind-Value="model.PersonalName" Label="Nume și prenume" />
}
else
{
<FODInputText @bind-Value="model.OrganizationIdno" Label="IDNO" />
<FODInputText @bind-Value="model.OrganizationName" Label="Denumire organizație" />
<FODInputText @bind-Value="model.RepresentativeName" Label="Reprezentant" />
}
<FodButton Type="submit">Trimite</FodButton>
</EditForm>
@code {
private FormModel model = new();
private UserContext currentContext;
protected override async Task OnInitializedAsync()
{
currentContext = await ContextService.GetCurrent();
// Pre-populează date bazat pe context
if (currentContext?.ContextType == UserContextType.Organization)
{
model.OrganizationIdno = currentContext.ContextId;
model.OrganizationName = currentContext.ContextName;
}
}
}
Evenimente
ContextChanged
ContextService.ContextChanged += (sender, newContext) =>
{
// Reacționează la schimbarea contextului
Console.WriteLine($"Context schimbat la: {newContext?.ContextName}");
};
ChangeContextRequested
ContextService.ChangeContextRequested += (sender, args) =>
{
// Deschide UI pentru schimbare context
ShowContextSelector();
};
ChangeContextRequestedCard
ContextService.ChangeContextRequestedCard += (sender, args) =>
{
// Deschide selector tip card
ShowContextCards();
};
Persistență Context
Serviciul folosește ICurrentUserContextService
pentru persistența selecției:
// Salvare în localStorage/sessionStorage
await _currentUserContextService.Set(contextId, isSelected);
// Recuperare
var savedContext = await _currentUserContextService.GetSelectedContext();
Integrare cu ApplicationModel
// Contexte disponibile sunt încărcate din ApplicationModel
var contexts = (await _applicationModelLoader.Load())?.UserContexts;
Cazuri Speciale
Context Individual Implicit
Dacă nu există context selectat, serviciul selectează automat contextul Individual:
if (string.IsNullOrEmpty(selectedContext?.ContextId))
{
var individualContext = contexts.FirstOrDefault(c => c.ContextType == UserContextType.Individual);
await SetCurrent(individualContext, contexts.Count() == 1);
return individualContext;
}
Clear Data
// Șterge toate datele de context
await ContextService.ClearData();
Best Practices
- Verificați context la inițializare - Asigurați-vă că există un context valid
- Abonați-vă la evenimente - Pentru a reacționa la schimbări
- Dezabonați-vă corect - Preveniți memory leaks
- Validați contextul pentru operații - Unele acțiuni pot fi restricționate pe context
- Oferiți feedback vizual - Arătați clar contextul activ
Troubleshooting
Context nu se păstrează după refresh
- Verificați implementarea
ICurrentUserContextService
- Asigurați-vă că localStorage/sessionStorage funcționează
Evenimente nu se declanșează
- Verificați abonarea corectă la evenimente
- Folosiți
InvokeAsync(StateHasChanged)
în handler
Context Individual nu apare
- Verificați că ApplicationModel conține contextul Individual
- Verificați permisiunile utilizatorului
Concluzie
ContextService este fundamental pentru aplicațiile multi-context, oferind o gestionare completă a contextelor utilizator cu suport pentru persistență, evenimente și integrare ușoară în UI. Este esențial pentru aplicații care permit acțiuni atât personale cât și în numele organizațiilor.