Hello, I recommend the following steps to achieve this:
First, create a new tab component
//new-feature-management-tab.component.ts
import { LocalizationModule, ReplaceableTemplateDirective } from '@abp/ng.core';
import { FeatureManagementTabComponent } from '@abp/ng.feature-management';
import { Component } from '@angular/core';
import { NewFeatureManagementComponent } from '../new-feature-management/new-feature-management.component';
@Component({
selector: 'app-new-feature-management-tab',
imports: [LocalizationModule, NewFeatureManagementComponent, ReplaceableTemplateDirective],
template: `
<p class="pt-2 text-wrap">
{{ 'AbpFeatureManagement::ManageHostFeaturesText' | abpLocalization }}
</p>
<button class="btn btn-primary" type="button" (click)="openFeaturesModal()">
<i class="me-1 fa fa-cog" aria-hidden="true"></i>
{{ 'AbpFeatureManagement::ManageHostFeatures' | abpLocalization }}
</button>
@if (visibleFeatures) {
<app-new-feature-management
*abpReplaceableTemplate="{
inputs: {
providerName: { value: 'T' },
providerKey: { value: providerKey },
visible: { value: visibleFeatures, twoWay: true }
},
outputs: { visibleChange: onVisibleFeaturesChange },
componentKey: 'FeatureManagement.FeatureManagementComponent'
}"
[(visible)]="visibleFeatures"
providerName="T"
[providerKey]="providerKey"
>
</app-new-feature-management>
}
`,
styleUrl: './new-feature-management-tab.component.scss',
})
export class NewFeatureManagementTabComponent extends FeatureManagementTabComponent {}
Secondly, create a new management component
// new-feature-management.component.ts
@Component({
selector: 'app-new-feature-management',
imports: [
CommonModule,
ThemeSharedModule,
LocalizationModule,
FormsModule,
NgbNavModule,
FreeTextInputDirective,
NgTemplateOutlet,
],
template: `@if (visible) {
<abp-modal [(visible)]="visible" [busy]="modalBusy" [options]="{ size: 'lg' }">
<ng-template #abpHeader>
<h3 class="test">
{{ 'AbpFeatureManagement::Features' | abpLocalization }}
@if (providerTitle) { - {{ providerTitle }}
}
</h3>
</ng-template>
<ng-template #abpBody>
<div class="row">
@if (groups.length) {
<div class="col-md-4">
<ul
ngbNav
#nav="ngbNav"
[(activeId)]="selectedGroupDisplayName"
class="nav-pills"
orientation="vertical"
>
@for (group of groups; track group.name) {
<li [ngbNavItem]="group.displayName">
<a ngbNavLink>{{ group.displayName }}</a>
<ng-template ngbNavContent>
<h4>{{ selectedGroupDisplayName }}</h4>
<hr class="mt-2 mb-3" />
@for (feature of features[group.name]; track feature.id || i; let i = $index) {
<div class="mt-2" [ngStyle]="feature.style" (keyup.enter)="save()">
@switch (feature.valueType?.name) { @case (valueTypes.ToggleStringValueType) {
<div class="form-check" [class.px-4]="!!feature.parentName">
<input
class="form-check-input"
type="checkbox"
[id]="feature.name"
[(ngModel)]="feature.value"
(ngModelChange)="onCheckboxClick($event, feature)"
/>
<label class="form-check-label" [htmlFor]="feature.name">{{
feature.displayName
}}</label>
<ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
></ng-container>
</div>
} @case (valueTypes.FreeTextStringValueType) {
<div class="mb-3 form-group" [class.px-2]="!!feature.parentName">
<label [htmlFor]="feature.name" class="form-label">{{
feature.displayName
}}</label>
<input
class="form-control"
type="text"
[id]="feature.name"
[(ngModel)]="feature.value"
[abpFeatureManagementFreeText]="feature"
/>
<ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
></ng-container>
</div>
} @case (valueTypes.SelectionStringValueType) { @if
(feature.valueType.itemSource?.items?.length) {
<div class="mb-3 form-group" [class.px-2]="!!feature.parentName">
<label [htmlFor]="feature.name" class="form-label">{{
feature.displayName
}}</label>
<select class="form-select" [id]="feature.name" [(ngModel)]="feature.value">
@for ( item of feature.valueType.itemSource?.items; track item.value ) {
<option [ngValue]="item.value">
{{
item.displayText?.resourceName + '::' + item.displayText?.name
| abpLocalization
}}
</option>
}
</select>
<ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
></ng-container>
</div>
} } @default {
{{ feature.displayName }}
} }
</div>
}
</ng-template>
</li>
}
</ul>
</div>
<ng-template #descTmp let-description>
@if (description) {
<small class="d-block form-text text-muted">{{ description }}</small>
}
</ng-template>
<div class="col-md-8"><div class="py-0" [ngbNavOutlet]="nav"></div></div>
} @if (!groups.length) {
<div class="col">
{{ 'AbpFeatureManagement::NoFeatureFoundMessage' | abpLocalization }}
</div>
}
</div>
</ng-template>
<ng-template #abpFooter>
<button abpClose type="button" class="btn btn-link">
{{ 'AbpFeatureManagement::Cancel' | abpLocalization }}
</button>
@if (groups.length) {
<abp-button
buttonClass="btn btn-outline-primary"
[disabled]="modalBusy"
(click)="resetToDefault()"
aria-hidden="true"
>
{{ 'AbpFeatureManagement::ResetToDefault' | abpLocalization }}
</abp-button>
} @if (groups.length) {
<abp-button
iconClass="fa fa-check"
[disabled]="modalBusy"
(click)="save()"
aria-hidden="true"
>
{{ 'AbpFeatureManagement::Save' | abpLocalization }}
</abp-button>
}
</ng-template>
</abp-modal>
} `,
styleUrl: './new-feature-management.component.scss',
})
export class NewFeatureManagementComponent extends FeatureManagementComponent {}
Lastly, update the settings tabs to include your changes
//app.component.ts
import { SettingTabsService } from '@abp/ng.setting-management/config';
export class AppComponent {
protected settingTabs = inject(SettingTabsService);
constructor() {
this.settingTabs.patch('AbpFeatureManagement::Permission:FeatureManagement', {
name: 'New Feature Management',
order: 100,
requiredPolicy: 'FeatureManagement.ManageHostFeatures',
component: NewFeatureManagementTabComponent,
});
}
}
Let me know if you need further assistance.
Hello,
To add a module to your solution, please refer to the following documentation:
👉 Modular Monolith Application Tutorial
Additionally, you can use the ABP CLI to create a new module. Detailed instructions are available here:
👉 Creating a New Module with ABP CLI
Let me know if you need further assistance, or your aim is to utilize the standalone structure for your modules!
Thank you for providing extra details on your environment file.
After checking the open-id configuration request, I see two main issues:
HTTPS Requirement: Your configuration has requireHttps: true, but the discovery document returns HTTP endpoints (not HTTPS) for all endpoints except the issuer.
Issuer URL Mismatch: The endpoints in the discovery document don't start with the issuer URL (they use http:// while the issuer uses https://).
"issuer": "https://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/",
"authorization_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/authorize",
"token_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/token",
"introspection_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/introspect",
"end_session_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/logout",
"revocation_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/revocat",
"userinfo_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/userinfo",
"device_authorization_endpoint": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/device",
"jwks_uri": "http://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/.well-known/jwks",
I can suggest you an optimum solution to configure your auth server to:
You can also manually configure the discovery document as follows: https://manfredsteyer.github.io/angular-oauth2-oidc/docs/additional-documentation/using-an-id-provider-that-fails-discovery-document-validation.html
const oAuthConfig = {
issuer: 'https://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/',
redirectUri: baseUrl,
clientId: 'Bluestar_App',
responseType: 'code',
scope: 'offline_access Bluestar',
requireHttps: true,
strictDiscoveryDocumentValidation: false,
skipIssuerCheck: true,
// Manually specify endpoints with HTTPS
loginUrl: 'https://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/authorize',
tokenEndpoint: 'https://bluestar-authserver.lemonpond-86e62977.centralindia.azurecontainerapps.io/connect/token',
// Add other endpoints as needed
};
It is important to note that using HTTP endpoints or disabling security validations should only be done in development environments. For production, you should always use the https for all endpoints and keep strict validations enabled.
You can let me know if you need further assistance.
Hello,
It appears that the login process has not been configured to require HTTPS. Additionally, the issuer setting may not be correctly specified.
To address this, I recommend adding the following flags to your authConfig in the environment file:
strictDiscoveryDocumentValidation: true,
skipIssuerCheck: true,
If you require further assistance, please feel free to share the relevant details of your authConfig.
To further investigate the console error you're still seeing, could you please provide a minimal, reproducible example that triggers the issue? This will help us pinpoint the root cause much more effectively. You can send it directly to sumeyye.kurtulus@volosoft.com.
If possible, please include:
Appreciate your help — we’ll dive right in as soon as we receive it.
Hello again. Thank you for providing extra details about your problem.
However, I still cannot produce the same problem on our side. Could you please share a minimal, reproducible example via this email: sumeyye.kurtulus@volosoft.com, so that I could assist you further.
Thank you for your cooperation.
Hello, I cannot produce the same problem on my end.
Could you please share your app.config.ts file? Could you also clarify the package manager and its version?
Hello, I cannot produce the problem on my end. Could you please share a minimal, reproducible example via this e-mail address: sumeyye.kurtulus@volosoft.com so that I can assist you further?
Hello! To manage visibility based on user roles, you can use the invisible or requiredPolicy property. These properties help control which navigation items are shown depending on the user's permissions.
You can find more details about these properties in the ABP Framework codebase here.
For example, if you decide to use the invisible property, you can modify the navigation items like this:
import { ABP, AuthService, RoutesService } from '@abp/ng.core';
...
protected routes = inject(RoutesService);
updateRouteVisibility(name: string, invisible: boolean) {
const route = this.routes.find(route => route.name === name);
// const invisible = --you can add your own logic to decide visibility--
if (route) {
this.routes.patch(name, {
invisible: invisible,
} as Partial<ABP.Route>);
}
}
...
Let me know if you need further assistance on that.