| @@ -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; | |||
| } | |||