diff --git a/components/questionnaire/forms/DigitalCertificates.js b/components/questionnaire/forms/DigitalCertificates.js index 96aa769..9dfff54 100644 --- a/components/questionnaire/forms/DigitalCertificates.js +++ b/components/questionnaire/forms/DigitalCertificates.js @@ -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,63 +64,70 @@ 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['OGRNIP'] !== undefined && cert.Subject['OGRNIP'] !== null && cert.Subject['OGRNIP'] !== "") + if(cert.Subject['INN'] !== undefined && cert.Subject['INN'] !== null && cert.Subject['INN'] !== "") { - 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, []); - } - } - else - { - callback(null, []); + errors.push('inn'); } } else { - if(cert.Subject['INNLE'] !== undefined && cert.Subject['INNLE'] !== null && cert.Subject['INNLE'] !== "") + apply = false; + } + } + else + { + if(cert.Subject['INNLE'] !== undefined && cert.Subject['INNLE'] !== null && cert.Subject['INNLE'] !== "") + { + if(main.inn !== cert.Subject['INN']) { - let owner_valid = true; - const fields = [ "lastname", "firstname", "middlename", ]; - const cert_owner = `${ cert.Subject['SN']} ${ cert.Subject['G']}`.toUpperCase(); - - for(let i in fields) - { - if(head_person[fields[i]] !== null && head_person[fields[i]] !== "") - { - if(cert_owner.indexOf(head_person[fields[i]].toUpperCase().trim()) < 0) - { - owner_valid = false; - } - } - } - - if(owner_valid) - { - callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY") } ]); - } - else - { - callback(null, []); - } - } - else - { - callback(null, []); + 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(); + + for(let i in fields) + { + if(head_person[fields[i]] !== null && head_person[fields[i]] !== "") + { + if(cert_owner.indexOf(head_person[fields[i]].toUpperCase().trim()) < 0) + { + owner_valid = false; + } + } + } + + if(!owner_valid) + { + errors.push('name'); + } + + if(!cert.IsValid) + { + errors.push('date'); + } + + if(apply) + { + callback(null, [ { id: certificate.id, fields: cert.Subject, valid_to_date: moment(cert.ValidToDate).format("DD.MM.YYYY"), errors } ]); } else { @@ -131,14 +145,34 @@ export default class DigitalCertificates extends React.Component { console.log("applicable certificates", JSON.stringify(certificates)); Sentry.captureMessage("Client digital sign step, list of applicable digital certificates"); - + 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,88 +248,104 @@ export default class DigitalCertificates extends React.Component } else { - if(certificates_error === null) - { - console.log({ certificates }); - - return ( - -
-

Выберите подписанта

-
- - { certificates.map((certificate, index) => ( -
- { this.setState({ certificate_selected: certificate }) } } - /> - -
- )) } - -
-
-
-
- -
-
- ) - } - else - { - return ( - - { certificates_error === "NOT_INSTALLED" && ( - Плагин КриптоПРО не установлен, посмотрите инструкцию как установить плагин КриптоПРО. }/> + return ( + <> + { (certificates_error !== null) && + <> + { certificates_error === "NOT_INSTALLED" || certificates_error === "CERTIFICATES" && ( + + Плагин КриптоПРО не установлен или не активирован. Посмотите инструкцию как установить, активировать и проверить работу плагина КриптоПро.
+ }/> ) } - { certificates_error === "CERTIFICATES" && ( - Плагин КриптоПРО не активирован, пожалуйста, обновите страницу и подтвердите разрешение для сайта на доступ к списку сертификатов. }/> - ) } - { certificates_error === "ISSUED" && ( - Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН: { main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }. Такой сертификат не найден. }/> + { certificates_error === "INVALID" && ( + + Анкету необходимо подписать по ЭЦП сертификатом юридического лица с ИНН:
+ { main.inn }, выданного: { head_person.lastname } { head_person.firstname } { head_person.middlename }.
+ Такой сертификат не найден!

+ Если Вы уверены в наличии такого сертификата: +
    +
  1. Проверьте, вставлен ли ключ с этим сертификатом.
  2. +
  3. Проверьте возможность подписания этим сертификатом с помощью средства проверки КриптоПро.
  4. +
  5. Возможно, сертификат нужно установить. Воспользуйтесь инструкцией.
  6. +
+ }/> ) } { certificates_error === "MISMATCH" && ( - Подписант не соответствует указанному подписанту в анкете. }/> + Подписант не соответствует указанному подписанту в анкете. }/> ) } -
+ + } + { certificates.length > 0 && ( + <> +
+

Выберите подписанта

+
- ) - } + { certificates.slice(0, show_all ? certificates.length : 2).map((certificate, index) => ( +
0 ? "error" : "" } ${ certificates.length > 1 && !show_all && index === 1 ? "show_all_holder" : ""}`} key={ index }> + { this.setState({ certificate_selected: certificate }) } } + /> + + { certificates.length > 1 && !show_all && index === 1 && ( +
this.setState({ show_all: true }) }>Посмотреть все
+ ) } +
+ )) } + +
+
+
+
+ +
+ + ) } + + ) } } } \ No newline at end of file diff --git a/components/questionnaire/forms/Form_4_Shareholders/index.js b/components/questionnaire/forms/Form_4_Shareholders/index.js index 0ff4f9b..93f4caa 100644 --- a/components/questionnaire/forms/Form_4_Shareholders/index.js +++ b/components/questionnaire/forms/Form_4_Shareholders/index.js @@ -359,7 +359,7 @@ class ShareholderForm extends React.Component ) }
- + {/*} -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 { diff --git a/components/questionnaire/forms/QuestionnaireForm.js b/components/questionnaire/forms/QuestionnaireForm.js index 112009b..719432c 100644 --- a/components/questionnaire/forms/QuestionnaireForm.js +++ b/components/questionnaire/forms/QuestionnaireForm.js @@ -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); diff --git a/css/forms/style.css b/css/forms/style.css index 70861ec..dc3c0cc 100644 --- a/css/forms/style.css +++ b/css/forms/style.css @@ -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; +} diff --git a/css/forms/style.less b/css/forms/style.less index 018372b..1d6a071 100644 --- a/css/forms/style.less +++ b/css/forms/style.less @@ -1947,4 +1947,51 @@ .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; + } + } + } } \ No newline at end of file diff --git a/css/main/style.css b/css/main/style.css index ad283ae..acd0c04 100644 --- a/css/main/style.css +++ b/css/main/style.css @@ -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; } diff --git a/css/main/style.less b/css/main/style.less index 775d6f6..0f6ca84 100644 --- a/css/main/style.less +++ b/css/main/style.less @@ -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; diff --git a/pages/api/suggests/document/check.js b/pages/api/suggests/document/check.js index dc6602c..218a695 100644 --- a/pages/api/suggests/document/check.js +++ b/pages/api/suggests/document/check.js @@ -23,36 +23,44 @@ export default async function handler(req, res) { if(jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT)) { - try + if(parseInt(process.env.DADATA_API_CHECK_DOCUMENT_ENABLED, 10) === 1) { - const { seria, number } = req.body; + try + { + const { seria, number } = req.body; - axios.post(`https://cleaner.dadata.ru/api/v1/clean/passport`, [ `${ seria } ${ number }` ], { - httpAgent: keepAliveAgent, - headers: { - "Content-Type": "application/json", - "Authorization": `Token ${ process.env.DADATA_API_KEY }`, - "X-Secret": process.env.DADATA_SECRET_KEY - }, - }) - .then((api_response) => + axios.post(`https://cleaner.dadata.ru/api/v1/clean/passport`, [ `${ seria } ${ number }` ], { + httpAgent: keepAliveAgent, + headers: { + "Content-Type": "application/json", + "Authorization": `Token ${ process.env.DADATA_API_KEY }`, + "X-Secret": process.env.DADATA_SECRET_KEY + }, + }) + .then((api_response) => + { + res.status(200).send(api_response.data); + resolve(); + }) + .catch((error) => + { + console.log("error"); + console.error(error); + + res.status(500).send(); + resolve(); + }); + } + catch(e) { - res.status(200).send(api_response.data); - resolve(); - }) - .catch((error) => - { - console.log("error"); - console.error(error); - + console.error(e); res.status(500).send(); resolve(); - }); + } } - catch(e) + else { - console.error(e); - res.status(500).send(); + res.status(200).json([{ qc: 0 }]); resolve(); } } diff --git a/pages/recovery.js b/pages/recovery.js index 5bc17f2..7f33814 100644 --- a/pages/recovery.js +++ b/pages/recovery.js @@ -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) => diff --git a/public/assets/images/icons/delete_red.svg b/public/assets/images/icons/delete_red.svg new file mode 100644 index 0000000..14b7c74 --- /dev/null +++ b/public/assets/images/icons/delete_red.svg @@ -0,0 +1,3 @@ + + +