From 0d0dab12636b8c8e2447baf483e8311127984ed2 Mon Sep 17 00:00:00 2001 From: emkartal1 Date: Mon, 5 Dec 2022 15:07:59 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20des=20List,=20Des=20fonctionnalit=C3=A9?= =?UTF-8?q?s=20Ajouter,=20Suprimer,=20Editer...=20Pas=20de=20connexion=20?= =?UTF-8?q?=C3=A0=20l'API=20pour=20l'instant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CraftSharp/App.razor | 26 +- src/CraftSharp/CraftSharp.csproj | 4 + src/CraftSharp/Factories/ItemFactory.cs | 6 +- .../Modals/DeleteConfirmation.razor | 10 + .../Modals/DeleteConfirmation.razor.cs | 38 + src/CraftSharp/Models/Item.cs | 2 +- src/CraftSharp/Models/ItemModel.cs | 4 +- src/CraftSharp/Pages/Add.razor | 69 ++ src/CraftSharp/Pages/Add.razor.cs | 89 +++ src/CraftSharp/Pages/Crafting.razor | 5 + src/CraftSharp/Pages/Crafting.razor.cs | 32 + src/CraftSharp/Pages/Edit.razor | 88 +++ src/CraftSharp/Pages/Edit.razor.cs | 105 +++ src/CraftSharp/Pages/List.razor | 52 +- src/CraftSharp/Pages/List.razor.cs | 56 +- src/CraftSharp/Pages/_Layout.cshtml | 3 + src/CraftSharp/Program.cs | 16 +- src/CraftSharp/Services/DataLocalService.cs | 147 ++++ src/CraftSharp/Services/IDataService.cs | 3 +- src/CraftSharp/Shared/HeaderLayout.razor | 5 + src/CraftSharp/_Imports.razor | 3 + src/CraftSharp/wwwroot/Images/default.png | Bin 0 -> 2267 bytes src/CraftSharp/wwwroot/Images/sq.png | Bin 0 -> 8451 bytes src/CraftSharp/wwwroot/Images/weegee.png | Bin 0 -> 8451 bytes src/CraftSharp/wwwroot/fake-data.json | 690 ++++++------------ 25 files changed, 959 insertions(+), 494 deletions(-) create mode 100644 src/CraftSharp/Modals/DeleteConfirmation.razor create mode 100644 src/CraftSharp/Modals/DeleteConfirmation.razor.cs create mode 100644 src/CraftSharp/Pages/Add.razor create mode 100644 src/CraftSharp/Pages/Add.razor.cs create mode 100644 src/CraftSharp/Pages/Crafting.razor create mode 100644 src/CraftSharp/Pages/Crafting.razor.cs create mode 100644 src/CraftSharp/Pages/Edit.razor create mode 100644 src/CraftSharp/Pages/Edit.razor.cs create mode 100644 src/CraftSharp/Services/DataLocalService.cs create mode 100644 src/CraftSharp/wwwroot/Images/default.png create mode 100644 src/CraftSharp/wwwroot/Images/sq.png create mode 100644 src/CraftSharp/wwwroot/Images/weegee.png diff --git a/src/CraftSharp/App.razor b/src/CraftSharp/App.razor index 0643159..64264c7 100644 --- a/src/CraftSharp/App.razor +++ b/src/CraftSharp/App.razor @@ -1,12 +1,14 @@ - - - - - - - Not found - -

Sorry, there's nothing at this address.

-
-
-
+ + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
\ No newline at end of file diff --git a/src/CraftSharp/CraftSharp.csproj b/src/CraftSharp/CraftSharp.csproj index 8055e4b..85da056 100644 --- a/src/CraftSharp/CraftSharp.csproj +++ b/src/CraftSharp/CraftSharp.csproj @@ -25,7 +25,11 @@ + + + + diff --git a/src/CraftSharp/Factories/ItemFactory.cs b/src/CraftSharp/Factories/ItemFactory.cs index e0e91fd..a305db3 100644 --- a/src/CraftSharp/Factories/ItemFactory.cs +++ b/src/CraftSharp/Factories/ItemFactory.cs @@ -17,7 +17,7 @@ namespace CraftSharp.Factories StackSize = item.StackSize, ImageContent = imageContent, ImageBase64 = string.IsNullOrWhiteSpace(item.ImageBase64) ? Convert.ToBase64String(imageContent) : item.ImageBase64, - Rarity = item.Rarity + //Rarity = item.Rarity }; } @@ -34,7 +34,7 @@ namespace CraftSharp.Factories StackSize = model.StackSize, CreatedDate = DateTime.Now, ImageBase64 = Convert.ToBase64String(model.ImageContent), - Rarity = model.Rarity + //Rarity = model.Rarity }; } @@ -48,7 +48,7 @@ namespace CraftSharp.Factories item.StackSize = model.StackSize; item.UpdatedDate = DateTime.Now; item.ImageBase64 = Convert.ToBase64String(model.ImageContent); - item.Rarity = model.Rarity; + //item.Rarity = model.Rarity; } } diff --git a/src/CraftSharp/Modals/DeleteConfirmation.razor b/src/CraftSharp/Modals/DeleteConfirmation.razor new file mode 100644 index 0000000..b565665 --- /dev/null +++ b/src/CraftSharp/Modals/DeleteConfirmation.razor @@ -0,0 +1,10 @@ +
+ +

+ Are you sure you want to delete @item.DisplayName ? +

+ + + + +
\ No newline at end of file diff --git a/src/CraftSharp/Modals/DeleteConfirmation.razor.cs b/src/CraftSharp/Modals/DeleteConfirmation.razor.cs new file mode 100644 index 0000000..7775be9 --- /dev/null +++ b/src/CraftSharp/Modals/DeleteConfirmation.razor.cs @@ -0,0 +1,38 @@ +using Blazored.Modal; +using Blazored.Modal.Services; +using CraftSharp.Models; +using CraftSharp.Services; +using Microsoft.AspNetCore.Components; + +namespace CraftSharp.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(); + } + } +} diff --git a/src/CraftSharp/Models/Item.cs b/src/CraftSharp/Models/Item.cs index eaf12d8..48f3be7 100644 --- a/src/CraftSharp/Models/Item.cs +++ b/src/CraftSharp/Models/Item.cs @@ -13,6 +13,6 @@ public DateTime? UpdatedDate { get; set; } public string ImageBase64 { get; set; } - public Rarities Rarity { get; set; } + //public Rarities Rarity { get; set; } } } diff --git a/src/CraftSharp/Models/ItemModel.cs b/src/CraftSharp/Models/ItemModel.cs index a778e4a..88f7b6a 100644 --- a/src/CraftSharp/Models/ItemModel.cs +++ b/src/CraftSharp/Models/ItemModel.cs @@ -36,7 +36,7 @@ namespace CraftSharp.Models public string ImageBase64 { get; set; } - [Required] - public Rarities Rarity { get; set; } + /*[Required] + public Rarities Rarity { get; set; }*/ } } diff --git a/src/CraftSharp/Pages/Add.razor b/src/CraftSharp/Pages/Add.razor new file mode 100644 index 0000000..0ed932c --- /dev/null +++ b/src/CraftSharp/Pages/Add.razor @@ -0,0 +1,69 @@ +@page "/add" + +

Add

+ + + + + +

+ +

+

+ +

+

+ +

+

+ +

+

+ Enchant categories: +

+ @foreach (var item in enchantCategories) + { + + } +
+

+

+ Repair with: +

+ @foreach (var item in repairWith) + { + + } +
+

+

+ +

+

+ +

+ + +
\ No newline at end of file diff --git a/src/CraftSharp/Pages/Add.razor.cs b/src/CraftSharp/Pages/Add.razor.cs new file mode 100644 index 0000000..28d5d8e --- /dev/null +++ b/src/CraftSharp/Pages/Add.razor.cs @@ -0,0 +1,89 @@ +using Blazored.LocalStorage; +using CraftSharp.Models; +using CraftSharp.Services; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; + +namespace CraftSharp.Pages +{ + public partial class Add + { + /// + /// The default enchant categories. + /// + private List enchantCategories = new List() { "armor", "armor_head", "armor_chest", "weapon", "digger", "breakable", "vanishable" }; + + /// + /// The current item model + /// + private ItemModel itemModel = new() + { + EnchantCategories = new List(), + RepairWith = new List() + }; + + /// + /// The default repair with. + /// + private List repairWith = new List() { "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; } + + private async void HandleValidSubmit() + { + await DataService.Add(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); + } + } + } +} diff --git a/src/CraftSharp/Pages/Crafting.razor b/src/CraftSharp/Pages/Crafting.razor new file mode 100644 index 0000000..95529a1 --- /dev/null +++ b/src/CraftSharp/Pages/Crafting.razor @@ -0,0 +1,5 @@ +@page "/Crafting" + +
+ +
\ No newline at end of file diff --git a/src/CraftSharp/Pages/Crafting.razor.cs b/src/CraftSharp/Pages/Crafting.razor.cs new file mode 100644 index 0000000..0ce2f97 --- /dev/null +++ b/src/CraftSharp/Pages/Crafting.razor.cs @@ -0,0 +1,32 @@ +using CraftSharp.Components; +using CraftSharp.Models; +using CraftSharp.Services; +using Microsoft.AspNetCore.Components; + +namespace CraftSharp.Pages +{ + public partial class Crafting + { + [Inject] + public IDataService DataService { get; set; } + + public List Items { get; set; } = new List(); + + private List Recipes { get; set; } = new List(); + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + base.OnAfterRenderAsync(firstRender); + + if (!firstRender) + { + return; + } + + Items = await DataService.List(0, await DataService.Count()); + Recipes = await DataService.GetRecipes(); + + StateHasChanged(); + } + } +} diff --git a/src/CraftSharp/Pages/Edit.razor b/src/CraftSharp/Pages/Edit.razor new file mode 100644 index 0000000..029b141 --- /dev/null +++ b/src/CraftSharp/Pages/Edit.razor @@ -0,0 +1,88 @@ +@page "/edit/{Id:int}" + +

Edit

+ + + + + +

+ +

+

+ +

+

+ +

+

+ +

+

+ Enchant categories: +

+ @foreach (var item in enchantCategories) + { + + } +
+

+

+ Repair with: +

+ @foreach (var item in repairWith) + { + + } +
+

+

+ +

+

+ +

+

+ +

+

+ +

+ + +
\ No newline at end of file diff --git a/src/CraftSharp/Pages/Edit.razor.cs b/src/CraftSharp/Pages/Edit.razor.cs new file mode 100644 index 0000000..8ce6c2e --- /dev/null +++ b/src/CraftSharp/Pages/Edit.razor.cs @@ -0,0 +1,105 @@ +using CraftSharp.Factories; +using CraftSharp.Models; +using CraftSharp.Services; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; + +namespace CraftSharp.Pages +{ + public partial class Edit + { + [Parameter] + public int Id { get; set; } + + /// + /// The default enchant categories. + /// + private List enchantCategories = new List() { "armor", "armor_head", "armor_chest", "weapon", "digger", "breakable", "vanishable" }; + + /// + /// The current item model + /// + private ItemModel itemModel = new() + { + EnchantCategories = new List(), + RepairWith = new List() + }; + + /// + /// The default repair with. + /// + private List repairWith = new List() { "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"); + + // 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); + } + } + } +} diff --git a/src/CraftSharp/Pages/List.razor b/src/CraftSharp/Pages/List.razor index a2a357b..72bc9e9 100644 --- a/src/CraftSharp/Pages/List.razor +++ b/src/CraftSharp/Pages/List.razor @@ -1,8 +1,52 @@ @page "/list" -@using Models +@using CraftSharp.Models -

@Localizer["Title"]

+

List

-@code { +
+ + Ajouter + +
-} + + + + + @if (!string.IsNullOrWhiteSpace(context.ImageBase64)) + { + @context.DisplayName + } + else + { + @context.DisplayName + } + + + + + + + + @(string.Join(", ", ((Item)context).EnchantCategories)) + + + + + @(string.Join(", ", ((Item)context).RepairWith)) + + + + + + Editer + + + + \ No newline at end of file diff --git a/src/CraftSharp/Pages/List.razor.cs b/src/CraftSharp/Pages/List.razor.cs index 5bb544e..de707ae 100644 --- a/src/CraftSharp/Pages/List.razor.cs +++ b/src/CraftSharp/Pages/List.razor.cs @@ -1,12 +1,64 @@ -using Microsoft.AspNetCore.Components; +using Blazored.LocalStorage; +using Blazored.Modal; +using Blazored.Modal.Services; +using Blazorise.DataGrid; +using CraftSharp.Modals; +using CraftSharp.Models; +using CraftSharp.Services; +using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Localization; namespace CraftSharp.Pages { public partial class List { + private List items; + + private int totalItem; + + [Inject] + public NavigationManager NavigationManager { get; set; } + + [CascadingParameter] + public IModalService Modal { get; set; } [Inject] - public IStringLocalizer Localizer { get; set; } + public IDataService DataService { get; set; } + + [Inject] + public IWebHostEnvironment WebHostEnvironment { get; set; } + + private async Task OnReadData(DataGridReadDataEventArgs e) + { + if (e.CancellationToken.IsCancellationRequested) + { + return; + } + + if (!e.CancellationToken.IsCancellationRequested) + { + items = await DataService.List(e.Page, e.PageSize); + totalItem = await DataService.Count(); + } + } + private async void OnDelete(int id) + { + var parameters = new ModalParameters(); + parameters.Add(nameof(Item.Id), id); + + var modal = Modal.Show("Delete Confirmation", parameters); + var result = await modal.Result; + + if (result.Cancelled) + { + return; + } + + await DataService.Delete(id); + + // Reload the page + NavigationManager.NavigateTo("list", true); + } } } + diff --git a/src/CraftSharp/Pages/_Layout.cshtml b/src/CraftSharp/Pages/_Layout.cshtml index 50f7cc9..b8ac950 100644 --- a/src/CraftSharp/Pages/_Layout.cshtml +++ b/src/CraftSharp/Pages/_Layout.cshtml @@ -28,7 +28,10 @@ + + + diff --git a/src/CraftSharp/Program.cs b/src/CraftSharp/Program.cs index 5ced326..c9f4343 100644 --- a/src/CraftSharp/Program.cs +++ b/src/CraftSharp/Program.cs @@ -1,3 +1,8 @@ +using Blazored.LocalStorage; +using Blazorise; +using Blazored.Modal; +using Blazorise.Bootstrap; +using Blazorise.Icons.FontAwesome; using CraftSharp.Data; using CraftSharp.Services; using Microsoft.AspNetCore.Components; @@ -21,7 +26,16 @@ builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; }); builder.Services.AddHttpClient(); -builder.Services.AddScoped(); +builder.Services.AddBlazoredModal(); + +builder.Services + .AddBlazorise() + .AddBootstrapProviders() + .AddFontAwesomeIcons(); + +builder.Services.AddBlazoredLocalStorage(); + +builder.Services.AddScoped(); // Configure the localtization builder.Services.Configure(options => diff --git a/src/CraftSharp/Services/DataLocalService.cs b/src/CraftSharp/Services/DataLocalService.cs new file mode 100644 index 0000000..f248643 --- /dev/null +++ b/src/CraftSharp/Services/DataLocalService.cs @@ -0,0 +1,147 @@ +using Blazored.LocalStorage; +using CraftSharp.Components; +using CraftSharp.Factories; +using CraftSharp.Models; +using Microsoft.AspNetCore.Components; + +namespace CraftSharp.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(ItemModel 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(ItemFactory.Create(model)); + + // 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(); + } + public async Task GetById(int id) + { + // Get the current data + var currentData = await _localStorage.GetItemAsync>("data"); + + // Get the item int the list + var item = currentData.FirstOrDefault(w => w.Id == id); + + // Check if item exist + if (item == null) + { + throw new Exception($"Unable to found the item with ID: {id}"); + } + + return item; + } + + public async Task Update(int id, ItemModel model) + { + // Get the current data + var currentData = await _localStorage.GetItemAsync>("data"); + + // Get the item int the list + var item = currentData.FirstOrDefault(w => w.Id == id); + + // Check if item exist + if (item == null) + { + throw new Exception($"Unable to found the item with ID: {id}"); + } + + // Modify the content of the item + ItemFactory.Update(item, model); + + // Save the data + await _localStorage.SetItemAsync("data", currentData); + } + + public async Task Delete(int id) + { + // Get the current data + var currentData = await _localStorage.GetItemAsync>("data"); + + // Get the item int the list + var item = currentData.FirstOrDefault(w => w.Id == id); + + // Delete item in + currentData.Remove(item); + + // Save the data + await _localStorage.SetItemAsync("data", currentData); + } + + public Task> GetRecipes() + { + var items = new List + { + new CraftingRecipe + { + Give = new Item { DisplayName = "Diamond", Name = "diamond" }, + Have = new List> + { + new List { "dirt", "dirt", "dirt" }, + new List { "dirt", null, "dirt" }, + new List { "dirt", "dirt", "dirt" } + } + } + }; + + return Task.FromResult(items); + } + + } +} diff --git a/src/CraftSharp/Services/IDataService.cs b/src/CraftSharp/Services/IDataService.cs index c5a515c..2dd688d 100644 --- a/src/CraftSharp/Services/IDataService.cs +++ b/src/CraftSharp/Services/IDataService.cs @@ -14,8 +14,9 @@ namespace CraftSharp.Services Task Update(int id, ItemModel model); Task Delete(int id); - Task> GetRecipes(); + + //Task> GetRecipes(); } } diff --git a/src/CraftSharp/Shared/HeaderLayout.razor b/src/CraftSharp/Shared/HeaderLayout.razor index 4b6b97e..5c9ba21 100644 --- a/src/CraftSharp/Shared/HeaderLayout.razor +++ b/src/CraftSharp/Shared/HeaderLayout.razor @@ -30,6 +30,11 @@ Inventaire +
diff --git a/src/CraftSharp/_Imports.razor b/src/CraftSharp/_Imports.razor index 06955e4..b191629 100644 --- a/src/CraftSharp/_Imports.razor +++ b/src/CraftSharp/_Imports.razor @@ -8,3 +8,6 @@ @using Microsoft.JSInterop @using CraftSharp @using CraftSharp.Shared +@using Blazorise.DataGrid +@using Blazored.Modal +@using Blazored.Modal.Services \ No newline at end of file diff --git a/src/CraftSharp/wwwroot/Images/default.png b/src/CraftSharp/wwwroot/Images/default.png new file mode 100644 index 0000000000000000000000000000000000000000..a7446c9e8a8c755f1e21faf389634136d44fed6d GIT binary patch literal 2267 zcmeHIXBIq!$_ocGH~zGZI(lvb1m006)n z*5;1;HEW*%QWE<)?4{2);XtUPl_>z%dun#Sk$ecXg8~56=`uSWhX4Qxz%5&6i`?AY zyu7@(Z{OzU=NA+d6c-nll$4;+=+e^Cva+)B^74v`3JeB=#bPTfD{(knRaF%pkFTz- zCJ+dpKYy;Rt*xu8BNB=A_4N%64ULVBBoe8mrKPpCwXLnKy}iA&v$MOqyQin8x3{;i zudlzqe_&vMLZJ)}4pOPqp`jrfjYg-_hlht53FMd2nHe6BH#<8!H#f)U^XKR17Zw&47Z;b7mX?>7S5{U80>SF) z>e|}c`uh6D#>VF6rcfx{+S(F{MBCfjJ3BjKv3PfPcW-ZR-2fMBIvBA&SM#k#iJoyiib{34ADdDcA_31T)KpX{irS?~{BklHj+M+J% zdw&s15$x2E%ww_b0H zGN`Wrd|F^q*B=xc1K~Lt??CkcoFt51b6of2;ey0^_r$9zTQI|#vY7M?ow*q>9SF8- z@0iVrUJnQmYRvoR7rLH3!`KV^4KZPJv+ZSUf{CD-c{CJf7_T?DikxnFe$i3IWg0g2 z8v2|y1wY;cR@ycOa~`B6D4Xd0Y?F=Zy2WL(Sz}{QZDAy5>($Mi!c@bBwP~2}xXFM4 zuZi236Fe2m@7jC35T~iKy6(nCi6p^xq+%VK6rWTqDTU9^HVZc7N>f=YvH1%#>G~R% ztWwS$S4Cj$c|}Mt5&b?1PH1XsJ*=P*c;8XHl`h;qD`)csmh^*;Ziip9o=jM3227F8 zGZ>AGkd9ka_Ac5MHM|7lJ(R+fRS_ES7v)@X=iSMWqkq4Nk|x#Rgm$f_t*}zQ!jA|K zRz)QkD}}qV_E;EYEdSU3?oVs4d{vw@j`_P7cgT_%2??YQ!jK#rBos#}?S6pO{GJzO zSO%*hRbUDLG8fcv#_##pk;k6BOB-r6^)?$g;r~avGaYk)R-Th4hjZ1|vu#)R@`K6r z!L*z-e50v`Ks?iP*$*8`EH=wk%z6~8w~BZ<)M;hu8QxwA;?&Fi)&+0Vy{C;EyQ>^& ziS+-4nd#d=aM{pQ2suv|PKaoN*Fq>#LKsyUq)t4amGHVQh8@jG)`|u;8%Whfh_5J< zTN(*rSx-(aM+k(N5a9hTjs1}Zsax&EyR-zOwm;8l$&nN7QZ{};vtz%D6v=9-)H!P? z{bY_NSum|GVuI^xUE?OoLe*g*)SZ#ttQeoK#=uV{@mFJT2ZPmNE{%gy_uU3)(0l9x zx73I5aUQ83j};0gQ0ZxhqEjxs510W4{evv(Gw7`OBsfwDI*IqD5T{{Tv}C~r`M9YN zah2EQ*j>4xg4@A5?ZY_GR zmBeb<>8q*!FOJv@Tn9(^si4T-^I<>vLI(56kI}gtf%j20pN=VJDtS<=V}+tg$0Kbl z!!;H?``-8vO}uA81l5)Nwwb>B1i#R zUwuubIGlO9-DiKM2qM^#_tA4`D1xj!Dx_MzL$48HXY3cwwR8R_eDtd0wig> literal 0 HcmV?d00001 diff --git a/src/CraftSharp/wwwroot/Images/sq.png b/src/CraftSharp/wwwroot/Images/sq.png new file mode 100644 index 0000000000000000000000000000000000000000..d8bf2cef5d54faee6af2df719ac533e8ebdbf38c GIT binary patch literal 8451 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