Skip to content

Commit bf8f54f

Browse files
authored
#173 More on category management (#185)
1 parent 003ff23 commit bf8f54f

File tree

16 files changed

+1962
-77
lines changed

16 files changed

+1962
-77
lines changed

src/Modules/SimplCommerce.Module.Catalog/Components/CategoryMenuViewComponent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public CategoryMenuViewComponent(IRepository<Category> categoryRepository)
1818

1919
public IViewComponentResult Invoke()
2020
{
21-
var categories = _categoryRepository.Query().Where(x => !x.IsDeleted).ToList();
21+
var categories = _categoryRepository.Query().Where(x => !x.IsDeleted && x.IncludeInMenu).ToList();
2222

2323
var categoryMenuItems = new List<CategoryMenuItem>();
2424
foreach (var category in categories.Where(x => !x.ParentId.HasValue))

src/Modules/SimplCommerce.Module.Catalog/Controllers/CategoryApiController.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using SimplCommerce.Module.Catalog.ViewModels;
1414
using SimplCommerce.Module.Core.Services;
1515
using SimplCommerce.Module.Core.Models;
16+
using SimplCommerce.Infrastructure.Web.SmartTable;
1617

1718
namespace SimplCommerce.Module.Catalog.Controllers
1819
{
@@ -21,12 +22,14 @@ namespace SimplCommerce.Module.Catalog.Controllers
2122
public class CategoryApiController : Controller
2223
{
2324
private readonly IRepository<Category> _categoryRepository;
25+
private readonly IRepository<ProductCategory> _productCategoryRepository;
2426
private readonly ICategoryService _categoryService;
2527
private readonly IMediaService _mediaService;
2628

27-
public CategoryApiController(IRepository<Category> categoryRepository, ICategoryService categoryService, IMediaService mediaService)
29+
public CategoryApiController(IRepository<Category> categoryRepository, IRepository<ProductCategory> productCategoryRepository, ICategoryService categoryService, IMediaService mediaService)
2830
{
2931
_categoryRepository = categoryRepository;
32+
_productCategoryRepository = productCategoryRepository;
3033
_categoryService = categoryService;
3134
_mediaService = mediaService;
3235
}
@@ -47,6 +50,7 @@ public IActionResult Get(long id)
4750
Name = category.Name,
4851
Description = category.Description,
4952
ParentId = category.ParentId,
53+
IncludeInMenu = category.IncludeInMenu,
5054
IsPublished = category.IsPublished,
5155
ThumbnailImageUrl = _mediaService.GetThumbnailUrl(category.ThumbnailImage),
5256
};
@@ -66,6 +70,7 @@ public IActionResult Post(CategoryForm model)
6670
SeoTitle = model.Name.ToUrlFriendly(),
6771
Description = model.Description,
6872
ParentId = model.ParentId,
73+
IncludeInMenu = model.IncludeInMenu,
6974
IsPublished = model.IsPublished
7075
};
7176

@@ -89,6 +94,7 @@ public IActionResult Put(long id, CategoryForm model)
8994
category.SeoTitle = model.Name.ToUrlFriendly();
9095
category.Description = model.Description;
9196
category.ParentId = model.ParentId;
97+
category.IncludeInMenu = model.IncludeInMenu;
9298
category.IsPublished = model.IsPublished;
9399

94100
SaveCategoryImage(category, model);
@@ -121,6 +127,53 @@ public IActionResult Delete(long id)
121127
return Ok();
122128
}
123129

130+
[HttpPost("{id}/products")]
131+
public IActionResult GetProducts(long id, [FromBody] SmartTableParam param)
132+
{
133+
var query = _productCategoryRepository.Query().Include(x => x.Product)
134+
.Where(x => x.CategoryId == id && !x.Product.IsDeleted && x.Product.IsVisibleIndividually);
135+
136+
if (param.Search.PredicateObject != null)
137+
{
138+
dynamic search = param.Search.PredicateObject;
139+
if (search.Name != null)
140+
{
141+
string name = search.Name;
142+
query = query.Where(x => x.Product.Name.Contains(name));
143+
}
144+
145+
if (search.IsPublished != null)
146+
{
147+
bool isPublished = search.IsPublished;
148+
query = query.Where(x => x.Product.IsPublished == isPublished);
149+
}
150+
}
151+
152+
var gridData = query.ToSmartTableResult(
153+
param,
154+
x => new
155+
{
156+
Id = x.Id,
157+
ProductName = x.Product.Name,
158+
IsFeaturedProduct = x.IsFeaturedProduct,
159+
DisplayOrder = x.DisplayOrder,
160+
IsProductPublished = x.Product.IsPublished
161+
});
162+
163+
return Json(gridData);
164+
}
165+
166+
[HttpPut("update-product/{id}")]
167+
public IActionResult UpdateProduct(long id, [FromBody] ProductCategoryForm model)
168+
{
169+
var productCategory = _productCategoryRepository.Query().FirstOrDefault(x => x.Id == id);
170+
productCategory.IsFeaturedProduct = model.IsFeaturedProduct;
171+
productCategory.DisplayOrder = model.DisplayOrder;
172+
173+
_productCategoryRepository.SaveChange();
174+
return Ok();
175+
}
176+
124177
private void SaveCategoryImage(Category category, CategoryForm model)
125178
{
126179
if (model.ThumbnailImage != null)

src/Modules/SimplCommerce.Module.Catalog/Models/Category.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public class Category : EntityBase
1818

1919
public bool IsPublished { get; set; }
2020

21+
public bool IncludeInMenu { get; set; }
22+
2123
public bool IsDeleted { get; set; }
2224

2325
public long? ParentId { get; set; }

src/Modules/SimplCommerce.Module.Catalog/Services/CategoryService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public IList<CategoryListItem> GetAll()
3030
{
3131
Id = category.Id,
3232
IsPublished = category.IsPublished,
33+
IncludeInMenu = category.IncludeInMenu,
3334
Name = category.Name
3435
};
3536

src/Modules/SimplCommerce.Module.Catalog/ViewModels/CategoryForm.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public CategoryForm()
1919

2020
public long? ParentId { get; set; }
2121

22+
public bool IncludeInMenu { get; set; }
23+
2224
public bool IsPublished { get; set; }
2325

2426
public IFormFile ThumbnailImage { get; set; }

src/Modules/SimplCommerce.Module.Catalog/ViewModels/CategoryListItem.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ public class CategoryListItem
66

77
public string Name { get; set; }
88

9+
public bool IncludeInMenu { get; set; }
10+
911
public bool IsPublished { get; set; }
1012
}
1113
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace SimplCommerce.Module.Catalog.ViewModels
2+
{
3+
public class ProductCategoryForm
4+
{
5+
public long Id { get; set; }
6+
7+
public bool IsFeaturedProduct { get; set; }
8+
9+
public int DisplayOrder { get; set; }
10+
}
11+
}

src/Modules/SimplCommerce.Module.Catalog/wwwroot/admin/category/category-form.html

Lines changed: 135 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,140 @@ <h2 ng-if="!vm.isEditMode">{{::vm.translate.get('Create Category')}}</h2>
44
<h2 ng-if="vm.isEditMode">{{::vm.translate.get('Edit Category')}}</h2>
55
</div>
66
<div class="panel-body">
7-
<form name="categoryForm" class="form-horizontal">
8-
<div class="col-sm-offset-2 bg-danger" ng-show="vm.validationErrors">
9-
<ul>
10-
<li ng-repeat="error in vm.validationErrors">{{error}}</li>
11-
</ul>
12-
</div>
13-
<div class="form-group">
14-
<label class="col-sm-2 control-label">{{::vm.translate.get('Name')}}</label>
15-
<div class="col-sm-10">
16-
<input name="name" ng-model="vm.category.name" class="form-control" />
17-
</div>
18-
</div>
19-
<div class="form-group">
20-
<label class="col-sm-2 control-label">{{::vm.translate.get('Parent Category')}}</label>
21-
<div class="col-sm-10">
22-
<select class="form-control" ng-model="vm.category.parentId"
23-
ng-options="category.id as category.name for category in vm.categories">
24-
<option value="">Top</option>
25-
</select>
26-
</div>
27-
</div>
28-
<div class="form-group">
29-
<label class="col-sm-2 control-label">{{::vm.translate.get('Description')}}</label>
30-
<div class="col-sm-10">
31-
<textarea name="description" ng-model="vm.category.description" rows="3" class="form-control" />
32-
</div>
33-
</div>
34-
<div class="form-group">
35-
<label class="col-sm-2 control-label">{{::vm.translate.get('Thumbnail')}}</label>
36-
<div class="col-sm-10">
37-
<input class="form-control" type="file" ngf-select ng-model="vm.category.thumbnailImage" name="thumbnailImage" ngf-accept="'image/*'">
38-
<img ngf-src="vm.category.thumbnailImage" ngf-resize="{width: 100, height: 100, quality: 0.9}">
39-
<img ng-show="!vm.category.thumbnailImage && vm.category.thumbnailImageUrl" ng-src="{{vm.category.thumbnailImageUrl}}" style="width: 100px; height: 100px" />
40-
</div>
41-
</div>
42-
<div class="form-group">
43-
<div class="col-sm-offset-2 col-sm-10">
44-
<button class="btn btn-primary" ng-click="vm.save()"><span class="glyphicon glyphicon-ok"></span> {{::vm.translate.get('Save')}}</button>
45-
<button ui-sref="category" class="btn btn-default">{{::vm.translate.get('Cancel')}}</button>
46-
</div>
47-
</div>
48-
</form>
7+
<uib-tabset>
8+
<uib-tab index="0" heading="{{::vm.translate.get('General Information')}}">
9+
<form name="categoryForm" class="form-horizontal">
10+
<div class="col-sm-offset-2 bg-danger" ng-show="vm.validationErrors">
11+
<ul>
12+
<li ng-repeat="error in vm.validationErrors">{{error}}</li>
13+
</ul>
14+
</div>
15+
<div class="form-group">
16+
<label class="col-sm-2 control-label">{{::vm.translate.get('Name')}}</label>
17+
<div class="col-sm-10">
18+
<input name="name" ng-model="vm.category.name" class="form-control" />
19+
</div>
20+
</div>
21+
<div class="form-group">
22+
<label class="col-sm-2 control-label">{{::vm.translate.get('Parent Category')}}</label>
23+
<div class="col-sm-10">
24+
<select class="form-control" ng-model="vm.category.parentId"
25+
ng-options="category.id as category.name for category in vm.categories">
26+
<option value="">Top</option>
27+
</select>
28+
</div>
29+
</div>
30+
<div class="form-group">
31+
<label class="col-sm-2 control-label">{{::vm.translate.get('Description')}}</label>
32+
<div class="col-sm-10">
33+
<textarea name="description" ng-model="vm.category.description" rows="3" class="form-control" />
34+
</div>
35+
</div>
36+
<div class="form-group">
37+
<label class="col-sm-2 control-label">{{::vm.translate.get('Thumbnail')}}</label>
38+
<div class="col-sm-10">
39+
<input class="form-control" type="file" ngf-select ng-model="vm.category.thumbnailImage" name="thumbnailImage" ngf-accept="'image/*'">
40+
<img ngf-src="vm.category.thumbnailImage" ngf-resize="{width: 100, height: 100, quality: 0.9}">
41+
<img ng-show="!vm.category.thumbnailImage && vm.category.thumbnailImageUrl" ng-src="{{vm.category.thumbnailImageUrl}}" style="width: 100px; height: 100px" />
42+
</div>
43+
</div>
44+
<div class="form-group">
45+
<div class="col-sm-offset-2 col-sm-10">
46+
<div class="checkbox">
47+
<label>
48+
<input type="checkbox" ng-model="vm.category.includeInMenu">{{::vm.translate.get('Include in Menu')}}
49+
</label>
50+
</div>
51+
</div>
52+
</div>
53+
<div class="form-group">
54+
<div class="col-sm-offset-2 col-sm-10">
55+
<div class="checkbox">
56+
<label>
57+
<input type="checkbox" ng-model="vm.category.isPublished">{{::vm.translate.get('Is Published')}}
58+
</label>
59+
</div>
60+
</div>
61+
</div>
62+
<div class="form-group">
63+
<div class="col-sm-offset-2 col-sm-10">
64+
<button class="btn btn-primary" ng-click="vm.save()"><span class="glyphicon glyphicon-ok"></span> {{::vm.translate.get('Save')}}</button>
65+
<button ui-sref="category" class="btn btn-default">{{::vm.translate.get('Cancel')}}</button>
66+
</div>
67+
</div>
68+
</form>
69+
</uib-tab>
70+
<uib-tab index="1" heading="{{::vm.translate.get('Products')}}">
71+
<table class="table table-striped" st-pipe="vm.getProducts" st-table="vm.products">
72+
<thead>
73+
<tr>
74+
<th>{{::vm.translate.get('Name')}}</th>
75+
<th class="text-center">{{::vm.translate.get('Is Product Published')}}</th>
76+
<th st-sort="IsFeaturedProduct" class="text-center sortable">{{::vm.translate.get('Is Featured in this Category')}}</th>
77+
<th st-sort="DisplayOrder" class="text-center sortable">{{::vm.translate.get('Display Order')}}</th>
78+
<th class="text-center">{{::vm.translate.get('Actions')}}</th>
79+
</tr>
80+
<tr>
81+
<th>
82+
<div class="form-group">
83+
<input class="form-control" st-search="Name" />
84+
</div>
85+
</th>
86+
<th></th>
87+
<th>
88+
<div class="form-group">
89+
<select class="form-control" st-search="IsFeaturedProduct">
90+
<option value="">{{::vm.translate.get('All')}}</option>
91+
<option value="true">{{::vm.translate.get('Yes')}}</option>
92+
<option value="false">{{::vm.translate.get('No')}}</option>
93+
</select>
94+
</div>
95+
</th>
96+
<th></th>
97+
<th></th>
98+
</tr>
99+
</thead>
100+
<tbody ng-show="!vm.isLoading">
101+
<tr ng-repeat-start="product in vm.products">
102+
<td>{{product.productName}}</td>
103+
<td class="text-center"><i ng-attr-class="{{product.isProductPublished && 'fa fa-circle' || 'fa fa-circle-o'}}"></i></td>
104+
<td class="text-center"><i ng-attr-class="{{product.isFeaturedProduct && 'fa fa-circle' || 'fa fa-circle-o'}}"></i></td>
105+
<td class="text-center">{{product.displayOrder}}</td>
106+
<td class="text-center">
107+
<a ng-show="!product.isEditing" ng-click="vm.editProduct(product)" title="Edit" class="btn btn-default btn-xs"> <span class="glyphicon glyphicon-pencil"></span></a>
108+
</td>
109+
</tr>
110+
<tr ng-if="product.isEditing" ng-repeat-end>
111+
<td colspan="5" style="text-align:right">
112+
<form class="form-inline">
113+
<div class="checkbox">
114+
<label>
115+
{{::vm.translate.get('Is Featured in this Category')}}
116+
<input ng-model="product.editingIsFeaturedProduct" type="checkbox">
117+
</label>
118+
</div>
119+
<div class="form-group">
120+
<label style="font-weight:normal; margin-left:20px;">{{::vm.translate.get('Display Order')}}</label>
121+
<input type="number" ng-model="product.editingDisplayOrder" class="form-control" />
122+
</div>
123+
<button type="button" class="btn btn-default" ng-click="vm.saveProduct(product)">Save</button>
124+
<button type="button" class="btn btn-default" ng-click="product.isEditing = false">Cancel</button>
125+
</form>
126+
</td>
127+
</tr>
128+
</tbody>
129+
<tbody ng-show="vm.isLoading">
130+
<tr>
131+
<td colspan="5" class="text-center">Loading ... </td>
132+
</tr>
133+
</tbody>
134+
<tfoot>
135+
<tr>
136+
<td class="text-center" st-pagination="" st-items-by-page="50" st-displayed-pages="10" colspan="5"></td>
137+
</tr>
138+
</tfoot>
139+
</table>
140+
</uib-tab>
141+
</uib-tabset>
49142
</div>
50143
</div>

0 commit comments

Comments
 (0)