529 lines
14 KiB
JavaScript
529 lines
14 KiB
JavaScript
import React from "react";
|
||
import Head from "next/head";
|
||
import Image from "next/image";
|
||
import { connect } from "react-redux";
|
||
import { withRouter } from "next/router";
|
||
import moment from "moment";
|
||
import { SpinnerCircular } from "spinners-react";
|
||
import Dropzone from 'react-dropzone';
|
||
import Select from 'react-select'
|
||
|
||
import { reduxWrapper } from "../../store";
|
||
|
||
import Header from "../components/Header";
|
||
import Footer from "../components/Footer";
|
||
import Company from "../components/Company";
|
||
import InnerMenu from "./components/InnerMenu";
|
||
import SuccessMessage from "./components/SuccessMessage";
|
||
import AccountLayout from "../components/Layout/Account";
|
||
import TemplateFile from "./components/TemplateFile";
|
||
|
||
import {
|
||
getSupportThemes,
|
||
getContractsList,
|
||
getBitrixFile,
|
||
sendNewAppeal,
|
||
sendAppealAttachments
|
||
} from "../../actions";
|
||
|
||
class FileDropzone extends React.Component
|
||
{
|
||
constructor(props)
|
||
{
|
||
super(props);
|
||
this.state = {}
|
||
}
|
||
|
||
render()
|
||
{
|
||
const { files, onAddFile, onDeleteFile } = this.props;
|
||
return (
|
||
<>
|
||
{ files.length > 0 && (
|
||
<div className="column">
|
||
<div className="column_text_block">
|
||
<p><b>Приложенные файлы</b></p>
|
||
{ files.map((file, index) => (
|
||
<p key={ index }>{ file.name } <small style={{ color: "#A8026B", textDecoration: "underline", cursor: "pointer" }} onClick={ () => onDeleteFile(file.name) }>[ удалить ]</small></p>
|
||
)) }
|
||
</div>
|
||
</div>
|
||
) }
|
||
<Dropzone onDrop={ (acceptedFiles) => onAddFile(acceptedFiles) }>
|
||
{ ({getRootProps, getInputProps}) => (
|
||
<div className="file_upload dropzone" { ...getRootProps() }>
|
||
<div className="files"></div>
|
||
<div>
|
||
<p data-sm-text="Выберите файлы">
|
||
<span>Перенесите файлы на экран для быстрой загрузки или выберите файл с компьютера </span>
|
||
</p>
|
||
<label htmlFor="" className="button button-blue">Загрузить файл</label>
|
||
</div>
|
||
<input { ...getInputProps() } />
|
||
</div>
|
||
) }
|
||
</Dropzone>
|
||
</>
|
||
)
|
||
}
|
||
}
|
||
|
||
class SupportRequestPage extends React.Component
|
||
{
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
loading: false,
|
||
contracts: null,
|
||
themes: null,
|
||
themes_filtered: null,
|
||
name: "",
|
||
phone: "",
|
||
email: "",
|
||
question: "",
|
||
all_contracts: false,
|
||
selected_contracts: [],
|
||
file: null,
|
||
files: [],
|
||
opened_theme: 0,
|
||
opened_question: 0,
|
||
success: false,
|
||
};
|
||
|
||
this.formRef = React.createRef();
|
||
}
|
||
|
||
static getDerivedStateFromProps(nextProps, prevState)
|
||
{
|
||
return {
|
||
contracts: nextProps.contracts,
|
||
themes: nextProps.themes,
|
||
};
|
||
}
|
||
|
||
componentDidMount()
|
||
{
|
||
console.log("CDM", "SupportRequestPage", this.state);
|
||
|
||
if (!this.state.loading)
|
||
{
|
||
this.setState({ loading: true }, () =>
|
||
{
|
||
Promise.all([
|
||
getContractsList({ dispatch: this.props.dispatch, all: true }),
|
||
getSupportThemes({ dispatch: this.props.dispatch })
|
||
])
|
||
.then(() =>
|
||
{
|
||
let opened_theme, opened_question;
|
||
let found = false;
|
||
|
||
let themes_filtered = [];
|
||
for(let t in this.state.themes)
|
||
{
|
||
let questions = [];
|
||
|
||
for(let q in this.state.themes[t].questions)
|
||
{
|
||
if(this.state.themes[t].questions[q].request)
|
||
{
|
||
questions.push(this.state.themes[t].questions[q]);
|
||
}
|
||
}
|
||
|
||
if(questions.length > 0)
|
||
{
|
||
themes_filtered.push({ ...this.state.themes[t], ...{ questions } });
|
||
}
|
||
}
|
||
|
||
opened_theme = 0;
|
||
for(let t in themes_filtered)
|
||
{
|
||
opened_question = 0;
|
||
for(let q in themes_filtered[t].questions)
|
||
{
|
||
if(this.props.router.asPath.indexOf(themes_filtered[t].questions[q].id) > -1)
|
||
{
|
||
found = true;
|
||
break;
|
||
}
|
||
opened_question++;
|
||
}
|
||
|
||
if(found) { break; }
|
||
opened_theme++;
|
||
}
|
||
|
||
this.setState({ loading: false, themes_filtered, opened_theme: found ? opened_theme : 0, opened_question: found ? opened_question : 0 }, () =>
|
||
{
|
||
if(this.props.router.asPath.indexOf("#") > -1)
|
||
{
|
||
if(window.innerWidth <= 767)
|
||
{
|
||
const elementRect = this.formRef.current.getBoundingClientRect();
|
||
window.scrollTo({ top: parseInt(elementRect.top, 10) - 100, behavior: 'smooth' });
|
||
}
|
||
}
|
||
});
|
||
})
|
||
.catch(() => {});
|
||
});
|
||
}
|
||
}
|
||
|
||
componentDidUpdate(prevProps, prevState)
|
||
{
|
||
}
|
||
|
||
_handle_onBack = () =>
|
||
{
|
||
this.props.router.push('/support/faq/');
|
||
}
|
||
|
||
_handle_onChangeTheme = (index) =>
|
||
{
|
||
this.setState({ opened_theme: index, opened_question: 0 });
|
||
}
|
||
|
||
_handle_onChangeField = (field, value) =>
|
||
{
|
||
this.setState({ [ field ]: value, });
|
||
}
|
||
|
||
_handle_onSendAppeal = (event) =>
|
||
{
|
||
event.preventDefault();
|
||
|
||
if(!this._checkDisabled())
|
||
{
|
||
const { name, phone, email, question, selected_contracts, files } = this.state;
|
||
|
||
this.setState({ loading: true }, () =>
|
||
{
|
||
const contract_numbers = [];
|
||
for(let i in selected_contracts)
|
||
{
|
||
contract_numbers.push(selected_contracts.value);
|
||
}
|
||
|
||
sendNewAppeal({
|
||
phone: phone,
|
||
email: email,
|
||
description: question,
|
||
contract_numbers: contract_numbers
|
||
})
|
||
.then((result) =>
|
||
{
|
||
console.log("SupportRequestPage", "_handle_onSendAppeal", result);
|
||
if(files.length > 0)
|
||
{
|
||
sendAppealAttachments({
|
||
request_client_number: result.request_client_number,
|
||
files: files,
|
||
})
|
||
.then(() =>
|
||
{
|
||
this.setState({ loading: false, success: true, }, () =>
|
||
{
|
||
window.scrollTo(0, 0);
|
||
});
|
||
})
|
||
.catch(() =>
|
||
{
|
||
this.setState({ loading: false });
|
||
});
|
||
}
|
||
else
|
||
{
|
||
this.setState({ loading: false, success: true, }, () =>
|
||
{
|
||
window.scrollTo(0, 0);
|
||
});
|
||
}
|
||
})
|
||
.catch(() =>
|
||
{
|
||
this.setState({ loading: false });
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
_handle_onContract = (options) =>
|
||
{
|
||
const { contracts, all_contracts } = this.state;
|
||
const selected_contracts = [];
|
||
console.log("options", options);
|
||
|
||
if(all_contracts === true)
|
||
{
|
||
this.setState({ all_contracts: false, selected_contracts: [] });
|
||
}
|
||
else
|
||
{
|
||
let all = false;
|
||
for(let i in options)
|
||
{
|
||
if(options[i].value === null)
|
||
{
|
||
all = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(all)
|
||
{
|
||
const contracts_list = [];
|
||
for(let i in contracts)
|
||
{
|
||
contracts_list.push({ value: contracts[i].number, label: contracts[i].number });
|
||
}
|
||
|
||
this.setState({ all_contracts: true, selected_contracts: contracts_list });
|
||
}
|
||
else
|
||
{
|
||
if(options.length === contracts.length)
|
||
{
|
||
const contracts_list = [];
|
||
for(let i in contracts)
|
||
{
|
||
contracts_list.push({ value: contracts[i].number, label: contracts[i].number });
|
||
}
|
||
|
||
this.setState({ all_contracts: true, selected_contracts: contracts_list });
|
||
}
|
||
else
|
||
{
|
||
this.setState({ selected_contracts: options });
|
||
}
|
||
|
||
/*
|
||
for(let i in options)
|
||
{
|
||
selected_contracts.push(options[i].value);
|
||
this.setState({ selected_contracts });
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
}
|
||
|
||
_handle_onAddFile = (files) =>
|
||
{
|
||
console.log("_handle_onAdd", files);
|
||
|
||
const existed_files = [ ...this.state.files ];
|
||
for(let nf in files)
|
||
{
|
||
let e = false;
|
||
for(let ef in this.state.files)
|
||
{
|
||
if(this.state.files[ef].name === files[nf].name) { e = true; }
|
||
}
|
||
|
||
if(!e)
|
||
{
|
||
existed_files.push(files[nf]);
|
||
}
|
||
}
|
||
|
||
this.setState({ files: existed_files });
|
||
}
|
||
|
||
_handle_onDeleteFile = (file_name) =>
|
||
{
|
||
const files = [];
|
||
for(let i in this.state.files)
|
||
{
|
||
if(this.state.files[i].name !== file_name)
|
||
{
|
||
files.push(this.state.files[i]);
|
||
}
|
||
}
|
||
|
||
this.setState({ files });
|
||
}
|
||
|
||
_checkDisabled = () =>
|
||
{
|
||
const { phone, email, question, } = this.state;
|
||
if(phone === "" || email === "" || question === "")
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
_renderForm = () =>
|
||
{
|
||
const { loading, contracts, selected_contracts, all_contracts, themes, themes_filtered, name, phone, email, question, file, files, opened_theme, opened_question } = this.state;
|
||
|
||
const contracts_list = [];
|
||
if(!all_contracts)
|
||
{
|
||
for(let i in contracts)
|
||
{
|
||
contracts_list.push({
|
||
value: contracts[i].number, label: contracts[i].number
|
||
});
|
||
}
|
||
|
||
contracts_list.unshift({ value: null, label: "Все договоры" });
|
||
}
|
||
else
|
||
{
|
||
contracts_list.unshift({ value: null, label: "Все договоры" });
|
||
}
|
||
|
||
return (
|
||
<form onSubmit={ this._handle_onSendAppeal }>
|
||
<div className="form_field">
|
||
<Select
|
||
options={ contracts_list }
|
||
isMulti
|
||
className="custom-multi-select form_field"
|
||
classNamePrefix="custom-select"
|
||
placeholder="Выберите договоры"
|
||
onChange={ this._handle_onContract }
|
||
value={ all_contracts ? contracts_list : selected_contracts }
|
||
/>
|
||
</div>
|
||
<div className="form_field">
|
||
<input type="text" name="name" placeholder="Ваше ФИО" value={ name } onChange={ (event) => { this._handle_onChangeField("name", event.target.value) } }/>
|
||
</div>
|
||
<div className="form_field">
|
||
<input type="tel" name="phone" placeholder="Телефон" value={ phone } onChange={ (event) => { this._handle_onChangeField("phone", event.target.value) } }/>
|
||
</div>
|
||
<div className="form_field">
|
||
<input type="email" name="email" placeholder="Email" value={ email } onChange={ (event) => { this._handle_onChangeField("email", event.target.value) } }/>
|
||
</div>
|
||
<div className="form_field">
|
||
<textarea placeholder="Введите текст запроса" style={{ resize: "none" }} value={ question } onChange={ (event) => { this._handle_onChangeField("question", event.target.value) } }/>
|
||
</div>
|
||
<FileDropzone files={ files } onAddFile={ this._handle_onAddFile } onDeleteFile={ this._handle_onDeleteFile }/>
|
||
<div className="form_field" style={{ paddingTop: 24, display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }}>
|
||
<button className="button button-blue" disabled={ this._checkDisabled() ? true : false }>Отправить</button>
|
||
</div>
|
||
</form>
|
||
)
|
||
}
|
||
|
||
render()
|
||
{
|
||
const { number } = this.props;
|
||
const { loading, success, themes, themes_filtered, opened_theme, opened_question } = this.state;
|
||
|
||
const procedure = themes_filtered !== undefined && themes_filtered !== null ? themes_filtered[ opened_theme ].questions[ opened_question ] : undefined;
|
||
|
||
console.log("themes", themes);
|
||
console.log("themes_filtered", themes_filtered);
|
||
|
||
return (
|
||
<React.Fragment>
|
||
<Head>
|
||
<title>ЛК Эволюция автолизинга</title>
|
||
<meta name="description" content="ЛК Эволюция автолизинга" />
|
||
</Head>
|
||
<Header { ...this.props } />
|
||
<AccountLayout>
|
||
<div className="title_wrapper">
|
||
<div className="left" style={{ alignItems: "center", flexWrap: "wrap" }}>
|
||
<button className="back" onClick={ this._handle_onBack }>Назад</button>
|
||
<h1 className="section_title">Новое обращение</h1>
|
||
</div>
|
||
<Company { ...this.props }/>
|
||
</div>
|
||
<div className="aside_container about">
|
||
{ loading ? (
|
||
<div className="container" style={{ display: "flex", alignItems: "center", justifyContent: "center", }}>
|
||
<SpinnerCircular size={ 90 } thickness={ 51 } speed={ 100 } color="rgba(28, 1, 169, 1)" secondaryColor="rgba(236, 239, 244, 1)" />
|
||
</div>
|
||
) : (
|
||
<>
|
||
{ success ? (
|
||
<SuccessMessage/>
|
||
) : (
|
||
<article className="full">
|
||
<div className="new_appeal">
|
||
<div className="column">
|
||
<div className="dropdown_blocks_list appeal_list visible">
|
||
{ themes_filtered !== undefined && themes_filtered !== null && themes_filtered.map((theme, theme_index) =>
|
||
(
|
||
<React.Fragment key={ `theme_${ theme_index }` }>
|
||
<div className={ `appeal_item dropdown_block ${ theme_index === opened_theme && "open" }` } style={ theme_index === opened_theme ? { backgroundColor: "unset"} : {} } onClick={ () => this._handle_onChangeTheme(theme_index) }>
|
||
<div className="block_header">
|
||
<p style={{ fontWeight: "bold" }}>{ theme.name }</p>
|
||
<button className="rotate"></button>
|
||
</div>
|
||
</div>
|
||
{ theme_index === opened_theme && theme.questions.map((question, question_index) => (
|
||
<div key={ `question_${ question_index }` } className={ `appeal_item dropdown_block ${ question_index === opened_question && "open" }` } style={{ paddingLeft: "20px" }} onClick={ () => this.setState({ opened_question: question_index }) }>
|
||
<div className="block_header">
|
||
<p>{ question.title }</p>
|
||
<button className="rotate"></button>
|
||
</div>
|
||
</div>
|
||
) )}
|
||
</React.Fragment>
|
||
)) }
|
||
</div>
|
||
</div>
|
||
{ themes_filtered !== undefined && themes_filtered !== null && (
|
||
<div className="column" ref={ this.formRef }>
|
||
<div className="column_text_block" >
|
||
<p><b>Процедура</b></p>
|
||
<p dangerouslySetInnerHTML={{ __html: procedure.answer }}/>
|
||
</div>
|
||
{ procedure.documents !== null && (
|
||
<div className="column_text_block">
|
||
<p><b>Документы</b></p>
|
||
<p dangerouslySetInnerHTML={{ __html: procedure.documents }}/>
|
||
</div>
|
||
) }
|
||
{ procedure.templates !== null && (
|
||
<div className="column_text_block">
|
||
<p><b>Документы</b></p>
|
||
<div className="dosc_list"> {/* medium-icon */}
|
||
{ procedure.templates.map((template, index) => (<TemplateFile key={ `template_${ index }` } template={ template } />)) }
|
||
</div>
|
||
</div>
|
||
) }
|
||
{ this._renderForm() }
|
||
</div>
|
||
) }
|
||
</div>
|
||
</article>
|
||
) }
|
||
</>
|
||
) }
|
||
</div>
|
||
</AccountLayout>
|
||
<Footer/>
|
||
</React.Fragment>
|
||
);
|
||
}
|
||
}
|
||
|
||
function mapStateToProps(state, ownProps)
|
||
{
|
||
return {
|
||
contracts: state.contracts.list,
|
||
themes: state.support.themes,
|
||
appeal: state.support.appeal,
|
||
};
|
||
}
|
||
|
||
export const getServerSideProps = reduxWrapper.getServerSideProps(
|
||
(store) =>
|
||
async ({ req, res, query }) => {
|
||
return {
|
||
props: {
|
||
},
|
||
};
|
||
}
|
||
);
|
||
|
||
export default withRouter(connect(mapStateToProps)(SupportRequestPage)); |