mirror of
https://github.com/claunia/marechai.git
synced 2026-07-08 17:57:08 +00:00
Implement a new feature where users receive 5 invitation codes when they confirm their email.
Backend changes:
- Extend InvitationCodeGenerator with GenerateForOwnerAsync() for batch code creation
- Mint 5 codes on email confirmation (not registration) in AuthController.ConfirmEmailAsync
- Add GET /invitation-codes/mine (self-service) endpoint for users to view their own codes
- Add POST /users/{id}/invitation-codes (admin-only) endpoint to grant additional codes
- Create MyInvitationCodeDto and GrantInvitationCodesRequest DTOs
Frontend changes:
- Add "My Invitation Codes" tab to Profile.razor with list of user's codes
- Unused codes rendered normally with copy-to-clipboard button
- Used codes shown with strikethrough (no user identity revealed)
- Add "Give Invitation Codes" action to admin Users page
- Dialog prompts for count (1-50) of codes to grant to selected user
- Success/error messages displayed after granting
- Extend InvitationCodesService.GetMyAsync() and UsersService.GrantInvitationCodesAsync()
- Regenerate Kiota API client from updated OpenAPI schema
No database schema changes required - existing InvitationCode fields accommodate both
registration-issued and admin-granted codes.
116 lines
6.6 KiB
Plaintext
116 lines
6.6 KiB
Plaintext
@{
|
|
/******************************************************************************
|
|
// MARECHAI: Master repository of computing history artifacts information
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2003-2026 Natalia Portillo
|
|
*******************************************************************************/
|
|
}
|
|
|
|
@page "/admin/users"
|
|
@attribute [Authorize(Roles = "UberAdmin")]
|
|
@using Marechai.ApiClient.Models
|
|
@using Marechai.Services
|
|
@inject UsersService UsersService
|
|
@inject InvitationCodesService InvitationCodesService
|
|
@inject IStringLocalizer<UsersService> L
|
|
@inject IDialogService DialogService
|
|
@inject ISnackbar Snackbar
|
|
|
|
<MudPaper Class="pa-6 mb-6" Elevation="0" Style="background: linear-gradient(135deg, #7e57c2 0%, #42a5f5 100%); border-radius: 16px;">
|
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
|
|
<MudIcon Icon="@Icons.Material.Filled.AdminPanelSettings" Size="Size.Large" Style="color: white;"/>
|
|
<MudText Typo="Typo.h4" Style="color: white;">@L["User Management"]</MudText>
|
|
</MudStack>
|
|
<MudText Typo="Typo.subtitle1" Style="color: rgba(255,255,255,0.85);" Class="mt-2">@L["Manage users, passwords, and role assignments."]</MudText>
|
|
</MudPaper>
|
|
|
|
@if(!string.IsNullOrWhiteSpace(_errorMessage))
|
|
{
|
|
<MudAlert Severity="Severity.Error" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="() => _errorMessage = null">@_errorMessage</MudAlert>
|
|
}
|
|
|
|
@if(!string.IsNullOrWhiteSpace(_successMessage))
|
|
{
|
|
<MudAlert Severity="Severity.Success" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="() => _successMessage = null">@_successMessage</MudAlert>
|
|
}
|
|
|
|
<MudPaper Elevation="2" Class="rounded-lg">
|
|
<MudToolBar Dense="true">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.PersonAdd" OnClick="OpenAddUserDialog">@L["Add User"]</MudButton>
|
|
<MudSpacer/>
|
|
@if(_selectedUsers.Count > 0)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Info" Variant="Variant.Filled">@string.Format(L["{0} selected"], _selectedUsers.Count)</MudChip>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Error" StartIcon="@Icons.Material.Filled.DeleteSweep" Size="Size.Small" OnClick="ConfirmBulkDeleteUsers" Class="ml-2">@L["Delete"]</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Security" Size="Size.Small" OnClick="OpenBulkRoleDialog" Class="ml-1">@L["Roles"]</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Warning" StartIcon="@Icons.Material.Filled.Lock" Size="Size.Small" OnClick="OpenBulkLockoutDialog" Class="ml-1">@L["Lockout"]</MudButton>
|
|
}
|
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="@(async () => await _dataGrid.ReloadServerData())"/>
|
|
</MudToolBar>
|
|
|
|
<MudDataGrid @ref="_dataGrid" T="UserDto" ServerData="ServerReload" RowsPerPage="25" Dense="true" Hover="true" Striped="true" Filterable="true" MultiSelection="true" @bind-SelectedItems="_selectedUsers">
|
|
<Columns>
|
|
<SelectColumn T="UserDto"/>
|
|
<PropertyColumn Property="x => x.Email" Title="@L["Email"]"/>
|
|
<PropertyColumn Property="x => x.UserName" Title="@L["Username"]"/>
|
|
<PropertyColumn Property="x => x.PhoneNumber" Title="@L["Phone"]"/>
|
|
<TemplateColumn Title="@L["Roles"]" PropertyName="Roles" Sortable="false">
|
|
<CellTemplate>
|
|
<MudStack Row="true" Spacing="1">
|
|
@if(context.Item.Roles is { Count: > 0 })
|
|
{
|
|
@foreach(string role in context.Item.Roles)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Variant="Variant.Outlined">@role</MudChip>
|
|
}
|
|
}
|
|
</MudStack>
|
|
</CellTemplate>
|
|
</TemplateColumn>
|
|
<TemplateColumn Title="@L["Actions"]" Sortable="false" Filterable="false">
|
|
<CellTemplate>
|
|
<MudStack Row="true" Spacing="1">
|
|
<MudTooltip Text="@L["Edit"]"><MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Small" OnClick="() => OpenEditUserDialog(context.Item)"/></MudTooltip>
|
|
|
|
<MudTooltip Text="@L["Change Password"]"><MudIconButton Icon="@Icons.Material.Filled.Lock" Size="Size.Small" OnClick="() => OpenChangePasswordDialog(context.Item)"/></MudTooltip>
|
|
|
|
<MudTooltip Text="@L["Manage Roles"]"><MudIconButton Icon="@Icons.Material.Filled.Security" Size="Size.Small" OnClick="() => OpenManageRolesDialog(context.Item)"/></MudTooltip>
|
|
|
|
<MudTooltip Text="@L["Give Invitation Codes"]"><MudIconButton Icon="@Icons.Material.Filled.ConfirmationNumber" Size="Size.Small" OnClick="() => OpenGiveInvitationCodesDialog(context.Item)"/></MudTooltip>
|
|
|
|
@if(context.Item.TwoFactorEnabled == true)
|
|
{
|
|
<MudTooltip Text="@L["Disable 2FA"]"><MudIconButton Icon="@Icons.Material.Filled.LockOpen" Size="Size.Small" Color="Color.Warning" OnClick="() => ConfirmDisableTwoFactor(context.Item)"/></MudTooltip>
|
|
}
|
|
|
|
<MudTooltip Text="@L["Delete"]"><MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error" OnClick="() => ConfirmDeleteUser(context.Item)"/></MudTooltip>
|
|
|
|
</MudStack>
|
|
</CellTemplate>
|
|
</TemplateColumn>
|
|
</Columns>
|
|
<PagerContent>
|
|
<MudDataGridPager T="UserDto" PageSizeOptions="new[] { 10, 25, 50, 100 }"/>
|
|
</PagerContent>
|
|
</MudDataGrid>
|
|
</MudPaper>
|