MenuGroup Entity ve Seeder

This commit is contained in:
Sedat Öztürk 2026-03-17 22:56:34 +03:00
parent ffa3ff2d63
commit cedb535734
20 changed files with 431 additions and 59 deletions

View file

@ -164,6 +164,14 @@ public static class LookupQueryValues
$"FROM \"AbpPermissionGroups\" " + $"FROM \"AbpPermissionGroups\" " +
$"ORDER BY \"Name\";"; $"ORDER BY \"Name\";";
public static string MenuGroupValues =
$"SELECT " +
$"\"Id\" AS \"Key\", " +
$"\"Name\" AS \"Name\" " +
$"FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.MenuGroup))}\" " +
$"WHERE \"IsDeleted\" = 'false' " +
$"ORDER BY \"Name\";";
public static string MenuCodeValues = public static string MenuCodeValues =
$"SELECT " + $"SELECT " +
$"\"Code\" AS \"Key\", " + $"\"Code\" AS \"Key\", " +

View file

@ -1386,6 +1386,12 @@
"en": "Menu List", "en": "Menu List",
"tr": "Menü Listesi" "tr": "Menü Listesi"
}, },
{
"resourceName": "Platform",
"key": "App.Menus.MenuGroup",
"en": "Menu Groups",
"tr": "Menü Grupları"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Menus.Manager", "key": "App.Menus.Manager",

View file

@ -206,7 +206,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
ColumnOptionJson = DefaultColumnOptionJson(), ColumnOptionJson = DefaultColumnOptionJson(),
PermissionJson = DefaultPermissionJson(AbpIdentity.Permissions.Create, listFormName, AbpIdentity.Permissions.Update, AbpIdentity.Permissions.Delete, AbpIdentity.Permissions.Export, AbpIdentity.Permissions.Import, AbpIdentity.Permissions.Note), PermissionJson = DefaultPermissionJson(AbpIdentity.Permissions.Create, listFormName, AbpIdentity.Permissions.Update, AbpIdentity.Permissions.Delete, AbpIdentity.Permissions.Export, AbpIdentity.Permissions.Import, AbpIdentity.Permissions.Note),
PagerOptionJson = DefaultPagerOptionJson, PagerOptionJson = DefaultPagerOptionJson,
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 500, true, true, true, true, false), EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 600, true, true, true, true, false),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
{ {
new() { new() {
@ -236,7 +236,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid, SourceDbType = DbType.Guid,
FieldName = "Id", FieldName = "Id",
CaptionName = "App.Listform.ListformField.Id", CaptionName = "App.Listform.ListformField.Id",
Width = 100, Width = 100,
ListOrderNo = 1, ListOrderNo = 1,
Visible = false, Visible = false,
@ -251,7 +251,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "GroupName", FieldName = "GroupName",
CaptionName = "App.Listform.ListformField.GroupName", CaptionName = "App.Listform.ListformField.GroupName",
Width = 300, Width = 300,
ListOrderNo = 2, ListOrderNo = 2,
Visible = true, Visible = true,
@ -266,7 +266,6 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
ValueExpr = "Key", ValueExpr = "Key",
LookupQuery = LookupQueryValues.PermissionGroupValues LookupQuery = LookupQueryValues.PermissionGroupValues
}), }),
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(AbpIdentity.Permissions.Create, AbpIdentity.Permissions.Default, AbpIdentity.Permissions.Update, true, true, false), PermissionJson = DefaultFieldPermissionJson(AbpIdentity.Permissions.Create, AbpIdentity.Permissions.Default, AbpIdentity.Permissions.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson PivotSettingsJson = DefaultPivotSettingsJson
@ -276,13 +275,19 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "ParentName", FieldName = "ParentName",
CaptionName = "App.Listform.ListformField.ParentName", CaptionName = "App.Listform.ListformField.ParentName",
Width = 300, Width = 300,
ListOrderNo = 3, ListOrderNo = 3,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
IsDeleted = false, IsDeleted = false,
AllowSearch = true, AllowSearch = true,
LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name",
ValueExpr = "Key",
LookupQuery = LookupQueryValues.PermissionNameValues
}),
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(AbpIdentity.Permissions.Create, AbpIdentity.Permissions.Default, AbpIdentity.Permissions.Update, true, true, false), PermissionJson = DefaultFieldPermissionJson(AbpIdentity.Permissions.Create, AbpIdentity.Permissions.Default, AbpIdentity.Permissions.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson PivotSettingsJson = DefaultPivotSettingsJson
@ -292,7 +297,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "Name", FieldName = "Name",
CaptionName = "App.Listform.ListformField.Name", CaptionName = "App.Listform.ListformField.Name",
Width = 300, Width = 300,
ListOrderNo = 4, ListOrderNo = 4,
Visible = true, Visible = true,
@ -308,7 +313,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.String, SourceDbType = DbType.String,
FieldName = "DisplayName", FieldName = "DisplayName",
CaptionName = "App.Listform.ListformField.DisplayName", CaptionName = "App.Listform.ListformField.DisplayName",
Width = 400, Width = 400,
ListOrderNo = 5, ListOrderNo = 5,
Visible = true, Visible = true,
@ -331,7 +336,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.Boolean, SourceDbType = DbType.Boolean,
FieldName = "IsEnabled", FieldName = "IsEnabled",
CaptionName = "App.Listform.ListformField.IsEnabled", CaptionName = "App.Listform.ListformField.IsEnabled",
Width = 100, Width = 100,
ListOrderNo = 6, ListOrderNo = 6,
Visible = true, Visible = true,
@ -346,7 +351,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
CultureName = LanguageCodes.En, CultureName = LanguageCodes.En,
SourceDbType = DbType.Int16, SourceDbType = DbType.Int16,
FieldName = "MultiTenancySide", FieldName = "MultiTenancySide",
CaptionName = "App.Listform.ListformField.MultiTenancySide", CaptionName = "App.Listform.ListformField.MultiTenancySide",
Width = 100, Width = 100,
ListOrderNo = 7, ListOrderNo = 7,
Visible = true, Visible = true,
@ -381,13 +386,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
IsDeleted = false, IsDeleted = false,
LookupJson = JsonSerializer.Serialize(new LookupDto LookupJson = JsonSerializer.Serialize(new LookupDto
{ {
DataSourceType = UiLookupDataSourceTypeEnum.StaticData, DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "name", DisplayExpr = "name",
ValueExpr = "key", ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] { LookupQuery = LookupQueryValues.MenuGroupValues
new () { Key="Kurs",Name="Kurs" },
new () { Key="Erp",Name="Erp" },
}),
}), }),
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(AbpIdentity.Permissions.Create, AbpIdentity.Permissions.Default, AbpIdentity.Permissions.Update, true, true, false), PermissionJson = DefaultFieldPermissionJson(AbpIdentity.Permissions.Create, AbpIdentity.Permissions.Default, AbpIdentity.Permissions.Update, true, true, false),
@ -788,7 +790,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, listFormName, PlatformConsts.IdentityPermissions.Users.Update, PlatformConsts.IdentityPermissions.Users.Delete, PlatformConsts.IdentityPermissions.Users.Export, PlatformConsts.IdentityPermissions.Users.Import, PlatformConsts.IdentityPermissions.Users.Note), PermissionJson = DefaultPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, listFormName, PlatformConsts.IdentityPermissions.Users.Update, PlatformConsts.IdentityPermissions.Users.Delete, PlatformConsts.IdentityPermissions.Users.Export, PlatformConsts.IdentityPermissions.Users.Import, PlatformConsts.IdentityPermissions.Users.Note),
DeleteCommand = $"UPDATE \"AbpUsers\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id", DeleteCommand = $"UPDATE \"AbpUsers\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(), DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 450, true, true, true, true, false), EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 550, true, true, true, true, false),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() { EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[ new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[
new EditingFormItemDto { Order=1, DataField="Email", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=1, DataField="Email", ColSpan=1, IsRequired=true, EditorType2=EditorTypes.dxTextBox },

View file

@ -538,13 +538,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
IsActive = true, IsActive = true,
IsDeleted = false, IsDeleted = false,
LookupJson = JsonSerializer.Serialize(new LookupDto { LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.StaticData, DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "name", DisplayExpr = "name",
ValueExpr = "key", ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] { LookupQuery = LookupQueryValues.MenuGroupValues,
new () { Key="Kurs", Name="Kurs" },
new () { Key="Erp", Name="Erp" },
}),
}), }),
ColumnCustomizationJson = DefaultColumnCustomizationJson, ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false), PermissionJson = DefaultFieldPermissionJson(TenantManagementPermissions.Tenants.Create, TenantManagementPermissions.Tenants.Default, TenantManagementPermissions.Tenants.Update, true, true, false),
@ -5065,6 +5062,98 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
} }
#endregion #endregion
#region MenuGroup
listFormName = AppCodes.Menus.MenuGroup;
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
{
var listForm = await _listFormRepository.InsertAsync(
new ListForm
{
ListFormType = ListFormTypeEnum.List,
PageSize = 10,
ExportJson = DefaultExportJson,
IsSubForm = false,
ShowNote = true,
LayoutJson = DefaultLayoutJson(),
CultureName = LanguageCodes.En,
ListFormCode = listFormName,
Name = listFormName,
Title = listFormName,
DataSourceCode = SeedConsts.DataSources.DefaultCode,
IsTenant = false,
IsBranch = false,
IsOrganizationUnit = false,
Description = listFormName,
SelectCommandType = SelectCommandTypeEnum.Table,
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.MenuGroup)),
KeyFieldName = "Id",
KeyFieldDbSourceType = DbType.String,
SortMode = GridOptions.SortModeSingle,
DefaultFilter = DefaultFilterJson,
FilterRowJson = DefaultFilterRowJson,
HeaderFilterJson = DefaultHeaderFilterJson,
SearchPanelJson = DefaultSearchPanelJson,
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
SelectionJson = DefaultSelectionSingleJson,
ColumnOptionJson = DefaultColumnOptionJson(),
PermissionJson = DefaultPermissionJson(listFormName),
PagerOptionJson = DefaultPagerOptionJson,
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 350, true, true, true, true, false),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
{
new() { Order = 1, ColCount = 1, ColSpan = 1, ItemType = "group", Items =
[
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
]
}
}),
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.MenuGroup)),
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
});
#region MenuGroup Fields
await _listFormFieldRepository.InsertManyAsync([
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Id",
CaptionName = "App.Listform.ListformField.Id",
Width = 100,
ListOrderNo = 1,
Visible = false,
IsActive = true,
IsDeleted = false,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
},
new()
{
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Name",
CaptionName = "App.Listform.ListformField.Name",
Width = 300,
ListOrderNo = 2,
Visible = true,
IsActive = true,
IsDeleted = false,
AllowSearch = true,
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
PivotSettingsJson = DefaultPivotSettingsJson,
},
]);
#endregion
}
#endregion
#region Menu #region Menu
listFormName = AppCodes.Menus.Menu; listFormName = AppCodes.Menus.Menu;
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName)) if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))

View file

@ -19,6 +19,11 @@ public class RouteSeedDto
public string[] Authority { get; set; } public string[] Authority { get; set; }
} }
public class MenuGroupSeedDto
{
public string Name { get; set; }
}
public class MenuSeedDto public class MenuSeedDto
{ {
public string ParentCode { get; set; } public string ParentCode { get; set; }
@ -36,20 +41,24 @@ public class MenuSeederDto
{ {
public List<RouteSeedDto> Routes { get; set; } public List<RouteSeedDto> Routes { get; set; }
public List<MenuSeedDto> Menus { get; set; } public List<MenuSeedDto> Menus { get; set; }
public List<MenuGroupSeedDto> MenuGroups { get; set; }
} }
public class MenuDataSeeder : IDataSeedContributor, ITransientDependency public class MenuDataSeeder : IDataSeedContributor, ITransientDependency
{ {
private readonly IRepository<Route, Guid> _routeRepository; private readonly IRepository<Route, Guid> _routeRepository;
private readonly IRepository<Menu, Guid> _menuRepository; private readonly IRepository<Menu, Guid> _menuRepository;
private readonly IRepository<MenuGroup, string> _menuGroupRepository;
public MenuDataSeeder( public MenuDataSeeder(
IRepository<Route, Guid> routeRepository, IRepository<Route, Guid> routeRepository,
IRepository<Menu, Guid> menuRepository IRepository<Menu, Guid> menuRepository,
IRepository<MenuGroup, string> menuGroupRepository
) )
{ {
_routeRepository = routeRepository; _routeRepository = routeRepository;
_menuRepository = menuRepository; _menuRepository = menuRepository;
_menuGroupRepository = menuGroupRepository;
} }
public async Task SeedAsync(DataSeedContext context) public async Task SeedAsync(DataSeedContext context)
@ -77,6 +86,19 @@ public class MenuDataSeeder : IDataSeedContributor, ITransientDependency
} }
} }
foreach (var item in items.MenuGroups)
{
var exists = await _menuGroupRepository.AnyAsync(x => x.Name == item.Name);
if (!exists)
{
await _menuGroupRepository.InsertAsync(new MenuGroup(item.Name)
{
Name = item.Name
});
}
}
foreach (var item in items.Menus) foreach (var item in items.Menus)
{ {
var exists = await _menuRepository.AnyAsync(x => x.Code == item.Code); var exists = await _menuRepository.AnyAsync(x => x.Code == item.Code);

View file

@ -400,6 +400,14 @@
"authority": ["App.Contact"] "authority": ["App.Contact"]
} }
], ],
"MenuGroups": [
{
"Name": "Erp"
},
{
"Name": "Kurs"
}
],
"Menus": [ "Menus": [
{ {
"ParentCode": null, "ParentCode": null,
@ -784,11 +792,21 @@
"RequiredPermissionName": "App.Routes", "RequiredPermissionName": "App.Routes",
"IsDisabled": false "IsDisabled": false
}, },
{
"ParentCode": "App.Menus",
"Code": "App.Menus.MenuGroup",
"DisplayName": "App.Menus.MenuGroup",
"Order": 2,
"Url": "/admin/list/App.Menus.MenuGroup",
"Icon": "FcGrid",
"RequiredPermissionName": "App.Menus.MenuGroup",
"IsDisabled": false
},
{ {
"ParentCode": "App.Menus", "ParentCode": "App.Menus",
"Code": "App.Menus.Menu", "Code": "App.Menus.Menu",
"DisplayName": "App.Menus.Menu", "DisplayName": "App.Menus.Menu",
"Order": 2, "Order": 3,
"Url": "/admin/list/App.Menus.Menu", "Url": "/admin/list/App.Menus.Menu",
"Icon": "FcMenu", "Icon": "FcMenu",
"RequiredPermissionName": "App.Menus.Menu", "RequiredPermissionName": "App.Menus.Menu",
@ -798,7 +816,7 @@
"ParentCode": "App.Menus", "ParentCode": "App.Menus",
"Code": "App.Menus.Manager", "Code": "App.Menus.Manager",
"DisplayName": "App.Menus.Manager", "DisplayName": "App.Menus.Manager",
"Order": 3, "Order": 4,
"Url": "/admin/menuManager", "Url": "/admin/menuManager",
"Icon": "FaRegListAlt", "Icon": "FaRegListAlt",
"RequiredPermissionName": "App.Menus.Manager", "RequiredPermissionName": "App.Menus.Manager",

View file

@ -1549,7 +1549,6 @@
"MultiTenancySide": 2, "MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs" "MenuGroup": "Erp|Kurs"
}, },
{ {
"GroupName": "App.Saas", "GroupName": "App.Saas",
"Name": "App.Home", "Name": "App.Home",
@ -1577,7 +1576,6 @@
"MultiTenancySide": 2, "MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs" "MenuGroup": "Erp|Kurs"
}, },
{ {
"GroupName": "App.Saas", "GroupName": "App.Saas",
"Name": "App.Orders.Products", "Name": "App.Orders.Products",
@ -2109,6 +2107,69 @@
"MultiTenancySide": 2, "MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs" "MenuGroup": "Erp|Kurs"
}, },
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup",
"ParentName": null,
"DisplayName": "App.Menus.MenuGroup",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup.Create",
"ParentName": "App.Menus.MenuGroup",
"DisplayName": "Create",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup.Delete",
"ParentName": "App.Menus.MenuGroup",
"DisplayName": "Delete",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup.Export",
"ParentName": "App.Menus.MenuGroup",
"DisplayName": "Export",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup.Import",
"ParentName": "App.Menus.MenuGroup",
"DisplayName": "Import",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup.Note",
"ParentName": "App.Menus.MenuGroup",
"DisplayName": "Note",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{
"GroupName": "App.Saas",
"Name": "App.Menus.MenuGroup.Update",
"ParentName": "App.Menus.MenuGroup",
"DisplayName": "Update",
"IsEnabled": true,
"MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs"
},
{ {
"GroupName": "App.Saas", "GroupName": "App.Saas",
"Name": "App.Menus.Menu", "Name": "App.Menus.Menu",
@ -2271,7 +2332,6 @@
"MultiTenancySide": 2, "MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs" "MenuGroup": "Erp|Kurs"
}, },
{ {
"GroupName": "App.Saas", "GroupName": "App.Saas",
"Name": "App.DeveloperKit", "Name": "App.DeveloperKit",
@ -2470,7 +2530,6 @@
"MultiTenancySide": 2, "MultiTenancySide": 2,
"MenuGroup": "Erp|Kurs" "MenuGroup": "Erp|Kurs"
}, },
{ {
"GroupName": "App.Administration", "GroupName": "App.Administration",
"Name": "App.Setting", "Name": "App.Setting",

View file

@ -18,6 +18,7 @@ public enum TableNameEnum
BranchUsers, BranchUsers,
GlobalSearch, GlobalSearch,
Route, Route,
MenuGroup,
Menu, Menu,
DataSource, DataSource,
ListForm, ListForm,

View file

@ -60,6 +60,7 @@ public static class TableNameResolver
{ nameof(TableNameEnum.Demo), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.Demo), (TablePrefix.PlatformByName, MenuPrefix.Saas) },
{ nameof(TableNameEnum.Contact), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.Contact), (TablePrefix.PlatformByName, MenuPrefix.Saas) },
{ nameof(TableNameEnum.Route), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.Route), (TablePrefix.PlatformByName, MenuPrefix.Saas) },
{ nameof(TableNameEnum.MenuGroup), (TablePrefix.PlatformByName, MenuPrefix.Saas) },
{ nameof(TableNameEnum.Menu), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.Menu), (TablePrefix.PlatformByName, MenuPrefix.Saas) },
{ nameof(TableNameEnum.CrudEndpoint), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.CrudEndpoint), (TablePrefix.PlatformByName, MenuPrefix.Saas) },
{ nameof(TableNameEnum.CustomEndpoint), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.CustomEndpoint), (TablePrefix.PlatformByName, MenuPrefix.Saas) },

View file

@ -341,6 +341,7 @@ public static class SeedConsts
{ {
public const string Default = Prefix.App + ".Menus"; public const string Default = Prefix.App + ".Menus";
public const string MenuGroup = Default + ".MenuGroup";
public const string Menu = Default + ".Menu"; public const string Menu = Default + ".Menu";
public const string Manager = Default + ".Manager"; public const string Manager = Default + ".Manager";
} }

View file

@ -23,4 +23,3 @@ public class Menu : FullAuditedEntity<Guid>
public string RoleId { get; set; } // External role id (orn: ihracat) public string RoleId { get; set; } // External role id (orn: ihracat)
public string CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr" public string CultureName { get; set; } // Bu tanim hangi dil icin "en", "tr"
} }

View file

@ -0,0 +1,13 @@
using Volo.Abp.Domain.Entities.Auditing;
namespace Sozsoft.Platform.Entities;
public class MenuGroup : FullAuditedEntity<string>
{
public string Name { get; set; }
public MenuGroup(string id)
{
Id = id;
}
}

View file

@ -1,6 +1,5 @@
using System; using System;
using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.Platform.Entities; namespace Sozsoft.Platform.Entities;

View file

@ -48,6 +48,7 @@ public class PlatformDbContext :
public DbSet<GlobalSearch> GlobalSearchs { get; set; } public DbSet<GlobalSearch> GlobalSearchs { get; set; }
public DbSet<Route> Routes { get; set; } public DbSet<Route> Routes { get; set; }
public DbSet<Menu> Menus { get; set; } public DbSet<Menu> Menus { get; set; }
public DbSet<MenuGroup> MenuGroups { get; set; }
public DbSet<DataSource> DataSources { get; set; } public DbSet<DataSource> DataSources { get; set; }
public DbSet<ListForm> ListForms { get; set; } public DbSet<ListForm> ListForms { get; set; }
public DbSet<ListFormField> ListFormFields { get; set; } public DbSet<ListFormField> ListFormFields { get; set; }
@ -227,6 +228,14 @@ public class PlatformDbContext :
); );
}); });
builder.Entity<MenuGroup>(b =>
{
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.MenuGroup)), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(a => a.Name).IsRequired().HasMaxLength(128);
});
builder.Entity<Menu>(b => builder.Entity<Menu>(b =>
{ {
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Menu)), Prefix.DbSchema); b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Menu)), Prefix.DbSchema);

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations namespace Sozsoft.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20260317120000_Initial")] [Migration("20260317181749_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -2954,6 +2954,51 @@ namespace Sozsoft.Platform.Migrations
b.ToTable("Sas_H_Menu", (string)null); b.ToTable("Sas_H_Menu", (string)null);
}); });
modelBuilder.Entity("Sozsoft.Platform.Entities.MenuGroup", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.ToTable("Sas_H_MenuGroup", (string)null);
});
modelBuilder.Entity("Sozsoft.Platform.Entities.Note", b => modelBuilder.Entity("Sozsoft.Platform.Entities.Note", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")

View file

@ -1228,6 +1228,25 @@ namespace Sozsoft.Platform.Migrations
table.PrimaryKey("PK_Sas_H_Menu", x => x.Id); table.PrimaryKey("PK_Sas_H_Menu", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "Sas_H_MenuGroup",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Sas_H_MenuGroup", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Sas_H_NotificationRule", name: "Sas_H_NotificationRule",
columns: table => new columns: table => new
@ -3101,6 +3120,9 @@ namespace Sozsoft.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Sas_H_Menu"); name: "Sas_H_Menu");
migrationBuilder.DropTable(
name: "Sas_H_MenuGroup");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Sas_H_Notification"); name: "Sas_H_Notification");

View file

@ -2951,6 +2951,51 @@ namespace Sozsoft.Platform.Migrations
b.ToTable("Sas_H_Menu", (string)null); b.ToTable("Sas_H_Menu", (string)null);
}); });
modelBuilder.Entity("Sozsoft.Platform.Entities.MenuGroup", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.HasKey("Id");
b.ToTable("Sas_H_MenuGroup", (string)null);
});
modelBuilder.Entity("Sozsoft.Platform.Entities.Note", b => modelBuilder.Entity("Sozsoft.Platform.Entities.Note", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react' import React, { useState } from 'react'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { MenuItem } from '@/proxy/menus/menu' import { MenuItem } from '@/proxy/menus/menu'
@ -74,7 +74,9 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
const [isExpanded, setIsExpanded] = useState(true) const [isExpanded, setIsExpanded] = useState(true)
const [isModalOpen, setIsModalOpen] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false)
const [formData, setFormData] = useState<Partial<MenuDto>>({ const [modalMode, setModalMode] = useState<'create' | 'edit'>('create')
const getCreateInitialValues = (): Partial<MenuDto> => ({
code: '', code: '',
displayName: '', displayName: '',
order: (item.children?.length || 0) + 1, order: (item.children?.length || 0) + 1,
@ -88,15 +90,39 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
elementId: '', elementId: '',
}) })
const getEditInitialValues = (): Partial<MenuDto> => ({
id: item.id,
code: item.code || '',
displayName: item.displayName || '',
order: item.order,
parentCode: item.parentCode || '',
url: item.url || '',
icon: item.icon || '',
cssClass: item.cssClass || '',
requiredPermissionName: item.requiredPermissionName || '',
target: item.target || '',
isDisabled: item.isDisabled ?? false,
elementId: item.elementId || '',
})
const [formData, setFormData] = useState<Partial<MenuDto>>(getCreateInitialValues())
const toggleExpanded = () => { const toggleExpanded = () => {
if (!isDesignMode) setIsExpanded(!isExpanded) if (!isDesignMode) setIsExpanded(!isExpanded)
} }
const handleCreate = async () => { const openCreateModal = () => {
const menuService = new MenuService() setModalMode('create')
await menuService.create(formData as MenuDto) setFormData(getCreateInitialValues())
setIsModalOpen(false) setIsModalOpen(true)
refetch() }
const openEditModal = (event: React.MouseEvent) => {
if (!isDesignMode) return
event.stopPropagation()
setModalMode('edit')
setFormData(getEditInitialValues())
setIsModalOpen(true)
} }
const handleDelete = async () => { const handleDelete = async () => {
@ -123,7 +149,7 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
> >
{isDesignMode && ( {isDesignMode && (
<div className="flex gap-2 items-center mr-2"> <div className="flex gap-2 items-center mr-2">
<button onClick={() => setIsModalOpen(true)} title="New Item"> <button onClick={openCreateModal} title="New Item">
<FaPlus size={16} className="text-green-600 hover:text-green-800" /> <FaPlus size={16} className="text-green-600 hover:text-green-800" />
</button> </button>
<button onClick={handleDelete} title="Delete Item"> <button onClick={handleDelete} title="Delete Item">
@ -141,14 +167,17 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
)} )}
</div> </div>
<span <button
type="button"
onClick={openEditModal}
className={` className={`
truncate text-gray-800 leading-6 text-sm truncate text-gray-800 leading-6 text-sm text-left
${item.children && item.children.length > 0 ? 'font-semibold' : 'font-normal'} ${item.children && item.children.length > 0 ? 'font-semibold' : 'font-normal'}
${isDesignMode ? 'hover:text-blue-600' : ''}
`} `}
> >
{translate('::' + item.displayName)} {translate('::' + item.displayName)}
</span> </button>
{item.url && <FaExternalLinkAlt size={12} className="flex-shrink-0 text-gray-400" />} {item.url && <FaExternalLinkAlt size={12} className="flex-shrink-0 text-gray-400" />}
</div> </div>
@ -177,7 +206,9 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
onRequestClose={() => setIsModalOpen(false)} onRequestClose={() => setIsModalOpen(false)}
width={600} width={600}
> >
<h5 className="mb-4">{translate('::New Item')}</h5> <h5 className="mb-4">
{modalMode === 'edit' ? translate('::Edit Menu Item') : translate('::New Item')}
</h5>
<Formik <Formik
validationSchema={validationSchema} validationSchema={validationSchema}
initialValues={formData} initialValues={formData}
@ -185,10 +216,14 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
onSubmit={async (values, { setSubmitting }) => { onSubmit={async (values, { setSubmitting }) => {
try { try {
const menuService = new MenuService() const menuService = new MenuService()
await menuService.create(values as MenuDto) if (modalMode === 'edit' && item.id) {
await menuService.update(item.id, values as MenuDto)
} else {
await menuService.create(values as MenuDto)
}
toast.push( toast.push(
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
{translate('::KayitEklendi')} {modalMode === 'edit' ? translate('::KayitGuncellendi') : translate('::KayitEklendi')}
</Notification>, </Notification>,
{ placement: 'top-end' }, { placement: 'top-end' },
) )
@ -206,13 +241,12 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
} }
}} }}
> >
{({ values, errors, touched, isSubmitting, handleChange }) => ( {({ values, isSubmitting }) => (
<Form> <Form>
<FormContainer> <FormContainer>
<FormItem label="Code *" className="mb-2"> <FormItem label="Code *" className="mb-2">
<Field <Field
type="text" type="text"
au
name="code" name="code"
component={Input} component={Input}
className="h-8 text-sm px-2" className="h-8 text-sm px-2"
@ -247,7 +281,11 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
</FormItem> </FormItem>
<FormItem label="Parent Code" className="mb-2"> <FormItem label="Parent Code" className="mb-2">
<Input disabled value={item.code} className="h-8 text-sm px-2 bg-gray-100" /> <Input
disabled
value={values.parentCode || ''}
className="h-8 text-sm px-2 bg-gray-100"
/>
</FormItem> </FormItem>
<FormItem label="CSS Class" className="mb-2"> <FormItem label="CSS Class" className="mb-2">
@ -308,7 +346,7 @@ export const MenuItemComponent: React.FC<MenuItemComponentProps> = ({
{translate('::Cancel')} {translate('::Cancel')}
</Button> </Button>
<Button type="submit" variant="solid" size="sm" loading={isSubmitting}> <Button type="submit" variant="solid" size="sm" loading={isSubmitting}>
{translate('::Create')} {modalMode === 'edit' ? translate('::Save') : translate('::Create')}
</Button> </Button>
</div> </div>
</FormContainer> </FormContainer>

View file

@ -2,16 +2,11 @@ import React, { useState } from 'react'
import { SortableMenuTree } from './SortableMenuTree' import { SortableMenuTree } from './SortableMenuTree'
import { MenuItem } from '@/proxy/menus/menu' import { MenuItem } from '@/proxy/menus/menu'
import { useMenuData } from '@/utils/hooks/useMenuData' import { useMenuData } from '@/utils/hooks/useMenuData'
import { import { FaRegBell, FaSpinner, FaBars, FaRegSave } from 'react-icons/fa'
FaRegBell,
FaSpinner,
FaBars,
FaRegSave
} from 'react-icons/fa';
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { APP_NAME } from '@/constants/app.constant'; import { APP_NAME } from '@/constants/app.constant'
export const MenuManager = () => { export const MenuManager = () => {
const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData() const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData()
@ -95,7 +90,7 @@ export const MenuManager = () => {
></Helmet> ></Helmet>
<div className="bg-white rounded px-2 sm:px-2 lg:px-3 py-3"> <div className="bg-white rounded px-2 sm:px-2 lg:px-3 py-3">
<div className="flex items-center justify-between mb-6 flex-wrap gap-4"> <div className="flex items-center justify-between mb-2 flex-wrap gap-4">
{/* Sol kısım: Başlık */} {/* Sol kısım: Başlık */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<FaBars size={20} className="text-gray-600" /> <FaBars size={20} className="text-gray-600" />

View file

@ -942,7 +942,7 @@ const Home: React.FC = () => {
isDesignMode={isDesignMode} isDesignMode={isDesignMode}
onSelect={handleSelectBlock} onSelect={handleSelectBlock}
> >
<div className="relative min-h-screen overflow-hidden"> <div className="relative min-h-screen p-20 overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900"></div> <div className="absolute inset-0 bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900"></div>
<div <div
className="absolute inset-0 opacity-20" className="absolute inset-0 opacity-20"
@ -976,7 +976,7 @@ const Home: React.FC = () => {
}} }}
className="h-full" className="h-full"
> >
<div className={`container mx-auto px-4 pt-32 ${slide.styleClass || ''}`}> <div className={`container mx-auto px-4 pt-16 ${slide.styleClass || ''}`}>
<div className="max-w-4xl mx-auto text-center"> <div className="max-w-4xl mx-auto text-center">
<h1 className="text-3xl md:text-6xl font-bold mb-6 text-white animate-fade-in"> <h1 className="text-3xl md:text-6xl font-bold mb-6 text-white animate-fade-in">
{slide.title} {slide.title}