Browse Source

Merge pull request #1556 from abpframework/feature/globalize-modal

feature: generalize modal component
pull/1565/head
Yasin Aydın 7 years ago
committed by GitHub
parent
commit
f5756309f5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      npm/ng-packs/.prettierrc
  2. 2
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  3. 40
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  4. 16
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts
  5. 205
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  6. 46
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts
  7. 2
      npm/ng-packs/packages/identity/src/lib/identity.module.ts
  8. 29
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html
  9. 56
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  10. 96
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html
  11. 121
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts
  12. 10
      npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts
  13. 38
      npm/ng-packs/packages/theme-basic/src/lib/components/change-password/change-password.component.html
  14. 45
      npm/ng-packs/packages/theme-basic/src/lib/components/change-password/change-password.component.ts
  15. 19
      npm/ng-packs/packages/theme-basic/src/lib/components/layout-application/layout-application.component.html
  16. 40
      npm/ng-packs/packages/theme-basic/src/lib/components/profile/profile.component.html
  17. 65
      npm/ng-packs/packages/theme-basic/src/lib/components/profile/profile.component.ts
  18. 2
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
  19. 3
      npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts

5
npm/ng-packs/.prettierrc

@ -0,0 +1,5 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all"
}

2
npm/ng-packs/packages/core/src/lib/states/config.state.ts

@ -107,6 +107,8 @@ export class ConfigState {
}
static getCopy(key: string, ...interpolateParams: string[]) {
if (!key) key = '';
const keys = key.split('::') as string[];
const selector = createSelector(
[ConfigState],

40
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html

@ -58,17 +58,13 @@
</div>
</div>
<ng-template #modalContent let-modal>
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">
{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}
</h5>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form [formGroup]="form" (ngSubmit)="save()">
<div class="modal-body">
<abp-modal size="md" [(visible)]="isModalVisible" *ngIf="isModalVisible" [centered]="true">
<ng-template #abpHeader>
<h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form">
<div class="form-group">
<label for="role-name">{{ 'AbpIdentity::RoleName' | abpLocalization }}</label
><span> * </span>
@ -88,18 +84,18 @@
'AbpIdentity::DisplayName:IsPublic' | abpLocalization
}}</label>
</div>
</div>
</form>
</ng-template>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="modal.close()">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="button" class="btn btn-primary" (click)="save()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</ng-template>
</abp-modal>
<abp-permission-management
[(visible)]="visiblePermissions"

16
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts

@ -1,7 +1,6 @@
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { IdentityState } from '../../states/identity.state';
import { Identity } from '../../models/identity';
@ -26,6 +25,8 @@ export class RolesComponent {
selected: Identity.RoleItem;
isModalVisible: boolean;
visiblePermissions: boolean = false;
providerKey: string;
@ -33,12 +34,7 @@ export class RolesComponent {
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
constructor(
private confirmationService: ConfirmationService,
private modalService: NgbModal,
private fb: FormBuilder,
private store: Store,
) {}
constructor(private confirmationService: ConfirmationService, private fb: FormBuilder, private store: Store) {}
createForm() {
this.form = this.fb.group({
@ -50,7 +46,7 @@ export class RolesComponent {
openModal() {
this.createForm();
this.modalService.open(this.modalContent);
this.isModalVisible = true;
}
onAdd() {
@ -77,7 +73,9 @@ export class RolesComponent {
? new IdentityUpdateRole({ ...this.form.value, id: this.selected.id })
: new IdentityAddRole(this.form.value),
)
.subscribe(() => this.modalService.dismissAll());
.subscribe(() => {
this.isModalVisible = false;
});
}
delete(id: string, name: string) {

205
npm/ng-packs/packages/identity/src/lib/components/users/users.component.html

@ -77,119 +77,118 @@
</div>
</div>
<ng-template #modalContent let-modal>
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">
{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }}
</h5>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form [formGroup]="form" (ngSubmit)="save()">
<div class="modal-body">
<ngb-tabset>
<ngb-tab [title]="'AbpIdentity::UserInformations' | abpLocalization">
<ng-template ngbTabContent>
<div class="mt-2">
<div class="form-group">
<label for="user-name">{{ 'AbpIdentity::UserName' | abpLocalization }}</label
><span> * </span>
<input type="text" id="user-name" class="form-control" formControlName="userName" />
</div>
<abp-modal [(visible)]="isModalVisible" *ngIf="isModalVisible">
<ng-template #abpHeader>
<h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }}</h3>
</ng-template>
<div class="form-group">
<label for="name">{{ 'AbpIdentity::Name' | abpLocalization }}</label>
<input type="text" id="name" class="form-control" formControlName="name" />
</div>
<ng-template #abpBody>
<perfect-scrollbar class="ps-show-always" style="max-height: 65vh;">
<form [formGroup]="form">
<ngb-tabset>
<ngb-tab [title]="'AbpIdentity::UserInformations' | abpLocalization">
<ng-template ngbTabContent>
<div class="mt-2">
<div class="form-group">
<label for="user-name">{{ 'AbpIdentity::UserName' | abpLocalization }}</label
><span> * </span>
<input type="text" id="user-name" class="form-control" formControlName="userName" />
</div>
<div class="form-group">
<label for="surname">{{ 'AbpIdentity::DisplayName:Surname' | abpLocalization }}</label>
<input type="text" id="surname" class="form-control" formControlName="surname" />
</div>
<div class="form-group">
<label for="name">{{ 'AbpIdentity::DisplayName:Name' | abpLocalization }}</label>
<input type="text" id="name" class="form-control" formControlName="name" />
</div>
<div class="form-group">
<label for="password">{{ 'AbpIdentity::Password' | abpLocalization }}</label
><span> * </span>
<input
type="password"
id="password"
autocomplete="new-password"
class="form-control"
formControlName="password"
/>
</div>
<div class="form-group">
<label for="surname">{{ 'AbpIdentity::DisplayName:Surname' | abpLocalization }}</label>
<input type="text" id="surname" class="form-control" formControlName="surname" />
</div>
<div class="form-group">
<label for="email">{{ 'AbpIdentity::EmailAddress' | abpLocalization }}</label
><span> * </span>
<input type="text" id="email" class="form-control" formControlName="email" />
</div>
<div class="form-group">
<label for="password">{{ 'AbpIdentity::Password' | abpLocalization }}</label
><span> * </span>
<input
type="password"
id="password"
autocomplete="new-password"
class="form-control"
formControlName="password"
/>
</div>
<div class="form-group">
<label for="phone-number">{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}</label>
<input type="text" id="phone-number" class="form-control" formControlName="phoneNumber" />
</div>
<div class="form-group">
<label for="email">{{ 'AbpIdentity::EmailAddress' | abpLocalization }}</label
><span> * </span>
<input type="text" id="email" class="form-control" formControlName="email" />
</div>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="lockout-checkbox"
class="custom-control-input"
formControlName="lockoutEnabled"
/>
<label class="custom-control-label" for="lockout-checkbox">{{
'AbpIdentity::DisplayName:LockoutEnabled' | abpLocalization
}}</label>
</div>
<div class="form-group">
<label for="phone-number">{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}</label>
<input type="text" id="phone-number" class="form-control" formControlName="phoneNumber" />
</div>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="two-factor-checkbox"
class="custom-control-input"
formControlName="twoFactorEnabled"
/>
<label class="custom-control-label" for="two-factor-checkbox">{{
'AbpIdentity::DisplayName:TwoFactorEnabled' | abpLocalization
}}</label>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="lockout-checkbox"
class="custom-control-input"
formControlName="lockoutEnabled"
/>
<label class="custom-control-label" for="lockout-checkbox">{{
'AbpIdentity::DisplayName:LockoutEnabled' | abpLocalization
}}</label>
</div>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="two-factor-checkbox"
class="custom-control-input"
formControlName="twoFactorEnabled"
/>
<label class="custom-control-label" for="two-factor-checkbox">{{
'AbpIdentity::DisplayName:TwoFactorEnabled' | abpLocalization
}}</label>
</div>
</div>
</div>
</ng-template>
</ngb-tab>
<ngb-tab [title]="'AbpIdentity::Roles' | abpLocalization">
<ng-template ngbTabContent>
<div class="mt-2">
<div
*ngFor="let roleGroup of roleGroups; let i = index; trackBy: trackByFn"
class="custom-checkbox custom-control mb-2"
>
<input
type="checkbox"
name="Roles[0].IsAssigned"
value="true"
class="custom-control-input"
[attr.id]="'roles-' + i"
[formControl]="roleGroup.controls[roles[i].name]"
/>
<label class="custom-control-label" [attr.for]="'roles-' + i">{{ roles[i].name }}</label>
</ng-template>
</ngb-tab>
<ngb-tab [title]="'AbpIdentity::Roles' | abpLocalization">
<ng-template ngbTabContent>
<div class="mt-2">
<div
*ngFor="let roleGroup of roleGroups; let i = index; trackBy: trackByFn"
class="custom-checkbox custom-control mb-2"
>
<input
type="checkbox"
name="Roles[0].IsAssigned"
value="true"
class="custom-control-input"
[attr.id]="'roles-' + i"
[formControl]="roleGroup.controls[roles[i].name]"
/>
<label class="custom-control-label" [attr.for]="'roles-' + i">{{ roles[i].name }}</label>
</div>
</div>
</div>
</ng-template>
</ngb-tab>
</ngb-tabset>
</div>
</ng-template>
</ngb-tab>
</ngb-tabset>
</form>
</perfect-scrollbar>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="button" class="btn btn-primary" (click)="save()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</ng-template>
</abp-modal>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="modal.close()">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</div>
</form>
</ng-template>
<abp-permission-management
[(visible)]="visiblePermissions"
providerName="User"

46
npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts

@ -1,24 +1,22 @@
import { Component, TemplateRef, ViewChild, TrackByFunction, OnInit } from '@angular/core';
import { ABP } from '@abp/ng.core';
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
import { Component, OnInit, TemplateRef, TrackByFunction, ViewChild } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { validatePassword } from '@ngx-validate/core';
import { Select, Store } from '@ngxs/store';
import { Observable, combineLatest, Subject } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FormBuilder, FormGroup, Validators, FormArray, AbstractControl } from '@angular/forms';
import { IdentityState } from '../../states/identity.state';
import { Identity } from '../../models/identity';
import { combineLatest, Observable, Subject } from 'rxjs';
import { debounceTime, filter, map, pluck, take } from 'rxjs/operators';
import snq from 'snq';
import {
IdentityUpdateUser,
IdentityAddUser,
IdentityDeleteUser,
IdentityGetUserById,
IdentityGetUserRoles,
IdentityGetUsers,
IdentityUpdateUser,
} from '../../actions/identity.actions';
import { pluck, filter, map, take, debounceTime } from 'rxjs/operators';
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
import snq from 'snq';
import { IdentityGetUserRoles } from '../../actions/identity.actions';
import { validatePassword } from '@ngx-validate/core';
import { ABP } from '@abp/ng.core';
import { Identity } from '../../models/identity';
import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-users',
templateUrl: './users.component.html',
@ -49,6 +47,8 @@ export class UsersComponent implements OnInit {
sorting: 'userName',
};
isModalVisible: boolean;
loading: boolean = false;
search$ = new Subject<string>();
@ -59,12 +59,7 @@ export class UsersComponent implements OnInit {
return snq(() => (this.form.get('roleNames') as FormArray).controls as FormGroup[], []);
}
constructor(
private confirmationService: ConfirmationService,
private modalService: NgbModal,
private fb: FormBuilder,
private store: Store,
) {}
constructor(private confirmationService: ConfirmationService, private fb: FormBuilder, private store: Store) {}
ngOnInit() {
this.search$.pipe(debounceTime(300)).subscribe(value => {
@ -105,7 +100,7 @@ export class UsersComponent implements OnInit {
openModal() {
this.buildForm();
this.modalService.open(this.modalContent);
this.isModalVisible = true;
}
onAdd() {
@ -146,9 +141,14 @@ export class UsersComponent implements OnInit {
id: this.selected.id,
roleNames: mappedRoleNames,
})
: new IdentityAddUser({ ...this.form.value, roleNames: mappedRoleNames }),
: new IdentityAddUser({
...this.form.value,
roleNames: mappedRoleNames,
}),
)
.subscribe(() => this.modalService.dismissAll());
.subscribe(() => {
this.isModalVisible = false;
});
}
delete(id: string, userName: string) {

2
npm/ng-packs/packages/identity/src/lib/identity.module.ts

@ -10,6 +10,7 @@ import { UsersComponent } from './components/users/users.component';
import { PermissionManagementModule } from '@abp/ng.permission-management';
import { TableModule } from 'primeng/table';
import { NgxValidateCoreModule } from '@ngx-validate/core';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
@NgModule({
declarations: [RolesComponent, UsersComponent],
@ -23,6 +24,7 @@ import { NgxValidateCoreModule } from '@ngx-validate/core';
NgbDropdownModule,
PermissionManagementModule,
NgxValidateCoreModule,
PerfectScrollbarModule,
],
})
export class IdentityModule {}

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

@ -1,15 +1,9 @@
<ng-template #modalContent let-modal>
<abp-modal [(visible)]="visible" *ngIf="visible" size="lg">
<ng-container *ngIf="{ entityName: entityName$ | async } as data">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">
{{ 'AbpPermissionManagement::Permissions' | abpLocalization }} -
{{ data.entityName }}
</h4>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<ng-template #abpHeader>
<h4>{{ 'AbpPermissionManagement::Permissions' | abpLocalization }} - {{ data.entityName }}</h4>
</ng-template>
<ng-template #abpBody>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
@ -31,7 +25,7 @@
<perfect-scrollbar class="ps-show-always" style="max-height: 70vh;">
<li *ngFor="let group of groups$ | async; trackBy: trackByFn" class="nav-item">
<a class="nav-link" [class.active]="selectedGroup.name === group.name" (click)="onChangeGroup(group)">{{
group.displayName
group?.displayName
}}</a>
</li>
</perfect-scrollbar>
@ -80,15 +74,14 @@
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="modal.close()">
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="submit" class="btn btn-primary" (click)="onSubmit()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</div>
</ng-template>
</ng-container>
</ng-template>
</abp-modal>

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

@ -7,11 +7,8 @@ import {
Output,
Renderer2,
SimpleChanges,
TemplateRef,
TrackByFunction,
ViewChild,
} from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { Select, Store } from '@ngxs/store';
import { from, Observable } from 'rxjs';
import { map, pluck, take } from 'rxjs/operators';
@ -35,23 +32,33 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
@Input()
providerKey: string;
protected _visible;
@Input()
visible: boolean;
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
if (!this.selectedGroup) return;
this._visible = value;
this.visibleChange.emit(value);
if (!value) {
this.selectedGroup = null;
}
}
@Output()
visibleChange = new EventEmitter<boolean>();
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
@Select(PermissionManagementState.getPermissionGroups)
groups$: Observable<PermissionManagement.Group[]>;
@Select(PermissionManagementState.getEntitiyDisplayName)
entityName$: Observable<string>;
modalRef: NgbModalRef;
selectedGroup: PermissionManagement.Group;
permissions: PermissionManagement.Permission[] = [];
@ -80,7 +87,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
);
}
constructor(private modalService: NgbModal, private store: Store, private renderer: Renderer2) {}
constructor(private store: Store, private renderer: Renderer2) {}
ngOnInit(): void {}
@ -186,9 +193,11 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
permissions: changedPermissions,
}),
)
.subscribe(() => this.modalRef.close());
.subscribe(() => {
this.visible = false;
});
} else {
this.modalRef.close();
this.visible = false;
}
}
@ -206,39 +215,22 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
this.modalRef = this.modalService.open(this.modalContent, { size: 'lg' });
this.visibleChange.emit(true);
this.visible = true;
setTimeout(() => {
this.setTabCheckboxState();
this.setGrantCheckboxState();
}, 0);
from(this.modalRef.result)
.pipe(take(1))
.subscribe(
data => {
this.setVisible(false);
},
reason => {
this.setVisible(false);
},
);
});
}
setVisible(value: boolean) {
this.visible = value;
this.visibleChange.emit(value);
}
ngOnChanges({ visible }: SimpleChanges): void {
if (!visible) return;
if (visible.currentValue) {
this.openModal();
} else if (visible.currentValue === false && this.modalService.hasOpenModals()) {
this.modalRef.close();
} else if (visible.currentValue === false && this.visible) {
this.visible = false;
}
}
}

96
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html

@ -12,7 +12,7 @@
id="create-tenants"
class="btn btn-primary"
type="button"
(click)="onAdd()"
(click)="onAddTenant()"
>
<i class="fa fa-plus mr-1"></i>
<span>{{ 'AbpTenantManagement::NewTenant' | abpLocalization }}</span>
@ -53,14 +53,14 @@
<button
[abpPermission]="'AbpTenantManagement.Tenants.Update'"
ngbDropdownItem
(click)="onEdit(data.id)"
(click)="onEditTenant(data.id)"
>
{{ 'AbpTenantManagement::Edit' | abpLocalization }}
</button>
<button
[abpPermission]="'AbpTenantManagement.Tenants.ManageConnectionStrings'"
ngbDropdownItem
(click)="onEditConnStr(data.id)"
(click)="onEditConnectionString(data.id)"
>
{{ 'AbpTenantManagement::ConnectionStrings' | abpLocalization }}
</button>
@ -88,60 +88,62 @@
</div>
</div>
<ng-template #modalWrapper let-modal>
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">
{{ selectedModalContent.title | abpLocalization }}
</h5>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<abp-modal [(visible)]="isModalVisible" *ngIf="isModalVisible">
<ng-template #abpHeader>
<h3>{{ selectedModalContent.title | abpLocalization }}</h3>
</ng-template>
<form [formGroup]="tenantForm" (ngSubmit)="selectedModalContent.onSave()">
<div class="modal-body">
<ng-container *ngTemplateOutlet="selectedModalContent.template; context: { $implicit: modal }"></ng-container>
</div>
<ng-template #abpBody>
<ng-container *ngTemplateOutlet="selectedModalContent?.template"></ng-container>
</ng-template>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="modal.close()">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpTenantManagement::Save' | abpLocalization }}</span>
</button>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<button class="btn btn-primary" type="button" (click)="save()">
{{ 'AbpTenantManagement::Save' | abpLocalization }}
</button>
</ng-template>
</abp-modal>
<ng-template #mTemplateTenant let-modal>
<div class="mt-2">
<div class="form-group">
<label for="name">{{ 'AbpTenantManagement::TenantName' | abpLocalization }}</label>
<input type="text" id="name" class="form-control" formControlName="name" />
<ng-template #tenantModalTemplate>
<form [formGroup]="tenantForm">
<div class="mt-2">
<div class="form-group">
<label for="name">{{ 'AbpTenantManagement::TenantName' | abpLocalization }}</label>
<input type="text" id="name" class="form-control" formControlName="name" />
</div>
</div>
</div>
</form>
</ng-template>
<ng-template #mTemplateConnStr let-modal>
<div class="mt-2">
<div class="form-group">
<div class="form-check">
<input id="useSharedDatabase" type="checkbox" class="form-check-input" formControlName="useSharedDatabase" />
<label for="useSharedDatabase" class="font-check-label">{{
'AbpTenantManagement::DisplayName:UseSharedDatabase' | abpLocalization
<ng-template #connectionStringModalTemplate>
<form [formGroup]="defaultConnectionStringForm">
<div class="mt-2">
<div class="form-group">
<div class="form-check">
<input id="useSharedDatabase" type="checkbox" class="form-check-input" formControlName="useSharedDatabase" />
<label for="useSharedDatabase" class="font-check-label">{{
'AbpTenantManagement::DisplayName:UseSharedDatabase' | abpLocalization
}}</label>
</div>
</div>
<div class="form-group" *ngIf="!useSharedDatabase">
<label for="defaultConnectionString">{{
'AbpTenantManagement::DisplayName:DefaultConnectionString' | abpLocalization
}}</label>
<input
type="text"
id="defaultConnectionString"
class="form-control"
formControlName="defaultConnectionString"
/>
</div>
</div>
<div class="form-group" *ngIf="showInput">
<label for="defaultConnectionString">{{
'AbpTenantManagement::DisplayName:DefaultConnectionString' | abpLocalization
}}</label>
<input type="text" id="defaultConnectionString" class="form-control" formControlName="defaultConnectionString" />
</div>
</div>
</form>
</ng-template>
<ng-template #mTemplateFeatures let-modal>
<ng-template #featuresModalTemplate>
Manage Features
</ng-template>

121
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts

@ -2,7 +2,6 @@ import { ABP } from '@abp/ng.core';
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { pluck, switchMap, take } from 'rxjs/operators';
@ -15,6 +14,12 @@ import {
import { TenantManagementService } from '../../services/tenant-management.service';
import { TenantManagementState } from '../../states/tenant-management.state';
type SelectedModalContent = {
type: string;
title: string;
template: TemplateRef<any>;
};
@Component({
selector: 'abp-tenants',
templateUrl: './tenants.component.html',
@ -31,46 +36,36 @@ export class TenantsComponent {
defaultConnectionString: string;
useSharedDatabase: boolean;
isModalVisible: boolean;
selectedModalContent = {} as SelectedModalContent;
selectedModalContent: {
title: string;
template: TemplateRef<any>;
onSave: () => void;
};
_useSharedDatabase: boolean;
get showInput(): boolean {
return !this.defaultConnectionStringForm.get('useSharedDatabase').value;
get useSharedDatabase(): boolean {
return this.defaultConnectionStringForm.get('useSharedDatabase').value;
}
get connectionString(): string {
return this.defaultConnectionStringForm.get('defaultConnectionString').value;
}
@ViewChild('modalWrapper', { static: false })
modalWrapper: TemplateRef<any>;
@ViewChild('mTemplateConnStr', { static: false })
mTemplateConnStr: TemplateRef<any>;
@ViewChild('tenantModalTemplate', { static: false })
tenantModalTemplate: TemplateRef<any>;
@ViewChild('mTemplateFeatures', { static: false })
mTemplateFeatures: TemplateRef<any>;
@ViewChild('connectionStringModalTemplate', { static: false })
connectionStringModalTemplate: TemplateRef<any>;
@ViewChild('mTemplateTenant', { static: false })
mTemplateTenant: TemplateRef<any>;
@ViewChild('featuresModalTemplate', { static: false })
featuresModalTemplate: TemplateRef<any>;
constructor(
private confirmationService: ConfirmationService,
private tenantService: TenantManagementService,
private modalService: NgbModal,
private fb: FormBuilder,
private store: Store,
) {}
openModal() {
this.modalService.open(this.modalWrapper);
}
private createTenantForm() {
this.tenantForm = this.fb.group({
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]],
@ -79,17 +74,22 @@ export class TenantsComponent {
private createDefaultConnectionStringForm() {
this.defaultConnectionStringForm = this.fb.group({
useSharedDatabase: this.useSharedDatabase,
useSharedDatabase: this._useSharedDatabase,
defaultConnectionString: this.defaultConnectionString || '',
});
}
onEditConnStr(id: string) {
openModal(title: string, template: TemplateRef<any>, type: string) {
this.selectedModalContent = {
title: this.selected && this.selected.id ? 'AbpTenantManagement::Edit' : 'AbpTenantManagement::NewTenant',
template: this.mTemplateConnStr,
onSave: () => this.saveConnStr,
title,
template,
type,
};
this.isModalVisible = true;
}
onEditConnectionString(id: string) {
this.store
.dispatch(new TenantManagementGetById(id))
.pipe(
@ -100,56 +100,59 @@ export class TenantsComponent {
}),
)
.subscribe(fetchedConnectionString => {
this.useSharedDatabase = fetchedConnectionString ? false : true;
this._useSharedDatabase = fetchedConnectionString ? false : true;
this.defaultConnectionString = fetchedConnectionString ? fetchedConnectionString : '';
this.createDefaultConnectionStringForm();
this.openModal();
this.openModal('AbpTenantManagement::ConnectionStrings', this.connectionStringModalTemplate, 'saveConnStr');
});
}
saveConnStr() {
this.tenantService
.updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString })
.pipe(take(1))
.subscribe(() => this.modalService.dismissAll());
}
onManageFeatures(id: string) {
this.selectedModalContent = {
title: this.selected && this.selected.id ? 'AbpTenantManagement::Edit' : 'AbpTenantManagement::NewTenant',
template: this.mTemplateFeatures,
onSave: () => {},
};
this.openModal();
this.openModal('AbpTenantManagement::Features', this.featuresModalTemplate, 'saveFeatures');
}
onAdd() {
onAddTenant() {
this.selected = {} as ABP.BasicItem;
this.createTenantForm();
this.openModal();
this.selectedModalContent = {
title: 'AbpTenantManagement::NewTenant',
template: this.mTemplateTenant,
onSave: () => this.saveTenant,
};
this.openModal('AbpTenantManagement::NewTenant', this.tenantModalTemplate, 'saveTenant');
}
onEdit(id: string) {
onEditTenant(id: string) {
this.store
.dispatch(new TenantManagementGetById(id))
.pipe(pluck('TenantManagementState', 'selectedItem'))
.subscribe(selected => {
this.selected = selected;
this.selectedModalContent = {
title: 'AbpTenantManagement::Edit',
template: this.mTemplateTenant,
onSave: () => this.saveTenant,
};
this.createTenantForm();
this.openModal();
this.openModal('AbpTenantManagement::Edit', this.tenantModalTemplate, 'saveTenant');
});
}
save() {
const { type } = this.selectedModalContent;
if (!type) return;
if (type === 'saveTenant') this.saveTenant();
else if (type === 'saveConnStr') this.saveConnectionString();
}
saveConnectionString() {
if (this.useSharedDatabase) {
this.tenantService
.deleteDefaultConnectionString(this.selected.id)
.pipe(take(1))
.subscribe(() => {
this.isModalVisible = false;
});
} else {
this.tenantService
.updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString })
.pipe(take(1))
.subscribe(() => {
this.isModalVisible = false;
});
}
}
saveTenant() {
if (!this.tenantForm.valid) return;
@ -159,7 +162,9 @@ export class TenantsComponent {
? new TenantManagementUpdate({ ...this.tenantForm.value, id: this.selected.id })
: new TenantManagementAdd(this.tenantForm.value),
)
.subscribe(() => this.modalService.dismissAll());
.subscribe(() => {
this.isModalVisible = false;
});
}
delete(id: string, name: string) {

10
npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts

@ -80,4 +80,14 @@ export class TenantManagementService {
};
return this.rest.request<TenantManagement.DefaultConnectionStringRequest, any>(request);
}
deleteDefaultConnectionString(id: string): Observable<string> {
const url = `/api/multi-tenancy/tenant/${id}/defaultConnectionString`;
const request: Rest.Request<TenantManagement.DefaultConnectionStringRequest> = {
method: 'DELETE',
url,
};
return this.rest.request<TenantManagement.DefaultConnectionStringRequest, any>(request);
}
}

38
npm/ng-packs/packages/theme-basic/src/lib/components/change-password/change-password.component.html

@ -1,12 +1,9 @@
<ng-template #modalContent let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{ 'AbpIdentity::ChangePassword' | abpLocalization }}</h4>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form [formGroup]="form" (ngSubmit)="onSubmit()" novalidate>
<div class="modal-body">
<abp-modal *ngIf="visible" [(visible)]="visible">
<ng-template #abpHeader>
<h4>{{ 'AbpIdentity::ChangePassword' | abpLocalization }}</h4>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form" novalidate>
<div class="form-group">
<label for="current-password">{{ 'AbpIdentity::DisplayName:CurrentPassword' | abpLocalization }}</label
><span> * </span><input type="password" id="current-password" class="form-control" formControlName="password" />
@ -23,15 +20,14 @@
{{ 'AbpIdentity::Identity.PasswordConfirmationFailed' | abpLocalization }}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="modal.close()">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</div>
</form>
</ng-template>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="button" class="btn btn-primary" (click)="onSubmit()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</ng-template>
</abp-modal>

45
npm/ng-packs/packages/theme-basic/src/lib/components/change-password/change-password.component.ts

@ -11,11 +11,8 @@ import {
ViewChild,
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { comparePasswords, validatePassword } from '@ngx-validate/core';
import { Store } from '@ngxs/store';
import { from } from 'rxjs';
import { take } from 'rxjs/operators';
const { minLength, required } = Validators;
@ -24,8 +21,17 @@ const { minLength, required } = Validators;
templateUrl: './change-password.component.html',
})
export class ChangePasswordComponent implements OnInit, OnChanges {
protected _visible;
@Input()
visible: boolean;
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
this._visible = value;
this.visibleChange.emit(value);
}
@Output()
visibleChange = new EventEmitter<boolean>();
@ -35,9 +41,7 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
form: FormGroup;
modalRef: NgbModalRef;
constructor(private fb: FormBuilder, private modalService: NgbModal, private store: Store) {}
constructor(private fb: FormBuilder, private store: Store) {}
ngOnInit(): void {
this.form = this.fb.group(
@ -62,28 +66,13 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
newPassword: this.form.get('newPassword').value,
}),
)
.subscribe(() => this.modalRef.close());
.subscribe(() => {
this.visible = false;
});
}
openModal() {
this.modalRef = this.modalService.open(this.modalContent);
this.visibleChange.emit(true);
from(this.modalRef.result)
.pipe(take(1))
.subscribe(
data => {
this.setVisible(false);
},
reason => {
this.setVisible(false);
},
);
}
setVisible(value: boolean) {
this.visible = value;
this.visibleChange.emit(value);
this.visible = true;
}
ngOnChanges({ visible }: SimpleChanges): void {
@ -91,8 +80,8 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
if (visible.currentValue) {
this.openModal();
} else if (visible.currentValue === false && this.modalService.hasOpenModals()) {
this.modalRef.close();
} else if (visible.currentValue === false && this.visible) {
this.visible = false;
}
}
}

19
npm/ng-packs/packages/theme-basic/src/lib/components/layout-application/layout-application.component.html

@ -9,14 +9,20 @@
<ng-template #defaultLink let-route>
<li class="nav-item" [abpPermission]="route.requiredPolicy">
<a class="nav-link" [routerLink]="[route.url]">{{ route.name }}</a>
<a class="nav-link" [routerLink]="[route.url]">{{ route.name | abpLocalization }}</a>
</li>
</ng-template>
<ng-template #dropdownLink let-route>
<li class="nav-item dropdown" ngbDropdown [abpPermission]="route.requiredPolicy" [abpVisibility]="routeContainer">
<li
class="nav-item dropdown"
ngbDropdown
display="static"
[abpPermission]="route.requiredPolicy"
[abpVisibility]="routeContainer"
>
<a ngbDropdownToggle class="nav-link dropdown-toggle" data-toggle="dropdown">
{{ route.name }}
{{ route.name | abpLocalization }}
</a>
<div #routeContainer ngbDropdownMenu class="dropdown-menu dropdown-menu-right">
<ng-template
@ -39,7 +45,7 @@
<ng-template #defaultChild let-child>
<div class="dropdown-submenu" [abpPermission]="child.requiredPolicy">
<a class="dropdown-item py-2 px-2" [routerLink]="[child.url]">{{ child.name }}</a>
<a class="dropdown-item py-2 px-2" [routerLink]="[child.url]">{{ child.name | abpLocalization }}</a>
</div>
</ng-template>
@ -48,10 +54,13 @@
[abpVisibility]="childrenContainer"
class="dropdown-submenu"
ngbDropdown
display="static"
placement="right-top"
[abpPermission]="child.requiredPolicy"
>
<a role="button" class="btn d-block text-left py-2 px-2" ngbDropdownToggle> {{ child.name }} </a>
<a role="button" class="btn d-block text-left py-2 px-2" ngbDropdownToggle>
{{ child.name | abpLocalization }}
</a>
<div #childrenContainer ngbDropdownMenu class="dropdown-menu dropdown-menu-right">
<ng-template
ngFor

40
npm/ng-packs/packages/theme-basic/src/lib/components/profile/profile.component.html

@ -1,12 +1,9 @@
<ng-template #modalContent let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{ 'AbpIdentity::PersonalInfo' | abpLocalization }}</h4>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form *ngIf="form" [formGroup]="form" (ngSubmit)="onSubmit()" novalidate>
<div class="modal-body">
<abp-modal *ngIf="visible" [(visible)]="visible">
<ng-template #abpHeader>
<h4>{{ 'AbpIdentity::PersonalInfo' | abpLocalization }}</h4>
</ng-template>
<ng-template #abpBody>
<form *ngIf="form" [formGroup]="form" novalidate>
<div class="form-group">
<label for="username">{{ 'AbpIdentity::DisplayName:UserName' | abpLocalization }}</label
><span> * </span><input type="text" id="username" class="form-control" formControlName="userName" />
@ -26,22 +23,21 @@
</div>
</div>
<div class="form-group">
<label for="email-address">{{ 'AbpIdentity::DisplayName:EmailAddress' | abpLocalization }}</label
<label for="email-address">{{ 'AbpIdentity::DisplayName:Email' | abpLocalization }}</label
><span> * </span><input type="text" id="email-address" class="form-control" formControlName="email" />
</div>
<div class="form-group">
<label for="phone-number">{{ 'AbpIdentity::DisplayName:PhoneNumber' | abpLocalization }}</label
><input type="text" id="phone-number" class="form-control" formControlName="phoneNumber" />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="modal.close()">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</div>
</form>
</ng-template>
</form>
</ng-template>
<ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button type="button" class="btn btn-primary" (click)="onSubmit()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpIdentity::Save' | abpLocalization }}</span>
</button>
</ng-template>
</abp-modal>

65
npm/ng-packs/packages/theme-basic/src/lib/components/profile/profile.component.ts

@ -1,20 +1,9 @@
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
TemplateRef,
ViewChild,
} from '@angular/core';
import { Profile, ProfileGet, ProfileState, ProfileUpdate } from '@abp/ng.core';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { Store, Select } from '@ngxs/store';
import { from, Observable } from 'rxjs';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { take, withLatestFrom } from 'rxjs/operators';
import { ProfileGet, ProfileState, Profile, ProfileUpdate } from '@abp/ng.core';
const { maxLength, required, email } = Validators;
@ -23,23 +12,27 @@ const { maxLength, required, email } = Validators;
templateUrl: './profile.component.html',
})
export class ProfileComponent implements OnChanges {
protected _visible;
@Input()
visible: boolean;
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
this._visible = value;
this.visibleChange.emit(value);
}
@Output()
visibleChange = new EventEmitter<boolean>();
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
@Select(ProfileState.getProfile)
profile$: Observable<Profile.Response>;
form: FormGroup;
modalRef: NgbModalRef;
constructor(private fb: FormBuilder, private modalService: NgbModal, private store: Store) {}
constructor(private fb: FormBuilder, private store: Store) {}
buildForm() {
this.store
@ -62,30 +55,14 @@ export class ProfileComponent implements OnChanges {
onSubmit() {
if (this.form.invalid) return;
this.store.dispatch(new ProfileUpdate(this.form.value)).subscribe(() => this.modalRef.close());
this.store.dispatch(new ProfileUpdate(this.form.value)).subscribe(() => {
this.visible = false;
});
}
openModal() {
this.buildForm();
this.modalRef = this.modalService.open(this.modalContent);
this.visibleChange.emit(true);
from(this.modalRef.result)
.pipe(take(1))
.subscribe(
data => {
this.setVisible(false);
},
reason => {
this.setVisible(false);
},
);
}
setVisible(value: boolean) {
this.visible = value;
this.visibleChange.emit(value);
this.visible = true;
}
ngOnChanges({ visible }: SimpleChanges): void {
@ -93,8 +70,8 @@ export class ProfileComponent implements OnChanges {
if (visible.currentValue) {
this.openModal();
} else if (visible.currentValue === false && this.modalService.hasOpenModals()) {
this.modalRef.close();
} else if (visible.currentValue === false && this.visible) {
this.visible = false;
}
}
}

2
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

@ -116,6 +116,8 @@ export class ModalComponent implements OnDestroy {
this.close();
});
if (!this.abpClose) return;
fromEvent(this.abpClose.nativeElement, 'click')
.pipe(
takeUntil(this.destroy$),

3
npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts

@ -24,6 +24,9 @@ export default `
top: 18px;
}
.modal {
background-color: rgba(0, 0, 0, .6);
}
/* <animations */

Loading…
Cancel
Save