commit c8b2f25d95bb15cd0f7539d8f83b1e4ecb4eaa83 Author: Булат Хайруллин Date: Tue Jun 11 10:58:00 2024 +0300 Project first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..de62d734 --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +#ignore target dir +target*/ + +#gradle files +.gradle*/ + +*.orig +# +# Eclipse project files +# +#.classpath +#.project +#.settings*/ +.springBeans +.metadata/ +war*/ + +# +# IntelliJ IDEA project files +# +.idea*/ +.classes*/ +*.ipr +*.iml +*.iws +*.ids +atlassian-ide-plugin.xml + +#ignore NetBeans project files +nb-configuration.xml +profiles.xml +catalog.xml +nbactions.xml + +#ignore some temporary files +*.vpp~* + +# os meta files +Thumbs.db +.DS_Store + + +pom.xml.versionsBackup +*.jasper + +#studio +.studio*/ + +resources/src/main/generated-resources*/ +resources/src/main/resources/database/database_structure.xml + +frontend/build*/ +frontend/tmp*/ +frontend/.angular*/ +frontend/build_dev*/ +frontend/dist*/ +frontend/node_modules*/ +frontend/src/ts/**/*.js +frontend/src/ts/**/*.js.map +frontend/src/ts/**/*.ngsummary.json +frontend/src/ts/aot*/ +frontend/src/ts/generated*/ +npm-debug.log + +#Sublime project files +*.sublime-project +*.sublime-workspace diff --git a/.studioignore b/.studioignore new file mode 100644 index 00000000..3fc9113d --- /dev/null +++ b/.studioignore @@ -0,0 +1,29 @@ +#Files for Webbpm-Studio to ignore +frontend/build/ +frontend/build_dev/ +frontend/dist/ +frontend/node_modules/ +frontend/src/ts/page.routing.ts +frontend/src/ts/generated-sources/ +frontend/src/ts/generated/ +frontend/target/ + +backend/target/ +backend/src/main/generated-sources/ + +distribution/target/ + +resources/target/ + +test/ +extensions/ + +config/ +target/ +themes/ + +.studio/ +.git/ +.idea/ +.studioignore +**.js \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..acbeeb3d --- /dev/null +++ b/README.md @@ -0,0 +1,224 @@ +# Создание БД проекта + +Создание роли для основной схемы БД проекта + +``` +CREATE ROLE "" WITH + LOGIN + NOSUPERUSER + INHERIT + NOCREATEDB + NOCREATEROLE + NOREPLICATION + PASSWORD ''; +``` + +Создание роли для схемы безопасности БД проекта + +``` +CREATE ROLE "" WITH + LOGIN + NOSUPERUSER + INHERIT + NOCREATEDB + NOCREATEROLE + NOREPLICATION + PASSWORD ''; +``` + +Создание БД проекта + +``` +CREATE DATABASE "" + WITH + OWNER = ""; +``` + +ВНИМАНИЕ: в общем случае, отдельную БД для безопасности создавать не нужно. В конфигурации источника данных security-ds в файле standalone.xml в качестве имени базы данных используйте базу данных приложения. + +Предоставление необходимых прав для роли <your-project-security-role> + +``` +GRANT CREATE ON DATABASE "" TO ""; +``` + +Создание таблицы shedlock для автосинхронизации + +``` +CREATE TABLE shedlock +( + name varchar not null + constraint tasks_lock_pkey + primary key, + lock_until timestamp, + locked_at timestamp, + locked_by varchar +); + +comment on table shedlock is 'Таблица для синхронизации выполнения запланированных задач между нодами.'; + +ALTER TABLE shedlock + OWNER to "owner"; +``` + +## Дополнительные ограничения базы секьюрити + +Логин пользователя <user_account.username> и имена ролей <user_role.name> не должны совпадать, так как в ходе работы jbpm-а они сохраняются в одну и ту же таблицу. +Пример ошибки при совпадении: username = 'qa_test' и role_name = 'qa_test' (роль привязана к этому пользователю). Ошибка возникает при запуске любого процесса под этим пользователем. + +``` +ERROR [errorhandling.ExceptionHandlerController] (default task-5) [19usm9-bgyi63] +Organizational entity already exists with [GroupImpl:'qa_test'] id, +please check that there is no group and user with same id: +java.lang.RuntimeException: Organizational entity already exists with [GroupImpl:'qa_test'] id, +please check that there is no group and user with same id +``` + +## Создание нового администратора + +Создайте группу <your-admin-group> и предоставьте ей права в модуль администрирования. Для этого выполните в БД проекта + +``` +INSERT INTO security.user_group( + user_group_id, name, access_level_id) + (SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring), + '', access_level_id FROM security.access_level where level=999); +``` + +``` +INSERT INTO security.link_user_group_user_role( + link_user_group_user_role_id, user_group_id, user_role_id) + (SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring), + (SELECT user_group_id FROM security.user_group WHERE name = ''), + (SELECT user_role_id FROM security.user_role WHERE name = 'Security - User Admin')); +``` + +``` +INSERT INTO security.link_user_group_user_role( + link_user_group_user_role_id, user_group_id, user_role_id) + (SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring), + (SELECT user_group_id FROM security.user_group WHERE name = ''), + (SELECT user_role_id FROM security.user_role WHERE name = 'Security - Group Admin')); +``` + +``` +INSERT INTO security.link_user_group_user_role( + link_user_group_user_role_id, user_group_id, user_role_id) + (SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring), + (SELECT user_group_id FROM security.user_group WHERE name = ''), + (SELECT user_role_id FROM security.user_role WHERE name = 'Security - Role Admin')); +``` + +# Настройка браузера для входа в систему с помощью Kerberos + +1. Запустите браузер firefox. +2. В адресной строке введите about:config, нажать кнопку "я принимаю на себя риск" +3. С помощью поиска найдите параметр network.negotiate-auth.trusted-uris и в качестве значения ввести домен(например для домена example.com надо ввести .example.com) +4. Откройте в браузере приложение. Пример http://app.example.com/ . Приложение должно открыться без запроса логина/пароля + +# Восстановление структуры БД + +На основе БД проекта с помощью jOOQ генерируются Java классы для каждого объекта БД. Это происходит по нажатию кнопки Обновить на панели БД в студии. При необходимости можно сформировать DDL на основе данных классов. Пример класса для генерации DDL + +``` +package ru.cg.webbpm.test_project.db_beans; + +import org.jooq.*; +import org.jooq.impl.*; + +public class Main { + public static void main (String args []) { + DefaultConfiguration defaultConfiguration = new DefaultConfiguration(); + defaultConfiguration.setSQLDialect(SQLDialect.POSTGRES); + Queries ddl = DSL.using(defaultConfiguration).ddl(DefaultCatalog.DEFAULT_CATALOG); + + for (Query query : ddl.queries()) { + System.out.println(query); + } + } +} +``` + +** ВНИМАНИЕ: ** + +- этим способом нельзя восстановить функции/процедуры БД + +см. также [https://www.jooq.org/doc/latest/manual/sql-building/ddl-statements/generating-ddl/](https://www.jooq.org/doc/latest/manual/sql-building/ddl-statements/generating-ddl/) + +# Сборка проекта +## В dev режиме +```bash +mvn clean && mvn package +``` +## В prod режиме +```bash +mvn clean && mvn package -Pprod -DngcCoreCount=4 -DpagePackSizeMb=24 +``` +ngcCoreCount - количество ядер, выделяемых процессу компиляции ngc. По умолчанию - количество ядер - 1 +pagePackSizeMb - размер пачки в МБ. По умолчанию - количество ядер - 24 МБ. + +## С обновлением database beans +```bash +mvn clean && mvn package -Dwebbpm.generate-db-beans +``` + +# Версия проекта + +Если версия проекта содержит SNAPSHOT (например 1.0-SNAPSHOT), то при установке такой версии на сервере приложений будет запущена процедура остановки запущенных процессов данной версии. Этот режим удобен при отладке процесса на рабочем месте аналитика. +На боевом и тестовом стенде необходимо передавать дистрибутив проекта, с версией, которая не содержит SNAPSHOT. Например - 1.0 + +# Обновление платформы + +## Обновления версии платформы + +### С помощью студии + +1. Откройте проект в студии. Версия платформы обновится автоматически + +### Вручную + +1. Обновите значение webbpm-platform.version в pom.xml. Пример + + ```xml + 3.164.0-SNAPSHOT + ``` + +## Обновление базового пакета компонент + +### С помощью студии + +1. Откройте проект в студии. + +2. Откройте меню "Проект - Пакеты" + +3. Нажмите обновить. + + +### Вручную + +#### Из удаленного репозитория + +```bash +mvn webbpm:update-package -DpackageVersion="3.158.8" +``` + +#### Из файла + +```bash +mvn webbpm:update-package -DexecuteNpmInstall=false -Dpath=resources-.jar +``` + +#### Руками + +1. Измените версию платформы и backend модуля в файле [pom.xml](pom.xml) вашего проекта на нужную версию +2. Скопируйте ресурсы +``` +из директории: webbpm-platform\components\resources\target\classes\ +в директорию: {your-project}\packages\ru.cg.webbpm.packages.base.resources\ +``` +3. Скопируйте фронт +``` +из директории: webbpm-platform\components\frontend\dist +в директорию: {your-project}\frontend\node_modules\@webbpm\base-package\ +``` +4. Запретите выполнение npm install при запуске студии. Для этого добавьте параметр `-DexecuteNpmInstall=false` в настройках Run/Debug Configurations студии diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 00000000..69eee9a6 --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,394 @@ + + + 4.0.0 + + ervu_lkrp_ul + ervu_lkrp_ul + 1.0.0-SNAPSHOT + + + ervu_lkrp_ul.ervu_lkrp_ul + backend + war + + + + + org.springframework.security + spring-security-jwt + + + io.jsonwebtoken + jjwt-api + + + io.jsonwebtoken + jjwt-impl + runtime + + + + ervu_lkrp_ul.ervu_lkrp_ul + resources + runtime + + + + ru.cg.webbpm.modules.reporting.reporting-jasper + reporting-jasper-fonts + runtime + + + + org.ocpsoft.prettytime + prettytime + + + + org.jooq + jooq + + + + org.apache.santuario + xmlsec + + + + javax.servlet + javax.servlet-api + + + org.slf4j + slf4j-api + provided + + + + org.springframework + spring-core + + + org.springframework + spring-context + + + org.springframework + spring-beans + + + org.springframework + spring-aop + + + org.springframework + spring-jdbc + + + org.springframework + spring-tx + + + org.springframework + spring-aspects + + + org.springframework + spring-web + + + org.springframework + spring-webmvc + + + + + org.springframework.security + spring-security-web + + + org.springframework.security + spring-security-config + + + + + ru.cg.webbpm.modules + inject + + + + + ru.cg.webbpm.modules.bpmn + bpmn-workflow-api + + + ru.cg.webbpm.modules.bpmn + bpmn-workflow-jbpm-api + + + ru.cg.webbpm.modules.bpmn + bpmn-workflow-jbpm + + + ru.cg.webbpm.modules.bpmn + bpmn-beans + + + ru.cg.webbpm.modules.bpmn + bpmn-variable-condition-adapter + + + ru.cg.webbpm.modules.bpmn + bpmn-deploy + + + + ru.cg.webbpm.modules + webkit-rpc + + + ru.cg.webbpm.modules + webkit-beans + + + + ru.cg.webbpm.modules.core + core-runtime-api + + + ru.cg.webbpm.modules.resources + resources-api + + + ru.cg.webbpm.modules.core + error-handling-api + + + + + ru.cg.webbpm.modules.database + database-api + + + ru.cg.webbpm.modules.database + database-impl + + + + ru.cg.webbpm.modules.jndi + jndi-beans + + + ru.cg.webbpm.modules.jndi + jndi-inject + + + + + com.sun.mail + javax.mail + + + + ru.cg.webbpm.modules.database + database-test + test + + + + ru.cg.webbpm.modules + standard-annotations + + + ru.cg.webbpm.modules.core + metrics + + + ru.cg.webbpm.modules.webkit + active-users-tracker + + + ru.cg.webbpm.modules.security + security-beans + + + ru.cg.webbpm.modules.security + security-api + + + ru.cg.webbpm.modules.security + security-esia + + + + + ru.cg.webbpm.modules.reporting + reporting-api + + + ru.cg.webbpm.modules.reporting + reporting-runtime-api + + + ru.cg.webbpm.modules.reporting + reporting-runtime-impl + + + ru.cg.webbpm.modules.reporting.reporting-jasper + reporting-jasper-impl + + + ru.cg.webbpm.modules.reporting.reporting-jasper + reporting-jasper-runtime-impl + + + ru.cg.webbpm.modules.reporting.reporting-xdoc + reporting-xdoc-impl + + + ru.cg.webbpm.modules.reporting.reporting-xdoc + reporting-xdoc-runtime-impl + + + + org.liquibase + liquibase-core + + + ru.cg.webbpm.modules + webkit-base + + + + ru.cg.webbpm.modules.security + security-db-synchronization-api + + + ru.cg.webbpm.modules.security + security-db-synchronization-ldap-impl + + + + xerces + xercesImpl + + + + com.google.guava + guava + + + + ru.micord.fias + client + + + + org.apache.tika + tika-core + + + + org.springframework.security.kerberos + spring-security-kerberos-core + + + + org.springframework.security.kerberos + spring-security-kerberos-web + + + + org.bouncycastle + bcprov-jdk15on + + + + org.bouncycastle + bcpkix-jdk15on + + + + org.mnode.ical4j + ical4j + + + + net.javacrumbs.shedlock + shedlock-spring + + + + net.javacrumbs.shedlock + shedlock-provider-jdbc-template + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + false + true + 17 + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + add-source + generate-sources + + add-source + + + + ${project.basedir}/target/generated-sources/java + + + + + + + + + + + studio + + + ru.cg.webbpm.modules.resources + resources-impl-development + + + + + + org.apache.maven.plugins + maven-war-plugin + + ${project.artifactId} + + + + + + + dev + + + org.postgresql + postgresql + + + + + diff --git a/backend/src/main/java/AppConfig.java b/backend/src/main/java/AppConfig.java new file mode 100644 index 00000000..f7645b22 --- /dev/null +++ b/backend/src/main/java/AppConfig.java @@ -0,0 +1,65 @@ +import java.time.Duration; +import javax.sql.DataSource; + +import net.javacrumbs.shedlock.core.LockProvider; +import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; +import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration; +import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +/** + * Root application context + * This context imports XML configs from all the other jars, and is created by {@link WebAppInitializer} + * NB: modules are excluded from component scan since spring-context.xml sometimes holds important parameters and / or annotations + * @author krylov + */ +@Configuration +@ComponentScan(basePackages = { + "service", + "dao", + "bpmn", + "i18n", + "errorhandling", + "database", + "security", + "component.addresses", + "gen", + "ru.cg", + "ru.micord" +}) +@EnableAspectJAutoProxy(proxyTargetClass = true) +@EnableWebMvc +@EnableScheduling +public class AppConfig { + + @Bean + public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){ + return new PropertySourcesPlaceholderConfigurer(); + } + + @Bean + public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(12); + scheduler.initialize(); + return ScheduledLockConfigurationBuilder + .withLockProvider(lockProvider) + .withTaskScheduler(scheduler) + .withDefaultLockAtMostFor(Duration.ofHours(4)) + .build(); + } + + @Bean + public LockProvider lockProvider(@Qualifier("datasource") DataSource dataSource) { + return new JdbcTemplateLockProvider(dataSource); + } +} diff --git a/backend/src/main/java/WebAppInitializer.java b/backend/src/main/java/WebAppInitializer.java new file mode 100644 index 00000000..f4fef231 --- /dev/null +++ b/backend/src/main/java/WebAppInitializer.java @@ -0,0 +1,31 @@ +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; +import org.springframework.web.util.IntrospectorCleanupListener; + +/** + * This initializer creates root context and registers dispatcher servlet + * Spring scans for initializers automatically + */ +public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + + public void onStartup(ServletContext servletContext) throws ServletException { + super.onStartup(servletContext); + servletContext.addListener(new IntrospectorCleanupListener()); + } + + protected String[] getServletMappings() { + return new String[]{"/"}; + } + + @Override + protected Class[] getRootConfigClasses() { + return new Class[]{AppConfig.class}; + } + + @Override + protected Class[] getServletConfigClasses() { + return new Class[0]; + } +} diff --git a/backend/src/main/java/controller/ProfileController.java b/backend/src/main/java/controller/ProfileController.java new file mode 100644 index 00000000..c1a4e96c --- /dev/null +++ b/backend/src/main/java/controller/ProfileController.java @@ -0,0 +1,34 @@ +package controller; + +import dto.jivoprofile.JivoProfileDto; +import org.hsqldb.lib.StringUtil; +import org.springframework.web.bind.annotation.*; + +import ru.cg.webbpm.modules.security.api.model.User; +import ru.cg.webbpm.modules.security.api.service.UserService; + +@RestController +public class ProfileController { + + private UserService userService; + + public ProfileController(UserService userService) { + this.userService = userService; + } + + @RequestMapping(value = "/profile/jivo/{userId}", method = RequestMethod.GET) + public JivoProfileDto profile(@PathVariable("userId") String userId) { + + if (StringUtil.isEmpty(userId)) { + return new JivoProfileDto(); + } + + User user = userService.get(userId); + + JivoProfileDto profileDto = new JivoProfileDto(); + profileDto.setUsername(user.firstName()); + profileDto.setEmail(user.email()); + profileDto.setPhone(user.phone()); + return profileDto; + } +} diff --git a/backend/src/main/java/dto/jivoprofile/JivoProfileDto.java b/backend/src/main/java/dto/jivoprofile/JivoProfileDto.java new file mode 100644 index 00000000..c53717e8 --- /dev/null +++ b/backend/src/main/java/dto/jivoprofile/JivoProfileDto.java @@ -0,0 +1,35 @@ +package dto.jivoprofile; + +import ru.cg.webbpm.modules.webkit.annotations.Model; + +@Model +public class JivoProfileDto { + + public String username; + public String email; + public String phone; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} \ No newline at end of file diff --git a/backend/src/main/resources/.gitkeep b/backend/src/main/resources/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/config.md b/config.md new file mode 100644 index 00000000..74557103 --- /dev/null +++ b/config.md @@ -0,0 +1,749 @@ +# Описание параметров конфигурации сервера приложений + +Файл /standalone/configuration/standalone.xml + +## Общие + +- `webbpm.security.login.case_sensitive`. Default value = true. Параметр отвечающий за чувствительность к регистру при обработке логина пользователя. + - true - Login и login, обрабатываются как разные логины. + - false - Login и login, обрабатывается как один и тот же логин. +- `webbpm.db.pool_size`. Нужен только для `webbpm.mode` = development. +- `webbpm.mode`. Режим работы приложения. Значения - development, production. + - development - в этом режиме используется соединение к БД проекта, заданное в Studio. + - production - в этом режиме используется соединение к БД проекта, заданное с помощью jndi имени. + +## jBPM + +- `com.arjuna.ats.arjuna.allowMultipleLastResources` +- `webbpm.jbpm.audit-log.disabled` - флаг, отвечающий за включение/отключение аудита jBPM +- `webbpm.jbpm.cleaner_cron` - cron расписание автоматической очистки БД JBPM, по умолчанию "-" т.е. выключен. Очищаются незавершенные процессы. +- `webbpm.jbpm.cleaner_timeout` - время, спустя которое процесс считается устаревшим. По умолчанию - 10 часов. +- `webbpm.jbpm.finished_process_cleaner_cron` - cron расписание автоматической очистки аудита процессов в БД JBPM, по умолчанию "-" + т.е. выключен +- `webbpm.jbpm.finished_process_cleaner_timeout` - время, спустя которое процесс считается устаревшим. По умолчанию - 10 часов. + +Пример: + +```xml + + + +``` + +### jBPM Runtime Strategy + +Возможные варианты runtime strategy: + +- SINGLETON +- PER_REQUEST (значение по умолчанию) +- PER_PROCESS_INSTANCE +- PER_CASE + +Пример: + +```xml + +``` + +## Способ аутентификации + +- authentication.method - способ аутентификации. Поддерживаемые способы аутентификации: form, kerberos, cert_over_db, cert_over_ldap + +### По логину и паролю + +Пример конфигурации: + +```xml + +``` + +### По сертификату + +- cert_over_db - проверка наличия пользователя в базе данных безопасности +- cert_over_ldap - проверка наличия пользователя в базе данных безопасности и в LDAP + +Примеры: + +```xml + + +``` + +Параметр способа аутентификации authentication.method должен быть также установлен на клиентской части приложения в app-config.json + +Также для аутентификации по сертификату нужны свойства для хранилища сертификатов: + +- certificate.keystore.location - путь до java key store. Key store - это хранилище доверенных сертификатов, с помощью которых можно проверить корневой сертификат. Сертификат устанавливается с помощью команды: + + ```text + keytool -importcert -alias myAlias -file Example.cer -keystore exampleKeyStore + ``` + +- certificate.keystore.password - пароль для keystore, установленный при импорте сертификата + +Примеры: + +```xml + + +``` + +### Kerberos + +Получите от администратора Kerberos .keytab файл, из которого командой `klist -k http.keytab` можно получить список principal-ов +Проверить успешность авторизации principal-а можно командой `kinit -t -i http.keytab %principal%`. +В +случае успешной авторизации команда `klist` в качестве default principal которым проводилась авторизация. +После этого в standalone.xml поправить параметр `app.service-principal` на principal, которым успешно авторизовались. principal имеет формат: `HTTP/%hostname%@%REALM%` + +Пример конфигурации: + +```xml + + + +``` + +- app.service-principal. Пример - HTTP/oleg-rxserver.alt.dom@ALT.DOM +- app.keytab-location - расположение keytab файла. Пример - ${jboss.home.dir}/ +- http.keytab. + +Также необходимо в настройках браузера на клиенте задать параметр `network.negotiate-auth.trusted-uris` задать значение `.%domain%`. +Пример: + +```xml +.ALT.DOM +``` + +### Ldap + +Аутентификация происходит посредством логина и пароля синхронизированного пользователя Ldap. +Логин и пароль введенные в форму входа, будут проверены сервисом Ldap. + +Пример конфигурации: + +```xml + + + + + +``` + +#### Настройка сервера приложений для работы с Kerberos + +1. создать учетные записи в домене: + + - тестовые для проверки работоспособности функционала (пользователи домена, почтовые ящики): + + `User1 pass1 user1@example.com` + + `User2 pass2 user2@example.com` + + - сервисную для доступа сервиса(приложения) к MS AD - (пользователь домена - снять устаревание пароля, ограничение по времени действия, почтовый ящик): + + `serviceUser servicePass serviceuser@example.com` + + --- + +2. завести учетную запись машины `appserver.machine.name` (fqdn-имя сервиса) в AD (вручную, как запись в computer) и в DNS (A-запись) + + `appserver.machine.name 10.250.216.91` + +3. сгенерировать keytab (утилита ktpass) для аутентификации сервисов(приложения) для единой точки входа - `serviceUser` - с именем test.file.name.keytab - привязав к ней пользователя serviceUser + + например, так: + + ``` + ktpass -princ http/appserverMachineName.example.com@example.com -mapuser example.com\serviceUser -pass "пароль_уз_serviceUser" -crypto All -ptype KRB5_NT_PRINCIPAL -out "путь_к_директории_выгрузки_файла\test.file.name.keytab" + ``` + +##### Kerberos FAQ + +- В случае, если авторизация не проходит и в логах сервера приложений присутствует следующий вывод: + + ``` + 2019-05-14 05:33:36,588 INFO [security.controller.KerberosAuthenticationController] (default task-3) Authentication request header Authorization not exists + 2019-05-14 05:33:36,588 INFO [security.controller.KerberosAuthenticationController] (default task-3) Authentication object is not presented + ``` + + необходимо проверить настройку браузера firefox `network.negotiate-auth.trusted-uris`, она должна соответствовать домену из principal-а. + Для этого в поисковую строку браузера вводим "about:config", в открывшемся окне нажимаем "accept with risk and continue", в поисковой строке открывшейся страницы ввести `network.negotiate-auth.trusted-uris`. + Пример: для principal-а `HTTP/oleg-rxserver.alt.dom@ALT.DOM` настройка в браузере должна быть `.alt.dom`, приложение в браузере должно открываться по `http:\\oleg-rxserver.alt.dom:8080\...` + +- если в логах сервера приложений есть ошибка: + + ```xml + 2019-05-13 14:13:07,095 WARN [org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter] (default task-1) Negotiate Header was invalid: Negotiate TlRMTVNTUAABAAAAl4II4gAAAAAAAAAAAAAAAAAAAAAKAGNFAAAADw==: org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful + ... + + Caused by: java.security.PrivilegedActionException: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag) + at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.8.0_211] + at javax.security.auth.Subject.doAs(Subject.java:422) [rt.jar:1.8.0_211] + at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:68) [spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE] + ... 66 more + Caused by: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag) + at sun.security.jgss.GSSHeader.(GSSHeader.java:97) [rt.jar:1.8.0_211] + at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:306) [rt.jar:1.8.0_211] + at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:285) [rt.jar:1.8.0_211] + at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidatorKerberosValidateAction.run(SunJaasKerberosTicketValidator.java:170) [spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE] + at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidatorKerberosValidateAction.run(SunJaasKerberosTicketValidator.java:153) [spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE] + ... 69 more + ``` + + необходимо проверить правильность указанного в standalone.xml principal-а. + +### Комбинации нескольких способов аутентификации + +Приложение может обрабатывать запросы на несколько способов аутентификации. Для этого необходимо переичислить нужные профили через запятую. +Примеры: + +```xml + + + + +``` + +### Время жизни токенов аутентификации + +```xml + + + +``` + +`webbpm.security.access_token.duration.minutes` - опциональный параметр (значение по умолчанию 60), время жизни в минутах, сколько будет действителен токен, после истечения этого времени токен будет обновлён `webbpm.security.refresh_token.duration.days` - опциональный параметр (значение по умолчанию 30), время жизни в днях, после истечения этого времени с последнего обновления, пользователю будет необходимо повторно войти `webbpm.security.session.active.count` - опциональный параметр (значение по умолчанию 1), количество сохраняемых в базу токенов обновления (количество активных сессий) + +Примечания. + +- Нельзя использовать одновременно профили cert_over_db c cert_over_ldap и kerberos с form. + +## Синхронизация пользователей с LDAP + +```xml + + + + + + + +``` + +Настройки подключения к LDAP: + +- `ldap.auto.sync.enabled` - включает/отключает автоматическую синхронизацию с LDAP +- `ldap.synchronizer.cron` - cron расписание автоматической синхронизации с LDAP +- `ldap.url`. Пример - ldap://localhost:389 +- `ldap.base`. Пример - dc=alt,dc=dom +- `ldap.username`. Пример - uid=test,ou=People,dc=alt,dc=dom +- `ldap.password` +- `webbpm.ldap.implementation`. Допускается два значения: open-ldap и active-directory. + +## WEBGUARD + +Для синхронизации пользователей в WEBGUARD и для корректной работы админки необходимо указать настройки соединения к REST API WEBGUARD + +```xml + + + +``` + +## Статистика + +### jbpm hibernate statistics + +Статистика hibernate jbpm доступна по jmx по пути `org.hibernate:type=Stats,name=jbpm`. +Полный список параметров можно посмотреть через jconsole. + +По умолчанию включена, отключить можно настройкой `webbpm.jbpm.hibernate_statistics.enabled`: + +```xml + +``` + +## Schedulers по очистке базы *jbpm*. + +- Очистка таблиц аудита от завершенных процессов по истечении таймаута + + - `webbpm.jbpm.finished_process_cleaner_cron` - задает расписание (*spring cron*), если настройка не задана - джоб не запускается + - `webbpm.jbpm.finished_process_cleaner_timeout` - таймаут в часах (*целое числовое значение*) + + Пример: + + ```xml + + + ``` + + ## Ограничения для запросов БД. + +- `webbpm.db.query_limit_enabled` - флаг, отвечающий за вывод сообщений о превышении лимитов на количество возвращаемых записей. По умолчанию - false. + +- `webbpm.db.select_records_max_limit` - максимальный лимит возвращаемых строк для запросов в БД, при превышении/равенстве данного лимита будет выброшена ошибка (*целое числовое значение*). По умолчанию - 100000 + +- `webbpm.db.select_records_min_limit` - минимальный лимит возвращаемых строк для запросов в БД, при превышении/равенстве данного лимита в логи будет выведен warning (*целое числовое значение*). По умолчанию - 1000 + +- `webbpm.db.execution_time_threshold`. The threshold for time of executing a statement. Система выводит сообщение в логи при превышении. Действует для запросов, созданных в jOOQ. По умолчанию - 1000 миллисекунд + +- `webbpm.db.result_read_time_threshold`. The threshold for time of fetching a set of records from a ResultSet. + . Система выводит сообщение в логи при превышении. Действует для запросов, созданных в jOOQ. По умолчанию - 50 миллисекунд + +- `webbpm.db.results_count_threshold`. Система выводит сообщение в логи при превышении. Действует для запросов, созданных в jOOQ. По умолчанию - 1000 записей + +- `webbpm.db.full_time_threshold`. Ограничение на полное время выполнения запроса. Система выводит сообщение в логи при превышении. Действует для запросов, созданных в jOOQ. По умолчанию sum(webbpm.db.result_read_time_threshold, webbpm.db.execution_time_threshold) миллисекунд + +- `webbpm.db.query_timeout`. Ограничение на время выполнения запроса. При превышении запрос будет отклонен. Действует для запросов, созданных в jOOQ в dev режиме. По умолчанию 120 секунд. + +Пример: + +```xml + + + + + + + + +``` + +## Добавление версии приложения в URL при запросах к backend-у + +При сборке приложения с профилем enable-version-in-url в URL будет добавляться версия приложения, указанная в pom.xml. +Шаблон URL: + +``` +//:/backend/ +``` + +## Включение регистрации пользователя + +1. Укажите конфигурацию почтового сервера для отправки писем с подтверждением регистрации. +Для этого в файле проекта *jndi-resources.xml* добавьте ресурс SmtpConfiguration. +Шаблон: +``` +{"host":"host","port":1234,"login":"user","password":"password","from":"email_from","senderName":"sender_name","isSecured":true} +``` +Почтовый сервер - зарегистрированный актуальный почтовый адрес. В поле password нужно указывать не пароль для входа в почту, а создать пароль для приложений в учетке почты и указать его. +2. Для включения регистрации добавьте в *standalone.xml* свойство +``` + +``` +3. Также в *standalone.xml* укажите ресурс для отправки писем для подтверждения регистрации (из п.1) +``` + +``` +4. При необходимости, отредактируйте шаблон письма для подтверждения регистрации +(resources/src/main/resources/mail/confirmation.html) + +5. При необходимости, отредактируйте шаблон письма для восстановления пароля +(resources/src/main/resources/mail/reset_password.html) + + +#### Настройка браузера для входа в систему с помощью Kerberos + +1. Запустите браузер firefox. +2. В адресной строке введите about:config, нажать кнопку "я принимаю на себя риск" +3. С помощью поиска найдите параметр network.negotiate-auth.trusted-uris и в качестве значения ввести домен(например для домена example.com надо ввести .example.com) +4. Откройте в браузере приложение. Пример [http://app.example.com/](http://app.example.com/) . Приложение должно открыться без запроса логина/пароля + +## Восстановление структуры БД + +На основе БД проекта с помощью jOOQ генерируются Java классы для каждого объекта БД. Это происходит по нажатию кнопки Обновить на панели БД в студии. При необходимости можно сформировать DDL на основе данных классов. Пример класса для генерации DDL + +``` +package ru.cg.webbpm.test_project.db_beans; + +import org.jooq.*; +import org.jooq.impl.*; + +public class Main { + public static void main (String args []) { + DefaultConfiguration defaultConfiguration = new DefaultConfiguration(); + defaultConfiguration.setSQLDialect(SQLDialect.POSTGRES); + Queries ddl = DSL.using(defaultConfiguration).ddl(DefaultCatalog.DEFAULT_CATALOG); + + for (Query query : ddl.queries()) { + System.out.println(query); + } + } +} +``` + +** ВНИМАНИЕ: ** + +- этим способом нельзя восстановить функции/процедуры БД + +см. также [Generating DDL from objects](https://www.jooq.org/doc/latest/manual/sql-building/ddl-statements/generating-ddl/) + +## Распределенный кэш (Hazelcast) + +В платформе подключен кэш. Он используется для подсчёта количества пользователей и кэширования подсистемы безопасности + +- `webbpm.cache.hazelcast.port` - входящий порт hazelcast. по дефолту 5701. +- Обязательное задать одно из двух следующих параметров + - `webbpm.cache.hazelcast.hosts` - список хостов серверов приложений. + webbpm.cache.hazelcast.hosts = hostname1,hostname2,hostname3 + - `webbpm.cache.hazelcast.kubernetes.service_name` - имя сервиса в среде kubernetes, + используемый для обнаружения других подов. Подходит как стратегия обнаружения, если используется + kubernetes +- `webbpm.cache.hazelcast.outbound_port_definitions` - исходящие порты hazelcast. по дефолту не задано, система сама выбирает свободные порты. Задать диапазон 5801 - 5820 +- `webbpm.cache.hazelcast.backup_count`. Нужны чтобы когда сервер выключается был доступен бекап. Если предполагается выключать несколько серверов за раз, то нужно увеличить. + Данный бекап делает копии синхронно с основной записью и операция записи ждет пока будет записана везде. Можно делать бекап асинхронно, настраивается через async_backup_count + подробнее про бекапы [Hazelcast IMDG Reference Manual](https://docs.hazelcast.org/docs/3.11/manual/html-single/index.html#backing-up-maps) . по дефолту 1 +- `webbpm.cache.hazelcast.async_backup_count` +- `webbpm.cache.hazelcast.public_address` +- `webbpm.cache.hazelcast.interfaces` + +Пример конфигурации: + +```xml + + ` +``` + +## Подключение компоненты адреса в режиме ГАР (Государственный адресный реестр) + +Необходимо задать параметры: + +- `gar.enable` - флаг, который включает/отключает сервис для работы с ГАР. Должен быть задан для работы компоненты в режиме ГАР. По умолчанию true, для отключения задать false. +- `gar.elastic.url.host` - хост на котором развернут elasticsearch. +- `gar.elastic.password` - пароль для аутентификации elasticsearch. + +Дополнительные параметры: + +- `gar.elastic.url.port` - порт на котором развернут elasticsearch. +- `gar.elastic.username` - логин для аутентификации elasticsearch. + +Пример конфигурации: + +```xml + + + +``` + +## Метрики + +Отчет + +Отчет собирается раз в 30 секунд по дефолту, меняется параметром `webbpm.metrics.report_period_ms`. +Все метрики идут за отчетный период, после сбора отчета они сбрасываются. + +Получить json со всеми метриками можно по урлу `backend/metrics/v1/all` - метрики будут с последнего собранного отчета, запрос не триггерит сбор отчета - это независимые операции. Отчет содержит только метрики по которым был совершен хотя бы один вызов в отчетный период. То есть, если какая-то операция не была совершена в отчетный период, то соответствующая ей метрика не попадет в отчет - ее не будет в json. + +### Значения метрик + +Все метрики идут за отчетный период, после сбора отчета они сбрасываются. +Нас в основном интересуют `callsCountSum`, `latencyMin`, `latencyAvg`, `latencyMax` + +- `callsCountSum` - количество завершенных вызовов +- `latencyMin` - минимальное время выполнения +- `latencyAvg` - среднее время выполнения +- `latencyMax` - максимальное время выполнения +- `activeCallsCountMax` - количество начатых, но еще не завершенных вызовов. +- `activeCallsLatencyMax` - длительность самого долгого еще не завершенного вызова + +### Текущие метрики приложения + +- Получение коннекта из пула + + - `webbpm.jbpm.db.connection.acquire` + - `webbpm.security.db.connection.acquire` + - `webbpm.db.connection.acquire` + +- Время с момента получения коннекта до возврата его в пул + + - `webbpm.jbpm.db.connection.in_use` + - `webbpm.security.db.connection.in_use` + - `webbpm.db.connection.in_use` + +- Время выполнения запроса на бд проекта + + - `webbpm.db.query.success.execution_time` + +- Время выполнения запроса на бд проекта + время получения коннекта из пула + + - `webbpm.db.query.success.full_time` + +- active-users-count.indicatorMax + +- active-users-count-ttl.indicatorMax + + # Количество пользователей + +- `webbpm.active_users_counter.enabled` - включает подсчет пользователей, нужно чтобы не запускать hazelcast на дев машинах. по дефолту false. На боевых серверах необходимо установить в true. + +- `webbpm.active_users_counter.max_time_between_operations_in_seconds` - время, которое пользователь считается активным после действия. по дефолту 15 минут. + +- `webbpm.active_users_counter.hazelcast.app_pool_size`. Запись в hazelcast производится асинхронно в отдельном пуле, не блокируя обработку http запроса. Это размер этого пула. по дефолту 4. Можно пока оставить 4 и последить за метриками pool.hazelcast-executor.queue.indicatorMax и pool.hazelcast-executor.activeThreads.indicatorMax. Если очередь будет сильно копиться, то увеличить. + +## Настройка логов + +Все настройки делаются в файле `standalone.xml`, если не указано иначе. + +### Общие настройки + +Платформа Web-bpm использует корневую категорию логирования `ru.cg.webbpm`, рекомендуется выставлять ее в уровень `info`. todo check prod config + +```xml + + + +``` + +При этом компоненты используемые в проекте могут использовать другие категории. + +### Параметры конфигурации + +**Рекомендованное использование:** всегда в `info`. + +Все параметры конфигурации загружаемые платформой web-bpm и пользовательским приложением через api webbpm логируются категорией `ru.cg.webbpm.modules.core.app_info.api.property.BaseProperty`. Она всегда должна быть выставлена в `info`. + +Пример вывода: + +``` +2017-12-04 16:02:19,074 INFO [ru.cg.webbpm.modules.core.app_info.api.property.BaseProperty] (EclipseGeminiBlueprintExtenderThread-1) System property [webbpm.active_users_counter.enabled] not set. Using default value [false] +2017-12-04 16:02:19,074 INFO [ru.cg.webbpm.modules.core.app_info.api.property.BaseProperty] (EclipseGeminiBlueprintExtenderThread-1) System property [webbpm.active_users_counter.hazelcast.hosts] set to [127.0.0.1] +``` + +### БД проекта + +#### Логирование запросов в бд security и бд проекта + +**Рекомендованное использование:** только при разработке. + +Использовать только при разработке. Категория `org.jooq.tools.LoggerListener` в `debug` уровень. + +```xml + + + +``` + +Пример вывода: + +``` +2017-12-04 18:21:10,391 DEBUG [org.jooq.tools.LoggerListener] (default task-19) Executing query : select "department"."department_name", "department"."department_id", "department"."parent_department_id", "department"."parent_department_id", "department"."department_id", (select (count(*) <> ?) from "public"."department" as "$$child" where "$$child"."parent_department_id" = "department"."department_id") as "$$hasChildren" from "public"."department" as "department" limit ? +2017-12-04 18:21:10,395 DEBUG [org.jooq.tools.LoggerListener] (default task-19) -> with bind values : select "department"."department_name", "department"."department_id", "department"."parent_department_id", "department"."parent_department_id", "department"."department_id", (select (count(*) <> 0) from "public"."department" as "$$child" where "$$child"."parent_department_id" = "department"."department_id") as "$$hasChildren" from "public"."department" as "department" limit 2147483647 +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) Fetched result : +-----------------+-------------+--------------------+--------------------+-------------+-------------+ +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |department_name |department_id|parent_department_id|parent_department_id|department_id|$$hasChildren| +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : +-----------------+-------------+--------------------+--------------------+-------------+-------------+ +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |Головной офис 1 | 8| {null}| {null}| 8|true | +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |Головной офис 2 | 9| {null}| {null}| 9|true | +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |Головной офис 3 | 10| {null}| {null}| 10|true | +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |Подразделение 1.1| 11| 8| 8| 11|true | +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |Подразделение 1.2| 12| 8| 8| 12|true | +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : +-----------------+-------------+--------------------+--------------------+-------------+-------------+ +2017-12-04 18:21:10,568 DEBUG [org.jooq.tools.LoggerListener] (default task-19) : |...55 record(s) truncated... +``` + +#### Логирование больших запросов + +**Рекомендованное использование:** всегда в `info`, с подобранными для проекта значениями. В проде значения должны быть проверены что они не вызывают излишнее логирование. + +1. Для отслеживания больших запросов в пользовательскую базу нужно задать параметры что считать большими запросами, логироваться будут запросы больше заданных показателей. + Чтобы логировать все запросы можно задать значение `-1`. Добавляются как property в раздел `system-properties`: + + ```xml + + + ``` + + В `info` режиме работают: + + - `webbpm.db.full_time_threshold` - полное время выполнения запроса в миллисекундах. Включает построение запроса библиотекой, выполнение и загрузку результатов. Пример сообщения
`2023-01-11 13:09:38,361 WARN [ru.cg.webbpm.modules.database.impl.analytics.PerformanceListener] (default task-33) Query full time threshold exceeded. full_time=[6565ms] execution_time=[6565ms] read_time=[0ms] results_count=[4] query=[]` + - `webbpm.db.results_count_threshold` - количество записей, которое вернул запрос. Пример сообщения
`2023-01-11 13:10:34,088 WARN [ru.cg.webbpm.modules.database.impl.analytics.PerformanceListener] (default task-41) Query results count threshold exceeded. results_count=[11177] query=[]` + + В `debug` режиме дополнительно к работающим в info: + + - `webbpm.db.execution_time_threshold` - время выполнения запроса + построения запроса библиотекой. + - `webbpm.db.result_read_time_threshold` - время чтения результатов запроса + +2. После задания настроек нужно настроить логирование - категория `ru.cg.webbpm.modules.database.impl.analytics.PerformanceListener`, уровень `info` или `debug`. + + ```xml + + + + ``` + +### JBPM + +#### Основные настройки + +JBPM использует 3 корневых категории логирования – `org.jbpm`, `org.drools`, `org.kie`. Все они должны быть выставлены в `warn`. + +```xml + + + + + + + + + +``` + +#### Логирование запросов в БД + +**Рекомендованное использование:** только при разработке. + +Для логирования sql запросов нужно включить категорию `org.hibernate.SQL` в `debug`: + +```xml + + + +``` + +Чтобы вывести параметры запросов и результат выполнения – категорию `org.hibernate.type.descriptor.sql` в `trace`: + +```xml + + + +``` + +Пример вывода: + +``` +18:21:06,938 DEBUG [org.hibernate.SQL] (default task-47) select names0_.Task_Names_Id as Task_Nam7_16_0_, names0_.id as id1_16_0_, names0_.id as id1_16_1_, names0_.language as language2_16_1_, names0_.shortText as shortTex3_16_1_, names0_.text as text4_16_1_ from I18NText names0_ where names0_.Task_Names_Id=? +18:21:06,939 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] (default task-47) binding parameter [1] as [BIGINT] - [180] +18:21:06,940 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] (default task-47) extracted value ([id1_16_1_] : [BIGINT]) - [539] +18:21:06,940 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] (default task-47) extracted value ([language2_16_1_] : [VARCHAR]) - [en-UK] +18:21:06,940 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] (default task-47) extracted value ([shortTex3_16_1_] : [VARCHAR]) - [Список записей] +18:21:06,941 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] (default task-47) extracted value ([text4_16_1_] : [CLOB]) - [Список записей] +18:21:06,941 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] (default task-47) extracted value ([Task_Nam7_16_0_] : [BIGINT]) - [180] +18:21:06,942 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] (default task-47) extracted value ([id1_16_0_] : [BIGINT]) - [539] +``` + +#### Дополнительные логи hibernate + +**Рекомендованное использование:** только при разработке в случае необходимости. + +1. Время выполнения запроса и количество результатов. + + Включаются категорией `org.hibernate.stat` в `debug`. При этом в hibernate должен быть включен сбор статистики. + Похоже что логируется только hql select запросов. + + ```xml + + + + ``` + + Пример вывода: + + ``` + 18:21:06,858 DEBUG [org.hibernate.stat.internal.ConcurrentStatisticsImpl] (default task-41) HHH000117: HQL: select t from AuditTaskImpl t where t.taskId = :taskId, time: 6ms, rows: 1 + ``` + +2. Показатели hibernate сессий + + Включаются категорией `org.hibernate.engine.internal.StatisticalLoggingSessionEventListener` в `info`. + При этом в hibernate должен быть включен сбор статистики. Тут может быть интересно количество запросов, флашей и общее время на все запросы сессией. + + Пример вывода: + + ``` + 2017-12-04 17:25:58,493 INFO [org.hibernate.engine.internal.StatisticalLoggingSessionEventListener] (default task-21) Session Metrics { + 63408365 nanoseconds spent acquiring 17 JDBC connections; + 521509 nanoseconds spent releasing 17 JDBC connections; + 65732621 nanoseconds spent preparing 17 JDBC statements; + 31471897 nanoseconds spent executing 17 JDBC statements; + 0 nanoseconds spent executing 0 JDBC batches; + 0 nanoseconds spent performing 0 L2C puts; + 0 nanoseconds spent performing 0 L2C hits; + 0 nanoseconds spent performing 0 L2C misses; + 297556 nanoseconds spent executing 1 flushes (flushing a total of 16 entities and 0 collections); + 430168 nanoseconds spent executing 1 partial-flushes (flushing a total of 16 entities and 16 collections) + } + ``` + +#### Логирование hbm2ddl + +**Рекомендованное использование:** всегда в `trace`. + +Должно быть включено всегда. Позволяет убедиться что hibernate не накатывал никакие миграции на базу. Этот функционал отключен у нас в коде. + +```xml + + + +``` + +# Описание параметров конфигурации клиентской части + +Свойства задаются в файле frontend/src/resources/app-config.json или frontend.war/src/resources/app-config.json + +## Общие + +- `dev_mode` - настройка задающая dev_mode для просмотра логов (true/false). При отсутствие оставляет значение при сборке +- `guard.confirm_exit` - выводить или нет диалог подтверждения, если обнаружены несохраненные данные в форме. Значение по умолчанию - false. +- `password.pattern` - Регулярное выражение для валидации пароля. +- `password_pattern_error` - Сообщение об ошибке валидации. +- `show.client.errors` - отвечает за отображение ошибок javascript-a пользователю (должна использоваться только в тестовых контурах) по умолчанию выключена +- 'available_task.single_fetch' - Отвечает за количество запросов available_task при завершении процесса. true - одиночный запрос, false/не указано - 10 запросов(старая реализация). + +## Вывод сообщений + +- `message_service_error_timeout` время в мс, в течение которого будет отображено сообщение об ошибке. Значение по умолчанию - таймаут не задан (окно не закрывается). +- `message_service_warning_timeout` время в мс, в течение которого будет отображено предупреждающее сообщение. Значение по умолчанию - таймаут не задан (окно не закрывается). +- `message_service_success_timeout` время в мс, в течение которого будет отображено сообщение об успехе. Значение по умолчанию - таймаут не задан (окно не закрывается). +- `message_service_info_timeout` время в мс, в течение которого будет отображено информационное сообщение. Значение по умолчанию - таймаут не задан (окно не закрывается). + +## Электронная подпись + +### Esmart + +- `electronic_sign.esmart_extension_url` - url для создания расширенной подписи. Подробная информация по ссылке [http://demo.esmart.ru](http://demo.esmart.ru) +- `electronic_sign.tsp_address` - адрес сервера службы штампов времени + +Пример: + +```text +"electronic_sign.esmart_extension_url": "http://dsig.ibsdemo.ru/ibs_dsig/ibs_dSig.asmx" +``` + +## Способ аутентификации + +- `auth_method` - способ аутентификации. Может принимать одно значение из списка: form, kerberos, cert_over_db, cert_over_ldap + +## Таймер очистки закешированных значений фильтров + +- `filter_cleanup_interval_hours` - время жизни закешированного значения фильтра в часах. По умолчанию - 720 часов, +- `filter_cleanup_check_period_minutes` - период проверки наличия просроченных закешированных значений в минутах. По умолчанию - 30 минут + +## Добавление версии приложения в URL при запросах к frontend-у + +В модуле frontend в src/resources/app-config.json добавлены 2 переменные + +- `"enable.version.in.url": "%enable.version.in.url%"` - подставлять ли версию в URL приложения. По умолчанию false. Если сборка произведена +- `"app.version": "%project.version%"` - версия приложения. + с профилем `enable-version-in-url`, то значение будет true. + +## Добавление Jivo чат в проект + +Свойства задаются в файле frontend/src/resources/app-config.json или frontend.war/src/resources/app-config.json + +- `jivo_chat_widget_api_url` - API url для работы Jivo чата. Необходимо заменить {WIDGET_ID} на реальный Widget API ID +- `jivo_chat_widget_enabled` - параметр отвечающий за активацию Jivo чата. По дефолту false, для активации задать true. + +Пример: + +```json + "jivo_chat_widget_api_url": "https://code.jivo.ru/widget/{WIDGET_ID}", + "jivo_chat_widget_enabled": false +``` + +# Прочее + +## Смена удалённого репозитория + +1. Смените адрес NPM registry в файле frontend.npmrc. Пример - registry=https://repo.example.com/repository/npm-all/ +2. Поменяйте ссылки в блоке , файла pom.xml diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 00000000..ba9a786b --- /dev/null +++ b/config/.gitignore @@ -0,0 +1,2 @@ +/*.ear +/*.jar diff --git a/config/Dockerfile b/config/Dockerfile new file mode 100644 index 00000000..3183f9e9 --- /dev/null +++ b/config/Dockerfile @@ -0,0 +1,41 @@ +FROM quay.io/wildfly/wildfly:26.1.3.Final-jdk17 +USER root +ARG POSTGRES_DRIVER_VERSION=42.7.3 +RUN yum -y install sudo && chown -R jboss: /opt/jboss/ + +USER jboss +WORKDIR $JBOSS_HOME +ENV JAVA_ARGS=-Xmx3g +RUN echo 'JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,address=*:8787,server=y,suspend=n"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=1g"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:+ExplicitGCInvokesConcurrent"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS -XX:+UnlockDiagnosticVMOptions -XX:G1SummarizeRSetStatsPeriod=1"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS -XX:-OmitStackTraceInFastThrow"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS -Dorg.jboss.logmanager.nocolor=true"' >> bin/standalone.conf && \ + echo 'JAVA_OPTS="$JAVA_OPTS $JAVA_ARGS"' >> bin/standalone.conf + +### Locale support ru_RU ### +USER root +RUN localedef -i ru_RU -f UTF-8 ru_RU.UTF-8 +RUN echo "LANG=\"ru_RU.UTF-8\"" > /etc/locale.conf +USER jboss +ENV LANG ru_RU.UTF-8 +ENV LANGUAGE ru_RU.UTF-8 +ENV LC_ALL ru_RU.UTF-8 +### Locale Support END ### + +COPY --chown=jboss entrypoint.sh . +COPY --chown=jboss patches patches +RUN (cd patches && wget https://repo.micord.ru/repository/libs-releases-local/org/jboss/ironjacamar/ironjacamar-core-impl/1.5.3.Final/ironjacamar-core-impl-1.5.3.Final.jar) +RUN (cd patches/system && wget https://repo1.maven.org/maven2/org/postgresql/postgresql/$POSTGRES_DRIVER_VERSION/postgresql-$POSTGRES_DRIVER_VERSION.jar -O postgresql-driver.jar) + +RUN chmod -R +x patches && \ + chmod +x entrypoint.sh && \ + ./entrypoint.sh && \ + rm -rf patches + +ENV SERVER_START=true +COPY --chown=jboss *.ear $JBOSS_HOME/standalone/deployments/ + +HEALTHCHECK --timeout=3s --start-period=3600s CMD curl --fail 127.0.0.1:8080/backend/version || exit 1 diff --git a/config/Dockerfile.build b/config/Dockerfile.build new file mode 100644 index 00000000..00cefb87 --- /dev/null +++ b/config/Dockerfile.build @@ -0,0 +1,10 @@ +FROM maven:3-jdk-11-slim +RUN apt update \ + && apt upgrade -y \ + && curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \ + && apt install -y git nodejs \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY ../ . +RUN mvn clean -T4C && mvn package -T4C diff --git a/config/Dockerfile.secdb b/config/Dockerfile.secdb new file mode 100644 index 00000000..f9eeaac3 --- /dev/null +++ b/config/Dockerfile.secdb @@ -0,0 +1,5 @@ +FROM postgres:11-alpine + +COPY secdb.sql . + +CMD psql "postgresql://${DB_SEC_USERNAME:-security}:${DB_SEC_PASSWORD:-secpassword}@${DB_SEC_HOST:-db}/${DB_SEC_NAME:-security}" < secdb.sql diff --git a/config/JDK_version b/config/JDK_version new file mode 100644 index 00000000..98d9bcb7 --- /dev/null +++ b/config/JDK_version @@ -0,0 +1 @@ +17 diff --git a/config/browsers.json b/config/browsers.json new file mode 100644 index 00000000..68edc1e1 --- /dev/null +++ b/config/browsers.json @@ -0,0 +1,31 @@ +{ + "firefox": { + "default": "112.0", + "versions": { + "112.0": { + "image": "selenoid/firefox:112.0", + "port": "4444", + "path": "/wd/hub", + "tmpfs": { + "/tmp": "size=512m" + }, + "env" : ["LANG=ru_RU.UTF-8", "LANGUAGE=ru:en", "LC_ALL=ru_RU.UTF-8"], + "shmSize": 1073741824 + } + } + }, + "chrome": { + "default": "121.0", + "versions": { + "121.0": { + "image": "selenoid/chrome:121.0", + "port": "4444", + "tmpfs": { + "/tmp": "size=512m" + }, + "env" : ["LANG=ru_RU.UTF-8", "LANGUAGE=ru:en", "LC_ALL=ru_RU.UTF-8"], + "shmSize": 1073741824 + } + } + } +} \ No newline at end of file diff --git a/config/create-databases.sh b/config/create-databases.sh new file mode 100644 index 00000000..7550486a --- /dev/null +++ b/config/create-databases.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +IFS=',' +for databases in $WILDFLY_DATABASES + do + IFS=':' read name user password <<< $databases + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE USER $user WITH PASSWORD '$password'; + CREATE DATABASE $name WITH OWNER $user; +EOSQL +done diff --git a/config/docker-compose.secdb.yaml b/config/docker-compose.secdb.yaml new file mode 100644 index 00000000..2f6671b4 --- /dev/null +++ b/config/docker-compose.secdb.yaml @@ -0,0 +1,12 @@ +version: "3" +services: + secdb: + build: + context: . + dockerfile: Dockerfile.secdb + depends_on: + - db + env_file: + - testing.env + labels: + - "tmp=true" diff --git a/config/docker-compose.selenoid.yaml b/config/docker-compose.selenoid.yaml new file mode 100644 index 00000000..f284e11b --- /dev/null +++ b/config/docker-compose.selenoid.yaml @@ -0,0 +1,28 @@ +version: "3" + services: + selenoid: + network_mode: bridge + image: aerokube/selenoid:latest-release + container_name: "selenoid" + environment: + - OVERRIDE_VIDEO_OUTPUT_DIR=$PWD/video + volumes: + - ".:/etc/selenoid/:ro" + - "/var/run/docker.sock:/var/run/docker.sock" + - "./logs:/opt/selenoid/logs/" + - "./video/:/opt/selenoid/video/" + command: -session-attempt-timeout 1m -retry-count 3 -limit ${LIMIT:-4} -save-all-logs -log-output-dir /opt/selenoid/logs -video-output-dir /opt/selenoid/video + ports: + - "4444:4444" + +selenoid-ui: +image: aerokube/selenoid-ui +container_name: "selenoid-ui" +network_mode: bridge +depends_on: + - selenoid +links: + - "selenoid:selenoid" +ports: + - "8080:8080" +command: --selenoid-uri http://selenoid:4444` \ No newline at end of file diff --git a/config/docker-compose.tests.yaml b/config/docker-compose.tests.yaml new file mode 100644 index 00000000..bd0a2ed9 --- /dev/null +++ b/config/docker-compose.tests.yaml @@ -0,0 +1,41 @@ +version: "3" +services: + db: + ports: + - 5432:5432 + environment: + - WILDFLY_DATABASES=app:app_user:apppassword,security:security_user:secpassword,jbpm:jbpm:jbpmpassword + + webbpm-app: + env_file: + - testing.env + + selenoid: + network_mode: bridge + image: aerokube/selenoid:latest-release + container_name: "selenoid" + environment: + - OVERRIDE_VIDEO_OUTPUT_DIR=$PWD/video + volumes: + - "$PWD:/etc/selenoid/:ro" + - "/var/run/docker.sock:/var/run/docker.sock" + - "$PWD/logs:/opt/selenoid/logs/" + - "$PWD/video/:/opt/selenoid/video/" + command: -session-attempt-timeout 1m -retry-count 3 -limit ${LIMIT:-4} -save-all-logs -log-output-dir /opt/selenoid/logs -video-output-dir /opt/selenoid/video + ports: + - "4444:4444" + depends_on: + - webbpm-app + + selenoid-ui: + image: aerokube/selenoid-ui + container_name: "selenoid-ui" + network_mode: bridge + depends_on: + - selenoid + links: + - "selenoid:selenoid" + ports: + - "8080:8080" + command: --selenoid-uri http://selenoid:4444 + diff --git a/config/docker-compose.yaml b/config/docker-compose.yaml new file mode 100644 index 00000000..34a29594 --- /dev/null +++ b/config/docker-compose.yaml @@ -0,0 +1,31 @@ +version: "3" +services: + db: + image: postgres:15 + volumes: + - ./create-databases.sh:/docker-entrypoint-initdb.d/create-databases.sh + command: + - "--max_prepared_transactions=100" + ports: + - 5432 + environment: + - WILDFLY_DATABASES=security:security_user:secpassword,jbpm:jbpm:jbpmpassword + - POSTGRES_PASSWORD=supersecretpassword + labels: + - "tmp=true" + + webbpm-app: + build: + context: . + dockerfile: Dockerfile + depends_on: + - db + ports: + - 9990 + - 8080 + - 8787 + - 12345 + env_file: + - testing.env + labels: + - "tmp=true" diff --git a/config/entrypoint.sh b/config/entrypoint.sh new file mode 100644 index 00000000..7178804e --- /dev/null +++ b/config/entrypoint.sh @@ -0,0 +1,48 @@ +#! /bin/bash +set -e + +function wait_for_server() { + until `$JBOSS_HOME/bin/jboss-cli.sh -c ":read-attribute(name=server-state)" 2> /dev/null | grep -q running`; do + echo "Retry ..." + done +} + +echo "dump environment variables to env.properties file" +printenv > env.properties + +echo "starting JBoss" +nohup $JBOSS_HOME/bin/standalone.sh --admin-only 1>&2 2>/dev/null & + +# running system patches +wait_for_server +$JBOSS_HOME/bin/jboss-cli.sh --connect --file="./patches/system/init.cli" --properties=env.properties +$JBOSS_HOME/bin/jboss-cli.sh --connect --file="./patches/system/add-postgresql-driver.cli" --properties=env.properties +bash "./patches/system/add-demo-user.sh" + +# running project patches +find ./patches/ -type f -name '*.cli' -not -path './patches/system/*' -print0 | +while IFS= read -r -d '' f; do + wait_for_server + echo "running $f" + $JBOSS_HOME/bin/jboss-cli.sh --connect --file="$f" --properties=env.properties +done; + +find ./patches/ -type f -name '*.sh' -not -path './patches/system/*' -print0 | +while IFS= read -r -d '' f; do + wait_for_server + echo "running $f" + bash "$f" +done + +echo "stopping JBoss" +wait_for_server +$JBOSS_HOME/bin/jboss-cli.sh --connect --command=:shutdown + +if ! [[ -z $SERVER_START ]]; then + echo "starting JBoss in standalone" + sleep 10 # without this occurs error "address already in use" + /opt/jboss/wildfly/bin/standalone.sh -c standalone.xml -b 0.0.0.0 -bmanagement 0.0.0.0 +else + echo "cleaning up JBoss logs" + rm -rf $JBOSS_HOME/standalone/log +fi diff --git a/config/patches/add-logger-database.cli b/config/patches/add-logger-database.cli new file mode 100644 index 00000000..83ee8938 --- /dev/null +++ b/config/patches/add-logger-database.cli @@ -0,0 +1,3 @@ +/subsystem=logging/logger=org.jooq.tools:add() +/subsystem=logging/logger=org.jooq.tools:write-attribute(name=level, value=DEBUG) +/subsystem=logging/logger=org.jooq.tools:add-handler(name=CONSOLE) \ No newline at end of file diff --git a/config/patches/default.cli b/config/patches/default.cli new file mode 100644 index 00000000..a965cb04 --- /dev/null +++ b/config/patches/default.cli @@ -0,0 +1,64 @@ +xa-data-source add \ + --name=AppDS \ + --enabled=true \ + --driver-name=postgresql \ + --jndi-name=java:/webbpm/AppDS \ + --user-name=${env.DB_APP_USERNAME:app_user} \ + --password=${env.DB_APP_PASSWORD:apppassword} \ + --use-ccm=true \ + --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker \ + --validate-on-match=false \ + --background-validation=true \ + --background-validation-millis=5000 \ + --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter \ + --statistics-enabled=true \ + --max-pool-size=50 \ + --query-timeout=300 \ + --xa-datasource-properties=ServerName=${env.DB_APP_HOST:db},PortNumber=${env.DB_APP_PORT:5432},DatabaseName=${env.DB_APP_NAME:app} + +xa-data-source add \ + --name=JBPMDS \ + --enabled=true \ + --driver-name=postgresql \ + --jndi-name=java:jboss/datasources/jbpmDS \ + --user-name=${env.DB_JBPM_USERNAME:jbpm} \ + --password=${env.DB_JBPM_PASSWORD:jbpmpassword} \ + --use-ccm=true \ + --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker \ + --validate-on-match=false \ + --background-validation=true \ + --background-validation-millis=5000 \ + --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter \ + --statistics-enabled=true \ + --max-pool-size=50 \ + --query-timeout=300 \ + --xa-datasource-properties=ServerName=${env.DB_JBPM_HOST:db},PortNumber=${env.DB_JBPM_PORT:5432},DatabaseName=${env.DB_JBPM_NAME:jbpm} + +xa-data-source add \ + --name=SECURITYDS \ + --enabled=true \ + --driver-name=postgresql \ + --jndi-name=java:/webbpm/security-ds \ + --user-name=${env.DB_SEC_USERNAME:security_user} \ + --password=${env.DB_SEC_PASSWORD:secpassword} \ + --max-pool-size=70 \ + --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker \ + --validate-on-match=false \ + --background-validation=true \ + --background-validation-millis=5000 \ + --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter \ + --statistics-enabled=true \ + --query-timeout=300 \ + --xa-datasource-properties=ServerName=${env.DB_SEC_HOST:db},PortNumber=${env.DB_SEC_PORT:5432},DatabaseName=${env.DB_SEC_NAME:app} + +/system-property=ldap.mapping.login.param:add(value=${env.WEBBPM_LDAP_LOGIN_ATTR:uid}) +/system-property=ldap.mapping.org.code.param:add(value=${env.WEBBPM_LDAP_ORGANIZATION_ATTR:ou}) +/system-property=jboss.as.management.blocking.timeout:add(value=900) +/subsystem=undertow/server=default-server/http-listener=default/:write-attribute(name=record-request-start-time,value=true) +/subsystem=undertow/server=default-server/host=default-host/setting=access-log:add(pattern=%h %t "%r" %s %b %D) +/system-property=webbpm.cache.hazelcast.hosts:add(value="127.0.0.1") +/system-property=webbpm.cache.hazelcast.outbound_port_definitions:add(value="5801-5820") +/system-property=webbpm.security.session.active.count:add(value="20") +/system-property=gar.enable:add(value=false) +/system-property=security.password.regex:add(value="^((?=(.*\\d){1,})(?=.*[a-zа-яё])(?=.*[A-ZА-ЯЁ]).{8,})$") +/system-property=fias.enable:add(value=false) \ No newline at end of file diff --git a/config/patches/system/add-demo-user.sh b/config/patches/system/add-demo-user.sh new file mode 100644 index 00000000..32b2986a --- /dev/null +++ b/config/patches/system/add-demo-user.sh @@ -0,0 +1 @@ +$JBOSS_HOME/bin/add-user.sh demo@example.com demo diff --git a/config/patches/system/add-postgresql-driver.cli b/config/patches/system/add-postgresql-driver.cli new file mode 100644 index 00000000..66566bf1 --- /dev/null +++ b/config/patches/system/add-postgresql-driver.cli @@ -0,0 +1,5 @@ +/subsystem=datasources/jdbc-driver=postgresql:add( \ + driver-name="postgresql", \ + driver-module-name="org.postgresql", \ + driver-xa-datasource-class-name="org.postgresql.xa.PGXADataSource" \ +) diff --git a/config/patches/system/init.cli b/config/patches/system/init.cli new file mode 100644 index 00000000..100c4d37 --- /dev/null +++ b/config/patches/system/init.cli @@ -0,0 +1,14 @@ +/system-property=webbpm.mode:add(value=production) +/system-property=authentication.method:add(value=form) +/subsystem=undertow/configuration=filter/gzip=gzipFilter:add() +/subsystem=undertow/server=default-server/host=default-host/\ + filter-ref=gzipFilter:add(predicate="exists('%{o,Content-Type}') and regex(pattern='(?:application/javascript|text/css|text/html|text/xml|application/json)(;.*)?', value=%{o,Content-Type}, full-match=true)") +/subsystem=undertow/configuration=filter/response-header=vary-header:add(header-name="Vary", header-value="Accept-Encoding") +/subsystem=undertow/server=default-server/host=default-host/filter-ref=vary-header:add() +/subsystem=undertow/server=default-server/http-listener=default/:write-attribute(name=max-post-size,value=${env.MAX_POST_SIZE:104857600}) +data-source remove --name=ExampleDS +/subsystem=ee/service=default-bindings:remove +/system-property=jboss.bind.address.management:add(value=0.0.0.0) +/system-property=jboss.bind.address:add(value=0.0.0.0) +module add --name=org.postgresql --resources=./patches/system/postgresql-driver.jar --dependencies=javax.api,javax.transaction.api +shutdown --restart diff --git a/config/secdb.sql b/config/secdb.sql new file mode 100644 index 00000000..46856aa8 --- /dev/null +++ b/config/secdb.sql @@ -0,0 +1,25 @@ +INSERT INTO security.user_group(user_group_id, name, created, updated, access_level_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), 'Example', '2019-07-04 11:12:01.263+03', '2019-08-26 16:40:11.953+03', (SELECT access_level_id FROM security.access_level where level = 0)) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_group_user_role(link_user_group_user_role_id, user_group_id, user_role_id) SELECT uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_group_id FROM security.user_group where name = 'Example'), user_role_id FROM security.user_role WHERE name = 'Example' ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_group_user_role(link_user_group_user_role_id, user_group_id, user_role_id) SELECT uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_group_id FROM security.user_group where name = 'Demo'), user_role_id FROM security.user_role WHERE name = 'Example' ON CONFLICT DO NOTHING; + +INSERT INTO security.user_group(user_group_id, name, created, updated, access_level_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), 'Example', '2019-07-04 11:12:01.263+03', '2019-08-26 16:40:11.953+03', (SELECT access_level_id FROM security.access_level where level = 0)) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_group_user_role(link_user_group_user_role_id, user_group_id, user_role_id) SELECT uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_group_id FROM security.user_group where name = 'Example'), user_role_id FROM security.user_role WHERE name = 'Example' ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_group_user_role(link_user_group_user_role_id, user_group_id, user_role_id) SELECT uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_group_id FROM security.user_group where name = 'Demo'), user_role_id FROM security.user_role WHERE name = 'Example' ON CONFLICT DO NOTHING; + +INSERT INTO security.user_account(user_account_id, email, first_name, last_name, middle_name, created, updated, locked, org_unit_id, username) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), 'qa_test@micord.ru', 'Пользователь', 'Основной-Тестовый', NULL, '2020-01-20 08:41:25.526+03', '2020-01-20 09:57:07.25+03', false, (SELECT id from security.org_unit where code = 'DEFAULT'), 'qa_test') ON CONFLICT DO NOTHING; +INSERT INTO security.simple_credentials( user_account_id, password, password_expires, password_updated) VALUES ((SELECT user_account_id from security.user_account where username = 'qa_test'), '$2a$11$uTzaeewDp2NehwjmfRe/euxP.cy.4ecl5nYA.E5TV9AGjjDKdJwI2', NULL, '2020-01-20 05:41:25.746') ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_test'), (SELECT user_group_id FROM security.user_group where name = 'Security Admin')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_test'), (SELECT user_group_id FROM security.user_group where name = 'BPMN Superuser')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_test'), (SELECT user_group_id FROM security.user_group where name = 'BPMN Admin')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_test'), (SELECT user_group_id FROM security.user_group where name = 'Example')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_test'), (SELECT user_group_id FROM security.user_group where name = 'BPMN User')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_test'), (SELECT user_group_id FROM security.user_group where name = 'Demo')) ON CONFLICT DO NOTHING; + +INSERT INTO security.user_account(user_account_id, email, first_name, last_name, middle_name, created, updated, locked, org_unit_id, username) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), 'qa_admin@micord.ru', 'qa_admin', 'qa_admin', NULL, '2020-01-20 08:41:25.526+03', '2020-01-20 09:57:07.25+03', false, (SELECT id from security.org_unit where code = 'DEFAULT'), 'qa_admin') ON CONFLICT DO NOTHING; +INSERT INTO security.simple_credentials( user_account_id, password, password_expires, password_updated) VALUES ((SELECT user_account_id from security.user_account where username = 'qa_admin'), '$2a$11$Zzmuga/xw1c1UnUe9nP9c.aqJ2O.OAI/AbmphOm2jurxMI0S6VAcy', NULL, '2020-01-20 05:41:25.746') ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_admin'), (SELECT user_group_id FROM security.user_group where name = 'Security Admin')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_admin'), (SELECT user_group_id FROM security.user_group where name = 'BPMN Superuser')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_admin'), (SELECT user_group_id FROM security.user_group where name = 'BPMN Admin')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_admin'), (SELECT user_group_id FROM security.user_group where name = 'Example')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_admin'), (SELECT user_group_id FROM security.user_group where name = 'BPMN User')) ON CONFLICT DO NOTHING; +INSERT INTO security.link_user_account_user_group(link_user_account_user_group_id, user_account_id, user_group_id) VALUES (uuid_in(md5(random()::text || now()::text)::cstring), (SELECT user_account_id from security.user_account where username = 'qa_admin'), (SELECT user_group_id FROM security.user_group where name = 'Demo')) ON CONFLICT DO NOTHING; diff --git a/config/testing.env b/config/testing.env new file mode 100644 index 00000000..889ea2e9 --- /dev/null +++ b/config/testing.env @@ -0,0 +1,37 @@ +# Security datasource +DB_SEC_USERNAME=security_user +DB_SEC_PASSWORD=secpassword +DB_SEC_HOST=db +DB_SEC_PORT=5432 +DB_SEC_NAME=security +# App datasource +DB_APP_USERNAME=app_user +DB_APP_PASSWORD=apppassword +DB_APP_HOST=db +DB_APP_PORT=5432 +DB_APP_NAME=app +# jBPM datasorce +DB_JBPM_USERNAME=jbpm +DB_JBPM_PASSWORD=jbpmpassword +DB_JBPM_HOST=db +DB_JBPM_PORT=5432 +DB_JBPM_NAME=jbpm +# Elasticsearch +WEBBPM_ELASTIC_HOST= +WEBBPM_ELASTIC_USER_PASSWORD= +# LDAP +WEBBPM_LDAP_URL= +WEBBPM_LDAP_BASE= +WEBBPM_LDAP_USER= +WEBBPM_LDAP_PASSWORD= +WEBBPM_LDAP_SYNC_ENABLED=false +WEBBPM_LDAP_SYNC_CRON=0 0 * * * * +WEBBPM_LDAP_LOGIN_ATTR=uid +WEBBPM_LDAP_ORGANIZATION_ATTR=ou +# JIRA +WEBBPM_JIRA_URL=https:/jira.com +WEBBPM_JIRA_USER=jiraUser +WEBBPM_JIRA_PASSWORD=jiraPass +TZ=Europe/Moscow +WEBBPM_TELEGRAM_BOT_TOKEN= +WEBBPM_TELEGRAM_BOT_NAME= \ No newline at end of file diff --git a/distribution/pom.xml b/distribution/pom.xml new file mode 100644 index 00000000..a7c0b9fc --- /dev/null +++ b/distribution/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + ervu_lkrp_ul + ervu_lkrp_ul + 1.0.0-SNAPSHOT + + + ervu_lkrp_ul.ervu_lkrp_ul + distribution + ear + + + /backend + + + + + ervu_lkrp_ul.ervu_lkrp_ul + backend + war + + + ervu_lkrp_ul.ervu_lkrp_ul + frontend + war + + + + + + + org.apache.maven.plugins + maven-ear-plugin + 3.2.0 + + + + ervu_lkrp_ul.ervu_lkrp_ul + frontend + / + frontend.war + + + ervu_lkrp_ul.ervu_lkrp_ul + backend + ${backendContext} + backend.war + + + + + + ${project.parent.artifactId} + + + + + enable-version-in-url + + /backend-${project.version} + + + + diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 00000000..8ed054b1 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1 @@ +registry=https://repo.micord.ru/repository/npm-all/ diff --git a/frontend/angular.json b/frontend/angular.json new file mode 100644 index 00000000..965697d3 --- /dev/null +++ b/frontend/angular.json @@ -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" +} diff --git a/frontend/bs-config.json b/frontend/bs-config.json new file mode 100644 index 00000000..448da143 --- /dev/null +++ b/frontend/bs-config.json @@ -0,0 +1,10 @@ +{ + "port": 8000, + "open": false, + "files": [ + "./**/*.{html,htm,css,js}" + ], + "server": { + "baseDir": "./" + } +} \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 00000000..a48550ae --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,23 @@ + + + + ervu_lkrp_ul + + + + + + + + + + + + +
+ + diff --git a/frontend/index.webpack.html b/frontend/index.webpack.html new file mode 100644 index 00000000..6286b76e --- /dev/null +++ b/frontend/index.webpack.html @@ -0,0 +1,11 @@ + + + + ervu_lkrp_ul + + + + +
+ + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 00000000..7fb71ad4 --- /dev/null +++ b/frontend/package.json @@ -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" + } +} diff --git a/frontend/pom.xml b/frontend/pom.xml new file mode 100644 index 00000000..f007ad0e --- /dev/null +++ b/frontend/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + ervu_lkrp_ul + ervu_lkrp_ul + 1.0.0-SNAPSHOT + + + ervu_lkrp_ul.ervu_lkrp_ul + frontend + war + + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + replace-version-in-url + process-resources + + replace + + + + + + ${basedir}/src/resources/app-config.json + ${basedir}/dist/src/resources/app-config.json + ${basedir}/src/resources/app.version + ${basedir}/dist/src/resources/app.version + + + + %project.version% + ${project.version} + + + %enable.version.in.url% + ${enable.version.in.url} + + + + + + + + + + dev + + true + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + ${basedir} + + src/resources/**/* + build_dev/**/* + node_modules/**/* + index.html + systemjs.config.js + + + + + + + + + + compile-ts + + false + + + + prod + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + ${basedir}/dist + + + + + + + + + enable-version-in-url + + true + + + + diff --git a/frontend/preview.html b/frontend/preview.html new file mode 100644 index 00000000..45e34e99 --- /dev/null +++ b/frontend/preview.html @@ -0,0 +1,23 @@ + + + + Web BPM + + + + + + + + + + + + +
+ + diff --git a/frontend/save.ts.metadata.js b/frontend/save.ts.metadata.js new file mode 100644 index 00000000..3e193d07 --- /dev/null +++ b/frontend/save.ts.metadata.js @@ -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)); \ No newline at end of file diff --git a/frontend/src/resources/app-config.json b/frontend/src/resources/app-config.json new file mode 100644 index 00000000..2ccb6a6a --- /dev/null +++ b/frontend/src/resources/app-config.json @@ -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 +} diff --git a/frontend/src/resources/app.version b/frontend/src/resources/app.version new file mode 100644 index 00000000..8b9b8b3d --- /dev/null +++ b/frontend/src/resources/app.version @@ -0,0 +1 @@ +%project.version% \ No newline at end of file diff --git a/frontend/src/resources/css/components-app.css b/frontend/src/resources/css/components-app.css new file mode 100644 index 00000000..abaf1703 --- /dev/null +++ b/frontend/src/resources/css/components-app.css @@ -0,0 +1,1569 @@ +@charset "utf-8"; + +.webbpm [id="page"] > div > div > [page-object] { + padding: 0 40px; +} + +.webbpm #dashboard { + padding: 15px 40px 0 40px; +} + +.webbpm .title { + font-size: 24px; + font-family: 'SegoeSB'; + padding: 10px 0 0 0; +} + +.webbpm .sub-header { + color: #fff; + font-weight: normal; + padding: 0 20px; + margin-top: 10px; + border-radius: 20px; + background: #4c5969; +} + +.webbpm .sub-header > .form-group { + padding: 10px 0; + margin-bottom: 0; +} + +.webbpm .sub-header>div > div:last-of-type { + font-size: 18px; + padding: 0; +} + +.webbpm .form-signup input.ng-invalid.ng-touched, +.webbpm .form-signup input.ng-invalid.ng-touched ~ .input-group-append > .input-group-text, +.webbpm .form-signup input.ng-invalid.ng-dirty, +.webbpm .form-signup input.ng-invalid.ng-dirty ~ .input-group-append > .input-group-text { + border-color: #1c92ea !important; +} + +.webbpm .form-signup .msg-alert { + color: #1c92ea; +} + +.webbpm input[type="text"], +.webbpm input[type="date"], +.webbpm input[type="email"], +.webbpm input[type="password"], +.webbpm input[type="number"], +.webbpm input.form-control, +.webbpm textarea, +.webbpm select, +.webbpm .selectize-control.single .selectize-input, +.webbpm .multi.plugin-remove_button .selectize-input { + color: #404954; + font-size: 16px; + min-height: 38px; + padding: 6px 8px; + border: 1px solid #e3e6ed; + border-radius: 8px; + background-color: #fff; + background-image: none; + box-shadow: none; +} + +.webbpm .wb-touched.wb-invalid .form-control, +.webbpm .wb-touched.wb-invalid .form-control:focus, +.webbpm .wb-touched.wb-invalid input.form-control, +.webbpm .wb-touched.wb-invalid input.form-control:focus, +.webbpm .wb-touched.wb-invalid .input-group-addon, +.webbpm .wb-touched.wb-invalid .input-group-addon:focus, +.webbpm .wb-touched.wb-invalid .selectize-input, +.webbpm .wb-touched.wb-invalid .selectize-input:focus { + border-color: #1c92ea !important; + box-shadow: none !important; +} + +.webbpm select option:checked { + color: #1c92ea; +} + +.webbpm .input-group input[name="password"], +.webbpm .input-group input[name="confirmPassword"], +.webbpm .date input[type="text"] { + border-right: 0 ; + border-top-right-radius: 0; + border-bottom-right-radius: 0 ; +} + +.webbpm .input-group input[name="password"] ~ .input-group-append > .input-group-text, +.webbpm .input-group input[name="confirmPassword"] ~ .input-group-append > .input-group-text, +.webbpm .date input ~ .input-group-addon { + color: #404954; + border-color: #e3e6ed; + border-radius: 8px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + background-color: transparent; +} + +.webbpm .input-group input[name="password"] ~ .input-group-append > .input-group-text, +.webbpm .input-group input[name="confirmPassword"] ~ .input-group-append > .input-group-text, +.webbpm .date input ~ .input-group-addon { + color: #404954; + border-color: #e3e6ed; + border-radius: 8px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + background-color: transparent; +} + +.webbpm international-phone-number .input-group-addon.flagInput { + border: 0; + background-color: transparent; +} + +.webbpm international-phone-number .input-group-addon.flagInput .btn { + border: 1px solid #e3e6ed; + border-right: 0; + border-radius: 10px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.webbpm international-phone-number .input-group-addon.flagInput ~ input { + border-right: 1px solid #e3e6ed; + border-radius: 10px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.webbpm .selectize-dropdown { + box-shadow: none; +} + +.webbpm .selectize-dropdown-content { + border: 1px solid #e3e6ed; + border-radius: 10px; + background-color: #fff; + box-shadow: 0 8px 12px rgb(77 72 91 / 5%), 0 6px 10px rgb(77 72 91 / 0%); +} + +.webbpm .selectize-dropdown .active { + color: #1c92ea; + background-color: transparent; +} + +.webbpm .form-group > label, +.webbpm .form-group > div { + display: inline-block; + vertical-align: top; +} +.webbpm .width-full .form-group > label, +.webbpm .width-full .form-group > div { + display: table-cell; +} + +.webbpm .form-control { + background-color: inherit; +} + +.webbpm .form-control[disabled], +.webbpm .form-control[readonly], +.webbpm .date .form-control[disabled] ~ .input-group-addon, +.webbpm .date .form-control[readonly] ~ .input-group-addon, +.webbpm fieldset[disabled] .form-control, +.webbpm .selectize-control .selectize-input.disabled { + background-color: #f5f7fa; +} + +.webbpm .selectize-control.form-control { + min-height: 38px; +} + +.webbpm .selectize-control.single .selectize-input > .item { + line-height: normal; +} + +.webbpm .selectize-control.single .selectize-input input { + line-height: normal !important; + border-radius: 0; +} + +.webbpm drop-down-button .dropdown-menu.show { + border: 1px solid #e3e6ed; + border-radius: 10px; + background-color: #fff; + box-shadow: 0 8px 12px rgb(77 72 91 / 5%), 0 6px 10px rgb(77 72 91 / 0%); +} + +.webbpm drop-down-button .dropdown-menu.show .btn { + padding-top: 4px; + padding-bottom: 4px; + border: 0; + box-shadow: none; +} + +.webbpm .selectize-dropdown-content { + font-size: 16px; +} + +.webbpm .selectize-dropdown .more_values { + color: #404954; + background-color: #f0f7fd; +} + +/*----------------- Button ---------------- */ +.webbpm .btn { + font-size: 16px; + font-family: 'SegoeSB'; + padding: 6px 14px; + margin-bottom: 0; + border-radius: 20px; +} + +.webbpm .btn:focus, +.webbpm .btn:active:focus { + box-shadow: none !important; +} + +.webbpm .btn-main { + border-color: transparent !important; + background: transparent !important; +} + +.webbpm .btn-main > * > .btn, +.webbpm .btn-primary { + color: #fff !important; + border-color: #1c92ea; + background: #1c92ea; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 6%); +} +.webbpm .btn-main > * > .btn:not(:disabled):not(.disabled):hover, +.webbpm .btn-main > * > .btn:not(:disabled):not(.disabled):active, +.webbpm .btn-primary:hover, +.webbpm .btn-primary:active { + border-color: #1b84d2; + background: #1b84d2; +} +.webbpm .btn-main > * > .btn:not(:disabled):not(.disabled):focus, +.webbpm .btn-primary:focus { + border-color: #1b84d2; + background: #1b84d2; + box-shadow: none; +} + +.webbpm .btn-secondary, +.webbpm .btn-default { + color: #404954; + border: 1px solid #e3e6ed; + background: #fff; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 6%); +} +.webbpm .btn-secondary:not(:disabled):not(.disabled):hover, +.webbpm .btn-secondary:not(:disabled):not(.disabled):active, +.webbpm .btn-default:not(:disabled):not(.disabled):hover, +.webbpm .btn-default:not(:disabled):not(.disabled):active { + color: #1c92ea; + border-color: #e3e6ed; + background-color: #fff; +} +.webbpm .btn-secondary:not(:disabled):not(.disabled):focus, +.webbpm .btn-default:not(:disabled):not(.disabled):focus { + border-color: #e3e6ed; + background-color: #fff; + box-shadow: none; +} + +.webbpm .btn.disabled, +.webbpm .btn:disabled { + color: #666; + border-color: #f3f3f3; + background-color: #f3f3f3; + box-shadow: none; +} + +.webbpm .btn.btn-primary.disabled, +.webbpm .btn.btn-primary:disabled, +.webbpm .btn-main .btn.disabled, +.webbpm .btn-main .btn:disabled { + color: rgba(255, 255, 255, 0.8) !important; + border-color: #1c92ea; + background-color: #1c92ea; + box-shadow: none; +} + +.webbpm ag-grid-angular .state-btn-edit, +.webbpm ag-grid-angular .state-btn-delete { + color: #1c92ea !important; + border: 0; + background: transparent; + box-shadow: none; +} + +.webbpm button.close { + outline: none; +} +/*---------------- end - Button -------------- */ + +/*----------------- Bread-crumb -------------- */ +.webbpm bread-crumb { + display: flex; + flex-direction: column; + max-width: 800px; + margin-top: 20px; +} + +.webbpm bread-crumb .status_bar { + position: relative; + display: flex; + flex-direction: row; + justify-content: stretch; +} + +.webbpm bread-crumb .status_bar .crumb-element { + color: #fff; + font-size: 14px; + padding: 4px 15px; + background-color: #4c5969; + border-radius: 20px; + z-index: 1; +} + +.webbpm bread-crumb .status_bar > div + div { + position: relative; + display: flex; + justify-content: end; + flex: 1; +} +.webbpm bread-crumb .status_bar > div + div::before { + content: ""; + position: absolute; + left: 0; + right: 0; + top: 50%; + height: 2px; + background-color: #4c5969; +} + +.webbpm bread-crumb .status_bar > div.active ~ div::before { + background-color: #e3e6ed; +} + +.webbpm bread-crumb .status_bar > div.active .crumb-element { + background-color: #1c92ea; +} + +.webbpm bread-crumb .status_bar > div.active ~ div .crumb-element { + color: #989fa8; + background-color: #e3e6ed; +} +/*--------------- end Bread-crumb ------------ */ + +.webbpm grid-v2-with-project-defined-sidebar, +.webbpm in-memory-route-param-filter-grid { + display: flex; + flex-direction: column; + flex: 1 1 auto; +} + +.webbpm .container-inside > div > div > grid, +.webbpm .container-inside > div > div > grid-v2-with-project-defined-sidebar{ + height: auto !important; + flex: 1 1 auto; + overflow: hidden; +} + +.webbpm ag-grid-angular .ag-body-viewport, +.webbpm ag-grid-angular .ag-row-odd, +.webbpm ag-grid-angular .ag-row-even { + background-color: transparent; +} + +.webbpm ag-grid-angular .ag-side-bar { + border: 0; + background-color: transparent; +} + +.webbpm ag-grid-angular .ag-header-cell { + line-height: 40px; + padding: 0 10px; + border: 0; +} +.webbpm ag-grid-angular .ag-header-cell::after, +.webbpm ag-grid-angular .ag-header-group-cell::after { + border-color: transparent; +} + +.webbpm ag-grid-angular { + font-family: 'Segoe'; + font-weight: normal; +} + +.webbpm ag-grid-angular .ag-header { + border: 0; + border-radius: 12px; + background: #f5f7fa; +} + +.webbpm ag-grid-angular .ag-header-row { + color: #404954; + font-family: 'SegoeSB'; + font-size: 14px; + font-weight: normal; +} + +.webbpm ag-grid-angular .grid-filter-hide .ag-floating-filter-body, +.webbpm ag-grid-angular .grid-filter-hide .ag-floating-filter-button { + display: none; +} + +.webbpm ag-grid-angular .ag-header-group-cell { + font-family: 'SegoeB'; + font-size: 16px; +} + +.webbpm ag-grid-angular .ag-header-cell>.ag-cell-label-container>.ag-header-cell-label>.ag-header-cell-text { + white-space: nowrap; +} + +.webbpm ag-grid-angular .ag-header .ag-floating-filter-body input { + font-family: 'Segoe'; + font-size: 14px; + min-height: 20px; + padding: 2px; + border: 1px solid #e3e6ed; + border-radius: 4px; + background: transparent; +} + +.webbpm ag-grid-angular .ag-root, +.webbpm ag-grid-angular .ag-header-row { + border: 0; +} + +.webbpm ag-grid-angular .ag-root { + position: static; +} + +.webbpm ag-grid-angular .ag-row { + border-color: #e3e6ed; +} +.webbpm ag-grid-angular .ag-row::before { + display: none; +} + +.webbpm ag-grid-angular .ag-row:not(.ag-row-first) { + border-width: 1px 0 0; + border-style: solid; +} +.webbpm ag-grid-angular .ag-row-last:not(.ag-row-first) { + border-width: 1px 0; +} + +.webbpm ag-grid-angular .ag-row.ag-row-odd, +.webbpm ag-grid-angular .ag-row.ag-row-selected { + background-color: inherit !important; +} + +.webbpm ag-grid-angular .ag-row.ag-row-selected .ag-cell { + color: #1c92ea; +} + +.webbpm ag-grid-angular .ag-body-viewport .ag-row.ag-row-hover { + border-radius: 10px; +} +.webbpm ag-grid-angular .ag-body-viewport .ag-row.ag-row-hover, +.webbpm ag-grid-angular .ag-body-viewport .ag-row.ag-row-hover .ag-cell { + background-color: #f9f9f9 !important; +} + +.webbpm ag-grid-angular .ag-body-viewport .ag-row.ag-row-hover:not(.ag-full-width-row)::before, +.webbpm ag-grid-angular .ag-body-viewport .ag-row.ag-row-hover.ag-full-width-row.ag-row-group::before { + background-color: transparent; +} + +.webbpm ag-grid-angular .ag-row.ag-row-hover, +.webbpm ag-grid-angular .ag-row.ag-row-hover + .ag-row { + border-color: transparent; +} + +.webbpm ag-grid-angular .ag-row .ag-cell { + display: inline-flex; + color: #404954; + font-size: 16px; + padding: 0 10px; + border: 1px solid transparent; +} + +.webbpm ag-grid-angular .ag-row .ag-cell.webbpm-checkbox-grid-column { + justify-content: center; +} + +.webbpm ag-grid-angular .ag-row .ag-cell .ag-cell-wrapper { + flex: 1 1 auto; + overflow: hidden; +} + +.webbpm ag-grid-angular .ag-row .ag-cell.ag-cell-last-left-pinned:not(.ag-cell-range-right):not(.ag-cell-range-single-cell) { + border-right: 0; +} + +.webbpm ag-grid-angular .ag-pinned-left-floating-bottom:not(.ag-hidden) .ag-row, +.webbpm ag-grid-angular .ag-pinned-left-floating-bottom:not(.ag-hidden) ~ .ag-floating-bottom-viewport { + border: 0; + border-radius: 12px; + background: #f0f7fd; + overflow-y: auto !important; +} + +.webbpm ag-grid-angular .ag-pinned-left-floating-bottom:not(.ag-hidden) .ag-row { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.webbpm ag-grid-angular .ag-pinned-left-floating-bottom:not(.ag-hidden) ~ .ag-floating-bottom-viewport { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.webbpm ag-grid-angular .ag-floating-bottom-container .ag-row { + border: 0; + border-radius: 12px; + background: #f0f7fd; + overflow-y: auto !important; +} + +.webbpm ag-grid-angular .ag-cell.ag-cell-focus.ag-cell-inline-editing { + height: 100%; +} + +.webbpm ag-grid-angular .ag-cell.ag-cell-focus.ag-cell-inline-editing input { + font-size: 16px; + padding: 0; + border: 0; + background: transparent; + box-shadow: none; +} + +.webbpm ag-grid-angular .ag-paging-panel { + color: #404954; + font-size: 14px; + border: 0; + border-radius: 12px; + background: #f5f7fa; +} + +.webbpm ag-grid-angular .ag-paging-panel .ag-paging-button { + display: flex; + width: 32px; + height: 32px; + border: 1px solid #e3e6ed; + border-radius: 16px; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 6%); +} +.webbpm ag-grid-angular .ag-paging-panel .ag-paging-button:not(:disabled) { + opacity: 1; +} + +.webbpm ag-grid-angular .ag-paging-panel .ag-paging-button > span { + display: flex; + align-items: center; + justify-content: center; + width: 100%; +} + +.webbpm ag-grid-angular .ag-paging-panel .ag-paging-button + .ag-paging-button { + margin-left: 8px; +} + +.webbpm ag-grid-angular .ag-paging-panel .ag-paging-button[ref="btPrevious"] { + margin-right: 16px; +} +.webbpm ag-grid-angular .ag-paging-panel .ag-paging-button[ref="btNext"]{ + margin-left: 16px; +} + +.webbpm ag-grid-angular .ag-floating-bottom { + border-top: 0; + overflow-y: auto !important; +} + +.webbpm ag-grid-angular .ag-floating-bottom-container .ag-cell { + display: flex; + align-items: center; +} + +.webbpm ag-grid-angular .ag-floating-bottom-container input { + color: #404954; + font-family: 'SegoeSB'; + font-size: 16px; + height: 20px; + padding: 0; + border: 0; + background: transparent; + box-shadow: none; +} + +.webbpm ag-grid-angular .ag-header-cell-menu-button .ag-icon-menu { + height: 25px; +} + +.webbpm .grid-setting-icon { + width: 16px; + height: 25px; + right: 12px; + padding: 0; + background-position: center; +} + +.webbpm .grid-mtm { + width: 46%; +} +.webbpm .grid-mtm + .many-to-many-move-buttons { + width: 8%; +} +.webbpm .grid-mtm + .many-to-many-move-buttons button.btn { + width: 100%; +} + +.webbpm ag-grid-angular .ag-overlay-loading-center { + border: 1px solid #e3e6ed; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 6%); +} + +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) > .ag-row.ag-row-selected, +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) > .ag-row.ag-row-hover { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) + .ag-center-cols-clipper > div > div > .ag-row.ag-row-selected, +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) + .ag-center-cols-clipper > div > div > .ag-row.ag-row-hover { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) > .ag-row.ag-row-first, +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) + .ag-center-cols-clipper > div > div > .ag-row.ag-row-first { + border-width: 1px 0 0 0; +} + + +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) > .ag-row:not(.ag-row-first), +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) + .ag-center-cols-clipper > div > div > .ag-row:not(.ag-row-first) { + border-width: 1px 0 0 0; +} + +.webbpm editable-grid ag-grid-angular .ag-floating-bottom .ag-row, +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) > .ag-row-hover, +.webbpm editable-grid ag-grid-angular .ag-pinned-left-cols-container:not(.ag-hidden) + .ag-center-cols-clipper > div > div > .ag-row-hover { + border-color: transparent; +} + +.webbpm editable-grid ag-grid-angular .ag-floating-bottom { + margin-top: 2px; + border: 0; + border-radius: 10px; + background-color: #f0f7fd; +} + +.webbpm editable-grid ag-grid-angular .ag-cell-wrapper { + height: 100%; +} + +.webbpm editable-grid ag-grid-angular .ag-row.ag-row-first { + border-style: solid; + border-width: 0; + border-color: transparent; +} + +.webbpm editable-grid ag-grid-angular .ag-cell input { + padding: 0; + border-color: transparent !important; + background-color: transparent; + box-shadow: none; +} +.webbpm editable-grid ag-grid-angular .ag-cell input:focus { + border-color: transparent !important; + box-shadow: none !important; +} +.webbpm editable-grid ag-grid-angular .ag-cell.ag-cell-value.wb-invalid { + border-color: transparent; +} + +.webbpm editable-grid ag-grid-angular .ag-cell-inline-editing { + background-color: transparent; +} + +.webbpm editable-grid ag-grid-angular check-box-grid-editor .form-group { + margin-bottom: 0; +} + +.webbpm editable-grid ag-grid-angular check-box-grid-editor .form-group > label { + display: flex; + align-items: center; + padding-top: 0; +} + +.webbpm editable-grid ag-grid-angular .ag-body-viewport .ag-row .ag-cell.ag-cell-inline-editing { + border: 0 !important; + background-color: #f0f7fd !important; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor { + padding: 0 !important; + border: 0 !important; + border-radius: 0 !important; + background-color: transparent; + box-shadow: none !important; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor .form-group, +.webbpm editable-grid ag-grid-angular number-field-grid-editor .form-group, +.webbpm editable-grid ag-grid-angular text-field-grid-editor .form-group { + margin-bottom: 0; +} + +.webbpm editable-grid ag-grid-angular text-area-grid-editor .form-control { + font-size: 16px; + min-height: 100px; + border: 0; + background-color: #f0f7fd; +} +.webbpm editable-grid ag-grid-angular text-area-grid-editor .form-control:focus { + box-shadow: none; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-many-grid-editor > div, +.webbpm editable-grid ag-grid-angular .ag-popup-editor auto-complete-grid-editor > div, +.webbpm editable-grid ag-grid-angular .ag-popup-editor combo-box-grid-editor > div, +.webbpm editable-grid ag-grid-angular .ag-popup-editor static-combo-box-grid-editor > div { + align-items: center; + margin-top: -1px; +} + +.webbpm editable-grid ag-grid-angular .ag-header .alarm, +.webbpm editable-grid ag-grid-angular .ag-overlay-loading-center, +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor label, +.webbpm editable-grid ag-grid-angular number-field-grid-editor label { + display: none; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor { + flex: 1; + height: 100%; + padding: 0; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .form-group { + display: flex; + height: 100%; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .component-float { + flex: 1; + width: auto; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .selectize-control, +.webbpm editable-grid ag-grid-angular .ag-popup-editor auto-complete-grid-editor .selectize-control, +.webbpm editable-grid ag-grid-angular .ag-popup-editor combo-box-grid-editor .selectize-control, +.webbpm editable-grid ag-grid-angular .ag-popup-editor static-combo-box-grid-editor .selectize-control, +.webbpm editable-grid ag-grid-angular .ag-popup-editor .date > div, +.webbpm editable-grid ag-grid-angular .ag-popup-editor .date .input-group { + height: 100%; + min-height: auto; + width: 100%; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .selectize-control.single .selectize-input, +.webbpm editable-grid ag-grid-angular .ag-popup-editor auto-complete-grid-editor .selectize-control.single .selectize-input, +.webbpm editable-grid ag-grid-angular .ag-popup-editor combo-box-grid-editor .selectize-control.single .selectize-input, +.webbpm editable-grid ag-grid-angular .ag-popup-editor static-combo-box-grid-editor .selectize-control.single .selectize-input, +.webbpm editable-grid ag-grid-angular .ag-popup-editor .date input, +.webbpm editable-grid ag-grid-angular .ag-popup-editor .date .input-group-addon { + display: inline-flex; + align-items: center; + height: 100%; + min-height: auto; + padding: 0 10px 1px 11px; + border: 0; + border-radius: 0; + background: #f0f7fd; + box-shadow: none; +} +.webbpm editable-grid ag-grid-angular .ag-popup-editor .date input { + padding-right: 0; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .selectize-control.single .selectize-input.dropdown-active { + padding-right: 3px; +} +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .selectize-control.single .selectize-input.dropdown-active::before, +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor .selectize-control.single .selectize-input::after { + display: none; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor input { + padding-left: 5px !important; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor + button-component { + height: 100%; + background: #f0f7fd; +} + +.webbpm editable-grid ag-grid-angular .ag-popup-editor one-to-may-auto-complete-grid-editor + button-component .btn { + color: #404954 !important; + white-space: nowrap; + padding: 0; + background-color: transparent !important; + border: 0 !important; + box-shadow: none !important; +} + +/*---------------- Table Header Menu --------------- */ +.webbpm ag-grid-angular .ag-menu { + padding: 6px; + border: 1px solid #f5f5f5; + border-radius: 12px; + background-color: #fff; + box-shadow: 0px 100px 80px rgb(0 0 0 / 7%), 0px 40px 30px rgb(0 0 0 / 5%), 0px 22px 18px rgb(0 0 0 / 4%), 0px 12.5px 10px rgb(0 0 0 / 4%), 0px 6.5px 5px rgb(0 0 0 / 3%), 0px 2.5px 2px rgb(0 0 0 / 2%); +} + +.webbpm ag-grid-angular .ag-tab-header { + border: 0; + background-color: #fff; +} + +.webbpm ag-grid-angular .ag-tab-header .ag-tab { + margin: 0 6px 6px 0; +} + +.webbpm ag-grid-angular .ag-tab-header .ag-tab.ag-tab-selected { + border-color: transparent; + border-radius: 6px; + background-color: #f2f2f2; +} + +.webbpm ag-grid-angular .ag-pinned-left-header { + border-right: 0; +} + +.webbpm ag-grid-angular .ag-header-checkbox-container .ag-icon-checkbox-checked, +.webbpm ag-grid-angular .ag-header-checkbox-container .ag-icon-checkbox-unchecked { + margin-top: 5px; +} + +.webbpm ag-grid-angular .ag-tab-body { + color: #404954; + font-size: 14px; + padding: 0; +} + +.webbpm ag-grid-angular .ag-menu > .ag-menu-list { + margin: 0px; +} + +.webbpm ag-grid-angular .ag-menu .ag-menu-option { + color: #404954; + font-size: 14px; +} + +.webbpm ag-grid-angular .ag-menu .ag-menu-option-active { + background-color: #f5f5f5; +} +.webbpm ag-grid-angular .ag-menu .ag-menu-option-active > *:first-child { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} +.webbpm ag-grid-angular .ag-menu .ag-menu-option-active > *:last-child { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} + +.webbpm ag-grid-angular .ag-menu .ag-menu-separator > span { + height: 9px; + background-image: url("data:image/svg+xml;utf8,"); +} + +/*---------------- Side-bar --------------- */ +.webbpm ag-grid-angular .ag-status-bar, +.webbpm ag-grid-angular .ag-side-bar, +.webbpm ag-grid-angular .ag-side-bar .ag-side-buttons, +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-select-panel { + border: 0; +} +.webbpm ag-grid-angular .ag-side-bar { + background: transparent; +} + +.webbpm ag-grid-angular .ag-side-bar .ag-tool-panel-wrapper { + width: 300px; + border: 1px solid #efefef; + border-radius: 12px 0 0 12px; + background: #fff; + box-shadow: -2px 0px 12px -6px rgb(0 0 0 / 20%); +} + +.webbpm ag-grid-angular .ag-side-bar .ag-tool-panel-wrapper, +.webbpm ag-grid-angular .ag-side-bar .ag-tool-panel-wrapper .ag-filter-toolpanel-header { + color: #404954; + font-size: 16px; + font-weight: normal; + background: transparent; +} + +.webbpm ag-grid-angular .ag-side-bar .ag-tool-panel-wrapper .ag-filter-panel .ag-filter-air { + padding: 0; + border: 0; +} + +.webbpm ag-grid-angular .ag-side-bar .ag-side-buttons { + width: 30px; + padding-top: 0; +} + +.webbpm ag-grid-angular .ag-side-bar .ag-side-buttons .ag-side-button { + padding-left: 4px; + +} + +.webbpm ag-grid-angular .ag-side-bar .ag-side-buttons .ag-side-button button { + color: #404954; + font-family: 'Segoe'; + font-size: 14px; + border: 0; + border-radius: 10px; +} +.webbpm ag-grid-angular .ag-side-bar .ag-side-buttons .ag-side-button button:hover { + color: #eb5757; +} + +.webbpm ag-grid-angular .ag-side-bar .ag-side-buttons .ag-side-button.ag-selected button { + color: #eb5757; + background: #f2f2f2; +} + +.webbpm ag-grid-angular .ag-primary-cols-header-panel, +.webbpm ag-grid-angular .ag-filter-toolpanel-search { + align-items: center; + height: 50px; + padding-top: 0; + border-bottom: 1px solid #efefef; +} + +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-panel-column-select { + border: 0; +} + +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-select-panel .ag-column-tool-panel-column-group, +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-select-panel .ag-column-tool-panel-column { + height: 30px; + line-height: 30px; + margin-left: 6px; + margin-right: 6px; + border-radius: 8px; +} +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-select-panel .ag-column-tool-panel-column-group:hover, +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-select-panel .ag-column-tool-panel-column:hover { + background: #f5f5f5; + cursor: pointer; +} + +.webbpm ag-grid-angular .ag-tool-panel-wrapper .ag-column-select-panel .ag-column-tool-panel-column-group { + font-family: 'SegoeSB'; +} + +.webbpm ag-grid-angular .ag-column-select-panel .ag-column-tool-panel-column.ag-toolpanel-add-group-indent { + padding-left: 44px; +} + +.webbpm ag-grid-angular .ag-icon-settings { + height: 10px; + width: 10px; + background: url('../img/icons/settings-sm.svg') center no-repeat; + opacity: 0.7; +} + +.webbpm ag-grid-angular .ag-side-bar .card { + border: 0; +} + +.webbpm ag-grid-angular .ag-side-bar .card-header { + text-align: left; + padding: 6px; + border: 0; + background: transparent; +} + +.webbpm ag-grid-angular .ag-side-bar .card-header i { + float: none; + font-size: 14px; + text-align: center; + width: 16px; + margin: 0; +} +.webbpm ag-grid-angular .ag-side-bar .card-header i.fa-chevron-right::before { + content: "\f105"; +} +.webbpm ag-grid-angular .ag-side-bar .card-header i.fa-chevron-down::before { + content: "\f107"; +} + +.webbpm ag-grid-angular .ag-side-bar .card-block, +.webbpm ag-grid-angular.ag-side-bar .ag-filter-toolpanel-body { + margin: 0px 6px; +} + +.webbpm ag-grid-angular .ag-side-bar .card-block button { + color: #404954; + min-height: 35px; + margin-bottom: 6px; + border: 1px solid #e3e6ed; + border-radius: 8px; + background: #fff; + box-shadow: 0px 2px 3px -2px rgb(0 0 0 / 20%); +} + +.webbpm ag-grid-angular .ag-side-bar .ag-filter-body-wrapper > *, +.webbpm ag-grid-angular .ag-filter .ag-filter-body-wrapper > * { + margin-bottom: 6px; +} + +.webbpm ag-grid-angular .ag-side-bar .ag-filter-toolpanel .ag-group, +.webbpm ag-grid-angular .ag-side-bar .ag-filter-toolpanel .ag-group .ag-filter-toolpanel-instance-filter { + margin: 0; + border: 0; +} + +.webbpm ag-grid-angular .ag-column-drop-horizontal.ag-column-drop { + padding: 0; + border: 0; + background-color: transparent; +} + +.webbpm ag-grid-angular .ag-column-drop-horizontal.ag-column-drop > div:not(.ag-column-drop-list) { + display: none; +} + +.webbpm ag-grid-angular .ag-popup .ag-select-list-item { + font-size: 16px; + background: transparent; +} +.webbpm ag-grid-angular .ag-popup .ag-select-list-item.ag-active-item { + color: #1c92ea; +} + +.webbpm ag-grid-angular .ag-popup .ag-popup-child:not(.ag-tooltip-custom) { + padding: 6px 4px; + border: 0; + border-radius: 10px; + background-color: #fff; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 12%); +} + +.webbpm ag-grid-angular .ag-column-drag { + margin: 0 0 0 6px; + background: url('../img/icons/dots-six-vertical.svg') 0 0 no-repeat; +} + +.webbpm sales-grid .grid { + display: flex; + flex-direction: column; +} + +.webbpm sales-grid .grid > ag-grid-angular { + flex: 1; + overflow: hidden; +} + +.webbpm sales-grid .grid .ag-paging-panel { + display: flex; + flex-direction: row; +} + +.webbpm sales-grid .grid .table-panel-legend { + display: flex; + flex: 1; + justify-content: right; + margin: 0 80px 0 0; +} + +.webbpm sales-grid .grid .table-panel-legend > span { + position: relative; + margin-left: 48px; +} +.webbpm sales-grid .grid .table-panel-legend > span::before { + content: ""; + position: absolute; + width: 16px; + height: 16px; + left: -24px; + top: 0; + border: 1px solid #fff; + border-radius: 8px; + box-shadow: 0px 1px 2px -1px rgb(0 0 0 / 45%); +} +.webbpm sales-grid .grid .table-panel-legend > .order-reject::before { + background: #feffee; +} +.webbpm sales-grid .grid .table-panel-legend > .order-return::before { + background: #fff1f1; +} +.webbpm sales-grid .grid .table-panel-legend > .order-self-redemption::before { + background: #edfcf8; +} + +.webbpm ag-grid-angular .ag-overlay-loading-center { + color: #404954; + font-size: 14px; + padding: 20px; + border: 1px solid #f5f5f5; + border-radius: 12px; + background: #fff; + box-shadow: 0px 100px 80px rgb(0 0 0 / 7%), 0px 40px 30px rgb(0 0 0 / 5%), 0px 22px 18px rgb(0 0 0 / 4%), 0px 12.5px 10px rgb(0 0 0 / 4%), 0px 6.5px 5px rgb(0 0 0 / 3%), 0px 2.5px 2px rgb(0 0 0 / 2%); +} + +.webbpm ag-grid-angular .ag-filter .ag-filter-header-container { + height: 32px; +} + +.webbpm ag-grid-angular .ag-filter .ag-filter-header-container:nth-child(2) { + margin-top: 8px; + border-bottom: 1px solid #e2e2e2; +} + +.webbpm ag-grid-angular .ag-virtual-list-item:hover { + border-radius: 8px; + background: #f5f5f5; +} + +.webbpm ag-grid-angular .ag-virtual-list-item:hover > label { + cursor: pointer; +} + +.webbpm ag-grid-angular .ag-filter label { + font-size: 16px; + width: auto; + margin: 0; +} + +.webbpm ag-grid-angular .ag-filter .ag-filter-checkbox { + margin-right: 6px; +} + +.webbpm .modal-content { + border: 0; + border-radius: 15px; + box-shadow: 0px 0px 30px 2px rgb(77 72 91 / 12%); + background-color: #fff; +} + +.webbpm .modal-header { + border-bottom: 1px solid #e3e6ed; +} + +/*-------------- Containers --------------- */ +.webbpm .block-content { + margin: 0; + border: 0; +} + +.webbpm .block-content, +.webbpm .block-content > div { + display: flex; + flex-direction: column; + height: 100%; +} + +.webbpm .nav-tabs { + width: max-content; + margin-bottom: 30px; + border: 0; + border-radius: 20px; + background-color: #e3e6ed; +} + +.webbpm .nav-tabs .nav-item { + margin-bottom: 0; +} + +.webbpm .nav-tabs .nav-link { + color: #404954; + padding: 6px 14px; + border: 0; + background-color: transparent; +} + +.webbpm .nav-tabs .nav-link:hover, +.webbpm .nav-tabs .nav-link:focus, +.webbpm .nav-tabs .nav-link:active { + background-color: transparent; +} + +.webbpm .nav-tabs .active .nav-link { + color: #fff; + border: 0; + border-radius: 20px; + background-color: #1c92ea; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 24%); +} + +.webbpm tab-container .nav-tabs ~ .tab-active > .active { + flex-direction: column; + display: flex; + padding: 0; + border: 0; +} +/*------------- end Containers ------------ */ + +/*----------------- Filters ---------------- */ +.webbpm collapsible-panel:not(.grid-setting-panel):not(.column-states-panel):not(.filter-states-panel) .card, +.webbpm .fieldset { + border: 0; + border-radius: 8px; + background-color: #f5f7fa; +} + +.webbpm collapsible-panel:not(.grid-setting-panel):not(.column-states-panel):not(.filter-states-panel) .card-header, +.webbpm .fieldset > legend { + font-family: 'SegoeSB'; + font-size: 18px; + padding: 15px 20px; + border: 0; + background: transparent; +} + +.webbpm collapsible-panel:not(.grid-setting-panel):not(.column-states-panel):not(.filter-states-panel) .card-header .font-bold { + margin-top: 10px; +} + +.webbpm collapsible-panel:not(.grid-setting-panel):not(.column-states-panel):not(.filter-states-panel) .card-block { + padding: 0 20px 15px 20px; +} + +.webbpm .fieldset > .legend + div { + padding: 55px 20px 15px 20px; +} + +.webbpm collapsible-panel:not(.grid-setting-panel):not(.column-states-panel):not(.filter-states-panel) .card i { + float: none; + font-size: 14px; + margin-right: 6px; + margin-left: 0; +} +/*-------------- end Filters -------------- */ + +/*-------------- Admin-pages -------------- */ +.webbpm [page-processinstance], +.webbpm #process-instance, +.webbpm #process-instance > vbox, +.webbpm #process-instance > vbox > div { + height: 100%; +} + +.webbpm [page-processinstance] #process-instance tab-container > div, +.webbpm [page-processinstance] #process-instance tab-container .tab-active { + display: flex !important; +} + +.webbpm [id^="user-management-"], +.webbpm #process-instance-list, +.webbpm #process-instance { + padding: 0 40px; +} + +.webbpm [id^="user-management-"] > vbox > div > *:not(.title), +.webbpm #process-instance-list > vbox > div > *:not(.title) { + margin-bottom: 20px; +} + +.webbpm [id^="user-management-"] .card label, +.webbpm #process-instance-list .card label { + display: inline-flex; +} + +.webbpm [id^="user-management-"] .card .width-full>div > label, +.webbpm #process-instance-list .card .width-full>div > label { + display: table-cell; + padding-top: 5px; +} + +.webbpm #user-management-authorities { + margin-top: 40px; +} + +.webbpm #process-instance-list static-column-grid { + margin-top: 20px; +} + +.webbpm #process-instance-list .radio-block .radio-content { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +.webbpm #process-instance-list .radio-block .radio-content label.control-label { + padding-top: 0; +} + +.webbpm #process-instance bpmn-back-button { + margin: 20px 0; +} +/*----------- end Admin-pages ------------- */ + +/*---------------- Calendar --------------- */ +.webbpm .calendar .months-container { + display: flex !important; + flex-wrap: wrap; + width: 100%; +} + +.webbpm .calendar .month-container { + float: none; + height: max-content; + margin-bottom: 20px; +} + +.webbpm .calendar .month .day { + font-size: 14px; +} + +.webbpm .calendar .month .day.day-start { + color: #fff; + border-radius: 4px; + background-color: #1c92ea !important; +} +.webbpm .calendar .month .day.day-start .day-content:hover { + color: #fff; +} + +.webbpm .calendar .month .day .day-content:hover { + color: #1c92ea; + background-color: transparent; +} + +.webbpm .date .bootstrap-datetimepicker-widget { + color: #404954; + width: min-content; + padding: 10px; + margin: 0; + border: 1px solid #e3e6ed; + + border-radius: 10px; + background-color: #fff; + box-shadow: 0 8px 12px rgb(77 72 91 / 5%), 0 6px 10px rgb(77 72 91 / 0%); +} +.webbpm .date .bootstrap-datetimepicker-widget.dropdown-menu.bottom:before, +.webbpm .date .bootstrap-datetimepicker-widget.dropdown-menu.bottom:after, +.webbpm .date .bootstrap-datetimepicker-widget table td.today:before { + display: none; +} + +.webbpm .date .bootstrap-datetimepicker-widget table thead tr:first-child th:hover { + color: #1c92ea; + background-color: transparent; +} + +.webbpm .bootstrap-datetimepicker-widget table td.day:hover, +.webbpm .bootstrap-datetimepicker-widget table td.hour:hover, +.webbpm .bootstrap-datetimepicker-widget table td.minute:hover, +.webbpm .bootstrap-datetimepicker-widget table td.second:hover { + color: #1c92ea; + background-color: transparent; +} + +.webbpm .date .bootstrap-datetimepicker-widget table td.today, +.webbpm .date .bootstrap-datetimepicker-widget table td.today:hover { + color: #fff !important; + background-color: #1c92ea; +} + +.webbpm .date .bootstrap-datetimepicker-widget table td.active { + color: #404954 !important; + text-shadow: none; + background-color: #f9f9f9; +} + +.webbpm .btn-group-viewdate.btn-group .btn { + width: 38px; + height: 38px; + border-radius: 19px; + cursor: pointer; +} + +.webbpm .btn-group-viewperiod.btn-group .btn { + border-radius: 20px; + cursor: pointer; +} + +.webbpm .btn-group-viewperiod.btn-group .btn.active { + color: #fff !important; + border-color: #1c92ea !important; + background: #1c92ea !important; + box-shadow: 0px 0px 6px 2px rgb(77 72 91 / 6%); +} +.webbpm .btn-group-viewperiod.btn-group .btn.active:hover, +.webbpm .btn-group-viewperiod.btn-group .btn.active:active { + border-color: #1b84d2 !important; + background: #1b84d2 !important; +} +.webbpm .btn-group-viewperiod.btn-group .btn.active:focus { + border-color: #1b84d2 !important; + background: #1b84d2 !important; + box-shadow: none; +} + +.webbpm .btn-group-viewdate.btn-group .btn + .btn, +.webbpm .btn-group-viewperiod.btn-group .btn + .btn { + margin-left: 10px; +} + +.webbpm .cal-week-view .cal-header.cal-weekend span { + color: #1c92ea; + opacity: 0.7; +} + +.webbpm .cal-month-view .cal-day-badge { + height: 20px; + background-color: #1c92ea; +} + +.webbpm .cal-month-view .cal-open-day-events { + color: #404954; + background-color: #e6ebee; + box-shadow: none; +} + +.webbpm .cal-week-view .cal-event { + color: #1c92ea; +} +/*------------- Calendar - end ------------ */ + +/*---------- Block-dialog - end ----------- */ +.webbpm .block-dialog { + position: relative; + max-width: 800px; + padding: 60px 60px 50px 110px !important; + margin: 30px auto; + border: 0; + border-radius: 15px; + background-color: #fff; + box-shadow: 0px 0px 30px 2px rgb(77 72 91 / 12%); +} +.webbpm .block-dialog::before { + content: "\f332"; + position: absolute; + color: #1c92ea; + font-family: bootstrap-icons !important; + font-size: 30px; + top: 60px; + left: 60px; +} + +.webbpm .block-dialog .title, +.webbpm .block-dialog .title > div > div:last-of-type{ + padding: 0; +} +/*---------- Block-dialog - end ----------- */ + +/*------------------ Левое меню -------------------*/ +/* +.webbpm .task-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: 0 !important; + padding: 0 !important; + overflow: hidden; +} + +.webbpm .task-left-menu { + float: none; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 240px; + padding: 0; + margin: 0; + background: #eef2f4; + z-index: 1; + overflow: auto; +} + +.webbpm .task-right-content { + float: none; + position: absolute; + top: 0; + bottom: 0; + left: 240px; + right: 0; + padding: 0; + margin: 0; + overflow: auto; +} + +.webbpm .task-left-menu > div { + padding: 15px 10px 15px 15px; +} + +.webbpm .task-left-menu auto-complete .component-float { + width: 100%; +} + +.webbpm .task-right-content > div { + padding: 0 15px 15px; +} + +.webbpm .menu-group-title { + font-weight: normal; +} + +.webbpm .menu-element > li > div:first-child { + font-size: 15px; + width: 100%; +} + +.webbpm .menu-element > li > div:last-child { + padding: 2px 6px; + border-radius: 6px; +} + +.webbpm .menu-group-title + menu { + padding-left: 0; +} + +.webbpm .menu-group .menu-group { + padding-left: 15px; +} +*/ +/*----------------- end - Левое меню -----------------*/ + +/*-------------------- Диаграмма Ганта --------------------*/ +.webbpm .gmajorheading { + background-color: #e0e5e9; +} +.webbpm .gminorheading { + background-color: #f1f5f7; +} +.webbpm .gminorheadingwkend { + background-color: #cce9f4; +} +.webbpm .gtaskcellwkend { + background-color: #f9fafb; +} + +.webbpm .gitemhighlight td { + background-color: #bcedfc !important; +} +/*----------------- end - Диаграмма Ганта -----------------*/ + +.webbpm .modal-backdrop.show { + filter: alpha(opacity=25); + opacity: 0.25; + background-color: #000; +} + +/*------------------ FilePreview ------------------*/ +.webbpm .files-view { + width: auto; +} +/*--------------- end - FilePreview ---------------*/ diff --git a/frontend/src/resources/css/inbox-app.css b/frontend/src/resources/css/inbox-app.css new file mode 100644 index 00000000..ac378d03 --- /dev/null +++ b/frontend/src/resources/css/inbox-app.css @@ -0,0 +1,351 @@ +@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: #1c92ea; +} +.webbpm a:hover, +.webbpm a:focus, +.webbpm a:active { + color: #1b84d2; + text-decoration: none; +} + +body.webbpm { + display: flex; + flex-direction: column; + color: #404954; + font-family: 'Segoe'; + background-color: #fff; +} + +.webbpm .container { + padding: 70px 0 0; +} + +body.webbpm [id="page"], +.webbpm .container .container-inside { + font-family: 'Segoe'; + font-size: 16px; +} + +.webbpm .logo { + height: auto; + width: auto; +} + +.webbpm .header { + display: flex; + font-family: 'Segoe'; + width: 100%; + height: auto; + min-height: 70px; + border-bottom: 1px solid #f5f7fa; + background: #fff; + box-shadow: 0px 15px 20px 0px rgb(0 0 0 / 4%); +} + +.webbpm .header-logo { + display: flex; + flex-direction: row; + align-items: center; + margin-left: 40px; +} + +.webbpm .header-logo .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; +} + +.webbpm .header > div > * { + position: relative; + display: flex; + align-items: center; +} + +.webbpm .header-menu > * { + margin-right: 20px; +} +.webbpm .header-menu > *:last-child { + margin-right: 0; +} + +.webbpm .header-menu .nav-link { + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 16px; + width: 30px; + height: 30px; + padding: 0; + border: 0; + border-radius: 15px; + background-color: #404954; + outline: transparent; +} +.webbpm .header-menu .nav-link::after { + display: none; +} + +.webbpm .header-menu .nav-link:hover, +.webbpm .header-menu .nav-link:focus, +.webbpm .header-menu .nav-link:active { + background-color: #1c92ea; +} + +.webbpm .header-menu .logout .user-info { + display: flex; + flex-direction: column; + color: #000; + padding: 4px 20px; + background: transparent; + cursor: default; +} + +.webbpm .header-menu .user-info > * { + display: flex; + padding-bottom: 10px; + margin: 0 0 10px 0; + border-bottom: 1px solid #f1f5f9; +} +.webbpm .header-menu .user-info > *:last-child { + margin-bottom: 0; +} + +.webbpm .header-menu .user-info .user-fio { + padding-bottom: 0; + margin-bottom: 0; + border-bottom: 0; +} + +.webbpm .header-menu .user-info .user-department { + color: #a0b1bc; + line-height: 1.2; +} + +.webbpm .header .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: #fff; + box-shadow: 0 8px 12px rgb(77 72 91 / 5%), 0 6px 10px rgb(77 72 91 / 0%); +} + +.webbpm .header .nav-item .dropdown-menu.show .dropdown-menu-inner { + max-height: calc(100vh - 140px); + overflow-y: auto; +} + +.webbpm .header process .dropdown-menu.show, +.webbpm .header admin-menu .dropdown-menu.show { + top: 49px !important; +} + +.webbpm .header .logout .dropdown-menu.show { + width: 300px; +} + +.webbpm .dropdown-menu-inner:hover { + background-color: transparent; +} + +.webbpm .dropdown-item { + padding: 4px 20px; +} + +.webbpm .dropdown-item:hover, +.webbpm .dropdown-item:focus, +.webbpm .dropdown-item:active { + color: #1c92ea; + background-color: transparent; + outline: transparent; +} + +.webbpm footer { + color: #404954; + font-size: 14px; + left: 0; + right: 0; + padding: 15px 40px; + border-top: 1px solid #e3e6ed; +} + +.webbpm footer a { + color: #404954; +} +.webbpm footer a:hover { + color: #1c92ea; +} + +/*-------------- Menu tasks -------------- */ +.webbpm .task-list-tree-panel { + padding: 0 40px; +} + +.webbpm .task-list-tree-panel .task-list-filter { + font-family: 'Segoe'; + box-shadow: none; +} + +.webbpm .task-list-workplace { + padding: 20px 40px 0 40px; +} + +.webbpm .task-list-tree-panel .task-list-filter li:first-of-type { + font-family: 'SegoeSB'; + font-weight: normal; + padding-left: 0; +} + +.webbpm .task-tbl .tr.task-ontime > .td.task::before, +.webbpm .task-tbl .tr.task-overdue > .td.task::before { + top: 24px; + background-color: #31c980; +} + +.webbpm .task-list-tree-panel .task-list-filter li.ontime label div { + background-color: #31c980; +} + +.webbpm .task-tbl .tr.task-overdue > .td.task::before, +.webbpm .task-list-tree-panel .task-list-filter li.overdue label div { + background-color: #1c92ea; +} + +.webbpm .task-tbl .thead { + display: table-header-group; + border: 0; + background: transparent; +} + +.webbpm .task-tbl .th { + color: #404954; + font-family: 'SegoeSB'; + font-size: 14px; + font-weight: normal; + padding: 9px 12px; + border: 0; + background: #f5f7fa; +} +.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: #404954; + font-size: 16px; + padding: 16px 12px; + border: 0; +} + +.webbpm .task-tbl .thead ~ .tr { + border-color: #e3e6ed; + 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 .form-signin, +.webbpm .form-signup, +.webbpm .confirm { + color: #404954; + 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: #fff; +} + +.webbpm .form-signin .row.title, +.webbpm .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 .form-signin .logo, +.webbpm .form-signup .logo, +.webbpm .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 -------------- */ \ No newline at end of file diff --git a/frontend/src/resources/css/structure.css b/frontend/src/resources/css/structure.css new file mode 100644 index 00000000..09854cbd --- /dev/null +++ b/frontend/src/resources/css/structure.css @@ -0,0 +1,859 @@ +* { + margin: 0; + padding: 0; +} + +*, *:before, *:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + + +body.webbpm { + color: #000; +} + +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 a { + color: #428bca; +} + +.webbpm .fl-left { + float: left; +} + +.webbpm .fl-right { + float: right; +} + +.webbpm .anchor { + float: none; + clear: both; +} + +.webbpm ul li, .webbpm ol li { + list-style: none; +} + +.webbpm h1, .webbpm h2, .webbpm h3 { + margin: 0; +} + +.webbpm h1 { + font-size: 2.33em; + font-weight: normal; +} + +.webbpm h2 { + font-size: 2em; + font-weight: normal; +} + +.webbpm h3 { + font-size: 1.5em; + font-weight: normal; +} + +.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: 14px; + 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; +} + +.webbpm .container [ng-include="taskPageFile"] { + position: relative; + min-height: 100%; + height: 100%; + overflow: auto; +} + +.webbpm .container .container-inside { + font-family: Arial; + font-size: 14px; + position: relative; + height: 100%; + overflow: auto; +} + +.webbpm footer { + position: absolute; + color: #000; + font-size: 12px; + bottom: 0; + left: 15px; + right: 15px; + height: 50px; + padding: 15px 0; + border-top: 1px solid #c1c1c1; + background: transparent; +} + +.webbpm footer span + span { + padding-left: 20px; +} + +/*--------- TOP MENU ----------*/ +.webbpm .header { + position: absolute; + color: #fff; + font-family: Corbel; + font-size: 14px; + top: 0; + left: 0; + right: 0; + height: 67px; + min-height: 67px; + line-height: normal; + border: 0; + padding: 0; + background: #b9c0ca; + z-index: 997; +} + +.webbpm .logo { + display: inline-block; + margin: 4px 10px; + float: left; +} + +.webbpm .logo, +.webbpm .logo a { + margin: 0; + width: 200px; + height: 67px; +} + +.webbpm .logo a { + position: absolute; + background: url("../img/logo.png") no-repeat 0 0; +} + +.webbpm .header .nav .nav-link { + color: #fff; + float: none; + display: block; + line-height: 60px; + padding: 0 15px 0 60px; + text-shadow: none; + cursor: pointer; +} + +.webbpm .header .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: 14px; +} + +.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: #fff; + 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: #fff; +} + +.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: 14px; + 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: #fff; + 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: #000; + 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: #fff; + 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: #fff; + border-right: 0; +} + +.webbpm .task-list > .task-list-tree-panel, +.webbpm .task-list > .task-list-workplace { + font-size: 14px; + 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); +} + +.webbpm .task-list-tree-panel .task-list-filter ul { + margin: 0; +} + +.webbpm .task-list-tree-panel .task-list-filter li { + display: inline-block; + padding: 12px 0px; + margin: 0; +} + +.webbpm .task-list-tree-panel .task-list-filter li:first-of-type { + font-size: 1.4em; + font-weight: bold; + width: 197px; + padding: 0 15px; +} + +.webbpm .task-list-tree-panel .task-list-filter li::before { + display: none; +} + +.webbpm .task-list-tree-panel .task-list-filter li label { + font-size: 1.3em; + font-weight: normal; +} + +.webbpm .task-list-tree-panel .task-list-filter li label div { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 10px; + margin-right: 8px; +} + +.webbpm .task-list-tree-panel .task-list-filter li.ontime label div { + background-color: #2da6a1; +} + +.webbpm .task-list-tree-panel .task-list-filter li.overdue label div { + background-color: #9c5d7a; +} + +.webbpm .task-list-tree-panel .task-list-filter li label input[type="radio"] { + float: none; + display: none; +} + +.webbpm .task-list-tree-panel .task-list-filter li label span { + float: none; + color: #000; + font-weight: normal; + text-align: left; + min-width: auto; + padding: 5px 15px; + border-radius: 0; + background-color: transparent; +} + +.task-list-tree-panel .task-list-filter li 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 .form-signin, +.webbpm .form-signup, +.webbpm .confirm { + color: #333; + width: 580px; + padding: 80px 100px; + margin: 20px auto; + border: 1px solid #b2b6b8; + background: #fff; +} + +.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 h1, +.webbpm .form-signin.esia h2 { + margin-left: 0; +} + +.webbpm .form-signin label, +.webbpm .form-signup label { + margin-bottom: 0; +} + +.webbpm .form-signin input { + width: 240px; + font-size: 14px; + display: inline-block; +} + +.webbpm .form-signin.esia input { + width: 160px; + margin-top: 40px; +} + +.webbpm .form-signin .row, +.webbpm .form-signup .row { + display: flex; + margin: 0 0 20px; +} + +.webbpm .registration-link, +.webbpm .login-link { + margin-right: 20px; + font-size: 14px; +} + +.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: 14px; +} + +.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 Поле телефона ------------ */ + diff --git a/frontend/src/resources/css/style.css b/frontend/src/resources/css/style.css new file mode 100644 index 00000000..d7d29b3e --- /dev/null +++ b/frontend/src/resources/css/style.css @@ -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"; \ No newline at end of file diff --git a/frontend/src/resources/fonts/Segoe.ttf b/frontend/src/resources/fonts/Segoe.ttf new file mode 100644 index 00000000..0f52cbd9 Binary files /dev/null and b/frontend/src/resources/fonts/Segoe.ttf differ diff --git a/frontend/src/resources/fonts/SegoeB.ttf b/frontend/src/resources/fonts/SegoeB.ttf new file mode 100644 index 00000000..ff196d60 Binary files /dev/null and b/frontend/src/resources/fonts/SegoeB.ttf differ diff --git a/frontend/src/resources/fonts/SegoeBL.ttf b/frontend/src/resources/fonts/SegoeBL.ttf new file mode 100644 index 00000000..b2966570 Binary files /dev/null and b/frontend/src/resources/fonts/SegoeBL.ttf differ diff --git a/frontend/src/resources/fonts/SegoeSB.ttf b/frontend/src/resources/fonts/SegoeSB.ttf new file mode 100644 index 00000000..90b39f72 Binary files /dev/null and b/frontend/src/resources/fonts/SegoeSB.ttf differ diff --git a/frontend/src/resources/fonts/SegoeSL.ttf b/frontend/src/resources/fonts/SegoeSL.ttf new file mode 100644 index 00000000..a276476a Binary files /dev/null and b/frontend/src/resources/fonts/SegoeSL.ttf differ diff --git a/frontend/src/resources/img/access_denied.png b/frontend/src/resources/img/access_denied.png new file mode 100644 index 00000000..463c9ca0 Binary files /dev/null and b/frontend/src/resources/img/access_denied.png differ diff --git a/frontend/src/resources/img/admin.png b/frontend/src/resources/img/admin.png new file mode 100644 index 00000000..875b0957 Binary files /dev/null and b/frontend/src/resources/img/admin.png differ diff --git a/frontend/src/resources/img/country-flags.jpg b/frontend/src/resources/img/country-flags.jpg new file mode 100644 index 00000000..84041f1c Binary files /dev/null and b/frontend/src/resources/img/country-flags.jpg differ diff --git a/frontend/src/resources/img/create.png b/frontend/src/resources/img/create.png new file mode 100644 index 00000000..6acd9d09 Binary files /dev/null and b/frontend/src/resources/img/create.png differ diff --git a/frontend/src/resources/img/logo-full.png b/frontend/src/resources/img/logo-full.png new file mode 100644 index 00000000..902c1b92 Binary files /dev/null and b/frontend/src/resources/img/logo-full.png differ diff --git a/frontend/src/resources/img/logo.png b/frontend/src/resources/img/logo.png new file mode 100644 index 00000000..d2d91c91 Binary files /dev/null and b/frontend/src/resources/img/logo.png differ diff --git a/frontend/src/resources/img/progress.gif b/frontend/src/resources/img/progress.gif new file mode 100644 index 00000000..1a3b525b Binary files /dev/null and b/frontend/src/resources/img/progress.gif differ diff --git a/frontend/src/resources/img/project.png b/frontend/src/resources/img/project.png new file mode 100644 index 00000000..1ff9bcf1 Binary files /dev/null and b/frontend/src/resources/img/project.png differ diff --git a/frontend/src/resources/img/tasks.png b/frontend/src/resources/img/tasks.png new file mode 100644 index 00000000..4a70c519 Binary files /dev/null and b/frontend/src/resources/img/tasks.png differ diff --git a/frontend/src/resources/template/app/component/access_denied.html b/frontend/src/resources/template/app/component/access_denied.html new file mode 100644 index 00000000..c7db7c20 --- /dev/null +++ b/frontend/src/resources/template/app/component/access_denied.html @@ -0,0 +1,4 @@ +
+
403
+
Доступ запрещен
+
\ No newline at end of file diff --git a/frontend/src/resources/template/app/component/admin_menu.html b/frontend/src/resources/template/app/component/admin_menu.html new file mode 100644 index 00000000..f187ba8a --- /dev/null +++ b/frontend/src/resources/template/app/component/admin_menu.html @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/frontend/src/resources/template/app/component/app_footer.html b/frontend/src/resources/template/app/component/app_footer.html new file mode 100644 index 00000000..041c050e --- /dev/null +++ b/frontend/src/resources/template/app/component/app_footer.html @@ -0,0 +1,10 @@ + diff --git a/frontend/src/resources/template/app/component/app_header.html b/frontend/src/resources/template/app/component/app_header.html new file mode 100644 index 00000000..2b65eabd --- /dev/null +++ b/frontend/src/resources/template/app/component/app_header.html @@ -0,0 +1,13 @@ + diff --git a/frontend/src/resources/template/app/component/application_version.html b/frontend/src/resources/template/app/component/application_version.html new file mode 100644 index 00000000..61be19b2 --- /dev/null +++ b/frontend/src/resources/template/app/component/application_version.html @@ -0,0 +1 @@ +Версия: {{applicationVersion}} \ No newline at end of file diff --git a/frontend/src/resources/template/app/component/confirm-user-email.html b/frontend/src/resources/template/app/component/confirm-user-email.html new file mode 100644 index 00000000..65a5d1d2 --- /dev/null +++ b/frontend/src/resources/template/app/component/confirm-user-email.html @@ -0,0 +1,27 @@ +
+ +
+
+

Подтверждение почты

+ +
+ Подтверждение... +
+
+
+ Адрес электронной почты успешно подтвержден +
+
+
+
{{ errorMessage }}
+
+ + +
+
+
diff --git a/frontend/src/resources/template/app/component/log_out.html b/frontend/src/resources/template/app/component/log_out.html new file mode 100644 index 00000000..9fc26834 --- /dev/null +++ b/frontend/src/resources/template/app/component/log_out.html @@ -0,0 +1,9 @@ + +
+ + +
\ No newline at end of file diff --git a/frontend/src/resources/template/app/component/login.html b/frontend/src/resources/template/app/component/login.html new file mode 100644 index 00000000..109c8b9a --- /dev/null +++ b/frontend/src/resources/template/app/component/login.html @@ -0,0 +1,51 @@ + diff --git a/frontend/src/resources/template/app/component/new_password.html b/frontend/src/resources/template/app/component/new_password.html new file mode 100644 index 00000000..4b315762 --- /dev/null +++ b/frontend/src/resources/template/app/component/new_password.html @@ -0,0 +1,103 @@ + diff --git a/frontend/src/resources/template/app/component/process_list.html b/frontend/src/resources/template/app/component/process_list.html new file mode 100644 index 00000000..a24adc3b --- /dev/null +++ b/frontend/src/resources/template/app/component/process_list.html @@ -0,0 +1,12 @@ +