faq search, events search

This commit is contained in:
merelendor 2022-09-08 08:37:39 +03:00
parent 22cc5e3224
commit bb20056ca9
10 changed files with 464 additions and 78 deletions

View File

@ -88,7 +88,14 @@ export const getSupportThemes = ({ dispatch, query, }) =>
.then(async (response) =>
{
console.log("getContractRules", "response.data", response.data);
if(query !== undefined)
{
dispatch({ type: actionTypes.SUPPORT_THEMES_FILTERED, data: { filtered: response.data.themes } });
}
else
{
dispatch({ type: actionTypes.SUPPORT_THEMES, data: { themes: response.data.themes } });
}
resolve();
})
@ -100,6 +107,15 @@ export const getSupportThemes = ({ dispatch, query, }) =>
});
}
export const resetFilteredThemes = ({ dispatch }) =>
{
return new Promise((resolve, reject) =>
{
dispatch({ type: actionTypes.SUPPORT_THEMES_FILTERED, data: { filtered: null } });
resolve();
});
}
export const sendNewAppeal = (appeal) =>
{
console.log("ACTION", "support", "sendNewAppeal", appeal);

View File

@ -35,7 +35,7 @@ export const EVENTS_FILTERED = 'EVENTS_FILTERED';
export const EVENTS_RESET = 'EVENTS_RESET';
export const SUPPORT_THEMES = 'SUPPORT_THEMES';
export const SUPPORT_THEMES_SEARCHED = 'SUPPORT_THEMES_SEARCHED';
export const SUPPORT_THEMES_FILTERED = 'SUPPORT_THEMES_FILTERED';
export const SUPPORT_APPEALS = 'SUPPORT_APPEALS';
export const SUPPORT_APPEAL = 'SUPPORT_APPEAL';
export const SUPPORT_RESET = 'SUPPORT_RESET';

View File

@ -5014,3 +5014,89 @@ main .dropdown_blocks_list.zero-margin.gibdd .dropdown_block .block_body .fines_
min-width: 195px;
}
}
@media all and (max-width: 1280px) {
.contract_payments_status_cell {
border-top: unset!important;
}
}
@media all and (max-width: 1280px) {
.contract_payments_invoices_cell {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
}
.contract_payments_invoices_cell p {
white-space: nowrap!important;
display: flex;
}
.contract_payments_invoices_cell p span {
white-space: nowrap;
padding-left: 10px;
margin-top: 0px!important;
}
}
@media all and (max-width: 900px) {
.contract_payments_invoices_cell {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
}
.contract_payments_invoices_cell p {
white-space: normal!important;
display: flex;
flex-direction: column;
}
.contract_payments_invoices_cell p span {
padding-left: 0px;
}
}
@media all and (max-width: 768px) {
.contract_payments_invoices_cell {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
}
.contract_payments_invoices_cell p {
flex-direction: row;
white-space: nowrap!important;
display: flex;
}
.contract_payments_invoices_cell p span {
white-space: nowrap;
padding-left: 10px;
margin-top: 0px!important;
}
}
@media all and (max-width: 400px) {
.contract_payments_invoices_cell {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
}
.contract_payments_invoices_cell p {
white-space: normal!important;
display: flex;
flex-direction: column;
}
.contract_payments_invoices_cell p span {
padding-left: 0px;
}
}
.search_form_buttons_wrapper {
flex-direction: row;
display: flex;
align-items: flex-start;
justify-content: flex-end;
margin-left: auto;
}
.search_form_buttons_wrapper .button {
display: block!important;
}

View File

@ -5663,3 +5663,98 @@ main .dropdown_blocks_list .dropdown_block .block_body {
min-width: 195px;
}
}
.contract_payments_status_cell {
@media all and (max-width: 1280px) {
border-top: unset!important;
}
}
.contract_payments_invoices_cell {
@media all and (max-width: 1280px) {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
p {
white-space: nowrap!important;
display: flex;
span {
white-space: nowrap;
padding-left: 10px;
margin-top: 0px!important;
}
}
}
@media all and (max-width: 900px) {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
p {
white-space: normal!important;
display: flex;
flex-direction: column;
span {
padding-left: 0px;
}
}
}
@media all and (max-width: 768px) {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
p {
flex-direction: row;
white-space: nowrap!important;
display: flex;
span {
white-space: nowrap;
padding-left: 10px;
margin-top: 0px!important;
}
}
}
@media all and (max-width: 400px) {
flex-direction: column;
width: 100%;
display: flex;
white-space: nowrap;
min-width: 100% !important;
p {
white-space: normal!important;
display: flex;
flex-direction: column;
span {
padding-left: 0px;
}
}
}
}
.search_form_buttons_wrapper {
flex-direction: row;
display: flex;
align-items: flex-start;
justify-content: flex-end;
margin-left: auto;
.button {
display: block!important;
}
}

View File

@ -256,7 +256,7 @@ class ContractSchedulePage extends React.Component
<div className="table_cell" data-title="от">{ moment(payment.date, "DD-MM-YYYY").format("DD.MM.YYYY") }</div>
<div className="table_cell" data-title="На сумму" style={{ whiteSpace: "nowrap" }}>{ numeral(payment.total_amount).format(' ., ') }&nbsp;</div>
<div className="table_cell" data-title="НДС, 20%" style={{ whiteSpace: "nowrap" }}>{ numeral(payment.vat_amount).format(' ., ') }&nbsp;</div>
<div className="table_cell">
<div className="table_cell contract_payments_status_cell">
{ payment.status === "Paid" && "Оплачено" }
{ payment.status === "NotPaid" && "Не оплачено" }
{ payment.status === "HalfPaid" && (
@ -266,11 +266,11 @@ class ContractSchedulePage extends React.Component
<><span>Переплата</span> <span style={{ whiteSpace: "nowrap" }}>{ numeral(payment.total_amount).format(' ., ') }&nbsp;</span></>
) }
</div>
<div className="table_cell">
<div className="table_cell contract_payments_invoices_cell">
{ payment.invoices !== undefined && payment.invoices.map((invoice, invoice_index) =>
(
<React.Fragment key={invoice_index}>
<p style={{ paddingBottom: "15px", lineHeight: "18px" }}>{ invoice.number } от { moment(invoice.date, "DD-MM-YYYY").format("DD.MM.YYYY") } на сумму <span style={{ whiteSpace: "nowrap" }}>{ numeral(invoice.total_amount).format(' ., ') }&nbsp;</span></p>
<p style={{ paddingBottom: "15px", lineHeight: "18px" }}>{ invoice.number } от { moment(invoice.date, "DD-MM-YYYY").format("DD.MM.YYYY") } на сумму: <span style={{ whiteSpace: "nowrap" }}>{ numeral(invoice.total_amount).format(' ., ') }&nbsp;</span></p>
</React.Fragment>
)) }
</div>

View File

@ -39,7 +39,8 @@ class EventsPage extends React.Component
"return_pts": "pts",
},
loading: false,
search: ""
search: "",
searched: false,
};
}
@ -88,8 +89,19 @@ class EventsPage extends React.Component
}
_handle_onSearch = () =>
{
this.setState({ searched: true }, () =>
{
this._filterEvents();
});
}
_handle_onSearchReset = () =>
{
this.setState({ searched: false, search: "" }, () =>
{
this._filterEvents();
});
}
_filterEvents = () =>
@ -133,7 +145,7 @@ class EventsPage extends React.Component
render()
{
const { loading, type, types, search, events, events_loaded, filtered } = this.state;
const { loading, type, types, search, events, events_loaded, filtered, searched } = this.state;
console.log("events", "type", type);
return (
@ -160,7 +172,12 @@ class EventsPage extends React.Component
this._handle_onChange_search(event.target.value);
} }/>
</div>
<button className="button" disabled={ search !== "" ? false : true } onClick={ this._handle_onSearch }>Поиск</button>
<div className="search_form_buttons_wrapper">
<button className="button search_button" disabled={ search !== "" ? false : true } onClick={ this._handle_onSearch }>Поиск</button>
{ searched && (
<button className="button" onClick={ this._handle_onSearchReset }>Сбросить</button>
) }
</div>
</form>
</div>
{ events_loaded && (

View File

@ -17,6 +17,7 @@ import TemplateFile from "./components/TemplateFile";
import {
getSupportThemes,
resetFilteredThemes,
} from "../../actions";
class ContractPage extends React.Component
@ -29,9 +30,11 @@ class ContractPage extends React.Component
question_selected: undefined,
appeals: null,
appeal: null,
searched: null,
filtered: null,
loading: false,
search: "",
query: "",
searched: false,
};
this.question_refs = [];
@ -43,7 +46,7 @@ class ContractPage extends React.Component
themes: nextProps.themes,
appeals: nextProps.appeals,
appeal: nextProps.appeal,
searched: nextProps.searched,
filtered: nextProps.filtered,
};
}
@ -63,9 +66,10 @@ class ContractPage extends React.Component
{
for(let q in this.state.themes[t].questions)
{
this.question_refs[this.state.themes[t].questions[q].id] = React.createRef();
if(this.props.router.asPath.indexOf(this.state.themes[t].questions[q].id) > -1)
{
this.question_refs[this.state.themes[t].questions[q].id] = React.createRef();
question_selected = this.state.themes[t].questions[q].id;
found = true;
}
@ -113,13 +117,89 @@ class ContractPage extends React.Component
_handle_onChangeSearchQuery = (value) =>
{
this.setState({ query: value });
this.setState({ search: value });
}
_handle_onSearch = () =>
{
const { search } = this.state;
this.setState({ searched: true, query: search }, () =>
{
getSupportThemes({ dispatch: this.props.dispatch, query: search })
.then(() =>
{
});
});
}
_handle_onSearchReset = () =>
{
this.setState({ searched: false, search: "", query: "" });
}
_handle_onLocalLink = (id) =>
{
const { dispatch } = this.props;
window.location = `/support/faq#${ id }`;
this.setState({ searched: false, search: "", query: "", question_selected: id }, () =>
{
resetFilteredThemes({ dispatch });
setTimeout(() => {
this.question_refs[id].current.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
}, 100);
});
}
_getMarks = (content) =>
{
const { query } = this.state;
const search_array = query.split(" ");
const chunks = [];
const content_array = content.split(" ");
for(let i in content_array)
{
let found = false;
for(let s in search_array)
{
if(content_array[i].toLowerCase() == search_array[s].toLowerCase())
{
found = true;
}
}
if(found)
{
chunks.push(`<mark>${ content_array[i] }</mark>`);
}
else
{
chunks.push(content_array[i]);
}
}
return chunks.join(" ");
}
render()
{
const { loading, themes, question_selected, searched, appleals, query } = this.state;
console.log("themes", themes);
//filtered,
const { loading, themes, filtered, question_selected, searched, appleals, search } = this.state;
let filtered_count = 0;
if(filtered !== undefined && filtered !== null)
{
for(let i in filtered)
{
filtered_count = filtered_count + filtered[i].questions.length;
}
}
return (
<React.Fragment>
@ -147,13 +227,46 @@ class ContractPage extends React.Component
<div className="contract_search">
<form onSubmit={(event) => { event.preventDefault(); }}>
<div className="form_field full">
<input type="search" value={ query } placeholder="Поиск" onChange={(event) => { this._handle_onChangeSearchQuery(event.target.value);} }/>
<input type="search" value={ search } placeholder="Поиск" onChange={(event) => { this._handle_onChangeSearchQuery(event.target.value);} }/>
</div>
<div className="search_form_buttons_wrapper">
<button className="button search_button" disabled={ search !== "" ? false : true } onClick={ this._handle_onSearch }>Поиск</button>
{ searched && (
<button className="button" onClick={ this._handle_onSearchReset }>Сбросить</button>
) }
</div>
<button className="button" disabled={ query !== "" ? false : true } onClick={ this._handle_onSearch }>
Поиск
</button>
</form>
</div>
{/* Результат поиска */}
{ searched && filtered !== undefined && filtered !== null ? (
<>
{ filtered_count > 0 ? (
<>
{ filtered.map((theme, theme_index) => (
<React.Fragment key={ theme_index }>
{ theme.questions.map((question, index) =>
(
<div className="search_list" key={ `searched_${ question.id }` }>
<div className="search_item">
<p className="item_title" dangerouslySetInnerHTML={{ __html: this._getMarks(`${ theme.name } / ${ question.title }`) }}></p>
<p dangerouslySetInnerHTML={{ __html: this._getMarks(`${ theme.name } / ${ question.answer }`) }}/>
{/*}
<p>К каждой теме свободное <mark>html поле для миниинструкции</mark> (со ссылками на формы документов и документы). Привязка к теме обращения в CRM</p>
{*/}
<div style={{ display: "flex", flexDirection: "row", alignItems: "flex-end", justifyContent: "flex-end" }}>
<a className="interactive" onClick={ () => { this._handle_onLocalLink(question.id) } }>Подробнее</a>
</div>
</div>
</div>
)) }
</React.Fragment>
)) }
</>
) : (
<p>К сожалению по Вашему запросу нет результатов.</p>
) }
</>
) : (
<div className="list faq-list">
{ themes !== undefined && themes !== null && themes.map((theme) =>
(
@ -198,15 +311,7 @@ class ContractPage extends React.Component
</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>
)) }
) }
</>
) }
</article>
@ -222,7 +327,7 @@ function mapStateToProps(state, ownProps)
{
return {
themes: state.support.themes,
searched: state.support.searched,
filtered: state.support.filtered,
appeals: state.support.appeals,
appeal: state.support.appeal,
};

View File

@ -81,6 +81,7 @@ class SupportRequestPage extends React.Component
phone: "",
email: "",
question: "",
all_contracts: false,
selected_contracts: [],
file: null,
files: [],
@ -200,11 +201,17 @@ class SupportRequestPage extends React.Component
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: selected_contracts
contract_numbers: contract_numbers
})
.then((result) =>
{
@ -245,12 +252,62 @@ class SupportRequestPage extends React.Component
_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) =>
@ -302,9 +359,11 @@ class SupportRequestPage extends React.Component
_renderForm = () =>
{
const { loading, contracts, themes, themes_filtered, name, phone, email, question, file, files, opened_theme, opened_question } = this.state;
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({
@ -312,6 +371,13 @@ class SupportRequestPage extends React.Component
});
}
contracts_list.unshift({ value: null, label: "Все договоры" });
}
else
{
contracts_list.unshift({ value: null, label: "Все договоры" });
}
return (
<form onSubmit={ this._handle_onSendAppeal }>
<div className="form_field">
@ -322,6 +388,7 @@ class SupportRequestPage extends React.Component
classNamePrefix="custom-select"
placeholder="Выберите договоры"
onChange={ this._handle_onContract }
value={ all_contracts ? contracts_list : selected_contracts }
/>
</div>
<div className="form_field">

View File

@ -94,7 +94,7 @@ export const defaultState = {
support:
{
themes: null,
searched: null,
filtered: null,
appeals: { list: null, new: 0 },
appeal: null,
request: null,

View File

@ -23,11 +23,11 @@ const supportReducer = (state = initialState.support, action) =>
};
}
case actionTypes.SUPPORT_THEMES_SEARCHED:
case actionTypes.SUPPORT_THEMES_FILTERED:
{
return {
...state,
searched: action.data.searched,
filtered: action.data.filtered,
};
}