Browse Source

Merge branch 'dev' of https://github.com/volosoft/abp into dev

pull/1584/head
Yunus Emre Kalkan 7 years ago
parent
commit
108823d2fd
  1. 2
      npm/ng-packs/package.json
  2. 4
      npm/ng-packs/packages/account/src/lib/components/login/login.component.html
  3. 22
      npm/ng-packs/packages/account/src/lib/components/register/register.component.html
  4. 33
      npm/ng-packs/packages/account/src/lib/components/register/register.component.ts
  5. 42
      npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.html
  6. 52
      npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
  7. 2
      npm/ng-packs/packages/account/src/lib/models/index.ts
  8. 4
      npm/ng-packs/packages/account/src/lib/models/tenant.ts
  9. 29
      npm/ng-packs/packages/account/src/lib/models/user.ts
  10. 30
      npm/ng-packs/packages/account/src/lib/services/account.service.ts
  11. 0
      npm/ng-packs/packages/account/src/lib/services/index.ts
  12. 4
      npm/ng-packs/packages/core/src/lib/actions/session.actions.ts
  13. 10
      npm/ng-packs/packages/core/src/lib/core.module.ts
  14. 15
      npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts
  15. 1
      npm/ng-packs/packages/core/src/lib/directives/index.ts
  16. 1
      npm/ng-packs/packages/core/src/lib/models/session.ts
  17. 10
      npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts
  18. 16
      npm/ng-packs/packages/core/src/lib/states/session.state.ts
  19. 4
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  20. 6
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  21. 11
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.html
  22. 10
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  23. 6
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html
  24. 5
      npm/ng-packs/packages/theme-basic/src/lib/components/change-password/change-password.component.html
  25. 18
      npm/ng-packs/packages/theme-basic/src/lib/components/layout-application/layout-application.component.html
  26. 8
      npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.html
  27. 2
      npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts
  28. 4
      npm/ng-packs/packages/theme-basic/src/lib/components/profile/profile.component.html
  29. 8
      npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts
  30. 1
      npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts
  31. 6
      npm/ng-packs/packages/theme-shared/src/lib/animations/slide.animation.ts
  32. 68
      npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts
  33. 4
      npm/ng-packs/packages/theme-shared/src/lib/components/index.ts
  34. 2
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md
  35. 12
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html
  36. 54
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
  37. 4
      npm/ng-packs/packages/theme-shared/src/lib/contants/styles.ts
  38. 3
      npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
  39. 8
      npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts
  40. 4
      npm/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts
  41. 12
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  42. 1
      npm/ng-packs/packages/theme-shared/src/public-api.ts
  43. 2
      npm/ng-packs/scripts/build.js
  44. 3
      npm/ng-packs/scripts/sync.js
  45. 25
      templates/app/angular/package.json
  46. 4
      templates/app/angular/src/app/app.component.ts

2
npm/ng-packs/package.json

@ -48,7 +48,7 @@
"protractor": "~5.4.0",
"rxjs": "~6.4.0",
"snq": "^1.0.3",
"symlink-manager": "^1.2.2",
"symlink-manager": "^1.3.0",
"ts-node": "~7.0.0",
"tsickle": "^0.35.0",
"tslib": "^1.9.0",

4
npm/ng-packs/packages/account/src/lib/components/login/login.component.html

@ -14,6 +14,7 @@
type="text"
id="login-input-user-name-or-email-address"
formControlName="username"
autofocus
/>
</div>
<div class="form-group">
@ -27,9 +28,6 @@
</label>
</div>
<div class="mt-2">
<button type="button" name="Action" value="Cancel" class="btn btn-secondary">
{{ 'AbpAccount::Cancel' | abpLocalization }}
</button>
<button [disabled]="inProgress" type="submit" name="Action" value="Login" class="btn btn-primary ml-1">
{{ 'AbpAccount::Login' | abpLocalization }}
</button>

22
npm/ng-packs/packages/account/src/lib/components/register/register.component.html

@ -3,22 +3,28 @@
<abp-tenant-box></abp-tenant-box>
<div class="abp-account-container">
<h2>Register</h2>
<h2>{{ 'AbpAccount::Register' | abpLocalization }}</h2>
<form [formGroup]="form" (ngSubmit)="onSubmit()" novalidate>
<div class="form-group">
<label for="input-user-name">User name</label><span> * </span
><input autofocus type="text" id="input-user-name" class="form-control" formControlName="username" />
<label for="input-user-name">{{ 'AbpAccount::UserName' | abpLocalization }}</label
><span> * </span
><input autofocus type="text" id="input-user-name" class="form-control" formControlName="username" autofocus/>
</div>
<div class="form-group">
<label for="input-email-address">Email address</label><span> * </span
><input type="email" id="input-email-address" class="form-control" formControlName="email" />
<label for="input-email-address">{{ 'AbpAccount::EmailAddress' | abpLocalization }}</label
><span> * </span><input type="email" id="input-email-address" class="form-control" formControlName="email" />
</div>
<div class="form-group">
<label for="input-password">Password</label><span> * </span
><input type="password" id="input-password" class="form-control" formControlName="password" />
<label for="input-password">{{ 'AbpAccount::Password' | abpLocalization }}</label
><span> * </span><input type="password" id="input-password" class="form-control" formControlName="password" />
</div>
<button type="submit" class="btn btn-primary">Register</button>
<button [disabled]="inProgress" type="submit" name="Action" value="Register" class="btn btn-primary">
{{ 'AbpAccount::Register' | abpLocalization }}
</button>
</form>
<div style="padding-top: 20px">
<a routerLink="/account/login">{{ 'AbpAccount::Login' | abpLocalization }}</a>
</div>
</div>
</div>
</div>

33
npm/ng-packs/packages/account/src/lib/components/register/register.component.ts

@ -1,8 +1,12 @@
import { ToasterService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { validatePassword } from '@ngx-validate/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { throwError } from 'rxjs';
import { catchError, finalize, take } from 'rxjs/operators';
import snq from 'snq';
import { RegisterRequest } from '../../models';
import { AccountService } from '../../services/account.service';
const { maxLength, minLength, required, email } = Validators;
@ -13,7 +17,9 @@ const { maxLength, minLength, required, email } = Validators;
export class RegisterComponent {
form: FormGroup;
constructor(private fb: FormBuilder, private oauthService: OAuthService, private router: Router) {
inProgress: boolean;
constructor(private fb: FormBuilder, private accountService: AccountService, private toasterService: ToasterService) {
this.form = this.fb.group({
username: ['', [required, maxLength(255)]],
password: [
@ -26,5 +32,26 @@ export class RegisterComponent {
onSubmit() {
if (this.form.invalid) return;
this.inProgress = true;
const newUser = {
userName: this.form.get('username').value,
password: this.form.get('password').value,
emailAddress: this.form.get('email').value,
appName: 'angular',
} as RegisterRequest;
this.accountService
.register(newUser)
.pipe(
take(1),
catchError(err => {
this.toasterService.error(snq(() => err.error.error_description, 'An error occured.'), 'Error');
return throwError(err);
}),
finalize(() => (this.inProgress = false)),
)
.subscribe();
}
}

42
npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.html

@ -12,33 +12,27 @@
>)
</div>
<ng-template #modalContent let-modal>
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">
SwitchTenant
</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 [(visible)]="isModalVisible" size="md">
<ng-template #abpHeader>
<h5>Switch Tenant</h5>
</ng-template>
<ng-template #abpBody>
<form>
<div class="mt-2">
<div class="form-group">
<label for="name">{{ 'AbpUiMultiTenancy::Name' | abpLocalization }}</label>
<input type="text" id="name" class="form-control" formControlName="name" />
<input [(ngModel)]="selected.name" type="text" id="name" name="tenant" class="form-control" autofocus />
</div>
<p>{{ 'AbpUiMultiTenancy::SwitchTenantHint' | abpLocalization }}</p>
</div>
</div>
<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>
</form>
</ng-template>
<ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<button type="button" class="btn btn-primary" (click)="save()">
<i class="fa fa-check mr-1"></i> <span>{{ 'AbpTenantManagement::Save' | abpLocalization }}</span>
</button>
</ng-template>
</abp-modal>

52
npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts

@ -1,40 +1,50 @@
import { ABP, SessionSetTenantId } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ABP } from '@abp/ng.core';
import { Store } from '@ngxs/store';
import { throwError } from 'rxjs';
import { catchError, take } from 'rxjs/operators';
import snq from 'snq';
import { AccountService } from '../../services/account.service';
@Component({
selector: 'abp-tenant-box',
templateUrl: './tenant-box.component.html',
})
export class TenantBoxComponent {
constructor(private modalService: NgbModal, private fb: FormBuilder) {}
constructor(private store: Store, private toasterService: ToasterService, private accountService: AccountService) {}
form: FormGroup;
selected = {} as ABP.BasicItem;
selected: ABP.BasicItem;
isModalVisible: boolean;
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
createForm() {
this.form = this.fb.group({
name: [this.selected.name],
});
}
openModal() {
this.createForm();
this.modalService.open(this.modalContent);
}
onSwitch() {
this.selected = {} as ABP.BasicItem;
this.openModal();
this.isModalVisible = true;
}
save() {
this.selected = this.form.value;
this.modalService.dismissAll();
this.selected.name = this.selected.name || '';
this.accountService
.findTenant(this.selected.name)
.pipe(
take(1),
catchError(err => {
this.toasterService.error(snq(() => err.error.error_description, 'An error occured.'), 'Error');
return throwError(err);
}),
)
.subscribe(({ success, tenantId }) => {
if (success) {
this.isModalVisible = false;
} else {
this.toasterService.error(`Given tenant is not available: ${this.selected.name}`, 'Error');
this.selected = {} as ABP.BasicItem;
}
this.store.dispatch(new SessionSetTenantId(success ? tenantId : null));
});
}
}

2
npm/ng-packs/packages/account/src/lib/models/index.ts

@ -1 +1,3 @@
export * from './options';
export * from './user';
export * from './tenant';

4
npm/ng-packs/packages/account/src/lib/models/tenant.ts

@ -0,0 +1,4 @@
export interface Tenant {
success: boolean;
tenantId: string;
}

29
npm/ng-packs/packages/account/src/lib/models/user.ts

@ -0,0 +1,29 @@
export interface RegisterRequest {
userName: string;
emailAddress: string;
password: string;
appName?: string;
}
export interface RegisterResponse {
tenantId: string;
userName: string;
name: string;
surname: string;
email: string;
emailConfirmed: boolean;
phoneNumber: string;
phoneNumberConfirmed: boolean;
twoFactorEnabled: boolean;
lockoutEnabled: boolean;
lockoutEnd: string;
concurrencyStamp: string;
isDeleted: boolean;
deleterId: string;
deletionTime: string;
lastModificationTime: string;
lastModifierId: string;
creationTime: string;
creatorId: string;
id: string;
}

30
npm/ng-packs/packages/account/src/lib/services/account.service.ts

@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { RestService, Rest } from '@abp/ng.core';
import { RegisterResponse, RegisterRequest, Tenant } from '../models';
@Injectable({
providedIn: 'root',
})
export class AccountService {
constructor(private rest: RestService) {}
findTenant(tenantName: string): Observable<Tenant> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/abp/multi-tenancy/find-tenant/${tenantName}`,
};
return this.rest.request<null, Tenant>(request);
}
register(body: RegisterRequest): Observable<RegisterResponse> {
const request: Rest.Request<RegisterRequest> = {
method: 'POST',
url: `/api/account/register`,
body,
};
return this.rest.request<RegisterRequest, RegisterResponse>(request, { throwErr: true });
}
}

0
npm/ng-packs/packages/account/src/lib/services/index.ts

4
npm/ng-packs/packages/core/src/lib/actions/session.actions.ts

@ -2,3 +2,7 @@ export class SessionSetLanguage {
static readonly type = '[Session] Set Language';
constructor(public payload: string) {}
}
export class SessionSetTenantId {
static readonly type = '[Session] Set Tenant Id';
constructor(public payload: string) {}
}

10
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -19,6 +19,7 @@ import { ProfileState } from './states/profile.state';
import { SessionState } from './states/session.state';
import { getInitialData } from './utils/initial-utils';
import { EllipsisDirective } from './directives/ellipsis.directive';
import { AutofocusDirective } from './directives/autofocus.directive';
@NgModule({
imports: [
@ -34,10 +35,11 @@ import { EllipsisDirective } from './directives/ellipsis.directive';
declarations: [
RouterOutletComponent,
DynamicLayoutComponent,
AutofocusDirective,
EllipsisDirective,
LocalizationPipe,
PermissionDirective,
VisibilityDirective,
LocalizationPipe,
EllipsisDirective,
],
exports: [
CommonModule,
@ -47,9 +49,11 @@ import { EllipsisDirective } from './directives/ellipsis.directive';
RouterModule,
RouterOutletComponent,
DynamicLayoutComponent,
AutofocusDirective,
EllipsisDirective,
LocalizationPipe,
PermissionDirective,
VisibilityDirective,
EllipsisDirective,
LocalizationPipe,
],
providers: [LocalizationPipe],

15
npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts

@ -0,0 +1,15 @@
import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core';
@Directive({
selector: '[autofocus]',
})
export class AutofocusDirective implements AfterViewInit {
@Input('autofocus')
delay: number = 0;
constructor(private elRef: ElementRef) {}
ngAfterViewInit(): void {
setTimeout(() => this.elRef.nativeElement.focus(), this.delay);
}
}

1
npm/ng-packs/packages/core/src/lib/directives/index.ts

@ -1,3 +1,4 @@
export * from './autofocus.directive';
export * from './ellipsis.directive';
export * from './permission.directive';
export * from './visibility.directive';

1
npm/ng-packs/packages/core/src/lib/models/session.ts

@ -1,5 +1,6 @@
export namespace Session {
export interface State {
language: string;
tenantId: string;
}
}

10
npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts

@ -1,9 +1,9 @@
import { Injectable, Inject, InjectionToken, Type } from '@angular/core';
import { NgxsPlugin, setValue, actionMatcher, InitState, UpdateState, NgxsNextPluginFn } from '@ngxs/store';
import { Inject, Injectable, InjectionToken } from '@angular/core';
import { Router, Routes } from '@angular/router';
import { ABP } from '../models';
import { actionMatcher, InitState, NgxsNextPluginFn, NgxsPlugin, setValue, UpdateState } from '@ngxs/store';
import snq from 'snq';
import { setChildRoute, sortRoutes, organizeRoutes } from '../utils/route-utils';
import { ABP } from '../models';
import { organizeRoutes } from '../utils/route-utils';
export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS');
@ -45,7 +45,7 @@ function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): a
wrappers = abpRoutes.filter(ar => ar.wrapper);
const transformed = [] as ABP.FullRoute[];
routes
.filter(route => route.component || route.loadChildren)
.filter(route => (route.data || {}).routes && (route.component || route.loadChildren))
.forEach(route => {
const abpPackage = abpRoutes.find(abp => abp.path.toLowerCase() === route.path.toLowerCase());
const { length } = transformed;

16
npm/ng-packs/packages/core/src/lib/states/session.state.ts

@ -1,5 +1,5 @@
import { State, Action, StateContext, Selector } from '@ngxs/store';
import { SessionSetLanguage } from '../actions/session.actions';
import { Action, Selector, State, StateContext } from '@ngxs/store';
import { SessionSetLanguage, SessionSetTenantId } from '../actions/session.actions';
import { Session } from '../models/session';
@State<Session.State>({
@ -12,6 +12,11 @@ export class SessionState {
return language;
}
@Selector()
static getSelectedTenantId({ tenantId }: Session.State): string {
return tenantId;
}
constructor() {}
@Action(SessionSetLanguage)
@ -20,4 +25,11 @@ export class SessionState {
language: payload,
});
}
@Action(SessionSetTenantId)
sessionSetTenantId({ patchState }: StateContext<Session.State>, { payload }: SessionSetTenantId) {
patchState({
tenantId: payload,
});
}
}

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

@ -58,7 +58,7 @@
</div>
</div>
<abp-modal size="md" [(visible)]="isModalVisible" *ngIf="isModalVisible" [centered]="true">
<abp-modal size="md" [(visible)]="isModalVisible">
<ng-template #abpHeader>
<h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}</h3>
</ng-template>
@ -68,7 +68,7 @@
<div class="form-group">
<label for="role-name">{{ 'AbpIdentity::RoleName' | abpLocalization }}</label
><span> * </span>
<input type="text" id="role-name" class="form-control" formControlName="name" />
<input type="text" id="role-name" class="form-control" formControlName="name" autofocus />
</div>
<div class="custom-checkbox custom-control mb-2">

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

@ -77,13 +77,13 @@
</div>
</div>
<abp-modal [(visible)]="isModalVisible" *ngIf="isModalVisible">
<abp-modal [(visible)]="isModalVisible">
<ng-template #abpHeader>
<h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser') | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<perfect-scrollbar class="ps-show-always" style="max-height: 65vh;">
<perfect-scrollbar class="ps-show-always" style="max-height: 70vh;">
<form [formGroup]="form">
<ngb-tabset>
<ngb-tab [title]="'AbpIdentity::UserInformations' | abpLocalization">
@ -92,7 +92,7 @@
<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" />
<input type="text" id="user-name" class="form-control" formControlName="userName" autofocus />
</div>
<div class="form-group">

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

@ -1,4 +1,4 @@
<abp-modal [(visible)]="visible" *ngIf="visible" size="lg">
<abp-modal [(visible)]="visible" (init)="initModal()">
<ng-container *ngIf="{ entityName: entityName$ | async } as data">
<ng-template #abpHeader>
<h4>{{ 'AbpPermissionManagement::Permissions' | abpLocalization }} - {{ data.entityName }}</h4>
@ -24,9 +24,12 @@
<ul class="nav nav-pills flex-column">
<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
}}</a>
<a
class="nav-link pointer"
[class.active]="selectedGroup.name === group.name"
(click)="onChangeGroup(group)"
>{{ group?.displayName }}</a
>
</li>
</perfect-scrollbar>
</ul>

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

@ -216,14 +216,14 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
this.permissions = getPermissions(permissionRes.groups);
this.visible = true;
setTimeout(() => {
this.setTabCheckboxState();
this.setGrantCheckboxState();
}, 0);
});
}
initModal() {
this.setTabCheckboxState();
this.setGrantCheckboxState();
}
ngOnChanges({ visible }: SimpleChanges): void {
if (!visible) return;

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

@ -88,7 +88,7 @@
</div>
</div>
<abp-modal [(visible)]="isModalVisible" *ngIf="isModalVisible">
<abp-modal [(visible)]="isModalVisible">
<ng-template #abpHeader>
<h3>{{ selectedModalContent.title | abpLocalization }}</h3>
</ng-template>
@ -112,7 +112,7 @@
<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" />
<input type="text" id="name" class="form-control" formControlName="name" autofocus/>
</div>
</div>
</form>
@ -123,7 +123,7 @@
<div class="mt-2">
<div class="form-group">
<div class="form-check">
<input id="useSharedDatabase" type="checkbox" class="form-check-input" formControlName="useSharedDatabase" />
<input id="useSharedDatabase" type="checkbox" class="form-check-input" formControlName="useSharedDatabase" autofocus/>
<label for="useSharedDatabase" class="font-check-label">{{
'AbpTenantManagement::DisplayName:UseSharedDatabase' | abpLocalization
}}</label>

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

@ -1,4 +1,4 @@
<abp-modal *ngIf="visible" [(visible)]="visible">
<abp-modal [(visible)]="visible">
<ng-template #abpHeader>
<h4>{{ 'AbpIdentity::ChangePassword' | abpLocalization }}</h4>
</ng-template>
@ -6,7 +6,8 @@
<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" />
><span> * </span
><input type="password" id="current-password" class="form-control" formControlName="password" autofocus />
</div>
<div class="form-group">
<label for="new-password">{{ 'AbpIdentity::DisplayName:NewPassword' | abpLocalization }}</label

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

@ -16,13 +16,13 @@
<ng-template #dropdownLink let-route>
<li
#navbarRootDropdown
class="nav-item dropdown"
ngbDropdown
display="static"
[abpPermission]="route.requiredPolicy"
[abpVisibility]="routeContainer"
class="nav-item dropdown pointer"
display="static"
>
<a ngbDropdownToggle class="nav-link dropdown-toggle" data-toggle="dropdown">
<a ngbDropdownToggle class="nav-link dropdown-toggle pointer" data-toggle="dropdown">
{{ route.name | abpLocalization }}
</a>
<div #routeContainer ngbDropdownMenu class="dropdown-menu dropdown-menu-right">
@ -53,13 +53,13 @@
<ng-template #dropdownChild let-child>
<div
[abpVisibility]="childrenContainer"
class="dropdown-submenu"
class="dropdown-submenu pointer"
ngbDropdown
[display]="isDropdownChildDynamic ? 'dynamic' : 'static'"
placement="right-top"
[abpPermission]="child.requiredPolicy"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<div ngbDropdownToggle [class.dropdown-toggle]="false" class="pointer">
<a
abpEllipsis="140px"
[abpEllipsisEnabled]="isDropdownChildDynamic"
@ -90,8 +90,8 @@
</abp-layout>
<ng-template #language>
<li class="nav-item dropdown" ngbDropdown>
<a ngbDropdownToggle class="nav-link dropdown-toggle" data-toggle="dropdown">
<li class="nav-item dropdown pointer" ngbDropdown>
<a ngbDropdownToggle class="nav-link dropdown-toggle pointer" data-toggle="dropdown">
{{ defaultLanguage$ | async }}
</a>
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-right">
@ -106,8 +106,8 @@
</ng-template>
<ng-template #currentUser>
<li *ngIf="(currentUser$ | async)?.isAuthenticated" class="nav-item dropdown" ngbDropdown>
<a ngbDropdownToggle class="nav-link dropdown-toggle" data-toggle="dropdown">
<li *ngIf="(currentUser$ | async)?.isAuthenticated" class="nav-item dropdown pointer" ngbDropdown>
<a ngbDropdownToggle class="nav-link dropdown-toggle pointer" data-toggle="dropdown">
{{ (currentUser$ | async)?.userName }}
</a>
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-right">

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

@ -8,8 +8,12 @@
</div>
</nav>
<div style="padding-top: 5rem;" class="container">
<router-outlet></router-outlet>
<div
[@routeAnimations]="outlet && outlet.activatedRoute && outlet.activatedRoute.routeConfig.path"
style="padding-top: 5rem;"
class="container"
>
<router-outlet #outlet="outlet"></router-outlet>
</div>
<abp-confirmation></abp-confirmation>

2
npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts

@ -1,8 +1,10 @@
import { Component } from '@angular/core';
import { slideFromBottom } from '@abp/ng.theme.shared';
@Component({
selector: ' abp-layout',
templateUrl: './layout.component.html',
animations: [slideFromBottom],
})
export class LayoutComponent {
isCollapsed: boolean = false;

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

@ -1,4 +1,4 @@
<abp-modal *ngIf="visible" [(visible)]="visible">
<abp-modal [(visible)]="visible">
<ng-template #abpHeader>
<h4>{{ 'AbpIdentity::PersonalInfo' | abpLocalization }}</h4>
</ng-template>
@ -6,7 +6,7 @@
<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" />
><span> * </span><input type="text" id="username" class="form-control" formControlName="userName" autofocus/>
</div>
<div class="row">
<div class="col col-md-6">

8
npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts

@ -2,12 +2,12 @@ import { MessageService } from 'primeng/components/common/messageservice';
import { Observable, Subject } from 'rxjs';
import { Toaster } from '../models/toaster';
export class AbstractToasterClass<T = Toaster.Options> {
protected status$: Subject<Toaster.Status>;
export class AbstractToaster<T = Toaster.Options> {
status$: Subject<Toaster.Status>;
protected key: string = 'abpToast';
key: string = 'abpToast';
protected sticky: boolean = false;
sticky: boolean = false;
constructor(protected messageService: MessageService) {}
info(message: string, title: string, options?: T): Observable<Toaster.Status> {

1
npm/ng-packs/packages/theme-shared/src/lib/animations/index.ts

@ -0,0 +1 @@
export * from './slide.animation';

6
npm/ng-packs/packages/theme-shared/src/lib/animations/slide.animation.ts

@ -0,0 +1,6 @@
import { animate, state, style, transition, trigger, query } from '@angular/animations';
export const slideFromBottom = trigger('routeAnimations', [
state('void', style({ 'margin-top': '20px', opacity: '0' })),
state('*', style({ 'margin-top': '0px', opacity: '1' })),
transition(':enter', [animate('0.2s ease-out', style({ opacity: '1', 'margin-top': '0px' }))]),
]);

68
npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts

@ -0,0 +1,68 @@
import { LoaderStart, LoaderStop } from '@abp/ng.core';
import { Component, Input, OnInit } from '@angular/core';
import { Actions, ofActionSuccessful } from '@ngxs/store';
import { filter } from 'rxjs/operators';
@Component({
selector: 'abp-button',
template: `
<button [attr.type]="buttonType" [ngClass]="buttonClass" [disabled]="loading">
<i [ngClass]="icon" class="mr-1"></i><ng-content></ng-content>
</button>
`,
})
export class ButtonComponent implements OnInit {
@Input()
buttonClass: string = 'btn btn-primary';
@Input()
buttonType: string = 'button';
@Input()
iconClass: string;
@Input()
loading: boolean = false;
@Input()
requestType: string | string[];
@Input()
requestURLContainSearchValue: string;
get icon(): string {
return `${this.loading ? 'fa fa-spin fa-spinner' : this.iconClass || 'd-none'}`;
}
constructor(private actions: Actions) {}
ngOnInit(): void {
if (this.requestType || this.requestURLContainSearchValue) {
this.actions
.pipe(
ofActionSuccessful(LoaderStart, LoaderStop),
filter((event: LoaderStart | LoaderStop) => {
let condition = true;
if (this.requestType) {
if (!Array.isArray(this.requestType)) this.requestType = [this.requestType];
condition =
condition &&
this.requestType.findIndex(type => type.toLowerCase() === event.payload.method.toLowerCase()) > -1;
}
if (condition && this.requestURLContainSearchValue) {
condition =
condition &&
event.payload.url.toLowerCase().indexOf(this.requestURLContainSearchValue.toLowerCase()) > -1;
}
return condition;
}),
)
.subscribe(() => {
this.loading = !this.loading;
});
}
}
}

4
npm/ng-packs/packages/theme-shared/src/lib/components/index.ts

@ -1,3 +1,5 @@
export * from './button/button.component';
export * from './confirmation/confirmation.component';
export * from './toast/toast.component';
export * from './loader-bar/loader-bar.component';
export * from './modal/modal.component';
export * from './toast/toast.component';

2
npm/ng-packs/packages/theme-shared/src/lib/components/modal/README.md

@ -3,7 +3,7 @@
Example Usage:
```html
<abp-modal size="lg" [(visible)]="isModalShow" [centered]="true">
<abp-modal size="md" [(visible)]="isModalShow" [centered]="false">
<ng-template #abpHeader>
<h3>Modal Title</h3>
</ng-template>

12
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html

@ -1,9 +1,8 @@
<div
*ngIf="showModal"
id="abp-modal"
tabindex="-1"
class="modal fade {{ modalClass }}"
[class.show]="visible"
[style.display]="visible ? 'block' : 'none'"
class="modal fade {{ modalClass }} d-block show"
[style.padding-right.px]="'15'"
>
<div
@ -20,7 +19,12 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="abp-modal-body" class="modal-body">
<div
id="abp-modal-body"
class="modal-body"
[style.height]="height || undefined"
[style.minHeight]="minHeight || undefined"
>
<ng-container *ngTemplateOutlet="abpBody"></ng-container>
<div id="abp-modal-footer" class="modal-footer">

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

@ -17,6 +17,8 @@ import { Toaster } from '../../models/toaster';
export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
const ANIMATION_TIMEOUT = 200;
@Component({
selector: 'abp-modal',
templateUrl: './modal.component.html',
@ -27,8 +29,15 @@ export class ModalComponent implements OnDestroy {
return this._visible;
}
set visible(value: boolean) {
if (typeof value !== 'boolean') return;
if (!this.modalContent) {
setTimeout(() => (this.visible = value), 0);
if (value) {
setTimeout(() => {
this.showModal = value;
this.visible = value;
}, 0);
}
return;
}
@ -40,13 +49,13 @@ export class ModalComponent implements OnDestroy {
this.renderer.addClass(this.modalContent.nativeElement, 'fade-out-top');
setTimeout(() => {
this.setVisible(value);
this.renderer.removeClass(this.modalContent.nativeElement, 'fade-out-top');
// this.renderer.removeClass(this.modalContent.nativeElement, 'fade-out-top');
this.ngOnDestroy();
}, 350);
}, ANIMATION_TIMEOUT - 10);
}
}
@Input() centered: boolean = true;
@Input() centered: boolean = false;
@Input() modalClass: string = '';
@ -54,6 +63,12 @@ export class ModalComponent implements OnDestroy {
@Output() visibleChange = new EventEmitter<boolean>();
@Input() height: number;
@Input() minHeight: number;
@Output() init = new EventEmitter<void>();
@ContentChild('abpHeader', { static: false }) abpHeader: TemplateRef<any>;
@ContentChild('abpBody', { static: false }) abpBody: TemplateRef<any>;
@ -66,10 +81,12 @@ export class ModalComponent implements OnDestroy {
_visible: boolean = false;
closable: boolean = false;
showModal: boolean = false;
isOpenConfirmation: boolean = false;
closable: boolean = false;
destroy$ = new Subject<void>();
constructor(private renderer: Renderer2, private confirmationService: ConfirmationService) {}
@ -81,8 +98,10 @@ export class ModalComponent implements OnDestroy {
setVisible(value: boolean) {
this._visible = value;
this.visibleChange.emit(value);
this.showModal = value;
value
? timer(500)
? timer(ANIMATION_TIMEOUT + 100)
.pipe(take(1))
.subscribe(_ => (this.closable = true))
: (this.closable = false);
@ -91,16 +110,18 @@ export class ModalComponent implements OnDestroy {
listen() {
fromEvent(document, 'click')
.pipe(
debounceTime(350),
debounceTime(100),
takeUntil(this.destroy$),
filter(
(event: MouseEvent) =>
filter((event: MouseEvent) => {
const isOpenConfirmation = this.isOpenConfirmation || document.querySelector('p-toastitem');
return (
event &&
this.closable &&
this.modalContent &&
!this.isOpenConfirmation &&
!this.modalContent.nativeElement.contains(event.target),
),
!isOpenConfirmation &&
!this.modalContent.nativeElement.contains(event.target)
);
}),
)
.subscribe(_ => {
this.close();
@ -109,8 +130,8 @@ export class ModalComponent implements OnDestroy {
fromEvent(document, 'keyup')
.pipe(
takeUntil(this.destroy$),
debounceTime(250),
filter((key: KeyboardEvent) => key && key.code === 'Escape' && this.closable),
debounceTime(350),
)
.subscribe(_ => {
this.close();
@ -122,9 +143,10 @@ export class ModalComponent implements OnDestroy {
.pipe(
takeUntil(this.destroy$),
filter(() => !!(this.closable && this.modalContent)),
debounceTime(350),
)
.subscribe(() => this.close());
this.init.emit();
}
close() {
@ -139,7 +161,7 @@ export class ModalComponent implements OnDestroy {
this.confirmationService
.warn('AbpAccount::AreYouSureYouWantToCancelEditingWarningMessage', 'AbpAccount::AreYouSure')
.subscribe((status: Toaster.Status) => {
timer(400).subscribe(() => {
timer(ANIMATION_TIMEOUT).subscribe(() => {
this.isOpenConfirmation = false;
});
@ -155,7 +177,7 @@ export class ModalComponent implements OnDestroy {
function getFlatNodes(nodes: NodeList): HTMLElement[] {
return Array.from(nodes).reduce(
(acc, val) => [...acc, ...(val.childNodes && val.childNodes.length ? Array.from(val.childNodes) : [val])],
(acc, val) => [...acc, ...(val.childNodes && val.childNodes.length ? getFlatNodes(val.childNodes) : [val])],
[],
);
}

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

@ -38,11 +38,11 @@ export default `
/* <animations */
.fade-in-top {
animation: fadeInTop 0.4s ease-in-out;
animation: fadeInTop 0.2s ease-in-out;
}
.fade-out-top {
animation: fadeOutTop 0.4s ease-in-out;
animation: fadeOutTop 0.2s ease-in-out;
}

3
npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts

@ -14,6 +14,7 @@ import { Observable } from 'rxjs';
import { ErrorComponent } from '../components/errors/error.component';
import { Toaster } from '../models/toaster';
import { ConfirmationService } from '../services/confirmation.service';
import snq from 'snq';
const DEFAULTS = {
defaultError: {
@ -50,7 +51,7 @@ export class ErrorHandler {
) {
actions.pipe(ofActionSuccessful(RestOccurError)).subscribe(res => {
const { payload: err = {} as HttpErrorResponse | any } = res;
const body = (err as HttpErrorResponse).error.error;
const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULTS.defaultError.message);
if (err.headers.get('_AbpErrorFormat')) {
const confirmation$ = this.showError(null, null, body);

8
npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts

@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
import { AbstractToasterClass } from '../abstracts/toaster';
import { AbstractToaster } from '../abstracts/toaster';
import { Confirmation } from '../models/confirmation';
@Injectable({ providedIn: 'root' })
export class ConfirmationService extends AbstractToasterClass<Confirmation.Options> {
protected key: string = 'abpConfirmation';
export class ConfirmationService extends AbstractToaster<Confirmation.Options> {
key: string = 'abpConfirmation';
protected sticky: boolean = true;
sticky: boolean = true;
}

4
npm/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts

@ -1,9 +1,9 @@
import { Injectable } from '@angular/core';
import { AbstractToasterClass } from '../abstracts/toaster';
import { AbstractToaster } from '../abstracts/toaster';
import { Message } from 'primeng/components/common/message';
@Injectable({ providedIn: 'root' })
export class ToasterService extends AbstractToasterClass {
export class ToasterService extends AbstractToaster {
addAll(messages: Message[]): void {
this.messageService.addAll(messages.map(message => ({ key: this.key, ...message })));
}

12
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -13,6 +13,7 @@ import { ModalComponent } from './components/modal/modal.component';
import { ToastComponent } from './components/toast/toast.component';
import styles from './contants/styles';
import { ErrorHandler } from './handlers/error.handler';
import { ButtonComponent } from './components/button/button.component';
export function appendScript(injector: Injector) {
const fn = function() {
@ -41,8 +42,15 @@ export function appendScript(injector: Injector) {
targetSelector: '.form-group',
}),
],
declarations: [ConfirmationComponent, ToastComponent, ModalComponent, ErrorComponent, LoaderBarComponent],
exports: [NgbModalModule, ConfirmationComponent, ToastComponent, ModalComponent, LoaderBarComponent],
declarations: [
ButtonComponent,
ConfirmationComponent,
ToastComponent,
ModalComponent,
ErrorComponent,
LoaderBarComponent,
],
exports: [NgbModalModule, ButtonComponent, ConfirmationComponent, ToastComponent, ModalComponent, LoaderBarComponent],
entryComponents: [ErrorComponent],
})
export class ThemeSharedModule {

1
npm/ng-packs/packages/theme-shared/src/public-api.ts

@ -3,6 +3,7 @@
*/
export * from './lib/theme-shared.module';
export * from './lib/animations';
export * from './lib/components';
export * from './lib/models';
export * from './lib/services';

2
npm/ng-packs/scripts/build.js

@ -35,4 +35,6 @@ import fse from 'fs-extra';
cwd: '../',
});
});
process.exit(0);
})();

3
npm/ng-packs/scripts/sync.js

@ -1,5 +1,4 @@
// ESM syntax is supported.
import execa from 'execa';
import fse from 'fs-extra';
(async () => {
@ -21,4 +20,6 @@ import fse from 'fs-extra';
await fse.writeJSON(srcPackagePath, { ...srcPackage, version }, { spaces: 2 });
});
process.exit(0);
})();

25
templates/app/angular/package.json

@ -17,14 +17,14 @@
"@abp/ng.identity": "^0.8.0",
"@abp/ng.tenant-management": "^0.8.0",
"@abp/ng.theme.basic": "^0.8.0",
"@angular/animations": "~8.1.2",
"@angular/common": "~8.1.2",
"@angular/compiler": "~8.1.2",
"@angular/core": "~8.1.2",
"@angular/forms": "~8.1.2",
"@angular/platform-browser": "~8.1.2",
"@angular/platform-browser-dynamic": "~8.1.2",
"@angular/router": "~8.1.2",
"@angular/animations": "~8.2.0",
"@angular/common": "~8.2.0",
"@angular/compiler": "~8.2.0",
"@angular/core": "~8.2.0",
"@angular/forms": "~8.2.0",
"@angular/platform-browser": "~8.2.0",
"@angular/platform-browser-dynamic": "~8.2.0",
"@angular/router": "~8.2.0",
"@angularclass/hmr": "^2.1.3",
"@ngxs/devtools-plugin": "^3.5.0",
"@ngxs/hmr-plugin": "^3.5.0",
@ -33,10 +33,10 @@
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.801.2",
"@angular/cli": "~8.1.2",
"@angular/compiler-cli": "~8.1.2",
"@angular/language-service": "~8.1.2",
"@angular-devkit/build-angular": "~0.802.0",
"@angular/cli": "~8.2.0",
"@angular/compiler-cli": "~8.2.0",
"@angular/language-service": "~8.2.0",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
@ -48,6 +48,7 @@
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"ngxs-schematic": "^1.1.5",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",

4
templates/app/angular/src/app/app.component.ts

@ -7,6 +7,4 @@ import { Component } from '@angular/core';
<router-outlet></router-outlet>
`,
})
export class AppComponent {
title = 'myProjectName';
}
export class AppComponent {}

Loading…
Cancel
Save