mirror of https://github.com/abpframework/abp.git
Browse Source
Introduces ResourcePermissionStateService to centralize and manage state for resource permission management. Refactors resource-permission-management.component and related templates to use the new state service, and extracts form, list, and search UI into dedicated components for improved modularity and maintainability.pull/24459/head
9 changed files with 591 additions and 404 deletions
@ -1,2 +1,6 @@ |
|||||
export * from './permission-management.component'; |
export * from './permission-management.component'; |
||||
export * from './resource-permission-management/resource-permission-management.component'; |
export * from './resource-permission-management/resource-permission-management.component'; |
||||
|
export * from './resource-permission-management/provider-key-search/provider-key-search.component'; |
||||
|
export * from './resource-permission-management/permission-checkbox-list/permission-checkbox-list.component'; |
||||
|
export * from './resource-permission-management/resource-permission-list/resource-permission-list.component'; |
||||
|
export * from './resource-permission-management/resource-permission-form/resource-permission-form.component'; |
||||
|
|||||
@ -0,0 +1,57 @@ |
|||||
|
import { Component, input, inject, ChangeDetectionStrategy } from '@angular/core'; |
||||
|
import { LocalizationPipe } from '@abp/ng.core'; |
||||
|
import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; |
||||
|
|
||||
|
interface PermissionItem { |
||||
|
name?: string | null; |
||||
|
displayName?: string | null; |
||||
|
} |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-permission-checkbox-list', |
||||
|
template: ` |
||||
|
<div class="mb-3"> |
||||
|
@if (showTitle()) { |
||||
|
<h5>{{ title() | abpLocalization }}</h5> |
||||
|
} |
||||
|
<div class="form-check form-switch mb-2"> |
||||
|
<input |
||||
|
class="form-check-input" |
||||
|
type="checkbox" |
||||
|
[id]="'grantAll-' + idPrefix()" |
||||
|
[checked]="state.allPermissionsSelected()" |
||||
|
(change)="state.toggleAllPermissions(!state.allPermissionsSelected())" |
||||
|
/> |
||||
|
<label class="form-check-label" [for]="'grantAll-' + idPrefix()"> |
||||
|
{{ 'AbpPermissionManagement::GrantAllResourcePermissions' | abpLocalization }} |
||||
|
</label> |
||||
|
</div> |
||||
|
<div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;"> |
||||
|
@for (perm of permissions(); track perm.name) { |
||||
|
<div class="form-check"> |
||||
|
<input |
||||
|
class="form-check-input" |
||||
|
type="checkbox" |
||||
|
[id]="'perm-' + idPrefix() + '-' + perm.name" |
||||
|
[checked]="state.isPermissionSelected(perm.name || '')" |
||||
|
(change)="state.togglePermission(perm.name || '')" |
||||
|
/> |
||||
|
<label class="form-check-label" [for]="'perm-' + idPrefix() + '-' + perm.name"> |
||||
|
{{ perm.displayName }} |
||||
|
</label> |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
</div> |
||||
|
`,
|
||||
|
imports: [LocalizationPipe], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
|
}) |
||||
|
export class PermissionCheckboxListComponent { |
||||
|
readonly state = inject(ResourcePermissionStateService); |
||||
|
|
||||
|
readonly permissions = input.required<PermissionItem[]>(); |
||||
|
readonly idPrefix = input<string>('default'); |
||||
|
readonly title = input<string>('AbpPermissionManagement::ResourcePermissionPermissions'); |
||||
|
readonly showTitle = input<boolean>(true); |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
import { Component, input, inject, output, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core'; |
||||
|
import { FormsModule } from '@angular/forms'; |
||||
|
import { LocalizationPipe } from '@abp/ng.core'; |
||||
|
import { PermissionsService, SearchProviderKeyInfo } from '@abp/ng.permission-management/proxy'; |
||||
|
import { Subject, debounceTime, distinctUntilChanged, takeUntil } from 'rxjs'; |
||||
|
import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-provider-key-search', |
||||
|
template: ` |
||||
|
<div class="position-relative"> |
||||
|
<label class="form-label">{{ 'AbpPermissionManagement::SearchProviderKey' | abpLocalization }}</label> |
||||
|
<input |
||||
|
type="text" |
||||
|
class="form-control" |
||||
|
[placeholder]="'AbpPermissionManagement::SearchProviderKey' | abpLocalization" |
||||
|
[ngModel]="state.searchFilter()" |
||||
|
(ngModelChange)="onSearchInput($event)" |
||||
|
(focus)="onSearchFocus()" |
||||
|
(blur)="onSearchBlur($event)" |
||||
|
/> |
||||
|
@if (state.searchResults().length > 0 && state.showDropdown()) { |
||||
|
<div |
||||
|
class="list-group position-absolute w-100 shadow" |
||||
|
style="z-index: 1000; max-height: 200px; overflow-y: auto;" |
||||
|
> |
||||
|
@for (result of state.searchResults(); track result.providerKey) { |
||||
|
<button |
||||
|
type="button" |
||||
|
class="list-group-item list-group-item-action" |
||||
|
(mousedown)="selectProviderKey(result)" |
||||
|
> |
||||
|
{{ result.providerDisplayName || result.providerKey }} |
||||
|
</button> |
||||
|
} |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
`,
|
||||
|
imports: [FormsModule, LocalizationPipe], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
|
}) |
||||
|
export class ProviderKeySearchComponent implements OnInit, OnDestroy { |
||||
|
readonly state = inject(ResourcePermissionStateService); |
||||
|
private readonly service = inject(PermissionsService); |
||||
|
|
||||
|
readonly resourceName = input.required<string>(); |
||||
|
|
||||
|
readonly keySelected = output<SearchProviderKeyInfo>(); |
||||
|
|
||||
|
private readonly searchSubject = new Subject<string>(); |
||||
|
private readonly destroy$ = new Subject<void>(); |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.searchSubject.pipe( |
||||
|
debounceTime(300), |
||||
|
distinctUntilChanged(), |
||||
|
takeUntil(this.destroy$) |
||||
|
).subscribe(filter => { |
||||
|
this.loadProviderKeys(filter); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy() { |
||||
|
this.destroy$.next(); |
||||
|
this.destroy$.complete(); |
||||
|
} |
||||
|
|
||||
|
onSearchInput(filter: string) { |
||||
|
this.state.searchFilter.set(filter); |
||||
|
this.state.showDropdown.set(true); |
||||
|
this.searchSubject.next(filter); |
||||
|
} |
||||
|
|
||||
|
onSearchFocus() { |
||||
|
this.state.showDropdown.set(true); |
||||
|
this.loadProviderKeys(this.state.searchFilter() || ''); |
||||
|
} |
||||
|
|
||||
|
onSearchBlur(event: FocusEvent) { |
||||
|
const relatedTarget = event.relatedTarget as HTMLElement; |
||||
|
if (!relatedTarget?.closest('.list-group')) { |
||||
|
this.state.showDropdown.set(false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
selectProviderKey(key: SearchProviderKeyInfo) { |
||||
|
this.state.selectProviderKey(key); |
||||
|
this.keySelected.emit(key); |
||||
|
} |
||||
|
|
||||
|
private loadProviderKeys(filter: string) { |
||||
|
const providerName = this.state.selectedProviderName(); |
||||
|
if (!providerName) return; |
||||
|
|
||||
|
this.service.searchResourceProviderKey( |
||||
|
this.resourceName(), |
||||
|
providerName, |
||||
|
filter, |
||||
|
1 |
||||
|
).subscribe(res => { |
||||
|
this.state.searchResults.set(res.keys || []); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
import { Component, input, inject, output, ChangeDetectionStrategy } from '@angular/core'; |
||||
|
import { FormsModule } from '@angular/forms'; |
||||
|
import { LocalizationPipe } from '@abp/ng.core'; |
||||
|
import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; |
||||
|
import { ProviderKeySearchComponent } from '../provider-key-search/provider-key-search.component'; |
||||
|
import { PermissionCheckboxListComponent } from '../permission-checkbox-list/permission-checkbox-list.component'; |
||||
|
|
||||
|
export type FormMode = 'add' | 'edit'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-resource-permission-form', |
||||
|
template: ` |
||||
|
@if (mode() === 'add') { |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label fw-bold"> |
||||
|
{{ 'AbpPermissionManagement::SelectProvider' | abpLocalization }} |
||||
|
</label> |
||||
|
<div class="mb-2"> |
||||
|
@for (provider of state.providers(); track provider.name; let i = $index) { |
||||
|
<div class="form-check form-check-inline"> |
||||
|
<input |
||||
|
class="form-check-input" |
||||
|
type="radio" |
||||
|
[id]="'provider-' + i" |
||||
|
[value]="provider.name" |
||||
|
[checked]="state.selectedProviderName() === provider.name" |
||||
|
(change)="state.onProviderChange(provider.name || '')" |
||||
|
/> |
||||
|
<label class="form-check-label" [for]="'provider-' + i"> |
||||
|
{{ provider.displayName }} |
||||
|
</label> |
||||
|
</div> |
||||
|
} |
||||
|
</div> |
||||
|
|
||||
|
<abp-provider-key-search [resourceName]="resourceName()" /> |
||||
|
</div> |
||||
|
|
||||
|
<abp-permission-checkbox-list |
||||
|
[permissions]="state.permissionDefinitions()" |
||||
|
idPrefix="add" |
||||
|
/> |
||||
|
} @else { |
||||
|
<div class="mb-3" id="permissionList"> |
||||
|
<h4>{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}</h4> |
||||
|
<abp-permission-checkbox-list |
||||
|
[permissions]="state.permissionsWithProvider()" |
||||
|
idPrefix="edit" |
||||
|
[showTitle]="false" |
||||
|
/> |
||||
|
</div> |
||||
|
} |
||||
|
`,
|
||||
|
imports: [ |
||||
|
FormsModule, |
||||
|
LocalizationPipe, |
||||
|
ProviderKeySearchComponent, |
||||
|
PermissionCheckboxListComponent, |
||||
|
], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
|
}) |
||||
|
export class ResourcePermissionFormComponent { |
||||
|
readonly state = inject(ResourcePermissionStateService); |
||||
|
|
||||
|
readonly mode = input.required<FormMode>(); |
||||
|
readonly resourceName = input.required<string>(); |
||||
|
|
||||
|
readonly save = output<void>(); |
||||
|
readonly cancel = output<void>(); |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
import { Component, inject, output, ChangeDetectionStrategy } from '@angular/core'; |
||||
|
import { ListService, LocalizationPipe } from '@abp/ng.core'; |
||||
|
import { ExtensibleTableComponent, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible'; |
||||
|
import { ResourcePermissionGrantInfoDto } from '@abp/ng.permission-management/proxy'; |
||||
|
import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; |
||||
|
import { ePermissionManagementComponents } from '../../../enums/components'; |
||||
|
import { configureResourcePermissionExtensions } from '../../../services/extensions.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-resource-permission-list', |
||||
|
template: ` |
||||
|
<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)="addClicked.emit()"> |
||||
|
<i class="fa fa-plus me-1"></i> |
||||
|
{{ 'AbpPermissionManagement::AddResourcePermission' | abpLocalization }} |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<ng-template #actionsTemplate let-row> |
||||
|
<div class="btn-group btn-group-sm" role="group"> |
||||
|
<button |
||||
|
class="btn btn-outline-primary" |
||||
|
type="button" |
||||
|
(click)="editClicked.emit(row)" |
||||
|
[title]="'AbpUi::Edit' | abpLocalization" |
||||
|
> |
||||
|
<i class="fa fa-edit"></i> |
||||
|
</button> |
||||
|
<button |
||||
|
class="btn btn-outline-danger" |
||||
|
type="button" |
||||
|
(click)="deleteClicked.emit(row)" |
||||
|
[title]="'AbpUi::Delete' | abpLocalization" |
||||
|
> |
||||
|
<i class="fa fa-trash"></i> |
||||
|
</button> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
|
||||
|
@if (state.resourcePermissions().length > 0) { |
||||
|
<abp-extensible-table |
||||
|
[data]="state.resourcePermissions()" |
||||
|
[recordsTotal]="state.totalCount()" |
||||
|
[list]="list" |
||||
|
[actionsTemplate]="actionsTemplate" |
||||
|
actionsText="AbpUi::Actions" |
||||
|
></abp-extensible-table> |
||||
|
} @else { |
||||
|
<div class="alert alert-info"> |
||||
|
{{ 'AbpPermissionManagement::NoPermissionsAssigned' | abpLocalization }} |
||||
|
</div> |
||||
|
} |
||||
|
`,
|
||||
|
providers: [ |
||||
|
ListService, |
||||
|
{ |
||||
|
provide: EXTENSIONS_IDENTIFIER, |
||||
|
useValue: ePermissionManagementComponents.ResourcePermissions, |
||||
|
}, |
||||
|
], |
||||
|
imports: [LocalizationPipe, ExtensibleTableComponent], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
|
}) |
||||
|
export class ResourcePermissionListComponent { |
||||
|
readonly state = inject(ResourcePermissionStateService); |
||||
|
readonly list = inject(ListService); |
||||
|
|
||||
|
readonly addClicked = output<void>(); |
||||
|
readonly editClicked = output<ResourcePermissionGrantInfoDto>(); |
||||
|
readonly deleteClicked = output<ResourcePermissionGrantInfoDto>(); |
||||
|
|
||||
|
constructor() { |
||||
|
configureResourcePermissionExtensions(); |
||||
|
} |
||||
|
} |
||||
@ -1,165 +1,60 @@ |
|||||
<abp-modal [(visible)]="visible" [busy]="modalBusy()" [options]="{ size: 'xl', scrollable: false }"> |
<abp-modal [(visible)]="visible" [busy]="state.modalBusy()" [options]="{ size: 'xl', scrollable: false }"> |
||||
<ng-template #abpHeader> |
<ng-template #abpHeader> |
||||
<h5 class="modal-title"> |
<h5 class="modal-title"> |
||||
@if (viewMode() === 'edit') { |
@switch (state.viewMode()) { |
||||
|
@case ('edit') { |
||||
{{ 'AbpPermissionManagement::UpdatePermission' | abpLocalization }} |
{{ 'AbpPermissionManagement::UpdatePermission' | abpLocalization }} |
||||
} @else if (viewMode() === 'add') { |
} |
||||
|
@case ('add') { |
||||
{{ 'AbpPermissionManagement::AddResourcePermission' | abpLocalization }} |
{{ 'AbpPermissionManagement::AddResourcePermission' | abpLocalization }} |
||||
} @else { |
} |
||||
|
@default { |
||||
{{ 'AbpPermissionManagement::ResourcePermissions' | abpLocalization }} |
{{ 'AbpPermissionManagement::ResourcePermissions' | abpLocalization }} |
||||
@if (resourceDisplayName) { |
@if (resourceDisplayName()) { |
||||
- {{ resourceDisplayName }} |
- {{ resourceDisplayName() }} |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
</h5> |
</h5> |
||||
</ng-template> |
</ng-template> |
||||
|
|
||||
<ng-template #abpBody> |
<ng-template #abpBody> |
||||
@if (!hasResourcePermission() || !hasProviderKeyLookupService()) { |
@if (!state.hasResourcePermission() || !state.hasProviderKeyLookupService()) { |
||||
<div class="alert alert-warning" role="alert"> |
<div class="alert alert-warning" role="alert"> |
||||
@if (!hasResourcePermission()) { |
@if (!state.hasResourcePermission()) { |
||||
{{ 'AbpPermissionManagement::NoResourcePermissionFound' | abpLocalization }} |
{{ 'AbpPermissionManagement::NoResourcePermissionFound' | abpLocalization }} |
||||
} @else { |
} @else { |
||||
{{ 'AbpPermissionManagement::NoResourceProviderKeyLookupServiceFound' | abpLocalization }} |
{{ 'AbpPermissionManagement::NoResourceProviderKeyLookupServiceFound' | abpLocalization }} |
||||
} |
} |
||||
</div> |
</div> |
||||
} @else { |
} @else { |
||||
<!-- LIST VIEW --> |
@switch (state.viewMode()) { |
||||
@if (viewMode() === 'list') { |
@case ('list') { |
||||
<div class="d-grid gap-2 mb-3 d-md-flex justify-content-md-end"> |
<abp-resource-permission-list (addClicked)="onAddClicked()" (editClicked)="onEditClicked($event)" |
||||
<button class="btn btn-sm btn-primary" type="button" (click)="goToAddMode()"> |
(deleteClicked)="onDeleteClicked($event)" /> |
||||
<i class="fa fa-plus me-1"></i> |
|
||||
{{ 'AbpPermissionManagement::AddResourcePermission' | abpLocalization }} |
|
||||
</button> |
|
||||
</div> |
|
||||
|
|
||||
<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" actionsText="AbpUi::Actions"></abp-extensible-table> |
|
||||
} @else { |
|
||||
<div class="alert alert-info"> |
|
||||
{{ 'AbpPermissionManagement::NoPermissionsAssigned' | abpLocalization }} |
|
||||
</div> |
|
||||
} |
} |
||||
|
@case ('add') { |
||||
|
<abp-resource-permission-form mode="add" [resourceName]="resourceName()" /> |
||||
} |
} |
||||
|
@case ('edit') { |
||||
<!-- ADD VIEW --> |
<abp-resource-permission-form mode="edit" [resourceName]="resourceName()" /> |
||||
@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"> |
|
||||
<input class="form-check-input" type="radio" [id]="'provider-' + i" [value]="provider.name" |
|
||||
[checked]="selectedProviderName() === provider.name" |
|
||||
(change)="onProviderChange(provider.name || '')" /> |
|
||||
<label class="form-check-label" [for]="'provider-' + i"> |
|
||||
{{ provider.displayName }} |
|
||||
</label> |
|
||||
</div> |
|
||||
} |
|
||||
</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)" (focus)="onSearchFocus()" |
|
||||
(blur)="onSearchBlur()" /> |
|
||||
@if (searchResults().length > 0 && showDropdown()) { |
|
||||
<div class="list-group position-absolute w-100 shadow" |
|
||||
style="z-index: 1000; max-height: 200px; overflow-y: auto;"> |
|
||||
@for (result of searchResults(); track result.providerKey) { |
|
||||
<button type="button" class="list-group-item list-group-item-action" |
|
||||
(mousedown)="selectProviderKey(result)"> |
|
||||
{{ result.providerDisplayName || result.providerKey }} |
|
||||
</button> |
|
||||
} |
|
||||
</div> |
|
||||
} |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="mb-3"> |
|
||||
<h5>{{ 'AbpPermissionManagement::ResourcePermissionPermissions' | abpLocalization }}</h5> |
|
||||
<div class="form-check form-switch mb-2"> |
|
||||
<input class="form-check-input" type="checkbox" id="grantAllAdd" [checked]="allPermissionsSelected()" |
|
||||
(change)="toggleAllPermissions(!allPermissionsSelected())" /> |
|
||||
<label class="form-check-label" for="grantAllAdd"> |
|
||||
{{ 'AbpPermissionManagement::GrantAllResourcePermissions' | abpLocalization }} |
|
||||
</label> |
|
||||
</div> |
|
||||
<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" 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())" /> |
|
||||
<label class="form-check-label" for="grantAllEdit"> |
|
||||
{{ 'AbpPermissionManagement::GrantAllResourcePermissions' | abpLocalization }} |
|
||||
</label> |
|
||||
</div> |
|
||||
@for (perm of permissionsWithProvider(); track perm.name) { |
|
||||
<div class="form-check"> |
|
||||
<input class="form-check-input" type="checkbox" [id]="'perm-edit-' + perm.name" |
|
||||
[checked]="isPermissionSelected(perm.name || '')" (change)="togglePermission(perm.name || '')" /> |
|
||||
<label class="form-check-label" [for]="'perm-edit-' + perm.name"> |
|
||||
{{ perm.displayName }} |
|
||||
</label> |
|
||||
</div> |
|
||||
} |
|
||||
</div> |
|
||||
} |
} |
||||
} |
} |
||||
</ng-template> |
</ng-template> |
||||
|
|
||||
<ng-template #abpFooter> |
<ng-template #abpFooter> |
||||
@if (viewMode() === 'list') { |
@if (state.isListMode()) { |
||||
<button type="button" class="btn btn-outline-primary" abpClose> |
<button type="button" class="btn btn-outline-primary" abpClose> |
||||
{{ 'AbpUi::Close' | abpLocalization }} |
{{ 'AbpUi::Close' | abpLocalization }} |
||||
</button> |
</button> |
||||
} @else { |
} @else { |
||||
<button type="button" class="btn btn-outline-secondary" (click)="goToListMode()"> |
<button type="button" class="btn btn-outline-secondary" (click)="state.goToListMode()"> |
||||
{{ 'AbpUi::Cancel' | abpLocalization }} |
{{ 'AbpUi::Cancel' | abpLocalization }} |
||||
</button> |
</button> |
||||
@if (viewMode() === 'add') { |
<abp-button iconClass="fa fa-check" (click)="savePermission()"> |
||||
<abp-button iconClass="fa fa-check" (click)="saveAddPermission()"> |
|
||||
{{ 'AbpUi::Save' | abpLocalization }} |
{{ 'AbpUi::Save' | abpLocalization }} |
||||
</abp-button> |
</abp-button> |
||||
} @else { |
|
||||
<abp-button iconClass="fa fa-check" (click)="saveEditPermission()"> |
|
||||
{{ 'AbpUi::Save' | abpLocalization }} |
|
||||
</abp-button> |
|
||||
} |
|
||||
} |
} |
||||
</ng-template> |
</ng-template> |
||||
</abp-modal> |
</abp-modal> |
||||
@ -1 +1,2 @@ |
|||||
export * from './extensions.service'; |
export * from './extensions.service'; |
||||
|
export * from './resource-permission-state.service'; |
||||
|
|||||
@ -0,0 +1,165 @@ |
|||||
|
import { Injectable, signal, computed } from '@angular/core'; |
||||
|
import { |
||||
|
ResourcePermissionGrantInfoDto, |
||||
|
ResourceProviderDto, |
||||
|
ResourcePermissionDefinitionDto, |
||||
|
SearchProviderKeyInfo, |
||||
|
ResourcePermissionWithProdiverGrantInfoDto, |
||||
|
} from '@abp/ng.permission-management/proxy'; |
||||
|
|
||||
|
export type ViewMode = 'list' | 'add' | 'edit'; |
||||
|
|
||||
|
@Injectable() |
||||
|
export class ResourcePermissionStateService { |
||||
|
// View state
|
||||
|
readonly viewMode = signal<ViewMode>('list'); |
||||
|
readonly modalBusy = signal(false); |
||||
|
readonly hasResourcePermission = signal(false); |
||||
|
readonly hasProviderKeyLookupService = signal(false); |
||||
|
|
||||
|
// Resource data
|
||||
|
readonly resourceName = signal(''); |
||||
|
readonly resourceKey = signal(''); |
||||
|
readonly resourceDisplayName = signal<string | undefined>(undefined); |
||||
|
|
||||
|
// Permissions data
|
||||
|
readonly allResourcePermissions = signal<ResourcePermissionGrantInfoDto[]>([]); |
||||
|
readonly resourcePermissions = signal<ResourcePermissionGrantInfoDto[]>([]); |
||||
|
readonly totalCount = signal(0); |
||||
|
readonly permissionDefinitions = signal<ResourcePermissionDefinitionDto[]>([]); |
||||
|
readonly permissionsWithProvider = signal<ResourcePermissionWithProdiverGrantInfoDto[]>([]); |
||||
|
readonly selectedPermissions = signal<string[]>([]); |
||||
|
|
||||
|
// Provider data
|
||||
|
readonly providers = signal<ResourceProviderDto[]>([]); |
||||
|
readonly selectedProviderName = signal(''); |
||||
|
readonly selectedProviderKey = signal(''); |
||||
|
|
||||
|
// Edit mode specific
|
||||
|
readonly editProviderName = signal(''); |
||||
|
readonly editProviderKey = signal(''); |
||||
|
|
||||
|
// Search state
|
||||
|
readonly searchFilter = signal(''); |
||||
|
readonly searchResults = signal<SearchProviderKeyInfo[]>([]); |
||||
|
readonly showDropdown = signal(false); |
||||
|
|
||||
|
// Computed properties
|
||||
|
readonly isAddMode = computed(() => this.viewMode() === 'add'); |
||||
|
readonly isEditMode = computed(() => this.viewMode() === 'edit'); |
||||
|
readonly isListMode = computed(() => this.viewMode() === 'list'); |
||||
|
|
||||
|
readonly currentPermissionsList = computed(() => |
||||
|
this.isAddMode() ? this.permissionDefinitions() : this.permissionsWithProvider() |
||||
|
); |
||||
|
|
||||
|
readonly allPermissionsSelected = computed(() => { |
||||
|
const definitions = this.currentPermissionsList(); |
||||
|
return definitions.length > 0 && |
||||
|
definitions.every(p => this.selectedPermissions().includes(p.name || '')); |
||||
|
}); |
||||
|
|
||||
|
readonly canSave = computed(() => { |
||||
|
if (this.isAddMode()) { |
||||
|
return !!this.selectedProviderKey() && this.selectedPermissions().length > 0; |
||||
|
} |
||||
|
return this.selectedPermissions().length >= 0; |
||||
|
}); |
||||
|
|
||||
|
// State transition methods
|
||||
|
goToListMode() { |
||||
|
this.viewMode.set('list'); |
||||
|
this.selectedPermissions.set([]); |
||||
|
} |
||||
|
|
||||
|
goToAddMode() { |
||||
|
this.viewMode.set('add'); |
||||
|
this.selectedPermissions.set([]); |
||||
|
this.selectedProviderKey.set(''); |
||||
|
this.searchResults.set([]); |
||||
|
this.searchFilter.set(''); |
||||
|
} |
||||
|
|
||||
|
prepareEditMode(grant: ResourcePermissionGrantInfoDto) { |
||||
|
this.editProviderName.set(grant.providerName || ''); |
||||
|
this.editProviderKey.set(grant.providerKey || ''); |
||||
|
} |
||||
|
|
||||
|
setEditModePermissions(permissions: ResourcePermissionWithProdiverGrantInfoDto[]) { |
||||
|
this.permissionsWithProvider.set(permissions); |
||||
|
this.selectedPermissions.set( |
||||
|
permissions.filter(p => p.isGranted).map(p => p.name || '') |
||||
|
); |
||||
|
this.viewMode.set('edit'); |
||||
|
} |
||||
|
|
||||
|
// Permission selection methods
|
||||
|
togglePermission(permissionName: string) { |
||||
|
const current = this.selectedPermissions(); |
||||
|
if (current.includes(permissionName)) { |
||||
|
this.selectedPermissions.set(current.filter(p => p !== permissionName)); |
||||
|
} else { |
||||
|
this.selectedPermissions.set([...current, permissionName]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
toggleAllPermissions(selectAll: boolean) { |
||||
|
const permissions = this.currentPermissionsList(); |
||||
|
this.selectedPermissions.set( |
||||
|
selectAll ? permissions.map(p => p.name || '') : [] |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
isPermissionSelected(permissionName: string): boolean { |
||||
|
return this.selectedPermissions().includes(permissionName); |
||||
|
} |
||||
|
|
||||
|
// Provider search methods
|
||||
|
onProviderChange(providerName: string) { |
||||
|
this.selectedProviderName.set(providerName); |
||||
|
this.selectedProviderKey.set(''); |
||||
|
this.searchResults.set([]); |
||||
|
this.searchFilter.set(''); |
||||
|
} |
||||
|
|
||||
|
selectProviderKey(key: SearchProviderKeyInfo) { |
||||
|
this.selectedProviderKey.set(key.providerKey || ''); |
||||
|
this.searchFilter.set(key.providerDisplayName || key.providerKey || ''); |
||||
|
this.searchResults.set([]); |
||||
|
this.showDropdown.set(false); |
||||
|
} |
||||
|
|
||||
|
// Reset all state
|
||||
|
reset() { |
||||
|
this.viewMode.set('list'); |
||||
|
this.allResourcePermissions.set([]); |
||||
|
this.resourcePermissions.set([]); |
||||
|
this.totalCount.set(0); |
||||
|
this.selectedProviderName.set(''); |
||||
|
this.selectedProviderKey.set(''); |
||||
|
this.searchFilter.set(''); |
||||
|
this.selectedPermissions.set([]); |
||||
|
this.searchResults.set([]); |
||||
|
this.editProviderName.set(''); |
||||
|
this.editProviderKey.set(''); |
||||
|
} |
||||
|
|
||||
|
// Data loading helpers
|
||||
|
setResourceData(permissions: ResourcePermissionGrantInfoDto[]) { |
||||
|
this.allResourcePermissions.set(permissions); |
||||
|
this.totalCount.set(permissions.length); |
||||
|
} |
||||
|
|
||||
|
setProviders(providers: ResourceProviderDto[]) { |
||||
|
this.providers.set(providers); |
||||
|
this.hasProviderKeyLookupService.set(providers.length > 0); |
||||
|
if (providers.length) { |
||||
|
this.selectedProviderName.set(providers[0].name || ''); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setDefinitions(permissions: ResourcePermissionDefinitionDto[]) { |
||||
|
this.permissionDefinitions.set(permissions); |
||||
|
this.hasResourcePermission.set(permissions.length > 0); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue