From ec91f9ee4ff658a0f57ab356eaeb109ac0f37ed2 Mon Sep 17 00:00:00 2001 From: Sk Shaifat Murshed Date: Thu, 30 Jan 2025 19:02:44 +0530 Subject: [PATCH 1/2] User & Role Override --- angular/package.json | 3 +- angular/src/app/app-routing.module.ts | 12 +- .../custom-identity/custom-identity.module.ts | 23 ++ .../custom-roles/custom-roles.component.html | 78 +++++++ .../custom-roles/custom-roles.component.scss | 0 .../custom-roles.component.spec.ts | 23 ++ .../custom-roles/custom-roles.component.ts | 162 ++++++++++++++ .../custom-users/custom-users.component.html | 147 ++++++++++++ .../custom-users/custom-users.component.scss | 0 .../custom-users.component.spec.ts | 23 ++ .../custom-users/custom-users.component.ts | 211 ++++++++++++++++++ angular/src/app/route.provider.ts | 2 + 12 files changed, 679 insertions(+), 5 deletions(-) create mode 100644 angular/src/app/modules/custom-identity/custom-identity.module.ts create mode 100644 angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html create mode 100644 angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.scss create mode 100644 angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.spec.ts create mode 100644 angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts create mode 100644 angular/src/app/modules/custom-identity/custom-users/custom-users.component.html create mode 100644 angular/src/app/modules/custom-identity/custom-users/custom-users.component.scss create mode 100644 angular/src/app/modules/custom-identity/custom-users/custom-users.component.spec.ts create mode 100644 angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts diff --git a/angular/package.json b/angular/package.json index 8c576f9..36084b1 100644 --- a/angular/package.json +++ b/angular/package.json @@ -20,7 +20,7 @@ "@abp/ng.setting-management": "~9.0.2", "@abp/ng.tenant-management": "~9.0.2", "@abp/ng.theme.lepton-x": "~4.0.3", - "@abp/ng.theme.shared": "~9.0.2", + "@abp/ng.theme.shared": "^9.0.4", "@angular/animations": "~18.1.0", "@angular/common": "~18.1.0", "@angular/compiler": "~18.1.0", @@ -30,6 +30,7 @@ "@angular/platform-browser": "~18.1.0", "@angular/platform-browser-dynamic": "~18.1.0", "@angular/router": "~18.1.0", + "@swimlane/ngx-datatable": "^20.1.0", "bootstrap-icons": "~1.8.0", "primeflex": "^3.3.1", "primeicons": "^6.0.1", diff --git a/angular/src/app/app-routing.module.ts b/angular/src/app/app-routing.module.ts index d7050db..344da08 100644 --- a/angular/src/app/app-routing.module.ts +++ b/angular/src/app/app-routing.module.ts @@ -1,5 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { CustomUsersComponent } from './modules/custom-identity/custom-users/custom-users.component'; +import { CustomRolesComponent } from './modules/custom-identity/custom-roles/custom-roles.component'; const routes: Routes = [ { @@ -11,10 +13,12 @@ const routes: Routes = [ path: 'account', loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()), }, - { - path: 'identity', - loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()), - }, + { path: 'identity/users', component: CustomUsersComponent }, + { path: 'identity/roles', component: CustomRolesComponent }, + // { + // path: 'identity', + // loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()), + // }, { path: 'tenant-management', loadChildren: () => diff --git a/angular/src/app/modules/custom-identity/custom-identity.module.ts b/angular/src/app/modules/custom-identity/custom-identity.module.ts new file mode 100644 index 0000000..f84eb9b --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-identity.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IdentityModule } from '@abp/ng.identity'; +import { TableModule } from 'primeng/table'; +import { ButtonModule } from 'primeng/button'; +import { CustomRolesComponent } from './custom-roles/custom-roles.component'; + + + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + IdentityModule, + TableModule, + ButtonModule, + CustomRolesComponent + ], + exports:[ + CustomRolesComponent + ] +}) +export class CustomIdentityModule { } diff --git a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html new file mode 100644 index 0000000..acf212d --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html @@ -0,0 +1,78 @@ + +
+
+ +
+ +
+ + + + name + + Actions + + + + + + {{ role.name }} + + + + + + + + + + + +
+
+ + + +

{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}

+
+ + +
+ +
+
+ + + + {{ + 'AbpIdentity::Save' | abpLocalization + }} + +
+ + + +
+ \ No newline at end of file diff --git a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.scss b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.spec.ts b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.spec.ts new file mode 100644 index 0000000..dcb56ce --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CustomRolesComponent } from './custom-roles.component'; + +describe('CustomRolesComponent', () => { + let component: CustomRolesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CustomRolesComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CustomRolesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts new file mode 100644 index 0000000..693138f --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts @@ -0,0 +1,162 @@ +import { + ExtensibleModule, + EXTENSIONS_IDENTIFIER, + FormPropData, + generateFormFromProps, +} from '@abp/ng.components/extensible'; +import { PageModule } from '@abp/ng.components/page'; +import { + CoreModule, + ListResultDto, + ListService, + LocalizationModule, + PagedAndSortedResultRequestDto, + PagedResultDto, +} from '@abp/ng.core'; +import { IdentityRoleDto, IdentityRoleService } from '@abp/ng.identity/proxy'; +import { + ThemeSharedModule, + ConfirmationService, + ToasterService, + Confirmation, +} from '@abp/ng.theme.shared'; +import { CommonModule } from '@angular/common'; +import { Component, inject, Injector, OnInit } from '@angular/core'; +import { FormsModule, UntypedFormGroup } from '@angular/forms'; +import { ButtonModule } from 'primeng/button'; +import { TableModule } from 'primeng/table'; +import { finalize } from 'rxjs'; +import { ePermissionManagementComponents } from '@abp/ng.permission-management'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { eIdentityComponents } from '@abp/ng.identity'; +import { PermissionManagementModule } from '@abp/ng.permission-management'; +import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'app-custom-roles', + standalone: true, + imports: [ + CommonModule, + FormsModule, + TableModule, + ButtonModule, + PageModule, + LocalizationModule, + ThemeSharedModule, + NgxDatatableModule, + ExtensibleModule, + PermissionManagementModule, + CoreModule, + NgbNavModule + ], + templateUrl: './custom-roles.component.html', + providers: [ + ListService, + { + provide: EXTENSIONS_IDENTIFIER, + useValue: eIdentityComponents.Roles, + }, + ], + styleUrl: './custom-roles.component.scss', +}) +export class CustomRolesComponent implements OnInit { + protected readonly list = inject(ListService); + protected readonly confirmationService = inject(ConfirmationService); + protected readonly toasterService = inject(ToasterService); + private readonly injector = inject(Injector); + protected readonly service = inject(IdentityRoleService); + + data: PagedResultDto = { items: [], totalCount: 0 }; + + form!: UntypedFormGroup; + + selected?: IdentityRoleDto; + + isModalVisible!: boolean; + + visiblePermissions = false; + + providerKey?: string; + + modalBusy = false; + + permissionManagementKey = ePermissionManagementComponents.PermissionManagement; + + onVisiblePermissionChange = (event: boolean) => { + this.visiblePermissions = event; + }; + + ngOnInit() { + this.hookToQuery(); + } + + buildForm() { + const data = new FormPropData(this.injector, this.selected); + this.form = generateFormFromProps(data); + } + + openModal() { + this.buildForm(); + this.isModalVisible = true; + } + + add() { + this.selected = {} as IdentityRoleDto; + this.openModal(); + } + + edit(id: string) { + debugger + this.service.get(id).subscribe(res => { + this.selected = res; + this.openModal(); + }); + } + + save() { + if (!this.form.valid) return; + this.modalBusy = true; + + const { id } = this.selected || {}; + (id + ? this.service.update(id, { ...this.selected, ...this.form.value }) + : this.service.create(this.form.value) + ) + .pipe(finalize(() => (this.modalBusy = false))) + .subscribe(() => { + this.isModalVisible = false; + this.toasterService.success('AbpUi::SavedSuccessfully'); + this.list.get(); + }); + } + + delete(id: string, name: string) { + this.confirmationService + .warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { + messageLocalizationParams: [name], + }) + .subscribe((status: Confirmation.Status) => { + if (status === Confirmation.Status.confirm) { + this.toasterService.success('AbpUi::DeletedSuccessfully'); + this.service.delete(id).subscribe(() => this.list.get()); + } + }); + } + + private hookToQuery() { + this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res)); + } + + openPermissionsModal(providerKey: string) { + this.providerKey = providerKey; + setTimeout(() => { + this.visiblePermissions = true; + }, 0); + } + + sort(data: any) { + const { prop, dir } = data.sorts[0]; + this.list.sortKey = prop; + this.list.sortOrder = dir; + } +} diff --git a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html new file mode 100644 index 0000000..f37936d --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html @@ -0,0 +1,147 @@ + + + +
+
+
+
+ +
+
+ +
+
+ + + + + + Username + First Name + Last Name + Email + Status + Actions + + + + + + {{ user.userName }} + {{ user.name }} + {{ user.surname }} + {{ user.email }} + + + + + + + + + + + + + + +
+
+ + + +

{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }}

+
+ + + @if (form) { +
+ +
+
+ } @else { +
+ } +
+ + + + {{ + 'AbpIdentity::Save' | abpLocalization + }} + +
+ + + +
+ \ No newline at end of file diff --git a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.scss b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.spec.ts b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.spec.ts new file mode 100644 index 0000000..5bb528b --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CustomUsersComponent } from './custom-users.component'; + +describe('CustomUsersComponent', () => { + let component: CustomUsersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CustomUsersComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CustomUsersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts new file mode 100644 index 0000000..b42e136 --- /dev/null +++ b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts @@ -0,0 +1,211 @@ +import { FormPropData,EXTENSIONS_IDENTIFIER ,generateFormFromProps, ExtensibleModule} from '@abp/ng.components/extensible'; +import { PageModule } from '@abp/ng.components/page'; +import { CoreModule, ListResultDto, ListService, LocalizationModule, PagedResultDto } from '@abp/ng.core'; +import { eIdentityComponents } from '@abp/ng.identity'; +import { GetIdentityUsersInput, IdentityRoleDto, IdentityUserDto, IdentityUserService } from '@abp/ng.identity/proxy'; +import { CommonModule } from '@angular/common'; +import { Component, inject, Injector, OnInit, TemplateRef, TrackByFunction, ViewChild } from '@angular/core'; +import { AbstractControl, FormsModule, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { ButtonModule } from 'primeng/button'; +import { TableModule } from 'primeng/table'; +import { Confirmation, eFormComponets, ThemeSharedModule,ToasterService ,ConfirmationService} from '@abp/ng.theme.shared'; +import { finalize, switchMap, tap } from 'rxjs'; +import { ePermissionManagementComponents, PermissionManagementModule } from '@abp/ng.permission-management'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; +import { PaginatorModule } from 'primeng/paginator'; +import { TagModule } from 'primeng/tag'; // For active/inactive status + +@Component({ + selector: 'app-custom-users', + standalone: true, + imports: [CommonModule,FormsModule,TableModule,ButtonModule,PageModule,LocalizationModule,ThemeSharedModule,NgxDatatableModule,ExtensibleModule, + PermissionManagementModule, + CoreModule, + NgbNavModule, + PaginatorModule,TagModule + ], + templateUrl: './custom-users.component.html', + providers: [ + ListService, + { + provide: EXTENSIONS_IDENTIFIER, + useValue: eIdentityComponents.Users, + }, + ], + styleUrl: './custom-users.component.scss' +}) +export class CustomUsersComponent implements OnInit{ + protected readonly list = inject(ListService); + protected readonly confirmationService = inject(ConfirmationService); + protected readonly service = inject(IdentityUserService); + protected readonly toasterService = inject(ToasterService); + private readonly fb = inject(UntypedFormBuilder); + private readonly injector = inject(Injector); + + data: PagedResultDto = { items: [], totalCount: 0 }; + + @ViewChild('modalContent', { static: false }) + modalContent!: TemplateRef; + + form!: UntypedFormGroup; + + selected?: IdentityUserDto; + + selectedUserRoles?: IdentityRoleDto[]; + + roles?: IdentityRoleDto[]; + + visiblePermissions = false; + + providerKey?: string; + + isModalVisible?: boolean; + + modalBusy = false; + + permissionManagementKey = ePermissionManagementComponents.PermissionManagement; + + entityDisplayName: string; + + inputKey = eFormComponets.FormCheckboxComponent; + + trackByFn: TrackByFunction = (index, item) => Object.keys(item)[0] || index; + + onVisiblePermissionChange = (event: boolean) => { + this.visiblePermissions = event; + }; + + get roleGroups(): UntypedFormGroup[] { + return ((this.form.get('roleNames') as UntypedFormArray)?.controls as UntypedFormGroup[]) || []; + } + + ngOnInit() { + this.hookToQuery(); + } + + buildForm() { + debugger + const data = new FormPropData(this.injector, this.selected); + this.form = generateFormFromProps(data); + + this.service.getAssignableRoles().subscribe(({ items }) => { + this.roles = items; + if (this.roles) { + this.form.addControl( + 'roleNames', + this.fb.array( + this.roles.map(role => + this.fb.group({ + [role.name as string]: [ + this.selected?.id + ? !!this.selectedUserRoles?.find(userRole => userRole.id === role.id) + : role.isDefault, + ], + }), + ), + ), + ); + } + }); + } + + openModal() { + this.buildForm(); + this.isModalVisible = true; + } + + add() { + debugger + this.selected = {} as IdentityUserDto; + this.selectedUserRoles = [] as IdentityRoleDto[]; + this.openModal(); + } + + edit(id: string) { + debugger + this.service + .get(id) + .pipe( + tap(user => (this.selected = user)), + switchMap(() => this.service.getRoles(id)), + ) + .subscribe(userRole => { + debugger + + this.selectedUserRoles = userRole.items || []; + this.openModal(); + }); + } + + save() { + if (!this.form.valid || this.modalBusy) return; + this.modalBusy = true; + + const { roleNames = [] } = this.form.value; + const mappedRoleNames = + roleNames + .filter((role: { [key: string]: any }) => !!role[Object.keys(role)[0]]) + .map((role: { [key: string]: any }) => Object.keys(role)[0]) || []; + + const { id } = this.selected || {}; + + (id + ? this.service.update(id, { + ...this.selected, + ...this.form.value, + roleNames: mappedRoleNames, + }) + : this.service.create({ ...this.form.value, roleNames: mappedRoleNames }) + ) + .pipe(finalize(() => (this.modalBusy = false))) + .subscribe(() => { + this.isModalVisible = false; + this.toasterService.success('AbpUi::SavedSuccessfully'); + this.list.get(); + }); + } + + delete(id: string, userName: string) { + this.confirmationService + .warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { + messageLocalizationParams: [userName], + }) + .subscribe((status: Confirmation.Status) => { + if (status === Confirmation.Status.confirm) { + this.service.delete(id).subscribe(() => { + this.toasterService.success('AbpUi::DeletedSuccessfully'); + this.list.get(); + }); + } + }); + } + + sort(data: any) { + const { prop, dir } = data.sorts[0]; + this.list.sortKey = prop; + this.list.sortOrder = dir; + } + + // private hookToQuery() { + // this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res)); + // } + private hookToQuery() { + this.list.hookToQuery(query => { + console.log('Query:', query); // Log the query + return this.service.getList(query); + }).subscribe(res => { +debugger + this.data = res; + }); + } + + openPermissionsModal(providerKey: string, entityDisplayName?: string) { + debugger + this.providerKey = providerKey; + this.entityDisplayName = entityDisplayName; + setTimeout(() => { + this.visiblePermissions = true; + }, 0); + } +} diff --git a/angular/src/app/route.provider.ts b/angular/src/app/route.provider.ts index 7a54ae8..dae1844 100644 --- a/angular/src/app/route.provider.ts +++ b/angular/src/app/route.provider.ts @@ -7,6 +7,7 @@ export const APP_ROUTE_PROVIDER = [ function configureRoutes(routesService: RoutesService) { return () => { + routesService.add([ { path: '/', @@ -15,6 +16,7 @@ function configureRoutes(routesService: RoutesService) { order: 1, layout: eLayoutType.application, }, + { path: '/appointment', name: 'Appointments', From 14eb61b75d27ff485f5822c51b4badea3d06fc0f Mon Sep 17 00:00:00 2001 From: Sk Shaifat Murshed Date: Fri, 31 Jan 2025 18:50:30 +0530 Subject: [PATCH 2/2] Fixed issue. --- .../custom-roles/custom-roles.component.html | 61 ++- .../custom-roles/custom-roles.component.ts | 89 +++- .../custom-users/custom-users.component.html | 435 +++++++++++++----- .../custom-users/custom-users.component.ts | 344 ++++++++++---- 4 files changed, 698 insertions(+), 231 deletions(-) diff --git a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html index acf212d..6024e0d 100644 --- a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html +++ b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.html @@ -12,7 +12,7 @@ - name + Role name Actions @@ -22,11 +22,11 @@ {{ role.name }} - + - + @@ -40,19 +40,66 @@

{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}

- + + + +
+ +
+ + + Name is required +
+ + +
+ + +
+ + +
+ + +
+
+ {{ + 'AbpIdentity::Save' | abpLocalization + }} +
- + - {{ + diff --git a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts index 693138f..27e206d 100644 --- a/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts +++ b/angular/src/app/modules/custom-identity/custom-roles/custom-roles.component.ts @@ -13,7 +13,7 @@ import { PagedAndSortedResultRequestDto, PagedResultDto, } from '@abp/ng.core'; -import { IdentityRoleDto, IdentityRoleService } from '@abp/ng.identity/proxy'; +import { IdentityRoleCreateDto, IdentityRoleDto, IdentityRoleService, IdentityRoleUpdateDto } from '@abp/ng.identity/proxy'; import { ThemeSharedModule, ConfirmationService, @@ -22,7 +22,7 @@ import { } from '@abp/ng.theme.shared'; import { CommonModule } from '@angular/common'; import { Component, inject, Injector, OnInit } from '@angular/core'; -import { FormsModule, UntypedFormGroup } from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, UntypedFormGroup, Validators } from '@angular/forms'; import { ButtonModule } from 'primeng/button'; import { TableModule } from 'primeng/table'; import { finalize } from 'rxjs'; @@ -31,6 +31,8 @@ import { NgxDatatableModule } from '@swimlane/ngx-datatable'; import { eIdentityComponents } from '@abp/ng.identity'; import { PermissionManagementModule } from '@abp/ng.permission-management'; import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; +import { MenuItem } from 'primeng/api'; +import { Dropdown, DropdownModule } from 'primeng/dropdown'; @Component({ selector: 'app-custom-roles', @@ -47,7 +49,8 @@ import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; ExtensibleModule, PermissionManagementModule, CoreModule, - NgbNavModule + NgbNavModule, + DropdownModule ], templateUrl: './custom-roles.component.html', providers: [ @@ -87,49 +90,97 @@ export class CustomRolesComponent implements OnInit { }; ngOnInit() { + this.hookToQuery(); } + // buildForm() { + // const data = new FormPropData(this.injector, this.selected); + // this.form = generateFormFromProps(data); + // } buildForm() { - const data = new FormPropData(this.injector, this.selected); - this.form = generateFormFromProps(data); + this.form = new FormGroup({ + name: new FormControl(this.selected?.name || '', Validators.required), + isDefault: new FormControl(this.selected?.isDefault || false), + isPublic: new FormControl(this.selected?.isPublic || false) + }); + } - + openModal() { this.buildForm(); this.isModalVisible = true; } add() { - this.selected = {} as IdentityRoleDto; - this.openModal(); + debugger + // this.selected = {} as IdentityRoleDto; + // this.openModal(); + this.selected = { name: '', isDefault: false, isPublic: false } as IdentityRoleDto; + this.isModalVisible = true; } edit(id: string) { debugger + // this.service.get(id).subscribe(res => { + // this.selected = res; + // this.openModal(); + // }); this.service.get(id).subscribe(res => { this.selected = res; - this.openModal(); + this.isModalVisible = true; }); } - save() { - if (!this.form.valid) return; - this.modalBusy = true; + // save() { + // debugger + // if (!this.form.valid) return; + // this.modalBusy = true; - const { id } = this.selected || {}; - (id - ? this.service.update(id, { ...this.selected, ...this.form.value }) - : this.service.create(this.form.value) - ) - .pipe(finalize(() => (this.modalBusy = false))) + // const { id } = this.selected || {}; + // (id + // ? this.service.update(id, { ...this.selected, ...this.form.value }) + // : this.service.create(this.form.value) + // ) + // .pipe(finalize(() => (this.modalBusy = false))) + // .subscribe(() => { + // this.isModalVisible = false; + // this.toasterService.success('AbpUi::SavedSuccessfully'); + // this.list.get(); + // }); + // } + save() { + debugger + if (!this.selected?.name) { + return; + } + + this.modalBusy = true; + + const { id, name, isDefault, isPublic, concurrencyStamp } = this.selected; + + const roleDto = { + name, + isDefault, + isPublic, + concurrencyStamp + }; + + const saveObservable = id + ? this.service.update(id, roleDto) // Update call + : this.service.create(roleDto); // Create call + + saveObservable + .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; this.toasterService.success('AbpUi::SavedSuccessfully'); this.list.get(); }); } - + + + delete(id: string, name: string) { this.confirmationService .warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', { diff --git a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html index f37936d..779d8d7 100644 --- a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html +++ b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.html @@ -8,24 +8,29 @@ --> -
-
-
-
- -
-
- -
+
+
+
+
+
+
+ +
+
- - - - - Username - First Name - Last Name - Email - Status - Actions - - - - - - {{ user.userName }} - {{ user.name }} - {{ user.surname }} - {{ user.email }} - - - - - - - + + + + Username + First Name + Last Name + Email + Phone No + Status + Actions + + - - - - - - -
+ + + {{ user.userName }} + {{ user.name }} + {{ user.surname }} + {{ user.email }} + {{ user.phoneNumber }} + + + + +
+ + +
+ + + + +
+
- - - -

{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }}

-
- - - @if (form) { -
- -
-
- } @else { -
- } -
- - - - {{ +
+ + + +

+ {{ (selected?.id ? 'AbpIdentity::EditUser' : 'AbpIdentity::NewUser') | abpLocalization }} +

+
+ + +
+ + +
+ +
+ +
+
+
+ + + + + +
+ + + + diff --git a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts index b42e136..78f8d8c 100644 --- a/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts +++ b/angular/src/app/modules/custom-identity/custom-users/custom-users.component.ts @@ -1,29 +1,83 @@ -import { FormPropData,EXTENSIONS_IDENTIFIER ,generateFormFromProps, ExtensibleModule} from '@abp/ng.components/extensible'; +import { + FormPropData, + EXTENSIONS_IDENTIFIER, + generateFormFromProps, + ExtensibleModule, +} from '@abp/ng.components/extensible'; import { PageModule } from '@abp/ng.components/page'; -import { CoreModule, ListResultDto, ListService, LocalizationModule, PagedResultDto } from '@abp/ng.core'; +import { + CoreModule, + ListResultDto, + ListService, + LocalizationModule, + PagedResultDto, +} from '@abp/ng.core'; import { eIdentityComponents } from '@abp/ng.identity'; -import { GetIdentityUsersInput, IdentityRoleDto, IdentityUserDto, IdentityUserService } from '@abp/ng.identity/proxy'; +import { + GetIdentityUsersInput, + IdentityRoleDto, + IdentityUserDto, + IdentityUserService, + IdentityUserUpdateDto, +} from '@abp/ng.identity/proxy'; import { CommonModule } from '@angular/common'; -import { Component, inject, Injector, OnInit, TemplateRef, TrackByFunction, ViewChild } from '@angular/core'; -import { AbstractControl, FormsModule, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { + Component, + ElementRef, + HostListener, + inject, + Injector, + OnInit, + TemplateRef, + TrackByFunction, + ViewChild, +} from '@angular/core'; +import { + AbstractControl, + FormsModule, + NgForm, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup, +} from '@angular/forms'; import { ButtonModule } from 'primeng/button'; import { TableModule } from 'primeng/table'; -import { Confirmation, eFormComponets, ThemeSharedModule,ToasterService ,ConfirmationService} from '@abp/ng.theme.shared'; +import { + Confirmation, + eFormComponets, + ThemeSharedModule, + ToasterService, + ConfirmationService, +} from '@abp/ng.theme.shared'; import { finalize, switchMap, tap } from 'rxjs'; -import { ePermissionManagementComponents, PermissionManagementModule } from '@abp/ng.permission-management'; +import { + ePermissionManagementComponents, + PermissionManagementModule, +} from '@abp/ng.permission-management'; import { NgxDatatableModule } from '@swimlane/ngx-datatable'; -import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { PaginatorModule } from 'primeng/paginator'; import { TagModule } from 'primeng/tag'; // For active/inactive status +import { SplitButtonModule } from 'primeng/splitbutton'; @Component({ selector: 'app-custom-users', standalone: true, - imports: [CommonModule,FormsModule,TableModule,ButtonModule,PageModule,LocalizationModule,ThemeSharedModule,NgxDatatableModule,ExtensibleModule, - PermissionManagementModule, - CoreModule, - NgbNavModule, - PaginatorModule,TagModule + imports: [ + CommonModule, + FormsModule, + TableModule, + ButtonModule, + PageModule, + LocalizationModule, + ThemeSharedModule, + NgxDatatableModule, + ExtensibleModule, + PermissionManagementModule, + CoreModule, + NgbNavModule, + PaginatorModule, + TagModule,SplitButtonModule ], templateUrl: './custom-users.component.html', providers: [ @@ -33,15 +87,16 @@ import { TagModule } from 'primeng/tag'; // For active/inactive status useValue: eIdentityComponents.Users, }, ], - styleUrl: './custom-users.component.scss' + styleUrl: './custom-users.component.scss', }) -export class CustomUsersComponent implements OnInit{ +export class CustomUsersComponent implements OnInit { protected readonly list = inject(ListService); protected readonly confirmationService = inject(ConfirmationService); protected readonly service = inject(IdentityUserService); protected readonly toasterService = inject(ToasterService); private readonly fb = inject(UntypedFormBuilder); private readonly injector = inject(Injector); + @ViewChild('firstDropdownItem') firstDropdownItem: ElementRef; data: PagedResultDto = { items: [], totalCount: 0 }; @@ -49,12 +104,13 @@ export class CustomUsersComponent implements OnInit{ modalContent!: TemplateRef; form!: UntypedFormGroup; - + password:string; selected?: IdentityUserDto; selectedUserRoles?: IdentityRoleDto[]; - roles?: IdentityRoleDto[]; + // roles?: IdentityRoleDto[]; + roles: (IdentityRoleDto & { selected: boolean })[] = []; // Use intersection type to add 'selected' visiblePermissions = false; @@ -71,8 +127,32 @@ export class CustomUsersComponent implements OnInit{ inputKey = eFormComponets.FormCheckboxComponent; trackByFn: TrackByFunction = (index, item) => Object.keys(item)[0] || index; + isDropdownOpen = false; + + toggleDropdown() { + this.isDropdownOpen = !this.isDropdownOpen; + + // After the dropdown is shown, focus the first item in the dropdown + setTimeout(() => { + if (this.firstDropdownItem) { + this.firstDropdownItem.nativeElement.focus(); + } + }, 100); // Delay to ensure dropdown is rendered + } + + closeDropdown() { + this.isDropdownOpen = false; + } + actionItems = [ + { label: 'Edit', icon: 'pi pi-pencil', command: () => this.edit("") }, + { label: 'Delete', icon: 'pi pi-trash', command: () => this.delete("","") }, + // { label: 'Permissions', icon: 'pi pi-shield', command: () => this.openPermissionsModal("") }, + ]; + + onVisiblePermissionChange = (event: boolean) => { + debugger this.visiblePermissions = event; }; @@ -83,87 +163,189 @@ export class CustomUsersComponent implements OnInit{ ngOnInit() { this.hookToQuery(); } + formValid() { + return true; + } + // buildForm() { + // debugger + // const data = new FormPropData(this.injector, this.selected); + // this.form = generateFormFromProps(data); + // this.service.getAssignableRoles().subscribe(({ items }) => { + // this.roles = items; + // if (this.roles) { + // this.form.addControl( + // 'roleNames', + // this.fb.array( + // this.roles.map(role => + // this.fb.group({ + // [role.name as string]: [ + // this.selected?.id + // ? !!this.selectedUserRoles?.find(userRole => userRole.id === role.id) + // : role.isDefault, + // ], + // }), + // ), + // ), + // ); + // } + // }); + // } + + // openModal() { + // this.buildForm(); + // this.isModalVisible = true; + // } + + // add() { + // debugger + // this.selected = {} as IdentityUserDto; + // this.selectedUserRoles = [] as IdentityRoleDto[]; + // this.openModal(); + // } + + // edit(id: string) { + // debugger + // this.service + // .get(id) + // .pipe( + // tap(user => (this.selected = user)), + // switchMap(() => this.service.getRoles(id)), + // ) + // .subscribe(userRole => { + // debugger + + // this.selectedUserRoles = userRole.items || []; + // this.openModal(); + // }); + // } + + // save() { + // if (!this.form.valid || this.modalBusy) return; + // this.modalBusy = true; + + // const { roleNames = [] } = this.form.value; + // const mappedRoleNames = + // roleNames + // .filter((role: { [key: string]: any }) => !!role[Object.keys(role)[0]]) + // .map((role: { [key: string]: any }) => Object.keys(role)[0]) || []; + + // const { id } = this.selected || {}; + + // (id + // ? this.service.update(id, { + // ...this.selected, + // ...this.form.value, + // roleNames: mappedRoleNames, + // }) + // : this.service.create({ ...this.form.value, roleNames: mappedRoleNames }) + // ) + // .pipe(finalize(() => (this.modalBusy = false))) + // .subscribe(() => { + // this.isModalVisible = false; + // this.toasterService.success('AbpUi::SavedSuccessfully'); + // this.list.get(); + // }); + // } buildForm() { - debugger - const data = new FormPropData(this.injector, this.selected); - this.form = generateFormFromProps(data); - this.service.getAssignableRoles().subscribe(({ items }) => { - this.roles = items; - if (this.roles) { - this.form.addControl( - 'roleNames', - this.fb.array( - this.roles.map(role => - this.fb.group({ - [role.name as string]: [ - this.selected?.id - ? !!this.selectedUserRoles?.find(userRole => userRole.id === role.id) - : role.isDefault, - ], - }), - ), - ), - ); + this.roles = items.map(role => ({ + ...role, + selected: this.selectedUserRoles.some(userRole => userRole.id === role.id), + })); + + // Initialize the selected roles based on existing data for selected user + if (this.selected?.id) { + this.roles.forEach(role => { + role.selected = this.selectedUserRoles.some(userRole => userRole.id === role.id); + }); + } else { + // Default values when creating a new user + this.roles.forEach(role => { + role.selected = role.isDefault; + }); } }); } openModal() { - this.buildForm(); + this.loadRoles(); // Load roles when modal opens this.isModalVisible = true; } - + loadRoles() { + this.service.getAssignableRoles().subscribe(({ items }) => { + this.roles = items.map(role => ({ + ...role, // spread IdentityRoleDto properties + selected: false, // Initially no role is selected + })); + }); + } add() { - debugger this.selected = {} as IdentityUserDto; this.selectedUserRoles = [] as IdentityRoleDto[]; + this.password = ''; // Store password separately + this.openModal(); } edit(id: string) { - debugger this.service .get(id) - .pipe( - tap(user => (this.selected = user)), - switchMap(() => this.service.getRoles(id)), - ) - .subscribe(userRole => { - debugger - - this.selectedUserRoles = userRole.items || []; - this.openModal(); + .pipe(finalize(() => this.buildForm())) + .subscribe(user => { + this.selected = user; + this.service.getRoles(id).subscribe(userRoles => { + this.selectedUserRoles = userRoles.items || []; + }); }); + this.openModal(); } - save() { - if (!this.form.valid || this.modalBusy) return; + // Save user data (either create or update) + save(form: NgForm) { + debugger; + if (this.modalBusy || form.invalid) return; + this.modalBusy = true; - const { roleNames = [] } = this.form.value; - const mappedRoleNames = - roleNames - .filter((role: { [key: string]: any }) => !!role[Object.keys(role)[0]]) - .map((role: { [key: string]: any }) => Object.keys(role)[0]) || []; + // Prepare roleNames array + // Prepare roleNames array + const selectedRoleNames = this.roles + .filter(role => role.selected) // Only include selected roles + .map(role => role.name); // Extract role names - const { id } = this.selected || {}; + // Create user payload - (id - ? this.service.update(id, { - ...this.selected, - ...this.form.value, - roleNames: mappedRoleNames, - }) - : this.service.create({ ...this.form.value, roleNames: mappedRoleNames }) - ) - .pipe(finalize(() => (this.modalBusy = false))) - .subscribe(() => { - this.isModalVisible = false; - this.toasterService.success('AbpUi::SavedSuccessfully'); - this.list.get(); - }); + const userPayload: any = { + userName: this.selected.userName, + name: this.selected.name, + surname: this.selected.surname, + email: this.selected.email, + phoneNumber: this.selected.phoneNumber, + isActive: this.selected.isActive, + lockoutEnabled: this.selected.lockoutEnabled, + roleNames: selectedRoleNames, // Include selected roles + + }; + + // Only include password if creating a new user + if (!this.selected.id && this.password) { + userPayload.password = this.password; + } + // Handle create or update + const saveObservable = this.selected.id + ? this.service.update(this.selected.id, userPayload) // Update if id exists + : this.service.create(userPayload); // Create if no id + + saveObservable.pipe(finalize(() => (this.modalBusy = false))).subscribe(() => { + this.isModalVisible = false; + this.toasterService.success('AbpUi::SavedSuccessfully'); + this.list.get(); // Reload the list if needed + }); + } + + private getRoleNames() { + return this.roles.filter(role => role.selected).map(role => role.name); } delete(id: string, userName: string) { @@ -191,17 +373,19 @@ export class CustomUsersComponent implements OnInit{ // this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res)); // } private hookToQuery() { - this.list.hookToQuery(query => { - console.log('Query:', query); // Log the query - return this.service.getList(query); - }).subscribe(res => { -debugger - this.data = res; - }); + this.list + .hookToQuery(query => { + console.log('Query:', query); // Log the query + return this.service.getList(query); + }) + .subscribe(res => { + debugger; + this.data = res; + }); } openPermissionsModal(providerKey: string, entityDisplayName?: string) { - debugger + debugger; this.providerKey = providerKey; this.entityDisplayName = entityDisplayName; setTimeout(() => {