Compare commits

..

41 Commits

Author SHA1 Message Date
Antoine PINAGOT d51484101c Ajout Tests
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII d01c538964 Update 'Sources/HeartTrack/Dockerfile'
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII 89cc3c41e3 Update '.drone.yml'
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII c4602a4b0e Update '.drone.yml'
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII d00c5198a3 Update '.drone.yml'
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII 0cf6cfcbff Update '.drone.yml'
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII a3ba1c9b15 🧪 Test deploying Web Site
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 65eb8b9d95 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA adb9655a87 Mise à jour de '.drone.yml'
1 year ago
Antoine PEREDERII ed8ed96133 🧪 Test CI/CD
1 year ago
Antoine PINAGOT bc87676131 Résolution erreur affichage Datagrid ticket
1 year ago
Antoine PINAGOT 207ac95462 Mise à jour de 'Sources/HeartTrack/Pages/Tickets.razor.cs'
1 year ago
Antoine PINAGOT bb7eb27e9d Test
1 year ago
Antoine PEREDERII 7052f5c3ee Update 'README.md'
1 year ago
Antoine PEREDERII 9139329c60 Merge pull request 'merge merged_APE into master for evaluation' (#2) from merged_APE into main
1 year ago
Antoine PEREDERII 75f2e18c31 update README.md
1 year ago
Antoine PINAGOT 0305b1187f Suppression code inutile
1 year ago
Antoine PEREDERII a4ae9e1426 update program.cs
1 year ago
Antoine PINAGOT bb4e8620f1 Suppression code inutile
1 year ago
Antoine PEREDERII 9a0abe0206 Merge branch 'merged_APE' of codefirst.iut.uca.fr:HeartDev/Admin into merged_APE
1 year ago
Antoine PEREDERII 7b50b1e5ad remove fakes data's files
1 year ago
Antoine PINAGOT f223bd6a79 Link fonctionne
1 year ago
Antoine PINAGOT 0e51eab49e link ticket service w/ tickets pages
1 year ago
Antoine PEREDERII 013b41e3ef merge Test_API and WORK_API into merged_APE
1 year ago
Antoine PINAGOT e29d3fc988 Résolution de la connerie d'Antoine sur ma branche et page Ticket presque terminé: sorting in coming
1 year ago
Antoine PEREDERII dd0e652dd6 updates
1 year ago
Paul LEVRAULT f1688e278d Debut siganlements
1 year ago
Antoine PEREDERII 7e56cbfb00 fix bugs with ReportDataService
1 year ago
Antoine PEREDERII a6efaf9d5f update DataServices
1 year ago
Antoine PEREDERII 2b6bb5a983 update Activities
1 year ago
Antoine PEREDERII 8872c10e2f add all the services
1 year ago
Antoine PEREDERII 4bd6617ddf merge WORK_APE on this branch
1 year ago
Antoine PEREDERII 9ab1129011 add IActivityDataServices
1 year ago
Antoine PEREDERII f484afee83 add sources projects
1 year ago
Antoine PEREDERII f12aa192e9 update .giti
1 year ago
Antoine PEREDERII 6dd85a1cf1 add sources project
1 year ago
Antoine PEREDERII aa94fb211e update .gitignore
1 year ago
Antoine PEREDERII 37e82f06a8 update .gitignore
1 year ago
Antoine PINAGOT 4a25bcaf21 Suppression authentificationcar marche pas
1 year ago
Antoine PINAGOT 91a6b54ed3 Ajout model Signalement
1 year ago
Antoine PINAGOT 2449c27f4e Connexion + Tab dans des pages
1 year ago

@ -0,0 +1,81 @@
kind: pipeline
type: docker
name: HeartTrack-Admin-CI
trigger:
event:
- push
steps:
- name: build
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
- cd Sources/HeartTrack/
- dotnet restore HeartTrack.sln
- dotnet build HeartTrack.sln -c Release --no-restore
- dotnet publish HeartTrack.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release
- name: tests
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
- cd Sources/HeartTrack/
- dotnet restore HeartTrack.sln
- dotnet test HeartTrack.sln --no-restore
depends_on: [build]
- name: code-analysis
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet8
secrets: [ SECRET_SONAR_LOGIN ]
settings:
sonar_host: https://codefirst.iut.uca.fr/sonar/
sonar_token:
from_secret: SECRET_SONAR_LOGIN
project_key: HeartTrack-API
coverage_exclusions: Tests/**, StubbedContextLib/**, StubAPI/**
duplication_exclusions: Tests/**, StubbedContextLib/**
commands:
- cd Sources/HeartTrack/
- dotnet restore HeartTrack.sln
- dotnet sonarscanner begin /k:HeartTrack-API /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.login=$${PLUGIN_SONAR_TOKEN} /d:sonar.coverage.exclusions="Tests/**, StubbedContextLib/**, StubAPI/**, HeartTrackAPI/Utils/**" /d:sonar.cpd.exclusions="Tests/**, StubbedContextLib/**, StubAPI/**" /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml"
- dotnet build HeartTrack.sln -c Release --no-restore
- dotnet test HeartTrack.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage"
- reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport"
- dotnet publish HeartTrack.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release
- dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
depends_on: [ tests ]
---
kind: pipeline
type: docker
name: HeartTrack-Admin-CD
trigger:
event:
- push
steps:
- name: docker-build-and-push
image: plugins/docker
settings:
dockerfile: Sources/HeartTrack/Dockerfile
context: Sources/HeartTrack/
registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/david.d_almeida/admin
username:
from_secret: SECRET_REGISTRY_USERNAME
password:
from_secret: SECRET_REGISTRY_PASSWORD
# database container admin
- name: deploy-container-admin
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
CODEFIRST_CLIENTDRONE_ENV_TYPE: ADMIN
IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/admin:latest
CONTAINERNAME: heart_track_admin
COMMAND: create
ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond,marcchevaldonne
OVERWRITE: true
depends_on: [ docker-build-and-push ]

@ -43,12 +43,38 @@ Ce dépôt contient une application Blazor conçue pour faciliter l'administrati
4. **Sécurité Intégrée**: Utilisation des fonctionnalités de sécurité de Blazor pour protéger les données sensibles.
## Les attendus du projet
* [x] Implementation of a data visualization page with pagination (2 points)
* [x] Page for adding an element with validation (2 point)
* [x] Edit page of an element without validation (2 point)
* [x] Deletion of an element with a confirmation (2 point)
* [ ] Complex component (5 point)
* [x] Use API (Get / Insert / Update / Delete) (3 point)
* [x] IOC & DI use (4 point)
* [x] Localization & Globalization (at least two languages) (1 point)
* [ ] Use of the configuration (1 point)
* [ ] Logs (2 points)
* [x] Code cleanliness (2 point)
* [x] GUI (Global design, placement of buttons, ...) (2 point)
* [x] Code location (No code in views) (2 point)
* [x] The Readme (2 points)
* [x] Description of how the client solution works (code-level illustration) (6 points)
* [x] Merge request (2 points)
## Ce que nous avons fait
Pour d'avantages d'informations, voir les branches `issue_auth` et `merged_APE`
* [x] Les listing dans la page de tickets et d'Activités
* [x] La modification dans la page de tickets
* [x] La suppression dans la page de tickets
* [x] Le get by id dans la page de tickets
* [x] L'implementation de la partie API et du data service dans la pages d'Activités
* [x] L'authentification dans la branch `issue_auth` mais des problèmes persistent (les actions des services ne sont pas gerer par l'authentification)
* [ ] Le data service dans toutes les pages
* [ ] La répartitions des fonctionnalités de la page de tickets sur les autres pages
## Répartition du Git
[**Sources**](Sources/) : **Code de l'application**
[**Documents**](Documents/README_DOCUMENTS.md) : **Documentation de l'application et diagrammes**
[**Sources**](Sources/HeartTrack) : **Code de l'application**
---
@ -61,7 +87,7 @@ Le projet HeartTrack utilise un modèle de flux de travail Git (Gitflow) pour or
### API PHP
L'application Blazor utilise une API en PHP pour récupérer les données depuis la base de données.
Pour l'utiliser, vous devez faire tourner notre projet PHP disponible [ici](https://codefirst.iut.uca.fr/git/HeartDev/Web/src/branch/API_tests) sur votre machine locale. Pour ensuite changer le type de skockage par `API` dans le fichier `monfichier.cs` dans le dossier `Data` de l'application Blazor.
Pour l'utiliser, vous devez faire tourner notre projet PHP disponible [ici](https://codefirst.iut.uca.fr/git/HeartDev/Web/src/branch/API_tests) sur votre machine locale. Pour ensuite changer le type de skockage `ActivityDataServiceFactice` par `ActivityDataServiceAPI` et vice versa dans le fichier `Program.cs` de l'application Blazor en ligne 24 `Add Data Services`.
### Prérequis
@ -74,30 +100,19 @@ Pour l'utiliser, vous devez faire tourner notre projet PHP disponible [ici](http
1. Clonez ce dépôt sur votre machine locale :
```bash
git clone https://github.com/votre-utilisateur/Blazor-User-Admin.git
git clone https://codefirst.iut.uca.fr/git/HeartDev/Admin.git
```
2. Accédez au répertoire du projet :
```bash
cd Blazor-User-Admin
```
2. Lacer Visual Studio et ouvrez le projet `HeartTrack.sln` dans le dossier `Sources/HeartTrack`.
3. Lancez l'application avec la commande :
```bash
dotnet run
```
Pour des raison de manque de la partie php, vous ne pourrez utiliser l'application qu'en localStorage. Il est cependant possible de tester l'aaplication avec la partie API en PHP en faisant tourner en local le projet php disponible [ici](https://codefirst.iut.uca.fr/git/HeartDev/Web/src/branch/API_tests) et en changant le type de skockage `ActivityDataServiceFactice` par `ActivityDataServiceAPI` dans le fichier `Program.cs` de l'application Blazor en ligne 24 `Add Data Services` et importer les dépendances en conséquence.
4. Ouvrez votre navigateur et accédez à [https://localhost:5001](https://localhost:5001) pour voir l'application en action.
Pour des raison de manque de la partie php, vous ne pourrez utiliser l'application qu'en localStorage. Il est cependant possible de tester l'aaplication avec la partie API en PHP en faisant tourner en local le projet php disponible [ici](https://codefirst.iut.uca.fr/git/HeartDev/Web/src/branch/API_tests) et en changant le type de stockage dans le fichier `monfichier.cs` dans le dossier `Data` de l'application Blazor.
Comme ceci :
```csharp
public class MonFichier
{
public static string TypeStockage = "API";
}
// Add Data Services
builder.Services.AddScoped<IActivityDataService, ActivityDataServiceAPI>();
builder.Services.AddScoped<ITicketDataService, TicketDataServiceAPI>();
```
### Fabriqué avec

@ -10,4 +10,3 @@
</LayoutView>
</NotFound>
</Router>

@ -3,25 +3,13 @@
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
/// <summary>
/// The culture controller.
/// </summary>
[Route("[controller]/[action]")]
public class CultureController : Controller
{
/// <summary>
/// Sets the culture.
/// </summary>
/// <param name="culture">The culture.</param>
/// <param name="redirectUri">The redirect URI.</param>
/// <returns>
/// The action result.
/// </returns>
public IActionResult SetCulture(string culture, string redirectUri)
{
if (culture != null)
{
// Define a cookie with the selected culture
this.HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(

@ -0,0 +1,20 @@
# Utiliser l'image SDK .NET pour construire l'application
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app
# Copier les fichiers du projet et restaurer les dépendances
COPY *.csproj .
RUN dotnet restore
# Copier tout le reste et construire l'application
COPY . .
RUN dotnet publish -c Release -o out
# Utiliser l'image runtime .NET pour exécuter l'application
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
WORKDIR /app
COPY --from=build /app/out .
EXPOSE 8080
ENTRYPOINT ["dotnet", "HeartTrack.dll"]

@ -7,10 +7,33 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
<PackageReference Include="Blazorise" Version="1.4.1" />
<PackageReference Include="Blazorise.Bootstrap" Version="1.4.0" />
<PackageReference Include="Blazorise.DataGrid" Version="1.4.0" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.1" />
<PackageReference Include="MudBlazor" Version="6.12.0" />
<PackageReference Include="xunit" Version="2.7.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\ActivityDataService\" />
<Folder Include="Models\Activity\" />
<Folder Include="wwwroot\data\" />
<Folder Include="Services\TicketDataService\" />
</ItemGroup>
<ItemGroup>
<None Remove="Services\ActivityDataService\" />
<None Remove="Models\Activity\" />
<None Remove="Services\DataLocalService\" />
<None Remove="Services\ReportDataService\" />
<None Remove="Services\UserDataService\" />
<None Remove="Services\TicketDataService\" />
</ItemGroup>
<ItemGroup>
<Content Remove="wwwroot\data\" />
</ItemGroup>
</Project>

@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188

@ -1,39 +0,0 @@
namespace HeartTrack.Models
{
public class Activity
{
public int IdActivity { get; set; }
public string Type { get; set; }
public DateOnly Date { get; set; }
public TimeOnly StartTime { get; set; }
public DateTime EndTime { get; set; }
public int EffortRessenti { get; set; }
public float Variability { get; set; }
public float Variance { get; set; }
public float StandardDeviation { get; set; }
public float Average { get; set; }
public int Maximum { get; set; }
public int Minimum { get; set; }
public float AvrTemperature { get; set; }
public bool HasAutoPause { get; set; }
}
}
//[
// '{{repeat(15, 30)}}',
// {
// idActivity: '{{index(1)}}',
// type: '{{random("Type1", "Type2", "Type3")}}',
// date: '{{date(new Date(2014, 0, 1), new Date(), "YYYY-MM-dd")}}',
// startTime: '{{date(new Date(2014, 0, 1), new Date(), "HH:mm:ss")}}',
// endTime: '{{date(new Date(2014, 0, 1), new Date(), "HH:mm:ss")}}',
// effortRessenti: '{{integer(1, 5)}}',
// variability: '{{floating(1, 100)}}',
// variance: '{{floating(1, 100)}}',
// standardDeviation: '{{floating(1, 100)}}',
// average: '{{floating(1, 100)}}',
// maximum: '{{integer(80, 200)}}',
// minimum: '{{integer(30, 100)}}',
// avrTemperature: '{{floating(1, 100)}}',
// hasAutoPause: '{{bool()}}'
// }
//]

@ -0,0 +1,20 @@
namespace HeartTrack.Models
{
public class Activity
{
public int IdActivity { get; set; }
public string Type { get; set; }
public DateOnly Date { get; set; }
public DateOnly StartTime { get; set; }
public DateOnly EndTime { get; set; }
public int EffortRessenti { get; set; }
public float Variability { get; set; }
public float Variance { get; set; }
public float StandardDeviation { get; set; }
public float Average { get; set; }
public int Maximum { get; set; }
public int Minimum { get; set; }
public float AvrTemperature { get; set; }
public bool HasAutoPause { get; set; }
}
}

@ -0,0 +1,14 @@
using Blazorise;
namespace HeartTrack.Models
{
public class Report
{
public int Id { get; set; }
public string Username { get; set; }
public string ReportedUser { get; set; }
public string Raison { get; set; }
public string Description { get; set; }
public Image Image { get; set; }
}
}

@ -0,0 +1,25 @@
using Blazorise;
using System.ComponentModel.DataAnnotations;
namespace HeartTrack.Models
{
public class ReportModel
{
[Required]
[Range(0, 2500000)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Username { get; set; }
[Required]
[StringLength(50)]
public string ReportedUser { get; set; }
[Required]
[StringLength(150)]
public string Raison { get; set; }
[Required]
[StringLength(500)]
public string Description { get; set; }
public Image Image { get; set; }
}
}

@ -0,0 +1,11 @@
namespace HeartTrack.Models
{
public class Ticket
{
public int Id { get; set; }
public string Username { get; set; }
public string Contexte { get; set; }
public string Description { get; set; }
public Boolean isCheck { get; set; }
}
}

@ -0,0 +1,25 @@
using Blazorise;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace HeartTrack.Models
{
public class TicketModel
{
[Required]
[Range(0, 2500000)]
public int Id { get; set; }
[Required]
[StringLength(50, ErrorMessage = "The username must not exceed 50 characters.")]
public string Username { 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 isCheck { get; set; } = false;
}
}

@ -12,5 +12,6 @@
public float Taille { get; set; }
public float Poids { get; set; }
public DateTime BirthDate { get; set; }
public Boolean isBan { get; set; }
}
}

@ -0,0 +1,39 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace HeartTrack.Models
{
public class UserModel
{
[Required]
[Range(0,2500000)]
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; }
}
}

@ -4,6 +4,7 @@ using Blazorise.DataGrid;
using HeartTrack.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using HeartTrack.Services.ActivityDataService;
namespace HeartTrack.Pages
{
@ -14,41 +15,13 @@ namespace HeartTrack.Pages
private int totalActivity;
[Inject]
public HttpClient Http { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
private IActivityDataService ActivitiesDataService { get; set; }
[Inject]
public IStringLocalizer<Activities> Localizer { get; set; }
private async Task OnReadData(DataGridReadDataEventArgs<Activity> e)
private async Task OnReadData()
{
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
// When you use a real API, we use this follow code
//var response = await Http.GetJsonAsync<Item[]>( $"http://my-api/api/data?page={e.Page}&pageSize={e.PageSize}" );
//var response = await Http.GetStringAsync("http://localhost:8080/api");
//var activityList = JsonConvert.DeserializeObject<List<Activity>>(response);
var response = (await Http.GetFromJsonAsync<Activity[]>("http://localhost:8080/api")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList();
//await Http.GetFromJsonAsync<Activity[]>("http://localhost:8080/api");
//Console.WriteLine(response);
//var response = (await Http.GetFromJsonAsync<Activity[]>($"{NavigationManager.BaseUri}fake-data-activity.json")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList();
if (!e.CancellationToken.IsCancellationRequested)
{
totalActivity = (await Http.GetFromJsonAsync<List<Activity>>("http://localhost:8080/api")).Count;
//(await Http.GetFromJsonAsync<List<Activity>>($"{NavigationManager.BaseUri}fake-data-activity.json")).Count;
activities = new List<Activity>(response); // an actual data for the current page
//Console.WriteLine(totalActivity);
}
this.activities = await this.ActivitiesDataService.getAllActivities();
this.totalActivity = activities.Count();
}
}
}

@ -0,0 +1,30 @@
@page "/tickets/add"
<h3>Add Ticket</h3>
<EditForm Model="@ticketModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
<label for="username">
Username:
<InputText id="username" @bind-Value="ticketModel.Username" />
</label>
</p>
<p>
<label for="context">
Context:
<InputText id="context" @bind-Value="ticketModel.Contexte" />
</label>
</p>
<p>
<label for="description">
Description:
<InputText id="description" @bind-Value="ticketModel.Description" />
</label>
</p>
<button type="submit">Submit</button>
</EditForm>

@ -0,0 +1,43 @@
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components;
using HeartTrack.Models;
using HeartTrack.Services;
using HeartTrack.Services.TicketDataService;
namespace HeartTrack.Pages
{
public partial class AddTicket
{
[Inject]
public ITicketDataService TicketStorage { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IWebHostEnvironment WebHostEnvironment { get; set; }
public TicketModel ticketModel = new(){};
private async void HandleValidSubmit()
{
var currentData = await TicketStorage.getAllTickets();
ticketModel.Id = currentData.Max(s => s.Id) + 1;
currentData.Add(new Ticket
{
Id = ticketModel.Id,
Username = ticketModel.Username,
Contexte = ticketModel.Contexte,
Description = ticketModel.Description/*,
Urgence = ticketModel.Urgence*/
});
await TicketStorage.SaveAllTickets(currentData);
NavigationManager.NavigateTo("tickets");
}
}
}

@ -1,11 +0,0 @@
@page "/banned-users"
<PageTitle>Banned Users</PageTitle>
<h1>Banned Users</h1>
This is banned users list of this website.
<SurveyPrompt Title="How is Blazor working for you?" />

@ -6,11 +6,11 @@
<h1>Global View</h1>
This is the global statistics of our website.
<div style="align-content:flex-start">
<div style=" display:block; margin:50px;">
<p>Number of views of the website</p>
<MudChart ChartType="ChartType.Line" ChartSeries="@Series" @bind-SelectedIndex="IndexChart" XAxisLabels="@XAxisLabels" Width="100%" Height="350px" ChartOptions="@Options" />
</div>
Actual language:
<p>
<b>CurrentCulture</b>: @CultureInfo.CurrentCulture
</p>
<SurveyPrompt Title="How is Blazor working for you?" />
</div>

@ -0,0 +1,18 @@
using MudBlazor;
namespace HeartTrack.Pages
{
public partial class Index
{
private int IndexChart = -1;
public ChartOptions Options = new ChartOptions();
public List<ChartSeries> Series = new List<ChartSeries>()
{
new ChartSeries() { Name = "Views", Data = new double[] { 90, 79, 72, 69, 62, 62, 55, 65, 70 } }
};
public string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" };
}
}

@ -1,10 +0,0 @@
@page "/reports"
<PageTitle>Reports</PageTitle>
<h1>Report list</h1>
This is the report list of users.
<SurveyPrompt Title="How is Blazor working for you?" />

@ -1,4 +1,6 @@
@page "/tickets"
@using HeartTrack.Models
<PageTitle>Tickets</PageTitle>
@ -6,5 +8,52 @@
This is the ticket list of users.
<SurveyPrompt Title="How is Blazor working for you?" />
<div>
<MudButtonGroup Color="Color.Primary" Variant="Variant.Filled" Style="margin-top:50px; margin-bottom:5px;">
<MudButton OnClick="OnNavigateOnAddClicked">Add</MudButton>
</MudButtonGroup>
</div>
<DataGrid @ref="dataGrid"
TItem="Ticket"
Data="@tickets"
ReadData="@OnReadData"
TotalItems="@totalTicket"
PageSize="10"
ShowPager
Responsive
Sortable
SortMode="DataGridSortMode.Single">
<DataGridColumn TItem="Ticket" Field="@nameof(Ticket.Username)" Caption="Username" Width="200px"/>
<DataGridColumn TItem="Ticket" Field="@nameof(Ticket.Contexte)" Caption="Context" />
<DataGridColumn TItem="Ticket" Field="@nameof(Ticket.isCheck)" Caption="Status" SortField="@nameof( Ticket.isCheck )" SortDirection="Blazorise.SortDirection.Ascending" Width="150px" Editable>
<DisplayTemplate>
@if (context.isCheck)
{
<MudChip Variant="Variant.Outlined" Color="Color.Success">Closed</MudChip>
} else
{
<MudChip Variant="Variant.Outlined" Color="Color.Error">Open</MudChip>
}
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="Ticket" Field="@nameof(Ticket.Id)" Caption="Actions" Sortable="false" Width="150px">
<DisplayTemplate>
@if (context.isCheck)
{
<MudFab Color="Color.Tertiary" StartIcon="@Icons.Material.Filled.RemoveRedEye" Size="Size.Small" @onclick="(() => OnView(context.Id))"/>
<MudFab Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Delete" Size="Size.Small" @onclick="() => OnDelete(context)" />
}
else
{
<MudFab Color="Color.Tertiary" StartIcon="@Icons.Material.Filled.RemoveRedEye" Size="Size.Small" @onclick="() => OnView(context.Id)" />
<MudFab Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Close" Size="Size.Small" @onclick="() => OnClose(context.Id)" />
<MudFab Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Delete" Size="Size.Small" @onclick="() => OnDelete(context)" />
}
</DisplayTemplate>
</DataGridColumn>
</DataGrid>

@ -0,0 +1,100 @@
using Blazored.LocalStorage;
using Blazorise;
using Blazorise.DataGrid;
using HeartTrack.Models;
using HeartTrack.Services;
using HeartTrack.Services.TicketDataService;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using System;
using static MudBlazor.CategoryTypes;
namespace HeartTrack.Pages
{
public partial class Tickets
{
private List<Ticket> tickets;
private int totalTicket;
private DataGrid<Ticket> dataGrid;
[Inject]
public HttpClient Http { get; set; }
[Inject]
public ILocalStorageService LocalStorage { get; set; }
[Inject]
public ITicketDataService TicketService { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
private ISnackbar Snackbar { 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<Ticket[]>("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<Ticket[]>($"{NavigationManager.BaseUri}fake-tickets.json").Result;
await LocalStorage.SetItemAsync("data", originalData);
}
}
private async Task OnReadData(DataGridReadDataEventArgs<Ticket> e)
{
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
// When you use a real API, we use this follow code
//var response = await Http.GetJsonAsync<Data[]>( $"http://my-api/api/data?page={e.Page}&pageSize={e.PageSize}" );
var response = (await LocalStorage.GetItemAsync<Ticket[]>("data")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList();
if (!e.CancellationToken.IsCancellationRequested)
{
totalTicket = (await LocalStorage.GetItemAsync<List<Ticket>>("data")).Count;
tickets = new List<Ticket>(response); // an actual data for the current page
}
}
private async void OnClose(int id)
{
await TicketService.Close(id);
NavigationManager.NavigateTo("tickets", true);
Snackbar.Add("Ticket fermé !");
}
private void OnView(int id)
{
NavigationManager.NavigateTo("tickets/view/"+id);
}
private void OnNavigateOnAddClicked()
{
NavigationManager.NavigateTo("tickets/add");
}
private async void OnDelete(Ticket t)
{
await TicketService.RemoveTicket(t);
NavigationManager.NavigateTo("tickets", true);
}
}
}

@ -1,26 +0,0 @@
@page "/tokens"
@using HeartTrack.Models
<PageTitle>Tokens</PageTitle>
<h1>@Localizer["Title"]</h1>
<p>
@Localizer["Description"]
</p>
<DataGrid TItem="User"
Data="@users"
ReadData="@OnReadData"
TotalItems="@totalUser"
PageSize="10"
ShowPager
Responsive>
<DataGridColumn TItem="User" Field="@nameof(User.Id)" Caption="Id" />
<DataGridColumn TItem="User" Field="@nameof(User.Username)" Caption="@Localizer["Username"]" />
<DataGridColumn TItem="User" Field="@nameof(User.Nom)" Caption="@Localizer["FirstN"]" />
<DataGridColumn TItem="User" Field="@nameof(User.Prenom)" Caption="@Localizer["LastN"]" />
<DataGridColumn TItem="User" Field="@nameof(User.Password)" Caption="@Localizer["Password"]" />
</DataGrid>

@ -1,44 +0,0 @@
using Blazorise;
using Blazorise.DataGrid;
using HeartTrack.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using System.Collections.Generic;
using static System.Net.WebRequestMethods;
namespace HeartTrack.Pages
{
public partial class Tokens
{
private List<User> users;
private int totalUser;
[Inject]
public HttpClient Http { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IStringLocalizer<Tokens> Localizer { get; set; }
private async Task OnReadData(DataGridReadDataEventArgs<User> e)
{
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
// When you use a real API, we use this follow code
//var response = await Http.GetJsonAsync<Item[]>( $"http://my-api/api/data?page={e.Page}&pageSize={e.PageSize}" );
var response = (await Http.GetFromJsonAsync<User[]>($"{NavigationManager.BaseUri}fake-data.json")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList();
if (!e.CancellationToken.IsCancellationRequested)
{
totalUser = (await Http.GetFromJsonAsync<List<User>>($"{NavigationManager.BaseUri}fake-data.json")).Count;
users = new List<User>(response); // an actual data for the current page
}
}
}
}

@ -0,0 +1,19 @@
@page "/tickets/view/{Id:int}"
<h3>Ticket number @Id</h3>
<p>
Username: @ticket.Username
</p>
<p>
Contexte: @ticket.Contexte
</p>
<p>
Description: @ticket.Description
</p>
<p>
Status: @ticket.isCheck
</p>
<MudButton @OnClick="OnNavigateOnReturnClicked">Return</MudButton>

@ -0,0 +1,43 @@
using HeartTrack.Models;
using HeartTrack.Services;
using HeartTrack.Services.TicketDataService;
using Microsoft.AspNetCore.Components;
namespace HeartTrack.Pages
{
public partial class ViewTicket
{
[Parameter]
public int Id { get; set; }
private Ticket ticket { get; set; } = new();
[Inject]
public ITicketDataService TicketService { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IWebHostEnvironment WebHostEnvironment { get; set; }
protected async Task OnInitializedAsync()
{
var item = await TicketService.getTicketById(Id);
ticket = new Ticket
{
Id = item.Id,
Username = item.Username,
Contexte = item.Contexte,
Description = item.Description,
isCheck = item.isCheck
};
}
private void OnNavigateOnReturnClicked()
{
NavigationManager.NavigateTo("/tickets", true);
}
}
}

@ -30,8 +30,14 @@
<script src="_framework/blazor.server.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css">
@* Blazorised *@
<link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
<link href="_content/Blazorise.Bootstrap/blazorise.bootstrap.css" rel="stylesheet" />
@* MudBlazor *@
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>

@ -1,17 +1,65 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Localization;
using System.Globalization;
using Microsoft.Extensions.Options;
using Blazored.LocalStorage;
using HeartTrack.Services.ActivityDataService;
using HeartTrack.Services.TicketDataService;
using MudBlazor.Services;
using HeartTrack.Services.ActivityDataServiceFactice;
using HeartTrack.Services.TicketDataServiceFactice;
using MudBlazor;
var builder = WebApplication.CreateBuilder(args);
// Add Badge Component services
builder.Services.AddMudServices(config =>
{
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft;
config.SnackbarConfiguration.PreventDuplicates = false;
config.SnackbarConfiguration.NewestOnTop = false;
config.SnackbarConfiguration.ShowCloseIcon = true;
config.SnackbarConfiguration.VisibleStateDuration = 10000;
config.SnackbarConfiguration.HideTransitionDuration = 500;
config.SnackbarConfiguration.ShowTransitionDuration = 500;
config.SnackbarConfiguration.SnackbarVariant = Variant.Filled;
});
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddHttpClient();
// Add Data Services
builder.Services.AddScoped<IActivityDataService, ActivityDataServiceFactice>();
builder.Services.AddScoped<ITicketDataService, TicketDataServiceFactice>();
builder.Services.AddBlazorise()
.AddBootstrapProviders()
.AddFontAwesomeIcons();
// Add the controller of the app
builder.Services.AddControllers();
// Add the localization to the app and specify the resources path
builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; });
// Configure the localtization
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
// Set the default culture of the web site
options.DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US"));
// Declare the supported culture
options.SupportedCultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("fr-FR") };
options.SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("fr-FR") };
});
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddHttpClient();
@ -36,6 +84,8 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
options.SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("fr-FR") };
});
builder.Services.AddBlazoredLocalStorage();
var app = builder.Build();
// Configure the HTTP request pipeline.
@ -71,4 +121,3 @@ app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();

@ -120,6 +120,9 @@
<data name="Activity" xml:space="preserve">
<value>Activité</value>
</data>
<data name="Admin" xml:space="preserve">
<value>Gérer les admins</value>
</data>
<data name="Ban" xml:space="preserve">
<value>Utilisateurs bannis</value>
</data>

@ -120,6 +120,9 @@
<data name="Activity" xml:space="preserve">
<value>Activities</value>
</data>
<data name="Admin" xml:space="preserve">
<value>Admin managment</value>
</data>
<data name="Ban" xml:space="preserve">
<value>Ban users</value>
</data>

@ -0,0 +1,87 @@
using System;
using HeartTrack.Models;
using Microsoft.AspNetCore.Components;
namespace HeartTrack.Services.ActivityDataService
{
public class ActivityDataServiceAPI : IActivityDataService
{
[Inject]
private HttpClient _clientHttp { get; set; }
public ActivityDataServiceAPI(HttpClient clientHttp)
{
this._clientHttp = clientHttp;
}
public async Task AddActivity(Activity a)
{
HttpResponseMessage response = await _clientHttp.PostAsJsonAsync("http://localhost:8080/api/activities", a);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - Activité avec l'id " + a.IdActivity + " ajouté avec succès");
}
else
{
Console.WriteLine("API - Problème ajout Activité");
}
}
public async Task<Activity> getActivityById(int id)
{
Activity activity = await _clientHttp.GetFromJsonAsync<Activity>("http://localhost:8080/api/activities/{id}");
return activity;
}
public async Task<List<Activity>> getAllActivities()
{
List<Activity> lActivities = await _clientHttp.GetFromJsonAsync<List<Activity>>("http://localhost:8080/api/activities");
return lActivities;
}
public async Task RemoveActivity(Activity a)
{
HttpResponseMessage response = await _clientHttp.DeleteAsync($"http://localhost:8080/api/activities/{a.IdActivity}");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - Activité avec l'id " + a.IdActivity + " supprimé avec succès");
}
else
{
Console.WriteLine("API - Problème suppression Activité");
}
}
public async Task SaveAllActivities(List<Activity> list)
{
HttpResponseMessage response = await _clientHttp.PutAsJsonAsync("http://localhost:8080/api/activities", list);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - List d'activités sauvegardé avec succès");
}
else
{
Console.WriteLine("API - Problème sauvegarde List d'activités");
}
}
public async Task UpdateActivity(Activity a)
{
HttpResponseMessage response = await _clientHttp.PutAsJsonAsync($"http://localhost:8080/api/activities/{a.IdActivity}", a);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - Activité avec l'id " + a.IdActivity + " mis à jour avec succès");
}
else
{
Console.WriteLine("API - Problème mise à jour Activité");
}
}
}
}

@ -0,0 +1,140 @@
using System;
using Blazored.LocalStorage;
using HeartTrack.Models;
using HeartTrack.Services.ActivityDataService;
using Microsoft.AspNetCore.Components;
namespace HeartTrack.Services.ActivityDataServiceFactice
{
public class ActivityDataServiceFactice : IActivityDataService
{
[Inject]
private HttpClient _clientHttp { get; set; }
[Inject]
public ILocalStorageService _localStorage { get; set; }
[Inject]
public NavigationManager _navigationManager { get; set; }
private String EmplacementLocalStorage { get; set; }
private String EmplacementJson { get; set; }
public ActivityDataServiceFactice(HttpClient clientHttp, ILocalStorageService localStorage, NavigationManager navigationManager)
{
this._clientHttp = clientHttp;
this._localStorage = localStorage;
this._navigationManager = navigationManager;
this.EmplacementLocalStorage = "activitiesData";
this.EmplacementJson = $"{_navigationManager.BaseUri}data/fake-activities.json";
}
public async Task AddActivity(Activity a)
{
List<Activity> data = await getAllActivities();
data.Add(a);
await this.SaveAllActivities(data);
}
public async Task<Activity> getActivityById(int id)
{
Console.WriteLine("Passage dans le getFromPseudo...");
List<Activity> activities = await getAllActivities();
Activity? temp = null;
foreach (Activity a in activities)
{
if (a.IdActivity == id)
{
temp = a;
}
}
return temp;
}
public async Task<List<Activity>> getAllActivities()
{
List<Activity> lActivities = new List<Activity>();
lActivities = await this.getActivitiesFromLocalStorage();
if(lActivities.Count == 0)
{
lActivities = await this.getActivitiesFromJson(this.EmplacementJson);
await this.saveActivitiesLocalStorage(lActivities);
}
return lActivities;
}
private async Task<List<Activity>> getActivitiesFromJson(String cheminVersJson)
{
List<Activity> activitiesDeserialiser = new List<Activity>();
var data = await _clientHttp.GetFromJsonAsync<Activity[]>(cheminVersJson);
activitiesDeserialiser = data.ToList();
return activitiesDeserialiser;
}
private async Task<List<Activity>> getActivitiesFromLocalStorage()
{
List<Activity> activitiesFromLocalStorage = null;
var data = await _localStorage.GetItemAsync<Activity[]>(EmplacementLocalStorage);
if (data == null)
{
activitiesFromLocalStorage = new List<Activity>();
}
else
{
activitiesFromLocalStorage = data.ToList();
}
return activitiesFromLocalStorage;
}
public async Task RemoveActivity(Activity a)
{
List<Activity> data = await getAllActivities();
int index = -1;
foreach (Activity temp in data)
{
if (temp.IdActivity == a.IdActivity)
{
index = data.IndexOf(temp);
}
}
if (index != -1)
{
data.RemoveAt(index);
}
await this.SaveAllActivities(data);
data = await this.getAllActivities();
}
public async Task SaveAllActivities(List<Activity> list)
{
await this.saveActivitiesLocalStorage(list);
}
private async Task saveActivitiesLocalStorage(List<Activity> lActivities)
{
await _localStorage.SetItemAsync(this.EmplacementLocalStorage, lActivities);
}
public async Task UpdateActivity(Activity a)
{
await this.RemoveActivity(a);
await this.AddActivity(a);
}
}
}

@ -0,0 +1,21 @@
using System;
using HeartTrack.Models;
namespace HeartTrack.Services.ActivityDataService
{
public interface IActivityDataService
{
public Task<List<Activity>> getAllActivities();
public Task SaveAllActivities(List<Activity> list);
public Task AddActivity(Activity u);
public Task RemoveActivity(Activity u);
public Task UpdateActivity(Activity u);
public Task<Activity> getActivityById(int id);
}
}

@ -0,0 +1,22 @@
using System;
using HeartTrack.Models;
namespace HeartTrack.Services.TicketDataService
{
public interface ITicketDataService
{
public Task<List<Ticket>> getAllTickets();
public Task SaveAllTickets(List<Ticket> list);
public Task AddTicket(Ticket t);
public Task RemoveTicket(Ticket t);
public Task UpdateTicket(Ticket t);
public Task<Ticket> getTicketById(int id);
public Task Close(int Id);
}
}

@ -0,0 +1,91 @@
using System;
using HeartTrack.Models;
using Microsoft.AspNetCore.Components;
namespace HeartTrack.Services.TicketDataService
{
public class TicketDataServiceAPI : ITicketDataService
{
[Inject]
private HttpClient _clientHttp { get; set; }
public TicketDataServiceAPI(HttpClient clientHttp)
{
this._clientHttp = clientHttp;
}
public async Task AddTicket(Ticket t)
{
HttpResponseMessage response = await _clientHttp.PostAsJsonAsync("http://localhost:8080/api/tickets", t);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - Ticket avec l'id " + t.Id + " ajouté avec succès");
}
else
{
Console.WriteLine("API - Problème ajout Ticket");
}
}
public async Task<Ticket> getTicketById(int id)
{
Ticket Ticket = await _clientHttp.GetFromJsonAsync<Ticket>("http://localhost:8080/api/tickets/{id}");
return Ticket;
}
public async Task<List<Ticket>> getAllTickets()
{
List<Ticket> lTickets = await _clientHttp.GetFromJsonAsync<List<Ticket>>("http://localhost:8080/api/tickets");
return lTickets;
}
public async Task RemoveTicket(Ticket t)
{
HttpResponseMessage response = await _clientHttp.DeleteAsync($"http://localhost:8080/api/tickets/{t.Id}");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - Ticket avec l'id " + t.Id + " supprimé avec succès");
}
else
{
Console.WriteLine("API - Problème suppression Ticket");
}
}
public async Task SaveAllTickets(List<Ticket> list)
{
HttpResponseMessage response = await _clientHttp.PutAsJsonAsync("http://localhost:8080/api/tickets", list);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - List de tickets sauvegardé avec succès");
}
else
{
Console.WriteLine("API - Problème sauvegarde List de tickets");
}
}
public async Task UpdateTicket(Ticket t)
{
HttpResponseMessage response = await _clientHttp.PutAsJsonAsync($"http://localhost:8080/api/tickets/{t.Id}", t);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API - Ticket avec l'id " + t.Id + " mis à jour avec succès");
}
else
{
Console.WriteLine("API - Problème mise à jour Ticket");
}
}
public async Task Close(int id)
{
}
}
}

@ -0,0 +1,144 @@
using System;
using Blazored.LocalStorage;
using HeartTrack.Models;
using HeartTrack.Services.TicketDataService;
using Microsoft.AspNetCore.Components;
namespace HeartTrack.Services.TicketDataServiceFactice
{
public class TicketDataServiceFactice : ITicketDataService
{
[Inject]
private HttpClient _clientHttp { get; set; }
[Inject]
public ILocalStorageService _localStorage { get; set; }
[Inject]
public NavigationManager _navigationManager { get; set; }
private String EmplacementLocalStorage { get; set; }
private String EmplacementJson { get; set; }
public TicketDataServiceFactice(HttpClient clientHttp, ILocalStorageService localStorage, NavigationManager navigationManager)
{
this._clientHttp = clientHttp;
this._localStorage = localStorage;
this._navigationManager = navigationManager;
this.EmplacementLocalStorage = "ticketsData";
this.EmplacementJson = $"{_navigationManager.BaseUri}data/fake-tickets.json";
}
public async Task AddTicket(Ticket t)
{
List<Ticket> data = await getAllTickets();
data.Add(t);
await this.SaveAllTickets(data);
}
public async Task<Ticket> getTicketById(int id)
{
List<Ticket> tickets = await getAllTickets();
Ticket? temp = null;
foreach (Ticket t in tickets)
{
if (t.Id == id)
{
temp = t;
}
}
return temp;
}
public async Task<List<Ticket>> getAllTickets()
{
List<Ticket> lTickets = new List<Ticket>();
lTickets = await this.getTicketsFromLocalStorage();
if (lTickets.Count == 0)
{
lTickets = await this.getTicketsFromJson(this.EmplacementJson);
await this.saveTicketsLocalStorage(lTickets);
}
return lTickets;
}
private async Task<List<Ticket>> getTicketsFromJson(String cheminVersJson)
{
List<Ticket> TicketsDeserialiser = new List<Ticket>();
var data = await _clientHttp.GetFromJsonAsync<Ticket[]>(cheminVersJson);
TicketsDeserialiser = data.ToList();
return TicketsDeserialiser;
}
private async Task<List<Ticket>> getTicketsFromLocalStorage()
{
List<Ticket> TicketsFromLocalStorage = null;
var data = await _localStorage.GetItemAsync<Ticket[]>(EmplacementLocalStorage);
if (data == null)
{
TicketsFromLocalStorage = new List<Ticket>();
}
else
{
TicketsFromLocalStorage = data.ToList();
}
return TicketsFromLocalStorage;
}
public async Task RemoveTicket(Ticket t)
{
// Get the current data
var currentData = await _localStorage.GetItemAsync<List<Ticket>>("data");
// Get the item int the list
var item = currentData.FirstOrDefault(w => w.Id == t.Id);
// Delete item in
currentData.Remove(item);
// Save the data
await _localStorage.SetItemAsync("data", currentData);
}
public async Task SaveAllTickets(List<Ticket> list)
{
await this.saveTicketsLocalStorage(list);
}
private async Task saveTicketsLocalStorage(List<Ticket> lTickets)
{
await _localStorage.SetItemAsync(this.EmplacementLocalStorage, lTickets);
}
public async Task UpdateTicket(Ticket t)
{
await this.RemoveTicket(t);
await this.AddTicket(t);
}
public async Task Close(int Id)
{
// Get the current data
var currentData = await _localStorage.GetItemAsync<List<Ticket>>("data");
// Get the item int the list
var item = currentData.FirstOrDefault(w => w.Id == Id);
// Update item status
item.isCheck = true;
// Save the data
await _localStorage.SetItemAsync("data", currentData);
}
}
}

@ -1,42 +1,12 @@
@using System.Globalization
@inject NavigationManager NavigationManager
<p>
<label>
<select @bind="Culture">
@foreach (var culture in supportedCultures)
@foreach (var culture in @supportedCultures)
{
<option value="@culture">@culture.DisplayName</option>
}
</select>
</label>
</p>
@code
{
private CultureInfo[] supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("fr-FR")
};
private CultureInfo Culture
{
get => CultureInfo.CurrentCulture;
set
{
if (CultureInfo.CurrentUICulture == value)
{
return;
}
var culture = value.Name.ToLower(CultureInfo.InvariantCulture);
var uri = new Uri(this.NavigationManager.Uri).GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
var query = $"?culture={Uri.EscapeDataString(culture)}&" + $"redirectUri={Uri.EscapeDataString(uri)}";
// Redirect the user to the culture controller to set the cookie
this.NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
}
}
}

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Components;
using System.Globalization;
namespace HeartTrack.Shared
{
public partial class CultureSelector
{
[Inject]
private NavigationManager NavigationManager { get; set; }
private CultureInfo[] supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("fr-FR")
};
private CultureInfo Culture
{
get => CultureInfo.CurrentCulture;
set
{
if (CultureInfo.CurrentUICulture == value)
{
return;
}
var culture = value.Name.ToLower(CultureInfo.InvariantCulture);
var uri = new Uri(this.NavigationManager.Uri).GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
var query = $"?culture={Uri.EscapeDataString(culture)}&" + $"redirectUri={Uri.EscapeDataString(uri)}";
// Redirect the user to the culture controller to set the cookie
this.NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
}
}
public int getSizeCultures()
{
return supportedCultures.Length;
}
}
}

@ -1,4 +1,7 @@
@inherits LayoutComponentBase
<MudThemeProvider />
<MudDialogProvider />
<MudSnackbarProvider />
<PageTitle>HeartTrack</PageTitle>
@ -8,31 +11,21 @@
</div>
<main>
<div class="top-row px-4 auth">
<div class="container-fluid toggler-container">
<button title="Navigation menu" class="navbar-toggler custom-toggler" @onclick="ToggleNavMenu">
<span class="navbat-toggler-icon"></span>
</button>
</div>
@* Messages, notifs et pp compte à mettre *@
<div class="px-4">
<CultureSelector />
</div>
</div>
<MudAppBar Color="Color.Primary" Fixed="false">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" @onclick="ToggleNavMenu">
<span class="navbat-toggler-icon"></span>
</MudIconButton>
<MudSpacer />
<CultureSelector></CultureSelector>
<MudMenu Icon="@Icons.Material.Filled.AccountCircle" Color="Color.Inherit" Edge="Edge.End">
<MudMenuItem>Profile</MudMenuItem>
<MudButton type="button" class="btn btn-link ml-md-auto">Logout</MudButton>
</MudMenu>
</MudAppBar>
<article class="content px-4">
@Body
</article>
</main>
</div>
@code {
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

@ -0,0 +1,26 @@
using HeartTrack.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components;
namespace HeartTrack.Shared
{
public partial class MainLayout
{
[Inject]
public NavigationManager NavigationManager { get; set; }
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
public void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
public bool getCollapseNavMenu()
{
return this.collapseNavMenu;
}
}
}

@ -11,30 +11,16 @@
<span class="oi oi-home" aria-hidden="true"></span> @Localizer["Global"]
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="tickets">
<span class="oi oi-plus" aria-hidden="true"></span> @Localizer["Ticket"]
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="reports">
<span class="oi oi-list-rich" aria-hidden="true"></span> @Localizer["Report"]
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="activities">
<span class="oi oi-list-rich" aria-hidden="true"></span> @Localizer["Activity"]
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="banned-users">
<span class="oi oi-list-rich" aria-hidden="true"></span> @Localizer["Ban"]
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="tokens">
<span class="oi oi-list-rich" aria-hidden="true"></span> @Localizer["Tokens"]
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="tickets">
<span class="oi oi-plus" aria-hidden="true"></span> @Localizer["Ticket"]
</NavLink>
</div>
</nav>
</div>

@ -7,6 +7,6 @@ namespace HeartTrack.Shared
public partial class NavMenu
{
[Inject]
public IStringLocalizer<Tokens> Localizer { get; set; }
public IStringLocalizer<NavMenu> Localizer { get; set; }
}
}

@ -1,17 +0,0 @@
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2149017">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}

@ -0,0 +1,25 @@
using HeartTrack.Controllers;
using Microsoft.AspNetCore.Mvc;
using Xunit;
namespace HeartTrack.Tests.Controller
{
public class CultureControllerTests
{
[Fact]
public void IsCultureControllerInstanceOfCultureController()
{
var culture = new CultureController();
Assert.IsType<CultureController>(culture);
}
[Fact]
public void IsSetCultureRedirecting()
{
var culture = new CultureController();
Assert.IsAssignableFrom<IActionResult>(culture.SetCulture("",""));
}
}
}

@ -0,0 +1,37 @@
using HeartTrack.Models;
using Xunit;
namespace HeartTrack.Tests.Models
{
public class ActivityTests
{
[Fact]
public void IsActivityInstanceOfActivity()
{
var activity = new Activity();
Assert.IsType<Activity>(activity);
}
[Fact]
public void HasActivityAttrivutesRightInstance()
{
var activity = new Activity();
Assert.IsType<int>(activity.IdActivity);
Assert.IsType<string>(activity.Type);
Assert.IsType<DateOnly>(activity.Date);
Assert.IsType<DateOnly>(activity.StartTime);
Assert.IsType<DateOnly>(activity.EndTime);
Assert.IsType<int>(activity.EffortRessenti);
Assert.IsType<float>(activity.Variability);
Assert.IsType<float>(activity.Variance);
Assert.IsType<float>(activity.StandardDeviation);
Assert.IsType<float>(activity.Average);
Assert.IsType<int>(activity.Maximum);
Assert.IsType<int>(activity.Minimum);
Assert.IsType<float>(activity.AvrTemperature);
Assert.IsType<bool>(activity.HasAutoPause);
}
}
}

@ -0,0 +1,42 @@
using Blazorise;
using HeartTrack.Models;
using Xunit;
namespace HeartTrack.Tests.Models
{
public class ReportTests
{
[Fact]
public void IsReportInstanceOfReport()
{
var report = new Report();
Assert.IsType<Report>(report);
}
[Fact]
public void HasReportAttributeRightInstace()
{
var report = new Report();
Assert.IsType<int>(report.Id);
Assert.IsType<string>(report.Username);
Assert.IsType<string>(report.ReportedUser);
Assert.IsType<string>(report.Raison);
Assert.IsType<string>(report.Description);
Assert.IsType<Image>(report.Image);
}
[Fact]
public void IsReportModelValidate()
{
var report = new ReportModel();
Assert.True(report.Id < 2500000);
Assert.True(report.Username.Length < 50 && report.Username.Length>0);
Assert.True(report.ReportedUser.Length < 50 && report.ReportedUser.Length > 0);
Assert.True(report.Raison.Length < 150 && report.Raison.Length > 0);
Assert.True(report.Description.Length < 500 && report.Description.Length > 0);
}
}
}

@ -0,0 +1,40 @@
using HeartTrack.Models;
using Xunit;
namespace HeartTrack.Tests.Models
{
public class TicketTests
{
[Fact]
public void IsTicketInstanceOfTicket()
{
var ticket = new Ticket();
Assert.IsType<Ticket>(ticket);
}
[Fact]
public void HasTicketAttributeRightInstance()
{
var ticket = new Ticket();
Assert.IsType<int>(ticket.Id);
Assert.IsType<string>(ticket.Username);
Assert.IsType<string>(ticket.Contexte);
Assert.IsType<string>(ticket.Description);
Assert.IsType<Boolean>(ticket.isCheck);
}
[Fact]
public void IsTicketModelValidate()
{
var ticket = new TicketModel();
Assert.True(ticket.Id < 2500000);
Assert.True(ticket.Username.Length > 0 && ticket.Username.Length < 50);
Assert.True(ticket.Contexte.Length > 0 && ticket.Contexte.Length < 25);
Assert.True(ticket.Description.Length > 0 && ticket.Description.Length < 500);
Assert.True(ticket.isCheck);
}
}
}

@ -0,0 +1,52 @@
using Xunit;
using HeartTrack.Models;
namespace HeartTrack.Tests.Models
{
public class UserTests
{
[Fact]
public void IsUserInstanceOfUser()
{
var user = new User();
Assert.IsType<User>(user);
}
[Fact]
public void HasUserAttributeRightInstance()
{
var user = new User();
Assert.IsType<int>(user.Id);
Assert.IsType<string>(user.Username);
Assert.IsType<string>(user.Nom);
Assert.IsType<string>(user.Prenom);
Assert.IsType<string>(user.Email);
Assert.IsType<string>(user.Password);
Assert.IsType<string>(user.Sexe);
Assert.IsType<float>(user.Taille);
Assert.IsType<float>(user.Poids);
Assert.IsType<DateTime>(user.BirthDate);
Assert.IsType<Boolean>(user.isBan);
}
[Fact]
public void IsUserModelValidate()
{
var user = new UserModel();
Assert.NotNull(user.Username);
Assert.NotNull(user.FirstName);
Assert.NotNull(user.LastName);
Assert.NotNull(user.Email);
Assert.NotNull(user.Password);
Assert.NotNull(user.Sexe);
Assert.True(user.Id<2500000);
Assert.True(user.Username.Length >= 0 && user.Username.Length < 50);
Assert.True(user.FirstName.Length >= 0 && user.FirstName.Length < 50);
Assert.True(user.LastName.Length >= 0 && user.LastName.Length < 25);
}
}
}

@ -0,0 +1,24 @@
using HeartTrack.Shared;
using Xunit;
namespace HeartTrack.Tests.Shared
{
public class CultureSelectorTests
{
[Fact]
public void IsCultureSelectorInstanceOfCultureSelector()
{
var culture = new CultureSelector();
Assert.IsType<CultureSelector>(culture);
}
[Fact]
public void HasCultureEnoughLanguages()
{
var culture = new CultureSelector();
Assert.Equal(2, culture.getSizeCultures());
}
}
}

@ -0,0 +1,25 @@
using HeartTrack.Shared;
using Xunit;
namespace HeartTrack.Tests.Shared
{
public class MainLayoutTests
{
[Fact]
public void IsMainLayoutInstanceOfMainLayout()
{
var main = new MainLayout();
Assert.IsType<MainLayout>(main);
}
[Fact]
public void StateChangeOnToggleNavMenu()
{
var main = new MainLayout();
Assert.True(main.getCollapseNavMenu());
main.ToggleNavMenu();
Assert.False(main.getCollapseNavMenu());
}
}
}

@ -9,3 +9,4 @@
@using HeartTrack
@using HeartTrack.Shared
@using Blazorise.DataGrid
@using MudBlazor

@ -0,0 +1,186 @@
[
{
"id": 1,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 2,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 3,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 4,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 5,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 6,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 7,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 8,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 9,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description",
},
{
"id": 10,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 11,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 12,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 13,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 14,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 15,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 16,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 17,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 18,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 19,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 20,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 21,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 22,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
},
{
"id": 23,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"contexte": "Jvais dire wallah",
"description": "Wallah c`est la description"
}
]

@ -1,362 +0,0 @@
[
{
"id": 1,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2019-10-20"
},
{
"id": 2,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2022-01-06"
},
{
"id": 3,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2015-02-05"
},
{
"id": 4,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2015-04-20"
},
{
"id": 5,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2020-02-22"
},
{
"id": 6,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2018-03-11"
},
{
"id": 7,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2019-04-21"
},
{
"id": 8,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2023-05-17"
},
{
"id": 9,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2021-06-16"
},
{
"id": 10,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2014-09-24"
},
{
"id": 11,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2015-02-28"
},
{
"id": 12,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2022-09-26"
},
{
"id": 13,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2019-06-19"
},
{
"id": 14,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2022-09-25"
},
{
"id": 15,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2017-10-21"
},
{
"id": 16,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2021-02-05"
},
{
"id": 17,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2018-06-14"
},
{
"id": 18,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2017-02-09"
},
{
"id": 19,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2020-09-16"
},
{
"id": 20,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2020-01-31"
},
{
"id": 21,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2020-04-11"
},
{
"id": 22,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2016-10-30"
},
{
"id": 23,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2014-09-20"
},
{
"id": 24,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2019-01-21"
},
{
"id": 25,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2017-10-28"
},
{
"id": 26,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2021-11-05"
},
{
"id": 27,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2014-08-06"
},
{
"id": 28,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2020-06-03"
},
{
"id": 29,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2018-07-01"
},
{
"id": 30,
"username": "johndoe",
"nom": "Doe",
"prenom": "John",
"email": "john.doe@example.com",
"password": "password123",
"sexe": "male",
"taille": 1.76,
"poids": 56.3,
"birthdate": "2021-01-22"
}
]
Loading…
Cancel
Save