Passe l'edition utilisateur admin dans une modal

This commit is contained in:
2026-04-15 22:12:12 +02:00
parent 99a3f6d0aa
commit cd576f941d
2 changed files with 231 additions and 153 deletions

View File

@@ -222,159 +222,172 @@
}
</section>
<section class="panel panel-wide admin-detail-panel">
@if (IsLoadingDetail)
{
<p class="eyebrow">Chargement</p>
<h2>Recuperation de la fiche utilisateur</h2>
<p class="section-copy">Les details du compte sont en cours de chargement.</p>
}
else if (!string.IsNullOrWhiteSpace(DetailError))
{
<p class="eyebrow">Serveur</p>
<h2>Impossible de charger cette fiche</h2>
<p class="profile-feedback error">@DetailError</p>
}
else if (SelectedUser is null)
{
<p class="eyebrow">Edition</p>
<h2>Choisis un utilisateur dans le tableau</h2>
<p class="section-copy">
Le bouton `Modifier` charge la fiche detaillee ici pour editer le compte et le profil du site.
</p>
}
else
{
<div class="section-heading">
<div>
<p class="eyebrow">Edition</p>
<h2>@SelectedUser.DisplayName</h2>
</div>
<p class="section-copy">
Les roles restent geres dans Keycloak. Cette fiche couvre l'etat du compte et le profil du site.
</p>
</div>
<div class="profile-meta-grid">
<article class="profile-meta-card">
<span class="micro-label">Identite Keycloak</span>
<strong>@SelectedUser.IdentityDisplayName</strong>
</article>
<article class="profile-meta-card">
<span class="micro-label">Compte cree le</span>
<strong>@FormatDate(SelectedUser.AccountCreatedUtc)</strong>
</article>
<article class="profile-meta-card">
<span class="micro-label">Profil site cree le</span>
<strong>@FormatDate(SelectedUser.SiteProfileCreatedUtc)</strong>
</article>
<article class="profile-meta-card">
<span class="micro-label">Profil site mis a jour</span>
<strong>@FormatDate(SelectedUser.SiteProfileUpdatedUtc)</strong>
</article>
</div>
<EditForm Model="@EditFormModel" OnValidSubmit="SaveUserAsync">
<DataAnnotationsValidator />
<div class="admin-toggle-grid">
<label class="admin-toggle-card">
<InputCheckbox @bind-Value="EditFormModel.IsEnabled" />
<div>
<strong>Compte actif</strong>
<span>Autoriser ou bloquer la connexion a l'application.</span>
</div>
</label>
<label class="admin-toggle-card">
<InputCheckbox @bind-Value="EditFormModel.IsEmailVerified" />
<div>
<strong>Email verifie</strong>
<span>Indique si l'adresse email a ete validee dans Keycloak.</span>
</div>
</label>
</div>
<div class="admin-form-grid">
<label class="field">
<span>Nom d'utilisateur</span>
<InputText @bind-Value="EditFormModel.Username" />
<ValidationMessage For="@(() => EditFormModel.Username)" />
</label>
<label class="field">
<span>Email</span>
<InputText @bind-Value="EditFormModel.Email" />
<ValidationMessage For="@(() => EditFormModel.Email)" />
</label>
<label class="field">
<span>Prenom</span>
<InputText @bind-Value="EditFormModel.FirstName" />
<ValidationMessage For="@(() => EditFormModel.FirstName)" />
</label>
<label class="field">
<span>Nom</span>
<InputText @bind-Value="EditFormModel.LastName" />
<ValidationMessage For="@(() => EditFormModel.LastName)" />
</label>
<label class="field">
<span>Nom affiche sur le site</span>
<InputText @bind-Value="EditFormModel.DisplayName" />
<ValidationMessage For="@(() => EditFormModel.DisplayName)" />
</label>
<label class="field">
<span>Club</span>
<InputText @bind-Value="EditFormModel.Club" />
<ValidationMessage For="@(() => EditFormModel.Club)" />
</label>
<label class="field">
<span>Ville</span>
<InputText @bind-Value="EditFormModel.City" />
<ValidationMessage For="@(() => EditFormModel.City)" />
</label>
<label class="field">
<span>Format prefere</span>
<InputSelect @bind-Value="EditFormModel.PreferredFormat">
<option value="">Aucune preference</option>
<option value="Twice">Twice</option>
<option value="Time">Time</option>
<option value="Les deux">Les deux</option>
</InputSelect>
<ValidationMessage For="@(() => EditFormModel.PreferredFormat)" />
</label>
<label class="field span-2">
<span>Cube favori</span>
<InputText @bind-Value="EditFormModel.FavoriteCube" />
<ValidationMessage For="@(() => EditFormModel.FavoriteCube)" />
</label>
<label class="field span-2">
<span>Bio</span>
<InputTextArea @bind-Value="EditFormModel.Bio" />
<ValidationMessage For="@(() => EditFormModel.Bio)" />
</label>
</div>
<div class="profile-actions">
<button class="button secondary" type="submit" disabled="@IsSaving">
@(IsSaving ? "Enregistrement..." : "Enregistrer les modifications")
</button>
<p class="section-copy">Le profil site est cree automatiquement lors du premier enregistrement.</p>
</div>
</EditForm>
}
</section>
}
</main>
</div>
<section class="modal @(ShowEditModal ? string.Empty : "hidden")" aria-hidden="@BoolString(!ShowEditModal)">
<div class="modal-backdrop" @onclick="CloseEditModal"></div>
<div class="modal-card admin-modal-card">
<div class="modal-head">
<div>
<p class="eyebrow">Edition</p>
<h2>@BuildEditModalTitle()</h2>
</div>
<button class="button ghost small" type="button" @onclick="CloseEditModal" disabled="@IsSaving">Fermer</button>
</div>
@if (IsLoadingDetail)
{
<p class="section-copy">La fiche utilisateur est en cours de chargement.</p>
}
else if (!string.IsNullOrWhiteSpace(DetailError))
{
<p class="profile-feedback error">@DetailError</p>
<div class="modal-actions">
<button class="button secondary" type="button" @onclick="ReloadSelectedUserAsync">Recharger</button>
<button class="button ghost" type="button" @onclick="CloseEditModal">Fermer</button>
</div>
}
else if (SelectedUser is null)
{
<p class="section-copy">Cette fiche n'est plus disponible pour le moment.</p>
}
else
{
<p class="section-copy">
Les roles restent geres dans Keycloak. Cette fiche couvre l'etat du compte et le profil du site.
</p>
@if (!string.IsNullOrWhiteSpace(SaveError))
{
<p class="profile-feedback error">@SaveError</p>
}
@if (!string.IsNullOrWhiteSpace(SaveMessage))
{
<p class="profile-feedback success">@SaveMessage</p>
}
<div class="profile-meta-grid">
<article class="profile-meta-card">
<span class="micro-label">Identite Keycloak</span>
<strong>@SelectedUser.IdentityDisplayName</strong>
</article>
<article class="profile-meta-card">
<span class="micro-label">Compte cree le</span>
<strong>@FormatDate(SelectedUser.AccountCreatedUtc)</strong>
</article>
<article class="profile-meta-card">
<span class="micro-label">Profil site cree le</span>
<strong>@FormatDate(SelectedUser.SiteProfileCreatedUtc)</strong>
</article>
<article class="profile-meta-card">
<span class="micro-label">Profil site mis a jour</span>
<strong>@FormatDate(SelectedUser.SiteProfileUpdatedUtc)</strong>
</article>
</div>
<EditForm Model="@EditFormModel" OnValidSubmit="SaveUserAsync">
<DataAnnotationsValidator />
<div class="admin-toggle-grid">
<label class="admin-toggle-card">
<InputCheckbox @bind-Value="EditFormModel.IsEnabled" />
<div>
<strong>Compte actif</strong>
<span>Autoriser ou bloquer la connexion a l'application.</span>
</div>
</label>
<label class="admin-toggle-card">
<InputCheckbox @bind-Value="EditFormModel.IsEmailVerified" />
<div>
<strong>Email verifie</strong>
<span>Indique si l'adresse email a ete validee dans Keycloak.</span>
</div>
</label>
</div>
<div class="admin-form-grid">
<label class="field">
<span>Nom d'utilisateur</span>
<InputText @bind-Value="EditFormModel.Username" />
<ValidationMessage For="@(() => EditFormModel.Username)" />
</label>
<label class="field">
<span>Email</span>
<InputText @bind-Value="EditFormModel.Email" />
<ValidationMessage For="@(() => EditFormModel.Email)" />
</label>
<label class="field">
<span>Prenom</span>
<InputText @bind-Value="EditFormModel.FirstName" />
<ValidationMessage For="@(() => EditFormModel.FirstName)" />
</label>
<label class="field">
<span>Nom</span>
<InputText @bind-Value="EditFormModel.LastName" />
<ValidationMessage For="@(() => EditFormModel.LastName)" />
</label>
<label class="field">
<span>Nom affiche sur le site</span>
<InputText @bind-Value="EditFormModel.DisplayName" />
<ValidationMessage For="@(() => EditFormModel.DisplayName)" />
</label>
<label class="field">
<span>Club</span>
<InputText @bind-Value="EditFormModel.Club" />
<ValidationMessage For="@(() => EditFormModel.Club)" />
</label>
<label class="field">
<span>Ville</span>
<InputText @bind-Value="EditFormModel.City" />
<ValidationMessage For="@(() => EditFormModel.City)" />
</label>
<label class="field">
<span>Format prefere</span>
<InputSelect @bind-Value="EditFormModel.PreferredFormat">
<option value="">Aucune preference</option>
<option value="Twice">Twice</option>
<option value="Time">Time</option>
<option value="Les deux">Les deux</option>
</InputSelect>
<ValidationMessage For="@(() => EditFormModel.PreferredFormat)" />
</label>
<label class="field span-2">
<span>Cube favori</span>
<InputText @bind-Value="EditFormModel.FavoriteCube" />
<ValidationMessage For="@(() => EditFormModel.FavoriteCube)" />
</label>
<label class="field span-2">
<span>Bio</span>
<InputTextArea @bind-Value="EditFormModel.Bio" />
<ValidationMessage For="@(() => EditFormModel.Bio)" />
</label>
</div>
<div class="profile-actions">
<button class="button secondary" type="submit" disabled="@IsSaving">
@(IsSaving ? "Enregistrement..." : "Enregistrer les modifications")
</button>
<button class="button ghost" type="button" @onclick="CloseEditModal" disabled="@IsSaving">Fermer</button>
<p class="section-copy">Le profil site est cree automatiquement lors du premier enregistrement.</p>
</div>
</EditForm>
}
</div>
</section>
<section class="modal @(ShowCreateModal ? string.Empty : "hidden")" aria-hidden="@BoolString(!ShowCreateModal)">
<div class="modal-backdrop" @onclick="CloseCreateModal"></div>
<div class="modal-card admin-modal-card">
@@ -553,6 +566,7 @@
private bool IsSaving;
private bool IsCreating;
private bool IsDeleting;
private bool ShowEditModal;
private bool ShowCreateModal;
private bool ShowDeleteModal;
private string? LoadError;
@@ -627,6 +641,7 @@
Users.Clear();
SelectedSubject = null;
SelectedUser = null;
ShowEditModal = false;
EditFormModel.Reset();
return;
}
@@ -638,6 +653,7 @@
Users.Clear();
SelectedSubject = null;
SelectedUser = null;
ShowEditModal = false;
EditFormModel.Reset();
return;
}
@@ -650,6 +666,7 @@
{
SelectedSubject = null;
SelectedUser = null;
ShowEditModal = false;
EditFormModel.Reset();
return;
}
@@ -658,22 +675,46 @@
? preferredSubject
: Users.Any(user => user.Subject == SelectedSubject)
? SelectedSubject
: Users[0].Subject;
: null;
if (!string.IsNullOrWhiteSpace(nextSubject))
{
await LoadUserDetailAsync(nextSubject, keepFeedback: true);
SelectedSubject = nextSubject;
if (ShowEditModal)
{
await LoadUserDetailAsync(nextSubject, keepFeedback: true);
}
else
{
SelectedUser = null;
EditFormModel.Reset();
}
}
else
{
SelectedSubject = null;
SelectedUser = null;
EditFormModel.Reset();
}
}
catch (HttpRequestException)
{
LoadError = "Le service d'administration est temporairement indisponible.";
Users.Clear();
SelectedSubject = null;
SelectedUser = null;
ShowEditModal = false;
EditFormModel.Reset();
}
catch (TaskCanceledException)
{
LoadError = "Le chargement de l'administration a pris trop de temps.";
Users.Clear();
SelectedSubject = null;
SelectedUser = null;
ShowEditModal = false;
EditFormModel.Reset();
}
finally
{
@@ -686,6 +727,7 @@
{
SaveError = null;
SaveMessage = null;
ShowEditModal = true;
await LoadUserDetailAsync(subject, keepFeedback: false);
}
@@ -798,6 +840,27 @@
}
}
private void CloseEditModal()
{
if (IsSaving)
{
return;
}
ShowEditModal = false;
DetailError = null;
}
private async Task ReloadSelectedUserAsync()
{
if (SelectedSubject is null)
{
return;
}
await LoadUserDetailAsync(SelectedSubject, keepFeedback: true);
}
private void OpenCreateModal()
{
CreateFormModel.Reset();
@@ -988,6 +1051,7 @@
SelectedSubject = null;
SelectedUser = null;
PendingDeleteUser = null;
ShowEditModal = false;
ShowCreateModal = false;
ShowDeleteModal = false;
EditFormModel.Reset();
@@ -1018,6 +1082,21 @@
? "is-selected"
: string.Empty;
private string BuildEditModalTitle()
{
if (SelectedUser is not null)
{
return !string.IsNullOrWhiteSpace(SelectedUser.DisplayName)
? SelectedUser.DisplayName
: SelectedUser.Username;
}
var summary = Users.FirstOrDefault(user => user.Subject == SelectedSubject);
return summary?.IdentityDisplayName
?? summary?.Username
?? "Modifier un utilisateur";
}
private static string BuildUserCardFootnote(AdminUserSummaryResponse user)
=> user.SiteProfileUpdatedUtc is not null
? FormatDate(user.SiteProfileUpdatedUtc)

View File

@@ -1598,8 +1598,7 @@ body.site-menu-hidden .site-menu-shell {
margin-top: 0.8rem;
}
.admin-table-panel,
.admin-detail-panel {
.admin-table-panel {
display: grid;
align-content: start;
gap: 1rem;