pull/1875/head
Halil İbrahim Kalkan 5 years ago
commit fe636cdd86

@ -87,7 +87,7 @@
</label>
</div>
<select asp-items="Model.ProjectSelectItems" class="form-control" onchange="window.location.replace(this.value)"></select>
<select asp-items="Model.ProjectSelectItems" class="form-control" onchange="window.location.pathname = this.value"></select>
</div>
</div>
</div>

@ -187,7 +187,7 @@ namespace Volo.Docs.Pages.Documents.Project
ProjectSelectItems = projects.Items.Select(p => new SelectListItem
{
Text = p.Name,
Value = p.Id != Project.Id ? "/" + DocumentsUrlPrefix + LanguageCode + "/" + p.ShortName + "/" + DocsAppConsts.Latest : null,
Value = p.Id != Project.Id ? DocumentsUrlPrefix + LanguageCode + "/" + p.ShortName + "/" + DocsAppConsts.Latest : null,
Selected = p.Id == Project.Id
}).ToList();
}

@ -25,6 +25,11 @@ const routes: Routes = [
loadChildren: () =>
import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule),
},
{
path: 'setting-management',
loadChildren: () =>
import('./lazy-libs/setting-management-wrapper.module').then(m => m.SettingManagementWrapperModule),
},
];
@NgModule({

@ -14,6 +14,7 @@ import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { AccountConfigModule } from '@abp/ng.account.config';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management.config';
@NgModule({
declarations: [AppComponent],
@ -28,6 +29,7 @@ import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
AccountConfigModule.forRoot({ redirectUrl: '/' }),
IdentityConfigModule,
TenantManagementConfigModule,
SettingManagementConfigModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { SettingManagementModule } from '@abp/ng.setting-management';
@NgModule({
imports: [SettingManagementModule],
})
export class SettingManagementWrapperModule {}

@ -1,4 +1,4 @@
import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core';
import { eLayoutType, RestService, addAbpRoutes } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@ -7,7 +7,7 @@ import { Router } from '@angular/router';
})
export class AccountConfigService {
constructor(private router: Router, private restService: RestService) {
ABP_ROUTES.push({
addAbpRoutes({
name: 'AbpAccount::Menu:Account',
path: 'account',
invisible: true,

@ -4,5 +4,5 @@
"lib": {
"entryFile": "src/public-api.ts"
},
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"]
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "abp/ng.account.config"]
}

@ -2,7 +2,8 @@
"name": "@abp/ng.account",
"version": "0.9.0",
"dependencies": {
"@abp/ng.theme.shared": "^0.9.0"
"@abp/ng.theme.shared": "^0.9.0",
"abp/ng.account.config": "^0.0.1"
},
"publishConfig": {
"access": "public"

@ -4,7 +4,7 @@ import { RestService, Rest } from '@abp/ng.core';
import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class AccountService {
constructor(private rest: RestService) {}
@ -12,7 +12,7 @@ export class AccountService {
findTenant(tenantName: string): Observable<TenantIdResponse> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`,
};
return this.rest.request<null, TenantIdResponse>(request);
@ -22,7 +22,7 @@ export class AccountService {
const request: Rest.Request<RegisterRequest> = {
method: 'POST',
url: '/api/account/register',
body
body,
};
return this.rest.request<RegisterRequest, RegisterResponse>(request, { skipHandleError: true });

@ -27,6 +27,7 @@ import { getInitialData, localeInitializer } from './utils/initial-utils';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS } from './plugins/config/config.plugin';
import { ForDirective } from './directives/for.directive';
import { AbstractNgModelComponent } from './abstracts/ng-model.component';
import { SortDirective } from './directives/sort.directive';
@NgModule({
imports: [
@ -46,6 +47,7 @@ import { AbstractNgModelComponent } from './abstracts/ng-model.component';
EllipsisDirective,
ForDirective,
FormSubmitDirective,
SortDirective,
LocalizationPipe,
SortPipe,
PermissionDirective,
@ -68,6 +70,7 @@ import { AbstractNgModelComponent } from './abstracts/ng-model.component';
FormSubmitDirective,
LocalizationPipe,
SortPipe,
SortDirective,
PermissionDirective,
VisibilityDirective,
InputEventDebounceDirective,

@ -0,0 +1,12 @@
import { Directive, ElementRef, Input, Optional, Self } from '@angular/core';
import { Table } from 'primeng/table';
@Directive({
selector: '[abpSort]',
})
export class SortDirective {
constructor(private elementRef: ElementRef, @Optional() @Self() table: Table) {
console.warn(elementRef);
setInterval(() => console.warn(table.value), 1000);
}
}

@ -9,7 +9,7 @@ export type SortOrder = 'asc' | 'desc';
export class SortPipe implements PipeTransform {
intialValue: any[];
transform(value: any[], sortOrder: SortOrder = 'asc', sortKey: string): any {
transform(value: any[], sortOrder: SortOrder | string = 'asc', sortKey?: string): any {
sortOrder = sortOrder && (sortOrder.toLowerCase() as any);
if (!this.intialValue) this.intialValue = clone(value);

@ -3,13 +3,11 @@ import { Router, Routes } from '@angular/router';
import { actionMatcher, InitState, NgxsNextPluginFn, NgxsPlugin, setValue, UpdateState } from '@ngxs/store';
import snq from 'snq';
import { ABP } from '../../models';
import { organizeRoutes } from '../../utils/route-utils';
import { organizeRoutes, getAbpRoutes } from '../../utils/route-utils';
import clone from 'just-clone';
export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN_OPTIONS');
export let ABP_ROUTES = [] as ABP.FullRoute[];
@Injectable()
export class ConfigPlugin implements NgxsPlugin {
private initialized = false;
@ -42,24 +40,21 @@ export class ConfigPlugin implements NgxsPlugin {
}
function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): any {
/**
*
* @deprecated since version 0.9.0
*/
const abpRoutes: ABP.FullRoute[] = routes
// TODO: remove in v1
const oldAbpRoutes: ABP.FullRoute[] = routes
.filter(route => {
return snq(() => route.data.routes.routes.find(r => r.path === route.path), false);
})
.reduce((acc, val) => [...acc, ...val.data.routes.routes], []);
// tslint:disable-next-line: deprecation
ABP_ROUTES = [...ABP_ROUTES, ...abpRoutes];
const abpRoutes = [...getAbpRoutes(), ...oldAbpRoutes];
wrappers = ABP_ROUTES.filter(ar => ar.wrapper);
wrappers = abpRoutes.filter(ar => ar.wrapper);
const transformed = [] as ABP.FullRoute[];
routes
.filter(route => route.component || route.loadChildren)
.forEach(route => {
const abpPackage = ABP_ROUTES.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper);
const abpPackage = abpRoutes.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper);
const { length } = transformed;

@ -230,6 +230,7 @@ export class ConfigState {
});
}
if (typeof localization !== 'string') localization = '';
return localization || defaultValue || key;
},
);

@ -4,13 +4,13 @@ import { NGXS_PLUGINS, NgxsModule, InitState, Store } from '@ngxs/store';
import { environment } from '../../../../../apps/dev-app/src/environments/environment';
import { LAYOUTS } from '../../../../theme-basic/src/public-api';
import { ABP } from '../models';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS, ABP_ROUTES } from '../plugins';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS, addAbpRoutes } from '../plugins';
import { RouterOutletComponent } from '../components';
import { ConfigState } from '../states';
import { CoreModule } from '../core.module';
import { eLayoutType } from '../enums/common';
ABP_ROUTES.push(
addAbpRoutes([
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
@ -54,7 +54,7 @@ ABP_ROUTES.push(
},
],
},
);
]);
const expectedState = {
environment,

@ -60,3 +60,17 @@ export function sortRoutes(routes: ABP.FullRoute[] = []): ABP.FullRoute[] {
return route;
});
}
const ABP_ROUTES = [] as ABP.FullRoute[];
export function addAbpRoutes(routes: ABP.FullRoute | ABP.FullRoute[]): void {
if (!Array.isArray(routes)) {
routes = [routes];
}
ABP_ROUTES.push(...routes);
}
export function getAbpRoutes(): ABP.FullRoute[] {
return ABP_ROUTES;
}

@ -12,6 +12,7 @@ export * from './lib/enums';
export * from './lib/guards';
export * from './lib/interceptors';
export * from './lib/models';
export * from './lib/pipes';
export * from './lib/plugins';
export * from './lib/services';
export * from './lib/states';

@ -1,4 +1,4 @@
import { ABP_ROUTES, eLayoutType, RestService } from '@abp/ng.core';
import { addAbpRoutes, eLayoutType, RestService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
@ -8,7 +8,7 @@ import { Observable } from 'rxjs';
})
export class IdentityConfigService {
constructor(private router: Router, private restService: RestService) {
ABP_ROUTES.push(
addAbpRoutes([
{
name: 'AbpUiNavigation::Menu:Administration',
path: '',
@ -27,6 +27,6 @@ export class IdentityConfigService {
{ path: 'users', name: 'AbpIdentity::Users', order: 1, requiredPolicy: 'AbpIdentity.Users' },
],
},
);
]);
}
}

@ -24,7 +24,7 @@
</div>
<p-table
*ngIf="[130, 200] as columnWidths"
[value]="data$ | async"
[value]="data$ | async | abpSort: sortOrder:sortKey"
[lazy]="true"
[lazyLoadOnInit]="false"
[paginator]="true"
@ -51,11 +51,9 @@
<ng-template pTemplate="header" let-columns>
<tr>
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
<th pResizableColumn (click)="changeSortOrder()">
<th pResizableColumn (click)="sortByKey('name')">
{{ 'AbpIdentity::RoleName' | abpLocalization }}
<span class="float-right"
><i [ngClass]="['fa', sortOrder === 'desc' ? 'fa-sort-desc' : 'fa-sort-asc']"></i
></span>
<abp-sort-order-icon key="name" [selectedKey]="sortKey" [order]="sortOrder"></abp-sort-order-icon>
</th>
</tr>
</ng-template>

@ -11,7 +11,7 @@ import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-roles',
templateUrl: './roles.component.html'
templateUrl: './roles.component.html',
})
export class RolesComponent {
@Select(IdentityState.getRoles)
@ -30,15 +30,15 @@ export class RolesComponent {
providerKey: string;
pageQuery: ABP.PageQueryParams = {
sorting: 'name'
};
pageQuery: ABP.PageQueryParams = {};
loading = false;
modalBusy = false;
sortOrder = 'asc';
sortOrder: string = '';
sortKey: string = '';
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
@ -54,10 +54,10 @@ export class RolesComponent {
this.form = this.fb.group({
name: new FormControl({ value: this.selected.name || '', disabled: this.selected.isStatic }, [
Validators.required,
Validators.maxLength(256)
Validators.maxLength(256),
]),
isDefault: [this.selected.isDefault || false],
isPublic: [this.selected.isPublic || false]
isPublic: [this.selected.isPublic || false],
});
}
@ -89,7 +89,7 @@ export class RolesComponent {
.dispatch(
this.selected.id
? new UpdateRole({ ...this.form.value, id: this.selected.id })
: new CreateRole(this.form.value)
: new CreateRole(this.form.value),
)
.subscribe(() => {
this.modalBusy = false;
@ -100,7 +100,7 @@ export class RolesComponent {
delete(id: string, name: string) {
this.confirmationService
.warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [name]
messageLocalizationParams: [name],
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -124,7 +124,21 @@ export class RolesComponent {
.subscribe();
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
sortByKey(sortKey: string) {
this.sortKey = sortKey;
switch (this.sortOrder) {
case '':
this.sortOrder = 'asc';
break;
case 'asc':
this.sortOrder = 'desc';
break;
case 'desc':
this.sortOrder = '';
this.sortKey = '';
break;
default:
break;
}
}
}

@ -29,8 +29,9 @@
/></label>
</div>
<p-table
abpSort
*ngIf="[130, 200, 200, 200] as columnWidths"
[value]="data$ | async"
[value]="data$ | async | abpSort: sortOrder:sortKey"
[lazy]="true"
[lazyLoadOnInit]="false"
[paginator]="true"
@ -57,14 +58,27 @@
<ng-template pTemplate="header">
<tr>
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
<th pResizableColumn (click)="changeSortOrder()">
<th pResizableColumn (click)="usernameSortIcon.sort('userName')">
{{ 'AbpIdentity::UserName' | abpLocalization }}
<span class="float-right"
><i [ngClass]="['fa', sortOrder === 'desc' ? 'fa-sort-desc' : 'fa-sort-asc']"></i
></span>
<abp-sort-order-icon
#usernameSortIcon
key="userName"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
<th pResizableColumn (click)="usernameSortIcon.sort('email')">
{{ 'AbpIdentity::EmailAddress' | abpLocalization }}
<abp-sort-order-icon key="email" [(selectedKey)]="sortKey" [(order)]="sortOrder"></abp-sort-order-icon>
</th>
<th pResizableColumn (click)="usernameSortIcon.sort('phoneNumber')">
{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}
<abp-sort-order-icon
key="phoneNumber"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
<th pResizableColumn>{{ 'AbpIdentity::EmailAddress' | abpLocalization }}</th>
<th pResizableColumn>{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-data>

@ -12,13 +12,13 @@ import {
GetUserById,
GetUserRoles,
GetUsers,
UpdateUser
UpdateUser,
} from '../../actions/identity.actions';
import { Identity } from '../../models/identity';
import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-users',
templateUrl: './users.component.html'
templateUrl: './users.component.html',
})
export class UsersComponent {
@Select(IdentityState.getUsers)
@ -42,9 +42,7 @@ export class UsersComponent {
providerKey: string;
pageQuery: ABP.PageQueryParams = {
sorting: 'userName'
};
pageQuery: ABP.PageQueryParams = {};
isModalVisible: boolean;
@ -52,7 +50,9 @@ export class UsersComponent {
modalBusy = false;
sortOrder = 'asc';
sortOrder = '';
sortKey = '';
trackByFn: TrackByFunction<AbstractControl> = (index, item) => Object.keys(item)[0] || index;
@ -80,10 +80,10 @@ export class UsersComponent {
roleNames: this.fb.array(
this.roles.map(role =>
this.fb.group({
[role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))]
})
)
)
[role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))],
}),
),
),
});
if (!this.selected.userName) {
this.form.addControl('password', new FormControl('', [Validators.required, Validators.maxLength(32)]));
@ -107,7 +107,7 @@ export class UsersComponent {
.pipe(
switchMap(() => this.store.dispatch(new GetUserRoles(id))),
pluck('IdentityState'),
take(1)
take(1),
)
.subscribe((state: Identity.State) => {
this.selected = state.selectedUser;
@ -123,7 +123,7 @@ export class UsersComponent {
const { roleNames } = this.form.value;
const mappedRoleNames = snq(
() => roleNames.filter(role => !!role[Object.keys(role)[0]]).map(role => Object.keys(role)[0]),
[]
[],
);
this.store
@ -132,12 +132,12 @@ export class UsersComponent {
? new UpdateUser({
...this.form.value,
id: this.selected.id,
roleNames: mappedRoleNames
roleNames: mappedRoleNames,
})
: new CreateUser({
...this.form.value,
roleNames: mappedRoleNames
})
roleNames: mappedRoleNames,
}),
)
.subscribe(() => {
this.modalBusy = false;
@ -148,7 +148,7 @@ export class UsersComponent {
delete(id: string, userName: string) {
this.confirmationService
.warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [userName]
messageLocalizationParams: [userName],
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -171,8 +171,4 @@ export class UsersComponent {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
}

@ -1,18 +1,29 @@
import { Injectable } from '@angular/core';
import { ABP_ROUTES, eLayoutType } from '@abp/ng.core';
import { addAbpRoutes, eLayoutType, PatchRouteByName, ABP } from '@abp/ng.core';
import { getSettingTabs } from '@abp/ng.theme.shared';
import { Store } from '@ngxs/store';
@Injectable({
providedIn: 'root',
})
export class SettingManagementConfigService {
constructor() {
ABP_ROUTES.push({
name: 'Settings',
constructor(private store: Store) {
const route = {
name: 'AbpSettingManagement::Settings',
path: 'setting-management',
parentName: 'AbpUiNavigation::Menu:Administration',
layout: eLayoutType.application,
order: 6,
iconClass: 'fa fa-cog',
} as ABP.FullRoute;
addAbpRoutes(route);
setTimeout(() => {
const tabs = getSettingTabs();
if (!tabs || !tabs.length) {
this.store.dispatch(new PatchRouteByName('AbpSettingManagement::Settings', { ...route, invisible: true }));
}
});
}
}

@ -1,5 +1,5 @@
import { Component, TrackByFunction, OnInit } from '@angular/core';
import { SettingTab, SETTING_TABS } from '@abp/ng.theme.shared';
import { SettingTab, getSettingTabs } from '@abp/ng.theme.shared';
import { Router } from '@angular/router';
import { Store } from '@ngxs/store';
import { ConfigState } from '@abp/ng.core';
@ -18,9 +18,9 @@ export class SettingManagementComponent implements OnInit {
constructor(private router: Router, private store: Store) {}
ngOnInit() {
this.settings = SETTING_TABS.filter(setting =>
this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)),
).sort((a, b) => a.order - b.order);
this.settings = getSettingTabs()
.filter(setting => this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)))
.sort((a, b) => a.order - b.order);
if (this.settings.length) {
this.selected = this.settings[0];
}

@ -1,12 +1,12 @@
import { Injectable } from '@angular/core';
import { ABP_ROUTES, eLayoutType } from '@abp/ng.core';
import { addAbpRoutes, eLayoutType } from '@abp/ng.core';
@Injectable({
providedIn: 'root',
})
export class TenantManagementConfigService {
constructor() {
ABP_ROUTES.push({
addAbpRoutes({
name: 'AbpTenantManagement::Menu:TenantManagement',
path: 'tenant-management',
parentName: 'AbpUiNavigation::Menu:Administration',

@ -31,7 +31,7 @@
</div>
<p-table
*ngIf="[130, 200] as columnWidths"
[value]="data$ | async | abpSort: sortOrder"
[value]="data$ | async | abpSort: sortOrder:sortKey"
[lazy]="true"
[lazyLoadOnInit]="false"
[paginator]="true"
@ -58,11 +58,14 @@
<ng-template pTemplate="header" let-columns>
<tr>
<th>{{ 'AbpTenantManagement::Actions' | abpLocalization }}</th>
<th pResizableColumn (click)="changeSortOrder()">
<th pResizableColumn (click)="sortOrderIcon.sort('name')">
{{ 'AbpTenantManagement::TenantName' | abpLocalization }}
<span class="float-right"
><i [ngClass]="['fa', sortOrder === 'desc' ? 'fa-sort-desc' : 'fa-sort-asc']"></i
></span>
<abp-sort-order-icon
#sortOrderIcon
key="name"
[(selectedKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
</tr>
</ng-template>

@ -10,7 +10,7 @@ import {
DeleteTenant,
GetTenants,
GetTenantById,
UpdateTenant
UpdateTenant,
} from '../../actions/tenant-management.actions';
import { TenantManagementService } from '../../services/tenant-management.service';
import { TenantManagementState } from '../../states/tenant-management.state';
@ -23,7 +23,7 @@ interface SelectedModalContent {
@Component({
selector: 'abp-tenants',
templateUrl: './tenants.component.html'
templateUrl: './tenants.component.html',
})
export class TenantsComponent {
@Select(TenantManagementState.get)
@ -50,15 +50,15 @@ export class TenantsComponent {
_useSharedDatabase: boolean;
pageQuery: ABP.PageQueryParams = {
sorting: 'name'
};
pageQuery: ABP.PageQueryParams = {};
loading = false;
modalBusy = false;
sortOrder = 'asc';
sortOrder = '';
sortKey = '';
get useSharedDatabase(): boolean {
return this.defaultConnectionStringForm.get('useSharedDatabase').value;
@ -78,7 +78,7 @@ export class TenantsComponent {
private confirmationService: ConfirmationService,
private tenantService: TenantManagementService,
private fb: FormBuilder,
private store: Store
private store: Store,
) {}
onSearch(value) {
@ -88,14 +88,14 @@ export class TenantsComponent {
private createTenantForm() {
this.tenantForm = this.fb.group({
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]]
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]],
});
}
private createDefaultConnectionStringForm() {
this.defaultConnectionStringForm = this.fb.group({
useSharedDatabase: this._useSharedDatabase,
defaultConnectionString: [this.defaultConnectionString || '']
defaultConnectionString: [this.defaultConnectionString || ''],
});
}
@ -103,7 +103,7 @@ export class TenantsComponent {
this.selectedModalContent = {
title,
template,
type
type,
};
this.isModalVisible = true;
@ -117,7 +117,7 @@ export class TenantsComponent {
switchMap(selected => {
this.selected = selected;
return this.tenantService.getDefaultConnectionString(id);
})
}),
)
.subscribe(fetchedConnectionString => {
this._useSharedDatabase = fetchedConnectionString ? false : true;
@ -158,7 +158,7 @@ export class TenantsComponent {
.deleteDefaultConnectionString(this.selected.id)
.pipe(
take(1),
finalize(() => (this.modalBusy = false))
finalize(() => (this.modalBusy = false)),
)
.subscribe(() => {
this.isModalVisible = false;
@ -168,7 +168,7 @@ export class TenantsComponent {
.updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString })
.pipe(
take(1),
finalize(() => (this.modalBusy = false))
finalize(() => (this.modalBusy = false)),
)
.subscribe(() => {
this.isModalVisible = false;
@ -184,7 +184,7 @@ export class TenantsComponent {
.dispatch(
this.selected.id
? new UpdateTenant({ ...this.tenantForm.value, id: this.selected.id })
: new CreateTenant(this.tenantForm.value)
: new CreateTenant(this.tenantForm.value),
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
@ -195,7 +195,7 @@ export class TenantsComponent {
delete(id: string, name: string) {
this.confirmationService
.warn('AbpTenantManagement::TenantDeletionConfirmationMessage', 'AbpTenantManagement::AreYouSure', {
messageLocalizationParams: [name]
messageLocalizationParams: [name],
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -218,8 +218,4 @@ export class TenantsComponent {
.pipe(finalize(() => (this.loading = false)))
.subscribe();
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
}

@ -8,3 +8,4 @@ export * from './modal/modal.component';
export * from './profile/profile.component';
export * from './table-empty-message/table-empty-message.component';
export * from './toast/toast.component';
export * from './sort-order-icon/sort-order-icon.component';

@ -0,0 +1,3 @@
<span class="float-right {{ iconClass }}">
<i class="fa {{ icon }}"></i>
</span>

@ -0,0 +1,61 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'abp-sort-order-icon',
templateUrl: './sort-order-icon.component.html',
})
export class SortOrderIconComponent {
private _order: string;
private _selectedKey: string;
@Input()
set selectedKey(value: string) {
this._selectedKey = value;
this.selectedKeyChange.emit(value);
}
get selectedKey(): string {
return this._selectedKey;
}
@Output() readonly selectedKeyChange = new EventEmitter<string>();
@Input()
key: string;
@Input()
set order(value: string) {
this._order = value;
this.orderChange.emit(value);
}
get order(): string {
return this._order;
}
@Output() readonly orderChange = new EventEmitter<string>();
@Input()
iconClass: string;
get icon(): string {
if (!this.selectedKey) return 'fa-sort';
if (this.selectedKey === this.key) return `fa-sort-${this.order}`;
else return '';
}
sort(key: string) {
this.selectedKey = key;
switch (this.order) {
case '':
this.order = 'asc';
break;
case 'asc':
this.order = 'desc';
this.orderChange.emit('desc');
break;
case 'desc':
this.order = '';
this.selectedKey = '';
break;
}
}
}

@ -7,4 +7,16 @@ export interface SettingTab {
requiredPolicy?: string;
}
export const SETTING_TABS = [] as SettingTab[];
const SETTING_TABS = [] as SettingTab[];
export function addSettingTab(tab: SettingTab | SettingTab[]): void {
if (!Array.isArray(tab)) {
tab = [tab];
}
SETTING_TABS.push(...tab);
}
export function getSettingTabs(): SettingTab[] {
return SETTING_TABS;
}

@ -14,6 +14,7 @@ import { LoaderBarComponent } from './components/loader-bar/loader-bar.component
import { ModalComponent } from './components/modal/modal.component';
import { ProfileComponent } from './components/profile/profile.component';
import { ToastComponent } from './components/toast/toast.component';
import { SortOrderIconComponent } from './components/sort-order-icon/sort-order-icon.component';
import styles from './contants/styles';
import { ErrorHandler } from './handlers/error.handler';
import { chartJsLoaded$ } from './utils/widget-utils';
@ -54,6 +55,7 @@ export function appendScript(injector: Injector) {
ProfileComponent,
TableEmptyMessageComponent,
ToastComponent,
SortOrderIconComponent,
],
exports: [
BreadcrumbComponent,
@ -66,6 +68,7 @@ export function appendScript(injector: Injector) {
ProfileComponent,
TableEmptyMessageComponent,
ToastComponent,
SortOrderIconComponent,
],
entryComponents: [ErrorComponent],
})

@ -15,8 +15,9 @@
"dependencies": {
"@abp/ng.account": "^0.9.0",
"@abp/ng.identity": "^0.9.0",
"@abp/ng.tenant-management": "^0.9.0",
"@abp/ng.theme.basic": "^0.9.0",
"@abp/ng.tenant-management": "^0.9.0",
"@abp/ng.setting-management": "^0.9.0",
"@angular/animations": "~8.2.8",
"@angular/common": "~8.2.8",
"@angular/compiler": "~8.2.8",
@ -26,8 +27,8 @@
"@angular/platform-browser-dynamic": "~8.2.8",
"@angular/router": "~8.2.8",
"@angularclass/hmr": "^2.1.3",
"@ngxs/devtools-plugin": "^3.5.0",
"@ngxs/hmr-plugin": "^3.5.0",
"@ngxs/devtools-plugin": "^3.5.0",
"rxjs": "~6.4.0",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"

@ -25,6 +25,11 @@ const routes: Routes = [
loadChildren: () =>
import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule),
},
{
path: 'setting-management',
loadChildren: () =>
import('./lazy-libs/setting-management-wrapper.module').then(m => m.SettingManagementWrapperModule),
},
];
@NgModule({

@ -14,6 +14,7 @@ import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { AccountConfigModule } from '@abp/ng.account.config';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management.config';
@NgModule({
declarations: [AppComponent],
@ -30,6 +31,7 @@ import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
AccountConfigModule.forRoot({ redirectUrl: '/' }),
IdentityConfigModule,
TenantManagementConfigModule,
SettingManagementConfigModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { SettingManagementModule } from '@abp/ng.setting-management';
@NgModule({
imports: [SettingManagementModule],
})
export class SettingManagementWrapperModule {}

@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

@ -0,0 +1,48 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
yarn.lock*

@ -0,0 +1 @@
@volo:registry=http://192.168.1.45:4873/

@ -0,0 +1,206 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"myProjectName": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/myProjectName",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"extractCss": true,
"assets": ["src/favicon.ico", "src/assets"],
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.min.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
},
"hmr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "myProjectName:build"
},
"configurations": {
"production": {
"browserTarget": "myProjectName:build:production"
},
"hmr": {
"hmr": true,
"browserTarget": "myProjectName:build:hmr"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "myProjectName:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": ["src/favicon.ico", "src/assets"],
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.min.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "myProjectName:serve"
},
"configurations": {
"production": {
"devServerTarget": "myProjectName:serve:production"
}
}
}
}
},
"my-project-name": {
"projectType": "library",
"root": "projects/my-project-name",
"sourceRoot": "projects/my-project-name/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/my-project-name/tsconfig.lib.json",
"project": "projects/my-project-name/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/my-project-name/src/test.ts",
"tsConfig": "projects/my-project-name/tsconfig.spec.json",
"karmaConfig": "projects/my-project-name/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/my-project-name/tsconfig.lib.json",
"projects/my-project-name/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"my-project-name-config": {
"projectType": "library",
"root": "projects/my-project-name-config",
"sourceRoot": "projects/my-project-name-config/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/my-project-name-config/tsconfig.lib.json",
"project": "projects/my-project-name-config/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/my-project-name-config/src/test.ts",
"tsConfig": "projects/my-project-name-config/tsconfig.spec.json",
"karmaConfig": "projects/my-project-name-config/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/my-project-name-config/tsconfig.lib.json",
"projects/my-project-name-config/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "myProjectName"
}

@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

@ -0,0 +1,32 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to myProjectName!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/myProjectName'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

@ -0,0 +1,60 @@
{
"name": "my-project-name",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"start:hmr": "ng serve --configuration hmr",
"build": "ng build my-project-name",
"test": "ng test my-project-name",
"lint": "ng lint my-project-name",
"e2e": "ng e2e my-project-name"
},
"private": true,
"dependencies": {
"@abp/ng.account": "^0.9.0",
"@abp/ng.theme.basic": "^0.9.0",
"@abp/ng.identity": "^0.9.0",
"@abp/ng.tenant-management": "^0.9.0",
"@abp/ng.setting-management": "^0.9.0",
"@angular/animations": "~8.2.8",
"@angular/common": "~8.2.8",
"@angular/compiler": "~8.2.8",
"@angular/core": "~8.2.8",
"@angular/forms": "~8.2.8",
"@angular/platform-browser": "~8.2.8",
"@angular/platform-browser-dynamic": "~8.2.8",
"@angular/router": "~8.2.8",
"@ngxs/devtools-plugin": "^3.5.0",
"@angularclass/hmr": "^2.1.3",
"@ngxs/hmr-plugin": "^3.5.0",
"rxjs": "~6.4.0",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.803.6",
"@angular-devkit/build-ng-packagr": "~0.803.6",
"@angular/cli": "~8.3.6",
"@angular/compiler-cli": "~8.2.8",
"@angular/language-service": "~8.2.8",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"ng-packagr": "^5.4.0",
"ngxs-schematic": "^1.1.9",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tsickle": "^0.37.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
}
}

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/my-project-name-config'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/my-project-name-config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

@ -0,0 +1,7 @@
{
"name": "my-project-name.config",
"version": "0.0.1",
"peerDependencies": {
"@abp/ng.core": ">=0.9.0"
}
}

@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'my-project-name-settings',
template: `
<h3>MyProjectName Settings</h3>
`,
})
export class MyProjectNameSettingsComponent {}

@ -0,0 +1,12 @@
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { MyProjectNameConfigService } from './services/my-project-name-config.service';
import { noop } from '@abp/ng.core';
import { MyProjectNameSettingsComponent } from './components/my-project-name-settings.component';
@NgModule({
declarations: [MyProjectNameSettingsComponent],
providers: [{ provide: APP_INITIALIZER, deps: [MyProjectNameConfigService], multi: true, useFactory: noop }],
exports: [MyProjectNameSettingsComponent],
entryComponents: [MyProjectNameSettingsComponent],
})
export class MyProjectNameConfigModule {}

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { eLayoutType, addAbpRoutes, ABP } from '@abp/ng.core';
import { addSettingTab } from '@abp/ng.theme.shared';
import { MyProjectNameSettingsComponent } from '../components/my-project-name-settings.component';
@Injectable({
providedIn: 'root',
})
export class MyProjectNameConfigService {
constructor() {
addAbpRoutes({
name: 'MyProjectName',
path: 'my-project-name',
layout: eLayoutType.application,
order: 2,
} as ABP.FullRoute);
const route = addSettingTab({
component: MyProjectNameSettingsComponent,
name: 'MyProjectName Settings',
order: 1,
requiredPolicy: '',
});
}
}

@ -0,0 +1,3 @@
export * from './lib/components/my-project-name-settings.component';
export * from './lib/services/my-project-name-config.service';
export * from './lib/my-project-name-config.module';

@ -0,0 +1,21 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

@ -0,0 +1,26 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/my-project-name'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

@ -0,0 +1,8 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/my-project-name",
"lib": {
"entryFile": "src/public-api.ts"
},
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared", "my-project-name.config"]
}

@ -0,0 +1,8 @@
{
"name": "my-project-name",
"version": "0.0.1",
"dependencies": {
"@abp/ng.theme.shared": "^0.9.0",
"my-project-name.config": "^0.0.1"
}
}

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MyProjectNameComponent } from './my-project-name.component';
describe('MyProjectNameComponent', () => {
let component: MyProjectNameComponent;
let fixture: ComponentFixture<MyProjectNameComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyProjectNameComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyProjectNameComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,19 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'lib-my-project-name',
template: `
<p>
my-project-name works!
</p>
`,
styles: []
})
export class MyProjectNameComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

@ -0,0 +1,20 @@
import { AuthGuard, DynamicLayoutComponent, PermissionGuard } from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { MyProjectNameComponent } from './components/my-project-name.component';
const routes: Routes = [
{
path: '',
component: DynamicLayoutComponent,
canActivate: [AuthGuard, PermissionGuard],
data: { requiredPolicy: '' },
children: [{ path: '', component: MyProjectNameComponent }],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class MyProjectNameRoutingModule {}

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { MyProjectNameComponent } from './components/my-project-name.component';
import { MyProjectNameRoutingModule } from './my-project-name-routing.module';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { CoreModule } from '@abp/ng.core';
@NgModule({
declarations: [MyProjectNameComponent],
imports: [CoreModule, ThemeSharedModule, MyProjectNameRoutingModule],
exports: [MyProjectNameComponent],
})
export class MyProjectNameModule {}

@ -0,0 +1,2 @@
export * from './lib/components/my-project-name.component';
export * from './lib/my-project-name.module';

@ -0,0 +1,21 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

@ -0,0 +1,26 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

@ -0,0 +1,43 @@
import { ABP } from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
data: {
routes: {
name: '::Menu:Home',
} as ABP.Route,
},
},
{
path: 'identity',
loadChildren: () => import('./lazy-libs/identity-wrapper.module').then(m => m.IdentityWrapperModule),
},
{
path: 'tenant-management',
loadChildren: () =>
import('./lazy-libs/tenant-management-wrapper.module').then(m => m.TenantManagementWrapperModule),
},
{
path: 'account',
loadChildren: () => import('./lazy-libs/account-wrapper.module').then(m => m.AccountWrapperModule),
},
{
path: 'setting-management',
loadChildren: () =>
import('./lazy-libs/setting-management-wrapper.module').then(m => m.SettingManagementWrapperModule),
},
{
path: 'my-project-name',
loadChildren: () => import('./lazy-libs/my-project-name-wrapper.module').then(m => m.MyProjectNameWrapperModule),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<router-outlet></router-outlet>
`,
})
export class AppComponent {}

@ -0,0 +1,46 @@
import { CoreModule } from '@abp/ng.core';
import { LAYOUTS } from '@abp/ng.theme.basic';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsModule } from '@ngxs/store';
import { OAuthModule } from 'angular-oauth2-oidc';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { AccountConfigModule } from '@abp/ng.account.config';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management.config';
import { MyProjectNameConfigModule } from '../../projects/my-project-name-config/src/public-api';
@NgModule({
declarations: [AppComponent],
imports: [
ThemeSharedModule.forRoot(),
CoreModule.forRoot({
environment,
requirements: {
layouts: LAYOUTS,
},
}),
OAuthModule.forRoot(),
NgxsModule.forRoot([]),
AccountConfigModule.forRoot({ redirectUrl: '/' }),
IdentityConfigModule,
TenantManagementConfigModule,
SettingManagementConfigModule,
MyProjectNameConfigModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
SharedModule,
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production }),
],
bootstrap: [AppComponent],
})
export class AppModule {}

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home.component';
import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';
const routes: Routes = [
{
path: '',
component: ApplicationLayoutComponent,
children: [{ path: '', component: HomeComponent }],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomeRoutingModule {}

@ -0,0 +1,15 @@
<div class="card">
<div class="card-header">{{ '::Welcome' | abpLocalization }}</div>
<div class="card-body">
<p>
{{ '::LongWelcomeMessage' | abpLocalization }}
</p>
<p *ngIf="!hasLoggedIn">
<a routerLink="/account/login" [state]="{ redirectUrl: '/' }" class="btn btn-primary" role="button"
><i class="fa fa-sign-in"></i>{{ 'AbpIdentity::Login' | abpLocalization }}</a
>
</p>
<hr />
<p class="text-right"><a href="https://abp.io?ref=tmpl" target="_blank">abp.io</a></p>
</div>
</div>

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
@Component({
selector: 'abp-home',
templateUrl: './home.component.html',
})
export class HomeComponent {
get hasLoggedIn(): boolean {
return this.oAuthService.hasValidAccessToken();
}
constructor(private oAuthService: OAuthService) {}
}

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
@NgModule({
declarations: [HomeComponent],
imports: [SharedModule, HomeRoutingModule],
})
export class HomeModule {}

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { AccountModule } from '@abp/ng.account';
@NgModule({
imports: [AccountModule],
})
export class AccountWrapperModule {}

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { IdentityModule } from '@abp/ng.identity';
@NgModule({
imports: [IdentityModule],
})
export class IdentityWrapperModule {}

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { MyProjectNameModule } from '../../../projects/my-project-name/src/public-api';
@NgModule({
imports: [MyProjectNameModule],
})
export class MyProjectNameWrapperModule {}

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { SettingManagementModule } from '@abp/ng.setting-management';
@NgModule({
imports: [SettingManagementModule],
})
export class SettingManagementWrapperModule {}

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { TenantManagementModule } from '@abp/ng.tenant-management';
@NgModule({
imports: [TenantManagementModule],
})
export class TenantManagementWrapperModule {}

@ -0,0 +1,14 @@
import { CoreModule } from '@abp/ng.core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgModule } from '@angular/core';
import { ThemeBasicModule } from '@abp/ng.theme.basic';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { TableModule } from 'primeng/table';
@NgModule({
declarations: [],
imports: [CoreModule, ThemeSharedModule, ThemeBasicModule, TableModule, NgbDropdownModule],
exports: [CoreModule, ThemeSharedModule, ThemeBasicModule, TableModule, NgbDropdownModule],
providers: [],
})
export class SharedModule {}

@ -0,0 +1,25 @@
export const environment = {
production: false,
hmr: true,
application: {
name: 'MyProjectName',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44305',
clientId: 'MyProjectName_App',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
showDebugInformation: true,
oidc: false,
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44305',
},
},
localization: {
defaultResourceName: 'MyProjectName',
},
};

@ -0,0 +1,25 @@
export const environment = {
production: true,
hmr: false,
application: {
name: 'MyProjectName',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44305',
clientId: 'MyProjectName_App',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
showDebugInformation: true,
oidc: false,
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44305',
},
},
localization: {
defaultResourceName: 'MyProjectName',
},
};

@ -0,0 +1,25 @@
export const environment = {
production: false,
hmr: false,
application: {
name: 'MyProjectName',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44305',
clientId: 'MyProjectName_App',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
showDebugInformation: true,
oidc: false,
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44305',
},
},
localization: {
defaultResourceName: 'MyProjectName',
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>MyProjectName</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root>
<div class="donut centered"></div>
</app-root>
</body>
</html>

@ -0,0 +1,20 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { BootstrapModuleFn as Bootstrap, hmr, WebpackModule } from '@ngxs/hmr-plugin';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
declare const module: WebpackModule;
if (environment.production) {
enableProdMode();
}
const bootstrap: Bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);
if (environment.hmr) {
hmr(module, bootstrap).catch(err => console.error(err));
} else {
bootstrap().catch(err => console.log(err));
}

@ -0,0 +1,63 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

@ -0,0 +1,27 @@
/* You can add global styles to this file, and also import other style files */
@keyframes donut-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.donut {
display: inline-block;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #7983ff;
border-radius: 50%;
width: 30px;
height: 30px;
animation: donut-spin 1.2s linear infinite;
&.centered {
position: fixed;
top: 50%;
left: 50%;
/* bring your own prefixes */
transform: translate(-50%, -50%);
}
}

@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["src/test.ts", "src/**/*.spec.ts"]
}

@ -0,0 +1,37 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
],
"paths": {
"my-project-name": [
"dist/my-project-name"
],
"my-project-name/*": [
"dist/my-project-name/*"
],
"my-project-name-config": [
"dist/my-project-name-config"
],
"my-project-name-config/*": [
"dist/my-project-name-config/*"
]
}
}
}

@ -0,0 +1,18 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

@ -0,0 +1,92 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}
Loading…
Cancel
Save