chore: Quellcode der bestehenden Angular-Anwendung für die Entwicklung hinzugefügt.

This commit is contained in:
Developer 02 2024-08-05 16:41:22 +02:00
parent bb9f524648
commit 772a6af503
332 changed files with 38857 additions and 0 deletions

View File

@ -0,0 +1,22 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"url": "http://localhost:4204",
"webRoot": "${workspaceFolder}"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:4204/debug.html",
"webRoot": "${workspaceFolder}"
}
]
}

View File

@ -0,0 +1,6 @@
{
"angular.enable-strict-mode-prompt": false,
"typescript.tsdk": "node_modules\\typescript\\lib",
"azdoPullRequests.projectName": "StaffDB",
"azdoPullRequests.pullRequestTitle": "commit"
}

View File

@ -0,0 +1,302 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"staffdb": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"aot": true,
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/assets",
{
"glob": "**/*",
"ignore": [
"styles/*"
],
"input": "src/app/shared/core/assets/",
"output": "/assets/"
},
{
"glob": "dhr-icon-152x152i.png",
"input": "src/assets/icons/develop/",
"output": "/assets/icons/"
},
"src/webmanifest.json",
{
"glob": "environment.js",
"input": "src/environments/",
"output": "/assets/js/"
},
{
"glob": "package.json",
"input": "./",
"output": "./assets/json"
}
],
"styles": [
"src/styles.scss",
"node_modules/ngx-spinner/animations/ball-clip-rotate.css"
],
"stylePreprocessorOptions": {
"includePaths": [
"src/app/shared/core/css/0-base/",
"src/app/shared/core/components/popup-base"
]
},
"scripts": [],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
},
"configurations": {
"default": {
"progress": true,
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"namedChunks": true,
"aot": true,
"extractLicenses": false,
"vendorChunk": true,
"buildOptimizer": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "30mb",
"maximumError": "70mb"
}
]
},
"develop": {
"assets": [
"src/assets",
{
"glob": "**/*",
"ignore": [
"styles/*"
],
"input": "src/app/shared/core/assets/",
"output": "/assets/"
},
{
"glob": "dhr-icon-152x152i.png",
"input": "src/assets/icons/develop/",
"output": "/assets/icons/"
},
{
"glob": "webmanifest.json",
"input": "src/manifest/develop/",
"output": "/"
},
{
"glob": "environment.js",
"input": "src/environments/develop/",
"output": "/assets/js/"
},
{
"glob": "package.json",
"input": "./",
"output": "./assets/json"
}
],
"progress": true,
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"namedChunks": true,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "30mb",
"maximumError": "70mb"
}
]
},
"production": {
"assets": [
"src/assets",
{
"glob": "**/*",
"ignore": [
"styles/*"
],
"input": "src/app/shared/core/assets/",
"output": "/assets/"
},
{
"glob": "dhr-icon-152x152i.png",
"input": "src/assets/icons/production/",
"output": "/assets/icons/"
},
{
"glob": "webmanifest.json",
"input": "src/manifest/production/",
"output": "/"
},
{
"glob": "environment.js",
"input": "src/environments/production/",
"output": "/assets/js/"
},
{
"glob": "package.json",
"input": "./",
"output": "./assets/json"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "30mb",
"maximumError": "50mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"liveReload": false,
"port": 4204,
"buildTarget": "staffdb:build"
},
"configurations": {
"production": {
"buildTarget": "staffdb:build:production"
},
"develop": {
"buildTarget": "staffdb:build:develop"
},
"default": {
"buildTarget": "staffdb:build:default"
}
},
"defaultConfiguration": "default"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "staffdb:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"src/styles.scss",
"node_modules/ngx-spinner/animations/ball-clip-rotate.css"
],
"scripts": [],
"assets": [
"src/assets",
{
"glob": "translate-core/*",
"input": "src/app/shared/core/assets/",
"output": "/assets/"
},
{
"glob": "dhr-icon-152x152i.png",
"input": "src/assets/icons/develop/",
"output": "/assets/icons/"
},
"src/webmanifest.json",
{
"glob": "environment.js",
"input": "src/environments/",
"output": "/assets/js/"
},
{
"glob": "package.json",
"input": "./",
"output": "./assets/json"
}
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"staffdb-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "staffdb:serve"
},
"configurations": {
"production": {
"devServerTarget": "staffdb:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"cli": {
"analytics": false,
"cache": {
"enabled": false
}
}
}

View File

@ -0,0 +1,9 @@
module.exports = {
packages: {
'devextreme-angular': {
ignorableDeepImportMatchers: [
/devextreme\//
]
},
}
};

View File

@ -0,0 +1,30 @@
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/assets/icons/hr-logo_color.ico",
"/index.html",
"/webmanifest.json",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
]
}
}
]
}

16517
ClientApp/staff-db-ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
{
"name": "staffdb",
"version": "14.23.33",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"reinstall:packages": "rm -rf node_modules dist && npm cache clean --force && rm package-lock.json && npm install",
"win_reinstall:packages": "rd /s /q node_modules && npm cache clean --force && del package-lock.json && npm install",
"add_pwa": "ng add @angular/pwa@latest"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.10",
"@angular/cdk": "^16.2.14",
"@angular/common": "^17.3.10",
"@angular/compiler": "^17.3.10",
"@angular/core": "^17.3.10",
"@angular/forms": "^17.3.10",
"@angular/localize": "^17.3.10",
"@angular/material": "^16.2.14",
"@angular/material-moment-adapter": "^16.2.14",
"@angular/platform-browser": "^17.3.10",
"@angular/platform-browser-dynamic": "^17.3.10",
"@angular/pwa": "^17.3.8",
"@angular/router": "^17.3.10",
"@angular/service-worker": "^17.3.10",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@sentry/angular": "^8.4.0",
"angular-in-memory-web-api": "^0.17.0",
"devextreme-angular": "~23.2.6",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"jspdf": "^2.5.1",
"moment": "^2.30.1",
"ngx-clipboard": "^16.0.0",
"ngx-device-detector": "^7.0.0",
"ngx-mask": "^17.0.8",
"ngx-spinner": "^17.0.0",
"ngx-translate-multi-http-loader": "^17.0.0",
"ngx-webcam": "^0.4.1",
"rxjs": "^7.8.1",
"zone.js": "^0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.10",
"@angular/language-service": "^17.3.10",
"@types/jasmine": "latest",
"@types/node": "latest",
"codelyzer": "latest",
"jasmine-core": "latest",
"jasmine-spec-reporter": "latest",
"karma": "latest",
"karma-chrome-launcher": "latest",
"karma-coverage": "latest",
"karma-jasmine": "latest",
"karma-jasmine-html-reporter": "latest",
"protractor": "latest",
"ts-node": "latest",
"tslint": "latest",
"typescript": "~5.4.5"
}
}

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
import { APP_PAGES } from '@app_consts';
import { ACCOUNT_PAGE, Globals, LOGIN_PAGE } from '@app_core/services/globals';
ACCOUNT_PAGE.loadChildren = () => import('@app_modules/app-account/app-account.module').then(m => m.AppAccountModule);
const routes: Routes = [
...APP_PAGES,
{
path: '',
redirectTo: '/' + LOGIN_PAGE.path,
pathMatch: 'full'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true, preloadingStrategy: PreloadAllModules })],
exports: [RouterModule]
})
export class AppRoutingModule { public globals: Globals; }

View File

@ -0,0 +1,2 @@
<app-layout [title]=globals.appTitleWithVersion></app-layout>
<ngx-spinner type="ball-clip-rotate"> </ngx-spinner>

View File

@ -0,0 +1,27 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'staffdb'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('ang-material');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to Angular!');
}));
});

View File

@ -0,0 +1,58 @@
import { Component, inject, OnInit } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import * as AppCnst from '@app_consts';
import * as Cnst from '@app_core/consts';
import * as Utils from '@app_core/utils';
import { TranslateService } from '@ngx-translate/core';
import * as AppModule from './app.module';
import { ENVIRONMENT_TOKEN } from '@app_core/injection-tokens';
import { Globals } from '@app_core/services/globals';
import { RepositoryService } from '@app_core/services/http/repository.service';
import { ServerInfoService } from '@app_core/services/serverinfo.service';
import { LocaleService } from '@app_core/services/localization/locale.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
private environment = inject(ENVIRONMENT_TOKEN);
constructor(
public globals: Globals,
public repository: RepositoryService,
public translate: TranslateService,
private serverInfoService: ServerInfoService,
private localeService: LocaleService,
private updateService: SwUpdate
) {
this.initLangCulture();
globals.appTitle = AppCnst.APP_TITLE;
Utils.setIndexTitle(globals.appTitle);
globals.appTitleWithVersion = `${AppCnst.APP_TITLE} V.${this.environment.appVersion} (${this.environment.appBuild})`;
globals.appPages = AppCnst.APP_PAGES;
globals.mainPage = AppCnst.APP_PAGES[0];
this.serverInfoService.defaultServerInfoVisible = false;
}
private initLangCulture() {
this.translate.addLangs(AppCnst.SUPPORTED_LANGUAGES);
const languageFromBrowser: string = navigator.language.split('-')[0];
const startLanguage: string = Utils.isLanguageSupported(languageFromBrowser, AppCnst.SUPPORTED_LANGUAGES, Cnst.SUPPORTED_LANGUAGES) ? languageFromBrowser : AppCnst.cnst_DefaultLanguage;
const startCulture: string = Utils.isCultureSupported(navigator.language, AppModule.SUPPORTED_CULTURES, Cnst.SUPPORTED_CULTURES) ? navigator.language : AppCnst.cnst_DefaultCulture;
this.localeService.initLocaleLanguage(startCulture, startLanguage);
}
ngOnInit(): void {
this.updateService.checkForUpdate().then((res) => { if (res) { this.updateService.activateUpdate(); window.location.reload(); } });
}
}

View File

@ -0,0 +1,144 @@
import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
/* Routing */
import { AppRoutingModule } from './app-routing.module';
/* Angular Material */
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularMaterialModule } from '@app_core/components/angular-material.module';
import { APPICON4LIVE_TOKEN, APPICON4NAVBAR_TOKEN, APPICON4TEST_TOKEN, ENVIRONMENT_TOKEN, IENVIRONMENT, SUPPORTED_CULTURES_TOKEN, SUPPORTED_LANGUAGES_TOKEN } from '@app_core/injection-tokens';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, Injectable } from '@angular/core';
/* FormsModule */
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
/* Components */
import { LayoutModule } from '@app_core/components/layout/layout.module';
import { NgxSpinnerModule } from 'ngx-spinner';
/* data acess */
import { HttpInterceptorService } from '@app_core/services/http/http-interceptor.service';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
/* Globals */
import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@angular/material/core';
import { DxFormModule, DxButtonModule, DxTemplateHost, DxTemplateModule, DxChartModule, DxDataGridModule } from 'devextreme-angular';
import { NgxMaskDirective, NgxMaskPipe, optionsConfig, provideNgxMask } from 'ngx-mask';
import { Router } from '@angular/router';
import { ServiceWorkerModule } from '@angular/service-worker';
import { MessageBoxModule } from '@app_core/components/message-box/message-box.module';
import { ClipboardModule } from 'ngx-clipboard';
/* Localisaton */
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { LocaleProvider } from '@app_core/services/localization/locale.provider';
import { HenselTranslateService } from '@app_core/services/localization/hensel-translate.service';
import { registerLocaleData } from '@angular/common';
import { loadMessages } from 'devextreme/localization';
import localeDe from '@angular/common/locales/de';
import localeUs from '@angular/common/locales/en';
import localeFr from '@angular/common/locales/fr';
import localeGb from '@angular/common/locales/en-GB';
export const SUPPORTED_CULTURES = ['de-DE', 'en-GB', 'en-US', 'fr-FR'];
registerLocaleData(localeDe, 'de-DE');
registerLocaleData(localeUs, 'en-US');
registerLocaleData(localeFr, 'fr-FR');
registerLocaleData(localeGb, 'en-GB');
import * as deMessages from 'devextreme/localization/messages/de.json';
loadMessages(deMessages);
// import { HammerGestureConfig, HAMMER_GESTURE_CONFIG, HammerModule } from '@angular/platform-browser';
// AoT requires an exported function for factories
// export class MyHammerConfig extends HammerGestureConfig {
// // tslint:disable-next-line:no-angle-bracket-type-assertion
// overrides = <any>{
// swipe: { direction: Hammer.DIRECTION_ALL },
// };
// }
/* Sentry */
import { APP_INITIALIZER, ErrorHandler } from '@angular/core';
import * as Sentry from '@sentry/angular';
import { SUPPORTED_LANGUAGES } from '@app_consts';
import { TranslateLoaderProvider } from '@app_core/services/localization/translation-loader.provider';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@app_core/components/angular-material-index';
/* important to use mask functionality */
const maskConfig: optionsConfig = {
validation: false,
};
declare const environment: IENVIRONMENT;
declare const appVersion: string;
declare const appBuild: string;
environment['appVersion'] = appVersion;
environment['appBuild'] = appBuild;
@Injectable()
export class SentryErrorHandler implements ErrorHandler {
handleError(error) {
Sentry.captureException(error.originalError || error);
}
}
@NgModule({
declarations: [
AppComponent,
],
imports: [
HttpClientModule,
BrowserModule,
BrowserAnimationsModule,
AngularMaterialModule,
ReactiveFormsModule,
FormsModule,
DxFormModule,
DxTemplateModule,
DxDataGridModule,
DxChartModule,
DxButtonModule,
MessageBoxModule,
ClipboardModule,
NgxSpinnerModule,
NgxMaskDirective,
NgxMaskPipe,
TranslateModule.forRoot({loader: TranslateLoaderProvider}),
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: true, //environment.production,
// Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000'
}),
LayoutModule,
AppRoutingModule, // must be the last!!!
],
providers: [
DxTemplateHost,
LocaleProvider,
{ provide: ENVIRONMENT_TOKEN, useValue: environment },
{ provide: SUPPORTED_LANGUAGES_TOKEN, useValue: SUPPORTED_LANGUAGES},
{ provide: SUPPORTED_CULTURES_TOKEN, useValue: SUPPORTED_CULTURES},
{ provide: APPICON4TEST_TOKEN, useValue: 'assets/icons/develop/dhr-icon-48x48.png'},
{ provide: APPICON4LIVE_TOKEN, useValue: 'assets/icons/production/dhr-icon-48x48.png'},
{ provide: APPICON4NAVBAR_TOKEN, useValue: 'assets/icons/main_48x48.png'},
{ provide: TranslateService, useClass: HenselTranslateService },
{ provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, multi: true },
{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { floatLabel: 'auto' } },
{ provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher },
/* Sentry */
{ provide: ErrorHandler, useClass: SentryErrorHandler/*, useValue: Sentry.createErrorHandler({showDialog: false, })*/ },
{ provide: Sentry.TraceService, deps: [Router] },
{ provide: APP_INITIALIZER, useFactory: () => () => {}, deps: [Sentry.TraceService], multi: true },
provideNgxMask(maskConfig)
],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule { }

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AppAccountComponent } from './app-account.component';
import { AuthGuard } from '@app_core/services/authguard';
const routes: Routes = [
{
path: '',
component: AppAccountComponent,
canActivate: [AuthGuard]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AppAccountRoutingModule { }

View File

@ -0,0 +1,22 @@
<app-account>
<div profile-settings></div>
<div admin-settings>
<hensel-selection
id="input-WebApp"
class="input-webapp"
[dataSource]=appDataService.webAppList.items
placeholder="Webapp"
keyExpr="entityId"
displayExpr="webAppName"
sortField="*"
[multiSelect]=true
separator=","
(selectedItemsChange)="webAppIdList=$event">
</hensel-selection>
<button mat-button (click)="onClick()" class="btn">
<mat-icon>autorenew</mat-icon>
Synchronize WebAppRole mit ADGroupen
</button>
</div>
<div other-settings></div>
</app-account>

View File

@ -0,0 +1,9 @@
.input-webapp{
float: left;
}
.btn{
margin-left: 10px;
margin-top: 8px;
}

View File

@ -0,0 +1,16 @@
import { Component, inject } from '@angular/core';
import { AccountComponent } from '@app_core/components/account/account.component';
import { AppDataService } from '@app_services/app.data.service';
@Component({
selector: 'app-account-ext',
templateUrl: 'app-account.component.html',
styleUrls: ['app-account.component.scss']
})
export class AppAccountComponent extends AccountComponent {
public appDataService = inject(AppDataService);
onClick() {
}
}

View File

@ -0,0 +1,29 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { DxDataGridModule } from 'devextreme-angular';
import { DxoSearchPanelModule } from 'devextreme-angular/ui/nested';
import { AppAccountRoutingModule } from './app-account-routing.module';
import { AppAccountComponent } from './app-account.component';
import { AccountModule } from '@app_core/components/account/account.module';
import { AngularMaterialModule } from '@app_core/components/angular-material.module';
import { HenselSelectionComponent } from '@app_core/components/hensel-selection/hensel-selection.component';
@NgModule({
declarations: [AppAccountComponent],
imports: [
CommonModule,
AppAccountRoutingModule, //must be earlier AccountModule
AccountModule,
AngularMaterialModule,
ReactiveFormsModule,
FormsModule,
DxDataGridModule,
DxoSearchPanelModule,
HenselSelectionComponent,
TranslateModule
]
})
export class AppAccountModule { }

View File

@ -0,0 +1,181 @@
import { Injectable } from '@angular/core';
import { cnst_LoadedEntitiesExpiresInMs, EN_AppEntities, EN_AppPages, EN_HttpQueriesCount4Page } from '@app_consts';
import { CoreUser } from '@app_core/models/coreuser';
import { RepositoryService } from '@app_core/services/http/repository.service';
import { CorePageService } from '@app_core/services/page.service';
import { Department, DepartmentFilter } from '@app_models/basedata/department';
import { DocumentArtToDepartment } from '@app_models/documentart-to-department';
import { Employee } from '@app_models/employee';
import { User } from '@app_models/user';
import { WindreamIndex } from '@app_models/windream-index';
import { WindreamIndexToWindreamSearchToDepartment } from '@app_models/windream-index-to-windream-search-to-department';
import { ClientIdFilter, WindreamSearch } from '@app_models/windream-search';
import { WindreamSearchItem } from '@app_models/windream-search-item';
import { WindreamSearchItemToWindreamSearchToDepartment } from '@app_models/windream-search-item-to-windream-search-to-department';
import { WindreamSearchToDepartment, WindreamSearchToDepartmentFilter } from '@app_models/windream-search-to-department';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
@Injectable({ providedIn: 'root' })
export class DepartmentDataService extends CorePageService {
protected appPage: EN_AppPages = EN_AppPages.Department;
protected loadImedeately: boolean = true; //load on pening the page
protected get appPageNoQueries() { return EN_HttpQueriesCount4Page.Department; }
// --- running data
departmentFilter: DepartmentFilter = new DepartmentFilter('DepartmentFullFilter');
departmentList: AppBaseEntityListWrapper<Department> = new AppBaseEntityListWrapper<Department>(Department, 'Abteilungsliste', EN_AppEntities.Department);
departmentDetails: AppBaseEntityWrapper<Department>;
windreamSearchToDepartmentDetails: AppBaseEntityWrapper<WindreamSearchToDepartment>;
// tslint:disable-next-line: max-line-length
windreamSearchToDepartmentList: AppBaseEntityListWrapper<WindreamSearchToDepartment> = new AppBaseEntityListWrapper<WindreamSearchToDepartment>(WindreamSearchToDepartment, 'Windream Suche Kacheln', EN_AppEntities.WindreamSearchToDepartment);
windreamSearchToDepartmentFilter: WindreamSearchToDepartmentFilter = new WindreamSearchToDepartmentFilter('DepartmentFilter');
// tslint:disable-next-line: max-line-length
windreamSearchItemToWindreamSearchToDepartmentList: AppBaseEntityListWrapper<WindreamSearchItemToWindreamSearchToDepartment> = new AppBaseEntityListWrapper<WindreamSearchItemToWindreamSearchToDepartment>(WindreamSearchItemToWindreamSearchToDepartment, 'Windream Suchbausteine', EN_AppEntities.WindreamSearchItemToWindreamSearchToDepartment);
windreamSearchItemToWindreamSearchToDepartmentFilter: WindreamSearchToDepartmentFilter = new WindreamSearchToDepartmentFilter('WindreamSearchToDepartmentFilter');
windreamSearchItemToWindreamSearchToDepartmentDetails: AppBaseEntityWrapper<WindreamSearchItemToWindreamSearchToDepartment>;
windreamSearchItemList: AppBaseEntityListWrapper<WindreamSearchItem> = new AppBaseEntityListWrapper<WindreamSearchItem>(WindreamSearchItem, 'windream suche kacheln attribute', EN_AppEntities.WindreamSearchItem);
windreamIndexToWindreamSearchToDepartmentDetails: AppBaseEntityWrapper<WindreamIndexToWindreamSearchToDepartment>;
// tslint:disable-next-line: max-line-length
windreamIndexToWindreamSearchToDepartmentList: AppBaseEntityListWrapper<WindreamIndexToWindreamSearchToDepartment> = new AppBaseEntityListWrapper<WindreamIndexToWindreamSearchToDepartment>(WindreamIndexToWindreamSearchToDepartment, 'Ausgabespalten für Windream Suche', EN_AppEntities.WindreamIndexToWindreamSearchToDepartment);
windreamIndexToWindreamSearchToDepartmentFilter: WindreamSearchToDepartmentFilter = new WindreamSearchToDepartmentFilter('WindreamSearchToDepartmentFilter');
windreamSearchList: AppBaseEntityListWrapper<WindreamSearch> =
new AppBaseEntityListWrapper<WindreamSearch>(WindreamSearch, 'Windream Suche Kacheln', EN_AppEntities.WindreamSearch);
clientIdFilter: ClientIdFilter = new ClientIdFilter('ClientIdFilter');
windreamIndexList: AppBaseEntityListWrapper<WindreamIndex> =
new AppBaseEntityListWrapper<WindreamIndex>(WindreamIndex, 'Windream Index', EN_AppEntities.WindreamIndex);
documentArtToDepartmentListFilter: DepartmentFilter = new DepartmentFilter('DepartmentFilter');
documentArtToDepartmentList: AppBaseEntityListWrapper<DocumentArtToDepartment> =
new AppBaseEntityListWrapper<DocumentArtToDepartment>(DocumentArtToDepartment, 'Dokumentartenliste für Department', EN_AppEntities.DocumentArtToDepartment);
employeeList: AppBaseEntityListWrapper<Employee> = new AppBaseEntityListWrapper<Employee>(Employee, 'mitarbeiter', EN_AppEntities.Employee + '/all');
selectedDocumentarts: number[] = [];
clpbrdWindreamOutColHint1: string;
clpbrdWindreamOutColHints: string;
clpbrdWindreamOutColList: WindreamIndexToWindreamSearchToDepartment[] = [];
clpbrdWindreamSearchItemHint1: string;
clpbrdWindreamSearchItemHints: string;
clpbrdWindreamSearchItemList: WindreamSearchItemToWindreamSearchToDepartment[] = [];
constructor(
private repositoryService: RepositoryService,
) {
super();
this.departmentList.focusedCallBack = this.focusCallBack.bind(this);
this.departmentList.filter = this.departmentFilter;
this.departmentDetails = this.departmentList.createFocusedShadowEntity(AppBaseEntityWrapper.EN_ViewMode);
this.departmentDetails.loadedDataExpiresInMs = cnst_LoadedEntitiesExpiresInMs;
this.baseEntityWrapperWaitLoading = this.departmentDetails;
this.activateRouting4EntityList(this.departmentList); //we are using routing
this.windreamSearchToDepartmentList.filter = this.windreamSearchToDepartmentFilter;
this.windreamSearchToDepartmentDetails = this.windreamSearchToDepartmentList.createFocusedShadowEntity();
this.windreamSearchToDepartmentDetails.dontLoadEntity = true;
this.windreamSearchToDepartmentDetails.beforeNewCallBack = this.prepareSearchToDepartment.bind(this);
this.windreamIndexToWindreamSearchToDepartmentList.filter = this.windreamIndexToWindreamSearchToDepartmentFilter;
this.windreamIndexToWindreamSearchToDepartmentDetails = this.windreamIndexToWindreamSearchToDepartmentList.createFocusedShadowEntity();
this.windreamIndexToWindreamSearchToDepartmentDetails.dontLoadEntity = true;
this.windreamSearchItemToWindreamSearchToDepartmentList.filter = this.windreamSearchItemToWindreamSearchToDepartmentFilter;
this.windreamSearchItemToWindreamSearchToDepartmentDetails = this.windreamSearchItemToWindreamSearchToDepartmentList.createFocusedShadowEntity();
this.windreamSearchItemToWindreamSearchToDepartmentDetails.dontLoadEntity = true;
this.windreamSearchList.filter = this.clientIdFilter;
this.windreamIndexList.filter = this.clientIdFilter;
this.windreamSearchItemList.filter = this.clientIdFilter;
this.documentArtToDepartmentList.filter = this.documentArtToDepartmentListFilter;
this.departmentDetails.detailItems.register(this.windreamSearchToDepartmentList, null, 'departmentId');
this.departmentDetails.detailItems.register(this.documentArtToDepartmentList, null, 'departmentId', (docarts: DocumentArtToDepartment[]) => {
this.selectedDocumentarts = docarts.map(docart => docart.documentArtId);
});
this.windreamSearchToDepartmentDetails.detailItems.register(this.windreamIndexToWindreamSearchToDepartmentList, null, 'windreamSearchToDepartmentId');
this.windreamSearchToDepartmentDetails.detailItems.register(this.windreamSearchItemToWindreamSearchToDepartmentList, null, 'windreamSearchToDepartmentId');
this.loadBaseData();
this.init();
}
loadData() {
super.loadData();
this.departmentList.load(null, null, () => this.pageLoadingService.updatePageLoadedCounters(this.appPage));
}
public loadBaseData() {
super.loadBaseData(this.appPage, 1);
this.employeeList.load(null, null, () => this.pageLoadingService.updatePageLoadedCounters(this.appPage));
}
public focusCallBack(focusedItem: Department) {
this.clientIdFilter.clientId = focusedItem?.clientId;
}
public clearData() {
super.clearData();
this.departmentFilter.filterReset();
this.departmentList.clear();
}
protected onLoggedInOut(user: CoreUser) {
this.userChanged(new User(user));
super.onLoggedInOut(user);
}
private userChanged(user: User) {
if (this.authService.isLoggedIn()) {
this.departmentDetails.canNew = user.isMaster;
this.departmentDetails.canEdit = this.departmentDetails.canNew;
this.departmentDetails.canDelete = this.departmentDetails.canNew;
}
}
moveUpDown(direction: number,
list:
AppBaseEntityListWrapper<WindreamSearchToDepartment>
| AppBaseEntityListWrapper<WindreamSearchItemToWindreamSearchToDepartment>
| AppBaseEntityListWrapper<WindreamIndexToWindreamSearchToDepartment>
) {
const curItemIndex: number = list.focusedItemIndex;
if (!(direction < 0 && curItemIndex === 0 || direction > 0 && curItemIndex === list.items.length)) {
const targetItem = list.items[curItemIndex + direction];
const targetSeq: number = targetItem.seq;
const currentItem = list.focusedItem;
targetItem.seq = currentItem.seq;
currentItem.seq = targetSeq;
targetItem.entityChanged = true;
currentItem.entityChanged = true;
targetItem.save(this.repositoryService, null, null, null);
currentItem.save(this.repositoryService, null, null, null);
this.sortBySeq(list.items);
}
}
public sortBySeq(list: WindreamSearchToDepartment[] | WindreamSearchItemToWindreamSearchToDepartment[] | WindreamIndexToWindreamSearchToDepartment[]) {
list.sort((fp1, fp2) => (fp1.seq - fp2.seq));
}
prepareSearchToDepartment(item: WindreamSearchToDepartment) {
const maxSeq = this.windreamSearchToDepartmentList.items.reduce<number>((max: number, _index: WindreamSearchToDepartment) => max > _index.seq ? max : _index.seq, 0);
item.seq = maxSeq + 1;
item.isActive = true;
}
}

View File

@ -0,0 +1,100 @@
<form class="departmentDetailsContent" #detailForm="ngForm">
<hensel-input
class="input-departmentName"
name="departmentName"
placeholder="Abteilungsname"
maxlength="50"
required
[(ngModel)]=department.departmentName
[disabled]="!departmentDetails.inNewEditMode"
(blur)=depNameWasChanged()>
</hensel-input>
<hensel-selection
name="costcentre"
class="input-cost-centre"
[dataSource]=costCentreList
placeholder="CostCentre"
keyExpr="entityId"
displayExpr="costCentreName"
sortField="*" required
[multiSelect]=false
[(selectedItems)]="department.costCentreId"
[disabled]="!departmentDetails.inNewEditMode">
</hensel-selection>
<hensel-input
class="input-departmentType"
name="Type Id"
placeholder="Abteilungstyp"
maxlength="50"
department
[(ngModel)]=department.departmentTypeId
[disabled]="!departmentDetails.inNewEditMode">
</hensel-input>
<hensel-selection
name="headofDepartmentId"
class="input-headofDepartmentId"
[dataSource]=employeeList
placeholder="Abteilungsleiter"
keyExpr="entityId"
displayExpr="fullname"
sortField="*"
[multiSelect]=false
[(selectedItems)]="department.headofDepartmentId"
[disabled]="!departmentDetails.inNewEditMode">
</hensel-selection>
<hensel-selection
name="executiveDirectorId"
class="input-executiveDirectorId"
[dataSource]=employeeList
placeholder="CEO"
keyExpr="entityId"
displayExpr="fullname"
sortField="*"
[multiSelect]=false
[(selectedItems)]="department.executiveDirectorId"
[disabled]="!departmentDetails.inNewEditMode">
</hensel-selection>
<hensel-selection
name="managingDirectorId"
class="input-managingDirectorId"
[dataSource]=employeeList
placeholder="COO"
keyExpr="entityId"
displayExpr="fullname"
sortField="*"
[multiSelect]=false
[(selectedItems)]="department.managingDirectorId"
[disabled]="!departmentDetails.inNewEditMode">
</hensel-selection>
<hensel-input
class="input-departmentNameFolder"
name="departmentNameFolder"
placeholder="Folder"
maxlength="50"
required
[(ngModel)]=department.departmentNameFolder
[disabled]="!departmentDetails.inNewEditMode">
</hensel-input>
<hensel-input
class="input-adGroupDepartmentName"
name="adGroupDepartmentName"
placeholder="AD-Group Name"
maxlength="50"
required
[(ngModel)]=department.adGroupDepartmentName
[disabled]="!departmentDetails.inNewEditMode">
</hensel-input>
<hensel-input
class="input-clientId"
name="clientId"
placeholder="Client Id"
maxlength="50"
required
[(ngModel)]=department.clientId
[disabled]="!departmentDetails.inNewEditMode">
</hensel-input>
<mat-checkbox class="input-isVirtual check-box" name="Enabled" style="float: left; margin-top: auto; margin-bottom: 12px;" labelPosition="before"
[(ngModel)]=department.isVirtual inactive [disabled]="!departmentDetails.inNewEditMode">
Virtual
</mat-checkbox>
</form>

View File

@ -0,0 +1,52 @@
.departmentDetailsContent{
// z-index: 10;
// position: relative; //otherwise z-index is not working
padding: 0 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-column-gap: 8px;
grid-template-columns: repeat(2, minmax(1px, 1fr)); // column relationship
grid-template-rows: repeat(5, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas:
"input-departmentName input-cost-centre"
"input-departmentType input-headofDepartmentId"
"input-executiveDirectorId input-managingDirectorId"
"input-departmentNameFolder input-adGroupDepartmentName"
"input-clientId input-isVirtual"
;
}
.input-departmentName{
grid-area: input-departmentName;
}
.input-cost-centre{
grid-area: input-cost-centre;
}
.input-departmentType{
grid-area: input-departmentType;
}
.input-headofDepartmentId{
grid-area: input-headofDepartmentId;
}
.input-executiveDirectorId{
grid-area: input-executiveDirectorId;
}
.input-managingDirectorId{
grid-area: input-managingDirectorId;
}
.input-departmentNameFolder{
grid-area: input-departmentNameFolder;
}
.input-adGroupDepartmentName{
grid-area: input-adGroupDepartmentName;
}
.input-clientId{
grid-area: input-clientId;
}
.input-isVirtual{
grid-area: input-isVirtual;
}

View File

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

View File

@ -0,0 +1,53 @@
import { Component, Input, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { Department } from '@app_models/basedata/department';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { CostCentre } from '@app_models/basedata/cost-centre';
import { Employee } from '@app_models/employee';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-department-content',
templateUrl: './department-content.component.html',
styleUrls: ['./department-content.component.scss']
})
export class DepartmentContentComponent implements OnInit, AfterViewInit {
@ViewChild('detailForm') detailForm: NgForm;
@Input() departmentDetails: AppBaseEntityWrapper<Department>;
public get department(): Department {
return this.departmentDetails.entity;
}
public get employeeList(): Employee[] {
return this.departmentService.employeeList.items;
}
public get costCentreList(): CostCentre[] {
return this.appDataService.costCentreList.items;
}
constructor(
private appDataService: AppDataService,
private departmentService: DepartmentDataService) { }
ngOnInit() {
}
ngAfterViewInit(): void {
this.departmentDetails.detailForm = this.detailForm;
}
depNameWasChanged() {
if (!this.department.departmentName) return;
if (!this.department.departmentNameFolder) this.department.departmentNameFolder = this.department.departmentName;
if (!this.department.adGroupDepartmentName) this.department.adGroupDepartmentName = this.department.departmentName;
}
}

View File

@ -0,0 +1,59 @@
<form class="DepartmentDetailsForm" #detailForm="ngForm" id="form_department">
<mat-card class="card">
<mat-card-header>
</mat-card-header>
<mat-card-content class="DepartmentDetails">
<section class="toolbar">
<div class="toolbar-button-row">
<button *ngIf="departmentDetails.canNew" #btnNew id="btnNew" name="btnNew" class="abstand" mat-stroked-button
title="Neue Abteilung" (click)="departmentDetails.new()"
[disabled]="!departmentDetails.enabledNewButton()">
<mat-icon>add</mat-icon> Hinzufügen
</button>
<button *ngIf="departmentDetails.canEdit" #btnEdit id="btnEdit" name="btnEdit" class="abstand" mat-stroked-button
title="Bearbeiten" (click)="departmentDetails.edit()"
[disabled]="!departmentDetails.enabledEditButton()">
<mat-icon>edit</mat-icon> Bearbeiten
</button>
<button *ngIf="departmentDetails.canEdit||departmentDetails.canNew" id="btnSave" name="btnSave" class="abstand"
mat-stroked-button disabled title="Speichern" (click)="save()"
[disabled]="!departmentDetails.enabledSaveButton()">
<mat-icon>save</mat-icon> Speichern
</button>
<button *ngIf="departmentDetails.canEdit||departmentDetails.canNew" id="btnUndo" name="btnUndo" class="abstand"
mat-stroked-button disabled title="Änderungen verwerfen" (click)="cancel()" [autofocus]=true
[disabled]="!departmentDetails.enabledCancelButton()">
<mat-icon>undo</mat-icon> Verwerfen
</button>
<button *ngIf="departmentDetails.canDelete" id="btnDelete" name="btnDelete" mat-stroked-button class="abstand"
title="Benutzer löschen" class="last" (click)="departmentDetails.delete()"
[disabled]="!departmentDetails.enabledDeleteButton()">
<mat-icon>delete</mat-icon> Löschen
</button>
</div>
</section>
<app-department-content class="DetailsContent"
[departmentDetails]="departmentDetails">
</app-department-content>
<app-department-documentart class="DocumentArtList" [departmentDetails]=departmentDetails></app-department-documentart>
<app-department-windream-search class="WindreamSearchList"
[windreamSearchList]="windreamSearchList"
[departmentDetails]="departmentDetails">
</app-department-windream-search>
<app-department-windream-search-item class="WindreamSearchItemList"
[windreamSearchItemList]="windreamSearchItemToWindreamSearchToDepartmentList"
[windreamSearchItemDetails]="windreamSearchItemToWindreamSearchToDepartmentDetails"
[windreamSearchToDepartmentDetails]="windreamSearchToDepartmentDetails"
[departmentDetails]="departmentDetails">
</app-department-windream-search-item>
<app-department-windream-index class="WindreamIndexList"
[windreamIndexList]="windreamIndexList"
[windreamIndexDetails]="windreamIndexDetails"
[windreamSearchToDepartmentDetails]="windreamSearchToDepartmentDetails"
[departmentDetails]="departmentDetails">
</app-department-windream-index>
</mat-card-content>
</mat-card>
</form>

View File

@ -0,0 +1,46 @@
.DepartmentDetails {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-template-rows: auto repeat(1, minmax(1px, 250px)); // row relationships
grid-template-columns: minmax(1px, 0.7fr) minmax(1px, 0.7fr) minmax(1px, 0.5fr) minmax(1px, 0.5fr) minmax(1px, 1fr); // column relationship
grid-row-gap: 6px;
grid-column-gap: 10px;
grid-template-areas:
'toolbar toolbar toolbar toolbar toolbar'
'detail documentArtList windreamSearchList windreamSearchItemList windreamIndexList';
z-index: 10;
position: relative; //otherwise z-index is not working
}
.WindreamSearchItemList{
grid-area: windreamSearchItemList;
}
.WindreamIndexList{
grid-area: windreamIndexList;
}
.WindreamSearchList{
grid-area: windreamSearchList;
}
.DetailsContent {
grid-area: detail;
}
.DocumentArtList {
grid-area: documentArtList;
}
.abstand {
margin-right: 1px !important;
}
.toolbar {
grid-area: toolbar;
}
.card {
padding: 12px;
margin: 2px 2px 2px 2px;
}

View File

@ -0,0 +1,109 @@
import { Component, OnInit, Input, ChangeDetectorRef, DoCheck, ChangeDetectionStrategy, Output, EventEmitter, ViewChild, ElementRef, AfterViewInit, HostListener } from '@angular/core';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { DepartmentDataService } from '../department-data.service';
import { WindreamSearchToDepartment } from '@app_models/windream-search-to-department';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { WindreamIndexToWindreamSearchToDepartment } from '@app_models/windream-index-to-windream-search-to-department';
import { Department } from '@app_models/basedata/department';
import { WindreamSearchItemToWindreamSearchToDepartment } from '@app_models/windream-search-item-to-windream-search-to-department';
import { WindreamSearchItem } from '@app_models/windream-search-item';
import { NgForm } from '@angular/forms';
import { reduce } from 'rxjs/operators';
import { AuthorizeService } from '@app_core/services/authorize.service';
import { PageLoadingService } from '@app_core/services/pageloading.service';
import { EN_EntityBeforeSaveCallBackResults } from '@app_core/consts';
import { LoginPopupComponent } from '@app_core/components/login/login-popup/login-popup.component';
@Component({
selector: 'app-department-detail',
templateUrl: './department-detail.component.html',
styleUrls: ['./department-detail.component.scss'],
})
export class DepartmentDetailComponent implements AfterViewInit {
@ViewChild('detailForm') detailForm: NgForm;
@Input() public departmentDetails: AppBaseEntityWrapper<Department>;
public get windreamSearchToDepartmentDetails(): AppBaseEntityWrapper<WindreamSearchToDepartment> {
return this.departmentDataService.windreamSearchToDepartmentDetails;
}
constructor(
public departmentDataService: DepartmentDataService,
public authorizeService: AuthorizeService,
private pageLoadingService: PageLoadingService,
) {
}
public get windreamSearchList(): AppBaseEntityListWrapper<WindreamSearchToDepartment> {
return this.departmentDataService.windreamSearchToDepartmentList;
}
public get windreamSearchDetails(): AppBaseEntityWrapper<WindreamSearchToDepartment> {
return this.departmentDataService.windreamSearchToDepartmentDetails;
}
public get windreamSearchItemToWindreamSearchToDepartmentList(): AppBaseEntityListWrapper<WindreamSearchItemToWindreamSearchToDepartment> {
return this.departmentDataService.windreamSearchItemToWindreamSearchToDepartmentList;
}
public get windreamSearchItemToWindreamSearchToDepartmentDetails(): AppBaseEntityWrapper<WindreamSearchItemToWindreamSearchToDepartment> {
return this.departmentDataService.windreamSearchItemToWindreamSearchToDepartmentDetails;
}
public get windreamSearchItemList(): AppBaseEntityListWrapper<WindreamSearchItem> {
return this.departmentDataService.windreamSearchItemList;
}
public get windreamIndexList(): AppBaseEntityListWrapper<WindreamIndexToWindreamSearchToDepartment> {
return this.departmentDataService.windreamIndexToWindreamSearchToDepartmentList;
}
public get windreamIndexDetails(): AppBaseEntityWrapper<WindreamIndexToWindreamSearchToDepartment> {
return this.departmentDataService.windreamIndexToWindreamSearchToDepartmentDetails;
}
public get focusedItem(): Department {
return this.departmentDetails.entity;
}
ngAfterViewInit(): void {
this.departmentDetails.beforeNewCallBack = this.newDepartmentInit.bind(this);
this.departmentDetails.beforeSaveCallBack = this.beforeSaveDepartment.bind(this);
}
newDepartmentInit(department: Department) {
this.departmentDataService.selectedDocumentarts = [];
department.departmentTypeId = 1;
department.clientId = 24110;
}
async beforeSaveDepartment(department: Department): Promise<EN_EntityBeforeSaveCallBackResults> {
const maxId: number = this.departmentDataService.departmentList.items.reduce((max, currentItem) => (currentItem.entityId > max ? currentItem.entityId : max), 0);
if (department.isNew()) department.departmentId = -(maxId + 1);
this.departmentDataService.documentArtToDepartmentList.match2KeysArray(department, 'departmentId', this.departmentDataService.selectedDocumentarts, 'documentArtId');
return EN_EntityBeforeSaveCallBackResults.Ok_Continue;
}
public save = () => { //because of calling as callback of cancel
this.pageLoadingService.startSpinner();
this.departmentDetails.save((department: Department) => this.afterSave(department), null, () => this.pageLoadingService.hideSpinner());
}
afterSave(department: Department) {
// this.departmentDetails.loadDetails(this.catalogDataService.departmentCategoryList);
}
public cancel = () => {
// this.setCurrentMaster4Subdepartment = null;
this.departmentDetails.cancelWithSave(this.save);
}
@HostListener('window:keydown', ['$event'])
keyboardInput(event: any) {
return LoginPopupComponent.LOGIN_IS_SHOWN || this.departmentDetails.shortcutsHandler(event, this.save, this.cancel);
}
}

View File

@ -0,0 +1,21 @@
<div class="department-documentart">
<label class="label"></label>
<hensel-selection
id="documentarts"
class="documentart"
name="documentarts"
[dataSource]=appDataService.documentArtList.items
placeholder="Dokumentenart"
keyExpr="documentArtId"
displayTemplate="${name} (${shortname})"
sortField="*"
[multiSelect]=true
[customFilter]="'clientId='+departmentDetails.entity.clientId"
separator=","
[(selectedItems)]="departmentDataService.selectedDocumentarts"
(selectedItemsChange)="departmentDetails.entityWasChanged = true"
[disabled]=!departmentDetails.inNewEditMode
[height]="'100%'"
[useResultGrid]=true>
</hensel-selection>
</div>

View File

@ -0,0 +1,19 @@
.department-documentart {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: minmax(0, 1fr); // column relationship
grid-template-rows: 0 minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas: "label" "attributes";
}
.documentart {
grid-area: attributes;
height: 100%;
width: 100%;
}
.label {
grid-area: label;
}

View File

@ -0,0 +1,28 @@
import { Component, OnInit, Input } from '@angular/core';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { Department } from '@app_models/basedata/department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
@Component({
selector: 'app-department-documentart',
templateUrl: './department-documentart.component.html',
styleUrls: ['./department-documentart.component.scss']
})
export class DepartmentDocumentartsComponent implements OnInit {
@Input() public departmentDetails: AppBaseEntityWrapper<Department>;
constructor(
public appDataService: AppDataService,
public departmentDataService: DepartmentDataService,
) {
}
ngOnInit() {
}
}

View File

@ -0,0 +1,35 @@
<form class="PopupContainer" #popupForm="ngForm">
<!-- <dx-button class="buttonLayoutCancel" icon="close" (click)="close()"></dx-button> -->
<h2 mat-dialog-title class="messageBoxHeader">{{description}} - {{keyItemName}} </h2>
<mat-dialog-content class="PopupContent">
<div class="buttons">
<button id="btnSave" name="btnSave" class="btnSave" mat-stroked-button
[disabled]="!baseEntityWrapper.enabledSaveButton() || readOnly" (click)="save() ">
<mat-icon>save</mat-icon> Übernehmen
</button>
<button #btnCancel id="btnCancel" name="btnCancel" class="btnCancel" mat-stroked-button (click)="cancel()">
<mat-icon>cancel</mat-icon> Abbrechen
</button>
</div>
<hensel-selection
#input_windreamIndex
id="input-windreamIndex"
class="input-windreamIndex"
[dataSource]=windreamIndexList
placeholder="Ausgabespalte"
keyExpr="entityId"
displayExpr="selectionName"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="windreamIndexIds"
[disabled]="readOnly || focusedItem.entityId !== 0"
[customFilterFn]="focusedItem.entityId === 0? getNotUsedwindreamIndexes: ''"
[showSpinner]=this.departmentDataService.windreamIndexList.isLoading
required>
</hensel-selection>
</mat-dialog-content>
</form>

View File

@ -0,0 +1,24 @@
@import 'popup-base.component';
.PopupContent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid: ;
height: 100%; // 100% view height
grid-template-rows: auto auto; // row relationships
grid-template-columns: repeat(2, minmax(0, 1fr)); // column relationship
grid-column-gap: 15px;
grid-row-gap: 15px;
grid-template-areas:
'input-windreamSearch .'
'buttons buttons'
}
.input-windreamSearch {
grid-area: input-windreamSearch;
}
.input-seq {
grid-area: input-seq;
}

View File

@ -0,0 +1,57 @@
import { Component, ViewChild } from '@angular/core';
import { HenselSelectionComponent } from '@app_core/components/hensel-selection/hensel-selection.component';
import { PopupBaseComponent } from '@app_core/components/popup-base/popup-base.component';
import { WindreamIndex } from '@app_models/windream-index';
import { WindreamIndexToWindreamSearchToDepartment } from '@app_models/windream-index-to-windream-search-to-department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
@Component({
selector: 'app-department-windream-index-popupedit',
templateUrl: './department-windream-index-popupedit.component.html',
styleUrls: ['./department-windream-index-popupedit.component.scss']
})
export class DepartmentWindreamIndexPopupeditComponent extends PopupBaseComponent {
public windreamIndexIds: number[] = [];
@ViewChild('input_windreamIndex') input_windreamIndex: HenselSelectionComponent;
constructor(
public departmentDataService: DepartmentDataService,
) {
super();
this.departmentDataService.windreamIndexList.load();
}
public get windreamIndexList(): WindreamIndex[] {
return this.departmentDataService.windreamIndexList.items;
}
public get focusedItem(): WindreamIndexToWindreamSearchToDepartment {
return <WindreamIndexToWindreamSearchToDepartment>(this._focusedItem);
}
getNotUsedwindreamIndexes = (dep: WindreamIndex) => {
return !this.list.items.find((item) => item['windreamIndexId'] === dep.entityId);
}
save = () => {
let maxSeq = this.list.items.reduce<number>((max: number, _index: WindreamIndexToWindreamSearchToDepartment) => max > _index.seq ? max : _index.seq, 0);
// for each selected WindreamIndexIds
this.windreamIndexIds.forEach(windreamIndexId => {
// create new WindreamIndexMap Item
const newWindreamIndex = new WindreamIndexToWindreamSearchToDepartment();
// calculate highest seq
newWindreamIndex.seq = ++maxSeq;
newWindreamIndex.windreamIndexId = windreamIndexId;
newWindreamIndex.windreamSearchToDepartmentId = this.focusedItem.windreamSearchToDepartmentId;
newWindreamIndex.entityChanged = true;
this.list.items.push(newWindreamIndex);
});
this.list.save(() => {
this.baseEntityWrapper.resetEditMode();
this.close(true);
});
}
}

View File

@ -0,0 +1,115 @@
<div class="department-windreamsearch-windreamindex">
<label class="label">{{windreamIndexList._listName}}</label>
<section class="toolbar">
<div class="toolbar-button-row">
<div class="toolbar-flex-container">
<button mat-icon-button title="Hinzufügen" (click)=addItem()
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& windreamIndexList.focusedEntityShadowed.enabledNewButton())">
<mat-icon>add</mat-icon>
</button>
<!-- <button mat-icon-button title="Bearbeiten" (click)=editItem()
[disabled]="!departmentDetails.inEditMode || windreamIndexList.focusedItemId===0 || !windreamIndexList.focusedEntityShadowed.enabledEditButton()">
<mat-icon>edit</mat-icon>
</button> -->
<button mat-icon-button title="Kopieren zum Zwischenspeicher" (click)="copyWindreamOutCols()"
[disabled]="!(!windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& windreamIndexList.items.length > 0)">
<mat-icon>content_copy</mat-icon>
</button>
<button mat-icon-button [title]="departmentDataService.clpbrdWindreamOutColHint1+'\n'+departmentDataService.clpbrdWindreamOutColHints" (click)="pasteWindreamOutCols()"
[disabled]="!(departmentDataService.clpbrdWindreamOutColHint1
&& departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew)">
<mat-icon>content_paste</mat-icon>
</button>
<button mat-icon-button title="Löschen"
(click)=windreamIndexList.focusedEntityShadowed.delete();
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamIndexList.focusedEntityShadowed.isNew
&& windreamIndexList.focusedEntityShadowed.enabledDeleteButton())">
<mat-icon>delete</mat-icon>
</button>
<button mat-icon-button title="Vorrücken"
(click)="departmentDataService.moveUpDown(-1, windreamIndexList)"
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamIndexList.focusedEntityShadowed.isNew
&& windreamIndexList.focusedItemIndex>0)">
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
<button mat-icon-button title="Hinterrücken" class="toolbar-button-last"
(click)="departmentDataService.moveUpDown(1, windreamIndexList)"
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamIndexList.focusedEntityShadowed.isNew
&& windreamIndexList.focusedItemIndex<windreamIndexList.items.length-1)">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
</div>
</div>
</section>
<dx-data-grid
name="windreamSearchGrid"
[class]="'grid'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="windreamIndexList.items"
keyExpr="entityId"
[(focusedRowIndex)]=windreamIndexList.focusedItemGridIndex
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowKey)]="windreamIndexList.focusedItemId"
[(selectedRowKeys)]="windreamIndexList.selectedItemIds"
[showBorders]="true" [allowColumnResizing]="true"
columnResizingMode="widget"
noDataText="Keine Daten sind vorhanden"
[renderAsync]="true"
(onKeyDown)="$event.handled = $event.event.key==='Escape'"
(onExporting)="globals.doExcelExportSimple($event, windreamIndexList.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<dxo-header-filter [visible]="true"></dxo-header-filter> -->
<dxo-filter-row [visible]="false" [applyFilter]="true"></dxo-filter-row>
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple"></dxo-sorting>
<dxo-selection mode="none"></dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf="column.visible">
<dxi-column [allowHeaderFiltering]=column.allowHeaderFiltering [dataField]=column.dataField
[dataType]=column.dataType [caption]=column.caption [width]=column.width
[sortIndex]=column.sortIndex [sortOrder]=column.sortOrder [visibleIndex]=column.visibleIndex
[visible]=column.visible [fixed]=column.fixed [headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate [allowSorting]=column.allowSorting
[calculateCellValue]=column.calculateCellValue [calculateDisplayValue]=column.calculateDisplayValue
[calculateGroupValue]=column.calculateGroupValue [calculateSortValue]=column.calculateSortValue
[alignment]=column.alignment>
<dxo-format *ngIf="column.dataType == 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType == 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</div>

View File

@ -0,0 +1,23 @@
.department-windreamsearch-windreamindex{
width: 100%;
height: 100%;
display: grid;
grid-template-columns: auto minmax(0, 1fr); // column relationship
grid-template-rows: auto minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas: "label label" "toolbar grid";
}
.grid {
grid-area: grid;
height: 100%;
width: 100%;
}
.label {
grid-area: label;
// padding-bottom: 6px;
}
.toolbar {
grid-area: toolbar;
}

View File

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

View File

@ -0,0 +1,101 @@
import { Component, Input, OnInit } from '@angular/core';
import { Department } from '@app_models/basedata/department';
import { WindreamIndexToWindreamSearchToDepartment } from '@app_models/windream-index-to-windream-search-to-department';
import { WindreamSearchToDepartment } from '@app_models/windream-search-to-department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { DepartmentWindreamIndexPopupeditComponent } from './department-windream-index-popupedit/department-windream-index-popupedit.component';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { MatDialogService } from '@app_core/services/mat-dialog.service';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { Globals } from '@app_core/services/globals';
import { ColumnConfig, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
@Component({
selector: 'app-department-windream-index',
templateUrl: './department-windream-index.component.html',
styleUrls: ['./department-windream-index.component.scss']
})
export class DepartmentWindreamIndexComponent implements OnInit {
@Input() windreamIndexList: AppBaseEntityListWrapper<WindreamIndexToWindreamSearchToDepartment>;
@Input() windreamIndexDetails: AppBaseEntityWrapper<WindreamIndexToWindreamSearchToDepartment>;
@Input() windreamSearchToDepartmentDetails: AppBaseEntityWrapper<WindreamSearchToDepartment>;
@Input() departmentDetails: AppBaseEntityWrapper<Department>;
public get windreamSearchName(): string {
return this.windreamSearchToDepartmentDetails.entity.windreamSearchName;
}
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
private dialog: MatDialogService,
public departmentDataService: DepartmentDataService,
public localeService: LocaleService,
public globals: Globals,
) {
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
col = this.columnConfigList.add('Seq.', 'seq', 2);
col.sortIndex = 0;
col.sortOrder = EN_SortOrder.asc;
col = this.columnConfigList.add('Ausgabespalte', 'objectTypeAttributeSzName', 15);
col = this.columnConfigList.add('Indexname', 'attributeSzColumnName', 15);
this.columnConfigList.recalcWidth();
}
addItem() {
this.windreamIndexList.focusedEntityShadowed.new();
this.windreamIndexList.focusedEntityShadowed.entity.windreamSearchToDepartmentId = this.windreamSearchToDepartmentDetails.entityId;
this.openPopupDialog(this.windreamIndexList.focusedEntityShadowed);
}
public openPopupDialog(editEntity: AppBaseEntityWrapper<WindreamIndexToWindreamSearchToDepartment>) {
this.windreamSearchToDepartmentDetails.keyDownListenerStopped = true;
this.dialog.openDialog(DepartmentWindreamIndexPopupeditComponent, {
baseEntityWrapper: editEntity,
keyItemName: this.windreamSearchToDepartmentDetails.entity.windreamSearchName,
readOnly: !this.departmentDetails.inEditMode,
description: editEntity.entity.entitytitle + (editEntity.entity.entityId === 0 ? ' zuweisen' : ' bearbeiten '),
list: this.windreamIndexList,
}, true, () => this.windreamSearchToDepartmentDetails.keyDownListenerStopped = false);
}
private outColKeyItemDescription() {
return `Ausgabespalten für "${this.windreamSearchToDepartmentDetails.entity.windreamSearchName}" Abteilung "${this.departmentDetails.entity.departmentName}"`;
}
public copyWindreamOutCols() {
this.departmentDataService.clpbrdWindreamOutColList = this.windreamIndexList.items.slice();
this.departmentDataService.clpbrdWindreamOutColHint1 = this.outColKeyItemDescription();
this.departmentDataService.clpbrdWindreamOutColHints = '';
this.departmentDataService.clpbrdWindreamOutColList.forEach(col =>
this.departmentDataService.clpbrdWindreamOutColHints += '\n' + col.objectTypeAttributeSzName);
}
public pasteWindreamOutCols() {
this.windreamIndexDetails.confirmMessageBoxYesNo('Übernahme von ' + this.departmentDataService.clpbrdWindreamOutColHint1, this.outColKeyItemDescription() + ' werden mit ' + this.departmentDataService.clpbrdWindreamOutColHint1 + ' überschrieben. Sind Sie sicher?',
() => {
this.windreamIndexList.deleteAll();
this.windreamIndexList.items.push(...this.departmentDataService.clpbrdWindreamOutColList);
this.windreamIndexList.items.forEach(el => {
el.entityId = 0;
el.entityChanged = true;
el.windreamSearchToDepartmentId = this.windreamSearchToDepartmentDetails.entityId;
});
this.windreamIndexList.save();
}
);
}
}

View File

@ -0,0 +1,34 @@
<form class="PopupContainer" #popupForm="ngForm">
<!-- <dx-button class="buttonLayoutCancel" icon="close" (click)="close()"></dx-button> -->
<h2 mat-dialog-title class="messageBoxHeader">{{description}} - {{keyItemName}} </h2>
<mat-dialog-content class="PopupContent">
<div class="buttons">
<button id="btnSave" name="btnSave" class="btnSave" mat-stroked-button
[disabled]="!baseEntityWrapper.enabledSaveButton() || readOnly" (click)="save() ">
<mat-icon>save</mat-icon> Übernehmen
</button>
<button #btnCancel id="btnCancel" name="btnCancel" class="btnCancel" mat-stroked-button (click)="cancel()">
<mat-icon>cancel</mat-icon> Abbrechen
</button>
</div>
<hensel-selection
#input_windreamSearchItem
id="input_windreamSearchItem"
class="input_windreamSearchItem"
[dataSource]=windreamSearchItemList
placeholder="Suchbaustein"
keyExpr="entityId"
displayExpr="name"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="windreamSearchItemIds"
[disabled]="readOnly || focusedItem.entityId !== 0"
[customFilterFn]="focusedItem.entityId === 0? getNotUsedwindreamSearchItems: ''"
[showSpinner]=this.departmentDataService.windreamSearchItemList.isLoading
required>
</hensel-selection>
</mat-dialog-content>
</form>

View File

@ -0,0 +1,24 @@
@import 'popup-base.component';
.PopupContent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid: ;
height: 100%; // 100% view height
grid-template-rows: auto auto; // row relationships
grid-template-columns: repeat(2, minmax(0, 1fr)); // column relationship
grid-column-gap: 15px;
grid-row-gap: 15px;
grid-template-areas:
'input-windreamSearch .'
'buttons buttons'
}
.input-windreamSearch {
grid-area: input-windreamSearch;
}
.input-seq {
grid-area: input-seq;
}

View File

@ -0,0 +1,57 @@
import { Component } from '@angular/core';
import { PopupBaseComponent } from '@app_core/components/popup-base/popup-base.component';
import { WindreamSearchItem } from '@app_models/windream-search-item';
import { WindreamSearchItemToWindreamSearchToDepartment } from '@app_models/windream-search-item-to-windream-search-to-department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
@Component({
selector: 'app-department-windream-search-item-popupedit',
templateUrl: './department-windream-search-item-popupedit.component.html',
styleUrls: ['./department-windream-search-item-popupedit.component.scss']
})
export class DepartmentWindreamSearchItemPopupeditComponent extends PopupBaseComponent {
public windreamSearchItemIds: number[] = [];
constructor(
public departmentDataService: DepartmentDataService,
) {
super();
this.departmentDataService.windreamSearchItemList.load();
this.getNotUsedwindreamSearchItems = this.getNotUsedwindreamSearchItems.bind(this);
}
public get windreamSearchItemList(): WindreamSearchItem[] {
return this.departmentDataService.windreamSearchItemList.items;
}
public get focusedItem(): WindreamSearchItemToWindreamSearchToDepartment {
return <WindreamSearchItemToWindreamSearchToDepartment>(this._focusedItem);
}
getNotUsedwindreamSearchItems(dep: WindreamSearchItem) {
return !this.list.items.find((item) => item['windreamSearchItemId'] === dep.entityId);
}
save = () => {
let maxSeq = this.list.items.reduce<number>((max: number, _index: WindreamSearchItemToWindreamSearchToDepartment) => max > _index.seq ? max : _index.seq, 0);
// for each selected WindreamIndexIds
this.windreamSearchItemIds.forEach(windreamSearchItemId => {
// create new WindreamIndexMap Item
const newWindreamItem = new WindreamSearchItemToWindreamSearchToDepartment();
// calculate highest seq
newWindreamItem.seq = ++maxSeq;
newWindreamItem.windreamSearchItemId = windreamSearchItemId;
newWindreamItem.windreamSearchToDepartmentId = this.focusedItem.windreamSearchToDepartmentId;
newWindreamItem.entityChanged = true;
this.list.items.push(newWindreamItem);
});
this.list.save(() => {
this.baseEntityWrapper.resetEditMode();
this.close(true);
});
}
}

View File

@ -0,0 +1,117 @@
<div class="department-windreamsearch-windreamindex">
<label class="label">{{windreamSearchItemList._listName}}</label>
<section class="toolbar">
<div class="toolbar-button-row">
<div class="toolbar-flex-container">
<button mat-icon-button title="Hinzufügen" (click)=addItem()
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamSearchItemList.focusedEntityShadowed.entityIsLoading
&& windreamSearchItemList.focusedEntityShadowed.enabledNewButton())">
<mat-icon>add</mat-icon>
</button>
<!-- <button mat-icon-button title="Bearbeiten" (click)=editItem()
[disabled]="!departmentDetails.inEditMode || windreamSearchItemList.focusedItemId===0 || !windreamSearchItemList.focusedEntityShadowed.enabledEditButton()">
<mat-icon>edit</mat-icon>
</button> -->
<button mat-icon-button title="Kopieren zum Zwischenspeicher" (click)="copyWindreamOutCols()"
[disabled]="!(!windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& windreamSearchItemList.items.length > 0)">
<mat-icon>content_copy</mat-icon>
</button>
<button mat-icon-button [title]="departmentDataService.clpbrdWindreamSearchItemHint1+'\n'+departmentDataService.clpbrdWindreamSearchItemHints" (click)="pasteWindreamOutCols()"
[disabled]="!(departmentDataService.clpbrdWindreamSearchItemHint1
&& departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew)">
<mat-icon>content_paste</mat-icon>
</button>
<button mat-icon-button title="Löschen"
(click)=windreamSearchItemList.focusedEntityShadowed.delete();
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamSearchItemList.focusedEntityShadowed.isNew
&& windreamSearchItemList.focusedEntityShadowed.enabledDeleteButton())">
<mat-icon>delete</mat-icon>
</button>
<button mat-icon-button title="Vorrücken"
(click)="departmentDataService.moveUpDown(-1, windreamSearchItemList)"
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamSearchItemList.focusedEntityShadowed.isNew
&& windreamSearchItemList.focusedItemIndex>0)">
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
<button mat-icon-button title="Hinterrücken" class="toolbar-button-last"
(click)="departmentDataService.moveUpDown(1, windreamSearchItemList)"
[disabled]="!(departmentDetails.inEditMode
&& !windreamSearchToDepartmentDetails.entityIsLoading
&& !windreamSearchToDepartmentDetails.isNew
&& !windreamSearchItemList.focusedEntityShadowed.isNew
&& windreamSearchItemList.focusedItemIndex<windreamSearchItemList.items.length-1)">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
</div>
</div>
</section>
<dx-data-grid
name="windreamSearchGrid"
[class]="'grid'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="windreamSearchItemList.items"
keyExpr="entityId"
[(focusedRowIndex)]=windreamSearchItemList.focusedItemGridIndex
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowKey)]="windreamSearchItemList.focusedItemId"
[(selectedRowKeys)]="windreamSearchItemList.selectedItemIds"
[showBorders]="true"
[allowColumnResizing]="true"
columnResizingMode="widget"
noDataText="Keine Daten sind vorhanden"
[renderAsync]="true"
(onKeyDown)="$event.handled = $event.event.key==='Escape'"
(onExporting)="globals.doExcelExportSimple($event, windreamSearchItemList.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<dxo-header-filter [visible]="true"></dxo-header-filter> -->
<dxo-filter-row [visible]="false" [applyFilter]="true"></dxo-filter-row>
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple"></dxo-sorting>
<dxo-selection mode="none"></dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf="column.visible">
<dxi-column [allowHeaderFiltering]=column.allowHeaderFiltering [dataField]=column.dataField
[dataType]=column.dataType [caption]=column.caption [width]=column.width
[sortIndex]=column.sortIndex [sortOrder]=column.sortOrder [visibleIndex]=column.visibleIndex
[visible]=column.visible [fixed]=column.fixed [headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate [allowSorting]=column.allowSorting
[calculateCellValue]=column.calculateCellValue [calculateDisplayValue]=column.calculateDisplayValue
[calculateGroupValue]=column.calculateGroupValue [calculateSortValue]=column.calculateSortValue
[alignment]=column.alignment>
<dxo-format *ngIf="column.dataType == 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType == 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</div>

View File

@ -0,0 +1,23 @@
.department-windreamsearch-windreamindex{
width: 100%;
height: 100%;
display: grid;
grid-template-columns: auto minmax(0, 1fr); // column relationship
grid-template-rows: auto minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas: "label label" "toolbar grid";
}
.grid {
grid-area: grid;
height: 100%;
width: 100%;
}
.label {
grid-area: label;
// padding-bottom: 6px;
}
.toolbar {
grid-area: toolbar;
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DepartmentWindreamSearchItemComponent } from './department-windream-search-item.component';
describe('DepartmentWindreamIndexComponent', () => {
let component: DepartmentWindreamSearchItemComponent;
let fixture: ComponentFixture<DepartmentWindreamSearchItemComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DepartmentWindreamSearchItemComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DepartmentWindreamSearchItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,94 @@
import { Component, Input, OnInit } from '@angular/core';
import { Department } from '@app_models/basedata/department';
import { WindreamSearchItemToWindreamSearchToDepartment } from '@app_models/windream-search-item-to-windream-search-to-department';
import { WindreamSearchToDepartment } from '@app_models/windream-search-to-department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { DepartmentWindreamSearchItemPopupeditComponent } from './department-windream-search-item-popupedit.component/department-windream-search-item-popupedit.component';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { MatDialogService } from '@app_core/services/mat-dialog.service';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { Globals } from '@app_core/services/globals';
import { ColumnConfig, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
@Component({
selector: 'app-department-windream-search-item',
templateUrl: './department-windream-search-item.component.html',
styleUrls: ['./department-windream-search-item.component.scss']
})
export class DepartmentWindreamSearchItemComponent implements OnInit {
@Input() windreamSearchItemList: AppBaseEntityListWrapper<WindreamSearchItemToWindreamSearchToDepartment>;
@Input() windreamSearchItemDetails: AppBaseEntityWrapper<WindreamSearchItemToWindreamSearchToDepartment>;
@Input() windreamSearchToDepartmentDetails: AppBaseEntityWrapper<WindreamSearchToDepartment>;
@Input() departmentDetails: AppBaseEntityWrapper<Department>;
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
private dialog: MatDialogService,
public departmentDataService: DepartmentDataService,
public localeService: LocaleService,
public globals: Globals,
) {
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
col = this.columnConfigList.add('Seq.', 'seq', 2);
col.sortIndex = 0;
col.sortOrder = EN_SortOrder.asc;
col = this.columnConfigList.add('Suchbaustein', 'windreamSearchItemName', 10);
this.columnConfigList.recalcWidth();
}
addItem() {
this.windreamSearchItemList.focusedEntityShadowed.new();
this.windreamSearchItemList.focusedEntityShadowed.entity.windreamSearchToDepartmentId = this.windreamSearchToDepartmentDetails.entityId;
this.openPopupDialog(this.windreamSearchItemList.focusedEntityShadowed);
}
public openPopupDialog(editEntity: AppBaseEntityWrapper<WindreamSearchItemToWindreamSearchToDepartment>) {
this.windreamSearchToDepartmentDetails.keyDownListenerStopped = true;
this.dialog.openDialog(DepartmentWindreamSearchItemPopupeditComponent, {
baseEntityWrapper: editEntity,
keyItemName: this.windreamSearchToDepartmentDetails.entity._name,
readOnly: !this.departmentDetails.inEditMode,
description: editEntity.entity.entitytitle + (editEntity.entity.entityId === 0 ? ' zuweisen' : ' bearbeiten '),
list: this.windreamSearchItemList,
}, true, () => this.windreamSearchToDepartmentDetails.keyDownListenerStopped = false);
}
private outColKeyItemDescription() {
return `Suchfelder für "${this.windreamSearchToDepartmentDetails.entity.windreamSearchName}" Abteilung "${this.departmentDetails.entity.departmentName}"`;
}
public copyWindreamOutCols() {
this.departmentDataService.clpbrdWindreamSearchItemList = this.windreamSearchItemList.items.slice();
this.departmentDataService.clpbrdWindreamSearchItemHint1 = this.outColKeyItemDescription();
this.departmentDataService.clpbrdWindreamSearchItemHints = '';
this.departmentDataService.clpbrdWindreamSearchItemList.forEach(col =>
this.departmentDataService.clpbrdWindreamSearchItemHints += '\n' + col.windreamSearchItemName);
}
public pasteWindreamOutCols() {
this.windreamSearchItemDetails.confirmMessageBoxYesNo('Übernahme von ' + this.departmentDataService.clpbrdWindreamSearchItemHint1, this.outColKeyItemDescription() + ' werden mit ' + this.departmentDataService.clpbrdWindreamSearchItemHint1 + ' überschrieben. Sind Sie sicher?',
() => {
this.windreamSearchItemList.deleteAll();
this.windreamSearchItemList.items.push(...this.departmentDataService.clpbrdWindreamSearchItemList);
this.windreamSearchItemList.items.forEach(el => {
el.entityId = 0;
el.entityChanged = true;
el.windreamSearchToDepartmentId = this.windreamSearchToDepartmentDetails.entityId;
});
this.windreamSearchItemList.save();
}
);
}
}

View File

@ -0,0 +1,37 @@
<form class="PopupContainer" #popupForm="ngForm">
<!-- <dx-button class="buttonLayoutCancel" icon="close" (click)="close()"></dx-button> -->
<h2 mat-dialog-title class="messageBoxHeader">{{description}} - {{keyItemName}} </h2>
<mat-dialog-content class="PopupContent">
<div class="buttons">
<button id="btnSave" name="btnSave" class="btnSave" mat-stroked-button
[disabled]="!baseEntityWrapper.enabledSaveButton() || readOnly" (click)="save() ">
<mat-icon>save</mat-icon> Übernehmen
</button>
<button #btnCancel id="btnCancel" name="btnCancel" class="btnCancel" mat-stroked-button (click)="cancel()">
<mat-icon>cancel</mat-icon> Abbrechen
</button>
</div>
<hensel-selection
#input_windreamSearch
id="input-windreamSearch"
class="input-windreamSearch"
[dataSource]=windreamSearchList
placeholder="Windream Search Kachel"
keyExpr="entityId"
displayExpr="selectionName"
sortField="*"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.windreamSearchId"
[customFilterFn]="focusedItem.entityId === 0? getNotUsedWindreamSearches: ''"
[showSpinner]=this.departmentDataService.windreamSearchList.isLoading
required>
</hensel-selection>
</mat-dialog-content>
</form>

View File

@ -0,0 +1,20 @@
@import 'popup-base.component';
.PopupContent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid: ;
height: 100%; // 100% view height
grid-template-rows: auto auto; // row relationships
grid-template-columns: repeat(2, minmax(0, 1fr)); // column relationship
grid-column-gap: 15px;
grid-row-gap: 15px;
grid-template-areas:
'input-windreamSearch .'
'buttons buttons'
}
.input-windreamSearch {
grid-area: input-windreamSearch;
}

View File

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

View File

@ -0,0 +1,34 @@
import { Component } from '@angular/core';
import { PopupBaseComponent } from '@app_core/components/popup-base/popup-base.component';
import { WindreamSearch } from '@app_models/windream-search';
import { WindreamSearchToDepartment } from '@app_models/windream-search-to-department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
@Component({
selector: 'app-department-windream-search-popupedit',
templateUrl: './department-windream-search-popupedit.component.html',
styleUrls: ['./department-windream-search-popupedit.component.scss']
})
export class DepartmentWindreamSearchPopupeditComponent extends PopupBaseComponent {
constructor(
public departmentDataService: DepartmentDataService,
) {
super();
this.departmentDataService.windreamSearchList.load();
}
public get windreamSearchList(): WindreamSearch[] {
return this.departmentDataService.windreamSearchList.items;
}
public get focusedItem(): WindreamSearchToDepartment {
return <WindreamSearchToDepartment>(this._focusedItem);
}
getNotUsedWindreamSearches = (dep: WindreamSearch) => {
return !this.list.items.find((item) => item['windreamSearchId'] === dep.entityId);
}
}

View File

@ -0,0 +1,113 @@
<div class="department-windreamsearch">
<label class="label">{{windreamSearchList._listName}}</label>
<section class="toolbar">
<div class="toolbar-button-row">
<div class="toolbar-flex-container">
<button mat-icon-button title="Hinzufügen" (click)=addItem()
[disabled]="!(departmentDetails.inEditMode
&& !departmentDetails.entityIsLoading
&& windreamSearchList.focusedEntityShadowed.enabledNewButton())">
<mat-icon>add</mat-icon>
</button>
<!-- <button mat-icon-button title="Bearbeiten" (click)=editItem()
[disabled]="!departmentDetails.inEditMode || windreamSearchList.focusedItemId===0 || !windreamSearchList.focusedEntityShadowed.enabledEditButton()">
<mat-icon>edit</mat-icon>
</button> -->
<button mat-icon-button title="Kopiere alle Windream-Kacheln zu anderen Abteilungen" (click)="copyAllTiles()"
[disabled]="!(windreamSearchList.items.length>0
&& !departmentDetails.entityIsLoading)">
<mat-icon>copy_all</mat-icon>
</button>
<button mat-icon-button title="Füge die aktuelle Windream-Kachel zu anderen Abteilungen hinzu" (click)="copyCurrentTile()"
[disabled]="!(!departmentDetails.entityIsLoading
&& !windreamSearchList.focusedEntityShadowed.isNew)">
<mat-icon>content_copy</mat-icon>
</button>
<button mat-icon-button title="Löschen"
(click)=windreamSearchList.focusedEntityShadowed.delete();
[disabled]="!(departmentDetails.inEditMode
&& !departmentDetails.entityIsLoading
&& !windreamSearchList.focusedEntityShadowed.isNew
&& windreamSearchList.focusedEntityShadowed.enabledDeleteButton())">
<mat-icon>delete</mat-icon>
</button>
<button mat-icon-button title="Vorrücken"
(click)="departmentDataService.moveUpDown(-1, windreamSearchList)"
[disabled]="!(departmentDetails.inEditMode
&& !departmentDetails.entityIsLoading
&& !windreamSearchList.focusedEntityShadowed.isNew
&& windreamSearchList.focusedItemIndex>0)">
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
<button mat-icon-button title="Hinterrücken" class="toolbar-button-last"
(click)="departmentDataService.moveUpDown(1, windreamSearchList)"
[disabled]="!(departmentDetails.inEditMode
&& !departmentDetails.entityIsLoading
&& !windreamSearchList.focusedEntityShadowed.isNew
&& windreamSearchList.focusedItemIndex<windreamSearchList.items.length-1)">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
</div>
</div>
</section>
<dx-data-grid
name="windreamSearchGrid"
[class]="'grid'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="windreamSearchList.items"
keyExpr="entityId"
[(focusedRowIndex)]=windreamSearchList.focusedItemGridIndex
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowKey)]="windreamSearchList.focusedItemId"
[(selectedRowKeys)]="windreamSearchList.selectedItemIds"
[showBorders]="true"
[allowColumnResizing]="true"
columnResizingMode="widget"
noDataText="Keine Daten sind vorhanden"
[renderAsync]="true"
(onKeyDown)="$event.handled = $event.event.key==='Escape'"
(onExporting)="globals.doExcelExportSimple($event, windreamSearchList.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<dxo-header-filter [visible]="true"></dxo-header-filter> -->
<dxo-filter-row [visible]="false" [applyFilter]="true"></dxo-filter-row>
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple"></dxo-sorting>
<dxo-selection mode="none"></dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf="column.visible">
<dxi-column [allowHeaderFiltering]=column.allowHeaderFiltering [dataField]=column.dataField
[dataType]=column.dataType [caption]=column.caption [width]=column.width
[sortIndex]=column.sortIndex [sortOrder]=column.sortOrder [visibleIndex]=column.visibleIndex
[visible]=column.visible [fixed]=column.fixed [headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate [allowSorting]=column.allowSorting
[calculateCellValue]=column.calculateCellValue [calculateDisplayValue]=column.calculateDisplayValue
[calculateGroupValue]=column.calculateGroupValue [calculateSortValue]=column.calculateSortValue
[alignment]=column.alignment>
<dxo-format *ngIf="column.dataType == 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType == 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'checkbox_template'">
<mat-checkbox #checkbox [checked]="cell.value"
(click)="changeKeyWS(cell.data)"
[disabled]="!departmentDetails.inEditMode"></mat-checkbox>
</div>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</div>

View File

@ -0,0 +1,23 @@
.department-windreamsearch {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: auto minmax(0, 1fr); // column relationship
grid-template-rows: auto minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas: "label label" "toolbar grid";
}
.grid {
grid-area: grid;
height: 100%;
width: 100%;
}
.label {
grid-area: label;
// padding-bottom: 6px;
}
.toolbar {
grid-area: toolbar;
}

View File

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

View File

@ -0,0 +1,121 @@
import { Component, Input, OnInit } from '@angular/core';
import { EN_AppEntities } from '@app_consts';
import { Department } from '@app_models/basedata/department';
import { WindreamSearchToDepartment } from '@app_models/windream-search-to-department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { DepartmentWindreamSearchPopupeditComponent } from './department-windream-search-popupedit/department-windream-search-popupedit.component';
import { SelectDepartmentPopupComponent } from './select-department-popup/select-department-popup.component';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { MatDialogService } from '@app_core/services/mat-dialog.service';
import { RepositoryService } from '@app_core/services/http/repository.service';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { Globals } from '@app_core/services/globals';
import { ColumnConfig, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
@Component({
selector: 'app-department-windream-search',
templateUrl: './department-windream-search.component.html',
styleUrls: ['./department-windream-search.component.scss']
})
export class DepartmentWindreamSearchComponent implements OnInit {
@Input() windreamSearchList: AppBaseEntityListWrapper<WindreamSearchToDepartment>;
@Input() departmentDetails: AppBaseEntityWrapper<Department>;
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
private dialog: MatDialogService,
public departmentDataService: DepartmentDataService,
public repoService: RepositoryService,
public localeService: LocaleService,
public globals: Globals,
) {
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
col = this.columnConfigList.addNumber('Seq.', 'seq', 2);
col.sortIndex = 0;
col.sortOrder = EN_SortOrder.asc;
col = this.columnConfigList.add('Suche Kachel', 'windreamSearchName', 10);
col = this.columnConfigList.addBoolean('Active', 'isActive', 2);
col.cellTemplate = 'checkbox_template';
this.columnConfigList.recalcWidth();
}
addItem() {
this.windreamSearchList.focusedEntityShadowed.new();
this.windreamSearchList.focusedEntityShadowed.entity.departmentId = this.departmentDetails.entityId;
this.openPopupDialog(this.windreamSearchList.focusedEntityShadowed);
}
public openPopupDialog(editEntity: AppBaseEntityWrapper<WindreamSearchToDepartment>) {
this.departmentDetails.keyDownListenerStopped = true;
this.dialog.openDialog(DepartmentWindreamSearchPopupeditComponent, {
baseEntityWrapper: editEntity,
keyItemName: this.departmentDetails.entity.departmentName,
readOnly: !this.departmentDetails.inEditMode,
description: editEntity.entity.entitytitle + (editEntity.entity.entityId === 0 ? ' zuweisen' : ' bearbeiten '),
list: this.windreamSearchList,
}, true, () => this.departmentDetails.keyDownListenerStopped = false);
}
public get nameOfDepartment(): string {
return this.departmentDetails.entity.departmentName;
}
public changeKeyWS(wsTD: WindreamSearchToDepartment) {
if (this.departmentDetails.inEditMode) {
wsTD.isActive = !wsTD.isActive;
wsTD.entityChanged = true;
wsTD.save(this.repoService, null);
}
}
public copyAllTiles() {
this.openSelectDepartmentsPopup(`Wählen Sie Abteilungen, bei welchen alle Suchkacheln von Suchkacheln aus "${this.departmentDetails.entity.departmentName}" ersetzt werden sollen`
, 'content_copy'
, (depIds: number[]) => {
this.repoService.putDataById(EN_AppEntities.DepartmentCopyWindreamTiles, this.departmentDetails.entityId, depIds).subscribe(
() => this.departmentDetails.informationMessageBox('Kopieren von Suchkacheln', `Die Suchkacheln aus "${this.departmentDetails.entity.departmentName}" haben Suchkacheln aus den ausgewählten Abteilungen erfolgreich ersetzt`)
);
}
);
}
public copyCurrentTile() {
this.openSelectDepartmentsPopup(`Wählen Sie Abteilungen, für welche die Suchkachel "${this.windreamSearchList.focusedItem.windreamSearchName}" aus "${this.departmentDetails.entity.departmentName}" hinzugefügt werden soll`
, 'content_copy'
, (depIds: number[]) => {
this.repoService.putDataById(EN_AppEntities.WindreamSearchToDepartmentCopyWindreamTile, this.windreamSearchList.focusedItemId, depIds).subscribe(
() => this.departmentDetails.informationMessageBox('Kopieren von Suchkacheln', `Die Suchkachel "${this.windreamSearchList.focusedItem.windreamSearchName}" aus "${this.departmentDetails.entity.departmentName}" wurde zu den ausgewählten Abteilungen erfolgreich hinzugefügt`)
);
}
);
}
public openSelectDepartmentsPopup(text: string, iconanme: string, callBack: (depIds: number[]) => void) {
this.departmentDetails.keyDownListenerStopped = true;
this.dialog.openDialog(SelectDepartmentPopupComponent
, { text: text, iconanme: iconanme }
, true
, (result) => {
if (result && callBack) callBack(result);
this.departmentDetails.keyDownListenerStopped = false;
});
}
}

View File

@ -0,0 +1,40 @@
<form class="PopupContainer" #popupForm="ngForm">
<h3 mat-dialog-title class="messageBoxHeader">
<mat-icon>{{iconanme}}</mat-icon>
Windreameinstellungen übernehmen
</h3>
<div class="text">
{{text}}
</div>
<mat-dialog-content class="PopupContent">
<div class="buttons">
<button id="btnSave" name="btnSave" class="btnSave" mat-stroked-button
[disabled]="selectedDepartments.length === 0"
[mat-dialog-close]="selectedDepartments">
<mat-icon>save</mat-icon> Übernehmen
</button>
<button #btnCancel id="btnCancel" name="btnCancel" class="btnCancel" mat-stroked-button
[mat-dialog-close]="">
<mat-icon>cancel</mat-icon> Abbrechen
</button>
</div>
<hensel-selection
#input_windreamSearch
id="input-departments"
class="input-departments"
[dataSource]=departmentDataService.departmentList.items
placeholder="Windream Search Kachel"
keyExpr="entityId"
displayExpr="departmentName"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="selectedDepartments"
[customFilter]="'entityId != ' + departmentDataService.departmentList.focusedItemId"
required>
</hensel-selection>
</mat-dialog-content>
</form>

View File

@ -0,0 +1,21 @@
@import 'popup-base.component';
.PopupContent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid: ;
height: 100%; // 100% view height
grid-template-rows: auto auto auto; // row relationships
grid-template-columns: repeat(2, minmax(0, 1fr)); // column relationship
grid-column-gap: 15px;
grid-row-gap: 15px;
grid-template-areas:
'text text'
'input-departments input-departments'
'buttons buttons'
}
.input-departments {
grid-area: input-departments;
}

View File

@ -0,0 +1,25 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@app_core/components/angular-material-index';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
@Component({
selector: 'app-select-department-popup',
templateUrl: './select-department-popup.component.html',
styleUrls: [ './select-department-popup.component.scss']
})
export class SelectDepartmentPopupComponent {
public selectedDepartments: number[] = [];
public text: string;
public iconanme: string;
constructor(
public departmentDataService: DepartmentDataService,
@Inject(MAT_DIALOG_DATA) data: any
) {
this.text = data.text;
this.iconanme = data.iconanme;
}
}

View File

@ -0,0 +1,24 @@
<mat-card class="card">
<mat-card-header>
</mat-card-header>
<mat-card-content class="filterCardContainer">
<hensel-input class="filterDepartment"
placeholder="Abteilung"
[(ngModel)]=filterObject.departmentName
(keyup.Enter)="onFilterClick()">
</hensel-input>
<section class="filterButtons">
<button id="filterBtnClear" class="filterBtn" mat-stroked-button (click)=onClearClick() title="Reset">
<mat-icon>clear</mat-icon><span class="btn-lable-show"> Reset</span>
</button>
<button id="filterBtnFind" class="filterBtn" mat-stroked-button (click)=onFilterClick() title="Suchen">
<mat-icon>search</mat-icon><span class="btn-lable-show"> Suchen</span>
</button>
</section>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,49 @@
.filterCardContainer {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-column-gap: 15px;
grid-template-rows: repeat(1, auto); // row relationships
grid-template-columns: repeat(5, minmax(0, 1fr)) auto; // column relationship
grid-template-areas:
"filterDepartment . . . . filterButtons"
}
// @media screen and (max-width:1024px) {
// .woassays {
// font-size: 1vw;
// }
// }
.filterDepartment {
grid-area: filterDepartment;
}
.filterButtons {
grid-area: filterButtons;
margin-left: auto;
}
// .filterBtn {
// font-size: 1vw; //scales buttons caption depended from the current view port resolution
// }
.card {
padding: 10px;
margin: 2px 2px 0px 2px;
}
.filterBtn {
margin-left: 2px;
margin-right: 2px;
margin-right: 1px;
margin-top: 6px;
width: 90px;
}
// .filterButtons .filterBtn {
// width: unset;
// }

View File

@ -0,0 +1,61 @@
import * as keycode from '@angular/cdk/keycodes';
import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core';
import { Department, DepartmentFilter } from '@app_models/basedata/department';
import { DepartmentDataService } from '@app_modules/department/department-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import * as Cnst from '@app_core/consts';
import { AuthorizeService } from '@app_core/services/authorize.service';
import { LoginPopupComponent } from '@app_core/components/login/login-popup/login-popup.component';
@Component({
selector: 'app-department-filter',
templateUrl: './department-filter.component.html',
styleUrls: ['./department-filter.component.scss'],
})
export class DepartmentFilterComponent implements OnInit, AfterViewInit {
constructor(
public departmentDataService: DepartmentDataService,
public appDataService: AppDataService,
public authService: AuthorizeService,
) { }
@Input() public filterObject: DepartmentFilter;
@Input() departmentDetails: AppBaseEntityWrapper<Department>;
@Output() public filterFetch = new EventEmitter();
public dropDownAllCaption = Cnst.EN_DropDownConst4Filter.AllCaption;
public dropDownAllValueString = Cnst.EN_DropDownConst4Filter.AllValueString;
public dropDownAllValueInt = Cnst.EN_DropDownConst4Filter.AllValueInt;
public allCaption = Cnst.EN_DropDownConst4Filter.AllCaption;
public allValue: number = Cnst.EN_DropDownConst4Filter.AllValueInt;
public noneCaption = Cnst.EN_DropDownConst4Filter.NoneCaption;
public noneValue = Cnst.EN_DropDownConst4Filter.NoneValueInt;
ngOnInit() { }
ngAfterViewInit() { }
onFilterClick() {
if (this.departmentDetails.inViewMode) this.filterFetch.emit();
}
onClearClick() {
this.filterObject.filterReset();
}
@HostListener('window:keydown', ['$event'])
keyboardInput(event: any) {
if (!LoginPopupComponent.LOGIN_IS_SHOWN && !this.departmentDetails.keyDownListenerStopped) {
if (event.ctrlKey) {
switch (event.which) {
case keycode.F: //Ctrl + 'f'
event.stopPropagation();
this.onFilterClick();
return false; //processed
}
}
}
return true; //default processing
}
}

View File

@ -0,0 +1,86 @@
<div class="DepartmentList">
<app-department-filter class="FilterComponent"
[departmentDetails]=this.departmentDataService.departmentDetails
[filterObject]=dataSource.filter
(filterFetch)="loadData()">
</app-department-filter>
<mat-card class="card">
<mat-card-header>
</mat-card-header>
<mat-card-content>
<dx-data-grid
#gridDepartment
id="gridDepartment"
[class]="'gridDepartment'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="dataSource.items"
keyExpr="departmentId"
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowIndex)]=dataSource.focusedItemGridIndex
[(focusedRowKey)]="dataSource.focusedItemId"
[(selectedRowKeys)]="dataSource.selectedItemIds"
[showBorders]="true"
[allowColumnResizing]="true"
columnResizingMode="nextColumn"
noDataText="Keine Daten vorhanden"
(onKeyDown)="$event.handled = $event.event.key==='Escape'; dataSource.gridUpDownProcessing($event)"
(onFocusedRowChanging)="dataSource.onFocusedRowChanging($event)"
(onRowDblClick)="dataSource.focusedEntityShadowed.delayedEdit($event.data['entityId'])"
(onExporting)="globals.doExcelExportSimple($event, dataSource.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<dxo-header-filter [visible]="true"></dxo-header-filter>
<!-- <dxo-filter-row [visible]="true" [applyFilter]="true"></dxo-filter-row> -->
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple">
<!--"single" | "multiple" | "none" -->
</dxo-sorting>
<dxo-selection mode="none">
<!-- "multiple" | "none" -->
</dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf=column.visible>
<dxi-column
[allowHeaderFiltering]=column.allowHeaderFiltering
[dataField]=column.dataField
[dataType]=column.dataType
[caption]=column.caption
[width]=column.width
[sortIndex]=column.sortIndex
[sortOrder]=column.sortOrder
[visibleIndex]=column.visibleIndex
[visible]=column.visible
[fixed]=column.fixed
[headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate
[alignment]=column.alignment>
<!-- cssClass="headeralignment_center" -->
<dxo-format *ngIf="column.dataType === 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType === 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
<dxo-lookup *ngIf="column.dataField === 'rangId'" [dataSource]="appDataService.rangList?.items" valueExpr="entityId" displayExpr="rangShortname"></dxo-lookup>
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</mat-card-content>
</mat-card>
<div *ngIf="this.departmentDataService.departmentDetails.inNotViewMode" class="disableClicks"></div>
</div>

View File

@ -0,0 +1,39 @@
.DepartmentList {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-template-rows: auto minmax(0, 1fr); // row relationships
grid-template-columns: minmax(0, 1fr); // column relationship
grid-row-gap: 7px;
grid-template-areas:
'filter'
'grid';
}
.FilterComponent {
grid-area: filter; // grid area: auto height 1fr width
margin: 0 0 0 0;
}
.card {
grid-area: grid;
width: 100%;
padding: 0px;
margin: 0;
border-radius: 0px;
box-shadow: none;
}
.gridDepartment {
position: absolute; // position absolut ist hier zwingend! Die position ist absolut innerhalb mehrerer parent divs und darf sich NICHT über diese hinausstrecken.
height: 100%;
width: 100%;
padding-left: 1px;
padding-right: 1px;
padding-bottom: 1px;
}

View File

@ -0,0 +1,59 @@
import { Component, Input, OnInit } from '@angular/core';
import { Department } from '@app_models/basedata/department';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { DepartmentDataService } from '../department-data.service';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { Globals } from '@app_core/services/globals';
import { ColumnConfig, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
@Component({
selector: 'app-department-list',
templateUrl: './department-list.component.html',
styleUrls: ['./department-list.component.scss']
})
export class DepartmentListComponent implements OnInit {
@Input() public dataSource: AppBaseEntityListWrapper<Department>;
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
public departmentDataService: DepartmentDataService,
public appDataService: AppDataService,
public localeService: LocaleService,
public globals: Globals,
) {
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
col = this.columnConfigList.addNumber('Id', 'departmentId', 5);
col = this.columnConfigList.add('Name', 'departmentName', 13);
col.sortIndex = 0;
col.sortOrder = EN_SortOrder.asc;
col.allowHeaderFiltering = true;
col = this.columnConfigList.addNumber('Typ Id', 'departmentTypeId', 5);
col = this.columnConfigList.addNumber('CostCentre Id', 'costCentreId', 5);
col = this.columnConfigList.add('CostCentre', 'costCentre', 10);
col = this.columnConfigList.add('Abteilungsleiter', 'headofDepartment', 18);
col = this.columnConfigList.add('CEO', 'executiveDirector', 18);
col = this.columnConfigList.add('COO', 'managingDirector', 18);
col = this.columnConfigList.add('Folder', 'departmentNameFolder', 13);
col = this.columnConfigList.add('AD-Group Name', 'adGroupDepartmentName', 13);
col = this.columnConfigList.addNumber('Client Id', 'clientId', 5);
col = this.columnConfigList.addBoolean('Virtual', 'isVirtual', 2);
this.columnConfigList.recalcWidth();
}
public loadData() {
this.departmentDataService.loadData();
}
}

View File

@ -0,0 +1,7 @@
<div class="DepartmentComponent">
<app-department-list
[dataSource]=departmentDataService.departmentList class="DepartmentListComponent"></app-department-list>
<app-department-detail
[departmentDetails]="departmentDataService.departmentList.focusedEntityShadowed"
class="DepartmentDetailsComponent"></app-department-detail>
</div>

View File

@ -0,0 +1,22 @@
.DepartmentComponent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-template-rows: minmax(0, 1fr) auto; // row relationships
grid-template-columns: minmax(0, 1fr); // column relationship
grid-template-areas: 'list''details';
grid-column-gap: 5px;
grid-row-gap: 5px;
}
.DepartmentListComponent {
grid-area: list;
}
.DepartmentDetailsComponent {
grid-area: details;
z-index: 10;
}

View File

@ -0,0 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { DepartmentDataService } from './department-data.service';
@Component({
selector: 'app-department',
templateUrl: './department.component.html',
styleUrls: ['./department.component.scss']
})
export class DepartmentComponent implements OnInit {
constructor(
public departmentDataService: DepartmentDataService
) { }
ngOnInit() {
}
}

View File

@ -0,0 +1,57 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AngularMaterialModule } from '@app_core/components/angular-material.module';
import { DxButtonModule, DxDataGridModule, DxFileUploaderModule, DxPieChartModule, DxTemplateModule, DxTooltipModule } from 'devextreme-angular';
import { DepartmentContentComponent } from './department-detail/department-content/department-content.component';
import { DepartmentDetailComponent } from './department-detail/department-detail.component';
import { DepartmentDocumentartsComponent as DepartmentDocumentArtComponent } from './department-detail/department-documentart/department-documentart.component';
import { DepartmentWindreamIndexPopupeditComponent } from './department-detail/department-windream-index/department-windream-index-popupedit/department-windream-index-popupedit.component';
import { DepartmentWindreamIndexComponent } from './department-detail/department-windream-index/department-windream-index.component';
import { DepartmentWindreamSearchItemPopupeditComponent } from './department-detail/department-windream-search-item/department-windream-search-item-popupedit.component/department-windream-search-item-popupedit.component';
import { DepartmentWindreamSearchItemComponent } from './department-detail/department-windream-search-item/department-windream-search-item.component';
import { DepartmentWindreamSearchPopupeditComponent } from './department-detail/department-windream-search/department-windream-search-popupedit/department-windream-search-popupedit.component';
import { DepartmentWindreamSearchComponent } from './department-detail/department-windream-search/department-windream-search.component';
import { SelectDepartmentPopupComponent } from './department-detail/department-windream-search/select-department-popup/select-department-popup.component';
import { DepartmentFilterComponent } from './department-list/department-filter/department-filter.component';
import { DepartmentListComponent } from './department-list/department-list.component';
import { DepartmentComponent } from './department.component';
import { DepartmentRoutingModule } from './department.routing.module';
import { HenselSelectionComponent } from '@app_core/components/hensel-selection/hensel-selection.component';
import { HenselInputComponent } from '@app_core/components/hensel-input/hensel-input.component';
@NgModule({
declarations: [
DepartmentComponent,
DepartmentListComponent,
DepartmentDetailComponent,
DepartmentFilterComponent,
DepartmentContentComponent,
DepartmentWindreamSearchComponent,
DepartmentWindreamSearchPopupeditComponent,
DepartmentWindreamSearchItemComponent,
DepartmentWindreamSearchItemPopupeditComponent,
DepartmentWindreamIndexComponent,
DepartmentDocumentArtComponent,
DepartmentWindreamIndexPopupeditComponent,
SelectDepartmentPopupComponent
],
imports: [
CommonModule,
DepartmentRoutingModule,
AngularMaterialModule,
DxDataGridModule,
FormsModule,
HenselSelectionComponent,
HenselInputComponent,
DxFileUploaderModule,
DxButtonModule,
DxTooltipModule,
DxTemplateModule,
DxPieChartModule,
],
providers: [],
exports: []
})
export class DepartmentModule { }

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DepartmentComponent } from './department.component';
import { AuthGuard } from '@app_core/services/authguard';
const routes: Routes = [
{
path: '**',
component: DepartmentComponent,
canActivate: [AuthGuard]
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DepartmentRoutingModule { }

View File

@ -0,0 +1,160 @@
<mxfile host="65bd71144e" modified="2021-02-03T15:01:50.644Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.52.1 Chrome/83.0.4103.122 Electron/9.3.5 Safari/537.36" etag="vABciugGmu_biFYYDpc-" version="13.10.0" type="embed" pages="2">
<diagram id="crlGuFxm3afBVpfyN98v" name="Page-1">
<mxGraphModel dx="3633" dy="1330" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1920" pageHeight="1200" math="0" shadow="0">
<root>
<object label="" id="0">
<mxCell/>
</object>
<mxCell id="LfnUcp3E9milZ3mwyznO-87" value="Filter" parent="0"/>
<mxCell id="LfnUcp3E9milZ3mwyznO-177" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-87" vertex="1">
<mxGeometry x="-1900" y="30" width="1260" height="80" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-178" value="Filer" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="5" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-175" value="Name" style="text;fontSize=16;verticalAlign=middle;strokeColor=none;fillColor=none;opacity=50;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="10" y="30" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-179" value="Shortname" style="text;fontSize=16;verticalAlign=middle;strokeColor=none;fillColor=none;opacity=50;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="150" y="30" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-180" value="Login" style="text;fontSize=16;verticalAlign=middle;strokeColor=none;fillColor=none;opacity=50;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="280" y="30" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-181" value="Department" style="text;fontSize=16;verticalAlign=middle;strokeColor=none;fillColor=none;opacity=50;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="400" y="30" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-239" value="EmployeeId (Admin)" style="text;fontSize=16;verticalAlign=middle;opacity=50;glass=0;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="990" y="30" width="120" height="30" as="geometry"/>
</mxCell>
<mxCell id="35" value="" style="html=1;strokeWidth=2;shadow=0;dashed=0;shape=mxgraph.ios7ui.selectBar;dx=120;dy=5;dx2=101.43;size=5;fontSize=20;" parent="LfnUcp3E9milZ3mwyznO-177" vertex="1">
<mxGeometry x="740" y="27.75" width="215" height="34.5" as="geometry"/>
</mxCell>
<mxCell id="36" value="Zusatzinfo" style="shape=rect;fillColor=none;strokeColor=none;fontSize=15;fontFamily=Helvetica;perimeter=none;resizeHeight=1;" parent="35" vertex="1">
<mxGeometry width="95" height="49.03" relative="1" as="geometry"/>
</mxCell>
<mxCell id="37" value="Select" style="shape=rect;fillColor=none;strokeColor=none;fontSize=12;fontFamily=Helvetica;perimeter=none;resizeHeight=1;" parent="35" vertex="1">
<mxGeometry width="75" height="49.026315789473685" relative="1" as="geometry">
<mxPoint x="100" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="-TWQrLLDEwFAn9kzfAeb-19" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-87" vertex="1">
<mxGeometry x="-670" y="670" width="300" height="470" as="geometry"/>
</mxCell>
<mxCell id="-TWQrLLDEwFAn9kzfAeb-20" value="WebApp-List" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="-TWQrLLDEwFAn9kzfAeb-19" vertex="1">
<mxGeometry x="5" width="120" height="30" as="geometry"/>
</mxCell>
<object label="WebAppId WebAppName Rolle" placeholders="1" id="-TWQrLLDEwFAn9kzfAeb-21">
<mxCell style="rounded=1;arcSize=10;dashed=1;fillColor=none;dashPattern=8 3 1 3;strokeWidth=2;glass=0;fontSize=18;" parent="-TWQrLLDEwFAn9kzfAeb-19" vertex="1">
<mxGeometry x="20" y="40" width="270" height="60" as="geometry"/>
</mxCell>
</object>
<mxCell id="1" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-87" vertex="1">
<mxGeometry x="-350" y="670" width="300" height="470" as="geometry"/>
</mxCell>
<mxCell id="2" value="Merkmale" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="1" vertex="1">
<mxGeometry x="5" width="100" height="30" as="geometry"/>
</mxCell>
<object label="MerkmalId Merkmal" placeholders="1" id="3">
<mxCell style="rounded=1;arcSize=10;dashed=1;fillColor=none;dashPattern=8 3 1 3;strokeWidth=2;glass=0;fontSize=18;" parent="1" vertex="1">
<mxGeometry x="20" y="40" width="270" height="60" as="geometry"/>
</mxCell>
</object>
<mxCell id="39" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-87" vertex="1">
<mxGeometry x="-630" y="30" width="540" height="80" as="geometry"/>
</mxCell>
<mxCell id="40" value="Merkmal Administration" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="39" vertex="1">
<mxGeometry x="5" width="200" height="30" as="geometry"/>
</mxCell>
<mxCell id="46" value="" style="html=1;strokeWidth=2;shadow=0;dashed=0;shape=mxgraph.ios7ui.selectBar;dx=120;dy=5;dx2=101.43;size=5;fontSize=20;" parent="39" vertex="1">
<mxGeometry x="20" y="30" width="215" height="34.5" as="geometry"/>
</mxCell>
<mxCell id="47" value="Merkmal" style="shape=rect;fillColor=none;strokeColor=none;fontSize=15;fontFamily=Helvetica;perimeter=none;resizeHeight=1;" parent="46" vertex="1">
<mxGeometry width="95" height="49.03" relative="1" as="geometry"/>
</mxCell>
<mxCell id="48" value="Select" style="shape=rect;fillColor=none;strokeColor=none;fontSize=12;fontFamily=Helvetica;perimeter=none;resizeHeight=1;" parent="46" vertex="1">
<mxGeometry width="75" height="49.026315789473685" relative="1" as="geometry">
<mxPoint x="100" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="49" value="Bearbeiten" style="rounded=1;html=1;shadow=0;dashed=0;whiteSpace=wrap;fontSize=18;align=center;sketch=0;" parent="39" vertex="1">
<mxGeometry x="270" y="31.56" width="105.5" height="28.44" as="geometry"/>
</mxCell>
<mxCell id="50" value="Speichern" style="rounded=1;html=1;shadow=0;dashed=0;whiteSpace=wrap;fontSize=18;align=center;sketch=0;" parent="39" vertex="1">
<mxGeometry x="390" y="33.03" width="105.5" height="28.44" as="geometry"/>
</mxCell>
<mxCell id="52" value="&#10;&#10;&lt;div style=&quot;color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: consolas, &amp;quot;courier new&amp;quot;, monospace; font-weight: normal; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div&gt;&lt;span style=&quot;color: #9cdcfe&quot;&gt;grid-area&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4&quot;&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;" style="text;whiteSpace=wrap;html=1;" vertex="1" parent="LfnUcp3E9milZ3mwyznO-87">
<mxGeometry x="-1910" y="10" width="100" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-182" value="Lists" parent="0"/>
<mxCell id="LfnUcp3E9milZ3mwyznO-184" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-182" vertex="1">
<mxGeometry x="-1880" y="140" width="1830" height="470" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-185" value="Employee-List" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="LfnUcp3E9milZ3mwyznO-184" vertex="1">
<mxGeometry x="5" width="130" height="30" as="geometry"/>
</mxCell>
<mxCell id="LfnUcp3E9milZ3mwyznO-196" value="EmployeeId (Admin)" style="text;fontSize=16;verticalAlign=middle;opacity=50;" parent="LfnUcp3E9milZ3mwyznO-184" vertex="1">
<mxGeometry x="10" y="30" width="90" height="30" as="geometry"/>
</mxCell>
<object label="EmployeeNo&#9;Salutation&#9;FirstName&#9;LastName&#9;ShortName&#9;Position&#9;LoginName&#9;Email&#9;Rang&#9;ClientId" placeholders="1" id="LfnUcp3E9milZ3mwyznO-246">
<mxCell style="rounded=1;arcSize=10;dashed=1;fillColor=none;dashPattern=8 3 1 3;strokeWidth=2;glass=0;fontSize=23;" parent="LfnUcp3E9milZ3mwyznO-184" vertex="1">
<mxGeometry x="30" y="60" width="1160" height="60" as="geometry"/>
</mxCell>
</object>
<mxCell id="4" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.tick;fontSize=18;" parent="LfnUcp3E9milZ3mwyznO-184" vertex="1">
<mxGeometry x="10" y="140" width="30" height="20" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.tick;fontSize=18;" parent="LfnUcp3E9milZ3mwyznO-184" vertex="1">
<mxGeometry x="10" y="170" width="30" height="20" as="geometry"/>
</mxCell>
<mxCell id="31" value="" style="html=1;aspect=fixed;strokeColor=none;shadow=0;align=center;verticalAlign=top;shape=mxgraph.gcp2.view_list;fontSize=18;" parent="LfnUcp3E9milZ3mwyznO-184" vertex="1">
<mxGeometry x="40" y="130" width="110" height="137.5" as="geometry"/>
</mxCell>
<mxCell id="VQwmF7H8NuOuIEITXOnG-4" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-182" vertex="1">
<mxGeometry x="-1900" y="670" width="860" height="460" as="geometry"/>
</mxCell>
<mxCell id="VQwmF7H8NuOuIEITXOnG-5" value="Employee-Details" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="VQwmF7H8NuOuIEITXOnG-4" vertex="1">
<mxGeometry x="5" width="160" height="30" as="geometry"/>
</mxCell>
<object label="" placeholders="1" id="VQwmF7H8NuOuIEITXOnG-7">
<mxCell style="rounded=1;arcSize=10;dashed=1;fillColor=none;dashPattern=8 3 1 3;strokeWidth=2;glass=0;fontSize=23;" parent="VQwmF7H8NuOuIEITXOnG-4" vertex="1">
<mxGeometry x="20" y="60" width="360" height="60" as="geometry"/>
</mxCell>
</object>
<mxCell id="-TWQrLLDEwFAn9kzfAeb-5" value="" style="shape=mxgraph.mockup.containers.marginRect;rectMarginTop=10;strokeWidth=1;dashed=0;arcSize=5;recursiveResize=0;" parent="LfnUcp3E9milZ3mwyznO-182" vertex="1">
<mxGeometry x="-1040" y="670" width="360" height="470" as="geometry"/>
</mxCell>
<mxCell id="-TWQrLLDEwFAn9kzfAeb-6" value="Department-List" style="shape=rect;strokeColor=none;strokeWidth=1;dashed=0;arcSize=20;fontSize=17;spacing=2;spacingTop=-2;align=left;autosize=1;spacingLeft=4;resizeWidth=0;resizeHeight=0;perimeter=none;" parent="-TWQrLLDEwFAn9kzfAeb-5" vertex="1">
<mxGeometry x="5" width="140" height="30" as="geometry"/>
</mxCell>
<object label="DepartmentId  DepartmentName Rang" placeholders="1" id="-TWQrLLDEwFAn9kzfAeb-7">
<mxCell style="rounded=1;arcSize=10;dashed=1;fillColor=none;dashPattern=8 3 1 3;strokeWidth=2;glass=0;fontSize=18;" parent="-TWQrLLDEwFAn9kzfAeb-5" vertex="1">
<mxGeometry x="20" y="40" width="330" height="60" as="geometry"/>
</mxCell>
</object>
<mxCell id="-TWQrLLDEwFAn9kzfAeb-18" value="Details" parent="0"/>
</root>
</mxGraphModel>
</diagram>
<diagram id="xSUcNyafCY5EnCSOazaM" name="Page-2">
&#xa; &#xa;&#xa;
<mxGraphModel dx="1376" dy="910" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
&#xa; &#xa;&#xa;
<root>
&#xa; &#xa;&#xa;
<mxCell id="ewFg04713mjC0kNU1MT--0"/>
&#xa; &#xa;&#xa;
<mxCell id="ewFg04713mjC0kNU1MT--1" parent="ewFg04713mjC0kNU1MT--0"/>
&#xa; &#xa;&#xa;
<mxCell id="AtFcv4WY-9kmqpDSp6Za-0" value="" style="shape=mxgraph.cisco.misc.page_icon;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;rounded=0;sketch=0;fontColor=#1A1A1A;" vertex="1" parent="ewFg04713mjC0kNU1MT--1">
&#xa; &#xa;&#xa;
<mxGeometry x="70" y="160" width="170" height="190" as="geometry"/>
&#xa; &#xa;&#xa;
</mxCell>
&#xa; &#xa;&#xa;
</root>
&#xa; &#xa;&#xa;
</mxGraphModel>
&#xa; &#xa;&#xa;
</diagram>
</mxfile>

View File

@ -0,0 +1,167 @@
import { Injectable } from '@angular/core';
import { cnst_LoadedEntitiesExpiresInMs, EN_AppEntities, EN_AppPages, EN_HttpQueriesCount4Page } from '@app_consts';
import { EN_EntityBeforeSaveCallBackResults } from '@app_core/consts';
import { CoreUser } from '@app_core/models/coreuser';
import { RepositoryService } from '@app_core/services/http/repository.service';
import { UINotifierService } from '@app_core/services/notification/uinotifier.service';
import { CorePageService } from '@app_core/services/page.service';
import { Employee, EmployeeFilter, EmployeeFullFilter } from '@app_models/employee';
import { EmployeeToAttribute } from '@app_models/employee-to-attribute';
import { EmployeeToDepartment } from '@app_models/employee-to-department';
import { EmployeeToWebApp } from '@app_models/employee-to-webapp';
import { AppBaseEntity } from '@app_models/generic/app.baseentity';
import { User } from '@app_models/user';
import { EmployeeToWebAppFilter, WebAppToDepartment } from '@app_models/webapp-to-department';
import { WebAppToWebAppAdditionalRole } from '@app_models/webapp-to-webappadditionalrole';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { AppBaseEntityCallBack } from 'src/app/shared/app.types';
@Injectable({ providedIn: 'root' })
export class EmployeeDataService extends CorePageService {
protected appPage: EN_AppPages = EN_AppPages.Employee;
protected get appPageNoQueries() { return EN_HttpQueriesCount4Page.Employee; }
// --- running data
employeeDetails: AppBaseEntityWrapper<Employee>;
employeeList: AppBaseEntityListWrapper<Employee> = new AppBaseEntityListWrapper<Employee>(Employee, 'Mitarbeiterliste', EN_AppEntities.Employee);
employeeFilter: EmployeeFullFilter = new EmployeeFullFilter('EmployeeFullFilter');
employee2AllFilter: EmployeeFilter = new EmployeeFilter('EmployeeFilter');
employee2DepartmentList: AppBaseEntityListWrapper<EmployeeToDepartment> = new AppBaseEntityListWrapper<EmployeeToDepartment>(EmployeeToDepartment, 'Abteilungen von Mitarbeiter', EN_AppEntities.EmployeeToDepartment);
employee2WebAppList: AppBaseEntityListWrapper<EmployeeToWebApp> = new AppBaseEntityListWrapper<EmployeeToWebApp>(EmployeeToWebApp, 'Applikationen von Mitarbeiter', EN_AppEntities.EmployeeToWebapp);
employee2AttributeList: AppBaseEntityListWrapper<EmployeeToAttribute> = new AppBaseEntityListWrapper<EmployeeToAttribute>(EmployeeToAttribute, 'Merkmale von Mitarbeiter', EN_AppEntities.EmployeeToAttribute);
constructor(
private appDataService: AppDataService,
private repositoryService: RepositoryService,
protected uiNotificationsService: UINotifierService
) {
super();
this.employeeList.alwaysFocused = true;
this.employeeList.focusedCallBack = this.focusCallBack.bind(this);
this.employeeList.filter = this.employeeFilter;
this.employeeDetails = this.employeeList.createFocusedShadowEntity(AppBaseEntityWrapper.EN_ViewMode);
this.employeeDetails.loadedDataExpiresInMs = cnst_LoadedEntitiesExpiresInMs;
this.activateRouting4EntityList(this.employeeList); //we are using routing
this.baseEntityWrapperWaitLoading = this.employeeDetails;
this.prepareDetailsList(this.employee2DepartmentList);
this.prepareDetailsList(this.employee2WebAppList);
this.employeeDetails.detailItems.register(this.employee2DepartmentList, null, 'employeeId', /*() => this.employee2DepartmentList.focuseFirstItem()*/);
this.employeeDetails.detailItems.register(this.employee2WebAppList, null, 'employeeId');
this.employeeDetails.detailItems.register(this.employee2AttributeList, null, 'employeeId');
this.employee2WebAppList.batchInsert = false;
this.employee2WebAppList.beforeSaveCallBack = this.beforeSaveEmployee2WebAppList.bind(this);
this.employee2WebAppList.afterInsertCallBack = this.afterInsertEmployee2WebAppList.bind(this);
this.employee2AttributeList.filter = this.employee2AllFilter;
this.loadBaseData();
this.init();
}
async beforeSaveEmployee2WebAppList(entities: EmployeeToWebApp[]): Promise<EN_EntityBeforeSaveCallBackResults> {
entities.forEach((entity: EmployeeToWebApp) => {
if (entity.entityChanged && !entity.isNew()) this.afterInsertEmployee2WebAppList(entity, null, false);
});
this.employee2WebAppList.deletedItems.forEach((entity: EmployeeToWebApp) => {
entity.arExtendedDepartmentIdList = [];
entity.arAdditionalRoleIdList = [];
this.afterInsertEmployee2WebAppList(entity, null, false);
});
return EN_EntityBeforeSaveCallBackResults.Ok_Continue;
}
afterInsertEmployee2WebAppList(entity: EmployeeToWebApp, callback?: AppBaseEntityCallBack, reloadEntity = true) {
if (entity) {
const filter = new EmployeeToWebAppFilter('EmployeeToWebAppFilter');
filter.employeeToWebAppId = entity.employeeToWebAppId;
//to create wrapper not in the constructor we need to pass appLogsService, repositoryService, uiNotificationsService explicetely!
const departmentList = new AppBaseEntityListWrapper<WebAppToDepartment>(WebAppToDepartment, 'Abteilungen für Mitarbeiterapplikation', EN_AppEntities.WebAppToDepartment, filter, this.appLogsService, this.repositoryService, this.uiNotificationsService);
const addRoleList =
new AppBaseEntityListWrapper<WebAppToWebAppAdditionalRole>(WebAppToWebAppAdditionalRole, 'Zusatzrole für Mitarbeiterapplikation', EN_AppEntities.WebAppToWebAppAdditionalRole, filter, this.appLogsService, this.repositoryService, this.uiNotificationsService);
this._saveEmployee2WebAppSublists(entity
, departmentList
, 'arExtendedDepartmentIdList'
, 'departmentId'
, (result1) => {
this._saveEmployee2WebAppSublists(entity
, addRoleList
, 'arAdditionalRoleIdList'
, 'webAppAdditionalRoleId'
, (result2) => { if (reloadEntity && (result1 || result2)) entity.load(this.repositoryService, callback); else if (callback) callback(); }
);
}
);
}
}
private _saveEmployee2WebAppSublists(entity: EmployeeToWebApp, list: AppBaseEntityListWrapper<AppBaseEntity>, idListName: string, idName: string, callback?: (entity: boolean) => void) {
let result: boolean = false;
list.load(() => {
if (result = list.match2array(entity, 'employeeToWebAppId', idListName, idName)) {
list.save(() => { if (callback) callback(true); }, (err) => { if (callback) callback(true); });
} else if (callback) callback(false);
});
}
prepareDetailsList(list: AppBaseEntityListWrapper<AppBaseEntity>) {
list.createFocusedShadowEntity();
list.filter = this.employee2AllFilter;
list.focusedEntityShadowed.dontLoadEntity = true;
list.focusedEntityShadowed.dontUpdateEntityDirectly = true;
list.focusedEntityShadowed.dontInsertEntityDirectly = true;
}
loadData() {
super.loadData();
this.employeeList.load(null, null, () => this.pageLoadingService.updatePageLoadedCounters(this.appPage));
}
public loadBaseData() {
super.loadBaseData(EN_AppPages.Login, EN_HttpQueriesCount4Page.Login);
this.appDataService.loadData(() => this.pageLoadingService.updatePageLoadedCounters(EN_AppPages.Login));
}
public focusCallBack(focusedItem: Employee) {
}
public clearData() {
super.clearData();
this.employeeFilter.filterReset();
this.employeeList.clear();
}
protected onLoggedInOut(user: CoreUser) {
this.userChanged(new User(user));
super.onLoggedInOut(user);
}
private userChanged(user: User) {
if (this.authService.isLoggedIn()) {
this.employeeDetails.canNew = user.isMaster;
this.employeeDetails.canEdit = this.employeeDetails.canNew;
this.employeeDetails.canDelete = this.employeeDetails.canNew;
}
// this.globalsService.appPages[EN_AppPages.UserConfig].hidden = !user?.isMaster;
}
}

View File

@ -0,0 +1,20 @@
<div class="employee-attribute">
<label class="label">Merkmale</label>
<hensel-selection
id="attributes"
class="attributes"
name="attributes"
[dataSource]=appDataService.employeeAttributeList.items
placeholder="Merkmal"
keyExpr="entityId"
displayTemplate="${name} (${shortname})"
sortField="seqNo"
[multiSelect]=true
separator=","
[(selectedItems)]="employeeDetails.entity.arAttributeIdList"
(selectedItemsChange)="employeeDetails.entityWasChanged = true"
[disabled]=!employeeDetails.inNotViewMode
[height]="'100%'"
[useResultGrid]=true>
</hensel-selection>
</div>

View File

@ -0,0 +1,20 @@
.employee-attribute {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: minmax(0, 1fr); // column relationship
grid-template-rows: auto minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas: "label""attributes";
}
.attributes {
grid-area: attributes;
height: 100%;
width: calc(100% - 28px);
margin-left: 28px;
}
.label {
grid-area: label;
}

View File

@ -0,0 +1,24 @@
import { Component, OnInit, Input } from '@angular/core';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { Employee } from '@app_models/employee';
import { AppDataService } from '@app_services/app.data.service';
@Component({
selector: 'app-employee-attribute',
templateUrl: './employee-attribute.component.html',
styleUrls: ['./employee-attribute.component.scss']
})
export class EmployeeAttributeComponent implements OnInit {
@Input() public employeeDetails: AppBaseEntityWrapper<Employee>;
constructor(
public appDataService: AppDataService,
) {
}
ngOnInit() {
}
}

View File

@ -0,0 +1,147 @@
<form class="employeeDetailsContent" #detailForm="ngForm">
<!-- 1-st column start -->
<hensel-input
class="input-employeeno"
name="employeeNo"
placeholder="Mitarbeiter Nr."
required
autocomplete="off"
maxlength="50"
[(ngModel)]=employeeDetails.entity.employeeNo
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-salutation"
name="salutation"
placeholder="Anrede"
maxlength="50"
[(ngModel)]=employeeDetails.entity.salutation
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-firstname"
name="firstname"
placeholder="Vorname"
[(ngModel)]=employeeDetails.entity.firstName
[disabled]="!employeeDetails.inNewEditMode"
required
maxlength="50">
</hensel-input>
<hensel-input
class="input-lastname"
name="lastname"
placeholder="Nachname"
required
maxlength="50"
[(ngModel)]=employeeDetails.entity.lastName
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-shortname"
name="shortname"
placeholder="Kürzel"
required
maxlength="10"
[(ngModel)]=employeeDetails.entity.shortName
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-loginname"
name="login"
placeholder="Login"
required maxlength="50"
[(ngModel)]=employeeDetails.entity.loginName
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-email"
name="Email"
placeholder="User Email"
maxlength="50"
[(ngModel)]=employeeDetails.entity.email
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-selection
name="Mandant"
class="input-mandant"
[dataSource]=appDataService.subsidiaryList.items
placeholder="Mandant"
keyExpr="clientId"
displayExpr="subsidiaryCode"
sortField="*"
[multiSelect]=false
[(selectedItems)]="employeeDetails.entity.clientId"
[disabled]="!employeeDetails.inNewEditMode"
required>
</hensel-selection>
<!-- 1-st column end -->
<!-- 2-nd column start -->
<hensel-input
class="input-title"
name="title"
placeholder="Titel"
maxlength="50"
[(ngModel)]=employeeDetails.entity.title
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-position"
name="position"
placeholder="Position"
required
maxlength="50"
[(ngModel)]=employeeDetails.entity.position
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-selection
name="rang"
class="input-rang"
[dataSource]=appDataService.rangList.items
placeholder="Rang"
keyExpr="entityId"
displayExpr="rangName"
sortField="*"
[multiSelect]=false
[(selectedItems)]="employeeDetails.entity.rangId"
[disabled]="!employeeDetails.inNewEditMode"
required>
</hensel-selection>
<hensel-input
class="input-mobilephoneno"
name="mobileno"
placeholder="Handynummer"
maxlength="30"
[(ngModel)]=employeeDetails.entity.mobilePhoneNo
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<hensel-input
class="input-phoneno"
name="phoneno"
placeholder="Festnetznummer"
maxlength="30"
[(ngModel)]=employeeDetails.entity.phoneNo
[disabled]="!employeeDetails.inNewEditMode" >
</hensel-input>
<mat-checkbox
class="input-isActive"
name="Enabled"
style="float: left"
labelPosition="after"
[(ngModel)]="employeeDetails.entity.isActive"
[disabled]="!employeeDetails.inNewEditMode">
Aktiv
</mat-checkbox>
<!-- 2-nd column end -->
</form>

View File

@ -0,0 +1,97 @@
.employeeDetailsContent {
// z-index: 10;
// position: relative; //otherwise z-index is not working
padding: 0 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-column-gap: 8px;
grid-template-columns: repeat(2, minmax(1px, 1fr)); // column relationship
grid-template-rows: repeat(7, 42px); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas:
"input-employeeno input-title"
"input-salutation input-position"
"input-firstname input-rang"
"input-lastname input-mobilephoneno"
"input-shortname input-phoneno"
"input-loginname input-mandant"
"input-email input-isActive "
;
}
.input-isActive {
margin-top: 12px;
grid-area: input-isActive;
}
.input-mandant {
grid-area: input-mandant;
}
.input-employeeno {
grid-area: input-employeeno;
}
.input-firstname {
grid-area: input-firstname;
}
.input-lastname {
grid-area: input-lastname;
}
.input-shortname {
grid-area: input-shortname;
}
.input-loginname {
grid-area: input-loginname;
}
.input-email {
grid-area: input-email;
}
.input-salutation {
grid-area: input-salutation;
}
.input-title {
grid-area: input-title;
}
.input-position {
grid-area: input-position;
}
.input-rang {
grid-area: input-rang;
}
.input-email {
grid-area: input-email;
}
.input-mobilephoneno {
grid-area: input-mobilephoneno;
}
.input-phoneno {
grid-area: input-phoneno;
}
.check-box {
margin-top: 13px;
}
.card {
padding: 12px;
margin: 2px 2px 2px 2px;
}
.abstand {
margin-right: 1px !important;
}

View File

@ -0,0 +1,33 @@
import { Component, OnInit, Input, ViewChild, AfterViewInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { Employee } from '@app_models/employee';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { AuthorizeService } from '@app_core/services/authorize.service';
import { Globals } from '@app_core/services/globals';
@Component({
selector: 'app-details-content',
templateUrl: './employee-content.component.html',
styleUrls: ['./employee-content.component.scss'],
})
export class EmployeeContentComponent implements OnInit, AfterViewInit {
@Input() public employeeDetails: AppBaseEntityWrapper<Employee>;
@ViewChild('detailForm') detailForm: NgForm;
constructor(
public appDataService: AppDataService,
public employeeDataService: EmployeeDataService,
public authorizeService: AuthorizeService,
public globals: Globals
) { }
ngAfterViewInit(): void {
this.employeeDetails.detailForm = this.detailForm;
}
ngOnInit() { }
}

View File

@ -0,0 +1,94 @@
<div class="employee-department">
<label class="label">Abteilungsliste</label>
<section class="toolbar">
<div class="toolbar-button-row">
<div class="toolbar-flex-container">
<button mat-icon-button title="Hinzufügen" (click)=addItem()
[disabled]="!employeeDetails.inNotViewMode || !employeeDepartmentList.focusedEntityShadowed.enabledNewButton()">
<mat-icon>add</mat-icon>
</button>
<button mat-icon-button title="Bearbeiten" (click)=editItem()
[disabled]="!employeeDetails.inNotViewMode || !employeeDepartmentList.focusedItem || !employeeDepartmentList.focusedEntityShadowed.enabledEditButton()">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button title="Löschen" class="toolbar-button-last" (click)=deleteDepartment();
[disabled]="!employeeDetails.inNotViewMode
|| !employeeDepartmentList.focusedItem
|| !employeeDepartmentList.focusedEntityShadowed.enabledDeleteButton()">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</section>
<dx-data-grid
name="departmentGrid"
[class]="'grid'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="employeeDepartmentList.items"
keyExpr="entityId"
[(focusedRowIndex)]=employeeDepartmentList.focusedItemGridIndex
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowKey)]="employeeDepartmentList.focusedItemId"
[(selectedRowKeys)]="employeeDepartmentList.selectedItemIds"
[showBorders]="true"
[allowColumnResizing]="true"
columnResizingMode="widget"
noDataText="Keine Daten sind vorhanden"
[renderAsync]="true"
(onRowDblClick)="dblClick($event)"
(onKeyDown)="$event.handled = $event.event.key==='Escape'"
(onExporting)="globals.doExcelExportSimple($event, employeeDepartmentList.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<dxo-header-filter [visible]="true"></dxo-header-filter> -->
<dxo-filter-row [visible]="false" [applyFilter]="true"></dxo-filter-row>
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple"></dxo-sorting>
<dxo-selection mode="none"></dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf="column.visible">
<dxi-column
[allowHeaderFiltering]=column.allowHeaderFiltering
[dataField]=column.dataField
[dataType]=column.dataType
[caption]=column.caption
[width]=column.width
[sortIndex]=column.sortIndex
[sortOrder]=column.sortOrder
[visibleIndex]=column.visibleIndex
[visible]=column.visible
[fixed]=column.fixed
[headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate
[allowSorting]=column.allowSorting
[calculateCellValue]=column.calculateCellValue
[calculateDisplayValue]=column.calculateDisplayValue
[calculateGroupValue]=column.calculateGroupValue
[calculateSortValue]=column.calculateSortValue
[alignment]=column.alignment>
<dxo-format *ngIf="column.dataType == 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType == 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
<dxo-lookup *ngIf="column.dataField === 'departmentId'" [dataSource]="appDataService.departmentList?.items" valueExpr="entityId" displayExpr="departmentName"></dxo-lookup>
<dxo-lookup *ngIf="column.dataField === 'employeeStatusId'" [dataSource]="appDataService.employeeStatusList?.items" valueExpr="entityId" displayExpr="employeeStatusName"></dxo-lookup>
<dxo-lookup *ngIf="column.dataField === 'rangId'" [dataSource]="appDataService.rangList?.items" valueExpr="entityId" displayExpr="rangShortname"></dxo-lookup>
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</div>

View File

@ -0,0 +1,23 @@
.employee-department {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: auto minmax(0, 1fr); // column relationship
grid-template-rows: auto minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
grid-template-areas: "label label" "toolbar grid";
}
.grid {
grid-area: grid;
height: 100%;
width: 100%;
}
.label {
grid-area: label;
// padding-bottom: 6px;
}
.toolbar {
grid-area: toolbar;
}

View File

@ -0,0 +1,103 @@
import { Component, Input, OnInit } from '@angular/core';
import { EN_EmployeeRang, EN_EmployeeStatus } from '@app_consts';
import { Employee } from '@app_models/employee';
import { EmployeeToDepartment } from '@app_models/employee-to-department';
import { EmployeeToWebApp } from '@app_models/employee-to-webapp';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { EmployeeToDepartmentPopupeditComponent } from './employee-department.popupedit/employee-department.popupedit.component';
import { MatDialogService } from '@app_core/services/mat-dialog.service';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { Globals } from '@app_core/services/globals';
import { ColumnConfig, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
@Component({
selector: 'app-employee-department',
templateUrl: './employee-department.component.html',
styleUrls: ['./employee-department.component.scss']
})
export class EmployeeDepartmentComponent implements OnInit {
@Input() employeeDepartmentList: AppBaseEntityListWrapper<EmployeeToDepartment>;
@Input() public employeeDetails: AppBaseEntityWrapper<Employee>;
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
private dialog: MatDialogService,
public appDataService: AppDataService,
public employeeDataService: EmployeeDataService,
public localeService: LocaleService,
public globals: Globals,
) {
}
get emplDepDetails(): AppBaseEntityWrapper<EmployeeToDepartment> {
return this.employeeDepartmentList.focusedEntityShadowed;
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
if (this.appDataService.showIds) col = this.columnConfigList.addNumber('Id', 'entityId', 7, 0);
col = this.columnConfigList.add('Name', 'departmentId', 25);
col = this.columnConfigList.add('Status', 'employeeStatusId', 20);
col.sortIndex = 1;
col.sortOrder = EN_SortOrder.asc;
col = this.columnConfigList.add('Rang', 'rangId', 10);
this.columnConfigList.recalcWidth();
}
addItem() {
this.emplDepDetails.new();
this.emplDepDetails.entity.employeeId = this.employeeDetails.entityId;
const employeeStatusId = this.employeeDepartmentList.items.find(e2d => e2d.employeeStatusId === EN_EmployeeStatus.MainFunction) ? EN_EmployeeStatus.AdditionalFunction : EN_EmployeeStatus.MainFunction;
this.emplDepDetails.entity.employeeStatusId = employeeStatusId;
this.emplDepDetails.entity.rangId = EN_EmployeeRang.Mitarbeiter;
this.openPopupDialog(this.emplDepDetails);
}
dblClick(event: any) {
if (!this.employeeDetails.entityIsLoading) this.employeeDepartmentList.focusedEntityShadowed.delayedEdit(event.data['entityId'], this.editItem);
}
editItem = () => {
this.emplDepDetails.edit();
this.openPopupDialog(this.emplDepDetails);
}
public openPopupDialog(editEntity: AppBaseEntityWrapper<EmployeeToDepartment>) {
this.employeeDetails.keyDownListenerStopped = true;
this.dialog.openDialog(EmployeeToDepartmentPopupeditComponent, {
baseEntityWrapper: editEntity,
keyItemName: this.employeeDetails.entity.firstName + ' ' + this.employeeDetails.entity.lastName,
readOnly: !this.employeeDetails.inNotViewMode,
description: (editEntity.entity.entityId === 0 ? 'Neue Abteilung zuweisen' : 'Daten zur Abteilung "' + editEntity.entity.departmentName + '" bearbeiten '),
list: this.employeeDepartmentList,
}, true, () => this.employeeDetails.keyDownListenerStopped = false);
}
public deleteDepartment() {
let usedDepartmentIdsStr: string = '';
this.employeeDataService.employee2WebAppList.items.forEach((e2w: EmployeeToWebApp) => usedDepartmentIdsStr += ',' + e2w.departmentId + ',' + e2w.arExtendedDepartmentIdList);
const usedDepartmentIdsList: string[] = usedDepartmentIdsStr.split(',');
if (usedDepartmentIdsList.indexOf('' + this.emplDepDetails.entity.departmentId) > -1) {
this.emplDepDetails.warningMessageBox(`Löschen von Abteilung <${this.emplDepDetails.entity.departmentName}>`,
'Die Abteilung kann nicht gelöscht werden, weil sie zu einer Webapplikation zugewiesen ist');
} else this.emplDepDetails.delete();
}
}

View File

@ -0,0 +1,68 @@
<form class="PopupContainer" #popupForm="ngForm">
<!-- <dx-button class="buttonLayoutCancel" icon="close" (click)="close()"></dx-button> -->
<h2 mat-dialog-title class="messageBoxHeader">{{description}} - {{keyItemName}} </h2>
<mat-dialog-content class="PopupContent">
<div class="buttons">
<button id="btnSave" name="btnSave" class="btnSave" mat-stroked-button [disabled]="!baseEntityWrapper.enabledSaveButton() || readOnly" (click)="save() ">
<mat-icon>save</mat-icon> Übernehmen
</button>
<button #btnCancel id="btnCancel" name="btnCancel" class="btnCancel" mat-stroked-button (click)="cancel()">
<mat-icon>cancel</mat-icon> Abbrechen
</button>
</div>
<hensel-selection
#input_department
id="input-department"
class="input-department"
[dataSource]=appDataService.departmentList.items
placeholder="Abteilung"
keyExpr="entityId"
displayExpr="departmentName"
sortField="*"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.departmentId"
[disabled]="readOnly || focusedItem.entityId !== 0"
[customFilterFn]="focusedItem.entityId === 0?getNotUsedDepartments:''"
required>
</hensel-selection>
<hensel-selection
#input_emplstatus
id="input-emplstatus"
class="input-emplstatus"
[dataSource]=appDataService.employeeStatusList.items
placeholder="Status"
keyExpr="entityId"
displayExpr="employeeStatusName"
sortField="*"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.employeeStatusId"
[disabled]=readOnly
required>
</hensel-selection>
<hensel-selection
#input_rang
id="input-rang"
class="input-rang"
[dataSource]=appDataService.rangList.items
placeholder="Rang"
keyExpr="entityId"
displayExpr="rangName"
sortField="rangOrder"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.rangId"
[disabled]=readOnly
required>
</hensel-selection>
</mat-dialog-content>
</form>

View File

@ -0,0 +1,28 @@
@import 'popup-base.component';
.PopupContent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid: ;
height: 100%; // 100% view height
grid-template-rows: auto auto; // row relationships
grid-template-columns: repeat(3, minmax(0, 1fr)); // column relationship
grid-column-gap: 15px;
grid-row-gap: 15px;
grid-template-areas:
'input-department input-emplstatus input-rang'
'buttons buttons buttons'
}
.input-department {
grid-area: input-department;
}
.input-emplstatus {
grid-area: input-emplstatus;
}
.input-rang {
grid-area: input-rang;
}

View File

@ -0,0 +1,30 @@
import { Component } from '@angular/core';
import { PopupBaseComponent } from '@app_core/components/popup-base/popup-base.component';
import { Department } from '@app_models/basedata/department';
import { EmployeeToDepartment } from '@app_models/employee-to-department';
import { AppDataService } from '@app_services/app.data.service';
@Component({
selector: 'app-employee-department-popupedit',
templateUrl: 'employee-department.popupedit.component.html',
styleUrls: ['employee-department.popupedit.component.scss']
})
export class EmployeeToDepartmentPopupeditComponent extends PopupBaseComponent {
constructor(
public appDataService: AppDataService,
) {
super();
}
public get focusedItem(): EmployeeToDepartment {
return <EmployeeToDepartment>(this._focusedItem);
}
getNotUsedDepartments = (dep: Department) => {
return !this.list.items.find((item) => item['departmentId'] === dep.entityId);
}
}

View File

@ -0,0 +1,47 @@
<form class="EmployeeDetailsForm" #detailForm="ngForm" id="form_employee">
<mat-card class="card">
<mat-card-header>
</mat-card-header>
<mat-card-content class="EmployeeDetails">
<section class="toolbar">
<div class="toolbar-button-row">
<button *ngIf="employeeDetails.canNew" #btnNew id="btnNew" name="btnNew" class="abstand" mat-stroked-button
title="Neuer Mitarbeiter" (click)="add()"
[disabled]="!employeeDetails.enabledNewButton()">
<mat-icon>add</mat-icon> Hinzufügen
</button>
<button *ngIf="employeeDetails.canEdit" #btnEdit id="btnEdit" name="btnEdit" class="abstand" mat-stroked-button
title="Bearbeiten" (click)="employeeDetails.edit()"
[disabled]="!employeeDetails.enabledEditButton()">
<mat-icon>edit</mat-icon> Bearbeiten
</button>
<button *ngIf="employeeDetails.canEdit||employeeDetails.canNew" id="btnSave" name="btnSave" class="abstand"
mat-stroked-button disabled title="Speichern" (click)="save()"
[disabled]="!employeeDetails.enabledSaveButton()">
<mat-icon>save</mat-icon> Speichern
</button>
<button *ngIf="employeeDetails.canEdit||employeeDetails.canNew" id="btnUndo" name="btnUndo" class="abstand"
mat-stroked-button disabled title="Änderungen verwerfen" (click)="cancel()" [autofocus]=true
[disabled]="!employeeDetails.enabledCancelButton()">
<mat-icon>undo</mat-icon> Verwerfen
</button>
<button *ngIf="employeeDetails.canDelete" id="btnDelete" name="btnDelete" mat-stroked-button class="abstand"
title="Benutzer löschen" class="last" (click)="employeeDetails.delete()"
[disabled]="!employeeDetails.enabledDeleteButton()">
<mat-icon>delete</mat-icon> Löschen
</button>
</div>
</section>
<app-details-content class="DetailsContent" [employeeDetails]=employeeDetails></app-details-content>
<app-employee-department class=DepartmentList
[employeeDepartmentList]=employeeDataService.employee2DepartmentList
[employeeDetails]=employeeDetails>
</app-employee-department>
<app-employee-webapp class="WebAppList"
[employeeDetails]=employeeDetails>
</app-employee-webapp>
<app-employee-attribute class="AttributeList" [employeeDetails]=employeeDetails></app-employee-attribute>
</mat-card-content>
</mat-card>
</form>

View File

@ -0,0 +1,48 @@
.EmployeeDetails {
padding: 0;
margin: 0;
overflow: hidden;
display: grid;
width: 100%;
height: 100%;
grid-template-rows: 42px minmax(1px, 168px) minmax(1px, 120px);
grid-template-columns: auto minmax(1px, 1fr) minmax(1px, 1.8fr);
grid-row-gap: 6px;
grid-column-gap: 13px;
grid-template-areas:
'toolbar department-grid webapps-grid'
'detail department-grid webapps-grid '
'detail attribute-grid webapps-grid';
z-index: 10;
position: relative; //otherwise z-index is not working
}
.DetailsContent {
grid-area: detail;
}
.DepartmentList {
grid-area: department-grid;
}
.WebAppList {
grid-area: webapps-grid;
}
.AttributeList {
grid-area: attribute-grid;
}
.abstand {
margin-right: 1px !important;
}
.toolbar {
grid-area: toolbar;
}
.card {
padding: 12px;
margin: 2px 2px 2px 2px;
}

View File

@ -0,0 +1,71 @@
import { AfterViewInit, Component, HostListener, Input } from '@angular/core';
import { LoginPopupComponent } from '@app_core/components/login/login-popup/login-popup.component';
import { EN_EntityBeforeSaveCallBackResults } from '@app_core/consts';
import { AuthorizeService } from '@app_core/services/authorize.service';
import { PageLoadingService } from '@app_core/services/pageloading.service';
import { Employee } from '@app_models/employee';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
@Component({
selector: 'app-employee-detail',
templateUrl: './employee-detail.component.html',
styleUrls: ['./employee-detail.component.scss'],
})
export class EmployeeDetailComponent implements AfterViewInit {
@Input() public employeeDetails: AppBaseEntityWrapper<Employee>;
constructor(
public employeeDataService: EmployeeDataService,
public authorizeService: AuthorizeService,
private pageLoadingService: PageLoadingService,
) {
}
public get focusedItem(): Employee {
return this.employeeDetails.entity;
}
ngAfterViewInit(): void {
this.employeeDetails.beforeNewCallBack = this.newEmployeeInit.bind(this);
this.employeeDetails.beforeSaveCallBack = this.beforeSaveEmployee.bind(this);
}
newEmployeeInit(employee: Employee) {
/* employee.createdOn = new Date();
employee.createdBy = this.authorizeService.user.userCode;
employee.changedOn = employee.createdOn;
employee.changedBy = employee.createdBy;*/
}
async beforeSaveEmployee(employee: Employee): Promise<EN_EntityBeforeSaveCallBackResults> {
this.employeeDataService.employee2AttributeList.match2array(employee, 'employeeId', 'arAttributeIdList', 'employeeAttributeId');
return EN_EntityBeforeSaveCallBackResults.Ok_Continue;
}
public save = () => { //because of calling as callback of cancel
this.pageLoadingService.startSpinner();
this.employeeDetails.save((employee: Employee) => this.afterSave(employee), null, () => this.pageLoadingService.hideSpinner());
}
afterSave(employee: Employee) {
// this.employeeDetails.loadDetails(this.catalogDataService.employeeCategoryList);
}
public cancel = () => {
// this.setCurrentMaster4Subemployee = null;
this.employeeDetails.cancelWithSave(this.save);
}
public add(){
this.employeeDetails.new();
this.employeeDetails.entity.isActive = true;
}
@HostListener('window:keydown', ['$event'])
keyboardInput(event: any) {
return LoginPopupComponent.LOGIN_IS_SHOWN || this.employeeDetails.shortcutsHandler(event, this.save, this.cancel);
}
}

View File

@ -0,0 +1,96 @@
<div class="employee-webapp">
<label class="label">Webapp Liste</label>
<section class="toolbar">
<div class="toolbar-button-row">
<div class="toolbar-flex-container">
<button mat-icon-button title="Hinzufügen" (click)=addItem()
[disabled]="!this.employeeDetails.entity.isActive || !employeeDetails.inNotViewMode || !employeeWebappList.focusedEntityShadowed.enabledNewButton()">
<mat-icon>add</mat-icon>
</button>
<button mat-icon-button title="Bearbeiten" (click)=editItem()
[disabled]="!this.employeeDetails.entity.isActive || !employeeDetails.inNotViewMode || !employeeWebappList.focusedItem || !employeeWebappList.focusedEntityShadowed.enabledEditButton()">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button title="Löschen" class="toolbar-button-last" (click)=employeeWebappList.focusedEntityShadowed.delete()
[disabled]="!this.employeeDetails.entity.isActive
|| !employeeDetails.inNotViewMode
|| !employeeWebappList.focusedItem
|| !employeeWebappList.focusedEntityShadowed.enabledDeleteButton()">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</section>
<dx-data-grid
name="webappGrid"
[class]="'grid'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="employeeWebappList.items"
keyExpr="entityId"
[(focusedRowIndex)]=employeeWebappList.focusedItemGridIndex
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowKey)]="employeeWebappList.focusedItemId"
[(selectedRowKeys)]="employeeWebappList.selectedItemIds"
[showBorders]="true"
[allowColumnResizing]="true"
columnResizingMode="widget"
noDataText="Keine Daten sind vorhanden"
[renderAsync]="true"
(onRowDblClick)="dblClick($event)"
(onKeyDown)="$event.handled = $event.event.key==='Escape'; employeeWebappList.gridUpDownProcessing($event)"
(onExporting)="globals.doExcelExportSimple($event, employeeWebappList.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<!-- <dxo-header-filter [visible]="true"></dxo-header-filter> -->
<dxo-filter-row [visible]="true"></dxo-filter-row>
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple"></dxo-sorting>
<dxo-selection mode="none"></dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf="column.visible">
<dxi-column
[allowHeaderFiltering]=column.allowHeaderFiltering
[dataField]=column.dataField
[dataType]=column.dataType
[caption]=column.caption
[width]=column.width
[sortIndex]=column.sortIndex
[sortOrder]=column.sortOrder
[visibleIndex]=column.visibleIndex
[visible]=column.visible
[fixed]=column.fixed
[headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate
[allowSorting]=column.allowSorting
[calculateCellValue]=column.calculateCellValue
[calculateDisplayValue]=column.calculateDisplayValue
[calculateGroupValue]=column.calculateGroupValue
[calculateSortValue]=column.calculateSortValue
[alignment]=column.alignment>
<dxo-format *ngIf="column.dataType == 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType == 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
<!-- <dxo-lookup *ngIf="column.dataField === 'webAppId'" [dataSource]="{store:appDataService.webAppList?.items, sort:'webAppName'}" valueExpr="entityId" displayExpr="webAppName"></dxo-lookup> -->
<!-- <dxo-lookup *ngIf="column.dataField === 'webAppId'" [dataSource]="appDataService.webAppList?.items" valueExpr="entityId" displayExpr="webAppName"></dxo-lookup> -->
<!-- <dxo-lookup *ngIf="column.dataField === 'departmentId'" [dataSource]="appDataService.departmentList?.items" valueExpr="entityId" displayExpr="departmentName"></dxo-lookup>
<dxo-lookup *ngIf="column.dataField === 'webAppRoleId'" [dataSource]="appDataService.roleList?.items" valueExpr="entityId" displayExpr="webAppRoleName"></dxo-lookup> -->
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</div>

View File

@ -0,0 +1,24 @@
.employee-webapp {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: auto minmax(0, 1fr); // column relationship
grid-template-rows: auto minmax(1px, 1fr); // auto auto auto auto auto auto auto auto auto; // row relationships
// grid-template-areas: "label""toolbar""grid";
grid-template-areas: "label label" "toolbar grid";
}
.grid {
grid-area: grid;
height: 100%;
width: 100%;
}
.label {
grid-area: label;
// padding-bottom: 6px;
}
.toolbar {
grid-area: toolbar;
}

View File

@ -0,0 +1,124 @@
import * as keycode from '@angular/cdk/keycodes';
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { EN_EmployeeStatus } from '@app_consts';
import { ColumnConfig, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { Globals } from '@app_core/services/globals';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { MatDialogService } from '@app_core/services/mat-dialog.service';
import { Employee } from '@app_models/employee';
import { EmployeeToDepartment } from '@app_models/employee-to-department';
import { EmployeeToWebApp } from '@app_models/employee-to-webapp';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { EmployeeToWebAppPopupeditComponent } from './employee-webapp.popupedit/employee-webapp.popupedit.component';
@Component({
selector: 'app-employee-webapp',
templateUrl: './employee-webapp.component.html',
styleUrls: ['./employee-webapp.component.scss']
})
export class EmployeeWebappComponent implements OnInit {
public get employeeWebappList(): AppBaseEntityListWrapper<EmployeeToWebApp> {
return this.employeeDataService.employee2WebAppList;
}
@Input() public employeeDetails: AppBaseEntityWrapper<Employee>;
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
private dialog: MatDialogService,
public employeeDataService: EmployeeDataService,
public appDataService: AppDataService,
public localeService: LocaleService,
public globals: Globals,
) {
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
if (this.appDataService.showIds) col = this.columnConfigList.addNumber('Id', 'entityId', 7, 0);
col = this.columnConfigList.add('Webapp', 'webAppName', 15);
// col = this.columnConfigList.addId('Webapp', 'webAppId', 15);
// col.calculateSortValue = 'webAppName';
col.sortIndex = 1;
col.sortOrder = EN_SortOrder.asc;
col = this.columnConfigList.add('Abteilung', 'departmentName', 20);
// col = this.columnConfigList.add('Abteilung', 'departmentId', 20);
// col.calculateSortValue = 'departmentName';
col = this.columnConfigList.add('Role', 'webAppRoleName', 15);
// col = this.columnConfigList.add('Role', 'webAppRoleId', 15);
// col.calculateSortValue = 'webAppRoleName';
col = this.columnConfigList.add('Zusatzabteilungen', 'extendedDepartmentNameList', 30);
col = this.columnConfigList.add('Zusatzrolen', 'additionalRoleNameList', 30);
this.columnConfigList.recalcWidth();
}
addItem() {
this.employeeWebappList.focusedEntityShadowed.new();
this.employeeWebappList.focusedEntityShadowed.entity.employeeId = this.employeeDetails.entityId;
if (this.employeeDataService.employee2DepartmentList.items.length > 0) {
let mainDepId: number = 0;
const addDepIds: number[] = [];
let addDepNames = '';
this.employeeDataService.employee2DepartmentList.items.forEach((e2d: EmployeeToDepartment) => {
if (e2d.employeeStatusId === EN_EmployeeStatus.MainFunction) {
if (mainDepId === 0) mainDepId = e2d.departmentId;
} else {
addDepIds.push(e2d.departmentId);
const depname = this.appDataService.departmentList?.items.find(dep => dep.entityId === e2d.departmentId)?.departmentName;
addDepNames += depname ? depname + ', ' : '';
}
});
if (mainDepId > 0) this.employeeWebappList.focusedEntityShadowed.entity.departmentId = mainDepId;
this.employeeWebappList.focusedEntityShadowed.entity.arExtendedDepartmentIdList = addDepIds;
this.employeeWebappList.focusedEntityShadowed.entity.extendedDepartmentNameList = addDepNames.slice(0, -2);
}
this.openPopupDialog(this.employeeWebappList.focusedEntityShadowed);
}
dblClick(event: any) {
if (!this.employeeDetails.entityIsLoading) this.employeeWebappList.focusedEntityShadowed.delayedEdit(event.data['entityId'], this.editItem);
}
editItem = () => {
this.employeeWebappList.focusedEntityShadowed.edit();
this.openPopupDialog(this.employeeWebappList.focusedEntityShadowed);
}
public openPopupDialog(editEntity: AppBaseEntityWrapper<EmployeeToWebApp>) {
this.employeeDetails.keyDownListenerStopped = true;
this.dialog.openDialog(EmployeeToWebAppPopupeditComponent, {
baseEntityWrapper: editEntity,
keyItemName: this.employeeDetails.entity.firstName + ' ' + this.employeeDetails.entity.lastName,
readOnly: !this.employeeDetails.inNotViewMode || !this.employeeDetails.entity.isActive,
description: (editEntity.entity.entityId === 0 ? 'Neue Webbapp zuweisen' : 'Daten zur Webbapp "' + editEntity.entity.webAppName + '" bearbeiten '),
list: this.employeeWebappList,
}, true, () => this.employeeDetails.keyDownListenerStopped = false);
}
@HostListener('keydown', ['$event'])
onKeyDown(event: any) {
switch (event. which) {
case keycode.ENTER:
return false;
}
return true;
}
}

View File

@ -0,0 +1,113 @@
<form class="PopupContainer" #popupForm="ngForm">
<!-- <dx-button class="buttonLayoutCancel" icon="close" (click)="close()"></dx-button> -->
<h2 mat-dialog-title class="messageBoxHeader">{{description}} - {{keyItemName}} </h2>
<mat-dialog-content class="PopupContent">
<div class="buttons">
<button id="btnSave" name="btnSave" class="btnSave" mat-stroked-button [disabled]="!baseEntityWrapper.enabledSaveButton() || readOnly"
(click)="save()">
<mat-icon>save</mat-icon> Übernehmen
</button>
<button #btnCancel id="btnCancel" name="btnCancel" class="btnCancel" mat-stroked-button (click)="cancel()">
<mat-icon>cancel</mat-icon> Abbrechen
</button>
</div>
<hensel-selection
#input_webapp
id="input-webapp"
class="input-webapp"
[dataSource]=appDataService.webAppList.items
placeholder="Webapp"
keyExpr="entityId"
displayExpr="webAppName"
sortField="*"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.webAppId"
(selectedItemsChange)="loadRoles()"
[disabled]="readOnly || focusedItem?.entityId !== 0"
[customFilterFn]="getWebapps"
required>
</hensel-selection>
<hensel-selection
#input_department
id="input-department"
class="input-department"
[dataSource]=appDataService.departmentList.items
placeholder="Abteilung"
keyExpr="entityId"
displayExpr="departmentName"
sortField="*"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.departmentId"
[customFilterFn]="filterDepartments"
[disabled]="readOnly"
required>
</hensel-selection>
<hensel-selection
#input_role
id="input-role"
class="input-role"
[dataSource]=webAppRoleList.items
placeholder="Role"
keyExpr="webAppRoleId"
displayExpr="webAppRoleName"
sortField="webAppRoleHierarchy"
[multiSelect]=false
separator=","
[(selectedItems)]="focusedItem.webAppRoleId"
[disabled]=readOnly
[showSpinner]="webAppRoleList.isLoading"
required>
</hensel-selection>
<hensel-selection
#input_adddepartment
id="input-adddepartment"
class="input-adddepartment"
[dataSource]=appDataService.departmentList.items
placeholder="Zusatzabteilungen"
keyExpr="entityId"
displayExpr="departmentName"
sortField="*"
[multiSelect]=true
separator=","
[customFilterFn]="filterDepartments"
[(selectedItems)]="focusedItem.arExtendedDepartmentIdList"
(selectedItemsDisplayChange)="focusedItem.extendedDepartmentNameList = $event"
[disabled]="readOnly"
[height]="'100%'"
[useResultGrid]=true
>
</hensel-selection>
<hensel-selection
#input_addrole
*ngIf="!focusedItem.isNew() && focusedItem.additionalRoleIdList !== null || focusedItem.isNew() && additionalRoleList.items.length>0"
id="input-addrole"
class="input-addrole"
[dataSource]=additionalRoleList.items
placeholder="Zusatzrole"
keyExpr="entityId"
displayExpr="webAppAdditionalRoleName"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="focusedItem.arAdditionalRoleIdList"
(selectedItemsDisplayChange)="focusedItem.additionalRoleNameList = $event"
[disabled]="readOnly"
[height]="'100%'"
[width]="300"
[useResultGrid]=true
[showSpinner]="webAppRoleList.isLoading"
>
</hensel-selection>
</mat-dialog-content>
</form>

View File

@ -0,0 +1,38 @@
@import 'popup-base.component';
.PopupContent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid: ;
height: 100%; // 100% view height
grid-template-rows: repeat(3, 42px) auto; // row relationships
grid-template-columns: repeat(2, minmax(1px, 300px)) auto; // column relationship
grid-column-gap: 15px;
grid-row-gap: 15px;
grid-template-areas:
'input-webapp input-adddepartment input-addrole'
'input-department input-adddepartment input-addrole'
'input-role input-adddepartment input-addrole'
'buttons buttons buttons'
}
.input-department {
grid-area: input-department;
}
.input-adddepartment {
grid-area: input-adddepartment;
}
.input-addrole {
grid-area: input-addrole;
}
.input-webapp {
grid-area: input-webapp;
}
.input-role {
grid-area: input-role;
}

View File

@ -0,0 +1,70 @@
import { Component } from '@angular/core';
import { EN_AppEntities } from '@app_consts';
import { PopupBaseComponent } from '@app_core/components/popup-base/popup-base.component';
import { Department } from '@app_models/basedata/department';
import { WebApp } from '@app_models/basedata/webapp';
import { WebAppToWebAppRole } from '@app_models/basedata/webapp-to-webapprole';
import { WebAppAdditionalRole } from '@app_models/basedata/webappadditionalrole ';
import { EmployeeToWebApp } from '@app_models/employee-to-webapp';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
@Component({
selector: 'app-employee-webapp-popupedit',
templateUrl: 'employee-webapp.popupedit.component.html',
styleUrls: ['employee-webapp.popupedit.component.scss']
})
export class EmployeeToWebAppPopupeditComponent extends PopupBaseComponent {
public webAppRoleList: AppBaseEntityListWrapper<WebAppToWebAppRole> = new AppBaseEntityListWrapper<WebAppToWebAppRole>(WebAppToWebAppRole, 'Applikation Rolenliste', EN_AppEntities.WebAppToWebAppRole);
public additionalRoleList: AppBaseEntityListWrapper<WebAppAdditionalRole> =
new AppBaseEntityListWrapper<WebAppAdditionalRole>(WebAppAdditionalRole, 'Applikation Rolenliste', EN_AppEntities.WebAppAdditionalRole);
public departmentIdList: number[] = this.employeeService.employee2DepartmentList.items.map(e2d => e2d.departmentId);
constructor(
public appDataService: AppDataService,
public employeeService: EmployeeDataService,
) {
super();
this.webAppRoleList.filter = this.appDataService.webAppFilter;
this.additionalRoleList.filter = this.appDataService.webAppFilter;
this.loadRoles();
}
loadRoles() {
if (this.focusedItem.webAppId) {
this.appDataService.webAppFilter.webAppId = this.focusedItem.webAppId;
this.webAppRoleList.load();
if (this.focusedItem.isNew() || this.focusedItem.additionalRoleIdList !== null) this.additionalRoleList.load();
} else {
this.webAppRoleList.clear();
this.additionalRoleList.clear();
}
}
public get focusedItem(): EmployeeToWebApp {
return <EmployeeToWebApp>(this._focusedItem);
}
getWebapps = (wa: WebApp) => {
return this.focusedItem.entityId !== 0 || !this.list.items.find((item) => item['webAppId'] === wa.entityId) && wa.isActive;
}
public filterDepartments = (department: Department) => {
return this.departmentIdList.indexOf(department.departmentId) > -1;
}
public save = () => {
this.focusedItem.webAppName = this.appDataService.webAppList?.getItemById(this.focusedItem.webAppId)?.webAppName;
this.focusedItem.departmentName = this.appDataService.departmentList?.getItemById(this.focusedItem.departmentId)?.departmentName;
this.focusedItem.webAppRoleName = this.appDataService.roleList?.getItemById(this.focusedItem.webAppRoleId)?.webAppRoleName;
this.baseEntityWrapper.save(() => this.close(true));
}
}

View File

@ -0,0 +1,153 @@
<mat-card class="card">
<mat-card-header>
</mat-card-header>
<mat-card-content class="filterCardContainer">
<hensel-input class="filterName"
title="Suche in Nachnamen, Vornamen"
placeholder="Vor- oder Nachname"
[(ngModel)]=filterObject.name
(keyup.Enter)="onFilterClick()"
suffix="---">
</hensel-input>
<hensel-input class="filterShortName"
placeholder="Kürzel"
[(ngModel)]=filterObject.shortName
(keyup.Enter)="onFilterClick()">
</hensel-input>
<hensel-input class="filterLogin"
placeholder="Login"
[(ngModel)]=filterObject.loginName
(keyup.Enter)="onFilterClick()">
</hensel-input>
<!--
<hensel-input class="filterEmail"
placeholder="Email"
[(ngModel)]=filterObject.email
(keyup.Enter)="onFilterClick()">
</hensel-input> -->
<hensel-selection
id="filterDepartment"
class="filterDepartment"
[dataSource]=appDataService.departmentList.items
placeholder="Abteilung"
keyExpr="entityId"
displayExpr="departmentName"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="filterObject.departmentIds"
(selectedItemsChange)="onFilterClick()">
</hensel-selection>
<hensel-selection
id="filterWebApp"
class="filterWebApp"
[dataSource]=appDataService.webAppList.items
placeholder="Webapp"
keyExpr="entityId"
displayExpr="webAppName"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="filterObject.webappIds"
(selectedItemsChange)="onFilterClick()">
</hensel-selection>
<hensel-selection
id="filterAttribute"
class="filterAttribute"
[dataSource]=appDataService.employeeAttributeList.items
placeholder="Merkmal"
keyExpr="entityId"
displayExpr="name"
sortField="*"
[multiSelect]=true
separator=","
[(selectedItems)]="filterObject.attributeIds"
(selectedItemsChange)="onFilterClick()">
</hensel-selection>
<!-- <mat-form-field class="filterEmployeeId">
<input
[leadZero]="true"
matInput
type="tet"
mask="separator.3"
[decimalMarker]="localeService.decimalSeparator"
[thousandSeparator]="''"
placeholder="MitarbeiterId"
[(ngModel)]=filterObject.employeeId
greater="0"
(keyup.Enter)="onFilterClick()">
</mat-form-field> -->
<!--
<hensel-input class="filterEmployeeId"
mask="separator.2"
[leadZero]="false"
[decimalMarker]="localeService.decimalSeparator"
[thousandSeparator]="localeService.thousandSeparator"
placeholder="MitarbeiterId"
[(ngModel)]=filterObject.employeeId
henselDecimal="2"
[henselDecimalAllowEmpty]="true"
(keyup.Enter)="onFilterClick()"
greater="0"
>
</hensel-input>
-->
<hensel-input class="filterEmployeeId"
mask="separator.0"
[leadZero]="true"
[decimalMarker]="localeService.decimalSeparator"
[thousandSeparator]="''"
placeholder="MitarbeiterId"
[(ngModel)]=filterObject.employeeId
(keyup.Enter)="onFilterClick()"
>
</hensel-input>
<hensel-selection
id="filterMandant"
class="filterMandant"
[dataSource]=appDataService.subsidiaryList.items
placeholder="Mandant"
keyExpr="clientId"
displayExpr="subsidiaryCode"
sortField="*"
[multiSelect]=false
separator=","
[(selectedItems)]="filterObject.clientId"
(selectedItemsChange)="onFilterClick()">
</hensel-selection>
<mat-checkbox
class="filterIsActive"
name="Enabled"
style="float: left"
labelPosition="after"
(change)="onFilterClick()"
[(ngModel)]="filterObject.isActive">
Aktiv
</mat-checkbox>
<section class="filterButtons mat-button-withoutcaption">
<button id="filterBtnClear" class="filterBtn" mat-stroked-button type=button (click)=onClearClick() title="Reset">
<mat-icon>clear</mat-icon>
</button>
<button id="filterBtnEmails" class="filterBtn" mat-stroked-button type=button (click)=onEmailClick() title="Emaildresse für ausgewählten Mitarbeiter in die Zwischenablage speichern">
<mat-icon>alternate_email</mat-icon>
</button>
<button id="filterBtnFind" class="filterBtn" mat-stroked-button type=submit (click)=onFilterClick() titel="Suchen">
<mat-icon>search</mat-icon>
</button>
</section>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,77 @@
.filterCardContainer {
padding: 0;
margin: 0;
overflow: hidden;
display: grid;
width: 100%;
height: 100%;
grid-column-gap: 15px;
grid-template-rows: repeat(1, auto);
grid-template-columns: repeat(8, minmax(0, 1fr)) auto auto;
grid-template-areas:
"filterName filterShortName filterLogin filterDepartment filterWebApp filterAttribute filterEmployeeId filterMandant filterIsActive filterButtons "
}
// @media screen and (max-width:1024px) {
// .woassays {
// font-size: 1vw;
// }
// }
.filterIsActive{
margin: auto;
grid-area: filterIsActive;
}
.filterMandant {
grid-area: filterMandant;
}
.filterName {
grid-area: filterName;
}
.filterShortName {
grid-area: filterShortName;
}
.filterLogin {
grid-area: filterLogin;
}
.filterWebApp {
grid-area: filterWebApp;
}
// .filterEmail {
// grid-area: filterEmail;
// }
.filterDepartment {
grid-area: filterDepartment;
}
.filterEmployeeId {
grid-area: filterEmployeeId;
}
.filterAttribute {
grid-area: filterAttribute;
}
.filterButtons {
grid-area: filterButtons;
margin-left: auto;
}
// .filterBtn {
// font-size: 1vw; //scales buttons caption depended from the current view port resolution
// }
.card {
padding: 10px;
margin: 2px 2px 0px 2px;
}
.filterButtons .filterBtn {
width: unset;
}

View File

@ -0,0 +1,85 @@
import * as keycode from '@angular/cdk/keycodes';
import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core';
import { Employee, EmployeeFullFilter } from '@app_models/employee';
import { EmployeeDataService } from '@app_modules/employee/employee-data.service';
import { AppBaseEntityWrapper } from '@app_services/app.baseentity.wrapper';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import * as Cnst from '@app_core/consts';
import { ClipboardService } from 'ngx-clipboard';
import { AuthorizeService } from '@app_core/services/authorize.service';
import { UINotifierService } from '@app_core/services/notification/uinotifier.service';
import { LoginPopupComponent } from '@app_core/components/login/login-popup/login-popup.component';
import { LocaleService } from '@app_core/services/localization/locale.service';
@Component({
selector: 'app-employee-filter',
templateUrl: './employee-filter.component.html',
styleUrls: ['./employee-filter.component.scss'],
})
export class EmployeeFilterComponent implements OnInit, AfterViewInit {
constructor(
public employeeDataService: EmployeeDataService,
public appDataService: AppDataService,
public authService: AuthorizeService,
public localeService: LocaleService,
private clipboardService: ClipboardService,
private notifierService: UINotifierService,
) { }
@Input() public filterObject: EmployeeFullFilter;
@Input() employeeDetails: AppBaseEntityWrapper<Employee>;
@Input() employeeList: AppBaseEntityListWrapper<Employee>;
@Output() public filterFetch = new EventEmitter();
public dropDownAllCaption = Cnst.EN_DropDownConst4Filter.AllCaption;
public dropDownAllValueString = Cnst.EN_DropDownConst4Filter.AllValueString;
public dropDownAllValueInt = Cnst.EN_DropDownConst4Filter.AllValueInt;
public allCaption = Cnst.EN_DropDownConst4Filter.AllCaption;
public allValue: number = Cnst.EN_DropDownConst4Filter.AllValueInt;
public noneCaption = Cnst.EN_DropDownConst4Filter.NoneCaption;
public noneValue = Cnst.EN_DropDownConst4Filter.NoneValueInt;
ngOnInit() {
}
ngAfterViewInit() { }
onFilterClick() {
if (this.employeeDetails.inViewMode) this.filterFetch.emit();
}
onClearClick() {
this.filterObject.filterReset();
this.filterObject.isActive = true;
this.onFilterClick();
}
onEmailClick() {
const listEmployeeIds = this.employeeList.items.filter((empl: Employee) => !!empl.email).map((empl: Employee) => empl.entityId);
const listEmails = this.employeeList.items.sort((empl1, empl2) => empl1.lastName.localeCompare(empl2.lastName)).map((empl: Employee) => empl.email).filter(email => !!email);
if (listEmails.length === 0) return;
this.clipboardService.copy(listEmails.join('; '));
const endung = (listEmails.length > 1 ? 'n' : '');
this.notifierService.showMessage(`${listEmails.length} Emailadresse${endung} wurde${endung} in die Zwischenablage gespeichert`);
}
@HostListener('window:keydown', ['$event'])
keyboardInput(event: any) {
if (!LoginPopupComponent.LOGIN_IS_SHOWN && !this.employeeDetails.keyDownListenerStopped) {
if (event.ctrlKey) {
switch (event.which) {
case keycode.F: //Ctrl + 'f'
event.stopPropagation();
this.onFilterClick();
return false; //processed
}
}
}
return true; //default processing
}
}

View File

@ -0,0 +1,86 @@
<div class="EmployeeList">
<app-employee-filter class="FilterComponent"
[employeeDetails]=this.employeeDataService.employeeDetails
[employeeList]=this.employeeDataService.employeeList
[filterObject]=dataSource.filter
(filterFetch)="loadData()">
</app-employee-filter>
<mat-card class="card">
<mat-card-header>
</mat-card-header>
<mat-card-content>
<dx-data-grid
#gridEmployee
id="gridEmployee"
[class]="'gridEmployee'+(columnConfigList.withExport ? ' withexport' : '')"
[dataSource]="dataSource.items"
keyExpr="employeeId"
[focusedRowEnabled]="true"
[autoNavigateToFocusedRow]="true"
[(focusedRowIndex)]=dataSource.focusedItemGridIndex
[(focusedRowKey)]="dataSource.focusedItemId"
[(selectedRowKeys)]="dataSource.selectedItemIds"
[showBorders]="true"
[allowColumnResizing]="true"
columnResizingMode="nextColumn"
noDataText="Keine Daten vorhanden"
(onKeyDown)="$event.handled = $event.event.key==='Escape'; dataSource.gridUpDownProcessing($event)"
(onFocusedRowChanging)="dataSource.onFocusedRowChanging($event)"
(onRowDblClick)="dataSource.focusedEntityShadowed.delayedEdit($event.data['entityId'])"
(onExporting)="globals.doExcelExportSimple($event, dataSource.getExportFilename4List())">
<dxo-export [enabled]="columnConfigList.withExport" [texts]="{exportAll: 'Excel Export'}"></dxo-export>
<dxo-header-filter [visible]="false"></dxo-header-filter>
<dxo-filter-row [visible]="false" [applyFilter]="true"></dxo-filter-row>
<dxo-paging [enabled]="false"></dxo-paging>
<dxo-scrolling mode="virtual"></dxo-scrolling>
<dxo-load-panel [enabled]="true"></dxo-load-panel>
<dxo-sorting mode="multiple">
<!--"single" | "multiple" | "none" -->
</dxo-sorting>
<dxo-selection mode="none">
<!-- "multiple" | "none" -->
</dxo-selection>
<ng-container *ngFor="let column of columnConfigList.columns">
<ng-container *ngIf=column.visible>
<dxi-column
[allowHeaderFiltering]=column.allowHeaderFiltering
[dataField]=column.dataField
[dataType]=column.dataType
[caption]=column.caption
[width]=column.width
[sortIndex]=column.sortIndex
[sortOrder]=column.sortOrder
[visibleIndex]=column.visibleIndex
[visible]=column.visible
[fixed]=column.fixed
[headerCellTemplate]=column.headerCellTemplate
[cellTemplate]=column.cellTemplate
[alignment]=column.alignment>
<!-- cssClass="headeralignment_center" -->
<dxo-format *ngIf="column.dataType === 'date'" [type]=localeService.dateFormat></dxo-format>
<dxo-format *ngIf="column.dataType === 'datetime'" [type]=localeService.dateTimeFormat></dxo-format>
<dxo-lookup *ngIf="column.dataField === 'rangId'" [dataSource]="appDataService.rangList?.items" valueExpr="entityId" displayExpr="rangShortname"></dxo-lookup>
</dxi-column>
</ng-container>
</ng-container>
<div *dxTemplate="let cell of 'numberTemplate_0'">
{{cell.value | number:'1.0-0':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_1'">
{{cell.value | number:'1.1-1':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_2'">
{{cell.value | number:'1.2-2':culture}}
</div>
<div *dxTemplate="let cell of 'numberTemplate_3'">
{{cell.value | number:'1.3-3':culture}}
</div>
</dx-data-grid>
</mat-card-content>
</mat-card>
<div *ngIf="this.employeeDataService.employeeDetails.inNotViewMode" class="disableClicks"></div>
</div>

View File

@ -0,0 +1,38 @@
.EmployeeList {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-template-rows: auto minmax(0, 1fr); // row relationships
grid-template-columns: minmax(0, 1fr); // column relationship
grid-row-gap: 7px;
grid-template-areas:
'filter'
'grid';
}
.FilterComponent {
grid-area: filter; // grid area: auto height 1fr width
margin: 0 0 0 0;
}
.card {
grid-area: grid;
width: 100%;
padding: 0px;
margin: 0;
border-radius: 0px;
box-shadow: none;
}
.gridEmployee {
position: absolute; // position absolut ist hier zwingend! Die position ist absolut innerhalb mehrerer parent divs und darf sich NICHT über diese hinausstrecken.
height: 100%;
width: 100%;
padding-left: 1px;
padding-right: 1px;
padding-bottom: 1px;
}

View File

@ -0,0 +1,80 @@
import { Component, Input, OnInit } from '@angular/core';
import { ColumnConfigList } from '@app_core/components/grid-config/columnconfiglist';
import { Globals } from '@app_core/services/globals';
import { LocaleService } from '@app_core/services/localization/locale.service';
import { Employee } from '@app_models/employee';
import { AppBaseEntityListWrapper } from '@app_services/app.baseentitylist.wrapper';
import { AppDataService } from '@app_services/app.data.service';
import { EmployeeDataService } from '../employee-data.service';
import { ColumnConfig, EN_Alignment, EN_SortOrder } from '@app_core/components/grid-config/columnconfig';
@Component({
selector: 'app-employee-list',
templateUrl: './employee-list.component.html',
styleUrls: ['./employee-list.component.scss']
})
export class EmployeeListComponent implements OnInit {
@Input() public dataSource: AppBaseEntityListWrapper<Employee>;
// @ViewChild('gridCatalog') dxGrid: DxDataGridComponent;
public columnConfigList: ColumnConfigList = new ColumnConfigList();
constructor(
public employeeDataService: EmployeeDataService,
public appDataService: AppDataService,
public localeService: LocaleService,
public globals: Globals,
) {
}
ngOnInit() {
this.initColumns();
}
initColumns() {
let col: ColumnConfig;
// col = this.columnConfigList.addNumber('id', 'EmployeeId', 7);
if (this.appDataService.showIds) col = this.columnConfigList.addNumber('Id', 'entityId', 7, 0);
if (this.appDataService.showIds) col = this.columnConfigList.add('Nr', 'employeeNo', 7);
col = this.columnConfigList.add('Nachname', 'lastName', 25);
col.sortIndex = 1;
col.sortOrder = EN_SortOrder.asc;
col = this.columnConfigList.add('Vorname', 'firstName', 15);
col.sortIndex = 2;
col.sortOrder = EN_SortOrder.asc;
// col = this.columnConfigList.add('Anrede', 'salutation', 10);
col = this.columnConfigList.add('Kürzel', 'shortName', 7);
col = this.columnConfigList.add('Position', 'position', 25);
col = this.columnConfigList.add('Login', 'loginName', 15);
col = this.columnConfigList.add('Email', 'email', 30);
col = this.columnConfigList.add('Rang', 'rangId', 10);
col = this.columnConfigList.add('Handynummer', 'mobilePhoneNo', 20);
col = this.columnConfigList.add('Festnetznummer', 'phoneNo', 20);
// if (this.appDataService.showIds) col =
col = this.columnConfigList.add('Department', 'departmentNamesList', 30);
col = this.columnConfigList.add('Webapps', 'webappNamesList', 25);
col = this.columnConfigList.add('Merkmale', 'attributeNamesList', 20);
// this.columnConfigList.addNumber('Firma-Id', 'clientId', 7);
col = this.columnConfigList.add('Mandant', 'mandantCode', 15);
col = this.columnConfigList.addBoolean('Aktiv', 'isActive', -50);
col.alignment = EN_Alignment.left;
this.columnConfigList.recalcWidth();
}
public loadData() {
this.employeeDataService.loadData();
}
}

View File

@ -0,0 +1,4 @@
<div class="EmployeeComponent">
<app-employee-list [dataSource]=employeeDataService.employeeList class="EmployeeListComponent"></app-employee-list>
<app-employee-detail [employeeDetails]="employeeDataService.employeeList.focusedEntityShadowed" class="EmployeeDetailsComponent"></app-employee-detail>
</div>

View File

@ -0,0 +1,22 @@
.EmployeeComponent {
padding: 0 0 0; // no extra padding -> padding is calculated in the child comp
margin: 0; // see above
overflow: hidden; // overflow hidden => all "to big" child components are hidden
display: grid; // we define a grid
width: 100%; // 100 % view width (hardware screen)
height: 100%; // 100% view height
grid-template-rows: minmax(0, 1fr) auto; // row relationships
grid-template-columns: minmax(0, 1fr); // column relationship
grid-template-areas: 'list''details';
grid-column-gap: 5px;
grid-row-gap: 5px;
}
.EmployeeListComponent {
grid-area: list;
}
.EmployeeDetailsComponent {
grid-area: details;
z-index: 10;
}

View File

@ -0,0 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { EmployeeDataService } from './employee-data.service';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.scss']
})
export class EmployeeComponent implements OnInit {
constructor(
public employeeDataService: EmployeeDataService
) { }
ngOnInit() {
}
}

View File

@ -0,0 +1,59 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AngularMaterialModule } from '@app_core/components/angular-material.module';
import { DxButtonModule, DxDataGridModule, DxFileUploaderModule, DxPieChartModule, DxTemplateModule, DxTooltipModule } from 'devextreme-angular';
import { EmployeeAttributeComponent } from './employee-detail/employee-attribute/employee-attribute.component';
import { EmployeeContentComponent } from './employee-detail/employee-content/employee-content.component';
import { EmployeeDepartmentComponent } from './employee-detail/employee-department/employee-department.component';
import { EmployeeToDepartmentPopupeditComponent } from './employee-detail/employee-department/employee-department.popupedit/employee-department.popupedit.component';
import { EmployeeDetailComponent } from './employee-detail/employee-detail.component';
import { EmployeeWebappComponent } from './employee-detail/employee-webapp/employee-webapp.component';
import { EmployeeToWebAppPopupeditComponent } from './employee-detail/employee-webapp/employee-webapp.popupedit/employee-webapp.popupedit.component';
import { EmployeeFilterComponent } from './employee-list/employee-filter/employee-filter.component';
import { EmployeeListComponent } from './employee-list/employee-list.component';
import { EmployeeComponent } from './employee.component';
import { EmployeeRoutingModule } from './employee.routing.module';
import { HenselSelectionComponent } from '@app_core/components/hensel-selection/hensel-selection.component';
import { HenselInputComponent } from '@app_core/components/hensel-input/hensel-input.component';
import { NgxMaskDirective, NgxMaskPipe } from 'ngx-mask';
import { HenselMaskDecimalDirective } from '@app_core/directives/hensel-decimal.directive';
import { HenselValidator } from '@app_core/validators/hensel-validator.directive';
@NgModule({
declarations: [
EmployeeComponent,
EmployeeListComponent,
EmployeeContentComponent,
EmployeeAttributeComponent,
EmployeeDetailComponent,
EmployeeFilterComponent,
EmployeeWebappComponent,
EmployeeDepartmentComponent,
EmployeeToDepartmentPopupeditComponent,
EmployeeToWebAppPopupeditComponent
],
imports: [
CommonModule,
EmployeeRoutingModule,
AngularMaterialModule,
DxDataGridModule,
FormsModule,
HenselSelectionComponent,
HenselInputComponent,
HenselMaskDecimalDirective,
HenselValidator,
DxFileUploaderModule,
DxButtonModule,
DxTooltipModule,
DxTemplateModule,
DxPieChartModule,
NgxMaskDirective,
NgxMaskPipe,
],
providers: [],
exports: []
})
export class EmployeeModule { }

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { EmployeeComponent } from './employee.component';
import { AuthGuard } from '@app_core/services/authguard';
const routes: Routes = [
{
path: '**',
component: EmployeeComponent,
canActivate: [AuthGuard]
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class EmployeeRoutingModule { }

View File

@ -0,0 +1,97 @@
import { IENVIRONMENT } from '@app_core/injection-tokens';
import { ACCOUNT_PAGE, IAppPage, LOGIN_PAGE } from '@app_core/services/globals';
export const SUPPORTED_LANGUAGES = ['de', 'en'];
declare const environment: IENVIRONMENT;
export const APP_TITLE: string = environment['APP_TITLE'] ?? 'StaffDB';
export const cnst_DefaultCulture = environment.culture ?? 'de-DE';
export const cnst_DefaultLanguage = environment.language ?? 'de';
// page description, wich is used for routing as well for preparing routings menus and intern for page loading
export const APP_PAGES: IAppPage[] = [
{
caption: 'Mitarbeiter',
path: 'employee',
loadChildren: () => import('@app_modules/employee/employee.module').then(m => m.EmployeeModule),
icon: '',
messageCaption: 'Employee',
},
{
caption: 'Abteilungen',
path: 'departments',
loadChildren: () => import('@app_modules/department/department.module').then(m => m.DepartmentModule),
icon: '',
messageCaption: 'Department',
},
ACCOUNT_PAGE,
LOGIN_PAGE,
];
// named constants to access the arry APP_PAGES
export const enum EN_AppPages {
Employee = 0,
Department = 1,
Settings = 2,
Login = 3,
}
// named constants with numbers of http requests of data loading for every page for maintain of spinner
export const enum EN_HttpQueriesCount4Page {
Employee = 1,
Department = 1,
Login = 10, //no of quires in load stammdata
}
export function getPageIndex(page: string): number {
return APP_PAGES.findIndex((pg) => pg.path === page);
}
export enum EN_AppEntities {
//-- base data entities
Employee = 'employee',
CostCentre = 'costcentre',
Vendor = 'vendor',
EmployeeStatus = 'employeestatus',
Rang = 'rang',
DocumentArt = 'documentart',
Project = 'project',
Department = 'department',
WindreamSearch = 'windreamsearch',
WindreamIndex = 'windreamindex',
WindreamIndexToWindreamSearchToDepartment = 'windreamindextowindreamsearchtodepartment',
WindreamSearchToDepartment = 'windreamsearchtodepartment',
WindreamSearchItemToWindreamSearchToDepartment = 'windreamsearchitemtowindreamsearchtodepartment',
WindreamSearchItem = 'windreamsearchitem',
DocumentArtToDepartment = 'documentarttodepartment',
EmployeeToDepartment = 'employeetodepartment',
EmployeeToWebapp = 'employeetowebapp',
WebApp = 'webapp',
WebAppRole = 'webapprole',
WebAppToDepartment = 'webapptodepartment',
WebAppToWebAppRole = 'webapptowebapprole',
EmployeeAttribute = 'employeeattribute',
EmployeeToAttribute = 'employeetoattribute',
WebAppAdditionalRole = 'webappadditionalrole',
WebAppToWebAppAdditionalRole = 'webapptowebappadditionalrole',
WindreamSearchToDepartmentCopyWindreamTile = 'windreamSearchToDepartment/CopyWindreamTile',
DepartmentCopyWindreamTiles = 'department/copyWindreamTiles',
Subsidiary = 'Subsidiary'
}
export enum EN_EmployeeStatus {
MainFunction = 1,
AdditionalFunction = 2,
}
export enum EN_EmployeeRang {
Mitarbeiter = 4,
}
export const cnst_LoadedEntitiesExpiresInMs = 30000; //30s

Some files were not shown because too many files have changed in this diff Show More