feat(G7): add JoinClub page and no-club dashboard message
This commit is contained in:
parent
e8b2154f69
commit
97ea3b35ca
|
|
@ -0,0 +1,231 @@
|
||||||
|
@page "/account/join-club"
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@using Koogle.Application.DTOs
|
||||||
|
@using Koogle.Application.Interfaces
|
||||||
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
|
@inject IUserService UserService
|
||||||
|
@inject IClubService ClubService
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
<PageTitle>Club beitreten</PageTitle>
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Small" Class="mt-4">
|
||||||
|
<MudText Typo="Typo.h4" Class="mb-4">Club beitreten</MudText>
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Indeterminate="true" />
|
||||||
|
}
|
||||||
|
else if (_requestSent)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Success" Class="mb-4">
|
||||||
|
<MudText Typo="Typo.body1">
|
||||||
|
Dein Beitrittsantrag wurde erfolgreich gesendet!
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Class="mt-2">
|
||||||
|
Ein Admin des Clubs wird deinen Antrag pruefen. Du erhaeltst eine Benachrichtigung, sobald dein Antrag bearbeitet wurde.
|
||||||
|
</MudText>
|
||||||
|
</MudAlert>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="ResetForm">
|
||||||
|
Weiteren Antrag stellen
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Default" Class="ml-2" Href="/account/profile">
|
||||||
|
Zum Profil
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudPaper Class="pa-4" Elevation="2">
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-4">
|
||||||
|
Gib den Namen des Clubs ein, dem du beitreten moechtest. Dein Antrag wird vom Club-Admin geprueft.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_isValid">
|
||||||
|
<MudTextField @bind-Value="_clubName"
|
||||||
|
Label="Club-Name"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="Bitte gib einen Club-Namen ein"
|
||||||
|
Immediate="true"
|
||||||
|
OnKeyUp="OnClubNameKeyUp"
|
||||||
|
Class="mb-3" />
|
||||||
|
|
||||||
|
@if (_searchResult is not null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Info" Class="mb-3">
|
||||||
|
<div class="d-flex justify-space-between align-center">
|
||||||
|
<div>
|
||||||
|
<MudText Typo="Typo.body1"><strong>@_searchResult.Name</strong></MudText>
|
||||||
|
<MudText Typo="Typo.caption">@_searchResult.MemberCount Mitglieder</MudText>
|
||||||
|
</div>
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Check" Color="Color.Success" />
|
||||||
|
</div>
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
else if (_searched && !string.IsNullOrWhiteSpace(_clubName))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Warning" Class="mb-3">
|
||||||
|
Kein Club mit diesem Namen gefunden. Bitte pruefe die Schreibweise.
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(_error))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error" Class="mb-3">@_error</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="SearchClub"
|
||||||
|
Disabled="_isSearching || string.IsNullOrWhiteSpace(_clubName)">
|
||||||
|
@if (_isSearching)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
Club suchen
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="SubmitRequest"
|
||||||
|
Disabled="_isSubmitting || _searchResult is null">
|
||||||
|
@if (_isSubmitting)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||||
|
}
|
||||||
|
Beitrittsantrag senden
|
||||||
|
</MudButton>
|
||||||
|
</div>
|
||||||
|
</MudForm>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _isValid;
|
||||||
|
private string _clubName = "";
|
||||||
|
private ClubDto? _searchResult;
|
||||||
|
private bool _searched;
|
||||||
|
private bool _isLoading;
|
||||||
|
private bool _isSearching;
|
||||||
|
private bool _isSubmitting;
|
||||||
|
private bool _requestSent;
|
||||||
|
private string? _error;
|
||||||
|
private UserDto? _currentUser;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_currentUser = await UserService.GetCurrentUserAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnClubNameKeyUp(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
// Reset search result when user types
|
||||||
|
if (_searched)
|
||||||
|
{
|
||||||
|
_searched = false;
|
||||||
|
_searchResult = null;
|
||||||
|
_error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search on Enter key
|
||||||
|
if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(_clubName))
|
||||||
|
{
|
||||||
|
await SearchClub();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SearchClub()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_clubName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isSearching = true;
|
||||||
|
_error = null;
|
||||||
|
_searched = false;
|
||||||
|
_searchResult = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_searchResult = await ClubService.GetByNameAsync(_clubName.Trim());
|
||||||
|
_searched = true;
|
||||||
|
|
||||||
|
// Check if user is already member
|
||||||
|
if (_searchResult is not null && _currentUser is not null)
|
||||||
|
{
|
||||||
|
var existingMembership = _currentUser.ClubMemberships
|
||||||
|
.FirstOrDefault(m => m.ClubId == _searchResult.Id);
|
||||||
|
|
||||||
|
if (existingMembership is not null)
|
||||||
|
{
|
||||||
|
_error = "Du bist bereits Mitglied dieses Clubs oder hast einen offenen Antrag.";
|
||||||
|
_searchResult = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_error = $"Fehler bei der Suche: {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSearching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SubmitRequest()
|
||||||
|
{
|
||||||
|
if (_searchResult is null || _currentUser is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isSubmitting = true;
|
||||||
|
_error = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var success = await UserService.RequestClubMembershipAsync(
|
||||||
|
_currentUser.ProfileId,
|
||||||
|
_searchResult.Id);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
_requestSent = true;
|
||||||
|
Snackbar.Add("Beitrittsantrag erfolgreich gesendet", Severity.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_error = "Der Antrag konnte nicht gesendet werden. Moeglicherweise existiert bereits ein Antrag.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_error = $"Fehler beim Senden des Antrags: {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isSubmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetForm()
|
||||||
|
{
|
||||||
|
_clubName = "";
|
||||||
|
_searchResult = null;
|
||||||
|
_searched = false;
|
||||||
|
_requestSent = false;
|
||||||
|
_error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
@page "/dashboard"
|
@page "/dashboard"
|
||||||
@attribute [Authorize(Policy = "ClubViewer")]
|
@attribute [Authorize]
|
||||||
|
|
||||||
@using Koogle.Application.DTOs
|
@using Koogle.Application.DTOs
|
||||||
@using Koogle.Application.Interfaces
|
@using Koogle.Application.Interfaces
|
||||||
@using Koogle.Domain.Enums
|
@using Koogle.Domain.Enums
|
||||||
|
@using Koogle.Infrastructure.Security
|
||||||
@using Koogle.Web.Components.Shared
|
@using Koogle.Web.Components.Shared
|
||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
@inject IDashboardService DashboardService
|
@inject IDashboardService DashboardService
|
||||||
|
@inject IUserService UserService
|
||||||
|
@inject ICurrentClubContext CurrentClubContext
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ISnackbar Snackbar
|
@inject ISnackbar Snackbar
|
||||||
|
|
||||||
|
|
@ -19,6 +22,21 @@
|
||||||
{
|
{
|
||||||
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
|
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
|
||||||
}
|
}
|
||||||
|
else if (_hasNoClub)
|
||||||
|
{
|
||||||
|
<MudPaper Class="pa-6" Elevation="2" MaxWidth="600px">
|
||||||
|
<div class="d-flex flex-column align-center text-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.GroupOff" Size="Size.Large" Color="Color.Secondary" Class="mb-4" />
|
||||||
|
<MudText Typo="Typo.h5" Class="mb-2">Keinem Club zugeordnet</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Color="Color.Secondary" Class="mb-4">
|
||||||
|
Du bist noch keinem Club zugeordnet. Um Koogle zu nutzen, tritt einem bestehenden Club bei.
|
||||||
|
</MudText>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Href="/account/join-club" StartIcon="@Icons.Material.Filled.GroupAdd">
|
||||||
|
Einem Club beitreten
|
||||||
|
</MudButton>
|
||||||
|
</div>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
else if (_error is not null)
|
else if (_error is not null)
|
||||||
{
|
{
|
||||||
<MudAlert Severity="Severity.Error" Class="mb-4">@_error</MudAlert>
|
<MudAlert Severity="Severity.Error" Class="mb-4">@_error</MudAlert>
|
||||||
|
|
@ -149,6 +167,7 @@ else if (_dashboard is not null)
|
||||||
@code {
|
@code {
|
||||||
private DashboardDto? _dashboard;
|
private DashboardDto? _dashboard;
|
||||||
private bool _isLoading = true;
|
private bool _isLoading = true;
|
||||||
|
private bool _hasNoClub;
|
||||||
private string? _error;
|
private string? _error;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
|
|
@ -162,6 +181,20 @@ else if (_dashboard is not null)
|
||||||
{
|
{
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
_error = null;
|
_error = null;
|
||||||
|
|
||||||
|
// Check if user has club context
|
||||||
|
var clubId = CurrentClubContext.ClubId;
|
||||||
|
if (clubId == Guid.Empty)
|
||||||
|
{
|
||||||
|
// Double-check via user service
|
||||||
|
var user = await UserService.GetCurrentUserAsync();
|
||||||
|
if (user == null || user.ClubMemberships.Count == 0)
|
||||||
|
{
|
||||||
|
_hasNoClub = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_dashboard = await DashboardService.GetDashboardAsync();
|
_dashboard = await DashboardService.GetDashboardAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue