pre-publish test release

This commit is contained in:
merelendor 2022-08-09 16:18:42 +03:00
parent 78e4728a55
commit 6502705f3f
12 changed files with 377 additions and 159 deletions

View File

@ -8,4 +8,5 @@ export * from './navigationActions';
export * from './formsActions';
export * from './settingsActions';
export * from './announcementsActions';
export * from './eventsActions';
export * from './eventsActions';
export * from './supportActions';

80
actions/supportActions.js Normal file
View File

@ -0,0 +1,80 @@
import axios from 'axios';
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 getSupportThemes = ({ dispatch, query, }) =>
{
console.log("ACTION", "support", "getSupportThemes", { query });
return new Promise((resolve, reject) =>
{
axios.post(`${ process.env.NEXT_PUBLIC_SELF_API_HOST }/api/support/themes`, {
query
},
{
withCredentials: true,
})
.then(async (response) =>
{
console.log("getContractRules", "response.data", response.data);
dispatch({ type: actionTypes.SUPPORT_THEMES, data: { themes: response.data.themes } });
resolve();
})
.catch((error) =>
{
console.error(error);
reject();
});
});
}
export const sendNewAppeal = ({ name, phone, email, company }) =>
{
return new Promise((resolve, reject) =>
{
var formData = new FormData();
formData.append("form", "FORM_LEASING_REQUESTS");
formData.append("FORM_FIELD_FIO", name);
formData.append("FORM_FIELD_PHONE", phone);
formData.append("FORM_FIELD_EMAIL", email);
formData.append("FORM_FIELD_COMPANY", company);
formData.append("FORM_FIELD_PAGE_NAME", document.title);
formData.append("FORM_FIELD_PAGE_URL", window.location.href);
axios.post(`${ process.env.NEXT_PUBLIC_API_HOST }/api/forms/`, formData)
.then((response) =>
{
if(response.data.status === "complete")
{
resolve();
}
else
{
reject();
}
})
.catch((error) =>
{
console.error(error);
reject();
});
});
}

View File

@ -23,4 +23,9 @@ export const CONTRACT_CALCULATED = 'CONTRACT_CALCULATED';
export const CALENDAR = 'CALENDAR';
export const EVENTS = 'EVENTS';
export const EVENTS_FILTERED = 'EVENTS_FILTERED';
export const EVENTS_FILTERED = 'EVENTS_FILTERED';
export const SUPPORT_THEMES = 'SUPPORT_THEMES';
export const SUPPORT_THEMES_SEARCHED = 'SUPPORT_THEMES_SEARCHED';
export const SUPPORT_APPEALS = 'SUPPORT_APPEALS';
export const SUPPORT_APPEAL = 'SUPPORT_APPEAL';

View File

@ -32,7 +32,7 @@ module.exports = withImages(withFonts(withLess({
return [
{
source: '/support',
destination: '/support/faq/',
destination: '/support/faq',
permanent: false,
},
//{

View File

@ -0,0 +1,53 @@
// 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))
{
const response = await new Promise((resolve, reject) =>
{
axios.post(`${ process.env.NEXT_PUBLIC_API_HOST }/api/account/support/themes/`, {
query: req.body.query,
})
.then((api_response) =>
{
console.log("RESPONSE");
console.log(api_response.data);
resolve(api_response.data);
})
.catch((error) =>
{
console.log("error");
console.error(error);
reject([]);
});
});
res.status(200).json(response);
}
else
{
res.status(403);
}
}
else
{
res.status(403);
}
}
}

View File

@ -168,36 +168,32 @@ class Header extends React.Component
<ul id="menu_list" className={menuOpened ? "open" : ""}>
<li>
<Link href={`/`} shallow>
<a
className={ this.props.router && (this.props.router.route === "/" || this.props.router.route.indexOf("/contract") === 0) ? "active" : "" }
>
<a className={ this.props.router && (this.props.router.route === "/" || this.props.router.route.indexOf("/contract") === 0) ? "active" : "" }>
Договоры
</a>
</Link>
</li>
<li>
<Link href={`/documents/calendar/`} shallow>
<a
className={ this.props.router && this.props.router.route.indexOf("/documents/") === 0 ? "active" : "" }
>
<a className={ this.props.router && this.props.router.route.indexOf("/documents") === 0 ? "active" : "" }>
Взаиморасчеты и закрывающие документы
</a>
</Link>
</li>
<li>
<Link href="/settings/phone/" shallow>
<a
className={ this.props.router && this.props.router.route.indexOf("/settings/") === 0 ? "active" : "" }
>
<a className={ this.props.router && this.props.router.route.indexOf("/settings") === 0 ? "active" : "" }>
Настройки
</a>
</Link>
</li>
{/*}
<li>
<Link href={process.env.NEXT_PUBLIC_MAIN_SITE}>
<a>Обращения</a>
<Link href="/support/faq/" shallow>
<a className={ this.props.router && this.props.router.route.indexOf("/support") === 0 ? "active" : "" }>Обращения</a>
</Link>
</li>
{*/}
{/*
<li>
<Link href={ process.env.NEXT_PUBLIC_MAIN_SITE }>
@ -231,11 +227,15 @@ class Header extends React.Component
</div>
</div>
</li>
{/*}
<li>
<a data-icon="message" data-notify="0" className="button" onClick={ this._handle_onToggleMessages }>
Сообщения
</a>
<Link href="/support/" shallow>
<a data-icon="message" data-notify="0" className="button" onClick={ this._handle_onToggleMessages }>
Сообщения
</a>
</Link>
</li>
{*/}
<li>
<button
className="lk"

View File

@ -31,6 +31,11 @@ export default class InnerMenu extends React.Component
this.menuRef.current.scrollLeft = l - 50;
}
_handle_onNewAppeal = () =>
{
this.props.router.push('/support/new/');
}
render()
{
const { number } = this.props;
@ -42,17 +47,16 @@ export default class InnerMenu extends React.Component
</button>
<ul className="aside_nav" ref={ this.menuRef }>
<li>
<Link href={`/support/faq/`} shallow>
<a className={ this.props.router && this.props.router.asPath.indexOf("faq") > -1 ? "active" : "" }>FAQ</a>
</Link>
<Link href={`/support/faq`} shallow>
<a className={ this.props.router && this.props.router.asPath.indexOf("faq") > -1 ? "active" : "" }>FAQ</a></Link>
</li>
<li>
<Link href={`/support/messages/`} shallow>
<Link href={`/support/messages`} shallow>
<a className={ this.props.router && this.props.router.asPath.indexOf("messages") > -1 ? "active" : "" }>Мои обращения <span>3</span></a>
</Link>
</li>
</ul>
<button className="button button-blue new-appeal">Новое обращение</button>
<button className="button button-blue new-appeal" onClick={ this._handle_onNewAppeal }>Новое обращение</button>
</aside>
)
}

View File

@ -1,4 +1,5 @@
import React from "react";
import * as ReactDOM from 'react-dom';
import Head from "next/head";
import Image from "next/image";
import { connect } from "react-redux";
@ -18,6 +19,7 @@ import {
getContractAgreement,
getContractRules,
getFile,
getSupportThemes,
} from "../../actions";
class ContractPage extends React.Component
@ -26,54 +28,48 @@ class ContractPage extends React.Component
{
super(props);
this.state = {
date: null,
car: null,
contract_date: null,
agreement: null,
rules: null,
loading: false,
search: "",
themes: null,
question_selected: undefined,
appeals: null,
appeal: null,
searched: null,
loading: false,
search: "",
};
}
static getDerivedStateFromProps(nextProps, prevState)
{
return {
date: nextProps.date,
car: nextProps.car,
contract_date: nextProps.contract_date,
agreement: nextProps.agreement,
rules: nextProps.rules,
themes: nextProps.themes,
appeals: nextProps.appeals,
appeal: nextProps.appeal,
searched: nextProps.searched,
};
}
componentDidMount()
{
if (!this.state.loading && this.props.number !== undefined)
ReactDOM.findDOMNode(this).parentNode.style.height = "100%";
ReactDOM.findDOMNode(this).parentNode.style.display = "flex";
ReactDOM.findDOMNode(this).parentNode.style.flexDirection = "column";
ReactDOM.findDOMNode(this).parentNode.style.justifyContent = "spaceBetween";
ReactDOM.findDOMNode(this).parentNode.style.alignItems = "stretch";
document.documentElement.style.height = "100%";
document.documentElement.style.display = "flex";
document.documentElement.style.flexDirection = "column";
document.body.style.height = "100%";
document.body.style.display = "flex";
document.body.style.flexDirection = "column";
if (!this.state.loading)
{
this.setState({ loading: true }, () => {
getContractInfo({
dispatch: this.props.dispatch,
number: this.props.number,
})
.then((info) =>
this.setState({ loading: true }, () =>
{
getSupportThemes({ dispatch: this.props.dispatch })
.then(() =>
{
console.log("info", info);
getContractRules({
dispatch: this.props.dispatch,
date: moment(info.date, "YYYY-MM-DD").format("DD.MM.YYYY"),
})
.then(() => {})
.catch(() => {});
})
.catch(() => {});
getContractAgreement({
dispatch: this.props.dispatch,
number: this.props.number,
})
.then(() => {
this.setState({ loading: false });
})
.catch(() => {});
@ -81,10 +77,40 @@ class ContractPage extends React.Component
}
}
componentWillUnmount()
{
ReactDOM.findDOMNode(this).parentNode.style.height = "unset";
ReactDOM.findDOMNode(this).parentNode.style.display = "unset";
ReactDOM.findDOMNode(this).parentNode.style.flexDirection = "unset";
ReactDOM.findDOMNode(this).parentNode.style.justifyContent = "unset";
ReactDOM.findDOMNode(this).parentNode.style.alignItems = "unset";
document.documentElement.style.height = "unset";
document.documentElement.style.display = "unset";
document.documentElement.style.flexDirection = "unset";
document.body.style.height = "unset";
document.body.style.display = "unset";
document.body.style.flexDirection = "unset";
}
_handle_onQuestion = (question) =>
{
if(this.state.question_selected === question)
{
this.setState({ question_selected: undefined });
}
else
{
this.setState({ question_selected: question });
}
}
render()
{
const { loading, date, car, contract_date, agreement, rules, search } = this.state;
const { number } = this.props;
const { loading, themes, question_selected, searched, appleals, search } = this.state;
console.log("themesthemesthemesthemesthemes");
console.log(themes);
return (
<React.Fragment>
@ -93,7 +119,7 @@ class ContractPage extends React.Component
<meta name="description" content="ЛК Эволюция автолизинга" />
</Head>
<Header { ...this.props } />
<main>
<main style={{ flex: 1, display: "flex", flexDirection: "column" }}>
<section>
<div className="clear"></div>
<div className="container">
@ -106,91 +132,81 @@ class ContractPage extends React.Component
<div className="aside_container about">
<InnerMenu { ...this.props } />
<article>
<div className="contract_search">
<form
onSubmit={(event) => {
event.preventDefault();
}}
>
<div className="form_field full">
<input type="search" value={ search } placeholder="Поиск" onChange={(event) => { this._handle_onChange_search(event.target.value);} }
/>
</div>
<button className="button" disabled={ search !== "" ? false : true } onClick={ this._handle_onSearch }>
Поиск
</button>
</form>
</div>
<div className="list faq-list">
<div className="faq_item">
<p className="item_title">Изменение графика платежей</p>
<div className="dropdown_blocks_list">
<div className="dropdown_block">
<div className="block_header">
<p>
Как изменить график платежей по договору лизинга?
</p>
<button></button>
{ 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>
) : (
<>
<div className="contract_search">
<form onSubmit={(event) => { event.preventDefault(); }}>
<div className="form_field full">
<input type="search" value={ search } placeholder="Поиск" onChange={(event) => { this._handle_onChange_search(event.target.value);} }
/>
</div>
<div className="block_body">
<p>Lorem</p>
<button className="button" disabled={ search !== "" ? false : true } onClick={ this._handle_onSearch }>
Поиск
</button>
</form>
</div>
<div className="list faq-list">
{ themes !== undefined && themes !== null && themes.map((theme) =>
(
<div className="faq_item" key={ `theme_${ theme.id }` }>
<p className="item_title">{ theme.name }</p>
<div className="dropdown_blocks_list">
{ theme.questions.map((question) =>
(
<div className={ `dropdown_block ${ parseInt(question.id, 10) === parseInt(question_selected, 10) && "open" }` } key={ `question_${ question.id }` } onClick={ () => this._handle_onQuestion(question.id) }>
<div className={ `block_header` }>
<p>{ question.title }</p>
<button></button>
</div>
<div className="block_body">
<div className="column full">
<div className="column_text_block">
<p><b>Процедура</b></p>
<p dangerouslySetInnerHTML={{ __html: question.answer }}/>
</div>
{ question.documents !== null && (
<div className="column_text_block">
<p><b>Документы</b></p>
<p dangerouslySetInnerHTML={{ __html: question.documents }}/>
</div>
) }
{ question.templates !== null && (
<div className="column_text_block">
<p><b>Шаблоны документов</b></p>
<div className="dosc_list medium-icon">
{ question.templates.map((template, index) =>
(
<div className="row" key={ `template_${ index }` }>
<p className="doc_name i-pdf extension" data-format={ template.extension }>{ template.filename }</p>
</div>
)) }
</div>
</div>
) }
<button className="button button-blue">Создать обращение</button>
</div>
</div>
</div>
)) }
</div>
</div>
)) }
</div>
{/* Результат поиска */}
{ searched !== undefined && searched !== null && searched.map((question) => (
<div className="search_list" key={ `searched_${ question.id }` }>
<div className="search_item">
<p className="item_title">{ question.theme } / { question.name }</p>
<p>К каждой теме свободное <mark>html поле для миниинструкции</mark> (со ссылками на формы документов и документы). Привязка к теме обращения в CRM</p>
</div>
</div>
</div>
</div>
<div className="faq_item">
<p className="item_title">Переуступка прав</p>
<div className="dropdown_blocks_list">
<div className="dropdown_block">
<div className="block_header">
<p>Как получить консультацию по Цессии?</p>
<button></button>
</div>
<div className="block_body">
<p>Lorem</p>
</div>
</div>
<div className="dropdown_block">
<div className="block_header">
<p>
Оформление переуступки прав и обязательств по
договору лизинга
</p>
<button></button>
</div>
<div className="block_body">
<p>Lorem</p>
</div>
</div>
</div>
</div>
<div className="faq_item">
<p className="item_title">
Досрочное прекращение срока действия договора лизинга
</p>
<div className="dropdown_blocks_list">
<div className="dropdown_block">
<div className="block_header">
<p>
Процедура досрочного прекращения срока действия
договора лизинга
</p>
<button></button>
</div>
<div className="block_body">
<p>Lorem</p>
</div>
</div>
</div>
</div>
</div>
{/* Результат поиска */}
<div className="search_list">
<div className="search_item">
<p className="item_title">Изменение графика платежей / Как изменить график платежей по договору лизинга?</p>
<p>К каждой теме свободное <mark>html поле для миниинструкции</mark> (со ссылками на формы документов и документы). Привязка к теме обращения в CRM</p>
</div>
</div>
)) }
</>
) }
</article>
</div>
</div>
@ -205,11 +221,10 @@ class ContractPage extends React.Component
function mapStateToProps(state, ownProps)
{
return {
contract_date: state.contract.date,
date: state.contract.date,
car: state.contract.car,
agreement: state.contract.agreement,
rules: state.contract.rules,
themes: state.support.themes,
searched: state.support.searched,
appeals: state.support.appeals,
appeal: state.support.appeal,
};
}
@ -218,8 +233,6 @@ export const getServerSideProps = reduxWrapper.getServerSideProps(
async ({ req, res, query }) => {
return {
props: {
//number: query.number,
number: null,
},
};
}

View File

@ -47,20 +47,16 @@ class ContractPage extends React.Component
componentDidMount() {}
_handle_onBack = () =>
{
this.props.router.push('/support/faq/');
}
render()
{
const { loading, date, car, contract_date, agreement, rules } = this.state;
const { number } = this.props;
console.log("rules", rules);
const types = {
contracts: "Договор",
redemptions: "Выкупные документы",
agreements: "Дополнительное соглашение",
assignments: "Договор цессии",
};
return (
<React.Fragment>
<Head>
@ -74,7 +70,7 @@ class ContractPage extends React.Component
<div className="container">
<div className="title_wrapper">
<div className="left" style={{ alignItems: "center", flexWrap: "wrap" }}>
<button className="back">Назад</button>
<button className="back" onClick={ this._handle_onBack }>Назад</button>
<h1 className="section_title">Новое обращение</h1>
</div>
<Company />

View File

@ -69,6 +69,14 @@ export const defaultState = {
list: [],
filtered: undefined,
},
support:
{
themes: null,
searched: null,
appeals: null,
appeal: null,
request: null,
}
};
export default JSON.parse(JSON.stringify(defaultState));

View File

@ -0,0 +1,56 @@
import { HYDRATE } from 'next-redux-wrapper';
import * as actionTypes from '../constants/actionTypes';
import initialState from "./initialState";
const supportReducer = (state = initialState.support, action) =>
{
switch (action.type)
{
case HYDRATE:
{
return {
...state,
...action.payload.support,
};
}
case actionTypes.SUPPORT_THEMES:
{
return {
...state,
themes: action.data.themes,
};
}
case actionTypes.SUPPORT_THEMES_SEARCHED:
{
return {
...state,
searched: action.data.searched,
};
}
case actionTypes.SUPPORT_APPEALS:
{
return {
...state,
appeals: action.data.appeals,
};
}
case actionTypes.SUPPORT_APPEAL:
{
return {
...state,
appeal: action.data.appeal,
};
}
default: {
return state;
}
}
};
export default supportReducer;

View File

@ -8,6 +8,7 @@ import contractsReducer from '../reducers/contractsReducer';
import contractReducer from '../reducers/contractReducer';
import calendarReducer from '../reducers/calendarReducer';
import eventsReducer from '../reducers/eventsReducer';
import supportReducer from '../reducers/supportReducer';
const combinedReducer = combineReducers({
auth: authReducer,
@ -17,6 +18,7 @@ const combinedReducer = combineReducers({
contract: contractReducer,
calendar: calendarReducer,
events: eventsReducer,
support: supportReducer,
});
const makeStore = (context) =>