update for digital certs listing

This commit is contained in:
merelendor 2023-12-25 09:22:37 +03:00
parent d57da482a6
commit a9f5f56cbb
10 changed files with 320 additions and 158 deletions

View File

@ -24,6 +24,7 @@ export default class DigitalCertificates extends React.Component
certificates_error: null,
certificate_selected: undefined,
signing: false,
show_all: false,
};
this.cryptopro = null;
}
@ -41,11 +42,17 @@ export default class DigitalCertificates extends React.Component
.then((info) =>
{
//console.log('Initialized', info);
// console.log('-'.repeat(20));
// for(let i in this.cryptopro)
// {
// console.log(i);
// }
// console.log('-'.repeat(20));
this.cryptopro.listCertificates()
.then((certificates_list) =>
{
//console.log({ certificates_list });
console.log({ certificates_list });
console.log("all certificates", JSON.stringify(certificates_list));
Sentry.captureMessage("Client digital sign step, list of all digital certificates");
@ -57,34 +64,42 @@ export default class DigitalCertificates extends React.Component
this.cryptopro.certificateInfo(certificate.id)
.then((cert) =>
{
const errors = [];
let apply = true;
console.log("certificate", JSON.stringify(cert));
console.log("company", JSON.stringify(company));
//console.log({ cert });
if(cert.IsValid)
{
if(cert.Subject['OGRNIP'] !== undefined && cert.Subject['OGRNIP'] !== null && cert.Subject['OGRNIP'] !== "")
{
if(cert.Subject['INN'] !== undefined && cert.Subject['INN'] !== null && cert.Subject['INN'] !== "")
{
if(main.inn === cert.Subject['INN'])
if(main.inn !== cert.Subject['INN'])
{
callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY") } ]);
}
else
{
callback(null, []);
errors.push('inn');
}
}
else
{
callback(null, []);
apply = false;
}
}
else
{
if(cert.Subject['INNLE'] !== undefined && cert.Subject['INNLE'] !== null && cert.Subject['INNLE'] !== "")
{
if(main.inn !== cert.Subject['INN'])
{
errors.push('inn');
}
}
else
{
apply = false;
}
}
let owner_valid = true;
const fields = [ "lastname", "firstname", "middlename", ];
const cert_owner = `${ cert.Subject['SN']} ${ cert.Subject['G']}`.toUpperCase();
@ -100,20 +115,19 @@ export default class DigitalCertificates extends React.Component
}
}
if(owner_valid)
if(!owner_valid)
{
callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY") } ]);
errors.push('name');
}
else
if(!cert.IsValid)
{
callback(null, []);
errors.push('date');
}
}
else
if(apply)
{
callback(null, []);
}
}
callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY"), errors } ]);
}
else
{
@ -134,11 +148,31 @@ export default class DigitalCertificates extends React.Component
if(certificates.length === 0)
{
this.setState({ loading: false, certificates_error: "ISSUED" });
this.setState({ loading: false, certificates_error: "INVALID" });
}
else
{
this.setState({ loading: false, certificates });
const list = [];
let valid_certificate_exists = false;
for(let i in certificates) { if(certificates[i].errors.length === 0) { list.push(certificates[i]); valid_certificate_exists = true; } }
for(let i in certificates) { if(certificates[i].errors.length !== 0) { list.push(certificates[i]); } }
const update = { loading: false, certificates: list, certificates_error: null };
if(list[0] !== undefined)
{
if(list[0].errors.length === 0)
{
update.certificate_selected = list[0];
}
}
if(!valid_certificate_exists)
{
update.certificates_error = "INVALID";
}
this.setState(update);
}
});
})
@ -201,7 +235,7 @@ export default class DigitalCertificates extends React.Component
render()
{
const { company, main, head_person } = this.props;
const { loading, certificates, certificates_error, certificate_selected, signing } = this.state;
const { loading, certificates, certificates_error, certificate_selected, signing, show_all } = this.state;
//console.log("render()", { certificates });
if(loading)
@ -214,18 +248,41 @@ export default class DigitalCertificates extends React.Component
}
else
{
if(certificates_error === null)
{
console.log({ certificates });
return (
<React.Fragment>
<>
{ (certificates_error !== null) &&
<>
{ certificates_error === "NOT_INSTALLED" || certificates_error === "CERTIFICATES" && (
<FormMessage type="moderate" title="Внимание!" message={ <>
Плагин КриптоПРО не установлен или не активирован. Посмотите <a style={{ color: "#fff", textDecoration: "underline", }} href={ `${ process.env.NEXT_PUBLIC_MAIN_SITE }/upload/docs/anketa-sign.pdf` }>инструкцию</a> как установить, активировать и проверить работу плагина КриптоПро.<br/>
</> }/>
) }
{ certificates_error === "INVALID" && (
<FormMessage type="moderate" title="Внимание!" message={ <>
Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН: <br/>
{ main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }. <br/>
Такой сертификат не найден!<br/><br/>
Если Вы уверены в наличии такого сертификата:
<ol style={{ listStyle: "decimal", padding: "revert" }}>
<li>Проверьте, вставлен ли ключ с этим сертификатом.</li>
<li>Проверьте возможность подписания этим сертификатом с помощью средства <a style={{ color: "#fff", textDecoration: "underline", }} href="https://cryptopro.ru/sites/default/files/products/cades/demopage/cades_bes_sample.html">проверки КриптоПро</a>.</li>
<li>Возможно, сертификат нужно установить. <a style={{ color: "#fff", textDecoration: "underline", }} href={ `${ process.env.NEXT_PUBLIC_MAIN_SITE }/upload/docs/anketa-sign.pdf` }>Воспользуйтесь инструкцией</a>.</li>
</ol>
</> }/>
) }
{ certificates_error === "MISMATCH" && (
<FormMessage type="moderate" title="Внимание!" message={ <>Подписант не соответствует указанному подписанту в анкете.</> }/>
) }
</>
}
{ certificates.length > 0 && (
<>
<div className="feed">
<p>Выберите подписанта</p>
<div className="feed_list">
{ certificates.map((certificate, index) => (
<div className="form_field checkbox" key={ index }>
{ certificates.slice(0, show_all ? certificates.length : 2).map((certificate, index) => (
<div className={`form_field checkbox digital_certificates_status ${ certificate.errors.length > 0 ? "error" : "" } ${ certificates.length > 1 && !show_all && index === 1 ? "show_all_holder" : ""}`} key={ index }>
<input type="radio"
hidden=""
id={ `certificate_${ certificate.id }` }
@ -252,8 +309,20 @@ export default class DigitalCertificates extends React.Component
<span>Подпись действительна до { certificate.valid_to_date }</span>
</p>
</div>
{ certificate.errors.length > 0 && (
<div>
<p className="item_title">&nbsp;</p>
{ certificate.errors.indexOf("name") > -1 ? <p className="item_desc" style={{ color: "#A8026B" }}>Не соответствует ФИО</p> : <p className="item_desc">&nbsp;</p>}
{ certificate.errors.indexOf("inn") > -1 ? <p className="item_desc" style={{ color: "#A8026B" }}>Не соответствует ИНН</p> : <p className="item_desc">&nbsp;</p>}
<p className="item_desc">&nbsp;</p>
{ certificate.errors.indexOf("date") > -1 ? <p className="item_desc" style={{ color: "#A8026B" }}>Срок действия закончился</p> : <p className="item_desc">&nbsp;</p>}
</div>
) }
</div>
</label>
{ certificates.length > 1 && !show_all && index === 1 && (
<div className="show_all" onClick={ () => this.setState({ show_all: true }) }><span>Посмотреть все</span></div>
) }
</div>
)) }
@ -264,7 +333,7 @@ export default class DigitalCertificates extends React.Component
<button
type="submit"
className="button button-blue"
disabled={ certificate_selected !== undefined ? false : true }
disabled={ certificate_selected !== null ? true : certificate_selected !== undefined ? false : true }
onClick={ this._sign }
style={{ minWidth: "250px" }}
>
@ -273,29 +342,10 @@ export default class DigitalCertificates extends React.Component
) : "Подписать выбранной подписью" }
</button>
</div>
</React.Fragment>
)
}
else
{
return (
<React.Fragment>
{ certificates_error === "NOT_INSTALLED" && (
<FormMessage type="moderate" title="Внимание" message={ <>Плагин КриптоПРО не установлен, <Link href="https://cryptopro.ru/products/cades/plugin"><a target="_blank" rel="noopener noreferrer" style={{ color: "white", textDecoration: "underline" }}>посмотрите инструкцию</a></Link> как установить плагин КриптоПРО.</> }/>
</>
) }
{ certificates_error === "CERTIFICATES" && (
<FormMessage type="moderate" title="Ошибка" message={ <>Плагин КриптоПРО не активирован, пожалуйста, обновите страницу и подтвердите разрешение для сайта на доступ к списку сертификатов.</> }/>
) }
{ certificates_error === "ISSUED" && (
<FormMessage type="moderate" title="Ошибка" message={ <>Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН: { main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }. Такой сертификат не найден.</> }/>
) }
{ certificates_error === "MISMATCH" && (
<FormMessage type="moderate" title="Ошибка" message={ <>Подписант не соответствует указанному подписанту в анкете.</> }/>
) }
</React.Fragment>
</>
)
}
}
}
}

View File

@ -359,7 +359,7 @@ class ShareholderForm extends React.Component
) }
<div className="form_field">
<label>Доля в уставном капитале (%) <sup className="required_label">*</sup></label>
<label>Доля в уставном капитале (%) <sup className="required_label">*</sup><small>не менее 25%</small></label>
{/*}
<InputMask
className={ errors.indexOf("founder_part") > -1 ? "error" : "" }
@ -791,6 +791,11 @@ class Form_4_Shareholders extends QuestionnaireForm
if(v !== "" && v !== null && !isNaN(parseFloat(v)))
{
total_parts = total_parts + parseFloat(v)
if(parseFloat(v) < 25)
{
errors[f].push(`founder_part`);
}
}
else
{

View File

@ -114,7 +114,6 @@ export default class QuestionnaireForm extends React.Component
{
//console.log("QuestionnaireForm", "_handle_onCheckboxFieldChange", { name, value });
const update = { ...this.state };
_set(update, name, value);
this._updateQuestionnaire(update);

View File

@ -1663,3 +1663,44 @@
.deal_offers_table {
width: 100%;
}
.digital_certificates_status {
position: relative;
}
.digital_certificates_status.error {
border: none !important;
}
.digital_certificates_status.error label {
pointer-events: none;
cursor: unset;
}
.digital_certificates_status.error label::before {
border: none !important;
width: 16px;
height: 16px;
background: url("/assets/images/icons/delete_red.svg") no-repeat center;
z-index: 2;
transform: scale(1.3);
margin-right: 12px;
}
.digital_certificates_status.show_all_holder {
margin-bottom: 40px !important;
}
.digital_certificates_status .show_all {
position: absolute;
left: 0px;
top: 10px;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: flex-start;
background: linear-gradient(0deg, #ffffff 0%, rgba(255, 255, 255, 0) 100%);
}
.digital_certificates_status .show_all span {
color: var(--blue);
text-decoration: underline;
cursor: pointer;
}
.digital_certificates_status .show_all span:hover {
text-decoration: none;
}

View File

@ -1948,3 +1948,50 @@
.deal_offers_table {
width: 100%;
}
.digital_certificates_status {
position: relative;
&.error {
border: none !important;
label {
pointer-events: none;
cursor: unset;
&::before {
border: none !important;
width: 16px;
height: 16px;
background: url("/assets/images/icons/delete_red.svg") no-repeat center;
z-index: 2;
transform: scale(1.3);
margin-right: 12px;
}
}
}
&.show_all_holder {
margin-bottom: 40px !important;
}
.show_all {
position: absolute;
left: 0px;
top: 10px;
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: flex-start;
background: linear-gradient(0deg, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
span {
color: var(--blue);
text-decoration: underline;
cursor: pointer;
&:hover {
text-decoration: none;
}
}
}
}

View File

@ -114,7 +114,7 @@ main .title_wrapper .right {
}
main .title_wrapper .company-dropdown {
position: relative;
z-index: 100;
z-index: 90;
}
main .title_wrapper .company-dropdown .arrow {
cursor: pointer;
@ -3598,7 +3598,7 @@ main .dropdown_blocks_list .dropdown_block .block_body .fines_detail ul li {
bottom: -30px;
right: 0;
pointer-events: none;
z-index: 98;
z-index: 80;
margin: 0 auto;
max-width: 1310px;
}

View File

@ -121,7 +121,7 @@ main {
.company-dropdown {
position: relative;
z-index: 100;
z-index: 90;
.arrow {
cursor: pointer;
@ -4019,7 +4019,7 @@ main .dropdown_blocks_list .dropdown_block .block_body {
bottom: -30px;
right: 0;
pointer-events: none;
z-index: 98;
z-index: 80;
margin: 0 auto;
max-width: 1310px;

View File

@ -22,6 +22,8 @@ export default async function handler(req, res)
if(cookies.jwt !== undefined && cookies.jwt !== null)
{
if(jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT))
{
if(parseInt(process.env.DADATA_API_CHECK_DOCUMENT_ENABLED, 10) === 1)
{
try
{
@ -57,6 +59,12 @@ export default async function handler(req, res)
}
}
else
{
res.status(200).json([{ qc: 0 }]);
resolve();
}
}
else
{
res.status(403).send();
resolve();

View File

@ -29,7 +29,7 @@ export default class RecoveryPage extends React.Component
email_error: false,
recovery_form_step: 1,
email_code_error: false,
email_send_disabled: false,
email_send_disabled: true,
set_password_disabled: true,
email_code_submit_disabled: true,
email_code_resend_disabled: true,
@ -137,17 +137,26 @@ export default class RecoveryPage extends React.Component
_handle_onEmailChange = (value) =>
{
this.setState({ email: value, email_send_disabled: this._check_fields_disabled([ value, this.state.email ]), email_error: false });
this.setState({ email: value, email_error: false }, () =>
{
this.setState({ email_send_disabled: this._check_fields_disabled([ value, this.state.email ]) });
});
}
_handle_onPasswordChange = (value) =>
{
this.setState({ password: value, set_password_disabled: this._check_fields_disabled([ value, this.state.password ]), password_error: false });
this.setState({ password: value, password_error: false }, () =>
{
this.setState({ set_password_disabled: this._check_fields_disabled([ value, this.state.password ]) });
});
}
_handle_onPasswordCheckChange = (value) =>
{
this.setState({ password_repeat: value, set_password_disabled: this._check_fields_disabled([ value, this.state.password_repeat ]), password_error: false });
this.setState({ password_repeat: value, password_error: false }, () =>
{
this.setState({ set_password_disabled: this._check_fields_disabled([ value, this.state.password_repeat ]) });
});
}
_handle_onEmailCodeChange = (value) =>

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.58625 7.9995L4.29325 5.7065C3.90225 5.3165 3.90225 4.6825 4.29325 4.2925C4.68325 3.9025 5.31725 3.9025 5.70725 4.2925L8.00025 6.5855L10.2933 4.2925C10.6832 3.9025 11.3173 3.9025 11.7073 4.2925C12.0972 4.6825 12.0972 5.3165 11.7073 5.7065L9.41425 7.9995L11.7073 10.2925C12.0972 10.6825 12.0972 11.3165 11.7073 11.7065C11.3173 12.0965 10.6832 12.0965 10.2933 11.7065L8.00025 9.4135L5.70725 11.7065C5.31725 12.0965 4.68325 12.0965 4.29325 11.7065C3.90225 11.3165 3.90225 10.6825 4.29325 10.2925L6.58625 7.9995Z" fill="#A8026B"/>
</svg>

After

Width:  |  Height:  |  Size: 681 B