Skip to content

Commit 93674ab

Browse files
authored
Menu management (#175)
* #92 added menu menu management
1 parent ae3e740 commit 93674ab

36 files changed

+4690
-35
lines changed

src/Database/StaticData.sql

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ INSERT [dbo].[Core_UserRole] ([UserId], [RoleId]) VALUES (1, 1)
2222
GO
2323

2424
SET IDENTITY_INSERT [dbo].[Core_EntityType] ON
25-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (1, N'Category', N'Category', N'CategoryDetail')
26-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (2, N'Brand', N'Brand', N'BrandDetail')
27-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (3, N'Product', N'Product', N'ProductDetail')
28-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (4, N'Page', N'Page', N'PageDetail')
29-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (5, N'Vendor', N'Vendor', N'VendorDetail')
30-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (6, N'NewsCategory', N'NewsCategory', N'NewsCategoryDetail')
31-
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction]) VALUES (7, N'NewsItem', N'NewsItem', N'NewsItemDetail')
25+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (1, N'Category', N'Category', N'CategoryDetail', 1)
26+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (2, N'Brand', N'Brand', N'BrandDetail', 1)
27+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (3, N'Product', N'Product', N'ProductDetail', 0)
28+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (4, N'Page', N'Page', N'PageDetail', 1)
29+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (5, N'Vendor', N'Vendor', N'VendorDetail', 0)
30+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (6, N'NewsCategory', N'NewsCategory', N'NewsCategoryDetail', 1)
31+
INSERT [dbo].[Core_EntityType] ([Id], [Name], [RoutingController], [RoutingAction], [IsMenuable]) VALUES (7, N'NewsItem', N'NewsItem', N'NewsItemDetail', 0)
3232
SET IDENTITY_INSERT [dbo].[Core_EntityType] OFF
3333
GO
3434

@@ -50,6 +50,12 @@ INSERT [dbo].[Core_WidgetZone] ([Id], [Description], [Name]) VALUES (2, NULL, N'
5050
INSERT [dbo].[Core_WidgetZone] ([Id], [Description], [Name]) VALUES (3, NULL, N'Home After Main Content')
5151
SET IDENTITY_INSERT [dbo].[Core_WidgetZone] OFF
5252

53+
SET IDENTITY_INSERT [dbo].[Cms_Menu] ON
54+
INSERT [dbo].[Cms_Menu] ([Id], [IsPublished], [IsSystem], [Name]) VALUES (1, 1, 1, N'Customer services')
55+
INSERT [dbo].[Cms_Menu] ([Id], [IsPublished], [IsSystem], [Name]) VALUES (2, 1, 1, N'Information')
56+
SET IDENTITY_INSERT [dbo].[Cms_Menu] OFF
57+
GO
58+
5359
SET IDENTITY_INSERT [dbo].[Catalog_ProductOption] ON
5460
INSERT [dbo].[Catalog_ProductOption] ([Id], [Name]) VALUES (1, N'Color')
5561
INSERT [dbo].[Catalog_ProductOption] ([Id], [Name]) VALUES (2, N'Size')

src/Database/StaticData_Postgres.sql

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ SELECT pg_catalog.setval('"Core_User_Id_seq"', 1, true);
1414

1515
INSERT INTO "Core_UserRole" ("UserId", "RoleId") VALUES (1, 1);
1616

17-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (1, 'Category', 'Category', 'CategoryDetail');
18-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (2, 'Brand', 'Brand', 'BrandDetail');
19-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (3, 'Product', 'Product', 'ProductDetail');
20-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (4, 'Page', 'Page', 'PageDetail');
21-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (5, 'Vendor', 'Vendor', 'VendorDetail');
22-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (6, 'NewsCategory', 'NewsCategory', 'NewsCategoryDetail');
23-
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction") VALUES (7, 'NewsItem', 'NewsItem', 'NewsItemDetail');
17+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (1, 'Category', 'Category', 'CategoryDetail', true);
18+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (2, 'Brand', 'Brand', 'BrandDetail', true);
19+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (3, 'Product', 'Product', 'ProductDetail', false);
20+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (4, 'Page', 'Page', 'PageDetail', true);
21+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (5, 'Vendor', 'Vendor', 'VendorDetail', false);
22+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (6, 'NewsCategory', 'NewsCategory', 'NewsCategoryDetail', true);
23+
INSERT INTO "Core_EntityType" ("Id", "Name", "RoutingController", "RoutingAction", "IsMenuable") VALUES (7, 'NewsItem', 'NewsItem', 'NewsItemDetail', false);
2424
SELECT pg_catalog.setval('"Core_EntityType_Id_seq"', 7, true);
2525

2626
INSERT INTO "ActivityLog_ActivityType" ("Id", "Name") VALUES (1, 'ProductView');
@@ -36,6 +36,10 @@ INSERT INTO "Core_WidgetZone" ("Id", "Description", "Name") VALUES (2, NULL, 'Ho
3636
INSERT INTO "Core_WidgetZone" ("Id", "Description", "Name") VALUES (3, NULL, 'Home After Main Content');
3737
SELECT pg_catalog.setval('"Core_WidgetZone_Id_seq"', 3, true);
3838

39+
INSERT "Cms_Menu" ("Id", "IsPublished", "IsSystem", "Name") VALUES (1, true, true, 'Customer services');
40+
INSERT "Cms_Menu" ("Id", "IsPublished", "IsSystem", "Name") VALUES (2, true, true, 'Information');
41+
SELECT pg_catalog.setval('"Cms_Menu_Id_seq"', 2, true);
42+
3943
INSERT INTO "Catalog_ProductOption" ("Id", "Name") VALUES (1, 'Color');
4044
INSERT INTO "Catalog_ProductOption" ("Id", "Name") VALUES (2, 'Size');
4145
SELECT pg_catalog.setval('"Catalog_ProductOption_Id_seq"', 2, true);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.EntityFrameworkCore;
7+
using SimplCommerce.Infrastructure.Data;
8+
using SimplCommerce.Module.Cms.Models;
9+
using SimplCommerce.Module.Cms.ViewModels;
10+
11+
namespace SimplCommerce.Module.Cms.Components
12+
{
13+
public class MenuViewComponent : ViewComponent
14+
{
15+
private readonly IRepository<Menu> _menuRepository;
16+
17+
public MenuViewComponent(IRepository<Menu> menuRepository)
18+
{
19+
_menuRepository = menuRepository;
20+
}
21+
22+
public async Task<IViewComponentResult> InvokeAsync(long menuId)
23+
{
24+
var menu = await _menuRepository.Query().Include(x => x.MenuItems).ThenInclude(m => m.Entity).FirstOrDefaultAsync(x => x.Id == menuId);
25+
if(menu == null)
26+
{
27+
throw new ArgumentException($"Cannot found menu item id {menuId}");
28+
}
29+
30+
var menuItemVms = new List<MenuItemVm>();
31+
foreach (var item in menu.MenuItems.Where(x => !x.ParentId.HasValue))
32+
{
33+
var menuItemVm = Map(item);
34+
menuItemVms.Add(menuItemVm);
35+
}
36+
37+
return View("/Modules/SimplCommerce.Module.Cms/Views/Components/Menu.cshtml", menuItemVms);
38+
}
39+
40+
private MenuItemVm Map(MenuItem menuItem)
41+
{
42+
var menuItemVm = new MenuItemVm
43+
{
44+
Id = menuItem.Id,
45+
Name = menuItem.Name,
46+
Link = menuItem.Entity == null ? menuItem.CustomLink : $"/{menuItem.Entity.Slug}"
47+
};
48+
49+
var childItems = menuItem.Children;
50+
foreach (var childItem in childItems)
51+
{
52+
var childMenuItemVm = Map(childItem);
53+
menuItemVm.AddChildItem(childMenuItemVm);
54+
}
55+
56+
return menuItemVm;
57+
}
58+
}
59+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Microsoft.AspNetCore.Authorization;
4+
using Microsoft.AspNetCore.Mvc;
5+
using SimplCommerce.Infrastructure.Data;
6+
using SimplCommerce.Module.Cms.Models;
7+
using SimplCommerce.Module.Cms.ViewModels;
8+
using Microsoft.EntityFrameworkCore;
9+
10+
namespace SimplCommerce.Module.Cms.Controllers
11+
{
12+
[Authorize(Roles = "admin")]
13+
[Route("api/menus")]
14+
public class MenuApiController : Controller
15+
{
16+
private readonly IRepository<Menu> _menuRepository;
17+
private readonly IRepository<MenuItem> _menuItemRepository;
18+
19+
public MenuApiController(IRepository<Menu> menuRepository, IRepository<MenuItem> menuItemRepository)
20+
{
21+
_menuRepository = menuRepository;
22+
_menuItemRepository = menuItemRepository;
23+
}
24+
25+
[HttpGet]
26+
public IActionResult Get()
27+
{
28+
var menuList = _menuRepository.Query().ToList();
29+
return Json(menuList);
30+
}
31+
32+
[HttpGet("{id}")]
33+
public IActionResult Get(long id)
34+
{
35+
var menu = _menuRepository.Query().Include(x => x.MenuItems).ThenInclude(m => m.Entity).FirstOrDefault(x => x.Id == id);
36+
if(menu == null)
37+
{
38+
return NotFound();
39+
}
40+
41+
var model = new MenuForm
42+
{
43+
Id = menu.Id,
44+
Name = menu.Name,
45+
IsPublished = menu.IsPublished,
46+
Items = menu.MenuItems.Select(x => new MenuItemForm
47+
{
48+
Id = x.Id,
49+
EntityId = x.EntityId,
50+
ParentId = x.ParentId,
51+
Name = x.Entity == null ? x.Name : x.Entity.Name,
52+
CustomLink = x.CustomLink
53+
}).ToList()
54+
};
55+
56+
return Json(model);
57+
}
58+
59+
[HttpPost("{id}/add-items")]
60+
public IActionResult AddItem(long id, [FromBody] IList<MenuItemForm> model)
61+
{
62+
if (ModelState.IsValid)
63+
{
64+
var menu = _menuRepository.Query().Include(x => x.MenuItems).FirstOrDefault(x => x.Id == id);
65+
var addedMenuItems = new List<MenuItem>();
66+
foreach (var item in model)
67+
{
68+
var menuItem = new MenuItem
69+
{
70+
Menu = menu,
71+
CustomLink = item.CustomLink,
72+
Name = item.Name,
73+
EntityId = item.EntityId,
74+
ParentId = item.ParentId
75+
};
76+
77+
menu.MenuItems.Add(menuItem);
78+
addedMenuItems.Add(menuItem);
79+
}
80+
81+
_menuRepository.SaveChange();
82+
return Ok(addedMenuItems.Select(x => new MenuItemForm
83+
{
84+
Id = x.Id,
85+
EntityId = x.EntityId,
86+
Name = x.Name,
87+
CustomLink = x.CustomLink
88+
}));
89+
}
90+
return new BadRequestObjectResult(ModelState);
91+
}
92+
93+
[HttpDelete("delete-item/{id}")]
94+
public IActionResult DeleteItem(long id)
95+
{
96+
var menuItem = _menuItemRepository.Query().FirstOrDefault(x => x.Id == id);
97+
if (menuItem == null)
98+
{
99+
return new NotFoundResult();
100+
}
101+
102+
_menuItemRepository.Remove(menuItem);
103+
_menuItemRepository.SaveChange();
104+
105+
return Ok();
106+
}
107+
108+
[HttpPost]
109+
public IActionResult Post([FromBody] MenuForm model)
110+
{
111+
if (ModelState.IsValid)
112+
{
113+
var menu = new Menu
114+
{
115+
Name = model.Name,
116+
IsPublished = model.IsPublished
117+
};
118+
119+
_menuRepository.Add(menu);
120+
_menuRepository.SaveChange();
121+
122+
return Json(menu);
123+
}
124+
return new BadRequestObjectResult(ModelState);
125+
}
126+
127+
[HttpPut("{id}")]
128+
public IActionResult Put(long id, [FromBody] MenuForm model)
129+
{
130+
if (ModelState.IsValid)
131+
{
132+
var menu = _menuRepository.Query().Include(x => x.MenuItems).FirstOrDefault(x => x.Id == id);
133+
menu.Name = model.Name;
134+
menu.IsPublished = model.IsPublished;
135+
foreach(var item in menu.MenuItems)
136+
{
137+
var modelMenuItem = model.Items.FirstOrDefault(x => x.Id == item.Id);
138+
if (modelMenuItem == null)
139+
{
140+
continue;
141+
}
142+
143+
item.EntityId = modelMenuItem.EntityId;
144+
item.Name = modelMenuItem.Name;
145+
item.CustomLink = modelMenuItem.CustomLink;
146+
item.ParentId = modelMenuItem.ParentId;
147+
}
148+
149+
var deletedMenuItems = menu.MenuItems.Where(x => !model.Items.Any(m => m.Id == x.Id));
150+
foreach (var item in deletedMenuItems)
151+
{
152+
_menuItemRepository.Remove(item);
153+
}
154+
155+
_menuRepository.SaveChange();
156+
return Ok();
157+
}
158+
159+
return new BadRequestObjectResult(ModelState);
160+
}
161+
162+
[HttpDelete("{id}")]
163+
public IActionResult Delete(long id)
164+
{
165+
var menu = _menuRepository.Query().FirstOrDefault(x => x.Id == id);
166+
if (menu == null)
167+
{
168+
return new NotFoundResult();
169+
}
170+
171+
if (menu.IsSystem)
172+
{
173+
return BadRequest(new { Error = "A system menu cannot be deleted." });
174+
}
175+
176+
_menuRepository.Remove(menu);
177+
_menuRepository.SaveChange();
178+
179+
return Ok();
180+
}
181+
}
182+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using SimplCommerce.Infrastructure.Models;
2+
using System.Collections.Generic;
3+
4+
namespace SimplCommerce.Module.Cms.Models
5+
{
6+
public class Menu : EntityBase
7+
{
8+
public string Name { get; set; }
9+
10+
public bool IsPublished { get; set; }
11+
12+
public bool IsSystem { get; set; }
13+
14+
public IList<MenuItem> MenuItems { get; protected set; } = new List<MenuItem>();
15+
}
16+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Collections.Generic;
2+
using SimplCommerce.Infrastructure.Models;
3+
using SimplCommerce.Module.Core.Models;
4+
5+
namespace SimplCommerce.Module.Cms.Models
6+
{
7+
public class MenuItem : EntityBase
8+
{
9+
public long? ParentId { get; set; }
10+
11+
public MenuItem Parent { get; set; }
12+
13+
public IList<MenuItem> Children { get; protected set; } = new List<MenuItem>();
14+
15+
public long MenuId { get; set; }
16+
17+
public Menu Menu { get; set; }
18+
19+
public long? EntityId { get; set; }
20+
21+
public Entity Entity { get; set; }
22+
23+
public string CustomLink { get; set; }
24+
25+
public string Name { get; set; }
26+
}
27+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Collections.Generic;
2+
using System.ComponentModel.DataAnnotations;
3+
4+
namespace SimplCommerce.Module.Cms.ViewModels
5+
{
6+
public class MenuForm
7+
{
8+
public long Id { get; set; }
9+
10+
[Required]
11+
public string Name { get; set; }
12+
13+
public bool IsPublished { get; set; }
14+
15+
public IList<MenuItemForm> Items { get; set; } = new List<MenuItemForm>();
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace SimplCommerce.Module.Cms.ViewModels
2+
{
3+
public class MenuItemForm
4+
{
5+
public long Id { get; set; }
6+
7+
public long? ParentId { get; set; }
8+
9+
public long? EntityId { get; set; }
10+
11+
public string Name { get; set; }
12+
13+
public string CustomLink { get; set; }
14+
}
15+
}

0 commit comments

Comments
 (0)