Skip to content

Commit 7ebd09c

Browse files
authored
feat(templates): improve Category / Product CRUD pages in Boilerplate #10129 (#10179)
1 parent 6f3280e commit 7ebd09c

37 files changed

+513
-339
lines changed

.github/workflows/bit.full.ci.yml

+12
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,17 @@ jobs:
149149
cd TestPostgreSQL/src/Server/TestPostgreSQL.Server.Web/
150150
dotnet build
151151
cd ../../../../
152+
rm -r "TestPostgreSQL"
152153
dotnet new bit-bp --name TestMySql --database MySql --framework net8.0 --module Admin --offlineDb
153154
cd TestMySql/src/Server/TestMySql.Server.Web/
154155
dotnet build
155156
cd ../../../../
157+
rm -r "TestMySql"
156158
dotnet new bit-bp --name TestOther --database Other --framework net9.0 --sample --sentry
157159
cd TestOther/src/Server/TestOther.Server.Web/
158160
dotnet build
161+
cd ../../../../
162+
rm -r "TestOther"
159163
160164
- name: Test file storage options
161165
continue-on-error: true
@@ -164,9 +168,12 @@ jobs:
164168
cd TestLocal/src/Server/TestLocal.Server.Web/
165169
dotnet build
166170
cd ../../../../
171+
rm -r "TestLocal"
167172
dotnet new bit-bp --name TestAzureBlobStorage --filesStorage AzureBlobStorage --framework net9.0 --captcha reCaptcha --notification
168173
cd TestAzureBlobStorage/src/Server/TestAzureBlobStorage.Server.Web/
169174
dotnet build
175+
cd ../../../../
176+
rm -r "TestAzureBlobStorage"
170177
171178
- name: Test backend setup options
172179
continue-on-error: true
@@ -178,21 +185,26 @@ jobs:
178185
cd TestStandalone.Server.Web/
179186
dotnet build
180187
cd ../../../../
188+
rm -r "TestStandalone"
181189
dotnet new bit-bp --name TestIntegrated --api Integrated --framework net9.0
182190
cd TestIntegrated/src/Server/TestIntegrated.Server.Web/
183191
dotnet build
192+
cd ../../../../
193+
rm -r "TestIntegrated"
184194
185195
- name: Test sample configuration 1
186196
continue-on-error: true
187197
run: |
188198
dotnet new bit-bp --name TestProject --database SqlServer --filesStorage AzureBlobStorage --api Integrated --captcha reCaptcha --pipeline Azure --module Admin --offlineDb --appInsights --sentry --signalR --notification --cloudflare --framework net9.0
189199
dotnet build TestProject/TestProject.sln -p:MultilingualEnabled=true -p:Environment=Staging
200+
rm -r "TestProject"
190201
191202
- name: Test sample configuration 2
192203
continue-on-error: true
193204
run: |
194205
dotnet new bit-bp --name TestProject2 --database Other --filesStorage Other --api Standalone --captcha None --pipeline None --module None --offlineDb false --appInsights false --sentry false --signalR false --notification false --cloudflare false --framework net8.0
195206
dotnet build TestProject2/TestProject2.sln -p:MultilingualEnabled=false -p:Environment=Development
207+
rm -r "TestProject2"
196208
197209
- name: Create projects from BlazorEmpty project template with different parameters
198210
run: |

src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,7 @@
436436
"src/Shared/Controllers/Products/IProductViewController.cs",
437437
"src/Client/Boilerplate.Client.Core/Components/Pages/Home/ProductsCarousel.*",
438438
"src/Client/Boilerplate.Client.Core/Components/Pages/Home/ProductsSection.*",
439-
"src/Client/Boilerplate.Client.Core/Components/Pages/ProductPage.*",
440-
"src/Client/Boilerplate.Client.Core/wwwroot/images/product-placeholder.svg"
439+
"src/Client/Boilerplate.Client.Core/Components/Pages/ProductPage.*"
441440
]
442441
},
443442
{
@@ -450,7 +449,8 @@
450449
"src/Server/Boilerplate.Server.Api/Mappers/CategoriesMapper.cs",
451450
"src/Server/Boilerplate.Server.Api/Mappers/ProductsMapper.cs",
452451
"src/Server/Boilerplate.Server.Api/Models/Categories/**",
453-
"src/Server/Boilerplate.Server.Api/Models/Products/**"
452+
"src/Server/Boilerplate.Server.Api/Models/Products/**",
453+
"src/Client/Boilerplate.Client.Core/wwwroot/images/product-placeholder.svg"
454454
]
455455
},
456456
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
@inherits AppPageBase
2+
3+
<BitModal @bind-IsOpen="isOpen" Blocking AutoToggleScroll="false">
4+
<section>
5+
<BitStack>
6+
<BitStack Horizontal VerticalAlign="BitAlignment.Center" Gap="0.5rem">
7+
<BitButton Variant="BitVariant.Text"
8+
Href="@Urls.CategoriesPage"
9+
IconName="@BitIconName.Back"
10+
Title="@Localizer[nameof(AppStrings.Back)]" />
11+
<BitText Typography="BitTypography.H5">
12+
@if (category.Id == default)
13+
{
14+
@Localizer[nameof(AppStrings.AddCategory)]
15+
}
16+
else
17+
{
18+
@Localizer[nameof(AppStrings.EditCategory)]
19+
}
20+
</BitText>
21+
</BitStack>
22+
<BitCard FullSize>
23+
<EditForm Model="category" OnValidSubmit="WrapHandled(Save)" novalidate>
24+
<AppDataAnnotationsValidator @ref="validatorRef" />
25+
26+
<BitStack Gap="0.25rem">
27+
<BitTextField @bind-Value="category.Name"
28+
Label="@Localizer[nameof(AppStrings.Name)]"
29+
Placeholder="@Localizer[nameof(AppStrings.EnterCategoryName)]" />
30+
<ValidationMessage For="() => category.Name" />
31+
<br />
32+
<BitLabel For="catColorInput">@Localizer[nameof(AppStrings.Color)]</BitLabel>
33+
34+
<BitStack>
35+
<BitStack Horizontal>
36+
@foreach (var color in new[] { "#FFCD56", "#FF6384", "#4BC0C0", "#FF9124", "#2B88D8", "#C7E0F4" })
37+
{
38+
<button @onclick="() => SetCategoryColor(color)"
39+
type="button"
40+
style="background-color: @color"
41+
class="color-btn @(category.Color == color ? "color-btn--active" : null)" />
42+
}
43+
</BitStack>
44+
45+
<BitStack Horizontal>
46+
<div class="color-square selected" style="background-color: @category.Color"></div>
47+
<BitToggleButton @bind-IsChecked="isColorPickerOpen" Variant="BitVariant.Outline">
48+
@Localizer[(nameof(AppStrings.CustomColor))]
49+
</BitToggleButton>
50+
</BitStack>
51+
52+
@if (isColorPickerOpen)
53+
{
54+
<div class="color-picker-container">
55+
<BitColorPicker @bind-Color="category.Color" Id="catColorInput" ShowPreview="true">
56+
@Localizer[nameof(AppStrings.DefaultColorPicker)]
57+
</BitColorPicker>
58+
</div>
59+
}
60+
</BitStack>
61+
<ValidationMessage For="() => category.Color" />
62+
<br />
63+
<BitStack Horizontal>
64+
<BitButton ButtonType="BitButtonType.Button" Href="@Urls.CategoriesPage" Variant="BitVariant.Outline">
65+
@Localizer[nameof(AppStrings.Cancel)]
66+
</BitButton>
67+
68+
<BitButton IsLoading=isSaving ButtonType="BitButtonType.Submit">
69+
@Localizer[nameof(AppStrings.Save)]
70+
</BitButton>
71+
</BitStack>
72+
</BitStack>
73+
</EditForm>
74+
</BitCard>
75+
</BitStack>
76+
</section>
77+
</BitModal>
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,31 @@
1-
using Boilerplate.Shared.Controllers.Categories;
2-
using Boilerplate.Shared.Dtos.Categories;
1+
using Boilerplate.Shared.Dtos.Categories;
2+
using Boilerplate.Shared.Controllers.Categories;
33

44
namespace Boilerplate.Client.Core.Components.Pages.Authorized.Categories;
55

6-
public partial class AddOrEditCategoryPage
6+
public partial class AddOrEditCategoryModal
77
{
88
protected override string? Title => Localizer[nameof(AppStrings.Category)];
99
protected override string? Subtitle => string.Empty;
1010

1111
[AutoInject] ICategoryController categoryController = default!;
1212

13-
[Parameter] public Guid? Id { get; set; }
13+
[Parameter] public EventCallback OnSave { get; set; }
1414

15-
private bool isLoading;
15+
private bool isOpen;
1616
private bool isSaving;
1717
private bool isColorPickerOpen;
1818
private CategoryDto category = new();
1919
private AppDataAnnotationsValidator validatorRef = default!;
2020

21-
protected override async Task OnInitAsync()
21+
public async Task ShowModal(CategoryDto categoryToShow)
2222
{
23-
await LoadCategory();
24-
}
25-
26-
private async Task LoadCategory()
27-
{
28-
if (Id is null) return;
29-
30-
isLoading = true;
31-
32-
try
23+
await InvokeAsync(async () =>
3324
{
34-
category = await categoryController.Get(Id.Value, CurrentCancellationToken);
35-
}
36-
finally
37-
{
38-
isLoading = false;
39-
}
25+
isOpen = true;
26+
categoryToShow.Patch(category);
27+
StateHasChanged();
28+
});
4029
}
4130

4231
private void SetCategoryColor(string color)
@@ -61,7 +50,8 @@ private async Task Save()
6150
await categoryController.Update(category, CurrentCancellationToken);
6251
}
6352

64-
NavigationManager.NavigateTo(Urls.CategoriesPage);
53+
await OnSave.InvokeAsync();
54+
isOpen = false;
6555
}
6656
catch (ResourceValidationException e)
6757
{

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/AddOrEditCategoryPage.razor

-93
This file was deleted.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor

+2
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,5 @@
8787
CancelText="@Localizer[nameof(AppStrings.No)]"
8888
Title="@Localizer[nameof(AppStrings.DeleteProduct)]"
8989
Message="@Localizer.GetString(nameof(AppStrings.AreYouSureWannaDeleteProduct), deletingCategory?.Name ?? "")" />
90+
91+
<AddOrEditCategoryModal @ref="modal" OnSave="WrapHandled(RefreshData)" />

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Categories/CategoriesPage.razor.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-:cnd:noEmit
2-
using Boilerplate.Shared.Controllers.Categories;
32
using Boilerplate.Shared.Dtos.Categories;
3+
using Boilerplate.Shared.Controllers.Categories;
44

55
namespace Boilerplate.Client.Core.Components.Pages.Authorized.Categories;
66

@@ -14,6 +14,7 @@ public partial class CategoriesPage
1414
private bool isLoading;
1515
private bool isDeleteDialogOpen;
1616
private CategoryDto? deletingCategory;
17+
private AddOrEditCategoryModal? modal;
1718
private BitDataGrid<CategoryDto>? dataGrid;
1819
private string categoryNameFilter = string.Empty;
1920
private BitDataGridItemsProvider<CategoryDto> categoriesProvider = default!;
@@ -79,14 +80,14 @@ private async Task RefreshData()
7980
await dataGrid!.RefreshDataAsync();
8081
}
8182

82-
private void CreateCategory()
83+
private async Task CreateCategory()
8384
{
84-
NavigationManager.NavigateTo(Urls.AddOrEditCategoryPage);
85+
await modal!.ShowModal(new CategoryDto());
8586
}
8687

87-
private void EditCategory(CategoryDto category)
88+
private async Task EditCategory(CategoryDto category)
8889
{
89-
NavigationManager.NavigateTo($"{Urls.AddOrEditCategoryPage}/{category.Id}");
90+
await modal!.ShowModal(category);
9091
}
9192

9293
private async Task DeleteCategory()

0 commit comments

Comments
 (0)