Browse Source

update: permission-management package for the latest upgrade (state management and html properties)

pull/25690/head
sumeyye 2 months ago
parent
commit
befbbf87f1
  1. 2
      npm/ng-packs/packages/permission-management/project.json
  2. 23
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html
  3. 208
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  4. 4
      npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.html

2
npm/ng-packs/packages/permission-management/project.json

@ -31,7 +31,7 @@
"executor": "@nx/vitest:test", "executor": "@nx/vitest:test",
"outputs": ["{options.reportsDirectory}"], "outputs": ["{options.reportsDirectory}"],
"options": { "options": {
"reportsDirectory": "../../coverage/packages/permission-management" "reportsDirectory": "{projectRoot}/../../coverage/packages/permission-management"
} }
} }
} }

23
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html

@ -1,4 +1,11 @@
<abp-modal [(visible)]="visible" [busy]="modalBusy" [options]="{ size: 'lg', scrollable: false }"> <abp-modal
[(visible)]="modalVisible"
(visibleChange)="onModalVisibleChange($event)"
(disappear)="onModalDisappear()"
[busy]="modalBusy"
[suppressUnsavedChangesWarning]="true"
[options]="{ size: 'lg', scrollable: false }"
>
@if (data.entityDisplayName || entityDisplayName()) { @if (data.entityDisplayName || entityDisplayName()) {
<ng-template #abpHeader> <ng-template #abpHeader>
<h4> <h4>
@ -17,7 +24,7 @@
id="permission-search" id="permission-search"
placeholder="Filter" placeholder="Filter"
[ngModel]="filter()" [ngModel]="filter()"
(ngModelChange)="filter.set($event)" (ngModelChange)="onFilterChange($event)"
/> />
</div> </div>
</div> </div>
@ -29,8 +36,8 @@
id="select-all-in-all-tabs" id="select-all-in-all-tabs"
name="select-all-in-all-tabs" name="select-all-in-all-tabs"
class="form-check-input" class="form-check-input"
[(ngModel)]="selectAllTab" [checked]="selectAllTab"
(click)="onClickSelectAll()" (change)="onSelectAllChange($event)"
[disabled]="disabledSelectAllInAllTabs" [disabled]="disabledSelectAllInAllTabs"
/> />
<label class="form-check-label" for="select-all-in-all-tabs">{{ <label class="form-check-label" for="select-all-in-all-tabs">{{
@ -92,9 +99,9 @@
id="select-all-in-this-tabs" id="select-all-in-this-tabs"
name="select-all-in-this-tabs" name="select-all-in-this-tabs"
class="form-check-input" class="form-check-input"
[(ngModel)]="selectThisTab" [checked]="selectThisTab"
[disabled]="disableSelectAllTab" [disabled]="disableSelectAllTab"
(click)="onClickSelectThisTab()" (change)="onSelectThisTabChange($event)"
/> />
<label class="form-check-label" for="select-all-in-this-tabs">{{ <label class="form-check-label" for="select-all-in-this-tabs">{{
'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization 'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization
@ -111,7 +118,7 @@
[attr.id]="permission.name" [attr.id]="permission.name"
class="form-check-input" class="form-check-input"
[disabled]="isGrantedByOtherProviderName(permission.grantedProviders)" [disabled]="isGrantedByOtherProviderName(permission.grantedProviders)"
(click)="onClickCheckbox(permission, permissionCheckbox.value)" (click)="onClickCheckbox(permission)"
/> />
<label class="form-check-label" [attr.for]="permission.name" <label class="form-check-label" [attr.for]="permission.name"
>{{ permission.displayName }} >{{ permission.displayName }}
@ -138,7 +145,7 @@
<button type="button" class="btn btn-outline-primary" abpClose> <button type="button" class="btn btn-outline-primary" abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }} {{ 'AbpIdentity::Cancel' | abpLocalization }}
</button> </button>
<abp-button iconClass="fa fa-check" (click)="submit()">{{ <abp-button iconClass="fa fa-check" buttonType="button" (click)="submit()">{{
'AbpIdentity::Save' | abpLocalization 'AbpIdentity::Save' | abpLocalization
}}</abp-button> }}</abp-button>
</ng-template> </ng-template>

208
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts

@ -20,7 +20,6 @@ import {
Component, Component,
computed, computed,
DOCUMENT, DOCUMENT,
effect,
ElementRef, ElementRef,
inject, inject,
Injector, Injector,
@ -28,7 +27,6 @@ import {
output, output,
signal, signal,
TrackByFunction, TrackByFunction,
untracked,
viewChildren, viewChildren,
} from '@angular/core'; } from '@angular/core';
import { of } from 'rxjs'; import { of } from 'rxjs';
@ -40,7 +38,7 @@ import { FormsModule } from '@angular/forms';
import { Tabs, TabList, Tab, TabPanel, TabContent } from '@angular/aria/tabs'; import { Tabs, TabList, Tab, TabPanel, TabContent } from '@angular/aria/tabs';
type PermissionWithStyle = PermissionGrantInfoDto & { type PermissionWithStyle = PermissionGrantInfoDto & {
style: string; style: Record<string, number>;
}; };
type PermissionWithGroupName = PermissionGrantInfoDto & { type PermissionWithGroupName = PermissionGrantInfoDto & {
@ -126,11 +124,13 @@ export class PermissionManagementComponent {
readonly entityDisplayName = input<string | undefined>(undefined); readonly entityDisplayName = input<string | undefined>(undefined);
readonly visibleInput = input(false, { alias: 'visible' }); readonly visibleInput = input(false, { alias: 'visible' });
// Output signals
readonly visibleChange = output<boolean>(); readonly visibleChange = output<boolean>();
// Internal state protected readonly modalVisible = signal(false);
protected readonly _visible = signal(false);
private isOpening = false;
private isClosingModal = false;
// Backward-compatible getters/setters for ReplaceableTemplateDirective. // Backward-compatible getters/setters for ReplaceableTemplateDirective.
private _providerNameOverride?: string; private _providerNameOverride?: string;
@ -184,77 +184,89 @@ export class PermissionManagementComponent {
permissionGroups = computed(() => { permissionGroups = computed(() => {
const search = this.filter().toLowerCase().trim(); const search = this.filter().toLowerCase().trim();
let groups = this.permissionGroupSignal(); const groups = this.permissionGroupSignal();
if (!search) { if (!search) {
this.setSelectedGroup(groups[0]);
return groups; return groups;
} }
const includesSearch = text => text.toLowerCase().includes(search); const includesSearch = (text: string) => text.toLowerCase().includes(search);
groups = groups.filter(group => return groups.filter(group =>
group.permissions.some( group.permissions.some(
permission => includesSearch(permission.displayName) || includesSearch(group.displayName), permission => includesSearch(permission.displayName) || includesSearch(group.displayName),
), ),
); );
if (groups.length) {
this.setSelectedGroup(groups[0]);
} else {
this.selectedGroupPermissions = [];
}
return groups;
}); });
trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name; trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name;
// Getter/setter for visible - used by ReplaceableTemplateDirective and internal code // Backward-compatible getter/setter for ReplaceableTemplateDirective.
get visible(): boolean { get visible(): boolean {
return this._visible(); return this.modalVisible();
} }
set visible(value: boolean) { set visible(value: boolean) {
if (value === this._visible()) { if (value && this.isClosingModal) {
return;
}
if (value === this.modalVisible()) {
return; return;
} }
if (value) { if (value) {
this.openModal().subscribe(() => { this.showModal();
this._visible.set(true); } else {
this.hideModal();
}
}
onModalVisibleChange(value: boolean) {
this.visibleChange.emit(value);
}
onModalDisappear() {
setTimeout(() => {
if (!this.modalVisible()) {
this.resetModalState();
}
this.isClosingModal = false;
});
}
private showModal() {
if (this.isOpening || this.modalVisible()) {
return;
}
this.isClosingModal = false;
this.isOpening = true;
this.openModal()
.pipe(finalize(() => (this.isOpening = false)))
.subscribe(() => {
this.modalVisible.set(true);
this.visibleChange.emit(true); this.visibleChange.emit(true);
afterNextRender(() => { afterNextRender(() => {
this.initModal(); this.initModal();
}, { injector: this.injector }); }, { injector: this.injector });
}); });
} else { }
this.setSelectedGroup(null);
this._visible.set(false); private hideModal() {
this.visibleChange.emit(false); if (!this.modalVisible()) {
this.filter.set(''); return;
} }
this.isClosingModal = true;
this.visibleChange.emit(false);
this.modalVisible.set(false);
} }
constructor() { private resetModalState() {
effect(() => { this.setSelectedGroup(null);
const inputValue = this.visibleInput(); this.filter.set('');
untracked(() => { this.selectAllTab = false;
if (this._visible() !== inputValue) { this.selectThisTab = false;
if (inputValue) {
this.openModal().subscribe(() => {
this._visible.set(true);
afterNextRender(() => {
this.initModal();
}, { injector: this.injector });
});
} else {
this.setSelectedGroup(null);
this._visible.set(false);
this.filter.set('');
}
}
});
});
} }
getChecked(name: string) { getChecked(name: string) {
@ -275,14 +287,11 @@ export class PermissionManagementComponent {
const permissions = const permissions =
(this.data.groups.find(group => group.name === this.selectedGroup?.name) || {}).permissions || (this.data.groups.find(group => group.name === this.selectedGroup?.name) || {}).permissions ||
[]; [];
this.selectedGroupPermissions = permissions.map( this.selectedGroupPermissions = permissions.map(permission => ({
permission => ...permission,
({ style: { [margin]: findMargin(permissions, permission) },
...permission, isGranted: (this.permissions.find(per => per.name === permission.name) || {}).isGranted,
style: { [margin]: findMargin(permissions, permission) }, }));
isGranted: (this.permissions.find(per => per.name === permission.name) || {}).isGranted,
}) as unknown as PermissionWithStyle,
);
} }
setDisabled(permissions: PermissionGrantInfoDto[]) { setDisabled(permissions: PermissionGrantInfoDto[]) {
@ -297,8 +306,8 @@ export class PermissionManagementComponent {
} }
} }
isGrantedByOtherProviderName(grantedProviders: ProviderInfoDto[]): boolean { isGrantedByOtherProviderName(grantedProviders?: ProviderInfoDto[]): boolean {
if (grantedProviders.length) { if (grantedProviders?.length) {
return grantedProviders.findIndex(p => p.providerName !== this.providerName) > -1; return grantedProviders.findIndex(p => p.providerName !== this.providerName) > -1;
} }
return false; return false;
@ -390,11 +399,13 @@ export class PermissionManagementComponent {
setTabCheckboxState() { setTabCheckboxState() {
const providerName = this.providerName; const providerName = this.providerName;
const selectablePermissions = this.selectedGroupPermissions.filter(per => const selectablePermissions = this.selectedGroupPermissions.filter(per =>
per.grantedProviders.every(p => p.providerName === providerName), (per.grantedProviders ?? []).every(p => p.providerName === providerName),
); );
const selectedPermissions = selectablePermissions.filter(per => per.isGranted); const selectedPermissions = selectablePermissions.filter(per => per.isGranted);
const element = this.document.querySelector('#select-all-in-this-tabs') as any; const element = this.document.querySelector(
'#select-all-in-this-tabs',
) as HTMLInputElement | null;
if (!element) { if (!element) {
return; return;
} }
@ -413,10 +424,16 @@ export class PermissionManagementComponent {
setGrantCheckboxState() { setGrantCheckboxState() {
const providerName = this.providerName; const providerName = this.providerName;
const selectablePermissions = this.permissions.filter(per => const selectablePermissions = this.permissions.filter(per =>
per.grantedProviders.every(p => p.providerName === providerName), (per.grantedProviders ?? []).every(p => p.providerName === providerName),
); );
const selectedAllPermissions = selectablePermissions.filter(per => per.isGranted); const selectedAllPermissions = selectablePermissions.filter(per => per.isGranted);
const checkboxElement = this.document.querySelector('#select-all-in-all-tabs') as any; const checkboxElement = this.document.querySelector(
'#select-all-in-all-tabs',
) as HTMLInputElement | null;
if (!checkboxElement) {
return;
}
if (selectedAllPermissions.length === selectablePermissions.length) { if (selectedAllPermissions.length === selectablePermissions.length) {
checkboxElement.indeterminate = false; checkboxElement.indeterminate = false;
@ -429,42 +446,69 @@ export class PermissionManagementComponent {
} }
} }
onClickSelectThisTab() { onSelectThisTabChange(event: Event) {
const checked = (event.target as HTMLInputElement).checked;
this.selectThisTab = checked;
this.selectedGroupPermissions.forEach(permission => { this.selectedGroupPermissions.forEach(permission => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders)) if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders)) {
return; return;
}
const index = this.permissions.findIndex(per => per.name === permission.name); const index = this.permissions.findIndex(per => per.name === permission.name);
this.permissions = [ this.permissions = [
...this.permissions.slice(0, index), ...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab }, { ...this.permissions[index], isGranted: checked },
...this.permissions.slice(index + 1), ...this.permissions.slice(index + 1),
]; ];
}); });
this.onChangeGroup(this.selectedGroup!);
this.setGrantCheckboxState(); this.setGrantCheckboxState();
} }
onClickSelectAll() { onSelectAllChange(event: Event) {
const checked = (event.target as HTMLInputElement).checked;
this.selectAllTab = checked;
if (this.filter()) { if (this.filter()) {
this.filter.set(''); this.onFilterChange('');
} }
this.permissions = this.permissions.map(permission => ({ this.permissions = this.permissions.map(permission => ({
...permission, ...permission,
isGranted: isGranted: this.isGrantedByOtherProviderName(permission.grantedProviders) || checked,
this.isGrantedByOtherProviderName(permission.grantedProviders) || !this.selectAllTab,
})); }));
if (!this.disableSelectAllTab) { if (!this.disableSelectAllTab) {
this.selectThisTab = !this.selectAllTab; this.selectThisTab = checked;
this.setTabCheckboxState(); this.setTabCheckboxState();
if (this.filter()) {
this.setGrantCheckboxState();
}
} }
this.onChangeGroup(this.selectedGroup);
if (this.selectedGroup) {
this.onChangeGroup(this.selectedGroup);
}
this.setGrantCheckboxState();
}
onFilterChange(value: string) {
this.filter.set(value);
this.syncSelectedGroupWithFilter();
}
private syncSelectedGroupWithFilter() {
const groups = this.permissionGroups();
if (!groups.length) {
this.setSelectedGroup(null);
return;
}
if (!groups.some(group => group.name === this.selectedGroup?.name)) {
this.onChangeGroup(groups[0]);
}
} }
onTabChange(groupName: string) { onTabChange(groupName: string) {
@ -481,6 +525,10 @@ export class PermissionManagementComponent {
} }
submit() { submit() {
if (this.modalBusy) {
return;
}
const unchangedPermissions = getPermissions(this.data.groups); const unchangedPermissions = getPermissions(this.data.groups);
const changedPermissions: UpdatePermissionDto[] = this.permissions const changedPermissions: UpdatePermissionDto[] = this.permissions
@ -493,7 +541,7 @@ export class PermissionManagementComponent {
.map(({ name, isGranted }) => ({ name, isGranted })); .map(({ name, isGranted }) => ({ name, isGranted }));
if (!changedPermissions.length) { if (!changedPermissions.length) {
this.visible = false; this.hideModal();
return; return;
} }
@ -501,13 +549,17 @@ export class PermissionManagementComponent {
this.service this.service
.update(this.providerName, this.providerKey, { permissions: changedPermissions }) .update(this.providerName, this.providerKey, { permissions: changedPermissions })
.pipe( .pipe(
tap(() => this.hideModal()),
switchMap(() => switchMap(() =>
this.shouldFetchAppConfig() ? this.configState.refreshAppState() : of(null), this.shouldFetchAppConfig() ? this.configState.refreshAppState() : of(null),
), ),
finalize(() => (this.modalBusy = false)), finalize(() => {
if (this.modalBusy) {
this.modalBusy = false;
}
}),
) )
.subscribe(() => { .subscribe(() => {
this.visible = false;
this.toasterService.success('AbpUi::SavedSuccessfully'); this.toasterService.success('AbpUi::SavedSuccessfully');
}); });
} }
@ -532,7 +584,7 @@ export class PermissionManagementComponent {
this.disabledSelectAllInAllTabs = this.permissions.every( this.disabledSelectAllInAllTabs = this.permissions.every(
per => per =>
per.isGranted && per.isGranted &&
per.grantedProviders.every(provider => provider.providerName !== providerName), (per.grantedProviders ?? []).every(provider => provider.providerName !== providerName),
); );
}), }),
); );

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

@ -41,10 +41,10 @@
/> />
} }
@case (eResourcePermissionViewModes.Add) { @case (eResourcePermissionViewModes.Add) {
<abp-resource-permission-form mode="add" [resourceName]="resourceName()" /> <abp-resource-permission-form [mode]="eResourcePermissionViewModes.Add" [resourceName]="resourceName()" />
} }
@case (eResourcePermissionViewModes.Edit) { @case (eResourcePermissionViewModes.Edit) {
<abp-resource-permission-form mode="edit" [resourceName]="resourceName()" /> <abp-resource-permission-form [mode]="eResourcePermissionViewModes.Edit" [resourceName]="resourceName()" />
} }
} }
} }

Loading…
Cancel
Save