| @@ -0,0 +1 @@ | |||||
| /.idea | |||||
| @@ -0,0 +1,12 @@ | |||||
| # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. | |||||
| # For additional information regarding the format and rule options, please see: | |||||
| # https://github.com/browserslist/browserslist#queries | |||||
| # You can see what browsers were selected by your queries by running: | |||||
| # npx browserslist | |||||
| > 0.5% | |||||
| last 2 versions | |||||
| Firefox ESR | |||||
| not dead | |||||
| not IE 9-11 # For IE 9-11 support, remove 'not'. | |||||
| @@ -0,0 +1,13 @@ | |||||
| # Editor configuration, see https://editorconfig.org | |||||
| root = true | |||||
| [*] | |||||
| charset = utf-8 | |||||
| indent_style = space | |||||
| indent_size = 4 | |||||
| insert_final_newline = true | |||||
| trim_trailing_whitespace = true | |||||
| [*.md] | |||||
| max_line_length = off | |||||
| trim_trailing_whitespace = false | |||||
| @@ -0,0 +1,49 @@ | |||||
| # See http://help.github.com/ignore-files/ for more about ignoring files. | |||||
| # compiled output | |||||
| /dist | |||||
| /tmp | |||||
| /out-tsc | |||||
| # Only exists if Bazel was run | |||||
| /bazel-out | |||||
| # dependencies | |||||
| /node_modules | |||||
| # profiling files | |||||
| chrome-profiler-events*.json | |||||
| speed-measure-plugin*.json | |||||
| # IDEs and editors | |||||
| /.idea | |||||
| .project | |||||
| .classpath | |||||
| .c9/ | |||||
| *.launch | |||||
| .settings/ | |||||
| *.sublime-workspace | |||||
| # IDE - VSCode | |||||
| .vscode/* | |||||
| !.vscode/settings.json | |||||
| !.vscode/tasks.json | |||||
| !.vscode/launch.json | |||||
| !.vscode/extensions.json | |||||
| .history/* | |||||
| # misc | |||||
| /.sass-cache | |||||
| /connect.lock | |||||
| /coverage | |||||
| /libpeerconnection.log | |||||
| npm-debug.log | |||||
| yarn-error.log | |||||
| testem.log | |||||
| /typings | |||||
| # System Files | |||||
| .DS_Store | |||||
| Thumbs.db | |||||
| # cache etc | |||||
| /.angular | |||||
| @@ -0,0 +1,84 @@ | |||||
| RewriteEngine on | |||||
| # Don't rewrite files or directories | |||||
| RewriteCond %{REQUEST_FILENAME} -f [OR] | |||||
| RewriteCond %{REQUEST_FILENAME} -d | |||||
| RewriteRule ^ - [L] | |||||
| # Rewrite everything else to index.html to allow html5 state links | |||||
| RewriteRule ^ index.html [L] | |||||
| # Enable Compression | |||||
| <IfModule mod_deflate.c> | |||||
| AddOutputFilterByType DEFLATE application/javascript | |||||
| AddOutputFilterByType DEFLATE application/rss+xml | |||||
| AddOutputFilterByType DEFLATE application/vnd.ms-fontobject | |||||
| AddOutputFilterByType DEFLATE application/x-font | |||||
| AddOutputFilterByType DEFLATE application/x-font-opentype | |||||
| AddOutputFilterByType DEFLATE application/x-font-otf | |||||
| AddOutputFilterByType DEFLATE application/x-font-truetype | |||||
| AddOutputFilterByType DEFLATE application/x-font-ttf | |||||
| AddOutputFilterByType DEFLATE application/x-javascript | |||||
| AddOutputFilterByType DEFLATE application/xhtml+xml | |||||
| AddOutputFilterByType DEFLATE application/xml | |||||
| AddOutputFilterByType DEFLATE application/atom_xml | |||||
| AddOutputFilterByType DEFLATE application/x-shockwave-flash | |||||
| AddOutputFilterByType DEFLATE font/opentype | |||||
| AddOutputFilterByType DEFLATE font/otf | |||||
| AddOutputFilterByType DEFLATE font/ttf | |||||
| AddOutputFilterByType DEFLATE image/svg+xml | |||||
| AddOutputFilterByType DEFLATE image/x-icon | |||||
| AddOutputFilterByType DEFLATE image/jpeg | |||||
| AddOutputFilterByType DEFLATE image/png | |||||
| AddOutputFilterByType DEFLATE text/css | |||||
| AddOutputFilterByType DEFLATE text/html | |||||
| AddOutputFilterByType DEFLATE text/javascript | |||||
| AddOutputFilterByType DEFLATE text/plain | |||||
| AddOutputFilterByType DEFLATE text/xml | |||||
| </IfModule> | |||||
| <IfModule mod_gzip.c> | |||||
| mod_gzip_on Yes | |||||
| mod_gzip_dechunk Yes | |||||
| mod_gzip_item_include file .(html?|txt|css|js|php|pl)$ | |||||
| mod_gzip_item_include handler ^cgi-script$ | |||||
| mod_gzip_item_include mime ^text/.* | |||||
| mod_gzip_item_include mime ^application/x-javascript.* | |||||
| mod_gzip_item_exclude mime ^image/.* | |||||
| mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.* | |||||
| </IfModule> | |||||
| # Leverage Browser Caching | |||||
| <IfModule mod_expires.c> | |||||
| ExpiresActive On | |||||
| ExpiresByType image/jpg "access 1 year" | |||||
| ExpiresByType image/jpeg "access 1 year" | |||||
| ExpiresByType image/gif "access 1 year" | |||||
| ExpiresByType image/png "access 1 year" | |||||
| ExpiresByType text/css "access 1 month" | |||||
| ExpiresByType text/html "access 1 month" | |||||
| ExpiresByType application/pdf "access 1 month" | |||||
| ExpiresByType text/x-javascript "access 1 month" | |||||
| ExpiresByType application/x-shockwave-flash "access 1 month" | |||||
| ExpiresByType image/x-icon "access 1 year" | |||||
| ExpiresDefault "access 1 month" | |||||
| </IfModule> | |||||
| <IfModule mod_headers.c> | |||||
| <filesmatch "\.(ico|flv|jpg|jpeg|png|gif|css|swf)$"> | |||||
| Header set Cache-Control "max-age=2678400, public" | |||||
| </filesmatch> | |||||
| <filesmatch "\.(html|htm)$"> | |||||
| Header set Cache-Control "max-age=7200, private, must-revalidate" | |||||
| </filesmatch> | |||||
| <filesmatch "\.(pdf)$"> | |||||
| Header set Cache-Control "max-age=86400, public" | |||||
| </filesmatch> | |||||
| <filesmatch "\.(js)$"> | |||||
| Header set Cache-Control "max-age=2678400, private" | |||||
| </filesmatch> | |||||
| </IfModule> | |||||
| AuthType Basic | |||||
| AuthName "Password Protected Area" | |||||
| AuthUserFile /var/www/vhosts/plp-tool.de/httpdocs/plp-tool-live/plp-angular/dist/plp-angular/.htpasswd | |||||
| Require valid-user | |||||
| @@ -0,0 +1 @@ | |||||
| plp:$2y$10$FvzeO0lzdARUTVPMithMoO9lYM7tTCniHb2DiHjKt.YGAdtuVpodG | |||||
| @@ -0,0 +1,27 @@ | |||||
| # PlpAngular | |||||
| This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.5. | |||||
| ## Development server | |||||
| Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. | |||||
| ## Code scaffolding | |||||
| Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. | |||||
| ## Build | |||||
| Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. | |||||
| ## Running unit tests | |||||
| Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). | |||||
| ## Running end-to-end tests | |||||
| Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). | |||||
| ## Further help | |||||
| To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). | |||||
| @@ -0,0 +1,149 @@ | |||||
| { | |||||
| "$schema": "./node_modules/@angular/cli/lib/config/schema.json", | |||||
| "version": 1, | |||||
| "newProjectRoot": "projects", | |||||
| "projects": { | |||||
| "plp-angular": { | |||||
| "projectType": "application", | |||||
| "schematics": { | |||||
| "@schematics/angular:component": { | |||||
| "style": "scss" | |||||
| } | |||||
| }, | |||||
| "root": "", | |||||
| "sourceRoot": "src", | |||||
| "prefix": "app", | |||||
| "architect": { | |||||
| "build": { | |||||
| "builder": "@angular-devkit/build-angular:browser", | |||||
| "options": { | |||||
| "crossOrigin": "anonymous", | |||||
| "outputPath": "dist/plp-angular", | |||||
| "index": "src/index.html", | |||||
| "main": "src/main.ts", | |||||
| "polyfills": "src/polyfills.ts", | |||||
| "tsConfig": "tsconfig.app.json", | |||||
| "aot": true, | |||||
| "assets": [ | |||||
| "src/favicon.ico", | |||||
| "src/assets" | |||||
| ], | |||||
| "styles": [ | |||||
| "src/scss/styles.scss", | |||||
| "./node_modules/angular-calendar/css/angular-calendar.css", | |||||
| "./node_modules/ag-grid-community/styles/ag-grid.css", | |||||
| "./node_modules/ag-grid-community/styles/ag-theme-balham.css" | |||||
| ], | |||||
| "scripts": [] | |||||
| }, | |||||
| "configurations": { | |||||
| "gamma": { | |||||
| "fileReplacements": [ | |||||
| { | |||||
| "replace": "src/environments/environment.ts", | |||||
| "with": "src/environments/environment.gamma.ts" | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "beta": { | |||||
| "fileReplacements": [ | |||||
| { | |||||
| "replace": "src/environments/environment.ts", | |||||
| "with": "src/environments/environment.beta.ts" | |||||
| } | |||||
| ] | |||||
| }, | |||||
| "production": { | |||||
| "fileReplacements": [ | |||||
| { | |||||
| "replace": "src/environments/environment.ts", | |||||
| "with": "src/environments/environment.prod.ts" | |||||
| } | |||||
| ], | |||||
| "optimization": true, | |||||
| "outputHashing": "all", | |||||
| "sourceMap": false, | |||||
| "extractCss": true, | |||||
| "namedChunks": false, | |||||
| "aot": true, | |||||
| "extractLicenses": true, | |||||
| "vendorChunk": false, | |||||
| "buildOptimizer": true, | |||||
| "budgets": [ | |||||
| { | |||||
| "type": "initial", | |||||
| "maximumWarning": "2mb", | |||||
| "maximumError": "5mb" | |||||
| }, | |||||
| { | |||||
| "type": "anyComponentStyle", | |||||
| "maximumWarning": "6kb", | |||||
| "maximumError": "10kb" | |||||
| } | |||||
| ] | |||||
| } | |||||
| } | |||||
| }, | |||||
| "serve": { | |||||
| "builder": "@angular-devkit/build-angular:dev-server", | |||||
| "options": { | |||||
| "browserTarget": "plp-angular:build" | |||||
| }, | |||||
| "configurations": { | |||||
| "production": { | |||||
| "browserTarget": "plp-angular:build:production" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "extract-i18n": { | |||||
| "builder": "@angular-devkit/build-angular:extract-i18n", | |||||
| "options": { | |||||
| "browserTarget": "plp-angular:build" | |||||
| } | |||||
| }, | |||||
| "test": { | |||||
| "builder": "@angular-devkit/build-angular:karma", | |||||
| "options": { | |||||
| "main": "src/test.ts", | |||||
| "polyfills": "src/polyfills.ts", | |||||
| "tsConfig": "tsconfig.spec.json", | |||||
| "karmaConfig": "karma.conf.js", | |||||
| "assets": [ | |||||
| "src/favicon.ico", | |||||
| "src/assets" | |||||
| ], | |||||
| "styles": [ | |||||
| "src/scss/styles.scss" | |||||
| ], | |||||
| "scripts": [] | |||||
| } | |||||
| }, | |||||
| "lint": { | |||||
| "builder": "@angular-devkit/build-angular:tslint", | |||||
| "options": { | |||||
| "tsConfig": [ | |||||
| "tsconfig.app.json", | |||||
| "tsconfig.spec.json", | |||||
| "e2e/tsconfig.json" | |||||
| ], | |||||
| "exclude": [ | |||||
| "**/node_modules/**" | |||||
| ] | |||||
| } | |||||
| }, | |||||
| "e2e": { | |||||
| "builder": "@angular-devkit/build-angular:protractor", | |||||
| "options": { | |||||
| "protractorConfig": "e2e/protractor.conf.js", | |||||
| "devServerTarget": "plp-angular:serve" | |||||
| }, | |||||
| "configurations": { | |||||
| "production": { | |||||
| "devServerTarget": "plp-angular:serve:production" | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| }}, | |||||
| "defaultProject": "plp-angular" | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| // @ts-check | |||||
| // Protractor configuration file, see link for more information | |||||
| // https://github.com/angular/protractor/blob/master/lib/config.ts | |||||
| const { SpecReporter } = require('jasmine-spec-reporter'); | |||||
| /** | |||||
| * @type { import("protractor").Config } | |||||
| */ | |||||
| exports.config = { | |||||
| allScriptsTimeout: 11000, | |||||
| specs: [ | |||||
| './src/**/*.e2e-spec.ts' | |||||
| ], | |||||
| capabilities: { | |||||
| 'browserName': 'chrome' | |||||
| }, | |||||
| directConnect: true, | |||||
| baseUrl: 'http://localhost:4200/', | |||||
| framework: 'jasmine', | |||||
| jasmineNodeOpts: { | |||||
| showColors: true, | |||||
| defaultTimeoutInterval: 30000, | |||||
| print: function() {} | |||||
| }, | |||||
| onPrepare() { | |||||
| require('ts-node').register({ | |||||
| project: require('path').join(__dirname, './tsconfig.json') | |||||
| }); | |||||
| jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1,23 @@ | |||||
| import { AppPage } from './app.po'; | |||||
| import { browser, logging } from 'protractor'; | |||||
| describe('workspace-project App', () => { | |||||
| let page: AppPage; | |||||
| beforeEach(() => { | |||||
| page = new AppPage(); | |||||
| }); | |||||
| it('should display welcome message', () => { | |||||
| page.navigateTo(); | |||||
| expect(page.getTitleText()).toEqual('plp-angular app is running!'); | |||||
| }); | |||||
| afterEach(async () => { | |||||
| // Assert that there are no errors emitted from the browser | |||||
| const logs = await browser.manage().logs().get(logging.Type.BROWSER); | |||||
| expect(logs).not.toContain(jasmine.objectContaining({ | |||||
| level: logging.Level.SEVERE, | |||||
| } as logging.Entry)); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,11 @@ | |||||
| import { browser, by, element } from 'protractor'; | |||||
| export class AppPage { | |||||
| navigateTo() { | |||||
| return browser.get(browser.baseUrl) as Promise<any>; | |||||
| } | |||||
| getTitleText() { | |||||
| return element(by.css('app-root .content span')).getText() as Promise<string>; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| { | |||||
| "extends": "../tsconfig.json", | |||||
| "compilerOptions": { | |||||
| "outDir": "../out-tsc/e2e", | |||||
| "module": "commonjs", | |||||
| "target": "es2018", | |||||
| "types": [ | |||||
| "jasmine", | |||||
| "jasminewd2", | |||||
| "node" | |||||
| ] | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| // Karma configuration file, see link for more information | |||||
| // https://karma-runner.github.io/1.0/config/configuration-file.html | |||||
| module.exports = function (config) { | |||||
| config.set({ | |||||
| basePath: '', | |||||
| frameworks: ['jasmine', '@angular-devkit/build-angular'], | |||||
| plugins: [ | |||||
| require('karma-jasmine'), | |||||
| require('karma-chrome-launcher'), | |||||
| require('karma-jasmine-html-reporter'), | |||||
| require('karma-coverage-istanbul-reporter'), | |||||
| require('@angular-devkit/build-angular/plugins/karma') | |||||
| ], | |||||
| client: { | |||||
| clearContext: false // leave Jasmine Spec Runner output visible in browser | |||||
| }, | |||||
| coverageIstanbulReporter: { | |||||
| dir: require('path').join(__dirname, './coverage/plp-angular'), | |||||
| reports: ['html', 'lcovonly', 'text-summary'], | |||||
| fixWebpackSourcePaths: true | |||||
| }, | |||||
| reporters: ['progress', 'kjhtml'], | |||||
| port: 9876, | |||||
| colors: true, | |||||
| logLevel: config.LOG_INFO, | |||||
| autoWatch: true, | |||||
| browsers: ['Chrome'], | |||||
| singleRun: false, | |||||
| restartOnFileChange: true | |||||
| }); | |||||
| }; | |||||
| @@ -0,0 +1,58 @@ | |||||
| { | |||||
| "name": "plp-angular", | |||||
| "version": "0.0.0", | |||||
| "scripts": { | |||||
| "ng": "ng", | |||||
| "start": "ng serve", | |||||
| "build": "ng build", | |||||
| "test": "ng test", | |||||
| "lint": "ng lint", | |||||
| "e2e": "ng e2e" | |||||
| }, | |||||
| "private": true, | |||||
| "dependencies": { | |||||
| "@angular-devkit/build-angular": "^15.2.8", | |||||
| "@angular-devkit/schematics": "^15.2.8", | |||||
| "@angular/animations": "^15.2.8", | |||||
| "@angular/common": "^15.2.8", | |||||
| "@angular/compiler": "^15.2.8", | |||||
| "@angular/core": "^15.2.8", | |||||
| "@angular/forms": "^15.2.8", | |||||
| "@angular/platform-browser": "^15.2.8", | |||||
| "@angular/platform-browser-dynamic": "^15.2.8", | |||||
| "@angular/router": "^15.2.8", | |||||
| "@ng-bootstrap/ng-bootstrap": "^14.1.1", | |||||
| "@npmcli/fs": "^3.1.0", | |||||
| "ag-grid-angular": "^29.3.5", | |||||
| "ag-grid-community": "^29.3.5", | |||||
| "angular-calendar": "^0.31.0", | |||||
| "angularx-flatpickr": "^6.2.0", | |||||
| "codelyzer": "^6.0.2", | |||||
| "date-fns": "^1.30.1", | |||||
| "eslint": "^8.40.0", | |||||
| "file-saver": "^2.0.2", | |||||
| "flatpickr": "^4.6.13", | |||||
| "ng6-breadcrumbs": "^1.0.7", | |||||
| "rxjs": "~6.6.7", | |||||
| "tslib": "^2.0.0", | |||||
| "zone.js": "~0.13.0" | |||||
| }, | |||||
| "devDependencies": { | |||||
| "@angular/cli": "^15.2.8", | |||||
| "@angular/compiler-cli": "^15.2.8", | |||||
| "@angular/language-service": "^15.2.8", | |||||
| "@types/file-saver": "^2.0.1", | |||||
| "@types/jasmine": "~3.3.8", | |||||
| "@types/jasminewd2": "^2.0.8", | |||||
| "@types/node": "^20.1.5", | |||||
| "jasmine-core": "~3.8.0", | |||||
| "jasmine-spec-reporter": "~5.0.0", | |||||
| "karma": "^6.4.2", | |||||
| "karma-chrome-launcher": "~3.1.0", | |||||
| "karma-coverage-istanbul-reporter": "~3.0.2", | |||||
| "karma-jasmine": "~4.0.0", | |||||
| "karma-jasmine-html-reporter": "^1.5.0", | |||||
| "ts-node": "~7.0.0", | |||||
| "typescript": "~4.9.5" | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| import { NgModule } from '@angular/core'; | |||||
| import { Routes, RouterModule } from '@angular/router'; | |||||
| import { StartComponent } from './views/start/start.component'; | |||||
| import {CustomerManagementComponent} from './views/customer-management/customer-management.component'; | |||||
| import {LoginComponent} from './views/login/login.component'; | |||||
| import {RouteInterceptor} from './interceptor/route-interceptor'; | |||||
| import {AccountingComponent} from './views/accounting/accounting.component'; | |||||
| import {TechniqueComponent} from './views/technique/technique.component'; | |||||
| import {SalesComponent} from './views/sales/sales.component'; | |||||
| const routes: Routes = [ | |||||
| {path: '', redirectTo: '/start', pathMatch: 'full'}, | |||||
| {path: 'login', component: LoginComponent, data: {breadcrumb: 'Login'}}, | |||||
| {path: 'start', component: StartComponent, data: {breadcrumb: 'Home'}, canActivate: [RouteInterceptor]}, | |||||
| {path: 'customer-management', component: CustomerManagementComponent, data: {breadcrumb: 'Kunden'}, canActivate: [RouteInterceptor]}, | |||||
| // {path: 'accounting', component: AccountingComponent, data: {breadcrumb: 'Buchhaltung'}, canActivate: [RouteInterceptor]}, | |||||
| // {path: 'technique', component: TechniqueComponent, data: {breadcrumb: 'Technik'}, canActivate: [RouteInterceptor]}, | |||||
| // {path: 'sales', component: SalesComponent, data: {breadcrumb: 'Vertrieb'}, canActivate: [RouteInterceptor]}, | |||||
| {path: '**', redirectTo: '/start', pathMatch: 'full'}, | |||||
| ]; | |||||
| @NgModule({ | |||||
| imports: [RouterModule.forRoot(routes, {useHash: true})], | |||||
| exports: [RouterModule] | |||||
| }) | |||||
| export class AppRoutingModule { | |||||
| } | |||||
| export const routingComponents = [ | |||||
| LoginComponent, | |||||
| StartComponent, | |||||
| CustomerManagementComponent, | |||||
| AccountingComponent, | |||||
| TechniqueComponent, | |||||
| SalesComponent, | |||||
| ]; | |||||
| @@ -0,0 +1,16 @@ | |||||
| <div id="wrapper"> | |||||
| <header id="header"> | |||||
| <div class="user" *ngIf="activeUser != null">{{activeUser.firstname}} {{activeUser.lastname}} ({{activeUser.v_translated_role}})</div> | |||||
| </header> | |||||
| <div id="content"> | |||||
| <aside *ngIf="isLoggedIn"> | |||||
| <div class="aside-inner"> | |||||
| <app-aside></app-aside> | |||||
| </div> | |||||
| </aside> | |||||
| <div id="main" [class.not-logged-in]="!isLoggedIn"> | |||||
| <router-outlet></router-outlet> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <app-message></app-message> | |||||
| @@ -0,0 +1,53 @@ | |||||
| import {Component, OnDestroy, OnInit} from '@angular/core'; | |||||
| import {AppService} from './services/app.service'; | |||||
| import {Subscription} from 'rxjs'; | |||||
| import {IUser} from './model/entities/user'; | |||||
| @Component({ | |||||
| selector: 'app-root', | |||||
| templateUrl: './app.component.html', | |||||
| styleUrls: ['./app.component.scss'] | |||||
| }) | |||||
| export class AppComponent implements OnInit, OnDestroy { | |||||
| private loginSub: Subscription; | |||||
| private userSub: Subscription; | |||||
| public isLoggedIn: boolean; | |||||
| public activeUser: IUser; | |||||
| constructor(public appService: AppService) { | |||||
| } | |||||
| ngOnInit() { | |||||
| this.isLoggedIn = false; | |||||
| // Observe login state | |||||
| this.loginSub = this.appService.getLoginState$().subscribe( | |||||
| data => { | |||||
| if (data !== null) { | |||||
| this.isLoggedIn = true; | |||||
| } else { | |||||
| this.isLoggedIn = false; | |||||
| } | |||||
| } | |||||
| ); | |||||
| this.userSub = this.appService.getUser$().subscribe( | |||||
| data => { | |||||
| this.activeUser = data as IUser; | |||||
| } | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * Destroy | |||||
| */ | |||||
| ngOnDestroy(): void { | |||||
| if (this.loginSub !== null && this.loginSub !== undefined) { | |||||
| this.loginSub.unsubscribe(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,151 @@ | |||||
| import { BrowserModule } from '@angular/platform-browser'; | |||||
| import { NgModule } from '@angular/core'; | |||||
| import { CommonModule } from '@angular/common'; | |||||
| import { FlatpickrModule } from 'angularx-flatpickr'; | |||||
| import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; | |||||
| import {AppRoutingModule, routingComponents} from './app-routing.module'; | |||||
| import { AppComponent } from './app.component'; | |||||
| import { StartComponent } from './views/start/start.component'; | |||||
| import { BrowserAnimationsModule} from '@angular/platform-browser/animations'; | |||||
| import {CalendarModule, DateAdapter} from 'angular-calendar'; | |||||
| import { adapterFactory} from 'angular-calendar/date-adapters/date-fns'; | |||||
| import {registerLocaleData} from '@angular/common'; | |||||
| import localeDe from '@angular/common/locales/de'; | |||||
| import { CustomerManagementComponent } from './views/customer-management/customer-management.component'; | |||||
| import { AgGridModule } from 'ag-grid-angular'; | |||||
| import { LoginComponent } from './views/login/login.component'; | |||||
| import {FormsModule, ReactiveFormsModule} from '@angular/forms'; | |||||
| import { AsideComponent } from './components/aside/aside.component'; | |||||
| import {AppService} from './services/app.service'; | |||||
| import {CacheService} from './services/cache.service'; | |||||
| import {MessageService} from './services/message.service'; | |||||
| import {HttpService} from './services/http.service'; | |||||
| import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; | |||||
| import {HttpClientInterceptor} from './services/http.interceptor'; | |||||
| import {RouteInterceptor} from './interceptor/route-interceptor'; | |||||
| import { TechniqueComponent } from './views/technique/technique.component'; | |||||
| import { SalesComponent } from './views/sales/sales.component'; | |||||
| import { AccountingComponent } from './views/accounting/accounting.component'; | |||||
| import { ModalComponent } from './components/modal/modal.component'; | |||||
| import { CustomerViewComponent } from './views/customer-management/customer-view/customer-view.component'; | |||||
| import { TabsComponent } from './components/tabs/tabs.component'; | |||||
| import { TabComponent } from './components/tab/tab.component'; | |||||
| import { CustomerDataViewComponent } from './views/customer-management/customer-view/customer-data-view/customer-data-view.component'; | |||||
| import { CustomerDataEditComponent } from './views/customer-management/customer-view/customer-data-edit/customer-data-edit.component'; | |||||
| import { CustomerContactPersonViewComponent } from './views/customer-management/customer-view/customer-contact-person-view/customer-contact-person-view.component'; | |||||
| import { CustomerContactPersonEditComponent } from './views/customer-management/customer-view/customer-contact-person-edit/customer-contact-person-edit.component'; | |||||
| import { CustomerNoteViewComponent } from './views/customer-management/customer-view/customer-note-view/customer-note-view.component'; | |||||
| import { CustomerNoteEditComponent } from './views/customer-management/customer-view/customer-note-edit/customer-note-edit.component'; | |||||
| import {CustomerService} from './services/customer.service'; | |||||
| import { CustomerNoteDetailComponent } from './views/customer-management/customer-view/customer-note-detail/customer-note-detail.component'; | |||||
| import {GridCheckboxRendererComponent} from './grid-cellrenderer/grid-checkbox/grid-checkbox-renderer/grid-checkbox-renderer.component'; | |||||
| import {GridCheckboxEditorComponent} from './grid-cellrenderer/grid-checkbox/grid-checkbox-editor/grid-checkbox-editor.component'; | |||||
| import {GridSelectEditorComponent} from './grid-cellrenderer/grid-select/grid-select-editor/grid-select-editor.component'; | |||||
| import {GridSelectRendererComponent} from './grid-cellrenderer/grid-select/grid-select-renderer/grid-select-renderer.component'; | |||||
| import {GridDateEditorComponent} from './grid-cellrenderer/grid-date/grid-date-editor/grid-date-editor.component'; | |||||
| import {GridDateRendererComponent} from './grid-cellrenderer/grid-date/grid-date-renderer/grid-date-renderer.component'; | |||||
| import {GridTextRendererComponent} from './grid-cellrenderer/grid-text/grid-text-renderer/grid-text-renderer.component'; | |||||
| import {GridTextEditorComponent} from './grid-cellrenderer/grid-text/grid-text-editor/grid-text-editor.component'; | |||||
| import {GridInputRendererComponent} from './grid-cellrenderer/grid-input/grid-input-renderer/grid-input-renderer.component'; | |||||
| import {GridInputEditorComponent} from './grid-cellrenderer/grid-input/grid-input-editor/grid-input-editor.component'; | |||||
| import {GridBlockedRendererComponent} from './grid-cellrenderer/grid-blocked/grid-blocked-renderer/grid-blocked-renderer.component'; | |||||
| import {GridBlockedEditorComponent} from './grid-cellrenderer/grid-blocked/grid-blocked-editor/grid-blocked-editor.component'; | |||||
| import { CustomerMeetingViewComponent } from './views/customer-management/customer-view/customer-meeting-view/customer-meeting-view.component'; | |||||
| import { CustomerMeetingEditComponent } from './views/customer-management/customer-view/customer-meeting-edit/customer-meeting-edit.component'; | |||||
| import { CustomerMeetingDetailComponent } from './views/customer-management/customer-view/customer-meeting-detail/customer-meeting-detail.component'; | |||||
| import { MessageComponent } from './components/message/message.component'; | |||||
| import { CustomerListComponent } from './views/customer-management/customer-list/customer-list.component'; | |||||
| import { CustomerContactListComponent } from './views/customer-management/customer-contact-list/customer-contact-list.component'; | |||||
| import { MeetingListComponent } from './views/start/meeting-list/meeting-list.component'; | |||||
| import { MeetingCalendarComponent } from './views/start/meeting-calendar/meeting-calendar.component'; | |||||
| import { CustomerContactPersonDetailComponent } from './views/customer-management/customer-view/customer-contact-person-detail/customer-contact-person-detail.component'; | |||||
| import { InternalMeetingEditComponent } from './views/start/internal-meeting-edit/internal-meeting-edit.component'; | |||||
| import { InternalMeetingDetailComponent } from './views/start/internal-meeting-detail/internal-meeting-detail.component'; | |||||
| import { CalendarLegendComponent } from './components/calendar-legend/calendar-legend.component'; | |||||
| import { CommonService } from './services/common.service'; | |||||
| import {GridRendererComponent} from './grid-cellrenderer/grid-renderer-component'; | |||||
| import {GridEditorComponent} from './grid-cellrenderer/grid-editor-component'; | |||||
| registerLocaleData(localeDe); | |||||
| @NgModule({ | |||||
| declarations: [ | |||||
| AppComponent, | |||||
| routingComponents, | |||||
| StartComponent, | |||||
| CustomerManagementComponent, | |||||
| LoginComponent, | |||||
| AsideComponent, | |||||
| TechniqueComponent, | |||||
| SalesComponent, | |||||
| AccountingComponent, | |||||
| ModalComponent, | |||||
| CustomerViewComponent, | |||||
| TabsComponent, | |||||
| TabComponent, | |||||
| CustomerDataViewComponent, | |||||
| CustomerDataEditComponent, | |||||
| CustomerContactPersonViewComponent, | |||||
| CustomerContactPersonEditComponent, | |||||
| CustomerNoteViewComponent, | |||||
| CustomerNoteEditComponent, | |||||
| CustomerNoteDetailComponent, | |||||
| GridRendererComponent, | |||||
| GridEditorComponent, | |||||
| GridCheckboxRendererComponent, | |||||
| GridCheckboxEditorComponent, | |||||
| GridSelectEditorComponent, | |||||
| GridSelectRendererComponent, | |||||
| GridDateEditorComponent, | |||||
| GridDateRendererComponent, | |||||
| GridBlockedRendererComponent, | |||||
| GridTextRendererComponent, | |||||
| GridTextEditorComponent, | |||||
| GridInputRendererComponent, | |||||
| GridInputEditorComponent, | |||||
| GridBlockedEditorComponent, | |||||
| CustomerMeetingViewComponent, | |||||
| CustomerMeetingEditComponent, | |||||
| CustomerMeetingDetailComponent, | |||||
| MessageComponent, | |||||
| CustomerListComponent, | |||||
| CustomerContactListComponent, | |||||
| MeetingListComponent, | |||||
| MeetingCalendarComponent, | |||||
| CustomerContactPersonDetailComponent, | |||||
| InternalMeetingEditComponent, | |||||
| InternalMeetingDetailComponent, | |||||
| CalendarLegendComponent, | |||||
| ], | |||||
| imports: [ | |||||
| BrowserModule, | |||||
| CommonModule, | |||||
| NgbModalModule, | |||||
| FlatpickrModule.forRoot(), | |||||
| AppRoutingModule, | |||||
| HttpClientModule, | |||||
| ReactiveFormsModule, | |||||
| FormsModule, | |||||
| BrowserAnimationsModule, | |||||
| // ScrollToModule.forRoot(), | |||||
| CalendarModule.forRoot({ | |||||
| provide: DateAdapter, | |||||
| useFactory: adapterFactory | |||||
| }), | |||||
| AgGridModule, | |||||
| ], | |||||
| providers: [ | |||||
| AppService, | |||||
| RouteInterceptor, | |||||
| CacheService, | |||||
| MessageService, | |||||
| HttpService, | |||||
| CustomerService, | |||||
| CommonService, | |||||
| {provide: HTTP_INTERCEPTORS, useClass: HttpClientInterceptor, multi: true}, | |||||
| ], | |||||
| bootstrap: [AppComponent] | |||||
| }) | |||||
| export class AppModule { } | |||||
| @@ -0,0 +1,158 @@ | |||||
| import {GridFactory} from '../grid-cellrenderer/grid-factory'; | |||||
| import {IGridCellParams} from '../grid-cellrenderer/grid-cell-params'; | |||||
| import {Const} from '../utils/const'; | |||||
| export class AgGridComponentConst { | |||||
| // Key for cell type (must be used in data of grid item) | |||||
| public static CELL_KEY_TYPE = 'cellKeyType'; | |||||
| // Cell renderers | |||||
| public static CELL_RENDERER_CHECKBOX = 'gridCheckboxRenderer'; | |||||
| public static CELL_RENDERER_TEXT = 'gridTextRenderer'; | |||||
| public static CELL_RENDERER_DATE = 'gridDateRenderer'; | |||||
| public static CELL_RENDERER_INPUT = 'gridInputRenderer'; | |||||
| public static CELL_RENDERER_SELECT = 'gridSelectRenderer'; | |||||
| public static CELL_RENDERER_BLOCKED = 'gridBlockedRenderer'; | |||||
| public static CELL_RENDERER_DEFAULT = ''; | |||||
| // Cell editors | |||||
| public static CELL_EDITOR_CHECKBOX = 'gridCheckboxEditor'; | |||||
| public static CELL_EDITOR_TEXT = 'gridTextEditor'; | |||||
| public static CELL_EDITOR_DATE = 'gridDateEditor'; | |||||
| public static CELL_EDITOR_INPUT = 'gridInputEditor'; | |||||
| public static CELL_EDITOR_SELECT = 'gridSelectEditor'; | |||||
| public static CELL_EDITOR_BLOCKED = 'gridBlockedEditor'; | |||||
| public static CELL_EDITOR_DEFAULT = ''; | |||||
| // Cell types | |||||
| public static CELL_TYPE_DEFAULT = 'default'; | |||||
| public static CELL_TYPE_CHECKBOX = 'checkbox'; | |||||
| public static CELL_TYPE_TEXT = 'text'; | |||||
| public static CELL_TYPE_DATE = 'date'; | |||||
| public static CELL_TYPE_INPUT = 'input'; | |||||
| public static CELL_TYPE_SELECT = 'select'; | |||||
| public static CELL_TYPE_BLOCKED = 'blocked'; | |||||
| // Cell units | |||||
| public static CELL_UNIT_PERCENT = '%'; | |||||
| public static CELL_UNIT_EURO = '€'; | |||||
| public static CELL_UNIT_DAYS = ' Tage'; | |||||
| public static CELL_UNIT_SQUARE_METER: string = ' ' + Const.UNIT_SQUARE_METER; | |||||
| // Cell value types | |||||
| public static CELL_VALUE_TYPE_BOOL = 'bool'; | |||||
| public static CELL_VALUE_TYPE_INT = 'int'; | |||||
| public static CELL_VALUE_TYPE_FLOAT = 'float'; | |||||
| public static CELL_VALUE_TYPE_STRING = 'string'; | |||||
| public static validCellUnits: string[] = [ | |||||
| AgGridComponentConst.CELL_UNIT_PERCENT, | |||||
| AgGridComponentConst.CELL_UNIT_EURO, | |||||
| AgGridComponentConst.CELL_UNIT_DAYS, | |||||
| AgGridComponentConst.CELL_UNIT_SQUARE_METER, | |||||
| ]; | |||||
| public static validCellValueTypes: string[] = [ | |||||
| AgGridComponentConst.CELL_VALUE_TYPE_BOOL, | |||||
| AgGridComponentConst.CELL_VALUE_TYPE_INT, | |||||
| AgGridComponentConst.CELL_VALUE_TYPE_FLOAT, | |||||
| AgGridComponentConst.CELL_VALUE_TYPE_STRING, | |||||
| ]; | |||||
| public static gridInputError: string; | |||||
| /** | |||||
| * Sets input error | |||||
| */ | |||||
| public static setGridInputError(errorMessage: string = null): void { | |||||
| AgGridComponentConst.gridInputError = errorMessage; | |||||
| if (null !== errorMessage) { | |||||
| setTimeout(() => { | |||||
| AgGridComponentConst.setGridInputError(); | |||||
| }, 10); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Returns a cell renderer for given type with given parameters | |||||
| */ | |||||
| public static cellRendererSelector(params: any, gridCellParams: IGridCellParams = null): {} { | |||||
| if (null === gridCellParams) { | |||||
| gridCellParams = GridFactory.getGridCellParams(); | |||||
| } | |||||
| AgGridComponentConst.checkCellValueType(gridCellParams.type); | |||||
| AgGridComponentConst.checkCellUnit(gridCellParams.unit); | |||||
| switch (params.data[AgGridComponentConst.CELL_KEY_TYPE]) { | |||||
| case AgGridComponentConst.CELL_TYPE_CHECKBOX: | |||||
| return {component: AgGridComponentConst.CELL_RENDERER_CHECKBOX, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_TEXT: | |||||
| return {component: AgGridComponentConst.CELL_RENDERER_TEXT, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_INPUT: | |||||
| return {component: AgGridComponentConst.CELL_RENDERER_INPUT, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_DATE: | |||||
| return {component: AgGridComponentConst.CELL_RENDERER_DATE, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_SELECT: | |||||
| return {component: AgGridComponentConst.CELL_RENDERER_SELECT, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_BLOCKED: | |||||
| return {component: AgGridComponentConst.CELL_RENDERER_BLOCKED, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_DEFAULT: | |||||
| // Nothing specific, so ag grid will take it's own default cell renderer | |||||
| return; | |||||
| default: | |||||
| throw new Error('Unknown cell type'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Returns a cell editor for given type with given parameters | |||||
| */ | |||||
| public static cellEditorSelector(params: any, gridCellParams: IGridCellParams = null) { | |||||
| if (null === gridCellParams) { | |||||
| gridCellParams = GridFactory.getGridCellParams(); | |||||
| } | |||||
| AgGridComponentConst.checkCellValueType(gridCellParams.type); | |||||
| AgGridComponentConst.checkCellUnit(gridCellParams.unit); | |||||
| switch (params.data[AgGridComponentConst.CELL_KEY_TYPE]) { | |||||
| case AgGridComponentConst.CELL_TYPE_CHECKBOX: | |||||
| // NOTE: Overwrite any given type for checkbox with boolean, since checkbox can only hold boolean values | |||||
| gridCellParams.type = AgGridComponentConst.CELL_VALUE_TYPE_BOOL; | |||||
| return {component: AgGridComponentConst.CELL_EDITOR_CHECKBOX, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_TEXT: | |||||
| return {component: AgGridComponentConst.CELL_EDITOR_TEXT, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_INPUT: | |||||
| return {component: AgGridComponentConst.CELL_EDITOR_INPUT, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_DATE: | |||||
| return {component: AgGridComponentConst.CELL_EDITOR_DATE, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_SELECT: | |||||
| return {component: AgGridComponentConst.CELL_EDITOR_SELECT, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_BLOCKED: | |||||
| return {component: AgGridComponentConst.CELL_EDITOR_BLOCKED, params: gridCellParams}; | |||||
| case AgGridComponentConst.CELL_TYPE_DEFAULT: | |||||
| // Nothing specific, so ag grid will take it's own default cell editor | |||||
| return; | |||||
| default: | |||||
| throw new Error('Unknown cell type'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Checks if cell value type is valid | |||||
| * @param {string} type | |||||
| */ | |||||
| private static checkCellValueType(type: string = null): void { | |||||
| if (null !== type && AgGridComponentConst.validCellValueTypes.indexOf(type) < 0) { | |||||
| throw new Error('Unknown cell value type'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Checks if cell unit is valid | |||||
| * @param {string} type | |||||
| */ | |||||
| private static checkCellUnit(type: string = null): void { | |||||
| if (null !== type && AgGridComponentConst.validCellUnits.indexOf(type) < 0) { | |||||
| throw new Error('Unknown cell value type'); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,466 @@ | |||||
| import {path} from '../../environments/path'; | |||||
| import {AgGridLocale} from '../lang/ag-gridlocale'; | |||||
| import {IGridValidationErrors} from '../model/virtual/grid-validation-errors'; | |||||
| import {GridSelectEditorComponent} from '../grid-cellrenderer/grid-select/grid-select-editor/grid-select-editor.component'; | |||||
| import {GridSelectRendererComponent} from '../grid-cellrenderer/grid-select/grid-select-renderer/grid-select-renderer.component'; | |||||
| import {GridCheckboxRendererComponent} from '../grid-cellrenderer/grid-checkbox/grid-checkbox-renderer/grid-checkbox-renderer.component'; | |||||
| import {GridCheckboxEditorComponent} from '../grid-cellrenderer/grid-checkbox/grid-checkbox-editor/grid-checkbox-editor.component'; | |||||
| import {GridDateRendererComponent} from '../grid-cellrenderer/grid-date/grid-date-renderer/grid-date-renderer.component'; | |||||
| import {GridDateEditorComponent} from '../grid-cellrenderer/grid-date/grid-date-editor/grid-date-editor.component'; | |||||
| import {GridTextRendererComponent} from '../grid-cellrenderer/grid-text/grid-text-renderer/grid-text-renderer.component'; | |||||
| import {GridTextEditorComponent} from '../grid-cellrenderer/grid-text/grid-text-editor/grid-text-editor.component'; | |||||
| import {AgGridComponentConst} from './ag-grid-component-const'; | |||||
| import {GridInputRendererComponent} from '../grid-cellrenderer/grid-input/grid-input-renderer/grid-input-renderer.component'; | |||||
| import {GridInputEditorComponent} from '../grid-cellrenderer/grid-input/grid-input-editor/grid-input-editor.component'; | |||||
| import {GridBlockedRendererComponent} from '../grid-cellrenderer/grid-blocked/grid-blocked-renderer/grid-blocked-renderer.component'; | |||||
| import {GridBlockedEditorComponent} from '../grid-cellrenderer/grid-blocked/grid-blocked-editor/grid-blocked-editor.component'; | |||||
| import {Utils} from '../utils/utils'; | |||||
| export class AgGridComponent { | |||||
| static readonly MIN_HEIGHT: number = 16; | |||||
| public params: any; | |||||
| public columnDefs: any[] = []; | |||||
| public defaultColDef: {} = {}; | |||||
| public rowData: any[] = []; | |||||
| public rowSelection: string; | |||||
| public gridParamsColumnApi; | |||||
| public gridParamsApi; | |||||
| public apiAssetsPath: string; | |||||
| public resetSearchBtn: boolean; | |||||
| public localeText: {}; | |||||
| public frameworkComponents: {}; | |||||
| public bSizeColumnsToFit: boolean; | |||||
| public gridErrors: IGridValidationErrors[]; | |||||
| public errorIndex: number; | |||||
| public resetValueByError: boolean; | |||||
| public errorMessage: string; | |||||
| public nextAddedItemId: number; | |||||
| /** | |||||
| * Constructor | |||||
| */ | |||||
| constructor(bSizeColumnsToFit: boolean = true) { | |||||
| AgGridComponentConst.setGridInputError(); | |||||
| this.rowData = []; | |||||
| this.columnDefs = []; | |||||
| this.rowSelection = 'multiple'; | |||||
| this.defaultColDef = { | |||||
| editable: true | |||||
| }; | |||||
| this.apiAssetsPath = path.path_assets; | |||||
| this.frameworkComponents = {}; | |||||
| this.localeText = AgGridLocale.getLocale(); | |||||
| this.bSizeColumnsToFit = bSizeColumnsToFit; | |||||
| this.gridErrors = []; | |||||
| this.errorIndex = -1; | |||||
| this.resetValueByError = false; | |||||
| this.errorMessage = null; | |||||
| this.nextAddedItemId = null; | |||||
| this.frameworkComponents = { | |||||
| gridSelectEditor: GridSelectEditorComponent, | |||||
| gridSelectRenderer: GridSelectRendererComponent, | |||||
| gridCheckboxRenderer: GridCheckboxRendererComponent, | |||||
| gridCheckboxEditor: GridCheckboxEditorComponent, | |||||
| gridDateRenderer: GridDateRendererComponent, | |||||
| gridDateEditor: GridDateEditorComponent, | |||||
| gridInputRenderer: GridInputRendererComponent, | |||||
| gridInputEditor: GridInputEditorComponent, | |||||
| gridTextRenderer: GridTextRendererComponent, | |||||
| gridTextEditor: GridTextEditorComponent, | |||||
| gridBlockedRenderer: GridBlockedRendererComponent, | |||||
| gridBlockedEditor: GridBlockedEditorComponent | |||||
| }; | |||||
| } | |||||
| /** | |||||
| * Callback on grid ready | |||||
| */ | |||||
| public onGridReady(params): void { | |||||
| this.params = params; | |||||
| this.gridParamsColumnApi = params.columnApi; | |||||
| this.gridParamsApi = params.api; | |||||
| // Set for cadasters | |||||
| if (!this.bSizeColumnsToFit) { | |||||
| const allColumnIds = []; | |||||
| this.gridParamsColumnApi.getAllColumns().forEach((column) => { | |||||
| allColumnIds.push(column.colId); | |||||
| }); | |||||
| this.gridParamsColumnApi.autoSizeColumns(allColumnIds); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * On row clicked | |||||
| */ | |||||
| public onRowClicked(e): void { | |||||
| // Reset error message | |||||
| AgGridComponentConst.setGridInputError(); | |||||
| this.errorMessage = null; | |||||
| } | |||||
| /** | |||||
| * On cell editing stopped | |||||
| */ | |||||
| public onCellEditingStopped(e): void { | |||||
| if (null !== AgGridComponentConst.gridInputError) { | |||||
| this.errorMessage = AgGridComponentConst.gridInputError; | |||||
| e.api.ensureIndexVisible(e.rowIndex, 'middle'); | |||||
| e.api.ensureColumnVisible(e.colDef.field.toString()); | |||||
| } else { | |||||
| this.errorMessage = null; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Sizes columns to fit | |||||
| */ | |||||
| public onFirstDataRendered(params: any): void { | |||||
| // Set for info components | |||||
| if (this.bSizeColumnsToFit) { | |||||
| params.api.sizeColumnsToFit(); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Resizes grid | |||||
| */ | |||||
| public gridSizeChanged(params: any): void { | |||||
| if (undefined !== this.gridParamsApi) { | |||||
| this.gridParamsApi.sizeColumnsToFit(); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Search Form | |||||
| */ | |||||
| public searchFormSubmit(params: any): void { | |||||
| if (params.target.value !== '') { | |||||
| this.resetSearchBtn = true; | |||||
| this.gridParamsApi.setQuickFilter(params.target.value); | |||||
| } else { | |||||
| this.resetSearchBtn = false; | |||||
| this.gridParamsApi.setQuickFilter(null); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Reset search input | |||||
| */ | |||||
| public resetSearch(e: any): void { | |||||
| e.originalTarget[0].value = ''; | |||||
| this.resetSearchBtn = false; | |||||
| this.gridParamsApi.setQuickFilter(null); | |||||
| } | |||||
| /** | |||||
| * Stop editing state | |||||
| */ | |||||
| public stopEditing(): void { | |||||
| this.params.api.stopEditing(); | |||||
| } | |||||
| /** | |||||
| * Configures scrollToService - https://www.npmjs.com/package/@nicky-lenaers/ngx-scroll-to | |||||
| */ | |||||
| public scrollTo(targetFieldName: string = null, lastFieldName: string = null): void { | |||||
| if (null !== targetFieldName) { | |||||
| if (null !== lastFieldName) { | |||||
| // Scroll to last field first to ensure our target field name is on left-hand side | |||||
| this.params.api.ensureColumnVisible(lastFieldName); | |||||
| } | |||||
| this.params.api.ensureColumnVisible(targetFieldName); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Cell renderer selector for individual cells with optional default parameters | |||||
| */ | |||||
| public cellRendererSelector(params: any): {} { | |||||
| return AgGridComponentConst.cellRendererSelector(params); | |||||
| } | |||||
| /** | |||||
| * Cell editor selector for individual cells | |||||
| */ | |||||
| public cellEditorSelector(params: any): {} { | |||||
| return AgGridComponentConst.cellEditorSelector(params); | |||||
| } | |||||
| /** | |||||
| * Validates grid | |||||
| */ | |||||
| public validateGrid(): boolean { | |||||
| const oldErrors: IGridValidationErrors[] = this.gridErrors; | |||||
| this.gridErrors = []; | |||||
| this.errorIndex = -1; | |||||
| const primaryColumns = this.params.api.columnController.primaryColumns; | |||||
| const errorColumn: any[] = []; | |||||
| const errors: IGridValidationErrors[] = []; | |||||
| primaryColumns.forEach((value) => { | |||||
| if (undefined !== value.colDef.cellEditorParams) { | |||||
| if (undefined !== value.colDef.cellEditorParams.mandatory && true === value.colDef.cellEditorParams.mandatory) { | |||||
| errorColumn.push(value.colDef.field); | |||||
| } | |||||
| } | |||||
| }); | |||||
| errorColumn.forEach((value, i) => { | |||||
| this.params.api.forEachNode((rowNode, index) => { | |||||
| if (null === rowNode.data[value] || undefined === rowNode.data[value]) { | |||||
| errors.push({ | |||||
| column: value, | |||||
| row: index | |||||
| }); | |||||
| } | |||||
| }); | |||||
| }); | |||||
| this.gridErrors = errors; | |||||
| if (this.gridErrors.length !== 0) { | |||||
| if (oldErrors.length !== this.gridErrors.length) { | |||||
| this.params.api.ensureIndexVisible(this.gridErrors[0].row, 'middle'); | |||||
| this.params.api.ensureColumnVisible(this.gridErrors[0].column); | |||||
| } | |||||
| } | |||||
| return this.gridErrors.length === 0; | |||||
| } | |||||
| /** | |||||
| * Jumps to error | |||||
| */ | |||||
| public jumpToError(direction: string): void { | |||||
| const errorLength: number = this.gridErrors.length - 1; | |||||
| // If any errors | |||||
| if (errorLength >= 0) { | |||||
| // if errorIndex is smaller or same as list of all errors | |||||
| if (errorLength === 0) { | |||||
| this.errorIndex = 0; | |||||
| } else { | |||||
| if (direction === 'next') { | |||||
| this.errorIndex = this.errorIndex < errorLength ? this.errorIndex + 1 : 0; | |||||
| } else { | |||||
| this.errorIndex = this.errorIndex <= 0 ? errorLength : this.errorIndex - 1; | |||||
| } | |||||
| } | |||||
| this.params.api.ensureIndexVisible(this.gridErrors[this.errorIndex].row, 'middle'); | |||||
| this.params.api.ensureColumnVisible(this.gridErrors[this.errorIndex].column); | |||||
| } else { | |||||
| // reset errorIndex if no errors | |||||
| this.errorIndex = -1; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Shows input error | |||||
| */ | |||||
| public showInputError(positionIndex: number, field: string, oldValue: string, type: string): void { | |||||
| this.resetValueByError = true; | |||||
| this.params.api.clearFocusedCell(); | |||||
| this.params.api.ensureIndexVisible(positionIndex, 'middle'); | |||||
| this.params.api.ensureColumnVisible(field); | |||||
| // Lifecycle hack | |||||
| setTimeout(() => { | |||||
| // Reset to old value | |||||
| this.params.api.getRowNode(positionIndex).setDataValue(field, oldValue); | |||||
| this.params.api.startEditingCell({ | |||||
| rowIndex: positionIndex, | |||||
| colKey: field, | |||||
| }); | |||||
| switch (type) { | |||||
| case 'int': | |||||
| this.errorMessage = 'Bitte geben Sie einen ganzzahligen Wert ein.'; | |||||
| break; | |||||
| case 'float': | |||||
| this.errorMessage = 'Bitte geben Sie einen dezimalen oder ganzzahligen Wert ein.'; | |||||
| break; | |||||
| } | |||||
| }, 10); | |||||
| } | |||||
| /** | |||||
| * Add row to grid and returns position index | |||||
| */ | |||||
| public addRow(item: {}): number { | |||||
| this.initNextAddedItemId(); | |||||
| const rowModel = this.gridParamsApi.rowModel; | |||||
| // Note: default is last row | |||||
| let addIndex: number = rowModel.rowsToDisplay.length + 1; | |||||
| rowModel.rowsToDisplay.forEach((row, i) => { | |||||
| if (row.selected === true) { | |||||
| // Note: Add index is the row after selected row | |||||
| addIndex = i + 1; | |||||
| return; | |||||
| } | |||||
| }); | |||||
| this.gridParamsApi.updateRowData({ | |||||
| add: [item], | |||||
| addIndex | |||||
| }); | |||||
| return addIndex; | |||||
| } | |||||
| /** | |||||
| * Removes selected rows | |||||
| */ | |||||
| public removeRows(): void { | |||||
| this.initNextAddedItemId(); | |||||
| const selectedRows: {}[] = this.gridParamsApi.getSelectedRows(); | |||||
| this.gridParamsApi.updateRowData({remove: selectedRows}); | |||||
| } | |||||
| /** | |||||
| * Gets next row position | |||||
| */ | |||||
| public getNextRowPosition(): number { | |||||
| return this.gridParamsApi.rowModel.length + 1; | |||||
| } | |||||
| /** | |||||
| * inits next dummy id for next added entry, if necessary | |||||
| */ | |||||
| private initNextAddedItemId(): void { | |||||
| if (this.nextAddedItemId === null) { | |||||
| this.nextAddedItemId = (this.rowData.length + 1) * -1; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Returns dummy id for added entry | |||||
| */ | |||||
| public getNextAddedItemId(): number { | |||||
| this.initNextAddedItemId(); | |||||
| const res: number = this.nextAddedItemId; | |||||
| this.nextAddedItemId--; | |||||
| return res; | |||||
| } | |||||
| /** | |||||
| * column resize event | |||||
| */ | |||||
| public onColumnResized(event): void { | |||||
| if (event.finished !== false) { | |||||
| event.api.setHeaderHeight(AgGridComponent.MIN_HEIGHT); | |||||
| const headerCells = Array.from(document.querySelectorAll('.content .ag-header-cell-label')); | |||||
| let minHeight: number = AgGridComponent.MIN_HEIGHT; | |||||
| headerCells.forEach(cell => { | |||||
| minHeight = Math.max(minHeight, cell.scrollHeight); | |||||
| }); | |||||
| // set header height to calculated height + padding (top: 8px, bottom: 8px) | |||||
| event.api.setHeaderHeight(minHeight + 16); | |||||
| } | |||||
| if (event.finished) { | |||||
| this.gridParamsApi.resetRowHeights(); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Renders colored dot | |||||
| */ | |||||
| public dotRenderer(params): string { | |||||
| return '<span class=\'dot dot--' + params.value + '\'></span>'; | |||||
| } | |||||
| /** | |||||
| * Renders email address | |||||
| */ | |||||
| public emailRenderer(params): string { | |||||
| return null !== params.value ? '<a href=\'mailto:' + params.value + '\'>' + params.value + '</a>' : ''; | |||||
| } | |||||
| /** | |||||
| * Renders phone number | |||||
| */ | |||||
| public phoneRenderer(params): string { | |||||
| return null !== params.value ? '<a href=\'tel:' + params.value + '\'>' + params.value + '</a>' : ''; | |||||
| } | |||||
| /** | |||||
| * Renders phone number | |||||
| */ | |||||
| public urlRenderer(params): string { | |||||
| return null !== params.value ? '<a href=\'' + Utils.checkUrl(params.value) + '\' target=\'_blank\'>' + params.value + '</a>' : ''; | |||||
| } | |||||
| /** | |||||
| * option renderer | |||||
| */ | |||||
| public optionRenderer(params): string { | |||||
| return params.data.is_option_meeting ? 'Ja' : ''; | |||||
| } | |||||
| /** | |||||
| * address renderer | |||||
| */ | |||||
| public addressRenderer(params): string { | |||||
| let address = ''; | |||||
| if (params.data.typeType === 'visit') { | |||||
| const street = params.data.street !== null ? params.data.street : ''; | |||||
| const streetNo = params.data.street_no !== null ? params.data.street_no : ''; | |||||
| const zip = params.data.zip !== null ? params.data.zip : ''; | |||||
| const city = params.data.city !== null ? params.data.city : ''; | |||||
| let br = ''; | |||||
| if (zip !== null || city !== null) { | |||||
| br = '<br />'; | |||||
| } | |||||
| address = street + ' ' + streetNo + ' ' + br + zip + ' ' + city; | |||||
| } | |||||
| return address; | |||||
| } | |||||
| /** | |||||
| * participants renderer | |||||
| */ | |||||
| public participantsRenderer(params): string { | |||||
| let participantNames = ''; | |||||
| params.data.participants.forEach((value) => { | |||||
| participantNames += params.data.that.appService.getConfig().vc_user_by_id[value.participant_user_id].firstname + | |||||
| ' ' + params.data.that.appService.getConfig().vc_user_by_id[value.participant_user_id].lastname + '<br />'; | |||||
| }); | |||||
| return participantNames; | |||||
| } | |||||
| /** | |||||
| * Time renderer | |||||
| */ | |||||
| public timeRenderer(params): string { | |||||
| return Utils.getDateTimeToDisplay(params.value, false, true); | |||||
| } | |||||
| /** | |||||
| * Date renderer | |||||
| */ | |||||
| public dateRenderer(params): string { | |||||
| return Utils.getDateTimeToDisplay(params.value); | |||||
| } | |||||
| /** | |||||
| * Datetime renderer | |||||
| */ | |||||
| public dateTimeRenderer(params): string { | |||||
| return Utils.getDateTimeToDisplay(params.value, true, true); | |||||
| } | |||||
| /** | |||||
| * Date comparator | |||||
| */ | |||||
| public dateComparator(date1, date2, callbackCheck: boolean = true) { | |||||
| if (date1 === null && date2 === null) { | |||||
| return 0; | |||||
| } | |||||
| if (date1 === null) { | |||||
| return -1; | |||||
| } | |||||
| if (date2 === null) { | |||||
| return 1; | |||||
| } | |||||
| const stamp1: number = Utils.getParsedDate(date1); | |||||
| const stamp2: number = Utils.getParsedDate(date2); | |||||
| return stamp1 < stamp2 ? 1 : stamp1 === stamp2 ? 0 : -1; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| <div class="home-btn" routerLink="/start"> | |||||
| Startseite | |||||
| </div> | |||||
| <ul id="navigation"> | |||||
| <li *ngFor="let navItem of navItems" routerLink="{{navItem.route}}"> | |||||
| <p>{{navItem.name}}</p> | |||||
| </li> | |||||
| </ul> | |||||
| <div id="logout" (click)="logout()">Logout</div> | |||||
| @@ -0,0 +1,37 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | |||||
| import {AppService} from '../../services/app.service'; | |||||
| @Component({ | |||||
| selector: 'app-aside', | |||||
| templateUrl: './aside.component.html', | |||||
| styleUrls: ['./aside.component.scss'] | |||||
| }) | |||||
| export class AsideComponent implements OnInit { | |||||
| /* Navigation items */ | |||||
| public navItems: Array<object>; | |||||
| constructor(public appService: AppService) { } | |||||
| ngOnInit() { | |||||
| this.navItems = []; | |||||
| this.navItems.push({name: 'Kunden', route: '/customer-management', className: 'customer-management'}); | |||||
| this.navItems.push({name: 'Betreiber', route: '/operators', className: 'operators'}); | |||||
| this.navItems.push({name: 'Produzenten', route: '/productions', className: 'productions'}); | |||||
| this.navItems.push({name: 'Service', route: '/service', className: 'service'}); | |||||
| // this.navItems.push({name: 'Buchhaltung', route: '/accounting', className: 'accounting'}); | |||||
| // this.navItems.push({name: 'Produktion', route: '/production', className: 'production'}); | |||||
| // this.navItems.push({name: 'Service', route: '/service', className: 'service'}); | |||||
| // this.navItems.push({name: 'Technik', route: '/technique', className: 'technique'}); | |||||
| // this.navItems.push({name: 'Vertrieb', route: '/sales', className: 'sales'}); | |||||
| } | |||||
| /** | |||||
| * Logout | |||||
| */ | |||||
| public logout(): void { | |||||
| this.appService.logout(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,3 @@ | |||||
| <ul class="calendar-legend"> | |||||
| <li *ngFor="let l of legendEntries" class="legend-color--{{l.type}}">{{l.name}}</li> | |||||
| </ul> | |||||
| @@ -0,0 +1,34 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | |||||
| import {ICalendarLegend} from '../../model/entities/calendar-legend'; | |||||
| @Component({ | |||||
| selector: 'app-calendar-legend', | |||||
| templateUrl: './calendar-legend.component.html', | |||||
| styleUrls: ['./calendar-legend.component.scss'] | |||||
| }) | |||||
| export class CalendarLegendComponent implements OnInit { | |||||
| public legendEntries: ICalendarLegend[]; | |||||
| constructor() { } | |||||
| ngOnInit() { | |||||
| // this.legendEntries = []; | |||||
| this.legendEntries = [ | |||||
| {id: 1, type: 'customer', name: 'Kunden'}, | |||||
| {id: 2, type: 'operator', name: 'Betreiber'}, | |||||
| {id: 3, type: 'internal', name: 'Intern'}, | |||||
| {id: 4, type: 'production', name: 'Produzenten'}, | |||||
| {id: 5, type: 'service', name: 'Service'}, | |||||
| ]; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * @param legendEntries | |||||
| */ | |||||
| public setData(legendEntries: []): void { | |||||
| // this.legendEntries = legendEntries; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| // import {ScrollToService} from '@nicky-lenaers/ngx-scroll-to'; | |||||
| export class FormComponent { | |||||
| public errorMsg: string; | |||||
| public isCreationMode: boolean; | |||||
| // protected scrollToService: ScrollToService; | |||||
| // constructor(scrollToService: ScrollToService) { | |||||
| // this.scrollToService = scrollToService; | |||||
| // this.errorMsg = ''; | |||||
| // this.isCreationMode = false; | |||||
| // } | |||||
| constructor() { | |||||
| this.errorMsg = ''; | |||||
| this.isCreationMode = false; | |||||
| } | |||||
| public checkDate(e: any): void { | |||||
| const checkedDate: Date = new Date(e.target.value.replace(/\s/, 'T')); | |||||
| const today: Date = new Date(); | |||||
| today.setHours(0); | |||||
| today.setMinutes(0); | |||||
| today.setSeconds(1); | |||||
| if (checkedDate < today) { | |||||
| alert('Hinweis: Das Datum liegt in der Vergangenheit.\nVergangene Termine können nicht bearbeitet oder gelöscht werden.'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Configures scrollToService - https://www.npmjs.com/package/@nicky-lenaers/ngx-scroll-to | |||||
| */ | |||||
| // public scrollUp(identifier: string): void { | |||||
| // this.scrollToService.scrollTo({target: identifier}); | |||||
| // } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| <div class="message-modal" [class.active]="showError == true || showClientError == true"> | |||||
| <div class="message message--error" [class.active]="showError == true || showClientError == true"> | |||||
| <p>{{message}}</p> | |||||
| <p *ngIf="optionalInfo != null" [innerHTML]="optionalInfo"></p> | |||||
| <button class="button" (click)="closeComponent()" title="Fenster schließen">schließen</button> | |||||
| </div> | |||||
| </div> | |||||
| <div class="message-modal" [class.active]="showSuccess == true"> | |||||
| <div class="message message--success" [class.active]="showSuccess == true"> | |||||
| <p>{{message}}</p> | |||||
| </div> | |||||
| </div> | |||||
| <div class="message-modal" [class.active]="showInfo == true"> | |||||
| <div class="message message--info" [class.active]="showInfo == true"> | |||||
| <p>{{message}}</p> | |||||
| <button class="button" (click)="closeComponent()" title="Fenster schließen">schließen</button> | |||||
| </div> | |||||
| </div> | |||||
| @@ -0,0 +1,141 @@ | |||||
| import {Component, OnDestroy, OnInit} from '@angular/core'; | |||||
| import {MessageService} from '../../services/message.service'; | |||||
| import {Subscription} from 'rxjs'; | |||||
| @Component({ | |||||
| selector: 'app-message', | |||||
| templateUrl: './message.component.html', | |||||
| styleUrls: ['./message.component.scss'] | |||||
| }) | |||||
| export class MessageComponent implements OnInit, OnDestroy { | |||||
| // Message display time in milliseconds | |||||
| static MESSAGE_DISPLAY_TIME = 2000; | |||||
| // Client Error codes | |||||
| static CLIENT_ERROR_DATA = 1; | |||||
| static CLIENT_ERROR_VALIDATION_FAILED = 2; | |||||
| private messageSub: Subscription; | |||||
| public showError: boolean; | |||||
| public showSuccess: boolean; | |||||
| public showInfo: boolean; | |||||
| public showClientError: boolean; | |||||
| public message: string; | |||||
| public optionalInfo: string; | |||||
| public errorMessages: string[]; | |||||
| public successMessages: string[]; | |||||
| public infoMessages: string[]; | |||||
| public clientErrorMessages: string[]; | |||||
| constructor(private messageService: MessageService) { | |||||
| // Init client error messages | |||||
| this.clientErrorMessages = []; | |||||
| this.clientErrorMessages[MessageComponent.CLIENT_ERROR_DATA] = 'Die Daten sind unvollständig oder fehlerhaft.'; | |||||
| this.clientErrorMessages[MessageComponent.CLIENT_ERROR_VALIDATION_FAILED] = 'Die Daten sind nicht valide, kein Speichern möglich. Bitte überprüfen Sie Ihre Eingaben.'; | |||||
| } | |||||
| ngOnInit() { | |||||
| this.messageSub = this.messageService.getMessage$().subscribe( | |||||
| data => { | |||||
| if (data !== null) { | |||||
| if (data.error_code != 0) { | |||||
| this.showErrorMessage(data.error_code, data.error_msg, data.optional_info); | |||||
| } else if (data.success_code != 0) { | |||||
| this.showSuccessMessage(data.success_code, data.success_msg); | |||||
| } else if (data.info_code != 0) { | |||||
| this.showInfoMessage(data.info_code, data.info_msg); | |||||
| } else if (data.client_error_code != 0) { | |||||
| this.showClientErrorMessage(data.client_error_code); | |||||
| } | |||||
| } | |||||
| }); | |||||
| this.showError = false; | |||||
| this.showSuccess = false; | |||||
| this.showInfo = false; | |||||
| this.showClientError = false; | |||||
| this.message = ''; | |||||
| this.optionalInfo = null; | |||||
| } | |||||
| /** | |||||
| * Shows error message | |||||
| * @param {number} errorCode | |||||
| * @param {string} errorMsg | |||||
| * @param {string} optionalInfo | |||||
| */ | |||||
| public showErrorMessage(errorCode: number, errorMsg: string, optionalInfo: string = null): void { | |||||
| this.showError = true; | |||||
| this.showSuccess = false; | |||||
| this.showInfo = false; | |||||
| this.showClientError = false; | |||||
| this.message = errorMsg; | |||||
| // Set optional info (if exists) | |||||
| this.optionalInfo = null !== optionalInfo ? optionalInfo : null; | |||||
| } | |||||
| /** | |||||
| * Shows success message | |||||
| * @param {number} successCode | |||||
| * @param {string} successMsg | |||||
| */ | |||||
| public showSuccessMessage(successCode: number, successMsg: string): void { | |||||
| this.showSuccess = true; | |||||
| this.showError = false; | |||||
| this.showInfo = false; | |||||
| this.showClientError = false; | |||||
| const me: MessageComponent = this; | |||||
| this.message = successMsg; | |||||
| setTimeout(function() { | |||||
| me.closeComponent(); | |||||
| }, MessageComponent.MESSAGE_DISPLAY_TIME); | |||||
| } | |||||
| /** | |||||
| * Shows info message | |||||
| * @param {number} infoCode | |||||
| * @param {string} infoMsg | |||||
| */ | |||||
| public showInfoMessage(infoCode: number, infoMsg: string): void { | |||||
| this.showInfo = true; | |||||
| this.showSuccess = false; | |||||
| this.showError = false; | |||||
| this.showClientError = false; | |||||
| this.message = infoMsg; | |||||
| } | |||||
| /** | |||||
| * Shows client error message | |||||
| * @param {number} clientErrorCode | |||||
| */ | |||||
| public showClientErrorMessage(clientErrorCode: number): void { | |||||
| this.showInfo = false; | |||||
| this.showSuccess = false; | |||||
| this.showError = false; | |||||
| this.showClientError = true; | |||||
| if (this.clientErrorMessages[clientErrorCode]) { | |||||
| this.message = this.clientErrorMessages[clientErrorCode]; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Hides error- / success-message | |||||
| */ | |||||
| public closeComponent(): void { | |||||
| this.showError = false; | |||||
| this.showSuccess = false; | |||||
| this.showInfo = false; | |||||
| this.showClientError = false; | |||||
| this.message = ''; | |||||
| } | |||||
| ngOnDestroy(): void { | |||||
| if (this.messageSub !== null) { | |||||
| this.messageSub.unsubscribe(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,8 @@ | |||||
| <div class="modal" (click)="closeModal()" [class.overlay]="overlay" [class.modal-open]="showModal" *ngIf="isVisible"></div> | |||||
| <div class="modal-content" [class.overlay]="overlay" [class.modal-open]="showModal" *ngIf="isVisible"> | |||||
| <div class="modal-content-inner"> | |||||
| <ng-content></ng-content> | |||||
| </div> | |||||
| <span class="close" (click)="closeModal()">X</span> | |||||
| </div> | |||||
| @@ -0,0 +1,79 @@ | |||||
| import {AfterContentInit, Component, EventEmitter, OnInit, Input, Output, Renderer2} from '@angular/core'; | |||||
| @Component({ | |||||
| selector: 'app-spt-modal', | |||||
| templateUrl: './modal.component.html', | |||||
| styleUrls: ['./modal.component.scss'] | |||||
| }) | |||||
| export class ModalComponent implements OnInit, AfterContentInit { | |||||
| @Input() autoclose = true; | |||||
| @Input() overlay = false; | |||||
| @Output() state: EventEmitter<boolean> = new EventEmitter<boolean>(); | |||||
| @Output() onClose: EventEmitter<boolean> = new EventEmitter<boolean>(); | |||||
| public showModal: boolean; | |||||
| public isVisible: boolean; | |||||
| constructor(private renderer: Renderer2) { } | |||||
| ngOnInit() { | |||||
| this.showModal = false; | |||||
| this.isVisible = false; | |||||
| } | |||||
| /** | |||||
| * After content children are set | |||||
| */ | |||||
| ngAfterContentInit() { | |||||
| this.isVisible = true; | |||||
| } | |||||
| /** | |||||
| * Opens modal dialog | |||||
| */ | |||||
| public openModal(): void { | |||||
| this.showModal = true; | |||||
| this.renderer.addClass(document.body, 'body--modal-open'); | |||||
| this.state.emit(this.showModal); | |||||
| } | |||||
| /** | |||||
| * Closes modal dialog if no parameter showDialog == false | |||||
| */ | |||||
| public closeModal(): void { | |||||
| if (!this.autoclose) { | |||||
| this.onClose.emit(true); | |||||
| } else { | |||||
| this.exitModal(); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Closes modal dialog with confirm message | |||||
| */ | |||||
| public closeWithConfirm(message: string): boolean { | |||||
| if (confirm(message)) { | |||||
| this.exitModal(); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Closes modal dialog | |||||
| */ | |||||
| public exitModal(): void { | |||||
| this.showModal = false; | |||||
| this.renderer.removeClass(document.body, 'body--modal-open'); | |||||
| this.state.emit(this.showModal); | |||||
| } | |||||
| /** | |||||
| * Returns if modal is open | |||||
| */ | |||||
| public isOpen(): boolean { | |||||
| return this.showModal; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,3 @@ | |||||
| <div class="tab-entry" [class.active]="active"> | |||||
| <ng-content></ng-content> | |||||
| </div> | |||||
| @@ -0,0 +1,21 @@ | |||||
| import {Component, Input, OnInit} from '@angular/core'; | |||||
| @Component({ | |||||
| selector: 'app-tab', | |||||
| templateUrl: './tab.component.html', | |||||
| styleUrls: ['./tab.component.scss'] | |||||
| }) | |||||
| export class TabComponent implements OnInit { | |||||
| @Input('tabTitle') title: string; | |||||
| @Input('tabIndex') index: number; | |||||
| @Input() active = false; | |||||
| @Input() disabled = false; | |||||
| constructor() { } | |||||
| ngOnInit() { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| <div class="tabs"> | |||||
| <ul class="tabs-nav"> | |||||
| <li *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active" [class.disabled]="tab.disabled"> | |||||
| {{tab.title}} | |||||
| </li> | |||||
| </ul> | |||||
| <div class="tab-entries"> | |||||
| <ng-content></ng-content> | |||||
| </div> | |||||
| </div> | |||||
| @@ -0,0 +1,99 @@ | |||||
| import { | |||||
| AfterContentInit, | |||||
| Component, | |||||
| ContentChildren, | |||||
| EventEmitter, | |||||
| Input, | |||||
| Output, | |||||
| QueryList | |||||
| } from '@angular/core'; | |||||
| import {TabComponent} from '../tab/tab.component'; | |||||
| @Component({ | |||||
| selector: 'app-tabs', | |||||
| templateUrl: './tabs.component.html', | |||||
| styleUrls: ['./tabs.component.scss'] | |||||
| }) | |||||
| export class TabsComponent implements AfterContentInit { | |||||
| @ContentChildren(TabComponent) tabs: QueryList<TabComponent>; | |||||
| @Input() notifyOnChange = false; | |||||
| @Output() onChange: EventEmitter<number> = new EventEmitter<number>(); | |||||
| constructor() { } | |||||
| /** | |||||
| * After content children are set | |||||
| */ | |||||
| public ngAfterContentInit(): void { | |||||
| // get all active tabs | |||||
| const activeTabs = this.tabs.filter((tab) => tab.active); | |||||
| // if there is no active tab set, activate the first | |||||
| if (activeTabs.length === 0) { | |||||
| this.setSelectedTabIndex(0); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Eventlistener for tab change | |||||
| * @param {TabComponent} tab | |||||
| */ | |||||
| public selectTab(tab: TabComponent): void { | |||||
| if (!tab.disabled) { | |||||
| if (this.notifyOnChange) { | |||||
| this.onChange.emit(tab.index); | |||||
| } else { | |||||
| this.setSelectedTabIndex(tab.index); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Sets selected tab by given | |||||
| * @param {number} i | |||||
| */ | |||||
| public setSelectedTabIndex(i: number): void { | |||||
| if (this.tabs != null) { | |||||
| const tabsArray: TabComponent[] = this.tabs.toArray(); | |||||
| if (i < tabsArray.length) { | |||||
| // Deactivate all tabs | |||||
| tabsArray.forEach(tab => tab.active = false); | |||||
| // Activate the tab the user has clicked on. | |||||
| tabsArray[i].active = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Disable tab at given index | |||||
| * @param {number} i | |||||
| */ | |||||
| public disableTabIndex(i: number): void { | |||||
| this.toggleTabIndex(i, false); | |||||
| } | |||||
| /** | |||||
| * Enable tab at given index | |||||
| * @param {number} i | |||||
| */ | |||||
| public enableTabIndex(i: number): void { | |||||
| this.toggleTabIndex(i, true); | |||||
| } | |||||
| /** | |||||
| * Sets selected tab disabled/enabled | |||||
| * @param {number} i | |||||
| * @param {boolean} enable | |||||
| */ | |||||
| private toggleTabIndex(i: number, enable: boolean): void { | |||||
| if (this.tabs != null) { | |||||
| const tabsArray: TabComponent[] = this.tabs.toArray(); | |||||
| if (i < tabsArray.length) { | |||||
| tabsArray[i].disabled = !enable; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,186 @@ | |||||
| import {IMessage} from '../model/virtual/message'; | |||||
| import {ICustomer} from '../model/entities/customer'; | |||||
| import {ICustomerContact} from '../model/entities/customer-contact'; | |||||
| import {ICustomerNote} from '../model/entities/customer-note'; | |||||
| import {ICustomerMeeting} from '../model/entities/customer-meeting'; | |||||
| import {IUser} from '../model/entities/user'; | |||||
| import {IMeetingType} from '../model/entities/meeting-type'; | |||||
| import {IInternalMeeting} from '../model/entities/internal-meeting'; | |||||
| import {Utils} from '../utils/utils'; | |||||
| export class Factory { | |||||
| static getEmptyUser(): IUser { | |||||
| const emptyUser: {} = { | |||||
| id: null, | |||||
| email: null, | |||||
| firstname: null, | |||||
| lastname: null, | |||||
| active: false | |||||
| }; | |||||
| return emptyUser as IUser; | |||||
| } | |||||
| /** | |||||
| * Returns empty message | |||||
| */ | |||||
| static getEmptyMessage(errorCode: number, errorMsg: string, successCode: number, successMsg: string, | |||||
| infoCode: number, infoMsg: string, clientErrorCode: number, optionalInfo: string = null): IMessage { | |||||
| const emptyMessage: {} = { | |||||
| error_code: errorCode, | |||||
| error_msg: errorMsg, | |||||
| success_code: successCode, | |||||
| success_msg: successMsg, | |||||
| info_code: infoCode, | |||||
| info_msg: infoMsg, | |||||
| client_error_code: clientErrorCode, | |||||
| optional_info: optionalInfo | |||||
| }; | |||||
| return emptyMessage as IMessage; | |||||
| } | |||||
| /** | |||||
| * Returns empty customer | |||||
| */ | |||||
| static getEmptyCustomer(): ICustomer { | |||||
| const emptyCustomer: {} = { | |||||
| id: null, | |||||
| old_plp_id: null, | |||||
| name: null, | |||||
| name_additional: null, | |||||
| consultant_user_id: null, | |||||
| street: null, | |||||
| street_no: null, | |||||
| zip: null, | |||||
| city: null, | |||||
| country_id: null, | |||||
| url: null, | |||||
| email: null, | |||||
| phone_no: null, | |||||
| mobile_no: null, | |||||
| fax_no: null, | |||||
| comment: null, | |||||
| active: true, | |||||
| }; | |||||
| return emptyCustomer as ICustomer; | |||||
| } | |||||
| /** | |||||
| * Returns empty customer contact person | |||||
| */ | |||||
| static getEmptyCustomerContact(customerId: number = null): ICustomerContact { | |||||
| const emptyCustomerContact: {} = { | |||||
| id: null, | |||||
| customer_id: customerId, | |||||
| gender: null, | |||||
| firstname: null, | |||||
| lastname: null, | |||||
| email: null, | |||||
| phone_no: null, | |||||
| mobile_no: null, | |||||
| fax_no: null, | |||||
| department: null, | |||||
| date_of_birth: null, | |||||
| comment: null, | |||||
| street: null, | |||||
| street_no: null, | |||||
| zip: null, | |||||
| city: null, | |||||
| country_id: null, | |||||
| is_xmas_mail_recipient: false, | |||||
| }; | |||||
| return emptyCustomerContact as ICustomerContact; | |||||
| } | |||||
| static getEmptyCustomerNote(customerId: number = null, noteDate: string = null): ICustomerNote { | |||||
| const emptyCustomerNote: {} = { | |||||
| id: customerId, | |||||
| customer_id: null, | |||||
| customer_contact_id: null, | |||||
| user_id: null, | |||||
| gender: null, | |||||
| firstname: null, | |||||
| lastname: null, | |||||
| email: null, | |||||
| phone_no: null, | |||||
| mobile_no: null, | |||||
| fax_no: null, | |||||
| department: null, | |||||
| title: null, | |||||
| comment: null, | |||||
| note_date: noteDate, | |||||
| creation_date: null, | |||||
| creation_user_id: null, | |||||
| creation_user_firstname: null, | |||||
| creation_user_lastname: null | |||||
| }; | |||||
| return emptyCustomerNote as ICustomerNote; | |||||
| } | |||||
| static getEmptyCustomerMeeting(customerId: number = null): ICustomerMeeting { | |||||
| const emptyCustomerMeeting: {} = { | |||||
| id: null, | |||||
| customer_id: customerId, | |||||
| creation_user_id: null, | |||||
| owner_user_id: null, | |||||
| meeting_type_id: null, | |||||
| is_option_meeting: false, | |||||
| title: null, | |||||
| description: null, | |||||
| start_date: Utils.getCurrentDate() + ' 09:00:00', | |||||
| end_date: Utils.getCurrentDate() + ' 18:00:00', | |||||
| customer_contact_id: null, | |||||
| gender: null, | |||||
| firstname: null, | |||||
| lastname: null, | |||||
| email: null, | |||||
| phone_no: null, | |||||
| mobile_no: null, | |||||
| department: null, | |||||
| street: null, | |||||
| street_no: null, | |||||
| zip: null, | |||||
| city: null, | |||||
| country_id: null, | |||||
| report: null, | |||||
| first_reminder_sent: false, | |||||
| second_reminder_sent: false, | |||||
| report_done: false, | |||||
| report_reminder_sent: false, | |||||
| creation_date: null, | |||||
| v_participants: [], | |||||
| v_is_editable: true | |||||
| }; | |||||
| return emptyCustomerMeeting as ICustomerMeeting; | |||||
| } | |||||
| static getEmptyInternalMeeting(): IInternalMeeting { | |||||
| const emptyInternalMeeting: {} = { | |||||
| id: null, | |||||
| creation_user_id: null, | |||||
| owner_user_id: null, | |||||
| title: null, | |||||
| description: null, | |||||
| start_date: Utils.getCurrentDate() + ' 09:00:00', | |||||
| end_date: Utils.getCurrentDate() + ' 18:00:00', | |||||
| report: null, | |||||
| first_reminder_sent: false, | |||||
| second_reminder_sent: false, | |||||
| report_done: false, | |||||
| report_reminder_sent: false, | |||||
| creation_date: null, | |||||
| v_participants: [], | |||||
| v_is_editable: true | |||||
| }; | |||||
| return emptyInternalMeeting as IInternalMeeting; | |||||
| } | |||||
| static getEmptyMeetingType(): IMeetingType { | |||||
| const emptyMeetingType: {} = { | |||||
| id: null, | |||||
| type: null, | |||||
| name: null, | |||||
| }; | |||||
| return emptyMeetingType as IMeetingType; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| <div class="block-renderer"></div> | |||||
| @@ -0,0 +1 @@ | |||||
| @@ -0,0 +1,19 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridEditorComponent} from '../../grid-editor-component'; | |||||
| @Component({ | |||||
| selector: 'app-grid-blocked-editor', | |||||
| templateUrl: './grid-blocked-editor.component.html', | |||||
| styleUrls: ['./grid-blocked-editor.component.scss'] | |||||
| }) | |||||
| export class GridBlockedEditorComponent extends GridEditorComponent { | |||||
| /** | |||||
| * Sets focus | |||||
| */ | |||||
| public afterGuiAttached(): void { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| <div class="block-renderer"></div> | |||||
| @@ -0,0 +1,21 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridRendererComponent} from '../../grid-renderer-component'; | |||||
| @Component({ | |||||
| selector: 'app-grid-blocked-renderer', | |||||
| templateUrl: './grid-blocked-renderer.component.html', | |||||
| styleUrls: ['./grid-blocked-renderer.component.scss'] | |||||
| }) | |||||
| export class GridBlockedRendererComponent extends GridRendererComponent { | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| * @param params | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| params.eGridCell.classList.remove('error'); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| export interface IGridCellEditorParams { | |||||
| itemKey: string; | |||||
| itemValueKeys: string[]; | |||||
| relatesOnDgField: string; | |||||
| values: any[]; | |||||
| exclusive: boolean; | |||||
| exclusiveCompareDataKey: string; | |||||
| exclusiveCompareData: any[]; | |||||
| } | |||||
| @@ -0,0 +1,8 @@ | |||||
| export interface IGridCellParams { | |||||
| type: string; | |||||
| min: number; | |||||
| max: number; | |||||
| unit: string; | |||||
| mandatory: boolean; | |||||
| altVal: number; | |||||
| } | |||||
| @@ -0,0 +1,3 @@ | |||||
| <input id="checkbox-editor-{{params.rowIndex}}-{{params.column.colId}}" type="checkbox" #editorInput [checked]="value" | |||||
| (change)="valueIsChanged($event)"/> | |||||
| <label for="checkbox-editor-{{params.rowIndex}}-{{params.column.colId}}"></label> | |||||
| @@ -0,0 +1,41 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridEditorComponent} from '../../grid-editor-component'; | |||||
| import {Utils} from '../../../utils/utils'; | |||||
| @Component({ | |||||
| selector: 'app-grid-checkbox-editor', | |||||
| templateUrl: './grid-checkbox-editor.component.html', | |||||
| styleUrls: ['./grid-checkbox-editor.component.scss'] | |||||
| }) | |||||
| export class GridCheckboxEditorComponent extends GridEditorComponent { | |||||
| /** | |||||
| * Initialize checkbox editor (called after double-click) | |||||
| * @param params | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| // Set true to false, because of single click | |||||
| this.value = null === this.value ? true : !this.value; | |||||
| if (!Utils.isBoolean(this.value)) { | |||||
| throw new Error('checkbox requires boolean value'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Changes value on checkbox click | |||||
| * @param e | |||||
| */ | |||||
| public valueIsChanged = (e) => { | |||||
| this.value = !this.value; | |||||
| } | |||||
| /** | |||||
| * Overwrite | |||||
| */ | |||||
| public afterGuiAttached(): void { | |||||
| // do nothing | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,8 @@ | |||||
| <input id="checkbox-renderer-{{params.rowIndex}}-{{params.colDef.field}}" *ngIf="params.colDef.editable" type="checkbox" | |||||
| [checked]="value" (click)="startEditing()"/> | |||||
| <label for="checkbox-renderer-{{params.rowIndex}}-{{params.colDef.field}}"></label> | |||||
| <ng-container *ngIf="!params.colDef.editable"> | |||||
| <input id="checkbox-renderer-{{params.rowIndex}}-{{params.colDef.field}}" disabled type="checkbox" | |||||
| [checked]="value"/> | |||||
| <label for="checkbox-renderer-{{params.rowIndex}}-{{params.colDef.field}}"></label> | |||||
| </ng-container> | |||||
| @@ -0,0 +1,34 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridRendererComponent} from '../../grid-renderer-component'; | |||||
| import {Utils} from '../../../utils/utils'; | |||||
| @Component({ | |||||
| selector: 'app-grid-checkbox-renderer', | |||||
| templateUrl: './grid-checkbox-renderer.component.html', | |||||
| styleUrls: ['./grid-checkbox-renderer.component.scss'] | |||||
| }) | |||||
| export class GridCheckboxRendererComponent extends GridRendererComponent { | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| * @param params | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| this.value = null === this.value ? false : this.value; | |||||
| if (!Utils.isBoolean(this.value)) { | |||||
| throw new Error('checkbox requires boolean value'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Triggers cell editing start without using "singleClickEdit" | |||||
| */ | |||||
| public startEditing(): void { | |||||
| const startEditingParams: {} = { | |||||
| rowIndex: this.params.rowIndex, | |||||
| colKey: this.params.column.getId() | |||||
| }; | |||||
| this.params.api.startEditingCell(startEditingParams); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| <input type="date" #editorInput (change)="valueIsChanged($event)" value="{{value}}" /> | |||||
| @@ -0,0 +1,12 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridEditorComponent} from '../../grid-editor-component'; | |||||
| @Component({ | |||||
| selector: 'app-grid-checkbox-editor', | |||||
| templateUrl: './grid-date-editor.component.html', | |||||
| styleUrls: ['./grid-date-editor.component.scss'] | |||||
| }) | |||||
| export class GridDateEditorComponent extends GridEditorComponent { | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| {{displayedValue}} | |||||
| @@ -0,0 +1,27 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridRendererComponent} from '../../grid-renderer-component'; | |||||
| @Component({ | |||||
| selector: 'app-grid-date-renderer', | |||||
| templateUrl: './grid-date-renderer.component.html', | |||||
| styleUrls: ['./grid-date-renderer.component.scss'] | |||||
| }) | |||||
| export class GridDateRendererComponent extends GridRendererComponent { | |||||
| public displayedValue: string; | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| let tempValue: string[] = []; | |||||
| if (null === this.value || undefined === this.value) { | |||||
| this.displayedValue = ''; | |||||
| } else { | |||||
| tempValue = this.value.split('-'); | |||||
| this.displayedValue = tempValue[2] + '.' + tempValue[1] + '.' + tempValue[0]; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,82 @@ | |||||
| import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; | |||||
| import {ICellEditorAngularComp} from 'ag-grid-angular'; | |||||
| import {IGridCellParams} from './grid-cell-params'; | |||||
| @Component({ | |||||
| template: '', | |||||
| }) | |||||
| export class GridEditorComponent implements OnInit, ICellEditorAngularComp { | |||||
| @ViewChild('editorInput', { static: true }) inputField: ElementRef; | |||||
| readonly GRID_ERROR_EMPTY: string = 'Bitte geben Sie einen Wert ein.'; | |||||
| readonly GRID_ERROR_INT: string = 'Bitte geben Sie einen ganzahligen Wert ein.'; | |||||
| readonly GRID_ERROR_FLOAT: string = 'Bitte geben Sie einen dezimalen oder ganzzahligen Wert ein.'; | |||||
| readonly GRID_ERROR_RANGE_MIN: string = 'Minimaler Wert: '; | |||||
| readonly GRID_ERROR_RANGE_MAX: string = 'Maximaler Wert: '; | |||||
| readonly GRID_ERROR_RANGE_ALTERNATIVE: string = 'Alternativer Wert: '; | |||||
| public value: any; | |||||
| public params: any; | |||||
| public cellParams: IGridCellParams; | |||||
| constructor() { | |||||
| this.cellParams = {} as IGridCellParams; | |||||
| } | |||||
| ngOnInit() { | |||||
| } | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| this.params = params; | |||||
| this.cellParams.type = params.type; | |||||
| this.cellParams.min = params.min; | |||||
| this.cellParams.max = params.max; | |||||
| this.cellParams.unit = params.unit; | |||||
| this.cellParams.mandatory = params.mandatory; | |||||
| this.cellParams.altVal = params.altVal; | |||||
| this.value = params.value; | |||||
| } | |||||
| /** | |||||
| * Returns value to cell renderer | |||||
| */ | |||||
| getValue(): any { | |||||
| return this.value; | |||||
| } | |||||
| /** | |||||
| * Cancels before value input (this editor is never editable) | |||||
| */ | |||||
| public isCancelBeforeStart(): boolean { | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Cancels input of inserted value under certain conditions | |||||
| */ | |||||
| isCancelAfterEnd(): boolean { | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Changes value on click | |||||
| */ | |||||
| public valueIsChanged = (e) => { | |||||
| this.value = e.srcElement.value; | |||||
| } | |||||
| /** | |||||
| * Sets focus | |||||
| */ | |||||
| public afterGuiAttached(): void { | |||||
| this.inputField.nativeElement.focus(); | |||||
| this.inputField.nativeElement.select(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| import {IGridCellParams} from './grid-cell-params'; | |||||
| export class GridFactory { | |||||
| /** | |||||
| * Returns param object for cell renderer and -editors | |||||
| */ | |||||
| static getGridCellParams(type: string = null, min: number = null, | |||||
| max: number = null, unit: string = null, | |||||
| mandatory: boolean = false, altValue: number = null): IGridCellParams { | |||||
| const gridCellParams: IGridCellParams = {} as IGridCellParams; | |||||
| gridCellParams.type = type; | |||||
| gridCellParams.min = min; | |||||
| gridCellParams.max = max; | |||||
| gridCellParams.unit = unit; | |||||
| gridCellParams.mandatory = mandatory; | |||||
| gridCellParams.altVal = altValue; | |||||
| return gridCellParams; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| <input type="text" [required]="cellParams.mandatory" #editorInput (change)="valueIsChanged($event)" [(ngModel)]="value" value="{{value}}" /> | |||||
| @@ -0,0 +1,107 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridEditorComponent} from '../../grid-editor-component'; | |||||
| import {AgGridComponentConst} from '../../../components/ag-grid-component-const'; | |||||
| import {Utils} from '../../../utils/utils'; | |||||
| @Component({ | |||||
| selector: 'app-grid-input-editor', | |||||
| templateUrl: './grid-input-editor.component.html', | |||||
| styleUrls: ['./grid-input-editor.component.scss'] | |||||
| }) | |||||
| export class GridInputEditorComponent extends GridEditorComponent { | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| if (this.cellParams !== undefined && this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_FLOAT) { | |||||
| // Replace "." with "," for display purpose | |||||
| this.value = isNaN(this.value) || null === this.value ? '' : parseFloat(this.value).toFixed(2).replace('.', ','); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Returns value to cell renderer | |||||
| */ | |||||
| getValue(): any { | |||||
| if (this.cellParams !== undefined && this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_FLOAT) { | |||||
| // Replace "." with "," for display purpose and cast back to number | |||||
| this.value = this.value.replace(',', '.'); | |||||
| this.value = isNaN(this.value) || null === this.value ? '' : Number(parseFloat(this.value).toFixed(2)); | |||||
| } | |||||
| return this.value; | |||||
| } | |||||
| /** | |||||
| * Cancels input of inserted value under certain conditions | |||||
| */ | |||||
| isCancelAfterEnd(): boolean { | |||||
| if (this.cellParams.mandatory && (null === this.value || this.value.toString().trim() === '')) { | |||||
| // Null value or empty string | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_EMPTY); | |||||
| return true; | |||||
| } | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_INT && !Utils.isInteger(this.value)) { | |||||
| // No int value given | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_INT); | |||||
| return true; | |||||
| } | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_FLOAT && !Utils.isFloat(this.value)) { | |||||
| // No float value given | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_FLOAT); | |||||
| return true; | |||||
| } | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_INT || | |||||
| this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_FLOAT) { | |||||
| const numericVal: number = Number(String(this.value).replace(',', '.')); | |||||
| if (null !== this.cellParams.min && numericVal < Number(this.cellParams.min)) { | |||||
| // Check on alternative value | |||||
| if (null !== this.cellParams.altVal) { | |||||
| if (numericVal === this.cellParams.altVal) { | |||||
| // Alternative value (to min and max) -> no error | |||||
| return false; | |||||
| } else { | |||||
| // Value is not in range and not alternative value - cancel value input | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_RANGE_MIN + this.cellParams.min + ' (' + this.GRID_ERROR_RANGE_ALTERNATIVE + this.cellParams.altVal + ')'); | |||||
| return true; | |||||
| } | |||||
| } else { | |||||
| // Value is not in range - cancel value input | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_RANGE_MIN + this.cellParams.min); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| if (null !== this.cellParams.max && numericVal > Number(this.cellParams.max)) { | |||||
| // Check on alternative value | |||||
| if (null !== this.cellParams.altVal) { | |||||
| if (numericVal === this.cellParams.altVal) { | |||||
| // Alternative value (to min and max) -> no error | |||||
| return false; | |||||
| } else { | |||||
| // Value is not in range and not alternative value - cancel value input | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_RANGE_MAX + this.cellParams.max + ' (' + this.GRID_ERROR_RANGE_ALTERNATIVE + this.cellParams.altVal + ')'); | |||||
| return true; | |||||
| } | |||||
| } else { | |||||
| // Value is not in range - cancel value input | |||||
| AgGridComponentConst.setGridInputError(this.GRID_ERROR_RANGE_MAX + this.cellParams.max); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Changes value on click | |||||
| * todo maybe this should get refacotred properly? | |||||
| */ | |||||
| public valueIsChanged = (e) => { | |||||
| this.value = e.srcElement.value; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| {{value}}<span *ngIf="cellParams.unit != null">{{cellParams.unit}}</span> | |||||
| @@ -0,0 +1,27 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridRendererComponent} from '../../grid-renderer-component'; | |||||
| import {AgGridComponentConst} from '../../../components/ag-grid-component-const'; | |||||
| @Component({ | |||||
| selector: 'app-grid-input-renderer', | |||||
| templateUrl: './grid-input-renderer.component.html', | |||||
| styleUrls: ['./grid-input-renderer.component.scss'] | |||||
| }) | |||||
| export class GridInputRendererComponent extends GridRendererComponent { | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| if (this.cellParams !== undefined) { | |||||
| // Replace "." with "," for display purpose | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_FLOAT) { | |||||
| this.value = isNaN(this.value) || null === this.value ? '' : parseFloat(parseFloat(this.value).toFixed(2)).toLocaleString('de-DE'); | |||||
| } | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_INT) { | |||||
| this.value = isNaN(this.value) || null === this.value ? '' : parseInt(parseInt(this.value).toFixed(2)).toLocaleString('de-DE'); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,49 @@ | |||||
| import {Component, OnInit} from '@angular/core'; | |||||
| import {ICellRendererAngularComp} from 'ag-grid-angular'; | |||||
| import {IGridCellParams} from './grid-cell-params'; | |||||
| @Component({ | |||||
| template: '', | |||||
| }) | |||||
| export class GridRendererComponent implements OnInit, ICellRendererAngularComp { | |||||
| public value: any; | |||||
| public params: any; | |||||
| public cellParams: IGridCellParams; | |||||
| constructor() { | |||||
| this.cellParams = {} as IGridCellParams; | |||||
| } | |||||
| ngOnInit() { | |||||
| } | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| this.params = params; | |||||
| this.cellParams.type = params.type; | |||||
| this.cellParams.min = params.min; | |||||
| this.cellParams.max = params.max; | |||||
| this.cellParams.unit = params.unit; | |||||
| this.cellParams.mandatory = params.mandatory; | |||||
| this.value = params.value; | |||||
| } | |||||
| /** | |||||
| * Sets value | |||||
| */ | |||||
| setValue(value: any): void { | |||||
| this.value = value; | |||||
| } | |||||
| /** | |||||
| * Refreshes cell - Gets called after editing | |||||
| */ | |||||
| refresh(params: any): boolean { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,4 @@ | |||||
| <select *ngIf="!noValueAvailable" (change)="getSelectedItem($event.target.value)"> | |||||
| <option *ngFor="let v of values" value="{{v.key}}" [selected]="v.key == selectedItem.key">{{v.value}}</option> | |||||
| </select> | |||||
| <ng-container *ngIf="noValueAvailable">Keine Werte vorhanden</ng-container> | |||||
| @@ -0,0 +1,8 @@ | |||||
| /* NOTE: column width 100% (app-grid-select-editor) in basic.scss */ | |||||
| select { | |||||
| width: 100%; | |||||
| max-width: 250px; | |||||
| } | |||||
| @@ -0,0 +1,104 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {IGridSelectItem} from '../grid-select-item'; | |||||
| import {GridEditorComponent} from '../../grid-editor-component'; | |||||
| import {GridSelectFactory} from '../grid-select-factory'; | |||||
| import {IGridCellEditorParams} from '../../grid-cell-editor-params'; | |||||
| @Component({ | |||||
| selector: 'app-grid-select-editor', | |||||
| templateUrl: './grid-select-editor.component.html', | |||||
| styleUrls: ['./grid-select-editor.component.scss'] | |||||
| }) | |||||
| export class GridSelectEditorComponent extends GridEditorComponent { | |||||
| public values: IGridSelectItem[]; | |||||
| public selectedItem: IGridSelectItem; | |||||
| public newValue: IGridSelectItem; | |||||
| public itemKey: string; | |||||
| public itemValueKeys: string[]; | |||||
| public noValueAvailable: boolean; | |||||
| /** | |||||
| * Initialize select editor (called after double-click) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| this.noValueAvailable = false; | |||||
| const cellEditorParams: IGridCellEditorParams = params.column.colDef.cellEditorParams as IGridCellEditorParams; | |||||
| this.values = []; | |||||
| this.itemKey = cellEditorParams.itemKey; | |||||
| this.itemValueKeys = cellEditorParams.itemValueKeys; | |||||
| const relatedValue: string | null = cellEditorParams.relatesOnDgField; | |||||
| if (null !== relatedValue) { | |||||
| const itemRange: any[] = (params.node.data.hasOwnProperty(relatedValue) && null !== params.node.data[relatedValue]) ? | |||||
| cellEditorParams.values[params.node.data[relatedValue]] : null; | |||||
| if (null !== itemRange && undefined !== itemRange) { | |||||
| itemRange.forEach(item => { | |||||
| this.values.push(GridSelectFactory.getGridSelectItem(item, this.itemKey, this.itemValueKeys)); | |||||
| }); | |||||
| } | |||||
| } else if (cellEditorParams.exclusive === true) { | |||||
| // Filter all items used by other selects (compareData) | |||||
| const compareExcludeKey: string = cellEditorParams.exclusiveCompareDataKey; | |||||
| const excludedItemKeys: {} = {}; | |||||
| cellEditorParams.exclusiveCompareData.forEach(item => { | |||||
| if (item[compareExcludeKey] !== this.value) { | |||||
| excludedItemKeys[item[compareExcludeKey]] = 1; | |||||
| } | |||||
| }); | |||||
| // This select contains only values that are not selected by other selects and it's own current item | |||||
| cellEditorParams.values.forEach(item => { | |||||
| if (!excludedItemKeys.hasOwnProperty(item[this.itemKey])) { | |||||
| this.values.push(GridSelectFactory.getGridSelectItem(item, this.itemKey, this.itemValueKeys)); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| // Static values (default behaviour) | |||||
| this.values = params.values; | |||||
| } | |||||
| this.selectedItem = this.getSelectedItem(params.value); | |||||
| } | |||||
| /** | |||||
| * Returns value to cell renderer | |||||
| */ | |||||
| getValue(): any { | |||||
| return null === this.newValue ?? this.newValue.key; | |||||
| } | |||||
| /** | |||||
| * Makes selectItem out of ID | |||||
| */ | |||||
| public getSelectedItem(e: any): IGridSelectItem { | |||||
| if (null !== e && e !== '') { | |||||
| for (const item of this.values) { | |||||
| if (item.key === e) { | |||||
| this.newValue = item; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| if (this.values.length > 0) { | |||||
| this.newValue = this.values[0]; | |||||
| } else { | |||||
| // No value in select available | |||||
| this.noValueAvailable = true; | |||||
| this.newValue = null; | |||||
| return; | |||||
| } | |||||
| } | |||||
| return this.newValue; | |||||
| } | |||||
| /** | |||||
| * Overwrite | |||||
| */ | |||||
| public afterGuiAttached(): void { | |||||
| // do nothing | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,123 @@ | |||||
| import {Const} from '../../utils/const'; | |||||
| import {IGridSelectItem} from './grid-select-item'; | |||||
| export class GridSelectFactory { | |||||
| /** | |||||
| * Returns select item for grid | |||||
| */ | |||||
| static getGridSelect(headerName: string, field: string, editable: boolean, values: any[], itemKey: string, | |||||
| itemValueKeys: string[], mandatory: boolean = true, relatesOnDgField: string = null, | |||||
| headerClass: string = null, pinned: boolean = false, cellClass: any = null, | |||||
| exclusive: boolean = false, exclusiveCompareData: any = null, exclusiveCompareDataKey: string = null): {} { | |||||
| if (null !== relatesOnDgField && exclusive) { | |||||
| // NOTE: This is not allowed for now, maybe needed in future | |||||
| throw new Error('Select can be either relative or exclusive'); | |||||
| } | |||||
| const res = { | |||||
| headerName, | |||||
| field, | |||||
| headerClass, | |||||
| pinned, | |||||
| editable, | |||||
| cellClass: null === cellClass ?? (editable ? '' : Const.CSS_NO_EDIT), | |||||
| cellRenderer: 'gridSelectRenderer', | |||||
| cellEditor: 'gridSelectEditor', | |||||
| cellEditorParams: { | |||||
| values: null === relatesOnDgField && !exclusive ? GridSelectFactory.getGridSelectItems(values, itemKey, itemValueKeys, mandatory) : values, | |||||
| itemKey, | |||||
| itemValueKeys, | |||||
| mandatory, | |||||
| relatesOnDgField, | |||||
| exclusive, | |||||
| exclusiveCompareData, | |||||
| exclusiveCompareDataKey | |||||
| } | |||||
| }; | |||||
| if (mandatory) { | |||||
| res['cellClassRules'] = { | |||||
| error(params) { | |||||
| return params.value == null; | |||||
| } | |||||
| }; | |||||
| } | |||||
| return res; | |||||
| } | |||||
| /** | |||||
| * Returns select item for grid without renderer and editor | |||||
| */ | |||||
| static getGridSelectForSelectors(headerName: string, field: string, editable: boolean, values: any[], itemKey: string, | |||||
| itemValueKeys: string[], mandatory: boolean = true, relatesOnDgField: string = null, | |||||
| headerClass: string = null, pinned: boolean = false, cellClass: any = null, | |||||
| exclusive: boolean = false, exclusiveCompareData: any = null, exclusiveCompareDataKey: string = null): {} { | |||||
| const res: {} = GridSelectFactory.getGridSelect( | |||||
| headerName, field, editable, values, itemKey, itemValueKeys, mandatory, relatesOnDgField, | |||||
| headerClass, pinned, cellClass, exclusive, exclusiveCompareData, exclusiveCompareDataKey | |||||
| ); | |||||
| delete res['cellRenderer']; | |||||
| delete res['cellEditor']; | |||||
| return res; | |||||
| } | |||||
| /** | |||||
| * Returns array of grid select items | |||||
| */ | |||||
| static getGridSelectItems(objArray: any[], key: string, valueKeys: string[], mandatory: boolean = true): IGridSelectItem[] { | |||||
| const res: IGridSelectItem[] = []; | |||||
| if (!mandatory) { | |||||
| // Add empty item (null value) | |||||
| res.push(GridSelectFactory.getGridSelectItem({key: null, value: '-'}, 'key', valueKeys, true)); | |||||
| } | |||||
| objArray.forEach(item => { | |||||
| res.push(GridSelectFactory.getGridSelectItem(item, key, valueKeys)); | |||||
| }); | |||||
| return res; | |||||
| } | |||||
| /** | |||||
| * Returns single grid select item | |||||
| */ | |||||
| static getGridSelectItem(obj: any, key: string, valueKeys: string[], isNullItem: boolean = false): IGridSelectItem { | |||||
| if (!obj.hasOwnProperty(key)) { | |||||
| throw new Error('GridFactory: Key not found: ' + key); | |||||
| } | |||||
| let gridItem: {} = {}; | |||||
| if (!isNullItem) { | |||||
| for (const item of valueKeys) { | |||||
| if (!obj.hasOwnProperty(key) || !obj.hasOwnProperty(item)) { | |||||
| throw new Error('GridFactory: Value not found: ' + item); | |||||
| } | |||||
| } | |||||
| gridItem = { | |||||
| key: obj[key], | |||||
| value: GridSelectFactory.getGridSelectItemValue(obj, valueKeys) | |||||
| }; | |||||
| } else { | |||||
| // Empty placeholder item | |||||
| gridItem = { | |||||
| key: obj.key, | |||||
| value: obj.value | |||||
| }; | |||||
| } | |||||
| return gridItem as IGridSelectItem; | |||||
| } | |||||
| /** | |||||
| * Returns displayed value of grid item | |||||
| */ | |||||
| static getGridSelectItemValue(obj: any, valueKeys: string[]): string { | |||||
| let value = ''; | |||||
| for (let i = 0; i < valueKeys.length; i++) { | |||||
| value += obj[valueKeys[i]]; | |||||
| value += i < (valueKeys.length - 1) ? ' - ' : ''; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,4 @@ | |||||
| export interface IGridSelectItem { | |||||
| key: string; | |||||
| value: string; | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| {{value}} | |||||
| @@ -0,0 +1,83 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {IGridSelectItem} from '../grid-select-item'; | |||||
| import {GridRendererComponent} from '../../grid-renderer-component'; | |||||
| import {GridSelectFactory} from '../grid-select-factory'; | |||||
| @Component({ | |||||
| selector: 'app-grid-select-renderer', | |||||
| templateUrl: './grid-select-renderer.component.html', | |||||
| styleUrls: ['./grid-select-renderer.component.scss'] | |||||
| }) | |||||
| export class GridSelectRendererComponent extends GridRendererComponent { | |||||
| public itemRange: IGridSelectItem[]; | |||||
| public currentIndex: number; | |||||
| constructor() { | |||||
| super(); | |||||
| this.itemRange = null; | |||||
| this.currentIndex = null; | |||||
| } | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| if (null === params.colDef.cellEditorParams.relatesOnDgField) { | |||||
| // Static item range (default behaviour) | |||||
| this.setStaticValue(params); | |||||
| } else { | |||||
| // Item range relates on other dg field | |||||
| this.setRelatedValue(params); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Case for static item range (all items in column have the same select items - default) | |||||
| */ | |||||
| private setStaticValue(params: any): void { | |||||
| if (this.itemRange === null || undefined === this.itemRange) { | |||||
| // Item range is the same for all rows | |||||
| this.itemRange = params.colDef.cellEditorParams.values as IGridSelectItem[]; | |||||
| if (null !== params.value) { | |||||
| for (const item of this.itemRange) { | |||||
| if (item.key === params.value) { | |||||
| this.value = item.value; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| this.value = params.colDef.cellEditorParams.mandatory ? 'Bitte wählen' : ''; | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Case for item range related on other dg grid field (Select has always to be updated within init) | |||||
| */ | |||||
| private setRelatedValue(params: any): void { | |||||
| // @TODO: Might be checked | |||||
| const relatedValue: any = params.colDef.cellEditorParams.relatesOnDgField; | |||||
| const itemKey: any = params.colDef.cellEditorParams.itemKey; | |||||
| const itemValueKeys: string[] = params.colDef.cellEditorParams.itemValueKeys; | |||||
| // Get item range by related value | |||||
| this.itemRange = params.data.hasOwnProperty(relatedValue) && null !== params.data[relatedValue] ? | |||||
| params.colDef.cellEditorParams.values[params.data[relatedValue]] : null; | |||||
| if (null !== params.value && null !== this.itemRange) { | |||||
| // let value: string = GridFactory.getGridSelectItemValue() | |||||
| for (const item of this.itemRange) { | |||||
| if (item[itemKey] === params.value) { | |||||
| this.value = GridSelectFactory.getGridSelectItemValue(item, itemValueKeys); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| this.value = 'Bitte wählen'; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridEditorComponent} from '../../grid-editor-component'; | |||||
| @Component({ | |||||
| selector: 'app-grid-text-editor', | |||||
| templateUrl: './grid-text-editor.component.html', | |||||
| styleUrls: ['./grid-text-editor.component.scss'] | |||||
| }) | |||||
| export class GridTextEditorComponent extends GridEditorComponent { | |||||
| /** | |||||
| * Cancels before value input (this editor is never editable) | |||||
| */ | |||||
| public isCancelBeforeStart(): boolean { | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Overwrite | |||||
| */ | |||||
| public afterGuiAttached(): void { | |||||
| // do nothing | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1 @@ | |||||
| <div class="text-renderer">{{value}}</div> | |||||
| @@ -0,0 +1,27 @@ | |||||
| import {Component} from '@angular/core'; | |||||
| import {GridRendererComponent} from '../../grid-renderer-component'; | |||||
| import {AgGridComponentConst} from '../../../components/ag-grid-component-const'; | |||||
| @Component({ | |||||
| selector: 'app-grid-text-renderer', | |||||
| templateUrl: './grid-text-renderer.component.html', | |||||
| styleUrls: ['./grid-text-renderer.component.scss'] | |||||
| }) | |||||
| export class GridTextRendererComponent extends GridRendererComponent { | |||||
| /** | |||||
| * Initialize cell renderer (gets called on cell rendering and after editing / refresh) | |||||
| */ | |||||
| agInit(params: any): void { | |||||
| super.agInit(params); | |||||
| if (undefined !== this.cellParams) { | |||||
| // Replace "." with "," for display purpose | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_FLOAT) { | |||||
| this.value = isNaN(this.value) || null === this.value ? '' : parseFloat(parseFloat(this.value).toFixed(2)).toLocaleString('de-DE'); | |||||
| } | |||||
| if (this.cellParams.type === AgGridComponentConst.CELL_VALUE_TYPE_INT) { | |||||
| this.value = isNaN(this.value) || null === this.value? '' : this.value = parseInt(parseInt(this.value).toFixed(2)).toLocaleString('de-DE'); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,34 @@ | |||||
| import {Injectable} from '@angular/core'; | |||||
| import { Router } from '@angular/router'; | |||||
| import {AppService} from '../services/app.service'; | |||||
| import {Subscription} from 'rxjs/index'; | |||||
| import {ILoginState} from '../model/virtual/login-state'; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class RouteInterceptor { | |||||
| private loginSub: Subscription; | |||||
| private loginState: ILoginState; | |||||
| /** | |||||
| * Constructor of router service | |||||
| */ | |||||
| constructor(private router: Router, private appService: AppService) { | |||||
| // Observe login state | |||||
| this.loginSub = this.appService.getLoginState$().subscribe( | |||||
| data => this.loginState = data); | |||||
| } | |||||
| /** | |||||
| * Checks if user is logged in and redirects to login page if not | |||||
| * @returns {boolean} | |||||
| */ | |||||
| canActivate(): boolean { | |||||
| if (this.loginState == null || !this.loginState.isLoggedIn) { | |||||
| this.router.navigate(['./login']); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,96 @@ | |||||
| export class AgGridLocale { | |||||
| static getLocale(): {} { | |||||
| return { | |||||
| // for filter panel | |||||
| page: 'Seite', | |||||
| more: 'mehr', | |||||
| to: 'bis', | |||||
| of: 'von', | |||||
| next: 'nächstes', | |||||
| last: 'letztes', | |||||
| first: 'erstes', | |||||
| previous: 'vorheriges', | |||||
| loadingOoo: 'lade...', | |||||
| // for set filter | |||||
| selectAll: 'Alle auswählen', | |||||
| searchOoo: 'Suche...', | |||||
| blanks: 'leer', | |||||
| // for number filter and text filter | |||||
| filterOoo: 'Filter...', | |||||
| applyFilter: 'Filter anwenden...', | |||||
| equals: 'gleich', | |||||
| notEquals: 'nicht gleich', | |||||
| notEqual: 'nicht gleich', | |||||
| // for number filter | |||||
| lessThan: 'weniger als', | |||||
| greaterThan: 'größer als', | |||||
| lessThanOrEqual: 'weniger oder gleich', | |||||
| greaterThanOrEqual: 'größer oder gleich', | |||||
| inRange: 'in Spanne', | |||||
| // for text filter | |||||
| contains: 'beinhaltet', | |||||
| notContains: 'beinhaltet nicht', | |||||
| startsWith: 'beginnt mit', | |||||
| endsWith: 'endet mit', | |||||
| // the header of the default group column | |||||
| group: 'Gruppe', | |||||
| // tool panel | |||||
| columns: 'Spalten', | |||||
| filters: 'Filter', | |||||
| rowGroupColumns: 'Pivot Spalten', | |||||
| rowGroupColumnsEmptyMessage: 'Ziehe Spalten in Gruppe', | |||||
| valueColumns: 'Spalten Werte', | |||||
| pivotMode: 'Pivot-Mode', | |||||
| groups: 'Gruppen', | |||||
| values: 'Werte', | |||||
| pivots: 'Pivots', | |||||
| valueColumnsEmptyMessage: 'Ziehe Spalten zum zusammen', | |||||
| pivotColumnsEmptyMessage: 'Für Pivot hierher ziehen', | |||||
| toolPanelButton: 'Tool Feld', | |||||
| // other | |||||
| noRowsToShow: 'Keine Zeilen', | |||||
| // enterprise menu | |||||
| pinColumn: 'Spalte anpinnen', | |||||
| valueAggregation: 'Agg Wert', | |||||
| autosizeThiscolumn: 'Auto-Breite für diese Spalte', | |||||
| autosizeAllColumns: 'Auto-Breite für alle Spalte', | |||||
| groupBy: 'Gruppieren nach', | |||||
| ungroupBy: 'Gruppierung aufheben nach', | |||||
| resetColumns: 'Spalten zurücksetzen', | |||||
| expandAll: 'Alle öffnen', | |||||
| collapseAll: 'Alle schließen', | |||||
| toolPanel: 'Tool Feld', | |||||
| export: 'Export', | |||||
| csvExport: 'CSV Export', | |||||
| excelExport: 'Excel Export', | |||||
| // enterprise menu pinning | |||||
| pinLeft: 'Anpinnen <<', | |||||
| pinRight: 'Anpinnen >>', | |||||
| noPin: 'Nicht anpinnen <>', | |||||
| // enterprise menu aggregation and status bar | |||||
| sum: 'Summe', | |||||
| min: 'Min', | |||||
| max: 'Max', | |||||
| none: 'Nichts', | |||||
| count: 'Anzahl', | |||||
| average: 'Durchschnitt', | |||||
| // standard menu | |||||
| copy: 'Kopieren', | |||||
| copyWithHeaders: 'Mit Köpfen kopieren', | |||||
| ctrlC: 'strg und C', | |||||
| paste: 'Einfügen', | |||||
| ctrlV: 'strg und V' | |||||
| }; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| // The file contents for the current environment will overwrite these during build. | |||||
| // The build system defaults to the dev environment which uses `environment.ts`, but if you do | |||||
| // `ng build --env=prod` then `environment.prod.ts` will be used instead. | |||||
| // The list of which env maps to which file can be found in `.angular-cli.json`. | |||||
| export const tools = { | |||||
| ROLE_ADMIN: 'ROLE_ADMIN', | |||||
| ROLE_SALES: 'ROLE_SALES', | |||||
| ROLE_TECHNIQUE: 'ROLE_TECHNIQUE', | |||||
| ROLE_PRODUCTION: 'ROLE_PRODUCTION', | |||||
| ROLE_ACCOUNTING: 'ROLE_ACCOUNTING', | |||||
| ROLE_SERVICE: 'ROLE_SERVICE', | |||||
| COLORS: [ | |||||
| '#4397d4', // blue | |||||
| '#b56cdc', // purple | |||||
| '#dd6bbe', // pink | |||||
| '#45d4a7', // green | |||||
| '#fede62', // yellow | |||||
| '#fdb753', // orange | |||||
| '#808080', // grey | |||||
| '#363636' // black | |||||
| ], | |||||
| }; | |||||
| @@ -0,0 +1,5 @@ | |||||
| export interface ICalendarLegend { | |||||
| id: number; | |||||
| type: string; | |||||
| name: string; | |||||
| } | |||||
| @@ -0,0 +1,5 @@ | |||||
| export interface ICountry { | |||||
| id: number; | |||||
| name: string; | |||||
| iso_code: string; | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface ICustomerContact extends EntityInterface { | |||||
| customer_id: string; | |||||
| gender: string; | |||||
| firstname: string; | |||||
| lastname: string; | |||||
| email: string; | |||||
| phone_no: string; | |||||
| mobile_no: string; | |||||
| fax_no: string; | |||||
| department: string; | |||||
| date_of_birth: string; | |||||
| comment: string; | |||||
| street: string; | |||||
| street_no: string; | |||||
| zip: string; | |||||
| city: string; | |||||
| country_id: number; | |||||
| is_xmas_mail_recipient: boolean; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface ICustomerMeetingParticipant extends EntityInterface { | |||||
| customer_meeting_id: number; | |||||
| participant_user_id: number; | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| import {ICustomerMeetingParticipant} from './customer-meeting-participant'; | |||||
| import {MeetingInterface} from '../interface/meeting-interface'; | |||||
| export interface ICustomerMeeting extends EntityInterface, MeetingInterface { | |||||
| customer_id: number; | |||||
| meeting_type_id: number; | |||||
| is_option_meeting: boolean; | |||||
| customer_contact_id: number; | |||||
| gender: string; | |||||
| firstname: string; | |||||
| lastname: string; | |||||
| email: string; | |||||
| phone_no: string; | |||||
| mobile_no: string; | |||||
| department: string; | |||||
| street: string; | |||||
| street_no: string; | |||||
| zip: string; | |||||
| city: string; | |||||
| country_id: number; | |||||
| v_participants: ICustomerMeetingParticipant[]; | |||||
| v_is_editable: boolean; | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface ICustomerNote extends EntityInterface { | |||||
| customer_id: number; | |||||
| customer_contact_id: number; | |||||
| user_id: number; | |||||
| gender: string; | |||||
| firstname: string; | |||||
| lastname: string; | |||||
| email: string; | |||||
| phone_no: string; | |||||
| mobile_no: string; | |||||
| fax_no: string; | |||||
| department: string; | |||||
| title: string; | |||||
| comment: string; | |||||
| note_date: string; | |||||
| creation_date: string; | |||||
| creation_user_id: number; | |||||
| creation_user_firstname: string; | |||||
| creation_user_lastname: string; | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| import {ICustomerContact} from './customer-contact'; | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| import {ICustomerMeeting} from './customer-meeting'; | |||||
| import {ICustomerNote} from './customer-note'; | |||||
| export interface ICustomer extends EntityInterface { | |||||
| old_plp_id: string; | |||||
| name: string; | |||||
| name_additional: string; | |||||
| consultant_user_id: number; | |||||
| street: string; | |||||
| street_no: string; | |||||
| zip: string; | |||||
| city: string; | |||||
| country_id: number; | |||||
| url: string; | |||||
| email: string; | |||||
| phone_no: string; | |||||
| mobile_no: string; | |||||
| fax_no: string; | |||||
| comment: string; | |||||
| active: boolean; | |||||
| v_customer_contacts: ICustomerContact[]; | |||||
| v_customer_notes: ICustomerNote[]; | |||||
| v_customer_meetings: ICustomerMeeting[]; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface IInternalMeetingParticipant extends EntityInterface { | |||||
| internal_meeting_id: number; | |||||
| participant_user_id: number; | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| import {IInternalMeetingParticipant} from './internal-meeting-participant'; | |||||
| import {MeetingInterface} from '../interface/meeting-interface'; | |||||
| export interface IInternalMeeting extends EntityInterface, MeetingInterface { | |||||
| creation_user_id: number; | |||||
| v_participants: IInternalMeetingParticipant[]; | |||||
| v_is_editable: boolean; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface IMeetingType extends EntityInterface { | |||||
| type: string; | |||||
| name: string; | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface IUserType extends EntityInterface { | |||||
| user_type_id: number; | |||||
| type: string; | |||||
| v_roles: string[]; | |||||
| } | |||||
| @@ -0,0 +1,12 @@ | |||||
| import {IUserType} from './user-type'; | |||||
| import {EntityInterface} from '../interface/entity-interface'; | |||||
| export interface IUser extends EntityInterface { | |||||
| email: string; | |||||
| firstname: string; | |||||
| lastname: string; | |||||
| active: boolean; | |||||
| visible: boolean; | |||||
| v_translated_role: string; | |||||
| v_user_type: IUserType; | |||||
| } | |||||