mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2026-05-14 22:00:30 +09:00
#131 upgraded Angular to v21
This commit is contained in:
@@ -5,8 +5,8 @@
|
||||
# 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'.
|
||||
last 2 Chrome versions
|
||||
last 2 Firefox versions
|
||||
last 2 Edge versions
|
||||
last 2 Safari versions
|
||||
last 2 iOS versions
|
||||
|
||||
@@ -28,20 +28,12 @@ Happy linting! 💖
|
||||
"plugins": [
|
||||
"eslint-plugin-import",
|
||||
"@angular-eslint/eslint-plugin",
|
||||
"@typescript-eslint",
|
||||
"@typescript-eslint/tslint"
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"root": true,
|
||||
"rules": {
|
||||
"@angular-eslint/component-class-suffix": "error",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-class-suffix": "off",
|
||||
"@angular-eslint/component-selector": "off",
|
||||
"@angular-eslint/directive-class-suffix": "error",
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
@@ -51,7 +43,6 @@ Happy linting! 💖
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/no-host-metadata-property": "error",
|
||||
"@angular-eslint/no-input-rename": "error",
|
||||
"@angular-eslint/no-inputs-metadata-property": "error",
|
||||
"@angular-eslint/no-output-rename": "error",
|
||||
@@ -80,19 +71,8 @@ Happy linting! 💖
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "error",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "variable",
|
||||
"format": [
|
||||
"camelCase",
|
||||
"UPPER_CASE"
|
||||
],
|
||||
"leadingUnderscore": "forbid",
|
||||
"trailingUnderscore": "forbid"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "off",
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
@@ -109,26 +89,10 @@ Happy linting! 💖
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"off",
|
||||
null
|
||||
],
|
||||
"@typescript-eslint/tslint/config": [
|
||||
"error",
|
||||
{
|
||||
"rules": {
|
||||
"import-spacing": true,
|
||||
"invoke-injectable": true,
|
||||
"no-access-missing-member": true,
|
||||
"templates-use-public": true,
|
||||
"whitespace": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "off",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"brace-style": [
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"allowedCommonJsDependencies": [
|
||||
"stompjs", "sockjs-client", "moment", "angular2-uuid"
|
||||
"@stomp/stompjs", "stompjs", "sockjs-client", "angular2-uuid"
|
||||
],
|
||||
"assets": [
|
||||
"src/assets",
|
||||
@@ -67,18 +67,18 @@
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "quartz-manager-ui:build:development"
|
||||
"buildTarget": "quartz-manager-ui:build:development"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "quartz-manager-ui:build:production"
|
||||
"buildTarget": "quartz-manager-ui:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "quartz-manager-ui:build"
|
||||
"buildTarget": "quartz-manager-ui:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
@@ -89,38 +89,35 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"quartz-manager-ui-e2e": {
|
||||
"root": "e2e",
|
||||
"sourceRoot": "e2e",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "./protractor.conf.js",
|
||||
"devServerTarget": "quartz-manager-ui:serve"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"e2e/tsconfig.e2e.json"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "qrzmng",
|
||||
"style": "css"
|
||||
"style": "css",
|
||||
"type": "component"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "qrzmng"
|
||||
"prefix": "qrzmng",
|
||||
"type": "directive"
|
||||
},
|
||||
"@schematics/angular:service": {
|
||||
"type": "service"
|
||||
},
|
||||
"@schematics/angular:guard": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:interceptor": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:module": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:resolver": {
|
||||
"typeSeparator": "."
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
|
||||
12
quartz-manager-frontend/jest.config.js
Normal file
12
quartz-manager-frontend/jest.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const {createEsmPreset} = require('jest-preset-angular/presets');
|
||||
|
||||
module.exports = {
|
||||
...createEsmPreset({
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$'
|
||||
}),
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(@angular|@stomp/rx-stomp|@stomp/stompjs|.*\\.mjs$)/)'
|
||||
]
|
||||
};
|
||||
@@ -1 +1,3 @@
|
||||
import 'jest-preset-angular/setup-jest';
|
||||
import {setupZoneTestEnv} from 'jest-preset-angular/setup-env/zone/index.mjs';
|
||||
|
||||
setupZoneTestEnv();
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/0.13/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
|
||||
},
|
||||
files: [
|
||||
|
||||
],
|
||||
preprocessors: {
|
||||
|
||||
},
|
||||
mime: {
|
||||
'text/x-typescript': ['ts','tsx']
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
|
||||
reporters: config.angularCli && config.angularCli.codeCoverage
|
||||
? ['progress', 'coverage-istanbul']
|
||||
: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
27959
quartz-manager-frontend/package-lock.json
generated
27959
quartz-manager-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,89 +6,68 @@
|
||||
"ng": "ng",
|
||||
"start": "ng serve --proxy-config proxy.conf.json",
|
||||
"build": "ng build --configuration production",
|
||||
"test": "jest",
|
||||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
||||
"lint": "ng lint",
|
||||
"lint:sonar": "eslint --no-eslintrc -c .eslintrc.sonar.json \"src/**/*.ts\"",
|
||||
"lint:sonar:fix": "eslint --no-eslintrc -c .eslintrc.sonar.json \"src/**/*.ts\" --fix",
|
||||
"e2e": "ng e2e"
|
||||
"lint:sonar:fix": "eslint --no-eslintrc -c .eslintrc.sonar.json \"src/**/*.ts\" --fix"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular-material-components/datetime-picker": "15.0.0",
|
||||
"@angular-material-components/moment-adapter": "15.0.0",
|
||||
"@angular/animations": "15.2.10",
|
||||
"@angular/cdk": "15.0.1",
|
||||
"@angular/common": "15.2.10",
|
||||
"@angular/compiler": "15.2.10",
|
||||
"@angular/core": "15.2.10",
|
||||
"@angular/flex-layout": "15.0.0-beta.42",
|
||||
"@angular/forms": "15.2.10",
|
||||
"@angular/material": "15.0.1",
|
||||
"@angular/platform-browser": "15.2.10",
|
||||
"@angular/platform-browser-dynamic": "15.2.10",
|
||||
"@angular/platform-server": "15.2.10",
|
||||
"@angular/router": "15.2.10",
|
||||
"@auth0/angular-jwt": "5.1.0",
|
||||
"@angular/animations": "21.2.12",
|
||||
"@angular/cdk": "21.2.10",
|
||||
"@angular/common": "21.2.12",
|
||||
"@angular/compiler": "21.2.12",
|
||||
"@angular/core": "21.2.12",
|
||||
"@angular/forms": "21.2.12",
|
||||
"@angular/material": "21.2.10",
|
||||
"@angular/platform-browser": "21.2.12",
|
||||
"@angular/platform-browser-dynamic": "21.2.12",
|
||||
"@angular/platform-server": "21.2.12",
|
||||
"@angular/router": "21.2.12",
|
||||
"@auth0/angular-jwt": "5.2.0",
|
||||
"@danielmoncada/angular-datetime-picker": "21.0.0",
|
||||
"@fortawesome/fontawesome": "^1.1.4",
|
||||
"@fortawesome/fontawesome-free-regular": "^5.0.8",
|
||||
"@fortawesome/fontawesome-free-solid": "^5.0.8",
|
||||
"@stomp/rx-stomp": "1.2.0",
|
||||
"core-js": "2.5.1",
|
||||
"@stomp/rx-stomp": "2.4.0",
|
||||
"@stomp/stompjs": "^7.2.0",
|
||||
"hammerjs": "2.0.8",
|
||||
"moment": "^2.29.1",
|
||||
"net": "^1.0.2",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"rxjs": "6.5.5",
|
||||
"rxjs": "^7.8.2",
|
||||
"sockjs-client": "^1.1.1",
|
||||
"stompjs": "^2.3.3",
|
||||
"tslib": "~2.4.1",
|
||||
"zone.js": "~0.12.0"
|
||||
"tslib": "^2.8.1",
|
||||
"uuid": "^13.0.0",
|
||||
"zone.js": "~0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.2.10",
|
||||
"@angular-devkit/core": "^15.2.10",
|
||||
"@angular-eslint/builder": "15.2.1",
|
||||
"@angular-eslint/eslint-plugin": "15.2.1",
|
||||
"@angular-eslint/eslint-plugin-template": "15.2.1",
|
||||
"@angular-eslint/schematics": "15.2.1",
|
||||
"@angular-eslint/template-parser": "15.2.1",
|
||||
"@angular/cli": "^15.2.10",
|
||||
"@angular/compiler-cli": "15.2.10",
|
||||
"@angular/language-service": "15.2.10",
|
||||
"@angular-devkit/build-angular": "^21.2.10",
|
||||
"@angular-devkit/core": "^21.2.10",
|
||||
"@angular-eslint/builder": "21.3.1",
|
||||
"@angular-eslint/eslint-plugin": "21.3.1",
|
||||
"@angular-eslint/eslint-plugin-template": "21.3.1",
|
||||
"@angular-eslint/schematics": "21.3.1",
|
||||
"@angular-eslint/template-parser": "21.3.1",
|
||||
"@angular/cli": "^21.2.10",
|
||||
"@angular/compiler-cli": "21.2.12",
|
||||
"@angular/language-service": "21.2.12",
|
||||
"@types/hammerjs": "2.0.34",
|
||||
"@types/jasmine": "2.5.54",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/jest": "28.1.1",
|
||||
"@types/node": "^12.11.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.43.0",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^5.46.0",
|
||||
"@typescript-eslint/parser": "5.43.0",
|
||||
"codelyzer": "6.0.2",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"@types/jasmine": "^5.1.13",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^22.13.14",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||
"@typescript-eslint/parser": "^8.48.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-sonarjs": "^0.16.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"jest": "28.1.3",
|
||||
"jest-preset-angular": "~12.2.3",
|
||||
"karma": "~6.4.1",
|
||||
"karma-chrome-launcher": "~3.1.1",
|
||||
"karma-cli": "2.0.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.3",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"eslint-plugin-sonarjs": "^4.0.3",
|
||||
"jest": "30.4.1",
|
||||
"jest-environment-jsdom": "^30.2.0",
|
||||
"jest-preset-angular": "^16.1.5",
|
||||
"jsdom": "^27.3.0",
|
||||
"prettier": "^2.8.1",
|
||||
"prettier-eslint": "^15.0.1",
|
||||
"protractor": "^7.0.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-preset-angular",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/jest.setup.ts"
|
||||
]
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/long-stack-trace-zone';
|
||||
import 'zone.js/dist/proxy.js';
|
||||
import 'zone.js/dist/sync-test';
|
||||
import 'zone.js/dist/jasmine-patch';
|
||||
import 'zone.js/dist/async-test';
|
||||
import 'zone.js/dist/fake-async-test';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
|
||||
declare let __karma__: any;
|
||||
declare let require: any;
|
||||
|
||||
// Prevent Karma from running prematurely.
|
||||
__karma__.loaded = function () {};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
// Finally, start Karma to run the tests.
|
||||
__karma__.start();
|
||||
@@ -1,8 +1,7 @@
|
||||
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxFill>
|
||||
<app-header fxFlex="0 0 auto"></app-header>
|
||||
<div class="app-shell flex flex-column justify-space-between h-100">
|
||||
<app-header class="flex-none"></app-header>
|
||||
<div class="content flex h-100">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-footer fxFlex="0 0 auto"></app-footer>
|
||||
<app-footer class="flex-none"></app-footer>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {TestBed, async, waitForAsync} from '@angular/core/testing';
|
||||
import {TestBed, waitForAsync} from '@angular/core/testing';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -42,7 +42,7 @@ describe('AppComponent', () => {
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', async(() => {
|
||||
it('should create the app', waitForAsync(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
// I remove temporary fontawesome5 and downgrade to fontawesome4
|
||||
import fontawesome from '@fortawesome/fontawesome';
|
||||
import solid from '@fortawesome/fontawesome-free-solid/';
|
||||
fontawesome.library.add(solid);
|
||||
import fontawesome from '@fortawesome/fontawesome';
|
||||
import {
|
||||
faCheckCircle,
|
||||
faExclamationCircle,
|
||||
faExclamationTriangle,
|
||||
faPause,
|
||||
faPlay,
|
||||
faTimesCircle
|
||||
} from '@fortawesome/fontawesome-free-solid';
|
||||
|
||||
fontawesome.library.add(faCheckCircle, faExclamationCircle, faExclamationTriangle, faPause, faPlay, faTimesCircle);
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
|
||||
export class AppComponent {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, APP_INITIALIZER} from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||
|
||||
import {JWT_OPTIONS, JwtModule} from '@auth0/angular-jwt';
|
||||
|
||||
@@ -17,18 +17,14 @@ import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatDatepickerModule} from '@angular/material/datepicker';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
|
||||
import {MatNativeDateModule} from '@angular/material/core';
|
||||
import { NgxMatTimepickerModule, NgxMatDatetimePickerModule} from '@angular-material-components/datetime-picker';
|
||||
import { NgxMatMomentModule } from '@angular-material-components/moment-adapter';
|
||||
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
|
||||
import {OwlDateTimeModule, OwlNativeDateTimeModule} from '@danielmoncada/angular-datetime-picker';
|
||||
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { ManagerComponent } from './views/manager';
|
||||
@@ -73,86 +69,79 @@ export function jwtOptionsFactory(apiService: ApiService) {
|
||||
tokenGetter: () => {
|
||||
return apiService.getToken();
|
||||
},
|
||||
whitelistedDomains: ['localhost:8080', 'localhost:4200']
|
||||
allowedDomains: ['localhost:8080', 'localhost:4200']
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
ManagerComponent,
|
||||
GithubComponent,
|
||||
LoginComponent,
|
||||
NotFoundComponent,
|
||||
AccountMenuComponent,
|
||||
SimpleTriggerConfigComponent,
|
||||
SchedulerControlComponent,
|
||||
LogsPanelComponent,
|
||||
ProgressPanelComponent,
|
||||
ForbiddenComponent,
|
||||
GenericErrorComponent,
|
||||
TriggerListComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
HttpClientModule,
|
||||
AppRoutingModule,
|
||||
JwtModule.forRoot({
|
||||
jwtOptionsProvider: {
|
||||
provide: JWT_OPTIONS,
|
||||
useFactory: jwtOptionsFactory,
|
||||
deps: [ApiService]
|
||||
}
|
||||
}),
|
||||
MatDialogModule,
|
||||
MatMenuModule,
|
||||
MatTooltipModule,
|
||||
MatButtonModule,
|
||||
MatChipsModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
MatToolbarModule,
|
||||
MatCardModule,
|
||||
MatListModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
MatDatepickerModule, MatNativeDateModule,
|
||||
NgxMatMomentModule,
|
||||
NgxMatDatetimePickerModule,
|
||||
MatSidenavModule,
|
||||
FlexLayoutModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_BASE_HREF,
|
||||
useValue: getHtmlBaseUrl()
|
||||
},
|
||||
{
|
||||
'provide': APP_INITIALIZER,
|
||||
'useFactory': initUserFactory,
|
||||
'deps': [UserService],
|
||||
'multi': true
|
||||
},
|
||||
LoginGuard,
|
||||
GuestGuard,
|
||||
AdminGuard,
|
||||
SchedulerService,
|
||||
JobService,
|
||||
TriggerService,
|
||||
ProgressRxWebsocketService,
|
||||
LogsRxWebsocketService,
|
||||
AuthService,
|
||||
ApiService,
|
||||
UserService,
|
||||
ConfigService,
|
||||
MatIconRegistry
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
@NgModule({ declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
ManagerComponent,
|
||||
GithubComponent,
|
||||
LoginComponent,
|
||||
NotFoundComponent,
|
||||
AccountMenuComponent,
|
||||
SimpleTriggerConfigComponent,
|
||||
SchedulerControlComponent,
|
||||
LogsPanelComponent,
|
||||
ProgressPanelComponent,
|
||||
ForbiddenComponent,
|
||||
GenericErrorComponent,
|
||||
TriggerListComponent
|
||||
],
|
||||
bootstrap: [AppComponent], imports: [BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
AppRoutingModule,
|
||||
JwtModule.forRoot({
|
||||
jwtOptionsProvider: {
|
||||
provide: JWT_OPTIONS,
|
||||
useFactory: jwtOptionsFactory,
|
||||
deps: [ApiService]
|
||||
}
|
||||
}),
|
||||
MatDialogModule,
|
||||
MatMenuModule,
|
||||
MatTooltipModule,
|
||||
MatButtonModule,
|
||||
MatChipsModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
MatToolbarModule,
|
||||
MatCardModule,
|
||||
MatListModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
OwlDateTimeModule,
|
||||
OwlNativeDateTimeModule,
|
||||
MatSidenavModule,
|
||||
], providers: [
|
||||
{
|
||||
provide: APP_BASE_HREF,
|
||||
useValue: getHtmlBaseUrl()
|
||||
},
|
||||
{
|
||||
'provide': APP_INITIALIZER,
|
||||
'useFactory': initUserFactory,
|
||||
'deps': [UserService],
|
||||
'multi': true
|
||||
},
|
||||
LoginGuard,
|
||||
GuestGuard,
|
||||
AdminGuard,
|
||||
SchedulerService,
|
||||
JobService,
|
||||
TriggerService,
|
||||
ProgressRxWebsocketService,
|
||||
LogsRxWebsocketService,
|
||||
AuthService,
|
||||
ApiService,
|
||||
UserService,
|
||||
ConfigService,
|
||||
MatIconRegistry,
|
||||
provideHttpClient(withInterceptorsFromDi())
|
||||
] })
|
||||
export class AppModule { }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<mat-toolbar id="footer" style="color: rgba(255, 255, 255, 0.541176);" fxLayout="row" fxLayoutAlign="center center">
|
||||
<mat-toolbar id="footer" class="flex flex-row justify-center align-items-center" style="color: rgba(255, 255, 255, 0.541176);">
|
||||
<a href="https://github.com/fabioformosa/quartz-manager" class="flex flex-row align-items-center" style="gap: 6px">
|
||||
<div class="flex"><img src="assets/image/github.png"/></div>
|
||||
<div class="font-size-14 font-weight-500 display-block line-height-100">Quartz Manager</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss']
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-github',
|
||||
templateUrl: './github.component.html',
|
||||
styleUrls: ['./github.component.scss']
|
||||
selector: 'app-github',
|
||||
templateUrl: './github.component.html',
|
||||
styleUrls: ['./github.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class GithubComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ import {
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account-menu',
|
||||
templateUrl: './account-menu.component.html',
|
||||
styleUrls: ['./account-menu.component.scss']
|
||||
selector: 'app-account-menu',
|
||||
templateUrl: './account-menu.component.html',
|
||||
styleUrls: ['./account-menu.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class AccountMenuComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
<mat-toolbar color="primary" class="app-navbar">
|
||||
<button mat-button mat-ripple routerLink="/">
|
||||
<!-- <img alt="Quartz Manager" class="app-angular-logo" src="assets/image/angular-white-transparent.svg">-->
|
||||
<span>Quartz Manager</span>
|
||||
</button>
|
||||
|
||||
<div class="right">
|
||||
<div fxFlex="1 1 auto" fxLayout="row" fxLayoutAlign="flex-end center">
|
||||
<button *ngIf="!hasSignedIn() && !noAuthenticationRequired()" routerLink="/login" mat-button mat-ripple>
|
||||
<span>Login</span>
|
||||
</button>
|
||||
<button
|
||||
class="greeting-button"
|
||||
*ngIf="hasSignedIn() && !noAuthenticationRequired()"
|
||||
mat-button mat-ripple
|
||||
[matMenuTriggerFor]="accountMenu">
|
||||
<span>Hi, {{userName()}}</span>
|
||||
</button>
|
||||
<button
|
||||
class="greeting-hamburger"
|
||||
*ngIf="hasSignedIn()"
|
||||
mat-icon-button mat-ripple
|
||||
[matMenuTriggerFor]="accountMenu">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
<mat-menu #accountMenu
|
||||
class="app-header-accountMenu"
|
||||
yposition="below"
|
||||
[overlapTrigger]="false">
|
||||
<app-account-menu ></app-account-menu>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<mat-toolbar color="primary" class="app-navbar">
|
||||
<button mat-button mat-ripple routerLink="/">
|
||||
<!-- <img alt="Quartz Manager" class="app-angular-logo" src="assets/image/angular-white-transparent.svg">-->
|
||||
<span>Quartz Manager</span>
|
||||
</button>
|
||||
|
||||
<div class="right">
|
||||
<div class="flex flex-row flex-1 justify-flex-end align-items-center">
|
||||
@if (!hasSignedIn() && !noAuthenticationRequired()) {
|
||||
<button routerLink="/login" mat-button mat-ripple>
|
||||
<span>Login</span>
|
||||
</button>
|
||||
} @if (hasSignedIn() && !noAuthenticationRequired()) {
|
||||
<button
|
||||
class="greeting-button"
|
||||
mat-button
|
||||
mat-ripple
|
||||
[matMenuTriggerFor]="accountMenu">
|
||||
<span>Hi, {{ userName() }}</span>
|
||||
</button>
|
||||
} @if (hasSignedIn()) {
|
||||
<button
|
||||
class="greeting-hamburger"
|
||||
mat-icon-button
|
||||
mat-ripple
|
||||
[matMenuTriggerFor]="accountMenu">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
}
|
||||
<mat-menu
|
||||
#accountMenu
|
||||
class="app-header-accountMenu"
|
||||
yposition="below"
|
||||
[overlapTrigger]="false">
|
||||
<app-account-menu></app-account-menu>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
@@ -6,10 +6,11 @@ import {
|
||||
} from '../../services';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss']
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,65 +1,67 @@
|
||||
<mat-card class="flex flex-1 max-h-100">
|
||||
<mat-card-header class="pb-16">
|
||||
<mat-card-subtitle ><b>JOB LOGS</b></mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="flex flex-1 overflow-y-auto">
|
||||
<div class="flex-1">
|
||||
<div *ngIf="!selectedTriggerName && (!logs || logs.length == 0)" fxFill class="h-100" style="text-align: center;">
|
||||
<img
|
||||
src="assets/image/logs.svg"
|
||||
alt="no logs"
|
||||
width="320"
|
||||
style="margin-top: 6em" />
|
||||
</div>
|
||||
<div *ngIf="isWaitingForLogs()" class="waitingLogs" fxLayout="column" fxLayoutAlign="center center" fxLayoutGap="12px">
|
||||
<mat-spinner diameter="36"></mat-spinner>
|
||||
<div>Waiting for logs from {{selectedTriggerName}}...</div>
|
||||
</div>
|
||||
|
||||
<div id="logs" fxFill style="height: 100%">
|
||||
<div
|
||||
*ngFor="let log of logs; let first = first"
|
||||
fxLayout="row"
|
||||
fxLayout.xs="column"
|
||||
fxLayoutAlign="start"
|
||||
fxLayoutGap="10px">
|
||||
<div style="flex: 1; max-width: 300px">
|
||||
<span
|
||||
[ngClass]="{
|
||||
'animate__animated animate__zoomIn zoomIn firstLog': first
|
||||
}">
|
||||
[{{ log.time | date : 'medium' }}]</span
|
||||
>
|
||||
</div>
|
||||
<div style="flex: 1; max-width: 16px">
|
||||
<span [ngClass]="{ 'animated zoomIn firstLog': first }">
|
||||
<i
|
||||
class="fas"
|
||||
[ngClass]="{
|
||||
'fa-check-circle green': log.type == 'INFO',
|
||||
'fa-exclamation-triangle yellow': log.type == 'WARN',
|
||||
'fa-times-circle red': log.type == 'ERROR'
|
||||
}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div style="flex: 1; max-width: 250px">
|
||||
<span
|
||||
[ngClass]="{
|
||||
'animate__animated animate__zoomIn zoomIn firstLog': first
|
||||
}">
|
||||
{{ log.threadName }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="flex: 1">
|
||||
<span
|
||||
[ngClass]="{
|
||||
'animate__animated animate__zoomIn zoomIn firstLog': first
|
||||
}">
|
||||
{{ log.msg }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="flex flex-1 max-h-100">
|
||||
<mat-card-header class="pb-16">
|
||||
<mat-card-subtitle><b>JOB LOGS</b></mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="flex flex-1 overflow-y-auto">
|
||||
<div class="flex-1">
|
||||
@if (!selectedTriggerName && (!logs || logs.length == 0)) {
|
||||
<div class="h-100 w-100" style="text-align: center">
|
||||
<img
|
||||
src="assets/image/logs.svg"
|
||||
alt="no logs"
|
||||
width="320"
|
||||
style="margin-top: 6em" />
|
||||
</div>
|
||||
} @if (isWaitingForLogs()) {
|
||||
<div
|
||||
class="waitingLogs flex flex-column align-items-center justify-center gap-12">
|
||||
<mat-spinner diameter="36"></mat-spinner>
|
||||
<div>Waiting for logs from {{ selectedTriggerName }}...</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div id="logs" class="w-100" style="height: 100%">
|
||||
@for (log of logs; track log; let first = $first) {
|
||||
<div
|
||||
class="log-row flex flex-row gap-10">
|
||||
<div style="flex: 1; max-width: 300px">
|
||||
<span
|
||||
[ngClass]="{
|
||||
'animate__animated animate__zoomIn zoomIn firstLog': first
|
||||
}">
|
||||
[{{ log.time | date : 'medium' }}]</span
|
||||
>
|
||||
</div>
|
||||
<div style="flex: 1; max-width: 16px">
|
||||
<span [ngClass]="{ 'animated zoomIn firstLog': first }">
|
||||
<i
|
||||
class="fas"
|
||||
[ngClass]="{
|
||||
'fa-check-circle green': log.type == 'INFO',
|
||||
'fa-exclamation-triangle yellow': log.type == 'WARN',
|
||||
'fa-times-circle red': log.type == 'ERROR'
|
||||
}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div style="flex: 1; max-width: 250px">
|
||||
<span
|
||||
[ngClass]="{
|
||||
'animate__animated animate__zoomIn zoomIn firstLog': first
|
||||
}">
|
||||
{{ log.threadName }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="flex: 1">
|
||||
<span
|
||||
[ngClass]="{
|
||||
'animate__animated animate__zoomIn zoomIn firstLog': first
|
||||
}">
|
||||
{{ log.msg }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('LogsPanelComponent', () => {
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-1', null);
|
||||
|
||||
expect(logsRxWebsocketService.watch).toHaveBeenCalledWith('/topic/logs/trigger-1');
|
||||
expect(logsRxWebsocketService.watch.mock.calls[0]).toEqual(['/topic/logs/trigger-1']);
|
||||
expect(component.selectedTriggerName).toEqual('trigger-1');
|
||||
expect(component.isWaitingForLogs()).toBeTruthy();
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('LogsPanelComponent', () => {
|
||||
component.triggerKey = new TriggerKey('trigger-2', null);
|
||||
|
||||
expect(firstSubscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(logsRxWebsocketService.watch).toHaveBeenCalledWith('/topic/logs/trigger-2');
|
||||
expect(logsRxWebsocketService.watch.mock.calls[1]).toEqual(['/topic/logs/trigger-2']);
|
||||
});
|
||||
|
||||
it('should clear logs when the trigger changes', () => {
|
||||
|
||||
@@ -6,10 +6,11 @@ import {map} from 'rxjs/operators';
|
||||
import {TriggerKey} from '../../model/triggerKey.model';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'logs-panel',
|
||||
templateUrl: './logs-panel.component.html',
|
||||
styleUrls: ['./logs-panel.component.scss']
|
||||
@Component({
|
||||
selector: 'logs-panel',
|
||||
templateUrl: './logs-panel.component.html',
|
||||
styleUrls: ['./logs-panel.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class LogsPanelComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
||||
@@ -1,43 +1,72 @@
|
||||
<!-- <div class="progress" [hidden]="progress.percentage < 0">
|
||||
<div class="progress-bar"
|
||||
role="progressbar"
|
||||
[ngStyle]="{width: percentageStr}">
|
||||
{{percentageStr}}
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<mat-card style="padding-bottom: 0" [ngClass]="{'progress-updated': progressUpdated}">
|
||||
<mat-card-header style="padding-bottom: 16px;">
|
||||
<mat-card-subtitle><b>JOB PROGRESS</b></mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div id="progressBarBox" *ngIf="progress.percentage !== -1">
|
||||
<mat-progress-bar mode="determinate" value="{{progress.percentage}}"></mat-progress-bar>
|
||||
{{percentageStr}}
|
||||
</div>
|
||||
|
||||
<div id="counterBox" fxLayout="row" fxLayoutAlign="center" *ngIf="progress.timesTriggered">
|
||||
<span id="timesTriggeredCounter" class="animated pulse">{{progress.timesTriggered}}</span>
|
||||
<span id="totCounter" *ngIf="progress.repeatCount > 0"> / {{progress.repeatCount}} </span>
|
||||
</div>
|
||||
<mat-divider *ngIf="progress.timesTriggered"></mat-divider>
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="space-around center">
|
||||
<div class="fireBox">
|
||||
<div class="fireBoxHeader">prev fire time</div>
|
||||
<div class="fireBoxContent"><span class="animated pulse">{{progress.previousFireTime|date:'dd-MM-yyyy HH:mm:ss'}}</span></div>
|
||||
<div class="fireBoxContent" [hidden]="progress.previousFireTime"><span>-</span></div>
|
||||
</div>
|
||||
<div class="fireBox">
|
||||
<div class="fireBoxHeader">next fire time</div>
|
||||
<div class="fireBoxContent"><span class="animated pulse">{{progress.nextFireTime|date:'dd-MM-yyyy HH:mm:ss'}}</span></div>
|
||||
<div class="fireBoxContent" [hidden]="progress.nextFireTime"><span>-</span></div>
|
||||
</div>
|
||||
<div class="fireBox">
|
||||
<div class="fireBoxHeader">final fire time</div>
|
||||
<div class="fireBoxContent"><span class="animated pulse">{{progress.finalFireTime|date:'dd-MM-yyyy HH:mm:ss'}}</span></div>
|
||||
<div class="fireBoxContent" [hidden]="progress.finalFireTime"><span>-</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<!-- <div class="progress" [hidden]="progress.percentage < 0">
|
||||
<div class="progress-bar"
|
||||
role="progressbar"
|
||||
[ngStyle]="{width: percentageStr}">
|
||||
{{percentageStr}}
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<mat-card
|
||||
style="padding-bottom: 0"
|
||||
[ngClass]="{ 'progress-updated': progressUpdated }">
|
||||
<mat-card-header style="padding-bottom: 16px">
|
||||
<mat-card-subtitle><b>JOB PROGRESS</b></mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
@if (progress.percentage !== -1) {
|
||||
<div id="progressBarBox">
|
||||
<mat-progress-bar
|
||||
mode="determinate"
|
||||
value="{{ progress.percentage }}"></mat-progress-bar>
|
||||
{{ percentageStr }}
|
||||
</div>
|
||||
} @if (progress.timesTriggered) {
|
||||
<div id="counterBox" class="flex flex-row justify-center">
|
||||
<span id="timesTriggeredCounter" class="animated pulse">{{
|
||||
progress.timesTriggered
|
||||
}}</span>
|
||||
@if (progress.repeatCount > 0) {
|
||||
<span id="totCounter"> / {{ progress.repeatCount }} </span>
|
||||
}
|
||||
</div>
|
||||
} @if (progress.timesTriggered) {
|
||||
<mat-divider></mat-divider>
|
||||
}
|
||||
|
||||
<div class="flex flex-row align-items-center justify-space-around">
|
||||
<div class="fireBox">
|
||||
<div class="fireBoxHeader">prev fire time</div>
|
||||
<div class="fireBoxContent">
|
||||
<span class="animated pulse">{{
|
||||
progress.previousFireTime | date : 'dd-MM-yyyy HH:mm:ss'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="fireBoxContent" [hidden]="progress.previousFireTime">
|
||||
<span>-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fireBox">
|
||||
<div class="fireBoxHeader">next fire time</div>
|
||||
<div class="fireBoxContent">
|
||||
<span class="animated pulse">{{
|
||||
progress.nextFireTime | date : 'dd-MM-yyyy HH:mm:ss'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="fireBoxContent" [hidden]="progress.nextFireTime">
|
||||
<span>-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fireBox">
|
||||
<div class="fireBoxHeader">final fire time</div>
|
||||
<div class="fireBoxContent">
|
||||
<span class="animated pulse">{{
|
||||
progress.finalFireTime | date : 'dd-MM-yyyy HH:mm:ss'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="fireBoxContent" [hidden]="progress.finalFireTime">
|
||||
<span>-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('ProgressPanelComponent', () => {
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-1', null);
|
||||
|
||||
expect(progressRxWebsocketService.watch).toHaveBeenCalledWith('/topic/progress/trigger-1');
|
||||
expect(progressRxWebsocketService.watch.mock.calls[0]).toEqual(['/topic/progress/trigger-1']);
|
||||
|
||||
messages.next({body: JSON.stringify({percentage: 75, timesTriggered: 3})});
|
||||
jest.runOnlyPendingTimers();
|
||||
@@ -48,7 +48,7 @@ describe('ProgressPanelComponent', () => {
|
||||
component.triggerKey = new TriggerKey('trigger-2', null);
|
||||
|
||||
expect(firstSubscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(progressRxWebsocketService.watch).toHaveBeenCalledWith('/topic/progress/trigger-2');
|
||||
expect(progressRxWebsocketService.watch.mock.calls[1]).toEqual(['/topic/progress/trigger-2']);
|
||||
});
|
||||
|
||||
it('should reset progress when the trigger changes', () => {
|
||||
|
||||
@@ -4,10 +4,11 @@ import {TriggerKey} from '../../model/triggerKey.model';
|
||||
import {ProgressRxWebsocketService} from '../../services/progress.rx-websocket.service';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'progress-panel',
|
||||
templateUrl: './progress-panel.component.html',
|
||||
styleUrls: ['./progress-panel.component.scss']
|
||||
@Component({
|
||||
selector: 'progress-panel',
|
||||
templateUrl: './progress-panel.component.html',
|
||||
styleUrls: ['./progress-panel.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class ProgressPanelComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
||||
@@ -1,26 +1,40 @@
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div fxLayout="row" fxLayoutAlign="left stretch" fxLayoutGap="30px">
|
||||
<button id="schedulerControllerBtn" mat-raised-button class="btn btn-default large-btn" (click)="startOrPause()">
|
||||
<span *ngIf = "scheduler?.status === 'RUNNING'">
|
||||
<i class="fas fa-pause red"></i>
|
||||
</span>
|
||||
<span *ngIf = "scheduler?.status === 'STOPPED' || scheduler?.status === 'PAUSED'">
|
||||
<i class="fas fa-play green"></i>
|
||||
</span>
|
||||
</button>
|
||||
<div fxLayout="column center">
|
||||
<mat-card-subtitle style="margin: auto;"><b>SCHEDULER</b></mat-card-subtitle>
|
||||
</div>
|
||||
<mat-divider [vertical]="true"></mat-divider>
|
||||
<div fxLayout="column" class="justify-space-between">
|
||||
<div><label>Name</label></div>
|
||||
<div><span id="scheduler-name">{{scheduler?.name}}</span></div>
|
||||
</div>
|
||||
<div fxLayout="column" class="justify-space-between">
|
||||
<div><label>Instance ID</label></div>
|
||||
<div><span id="scheduler-instance">{{scheduler?.instanceId}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div class="flex flex-row align-items-stretch gap-30">
|
||||
<button
|
||||
id="schedulerControllerBtn"
|
||||
mat-raised-button
|
||||
class="btn btn-default large-btn"
|
||||
(click)="startOrPause()">
|
||||
@if (scheduler?.status === 'RUNNING') {
|
||||
<span>
|
||||
<i class="fas fa-pause red"></i>
|
||||
</span>
|
||||
} @if (scheduler?.status === 'STOPPED' || scheduler?.status ===
|
||||
'PAUSED') {
|
||||
<span>
|
||||
<i class="fas fa-play green"></i>
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
<div class="flex flex-column align-items-center">
|
||||
<mat-card-subtitle style="margin: auto"
|
||||
><b>SCHEDULER</b></mat-card-subtitle
|
||||
>
|
||||
</div>
|
||||
<mat-divider [vertical]="true"></mat-divider>
|
||||
<div class="flex flex-column justify-space-between">
|
||||
<div><label>Name</label></div>
|
||||
<div>
|
||||
<span id="scheduler-name">{{ scheduler?.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-column justify-space-between">
|
||||
<div><label>Instance ID</label></div>
|
||||
<div>
|
||||
<span id="scheduler-instance">{{ scheduler?.instanceId }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
@@ -2,10 +2,11 @@ import {Component, OnInit} from '@angular/core';
|
||||
import {SchedulerService, UserService} from '../../services';
|
||||
import {Scheduler} from '../../model/scheduler.model';
|
||||
|
||||
@Component({
|
||||
selector: 'qrzmng-scheduler-control',
|
||||
templateUrl: './scheduler-control.component.html',
|
||||
styleUrls: ['./scheduler-control.component.scss']
|
||||
@Component({
|
||||
selector: 'qrzmng-scheduler-control',
|
||||
templateUrl: './scheduler-control.component.html',
|
||||
styleUrls: ['./scheduler-control.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class SchedulerControlComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,173 +1,257 @@
|
||||
<mat-card fxFlex="1 1 auto">
|
||||
<mat-card-header style="padding-bottom: 16px;">
|
||||
<mat-card class="trigger-config-card">
|
||||
<mat-card-header style="padding-bottom: 16px">
|
||||
<mat-card-subtitle><b>TRIGGER DETAILS</b></mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-card-content *ngIf="shouldShowTheTriggerCardContent()" style="position: relative; height: 100%">
|
||||
<div fxLayout="column" style="overflow-y: auto; position: absolute; left: 0; right: 0; top: 0; bottom: 0;
|
||||
overflow: auto;padding: 1em;">
|
||||
<mat-card id="noEligibleJobsAlert" *ngIf="jobs?.length === 0" style="background-color: #ff6385">
|
||||
@if (shouldShowTheTriggerCardContent()) {
|
||||
<mat-card-content class="trigger-config-content">
|
||||
<div
|
||||
class="flex flex-column"
|
||||
style="
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
padding: 1em;
|
||||
">
|
||||
@if (jobs?.length === 0) {
|
||||
<mat-card id="noEligibleJobsAlert" style="background-color: #ff6385">
|
||||
<mat-card-content>
|
||||
<i class="fas fa-exclamation-circle" style="color: #fff"></i> <strong>WARNING</strong>
|
||||
Not found any eligible job classes for quartz-manager! <br/>
|
||||
<p style="font-size: 0.8em">Please, make sure you have extended <i>AbstractQuartzManagerJob</i> and set the
|
||||
app prop <i>quartz-manager.jobClassPackages</i> with the correct java package </p>
|
||||
<i class="fas fa-exclamation-circle" style="color: #fff"></i
|
||||
> <strong>WARNING</strong> Not found any eligible job classes for
|
||||
quartz-manager! <br />
|
||||
<p style="font-size: 0.8em">
|
||||
Please, make sure you have extended
|
||||
<i>AbstractQuartzManagerJob</i> and set the app prop
|
||||
<i>quartz-manager.jobClassPackages</i> with the correct java package
|
||||
</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<form name="triggerConfigForm" class="trigger-config-form" fxFlex="1 1 100%"
|
||||
[formGroup]="simpleTriggerReactiveForm" (ngSubmit)="onSubmitTriggerConfig()">
|
||||
}
|
||||
<form
|
||||
name="triggerConfigForm"
|
||||
class="trigger-config-form"
|
||||
class="flex-1"
|
||||
[formGroup]="simpleTriggerReactiveForm"
|
||||
(ngSubmit)="onSubmitTriggerConfig()">
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input">
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>Trigger Name</mat-label>
|
||||
<input id="triggerName"
|
||||
matInput placeholder="name of the trigger (unique)"
|
||||
formControlName="triggerName" name="triggerName">
|
||||
<mat-error *ngIf="simpleTriggerReactiveForm.controls.triggerName.errors?.required">
|
||||
Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
<input
|
||||
id="triggerName"
|
||||
matInput
|
||||
placeholder="name of the trigger (unique)"
|
||||
formControlName="triggerName"
|
||||
name="triggerName" />
|
||||
@if
|
||||
(simpleTriggerReactiveForm.controls.triggerName.errors?.required) {
|
||||
<mat-error> Name is <strong>required</strong> </mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input"
|
||||
>
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>Job Class</mat-label>
|
||||
<mat-select id="jobClass" name="jobClass" formControlName="jobClass">
|
||||
<mat-option *ngFor="let job of jobs" [value]="job" class="font-13">
|
||||
{{job}}
|
||||
<mat-select
|
||||
id="jobClass"
|
||||
name="jobClass"
|
||||
formControlName="jobClass">
|
||||
@for (job of jobs; track job) {
|
||||
<mat-option [value]="job" class="font-13">
|
||||
{{ job }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error *ngIf="simpleTriggerReactiveForm.controls.jobClass.errors?.required">
|
||||
Job is <strong>required</strong>
|
||||
</mat-error>
|
||||
@if (simpleTriggerReactiveForm.controls.jobClass.errors?.required) {
|
||||
<mat-error> Job is <strong>required</strong> </mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input"
|
||||
>
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>Misfire Instruction</mat-label>
|
||||
<mat-select id="misfireInstruction" name="misfireInstruction" formControlName="misfireInstruction">
|
||||
<mat-option class="font-13" value="MISFIRE_INSTRUCTION_FIRE_NOW">FIRE NOW</mat-option>
|
||||
<mat-option class="font-13" value="MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT">RESCHEDULE NOW WITH
|
||||
EXISTING REPEAT COUNT
|
||||
<mat-select
|
||||
id="misfireInstruction"
|
||||
name="misfireInstruction"
|
||||
formControlName="misfireInstruction">
|
||||
<mat-option class="font-13" value="MISFIRE_INSTRUCTION_FIRE_NOW"
|
||||
>FIRE NOW</mat-option
|
||||
>
|
||||
<mat-option
|
||||
class="font-13"
|
||||
value="MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT"
|
||||
>RESCHEDULE NOW WITH EXISTING REPEAT COUNT
|
||||
</mat-option>
|
||||
<mat-option class="font-13" value="MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT">RESCHEDULE NOW WITH
|
||||
REMAINING REPEAT COUNT
|
||||
<mat-option
|
||||
class="font-13"
|
||||
value="MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT"
|
||||
>RESCHEDULE NOW WITH REMAINING REPEAT COUNT
|
||||
</mat-option>
|
||||
<mat-option class="font-13" value="MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT">RESCHEDULE NEXT WITH
|
||||
REMAINING COUNT
|
||||
<mat-option
|
||||
class="font-13"
|
||||
value="MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT"
|
||||
>RESCHEDULE NEXT WITH REMAINING COUNT
|
||||
</mat-option>
|
||||
<mat-option class="font-13" value="MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT">RESCHEDULE NEXT WITH EXISTING
|
||||
COUNT
|
||||
<mat-option
|
||||
class="font-13"
|
||||
value="MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT"
|
||||
>RESCHEDULE NEXT WITH EXISTING COUNT
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="simpleTriggerReactiveForm.controls.misfireInstruction.errors?.required">
|
||||
@if
|
||||
(simpleTriggerReactiveForm.controls.misfireInstruction.errors?.required)
|
||||
{
|
||||
<mat-error>
|
||||
The misfire instruction is <strong>required</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="small" [innerHTML]="getMisfireInstructionCaption()"></div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<br />
|
||||
<div formGroupName="triggerPeriod">
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input"
|
||||
>
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>Start Date (optional)</mat-label>
|
||||
<input id="startDate"
|
||||
matInput
|
||||
[ngxMatDatetimePicker]="startDatePicker" placeholder="Choose a start date"
|
||||
formControlName="startDate" name="startDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #startDatePicker showSpinners="true" showSeconds="true">
|
||||
</ngx-mat-datetime-picker>
|
||||
<input
|
||||
id="startDate"
|
||||
matInput
|
||||
[owlDateTime]="startDatePicker"
|
||||
[owlDateTimeTrigger]="startDatePicker"
|
||||
placeholder="Choose a start date"
|
||||
formControlName="startDate"
|
||||
name="startDate" />
|
||||
<button
|
||||
type="button"
|
||||
class="datetime-picker-trigger"
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
[owlDateTimeTrigger]="startDatePicker">
|
||||
<mat-icon>event</mat-icon>
|
||||
</button>
|
||||
<owl-date-time
|
||||
#startDatePicker
|
||||
[showSecondsTimer]="true">
|
||||
</owl-date-time>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input"
|
||||
>
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>End Date (optional)</mat-label>
|
||||
<input id="endDate"
|
||||
matInput
|
||||
[ngxMatDatetimePicker]="endDatePicker" placeholder="Choose a end date"
|
||||
formControlName="endDate" name="endDate"
|
||||
>
|
||||
<mat-datepicker-toggle matSuffix [for]="endDatePicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #endDatePicker showSpinners="true" showSeconds="true">
|
||||
</ngx-mat-datetime-picker>
|
||||
<input
|
||||
id="endDate"
|
||||
matInput
|
||||
[owlDateTime]="endDatePicker"
|
||||
[owlDateTimeTrigger]="endDatePicker"
|
||||
placeholder="Choose a end date"
|
||||
formControlName="endDate"
|
||||
name="endDate" />
|
||||
<button
|
||||
type="button"
|
||||
class="datetime-picker-trigger"
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
[owlDateTimeTrigger]="endDatePicker">
|
||||
<mat-icon>event</mat-icon>
|
||||
</button>
|
||||
<owl-date-time
|
||||
#endDatePicker
|
||||
[showSecondsTimer]="true">
|
||||
</owl-date-time>
|
||||
</mat-form-field>
|
||||
<mat-error *ngIf="simpleTriggerReactiveForm.controls.triggerPeriod.errors?.invalidTriggerPeriod" style="font-size: small">
|
||||
the end date cannot be <strong>before</strong> the start date
|
||||
@if
|
||||
(simpleTriggerReactiveForm.controls.triggerPeriod.errors?.invalidTriggerPeriod)
|
||||
{
|
||||
<mat-error style="font-size: small">
|
||||
the end date cannot be <strong>before</strong> the start date
|
||||
</mat-error>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div formGroupName="triggerRecurrence">
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input"
|
||||
>
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>Repeat Interval [in mills]</mat-label>
|
||||
<input id="repeatInterval"
|
||||
matInput placeholder="Repeat Interval [in mills]" type="number"
|
||||
formControlName="repeatInterval" name="repeatInterval"
|
||||
>
|
||||
<mat-error *ngIf="simpleTriggerReactiveForm.controls.triggerRecurrence.errors?.invalidTriggerRecurrence">
|
||||
repeatCount and repeatInterval must be <strong>both</strong> set or unset
|
||||
<input
|
||||
id="repeatInterval"
|
||||
matInput
|
||||
placeholder="Repeat Interval [in mills]"
|
||||
type="number"
|
||||
formControlName="repeatInterval"
|
||||
name="repeatInterval" />
|
||||
@if
|
||||
(simpleTriggerReactiveForm.controls.triggerRecurrence.errors?.invalidTriggerRecurrence)
|
||||
{
|
||||
<mat-error>
|
||||
repeatCount and repeatInterval must be <strong>both</strong> set
|
||||
or unset
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field
|
||||
class="full-size-input"
|
||||
>
|
||||
<mat-form-field class="full-size-input">
|
||||
<mat-label>Repeat Count</mat-label>
|
||||
<input id="repeatCount"
|
||||
matInput placeholder="Repeat Count (-1 REPEAT INDEFINITELY)" type="number"
|
||||
formControlName="repeatCount" name="repeatCount"
|
||||
>
|
||||
<mat-error *ngIf="simpleTriggerReactiveForm.controls.triggerRecurrence.errors?.invalidTriggerRecurrence">
|
||||
repeatCount and repeatInterval must be <strong>both</strong> set or unset
|
||||
<input
|
||||
id="repeatCount"
|
||||
matInput
|
||||
placeholder="Repeat Count (-1 REPEAT INDEFINITELY)"
|
||||
type="number"
|
||||
formControlName="repeatCount"
|
||||
name="repeatCount" />
|
||||
@if
|
||||
(simpleTriggerReactiveForm.controls.triggerRecurrence.errors?.invalidTriggerRecurrence)
|
||||
{
|
||||
<mat-error>
|
||||
repeatCount and repeatInterval must be <strong>both</strong> set
|
||||
or unset
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div fxLayout="row" fxFlexAlign="space-evenly center" style="padding-bottom: 1em;">
|
||||
<div fxFlex="1 1 auto" style="text-align: center" *ngIf="simpleTriggerReactiveForm.enabled">
|
||||
<button mat-raised-button
|
||||
type="button"
|
||||
(click)="onResetReactiveForm()">
|
||||
<br />
|
||||
<div
|
||||
class="flex flex-row align-items-center justify-space-evenly"
|
||||
style="padding-bottom: 1em">
|
||||
@if (simpleTriggerReactiveForm.enabled) {
|
||||
<div class="flex-1" style="text-align: center">
|
||||
<button
|
||||
mat-raised-button
|
||||
type="button"
|
||||
(click)="onResetReactiveForm()">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
<div fxFlex="1 1 auto" style="text-align: center" *ngIf="simpleTriggerReactiveForm.enabled">
|
||||
<button mat-raised-button
|
||||
type="submit" color="primary"
|
||||
[disabled]="simpleTriggerReactiveForm.invalid">
|
||||
} @if (simpleTriggerReactiveForm.enabled) {
|
||||
<div class="flex-1" style="text-align: center">
|
||||
<button
|
||||
mat-raised-button
|
||||
type="submit"
|
||||
color="primary"
|
||||
[disabled]="simpleTriggerReactiveForm.invalid">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
<div fxFlex="1 1 auto" style="text-align: center" *ngIf="!simpleTriggerReactiveForm.enabled">
|
||||
<button mat-raised-button type="button"
|
||||
(click)="openTriggerForm();simpleTriggerReactiveForm.controls['triggerName'].disable();">
|
||||
Reschedule
|
||||
} @if (!simpleTriggerReactiveForm.enabled) {
|
||||
<div class="flex-1" style="text-align: center">
|
||||
<button
|
||||
mat-raised-button
|
||||
type="button"
|
||||
(click)="
|
||||
openTriggerForm();
|
||||
simpleTriggerReactiveForm.controls['triggerName'].disable()
|
||||
">
|
||||
Reschedule
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
}
|
||||
</mat-card>
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
:host {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.trigger-config-card {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.trigger-config-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.small{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ describe('SimpleTriggerConfig', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const componentDe: DebugElement = fixture.debugElement;
|
||||
const submitButton = componentDe.query(By.css('form button'));
|
||||
const submitButton = componentDe.query(By.css('form button:not(.datetime-picker-trigger)'));
|
||||
expect(submitButton.nativeElement.textContent.trim()).toEqual('Reschedule');
|
||||
});
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@ import {SchedulerService} from '../../services';
|
||||
import {Scheduler} from '../../model/scheduler.model';
|
||||
import {SimpleTriggerCommand} from '../../model/simple-trigger.command';
|
||||
import {SimpleTrigger} from '../../model/simple-trigger.model';
|
||||
import * as moment from 'moment';
|
||||
import {TriggerKey} from '../../model/triggerKey.model';
|
||||
import JobService from '../../services/job.service';
|
||||
import {MisfireInstruction, MisfireInstructionCaption} from '../../model/misfire-instruction.model';
|
||||
import {AbstractControl, UntypedFormBuilder, UntypedFormGroup, ValidationErrors, Validators} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'qrzmng-simple-trigger-config',
|
||||
templateUrl: './simple-trigger-config.component.html',
|
||||
styleUrls: ['./simple-trigger-config.component.scss']
|
||||
selector: 'qrzmng-simple-trigger-config',
|
||||
templateUrl: './simple-trigger-config.component.html',
|
||||
styleUrls: ['./simple-trigger-config.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class SimpleTriggerConfigComponent implements OnInit {
|
||||
|
||||
@@ -22,8 +22,8 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
triggerName: [this.trigger?.triggerKeyDTO.name, Validators.required],
|
||||
jobClass: [this.trigger?.jobDetailDTO.jobClassName, Validators.required],
|
||||
triggerPeriod: this.formBuilder.group({
|
||||
startDate: [this.trigger?.startTime && moment(this.trigger?.startTime)],
|
||||
endDate: [this.trigger?.endTime && moment(this.trigger?.endTime)]
|
||||
startDate: [this.trigger?.startTime && new Date(this.trigger.startTime)],
|
||||
endDate: [this.trigger?.endTime && new Date(this.trigger.endTime)]
|
||||
}, {validators: this._triggerPeriodValidator}),
|
||||
triggerRecurrence: this.formBuilder.group({
|
||||
repeatCount: [this.trigger?.repeatCount],
|
||||
@@ -170,7 +170,7 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
const startDate = control.get('startDate');
|
||||
const endDate = control.get('endDate');
|
||||
if (startDate.value && endDate.value) {
|
||||
return endDate.value.isBefore(startDate.value) ?
|
||||
return endDate.value < startDate.value ?
|
||||
<ValidationErrors>{invalidTriggerPeriod: true} : null;
|
||||
}
|
||||
return null;
|
||||
@@ -196,8 +196,8 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
simpleTriggerReactiveForm.jobClass = simpleTrigger.jobDetailDTO.jobClassName;
|
||||
simpleTriggerReactiveForm.triggerRecurrence.repeatCount = simpleTrigger.repeatCount || null;
|
||||
simpleTriggerReactiveForm.triggerRecurrence.repeatInterval = simpleTrigger.repeatInterval || null;
|
||||
simpleTriggerReactiveForm.triggerPeriod.startDate = (simpleTrigger.startTime && moment(simpleTrigger.startTime)) || null;
|
||||
simpleTriggerReactiveForm.triggerPeriod.endDate = (simpleTrigger.endTime && moment(simpleTrigger.endTime)) || null;
|
||||
simpleTriggerReactiveForm.triggerPeriod.startDate = (simpleTrigger.startTime && new Date(simpleTrigger.startTime)) || null;
|
||||
simpleTriggerReactiveForm.triggerPeriod.endDate = (simpleTrigger.endTime && new Date(simpleTrigger.endTime)) || null;
|
||||
simpleTriggerReactiveForm.misfireInstruction = (simpleTrigger.misfireInstruction
|
||||
&& MisfireInstruction[simpleTrigger.misfireInstruction]) || null;
|
||||
return simpleTriggerReactiveForm;
|
||||
@@ -210,8 +210,8 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
simpleTriggerCommand.jobClass = reactiveFormValue.jobClass;
|
||||
simpleTriggerCommand.repeatCount = reactiveFormValue.triggerRecurrence.repeatCount;
|
||||
simpleTriggerCommand.repeatInterval = reactiveFormValue.triggerRecurrence.repeatInterval;
|
||||
simpleTriggerCommand.startDate = reactiveFormValue.triggerPeriod.startDate?.toDate();
|
||||
simpleTriggerCommand.endDate = reactiveFormValue.triggerPeriod.endDate?.toDate();
|
||||
simpleTriggerCommand.startDate = reactiveFormValue.triggerPeriod.startDate;
|
||||
simpleTriggerCommand.endDate = reactiveFormValue.triggerPeriod.endDate;
|
||||
simpleTriggerCommand.misfireInstruction = reactiveFormValue.misfireInstruction;
|
||||
return simpleTriggerCommand;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,42 @@
|
||||
<mat-card fxFlex="1 1 auto" style="padding-left: 0; padding-right: 0">
|
||||
<mat-card-header fxLayout="row" fxLayoutAlign="space-between none" style="padding-right: 1em;" >
|
||||
<mat-card class="trigger-list-card" style="padding-left: 0; padding-right: 0">
|
||||
<mat-card-header
|
||||
class="flex flex-row justify-space-between"
|
||||
style="padding-right: 1em">
|
||||
<mat-card-subtitle><b>TRIGGERS</b></mat-card-subtitle>
|
||||
<button *ngIf="!triggerFormIsOpen" mat-raised-button style="top: -0.5em" color="primary" (click)="onNewTriggerBtnClicked()">
|
||||
@if (!triggerFormIsOpen) {
|
||||
<button
|
||||
mat-raised-button
|
||||
style="top: -0.5em"
|
||||
color="primary"
|
||||
(click)="onNewTriggerBtnClicked()">
|
||||
new
|
||||
</button>
|
||||
}
|
||||
</mat-card-header>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-card-content style="position: relative; height: 100%">
|
||||
<mat-nav-list style="overflow-y: auto; position: absolute; left: 0; right: 0; top: 0; bottom: 0; overflow: auto; height: calc(100% - 3em)">
|
||||
<mat-list-item *ngFor="let triggerKey of getTriggerKeyList()" class="triggerItemList"
|
||||
[ngClass]="{'selectedTrigger': selectedTrigger && selectedTrigger.name==triggerKey.name}"
|
||||
(click)="selectTrigger(triggerKey)"
|
||||
>
|
||||
<mat-card-content class="trigger-list-content">
|
||||
<mat-nav-list
|
||||
style="
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
height: calc(100% - 3em);
|
||||
">
|
||||
@for (triggerKey of getTriggerKeyList(); track triggerKey) {
|
||||
<mat-list-item
|
||||
class="triggerItemList"
|
||||
[ngClass]="{
|
||||
selectedTrigger:
|
||||
selectedTrigger && selectedTrigger.name == triggerKey.name
|
||||
}"
|
||||
(click)="selectTrigger(triggerKey)">
|
||||
<a matLine>{{ triggerKey.name }}</a>
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-nav-list>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
:host {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.trigger-list-card {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.trigger-list-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ===== Scrollbar CSS ===== */
|
||||
/* Firefox */
|
||||
* {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {SimpleTrigger} from '../../model/simple-trigger.model';
|
||||
import {MatDialog, MatDialogRef} from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
template: `
|
||||
<div style="padding:16px">
|
||||
<h3 mat-dialog-title>Coming Soon</h3>
|
||||
<div mat-dialog-content>
|
||||
@@ -15,6 +15,7 @@ import {MatDialog, MatDialogRef} from '@angular/material/dialog';
|
||||
<button mat-button (click)="closeDialog()" style="padding: 0.5em;width: 5em;">Ok</button>
|
||||
</div>
|
||||
</div>`,
|
||||
standalone: false
|
||||
})
|
||||
// tslint:disable-next-line:component-class-suffix
|
||||
export class UnsupportedMultipleJobsDialog {
|
||||
@@ -26,9 +27,10 @@ export class UnsupportedMultipleJobsDialog {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'qrzmng-trigger-list',
|
||||
templateUrl: './trigger-list.component.html',
|
||||
styleUrls: ['./trigger-list.component.scss']
|
||||
selector: 'qrzmng-trigger-list',
|
||||
templateUrl: './trigger-list.component.html',
|
||||
styleUrls: ['./trigger-list.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class TriggerListComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
|
||||
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||
import {UserService} from '../services';
|
||||
import {Observable} from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class AdminGuard implements CanActivate {
|
||||
export class AdminGuard {
|
||||
constructor(private router: Router, private userService: UserService) {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router, CanActivate } from '@angular/router';
|
||||
import { Router } from '@angular/router';
|
||||
import { UserService } from '../services';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class GuestGuard implements CanActivate {
|
||||
export class GuestGuard {
|
||||
|
||||
constructor(private router: Router, private userService: UserService) {}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router, CanActivate } from '@angular/router';
|
||||
import { Router } from '@angular/router';
|
||||
import { UserService } from '../services';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class LoginGuard implements CanActivate {
|
||||
export class LoginGuard {
|
||||
|
||||
constructor(private router: Router, private userService: UserService) {}
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ export const MisfireInstructionCaption = new Map<number, string>([
|
||||
`In case of misfire event, the trigger is re-scheduled to the next scheduled time after 'now'
|
||||
with the repeat count set to what it would be if it had not missed any firings.<br/>
|
||||
Use this policy if no jobs must run after the end date time.<br/>
|
||||
<strong>Warning</strong> The actual number of job executions could be less than initially set, because the misfired trigger are ignored.<br/>
|
||||
<strong>Warning</strong> The actual number of job executions could be less than initially set,
|
||||
because the misfired trigger are ignored.<br/>
|
||||
This policy could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed.`
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import {Moment} from 'moment/moment';
|
||||
|
||||
export class SimpleTriggerForm {
|
||||
triggerName: string;
|
||||
jobClass: string;
|
||||
startDate: Moment;
|
||||
endDate: Moment;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
repeatCount: number;
|
||||
repeatInterval: number;
|
||||
misfireInstruction: string;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
|
||||
/** *************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||
// import 'core-js/es6/symbol';
|
||||
// import 'core-js/es6/object';
|
||||
// import 'core-js/es6/function';
|
||||
// import 'core-js/es6/parse-int';
|
||||
// import 'core-js/es6/parse-float';
|
||||
// import 'core-js/es6/number';
|
||||
// import 'core-js/es6/math';
|
||||
// import 'core-js/es6/string';
|
||||
// import 'core-js/es6/date';
|
||||
// import 'core-js/es6/array';
|
||||
// import 'core-js/es6/regexp';
|
||||
// import 'core-js/es6/map';
|
||||
// import 'core-js/es6/set';
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/** IE10 and IE11 requires the following to support `@angular/animation`. */
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
import 'core-js/es6/reflect';
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
|
||||
|
||||
/** ALL Firefox browsers require the following to support `@angular/animation`. **/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
|
||||
|
||||
/** *************************************************************************************************
|
||||
* Zone JS is required by Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
|
||||
|
||||
/** *************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Date, currency, decimal and percent pipes.
|
||||
* Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
|
||||
*/
|
||||
// import 'intl'; // Run `npm install --save intl`.
|
||||
|
||||
/** *************************************************************************************************
|
||||
* MATERIAL 2
|
||||
*/
|
||||
import 'hammerjs/hammer';
|
||||
@@ -1,4 +1,4 @@
|
||||
import {HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams} from '@angular/common/http';
|
||||
import { HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams } from '@angular/common/http';
|
||||
import {Router} from '@angular/router';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpHeaders, HttpResponse} from '@angular/common/http';
|
||||
import { HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import {ApiService} from './api.service';
|
||||
import {UserService} from './user.service';
|
||||
import {ConfigService} from './config.service';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {RxStomp} from '@stomp/rx-stomp';
|
||||
import {RxStompConfig} from '@stomp/rx-stomp/esm6/rx-stomp-config';
|
||||
import {RxStomp, RxStompConfig} from '@stomp/rx-stomp';
|
||||
|
||||
export class RxStompService extends RxStomp {
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import {Injectable} from '@angular/core';
|
||||
import {ApiService} from './api.service';
|
||||
import {ConfigService} from './config.service';
|
||||
|
||||
import {map} from 'rxjs/operators'
|
||||
import {HttpErrorResponse} from '@angular/common/http';
|
||||
import {Router} from '@angular/router';
|
||||
import {map} from 'rxjs/operators'
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import {Router} from '@angular/router';
|
||||
import {firstValueFrom} from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
@@ -22,7 +23,7 @@ export class UserService {
|
||||
refreshToken() {
|
||||
this.apiService.get(this.config.refresh_token_url).subscribe(res => {
|
||||
if (res.accessToken !== null) {
|
||||
return this.getUserInfo().toPromise()
|
||||
return firstValueFrom(this.getUserInfo())
|
||||
.then(user => {
|
||||
this.currentUser = user;
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div fxLayout="column" fxLayoutAlign="center" style="text-align: center">
|
||||
<div class="flex flex-column justify-center" style="text-align: center">
|
||||
<div>
|
||||
<div>
|
||||
<p style="font-size: 4em; margin-bottom: 0">Unexpected Error</p>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'qrzmng-generic-error',
|
||||
templateUrl: './genericError.component.html',
|
||||
styleUrls: ['./genericError.component.css']
|
||||
selector: 'qrzmng-generic-error',
|
||||
templateUrl: './genericError.component.html',
|
||||
styleUrls: ['./genericError.component.css'],
|
||||
standalone: false
|
||||
})
|
||||
export class GenericErrorComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div fxLayout="column" fxLayoutAlign="center" style="text-align: center">
|
||||
<div class="flex flex-column justify-center" style="text-align: center">
|
||||
<div>
|
||||
<div>
|
||||
<p style="font-size: 4em; margin-bottom: 0">Forbidden - Access Senied</p>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-forbidden',
|
||||
templateUrl: './forbidden.component.html',
|
||||
styleUrls: ['./forbidden.component.css']
|
||||
selector: 'app-forbidden',
|
||||
templateUrl: './forbidden.component.html',
|
||||
styleUrls: ['./forbidden.component.css'],
|
||||
standalone: false
|
||||
})
|
||||
export class ForbiddenComponent implements OnInit {
|
||||
|
||||
|
||||
@@ -1,32 +1,46 @@
|
||||
<div class="content" fxLayout="row" fxLayoutAlign="center" style="padding-bottom:160px;">
|
||||
|
||||
<mat-card elevation="5" fxFlex>
|
||||
|
||||
<mat-card-subtitle>
|
||||
<h2>Quartz Manager</h2>
|
||||
</mat-card-subtitle>
|
||||
|
||||
<mat-card-title>
|
||||
<h2>{{title}}</h2>
|
||||
</mat-card-title>
|
||||
|
||||
<mat-card-content>
|
||||
|
||||
<p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p>
|
||||
|
||||
<form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #loginForm="ngForm">
|
||||
<mat-form-field>
|
||||
<input matInput formControlName="username" required placeholder="user">
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput formControlName="password" required type="password" placeholder="password">
|
||||
</mat-form-field>
|
||||
<button type="submit" [disabled]="!loginForm.form.valid" mat-raised-button color="primary">Login</button>
|
||||
</form>
|
||||
|
||||
<mat-spinner *ngIf="submitted" mode="indeterminate"></mat-spinner>
|
||||
</mat-card-content>
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="content flex flex-row justify-center"
|
||||
style="padding-bottom: 160px">
|
||||
<mat-card elevation="5" class="flex-1">
|
||||
<mat-card-subtitle>
|
||||
<h2>Quartz Manager</h2>
|
||||
</mat-card-subtitle>
|
||||
|
||||
<mat-card-title>
|
||||
<h2>{{ title }}</h2>
|
||||
</mat-card-title>
|
||||
|
||||
<mat-card-content>
|
||||
@if (notification) {
|
||||
<p [class]="notification.msgType">{{ notification.msgBody }}</p>
|
||||
} @if (!submitted) {
|
||||
<form [formGroup]="form" (ngSubmit)="onSubmit()" #loginForm="ngForm">
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
formControlName="username"
|
||||
required
|
||||
placeholder="user" />
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
formControlName="password"
|
||||
required
|
||||
type="password"
|
||||
placeholder="password" />
|
||||
</mat-form-field>
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="!loginForm.form.valid"
|
||||
mat-raised-button
|
||||
color="primary">
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
} @if (submitted) {
|
||||
<mat-spinner mode="indeterminate"></mat-spinner>
|
||||
}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
@@ -7,10 +7,11 @@ import {delay, takeUntil} from 'rxjs/operators'
|
||||
|
||||
import {AuthService, UserService} from '../../services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss']
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class LoginComponent implements OnInit, OnDestroy {
|
||||
title = 'Login';
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
|
||||
<div id="manager-content-container" class="flex flex-row flex-1 gap-6">
|
||||
<div class="flex-1" style="max-width: 250px">
|
||||
<div fxLayout="row" fxLayoutAlign="stretch" fxFill>
|
||||
<qrzmng-trigger-list
|
||||
(onNewTriggerClicked)="onNewTriggerRequested()"
|
||||
[openedNewTriggerForm]="newTriggerFormOpened"
|
||||
(onSelectedTrigger)="setSelectedTrigger($event)"
|
||||
fxFill></qrzmng-trigger-list>
|
||||
</div>
|
||||
<div class="flex h-100">
|
||||
<qrzmng-trigger-list
|
||||
(onNewTriggerClicked)="onNewTriggerRequested()"
|
||||
[openedNewTriggerForm]="newTriggerFormOpened"
|
||||
(onSelectedTrigger)="setSelectedTrigger($event)"
|
||||
class="h-100 w-100"></qrzmng-trigger-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1" style="max-width: 350px">
|
||||
<div fxLayout="row" fxFill>
|
||||
<div fxLayout="column" fxFill>
|
||||
<qrzmng-simple-trigger-config
|
||||
fxFill
|
||||
<div class="flex h-100">
|
||||
<div class="flex flex-column h-100 w-100">
|
||||
<qrzmng-simple-trigger-config
|
||||
class="h-100 w-100"
|
||||
[triggerKey]="selectedTriggerKey"
|
||||
(triggerFormOpenChange)="setNewTriggerFormOpened($event)"
|
||||
(onTriggerSubmitting)="monitorTrigger($event)"
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { SimpleTrigger } from '../../model/simple-trigger.model';
|
||||
import { TriggerKey } from '../../model/triggerKey.model';
|
||||
import { SimpleTriggerConfigComponent } from '../../components/simple-trigger-config';
|
||||
import { TriggerListComponent } from '../../components';
|
||||
|
||||
@Component({
|
||||
selector: 'manager',
|
||||
templateUrl: './manager.component.html',
|
||||
styleUrls: ['./manager.component.scss']
|
||||
selector: 'manager',
|
||||
templateUrl: './manager.component.html',
|
||||
styleUrls: ['./manager.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class ManagerComponent implements OnInit {
|
||||
export class ManagerComponent implements OnInit, AfterViewInit {
|
||||
@ViewChild(SimpleTriggerConfigComponent)
|
||||
private triggerConfigComponent!: SimpleTriggerConfigComponent;
|
||||
|
||||
@@ -22,19 +23,34 @@ export class ManagerComponent implements OnInit {
|
||||
|
||||
monitoredTriggerKey: TriggerKey;
|
||||
|
||||
private pendingNewTriggerRequest = false;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (this.pendingNewTriggerRequest) {
|
||||
queueMicrotask(() => this.openNewTriggerForm());
|
||||
}
|
||||
}
|
||||
|
||||
onNewTriggerRequested() {
|
||||
this.selectedTriggerKey = null;
|
||||
this.monitoredTriggerKey = null;
|
||||
this.newTriggerFormOpened = true;
|
||||
if (this.triggerConfigComponent) {
|
||||
this.triggerConfigComponent.openNewTriggerForm();
|
||||
this.openNewTriggerForm();
|
||||
} else {
|
||||
this.pendingNewTriggerRequest = true;
|
||||
}
|
||||
}
|
||||
|
||||
private openNewTriggerForm() {
|
||||
this.newTriggerFormOpened = true;
|
||||
this.pendingNewTriggerRequest = false;
|
||||
this.triggerConfigComponent.openNewTriggerForm();
|
||||
}
|
||||
|
||||
onNewTriggerCreated(newTrigger: SimpleTrigger) {
|
||||
this.triggerListComponent.onNewTrigger(newTrigger);
|
||||
this.newTriggerFormOpened = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div fxLayout="column" fxLayoutAlign="center" style="text-align: center">
|
||||
<div class="flex flex-column justify-center" style="text-align: center">
|
||||
<div>
|
||||
<div>
|
||||
<p style="font-size: 4em; margin-bottom: 0">Not Found!</p>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
templateUrl: './not-found.component.html'
|
||||
templateUrl: './not-found.component.html',
|
||||
standalone: false
|
||||
})
|
||||
export class NotFoundComponent {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { enableProdMode, provideZoneChangeDetection } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
@@ -8,4 +8,6 @@ if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
platformBrowserDynamic().bootstrapModule(AppModule, {
|
||||
applicationProviders: [provideZoneChangeDetection()],
|
||||
});
|
||||
|
||||
@@ -1,75 +1,3 @@
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
import 'zone.js';
|
||||
|
||||
/** *************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||
// import 'core-js/es6/symbol';
|
||||
// import 'core-js/es6/object';
|
||||
// import 'core-js/es6/function';
|
||||
// import 'core-js/es6/parse-int';
|
||||
// import 'core-js/es6/parse-float';
|
||||
// import 'core-js/es6/number';
|
||||
// import 'core-js/es6/math';
|
||||
// import 'core-js/es6/string';
|
||||
// import 'core-js/es6/date';
|
||||
// import 'core-js/es6/array';
|
||||
// import 'core-js/es6/regexp';
|
||||
// import 'core-js/es6/map';
|
||||
// import 'core-js/es6/set';
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/** IE10 and IE11 requires the following to support `@angular/animation`. */
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
import 'core-js/es6/reflect';
|
||||
|
||||
|
||||
|
||||
/** ALL Firefox browsers require the following to support `@angular/animation`. **/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
|
||||
|
||||
/** *************************************************************************************************
|
||||
* Zone JS is required by Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
(window as any).global = window
|
||||
|
||||
|
||||
|
||||
/** *************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Date, currency, decimal and percent pipes.
|
||||
* Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
|
||||
*/
|
||||
// import 'intl'; // Run `npm install --save intl`.
|
||||
|
||||
/** *************************************************************************************************
|
||||
* MATERIAL 2
|
||||
*/
|
||||
import 'hammerjs/hammer';
|
||||
(window as any).global = window;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
|
||||
@import '@angular/material/prebuilt-themes/deeppurple-amber.css';
|
||||
@import '@danielmoncada/angular-datetime-picker/assets/style/picker.min.css';
|
||||
@import "animate.css";
|
||||
|
||||
html {
|
||||
@@ -32,6 +33,22 @@ body {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-space-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-space-evenly {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-flex-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
@@ -48,6 +65,10 @@ body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-none {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.h-100 {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -68,6 +89,18 @@ body {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.gap-10 {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.gap-12 {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.gap-30 {
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -100,3 +133,12 @@ body {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.align-items-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
@media (max-width: 599px) {
|
||||
.log-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,12 @@
|
||||
"baseUrl": "src",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2022",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2016",
|
||||
"dom"
|
||||
],
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"module": "es2020",
|
||||
"useDefineForClassFields": false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user