Browse Source

Refactor resource permission management to use extensible table

Moved resource-permission-management component files into a dedicated folder and updated imports. Replaced the manual table implementation with the ExtensibleTableComponent for resource permissions, added client-side pagination, and introduced extension points for entity property configuration. Added new defaults, services, and tokens to support extensibility and improved maintainability.
test-branch-24299
Fahri Gedik 2 months ago
parent
commit
04973110a1
  1. 2
      npm/ng-packs/packages/permission-management/src/lib/components/index.ts
  2. 94
      npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.html
  3. 74
      npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.ts
  4. 34
      npm/ng-packs/packages/permission-management/src/lib/defaults/default-resource-permission-entity-props.ts
  5. 1
      npm/ng-packs/packages/permission-management/src/lib/defaults/index.ts
  6. 1
      npm/ng-packs/packages/permission-management/src/lib/enums/components.ts
  7. 23
      npm/ng-packs/packages/permission-management/src/lib/services/extensions.service.ts
  8. 1
      npm/ng-packs/packages/permission-management/src/lib/services/index.ts
  9. 17
      npm/ng-packs/packages/permission-management/src/lib/tokens/extensions.token.ts
  10. 1
      npm/ng-packs/packages/permission-management/src/lib/tokens/index.ts

2
npm/ng-packs/packages/permission-management/src/lib/components/index.ts

@ -1,2 +1,2 @@
export * from './permission-management.component';
export * from './resource-permission-management.component';
export * from './resource-permission-management/resource-permission-management.component';

94
npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management.component.html → npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.html

@ -1,11 +1,17 @@
<abp-modal [(visible)]="visible" [busy]="modalBusy()" [options]="{ size: 'xl', scrollable: false }">
<ng-template #abpHeader>
<h4>
<h5 class="modal-title">
@if (viewMode() === 'edit') {
{{ 'AbpPermissionManagement::UpdatePermission' | abpLocalization }}
} @else if (viewMode() === 'add') {
{{ 'AbpPermissionManagement::AddResourcePermission' | abpLocalization }}
} @else {
{{ 'AbpPermissionManagement::ResourcePermissions' | abpLocalization }}
@if (resourceDisplayName) {
- {{ resourceDisplayName }}
}
</h4>
}
</h5>
</ng-template>
<ng-template #abpBody>
@ -20,54 +26,40 @@
} @else {
<!-- LIST VIEW -->
@if (viewMode() === 'list') {
<div class="d-grid gap-2 mb-2 d-md-flex justify-content-md-end">
<div class="d-grid gap-2 mb-3 d-md-flex justify-content-md-end">
<button class="btn btn-sm btn-primary" type="button" (click)="goToAddMode()">
<i class="fa fa-plus me-1"></i>
{{ 'AbpPermissionManagement::AddResourcePermission' | abpLocalization }}
</button>
</div>
@if (resourcePermissions().length === 0) {
<ng-template #actionsTemplate let-row>
<div class="btn-group btn-group-sm" role="group">
<button class="btn btn-outline-primary" type="button" (click)="goToEditMode(row)"
[title]="'AbpUi::Edit' | abpLocalization">
<i class="fa fa-edit"></i>
</button>
<button class="btn btn-outline-danger" type="button" (click)="deletePermission(row)"
[title]="'AbpUi::Delete' | abpLocalization">
<i class="fa fa-trash"></i>
</button>
</div>
</ng-template>
@if (resourcePermissions().length > 0) {
<abp-extensible-table [data]="resourcePermissions()" [recordsTotal]="totalCount()" [list]="list"
[actionsTemplate]="actionsTemplate"></abp-extensible-table>
} @else {
<div class="alert alert-info">
{{ 'AbpPermissionManagement::NoPermissionsAssigned' | abpLocalization }}
</div>
} @else {
<table class="table table-striped">
<thead>
<tr>
<th>{{ 'AbpPermissionManagement::Provider' | abpLocalization }}</th>
<th>{{ 'AbpPermissionManagement::ProviderKey' | abpLocalization }}</th>
<th>{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}</th>
<th>{{ 'AbpPermissionManagement::Actions' | abpLocalization }}</th>
</tr>
</thead>
<tbody>
@for (grant of resourcePermissions(); track grant.providerKey) {
<tr>
<td>{{ grant.providerNameDisplayName }}</td>
<td>{{ grant.providerDisplayName || grant.providerKey }}</td>
<td>
@for (perm of grant.permissions; track perm.name) {
<span class="badge bg-primary me-1">{{ perm.displayName }}</span>
}
</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1" (click)="goToEditMode(grant)">
<i class="fa fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger" (click)="deletePermission(grant)">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
}
</tbody>
</table>
}
}
<!-- ADD VIEW -->
@if (viewMode() === 'add') {
<div class="mb-3">
<label class="form-label fw-bold">{{ 'AbpPermissionManagement::SelectProvider' | abpLocalization }}</label>
<div class="mb-2">
@for (provider of providers(); track provider.name; let i = $index) {
<div class="form-check form-check-inline">
@ -82,6 +74,7 @@
</div>
<div class="position-relative mb-3">
<label class="form-label">{{ 'AbpPermissionManagement::SearchProviderKey' | abpLocalization }}</label>
<input type="text" class="form-control"
[placeholder]="'AbpPermissionManagement::SearchProviderKey' | abpLocalization"
[ngModel]="searchFilter()" (ngModelChange)="onSearchInput($event)" />
@ -108,25 +101,25 @@
{{ 'AbpPermissionManagement::GrantAllResourcePermissions' | abpLocalization }}
</label>
</div>
@for (perm of permissionDefinitions(); track perm.name) {
<div class="form-check">
<input class="form-check-input" type="checkbox" [id]="'perm-add-' + perm.name"
[checked]="isPermissionSelected(perm.name || '')" (change)="togglePermission(perm.name || '')" />
<label class="form-check-label" [for]="'perm-add-' + perm.name">
{{ perm.displayName }}
</label>
<div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;">
@for (perm of permissionDefinitions(); track perm.name) {
<div class="form-check">
<input class="form-check-input" type="checkbox" [id]="'perm-add-' + perm.name"
[checked]="isPermissionSelected(perm.name || '')"
(change)="togglePermission(perm.name || '')" />
<label class="form-check-label" [for]="'perm-add-' + perm.name">
{{ perm.displayName }}
</label>
</div>
}
</div>
}
</div>
}
<!-- EDIT VIEW -->
@if (viewMode() === 'edit') {
<div class="mb-3">
<h5>{{ 'AbpPermissionManagement::ResourcePermissionPermissions' | abpLocalization }}</h5>
<p class="text-muted">
{{ editProviderName() }}: {{ editProviderKey() }}
</p>
<div class="mb-3" id="permissionList">
<h4>{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}</h4>
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" id="grantAllEdit" [checked]="allPermissionsSelected()"
(change)="toggleAllPermissions(!allPermissionsSelected())" />
@ -140,9 +133,6 @@
[checked]="isPermissionSelected(perm.name || '')" (change)="togglePermission(perm.name || '')" />
<label class="form-check-label" [for]="'perm-edit-' + perm.name">
{{ perm.displayName }}
@for (provider of perm.providers; track provider) {
<span class="badge bg-secondary ms-1">{{ provider }}</span>
}
</label>
</div>
}

74
npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management.component.ts → npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.ts

@ -1,4 +1,4 @@
import { LocalizationPipe } from '@abp/ng.core';
import { ListService, LocalizationPipe } from '@abp/ng.core';
import {
ButtonComponent,
ModalCloseDirective,
@ -6,10 +6,6 @@ import {
ToasterService,
} from '@abp/ng.theme.shared';
import {
GetResourcePermissionListResultDto,
GetResourceProviderListResultDto,
GetResourcePermissionDefinitionListResultDto,
GetResourcePermissionWithProviderListResultDto,
PermissionsService,
ResourcePermissionGrantInfoDto,
ResourceProviderDto,
@ -18,7 +14,10 @@ import {
ResourcePermissionWithProdiverGrantInfoDto,
} from '@abp/ng.permission-management/proxy';
import {
ChangeDetectionStrategy,
ExtensibleTableComponent,
EXTENSIONS_IDENTIFIER,
} from '@abp/ng.components/extensible';
import {
Component,
EventEmitter,
inject,
@ -26,9 +25,12 @@ import {
Output,
signal,
computed,
OnInit,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { finalize, switchMap, of, debounceTime, Subject, distinctUntilChanged } from 'rxjs';
import { finalize, switchMap, debounceTime, Subject, distinctUntilChanged, of } from 'rxjs';
import { ePermissionManagementComponents } from '../../enums/components';
import { configureResourcePermissionExtensions } from '../../services/extensions.service';
type ViewMode = 'list' | 'add' | 'edit';
@ -36,18 +38,26 @@ type ViewMode = 'list' | 'add' | 'edit';
selector: 'abp-resource-permission-management',
templateUrl: './resource-permission-management.component.html',
exportAs: 'abpResourcePermissionManagement',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
ListService,
{
provide: EXTENSIONS_IDENTIFIER,
useValue: ePermissionManagementComponents.ResourcePermissions,
},
],
imports: [
FormsModule,
ModalComponent,
LocalizationPipe,
ButtonComponent,
ModalCloseDirective,
ExtensibleTableComponent,
],
})
export class ResourcePermissionManagementComponent {
export class ResourcePermissionManagementComponent implements OnInit {
protected readonly service = inject(PermissionsService);
protected readonly toasterService = inject(ToasterService);
readonly list = inject(ListService);
@Input() resourceName!: string;
@Input() resourceKey!: string;
@ -81,7 +91,9 @@ export class ResourcePermissionManagementComponent {
hasProviderKeyLookupService = signal(false);
// Data
resourcePermissions = signal<ResourcePermissionGrantInfoDto[]>([]);
allResourcePermissions = signal<ResourcePermissionGrantInfoDto[]>([]); // All data for client-side pagination
resourcePermissions = signal<ResourcePermissionGrantInfoDto[]>([]); // Paginated data for table
totalCount = signal(0);
providers = signal<ResourceProviderDto[]>([]);
permissionDefinitions = signal<ResourcePermissionDefinitionDto[]>([]);
searchResults = signal<SearchProviderKeyInfo[]>([]);
@ -101,6 +113,9 @@ export class ResourcePermissionManagementComponent {
private searchSubject = new Subject<string>();
constructor() {
// Configure extensions for entity props
configureResourcePermissionExtensions();
this.searchSubject.pipe(
debounceTime(300),
distinctUntilChanged()
@ -109,13 +124,39 @@ export class ResourcePermissionManagementComponent {
});
}
ngOnInit() {
// Configure list service for pagination
this.list.maxResultCount = 10;
// Hook to query for client-side pagination
this.list.hookToQuery(query => {
const allData = this.allResourcePermissions();
const skipCount = query.skipCount || 0;
const maxResultCount = query.maxResultCount || 10;
// Client-side pagination
const paginatedData = allData.slice(skipCount, skipCount + maxResultCount);
return of({
items: paginatedData,
totalCount: allData.length
});
}).subscribe(result => {
this.resourcePermissions.set(result.items);
this.totalCount.set(result.totalCount);
});
}
openModal() {
this.modalBusy.set(true);
// Load resource permissions and providers
this.service.getResource(this.resourceName, this.resourceKey).pipe(
switchMap(permRes => {
this.resourcePermissions.set(permRes.permissions || []);
this.allResourcePermissions.set(permRes.permissions || []);
this.totalCount.set(permRes.permissions?.length || 0);
// Trigger list refresh
this.list.get();
return this.service.getResourceProviderKeyLookupServices(this.resourceName);
}),
switchMap(providerRes => {
@ -140,7 +181,9 @@ export class ResourcePermissionManagementComponent {
resetState() {
this.viewMode.set('list');
this.allResourcePermissions.set([]);
this.resourcePermissions.set([]);
this.totalCount.set(0);
this.selectedProviderName.set('');
this.selectedProviderKey.set('');
this.searchFilter.set('');
@ -276,7 +319,8 @@ export class ResourcePermissionManagementComponent {
finalize(() => this.modalBusy.set(false))
).subscribe({
next: res => {
this.resourcePermissions.set(res.permissions || []);
this.allResourcePermissions.set(res.permissions || []);
this.list.get();
this.toasterService.success('AbpUi::SavedSuccessfully');
this.goToListMode();
}
@ -298,7 +342,8 @@ export class ResourcePermissionManagementComponent {
finalize(() => this.modalBusy.set(false))
).subscribe({
next: res => {
this.resourcePermissions.set(res.permissions || []);
this.allResourcePermissions.set(res.permissions || []);
this.list.get();
this.toasterService.success('AbpUi::SavedSuccessfully');
this.goToListMode();
}
@ -317,7 +362,8 @@ export class ResourcePermissionManagementComponent {
finalize(() => this.modalBusy.set(false))
).subscribe({
next: res => {
this.resourcePermissions.set(res.permissions || []);
this.allResourcePermissions.set(res.permissions || []);
this.list.get();
this.toasterService.success('AbpUi::SuccessfullyDeleted');
}
});

34
npm/ng-packs/packages/permission-management/src/lib/defaults/default-resource-permission-entity-props.ts

@ -0,0 +1,34 @@
import { EntityProp, ePropType } from '@abp/ng.components/extensible';
import { ResourcePermissionGrantInfoDto } from '@abp/ng.permission-management/proxy';
import { of } from 'rxjs';
export const DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS = EntityProp.createMany<ResourcePermissionGrantInfoDto>([
{
type: ePropType.String,
name: 'providerWithKey',
displayName: 'AbpPermissionManagement::Provider',
sortable: false,
valueResolver: data => {
const providerName = data.record.providerName || '';
const providerDisplayName = data.record.providerDisplayName || data.record.providerKey || '';
// Get first letter of provider name for abbreviation
const abbr = providerName.charAt(0).toUpperCase();
return of(
`<span class="d-inline-block bg-light rounded-pill px-2 me-1 ms-1 mb-1" title="${data.record.providerNameDisplayName || providerName}">${abbr}</span>${providerDisplayName}`
);
},
},
{
type: ePropType.String,
name: 'permissions',
displayName: 'AbpPermissionManagement::Permissions',
sortable: false,
valueResolver: data => {
const permissions = data.record.permissions || [];
const pills = permissions
.map(p => `<span class="d-inline-block bg-light rounded-pill px-2 me-1 mb-1">${p.displayName}</span>`)
.join('');
return of(pills);
},
},
]);

1
npm/ng-packs/packages/permission-management/src/lib/defaults/index.ts

@ -0,0 +1 @@
export * from './default-resource-permission-entity-props';

1
npm/ng-packs/packages/permission-management/src/lib/enums/components.ts

@ -1,3 +1,4 @@
export const enum ePermissionManagementComponents {
PermissionManagement = 'PermissionManagement.PermissionManagementComponent',
ResourcePermissions = 'PermissionManagement.ResourcePermissionsComponent',
}

23
npm/ng-packs/packages/permission-management/src/lib/services/extensions.service.ts

@ -0,0 +1,23 @@
import {
ExtensionsService,
mergeWithDefaultProps,
} from '@abp/ng.components/extensible';
import { inject } from '@angular/core';
import {
RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS,
DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS_MAP,
} from '../tokens';
export function configureResourcePermissionExtensions() {
const extensions = inject(ExtensionsService);
const config = { optional: true };
const propContributors = inject(RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS, config) || {};
mergeWithDefaultProps(
extensions.entityProps,
DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS_MAP,
propContributors,
);
}

1
npm/ng-packs/packages/permission-management/src/lib/services/index.ts

@ -0,0 +1 @@
export * from './extensions.service';

17
npm/ng-packs/packages/permission-management/src/lib/tokens/extensions.token.ts

@ -0,0 +1,17 @@
import { ResourcePermissionGrantInfoDto } from '@abp/ng.permission-management/proxy';
import { EntityPropContributorCallback } from '@abp/ng.components/extensible';
import { InjectionToken } from '@angular/core';
import { DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS } from '../defaults/default-resource-permission-entity-props';
import { ePermissionManagementComponents } from '../enums/components';
export const DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS_MAP = {
[ePermissionManagementComponents.ResourcePermissions]: DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS,
};
export const RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS = new InjectionToken<EntityPropContributors>(
'RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS',
);
type EntityPropContributors = Partial<{
[ePermissionManagementComponents.ResourcePermissions]: EntityPropContributorCallback<ResourcePermissionGrantInfoDto>[];
}>;

1
npm/ng-packs/packages/permission-management/src/lib/tokens/index.ts

@ -0,0 +1 @@
export * from './extensions.token';
Loading…
Cancel
Save