Project first commit
1
frontend/.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
registry=https://repo.micord.ru/repository/npm-all/
|
||||
71
frontend/angular.json
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"webbpm-frontend": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/ts/main.ts",
|
||||
"tsConfig": "src/tsconfig.json",
|
||||
"polyfills": "src/ts/polyfills.ts",
|
||||
"assets": [
|
||||
"src/resources"
|
||||
],
|
||||
"styles": [
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/jquery/dist/jquery.min.js",
|
||||
"node_modules/moment/min/moment-with-locales.js",
|
||||
"node_modules/moment-timezone/builds/moment-timezone-with-data.min.js",
|
||||
"node_modules/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js",
|
||||
"node_modules/selectize/dist/js/standalone/selectize.min.js",
|
||||
"node_modules/downloadjs/download.min.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "webbpm-frontend:build"
|
||||
},
|
||||
"configurations": {}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "webbpm-frontend:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "webbpm-frontend"
|
||||
}
|
||||
10
frontend/bs-config.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"port": 8000,
|
||||
"open": false,
|
||||
"files": [
|
||||
"./**/*.{html,htm,css,js}"
|
||||
],
|
||||
"server": {
|
||||
"baseDir": "./"
|
||||
}
|
||||
}
|
||||
23
frontend/index.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ervu_secret</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link rel="icon" type="image/png" href="src/resources/img/logo.png"/>
|
||||
<link rel="stylesheet" href="src/resources/css/style.css"/>
|
||||
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('webbpm').catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body webbpm class="webbpm ervu_secret">
|
||||
<div class="progress"></div>
|
||||
</body>
|
||||
</html>
|
||||
11
frontend/index.webpack.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ervu_secret</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link rel="icon" type="image/png" href="src/resources/img/logo.png"/>
|
||||
</head>
|
||||
<body webbpm class="webbpm ervu_secret">
|
||||
<div class="progress"></div>
|
||||
</body>
|
||||
</html>
|
||||
100
frontend/package.json
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"name": "webbpm-frontend",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"lite": "node ./node_modules/lite-server/bin/lite-server",
|
||||
"cleanup": "npm run cleanup-ngc && node ./node_modules/rimraf/bin ./build ./dist",
|
||||
"cleanup-ngc": "node ./node_modules/rimraf/bin ./src/ts/**/*.js ./src/ts/**/*.json ./src/ts/page.routing.ts",
|
||||
"cleanup-and-ngc": "npm run cleanup && npm run ngc",
|
||||
"ngc": "node --max-old-space-size=14336 ./node_modules/@angular/compiler-cli/src/main -p tsconfig.aot.json",
|
||||
"build-webpack": "node --max-old-space-size=14336 ./node_modules/webpack/bin/webpack --config webpack.aot.config.js --progress --profile",
|
||||
"save-ts-metadata": "node save.ts.metadata.js",
|
||||
"tsc": "node ./node_modules/typescript/bin/tsc",
|
||||
"tsc-watch": "node ./node_modules/typescript/bin/tsc --watch",
|
||||
"ts-watch": "node node_modules/cross-env/dist/bin/cross-env.js TSC_NONPOLLING_WATCHER=true npm run tsc-watch",
|
||||
"ts": "npm install && npm run tsc",
|
||||
"compile": "npm run ts-watch",
|
||||
"install-compile": "npm install && npm run ts-watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "7.2.15",
|
||||
"@angular/common": "7.2.15",
|
||||
"@angular/compiler": "7.2.15",
|
||||
"@angular/core": "7.2.15",
|
||||
"@angular/forms": "7.2.15",
|
||||
"@angular/http": "7.2.15",
|
||||
"@angular/platform-browser": "7.2.15",
|
||||
"@angular/platform-browser-dynamic": "7.2.15",
|
||||
"@angular/router": "7.2.15",
|
||||
"@ng-bootstrap/ng-bootstrap": "4.1.1",
|
||||
"ag-grid-angular": "29.0.0-micord.4",
|
||||
"ag-grid-community": "29.0.0-micord.4",
|
||||
"angular-calendar": "0.28.28",
|
||||
"autonumeric": "4.5.10-cg",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-icons": "1.10.3",
|
||||
"cadesplugin_api": "2.0.4-micord.1",
|
||||
"chart.js": "3.8.0-cg.1",
|
||||
"chartjs-adapter-moment": "1.0.0",
|
||||
"core-js": "2.4.1",
|
||||
"date-fns": "2.29.3",
|
||||
"downloadjs": "1.4.8",
|
||||
"eonasdan-bootstrap-datetimepicker": "4.17.47-micord.4",
|
||||
"esmarttokenjs": "2.2.1-cg",
|
||||
"font-awesome": "4.7.0",
|
||||
"google-libphonenumber": "3.0.9",
|
||||
"inputmask": "5.0.5-cg.2",
|
||||
"jquery": "3.3.1",
|
||||
"js-year-calendar": "1.0.0-cg.2",
|
||||
"jsgantt-improved": "2.0.10-cg",
|
||||
"moment": "2.17.1",
|
||||
"moment-timezone": "0.5.11",
|
||||
"ngx-cookie": "3.0.1",
|
||||
"ngx-international-phone-number": "1.0.6",
|
||||
"ngx-toastr": "10.2.0-cg",
|
||||
"popper.js": "1.14.7",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"rxjs-compat": "6.4.0",
|
||||
"selectize": "0.12.4-cg.10",
|
||||
"systemjs": "0.21.4",
|
||||
"systemjs-plugin-babel": "0.0.25",
|
||||
"tslib": "1.9.3",
|
||||
"zone.js": "0.8.29"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-optimizer": "0.13.9",
|
||||
"@angular-devkit/core": "7.3.9",
|
||||
"@angular/cli": "7.3.9",
|
||||
"@angular/compiler-cli": "7.2.15",
|
||||
"@angular/platform-server": "7.2.15",
|
||||
"@babel/core": "7.9.6",
|
||||
"@babel/preset-env": "7.9.6",
|
||||
"@types/bootstrap": "3.3.39",
|
||||
"@types/jquery": "2.0.49",
|
||||
"@types/node": "7.0.5",
|
||||
"@types/selectize": "0.12.33",
|
||||
"angular-router-loader": "0.8.5",
|
||||
"angular2-template-loader": "0.6.2",
|
||||
"babel-loader": "8.1.0",
|
||||
"codelyzer": "5.2.1",
|
||||
"copy-webpack-plugin": "5.0.3",
|
||||
"cross-env": "5.2.1",
|
||||
"css-loader": "2.1.0",
|
||||
"del": "2.2.2",
|
||||
"file-loader": "3.0.1",
|
||||
"html-webpack-plugin": "4.5.2",
|
||||
"lite-server": "2.3.0",
|
||||
"mini-css-extract-plugin": "0.6.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"raw-loader": "1.0.0",
|
||||
"style-loader": "0.23.1",
|
||||
"terser-webpack-plugin": "1.2.4",
|
||||
"tslint": "5.13.1",
|
||||
"typescript": "3.2.4",
|
||||
"typescript-parser": "2.6.1-cg-fork",
|
||||
"webpack": "4.32.2",
|
||||
"webpack-bundle-analyzer": "3.3.2",
|
||||
"webpack-cli": "3.3.2"
|
||||
}
|
||||
}
|
||||
113
frontend/pom.xml
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>ervu_secret</groupId>
|
||||
<artifactId>ervu_secret</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ervu_secret.ervu_secret</groupId>
|
||||
<artifactId>frontend</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>replacer</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>replace-version-in-url</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>replace</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>${basedir}/src/resources/app-config.json</include>
|
||||
<include>${basedir}/dist/src/resources/app-config.json</include>
|
||||
<include>${basedir}/src/resources/app.version</include>
|
||||
<include>${basedir}/dist/src/resources/app.version</include>
|
||||
</includes>
|
||||
<replacements>
|
||||
<replacement>
|
||||
<token>%project.version%</token>
|
||||
<value>${project.version}</value>
|
||||
</replacement>
|
||||
<replacement>
|
||||
<token>%enable.version.in.url%</token>
|
||||
<value>${enable.version.in.url}</value>
|
||||
</replacement>
|
||||
</replacements>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<copyWebResources>false</copyWebResources>
|
||||
<webResources>
|
||||
<resource>
|
||||
<directory>${basedir}</directory>
|
||||
<includes>
|
||||
<include>src/resources/**/*</include>
|
||||
<include>build_dev/**/*</include>
|
||||
<include>node_modules/**/*</include>
|
||||
<include>index.html</include>
|
||||
<include>systemjs.config.js</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>compile-ts</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>prod</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<copyWebResources>false</copyWebResources>
|
||||
<webResources>
|
||||
<resource>
|
||||
<directory>${basedir}/dist</directory>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>enable-version-in-url</id>
|
||||
<properties>
|
||||
<enable.version.in.url>true</enable.version.in.url>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
23
frontend/preview.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web BPM</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link rel="icon" type="image/png" href="src/resources/img/logo.png"/>
|
||||
<link rel="stylesheet" href="src/resources/css/style.css"/>
|
||||
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="systemjs.preview.config.js"></script>
|
||||
<script>
|
||||
System.import('preview').catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body preview-container id="webbpm-angular-application-container ervu_secret" class="webbpm">
|
||||
<div class="progress"></div>
|
||||
</body>
|
||||
</html>
|
||||
72
frontend/save.ts.metadata.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env node
|
||||
var tsp = require("typescript-parser");
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var ts = require("typescript");
|
||||
|
||||
var parser = new tsp.TypescriptParser();
|
||||
var excludedDirs = [
|
||||
'generated-sources'
|
||||
];
|
||||
|
||||
var walkFileTree = function (dir, action) {
|
||||
if (typeof action !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.readdirSync(dir).forEach(function (file) {
|
||||
var path = dir + "/" + file;
|
||||
var stat = fs.statSync(path);
|
||||
var extension = ".ts";
|
||||
if (stat && stat.isDirectory() && excludedDirs.indexOf(file) === -1) {
|
||||
walkFileTree(path, action);
|
||||
}
|
||||
else if (path.indexOf(extension, path.length - extension.length) !== -1) {
|
||||
action(null, path);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var dateInLong = Date.now();
|
||||
var arr = [];
|
||||
|
||||
var basePath = path.resolve(__dirname, "src/ts/");
|
||||
walkFileTree(basePath, function (err, file) {
|
||||
var content = fs.readFileSync(file).toString();
|
||||
var jsonStructure = parser.parseTypescript(ts.createSourceFile(
|
||||
file,
|
||||
content,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.TS
|
||||
),
|
||||
'/');
|
||||
jsonStructure['packageName'] = path.relative(path.resolve(__dirname, "src/ts/"),jsonStructure['filePath']);
|
||||
jsonStructure['imports'].forEach( function (val) {
|
||||
if (val.libraryName.startsWith(".")) {
|
||||
val['libraryName'] = path.resolve(path.dirname(jsonStructure['filePath']), val['libraryName']);
|
||||
val['libraryName'] = path.relative(path.resolve(__dirname, "src/ts/"), val['libraryName']);
|
||||
val['libraryName'] = path.dirname(val['libraryName']).split(path.sep).join(".");
|
||||
}
|
||||
});
|
||||
delete jsonStructure['filePath'];
|
||||
jsonStructure['packageName'] = path.dirname(jsonStructure['packageName']).split(path.sep).join( ".");
|
||||
arr.push(jsonStructure);
|
||||
});
|
||||
var cache = [];
|
||||
|
||||
fs.writeFileSync("./../.studio/typescript.metadata.json",
|
||||
JSON.stringify(arr, function (key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
// Circular reference found, discard key
|
||||
return;
|
||||
}
|
||||
// Store value in our collection
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
}));
|
||||
|
||||
cache = null;
|
||||
console.log("typescript parse time = " + (Date.now() - dateInLong));
|
||||
19
frontend/src/resources/app-config.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"electronic_sign.esmart_extension_url": "",
|
||||
"electronic_sign.tsp_address": "",
|
||||
"filter_cleanup_interval_hours": 720,
|
||||
"filter_cleanup_check_period_minutes": 30,
|
||||
"auth_method": "form",
|
||||
"enable.version.in.url": "%enable.version.in.url%",
|
||||
"guard.confirm_exit": false,
|
||||
"message_service_error_timeout": "",
|
||||
"message_service_warning_timeout": "",
|
||||
"message_service_success_timeout": "",
|
||||
"message_service_info_timeout": "",
|
||||
"jivo_chat_widget_api_url": "https://code.jivo.ru/widget/{ID}",
|
||||
"jivo_chat_widget_enabled": false,
|
||||
"password_pattern": "^((?=(.*\\d){1,})(?=.*[a-zа-яё])(?=.*[A-ZА-ЯЁ]).{8,})$",
|
||||
"password_pattern_error": "Пароль должен содержать заглавные или прописные буквы и как минимум 1 цифру",
|
||||
"show.client.errors": false,
|
||||
"available_task.single_fetch": true
|
||||
}
|
||||
1
frontend/src/resources/app.version
Normal file
|
|
@ -0,0 +1 @@
|
|||
%project.version%
|
||||
1584
frontend/src/resources/css/components-app.css
Normal file
342
frontend/src/resources/css/inbox-app.css
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
@font-face {
|
||||
font-family: 'Segoe';
|
||||
src: url('../fonts/Segoe.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SegoeSL';
|
||||
src: url('../fonts/SegoeSL.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SegoeSB';
|
||||
src: url('../fonts/SegoeSB.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SegoeB';
|
||||
src: url('../fonts/SegoeB.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SegoeBL';
|
||||
src: url('../fonts/SegoeBL.ttf');
|
||||
}
|
||||
|
||||
.webbpm a {
|
||||
color: var(--color-link);
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: var(--color-link-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
body.webbpm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--color-text-primary);
|
||||
font-family: 'Segoe';
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
.webbpm .container {
|
||||
padding: 70px 0 0;
|
||||
}
|
||||
|
||||
body.webbpm [id="page"],
|
||||
.webbpm .container .container-inside {
|
||||
font-family: 'Segoe';
|
||||
font-size: var(--size-text-primary);
|
||||
}
|
||||
|
||||
.webbpm .logo {
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.webbpm .header-logo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-left: 40px;
|
||||
|
||||
.logo a {
|
||||
background: url('../../../src/resources/img/logo-full.png') no-repeat 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm .header-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: auto;
|
||||
margin-right: 40px;
|
||||
& > * {
|
||||
margin-right: 20px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--white);
|
||||
font-size: var(--size-text-primary);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 15px;
|
||||
background-color: var(--color-text-primary);
|
||||
outline: transparent;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background-color: var(--color-link);
|
||||
}
|
||||
}
|
||||
.logout .user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--black);
|
||||
padding: 4px 20px;
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
margin: 0 0 10px 0;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.user-fio {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.user-department {
|
||||
color: #a0b1bc;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm .header {
|
||||
display: flex;
|
||||
font-family: 'Segoe';
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 70px;
|
||||
border-bottom: 1px solid var(--bg-light);
|
||||
background: var(--white);
|
||||
box-shadow: 0px 15px 20px 0px rgb(0 0 0 / 4%);
|
||||
|
||||
& > div > * {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
top: 69px !important;
|
||||
right: 0px !important;
|
||||
left: auto !important;
|
||||
transform: none !important;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0 0 10px 10px;
|
||||
background-color: var(--white);
|
||||
box-shadow: 0 8px 12px rgb(77 72 91 / 5%), 0 6px 10px rgb(77 72 91 / 0%);
|
||||
|
||||
.dropdown-menu-inner {
|
||||
max-height: calc(100vh - 140px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
:is(process, admin-menu) .dropdown-menu.show {
|
||||
top: 49px !important;
|
||||
}
|
||||
|
||||
.logout .dropdown-menu.show {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm .dropdown-menu-inner:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.webbpm .dropdown-item {
|
||||
padding: 4px 20px;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: var(--color-link);
|
||||
background-color: transparent;
|
||||
outline: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm footer {
|
||||
color: var(--color-text-primary);
|
||||
font-size: var(--size-text-secondary);
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 15px 40px;
|
||||
border-top: 1px solid var(--border-light);
|
||||
a {
|
||||
color: var(--color-text-primary);
|
||||
&:hover {
|
||||
color: var(--color-link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------- Menu tasks -------------- */
|
||||
.webbpm .task-list-tree-panel {
|
||||
padding: 0 40px;
|
||||
.task-list-filter {
|
||||
font-family: 'Segoe';
|
||||
box-shadow: none;
|
||||
|
||||
li:first-of-type {
|
||||
font-family: 'SegoeSB';
|
||||
font-weight: normal;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li.ontime label div {
|
||||
background-color: #31c980;
|
||||
}
|
||||
|
||||
li.overdue label div {
|
||||
background-color: var(--color-link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm .task-list-workplace {
|
||||
padding: 20px 40px 0 40px;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl :is(.tr.task-ontime, .tr.task-overdue) > .td.task::before {
|
||||
top: 24px;
|
||||
background-color: #31c980;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .tr.task-overdue > .td.task::before {
|
||||
background-color: var(--color-link);
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .thead {
|
||||
display: table-header-group;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .th {
|
||||
color: var(--color-text-primary);
|
||||
font-family: 'SegoeSB';
|
||||
font-size: var(--size-text-secondary);
|
||||
font-weight: normal;
|
||||
padding: 9px 12px;
|
||||
border: 0;
|
||||
background: var(--bg-light);
|
||||
}
|
||||
.webbpm .task-tbl .th:first-child {
|
||||
border-top-left-radius: 12px;
|
||||
border-bottom-left-radius: 12px;
|
||||
}
|
||||
.webbpm .task-tbl .th:last-child {
|
||||
border-top-right-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .td {
|
||||
color: var(--color-text-primary);
|
||||
font-size: var(--size-text-primary);
|
||||
padding: 16px 12px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .thead ~ .tr {
|
||||
border-color: var(--border-light);
|
||||
border-width: 1px 0 0 0;
|
||||
border-style: solid;
|
||||
}
|
||||
.webbpm .task-tbl .thead + .tr {
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .thead ~ .tr:hover {
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
.webbpm .task-tbl .thead ~ .tr:hover +.tr {
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
.webbpm .task-tbl .thead ~ .tr:hover .td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.webbpm .task-tbl .thead ~ .tr:hover .td:first-child {
|
||||
border-top-left-radius: 12px;
|
||||
border-bottom-left-radius: 12px;
|
||||
}
|
||||
.webbpm .task-tbl .thead ~ .tr:hover .td:last-child {
|
||||
border-top-right-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
/*------------- end Menu tasks ----------- */
|
||||
|
||||
/*----------------- Login ---------------- */
|
||||
.webbpm :is(.form-signin, .form-signup, .confirm) {
|
||||
color: var(--color-text-primary);
|
||||
width: 560px;
|
||||
padding: 60px 80px;
|
||||
margin: 30px auto;
|
||||
border: 0;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0px 0px 30px 2px rgb(77 72 91 / 12%);
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
.webbpm :is(.form-signin, .form-signup) .row.title {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.webbpm .form-signin h1,
|
||||
.webbpm .form-signin h2,
|
||||
.webbpm .form-signup h2,
|
||||
.webbpm .confirm h2 {
|
||||
font-family: 'SegoeB';
|
||||
font-size: 32px;
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.webbpm :is(.form-signin, .form-signup, .confirm) .logo {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: 0;
|
||||
width: 145px;
|
||||
height: 40px;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.webbpm .form-signin .row.registration > * + *,
|
||||
.webbpm .form-signin .login-btn-box .password,
|
||||
.webbpm .form-signin .login-btn-box .btn + .btn {
|
||||
margin-left: 20px;
|
||||
}
|
||||
/*--------------- end Login -------------- */
|
||||
846
frontend/src/resources/css/structure.css
Normal file
|
|
@ -0,0 +1,846 @@
|
|||
:root {
|
||||
--white: #ffffff;
|
||||
--black: #000000;
|
||||
--color-text-primary: #404954;
|
||||
--color-link: #1c92ea;
|
||||
--color-link-hover: #1b84d2;
|
||||
--bg-light: #f5f7fa;
|
||||
--bg-secondary: #4c5969;
|
||||
--border-light: #e3e6ed;
|
||||
|
||||
--size-text-primary: 16px;
|
||||
--size-text-secondary: 14px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body.webbpm .form-signin label {
|
||||
width: 160px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.webbpm .progress {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-bottom: 0 !important;
|
||||
background: url("../img/progress.gif") no-repeat 0 0;
|
||||
}
|
||||
|
||||
.webbpm > .progress {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -30px;
|
||||
margin-left: -30px;
|
||||
}
|
||||
|
||||
.webbpm .search-task-progress-bar {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.webbpm .modal-body .progress,
|
||||
.webbpm .search-task-progress-bar .progress {
|
||||
position: relative;
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/*-- common class --*/
|
||||
.webbpm .fl-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.webbpm .fl-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.webbpm .anchor {
|
||||
float: none;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.webbpm :is(ul, ol) li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.webbpm :is(h1, h2, h3) {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
.webbpm h1 {
|
||||
font-size: 2.33em;
|
||||
}
|
||||
.webbpm h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.webbpm h3 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.webbpm .table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.webbpm .tr {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.webbpm .td, .webbpm .th {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
/*-- layout --*/
|
||||
html, body.webbpm {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
body.webbpm {
|
||||
background-color: #f9f9fa;
|
||||
font-family: Arial;
|
||||
font-size: var(--size-text-secondary);
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.webbpm .wrapper {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.webbpm .container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 67px 0 0;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 50px;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
|
||||
[ng-include="taskPageFile"] {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.container-inside {
|
||||
font-family: Arial;
|
||||
font-size: var(--size-text-secondary);
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm footer {
|
||||
position: absolute;
|
||||
color: var(--black);
|
||||
font-size: 12px;
|
||||
bottom: 0;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
height: 50px;
|
||||
padding: 15px 0;
|
||||
border-top: 1px solid #c1c1c1;
|
||||
background: transparent;
|
||||
|
||||
span + span {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
/*--------- TOP MENU ----------*/
|
||||
.webbpm .logo {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
float: left;
|
||||
|
||||
a {
|
||||
width: 200px;
|
||||
height: 67px;
|
||||
position: absolute;
|
||||
background: url("../img/logo.png") no-repeat 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm .header {
|
||||
position: absolute;
|
||||
color: var(--white);
|
||||
font-family: Corbel;
|
||||
font-size: var(--size-text-secondary);
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 67px;
|
||||
min-height: 67px;
|
||||
line-height: normal;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: #b9c0ca;
|
||||
z-index: 997;
|
||||
|
||||
.nav .nav-link {
|
||||
color: var(--white);
|
||||
float: none;
|
||||
display: block;
|
||||
line-height: 60px;
|
||||
padding: 0 15px 0 60px;
|
||||
text-shadow: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav .nav-link:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.webbpm .dropdown-menu {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.webbpm .nav .nav-item .dropdown-menu:after {
|
||||
border-bottom: 6px solid #eee;
|
||||
}
|
||||
|
||||
.webbpm .inner {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.webbpm .navbar .nav > * {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.webbpm .dropdown-menu > div > button {
|
||||
color: #d1dbe5;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------- end - TOP MENU ----------*/
|
||||
.webbpm .user-department,
|
||||
.webbpm .user-info {
|
||||
color: #5a6473;
|
||||
}
|
||||
|
||||
.webbpm .user-info > * {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.webbpm [log-out] {
|
||||
max-width: 40%;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.webbpm .content {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.webbpm .inner {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
overflow-y : scroll;
|
||||
}
|
||||
|
||||
/*--------------task-list------------------*/
|
||||
.task-list {
|
||||
font-size: 0;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.task-list > div {
|
||||
display: block;
|
||||
float: left;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
font-size: var(--size-text-secondary);
|
||||
}
|
||||
|
||||
.task-list-tree-panel {
|
||||
width: 20%;
|
||||
background: #e9edf2;
|
||||
border-right: 1px solid #b9c1ca;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter {
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li {
|
||||
position: relative;
|
||||
padding: 8px 10px;
|
||||
margin: 2px 10px;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border: 1px solid rgb(206, 212, 219);
|
||||
border-radius: 4px;
|
||||
background-color: var(--white);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li label:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li label {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li label input[type="radio"] {
|
||||
float: left;
|
||||
margin-top: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li label span {
|
||||
float: right;
|
||||
background-color: #bbb;
|
||||
padding: 0px 4px;
|
||||
border-radius: 3px;
|
||||
min-width: 25px;
|
||||
text-align: center;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li.ontime label span {
|
||||
background-color: #a0c367;
|
||||
}
|
||||
|
||||
.task-list-tree-panel .task-list-filter li.overdue label span {
|
||||
background-color: #fc2d2d;
|
||||
}
|
||||
|
||||
.task-list-header {
|
||||
border-bottom: 1px solid #b9c1ca;
|
||||
background-color: #ccd6e0;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
color: #565968;
|
||||
font-size: var(--size-text-secondary);
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.structure-box {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.task-list-workplace {
|
||||
width: 80%;
|
||||
padding: 15px 15px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
/*--------------task-list end--------------*/
|
||||
|
||||
/*---------------table-list----------------*/
|
||||
.task-tbl {
|
||||
background: var(--white);
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.task-tbl .td, .task-tbl .th {
|
||||
border: 1px solid #b9c1ca;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.task-tbl .th {
|
||||
color: #565968;
|
||||
}
|
||||
|
||||
.task-tbl .td {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.task-tbl .thead {
|
||||
background: #ccd6e0;
|
||||
}
|
||||
|
||||
.task-tbl > .tr:hover {
|
||||
cursor: pointer;
|
||||
background: #e9edf2;
|
||||
}
|
||||
|
||||
.task-tbl .tr.task-ontime > .td.task,
|
||||
.task-tbl .tr.task-overdue > .td.task {
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.task-tbl .tr.task-ontime > .td.task::before,
|
||||
.task-tbl .tr.task-overdue > .td.task::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #a0c367;
|
||||
border-radius: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.task-tbl .tr.task-overdue > .td.task::before {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
/*----------------table-list end----------------*/
|
||||
|
||||
/*--------------Окно сообщения об ошибке--------------*/
|
||||
.webbpm #toast-container {
|
||||
font-size: 12px;
|
||||
bottom: auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 95%;
|
||||
}
|
||||
|
||||
.webbpm #toast-container .toast:hover {
|
||||
box-shadow: 0 0 12px #999999;
|
||||
}
|
||||
|
||||
.webbpm #toast-container .ngx-toastr {
|
||||
min-width: 540px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.webbpm #toast-container .toast-error {
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
.webbpm #toast-container .toast-close-button {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.webbpm .toast-message > div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.webbpm .toast-message > .active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.webbpm .toast-message > .active::after {
|
||||
display: block;
|
||||
content: "";
|
||||
float: none;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.webbpm .toast-message > .active a {
|
||||
float: right;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.webbpm .toast-message > .toast-msg-close.active ~ .toast-msg-text {
|
||||
display: block;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.webbpm .toast-message a:not([href]):not([tabindex]) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
/*------------Окно сообщения об ошибке end------------*/
|
||||
|
||||
/*----------------- Ошибка 404 -------------------*/
|
||||
.webbpm .container .task-not-found-page {
|
||||
position: relative;
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.webbpm .container .task-not-found-container {
|
||||
display: table-cell;
|
||||
color: var(--black);
|
||||
font-size: 1.8em;
|
||||
background-color: #c9d4e0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.webbpm .container .task-not-found-container > div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.webbpm .container .task-not-found-container > div:first-child {
|
||||
color: var(--white);
|
||||
font-size: 7.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.webbpm .container .task-not-found-container > div:last-child {
|
||||
text-align: left;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.webbpm .container .task-not-found-container h2 {
|
||||
font-size: 2em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
.webbpm .container .task-not-found-container a {
|
||||
cursor: pointer;
|
||||
}
|
||||
/*--------------- end Ошибка 404 -----------------*/
|
||||
|
||||
/*-------------- MOBILE --------------*/
|
||||
.webbpm.mobile .task-list-tree-panel,
|
||||
.webbpm.mobile footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.webbpm.mobile .task-list-workplace {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.webbpm.mobile .container {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.webbpm.mobile form {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.webbpm.mobile .form-signin {
|
||||
width: auto;
|
||||
padding: 20px;
|
||||
margin: 40px 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.webbpm.mobile .form-signin h1,
|
||||
.webbpm.mobile .form-signin h2 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.webbpm.mobile .form-signin label {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.webbpm.mobile .form-signin input[type="text"],
|
||||
.webbpm.mobile .form-signin input[type="password"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.webbpm.mobile .form-signin .login-btn-box {
|
||||
width: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
/*-------------- end MOBILE --------------*/
|
||||
|
||||
|
||||
/*-------------- НОВЫЙ ДИЗАЙН --------------*/
|
||||
/*------------------ Фильтры ------------------*/
|
||||
.webbpm .task-list {
|
||||
height: auto;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.webbpm .task-list > .task-list-tree-panel {
|
||||
background: var(--white);
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.webbpm .task-list > .task-list-tree-panel,
|
||||
.webbpm .task-list > .task-list-workplace {
|
||||
font-size: var(--size-text-secondary);
|
||||
float: none;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.webbpm .task-list-tree-panel .task-list-filter {
|
||||
font-family: Corbel;
|
||||
margin: 0;
|
||||
box-shadow: 0px 4px 10px -5px rgba(40, 40, 40, 0.3);
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding: 12px 0px;
|
||||
margin: 0;
|
||||
|
||||
&:first-of-type {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
width: 197px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 1.3em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
label div {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&.ontime label div {
|
||||
background-color: #2da6a1;
|
||||
}
|
||||
|
||||
&.overdue label div {
|
||||
background-color: #9c5d7a;
|
||||
}
|
||||
|
||||
label input[type="radio"] {
|
||||
float: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
label span {
|
||||
float: none;
|
||||
color: var(--black);
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
min-width: auto;
|
||||
padding: 5px 15px;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
label input[type="radio"].ng-valid-parse ~ span {
|
||||
background-color: #eaedf2;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
/*-------------- end НОВЫЙ ДИЗАЙН --------------*/
|
||||
|
||||
.webbpm .dialog-stack-trace {
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
max-height: 300px;
|
||||
}
|
||||
.webbpm .dialog-show-button {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
.webbpm .dialog-error-number {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.webbpm .dialog-error-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/*-- login --*/
|
||||
.webbpm :is(.form-signin, .form-signup, .confirm) {
|
||||
color: #333;
|
||||
width: 580px;
|
||||
padding: 80px 100px;
|
||||
margin: 20px auto;
|
||||
border: 1px solid var(--bg-light);
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
.webbpm .form-signin.esia {
|
||||
width: 450px;
|
||||
text-align: center;
|
||||
padding: 45px 55px 35px 55px;
|
||||
}
|
||||
|
||||
.webbpm .form-signin h1,
|
||||
.webbpm .form-signin h2,
|
||||
.webbpm .form-signup h2,
|
||||
.webbpm .confirm h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.webbpm .form-signin.esia :is(h1, h2) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.webbpm .form-signin label,
|
||||
.webbpm .form-signup label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.webbpm .form-signin input {
|
||||
width: 240px;
|
||||
font-size: var(--size-text-secondary);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.webbpm .form-signin.esia input {
|
||||
width: 160px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.webbpm :is(.form-signin, .form-signup) .row {
|
||||
display: flex;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.webbpm .registration-link,
|
||||
.webbpm .login-link {
|
||||
margin-right: 20px;
|
||||
font-size: var(--size-text-secondary);
|
||||
}
|
||||
|
||||
.webbpm .form-signin .row.registration {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.webbpm .form-signin .login-btn-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.webbpm .form-signin .row.registration > * + *,
|
||||
.webbpm .form-signin .login-btn-box .password,
|
||||
.webbpm .form-signin .login-btn-box .btn + .btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.webbpm .input-group > .input-group-append > .input-group-text {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.webbpm .form-signin .register-btn-box,
|
||||
.webbpm .form-signup .register-btn-box,
|
||||
.webbpm .form-signup .reset-password-btn-box {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.webbpm .form-signup .row international-phone-number .flagInput .btn {
|
||||
border-left: 1px solid #c6cdd3;
|
||||
}
|
||||
.webbpm .form-signup .row international-phone-number .flagInput ~ input {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.webbpm .form-signin .has-error .help-block {
|
||||
padding-left: 125px;
|
||||
font-size: var(--size-text-secondary);
|
||||
}
|
||||
|
||||
.webbpm .form-signup .has-account a {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/*------------------ Формы регистрации и подтверждения ------------------*/
|
||||
.form-signup .has-account {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.form-signup .has-account a span,
|
||||
.confirm a span {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.form-signup .dropbtn.btn {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-signup .input-group-text {
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.form-signup input.ng-invalid.ng-touched,
|
||||
.form-signup input.ng-invalid.ng-dirty {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.form-signup .msg-alert {
|
||||
color: red;
|
||||
font-size: 11px;
|
||||
padding: 3px 0 0;
|
||||
}
|
||||
|
||||
.form-signup .consent {
|
||||
color: #929292;
|
||||
font-size: 13px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
/*------------------ End - Формы регистрации и подтверждения ------------------*/
|
||||
|
||||
/*------------------ Сообщения об ошибке ------------------*/
|
||||
.webbpm .error_message {
|
||||
width: 650px;
|
||||
margin: 0 auto;
|
||||
margin-top: 10%;
|
||||
}
|
||||
|
||||
.webbpm .error_title {
|
||||
position: relative;
|
||||
color: #9c5d7a;
|
||||
font-size: 5.5em;
|
||||
font-weight: bold;
|
||||
margin-left: 100px;
|
||||
line-height: 1;
|
||||
}
|
||||
.webbpm .error_title::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
left: -100px;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
border-radius: 40px;
|
||||
background-color: #9c5d7a;
|
||||
background-image: url("../img/access_denied.png");
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
|
||||
.webbpm .error_title_long {
|
||||
margin-bottom: 20px;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
.webbpm .error_body {
|
||||
font-size: 2em;
|
||||
line-height: 1.2;
|
||||
margin: 5px 0 0 100px;
|
||||
}
|
||||
/*---------------- end Сообщения об ошибке ---------------*/
|
||||
/*-------------- Поле телефона ------------ */
|
||||
.flag {
|
||||
background-image: url('./../img/country-flags.jpg') !important;
|
||||
}
|
||||
/*-------------- end Поле телефона ------------ */
|
||||
|
||||
10
frontend/src/resources/css/style.css
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
@import "../../../node_modules/angular-calendar/css/angular-calendar.css";
|
||||
@import "../../../node_modules/bootstrap/dist/css/bootstrap-grid.css";
|
||||
@import "../../../node_modules/bootstrap/dist/css/bootstrap-reboot.css";
|
||||
@import "../../../node_modules/bootstrap/dist/css/bootstrap.css";
|
||||
@import "../../../node_modules/bootstrap-icons/font/bootstrap-icons.css";
|
||||
@import "../../../node_modules/font-awesome/css/font-awesome.css";
|
||||
@import "../../../node_modules/@webbpm/base-package/css/style.css";
|
||||
@import "structure.css";
|
||||
@import "inbox-app.css";
|
||||
@import "components-app.css";
|
||||
BIN
frontend/src/resources/fonts/Segoe.ttf
Normal file
BIN
frontend/src/resources/fonts/SegoeB.ttf
Normal file
BIN
frontend/src/resources/fonts/SegoeBL.ttf
Normal file
BIN
frontend/src/resources/fonts/SegoeSB.ttf
Normal file
BIN
frontend/src/resources/fonts/SegoeSL.ttf
Normal file
BIN
frontend/src/resources/img/access_denied.png
Normal file
|
After Width: | Height: | Size: 855 B |
BIN
frontend/src/resources/img/admin.png
Normal file
|
After Width: | Height: | Size: 811 B |
BIN
frontend/src/resources/img/country-flags.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
frontend/src/resources/img/create.png
Normal file
|
After Width: | Height: | Size: 673 B |
BIN
frontend/src/resources/img/logo-full.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
frontend/src/resources/img/logo.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
frontend/src/resources/img/progress.gif
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
frontend/src/resources/img/project.png
Normal file
|
After Width: | Height: | Size: 712 B |
BIN
frontend/src/resources/img/tasks.png
Normal file
|
After Width: | Height: | Size: 351 B |
|
|
@ -0,0 +1,4 @@
|
|||
<div class="error_message">
|
||||
<div class="error_title">403</div>
|
||||
<div class="error_body">Доступ запрещен</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<div *ngIf="(currentSession | async)?.authorities.includes('BPMN.ADMIN.PROCESS_INSTANCE.LIST') ||
|
||||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.USER.LIST') ||
|
||||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.GROUP.LIST') ||
|
||||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ROLE.LIST') ||
|
||||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ORG_UNIT.LIST') ||
|
||||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.AUTHORITY.LIST')" ngbDropdown class="nav-item" [placement]="placement">
|
||||
<button class="nav-link bi bi-gear-fill" id="adminDropdownMenu" ngbDropdownToggle title="Администрирование"></button>
|
||||
<div ngbDropdownMenu aria-labelledby="adminDropdownMenu">
|
||||
<button *ngIf="(currentSession | async)?.authorities.includes('BPMN.ADMIN.PROCESS_INSTANCE.LIST')"
|
||||
routerLink="/process/instance" ngbDropdownItem>
|
||||
Экземпляры процессов
|
||||
</button>
|
||||
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.USER.LIST')"
|
||||
routerLink="/user-management/users" ngbDropdownItem>
|
||||
Пользователи
|
||||
</button>
|
||||
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.GROUP.LIST')"
|
||||
routerLink="/user-management/groups" ngbDropdownItem>
|
||||
Группы
|
||||
</button>
|
||||
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ROLE.LIST')"
|
||||
routerLink="/user-management/roles" ngbDropdownItem>
|
||||
Роли
|
||||
</button>
|
||||
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ORG_UNIT.LIST')"
|
||||
routerLink="/user-management/org-units" ngbDropdownItem>
|
||||
Организации
|
||||
</button>
|
||||
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.AUTHORITY.LIST')"
|
||||
routerLink="/user-management/authorities" ngbDropdownItem>
|
||||
Безопасность действий
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<footer>
|
||||
<div id="webbpm-footer">
|
||||
<div class="fl-left"><span>Web-BPM</span></div>
|
||||
<div class="fl-right">
|
||||
<span><a href="mailto:info@micord.ru">Поддержка</a></span>
|
||||
<span><a href="http://www.micord.ru" target="_blank">Компания "Микорд"</a></span>
|
||||
<span><application-version></application-version></span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<nav class="header" *ngIf="currentSession | async as session" id="webbpm-header">
|
||||
<div class="header-logo">
|
||||
<div class="logo"><a routerLink="/"></a></div>
|
||||
</div>
|
||||
<div class="header-menu">
|
||||
<process *ngIf="session.authorities.includes('BPMN.USER.START_PROCESS')"></process>
|
||||
<div *ngIf="session.authorities.includes('BPMN.USER.TASK_LIST')">
|
||||
<button class="nav-link bi bi-file-text-fill" (click)="openTaskList()" title="Задачи"></button>
|
||||
</div>
|
||||
<admin-menu [placement]="'bottom'"></admin-menu>
|
||||
<div ngbDropdown class="logout" log-out></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<span id="version-footer">Версия: {{applicationVersion}}</span>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<div class="confirm">
|
||||
<div class="form-logo">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div>
|
||||
<h2>Подтверждение почты</h2>
|
||||
|
||||
<div *ngIf="verificationStatus.toString() === 'VERIFYING'">
|
||||
Подтверждение...
|
||||
</div>
|
||||
<div *ngIf="verificationStatus.toString() === 'VERIFIED'">
|
||||
<div class="alert alert-success">
|
||||
Адрес электронной почты успешно подтвержден
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="verificationStatus.toString() === 'FAILED'">
|
||||
<div class="alert alert-danger">{{ errorMessage }}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="(currentSession | async) == null">
|
||||
<a href="#/login"><span class="fa fa-lock"></span>Войти</a><br/>
|
||||
<a href="#/registration">Зарегистрироваться</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<button class="nav-link bi bi-person-fill" ngbDropdownToggle title="Пользователь"></button>
|
||||
<div ngbDropdownMenu *ngIf="currentSession | async as session">
|
||||
<div class="user-info">
|
||||
<div class="user-fio">{{session.fullUserName}}</div>
|
||||
<div class="user-login">{{session.username}}</div>
|
||||
<div class="user-department">{{getOrgUnitName()}}</div>
|
||||
</div>
|
||||
<button ngbDropdownItem *ngIf="isLogoutButtonVisible()" (click)="logout()">Выход</button>
|
||||
</div>
|
||||
51
frontend/src/resources/template/app/component/login.html
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<div class="form-signin">
|
||||
<form #formComponent="ngForm">
|
||||
<div class="alert alert-success" [hidden]="!confirmationSent">На ваш почтовый адрес было
|
||||
отправлено письмо. Подтвердите почту, чтобы войти в систему
|
||||
</div>
|
||||
<div class="alert alert-danger" [hidden]="!errorMessage">{{errorMessage}}</div>
|
||||
|
||||
<h2>Вход</h2>
|
||||
<div class="row registration">
|
||||
<span>Еще нет аккаунта?</span><a href="#/registration">Зарегистрироваться</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label>Пользователь</label>
|
||||
<div class="input-group">
|
||||
<input type="text" name="username" class="form-control" placeholder="Пользователь" required autofocus
|
||||
[(ngModel)]="username" maxlength="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
[type]="passwordType ? 'text' : 'password'"
|
||||
name="password"
|
||||
class="form-control field-password-view"
|
||||
placeholder="" required
|
||||
[(ngModel)]="password"
|
||||
maxlength="100"
|
||||
>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">
|
||||
<i
|
||||
(click)="togglePasswordType()"
|
||||
class="fa"
|
||||
[ngClass]="{
|
||||
'fa-eye': passwordType,
|
||||
'fa-eye-slash': !passwordType
|
||||
}"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-btn-box">
|
||||
<!--<esia-login-button></esia-login-button>-->
|
||||
<button type="submit" class="btn btn-primary" (click)="formComponent.form.valid && login()">Войти</button>
|
||||
<div class="password"><a href="#/reset-password">Забыли пароль?</a></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
103
frontend/src/resources/template/app/component/new_password.html
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<div class="form-signup">
|
||||
<div class="form-logo">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="form-new-password">
|
||||
<form #formComponent="ngForm">
|
||||
<div [hidden]="!errorMessage" class="alert alert-danger">{{ errorMessage }}</div>
|
||||
<p class="has-account">Вспомнили пароль?
|
||||
<a href="#/login"><span class="fa fa-lock"></span>Войти</a></p>
|
||||
|
||||
<p class="has-account">Задайте новый пароль</p>
|
||||
|
||||
<div class="row">
|
||||
<label>Пароль</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
#passwordInput="ngModel"
|
||||
[(ngModel)]="password"
|
||||
[type]="passwordType ? 'text' : 'password'"
|
||||
class="form-control"
|
||||
maxlength="32"
|
||||
minlength="6"
|
||||
name="password"
|
||||
pattern="^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$"
|
||||
required
|
||||
(change)="validPasswords()"
|
||||
>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">
|
||||
<i
|
||||
(click)="togglePasswordType()"
|
||||
class="fa"
|
||||
[ngClass]="{
|
||||
'fa-eye': passwordType,
|
||||
'fa-eye-slash': !passwordType
|
||||
}"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="passwordInput.invalid && (passwordInput.dirty || passwordInput.touched)">
|
||||
<div *ngIf="passwordInput.errors.required" class="msg-alert">Поле обязательно
|
||||
</div>
|
||||
<div *ngIf="passwordInput.errors.minlength" class="msg-alert">Пароль должен
|
||||
содержать как минимум 6 символов
|
||||
</div>
|
||||
<div *ngIf="passwordInput.errors.pattern" class="msg-alert">Пароль должен
|
||||
содержать заглавные и прописные буквы и как минимум 1 цифру
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Подтверждение пароля</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
#confirmPasswordInput="ngModel"
|
||||
[(ngModel)]="confirmPassword"
|
||||
[type]="confirmPasswordType ? 'text' : 'password'"
|
||||
class="form-control"
|
||||
maxlength="32"
|
||||
minlength="6"
|
||||
name="confirmPassword"
|
||||
pattern="^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$"
|
||||
required
|
||||
(change)="validPasswords()"
|
||||
>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">
|
||||
<i
|
||||
(click)="toggleConfirmPasswordType()"
|
||||
class="fa"
|
||||
[ngClass]="{
|
||||
'fa-eye': confirmPasswordType,
|
||||
'fa-eye-slash': !confirmPasswordType
|
||||
}"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="confirmPasswordInput.invalid && (confirmPasswordInput.dirty || confirmPasswordInput.touched)">
|
||||
<div *ngIf="confirmPasswordInput.errors.required" class="msg-alert">Поле обязательно
|
||||
</div>
|
||||
<div *ngIf="confirmPasswordInput.errors.minlength" class="msg-alert">Пароль должен
|
||||
содержать как минимум 6 символов
|
||||
</div>
|
||||
<div *ngIf="confirmPasswordInput.errors.pattern" class="msg-alert">Пароль должен
|
||||
содержать заглавные и прописные буквы и как минимум 1 цифру
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="reset-password-btn-box">
|
||||
<button
|
||||
(click)="formComponent.form.valid && validPasswords() && changePassword()"
|
||||
[disabled]="!formComponent.form.valid && !validPasswords()"
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
Изменить пароль
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<div ngbDropdown class="nav-item">
|
||||
<button class="nav-link bi bi-clipboard-plus-fill" id="startProcessDropdownMenu" ngbDropdownToggle title="Создать"></button>
|
||||
<div ngbDropdownMenu aria-labelledby="startProcessDropdownMenu">
|
||||
<div class="dropdown-menu-inner">
|
||||
<div *ngFor="let process of processList">
|
||||
<button (click)="startProcess(process.processDefId)" ngbDropdownItem>
|
||||
{{ process.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</div><!-- TODO: move to directive or something else -->
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<div class="modal-body">
|
||||
<div class="progress"></div>
|
||||
</div>
|
||||
121
frontend/src/resources/template/app/component/register.html
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<div class="form-signup">
|
||||
<div class="form-logo">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="form-register">
|
||||
<form #formComponent="ngForm">
|
||||
<div [hidden]="!errorMessage" class="alert alert-danger">{{ errorMessage }}</div>
|
||||
<h2>Регистрация</h2>
|
||||
<p class="has-account">Уже зарегистрированы?
|
||||
<a href="#/login"><span class="fa fa-lock"></span>Войти</a></p>
|
||||
|
||||
<div class="row">
|
||||
<label>Имя</label>
|
||||
<input
|
||||
#name="ngModel"
|
||||
[(ngModel)]="username"
|
||||
class="form-control"
|
||||
maxlength="100"
|
||||
name="username"
|
||||
required
|
||||
type="text"
|
||||
>
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)">
|
||||
<div *ngIf="name.errors.required" class="msg-alert">Поле обязательно</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Адрес эл. почты</label>
|
||||
<input
|
||||
#emailInput="ngModel"
|
||||
[(ngModel)]="email"
|
||||
class="form-control"
|
||||
email
|
||||
maxlength="100"
|
||||
name="email"
|
||||
required
|
||||
type="email"
|
||||
>
|
||||
<div *ngIf="emailInput.invalid && (emailInput.dirty || emailInput.touched)">
|
||||
<div *ngIf="emailInput.errors.required" class="msg-alert">Поле обязательно</div>
|
||||
<div *ngIf="emailInput.errors.email" class="msg-alert">Неверный формат адреса
|
||||
эл. почты
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Номер телефона</label>
|
||||
<international-phone-number
|
||||
#phoneInput="ngModel"
|
||||
[(ngModel)]="phoneNumber"
|
||||
[defaultCountry]="'ru'"
|
||||
[pattern]="'^\\+(?!7 ?\\d{11})[0-9 ]+$'"
|
||||
maxlength="20"
|
||||
minlength="8"
|
||||
name="phoneNumber"
|
||||
placeholder="+79991112233"
|
||||
(focusout)="phoneInputFocusOut()"
|
||||
required
|
||||
></international-phone-number>
|
||||
<div *ngIf="phoneInput.invalid && (phoneInput.dirty || phoneIsTouched)">
|
||||
<div *ngIf="phone.selectedCountry">
|
||||
<div *ngIf="phoneHasOnlyDialCode()" class="msg-alert">Поле обязательно</div>
|
||||
<div *ngIf="!phoneHasOnlyDialCode()" class="msg-alert">Введите корректный номер</div>
|
||||
</div>
|
||||
<div *ngIf="!phone.selectedCountry">
|
||||
<div *ngIf="phoneInput.errors.required" class="msg-alert">Поле обязательно</div>
|
||||
<div *ngIf="!phoneInput.errors.required" class="msg-alert">Введите код страны</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
#passwordInput="ngModel"
|
||||
[(ngModel)]="password"
|
||||
[type]="fieldType ? 'text' : 'password'"
|
||||
class="form-control"
|
||||
maxlength="32"
|
||||
minlength="8"
|
||||
name="password"
|
||||
[pattern]="passwordPattern"
|
||||
required
|
||||
>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">
|
||||
<i
|
||||
(click)="toggleFieldType()"
|
||||
class="fa"
|
||||
[ngClass]="{
|
||||
'fa-eye': fieldType,
|
||||
'fa-eye-slash': !fieldType
|
||||
}"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="passwordInput.invalid && (passwordInput.dirty || passwordInput.touched)">
|
||||
<div *ngIf="passwordInput.errors.required" class="msg-alert">Поле обязательно
|
||||
</div>
|
||||
<div *ngIf="passwordInput.errors.minlength" class="msg-alert">Пароль должен
|
||||
содержать как минимум 8 символов
|
||||
</div>
|
||||
<div *ngIf="passwordInput.errors.pattern" class="msg-alert" [innerText]="passwordPatternErrorMessage">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="register-btn-box">
|
||||
<button
|
||||
(click)="formComponent.form.valid && register()"
|
||||
[disabled]="!formComponent.form.valid"
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
Зарегистрироваться
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="consent" [innerHTML]="consent" class="consent"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<div class="form-signup">
|
||||
<div class="form-logo">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="form-reset-password">
|
||||
<form #formComponent="ngForm">
|
||||
<div [hidden]="!errorMessage" class="alert alert-danger">{{ errorMessage }}</div>
|
||||
<p class="has-account">Вспомнили пароль?
|
||||
<a href="#/login"><span class="fa fa-lock"></span>Войти</a></p>
|
||||
|
||||
<p class="has-account">Укажите адрес эл. почты, который был указан при регистрации,
|
||||
на него пришлем временный пароль. Пароль сможете поменять в личном кабинете.
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<label>Адрес эл. почты</label>
|
||||
<input
|
||||
#emailInput="ngModel"
|
||||
[(ngModel)]="email"
|
||||
class="form-control"
|
||||
email
|
||||
maxlength="100"
|
||||
name="email"
|
||||
required
|
||||
type="email"
|
||||
>
|
||||
<div *ngIf="emailInput.invalid && (emailInput.dirty || emailInput.touched)">
|
||||
<div *ngIf="emailInput.errors.required" class="msg-alert">Поле обязательно</div>
|
||||
<div *ngIf="emailInput.errors.email" class="msg-alert">Неверный формат адреса эл. почты
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="reset-password-btn-box">
|
||||
<button
|
||||
(click)="formComponent.form.valid && resetPassword()"
|
||||
[disabled]="!formComponent.form.valid"
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
Восстановить
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
3
frontend/src/resources/template/app/component/task.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<div id="page">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
49
frontend/src/resources/template/app/component/task_list.html
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<div class="inner">
|
||||
<div class="task-list">
|
||||
<div class="task-list-tree-panel">
|
||||
<div class="task-list-filter">
|
||||
<ul>
|
||||
<li>Фильтры</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="task-filter" value="All" [(ngModel)]="showMode" (ngModelChange)="filterVisibleTasks()">
|
||||
<span>Все [{{tasks.length}}]</span>
|
||||
</label></li>
|
||||
<li class="ontime">
|
||||
<label>
|
||||
<input type="radio" name="task-filter" value="OnTime" [(ngModel)]="showMode" (ngModelChange)="filterVisibleTasks()">
|
||||
<span><div></div>Без превышения срока [{{onTimeTasks.length}}]</span>
|
||||
</label></li>
|
||||
<li class="overdue">
|
||||
<label>
|
||||
<input type="radio" name="task-filter" value="Overdue" [(ngModel)]="showMode" (ngModelChange)="filterVisibleTasks()">
|
||||
<span><div></div>С превышением срока [{{overdueTasks.length}}]</span>
|
||||
</label></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-list-workplace">
|
||||
<div class="alert alert-danger" [hidden]="!errorMessage">{{errorMessage}}</div>
|
||||
<div class="table task-tbl">
|
||||
<div class="thead">
|
||||
<div class="tr">
|
||||
<div class="th">Процесс</div>
|
||||
<div class="th">Версия</div>
|
||||
<div class="th">Задача</div>
|
||||
<div class="th">Дата создания</div>
|
||||
<div class="th">Срок</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngFor="let task of visibleTasks"
|
||||
class="tr" [ngClass]="{'task-overdue': isOverdue(task), 'task-ontime': isOnTime(task)}"
|
||||
(click)="startTask(task)">
|
||||
<div class="td">{{ task.processName }}</div>
|
||||
<div class="td">{{ task.processVersion }}</div>
|
||||
<div class="td task">{{ task.name }}</div>
|
||||
<div class="td">{{ task.createdOn | date:'dd.MM.yyyy HH:mm:ss' }}</div>
|
||||
<div class="td">{{ task.expirationTime | date:'dd.MM.yyyy HH:mm:ss' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<div class="task-not-found-page">
|
||||
<div class="task-not-found-container">
|
||||
<div>:(</div>
|
||||
<div><h2>Ошибка</h2>
|
||||
Данная задача не существует.<br/>
|
||||
Перейти к <a (click)="goToTaskList()" tabindex>списку задач</a></div>
|
||||
</div>
|
||||
</div>
|
||||
3
frontend/src/resources/template/preview/preview.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<div id="page">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<router-outlet></router-outlet>
|
||||
6
frontend/src/resources/template/webbpm/home.html
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<div id="home">
|
||||
<div class="inner">
|
||||
<div class="task-list">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1
frontend/src/resources/template/webbpm/page.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<div #pageContent></div>
|
||||
12
frontend/src/resources/template/webbpm/webbpm.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<div class="wrapper">
|
||||
<app-header *ngIf="headerVisible">
|
||||
</app-header>
|
||||
|
||||
<div class="container">
|
||||
<div class="container-inside" id="webbpm-angular-application-container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
<app-footer *ngIf="footerVisible">
|
||||
</app-footer>
|
||||
</div>
|
||||
8
frontend/src/ts/main.aot.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import "../../src/resources/css/style.css";
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
import {enableProdMode} from "@angular/core";
|
||||
import {WebbpmModuleNgFactory} from "./modules/webbpm/webbpm.module.ngfactory";
|
||||
|
||||
window['dev_mode'] = false;
|
||||
enableProdMode();
|
||||
platformBrowser().bootstrapModuleFactory(WebbpmModuleNgFactory);
|
||||
7
frontend/src/ts/main.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
|
||||
import {enableProdMode} from "@angular/core";
|
||||
import {WebbpmModule} from "./modules/webbpm/webbpm.module";
|
||||
|
||||
window['dev_mode'] = true;
|
||||
enableProdMode();
|
||||
platformBrowserDynamic().bootstrapModule(WebbpmModule);
|
||||
72
frontend/src/ts/modules/app/app-routing.module.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import {NgModule} from "@angular/core";
|
||||
import {RouterModule, Routes} from "@angular/router";
|
||||
import {AccessDeniedComponent} from "./component/access-denied.component";
|
||||
import {LoginComponent} from "./component/login.component";
|
||||
import {AuthenticationGuard, ConfirmExitGuard, SignedInGuard, ProcessInstanceRouteResolver} from "@webbpm/base-package";
|
||||
import {RegisterComponent} from "./component/register.component";
|
||||
import {ConfirmUserEmailComponent} from "./component/confirm-user-email.component";
|
||||
import {ResetPasswordComponent} from "./component/reset-password.component";
|
||||
import {NewPasswordComponent} from "./component/new-password.component";
|
||||
import {TaskListComponent} from "./component/task-list.component";
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent,
|
||||
canActivate: [SignedInGuard]
|
||||
},
|
||||
{
|
||||
path: 'access-denied',
|
||||
component: AccessDeniedComponent,
|
||||
canActivate: [AuthenticationGuard, ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'registration',
|
||||
component: RegisterComponent,
|
||||
canActivate: [SignedInGuard]
|
||||
},
|
||||
{
|
||||
path: 'confirm',
|
||||
component: ConfirmUserEmailComponent
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
component: ResetPasswordComponent
|
||||
},
|
||||
{
|
||||
path: 'new-password',
|
||||
component: NewPasswordComponent
|
||||
},
|
||||
{
|
||||
path: 'process',
|
||||
canActivate: [AuthenticationGuard, ConfirmExitGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'instance',
|
||||
loadChildren: 'generated-sources/page-process-instance-list.module#PageprocessinstancelistModule',
|
||||
resolve: {
|
||||
processInstanceId: ProcessInstanceRouteResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'instance/:processInstanceId',
|
||||
loadChildren: 'generated-sources/page-process-instance.module#PageprocessinstanceModule',
|
||||
resolve: {
|
||||
processInstanceId: ProcessInstanceRouteResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tasks',
|
||||
component: TaskListComponent,
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(appRoutes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
|
||||
}
|
||||
80
frontend/src/ts/modules/app/app.module.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import {forwardRef, NgModule} from "@angular/core";
|
||||
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule, registerLocaleData} from "@angular/common";
|
||||
import localeRu from '@angular/common/locales/ru';
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {AgGridModule} from "ag-grid-angular";
|
||||
import {
|
||||
BpmnModule,
|
||||
ComponentsModule,
|
||||
CoreModule,
|
||||
ProgressIndicationService,
|
||||
SecurityModule
|
||||
} from "@webbpm/base-package";
|
||||
import {AdminMenuComponent} from "./component/admin-menu.component";
|
||||
import {AppHeaderComponent} from "./component/app-header.component";
|
||||
import {AppFooterComponent} from "./component/app-footer.component";
|
||||
import {LogOutComponent} from "./component/logout.component";
|
||||
import {LoginComponent} from "./component/login.component";
|
||||
import {AccessDeniedComponent} from "./component/access-denied.component";
|
||||
import {ApplicationVersionComponent} from "./component/application-version.component";
|
||||
import {RouterModule} from "@angular/router";
|
||||
import {RegisterComponent} from "./component/register.component";
|
||||
import {ConfirmUserEmailComponent} from "./component/confirm-user-email.component";
|
||||
import {InternationalPhoneNumberModule} from "ngx-international-phone-number";
|
||||
import {ResetPasswordComponent} from "./component/reset-password.component";
|
||||
import {NewPasswordComponent} from "./component/new-password.component";
|
||||
import {AppProgressIndicationComponent} from "./component/app-progress-indication.component";
|
||||
import {AppProgressIndicationService} from "./service/app-progress-indication.service";
|
||||
import {TaskListComponent} from "./component/task-list.component";
|
||||
import {ProcessListComponent} from "./component/process-list.component";
|
||||
import {TaskComponent} from "./component/task.component";
|
||||
import {TaskNotFoundComponent} from "./component/task-not-found.component";
|
||||
|
||||
registerLocaleData(localeRu);
|
||||
export const DIRECTIVES = [
|
||||
forwardRef(() => AppHeaderComponent),
|
||||
forwardRef(() => AppFooterComponent),
|
||||
forwardRef(() => AdminMenuComponent),
|
||||
forwardRef(() => ApplicationVersionComponent),
|
||||
forwardRef(() => LogOutComponent),
|
||||
forwardRef(() => LoginComponent),
|
||||
forwardRef(() => AccessDeniedComponent),
|
||||
forwardRef(() => RegisterComponent),
|
||||
forwardRef(() => ConfirmUserEmailComponent),
|
||||
forwardRef(() => ResetPasswordComponent),
|
||||
forwardRef(() => NewPasswordComponent),
|
||||
forwardRef(() => AppProgressIndicationComponent),
|
||||
forwardRef(() => TaskListComponent),
|
||||
forwardRef(() => ProcessListComponent),
|
||||
forwardRef(() => TaskComponent),
|
||||
forwardRef(() => TaskNotFoundComponent)
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
CoreModule,
|
||||
NgbModule,
|
||||
BpmnModule,
|
||||
SecurityModule,
|
||||
ComponentsModule,
|
||||
AgGridModule,
|
||||
RouterModule,
|
||||
InternationalPhoneNumberModule
|
||||
],
|
||||
declarations: [
|
||||
DIRECTIVES
|
||||
],
|
||||
exports: [
|
||||
DIRECTIVES
|
||||
],
|
||||
providers: [
|
||||
{ provide: ProgressIndicationService, useClass: AppProgressIndicationService }
|
||||
],
|
||||
bootstrap: [],
|
||||
entryComponents: [AppProgressIndicationComponent, TaskNotFoundComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "access-denied",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/access_denied.html",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AccessDeniedComponent {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import {ChangeDetectionStrategy, Component, Input} from "@angular/core";
|
||||
import {UserService, Session} from "@webbpm/base-package";
|
||||
import {NgbDropdownConfig, Placement} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'admin-menu',
|
||||
templateUrl: '../../../../../src/resources/template/app/component/admin_menu.html',
|
||||
providers: [NgbDropdownConfig],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AdminMenuComponent {
|
||||
|
||||
@Input()
|
||||
public placement: Placement = 'bottom';
|
||||
public currentSession: Observable<Session>;
|
||||
|
||||
constructor(protected userService: UserService, public config: NgbDropdownConfig) {
|
||||
this.config.placement = this.placement;
|
||||
this.currentSession = this.userService.getCurrentSession();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import {Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "app-footer",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/app_footer.html"
|
||||
})
|
||||
export class AppFooterComponent {
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
import {Router} from "@angular/router";
|
||||
import {UserService, Session} from "@webbpm/base-package";
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "app-header",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/app_header.html",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AppHeaderComponent {
|
||||
|
||||
public currentSession: Observable<Session>;
|
||||
|
||||
constructor(protected userService: UserService,
|
||||
protected router: Router) {
|
||||
this.currentSession = this.userService.getCurrentSession();
|
||||
}
|
||||
|
||||
public openTaskList(): void {
|
||||
this.router.navigateByUrl("/process/tasks");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: 'progress-indication-dialog-content',
|
||||
templateUrl: '../../../../../src/resources/template/app/component/progress-indication.html'
|
||||
})
|
||||
export class AppProgressIndicationComponent {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input} from "@angular/core";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "application-version",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/application_version.html",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ApplicationVersionComponent {
|
||||
@Input()
|
||||
public applicationVersion: string;
|
||||
|
||||
constructor(private httpClient: HttpClient, private cd: ChangeDetectorRef) {
|
||||
this.loadAppVersion(); //TODO: check version url
|
||||
}
|
||||
|
||||
private loadAppVersion() {
|
||||
this.httpClient.get("version").toPromise().then((version: any) => {
|
||||
this.applicationVersion = version.number;
|
||||
this.cd.markForCheck();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input} from "@angular/core";
|
||||
import {Session, UserService} from "@webbpm/base-package";
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
enum VerificationStatus {
|
||||
VERIFYING = "VERIFYING",
|
||||
VERIFIED = "VERIFIED",
|
||||
FAILED = "FAILED"
|
||||
}
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "confirm",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: "../../../../../src/resources/template/app/component/confirm-user-email.html"
|
||||
})
|
||||
export class ConfirmUserEmailComponent {
|
||||
public verificationStatus = VerificationStatus.VERIFYING;
|
||||
public currentSession: Observable<Session>;
|
||||
|
||||
@Input()
|
||||
public errorMessage: string;
|
||||
|
||||
constructor(private router: Router, private userService: UserService,
|
||||
private route: ActivatedRoute, private cd: ChangeDetectorRef) {
|
||||
this.currentSession = this.userService.getCurrentSession();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const link: string = this.route.snapshot.queryParamMap.get("link");
|
||||
// remove link from url to prevent http referer leakage
|
||||
this.router.navigate([], { relativeTo: this.route, replaceUrl: true });
|
||||
this.userService.confirm(
|
||||
link,
|
||||
(reason) => {
|
||||
this.verificationStatus = VerificationStatus.FAILED;
|
||||
if (reason.status === 404) {
|
||||
this.errorMessage = 'Ссылка недействительна. Требуется повторная регистрация.';
|
||||
}
|
||||
else {
|
||||
this.errorMessage = 'Произошла ошибка, обратитесь в службу технической поддержки!';
|
||||
}
|
||||
this.cd.markForCheck();
|
||||
})
|
||||
.then(() => {
|
||||
this.verificationStatus = VerificationStatus.VERIFIED;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
}
|
||||
68
frontend/src/ts/modules/app/component/login.component.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import {Component, Input} from "@angular/core";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {UserService, Credentials} from "@webbpm/base-package";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "login",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/login.html"
|
||||
})
|
||||
export class LoginComponent {
|
||||
|
||||
@Input()
|
||||
public username: string;
|
||||
|
||||
@Input()
|
||||
public password: string;
|
||||
public passwordType: boolean;
|
||||
|
||||
@Input()
|
||||
public errorMessage: string;
|
||||
|
||||
@Input()
|
||||
public confirmationSent: boolean;
|
||||
|
||||
constructor(private router: Router, private userService: UserService,
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.confirmationSent = this.route.snapshot.queryParamMap.get('confirmationSent') === 'true';
|
||||
this.router.navigate([], { relativeTo: this.route, replaceUrl: true });
|
||||
}
|
||||
|
||||
public login(): void {
|
||||
let credentials: Credentials = new Credentials();
|
||||
credentials.username = this.username;
|
||||
credentials.password = this.password;
|
||||
|
||||
this.userService.login(credentials, "Password")
|
||||
.then(() => this.router.navigateByUrl("/"),
|
||||
(reason: any) => {
|
||||
switch (reason.status) {
|
||||
case 401: {
|
||||
this.errorMessage = "Неправильный логин или пароль";
|
||||
break;
|
||||
}
|
||||
case 404: {
|
||||
this.errorMessage = "Приложение стартует. Пожалуйста, подождите...";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.errorMessage =
|
||||
"Произошла неизвестная ошибка, обратитесь в службу технической поддержки!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public goToRegister(): void {
|
||||
this.router.navigateByUrl("/register");
|
||||
}
|
||||
|
||||
togglePasswordType(): void {
|
||||
this.passwordType = !this.passwordType;
|
||||
}
|
||||
}
|
||||
37
frontend/src/ts/modules/app/component/logout.component.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import {Component} from "@angular/core";
|
||||
import {UserService, Session, AuthenticationMethodService} from "@webbpm/base-package";
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "[log-out]",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/log_out.html"
|
||||
})
|
||||
export class LogOutComponent {
|
||||
|
||||
public currentSession: Observable<Session>;
|
||||
|
||||
constructor(private userService: UserService, private authenticationMethodService: AuthenticationMethodService) {
|
||||
this.currentSession = userService.getCurrentSession();
|
||||
}
|
||||
|
||||
public logout(): void {
|
||||
this.userService.logout();
|
||||
}
|
||||
|
||||
public getCurrentUserName(): string {
|
||||
return this.userService.getCurrentUserName();
|
||||
}
|
||||
|
||||
public getFullUserName(): string {
|
||||
return this.userService.getFullUserName();
|
||||
}
|
||||
|
||||
public isLogoutButtonVisible(): boolean {
|
||||
return this.authenticationMethodService.isFormAuth();
|
||||
}
|
||||
|
||||
public getOrgUnitName(): string {
|
||||
return this.userService.getOrgUnitName();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {Component, Input} from "@angular/core";
|
||||
import {Session, UserPasswordResetRequestDto, UserService} from "@webbpm/base-package";
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "newPassword",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/new_password.html"
|
||||
})
|
||||
export class NewPasswordComponent {
|
||||
public currentSession: Observable<Session>;
|
||||
|
||||
private token: string;
|
||||
|
||||
@Input()
|
||||
public password: string;
|
||||
public passwordType: boolean;
|
||||
|
||||
@Input()
|
||||
public confirmPassword: string;
|
||||
public confirmPasswordType: boolean;
|
||||
|
||||
@Input()
|
||||
public errorMessage: string;
|
||||
|
||||
constructor(private router: Router, private userService: UserService,
|
||||
private route: ActivatedRoute) {
|
||||
this.currentSession = this.userService.getCurrentSession();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.token = this.route.snapshot.queryParamMap.get("token");
|
||||
|
||||
this.router.navigate([], {relativeTo: this.route, replaceUrl: true});
|
||||
|
||||
if (this.token == undefined || this.token === '') {
|
||||
this.errorMessage = 'Ссылка недействительна. Требуется повторить восстановление пароля.';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public changePassword(): void {
|
||||
let dto: UserPasswordResetRequestDto = new UserPasswordResetRequestDto();
|
||||
dto.password = this.password;
|
||||
dto.passwordConfirm = this.confirmPassword;
|
||||
this.userService.changePassword(dto, this.token)
|
||||
.then(() => this.router.navigateByUrl("/"),
|
||||
() => {
|
||||
this.errorMessage =
|
||||
'Произошла неизвестная ошибка, обратитесь в службу технической поддержки!';
|
||||
});
|
||||
}
|
||||
|
||||
togglePasswordType(): void {
|
||||
this.passwordType = !this.passwordType;
|
||||
}
|
||||
|
||||
toggleConfirmPasswordType(): void {
|
||||
this.confirmPasswordType = !this.confirmPasswordType;
|
||||
}
|
||||
|
||||
validPasswords(): boolean {
|
||||
if (this.password === undefined || this.confirmPassword === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let eq = this.password === this.confirmPassword;
|
||||
if (!eq) {
|
||||
this.errorMessage = 'Введенные пароли не совпадают. Убедитесь, что данные, ' +
|
||||
'введенные в поле "Подтверждение пароля", совпадают с теми, ' +
|
||||
'которые указаны в поле "Пароль".';
|
||||
}
|
||||
else {
|
||||
this.errorMessage = '';
|
||||
}
|
||||
return eq;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input} from "@angular/core";
|
||||
import {Process, TaskService, ProcessDefinitionResource, ProcessService} from "@webbpm/base-package";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'process',
|
||||
templateUrl: '../../../../../src/resources/template/app/component/process_list.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProcessListComponent {
|
||||
|
||||
@Input()
|
||||
public processList: Process[];
|
||||
@Input()
|
||||
public errorMessage: string;
|
||||
|
||||
constructor(private processDefinitionResource: ProcessDefinitionResource,
|
||||
private taskService: TaskService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private processService: ProcessService) {
|
||||
this.processList = [];
|
||||
this.loadProcessList();
|
||||
}
|
||||
|
||||
loadProcessList() {
|
||||
this.processDefinitionResource.list().then(
|
||||
(processList) => {
|
||||
this.processList = processList;
|
||||
this.cd.markForCheck();
|
||||
},
|
||||
() => {
|
||||
this.errorMessage = "Error load process list";
|
||||
this.cd.markForCheck();
|
||||
})
|
||||
}
|
||||
|
||||
startProcess(processDefinitionId: string) {
|
||||
this.processService.start(processDefinitionId, null).then(
|
||||
(processInstanceId: number) => {
|
||||
this.taskService.startAndOpenNextTask(processInstanceId);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
71
frontend/src/ts/modules/app/component/register.component.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import {Component, Input, ViewChild} from "@angular/core";
|
||||
import {AppConfigService, UserDto, UserService} from "@webbpm/base-package";
|
||||
import {Router} from "@angular/router";
|
||||
import {PhoneNumberComponent} from "ngx-international-phone-number";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "register",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/register.html"
|
||||
})
|
||||
export class RegisterComponent {
|
||||
|
||||
public passwordPattern: string;
|
||||
public passwordPatternErrorMessage: string;
|
||||
public errorMessage: string;
|
||||
|
||||
@Input()
|
||||
public username: string;
|
||||
|
||||
@Input()
|
||||
public email: string;
|
||||
@ViewChild(PhoneNumberComponent)
|
||||
public phone: PhoneNumberComponent;
|
||||
public phoneNumber: string;
|
||||
|
||||
public phoneIsTouched: boolean = false;
|
||||
@Input()
|
||||
public password: string;
|
||||
|
||||
public fieldType: boolean;
|
||||
|
||||
@Input()
|
||||
public consent: string;
|
||||
|
||||
constructor(private router: Router, private userService: UserService,private appConfigService: AppConfigService) {
|
||||
this.passwordPattern = appConfigService.getParamValue("password_pattern");
|
||||
this.passwordPatternErrorMessage = appConfigService.getParamValue("password_pattern_error");
|
||||
}
|
||||
|
||||
public register(): void {
|
||||
let user: UserDto = new UserDto();
|
||||
user.username = this.username;
|
||||
user.email = this.email;
|
||||
user.name = this.username;
|
||||
user.phone = this.phone.value;
|
||||
user.password = this.password;
|
||||
|
||||
this.userService.register(user)
|
||||
.then(() => this.router.navigateByUrl("/login?confirmationSent=true"),
|
||||
(reason: any) => {
|
||||
if (reason.status === 409) {
|
||||
this.errorMessage = 'Пользователь с данным почтовым адресом уже существует';
|
||||
}
|
||||
else {
|
||||
this.errorMessage = 'Произошла неизвестная ошибка, обратитесь в службу технической поддержки!';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleFieldType(): void {
|
||||
this.fieldType = !this.fieldType;
|
||||
}
|
||||
|
||||
phoneHasOnlyDialCode(): boolean {
|
||||
return this.phone.phoneNumber.trim() === this.phone.getSelectedCountryDialCode().trim()
|
||||
}
|
||||
|
||||
phoneInputFocusOut(): void {
|
||||
this.phoneIsTouched = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import {Component, Input} from "@angular/core";
|
||||
import {UserService} from "@webbpm/base-package";
|
||||
import {Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "resetPassword",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/reset_password.html"
|
||||
})
|
||||
export class ResetPasswordComponent {
|
||||
|
||||
@Input()
|
||||
public email: string;
|
||||
|
||||
@Input()
|
||||
public errorMessage: string;
|
||||
|
||||
constructor(private router: Router, private userService: UserService) {
|
||||
}
|
||||
|
||||
resetPassword(): void {
|
||||
|
||||
this.userService.resetPassword(this.email)
|
||||
.then(() => this.router.navigateByUrl("/"),
|
||||
(reason: any) => {
|
||||
this.errorMessage =
|
||||
'Произошла неизвестная ошибка, обратитесь в службу технической поддержки!';
|
||||
});
|
||||
}
|
||||
}
|
||||
121
frontend/src/ts/modules/app/component/task-list.component.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import {Component, Input} from "@angular/core";
|
||||
import {Task, TaskService, TaskStatus, TaskReference, TaskResource, ProcessVariable, UserService, RolesService, ProcessService} from "@webbpm/base-package";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "task-list",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/task_list.html"
|
||||
})
|
||||
export class TaskListComponent {
|
||||
@Input()
|
||||
public tasks: Task[];
|
||||
|
||||
@Input()
|
||||
public onTimeTasks: Task[];
|
||||
|
||||
@Input()
|
||||
public overdueTasks: Task[];
|
||||
|
||||
@Input()
|
||||
public visibleTasks: Task[];
|
||||
|
||||
@Input()
|
||||
public showMode: string;
|
||||
|
||||
@Input()
|
||||
public errorMessage: string;
|
||||
|
||||
@Input()
|
||||
public roles: string[];
|
||||
|
||||
constructor(private taskService: TaskService,
|
||||
private $taskResource: TaskResource,
|
||||
private userService: UserService,
|
||||
private rolesService: RolesService,
|
||||
private processService: ProcessService) {
|
||||
this.tasks = [];
|
||||
this.onTimeTasks = [];
|
||||
this.overdueTasks = [];
|
||||
this.visibleTasks = [];
|
||||
this.roles = [];
|
||||
this.showMode = 'All';
|
||||
|
||||
this.rolesService.getRoles().then((roles: string[]) => {
|
||||
this.roles = roles;
|
||||
});
|
||||
|
||||
this.loadTasks();
|
||||
}
|
||||
|
||||
private loadTasks(): void {
|
||||
this.$taskResource.list()
|
||||
.then(
|
||||
(tasks: Task[]) => this.initTasks(tasks),
|
||||
() => this.errorMessage = "Error load tasks"
|
||||
);
|
||||
}
|
||||
|
||||
public startTask(task: Task): Promise<any> {
|
||||
let taskRef: TaskReference = new TaskReference();
|
||||
taskRef.processInstanceId = task.processInstanceId;
|
||||
taskRef.taskId = task.id;
|
||||
|
||||
if (task.status == TaskStatus.InProgress) {
|
||||
return this.taskService.openTask(taskRef);
|
||||
}
|
||||
else if (task.status == TaskStatus.Reserved || task.status == TaskStatus.Ready) {
|
||||
return this.taskService.startAndOpenTask(taskRef);
|
||||
}
|
||||
}
|
||||
|
||||
private initTasks(tasks: Task[]): void {
|
||||
this.tasks = tasks;
|
||||
this.filterOnTimeTasks();
|
||||
this.filterOverdueTasks();
|
||||
this.filterVisibleTasks();
|
||||
}
|
||||
|
||||
public isVisible(task: Task): boolean {
|
||||
return this.showMode == 'All' ||
|
||||
this.showMode == 'OnTime' && !this.isOverdue(task) ||
|
||||
this.showMode == 'Overdue' && this.isOverdue(task);
|
||||
}
|
||||
|
||||
public isOverdue(task: Task): boolean {
|
||||
if (!task.expirationTime) {
|
||||
return false;
|
||||
}
|
||||
return new Date(task.expirationTime).getTime() <= new Date().getTime();
|
||||
}
|
||||
|
||||
public isOnTime(task: Task): boolean {
|
||||
return !this.isOverdue(task);
|
||||
}
|
||||
|
||||
public startProcess(processDefinitionId: string, processVars?: Array<ProcessVariable | any>) {
|
||||
this.processService.start(processDefinitionId, processVars).then(
|
||||
(processInstanceId: number) => {
|
||||
this.taskService.startAndOpenNextTask(processInstanceId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public filterOnTimeTasks() {
|
||||
this.onTimeTasks = this.tasks
|
||||
.filter((task: Task) => this.isOnTime(task));
|
||||
}
|
||||
|
||||
public filterOverdueTasks() {
|
||||
this.overdueTasks = this.tasks
|
||||
.filter((task: Task) => this.isOverdue(task));
|
||||
}
|
||||
|
||||
public filterVisibleTasks() {
|
||||
this.visibleTasks = this.tasks
|
||||
.filter((task: Task) => this.isVisible(task));
|
||||
}
|
||||
|
||||
public hasRole(role: string) {
|
||||
return this.roles.includes(role);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
import {Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'task-not-found',
|
||||
templateUrl: '../../../../../src/resources/template/app/component/task_not_found.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TaskNotFoundComponent {
|
||||
|
||||
constructor(private router: Router) {
|
||||
}
|
||||
|
||||
goToTaskList() {
|
||||
return this.router.navigateByUrl("/process/tasks");
|
||||
}
|
||||
}
|
||||
9
frontend/src/ts/modules/app/component/task.component.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import {Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "[task]",
|
||||
templateUrl: "../../../../../src/resources/template/app/component/task.html"
|
||||
})
|
||||
export class TaskComponent {
|
||||
}
|
||||
298
frontend/src/ts/modules/app/service/app-jivo-chat.service.ts
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
import {Injectable, OnDestroy} from "@angular/core";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {UserService, AppConfigService, Session} from "@webbpm/base-package";
|
||||
import {
|
||||
JivoProfileDto
|
||||
} from "../../../generated/dto/jivoprofile/JivoProfileDto";
|
||||
import {Observable, Subscription} from "rxjs";
|
||||
|
||||
declare function jivo_InitProfile(name, email, phone);
|
||||
|
||||
declare function jivo_ClearHistory();
|
||||
|
||||
@Injectable()
|
||||
export class AppJivoChatWidgetService implements OnDestroy {
|
||||
|
||||
public static LIVE_CHAT_WIDGET_API_URL: string = "jivo_chat_widget_api_url";
|
||||
public static LIVE_CHAT_WIDGET_ENABLE: string = "jivo_chat_widget_enabled";
|
||||
public static LIVE_CHAT_WIDGET_DEFAULT_VALUE: boolean = false;
|
||||
|
||||
private JIVO_CSS: string = `
|
||||
/* hide the original widget - that there were no two labels on the screen*/
|
||||
#jivo_chat_widget{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* the default style - for offline messages if no one is online */
|
||||
#jivo_custom_widget{
|
||||
position: fixed;
|
||||
z-index: 300000;
|
||||
cursor: pointer;
|
||||
|
||||
display: block;
|
||||
right: 60px;
|
||||
bottom: 10px;
|
||||
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-image: url('data:image/png;base64,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');
|
||||
background-size: 60px 60px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#jivo_custom_widget:hover{
|
||||
bottom: 13px;
|
||||
}
|
||||
|
||||
/* if there are operators online - show other label*/
|
||||
#jivo_custom_widget.jivo_online{
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
background-size: 60px 60px;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url('data:image/png;base64,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');
|
||||
}`;
|
||||
|
||||
private JIVO_JS: string = `
|
||||
/* Callback function that is called immediately after JivoChat is loaded */
|
||||
function jivo_onLoadCallback() {
|
||||
window.jivo_cstm_widget = document.createElement('div');
|
||||
jivo_cstm_widget.setAttribute('id', 'jivo_custom_widget');
|
||||
document.body.appendChild(jivo_cstm_widget);
|
||||
|
||||
/* Adds handlers click on the icon - to maximize the window when clicked */
|
||||
jivo_cstm_widget.onclick = function () {
|
||||
jivo_api.open();
|
||||
}
|
||||
|
||||
/* Change the CSS class if there are agents online */
|
||||
if (jivo_config.chat_mode === "online") {
|
||||
jivo_cstm_widget.setAttribute("class", "jivo_online");
|
||||
}
|
||||
|
||||
/* Show the user a shortcut */
|
||||
window.jivo_cstm_widget.style.display = 'block';
|
||||
}
|
||||
|
||||
/*
|
||||
Callback function jivo_onOpen and jivo_onClose called whenever the chat window JivoChat
|
||||
is expanded or collapsed by the user or by the proactive invitations rule.
|
||||
*/
|
||||
function jivo_onOpen() {
|
||||
/* If chat is deployed - hide shortcut */
|
||||
if (jivo_cstm_widget)
|
||||
jivo_cstm_widget.style.display = 'none';
|
||||
}
|
||||
|
||||
function jivo_onClose() {
|
||||
/* If chat is minimized - show label */
|
||||
if (jivo_cstm_widget)
|
||||
jivo_cstm_widget.style.display = 'block';
|
||||
}
|
||||
|
||||
function jivo_ClearHistory() {
|
||||
jivo_api.clearHistory();
|
||||
jivo_api.close();
|
||||
}
|
||||
|
||||
function jivo_InitProfile(name, email, phone) {
|
||||
let chatMode = jivo_api.chatMode();
|
||||
|
||||
let args = {
|
||||
name: name ? name : '',
|
||||
email: email ? email : '',
|
||||
phone: phone ? phone : ''
|
||||
};
|
||||
|
||||
if (chatMode === 'offline') {
|
||||
jivo_api.sendOfflineMessage(args);
|
||||
}
|
||||
else {
|
||||
jivo_api.setContactInfo(args);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
private currentUserName: string;
|
||||
private currentEmail: string;
|
||||
private currentPhone: string;
|
||||
|
||||
private jsLoaded = false;
|
||||
private jivoWidgetApiUrl: string;
|
||||
|
||||
private currentSession: Observable<Session>;
|
||||
private currentSessionSubscription: Subscription;
|
||||
private session: Session;
|
||||
private sessionInitJivoChatWidget: boolean;
|
||||
|
||||
constructor(private httpClient: HttpClient,
|
||||
private appConfigService: AppConfigService,
|
||||
private userService: UserService) {
|
||||
|
||||
this.currentSession = userService.getCurrentSession();
|
||||
this.currentSessionSubscription =
|
||||
this.currentSession.subscribe(val => {
|
||||
|
||||
if (!this.sessionInitJivoChatWidget || !this.session) {
|
||||
this.initJivoChatWidget();
|
||||
this.sessionInitJivoChatWidget = true;
|
||||
}
|
||||
|
||||
if (this.session && !val) {
|
||||
// logout
|
||||
this.clearJivoProfile();
|
||||
this.session = null;
|
||||
this.sessionInitJivoChatWidget = false;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
this.session = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private initJivoChatWidget() {
|
||||
let liveChatWidgetEnabled = this.getValueFromAppConfig(
|
||||
AppJivoChatWidgetService.LIVE_CHAT_WIDGET_ENABLE,
|
||||
AppJivoChatWidgetService.LIVE_CHAT_WIDGET_DEFAULT_VALUE);
|
||||
|
||||
if (liveChatWidgetEnabled) {
|
||||
this.jivoWidgetApiUrl = this.getValueFromAppConfig(
|
||||
AppJivoChatWidgetService.LIVE_CHAT_WIDGET_API_URL,
|
||||
'');
|
||||
|
||||
if (!this.jivoWidgetApiUrl) {
|
||||
throw new Error(
|
||||
`The configuration file does not contain the 'Jivo API url'. Please check that '${AppJivoChatWidgetService.LIVE_CHAT_WIDGET_API_URL}' has data`);
|
||||
}
|
||||
|
||||
let userId = this.userService.getCurrentUserId();
|
||||
if (userId) {
|
||||
this.clearJivoProfile();
|
||||
this.httpClient.get('profile/jivo/' + userId)
|
||||
.toPromise()
|
||||
.then((profile: JivoProfileDto) => {
|
||||
|
||||
this.currentUserName = profile.username;
|
||||
this.currentEmail = profile.email;
|
||||
this.currentPhone = profile.phone;
|
||||
|
||||
let initProfileFunction = () => this.initProfile(this.currentUserName,
|
||||
this.currentEmail,
|
||||
this.currentPhone);
|
||||
this.loadScripts(initProfileFunction);
|
||||
});
|
||||
}
|
||||
else {
|
||||
let initProfileFunction = () => this.initProfile(this.currentUserName, this.currentEmail,
|
||||
this.currentPhone);
|
||||
this.loadScripts(initProfileFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private clearJivoProfile() {
|
||||
this.clearProfileData();
|
||||
try {
|
||||
jivo_ClearHistory();
|
||||
}
|
||||
catch (ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private clearProfileData() {
|
||||
this.currentUserName = '';
|
||||
this.currentEmail = '';
|
||||
this.currentPhone = '';
|
||||
}
|
||||
|
||||
private loadScripts(initProfileFunction): void {
|
||||
let afterLoadMainJs = () => {
|
||||
this.loadScriptJsContent('jivoCustomJs', this.JIVO_JS, initProfileFunction);
|
||||
}
|
||||
this.loadScript('jivoWidget', this.jivoWidgetApiUrl, true, afterLoadMainJs);
|
||||
this.loadStyleContent('jivoCustomCss', this.JIVO_CSS);
|
||||
}
|
||||
|
||||
private initProfile(username: string, email: string, phone: string): void {
|
||||
try {
|
||||
jivo_InitProfile(username, email, phone);
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof ReferenceError) {
|
||||
if (!this.jsLoaded) {
|
||||
this.jsLoaded = true;
|
||||
setTimeout(() => {
|
||||
jivo_InitProfile(username, email, phone);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadScript(elementId, js, async, callback): void {
|
||||
let jivoJsLink = document.getElementById(elementId);
|
||||
if (!jivoJsLink) {
|
||||
let node = document.createElement('script');
|
||||
node.src = js;
|
||||
node.type = 'text/javascript';
|
||||
node.id = elementId;
|
||||
node.async = async;
|
||||
node.charset = 'utf-8';
|
||||
if (callback) {
|
||||
node.onload = function () {
|
||||
callback();
|
||||
};
|
||||
}
|
||||
document.getElementsByTagName('head')[0].appendChild(node);
|
||||
}
|
||||
else {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadScriptJsContent(elementId, js, callback): void {
|
||||
let jivoJs = document.getElementById(elementId);
|
||||
if (!jivoJs) {
|
||||
let node = document.createElement('script');
|
||||
node.innerHTML = js;
|
||||
node.type = 'text/javascript';
|
||||
node.id = elementId;
|
||||
node.charset = 'utf-8';
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
document.getElementsByTagName('head')[0].appendChild(node);
|
||||
}
|
||||
else {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadStyleContent(elementId, styleCss): void {
|
||||
let jivoCss = document.getElementById(elementId);
|
||||
if (!jivoCss) {
|
||||
const style = document.createElement('style');
|
||||
style.id = elementId;
|
||||
style.innerHTML = styleCss;
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
private getValueFromAppConfig(key, defaultValue) {
|
||||
let enabled = this.appConfigService.getParamValue(key);
|
||||
if (enabled === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.currentSessionSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import {Injectable} from "@angular/core";
|
||||
import {AppProgressIndicationComponent} from "../component/app-progress-indication.component";
|
||||
import {NgbModal, NgbModalOptions, NgbModalRef} from "@ng-bootstrap/ng-bootstrap";
|
||||
|
||||
@Injectable()
|
||||
export class AppProgressIndicationService {
|
||||
|
||||
private static readonly EVENT_INTERCEPTOR = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
private counter: number = 0;
|
||||
private focused: any;
|
||||
private ngbModalRef: NgbModalRef;
|
||||
private options: NgbModalOptions = {
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
windowClass: 'modal-center loader'
|
||||
};
|
||||
|
||||
constructor(private ngbModal: NgbModal) {
|
||||
}
|
||||
|
||||
public showProgressBar(): boolean {
|
||||
if (this.counter == 0) {
|
||||
this.disableEvents();
|
||||
this.saveFocus();
|
||||
this.showProgressIndicator();
|
||||
}
|
||||
++this.counter;
|
||||
return this.counter == 1;
|
||||
}
|
||||
|
||||
public hideProgressBar(): boolean {
|
||||
if (this.counter == 0) {
|
||||
return false;
|
||||
}
|
||||
if (this.counter == 1) {
|
||||
this.hideProgressIndicator();
|
||||
this.restoreFocus();
|
||||
this.enableEvents();
|
||||
}
|
||||
--this.counter;
|
||||
return this.counter == 0;
|
||||
}
|
||||
|
||||
public unconditionallyHideProgressBar(): void {
|
||||
this.enableEvents();
|
||||
this.hideProgressIndicator();
|
||||
}
|
||||
|
||||
public restoreProgressBar(): void {
|
||||
|
||||
if (this.counter > 0 && this.ngbModalRef == null) {
|
||||
this.disableEvents();
|
||||
this.showProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
private showProgressIndicator() {
|
||||
this.ngbModalRef = this.ngbModal.open(AppProgressIndicationComponent, this.options);
|
||||
}
|
||||
|
||||
private hideProgressIndicator() {
|
||||
this.ngbModalRef.dismiss('cancel');
|
||||
this.ngbModalRef = null;
|
||||
}
|
||||
|
||||
private saveFocus() {
|
||||
this.focused = $(':focus');
|
||||
}
|
||||
|
||||
private restoreFocus() {
|
||||
if (this.focused) {
|
||||
this.focused.focus();
|
||||
this.focused = null;
|
||||
}
|
||||
}
|
||||
|
||||
private disableEvents() {
|
||||
let body = $('body');
|
||||
body.keydown(AppProgressIndicationService.EVENT_INTERCEPTOR);
|
||||
body.keyup(AppProgressIndicationService.EVENT_INTERCEPTOR);
|
||||
body.contextmenu(AppProgressIndicationService.EVENT_INTERCEPTOR)
|
||||
}
|
||||
|
||||
private enableEvents() {
|
||||
let body = $('body');
|
||||
body.off('keydown', AppProgressIndicationService.EVENT_INTERCEPTOR);
|
||||
body.off('keyup', AppProgressIndicationService.EVENT_INTERCEPTOR);
|
||||
body.off('contextmenu', AppProgressIndicationService.EVENT_INTERCEPTOR);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import {Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "[preview-container]",
|
||||
templateUrl: "../../../../../src/resources/template/preview/preview_container.html"
|
||||
})
|
||||
export class PreviewContainerComponent {
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import {Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "[preview]",
|
||||
templateUrl: "../../../../../src/resources/template/preview/preview.html"
|
||||
})
|
||||
export class PreviewComponent {
|
||||
}
|
||||
20
frontend/src/ts/modules/preview/preview-routing.module.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import {NgModule} from "@angular/core";
|
||||
import {RouterModule, Routes} from "@angular/router";
|
||||
import {PreviewComponent} from "./component/preview.component";
|
||||
import {DYNAMIC_ROUTING} from "../../page.routing";
|
||||
|
||||
const previewRoutes: Routes = [
|
||||
{
|
||||
path: 'preview',
|
||||
component: PreviewComponent,
|
||||
children: DYNAMIC_ROUTING,
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(previewRoutes, {useHash: true})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class PreviewRoutingModule {
|
||||
|
||||
}
|
||||
7
frontend/src/ts/modules/preview/preview.main.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
|
||||
import {enableProdMode} from "@angular/core";
|
||||
import {PreviewModule} from "./preview.module";
|
||||
|
||||
window['dev_mode'] = true;
|
||||
enableProdMode();
|
||||
platformBrowserDynamic().bootstrapModule(PreviewModule);
|
||||
53
frontend/src/ts/modules/preview/preview.module.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import {NgModule, NgZone} from "@angular/core";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {BrowserModule} from "@angular/platform-browser";
|
||||
import {AgGridModule} from "ag-grid-angular";
|
||||
import {PreviewComponent} from "./component/preview.component";
|
||||
import {PreviewRoutingModule} from "./preview-routing.module";
|
||||
import {PreviewContainerComponent} from "./component/preview-container.component";
|
||||
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {ToastNoAnimationModule} from "ngx-toastr";
|
||||
import {AppModule} from "../app/app.module";
|
||||
import {ComponentsModule, CoreModule, SecurityModule} from "@webbpm/base-package";
|
||||
import {TaskParamsProvider} from "@webbpm/base-package";
|
||||
import {HTTP_INTERCEPTORS} from "@angular/common/http";
|
||||
import {HttpPreviewInterceptor} from "./service/http-preview-interceptor.service";
|
||||
|
||||
export const HTTP_INTERCEPTOR_PROVIDERS = [
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: HttpPreviewInterceptor, multi: true }
|
||||
];
|
||||
|
||||
let IMPORTS = [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
NgbModule,
|
||||
ToastNoAnimationModule.forRoot(),
|
||||
AgGridModule,
|
||||
CoreModule,
|
||||
ComponentsModule,
|
||||
AppModule,
|
||||
SecurityModule,
|
||||
PreviewRoutingModule
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: IMPORTS,
|
||||
declarations: [
|
||||
PreviewContainerComponent,
|
||||
PreviewComponent
|
||||
],
|
||||
exports: [],
|
||||
providers: [
|
||||
TaskParamsProvider,
|
||||
HTTP_INTERCEPTOR_PROVIDERS
|
||||
],
|
||||
bootstrap: [
|
||||
PreviewContainerComponent
|
||||
]
|
||||
})
|
||||
export class PreviewModule {
|
||||
|
||||
constructor(zone: NgZone) {
|
||||
window['zoneImpl'] = zone;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import {Injectable} from "@angular/core";
|
||||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
|
||||
import {EMPTY, Observable} from "rxjs";
|
||||
import {catchError} from "rxjs/operators";
|
||||
|
||||
@Injectable()
|
||||
export class HttpPreviewInterceptor implements HttpInterceptor {
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
|
||||
return next.handle(req).pipe(catchError(() => {
|
||||
return EMPTY;
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import {Component} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: "home",
|
||||
templateUrl: './../../../../../src/resources/template/webbpm/home.html'
|
||||
})
|
||||
export class HomeComponent {
|
||||
}
|
||||
36
frontend/src/ts/modules/webbpm/component/webbpm.component.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import {Component} from "@angular/core";
|
||||
import {
|
||||
Event,
|
||||
NavigationCancel,
|
||||
NavigationEnd,
|
||||
NavigationError,
|
||||
NavigationStart,
|
||||
Router
|
||||
} from "@angular/router";
|
||||
import {ProgressIndicationService} from "@webbpm/base-package";
|
||||
import {AppJivoChatWidgetService} from "../../app/service/app-jivo-chat.service";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: '[webbpm]',
|
||||
templateUrl: './../../../../../src/resources/template/webbpm/webbpm.html'
|
||||
})
|
||||
export class WebbpmComponent {
|
||||
public headerVisible: boolean = true;
|
||||
public footerVisible: boolean = true;
|
||||
|
||||
constructor(private router: Router,
|
||||
private progressIndicationService: ProgressIndicationService,
|
||||
private appJivoChatWidgetService: AppJivoChatWidgetService) {
|
||||
router.events.subscribe((event: Event) => {
|
||||
if (event instanceof NavigationStart) {
|
||||
progressIndicationService.showProgressBar();
|
||||
}
|
||||
else if (event instanceof NavigationEnd
|
||||
|| event instanceof NavigationError
|
||||
|| event instanceof NavigationCancel) {
|
||||
progressIndicationService.hideProgressBar();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import {ErrorHandler, Injectable, Injector} from '@angular/core';
|
||||
import {BaseErrorHandler} from "@webbpm/base-package";
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class GlobalErrorHandler extends BaseErrorHandler implements ErrorHandler {
|
||||
|
||||
constructor(injector: Injector) {
|
||||
super(injector);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import {ErrorHandler, Injectable, Injector} from '@angular/core';
|
||||
import {BaseErrorHandler} from "@webbpm/base-package";
|
||||
|
||||
//todo: will be used after angular update in dev mode
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class GlobalErrorHandler extends BaseErrorHandler implements ErrorHandler {
|
||||
|
||||
constructor(injector: Injector) {
|
||||
super(injector);
|
||||
}
|
||||
|
||||
handleError(error) {
|
||||
const chunkFailedMessage = /Loading chunk [\d]+ failed/;
|
||||
|
||||
if (chunkFailedMessage.test(error.message)) {
|
||||
window.location.reload();
|
||||
}
|
||||
else if (!this.isPreviewPage()) {
|
||||
super.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
internalHandleError(error) {
|
||||
if (this.isPreviewPage()) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
super.internalHandleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private isPreviewPage() {
|
||||
return window.location.hash.includes("webbpm-preview");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import {HTTP_INTERCEPTORS} from "@angular/common/http";
|
||||
import {
|
||||
FormDirtyInterceptor,
|
||||
HttpSecurityErrorInterceptor,
|
||||
HttpSecurityInterceptor
|
||||
} from "@webbpm/base-package";
|
||||
|
||||
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
|
||||
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityErrorInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true}
|
||||
];
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import {HTTP_INTERCEPTORS} from "@angular/common/http";
|
||||
import {FormDirtyInterceptor, HttpSecurityInterceptor} from "@webbpm/base-package";
|
||||
import {DevHttpSecurityErrorInterceptor} from "./http-security-error-interceptor.dev";
|
||||
|
||||
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
|
||||
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: DevHttpSecurityErrorInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true}
|
||||
];
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
|
||||
import {HttpSecurityErrorInterceptor, MessagesService, UserService} from "@webbpm/base-package";
|
||||
import {Injectable} from "@angular/core";
|
||||
import {Router} from "@angular/router";
|
||||
import {EMPTY, Observable} from "rxjs";
|
||||
import {catchError} from "rxjs/operators";
|
||||
|
||||
@Injectable()
|
||||
export class DevHttpSecurityErrorInterceptor extends HttpSecurityErrorInterceptor
|
||||
implements HttpInterceptor {
|
||||
private router: Router;
|
||||
|
||||
|
||||
constructor(router: Router, messagesService: MessagesService, userService: UserService) {
|
||||
super(router, messagesService, userService);
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
|
||||
if (window.location.hash.includes("webbpm-preview")) {
|
||||
return next.handle(req).pipe(catchError(() => {
|
||||
return EMPTY;
|
||||
}
|
||||
));
|
||||
}
|
||||
else {
|
||||
return super.intercept(req, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import {RouterModule, Routes} from "@angular/router";
|
||||
import {NgModule} from "@angular/core";
|
||||
import {
|
||||
AuthenticationGuard,
|
||||
ConfirmExitGuard
|
||||
} from "@webbpm/base-package";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'user-management',
|
||||
canActivate: [AuthenticationGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'users',
|
||||
loadChildren: 'generated-sources/page-user-management-users.module#PageusermanagementusersModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'users/new',
|
||||
loadChildren: 'generated-sources/page-user-management-user-create.module#PageusermanagementusercreateModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'users/:id',
|
||||
loadChildren: 'generated-sources/page-user-management-user-edit.module#PageusermanagementusereditModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'org-units',
|
||||
loadChildren: 'generated-sources/page-user-management-org-units.module#PageusermanagementorgunitsModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'org-units/new',
|
||||
loadChildren: 'generated-sources/page-user-management-org-unit.module#PageusermanagementorgunitModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'org-units/:id',
|
||||
loadChildren: 'generated-sources/page-user-management-org-unit.module#PageusermanagementorgunitModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'roles',
|
||||
loadChildren: 'generated-sources/page-user-management-roles.module#PageusermanagementrolesModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'roles/new',
|
||||
loadChildren: 'generated-sources/page-user-management-role.module#PageusermanagementroleModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'roles/:id',
|
||||
loadChildren: 'generated-sources/page-user-management-role.module#PageusermanagementroleModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'groups',
|
||||
loadChildren: 'generated-sources/page-user-management-groups.module#PageusermanagementgroupsModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'groups/new',
|
||||
loadChildren: 'generated-sources/page-user-management-group-create.module#PageusermanagementgroupcreateModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'groups/:id',
|
||||
loadChildren: 'generated-sources/page-user-management-group-edit.module#PageusermanagementgroupeditModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
},
|
||||
{
|
||||
path: 'authorities',
|
||||
loadChildren: 'generated-sources/page-user-management-authorities.module#PageusermanagementauthoritiesModule',
|
||||
canActivate: [ConfirmExitGuard]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class UserManagementRoutingModule {
|
||||
|
||||
}
|
||||
51
frontend/src/ts/modules/webbpm/webbpm-routing.module.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import {NgModule} from "@angular/core";
|
||||
import {RouterModule, Routes} from "@angular/router";
|
||||
import {HomeComponent} from "./component/home.component";
|
||||
import {
|
||||
AuthenticationGuard,
|
||||
HistoryLocationGuard,
|
||||
TaskPageRouteResolver,
|
||||
ConfirmExitGuard
|
||||
} from "@webbpm/base-package";
|
||||
import {DYNAMIC_ROUTING} from "../../page.routing";
|
||||
import {TaskComponent} from "./../app/component/task.component";
|
||||
import {TaskNotFoundComponent} from "./../app/component/task-not-found.component";
|
||||
|
||||
const webbpmRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: HomeComponent,
|
||||
canActivate: [AuthenticationGuard, ConfirmExitGuard],
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'process/:processInstanceId/task/:taskId',
|
||||
component: TaskComponent,
|
||||
children: DYNAMIC_ROUTING,
|
||||
canActivate: [HistoryLocationGuard],
|
||||
resolve: {
|
||||
taskPage: TaskPageRouteResolver
|
||||
},
|
||||
runGuardsAndResolvers: "always"
|
||||
},
|
||||
{
|
||||
path: 'process/task-not-found',
|
||||
component: TaskNotFoundComponent,
|
||||
canActivate: [AuthenticationGuard]
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: '',
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(webbpmRoutes, {
|
||||
useHash: true,
|
||||
onSameUrlNavigation: "reload"
|
||||
})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class WebbpmRoutingModule {
|
||||
|
||||
}
|
||||
62
frontend/src/ts/modules/webbpm/webbpm.module.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import {ErrorHandler, NgModule} from "@angular/core";
|
||||
import {BrowserModule} from "@angular/platform-browser";
|
||||
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {AgGridModule} from "ag-grid-angular";
|
||||
import {WebbpmComponent} from "./component/webbpm.component";
|
||||
import {WebbpmRoutingModule} from "./webbpm-routing.module";
|
||||
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {ToastNoAnimationModule} from "ngx-toastr";
|
||||
import {AppModule} from "../app/app.module";
|
||||
import {HomeComponent} from "./component/home.component";
|
||||
import {
|
||||
BpmnModule,
|
||||
ComponentsModule,
|
||||
CoreModule,
|
||||
SecurityModule,
|
||||
} from "@webbpm/base-package";
|
||||
import {TaskParamsProvider} from "@webbpm/base-package";
|
||||
import {ProcessInstanceParamsProvider} from "@webbpm/base-package";
|
||||
import {AppRoutingModule} from "../app/app-routing.module";
|
||||
import {AppJivoChatWidgetService} from "../app/service/app-jivo-chat.service";
|
||||
import {UserManagementRoutingModule} from "./user-management-routing.module";
|
||||
import {GlobalErrorHandler} from "./handler/global-error.handler.prod";
|
||||
import {DEFAULT_HTTP_INTERCEPTOR_PROVIDERS} from "./interceptor/default-interceptors.prod";
|
||||
|
||||
let IMPORTS = [
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
NgbModule,
|
||||
ToastNoAnimationModule.forRoot(),
|
||||
AgGridModule,
|
||||
AppRoutingModule,
|
||||
UserManagementRoutingModule,
|
||||
BpmnModule,
|
||||
CoreModule,
|
||||
ComponentsModule,
|
||||
SecurityModule,
|
||||
AppModule,
|
||||
WebbpmRoutingModule
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: IMPORTS,
|
||||
declarations: [
|
||||
WebbpmComponent,
|
||||
HomeComponent
|
||||
],
|
||||
exports: [],
|
||||
providers: [
|
||||
TaskParamsProvider,
|
||||
ProcessInstanceParamsProvider,
|
||||
AppJivoChatWidgetService,
|
||||
{provide: ErrorHandler, useClass: GlobalErrorHandler},
|
||||
DEFAULT_HTTP_INTERCEPTOR_PROVIDERS
|
||||
],
|
||||
bootstrap: [
|
||||
WebbpmComponent
|
||||
]
|
||||
})
|
||||
export class WebbpmModule {
|
||||
}
|
||||
3
frontend/src/ts/page.routing.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import {Routes} from "@angular/router";
|
||||
|
||||
declare const DYNAMIC_ROUTING: Routes;
|
||||
59
frontend/src/ts/polyfills.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/** 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/weak-map';
|
||||
import 'core-js/es6/set';
|
||||
import 'core-js/es6/promise';
|
||||
import 'core-js/es7';
|
||||
/** 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 for the Reflect API. */
|
||||
// import 'core-js/es6/reflect';
|
||||
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Required to support Web Animations `@angular/platform-browser/animations`.
|
||||
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
|
||||
**/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
*/
|
||||
|
||||
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
|
||||
/*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*/
|
||||
// (window as any).__Zone_enable_cross_context_check = true;
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone';// Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
38
frontend/src/ts/vendor.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Angular
|
||||
import '@angular/core';
|
||||
import '@angular/common';
|
||||
import '@angular/common/http';
|
||||
import '@angular/forms';
|
||||
import '@angular/platform-browser';
|
||||
import '@angular/router';
|
||||
import '@angular/animations';
|
||||
import '@webbpm/base-package';
|
||||
// RxJS
|
||||
import 'rxjs';
|
||||
import 'rxjs-compat';
|
||||
import 'rxjs/operators';
|
||||
import 'rxjs/internal-compatibility';
|
||||
import 'rxjs/ajax';
|
||||
import 'rxjs/testing';
|
||||
import 'rxjs/webSocket';
|
||||
//jquery
|
||||
import 'jquery';
|
||||
//popper
|
||||
import 'popper.js';
|
||||
//bootstrap
|
||||
import '@ng-bootstrap/ng-bootstrap';
|
||||
import 'bootstrap';
|
||||
//mask
|
||||
import 'inputmask';
|
||||
//grid
|
||||
import 'ag-grid-community';
|
||||
import 'ag-grid-angular';
|
||||
//selectize
|
||||
import 'selectize';
|
||||
//datepicker
|
||||
import 'eonasdan-bootstrap-datetimepicker';
|
||||
import 'moment-timezone';
|
||||
//ngx-toastr
|
||||
import 'ngx-toastr';
|
||||
//ngx-cookie
|
||||
import 'ngx-cookie';
|
||||
93
frontend/systemjs.config.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
(function(global) {
|
||||
System.config({
|
||||
transpiler: 'plugin-babel',
|
||||
paths: {
|
||||
'npm:': 'node_modules/',
|
||||
'generated-sources': 'build_dev/js/generated-sources'
|
||||
},
|
||||
map: {
|
||||
'webbpm': 'build_dev/js',
|
||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||
'@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
|
||||
'@angular/common/locales': 'npm:@angular/common/locales',
|
||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
||||
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
|
||||
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
|
||||
'@angular/platform-browser/animations':'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
||||
'@ng-bootstrap/ng-bootstrap': 'npm:@ng-bootstrap/ng-bootstrap/bundles/ng-bootstrap.umd.js',
|
||||
'bootstrap': 'npm:bootstrap',
|
||||
'@webbpm/base-package': 'npm:@webbpm/base-package/bundles/webbpm-base-package.umd.js',
|
||||
'angular-calendar': 'npm:angular-calendar/bundles/angular-calendar.umd.js',
|
||||
'angular-calendar/date-adapters/date-fns': 'npm:angular-calendar/date-adapters/date-fns/index.js',
|
||||
'angular-draggable-droppable': 'npm:angular-draggable-droppable/bundles/angular-draggable-droppable.umd.js',
|
||||
'angular-resizable-element': 'npm:angular-resizable-element/bundles/angular-resizable-element.umd.js',
|
||||
'calendar-utils': 'npm:calendar-utils/bundles/calendar-utils.umd.js',
|
||||
'calendar-utils/date-adapters/date-fns': 'npm:calendar-utils/date-adapters/date-fns/index.js',
|
||||
'date-fns': 'npm:date-fns',
|
||||
'ngx-cookie': 'npm:ngx-cookie/bundles/ngx-cookie.umd.js',
|
||||
'moment': 'npm:moment',
|
||||
'moment-timezone': 'npm:moment-timezone',
|
||||
'positioning': 'npm:positioning/dist/positioning.js',
|
||||
'rxjs': 'npm:rxjs',
|
||||
'rxjs-compat': 'npm:rxjs-compat',
|
||||
'rxjs/operators': 'npm:rxjs/operators',
|
||||
'rxjs/internal-compatibility': 'npm:rxjs/internal-compatibility',
|
||||
'rxjs/ajax': 'npm:rxjs/ajax',
|
||||
'rxjs/testing': 'npm:rxjs/testing',
|
||||
'rxjs/webSocket': 'npm:rxjs/webSocket',
|
||||
'jquery': 'npm:jquery/dist/jquery.js',
|
||||
'popper.js': 'npm:popper.js/dist/umd/popper.js',
|
||||
'sifter': 'npm:sifter/sifter.min.js',
|
||||
'microplugin': 'npm:microplugin/src/microplugin.js',
|
||||
'selectize': 'npm:selectize/dist/js/selectize.min.js',
|
||||
'ngx-toastr': 'npm:ngx-toastr/bundles/ngx-toastr.umd.min.js',
|
||||
'eonasdan-bootstrap-datetimepicker': 'npm:eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js',
|
||||
'autonumeric': 'npm:autonumeric',
|
||||
'jsgantt-improved': 'npm:jsgantt-improved/dist/jsgantt.js',
|
||||
'js-year-calendar': 'npm:js-year-calendar/dist/js-year-calendar.js',
|
||||
'ag-grid-angular': 'npm:ag-grid-angular/bundles/ag-grid-angular.umd.js',
|
||||
'ag-grid-community': 'npm:ag-grid-community/dist/ag-grid-community.cjs.js',
|
||||
'inputmask': 'npm:inputmask',
|
||||
'downloadjs': 'npm:downloadjs/download.js',
|
||||
'esmarttokenjs': 'npm:esmarttokenjs/esmarttoken.js',
|
||||
'cadesplugin_api': 'npm:cadesplugin_api/index.js',
|
||||
'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js',
|
||||
'systemjs-babel-build': 'npm:systemjs-plugin-babel/systemjs-babel-browser.js',
|
||||
'chart.js': 'npm:chart.js/dist',
|
||||
'chartjs-adapter-moment': 'npm:chartjs-adapter-moment/dist/chartjs-adapter-moment.js',
|
||||
'tslib': 'npm:tslib/tslib.js',
|
||||
'ngx-international-phone-number': 'npm:ngx-international-phone-number/ngx-international-phone-number.umd.js',
|
||||
'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js'
|
||||
},
|
||||
packages: {
|
||||
'webbpm': { main: 'main', defaultExtension: 'js'},
|
||||
'@angular/common/locales': { defaultExtension: 'js'},
|
||||
'date-fns': { main: 'index.js', defaultExtension: 'js'},
|
||||
'rxjs': { main: 'index.js', defaultExtension: 'js' },
|
||||
'rxjs-compat': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/operators': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/internal-compatibility':{ main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/ajax': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/testing': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/webSocket': { main: "index.js", defaultExtension: 'js'},
|
||||
'moment': { main: 'min/locales.min', defaultExtension: 'js' },
|
||||
'moment-es6': { main: 'index.js', defaultExtension: 'js' },
|
||||
'moment-timezone': { main: 'builds/moment-timezone-with-data.min', defaultExtension: 'js' },
|
||||
'bootstrap': { main: 'dist/js/bootstrap', defaultExtension: 'js'},
|
||||
'lib': { format: 'register', defaultExtension: 'js' },
|
||||
'autonumeric': {
|
||||
main: 'dist/autoNumeric.js'
|
||||
},
|
||||
'chart.js': { main: 'chart.js', defaultExtension: 'js' },
|
||||
'inputmask': {
|
||||
main: 'dist/inputmask.js',
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
}
|
||||
});
|
||||
})(this);
|
||||
92
frontend/systemjs.preview.config.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
(function(global) {
|
||||
System.config({
|
||||
transpiler: 'plugin-babel',
|
||||
paths: {
|
||||
'npm:': 'node_modules/',
|
||||
'generated-sources': 'build_dev/js/generated-sources'
|
||||
},
|
||||
map: {
|
||||
'preview': 'build_dev/js',
|
||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||
'@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
|
||||
'@angular/common/locales': 'npm:@angular/common/locales',
|
||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
||||
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
|
||||
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
|
||||
'@angular/platform-browser/animations':'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
||||
'@ng-bootstrap/ng-bootstrap': 'npm:@ng-bootstrap/ng-bootstrap/bundles/ng-bootstrap.umd.js',
|
||||
'bootstrap': 'npm:bootstrap',
|
||||
'@webbpm/base-package': 'npm:@webbpm/base-package/bundles/webbpm-base-package.umd.js',
|
||||
'angular-calendar': 'npm:angular-calendar/bundles/angular-calendar.umd.js',
|
||||
'angular-calendar/date-adapters/date-fns': 'npm:angular-calendar/date-adapters/date-fns/index.js',
|
||||
'angular-draggable-droppable': 'npm:angular-draggable-droppable/bundles/angular-draggable-droppable.umd.js',
|
||||
'angular-resizable-element': 'npm:angular-resizable-element/bundles/angular-resizable-element.umd.js',
|
||||
'calendar-utils': 'npm:calendar-utils/bundles/calendar-utils.umd.js',
|
||||
'calendar-utils/date-adapters/date-fns': 'npm:calendar-utils/date-adapters/date-fns/index.js',
|
||||
'date-fns': 'npm:date-fns',
|
||||
'ngx-cookie': 'npm:ngx-cookie/bundles/ngx-cookie.umd.js',
|
||||
'moment': 'npm:moment',
|
||||
'moment-timezone': 'npm:moment-timezone',
|
||||
'positioning': 'npm:positioning/dist/positioning.js',
|
||||
'rxjs': 'npm:rxjs',
|
||||
'rxjs-compat': 'npm:rxjs-compat',
|
||||
'rxjs/operators': 'npm:rxjs/operators',
|
||||
'rxjs/internal-compatibility': 'npm:rxjs/internal-compatibility',
|
||||
'rxjs/ajax': 'npm:rxjs/ajax',
|
||||
'rxjs/testing': 'npm:rxjs/testing',
|
||||
'rxjs/webSocket': 'npm:rxjs/webSocket',
|
||||
'jquery': 'npm:jquery/dist/jquery.js',
|
||||
'popper.js': 'npm:popper.js/dist/umd/popper.js',
|
||||
'sifter': 'npm:sifter/sifter.min.js',
|
||||
'microplugin': 'npm:microplugin/src/microplugin.js',
|
||||
'selectize': 'npm:selectize/dist/js/selectize.min.js',
|
||||
'ngx-toastr': 'npm:ngx-toastr/bundles/ngx-toastr.umd.min.js',
|
||||
'eonasdan-bootstrap-datetimepicker': 'npm:eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js',
|
||||
'autonumeric': 'npm:autonumeric',
|
||||
'jsgantt-improved': 'npm:jsgantt-improved/dist/jsgantt.js',
|
||||
'js-year-calendar': 'npm:js-year-calendar/dist/js-year-calendar.js',
|
||||
'ag-grid-angular': 'npm:ag-grid-angular/bundles/ag-grid-angular.umd.js',
|
||||
'ag-grid-community': 'npm:ag-grid-community/dist/ag-grid-community.cjs.js',
|
||||
'inputmask': 'npm:inputmask',
|
||||
'downloadjs': 'npm:downloadjs/download.js',
|
||||
'esmarttokenjs': 'npm:esmarttokenjs/esmarttoken.js',
|
||||
'cadesplugin_api': 'npm:cadesplugin_api/index.js',
|
||||
'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js',
|
||||
'systemjs-babel-build': 'npm:systemjs-plugin-babel/systemjs-babel-browser.js',
|
||||
'chart.js': 'npm:chart.js/dist',
|
||||
'chartjs-adapter-moment': 'npm:chartjs-adapter-moment/dist/chartjs-adapter-moment.js',
|
||||
'tslib': 'npm:tslib/tslib.js',
|
||||
'ngx-international-phone-number': 'npm:ngx-international-phone-number/ngx-international-phone-number.umd.js',
|
||||
'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js'
|
||||
},
|
||||
packages: {
|
||||
'preview': { main: './modules/preview/preview.main', defaultExtension: 'js'},
|
||||
'@angular/common/locales': { defaultExtension: 'js'},
|
||||
'date-fns': { main: 'index.js', defaultExtension: 'js'},
|
||||
'rxjs': { main: 'index.js', defaultExtension: 'js' },
|
||||
'rxjs-compat': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/operators': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/internal-compatibility':{ main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/ajax': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/testing': { main: "index.js", defaultExtension: 'js'},
|
||||
'rxjs/webSocket': { main: "index.js", defaultExtension: 'js'},
|
||||
'moment': { main: 'min/locales.min', defaultExtension: 'js' },
|
||||
'moment-timezone': { main: 'builds/moment-timezone-with-data.min', defaultExtension: 'js' },
|
||||
'bootstrap': { main: 'dist/js/bootstrap', defaultExtension: 'js'},
|
||||
'lib': { format: 'register', defaultExtension: 'js' },
|
||||
'autonumeric': {
|
||||
main: 'dist/autoNumeric.js'
|
||||
},
|
||||
'chart.js': { main: 'chart.js', defaultExtension: 'js' },
|
||||
'inputmask': {
|
||||
main: 'dist/inputmask.js',
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
}
|
||||
});
|
||||
})(this);
|
||||
42
frontend/tsconfig.aot.json
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": false,
|
||||
"sourceMap": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"skipLibCheck": true,
|
||||
"noImplicitAny": false,
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2016",
|
||||
"dom",
|
||||
"es2017.object",
|
||||
"es2018.promise"
|
||||
]
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false,
|
||||
"include": [
|
||||
"src/ts/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"./node_modules/@types",
|
||||
"src/ts/main.ts",
|
||||
"**/*.spec.ts",
|
||||
"src/test.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"skipMetadataEmit": true,
|
||||
"alwaysCompileGeneratedCode":true,
|
||||
"preserveWhitespaces": false,
|
||||
"annotationsAs": "decorators",
|
||||
"mainPath": "./src/ts"
|
||||
}
|
||||
}
|
||||
40
frontend/tsconfig.json
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": false,
|
||||
"inlineSourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"skipLibCheck": true,
|
||||
"noImplicitAny": false,
|
||||
"outDir": "build_dev/js",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2016",
|
||||
"dom",
|
||||
"es2017.object",
|
||||
"es2018.promise"
|
||||
]
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false,
|
||||
"include": [
|
||||
"src/ts/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"./node_modules/@types",
|
||||
"**/*.ngfactory.ts",
|
||||
"**/*.shim.ts",
|
||||
"src/ts/main.aot.ts",
|
||||
"src/ts/generated-sources/**/*",
|
||||
"src/ts/page.routing.ts",
|
||||
"src/ts/aot"
|
||||
]
|
||||
}
|
||||
136
frontend/webpack.aot.config.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
function _path(p) {
|
||||
return path.join(__dirname, p);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
'polyfills': './build/scripts/polyfills.js',
|
||||
'vendor': './build/scripts/vendor.js',
|
||||
'main': './build/scripts/main.aot.js'
|
||||
},
|
||||
|
||||
context: process.cwd(),
|
||||
|
||||
output: {
|
||||
path: path.join(process.cwd(), './dist'),
|
||||
filename: '[name].[chunkhash].bundle.js',
|
||||
chunkFilename: '[id].[chunkhash].chunk.js'
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [path.resolve(__dirname, "node_modules")],
|
||||
options: {
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'angular-router-loader?aot=true'
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'raw-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: {
|
||||
// you can specify a publicPath here
|
||||
// by default it use publicPath in webpackOptions.output
|
||||
// publicPath: '../'
|
||||
}
|
||||
},
|
||||
"css-loader"
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf)$/,
|
||||
loader: 'file-loader?name=src/resources/[name].[hash].[ext]'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
terserOptions: {
|
||||
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
|
||||
}
|
||||
})
|
||||
],
|
||||
splitChunks: {
|
||||
// include all types of chunks
|
||||
chunks: 'all'
|
||||
}
|
||||
},
|
||||
|
||||
plugins: [
|
||||
// new BundleAnalyzerPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'index.webpack.html',
|
||||
filename: 'index.html',
|
||||
chunksSortMode : 'none'
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{from: 'index.webpack.html', to: 'index.html'},
|
||||
{from: 'src/resources/img/progress.gif', to: 'src/resources/img/progress.gif'},
|
||||
{from: 'src/resources/img/logo.png', to: 'src/resources/img/logo.png'},
|
||||
{from: 'src/resources/app-config.json', to: 'src/resources/app-config.json'},
|
||||
{from: 'src/resources/app.version', to: 'src/resources/app.version'}
|
||||
]),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[hash].css',
|
||||
chunkFilename: '[id].[hash].css'
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
$: "jquery",
|
||||
jQuery: "jquery",
|
||||
"window.jQuery": "jquery",
|
||||
Popper: ['popper.js', 'default']
|
||||
})
|
||||
],
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'jquery': _path('node_modules/jquery/dist/jquery.min'),
|
||||
'inputmask': _path('node_modules/inputmask/dist/inputmask'),
|
||||
'downloadjs': _path('node_modules/downloadjs/download.min.js'),
|
||||
'esmarttokenjs': _path('node_modules/esmarttokenjs/esmarttoken.js'),
|
||||
'cadesplugin_api': _path('node_modules/cadesplugin_api/index.js'),
|
||||
'eonasdan-bootstrap-datetimepicker': _path('node_modules/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker'),
|
||||
'autonumeric': _path('node_modules/autonumeric/dist/autoNumeric.js'),
|
||||
'jsgantt-improved': _path('node_modules/jsgantt-improved/dist/jsgantt.js'),
|
||||
'js-year-calendar': _path('node_modules/js-year-calendar/dist/js-year-calendar.js'),
|
||||
'chart.js': _path('node_modules/chart.js/dist/chart.js')
|
||||
},
|
||||
modules: [
|
||||
'node_modules',
|
||||
path.resolve(process.cwd(), './build'),
|
||||
path.resolve(process.cwd(), './build/scripts'),
|
||||
],
|
||||
extensions: ['.js']
|
||||
},
|
||||
|
||||
stats: {
|
||||
children: false
|
||||
},
|
||||
devtool: false
|
||||
};
|
||||