diff --git a/Sources/HeartTrack/.vs/HeartTrack/DesignTimeBuild/.dtbcache.v2 b/Sources/HeartTrack/.vs/HeartTrack/DesignTimeBuild/.dtbcache.v2 index 3cd382a..8cd3a1c 100644 Binary files a/Sources/HeartTrack/.vs/HeartTrack/DesignTimeBuild/.dtbcache.v2 and b/Sources/HeartTrack/.vs/HeartTrack/DesignTimeBuild/.dtbcache.v2 differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/37af3ef7-fe21-4c00-9f8a-273a32ff2387.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/37af3ef7-fe21-4c00-9f8a-273a32ff2387.vsidx new file mode 100644 index 0000000..0692ef8 Binary files /dev/null and b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/37af3ef7-fe21-4c00-9f8a-273a32ff2387.vsidx differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/44ee5ece-d56f-42a2-aa17-7ee718ed688b.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/44ee5ece-d56f-42a2-aa17-7ee718ed688b.vsidx deleted file mode 100644 index 6ca74ff..0000000 Binary files a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/44ee5ece-d56f-42a2-aa17-7ee718ed688b.vsidx and /dev/null differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/496a5656-eb44-4ebe-ba8d-a6762b436736.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/496a5656-eb44-4ebe-ba8d-a6762b436736.vsidx new file mode 100644 index 0000000..e7679ab Binary files /dev/null and b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/496a5656-eb44-4ebe-ba8d-a6762b436736.vsidx differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/5add9c98-8b97-46d0-9588-e16a8e8f12e8.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/5add9c98-8b97-46d0-9588-e16a8e8f12e8.vsidx new file mode 100644 index 0000000..7552c9c Binary files /dev/null and b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/5add9c98-8b97-46d0-9588-e16a8e8f12e8.vsidx differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/988cb259-3d27-4e96-8943-566b62d64eae.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/988cb259-3d27-4e96-8943-566b62d64eae.vsidx deleted file mode 100644 index 7284701..0000000 Binary files a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/988cb259-3d27-4e96-8943-566b62d64eae.vsidx and /dev/null differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/a84ac8c7-da77-4564-b920-83df07e5e4b6.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/a84ac8c7-da77-4564-b920-83df07e5e4b6.vsidx new file mode 100644 index 0000000..0982bca Binary files /dev/null and b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/a84ac8c7-da77-4564-b920-83df07e5e4b6.vsidx differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/b4692239-296f-4d4b-a5be-f68bf82ee08c.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/b4692239-296f-4d4b-a5be-f68bf82ee08c.vsidx deleted file mode 100644 index 2f86ee0..0000000 Binary files a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/b4692239-296f-4d4b-a5be-f68bf82ee08c.vsidx and /dev/null differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/c35b252a-e1e2-432b-b53c-a629756d67dd.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/c35b252a-e1e2-432b-b53c-a629756d67dd.vsidx new file mode 100644 index 0000000..40faf9c Binary files /dev/null and b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/c35b252a-e1e2-432b-b53c-a629756d67dd.vsidx differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/d6bc084e-b8ab-435f-8578-c67fce6d404b.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/d6bc084e-b8ab-435f-8578-c67fce6d404b.vsidx deleted file mode 100644 index 825155f..0000000 Binary files a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/d6bc084e-b8ab-435f-8578-c67fce6d404b.vsidx and /dev/null differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/e08c635b-7d76-4234-98ec-526472a22be7.vsidx b/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/e08c635b-7d76-4234-98ec-526472a22be7.vsidx deleted file mode 100644 index f6da159..0000000 Binary files a/Sources/HeartTrack/.vs/HeartTrack/FileContentIndex/e08c635b-7d76-4234-98ec-526472a22be7.vsidx and /dev/null differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/v17/.futdcache.v2 b/Sources/HeartTrack/.vs/HeartTrack/v17/.futdcache.v2 index 01e6dba..c35c6da 100644 Binary files a/Sources/HeartTrack/.vs/HeartTrack/v17/.futdcache.v2 and b/Sources/HeartTrack/.vs/HeartTrack/v17/.futdcache.v2 differ diff --git a/Sources/HeartTrack/.vs/HeartTrack/v17/.suo b/Sources/HeartTrack/.vs/HeartTrack/v17/.suo index c118454..2dc1d01 100644 Binary files a/Sources/HeartTrack/.vs/HeartTrack/v17/.suo and b/Sources/HeartTrack/.vs/HeartTrack/v17/.suo differ diff --git a/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.metadata.v7.bin b/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.metadata.v7.bin index d33a096..89ed569 100644 Binary files a/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.metadata.v7.bin and b/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.metadata.v7.bin differ diff --git a/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.projects.v7.bin b/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.projects.v7.bin index 82aef15..7d0e8d2 100644 Binary files a/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.projects.v7.bin and b/Sources/HeartTrack/.vs/ProjectEvaluation/hearttrack.projects.v7.bin differ diff --git a/Sources/HeartTrack/App.razor b/Sources/HeartTrack/App.razor index 60f5744..6f7f696 100644 --- a/Sources/HeartTrack/App.razor +++ b/Sources/HeartTrack/App.razor @@ -1,4 +1,4 @@ - +@* @@ -9,5 +9,17 @@

Sorry, there's nothing at this address.

-
+
*@ + + + + + + + +

Sorry, there's nothing at this address.

+
+
+
+
\ No newline at end of file diff --git a/Sources/HeartTrack/HeartTrack.csproj b/Sources/HeartTrack/HeartTrack.csproj index 84b4d9c..723d747 100644 --- a/Sources/HeartTrack/HeartTrack.csproj +++ b/Sources/HeartTrack/HeartTrack.csproj @@ -7,10 +7,16 @@ + + + + + + diff --git a/Sources/HeartTrack/Models/Authentification/AppUser.cs b/Sources/HeartTrack/Models/Authentification/AppUser.cs new file mode 100644 index 0000000..8a20f8f --- /dev/null +++ b/Sources/HeartTrack/Models/Authentification/AppUser.cs @@ -0,0 +1,9 @@ +namespace HeartTrack.Models +{ + public class AppUser + { + public string Password { get; set; } + public List Roles { get; set; } + public string UserName { get; set; } + } +} diff --git a/Sources/HeartTrack/Models/Authentification/CurrentUser.cs b/Sources/HeartTrack/Models/Authentification/CurrentUser.cs new file mode 100644 index 0000000..ccee22d --- /dev/null +++ b/Sources/HeartTrack/Models/Authentification/CurrentUser.cs @@ -0,0 +1,9 @@ +namespace HeartTrack.Models +{ + public class CurrentUser + { + public Dictionary Claims { get; set; } + public bool IsAuthenticated { get; set; } + public string UserName { get; set; } + } +} diff --git a/Sources/HeartTrack/Models/Authentification/LoginRequest.cs b/Sources/HeartTrack/Models/Authentification/LoginRequest.cs new file mode 100644 index 0000000..e8688e7 --- /dev/null +++ b/Sources/HeartTrack/Models/Authentification/LoginRequest.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace HeartTrack.Models +{ + public class LoginRequest + { + [Required] + public string Password { get; set; } + + [Required] + public string UserName { get; set; } + } +} diff --git a/Sources/HeartTrack/Models/Authentification/RegisterRequest.cs b/Sources/HeartTrack/Models/Authentification/RegisterRequest.cs new file mode 100644 index 0000000..d41bb5f --- /dev/null +++ b/Sources/HeartTrack/Models/Authentification/RegisterRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace HeartTrack.Models +{ + public class RegisterRequest + { + [Required] + public string Password { get; set; } + + [Required] + [Compare(nameof(Password), ErrorMessage = "Passwords do not match!")] + public string PasswordConfirm { get; set; } + + [Required] + public string UserName { get; set; } + } +} diff --git a/Sources/HeartTrack/Models/Ticket/Ticket.cs b/Sources/HeartTrack/Models/Ticket/Ticket.cs new file mode 100644 index 0000000..621dded --- /dev/null +++ b/Sources/HeartTrack/Models/Ticket/Ticket.cs @@ -0,0 +1,13 @@ +namespace HeartTrack.Models +{ + public class Ticket + { + public int Id { get; set; } + public string Username { get; set; } + public string Nom { get; set; } + public string Prenom { get; set; } + public string Contexte { get; set; } + public string Description { get; set; } + public string Urgence { get; set; } + } +} diff --git a/Sources/HeartTrack/Models/Ticket/TicketModel.cs b/Sources/HeartTrack/Models/Ticket/TicketModel.cs new file mode 100644 index 0000000..3eb2bd5 --- /dev/null +++ b/Sources/HeartTrack/Models/Ticket/TicketModel.cs @@ -0,0 +1,33 @@ +using Blazorise; +using System.ComponentModel.DataAnnotations; + +namespace HeartTrack.Models +{ + public class TicketModel + { + [Required] + [Range(0, 121425711425541)] + public int Id { get; set; } + + [Required] + [StringLength(50, ErrorMessage = "The username must not exceed 50 characters.")] + public string Username { get; set; } + + [Required] + [StringLength(50, ErrorMessage = "The name must not exceed 50 characters.")] + public string Nom { get; set; } + + [Required] + [StringLength(25, ErrorMessage = "The last name must not exceed 25 characters.")] + public string Prenom { get; set; } + [Required] + [StringLength(25, ErrorMessage = "The subject must not exceed 25 characters.")] + public string Contexte { get; set; } + + [Required] + [StringLength(500, ErrorMessage = "Description must not exceed 500 characters.")] + public string Description { get; set; } + public Boolean Urgence { get; set; } + public Image Image { get; set; } + } +} diff --git a/Sources/HeartTrack/Models/User.cs b/Sources/HeartTrack/Models/User/User.cs similarity index 91% rename from Sources/HeartTrack/Models/User.cs rename to Sources/HeartTrack/Models/User/User.cs index a332514..d18a6aa 100644 --- a/Sources/HeartTrack/Models/User.cs +++ b/Sources/HeartTrack/Models/User/User.cs @@ -12,5 +12,6 @@ public float Taille { get; set; } public float Poids { get; set; } public DateTime BirthDate { get; set; } + public Boolean isBan { get; set; } } } diff --git a/Sources/HeartTrack/Models/User/UserModel.cs b/Sources/HeartTrack/Models/User/UserModel.cs new file mode 100644 index 0000000..276f786 --- /dev/null +++ b/Sources/HeartTrack/Models/User/UserModel.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace HeartTrack.Models +{ + public class UserModel + { + [Required] + [Range(0,121425711425541)] + public int Id { get; set; } + + [Required] + [StringLength(50, ErrorMessage = "The username must not exceed 50 characters.")] + public string Username { get; set; } + + [Required] + [StringLength(50, ErrorMessage = "The name must not exceed 50 characters.")] + [RegularExpression(@"^[A-Za-z]$", ErrorMessage = "Numbers are not accepted.")] + public string FirstName { get; set; } + + [Required] + [StringLength(25, ErrorMessage = "The last name must not exceed 25 characters.")] + [RegularExpression(@"^[A-Za-z]*$", ErrorMessage = "Numbers are not accepted.")] + public string LastName { get; set; } + [Required] + public string Email { get; set; } + [Required] + public string Password { get; set; } + [Required] + public string Sexe { get; set; } + [Required] + public float Taille { get; set; } + [Required] + public float Poids { get; set; } + [Required] + public DateTime BirthDate { get; set; } + public Boolean isBan { get; set; } + } +} diff --git a/Sources/HeartTrack/Pages/AddTicket.razor b/Sources/HeartTrack/Pages/AddTicket.razor new file mode 100644 index 0000000..765a20a --- /dev/null +++ b/Sources/HeartTrack/Pages/AddTicket.razor @@ -0,0 +1,16 @@ +@page "/tickets/add" + +

Add Ticket

+ +@* + + + +

+ +

+ + +
*@ diff --git a/Sources/HeartTrack/Pages/AddTicket.razor.cs b/Sources/HeartTrack/Pages/AddTicket.razor.cs new file mode 100644 index 0000000..2aa06e2 --- /dev/null +++ b/Sources/HeartTrack/Pages/AddTicket.razor.cs @@ -0,0 +1,107 @@ +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Components; +using HeartTrack.Models; + +namespace HeartTrack.Pages +{ + public partial class AddTicket + { + [Inject] + public ILocalStorageService LocalStorage { get; set; } + + [Inject] + public IWebHostEnvironment WebHostEnvironment { get; set; } + + /// + /// The current item model + /// + private TicketModel ticketModel = new(){}; + + /*private async void HandleValidSubmit() + { + // Get the current data + var currentData = await LocalStorage.GetItemAsync>("data"); + + // Simulate the Id + ticketModel.Id = currentData.Max(s => s.Id) + 1; + + // Add the item to the current data + currentData.Add(new Ticket + { + Id = ticketModel.Id, + Username = ticketModel.Username, + Nom = ticketModel.Nom, + Prenom = ticketModel.Prenom, + Contexte = ticketModel.Contexte, + Description = ticketModel.Description, + Urgence = ticketModel.Urgence + }); + + // Save the image + var imagePathInfo = new DirectoryInfo($"{WebHostEnvironment.WebRootPath}/images"); + + // Check if the folder "images" exist + if (!imagePathInfo.Exists) + { + imagePathInfo.Create(); + } + + // Determine the image name + var fileName = new FileInfo($"{imagePathInfo}/{itemModel.Name}.png"); + + // Write the file content + await File.WriteAllBytesAsync(fileName.FullName, itemModel.ImageContent); + + // Save the data + await LocalStorage.SetItemAsync("data", currentData); + } + + private async Task LoadImage(InputFileChangeEventArgs e) + { + // Set the content of the image to the model + using (var memoryStream = new MemoryStream()) + { + await e.File.OpenReadStream().CopyToAsync(memoryStream); + itemModel.ImageContent = memoryStream.ToArray(); + } + } + + private void OnEnchantCategoriesChange(string item, object checkedValue) + { + if ((bool)checkedValue) + { + if (!itemModel.EnchantCategories.Contains(item)) + { + itemModel.EnchantCategories.Add(item); + } + + return; + } + + if (itemModel.EnchantCategories.Contains(item)) + { + itemModel.EnchantCategories.Remove(item); + } + } + + private void OnRepairWithChange(string item, object checkedValue) + { + if ((bool)checkedValue) + { + if (!itemModel.RepairWith.Contains(item)) + { + itemModel.RepairWith.Add(item); + } + + return; + } + + if (itemModel.RepairWith.Contains(item)) + { + itemModel.RepairWith.Remove(item); + } + }*/ + + } +} diff --git a/Sources/HeartTrack/Pages/Admin.razor b/Sources/HeartTrack/Pages/Admin.razor new file mode 100644 index 0000000..6cf0b2b --- /dev/null +++ b/Sources/HeartTrack/Pages/Admin.razor @@ -0,0 +1,3 @@ +@page "/admin" + +

Admin Page

\ No newline at end of file diff --git a/Sources/HeartTrack/Pages/BannedUsers.razor b/Sources/HeartTrack/Pages/BannedUsers.razor index f35dc47..cdcfe9b 100644 --- a/Sources/HeartTrack/Pages/BannedUsers.razor +++ b/Sources/HeartTrack/Pages/BannedUsers.razor @@ -1,11 +1,29 @@ @page "/banned-users" - +@using HeartTrack.Models Banned Users

Banned Users

This is banned users list of this website. - +
+ + Ajouter + +
+ + + + + + + + diff --git a/Sources/HeartTrack/Pages/BannedUsers.razor.cs b/Sources/HeartTrack/Pages/BannedUsers.razor.cs new file mode 100644 index 0000000..d87c5c1 --- /dev/null +++ b/Sources/HeartTrack/Pages/BannedUsers.razor.cs @@ -0,0 +1,66 @@ +using Blazored.LocalStorage; +using Blazorise; +using Blazorise.DataGrid; +using HeartTrack.Models; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; + +namespace HeartTrack.Pages +{ + public partial class BannedUsers + { + private List users; + + private int totalUser; + + + [Inject] + public HttpClient Http { get; set; } + + [Inject] + public NavigationManager NavigationManager { get; set; } + + [Inject] + public ILocalStorageService LocalStorage { get; set; } + + [Inject] + public IStringLocalizer Localizer { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + // Do not treat this action if is not the first render + if (!firstRender) + { + return; + } + + var currentData = await LocalStorage.GetItemAsync("data"); + + // Check if data exist in the local storage + if (currentData == null) + { + // this code add in the local storage the fake data (we load the data sync for initialize the data before load the OnReadData method) + var originalData = Http.GetFromJsonAsync($"{NavigationManager.BaseUri}fake-data.json").Result; + await LocalStorage.SetItemAsync("data", originalData); + } + } + + private async Task OnReadData(DataGridReadDataEventArgs e) + { + if (e.CancellationToken.IsCancellationRequested) + { + return; + } + + // When you use a real API, we use this follow code + //var response = await Http.GetJsonAsync( $"http://my-api/api/data?page={e.Page}&pageSize={e.PageSize}" ); + var response = (await LocalStorage.GetItemAsync("data")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList(); + + if (!e.CancellationToken.IsCancellationRequested) + { + totalUser = (await LocalStorage.GetItemAsync>("data")).Count; + users = new List(response); // an actual data for the current page + } + } + } +} diff --git a/Sources/HeartTrack/Pages/FetchData.razor b/Sources/HeartTrack/Pages/FetchData.razor deleted file mode 100644 index 54db2b0..0000000 --- a/Sources/HeartTrack/Pages/FetchData.razor +++ /dev/null @@ -1,49 +0,0 @@ -@page "/fetchdata" - -Weather forecast - -@using HeartTrack.Data -@inject WeatherForecastService ForecastService - -

Weather forecast

- -

This component demonstrates fetching data from a service.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - forecasts = await ForecastService.GetForecastAsync(DateTime.Now); - } -} - diff --git a/Sources/HeartTrack/Pages/Index.razor b/Sources/HeartTrack/Pages/Index.razor index b27ac3c..2762adb 100644 --- a/Sources/HeartTrack/Pages/Index.razor +++ b/Sources/HeartTrack/Pages/Index.razor @@ -1,7 +1,7 @@ @page "/" @using System.Globalization -Global View +@* Global View

Global View

@@ -12,5 +12,28 @@ Actual language: CurrentCulture: @CultureInfo.CurrentCulture

- + *@ + + + +

Hello @context.User.Identity.Name !!

+ +

Welcome to Blazor Learner.

+ +
    + @foreach (var claim in context.User.Claims) + { +
  • @claim.Type: @claim.Value
  • + } +
+ +
+ +

Loading ...

+
+ +

Authentication Failure!

+

You're not signed in.

+
+
diff --git a/Sources/HeartTrack/Pages/Login.razor b/Sources/HeartTrack/Pages/Login.razor new file mode 100644 index 0000000..a4c7083 --- /dev/null +++ b/Sources/HeartTrack/Pages/Login.razor @@ -0,0 +1,26 @@ +@page "/login" +@layout AuthLayout + +

+ Login +

+ + \ No newline at end of file diff --git a/Sources/HeartTrack/Pages/Login.razor.cs b/Sources/HeartTrack/Pages/Login.razor.cs new file mode 100644 index 0000000..cd94365 --- /dev/null +++ b/Sources/HeartTrack/Pages/Login.razor.cs @@ -0,0 +1,32 @@ +using HeartTrack.Models; +using HeartTrack.Services; +using Microsoft.AspNetCore.Components; + +namespace HeartTrack.Pages +{ + public partial class Login + { + [Inject] + public CustomStateProvider AuthStateProvider { get; set; } + + [Inject] + public NavigationManager NavigationManager { get; set; } + + private string error { get; set; } + private LoginRequest loginRequest { get; set; } = new LoginRequest(); + + private async Task OnSubmit() + { + error = null; + try + { + await AuthStateProvider.Login(loginRequest); + NavigationManager.NavigateTo(""); + } + catch (Exception ex) + { + error = ex.Message; + } + } + } +} diff --git a/Sources/HeartTrack/Pages/Register.razor b/Sources/HeartTrack/Pages/Register.razor new file mode 100644 index 0000000..a8e7602 --- /dev/null +++ b/Sources/HeartTrack/Pages/Register.razor @@ -0,0 +1,29 @@ +@page "/register" +@layout AuthLayout + +

+ Register +

+ + \ No newline at end of file diff --git a/Sources/HeartTrack/Pages/Register.razor.cs b/Sources/HeartTrack/Pages/Register.razor.cs new file mode 100644 index 0000000..40266ab --- /dev/null +++ b/Sources/HeartTrack/Pages/Register.razor.cs @@ -0,0 +1,32 @@ +using HeartTrack.Models; +using HeartTrack.Services; +using Microsoft.AspNetCore.Components; + +namespace HeartTrack.Pages +{ + public partial class Register + { + [Inject] + public CustomStateProvider AuthStateProvider { get; set; } + + [Inject] + public NavigationManager NavigationManager { get; set; } + + private string error { get; set; } + private RegisterRequest registerRequest { get; set; } = new RegisterRequest(); + + private async Task OnSubmit() + { + error = null; + try + { + await AuthStateProvider.Register(registerRequest); + NavigationManager.NavigateTo(""); + } + catch (Exception ex) + { + error = ex.Message; + } + } + } +} diff --git a/Sources/HeartTrack/Pages/Test.razor b/Sources/HeartTrack/Pages/Test.razor new file mode 100644 index 0000000..6e56208 --- /dev/null +++ b/Sources/HeartTrack/Pages/Test.razor @@ -0,0 +1,20 @@ +@page "/test" +
+ +
+ +@if (!Users.Any()) +{ +

Loading some images...

+} +else +{ +
+ @foreach (var user in FilteredUsers) + { +

feur

+ } +
+} \ No newline at end of file diff --git a/Sources/HeartTrack/Pages/Test.razor.cs b/Sources/HeartTrack/Pages/Test.razor.cs new file mode 100644 index 0000000..705604c --- /dev/null +++ b/Sources/HeartTrack/Pages/Test.razor.cs @@ -0,0 +1,28 @@ +using Blazorise; +using HeartTrack.Models; +using HeartTrack.Services; +using Microsoft.AspNetCore.Components; + +namespace HeartTrack.Pages +{ + partial class Test : ComponentBase + { + public IEnumerable Users { get; set; } = new List(); + + public string SearchText = ""; + + [Inject] + public IDataService DataService { get; set; } + + [Inject] + public IWebHostEnvironment WebHostEnvironment { get; set; } + + protected override async Task OnInitializedAsync() + { + Users = await DataService.List(1, 50); ; + } + + List FilteredUsers => Users.Where( + user => user.Username.ToLower().Contains(SearchText.ToLower())).ToList(); + } +} diff --git a/Sources/HeartTrack/Program.cs b/Sources/HeartTrack/Program.cs index f16320e..dda86a9 100644 --- a/Sources/HeartTrack/Program.cs +++ b/Sources/HeartTrack/Program.cs @@ -7,6 +7,9 @@ using Blazorise.Icons.FontAwesome; using Microsoft.AspNetCore.Localization; using System.Globalization; using Microsoft.Extensions.Options; +using Blazored.LocalStorage; +using HeartTrack.Services; +using Microsoft.AspNetCore.Components.Authorization; var builder = WebApplication.CreateBuilder(args); @@ -15,8 +18,17 @@ builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton(); +// Authentification +builder.Services.AddOptions(); +builder.Services.AddAuthorizationCore(); +builder.Services.AddScoped(); +builder.Services.AddScoped(s => s.GetRequiredService()); +builder.Services.AddScoped(); + builder.Services.AddHttpClient(); +builder.Services.AddScoped(); + builder.Services.AddBlazorise() .AddBootstrapProviders() .AddFontAwesomeIcons(); @@ -38,6 +50,8 @@ builder.Services.Configure(options => options.SupportedUICultures = new List { new CultureInfo("en-US"), new CultureInfo("fr-FR") }; }); +builder.Services.AddBlazoredLocalStorage(); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/Sources/HeartTrack/Services/AuthService.cs b/Sources/HeartTrack/Services/AuthService.cs new file mode 100644 index 0000000..d724f3a --- /dev/null +++ b/Sources/HeartTrack/Services/AuthService.cs @@ -0,0 +1,57 @@ +using HeartTrack.Models; +using System.Security.Claims; + +namespace HeartTrack.Services +{ + public class AuthService : IAuthService + { + private static readonly List CurrentUser; + + static AuthService() + { + CurrentUser = new List + { + new AppUser { UserName = "Jesus", Password = "123456", Roles = new List { "admin" } }, + new AppUser { UserName = "Antoine", Password = "123456", Roles = new List { "admin" } }, + new AppUser { UserName = "Paul", Password = "123456", Roles = new List { "admin" } }, + new AppUser { UserName = "Kevin", Password = "123456", Roles = new List { "admin" } }, + new AppUser { UserName = "David", Password = "123456", Roles = new List { "admin" } } + }; + } + + public CurrentUser GetUser(string userName) + { + var user = CurrentUser.FirstOrDefault(w => w.UserName == userName); + + if (user == null) + { + throw new Exception("User name or password invalid !"); + } + + var claims = new List(); + claims.AddRange(user.Roles.Select(s => new Claim(ClaimTypes.Role, s))); + + return new CurrentUser + { + IsAuthenticated = true, + UserName = user.UserName, + Claims = claims.ToDictionary(c => c.Type, c => c.Value) + }; + } + + public void Login(LoginRequest loginRequest) + { + var user = CurrentUser.FirstOrDefault(w => w.UserName == loginRequest.UserName && w.Password == loginRequest.Password); + + if (user == null) + { + throw new Exception("User name or password invalid !"); + } + } + + public void Register(RegisterRequest registerRequest) + { + CurrentUser.Add(new AppUser { UserName = registerRequest.UserName, Password = registerRequest.Password, Roles = new List { "guest" } }); + } + } +} diff --git a/Sources/HeartTrack/Services/CustomStateProvider.cs b/Sources/HeartTrack/Services/CustomStateProvider.cs new file mode 100644 index 0000000..174a847 --- /dev/null +++ b/Sources/HeartTrack/Services/CustomStateProvider.cs @@ -0,0 +1,75 @@ +using HeartTrack.Models; +using Microsoft.AspNetCore.Components.Authorization; +using System.Security.Claims; + +namespace HeartTrack.Services +{ + public class CustomStateProvider : AuthenticationStateProvider + { + private readonly IAuthService _authService; + private CurrentUser _currentUser; + + public CustomStateProvider(IAuthService authService) + { + this._authService = authService; + } + + public override async Task GetAuthenticationStateAsync() + { + var identity = new ClaimsIdentity(); + try + { + var userInfo = GetCurrentUser(); + if (userInfo.IsAuthenticated) + { + var claims = new[] { new Claim(ClaimTypes.Name, _currentUser.UserName) }.Concat(_currentUser.Claims.Select(c => new Claim(c.Key, c.Value))); + identity = new ClaimsIdentity(claims, "Server authentication"); + } + } + catch (HttpRequestException ex) + { + Console.WriteLine("Request failed:" + ex); + } + + return new AuthenticationState(new ClaimsPrincipal(identity)); + } + + public async Task Login(LoginRequest loginParameters) + { + _authService.Login(loginParameters); + + // No error - Login the user + var user = _authService.GetUser(loginParameters.UserName); + _currentUser = user; + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + public async Task Logout() + { + _currentUser = null; + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + public async Task Register(RegisterRequest registerParameters) + { + _authService.Register(registerParameters); + + // No error - Login the user + var user = _authService.GetUser(registerParameters.UserName); + _currentUser = user; + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + private CurrentUser GetCurrentUser() + { + if (_currentUser != null && _currentUser.IsAuthenticated) + { + return _currentUser; + } + + return new CurrentUser(); + } + } +} diff --git a/Sources/HeartTrack/Services/DataLocalService.cs b/Sources/HeartTrack/Services/DataLocalService.cs new file mode 100644 index 0000000..aa5e843 --- /dev/null +++ b/Sources/HeartTrack/Services/DataLocalService.cs @@ -0,0 +1,85 @@ +using Blazored.LocalStorage; +using HeartTrack.Models; +using Microsoft.AspNetCore.Components; + +namespace HeartTrack.Services +{ + public class DataLocalService : IDataService + { + private readonly HttpClient _http; + private readonly ILocalStorageService _localStorage; + private readonly NavigationManager _navigationManager; + private readonly IWebHostEnvironment _webHostEnvironment; + + public DataLocalService( + ILocalStorageService localStorage, + HttpClient http, + IWebHostEnvironment webHostEnvironment, + NavigationManager navigationManager) + { + _localStorage = localStorage; + _http = http; + _webHostEnvironment = webHostEnvironment; + _navigationManager = navigationManager; + } + + public async Task Add(UserModel model) + { + // Get the current data + var currentData = await _localStorage.GetItemAsync>("data"); + + // Simulate the Id + model.Id = currentData.Max(s => s.Id) + 1; + + // Add the item to the current data + currentData.Add(new User + { + Id = model.Id, + Username = model.Username, + Nom = model.FirstName, + Prenom = model.LastName, + Email = model.Email, + Password = model.Password, + Sexe = model.Sexe, + Taille = model.Taille, + Poids = model.Poids, + BirthDate = model.BirthDate + }); + + // Save the data + await _localStorage.SetItemAsync("data", currentData); + } + + public async Task Count() + { + // Load data from the local storage + var currentData = await _localStorage.GetItemAsync("data"); + + // Check if data exist in the local storage + if (currentData == null) + { + // this code add in the local storage the fake data + var originalData = await _http.GetFromJsonAsync($"{_navigationManager.BaseUri}fake-data.json"); + await _localStorage.SetItemAsync("data", originalData); + } + + return (await _localStorage.GetItemAsync("data")).Length; + } + + public async Task> List(int currentPage, int pageSize) + { + // Load data from the local storage + var currentData = await _localStorage.GetItemAsync("data"); + + // Check if data exist in the local storage + if (currentData == null) + { + // this code add in the local storage the fake data + var originalData = await _http.GetFromJsonAsync($"{_navigationManager.BaseUri}fake-data.json"); + await _localStorage.SetItemAsync("data", originalData); + } + + return (await _localStorage.GetItemAsync("data")).Skip((currentPage - 1) * pageSize).Take(pageSize).ToList(); + } + } +} diff --git a/Sources/HeartTrack/Services/IAuthService.cs b/Sources/HeartTrack/Services/IAuthService.cs new file mode 100644 index 0000000..2cede2c --- /dev/null +++ b/Sources/HeartTrack/Services/IAuthService.cs @@ -0,0 +1,13 @@ +using HeartTrack.Models; + +namespace HeartTrack.Services +{ + public interface IAuthService + { + CurrentUser GetUser(string userName); + + void Login(LoginRequest loginRequest); + + void Register(RegisterRequest registerRequest); + } +} diff --git a/Sources/HeartTrack/Services/IDataService.cs b/Sources/HeartTrack/Services/IDataService.cs new file mode 100644 index 0000000..53da8e8 --- /dev/null +++ b/Sources/HeartTrack/Services/IDataService.cs @@ -0,0 +1,13 @@ +using HeartTrack.Models; + +namespace HeartTrack.Services +{ + public interface IDataService + { + Task Add(UserModel model); + + Task Count(); + + Task> List(int currentPage, int pageSize); + } +} diff --git a/Sources/HeartTrack/Shared/AuthLayout.razor b/Sources/HeartTrack/Shared/AuthLayout.razor new file mode 100644 index 0000000..ae7bc2a --- /dev/null +++ b/Sources/HeartTrack/Shared/AuthLayout.razor @@ -0,0 +1,6 @@ +@inherits LayoutComponentBase +
+
+ @Body +
+
\ No newline at end of file diff --git a/Sources/HeartTrack/Shared/MainLayout.razor b/Sources/HeartTrack/Shared/MainLayout.razor index 2772a45..1d56865 100644 --- a/Sources/HeartTrack/Shared/MainLayout.razor +++ b/Sources/HeartTrack/Shared/MainLayout.razor @@ -15,8 +15,9 @@ @* Messages, notifs et pp compte à mettre *@ -
+
+
diff --git a/Sources/HeartTrack/Shared/MainLayout.razor.cs b/Sources/HeartTrack/Shared/MainLayout.razor.cs new file mode 100644 index 0000000..cb15ad0 --- /dev/null +++ b/Sources/HeartTrack/Shared/MainLayout.razor.cs @@ -0,0 +1,32 @@ +using HeartTrack.Services; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components; + +namespace HeartTrack.Shared +{ + public partial class MainLayout + { + [Inject] + public CustomStateProvider AuthStateProvider { get; set; } + + [Inject] + public NavigationManager NavigationManager { get; set; } + + [CascadingParameter] + private Task AuthenticationState { get; set; } + + protected override async Task OnParametersSetAsync() + { + if (!(await AuthenticationState).User.Identity.IsAuthenticated) + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task LogoutClick() + { + await AuthStateProvider.Logout(); + NavigationManager.NavigateTo("/login"); + } + } +} diff --git a/Sources/HeartTrack/Shared/NavMenu.razor b/Sources/HeartTrack/Shared/NavMenu.razor index f0a793c..9b119bd 100644 --- a/Sources/HeartTrack/Shared/NavMenu.razor +++ b/Sources/HeartTrack/Shared/NavMenu.razor @@ -11,6 +11,13 @@ @Localizer["Global"] + + +