diff --git a/Sitko.Core.sln b/Sitko.Core.sln index 818ed5e58..ec62abf60 100644 --- a/Sitko.Core.sln +++ b/Sitko.Core.sln @@ -242,6 +242,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{0CD96A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudBlazorUnited", "apps\Blazor\MudBlazorUnited\MudBlazorUnited.csproj", "{AA9D56AD-4BC2-48A5-AF79-B6019415C5C9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudBlazorAuto", "apps\Blazor\MudBlazorAuto\MudBlazorAuto.csproj", "{1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudBlazorAuto.Client", "apps\Blazor\MudBlazorAuto.Client\MudBlazorAuto.Client.csproj", "{BBFB3B40-213F-49AF-B12A-38EDA1425BE3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudBlazorAuto.Data", "apps\Blazor\MudBlazorAuto.Data\MudBlazorAuto.Data.csproj", "{5131E964-D2FB-4DE4-B968-6C85EFF9092D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1443,6 +1449,42 @@ Global {AA9D56AD-4BC2-48A5-AF79-B6019415C5C9}.Release|x64.Build.0 = Release|Any CPU {AA9D56AD-4BC2-48A5-AF79-B6019415C5C9}.Release|x86.ActiveCfg = Release|Any CPU {AA9D56AD-4BC2-48A5-AF79-B6019415C5C9}.Release|x86.Build.0 = Release|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Debug|x64.ActiveCfg = Debug|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Debug|x64.Build.0 = Debug|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Debug|x86.ActiveCfg = Debug|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Debug|x86.Build.0 = Debug|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Release|Any CPU.Build.0 = Release|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Release|x64.ActiveCfg = Release|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Release|x64.Build.0 = Release|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Release|x86.ActiveCfg = Release|Any CPU + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F}.Release|x86.Build.0 = Release|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Debug|x64.ActiveCfg = Debug|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Debug|x64.Build.0 = Debug|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Debug|x86.ActiveCfg = Debug|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Debug|x86.Build.0 = Debug|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Release|Any CPU.Build.0 = Release|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Release|x64.ActiveCfg = Release|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Release|x64.Build.0 = Release|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Release|x86.ActiveCfg = Release|Any CPU + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3}.Release|x86.Build.0 = Release|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Debug|x64.ActiveCfg = Debug|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Debug|x64.Build.0 = Debug|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Debug|x86.ActiveCfg = Debug|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Debug|x86.Build.0 = Debug|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Release|Any CPU.Build.0 = Release|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Release|x64.ActiveCfg = Release|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Release|x64.Build.0 = Release|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Release|x86.ActiveCfg = Release|Any CPU + {5131E964-D2FB-4DE4-B968-6C85EFF9092D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {6677865F-C349-4D25-9E19-3DEC43E92DD5} = {109B331E-71B9-483C-8FC1-13B10C92A7F1} @@ -1546,5 +1588,8 @@ Global {5D438687-8576-425A-A8A9-A80B893DCE0C} = {109B331E-71B9-483C-8FC1-13B10C92A7F1} {0CD96A05-3892-4A9E-8768-C00B3ADF84CF} = {F626F7B7-70BB-4D3B-A803-68ADA8BA4234} {AA9D56AD-4BC2-48A5-AF79-B6019415C5C9} = {0CD96A05-3892-4A9E-8768-C00B3ADF84CF} + {1FDA9560-CDF6-4CF9-86FA-F03BF4AE373F} = {0CD96A05-3892-4A9E-8768-C00B3ADF84CF} + {BBFB3B40-213F-49AF-B12A-38EDA1425BE3} = {0CD96A05-3892-4A9E-8768-C00B3ADF84CF} + {5131E964-D2FB-4DE4-B968-6C85EFF9092D} = {0CD96A05-3892-4A9E-8768-C00B3ADF84CF} EndGlobalSection EndGlobal diff --git a/apps/Blazor/MudBlazorAuto.Client/.gitignore b/apps/Blazor/MudBlazorAuto.Client/.gitignore new file mode 100644 index 000000000..9a4e90292 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/.gitignore @@ -0,0 +1,2 @@ +wwwroot/static +!.gitignore diff --git a/apps/Blazor/MudBlazorAuto.Client/Components/Lists/BarRepositoryList.cs b/apps/Blazor/MudBlazorAuto.Client/Components/Lists/BarRepositoryList.cs new file mode 100644 index 000000000..3fe268f64 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/Components/Lists/BarRepositoryList.cs @@ -0,0 +1,17 @@ +using MudBlazorAuto.Data.Entities; +using Sitko.Core.Blazor.MudBlazorComponents; +using Sitko.Core.Repository; + +namespace MudBlazorAuto.Client.Components.Lists +{ + public class BarRepositoryList : MudRepositoryTable> + { + public Task UpdateAsync(BarModel barModel) => + ExecuteRepositoryOperation(async repository => + { + barModel.Date = DateTimeOffset.UtcNow; + var result = await repository.UpdateAsync(barModel); + return result.IsSuccess; + }); + } +} diff --git a/apps/Blazor/MudBlazorAuto.Client/Components/TestComponent.razor b/apps/Blazor/MudBlazorAuto.Client/Components/TestComponent.razor new file mode 100644 index 000000000..3a62d9a6a --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/Components/TestComponent.razor @@ -0,0 +1,6 @@ +@inherits BaseComponent +

TestComponent

+ +@code { + +} diff --git a/apps/Blazor/MudBlazorAuto.Client/Data/Repositories/BarModelRemoteRepository.cs b/apps/Blazor/MudBlazorAuto.Client/Data/Repositories/BarModelRemoteRepository.cs new file mode 100644 index 000000000..4f49dcfa1 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/Data/Repositories/BarModelRemoteRepository.cs @@ -0,0 +1,11 @@ +using MudBlazorAuto.Data.Entities; +using Sitko.Core.Repository.Remote; + +namespace MudBlazorAuto.Client.Data.Repositories; + +public class BarModelRemoteRepository : BaseRemoteRepository +{ + public BarModelRemoteRepository(RemoteRepositoryContext repositoryContext) : base(repositoryContext) + { + } +} diff --git a/apps/Blazor/MudBlazorAuto.Client/MudBlazorAuto.Client.csproj b/apps/Blazor/MudBlazorAuto.Client/MudBlazorAuto.Client.csproj new file mode 100644 index 000000000..c69d28924 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/MudBlazorAuto.Client.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + true + Default + + + + + + + + + + + + diff --git a/apps/Blazor/MudBlazorAuto.Client/Pages/Index.razor b/apps/Blazor/MudBlazorAuto.Client/Pages/Index.razor new file mode 100644 index 000000000..b7ca9e794 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/Pages/Index.razor @@ -0,0 +1,60 @@ +@page "/" +@using MudBlazorAuto.Data.Entities +@attribute [RenderModeInteractiveWebAssembly] +@inherits BaseComponent + + + + + + + + + + + + + + + + + + + + + Id + + + Название + + + Дата + + + + + + @context.Id + + + @context.Bar + + + @context.Date + + + Refresh + + + + + @Summary + + + + diff --git a/apps/Blazor/MudBlazorAuto.Client/Pages/Index.razor.cs b/apps/Blazor/MudBlazorAuto.Client/Pages/Index.razor.cs new file mode 100644 index 000000000..6e7d853c1 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/Pages/Index.razor.cs @@ -0,0 +1,157 @@ +using System.Globalization; +using Microsoft.AspNetCore.Components; +using MudBlazor; +using MudBlazorAuto.Client.Components.Lists; +using MudBlazorAuto.Data.Entities; +using Sitko.Core.Blazor.MudBlazorComponents; +using Sitko.Core.Repository; + +namespace MudBlazorAuto.Client.Pages +{ + public partial class Index + { + private int rowsPerPage = 10; + private const string FilterParamId = "id"; + private const string FilterParamTitle = "title"; + private const string FilterParamDateRange = "dateRange"; + + [Parameter] + [SupplyParameterFromQuery(Name = FilterParamId)] + public Guid? Id { get; set; } + + [Parameter] + [SupplyParameterFromQuery(Name = FilterParamTitle)] + public string? Title { get; set; } + + [Parameter] + [SupplyParameterFromQuery(Name = FilterParamDateRange)] + public string? DateRange { get; set; } + + private BarRepositoryList barList = null!; + private decimal Summary { get; set; } + + private async Task CountSummaryAsync(TableState state, MudTableFilter filter) => + Summary = await barList.SumAsync(model => model.Sum); + + private FilterList FilterList { get; set; } = new(); + private MudAutocomplete IdFilterAutocomplete { get; set; } = null!; + + [Inject] private IRepository BarRepository { get; set; } = null!; + private (BarModel[] items, int itemsCount) bars; + + protected override async Task InitializeAsync() + { + await base.InitializeAsync(); + bars = await BarRepository.GetAllAsync(); + } + + private async Task> SearchIdsAsync(string? value) => string.IsNullOrEmpty(value) + ? (await BarRepository.GetAllAsync()).items + : (await BarRepository.GetAllAsync(q => q.Where(b => b.Id == Guid.Parse(value)))).items; + + private async Task ChangeDateAsync(DateRange? dateRange) + { + FilterList.DateRange = dateRange; + await barList.RefreshAsync(); + } + + private async Task ChangeIdAsync(Guid? id) + { + FilterList.Model = bars.items.FirstOrDefault(b => id != null && b.Id == id); + await IdFilterAutocomplete.ToggleMenu(); + await barList.RefreshAsync(); + } + + private async Task SearchTitleAsync(string value) + { + FilterList.Title = value; + await barList.RefreshAsync(); + } + + private Task ConfigureQueryAsync(IRepositoryQuery query) + { + query.Where(model => model.Bar != ""); + if (FilterList.Model is not null && FilterList.Model.Id != Guid.Empty) + { + query.Where(p => p.Id == FilterList.Model.Id); + } + + if (!string.IsNullOrEmpty(FilterList.Title)) + { + query.Where(p => p.Bar == FilterList.Title); + } + + if (FilterList.DateRange is not null) + { + query.Where(p => p.Date >= new DateTimeOffset((DateTime)FilterList.DateRange.Start!).UtcDateTime && + p.Date <= new DateTimeOffset((DateTime)FilterList.DateRange.End!).UtcDateTime); + } + + return Task.CompletedTask; + } + + private Task> AddParamsToUrlAsync() + { + var urlParams = new Dictionary(); + urlParams.Add(FilterParamId, FilterList.Model?.Id); + urlParams.Add(FilterParamTitle, FilterList.Title); + if (FilterList.DateRange != null) + { + urlParams.Add(FilterParamDateRange, + $"{FilterList.DateRange.Start.ToString()}-{FilterList.DateRange.End.ToString()}"); + } + else + { + urlParams.Add(FilterParamDateRange, null); + } + + return Task.FromResult(urlParams); + } + + private Task GetParamsFromUrlAsync() + { + var hasChanged = false; + if (Id != null) + { + FilterList.Model = bars.items.FirstOrDefault(b => b.Id == Id); + hasChanged = true; + } + + if (!string.IsNullOrEmpty(Title)) + { + FilterList.Title = Title; + hasChanged = true; + } + + if (!string.IsNullOrEmpty(DateRange)) + { + var dateData = DateRange.Split("-"); + if (dateData.Length == 2) + { + FilterList.DateRange = new DateRange(DateTime.Parse(dateData[0], CultureInfo.InvariantCulture), + DateTime.Parse(dateData[1], CultureInfo.InvariantCulture)); + hasChanged = true; + } + } + + if (hasChanged) + { + StateHasChanged(); + } + + return Task.CompletedTask; + } + } + + public class BarStorageMetadata + { + public Guid Id { get; set; } = Guid.NewGuid(); + } + + public class FilterList + { + public BarModel? Model { get; set; } + public string? Title { get; set; } + public DateRange? DateRange { get; set; } + } +} diff --git a/apps/Blazor/MudBlazorAuto.Client/Program.cs b/apps/Blazor/MudBlazorAuto.Client/Program.cs new file mode 100644 index 000000000..a08fab8b5 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/Program.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Sitko.Core.App.Localization; +using Sitko.Core.Blazor.MudBlazorComponents; +using Sitko.Core.Blazor.Wasm; +using Sitko.Core.Repository.Remote; +using Sitko.Core.Repository.Remote.Wasm; +using Sitko.Core.Storage; +using Sitko.Core.Storage.Remote; +using Index = MudBlazorAuto.Client.Pages.Index; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +builder + .AddSitkoCore() + .AddMudBlazor() + .AddJsonLocalization(options => options.AddDefaultResource()) + .AddRemoteRepositories(options => options.AddRepositoriesFromAssemblyOf()) + .AddWasmHttpRepositoryTransport(options => + { + options.RepositoryControllerApiRoute = new Uri("https://localhost:7163/api"); + }); + +builder.Services + .AddHttpClient(nameof(HttpRepositoryTransport)).AddHttpMessageHandler(); +builder.Services.AddScoped(_ => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + +await builder.Build().RunAsync(); + +public class TestRemoteStorageOptions : StorageOptions, IRemoteStorageOptions +{ + public Uri RemoteUrl { get; set; } = new("https://localhost"); + public Func? HttpClientFactory { get; set; } +} diff --git a/apps/Blazor/MudBlazorAuto.Client/_Imports.razor b/apps/Blazor/MudBlazorAuto.Client/_Imports.razor new file mode 100644 index 000000000..f2c02c9b7 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/_Imports.razor @@ -0,0 +1,15 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using MudBlazorAuto.Client +@using MudBlazorAuto.Client.Components +@using MudBlazorAuto.Client.Components.Lists +@using Sitko.Core.Blazor.Components +@using Sitko.Core.Blazor.MudBlazorComponents +@using Sitko.Core.App.Collections +@using Sitko.Core.Storage +@using MudBlazor diff --git a/apps/Blazor/MudBlazorAuto.Client/wwwroot/appsettings.Development.json b/apps/Blazor/MudBlazorAuto.Client/wwwroot/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/apps/Blazor/MudBlazorAuto.Client/wwwroot/appsettings.json b/apps/Blazor/MudBlazorAuto.Client/wwwroot/appsettings.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Client/wwwroot/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/apps/Blazor/MudBlazorAuto.Data/Entities/BarModel.cs b/apps/Blazor/MudBlazorAuto.Data/Entities/BarModel.cs new file mode 100644 index 000000000..3360b1e58 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Data/Entities/BarModel.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations.Schema; +using FluentValidation; +using Sitko.Core.App.Collections; +using Sitko.Core.Repository; +using Sitko.Core.Storage; + +namespace MudBlazorAuto.Data.Entities; + +public class BarModel : Entity +{ + public string Bar { get; set; } = ""; + + public List Foos { get; set; } = new(); + public Guid? FooId { get; set; } + [ForeignKey(nameof(FooId))] public FooModel? Foo { get; set; } + public DateTimeOffset Date { get; set; } = DateTimeOffset.UtcNow; + public decimal Sum { get; set; } = 100; + + public StorageItem? StorageItem { get; set; } + + public ValueCollection StorageItems { get; set; } = new(); +} + +public class BarModelValidator : AbstractValidator +{ + public BarModelValidator() + { + RuleFor(m => m.Bar).NotEmpty().WithMessage("Blaaaaa"); + RuleFor(m => m.StorageItem).NotEmpty().WithMessage("Upload me!"); + RuleFor(m => m.StorageItems).NotEmpty().WithMessage("Upload us!"); + } +} + +public class BarStorageMetadata +{ + public Guid Id { get; set; } = Guid.NewGuid(); +} diff --git a/apps/Blazor/MudBlazorAuto.Data/Entities/FooModel.cs b/apps/Blazor/MudBlazorAuto.Data/Entities/FooModel.cs new file mode 100644 index 000000000..b2ced89c3 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Data/Entities/FooModel.cs @@ -0,0 +1,15 @@ +using FluentValidation; +using Sitko.Core.Repository; + +namespace MudBlazorAuto.Data.Entities +{ + public class FooModel : Entity + { + public string Foo { get; set; } = ""; + } + + public class FooValidator : AbstractValidator + { + public FooValidator() => RuleFor(f => f.Foo).NotEmpty(); + } +} diff --git a/apps/Blazor/MudBlazorAuto.Data/MudBlazorAuto.Data.csproj b/apps/Blazor/MudBlazorAuto.Data/MudBlazorAuto.Data.csproj new file mode 100644 index 000000000..410e30d87 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto.Data/MudBlazorAuto.Data.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/apps/Blazor/MudBlazorAuto/.gitignore b/apps/Blazor/MudBlazorAuto/.gitignore new file mode 100644 index 000000000..9a4e90292 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/.gitignore @@ -0,0 +1,2 @@ +wwwroot/static +!.gitignore diff --git a/apps/Blazor/MudBlazorAuto/Components/App.razor b/apps/Blazor/MudBlazorAuto/Components/App.razor new file mode 100644 index 000000000..49bdf6991 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/App.razor @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/Blazor/MudBlazorAuto/Components/Forms/BarForm.cs b/apps/Blazor/MudBlazorAuto/Components/Forms/BarForm.cs new file mode 100644 index 000000000..8509b5ead --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Forms/BarForm.cs @@ -0,0 +1,32 @@ +using MudBlazorAuto.Data.Entities; +using MudBlazorAuto.Data.Repositories; +using Sitko.Core.Blazor.MudBlazorComponents; +using Sitko.Core.Repository; + +namespace MudBlazorAuto.Components.Forms +{ + public class BarForm : BaseMudRepositoryForm + { + protected override Task ConfigureQueryAsync(IRepositoryQuery query) + { + query.Include(bar => bar.Foos).Include(bar => bar.Foo); + return Task.CompletedTask; + } + + public void SetFoo() => Entity.Foo = new FooModel(); + + public void AddFoo() + { + Entity.Foos.Add(new FooModel()); + NotifyChange(); + } + + public void RemoveFoo(FooModel foo) + { + Entity.Foos.Remove(foo); + NotifyChange(); + } + + public void DeleteFoo() => Entity.Foo = null; + } +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Forms/FooForm.cs b/apps/Blazor/MudBlazorAuto/Components/Forms/FooForm.cs new file mode 100644 index 000000000..84aa8bafb --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Forms/FooForm.cs @@ -0,0 +1,15 @@ +using MudBlazorAuto.Data.Entities; +using MudBlazorAuto.Data.Repositories; +using Sitko.Core.Blazor.Forms; +using Sitko.Core.Blazor.MudBlazorComponents; + +namespace MudBlazorAuto.Components.Forms; + +public class FooForm : BaseMudRepositoryForm +{ + protected override async Task AddAsync(FooModel entity) + { + await Task.Delay(2000); + return await base.AddAsync(entity); + } +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Layout/MainLayout.razor b/apps/Blazor/MudBlazorAuto/Components/Layout/MainLayout.razor new file mode 100644 index 000000000..bc163a04a --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Layout/MainLayout.razor @@ -0,0 +1,9 @@ +@inherits LayoutComponentBase + + + + + + @Body + + diff --git a/apps/Blazor/MudBlazorAuto/Components/Layout/NavMenu.razor b/apps/Blazor/MudBlazorAuto/Components/Layout/NavMenu.razor new file mode 100644 index 000000000..f33844564 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Layout/NavMenu.razor @@ -0,0 +1,10 @@ + + Dashboard + Options + QueryString + Logging + Add Bar + Switch + + +© 2021 Sitko.ru diff --git a/apps/Blazor/MudBlazorAuto/Components/Pages/Foo.razor b/apps/Blazor/MudBlazorAuto/Components/Pages/Foo.razor new file mode 100644 index 000000000..a635b2ebf --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Pages/Foo.razor @@ -0,0 +1,42 @@ +@page "/Foos/Add" +@page "/Foos/{Id:guid}/Edit" +@using MudBlazorAuto.Data.Entities +@attribute [RenderModeInteractiveServer] +@inherits BaseComponent + + + + + + + Foo + + + + + Save + Reset + + + + + + + + +@code { + + [Parameter] + public Guid Id { get; set; } + + private FooForm Form { get; set; } = null!; + + private async Task AfterSaveAsync(FooModel arg) + { + if (Id == Guid.Empty) + { + await Form.ResetAsync(); + } + } + +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Pages/Logging.razor b/apps/Blazor/MudBlazorAuto/Components/Pages/Logging.razor new file mode 100644 index 000000000..11521d780 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Pages/Logging.razor @@ -0,0 +1,80 @@ +@page "/Logging" +@using Serilog.Events +@using Microsoft.Extensions.Logging +@using System.Threading +@using Microsoft.Extensions.Configuration +@using Sitko.Core.App.Logging +@inherits BaseComponent +@attribute [RenderModeInteractiveServer] + + Set debug + Set info + Set warning + Set error + Reset + + @if (!string.IsNullOrEmpty(debugView)) + { +
+@debugView
+
+ } +
+ +@code +{ + private Task? logTask; + private readonly CancellationTokenSource cts = new(); + private string? debugView; + + protected override void Initialize() + { + base.Initialize(); + logTask = StartLoggingAsync(); + } + + private async Task StartLoggingAsync() + { + while (!cts.IsCancellationRequested) + { + try + { + await Task.Delay(5000, cts.Token); + } + catch (OperationCanceledException) + { + break; + } + Logger.LogDebug("Debug"); + Logger.LogInformation("Info"); + Logger.LogWarning("Warning"); + Logger.LogError("Error"); + } + } + + protected override async Task DisposeAsync(bool disposing) + { + if (logTask is not null) + { + await cts.CancelAsync(); + await logTask; + } + await base.DisposeAsync(disposing); + } + + private Task SetLevel(LogEventLevel level) + { + SerilogDynamicConfigurationProvider.Instance.SetLevel(level); + var root = (IConfigurationRoot)GetRequiredService(); + debugView = root.GetDebugView(); + return Task.CompletedTask; + } + + private Task ResetLevel() + { + SerilogDynamicConfigurationProvider.Instance.ResetLevel(); + var root = (IConfigurationRoot)GetRequiredService(); + debugView = root.GetDebugView(); + return Task.CompletedTask; + } +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Pages/QueryString.razor b/apps/Blazor/MudBlazorAuto/Components/Pages/QueryString.razor new file mode 100644 index 000000000..b4a9a3446 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Pages/QueryString.razor @@ -0,0 +1,31 @@ +@page "/QueryString" +@inherits BaseComponent + +

+ Guid: @Test +

+Change +
+ + +@code +{ + private Guid Test { get; set; } = Guid.Empty; + + protected override async Task OnLocationChangeAsync(string location, bool isNavigationIntercepted) + { + await base.OnLocationChangeAsync(location, isNavigationIntercepted); + SetParams(); + } + + protected override void Initialize() + { + base.Initialize(); + SetParams(); + } + + private void SetParams() + { + Test = GetQueryString("test"); + } +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Pages/Scopes.razor b/apps/Blazor/MudBlazorAuto/Components/Pages/Scopes.razor new file mode 100644 index 000000000..74d366fa8 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Pages/Scopes.razor @@ -0,0 +1,148 @@ +@page "/Bars/Add" +@page "/Bars/{Id:guid}/Edit" +@attribute [RenderModeInteractiveServer] +@using MudBlazorAuto.Data.Entities +@inherits BaseComponent + + + + + + + Multiple file input + + + + Edit Foo + @if (formContext.Entity.Foo is not null) + { + + } +
+ Set foo + Delete foo +
+
+ + Edit Foos + @foreach (var foo in formContext.Entity.Foos) + { + + Remove foo + } +
+ Add foo +
+
+ + Current storage items + @if (formContext.Entity.StorageItem is not null) + { + Storage item: @formContext.Entity.StorageItem + } + @foreach (var file in formContext.Entity.StorageItems) + { +

Storage item: @file

+ } +
+ + + + + + Simple custom file input + + + PRESS ME + + + + + + Multiple file input + + + + + + Single image + + + + + + Multiple image + + + + + + Save + Reset + + +
+
+
+
+
+ +@code { + + [Parameter] + public Guid Id { get; set; } + + private IStorage Storage => GetRequiredService>(); + + private static Task GenerateMetadataAsync() + { + var metadata = new BarStorageMetadata(); + return Task.FromResult(metadata); + } + + public List Breadcrumbs { get; set; } = new() + { + new BreadcrumbItem("Foo1", "/"), + new BreadcrumbItem("Bar", "/") + }; + + private BarForm Form { get; set; } = null!; + +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Pages/SwitchForm.razor b/apps/Blazor/MudBlazorAuto/Components/Pages/SwitchForm.razor new file mode 100644 index 000000000..bdb81edf2 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Pages/SwitchForm.razor @@ -0,0 +1,72 @@ +@page "/Switch/Add" +@page "/Switch/{Id:guid}/Edit" +@attribute [RenderModeInteractiveServer] +@inherits BaseComponent +@if (editMode) +{ + + + + Simple Foo + @if (context.Entity.Foo is not null) + { + + } + Delete foo + Multiple foo + @foreach (var foo in context.Entity.Foos) + { + + Remove foo + } + Add foo + Set foo + Save + Reset + + +@* *@ +@* *@ +@* *@ +@* *@ +@*

Simple Foo

*@ +@* @if (context.Entity.Foo is not null) *@ +@* { *@ +@* *@ +@*

Foo id: @context.Entity.Foo.Id

*@ +@* *@ +@*
*@ +@* } *@ +@* *@ +@*

Multiple foo

*@ +@* @foreach (var foo in context.Entity.Foos) *@ +@* { *@ +@* *@ +@*

Foo id: @foo.Id

*@ +@* *@ +@*
*@ +@* *@ +@* } *@ +@*
*@ +@* *@ +@* *@ +@* *@ +@* *@ +@*
*@ +@*
*@ +} +else +{ + Edit Bar @Id + Edit me +} + +@code { + + [Parameter] + public Guid Id { get; set; } + + private bool editMode; + private BarForm Form { get; set; } = null!; + +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Pages/TestPage.razor b/apps/Blazor/MudBlazorAuto/Components/Pages/TestPage.razor new file mode 100644 index 000000000..dac3aca3d --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Pages/TestPage.razor @@ -0,0 +1,27 @@ +@page "/TestPage" +@attribute [RenderModeInteractiveServer] +@inherits BaseComponent +

TestPage

+

@text

+Change +@code { + + protected override async Task InitializeAsync() + { + await base.InitializeAsync(); + } + + // protected override async Task OnInitializedAsync() + // { + // await base.OnInitializedAsync(); + // // do nothing + // } + + private string text = ""; + + private void ChangeText() + { + text = Guid.NewGuid().ToString(); + } + +} diff --git a/apps/Blazor/MudBlazorAuto/Components/Routes.razor b/apps/Blazor/MudBlazorAuto/Components/Routes.razor new file mode 100644 index 000000000..b2f39e8cf --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/Routes.razor @@ -0,0 +1,11 @@ + + + + + + + +

Sorry, there's nothing at this address.

+
+
+
diff --git a/apps/Blazor/MudBlazorAuto/Components/_Imports.razor b/apps/Blazor/MudBlazorAuto/Components/_Imports.razor new file mode 100644 index 000000000..ad793d3da --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Components/_Imports.razor @@ -0,0 +1,16 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using MudBlazorAuto +@using MudBlazorAuto.Client +@using MudBlazorAuto.Components +@using MudBlazorAuto.Components.Forms +@using Sitko.Core.Blazor.Components +@using Sitko.Core.Blazor.MudBlazorComponents +@using Sitko.Core.App.Collections +@using Sitko.Core.Storage +@using MudBlazor diff --git a/apps/Blazor/MudBlazorAuto/Controllers/BarModelRepositoryController.cs b/apps/Blazor/MudBlazorAuto/Controllers/BarModelRepositoryController.cs new file mode 100644 index 000000000..f72d68353 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Controllers/BarModelRepositoryController.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; +using MudBlazorAuto.Data.Entities; +using Sitko.Core.Repository; +using Sitko.Core.Repository.Remote.Server; + +namespace MudBlazorAuto.Controllers; +[Route("/api/BarModel")] +public class BarModelRepositoryController : BaseRemoteRepositoryController +{ + public BarModelRepositoryController(IRepository repository, + ILogger logger) : base(repository, logger) + { + } +} diff --git a/apps/Blazor/MudBlazorAuto/Controllers/UploadController.cs b/apps/Blazor/MudBlazorAuto/Controllers/UploadController.cs new file mode 100644 index 000000000..64fb713f6 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Controllers/UploadController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using MudBlazorAuto.Data.Entities; +using Sitko.Core.Storage; +using Sitko.Core.Storage.Remote.Server; + +namespace MudBlazorAuto.Controllers; + +[Route("/Upload")] +public class UploadController : BaseRemoteStorageController +{ + public UploadController(IStorage storage, ILogger logger) : base( + storage, logger) + { + } +} diff --git a/apps/Blazor/MudBlazorAuto/Data/BarContext.cs b/apps/Blazor/MudBlazorAuto/Data/BarContext.cs new file mode 100644 index 000000000..ceec18b50 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Data/BarContext.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using MudBlazorAuto.Data.Entities; +using Sitko.Core.App.Collections; +using Sitko.Core.Db.Postgres; +using Sitko.Core.Storage; + +namespace MudBlazorAuto.Data +{ + public class BarContext : DbContext + { + public DbSet Bars => Set(); + public DbSet Foos => Set(); + + public BarContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.RegisterJsonConversion(model => model.StorageItem, + nameof(BarModel.StorageItem)); + modelBuilder.RegisterJsonEnumerableConversion>( + model => model.StorageItems, + nameof(BarModel.StorageItems)); + } + } +} diff --git a/apps/Blazor/MudBlazorAuto/Data/Repositories/BarRepository.cs b/apps/Blazor/MudBlazorAuto/Data/Repositories/BarRepository.cs new file mode 100644 index 000000000..b46e05819 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Data/Repositories/BarRepository.cs @@ -0,0 +1,21 @@ +using MudBlazorAuto.Data.Entities; +using Sitko.Core.Repository.EntityFrameworkCore; + +namespace MudBlazorAuto.Data.Repositories +{ + public class BarRepository : EFRepository + { + public BarRepository(EFRepositoryContext repositoryContext) : base( + repositoryContext) + { + } + } + + public class FooRepository : EFRepository + { + public FooRepository(EFRepositoryContext repositoryContext) : base( + repositoryContext) + { + } + } +} diff --git a/apps/Blazor/MudBlazorAuto/MudBlazorAuto.csproj b/apps/Blazor/MudBlazorAuto/MudBlazorAuto.csproj new file mode 100644 index 000000000..44583dec5 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/MudBlazorAuto.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/apps/Blazor/MudBlazorAuto/Program.cs b/apps/Blazor/MudBlazorAuto/Program.cs new file mode 100644 index 000000000..ffbdaf66a --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Program.cs @@ -0,0 +1,76 @@ +using FluentValidation; +using MudBlazorAuto; +using MudBlazorAuto.Client.Pages; +using MudBlazorAuto.Components; +using MudBlazorAuto.Data; +using Sitko.Core.App.Localization; +using Sitko.Core.App.Web; +using Sitko.Core.Blazor.MudBlazor.Server; +using Sitko.Core.Blazor.MudBlazorComponents; +using Sitko.Core.Blazor.Server; +using Sitko.Core.Db.Postgres; +using Sitko.Core.Repository.EntityFrameworkCore; +using Sitko.Core.Storage.FileSystem; +using Sitko.Core.Storage.Metadata.Postgres; +using Sitko.Core.Storage.Remote; +using Index = MudBlazorAuto.Client.Pages.Index; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); + +builder + .AddBlazorServer() + .AddMudBlazorServer() + .AddPostgresDatabase(options => + { + options.EnableSensitiveLogging = true; + }) + .AddEFRepositories() + + // frontend storage, in wasm it should be in client project + .AddRemoteStorage() + .AddPostgresStorageMetadata() + .AddJsonLocalization(options => + { + options.AddDefaultResource(); + }); + +// backend storage, in wasm it should be in server project +builder.AddFileSystemStorage(); + +builder.Services.AddValidatorsFromAssemblyContaining(); +builder.Services.Configure(builder.Configuration.GetSection("MudLayout")); +builder.Services.AddControllers(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseWebAssemblyDebugging(); +} +else +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); +app.UseAntiforgery(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + .AddAdditionalAssemblies(typeof(Index).Assembly); + +app.ConfigureLocalization("ru-RU"); + +app.MapControllers(); + +app.Run(); diff --git a/apps/Blazor/MudBlazorAuto/Properties/launchSettings.json b/apps/Blazor/MudBlazorAuto/Properties/launchSettings.json new file mode 100644 index 000000000..984540690 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7163;http://localhost:5230", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } diff --git a/apps/Blazor/MudBlazorAuto/Resources/App.json b/apps/Blazor/MudBlazorAuto/Resources/App.json new file mode 100644 index 000000000..7c534c4ec --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Resources/App.json @@ -0,0 +1,3 @@ +{ + "Title": "App" +} diff --git a/apps/Blazor/MudBlazorAuto/Resources/App.ru.json b/apps/Blazor/MudBlazorAuto/Resources/App.ru.json new file mode 100644 index 000000000..e70a1904c --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/Resources/App.ru.json @@ -0,0 +1,6 @@ +{ + "Title": "Приложение", + "Delete record?": "Удалить запись?", + "Delete": "Удалить", + "Cancel": "Отмена" +} diff --git a/apps/Blazor/MudBlazorAuto/TestBlazorStorageOptions.cs b/apps/Blazor/MudBlazorAuto/TestBlazorStorageOptions.cs new file mode 100644 index 000000000..23b3ca903 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/TestBlazorStorageOptions.cs @@ -0,0 +1,9 @@ +using Sitko.Core.Storage; +using Sitko.Core.Storage.FileSystem; + +namespace MudBlazorAuto; + +public class TestBlazorStorageOptions : StorageOptions, IFileSystemStorageOptions +{ + public string StoragePath { get; set; } = ""; +} diff --git a/apps/Blazor/MudBlazorAuto/appsettings.Development.json b/apps/Blazor/MudBlazorAuto/appsettings.Development.json new file mode 100644 index 000000000..c475a97f4 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/appsettings.Development.json @@ -0,0 +1,12 @@ +{ + "DetailedErrors": true, + "Serilog":{ + "MinimumLevel": { + "Default": "Information", + "Override": { + "Sitko.Core.Apps.Blazor.Forms": "Debug", + "Sitko.Core.Repository": "Debug" + } + } + } +} diff --git a/apps/Blazor/MudBlazorAuto/appsettings.json b/apps/Blazor/MudBlazorAuto/appsettings.json new file mode 100644 index 000000000..2f7cc8924 --- /dev/null +++ b/apps/Blazor/MudBlazorAuto/appsettings.json @@ -0,0 +1,38 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information" + } + }, + "AllowedHosts": "*", + "Storage": { + "FileSystem": { + "TestBlazorStorageOptions": { + "PublicUri": "https://localhost:7163/static/", + "Name": "Test", + "StoragePath": "wwwroot/static" + } + }, + "Metadata": { + "Postgres": { + "TestBlazorStorageOptions": { + "Database": "dbname" + } + } + }, + "Remote": { + "TestRemoteStorageOptions": { + "PublicUri": "https://localhost:7163/static/", + "Name": "Remote", + "RemoteUrl": "https://localhost:7163/Upload" + } + } + }, + "MudLayout": { + "Theme": "Dark" + }, + "Application": { + "Name": "MudBlazorDemo", + "Version": "8.0.0" + } +} diff --git a/apps/Blazor/MudBlazorAuto/wwwroot/favicon.png b/apps/Blazor/MudBlazorAuto/wwwroot/favicon.png new file mode 100644 index 000000000..8422b5969 Binary files /dev/null and b/apps/Blazor/MudBlazorAuto/wwwroot/favicon.png differ