diff --git a/actions/index.js b/actions/index.js index 88c0469..66b4d08 100644 --- a/actions/index.js +++ b/actions/index.js @@ -10,4 +10,5 @@ export * from './settingsActions'; export * from './announcementsActions'; export * from './eventsActions'; export * from './supportActions'; -export * from './adminActions'; \ No newline at end of file +export * from './adminActions'; +export * from './suggestsActions'; \ No newline at end of file diff --git a/actions/suggestsActions.js b/actions/suggestsActions.js new file mode 100644 index 0000000..80d27c5 --- /dev/null +++ b/actions/suggestsActions.js @@ -0,0 +1,42 @@ +import axios from 'axios'; +import { eachSeries } from 'async'; + +import * as actionTypes from '../constants/actionTypes'; +import * as currentState from '../reducers/initialState'; + +if(process.browser) +{ + FormData.prototype.appendObject = function(obj, namespace) + { + let keyName; + for (var key in obj) + { + if (obj.hasOwnProperty(key)) + { + keyName = [namespace, '[', key, ']'].join(''); + this.append(keyName, obj[key]); + } + } + }; +} + +export const getAddress = (text) => +{ + return new Promise((resolve, reject) => + { + axios.post(`${ process.env.NEXT_PUBLIC_SELF_API_HOST }/api/suggests/address`, { text }, { + withCredentials: true, + }) + .then((response) => + { + resolve(""); + }) + .catch((error) => + { + console.log("error"); + console.error(error); + + reject(); + }); + }); +} \ No newline at end of file diff --git a/package.json b/package.json index 8b4cec6..8ea1251 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "axios": "^0.24.0", "cookie": "^0.4.1", "cors": "^2.8.5", + "dadata": "^0.0.3", + "debounce-promise": "^3.1.2", "form-data": "^4.0.0", "ioredis": "^4.28.2", "js-file-download": "^0.4.12", @@ -27,6 +29,7 @@ "next-redux-wrapper": "^7.0.5", "next-with-less": "^1.0.1", "nextjs-cors": "^2.1.0", + "node-fetch": "^3.3.1", "numeral": "^2.0.6", "pluralize-ru": "^1.0.1", "qs": "^6.10.1", diff --git a/pages/_app.js b/pages/_app.js index 835bb94..d07e895 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -2,6 +2,9 @@ import { reduxWrapper } from '../store' import numeral from 'numeral'; import '../css/style.css'; +//import DaData from 'dadata'; +//global.dadata = new DaData(process.env.DADATA_API_KEY, process.env.DADATA_SECRET_KEY); + const m = Math.random(); numeral.register('locale', `locale_${ m }`, { delimiters: { diff --git a/pages/api/suggests/address.js b/pages/api/suggests/address.js new file mode 100644 index 0000000..638ee13 --- /dev/null +++ b/pages/api/suggests/address.js @@ -0,0 +1,49 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import axios from 'axios'; +import { Cookies } from 'react-cookie'; +import cookie from 'cookie'; +import moment from 'moment'; +import jwt from 'jsonwebtoken'; +import { cors } from '../../../lib/cors'; + +export default async function handler(req, res) +{ + await cors(req, res); + + if(req.headers.cookie !== undefined) + { + const cookies = cookie.parse(req.headers?.cookie ? req.headers?.cookie : ""); + + if(cookies.jwt !== undefined && cookies.jwt !== null) + { + if(jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT)) + { + try + { + /* + dadata('address', ['мск сухонска 11/-89'], function (dadata_error, dadata_response) + { + console.log(dadata_error); + console.log(dadata_response); + + res.status(200).send(); + }); + */ + } + catch(e) + { + console.error(e); + res.status(500); + } + } + else + { + res.status(403); + } + } + else + { + res.status(403); + } + } +} \ No newline at end of file diff --git a/pages/components/Header/index.js b/pages/components/Header/index.js index e36790c..5ec42cd 100644 --- a/pages/components/Header/index.js +++ b/pages/components/Header/index.js @@ -57,6 +57,7 @@ class Header extends React.Component if (route.indexOf("/contract") === 0) return "Договоры"; if (route.indexOf("/support") === 0) return "Обращения"; if (route.indexOf("/events") === 0) return "События"; + if (route.indexOf("/questionnaire") === 0) return "Анкета лизингополучателя"; return null; }; diff --git a/pages/questionnaire/components/InnerMenu/index.js b/pages/questionnaire/components/InnerMenu/index.js new file mode 100644 index 0000000..0cbbfe0 --- /dev/null +++ b/pages/questionnaire/components/InnerMenu/index.js @@ -0,0 +1,115 @@ +import React from "react"; +import Link from "next/link"; +import { connect } from "react-redux"; +import { getContractEvents, getContractFines, getContractInfo, } from "../../../../actions"; + +class InnerMenu extends React.Component +{ + constructor(props) + { + super(props); + this.menuRef = React.createRef(); + + this.state = { + menuOpened: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + }; + } + + componentDidMount() + { + } + + componentDidUpdate(prevProps, prevState) + { + } + + _handle_onToggleMenu = () => + { + this.setState({ + menuOpened: !this.state.menuOpened, + }); + } + + _getActiveLink = (route) => + { + if (route.indexOf("#main") > -1) return "Информация о лизингополучателе"; + if (route.indexOf("#contacts") > -1) return "Адреса лизингополучателя"; + if (route.indexOf("#signer") > -1) return "Информация о единоличном исполнительном органе, подписанте договора лизинга"; + if (route.indexOf("#shareholders") > -1) return "Сведения об участниках (акционерах) и бенефициарных владельцах"; + if (route.indexOf("#regulatory") > -1) return "Сведения об органах управления"; + if (route.indexOf("#non-profit") > -1) return "Данные о некомерческой организации"; + if (route.indexOf("#check") > -1) return "Проверка введеных данных"; + if (route.indexOf("#signing") > -1) return "Выбор метода подписания"; + + return null; + } + + render() + { + const { menuOpened, } = this.state; + + return ( + + ) + } +} + +function mapStateToProps(state, ownProps) +{ + return { + }; +} + +export default connect(mapStateToProps)(InnerMenu); \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_1_Main/index.js b/pages/questionnaire/components/forms/Form_1_Main/index.js new file mode 100644 index 0000000..57a451c --- /dev/null +++ b/pages/questionnaire/components/forms/Form_1_Main/index.js @@ -0,0 +1,135 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; +import { getAddress } from '../../../../../actions'; +import debounce from 'debounce-promise'; + +const suggestsAddressDebounce = (text) => +{ + return getAddress(text); +} + +const suggestsAddress = debounce(suggestsAddressDebounce, 500); + +export default class Form_1_Main extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onPhoneSubmit = (event) => + { + event.preventDefault(); + + const { user, address, phone_check_loading } = this.state; + + if(!phone_check_loading) + { + this.setState({ phone_check_loading: true }, () => + { + }); + } + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormMain", "_handle_onFormSubmit"); + + const { address, phone_code, code_check_loading } = this.state; + + //if(!code_check_loading) + //{ + /* + this.setState({ code_check_loading: true }, () => + { + }); + */ + //} + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + /* + suggestsAddress("Москва") + .then((suggests) => + { + console.log({ suggests }); + }) + .catch(() => + { + console.log("_handle_onAddressChange", "error"); + }); + */ + }); + + //const phone_number_format_error = value.length > 1 && (value[0] !== "+" || value[1] !== "7") ? true : false; + //this.setState({ phone: value, phone_login_disabled: this._check_fields_disabled([ value ]), phone_number_error: false, phone_number_format_error: phone_number_format_error }); + } + +// _handle_onPhoneCodeChange = (value) => +// { +// this.setState({ phone_code: value, phone_code_submit_disabled: this._check_fields_disabled([ value ]), phone_sms_code_error: false }); +// } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ this._handle_onAddressChange(event.target.value) } required={ true }/> +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_2_Contacts/index.js b/pages/questionnaire/components/forms/Form_2_Contacts/index.js new file mode 100644 index 0000000..f93314e --- /dev/null +++ b/pages/questionnaire/components/forms/Form_2_Contacts/index.js @@ -0,0 +1,82 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_2_Contacts extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_3_Signer/index.js b/pages/questionnaire/components/forms/Form_3_Signer/index.js new file mode 100644 index 0000000..73b187a --- /dev/null +++ b/pages/questionnaire/components/forms/Form_3_Signer/index.js @@ -0,0 +1,82 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_3_Signer extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_4_Shareholders/index.js b/pages/questionnaire/components/forms/Form_4_Shareholders/index.js new file mode 100644 index 0000000..83e167a --- /dev/null +++ b/pages/questionnaire/components/forms/Form_4_Shareholders/index.js @@ -0,0 +1,82 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_4_Shareholders extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_5_Regulatory/index.js b/pages/questionnaire/components/forms/Form_5_Regulatory/index.js new file mode 100644 index 0000000..65eb7f7 --- /dev/null +++ b/pages/questionnaire/components/forms/Form_5_Regulatory/index.js @@ -0,0 +1,82 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_5_Regulatory extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_6_NonProfit/index.js b/pages/questionnaire/components/forms/Form_6_NonProfit/index.js new file mode 100644 index 0000000..65262fb --- /dev/null +++ b/pages/questionnaire/components/forms/Form_6_NonProfit/index.js @@ -0,0 +1,82 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_6_NonProfit extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_7_Check/index.js b/pages/questionnaire/components/forms/Form_7_Check/index.js new file mode 100644 index 0000000..bfb7128 --- /dev/null +++ b/pages/questionnaire/components/forms/Form_7_Check/index.js @@ -0,0 +1,82 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_7_Check extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+
+
+ +
+
+ +
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/components/forms/Form_8_Signing/index.js b/pages/questionnaire/components/forms/Form_8_Signing/index.js new file mode 100644 index 0000000..2ee0764 --- /dev/null +++ b/pages/questionnaire/components/forms/Form_8_Signing/index.js @@ -0,0 +1,73 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +export default class Form_8_Signing extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + address: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + static getDerivedStateFromProps(nextProps, prevState) + { + return { + observer: nextProps.observer, + user: nextProps.user, + }; + } + + componentDidMount() + { + } + + _handle_onFormSubmit = (event) => + { + event.preventDefault(); + console.log("FormAddress", "_handle_onFormSubmit"); + } + + _handle_onAddressChange = (value) => + { + this.setState({ address: value, }, () => + { + }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + render() + { + const { address, phone_check_loading, phone_number_format_error } = this.state; + + return ( + +
+

Тут блок подписания

+
+
+ ) + } +} \ No newline at end of file diff --git a/pages/questionnaire/index.js b/pages/questionnaire/index.js new file mode 100644 index 0000000..2b37e2e --- /dev/null +++ b/pages/questionnaire/index.js @@ -0,0 +1,233 @@ +import React from "react"; +import Head from 'next/head'; +import Image from 'next/image'; +import Link from "next/link"; +import cookie from 'cookie'; +import { connect } from "react-redux"; +import numeral from "numeral"; +import pluralize from 'pluralize-ru'; +import { SpinnerCircular } from 'spinners-react'; + +import { withRouter } from 'next/router'; +import { reduxWrapper } from '../../store'; + +import InnerMenu from "./components/InnerMenu"; +import Header from '../components/Header'; +import Footer from '../components/Footer'; + +import { sendPhoneChangeNumber, sendPhoneChangeNumberSmsCode, setUserPhone } from '../../actions'; +import AccountLayout from "../components/Layout/Account"; +import Form_1_Main from "./components/forms/Form_1_Main"; +import Form_2_Contacts from "./components/forms/Form_2_Contacts"; +import Form_3_Signer from "./components/forms/Form_3_Signer"; +import Form_4_Shareholders from "./components/forms/Form_4_Shareholders"; +import Form_5_Regulatory from "./components/forms/Form_5_Regulatory"; +import Form_6_NonProfit from "./components/forms/Form_6_NonProfit"; +import Form_7_Check from "./components/forms/Form_7_Check"; +import Form_8_Signing from "./components/forms/Form_8_Signing"; + +class QuestionnairePage extends React.Component +{ + constructor(props) + { + super(props); + this.state = { + phone: "", + phone_check_loading: false, + phone_number_format_error: false, + }; + } + + componentDidMount() + { + } + + _handle_onPhoneSubmit = (event) => + { + event.preventDefault(); + + const { user, phone, phone_check_loading } = this.state; + + if(!phone_check_loading) + { + this.setState({ phone_check_loading: true }, () => + { + sendPhoneChangeNumber({ email: user.email, phone }) + .then(() => + { + this.setState({ phone_check_loading: false, phone_number_error: false, timer: 60, phone_form_step: 2, }, () => + { + this.timer_ref = setInterval(() => + { + const t = this.state.timer - 1; + this.setState({ timer: t, }, () => + { + if(t === 0) + { + clearInterval(this.timer_ref); + } + }); + }, 1000); + }); + }) + .catch(() => + { + this.setState({ phone_number_error: true, phone_check_loading: false }); + }); + }); + } + } + + _handle_onCodeSubmit = (event) => + { + event.preventDefault(); + + const { phone, phone_code, code_check_loading } = this.state; + + if(!code_check_loading) + { + this.setState({ code_check_loading: true }, () => + { + sendPhoneChangeNumberSmsCode({ phone, code: phone_code }) + .then(() => + { + const new_user = { ...this.state.user }; + new_user.phone = phone; + + setUserPhone({ dispatch: this.props.dispatch, user: new_user }); + + this.setState({ phone_sms_code_error: false, code_check_loading: false, phone_form_step: 3 }); + }) + .catch(() => + { + this.setState({ phone_sms_code_error: true, code_check_loading: false }); + }); + }); + } + } + + _handle_onResendCode = (event) => + { + this.setState({ phone_sms_code_error: false }, () => + { + this._handle_onPhoneSubmit(event); + }); + } + + _handle_onPhoneChange = (value) => + { + const phone_number_format_error = value.length > 1 && (value[0] !== "+" || value[1] !== "7") ? true : false; + this.setState({ phone: value, phone_login_disabled: this._check_fields_disabled([ value ]), phone_number_error: false, phone_number_format_error: phone_number_format_error }); + } + + _handle_onPhoneCodeChange = (value) => + { + this.setState({ phone_code: value, phone_code_submit_disabled: this._check_fields_disabled([ value ]), phone_sms_code_error: false }); + } + + _check_fields_disabled = (values) => + { + for(let i in values) + { + if(values[i] === "") + { + return true; + } + } + + return false; + } + + _renderForm = () => + { + const route = this.props.router.asPath; + + if (route.indexOf("#main") > -1) return (); + if (route.indexOf("#contacts") > -1) return (); + if (route.indexOf("#signer") > -1) return (); + if (route.indexOf("#shareholders") > -1) return (); + if (route.indexOf("#regulatory") > -1) return (); + if (route.indexOf("#non-profit") > -1) return (); + if (route.indexOf("#check") > -1) return (); + if (route.indexOf("#signing") > -1) return (); + + return (); + } + + render() + { + const { phone, phone_check_loading, phone_number_format_error } = this.state; + + return ( + + + ЛК Эволюция автолизинга - Анкета лизингополучателя + + +
+ +
+
+

Анкета лизингополучателя

+
+
+
+ +
+ { this._renderForm() } +
+
+
+