Add new Feature(Edit, Update, Service and globalization)

pull/1/head
Victor Perez NGOUNOU 2 years ago
parent cdf25d55d6
commit 81b2650d53

@ -1,12 +1,14 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
<CascadingBlazoredModal>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingBlazoredModal>

@ -0,0 +1,48 @@
using TP_Blazor.Models;
namespace TP_Blazor.Factories;
public static class ItemFactory
{
public static ItemModel ToModel(Item item, byte[] imageContent)
{
return new ItemModel
{
Id = item.Id,
DisplayName = item.DisplayName,
Name = item.Name,
RepairWith = item.RepairWith,
EnchantCategories = item.EnchantCategories,
MaxDurability = item.MaxDurability,
StackSize = item.StackSize,
ImageContent = imageContent
};
}
public static Item Create(ItemModel model)
{
return new Item
{
Id = model.Id,
DisplayName = model.DisplayName,
Name = model.Name,
RepairWith = model.RepairWith,
EnchantCategories = model.EnchantCategories,
MaxDurability = model.MaxDurability,
StackSize = model.StackSize,
CreatedDate = DateTime.Now
};
}
public static void Update(Item item, ItemModel model)
{
item.DisplayName = model.DisplayName;
item.Name = model.Name;
item.RepairWith = model.RepairWith;
item.EnchantCategories = model.EnchantCategories;
item.MaxDurability = model.MaxDurability;
item.StackSize = model.StackSize;
item.UpdatedDate = DateTime.Now;
}
}

@ -0,0 +1,10 @@
<div class="simple-form">
<p>
Are you sure you want to delete @item.DisplayName ?
</p>
<button @onclick="ConfirmDelete" class="btn btn-primary">Delete</button>
<button @onclick="Cancel" class="btn btn-secondary">Cancel</button>
</div>

@ -0,0 +1,37 @@
using Blazored.Modal;
using Blazored.Modal.Services;
using Microsoft.AspNetCore.Components;
using TP_Blazor.Models;
using TP_Blazor.Services;
namespace TP_Blazor.Modals;
public partial class DeleteConfirmation
{
[CascadingParameter]
public BlazoredModalInstance ModalInstance { get; set; }
[Inject]
public IDataService DataService { get; set; }
[Parameter]
public int Id { get; set; }
private Item item = new Item();
protected override async Task OnInitializedAsync()
{
// Get the item
item = await DataService.GetById(Id);
}
void ConfirmDelete()
{
ModalInstance.CloseAsync(ModalResult.Ok(true));
}
void Cancel()
{
ModalInstance.CancelAsync();
}
}

@ -0,0 +1,81 @@
@page "/edit/{Id:int}"
<h3>Edit</h3>
<EditForm Model="@itemModel" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
<label for="display-name">
Display name:
<InputText id="display-name" @bind-Value="itemModel.DisplayName" />
</label>
</p>
<p>
<label for="name">
Name:
<InputText id="name" @bind-Value="itemModel.Name" />
</label>
</p>
<p>
<label for="stack-size">
Stack size:
<InputNumber id="stack-size" @bind-Value="itemModel.StackSize" />
</label>
</p>
<p>
<label for="max-durability">
Max durability:
<InputNumber id="max-durability" @bind-Value="itemModel.MaxDurability" />
</label>
</p>
<p>
Enchant categories:
<div>
@foreach (var item in enchantCategories)
{
<label>
<input type="checkbox" @onchange="@(e => OnEnchantCategoriesChange(item, e.Value))" checked="@(itemModel.EnchantCategories.Contains(item) ? "checked" : null)" />@item
</label>
}
</div>
</p>
<p>
Repair with:
<div>
@foreach (var item in repairWith)
{
<label>
<input type="checkbox" @onchange="@(e => OnRepairWithChange(item, e.Value))" checked="@(itemModel.RepairWith.Contains(item) ? "checked" : null)" />@item
</label>
}
</div>
</p>
<p>
<label>
Current Item image:
@if (File.Exists($"{WebHostEnvironment.WebRootPath}/images/{itemModel.Name}.png"))
{
<img src="images/@(itemModel.Name).png" class="img-thumbnail" title="@itemModel.DisplayName" alt="@itemModel.DisplayName" style="max-width: 150px"/>
}
else
{
<img src="images/default.png" class="img-thumbnail" title="@itemModel.DisplayName" alt="@itemModel.DisplayName" style="max-width: 150px"/>
}
</label>
</p>
<p>
<label>
Item image:
<InputFile OnChange="@LoadImage" accept=".png" />
</label>
</p>
<p>
<label>
Accept Condition:
<InputCheckbox @bind-Value="itemModel.AcceptCondition" />
</label>
</p>
<button type="submit">Submit</button>
</EditForm>

@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using TP_Blazor.Factories;
using TP_Blazor.Models;
using TP_Blazor.Services;
namespace TP_Blazor.Pages;
public partial class Edit
{
[Parameter]
public int Id { get; set; }
private List<string> enchantCategories = new List<string>() { "armor", "armor_head", "armor_chest", "weapon", "digger", "breakable", "vanishable" };
/// <summary>
/// The current item model
/// </summary>
private ItemModel itemModel = new()
{
EnchantCategories = new List<string>(),
RepairWith = new List<string>()
};
/// <summary>
/// The default repair with.
/// </summary>
private List<string> repairWith = new List<string>() { "oak_planks", "spruce_planks", "birch_planks", "jungle_planks", "acacia_planks", "dark_oak_planks", "crimson_planks", "warped_planks" };
[Inject]
public IDataService DataService { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IWebHostEnvironment WebHostEnvironment { get; set; }
protected override async Task OnInitializedAsync()
{
var item = await DataService.GetById(Id);
var fileContent = await File.ReadAllBytesAsync($"{WebHostEnvironment.WebRootPath}/images/default.png");
if (File.Exists($"{WebHostEnvironment.WebRootPath}/images/{itemModel.Name}.png"))
{
fileContent = await File.ReadAllBytesAsync($"{WebHostEnvironment.WebRootPath}/images/{item.Name}.png");
}
// Set the model with the item
itemModel =ItemFactory.ToModel(item, fileContent);
}
private async void HandleValidSubmit()
{
await DataService.Update(Id, itemModel);
NavigationManager.NavigateTo("list");
}
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);
}
}
}

@ -2,7 +2,11 @@
@using TP_Blazor.Models
<h3>Liste</h3>
@*@if (items!=null) { <table class="table"> <thead> <tr>
@*@if (items!=null)
{
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Display Name</th>
<th>Stack Size</th>
@ -10,7 +14,9 @@
<th>Enchant Categories</th>
<th>Repair With</th>
<th>Created Date</th>
</tr> </thead> <tbody>
</tr>
</thead>
<tbody>
@foreach (var item in items)
{
<tr>
@ -23,8 +29,24 @@
<td>@item.CreatedDate.ToShortDateString()</td>
</tr>
}
</tbody> </table> }*@ <DataGrid TItem="Item" Data="@items" ReadData="@OnReadData" TotalItems="@totalItem" ShowPager PageSize="10" Responsive >
</tbody>
</table>
}*@
<div>
<NavLink class="btn btn-primary" href="add" Match="NavLinkMatch.All">
<i class="fa fa-plus"></i> Ajouter
</NavLink>
</div>
<DataGrid TItem="Item" Data="@items" ReadData="@OnReadData" TotalItems="@totalItem" ShowPager PageSize="10" Responsive>
<DataGridColumn TItem="Item" Field="@nameof(Item.Id)" Caption="#" />
<DataGridColumn TItem="Item" Field="@nameof(Item.Id)" Caption="Image">
<DisplayTemplate>
<img src="images/@(context.Name).png" class="img-thumbnail" title="@context.DisplayName" alt="@context.DisplayName" style="max-width: 150px" />
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="Item" Field="@nameof(Item.DisplayName)" Caption="Display name" />
<DataGridColumn TItem="Item" Field="@nameof(Item.StackSize)" Caption="Stack size" />
<DataGridColumn TItem="Item" Field="@nameof(Item.MaxDurability)" Caption="Maximum durability" />
@ -39,4 +61,10 @@
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="Item" Field="@nameof(Item.CreatedDate)" Caption="Created date" DisplayFormat="{0:d}" DisplayFormatProvider="@System.Globalization.CultureInfo.GetCultureInfo("fr-FR")" />
<DataGridColumn TItem="Item" Field="@nameof(Item.Id)" Caption="Action">
<DisplayTemplate>
<a href="Edit/@(context.Id)" class="btn btn-primary"><i class="fa fa-edit"></i> Editer</a>
<button type="button" class="btn btn-primary" @onclick="() => OnDelete(context.Id)"><i class="fa fa-trash"></i> Supprimer</button>
</DisplayTemplate>
</DataGridColumn>
</DataGrid>

@ -2,6 +2,12 @@
using Blazorise.DataGrid;
using Microsoft.AspNetCore.Components;
using TP_Blazor.Models;
using Blazored;
using Blazored.LocalStorage;
using Blazored.Modal;
using Blazored.Modal.Services;
using TP_Blazor.Modals;
using TP_Blazor.Services;
namespace TP_Blazor.Pages;
@ -15,31 +21,73 @@ public partial class List
private int totalItem;
[Inject]
public HttpClient Http { get; set; }
public HttpClient HttpClient { get; set; }
[Inject]
public IDataService DataService { get; set; }
[CascadingParameter]
public IModalService Modal { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
IWebHostEnvironment WebHostEnvironment { get; set; }
[Inject]
public ILocalStorageService LocalStorage { get; set; }
private async Task OnReadData(DataGridReadDataEventArgs<Item> e)
{
if (e.CancellationToken.IsCancellationRequested)
{
return;
}
var response = (await Http.GetFromJsonAsync<Item[]>($"{NavigationManager.BaseUri}fake-data.json")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList();
//var response = (await HttpClient.GetFromJsonAsync<Item[]>($"{NavigationManager.BaseUri}fake-data.json")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList();
if (!e.CancellationToken.IsCancellationRequested)
{
totalItem = (await Http.GetFromJsonAsync<List<Item>>($"{NavigationManager.BaseUri}fake-data.json")).Count;
items = new List<Item>(response); // an actual data for the current page
//totalItem = (await HttpClient.GetFromJsonAsync<List<Item>>($"{NavigationManager.BaseUri}fake-data.json")).Count;
//items = new List<Item>(response); // an actual data for the current page
items = await DataService.List(e.Page, e.PageSize);
totalItem = await DataService.Count();
}
}
protected override void OnAfterRender(bool firstRender)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
base.OnAfterRender(firstRender);
if (!firstRender)
{
return;
}
var currentData = await LocalStorage.GetItemAsync<Item[]>("data");
if (currentData==null)
{
var originalData = HttpClient.GetFromJsonAsync<Item[]>($"{NavigationManager.BaseUri}fake-data.json").Result;
await LocalStorage.SetItemAsync("data", originalData);
}
}
private async void OnDelete(int id)
{
var parameters = new ModalParameters();
parameters.Add(nameof(Item.Id), id);
var modal = Modal.Show<DeleteConfirmation>("Delete Confirmation", parameters);
var result = await modal.Result;
if (result.Cancelled)
{
return;
}
await DataService.Delete(id);
// Reload the page
NavigationManager.NavigateTo("list", true);
}
}

@ -16,6 +16,7 @@
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css">
<link rel="stylesheet" href="_content/Blazorise.Icons.Material/blazorise.icons.material.css" />
<link href="_content/Blazored.Modal/blazored-modal.css" rel="stylesheet" />
<link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
<link href="_content/Blazorise.Bootstrap/blazorise.bootstrap.css" rel="stylesheet" />
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
@ -34,6 +35,7 @@
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
<script src="_framework/blazor.server.js"></script>
<script src="_content/Blazored.Modal/blazored.modal.js"></script>
</body>
</html>

@ -1,3 +1,4 @@
using System.Globalization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using TP_Blazor.Data;
@ -6,6 +7,10 @@ using Blazorise.Icons.FontAwesome;
using Microsoft.Extensions.DependencyInjection;
using Blazorise.Bootstrap;
using Blazored.LocalStorage;
using TP_Blazor.Services;
using Blazored.Modal;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
@ -16,8 +21,18 @@ builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddHttpClient();
builder.Services.AddBlazorise()
.AddBootstrapProviders()
.AddBlazoredLocalStorage()
.AddFontAwesomeIcons();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddBlazoredModal();
builder.Services.AddControllers();
builder.Services.AddLocalization(opt=>{opt.ResourcesPath="Resources";});
builder.Services.Configure<RequestLocalizationOptions>(option =>
{
option.DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US"));
option.SupportedCultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("fr-FR") };
option.SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("fr-FR") };
});
builder.Services.AddScoped<IDataService, DataLocalService>();
var app = builder.Build();
@ -32,6 +47,18 @@ app.UseStaticFiles();
app.UseRouting();
var options =((IApplicationBuilder)app).ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
if (options?.Value != null)
{
app.UseRequestLocalization(options.Value);
}
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

@ -0,0 +1,134 @@
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components;
using TP_Blazor.Factories;
using TP_Blazor.Models;
namespace TP_Blazor.Services;
public class DataLocalService:IDataService
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorageService;
private readonly NavigationManager _navigationManager;
private readonly IWebHostEnvironment _webHostEnvironment;
public DataLocalService(HttpClient httpClient, ILocalStorageService localStorageService, NavigationManager navigationManager, IWebHostEnvironment webHostEnvironment)
{
_httpClient = httpClient;
_localStorageService = localStorageService;
_navigationManager = navigationManager;
_webHostEnvironment = webHostEnvironment;
}
public async Task Add(ItemModel item)
{
var currentItems = await _localStorageService.GetItemAsync<List<Item>>("data");
item.Id = currentItems.Max(s=>s.Id)+1;
// currentItems.Add(new Item
// {
// Id = item.Id,
// Name = item.Name,
// DisplayName = item.DisplayName,
// RepairWith = item.RepairWith,
// EnchantCategories = item.EnchantCategories,
// MaxDurability = item.MaxDurability,
// StackSize = item.StackSize,
// CreatedDate = DateTime.Now
// });
currentItems.Add(ItemFactory.Create(item));
var imagePathsInfo = new DirectoryInfo(Path.Combine($"{_webHostEnvironment.ContentRootPath}/images"));
if (!imagePathsInfo.Exists)
{
imagePathsInfo.Create();
}
var fileName = new FileInfo($"{imagePathsInfo}/{item.Name}.png");
await File.WriteAllBytesAsync(fileName.FullName, item.ImageContent);
await _localStorageService.SetItemAsync("data", currentItems);
}
public async Task<int> Count()
{
var currentItems = _localStorageService.GetItemAsync<Item[]>("data");
if (currentItems== null)
{
var originalItems = _httpClient.GetFromJsonAsync<Item[]>($"f{_navigationManager.BaseUri}ake-data.json");
await _localStorageService.SetItemAsync("data", originalItems);
}
return (await _localStorageService.GetItemAsync<Item[]>("data")).Length;
}
public async Task<List<Item>> List(int page, int pageSize)
{
var currentItems = _localStorageService.GetItemAsync<Item[]>("data");
if (currentItems == null)
{
var originalItems = _httpClient.GetFromJsonAsync<Item[]>($"f{_navigationManager.BaseUri}ake-data.json");
_localStorageService.SetItemAsync("data", originalItems);
}
return (await _localStorageService.GetItemAsync<Item[]>("data")).Skip((page-1)*pageSize).Take(pageSize).ToList();
}
public async Task<Item> GetById(int id)
{
var currentItems =await _localStorageService.GetItemAsync<List<Item>>("data");
var item = currentItems.FirstOrDefault(s => s.Id == id);
if (item == null)
{
throw new Exception($"Item with id {id} not found");
}
return item;
}
public async Task Update(int id, ItemModel item)
{
var currentItems = await _localStorageService.GetItemAsync<List<Item>>("data");
var itemToUpdate = currentItems.FirstOrDefault(s => s.Id == id);
if (itemToUpdate == null)
{
throw new Exception($"Item with id {id} not found");
}
var imagePathsInfo = new DirectoryInfo($"{_webHostEnvironment.ContentRootPath}/images");
if (!imagePathsInfo.Exists)
{
imagePathsInfo.Create();
}
if (itemToUpdate.Name != item.Name)
{
var oldFileName = new FileInfo($"{imagePathsInfo}/{itemToUpdate.Name}.png");
if (oldFileName.Exists)
{
oldFileName.Delete();
}
}
var fileName = new FileInfo($"{imagePathsInfo}/{item.Name}.png");
await File.WriteAllBytesAsync(fileName.FullName, item.ImageContent);
// itemToUpdate.Name = item.Name;
// itemToUpdate.DisplayName = item.DisplayName;
// itemToUpdate.RepairWith = item.RepairWith;
// itemToUpdate.EnchantCategories = item.EnchantCategories;
// itemToUpdate.MaxDurability = item.MaxDurability;
// itemToUpdate.StackSize = item.StackSize;
// itemToUpdate.UpdatedDate = DateTime.Now;
ItemFactory.Update(itemToUpdate, item);
await _localStorageService.SetItemAsync("data", currentItems);
}
public async Task Delete(int id)
{
var currentItems =await _localStorageService.GetItemAsync<List<Item>>("data");
var itemToDelete = currentItems.FirstOrDefault(s => s.Id == id);
currentItems.Remove(itemToDelete);
var imagePathsInfo = new DirectoryInfo($"{_webHostEnvironment.ContentRootPath}/images");
var fileName = new FileInfo($"{imagePathsInfo}/{itemToDelete.Name}.png");
if (fileName.Exists)
{
File.Delete(fileName.FullName);
}
await _localStorageService.SetItemAsync("data", currentItems);
}
}

@ -0,0 +1,13 @@
using TP_Blazor.Models;
namespace TP_Blazor.Services;
public interface IDataService
{
public Task Add(ItemModel item);
Task<int> Count();
Task<List<Item>> List(int page, int pageSize);
Task<Item> GetById(int id);
Task Update(int id,ItemModel item);
Task Delete(int id);
}

@ -16,10 +16,12 @@
<None Remove="Blazored.LocalStorage" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Blazored.Modal" Version="7.1.0" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.1.5" />
<PackageReference Include="Blazorise.DataGrid" Version="1.1.5" />
<PackageReference Include="Blazorise.Bootstrap" Version="1.1.5" />
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />

@ -9,3 +9,5 @@
@using TP_Blazor
@using TP_Blazor.Shared
@using Blazorise.DataGrid
@using Blazored.Modal
@using Blazored.Modal.Services

Loading…
Cancel
Save