account updates implementation, initial

This commit is contained in:
merelendor 2022-07-13 10:10:29 +03:00
parent 19a8d0c3dd
commit f97a09661f
22 changed files with 4965 additions and 597 deletions

View File

@ -0,0 +1,33 @@
import axios from 'axios';
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 getAnnouncements = () =>
{
return new Promise((resolve, reject) =>
{
axios.get(`${ process.env.NEXT_PUBLIC_API_HOST }/api/announcements/`)
.then((response) =>
{
resolve(response.data.announcements);
})
.catch((error) =>
{
console.error(error);
});
});
}

47
actions/eventsActions.js Normal file
View File

@ -0,0 +1,47 @@
import axios from 'axios';
import { Cookies } from 'react-cookie';
import Router from 'next/router';
import moment from 'moment';
import * as actionTypes from '../constants/actionTypes';
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 getEvents = ({ dispatch }) =>
{
console.log("getEvents");
return new Promise((resolve, reject) =>
{
axios.post(`${ process.env.NEXT_PUBLIC_SELF_API_HOST }/api/events`, {}, {
withCredentials: true,
})
.then((response) =>
{
console.log("getEvents", "response", response.data);
dispatch({ type: actionTypes.EVENTS, data: { list: response.data } });
resolve();
})
.catch((error) =>
{
console.log("error");
console.error(error);
reject();
});
});
}

View File

@ -22,6 +22,20 @@ if(process.browser)
};
}
export const getImage = async ({ id }) =>
{
console.log("getImage");
const response = await axios.get(`${ process.env.NEXT_PUBLIC_SELF_API_HOST }/api/file/image`, {
params: { id },
responseType: 'blob',
});
console.log("getImage", "response");
console.log(response);
return response;
}
export const getFile = ({ id, filename }) =>
{
console.log("getFile");

View File

@ -7,3 +7,5 @@ export * from './fileActions';
export * from './navigationActions';
export * from './formsActions';
export * from './settingsActions';
export * from './announcementsActions';
export * from './eventsActions';

View File

@ -14,3 +14,4 @@ export const CONTRACT_DOCUMENTS = 'CONTRACT_DOCUMENTS';
export const CONTRACT_RULES = 'CONTRACT_RULES';
export const CONTRACT_MATERIALS = 'CONTRACT_MATERIALS';
export const CALENDAR = 'CALENDAR';
export const EVENTS = 'EVENTS';

File diff suppressed because one or more lines are too long

View File

@ -2917,6 +2917,9 @@ main {
&[data-additional-service="7"] {
background: url("/assets/images/icons/additional-service-7.svg") no-repeat bottom center;
}
&[data-additional-service="8"] {
background: url("/assets/images/icons/additional-service-8.svg") no-repeat bottom center;
}
}
}
}

51
pages/api/events.js Normal file
View File

@ -0,0 +1,51 @@
// 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)
{
var client_jwt_decoded = jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT);
var crm_jwt = jwt.sign(client_jwt_decoded, process.env.JWT_SECRET_CRM, { noTimestamp: true });
try
{
axios.get(`${ process.env.CRM_API_HOST }/lk/Account/GetEvents/`, {
params: client_jwt_decoded,
headers: {
"Authorization": `Bearer ${ crm_jwt }`,
}
})
.then((crm_response) =>
{
res.status(200).json(crm_response.data);
})
.catch((error) =>
{
console.error(error);
res.status(500);
});
}
catch(e)
{
console.error(e);
res.status(500);
}
}
else
{
res.status(403);
}
}
}

53
pages/api/file/image.js Normal file
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)
{
var client_jwt_decoded = jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT);
var crm_jwt = jwt.sign(client_jwt_decoded, process.env.JWT_SECRET_CRM, { noTimestamp: true });
try
{
console.log("/lk/GetImage", "req.query.id", req.query.id);
axios.get(`${ process.env.CRM_API_HOST }/lk/GetImage`, {
params: { id: req.query.id },
//responseType: 'arraybuffer',
headers: {
"Authorization": `Bearer ${ crm_jwt }`,
}
})
.then((crm_response) =>
{
res.status(200).send(crm_response.data);
})
.catch((error) =>
{
console.error(error);
res.status(500);
});
}
catch(e)
{
console.error(e);
res.status(500);
}
}
else
{
res.status(403);
}
}
}

View File

@ -0,0 +1,115 @@
import React from "react";
import { connect } from "react-redux";
import Slider from "react-slick";
import { getAnnouncements } from "../../../actions";
function NextArrow(props)
{
const { className, style, onClick } = props;
return (
<button
className={ className }
style={{ ...style }}
onClick={ onClick }
>
<svg width={ 8 } height={ 12 } fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="m1 1 6 5-5.25 5"
stroke="#fff"
strokeWidth={ 2 }
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
function PrevArrow(props)
{
const { className, style, onClick } = props;
return (
<button
className={ className }
style={{ ...style }}
onClick={ onClick }
>
<svg width={ 8 } height={ 12 } fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7 11 1 6l5.25-5"
stroke="#fff"
strokeWidth={ 2 }
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
class AnnouncementsList extends React.Component
{
constructor(props)
{
super(props);
this.state = {
company: {},
announcements: [],
};
}
componentDidMount()
{
getAnnouncements().then((announcements) => this.setState({ announcements: announcements })).catch(() => {});
}
static getDerivedStateFromProps(nextProps, prevState)
{
return {};
}
render()
{
const settings = {
dots: false,
infinite: false,
speed: 500,
slidesToShow: 2,
slidesToScroll: 1,
centerMode: false,
variableWidth: false,
nextArrow: <NextArrow />,
prevArrow: <PrevArrow />,
};
const { announcements } = this.state;
return (
<div className="feed">
<div className="feed_list">
<Slider { ...settings }>
{ announcements.map(( announcement, index ) => (
<div className="feed_item" key={ index }>
<p className="item_title">{ announcement.title }</p>
<p className="item_desc" dangerouslySetInnerHTML={{ __html: announcement.content }}></p>
{ announcement.url !== null && (
<a href={ announcement.url } className="item_link">Подробнее</a>
) }
</div>
)) }
</Slider>
</div>
</div>
);
}
}
function mapStateToProps(state, ownProps)
{
return {};
}
export default connect(mapStateToProps)(AnnouncementsList);

View File

@ -8,6 +8,7 @@ class Company extends React.Component
super(props);
this.state = {
company: {},
opened: false,
}
}
@ -18,18 +19,27 @@ class Company extends React.Component
};
}
_handleOnClick = () =>
{
const { opened } = this.state;
this.setState({ opened: !opened ? true : false })
}
render()
{
const { company } = this.state;
const { company, opened } = this.state;
console.log(this.props);
return (
<div className="right company-dropdown">
<div className="right company-dropdown" onClick={ this._handleOnClick }>
<p align="right" className="arrow">
<b>{ company.title }</b><br/>
{company.inn != null && <span>ИНН: { company.inn } </span>}
{company.kpp != null && <span>КПП: { company.kpp }</span>}
</p>
<div className="companies_list"> {/* opened */}
<div className={`companies_list ${ opened && "opened" }`}> {/* opened */}
<div className="company_item">
<p align="right">
<b>ООО ЮКС</b><br/>

View File

@ -1,141 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import Slider from "react-slick";
function NextArrow(props) {
const { className, style, onClick } = props;
return (
<button
className={className}
style={{ ...style }}
onClick={onClick}
>
<svg width={8} height={12} fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="m1 1 6 5-5.25 5"
stroke="#fff"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
function PrevArrow(props) {
const { className, style, onClick } = props;
return (
<button
className={className}
style={{ ...style }}
onClick={onClick}
>
<svg width={8} height={12} fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7 11 1 6l5.25-5"
stroke="#fff"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
class Feed extends React.Component {
constructor(props) {
super(props);
this.state = {
company: {},
};
}
static getDerivedStateFromProps(nextProps, prevState) {
return {};
}
render() {
const settings = {
dots: false,
infinite: false,
speed: 500,
slidesToShow: 2,
slidesToScroll: 1,
centerMode: false,
variableWidth: false,
nextArrow: <NextArrow />,
prevArrow: <PrevArrow />,
};
return (
<div className="feed">
<div className="feed_list">
<Slider {...settings}>
<div className="feed_item">
<p className="item_title">Чебоксары и Кемерово</p>
<p className="item_desc">
Укрепились на Волге и дотянулись до Кузбасса.Лизинговая компания
Эволюция ...
</p>
<a href="" className="item_link">
Подробнее
</a>
</div>
<div className="feed_item">
<p className="item_title">Чебоксары и Кемерово</p>
<p className="item_desc">
Укрепились на Волге и дотянулись до Кузбасса.Лизинговая компания
Эволюция ...
</p>
<a href="" className="item_link">
Подробнее
</a>
</div>
<div className="feed_item">
<p className="item_title">Чебоксары и Кемерово</p>
<p className="item_desc">
Укрепились на Волге и дотянулись до Кузбасса.Лизинговая компания
Эволюция ...
</p>
<a href="" className="item_link">
Подробнее
</a>
</div>
<div className="feed_item">
<p className="item_title">Чебоксары и Кемерово</p>
<p className="item_desc">
Укрепились на Волге и дотянулись до Кузбасса.Лизинговая компания
Эволюция ...
</p>
<a href="" className="item_link">
Подробнее
</a>
</div>
<div className="feed_item">
<p className="item_title">Чебоксары и Кемерово</p>
<p className="item_desc">
Укрепились на Волге и дотянулись до Кузбасса.Лизинговая компания
Эволюция ...
</p>
<a href="" className="item_link">
Подробнее
</a>
</div>
</Slider>
</div>
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {};
}
export default connect(mapStateToProps)(Feed);

View File

@ -2,8 +2,10 @@ import React from "react";
import { connect } from "react-redux";
import Slider from "react-slick";
function NextArrow(props) {
function NextArrow(props)
{
const { className, style, onClick } = props;
return (
<button
className={ className }
@ -23,8 +25,10 @@ function NextArrow(props) {
);
}
function PrevArrow(props) {
function PrevArrow(props)
{
const { className, style, onClick } = props;
return (
<button
className={ className }
@ -44,19 +48,24 @@ function PrevArrow(props) {
);
}
class FeedUsers extends React.Component {
constructor(props) {
class FeedUsers extends React.Component
{
constructor(props)
{
super(props);
this.state = {
company: {},
};
}
static getDerivedStateFromProps(nextProps, prevState) {
static getDerivedStateFromProps(nextProps, prevState)
{
return {};
}
render() {
render()
{
const settings = {
dots: false,
infinite: false,
@ -82,7 +91,6 @@ class FeedUsers extends React.Component {
</p>
</div>
</div>
<div className="feed_item user">
<img src="/assets/images/icons/avatar.svg" alt="" />
<div>
@ -92,7 +100,6 @@ class FeedUsers extends React.Component {
</p>
</div>
</div>
<div className="feed_item user">
<img src="/assets/images/icons/avatar.svg" alt="" />
<div>
@ -102,7 +109,6 @@ class FeedUsers extends React.Component {
</p>
</div>
</div>
<div className="feed_item user">
<img src="/assets/images/icons/avatar.svg" alt="" />
<div>
@ -112,7 +118,6 @@ class FeedUsers extends React.Component {
</p>
</div>
</div>
<div className="feed_item user">
<img src="/assets/images/icons/avatar.svg" alt="" />
<div>
@ -122,7 +127,6 @@ class FeedUsers extends React.Component {
</p>
</div>
</div>
<div className="feed_item user">
<img src="/assets/images/icons/avatar.svg" alt="" />
<div>
@ -139,7 +143,8 @@ class FeedUsers extends React.Component {
}
}
function mapStateToProps(state, ownProps) {
function mapStateToProps(state, ownProps)
{
return {};
}

View File

@ -0,0 +1,128 @@
import React from "react";
import Link from "next/link";
import moment from 'moment';
import pluralize from 'pluralize-ru';
export default class NotificationsList extends React.Component
{
constructor(props)
{
super(props);
this.state = {
};
}
_renderEvent = (event, index) =>
{
switch(event.event_type)
{
case "kasko_prolong":
{
const days_left = moment(event.event_date).diff(moment().startOf("day"), 'days');
return (
<li className="new" key={ index }>
<p className="name"><b>Внимание! { days_left === 0 ? "Сегодня последний день" : `${ pluralize(days_left, "Осталось", "Остался", "Осталось", "Осталось") } ${ days_left } ${ pluralize(days_left, "дней", "день", "дня", "дней") } до` } пролонгации КАСКО по договору { event.contract_number } (полис { event.add_info }). Не забудьте самостоятельно продлить полис КАСКО!</b></p>
<p className="date">{ event.event_date }</p>
<p className="action">
<Link href={`/contract/${ event.contract_number }/services#insurance`}>
<a>Подробнее</a>
</Link>
</p>
</li>
)
}
case "osago_prolong":
{
const days_left = moment(event.event_date).diff(moment().startOf("day"), 'days');
return (
<li className="new" key={ index }>
<p className="name"><b>Внимание! { days_left === 0 ? "Сегодня последний день" : `${ pluralize(days_left, "Осталось", "Остался", "Осталось", "Осталось") } ${ days_left } ${ pluralize(days_left, "дней", "день", "дня", "дней") } до` } пролонгации ОСАГО по договору { event.contract_number } (полис { event.add_info }). Не забудьте самостоятельно продлить полис ОСАГО!</b></p>
<p className="date">{ event.event_date }</p>
<p className="action">
<Link href={`/contract/${ event.contract_number }/services#insurance`}>
<a>Подробнее</a>
</Link>
</p>
</li>
)
}
case "fingap_prolong":
{
const days_left = moment(event.event_date).diff(moment().startOf("day"), 'days');
return (
<li className="new" key={ index }>
<p className="name"><b>Внимание! { days_left === 0 ? "Сегодня последний день" : `${ pluralize(days_left, "Осталось", "Остался", "Осталось", "Осталось") } ${ days_left } ${ pluralize(days_left, "дней", "день", "дня", "дней") } до` } пролонгации FinGAP по договору { event.contract_number }. Не забудьте самостоятельно продлить полис FinGAP!</b></p>
<p className="date">{ event.event_date }</p>
<p className="action">
<Link href={`/contract/${ event.contract_number }/services#insurance`}>
<a>Подробнее</a>
</Link>
</p>
</li>
)
}
}
return null;
{/*}
<li className="new" key={ index }>
<p className="name"><b>Внимание! Просрочена дата возврата СТС по договору: ХХ.ХХ.ХХХХ</b></p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="/">
<a>Посмотрите порядок возврата СТС</a>
</Link>
&nbsp; или &nbsp;
<Link href="/">
<a>Загрузите скан СТС</a>
</Link>
</p>
</li>
<li className="new">
<p className="name"><b>Внимание! Просрочена дата возврата СТС по договору: ХХ.ХХ.ХХХХ</b></p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="/">
<a>Посмотрите порядок возврата СТС</a>
</Link>
&nbsp; или &nbsp;
<Link href="/">
<a>Загрузите скан СТС</a>
</Link>
</p>
</li>
<li className="new">
<p className="name">Внимание! Осталось ХХ дней до пролонгации КАСКО по договору ХХХХ: ХХ.ХХ.ХХХХ. Не забудьте самостоятельно продлить полис ОСАГО</p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="/">
<a>Подробнее</a>
</Link>
</p>
</li>
{*/}
}
render()
{
const { events } = this.props;
console.log("events", events);
return (
<>
<ul className="list">
{ events.map((event, index) => this._renderEvent(event, index)) }
</ul>
<Link href="/events">
<a className="all">Все события</a>
</Link>
</>
);
}
}

View File

@ -1,42 +1,139 @@
import React from "react";
import Link from "next/link";
import { connect } from "react-redux";
import { logout } from "../../../actions";
import { logout, getEvents } from "../../../actions";
import NotificationsList from "./NotificationsList";
export default class Header extends React.Component {
constructor(props) {
class Header extends React.Component
{
constructor(props)
{
super(props);
this.state = {
menuOpened: false,
notificationsOpened: false,
messagesOpened: false,
events: [],
};
}
_handle_onToggleMenu = () => {
this.setState({
menuOpened: !this.state.menuOpened,
});
static getDerivedStateFromProps(nextProps, prevState)
{
return {
events: nextProps.events,
};
}
_getActiveLink = (route) => {
componentDidMount()
{
getEvents({ dispatch: this.props.dispatch });
}
_getActiveLink = (route) =>
{
if (route === "/") return "Договоры";
if (route.indexOf("/documents/") > -1)
return "Взаиморасчеты и закрывающие документы";
if (route.indexOf("/documents/") > -1) return "Взаиморасчеты и закрывающие документы";
if (route.indexOf("/settings/") > -1) return "Настройки";
if (route.indexOf("/contract") === 0) return "Договоры";
return null;
};
_handle_onLogout = () => {
_handle_onToggleMenu = () =>
{
this.setState({
menuOpened: !this.state.menuOpened,
});
};
_handle_onLogout = () =>
{
logout({ dispatch: this.props.dispatch });
};
render() {
const { menuOpened } = this.state;
_handle_onToggleNotifications = () =>
{
const { notificationsOpened } = this.state;
this.setState({ notificationsOpened: notificationsOpened ? false : true });
}
_handle_onToggleMessages = () =>
{
const { messagesOpened } = this.state;
this.setState({ messagesOpened: messagesOpened ? false : true });
}
_renderEvent = (event, index) =>
{
switch(event.event_type)
{
case "kasko_prolong":
{
return (
<li className="new" key={ index }>
<p className="name"><b>Тут бы какое-то сообщение передавать а не просто параметры, непонятно СКОЛЬКО дней осталось ДО окончания КАСКО. Или параметр kasko_end. Договор: { event.contract_number }, полис: { event.add_info }</b></p>
<p className="date">{ event.event_date }</p>
<p className="action">
<Link href={`/${ event.contract_number }`}>
<a>Подробнее</a>
</Link>
</p>
</li>
)
}
}
return null;
{/*}
<li className="new" key={ index }>
<p className="name"><b>Внимание! Просрочена дата возврата СТС по договору: ХХ.ХХ.ХХХХ</b></p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="/">
<a>Посмотрите порядок возврата СТС</a>
</Link>
&nbsp; или &nbsp;
<Link href="/">
<a>Загрузите скан СТС</a>
</Link>
</p>
</li>
<li className="new">
<p className="name"><b>Внимание! Просрочена дата возврата СТС по договору: ХХ.ХХ.ХХХХ</b></p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="/">
<a>Посмотрите порядок возврата СТС</a>
</Link>
&nbsp; или &nbsp;
<Link href="/">
<a>Загрузите скан СТС</a>
</Link>
</p>
</li>
<li className="new">
<p className="name">Внимание! Осталось ХХ дней до пролонгации КАСКО по договору ХХХХ: ХХ.ХХ.ХХХХ. Не забудьте самостоятельно продлить полис ОСАГО</p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="/">
<a>Подробнее</a>
</Link>
</p>
</li>
{*/}
}
render()
{
const { menuOpened, notificationsOpened, messagesOpened, events } = this.state;
console.log("events", events);
return (
<header>
<div className="container">
<a href="/" className="logo">
<Link href={`/`} shallow>
<a className="logo">
<img
src="/assets/images/logo.svg"
alt=""
@ -44,26 +141,20 @@ export default class Header extends React.Component {
height="32px"
/>
</a>
</Link>
<div className="header_menu">
<nav>
<button
className="nav_toggle"
onClick={this._handle_onToggleMenu}
>
{this.props.router &&
this._getActiveLink(this.props.router.route)}
{this.props.router && this._getActiveLink(this.props.router.route)}
</button>
<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"
: ""
}
className={ this.props.router && (this.props.router.route === "/" || this.props.router.route.indexOf("/contract") === 0) ? "active" : "" }
>
Договоры
</a>
@ -72,12 +163,7 @@ export default class Header extends React.Component {
<li>
<Link href={`/documents/calendar/`} shallow>
<a
className={
this.props.router &&
this.props.router.route.indexOf("/documents/") === 0
? "active"
: ""
}
className={ this.props.router && this.props.router.route.indexOf("/documents/") === 0 ? "active" : "" }
>
Взаиморасчеты и закрывающие документы
</a>
@ -86,12 +172,7 @@ export default class Header extends React.Component {
<li>
<Link href="/settings/phone/" shallow>
<a
className={
this.props.router &&
this.props.router.route.indexOf("/settings/") === 0
? "active"
: ""
}
className={ this.props.router && this.props.router.route.indexOf("/settings/") === 0 ? "active" : "" }
>
Настройки
</a>
@ -112,74 +193,25 @@ export default class Header extends React.Component {
</ul>
</nav>
</div>
<ul className="system_nav">
<li>
<Link href="">
<a data-icon="phone">Телефон</a>
</Link>
<a data-icon="phone" className="button">Телефон</a>
</li>
<li>
<Link href="">
<a data-icon="notify" data-notify="3">
<a data-icon="notify" data-notify={ events.length } className="button" onClick={ this._handle_onToggleNotifications }>
Уведомления
</a>
</Link>
<div className="backdrop"> {/* для открытия opened */}
<div className={`backdrop ${ notificationsOpened && "opened" }`}>
<div className="modal">
<ul className="list">
<li className="new">
<p className="name"><b>Внимание! Просрочена дата возврата СТС по договору: ХХ.ХХ.ХХХХ</b></p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="">
<a>Посмотрите порядок возврата СТС</a>
</Link>
&nbsp; или &nbsp;
<Link href="">
<a>Загрузите скан СТС</a>
</Link>
</p>
</li>
<li className="new">
<p className="name"><b>Внимание! Просрочена дата возврата СТС по договору: ХХ.ХХ.ХХХХ</b></p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="">
<a>Посмотрите порядок возврата СТС</a>
</Link>
&nbsp; или &nbsp;
<Link href="">
<a>Загрузите скан СТС</a>
</Link>
</p>
</li>
<li className="new">
<p className="name">Внимание! Осталось ХХ дней до пролонгации КАСКО по договору ХХХХ: ХХ.ХХ.ХХХХ. Не забудьте самостоятельно продлить полис ОСАГО</p>
<p className="date">10.01.2022</p>
<p className="action">
<Link href="">
<a>Подробнее</a>
</Link>
</p>
</li>
</ul>
<Link href="">
<a className="all">Все события</a>
</Link>
<button className="close">Закрыть</button>
<NotificationsList events={ events }/>
<button className="close" onClick={ this._handle_onToggleNotifications }>Закрыть</button>
</div>
</div>
</li>
<li>
<Link href="">
<a data-icon="message" data-notify="1">
<a data-icon="message" data-notify="0" className="button" onClick={ this._handle_onToggleMessages }>
Сообщения
</a>
</Link>
</li>
<li>
<button
@ -194,3 +226,12 @@ export default class Header extends React.Component {
);
}
}
function mapStateToProps(state, ownProps)
{
return {
events: state.events.list,
}
}
export default connect(mapStateToProps)(Header);

View File

@ -31,6 +31,7 @@ class ContractServicesPage extends React.Component
insurance: null,
registration: null,
telematic: null,
insurance_location_checked: false,
};
}
@ -49,6 +50,8 @@ class ContractServicesPage extends React.Component
componentDidMount()
{
console.log("document.location.hash", document.location.hash);
if(!this.state.loading && this.props.number !== undefined)
{
this.setState({ loading: true }, () =>
@ -67,6 +70,25 @@ class ContractServicesPage extends React.Component
}
}
componentDidUpdate(prevProps, prevState)
{
if(prevState.insurance === null && this.state.insurance !== null)
{
if(!this.state.insurance_location_checked)
{
this.setState({ insurance_location_checked: true }, () =>
{
if(document.location.hash.indexOf("insurance") > -1)
{
const o = [ ...this.state.opened ];
o.push("insurance");
this.setState({ opened: o });
}
});
}
}
}
_handle_onCard = (card) =>
{
const opened = [ ...this.state.opened ];
@ -87,6 +109,18 @@ class ContractServicesPage extends React.Component
const { opened, loading, date, car, contract_date, helpcard, insurance, registration, telematic, } = this.state;
const { number } = this.props;
console.log("helpcard");
console.log(helpcard);
console.log("insurance");
console.log(insurance);
console.log("registration");
console.log(registration);
console.log("telematic");
console.log(telematic);
return (
<React.Fragment>
<Head>
@ -184,7 +218,7 @@ class ContractServicesPage extends React.Component
<>
{ insurance.kasko !== undefined && insurance.kasko !== null && insurance.kasko !== "" && insurance.kasko.map !== undefined && insurance.kasko.map((entry, index) => (
<React.Fragment key={ index }>
<div className="company">
<div className= { `company ${ entry.period_type === "prolong" && "filled" }` }>
<p className="title">КАСКО</p>
<ul>
{ entry.company && (<li>Страховая компания: <b>{ entry.company }</b></li>) }
@ -193,6 +227,9 @@ class ContractServicesPage extends React.Component
{ entry.number && (<li>Номер полиса: <b>{ entry.number }</b></li>) }
{ entry.period && (<li>Период действия: <b>{ entry.period }</b></li>) }
{ entry.amount && (<li>Страховая сумма: <b style={{ whiteSpace: "nowrap" }}>{ numeral(entry.amount).format(' ., ') }&nbsp;</b></li>) }
{ entry.period_type === "prolong" && (
<li className="alert">Обращаем Ваше внимание, что пролонгация полиса КАСКО на второй и последующие периоды осуществляется Лизингополучателем самостоятельно</li>
) }
</ul>
</div>
{ entry.description && (<p>{ entry.description }</p>) }

View File

@ -17,9 +17,9 @@ import Company from "./components/Company";
import DateInput from './components/DatePicker';
import Pagination from './components/Pagination';
import Feed from "./components/Feed";
import AnnouncementsList from "./components/AnnouncementsList";
import { getContractsList } from '../actions';
import { getContractsList, getImage } from '../actions';
class IndexPage extends React.Component
{
@ -27,6 +27,7 @@ class IndexPage extends React.Component
{
super(props);
this.state = {
company: {},
contracts: null,
order: "date",
sort_number: "desc",
@ -47,6 +48,7 @@ class IndexPage extends React.Component
static getDerivedStateFromProps(nextProps, prevState)
{
return {
company: nextProps.company,
contracts: nextProps.contracts,
page: nextProps.page,
pages: nextProps.pages,
@ -146,12 +148,14 @@ class IndexPage extends React.Component
render()
{
const { loading, page, pages, search, date_from, date_from_type, date_to, date_to_type, contracts, sort_number, sort_date, sort_status, all } = this.state;
const { company, loading, page, pages, search, date_from, date_from_type, date_to, date_to_type, contracts, sort_number, sort_date, sort_status, all } = this.state;
const contract_status = {
"Действующий": "opened",
"Закрыт": "closed",
};
console.log("contracts", contracts);
return (
<React.Fragment>
<Head>
@ -172,12 +176,10 @@ class IndexPage extends React.Component
<h1 className="section_title">Личный кабинет</h1>
</div>
<div className="right">
<Company/>
<Company company={ company }/>
</div>
</div>
<Feed />
<AnnouncementsList />
<div className="contract_search">
<form onSubmit={ (event) => { event.preventDefault(); } }>
<div className="form_field">
@ -229,36 +231,38 @@ class IndexPage extends React.Component
<div className="table_cell">{ contract.car?.brand?.name } { contract.car?.model?.name }</div>
<div className="table_cell">
{ contract.car?.reg_number !== null ? contract.car?.reg_number : "Без рег. номера" }
<span>{ contract.car?.vin_number }</span>
</div>
<div className="table_cell">
<p className={ contract_status[contract.status] }>{ contract.status }</p>
{ contract.debt_leasing !== undefined && contract.debt_leasing !== null && parseFloat(contract.debt_leasing) > 0 && (
<p className="contract_debt">
<span>Задолжность:</span>
45 000
{ numeral(contract.debt_leasing).format(' ., ') }&nbsp;
</p>
) }
{ contract.debt_penalty_fee !== undefined && contract.debt_penalty_fee !== null && parseFloat(contract.debt_penalty_fee) > 0 && (
<p className="contract_debt">
<span>Пени:</span>
45 000
{ numeral(contract.debt_penalty_fee).format(' ., ') }&nbsp;
</p>
) }
</div>
<div className="table_cell">
{ contract.current_payment_date !== null ? (
<>{ moment(contract.current_payment_date).format("DD.MM.YYYY") }<b className="price" style={{ whiteSpace: "nowrap" }}>{ numeral(contract.current_payment_amount).format(' ., ') }&nbsp;</b></>
) : "-" }
</div>
<div className="table_cell">
<div className="service_list">
<i title="Телематика" data-additional-service="1"></i>
<i title="РАТ" data-additional-service="2"></i>
<i title="Регистрация в ГИБДД" data-additional-service="3"></i>
<i title="Топливные карты" data-additional-service="4"></i>
<i title="Каско" data-additional-service="5"></i>
<i title="ОСАГО" data-additional-service="6"></i>
<i title="НСИБ" data-additional-service="7"></i>
{ contract.telematics_exists && <i title="Телематика" data-additional-service="1"></i> }
{ contract.rat_exists && <i title="РАТ" data-additional-service="2"></i> }
{ contract.gibddreg_exists && <i title="Регистрация в ГИБДД" data-additional-service="3"></i> }
{ contract.fuelcard_exists && <i title="Топливные карты" data-additional-service="4"></i> }
{ contract.kasko_exists && <i title="КАСКО" data-additional-service="5"></i> }
{ contract.osago_exists && <i title="ОСАГО" data-additional-service="6"></i> }
{ contract.nsib_exists && <i title="НСИБ" data-additional-service="7"></i> }
{ contract.fingap_exists && <i title="FinGAP" data-additional-service="8"></i> }
</div>
</div>
</div>
@ -281,21 +285,25 @@ class IndexPage extends React.Component
{ !all && (
<Pagination page={ page } pages={ pages } onPage={ this._handle_onPage } onAll={ this._handle_onAll } all={ all } showAll={ true }/>
) }
<div className="helpBox">
<div className="avatar">
<img src="/assets/images/icons/avatar.svg" loading="lazy" alt="" />
{/*}
<img src={ company.manager_photo !== undefined && company.manager_photo !== null && company.manager_photo !== "" ? getImage({ id: company.manager_photo }) : `/assets/images/icons/avatar.svg` } loading="lazy" alt={ company.manager_fio } />
{*/}
<img src={ `/assets/images/icons/avatar.svg` } loading="lazy" alt={ company.manager_fio } />
</div>
<div className="content">
<p className="message">Помогу выбрать новый автомобиль</p>
<p className="name">Иванов Иван</p>
<p className="position">Менеджер по продажам</p>
<p className="name">{ company.manager_fio }</p>
<p className="position">{ company.manager_jobtitle }</p>
<div className="info">
<a href="mailto:">mail@mail.ru</a>
<a href="tel:">+7 900 123-23-23</a>
<a href="">+7 900 123-23-23</a>
<a href={ `mailto:${ company.manager_email }` }>{ company.manager_email }</a>
{ company.manager_mobilephone !== undefined && company.manager_mobilephone !== null && company.manager_mobilephone !== "" && (
<a href={ `tel:+${ company.manager_mobilephone.replace('/[^\d]/m', '') }` }>{ company.manager_mobilephone }</a>
) }
{ company.manager_telegram !== undefined && company.manager_telegram !== null && company.manager_telegram !== "" && (
<a href={ `https://telegram.me/${ company.manager_telegram.replace("@", "") }` } target="_blank">{ `@${ company.manager_telegram.replace("@", "")}` }</a>
) }
</div>
</div>
</div>
@ -312,6 +320,7 @@ class IndexPage extends React.Component
function mapStateToProps(state, ownProps)
{
return {
company: state.company,
contracts: state.contracts.list,
page: state.contracts.page,
pages: state.contracts.pages,

View File

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.8571 1.3335H6C4.89543 1.3335 4 2.22677 4 3.33134V16.2928C4 20.1237 10.453 24.9829 14.451 27.4229C15.0533 27.7906 15.8038 27.7906 16.4062 27.4229C20.4041 24.9829 26.8571 20.1237 26.8571 16.2928V3.33134C26.8571 2.22677 25.9617 1.3335 24.8571 1.3335Z" stroke="#8E94A7" stroke-width="2" stroke-linejoin="round"/>
<path d="M14.8906 7.78906H10.3203V10.7188H14.1406C14.4948 10.7188 14.7578 10.7995 14.9297 10.9609C15.1068 11.1172 15.1953 11.3281 15.1953 11.5938C15.1953 11.8594 15.1068 12.0703 14.9297 12.2266C14.7526 12.3828 14.4896 12.4609 14.1406 12.4609H10.3203V16.2344C10.3203 16.7135 10.2109 17.0703 9.99219 17.3047C9.77865 17.5339 9.5026 17.6484 9.16406 17.6484C8.82031 17.6484 8.53906 17.5312 8.32031 17.2969C8.10677 17.0625 8 16.7083 8 16.2344V7.42188C8 7.08854 8.04948 6.81771 8.14844 6.60938C8.2474 6.39583 8.40104 6.24219 8.60938 6.14844C8.82292 6.04948 9.09375 6 9.42188 6H14.8906C15.2604 6 15.5339 6.08333 15.7109 6.25C15.8932 6.41146 15.9844 6.625 15.9844 6.89062C15.9844 7.16146 15.8932 7.38021 15.7109 7.54688C15.5339 7.70833 15.2604 7.78906 14.8906 7.78906Z" fill="#8E94A7"/>
<path d="M23.7832 14.5078V16.1543C23.7832 16.373 23.7617 16.5488 23.7188 16.6816C23.6758 16.8105 23.5957 16.9277 23.4785 17.0332C23.3652 17.1387 23.2188 17.2402 23.0391 17.3379C22.5195 17.6191 22.0195 17.8242 21.5391 17.9531C21.0586 18.082 20.5352 18.1465 19.9688 18.1465C19.3086 18.1465 18.707 18.0449 18.1641 17.8418C17.6211 17.6387 17.1582 17.3438 16.7754 16.957C16.3926 16.5703 16.0977 16.1016 15.8906 15.5508C15.6875 15 15.5859 14.3848 15.5859 13.7051C15.5859 13.0371 15.6855 12.4258 15.8848 11.8711C16.084 11.3164 16.377 10.8457 16.7637 10.459C17.1504 10.0723 17.6211 9.77734 18.1758 9.57422C18.7305 9.36719 19.3594 9.26367 20.0625 9.26367C20.6406 9.26367 21.1523 9.3418 21.5977 9.49805C22.043 9.65039 22.4043 9.84375 22.6816 10.0781C22.959 10.3125 23.168 10.5605 23.3086 10.8223C23.4492 11.084 23.5195 11.3164 23.5195 11.5195C23.5195 11.7383 23.4375 11.9258 23.2734 12.082C23.1133 12.2344 22.9199 12.3105 22.6934 12.3105C22.5684 12.3105 22.4473 12.2812 22.3301 12.2227C22.2168 12.1641 22.1211 12.082 22.043 11.9766C21.8281 11.6406 21.6465 11.3867 21.498 11.2148C21.3496 11.043 21.1484 10.8984 20.8945 10.7812C20.6445 10.6641 20.3242 10.6055 19.9336 10.6055C19.5312 10.6055 19.1719 10.6758 18.8555 10.8164C18.5391 10.9531 18.2676 11.1543 18.041 11.4199C17.8184 11.6816 17.6465 12.0039 17.5254 12.3867C17.4082 12.7695 17.3496 13.1934 17.3496 13.6582C17.3496 14.666 17.5801 15.4414 18.041 15.9844C18.5059 16.5273 19.1523 16.7988 19.9805 16.7988C20.3828 16.7988 20.7598 16.7461 21.1113 16.6406C21.4668 16.5352 21.8262 16.3848 22.1895 16.1895V14.7949H20.8418C20.5176 14.7949 20.2715 14.7461 20.1035 14.6484C19.9395 14.5508 19.8574 14.3848 19.8574 14.1504C19.8574 13.959 19.9258 13.8008 20.0625 13.6758C20.2031 13.5508 20.3926 13.4883 20.6309 13.4883H22.6055C22.8477 13.4883 23.0527 13.5098 23.2207 13.5527C23.3887 13.5957 23.5234 13.6914 23.625 13.8398C23.7305 13.9883 23.7832 14.2109 23.7832 14.5078Z" fill="#8E94A7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

24
reducers/eventsReducer.js Normal file
View File

@ -0,0 +1,24 @@
import { HYDRATE } from 'next-redux-wrapper';
import * as actionTypes from '../constants/actionTypes';
import initialState from "./initialState";
const eventsReducer = (state = initialState.events, action) =>
{
switch (action.type)
{
case actionTypes.EVENTS:
{
return {
...state,
...action.data,
};
}
default: {
return state;
}
}
};
export default eventsReducer;

View File

@ -54,6 +54,10 @@ export const defaultState = {
page: null,
pages: false,
},
events:
{
list: [],
},
};
export default JSON.parse(JSON.stringify(defaultState));

View File

@ -7,6 +7,7 @@ import companyReducer from '../reducers/companyReducer';
import contractsReducer from '../reducers/contractsReducer';
import contractReducer from '../reducers/contractReducer';
import calendarReducer from '../reducers/calendarReducer';
import eventsReducer from '../reducers/eventsReducer';
const combinedReducer = combineReducers({
auth: authReducer,
@ -15,6 +16,7 @@ const combinedReducer = combineReducers({
contracts: contractsReducer,
contract: contractReducer,
calendar: calendarReducer,
events: eventsReducer,
});
const makeStore = (context) =>

View File

@ -792,7 +792,7 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
inherits "^2.0.1"
safe-buffer "^5.0.1"
classnames@*, classnames@^2.2.6:
classnames@*, classnames@^2.2.5, classnames@^2.2.6:
version "2.3.1"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
@ -1157,6 +1157,11 @@ encoding@0.1.13:
dependencies:
iconv-lite "^0.6.2"
enquire.js@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814"
integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==
enquirer@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
@ -2076,6 +2081,13 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json2mq@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==
dependencies:
string-convert "^0.2.0"
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
@ -2199,6 +2211,11 @@ lodash.camelcase@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
@ -2981,6 +2998,17 @@ react-refresh@0.8.3:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
react-slick@^0.29.0:
version "0.29.0"
resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.29.0.tgz#0bed5ea42bf75a23d40c0259b828ed27627b51bb"
integrity sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==
dependencies:
classnames "^2.2.5"
enquire.js "^2.1.6"
json2mq "^0.2.0"
lodash.debounce "^4.0.8"
resize-observer-polyfill "^1.5.0"
react-transition-group@^4.4.1:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@ -3103,6 +3131,11 @@ regexpp@^3.2.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
resize-observer-polyfill@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@ -3344,6 +3377,11 @@ stream-parser@^0.3.1:
dependencies:
debug "2"
string-convert@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97"
integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==
string-hash@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"