Skip to content

Commit d4f843b

Browse files
Activate Account + Delete Account (#6)
* activate account * complete account registration * delete account * clear cache * refactoring
1 parent 6d4b279 commit d4f843b

File tree

15 files changed

+311
-39
lines changed

15 files changed

+311
-39
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
@page "/activate/{userGuid:guid}"
2+
3+
@inherits CancellableComponent
4+
5+
@using RailwayResult
6+
7+
@inject IAuthService AuthService
8+
@inject AuthenticationStateProvider AuthenticationStateProvider
9+
@inject ApiClient ApiClient
10+
11+
<PageTitle>Activate Account</PageTitle>
12+
13+
<div class="d-flex flex-dir-column h-screen">
14+
<FluentSpacer />
15+
<FluentCard Style="margin-bottom: 200px" Class="body-small h-auto margin-auto padding-large">
16+
@if (Result is not null)
17+
{
18+
<div class="padding-small d-flex">
19+
@if (Result.IsSuccess)
20+
{
21+
<strong>Your account has been successfully activated.&nbsp;</strong>
22+
<FluentIcon Icon="Icons.Filled.Size20.CheckmarkCircle" />
23+
}
24+
else
25+
{
26+
<FluentIcon Icon="Icons.Filled.Size20.Warning" />
27+
<span style="margin-left: 8px">@Result.Error.Message</span>
28+
}
29+
</div>
30+
}
31+
32+
<FluentDivider Style="margin-top: 16px"/>
33+
34+
<FluentStack Orientation="Orientation.Horizontal" Style="margin-top: 8px">
35+
<FluentAnchor Href="login" Appearance="Appearance.Lightweight">Log in</FluentAnchor>
36+
<FluentAnchor Href="register" Appearance="Appearance.Lightweight">Register</FluentAnchor>
37+
</FluentStack>
38+
</FluentCard>
39+
<FluentSpacer />
40+
</div>
41+
42+
@code
43+
{
44+
[Parameter]
45+
public Guid UserGuid { get; init; } = default!;
46+
47+
private Result? Result { get; set; }
48+
49+
protected override async Task OnInitializedAsync()
50+
{
51+
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
52+
if (state.User.Identity?.IsAuthenticated == true)
53+
{
54+
await AuthService.LogoutAsync("", CTS.Token);
55+
}
56+
57+
var userId = UserId.FromGuid(UserGuid);
58+
Result = await ApiClient.ActivateAsync(userId, CTS.Token);
59+
}
60+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
@page "/complete-registration/{userGuid:guid}"
2+
3+
@rendermode InteractiveAuto
4+
5+
@inherits CancellableComponent
6+
7+
@using RailwayResult
8+
@using System.ComponentModel.DataAnnotations
9+
10+
@inject ApiClient ApiClient
11+
@inject IAuthService AuthService
12+
@inject AuthenticationStateProvider AuthenticationStateProvider
13+
@inject IToastService ToastService
14+
15+
<PageTitle>Complete Registration</PageTitle>
16+
17+
<div class="d-flex flex-dir-column h-screen">
18+
<FluentSpacer />
19+
<FluentCard Style="margin-bottom: 200px" Class="body-small h-auto margin-auto padding-large">
20+
<h1 style="margin-bottom: 16px">Complete Your Registration</h1>
21+
<FluentDivider Style="margin-bottom: 16px" />
22+
23+
@if (completed)
24+
{
25+
<div class="padding-small d-flex">
26+
<strong>Registration has been successfully completed.&nbsp;</strong>
27+
<FluentIcon Icon="Icons.Filled.Size20.CheckmarkCircle" />
28+
</div>
29+
30+
<FluentDivider Style="margin-top: 16px" />
31+
32+
<FluentStack Orientation="Orientation.Horizontal" Style="margin-top: 8px">
33+
<FluentAnchor Href="login" Appearance="Appearance.Lightweight">Log in</FluentAnchor>
34+
<FluentAnchor Href="register" Appearance="Appearance.Lightweight">Register</FluentAnchor>
35+
</FluentStack>
36+
}
37+
else
38+
{
39+
<EditForm Model="@Input" OnValidSubmit="CompleteRegistrationAsync" FormName="CompleteRegistrationForm">
40+
<CustomValidation />
41+
<FluentStack Orientation="Orientation.Vertical">
42+
<FluentTextField @bind-value="Input.Password" Immediate="true" AutoComplete="current-password" Required="true" Placeholder="password" Label="Password" type="password" Class="w-100" />
43+
<FluentValidationMessage For="() => Input.Password" Class="text-danger" />
44+
45+
<FluentTextField @bind-value="Input.ConfirmPassword" Immediate="true" Required="true" Placeholder="password" Label="Confirm Password" type="password" Class="w-100" />
46+
<FluentValidationMessage For="() => Input.ConfirmPassword" Class="text-danger" />
47+
48+
<FluentStack Orientation="Orientation.Horizontal" Style="margin-top: 8px">
49+
<FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent">Complete</FluentButton>
50+
<FluentAnchor Href="login" Appearance="Appearance.Lightweight">Log in</FluentAnchor>
51+
<FluentAnchor Href="register" Appearance="Appearance.Lightweight">Register</FluentAnchor>
52+
</FluentStack>
53+
</FluentStack>
54+
</EditForm>
55+
}
56+
</FluentCard>
57+
<FluentSpacer />
58+
</div>
59+
60+
@code
61+
{
62+
[Parameter]
63+
public Guid UserGuid { get; init; } = default!;
64+
65+
[SupplyParameterFromForm]
66+
private RegisterModel Input { get; set; } = new();
67+
68+
private bool completed = false;
69+
70+
public async Task CompleteRegistrationAsync()
71+
{
72+
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
73+
if (state.User.Identity?.IsAuthenticated == true)
74+
{
75+
await AuthService.LogoutAsync("", CTS.Token);
76+
}
77+
78+
var userId = UserId.FromGuid(UserGuid);
79+
var result = await ApiClient.CompleteRegistrationAsync(userId, Input.Password, CTS.Token);
80+
if (result.IsFailure)
81+
{
82+
ToastService.ShowError(result.Error.Message);
83+
Input.Password = "";
84+
Input.ConfirmPassword = "";
85+
return;
86+
}
87+
88+
ToastService.ShowSuccess("Registration completed");
89+
completed = true;
90+
}
91+
92+
private sealed class RegisterModel
93+
{
94+
[Required]
95+
[DataType(DataType.Password)]
96+
[Display(Name = "Password")]
97+
public string Password { get; set; } = "";
98+
99+
[DataType(DataType.Password)]
100+
[Display(Name = "Confirm password")]
101+
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
102+
public string ConfirmPassword { get; set; } = "";
103+
}
104+
}

src/TeamUp.Client/AnonymousPages/LoginPage.razor

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
@inject AuthenticationStateProvider AuthenticationStateProvider
1313
@inject ICookieService CookieService
1414
@inject IToastService ToastService
15-
@inject LoginService LoginService
15+
@inject AccountService AccountService
1616

1717
<PageTitle>Login</PageTitle>
1818

@@ -68,7 +68,7 @@
6868
public async Task LoginAsync()
6969
{
7070
processing = true;
71-
var result = await LoginService.LoginAsync(Input, CTS.Token);
71+
var result = await AccountService.LoginAsync(Input, CTS.Token);
7272
if (result.IsFailure)
7373
{
7474
if (result.Error is ApiValidationError error)
@@ -86,12 +86,12 @@
8686
if (AuthenticationStateProvider is ClientAuthenticationStateProvider persistAuthProvider)
8787
{
8888
persistAuthProvider.Login(result.Value);
89-
await LoginService.ValidateCacheAsync(CTS.Token);
89+
await AccountService.ValidateCacheAsync(CTS.Token);
9090
Redirect();
9191
return;
9292
}
9393

94-
await LoginService.ValidateCacheAsync(CTS.Token);
94+
await AccountService.ValidateCacheAsync(CTS.Token);
9595
Redirect(true);
9696
}
9797

src/TeamUp.Client/AnonymousPages/RegisterPage.razor

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
<FluentSpacer />
2020
<FluentCard Style="margin-bottom: 200px" Class="body-small h-auto margin-auto padding-large">
2121
<h1 style="margin-bottom: 16px">Register</h1>
22-
<FluentDivider />
23-
<div style="margin-top: 16px"></div>
22+
<FluentDivider Style="margin-bottom: 16px" />
2423
@if (email is not null)
2524
{
2625
<p>

src/TeamUp.Client/Layout/NavMenu.razor

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
@inject ApiClient ApiClient
1010
@inject IMessenger Messenger
1111
@inject TeamService TeamService
12+
@inject AccountService AccountService
1213

1314
<div class="navmenu">
1415
<input type="checkbox" title="Menu expand/collapse toggle" id="navmenu-toggle" class="navmenu-icon" />
@@ -59,7 +60,7 @@
5960
<FluentAnchor Href="logout" Appearance="Appearance.Accent" class="w-90">Logout</FluentAnchor>
6061
</div>
6162
<div class="text-center" style="margin-top: 4px;">
62-
<FluentAnchor Href="logout" Appearance="Appearance.Accent" class="w-90">Clear Cache</FluentAnchor>
63+
<FluentButton Href="logout" Appearance="Appearance.Accent" class="w-90" OnClick="ClearCacheAsync">Clear Cache</FluentButton>
6364
</div>
6465
</nav>
6566
</div>
@@ -77,6 +78,11 @@
7778
await LoadTeamsAsync();
7879
}
7980

81+
public async Task ClearCacheAsync()
82+
{
83+
await AccountService.ClearCacheAsync(CTS.Token);
84+
}
85+
8086
private async Task LoadTeamsAsync(bool forceLoad = false)
8187
{
8288
var teamsResult = await TeamService.GetMyTeamsAsync(forceLoad, CTS.Token);
Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,71 @@
11
@page "/account"
22

3-
<h3>Account</h3>
3+
@rendermode InteractiveAuto
4+
5+
@inherits CancellableComponent
6+
7+
@using System.ComponentModel.DataAnnotations
8+
@using TeamUp.DAL.Cache
9+
10+
@inject IDialogService DialogService
11+
@inject IToastService ToastService
12+
@inject ICacheStorage CacheStorage
13+
@inject IAuthService AuthService
14+
@inject ApiClient ApiClient
15+
16+
<PageTitle>Account</PageTitle>
17+
18+
<div class="d-flex align-center" style="margin-bottom: 16px">
19+
<h1 class="d-flex no-margin padding-small">Account</h1>
20+
</div>
21+
22+
<div class="body-small">
23+
<FluentCard Class="margin-sym">
24+
<EditForm Model="@Input" OnValidSubmit="DeleteAccountAsync" FormName="DeleteAccountForm">
25+
<CustomValidation />
26+
<FluentStack Orientation="Orientation.Vertical">
27+
<FluentTextField @bind-value="Input.Password" Immediate="true" AutoComplete="current-password" Required="true" Placeholder="password" Label="Password" type="password" Class="w-100" />
28+
<FluentValidationMessage For="() => Input.Password" Class="text-danger" />
29+
30+
<FluentStack Orientation="Orientation.Horizontal" Style="margin-top: 8px">
31+
<FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent">Delete</FluentButton>
32+
</FluentStack>
33+
</FluentStack>
34+
</EditForm>
35+
</FluentCard>
36+
</div>
437

538
@code
639
{
40+
[SupplyParameterFromForm]
41+
private PasswordModel Input { get; set; } = new();
42+
43+
public async Task DeleteAccountAsync()
44+
{
45+
var confirm = await DialogService.ShowConfirmationAsync("Do you want to delete your account?", "Yes", "No");
46+
if ((await confirm.Result).Cancelled == true)
47+
{
48+
Input.Password = "";
49+
return;
50+
}
51+
52+
var result = await ApiClient.DeleteAccountAsync(Input.Password, CTS.Token);
53+
if (result.IsFailure)
54+
{
55+
ToastService.ShowError(result.Error.Message);
56+
Input.Password = "";
57+
return;
58+
}
59+
60+
await CacheStorage.ClearAsync(CTS.Token);
61+
await AuthService.LogoutAsync("/login", CTS.Token);
62+
}
763

64+
private sealed class PasswordModel
65+
{
66+
[Required]
67+
[DataType(DataType.Password)]
68+
[Display(Name = "Password")]
69+
public string Password { get; set; } = "";
70+
}
871
}

src/TeamUp.Client/Pages/AuthorizedPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<AuthorizeView>
44
<Authorized>
5-
<div>
5+
<div class="padding-small">
66
Hello <strong>@(context.User.Identity?.Name)</strong>, you have been successfully authorized.
77
</div>
88
</Authorized>

src/TeamUp.Client/Pages/Event/EventsPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
}
111111
}
112112

113-
public sealed class EventRecord
113+
private sealed class EventRecord
114114
{
115115
public required TeamMemberResponse Member { get; set; }
116116
public required TeamSlimResponse Team { get; set; }

src/TeamUp.Client/Pages/InvitationsPage.razor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
@inject IMessenger Messenger
1111
@inject InvitationsService InvitationService
1212

13+
<PageTitle>Invitations</PageTitle>
14+
1315
<div class="d-flex align-center" style="margin-bottom: 16px">
1416
<h1 class="d-flex no-margin padding-small">Invitations</h1>
1517
<FluentButton IconStart="@(new Icons.Regular.Size20.ArrowClockwise())" Appearance="Appearance.Outline" OnClick="() => LoadInvitationsAsync(true)" Style="margin-top: 4px" />

src/TeamUp.Client/Services/AuthService.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public async Task LogoutAsync(string url = "/login", CancellationToken ct = defa
4444
authProvider.Logout();
4545
}
4646

47-
_navigationManager.NavigateTo(url);
47+
if (!string.IsNullOrWhiteSpace(url))
48+
{
49+
_navigationManager.NavigateTo(url);
50+
}
4851
}
4952
}

0 commit comments

Comments
 (0)