application-configuration içerisine Menus alt kısım eklendi

This commit is contained in:
Sedat ÖZTÜRK 2026-10-20 12:20:27 +03:00
parent 679f8fcb89
commit df3294e02a
13 changed files with 216 additions and 163 deletions

View file

@ -1250,7 +1250,7 @@ ekranın kendi yetki hatasını gösterir; ayrıca gizlemek istiyorsan `designer
Gömülen görünüm, hedef ekranın `LayoutJson`'ında açık olmalıdır.
**Public site düğümleri** yalnızca public bileşenlerde (`PublicHeader`, `PublicFooter`, public
sayfalar) anlamlıdır; veriyi uygulama konfigürasyonundan (`publicMenus`, tema, dil) okur:
sayfalar) anlamlıdır; veriyi uygulama konfigürasyonundan (`menus.public`, tema, dil) okur:
| Düğüm | Prop'lar (varsayılan) |
| --- | --- |
@ -1582,7 +1582,7 @@ Intranet de bu moduldedir: menü `Hr.Intranet`, ekran/yetki kodları `Hr.Intrane
(Developer Kit › Intranet Widgets) Saas'ta kalır; kayıtlarının `PermissionName` değerleri `Hr.Intranet.*.Widget`'tır.
İK yetkilerinin `Modules` değeri `Sas|Hr`'dir: host (`Sas`) her zaman görür, kiracı yalnızca modül
listesinde `Hr` varsa görür (modül listesi boş kiracıda süzme yoktur — `MenuAppService`,
listesinde `Hr` varsa görür (modül listesi boş kiracıda süzme yoktur — `MenuNavigationProvider`,
`RolesPermission.tsx`). Yetki ağacı: `Hr.Definitions` → `Hr.SkillType` (1),
`Hr.SkillLevel` (2), `Hr.Skill` (3), `Hr.Department` (4), `Hr.JobPosition` (5);
`Hr.OrgChart` grubun kökünde (2). ListForm ekranlarının alt yetkileri `.Create`, `.Update`,
@ -1685,7 +1685,7 @@ yazma; tasarımcıda yeni bir Custom Component + `Pub` menü kaydı (`Pub.Header
| Route | Bileşen | Veri |
| --- | --- | --- |
| `/home` · `/about` · `/contact` | Custom Component (`PublicHomePage` · `PublicAboutPage` · `PublicContactPage`) | `data/Sas.DeveloperKit.CustomComponents/{Id}.json` · SEO bileşen kaydında |
| (rotasız) | Custom Component (`PublicHeader` · `PublicFooter`) | Üst menü + alt bilgi hızlı bağlantıları: `Pub` menüsü `Pub.Header` (uygulama konfigürasyonunun `publicMenus` alanı) |
| (rotasız) | Custom Component (`PublicHeader` · `PublicFooter`) | Üst menü + alt bilgi hızlı bağlantıları: `Pub` menüsü `Pub.Header` (uygulama konfigürasyonunun `menus.public` alanı) |
| `/products` · `/checkout` · `/payment` · `/success` | `views/public/{Products,Checkout,Payment,Success}` | `Sas_H_Product` · `Sas_H_PaymentMethod` · `Sas_H_InstallmentOption` · `Sas_H_Order` |
| `/blog` · `/blog/:id` | `views/public/{Blog,BlogDetail}` | `Sas_H_BlogPost` · `Sas_H_BlogCategory` |
| `/demo` | `views/public/Demo` | `Sas_H_Demo` |

View file

@ -666,7 +666,7 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
silmek ya da yolunu değiştirmek çerçeve dışıdır.
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
`Pub.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `menus.public` alanıyla taşınır
(menü ucu `[Authorize]`). Sayfa ise Custom Component'tir: `/admin/` ile
başlamayan `RoutePath` otomatik public route olur ve aynı yolu paylaşan fiziksel route'u gölgeler;
`RoutePath` boş bırakılabilir (rotasız bileşen). Yeni public sayfa =

View file

@ -98,7 +98,9 @@ React/C# kodu yazmadan, yalnızca konfigürasyon ile mümkündür.
```
**Akışın özeti:** Kullanıcı giriş yapar → ABP `application-configuration` yanıtına
`PlatformApplicationConfigurationContributor` menü, route ve yetkileri ekler → `DynamicRouter`
`PlatformApplicationConfigurationContributor` menüleri (`extraProperties.menus`: `public` ziyaretçi
menüsü, `protected` kullanıcının yetkili yönetim navigasyonu — ayrı bir `/api/app/menu` isteği yoktur,
süzgeç `MenuNavigationProvider`), route ve yetkileri ekler → `DynamicRouter`
route kayıtlarını React bileşenlerine bağlar → `/admin/list/{ListFormCode}` gibi bir ekran
açıldığında `ListFormsAppService` metadata'yı, `ListFormDataAppService` ise veriyi döner →
DevExtreme bileşenleri bu metadata'ya göre kendini kurar.
@ -988,7 +990,8 @@ Route.Authority ──┘ └► Permission
`data/Sas.Menus.Module.json`): `Sas` → `Sas` (Saas), `Adm` → `Adm`,
`Hr` → `Hr` (İK), `Pub` → `Pub.Header` (public site). Tablo ön eki aynı modülden gelir
(`MenuPrefix`: `Sas_`, `Adm_`, `Hr_`; `TableNameResolver`). Her yetki satırı `Modules`
(pipe ile ayrılmış) değeri taşır; kiracının modül listesi doluysa menü (`MenuAppService`) ve rol
(pipe ile ayrılmış) değeri taşır; kiracının modül listesi doluysa menü (`MenuNavigationProvider`;
hem `menus.protected` hem Menü Yönetimi listesi bu süzgeçten geçer) ve rol
yetki ekranı (`RolesPermission.tsx`) yalnızca kesişen modüllerin yetkilerini gösterir. Sözleşme:
`Sas` host'ta görünürlüğü, `Adm` ve `Hr` kiracıdaki görünürlüğü belirler — Saas/Administration
yetkileri `Sas|Adm`, İK yetkileri `Sas|Hr` taşır.
@ -1133,9 +1136,9 @@ menü bir kez eklenir iki yerde birden görünür; alt kaydı olan bir menü üs
Alt bilgide `cta` kayıtları (Giriş) yer almaz: oradaki düğüm `source="navigation"` ile çizilir. `Url` sayfa yolu, `Icon`
react-icons adı, `Order` sıra, `IsDisabled` yayın anahtarı, `DisplayName` dil anahtarıdır;
`CssClass` içinde `cta` geçen kayıt düz bağlantı yerine vurgulu buton olarak çizilir. Menü ucu
`[Authorize]` olduğu için menüler uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
`[Authorize]` olduğu için menüler uygulama konfigürasyonunun `menus.public` alanıyla taşınır
(`PlatformApplicationConfigurationContributor`), istemcide `usePublicMenu` okur. `Pub` modülü
yönetim navigasyonuna girmez (`abpConfig.model.ts` menüyü kurarken bu modülü eler; Menü Yönetimi
yönetim navigasyonuna (`menus.protected`) girmez (`MenuNavigationProvider` bu modülü eler; Menü Yönetimi
ekranı elemez, menüler oradan düzenlenir). Seed: `configs/seeds/data/Sas.Menus.Module.json` ve
`Sas.Menus.Menu.json`.
@ -1954,6 +1957,9 @@ konfigürasyonu ile üretilir.
- **easy-peasy** global store: `auth`, `theme`, `locale`, `abpConfig`, `admin`, `client`, `base`.
Oturum ve tercih verileri `localStorage` yerine store üzerinden yönetilir (`stateSync.ts`).
Kenar çubuğu (`abpConfig.menu.mainMenu`) ayrı bir menü isteğiyle değil, `getConfig` içinde
application-configuration'ın `extraProperties.menus.protected` listesi + yüklü dil metinlerinden
kurulur; dil, yetki ya da menü kaydından sonra `getConfig` çağrısı menüyü de tazeler.
- **React Query**: sunucu verisi önbellekleme.
- **Context'ler**: `ComponentContext` (runtime bileşen kaydı), `DemoContext` (demo talebi diyaloğu).

View file

@ -0,0 +1,17 @@
namespace Sozsoft.Platform.Menus;
/// <summary>
/// Yönetim navigasyonunun application-configuration ile taşınan menü satırı. Yalnızca
/// istemcinin ağacı kurmak için okuduğu alanları taşır; düzenleme ekranı <see cref="MenuDto"/> kullanır.
/// </summary>
public class MenuNavigationDto
{
public string Code { get; set; }
public string DisplayName { get; set; }
public int Order { get; set; }
public string Url { get; set; }
public string Icon { get; set; }
public string ParentCode { get; set; }
public string RequiredPermissionName { get; set; }
public string ModuleId { get; set; }
}

View file

@ -4,18 +4,14 @@ using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using Sozsoft.Languages.Caching;
using Sozsoft.Languages.Entities;
using Sozsoft.Languages.Languages;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Extensions;
using Sozsoft.Platform.ListForms;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement;
using static Sozsoft.Platform.Data.Seeds.SeedConsts;
using Volo.Abp;
@ -31,8 +27,7 @@ public class MenuAppService : CrudAppService<
private readonly IRepository<Menu, Guid> _menuRepository;
private readonly IRepository<LanguageKey, Guid> _repositoryKey;
private readonly IRepository<LanguageText, Guid> _repositoryText;
private readonly ITenantRepository _tenantRepository;
private readonly IPermissionDefinitionRecordRepository _permissionRepository;
private readonly MenuNavigationProvider _navigationProvider;
private readonly DistributedCacheCleaner _cacheCleaner;
private readonly IRepository<Module, string> _moduleRepository;
private readonly IListFormSeedDataSynchronizer _seedDataSynchronizer;
@ -41,8 +36,7 @@ public class MenuAppService : CrudAppService<
IRepository<Menu, Guid> menuRepository,
IRepository<LanguageKey, Guid> languageKeyRepository,
IRepository<LanguageText, Guid> languageTextRepository,
ITenantRepository tenantRepository,
IPermissionDefinitionRecordRepository permissionRepository,
MenuNavigationProvider navigationProvider,
DistributedCacheCleaner cacheCleaner,
IRepository<Module, string> moduleRepository,
IListFormSeedDataSynchronizer seedDataSynchronizer
@ -51,8 +45,7 @@ public class MenuAppService : CrudAppService<
_menuRepository = menuRepository;
_repositoryKey = languageKeyRepository;
_repositoryText = languageTextRepository;
_tenantRepository = tenantRepository;
_permissionRepository = permissionRepository;
_navigationProvider = navigationProvider;
_cacheCleaner = cacheCleaner;
_moduleRepository = moduleRepository;
_seedDataSynchronizer = seedDataSynchronizer;
@ -68,34 +61,10 @@ public class MenuAppService : CrudAppService<
var query = await CreateFilteredQueryAsync(input);
query = query.Where(a => !a.IsDisabled);
//Tenant üzerinden modül bilgisi alınıp yalnızca o modüllerin menüleri listelenecek
if (CurrentTenant.Id is Guid tenantId)
{
var tenant = await _tenantRepository.FindAsync(tenantId);
if (tenant != null)
{
var tenantModules = tenant.GetModules();
if (tenantModules.Length > 0)
{
var permissionRecords = await _permissionRepository.GetListAsync();
var allowedPermissionNames = permissionRecords
.Where(p => p.GetModules().Intersect(tenantModules).Any())
.Select(p => p.Name)
.Distinct()
.ToList();
query = query.Where(menu =>
string.IsNullOrEmpty(menu.RequiredPermissionName) ||
allowedPermissionNames.Contains(menu.RequiredPermissionName));
}
}
}
query = await _navigationProvider.ApplyTenantModuleFilterAsync(query);
var totalCount = await AsyncExecuter.CountAsync(query);
var entities = new List<Menu>();
var entityDtos = new List<MenuDto>();
if (totalCount > 0)
@ -103,38 +72,7 @@ public class MenuAppService : CrudAppService<
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
var items = await AsyncExecuter.ToListAsync(query);
// Tüm unique permission'ları topla
var uniquePermissions = items
.Where(x => !string.IsNullOrWhiteSpace(x.RequiredPermissionName))
.Select(x => x.RequiredPermissionName)
.Distinct()
.ToList();
// Tüm permission'ları bir kerede kontrol et (N+1 query problemini önler)
var grantedPermissions = new HashSet<string>();
foreach (var permission in uniquePermissions)
{
try
{
if (await AuthorizationService.IsGrantedAsync(permission))
{
grantedPermissions.Add(permission);
}
}
catch (Exception ex)
{
Logger.LogError(ex, $"Permission error for {permission}: {ex.Message}");
}
}
// Sadece yetkili menüleri filtrele
entities = items
.Where(item => string.IsNullOrWhiteSpace(item.RequiredPermissionName) ||
grantedPermissions.Contains(item.RequiredPermissionName))
.ToList();
var entities = await _navigationProvider.FilterGrantedAsync(await AsyncExecuter.ToListAsync(query));
entityDtos = await base.MapToGetListOutputDtosAsync(entities);
var languageKeys = entityDtos
@ -291,22 +229,6 @@ public class MenuAppService : CrudAppService<
await _seedDataSynchronizer.RemoveRowsAsync(AppCodes.Menus.Menu, [menu.Code]);
}
public async Task<List<MenuDto>> GetListMainMenuAsync()
{
await CheckGetPolicyAsync();
var query = await _menuRepository.GetQueryableAsync();
// Public site menusu yonetim kenar cubugunda yer almaz; o agac ziyaretci
// duzenine aittir ve uygulama konfigurasyonu uzerinden tasinir.
query = query.Where(a => !a.IsDisabled && a.ModuleId != null &&
a.ModuleId != PlatformConsts.PublicSite.ModuleId);
var entities = await AsyncExecuter.ToListAsync(query);
var entityDtos = await MapToGetListOutputDtosAsync(entities);
return entityDtos;
}
/// <summary>
/// Menüyü tüm alanlarıyla günceller ve görünen adın dil metinlerini de yazar.
/// Kod (Code) dil anahtarı ve yetki eşleşmesi olduğu için değiştirilemez; gönderilen

View file

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Extensions;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement;
namespace Sozsoft.Platform.Menus;
/// <summary>
/// Oturum açık kullanıcının görebileceği menüleri süzer: kiracının modül listesi dışındaki
/// yetkiye bağlı menü ve kullanıcıya verilmemiş yetkili menü elenir. Yönetim navigasyonu
/// (application-configuration `menus`) ve menü düzenleme listesi aynı süzgeçten geçer; iki
/// kopya olsaydı kenar çubuğu ile düzenleme ekranı farklı menü gösterirdi.
/// </summary>
public class MenuNavigationProvider(
IRepository<Menu, Guid> menuRepository,
ITenantRepository tenantRepository,
IPermissionDefinitionRecordRepository permissionRepository,
ICurrentTenant currentTenant,
IAuthorizationService authorizationService,
IAsyncQueryableExecuter asyncExecuter,
ILogger<MenuNavigationProvider> logger) : ITransientDependency
{
/// <summary>Yönetim kenar çubuğunun menüleri: pasifler ve public site ağacı dışarıda, sıra `Order`.</summary>
public async Task<List<MenuNavigationDto>> GetNavigationAsync()
{
var query = (await menuRepository.GetQueryableAsync())
.Where(menu => !menu.IsDisabled && menu.ModuleId != null &&
menu.ModuleId != PlatformConsts.PublicSite.ModuleId);
query = await ApplyTenantModuleFilterAsync(query);
var menus = await FilterGrantedAsync(await asyncExecuter.ToListAsync(query.OrderBy(menu => menu.Order)));
return menus.Select(menu => new MenuNavigationDto
{
Code = menu.Code,
DisplayName = menu.DisplayName,
Order = menu.Order,
Url = menu.Url,
Icon = menu.Icon,
ParentCode = menu.ParentCode,
RequiredPermissionName = menu.RequiredPermissionName,
ModuleId = menu.ModuleId
}).ToList();
}
/// <summary>
/// Kiracının modül listesi doluysa yalnızca o modüllerin yetkisine bağlı (ya da yetkisiz)
/// menüler kalır; liste boş kiracıda süzme yoktur.
/// </summary>
public async Task<IQueryable<Menu>> ApplyTenantModuleFilterAsync(IQueryable<Menu> query)
{
if (currentTenant.Id is not Guid tenantId)
{
return query;
}
var tenant = await tenantRepository.FindAsync(tenantId);
var tenantModules = tenant?.GetModules() ?? [];
if (tenantModules.Length == 0)
{
return query;
}
var allowedPermissionNames = (await permissionRepository.GetListAsync())
.Where(permission => permission.GetModules().Intersect(tenantModules).Any())
.Select(permission => permission.Name)
.Distinct()
.ToList();
return query.Where(menu =>
string.IsNullOrEmpty(menu.RequiredPermissionName) ||
allowedPermissionNames.Contains(menu.RequiredPermissionName));
}
/// <summary>Yetki gerektiren menülerden yalnızca kullanıcıya verilmiş olanları bırakır; her yetki bir kez sorulur.</summary>
public async Task<List<Menu>> FilterGrantedAsync(IReadOnlyCollection<Menu> menus)
{
var grantedPermissions = new HashSet<string>();
foreach (var permission in menus
.Select(menu => menu.RequiredPermissionName)
.Where(name => !string.IsNullOrWhiteSpace(name))
.Distinct())
{
try
{
if (await authorizationService.IsGrantedAsync(permission))
{
grantedPermissions.Add(permission);
}
}
catch (Exception ex)
{
// Tanımı silinmiş bir yetkiye bağlı menü navigasyonu düşürmemeli; menü gizlenir.
logger.LogError(ex, "Permission check failed for menu permission {Permission}", permission);
}
}
return menus
.Where(menu => string.IsNullOrWhiteSpace(menu.RequiredPermissionName) ||
grantedPermissions.Contains(menu.RequiredPermissionName))
.ToList();
}
}

View file

@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Identity;
using Sozsoft.Platform.Menus;
using Sozsoft.Platform.Public;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.Data;
@ -54,13 +55,20 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura
context.ApplicationConfiguration.SetProperty(
"customComponents", await customComponentRepository.GetListAsync());
// Public site menusu: ziyaretci menu ucunu cagiramaz ([Authorize]), bu yuzden
// rotalar ve custom component'ler gibi anonim erisilen konfigurasyonla tasinir.
var menuRepository = context.ServiceProvider.GetRequiredService<IRepository<Menu, Guid>>();
var publicMenus = await menuRepository.GetListAsync(
menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled);
// Public sayfanin metasi ilk boyamada hazir olmali; ayri bir istek beklenirse
// tarayici basligi ve paylasim karti bir kare gec dolar.
var seoAppService = context.ServiceProvider.GetRequiredService<ISeoAppService>();
context.ApplicationConfiguration.SetProperty("seo", await seoAppService.GetConfigAsync());
context.ApplicationConfiguration.SetProperty("publicMenus", publicMenus
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
// Menüler tek başlıkta taşınır: `public` ziyaretçi menüsüdür (menü ucu [Authorize]
// olduğu için ayrı istek atamaz), `protected` oturum açık kullanıcının yönetim navigasyonudur.
// İstemci dil/yetki değişiminde konfigürasyonu zaten yeniden okur; ayrı bir menü isteği
// aynı veriyi ikinci kez taşırdı.
var menuRepository = context.ServiceProvider.GetRequiredService<IRepository<Menu, Guid>>();
var publicMenus = (await menuRepository.GetListAsync(
menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled))
.OrderBy(menu => menu.Order)
.Select(menu => new
{
@ -74,14 +82,12 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura
target = menu.Target,
requiredPermissionName = menu.RequiredPermissionName
})
.ToList());
.ToList();
var protectedMenus = currentUser.Id.HasValue
? await context.ServiceProvider.GetRequiredService<MenuNavigationProvider>().GetNavigationAsync()
: [];
context.ApplicationConfiguration.SetProperty("menus", new { @public = publicMenus, @protected = protectedMenus });
// Public sayfanin metasi ilk boyamada hazir olmali; ayri bir istek beklenirse
// tarayici basligi ve paylasim karti bir kare gec dolar.
var seoAppService = context.ServiceProvider.GetRequiredService<ISeoAppService>();
context.ApplicationConfiguration.SetProperty("seo", await seoAppService.GetConfigAsync());
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
if (currentUser.Id.HasValue)
{
var sessionRepository = context.ServiceProvider.GetRequiredService<IIdentitySessionRepository>();

View file

@ -1,4 +1,5 @@
import { FullAuditedEntityDto } from '../abp'
import type { PublicMenuItem } from '@/utils/hooks/usePublicMenu'
export interface MenuDto extends FullAuditedEntityDto<string> {
code?: string
@ -19,3 +20,15 @@ export interface MenuDto extends FullAuditedEntityDto<string> {
menuTextTr?: string
menuTextEn?: string
}
/** application-configuration `extraProperties.menus.protected` satırı: yönetim navigasyonunu kuran alanlar. */
export type MenuNavigationDto = Pick<
MenuDto,
'code' | 'displayName' | 'order' | 'url' | 'icon' | 'parentCode' | 'requiredPermissionName' | 'moduleId'
>
/** application-configuration `extraProperties.menus`: ziyaretçi menüsü + oturum açık kullanıcının navigasyonu. */
export interface ConfigMenus {
public: PublicMenuItem[]
protected: MenuNavigationDto[]
}

View file

@ -94,15 +94,6 @@ export class MenuService {
{ apiName: this.apiName },
)
getListMainMenu = () =>
apiService.fetchData<MenuDto[]>(
{
method: 'GET',
url: `/api/app/menu/main-menu`,
},
{ apiName: this.apiName },
)
getModules = () =>
apiService.fetchData<ModuleLookupDto[]>(
{

View file

@ -1,12 +1,11 @@
import type { Action, Thunk } from 'easy-peasy'
import { action, thunk } from 'easy-peasy'
import { Injections, StoreModel } from '.'
import { Injections } from '.'
import { getLocalization, setLocalization } from '../services/localization.service'
import appConfig from '../proxy/configs/app.config'
import { NavigationTree } from '../proxy/menus/navigation'
import { MenuDto } from '../proxy/menus/models'
import { ConfigMenus, MenuNavigationDto } from '../proxy/menus/models'
import getChildren from '../utils/navigation'
import { PUBLIC_MENU_MODULE_ID } from '../utils/hooks/usePublicMenu'
import {
ApplicationConfigurationDto,
ApplicationLocalizationRequestDto,
@ -30,6 +29,23 @@ const getWarmupRoutes = (routes: RouteDto[] | undefined, granted: Record<string,
)
.map((route) => route.componentPath.replace(/^@\//, ''))
/**
* Yönetim kenar çubuğu, application-configuration'ın `extraProperties.menus.protected` alanından kurulur;
* sunucu pasif, public ve yetkisiz menüleri zaten elemiştir. Görünen ad etkin dilin metnidir.
*/
const buildMainMenu = (
menus: MenuNavigationDto[] | undefined,
texts: Texts | undefined,
defaultResourceName: string | undefined,
): NavigationTree[] =>
getChildren(
(menus ?? []).map((menu) => ({
...menu,
displayName: getLocalization(texts ?? {}, defaultResourceName, '::' + menu.displayName),
})),
null,
)
export interface GetConfigOptions {
/** Localization kaynaklarını da zorla yeniden yükle. */
reloadTexts?: boolean
@ -51,7 +67,6 @@ export interface AbpConfigModel {
mainMenu: NavigationTree[]
}
setMenu: Action<AbpConfigModel, NavigationTree[]>
getMenu: Thunk<AbpConfigModel, never, Injections, StoreModel>
}
export const abpConfigModel: AbpConfigModel = {
@ -98,10 +113,15 @@ export const abpConfigModel: AbpConfigModel = {
: [],
)
//Eğer login değilse
if (result.data.currentUser.isAuthenticated) {
await actions.getMenu()
}
actions.setMenu(
result.data.currentUser.isAuthenticated
? buildMainMenu(
(result.data.extraProperties?.['menus'] as ConfigMenus | undefined)?.protected,
helpers.getState().texts,
result.data.localization.defaultResourceName,
)
: [],
)
}),
texts: undefined,
setTexts: action((state, payload) => {
@ -118,34 +138,4 @@ export const abpConfigModel: AbpConfigModel = {
setMenu: action((state, payload) => {
state.menu.mainMenu = [...payload]
}),
getMenu: thunk(async (actions, _, { injections, getState, getStoreState }) => {
const { signedIn } = getStoreState().auth.session
if (!signedIn) {
return
}
const result = await injections.menuService.getList(
{
sorting: 'order',
skipCount: 0,
maxResultCount: 1000,
}
)
const texts = getState().texts
const defaultResourceName = getState().config?.localization.defaultResourceName
// Public site menusu ziyaretci duzenine aittir; yonetim navigasyonunda yer almaz.
const items = (result.data.items ?? []).filter(
(menu: MenuDto) => menu.moduleId !== PUBLIC_MENU_MODULE_ID,
)
items.forEach((menu: MenuDto) => {
const displayName = '::' + menu.displayName
menu.displayName = getLocalization(texts || {}, defaultResourceName, displayName)
})
const menu = getChildren(items, null)
actions.setMenu(menu)
}),
}

View file

@ -9,7 +9,6 @@ import { ClientModel, clientModel } from './client.model'
import { LocaleModel, localeModel } from './locale.model'
import { ThemeModel, themeModel } from './theme.model'
import { refreshToken } from '../services/auth.service'
import { MenuService } from '../services/menu.service'
import { initStateSync, stateSyncMiddleware } from './stateSync'
export interface StoreModel {
@ -22,11 +21,8 @@ export interface StoreModel {
admin: AdminModel
}
const menuService = new MenuService()
const injections = {
abpConfigService,
menuService,
listFormService,
refreshToken,
}

View file

@ -1,9 +1,10 @@
import { useMemo } from 'react'
import { useStoreState } from '@/store'
import type { ConfigMenus } from '@/proxy/menus/models'
/**
* Public site menu kaydi. Menuler `Pub` modulu altinda tutulur ve uygulama
* konfigurasyonunun `publicMenus` alaniyla tasinir; menu ucu `[Authorize]`
* konfigurasyonunun `menus.public` alaniyla tasinir; menu ucu `[Authorize]`
* oldugu icin ziyaretci ayri bir istek atamaz.
*/
export interface PublicMenuItem {
@ -25,9 +26,6 @@ export interface PublicMenuNode extends PublicMenuItem {
/** Ust menude bu sinifi tasiyan kayit duz baglanti yerine vurgulu buton olarak cizilir. */
export const PUBLIC_MENU_CTA_CLASS = 'cta'
/** Public site menulerinin modulu; yonetim navigasyonu bu modulu disarida birakir. */
export const PUBLIC_MENU_MODULE_ID = 'Pub'
/** Ust ve alt bilgiyi cizen Custom Component'lerin adlari. */
export const PUBLIC_HEADER_COMPONENT = 'PublicHeader'
export const PUBLIC_FOOTER_COMPONENT = 'PublicFooter'
@ -51,8 +49,8 @@ function buildChildren(
*/
export function usePublicMenu() {
const publicMenus = useStoreState(
(state) => state.abpConfig.config?.extraProperties?.['publicMenus'],
) as PublicMenuItem[] | undefined
(state) => (state.abpConfig.config?.extraProperties?.['menus'] as ConfigMenus | undefined)?.public,
)
const grantedPolicies = useStoreState((state) => state.abpConfig.config?.auth?.grantedPolicies)
return useMemo(() => {

View file

@ -4,7 +4,7 @@ import {
NAV_ITEM_TYPE_ITEM,
NAV_ITEM_TYPE_TITLE,
} from '@/constants/navigation.constant'
import { MenuDto } from '@/proxy/menus/models'
import { MenuNavigationDto } from '@/proxy/menus/models'
export function navigationTreeToFlat(items: NavigationTree[]): NavigationTree[] {
const result: NavigationTree[] = []
@ -24,7 +24,7 @@ export function navigationTreeToFlat(items: NavigationTree[]): NavigationTree[]
return result
}
export default function getChildren(menu: MenuDto[], parentCode: string | null): NavigationTree[] {
export default function getChildren(menu: MenuNavigationDto[], parentCode: string | null): NavigationTree[] {
const menus: NavigationTree[] = []
for (const child of menu.filter((a) => a.parentCode === parentCode)) {
const item: NavigationTree = {