SUPPORT-8783: add landing

This commit is contained in:
Eduard Tihomirov 2025-01-23 11:19:24 +03:00
parent 6617e42343
commit ecec2d595d
9 changed files with 268 additions and 70 deletions

View file

@ -1,10 +1,14 @@
<nav class="header" id="webbpm-header">
<div class="header-logo">
<div *ngIf="isLanding" class="header">
<div class="header-logo"></div>
<div class="header-title">Реестр повесток юридических лиц</div>
</div>
<div *ngIf="!isLanding" class="header-logo">
<div class="logo"><a routerLink="/"></a></div>
<div class="main-page"><a routerLink="/">Главная</a></div>
<div class="header-menu-left"><a routerLink="/filesentlog">Журнал взаимодействий</a></div>
</div>
<div class="header-menu">
<div *ngIf="!isLanding" class="header-menu">
<div ngbDropdown class="logout" log-out></div>
</div>
</nav>

View file

@ -0,0 +1,82 @@
<div class="list-group lk-what">
<div>
<div class="title short-text"><a href=".">Личный кабинет</a> для ведения воинского учета в организациях</div>
<div class="paragraph">
<div class="paragraph-left">
<div class="text icon-checklist">Кому доступен личный кабинет?</div>
<div class="short-text">Организациям, за ĸоторыми заĸреплена обязанность по осуществлению воинсĸого учета в соответствии с <a href="https://www.consultant.ru/document/cons_doc_LAW_18260/">ФЗ от 28.03.1998 No 53-ФЗ</a></div>
</div>
<div class="paragraph-right">
<div class="text icon-clock">Для чего нужен личный кабинет?</div>
<div class="short-text">Для своевременной и оперативной передачи сведений в военĸоматы в элеĸтронном виде</div>
</div>
</div>
</div>
</div>
<div class="list-group lk-access">
<div class="paragraph">
<div class="paragraph-left">
<div class="subtitle short-text">Как получить доступ к Личному кабинету?</div>
</div>
<div class="paragraph-right">
<div class="list">
<div class="esia short-text">Необходимо авторизоваться</div>
<div class="case short-text">Потребуется подтвержденная учетная запись организации</div>
<div class="user short-text">Доступ предоставляется тольĸо сотрудниĸу, наделенному соответствующими полномочиями (ролью) на ведения воинсĸого учета внутри организации</div>
</div>
<div class="btn-group">
<a href="." class="btn">Войти в Личный кабинет</a>
</div>
</div>
</div>
</div>
<div class="list-group lk-info">
<div class="subtitle">Какие виды сведений доступны для отправки?</div>
<div class="paragraph">
<div class="paragraph-left">
<div class="section-group">
<div class="icon-case">
Сведения о приеме на работу (увольнении), зачислении в образовательную организацию (отчислении)
<div class="muted">Срок передачи сведений: <span class="detailed">до 5 дней</span></div>
</div>
<div class="icon-shield">
Изменения сведений сотрудников, необходимых для ведения воинского учета
<div class="muted">Срок передачи сведений: <span class="detailed">до 5 дней</span></div>
</div>
<div class="icon-clip">
Сообщение о гражданах, не состоящих, но обязанных состоять на воинском учете
<div class="muted">Срок передачи сведений: <span class="detailed">до 3 дней</span></div>
</div>
</div>
</div>
<div class="paragraph-right">
<div class="section-group">
<div class="icon-pers">
Ежегодное предоставление списка граждан мужского пола, подлежащих первоначальной постановке на воинский учет в год достижения ими возраста 17 лет
<div class="muted">Срок передачи сведений: <span class="detailed">ежегодно, в срок до 1 ноября</span></div>
</div>
<div class="icon-building">
Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету
<div class="muted">Срок передачи сведений: <span class="detailed">ежегодно, по согласованию с военкоматом</span></div>
</div>
</div>
</div>
</div>
</div>
<div class="list-group lk-pass">
<div class="subtitle">Как передавать сведения?</div>
<div class="pass-list">
<div>Войдите в личный ĸабинет организации через Госуслуги</div>
<div>Подготовьте файл-csv с данными в соответствии с форматом в личном кабинете организации</div>
<div>Убедитесь, что все данные в файле введены ĸорреĸтно</div>
<div>Выберите необходимый вид сведений и загрузите файл</div>
<div>Следите за статусом приема</div>
</div>
</div>
<div class="list-group lk-msg">
<div class="msg-list">
<span class="info"></span>Если в файле будут ошибĸи, данные не будут приняты Реестром и выгрузĸу сведений придется повторить
</div>
</div>
<div class="list-group lk-docs">
</div>

View file

@ -1,8 +1,19 @@
<div class="wrapper">
<app-header *ngIf="headerVisible">
</app-header>
<div class="container">
<div *ngIf="isLanding" id="browser-check-info">
<div class="browser-check-content">
<div class="browser-check-text">
<p class="plain-text text-header">
Доступ к личному кабинету для юридических лиц осуществляется исключительно через браузеры Яндекс или Chromium gost.
</p>
<p class="plain-text">
Пожалуйста, попробуйте снова, выбрав один из этих браузеров.
</p>
</div>
</div>
</div>
<div class="container-inside" id="webbpm-angular-application-container">
<router-outlet></router-outlet>
<app-footer *ngIf="footerVisible"></app-footer>

View file

@ -3,6 +3,7 @@ import {RouterModule, Routes} from "@angular/router";
import {AccessDeniedComponent} from "./component/access-denied.component";
import {AuthGuard} from "../security/guard/auth.guard";
import {ConfirmExitGuard} from "@webbpm/base-package";
import {HomeLandingComponent} from "./component/home-landing.component";
const appRoutes: Routes = [
{
@ -19,7 +20,12 @@ const appRoutes: Routes = [
path: 'filesentlog',
loadChildren: 'generated-sources/page-filesentlog.module#PagefilesentlogModule',
canActivate: [AuthGuard],
}
},
{
path: 'home-landing',
component: HomeLandingComponent,
canActivate: [ConfirmExitGuard],
},
];

View file

@ -26,6 +26,7 @@ import {ErvuFileUpload} from "../../ervu/component/fileupload/ErvuFileUpload";
import {InMemoryStaticGrid} from "../../ervu/component/grid/InMemoryStaticGrid";
import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFileButton";
import {AuthenticationService} from "../security/authentication.service";
import {HomeLandingComponent} from "./component/home-landing.component";
registerLocaleData(localeRu);
export const DIRECTIVES = [
@ -37,7 +38,8 @@ export const DIRECTIVES = [
forwardRef(() => AppProgressIndicationComponent),
forwardRef(() => ErvuFileUpload),
forwardRef(() => ErvuDownloadFileButton),
forwardRef(() => InMemoryStaticGrid)
forwardRef(() => InMemoryStaticGrid),
forwardRef(() => HomeLandingComponent),
];
export function checkAuthentication(authService: AuthenticationService): () => Promise<any> {

View file

@ -1,4 +1,4 @@
import {ChangeDetectionStrategy, Component} from "@angular/core";
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from "@angular/core";
import {Router} from "@angular/router";
@Component({
@ -8,7 +8,14 @@ import {Router} from "@angular/router";
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppHeaderComponent {
public isLanding: boolean = false;
constructor(protected router: Router) {
constructor(protected router: Router,
private cd: ChangeDetectorRef) {
this.isLanding = window.location.href.endsWith('home-landing');
router.events.subscribe((event) => {
this.isLanding = window.location.href.endsWith('home-landing');
this.cd.markForCheck();
})
}
}

View file

@ -0,0 +1,49 @@
import {ChangeDetectorRef, Component, OnInit} from "@angular/core";
import {CadesHelper} from "@webbpm/base-package";
@Component({
moduleId: module.id,
selector: "home-landing",
templateUrl: "../../../../../src/resources/template/app/component/home_landing.html",
})
export class HomeLandingComponent implements OnInit {
constructor(private cd: ChangeDetectorRef) {
}
ngOnInit(): void {
this.checkCsp()
.then((cspCheckPassed) => {
if (this.checkBrowser() && cspCheckPassed) {
document.getElementById("browser-check-info").hidden = true;
}
});
this.cd.markForCheck();
}
private checkBrowser(): boolean {
const userAgent = navigator.userAgent;
return userAgent.indexOf("Chromium GOST") > -1 || userAgent.indexOf("YaBrowser") > -1;
}
/**
* Проверка работы CSP через CadesPlugin (асинхронно)
*/
private async checkCsp(): Promise<boolean> {
try {
if (!CadesHelper.plugin()) {
console.error("Ошибка: cadesplugin не найден!");
return false;
}
await CadesHelper.plugin();
const oAbout = await CadesHelper.plugin().CreateObjectAsync("CAdESCOM.About");
const cspVersion = await oAbout.CSPVersion();
let cspVersionStr = (await cspVersion.MajorVersion) + "." + (await cspVersion.MinorVersion) + "." + (await cspVersion.BuildVersion);
return !!cspVersionStr;
} catch (error) {
console.error("Ошибка при получении версии CSP:", error);
return false;
}
}
}

View file

@ -5,6 +5,7 @@ import {HttpClient, HttpParams} from "@angular/common/http";
import {MessagesService} from "@webbpm/base-package";
import {AuthenticationService} from "../authentication.service";
import {EsiaErrorDetail} from "../EsiaErrorDetail";
import {CadesHelper} from "@webbpm/base-package";
@Injectable({providedIn:'root'})
export abstract class AuthGuard implements CanActivate {
@ -21,68 +22,79 @@ export abstract class AuthGuard implements CanActivate {
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
let url = new URL(window.location.href);
if (!this.checkBrowser()) {
window.open(url.origin + url.pathname + "home.html", "_self");
this.router.navigateByUrl('/home-landing');
return false;
}
let hasAccess: Promise<boolean> | boolean = this.checkAccess();
return Promise.resolve(hasAccess).then((isAccess) => {
let params = new URLSearchParams(url.search);
let code = params.get('code');
let error = params.get('error');
let errorDescription = params.get('error_description');
if (isAccess) {
return true;
}
if (code || error) {
const params = new HttpParams().set('code', code).set('error', error);
this.httpClient.get("esia/auth",
{
params: params, responseType: 'text', observe: 'response', headers: {
"Error-intercept-skip": "true"
}
})
.toPromise()
.then(
(response) => {
window.open(url.origin + url.pathname, "_self");
})
.catch((reason) => {
let errorMessage = reason.error.messages != null
? reason.error.messages
: reason.error.replaceAll('\\', '');
if (error) {
errorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору';
let errorCode = this.extractCode(errorDescription);
if (errorCode) {
errorMessage = EsiaErrorDetail.getDescription(errorCode);
}
let consoleError = error + ', error description = ' + errorDescription;
this.messageService.error(errorMessage);
console.error(consoleError);
}
else {
this.messageService.error(errorMessage);
console.error(reason);
}
});
return false;
}
else {
return this.httpClient.get<string>("esia/url")
.toPromise()
.then(url => {
window.open(url, "_self");
return true;
}).catch((reason)=> {
console.error(reason);
return false;
});
}
}).catch((reason) => {
console.error(reason);
return false
});
return this.checkCsp()
.then((cspCheckPassed) => {
if (!cspCheckPassed) {
this.router.navigateByUrl('/home-landing');
return Promise.reject("Navigation on /home-landing")
}
return Promise.resolve(this.checkAccess());
}).then((isAccess) => {
let params = new URLSearchParams(url.search);
let code = params.get('code');
let error = params.get('error');
let errorDescription = params.get('error_description');
if (isAccess) {
return true;
}
if (code || error) {
const params = new HttpParams().set('code', code).set('error', error);
this.httpClient.get("esia/auth",
{
params: params,
responseType: 'text',
observe: 'response',
headers: {
"Error-intercept-skip": "true"
}
})
.toPromise()
.then(
(response) => {
window.open(url.origin + url.pathname, "_self");
})
.catch((reason) => {
let errorMessage = reason.error.messages != null
? reason.error.messages
: reason.error.replaceAll('\\', '');
if (error) {
errorMessage =
'Произошла неизвестная ошибка. Обратитесь к системному администратору';
let errorCode = this.extractCode(errorDescription);
if (errorCode) {
errorMessage = EsiaErrorDetail.getDescription(errorCode);
}
let consoleError = error + ', error description = ' + errorDescription;
this.messageService.error(errorMessage);
console.error(consoleError);
}
else {
this.messageService.error(errorMessage);
console.error(reason);
}
});
return false;
}
else {
return this.httpClient.get<string>("esia/url")
.toPromise()
.then(url => {
window.open(url, "_self");
return true;
}).catch((reason) => {
console.error(reason);
return false;
});
}
}).catch((reason) => {
if (reason !== 'Navigation on /home-landing') {
console.error(reason);
}
return false
});
}
private checkAccess(): boolean {
@ -99,4 +111,24 @@ export abstract class AuthGuard implements CanActivate {
const userAgent = navigator.userAgent;
return userAgent.indexOf("Chromium GOST") > -1 || userAgent.indexOf("YaBrowser") > -1;
}
}
/**
* Проверка работы CSP через CadesPlugin (асинхронно)
*/
private async checkCsp(): Promise<boolean> {
try {
if (!CadesHelper.plugin()) {
console.error("Ошибка: cadesplugin не найден!");
return false;
}
await CadesHelper.plugin();
const oAbout = await CadesHelper.plugin().CreateObjectAsync("CAdESCOM.About");
const cspVersion = await oAbout.CSPVersion();
let cspVersionStr = (await cspVersion.MajorVersion) + "." + (await cspVersion.MinorVersion) + "." + (await cspVersion.BuildVersion);
return !!cspVersionStr;
} catch (error) {
console.error("Ошибка при получении версии CSP:", error);
return false;
}
}
}

View file

@ -1,4 +1,4 @@
import {Component} from "@angular/core";
import {ChangeDetectorRef, Component} from "@angular/core";
import {
Event,
NavigationCancel,
@ -17,10 +17,14 @@ import {ProgressIndicationService} from "@webbpm/base-package";
export class WebbpmComponent {
public headerVisible: boolean = true;
public footerVisible: boolean = true;
public isLanding: boolean = false;
constructor(private router: Router,
private progressIndicationService: ProgressIndicationService) {
private progressIndicationService: ProgressIndicationService,
private cd: ChangeDetectorRef) {
this.isLanding = window.location.href.endsWith('home-landing');
router.events.subscribe((event: Event) => {
this.isLanding = window.location.href.endsWith('home-landing');
if (event instanceof NavigationStart) {
progressIndicationService.showProgressBar();
}
@ -29,6 +33,7 @@ export class WebbpmComponent {
|| event instanceof NavigationCancel) {
progressIndicationService.hideProgressBar();
}
this.cd.markForCheck();
})
}
}