add documents

This commit is contained in:
m1kolay 2022-02-11 15:34:31 +02:00
parent d4ec833a31
commit 9f1179db39
57 changed files with 1659 additions and 8 deletions

View File

@ -16,6 +16,11 @@ import CalendarPage from './pages/CalendarPage'
import ReportPage from './pages/ReportFormPage.js' import ReportPage from './pages/ReportFormPage.js'
import FormPage from './pages/FormPage.js' import FormPage from './pages/FormPage.js'
import SingleReportPage from './pages/SingleReportPage' import SingleReportPage from './pages/SingleReportPage'
import Bookkeping from './pages/Bookkeeping'
import { TaxPage } from "./pages/TaxPage"
import { MoneyPage } from "./pages/MoneyPage"
import { ActPage } from "./pages/ActPage"
import { ContractPage } from "./pages/ContractPage"
const App = (props) => { const App = (props) => {
const isAuth = useSelector(selectAuth) const isAuth = useSelector(selectAuth)
@ -44,6 +49,11 @@ const App = (props) => {
/> />
<ProtectedRoute exact path='/report' component={ReportPage} /> <ProtectedRoute exact path='/report' component={ReportPage} />
<ProtectedRoute path='/report/:id' component={SingleReportPage} /> <ProtectedRoute path='/report/:id' component={SingleReportPage} />
<ProtectedRoute exact path='/documents' component={Bookkeping} />
<ProtectedRoute exact path='/tax' component={TaxPage} />
<ProtectedRoute exact path='/money' component={MoneyPage} />
<ProtectedRoute exact path='/documents/act' component={ActPage} />
<ProtectedRoute exact path='/documents/contract' component={ContractPage} />
<ProtectedRoute component={() => <div>Page not found</div>} /> <ProtectedRoute component={() => <div>Page not found</div>} />
</Switch> </Switch>
</Router> </Router>

View File

@ -2,10 +2,11 @@ import React, { useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { auth } from '../../redux/outstaffingSlice' import { auth, setUserInfo } from '../../redux/outstaffingSlice'
import { loading } from '../../redux/loaderSlice' import { loading } from '../../redux/loaderSlice'
import ellipse from '../../images/ellipse.png' import ellipse from '../../images/ellipse.png'
import { Loader } from '../Loader/Loader' import { Loader } from '../Loader/Loader'
import { fetchAuth } from '../../server/server' import { fetchAuth } from '../../server/server'
@ -74,8 +75,9 @@ export const AuthBox = ({ title, roleChangeLink }) => {
fetchAuth({ fetchAuth({
username, username,
password, password,
dispatch: () => { dispatch: (resJSON) => {
dispatch(auth(true)) dispatch(auth(true))
dispatch(setUserInfo(resJSON))
dispatch(loading(false)) dispatch(loading(false))
dispatch(setRole('ROLE_PARTNER')) dispatch(setRole('ROLE_PARTNER'))
}, },

View File

@ -1,4 +1,5 @@
import React from 'react' import React from 'react'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Achievement } from '../Achievement/Achievement' import { Achievement } from '../Achievement/Achievement'
@ -7,6 +8,9 @@ import { LEVELS, SKILLS } from '../constants/constants'
import maleBig from '../../images/medium_male_big.png' import maleBig from '../../images/medium_male_big.png'
import './candidateSidebar.scss' import './candidateSidebar.scss'
import { selectUserInfo } from '../../redux/outstaffingSlice'
import { isRejected } from '@reduxjs/toolkit'
const getYearsString = (years) => { const getYearsString = (years) => {
let yearsString let yearsString
if (years % 10 === 1) { if (years % 10 === 1) {
@ -22,6 +26,7 @@ const getYearsString = (years) => {
} }
const CandidateSidebar = ({ candidate, position }) => { const CandidateSidebar = ({ candidate, position }) => {
const userId = localStorage.getItem('id')
return ( return (
<div className='candidate-sidebar'> <div className='candidate-sidebar'>
<div className='candidate-sidebar__info'> <div className='candidate-sidebar__info'>
@ -45,16 +50,16 @@ const CandidateSidebar = ({ candidate, position }) => {
Выбрать к собеседованию Выбрать к собеседованию
</button> </button>
</Link> </Link>
<Link to={`/${candidate.id}/calendar`}> {userId && candidate.id === userId && (<Link to={`/${candidate.id}/calendar`}>
<button className='candidate-sidebar__select'> <button className='candidate-sidebar__select'>
Отчёты Отчёты
</button> </button>
</Link> </Link>)}
<div className='candidate-sidebar__achievements'> <div className='candidate-sidebar__achievements'>
{candidate && {candidate &&
candidate.achievements && candidate.achievements &&
candidate.achievements.map((item) => { candidate.achievements.map((item, index) => {
return <Achievement achievement={item.achievement} /> return <Achievement achievement={item.achievement} key={index}/>
})} })}
</div> </div>
</div> </div>

View File

@ -0,0 +1,24 @@
import React from "react"
import "./columnCard.css"
export const ColumnItem = (props) => {
return (
<div>
<div className="content-column-item">
<div className="content-column-item__info">
<div className="content-column-item__title">{props.title}</div>
<div className="content-column-item__date">{props.date} <span className="item-time">{props.time}</span></div>
</div>
<div className="content-column-item__amount">{props.amount}</div>
<div className="content-column-item__card">
<div className="item__card-kind">{props.card.name} --</div>
<div className="item__card-number">{props.card.number}</div>
</div>
<div className="content-column-item__balance">
<div className="item__balance-name">Баланс:</div>
<div className="item__balance-now">{props.balance}</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,73 @@
.content-column-item {
display: grid;
margin: 0 auto;
width: 349px;
height: 135px;
border-radius: 20px;
background-color: #f9f9f9;
justify-content: center;
align-content: center;
margin-bottom: 15px;
}
.content-column-item__info {
display: flex;
}
.content-column-item__title {
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 12px;
line-height: 36px;
}
.content-column-item__date {
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 12px;
line-height: 36px;
color: #59b522;
padding-left: 55px;
}
.item-time {
padding-left: 17px;
}
.content-column-item__amount {
font-size: 18px;
font-weight: 400;
font-family: 'GT Eesti Pro Display';
}
.content-column-item__card {
display: flex;
font-size: 12px;
font-weight: 300;
line-height: 36px;
font-family: 'GT Eesti Pro Display';
}
.content-column-item__balance {
display: flex;
}
.item__balance-name {
font-family: 'GT Eesti Pro Display';
font-size: 12px;
line-height: 36px;
font-weight: 300;
}
.item__balance-now {
font-family: 'GT Eesti Pro Display';
font-size: 12px;
line-height: 36px;
font-weight: 300;
}
@media (max-width: 430px) {
.content-column-item__title {
font-size: 8px;
}
}

View File

@ -0,0 +1,74 @@
import React from "react"
import "./columnList.css"
import { ColumnItem } from "../ColumnCard/ColumnCard"
const moneyCardarray = [{
title : "Оплата в Сбербанк Онлайн",
date : "12.01.2021",
time : "21:19",
amount : "100 P",
card : {
name : "Visa",
number : "2869",
},
balance : "8 719, 43 Р",
},
{
title : "Оплата в Сбербанк Онлайн",
date : "12.01.2021",
time : "21:19",
amount : "100 P",
card : {
name : "Visa",
number : "2869",
},
balance : "8 719, 43 Р",
},
{
title : "Оплата в Сбербанк Онлайн",
date : "12.01.2021",
time : "21:19",
amount : "100 P",
card : {
name : "Visa",
number : "2869",
},
balance : "8 719, 43 Р",
},
{
title : "Оплата в Сбербанк Онлайн",
date : "12.01.2021",
time : "21:19",
amount : "100 P",
card : {
name : "Visa",
number : "2869",
},
balance : "8 719, 43 Р",
},
{
title : "Оплата в Сбербанк Онлайн",
date : "12.01.2021",
time : "21:19",
amount : "100 P",
card : {
name : "Visa",
number : "2869",
},
balance : "8 719, 43 Р",
}, ]
export const ColumnList = (props) => {
return (
<div>
<div className="content-column">
<div className="content-column__title">{props.title}</div>
{moneyCardarray.map( (moneyCardItem, index) => {
return (
<ColumnItem {...moneyCardItem} />
)
})}
</div>
</div>
)
}

View File

@ -0,0 +1,21 @@
.content-column {
border-radius: 10px;
border: 1px solid #69bf2c;
width: 396px;
height: 854px;
display: grid;
padding: 23px 20px 50px 20px;
margin-right: 10px;
}
.content-column__title {
font-size: 22px;
font-family: 'GT Eesti Pro Display';
font-weight: 400;
line-height: 36px;
text-align: center;
line-height: 36px;
padding-bottom:20px;
}

View File

@ -0,0 +1,24 @@
import React from "react"
import { ColumnList } from "./ColumnList/ColumnList"
import "./moneyContent.css"
import { ContentTitle } from "../bookkeeping/ContentTitle/ContentTitle"
import { ContentTitleAbout } from "../bookkeeping/ContentTitleAbout/ContentTitleAbout"
import { ContentAbout } from "../bookkeeping/ContentAbout/ContentAbout"
export const MoneyContent = () => {
return (
<div>
<div className="content__info">
<ContentTitle title="Деньги" description="# Описание" />
<ContentTitleAbout descriptionTitle="История денежных операций" />
<ContentAbout contentAbout="Вы можете создать еще 4 документа. Чтобы создавать неограниченное число
документов, укажите ИНН и получите 30 дней Эльбы на тарифе «Премиум»
бесплатно. А для ИП младше 3 месяцев 0 год. " />
<div className="content__column-list">
<ColumnList title="Списание"/>
<ColumnList title="Зачисления" />
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,29 @@
.content__column-list {
display: flex;
}
@media (max-width: 1240px) {
.content__column-list {
justify-content: center;
}
}
@media (max-width: 840px) {
.content__column-list {
display: grid;
}
.content-column {
margin: 15px 0 0 0;
}
}
@media (max-width: 430px) {
.content-column {
width: 310px;
}
.content-column-item {
width: 270px;
}
}

View File

@ -0,0 +1,111 @@
.example__bills {
width: 605px;
height: 229px;
border-radius: 10px;
border: 1px solid #69bf2c;
margin-top: 15px;
}
.example__part-first {
display: grid;
align-content: center;
padding-left: 44px;
height: 50%;
}
.example-info__item {
display: flex;
padding-bottom: 21px;
}
.example-item__amount {
font-size: 18px;
font-family: 'GT Eesti Pro Display';
font-weight: 400;
}
.example-item__number {
font-size: 18px;
font-family: 'GT Eesti Pro Display';
font-weight: 400;
}
.example__part-second {
display: flex;
align-items: center;
padding-left: 44px;
border-radius: 0 0 20px;
border: 1px solid #efefef;
background-color: rgb(249, 249, 249, 0.87);
height: 50%;
}
.example__total span {
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 18px;
padding-right: 15px;
}
.example__total {
font-size: 25px;
font-family: 'GT Eesti Pro Display';
}
.example__info-status {
display: flex;
align-items: center;
justify-content: center;
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 400;
color: white;
border-radius: 10px;
max-height: 40px;
width: 131px;
height: 40px;
margin-left: 70px;
}
.example__info-status--completed {
background-color: #70a9e4;
}
@media (max-width: 700px) {
.example__bills {
width: 480px;
}
}
@media (max-width: 500px) {
.example__bills {
width: 300px;
}
.example__info-status {
font-size: 12px;
margin-right: 10px;
width: 105px;
}
.example-info__item {
padding: 10px;
}
.example-item__amount {
font-size: 12px;
}
.example-item__number {
font-size: 12px;
}
.example__total {
font-size: 12px;
display: grid;
}
.example__total span {
font-size: 14px;
}
}

View File

@ -0,0 +1,27 @@
import React from "react"
import './example.css'
export const Example = () => {
return (
<div className="example">
<div className="example__bills">
<div className="example__part-first">
<div className="example-info__item">
<div className="example-item__amount">50 т. руб. =</div>
<div className="example-item__number"> 123</div>
</div>
<div className="example-info__item">
<div className="example-item__amount">300т. руб. =</div>
<div className="example-item__number"> 70</div>
</div>
</div>
<div className="example__part-second">
<div className="example__total">
<span>Итого:</span> 300 т. руб.
</div>
<div className="example__info-status example__info-status--completed">Оплачено</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,16 @@
import React from "react"
import './reportTax.css'
export const ReportTax =(props) => {
return(
<div>
<div className="period-column__item">
<img className="period-item__img" src={props.img} alt='' />
<div className="period-item__name">
{props.time} <br/> <span>{props.period}/12</span>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,30 @@
.period-column__item {
display: flex;
width: 258px;
height: 112px;
border-radius: 20px;
background-color: #f9f9f9;
margin-bottom: 40px;
}
.period-item__img {
width: 50%;
position: relative;
left: -11%;
top: 18%;
}
.period-item__name {
width: 50%;
margin: auto 0;
font-family: 'GT Eesti Pro Display';
font-weight: 400;
font-size: 18px;
margin-left: 15px;
}
.period-item__name span {
font-family: 'GT Eesti Pro Display';
font-weight: 300;
}

View File

@ -0,0 +1,42 @@
import React from "react"
import "./taxContent.css"
import { ReportTax } from "./ReportTax/ReportTax"
import Quarter from "../../../../images/Reporting-1.png"
import HalfYear from "../../../../images/Reporting-2.png"
import NineMonth from "../../../../images/Reporting-3.png"
import Year from "../../../../images/Taxes.png"
import { Example } from "../Example/example"
import { ContentTitle } from "../../bookkeeping/ContentTitle/ContentTitle"
import { ContentAbout } from "../../bookkeeping/ContentAbout/ContentAbout"
export const TaxContent =() => {
return(
<div className="tax-content">
<ContentTitle title="Оплата налога и предоставление от четности" description="# Описание оплаты налога" />
<ContentAbout contentAbout="Налогоплательщики, применяющие упрощенную систему налогообложения,
не вправе до окончания налогового периода перейти на иной режим
налогообложения." />
<div className="content__period">
<div className="period__column-report">
<div className="period-column__name">Отчетный период</div>
<ReportTax img={Quarter} time="Квартал" period={3}></ReportTax>
<ReportTax img={HalfYear} time="Полугодие" period={6}></ReportTax>
<ReportTax img={NineMonth} time="9 месяцев" period={9}></ReportTax>
</div>
<div className="period__column-tax">
<div className="period-column__name">Налоговый период</div>
<ReportTax img={Year} time="Год" period={12}></ReportTax>
</div>
</div>
<Example></Example>
<div className="example__description">
<p>Если последний день срока уплаты налога (авансового платежа) выпадает
на выходной или нерабочий праздничный день, перечислить налог плательщик
обязан в ближайший следующий за ним рабочий день.</p>
</div>
</div>
)
}

View File

@ -0,0 +1,80 @@
.tax-content {
padding-left: 45px;
}
.content__period {
display: flex;
}
.period__column-report {
display: grid;
margin-right: 90px;
}
.period-column__name {
font-family: 'GT Eesti Pro Display';
font-weight: 400;
font-size: 22px;
padding-right: 60px;
padding-bottom: 50px;
}
.example__description p {
font-size: 18px;
font-weight: 300;
max-width: 640px;
padding-top: 33px;
color: #000000;
}
@media (max-width: 1240px) {
.content__info-title {
margin-top: 30px;
}
.content__info-title h2:after {
margin: 25px auto 30px;
}
.content__period {
justify-content: center;
}
.example {
display: flex;
justify-content: center;
}
.example__description p {
margin: 0 auto;
}
.tax-content {
padding: 0;
}
}
@media (max-width: 700px) {
.content__period {
display: grid;
}
.period__column-report {
margin: 0;
}
.period-column__name {
padding-right: 0;
text-align: center;
}
.example__description p {
font-size: 15px;
max-width: 515px;
}
}
@media (max-width: 500px) {
.example__description p {
max-width: 300px;
}
}

View File

@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import { ContentTitle } from "../ContentTitle/ContentTitle"
import { ContentButton } from "../ContentButton/ContentButton"
import { BookkeepingFormField } from "../BookkeepingFormField/BookkeepingFormField"
import { BookkepingSelect } from '../BookkepingSelect/BookkepingSelect';
import { BookkepingInput } from '../BookkepingInput/BookkepingInput';
import { fetchGet } from '../../../../server/server'
import { Link } from "react-router-dom"
import "./actContent.css"
export const ActContent = ()=> {
const [templates, setTemplates] = useState([])
const [selectedTemplate, setSelectedTemplate] = useState()
const [templatedFields, setTemplatedFields] = useState([])
useEffect(() => {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/template/get-template-list`,
}).then((res) => {
setTemplates(res)
})
}, [])
useEffect(() => {
if (selectedTemplate === undefined) {
return
}
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/template/get-template-fields?template_id=${selectedTemplate}`,
})
.then((res) => {
setTemplatedFields(res[0].templateDocumentFields)
})
}, [selectedTemplate])
return(
<div>
<div className="content__info">
<ContentTitle title="Создание акта" description="# Описание" />
<div className="content__info-main">
<form className='contract'>
<div className="contract__create">
<div className="contract__title">Создание акта </div>
<input type="text" className="contract__number" placeholder="#" />
<span>от</span>
<input type="date" className="contract__date" />
</div>
<BookkeepingFormField title="Шаблон документа"
Component={BookkepingSelect}
innerComponentProps={{
onSelect: setSelectedTemplate,
textField: "title",
options: templates,
defaultIndexSelected: 0,
}}
action={{
text: "Добавить свой шаблон",
method: () => {}
}}
/>
{templatedFields.map((field, index ) =>
<BookkeepingFormField title={field.field.title} key={index}
Component={BookkepingInput}
innerComponentProps={{
placeholder: "Введите данные",
}}
/>
)}
<div className="content__btn-list">
<ContentButton styles={{ width: "290px",
height: "75px",
boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
borderRadius: "38px",
backgroundColor: "#b64b3e",
border: "none",
color: "#ffffff",
}}>Сохранить</ContentButton>
<Link to="/documents" className="link-act-button">
<div className='act-Button'>
<ContentButton styles={{color: "#282828",
marginLeft: "40px",
background: "none",
border: "none"
}}>Отменить</ContentButton>
</div>
</Link>
</div>
</form>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,165 @@
.contract__create {
display: flex;
}
.contract__title {
font-family: 'GT Eesti Pro Display';
font-weight: 400;
font-size: 22px;
line-height: 36px;
}
.contract__create span {
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 300;
display: flex;
align-items: center;
}
.contract__create input {
margin: 0px 15px;
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 18px;
height: 33px;
border-radius: 17px;
background-color: #f9f9f9;
color: #000000;
outline:none;
border: none;
text-align: center;
}
.contract__number {
width: 70px;
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 18px;
}
.contract__date {
width: 110px;
}
.contract__date::-webkit-calendar-picker-indicator {
display: none;
}
.content__client {
display: flex;
margin: 40px 0;
}
.content__client-title {
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 300;
display: flex;
align-items: center;
width: 200px;
}
.content__client-name {
display: flex;
width: 358px;
height: 33px;
border-radius: 17px;
background-color: #f9f9f9;
border: none;
outline: none;
padding-left: 25px;
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 18px;
}
.content__requisite {
display: flex;
margin: 40px 0;
}
.content__requisite-title {
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 300;
display: flex;
align-items: center;
width: 200px;
}
.content__orders {
display: flex;
}
.content__orders-title {
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 300;
display: flex;
align-items: center;
width: 200px;
}
.content__orders-name {
display: flex;
width: 358px;
height: 33px;
border-radius: 17px;
background-color: #f9f9f9;
border: none;
outline: none;
padding-left: 25px;
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 18px;
}
.content__btn-list{
display: flex;
margin-top: 42px;
}
.act-Button {
display: flex;
align-items: center;
}
.link-act-button {
display: flex;
}
@media (max-width: 1240px) {
.page__breadcrumps {
display: none;
}
.content__info-main {
justify-content: center;
}
.contract__create {
justify-content: center;
}
.content__btn-list {
justify-content: center;
}
}
@media (max-width: 450px) {
.contract__create {
display: grid;
}
.contract__create input {
margin: 0 auto;
}
.contract__create span {
margin: 10px auto;
}
}

View File

@ -0,0 +1,32 @@
import React from "react";
import "./bookkeepingContent.css"
import { DocumentCard } from "../DocumentCard/DocumentCard"
import Act from "../../../../images/act.png"
import Contract from "../../../../images/Contract.png"
import Tax from "../../../../images/Tax.png"
import Money from "../../../../images/money.png"
import { ContentTitle } from "../ContentTitle/ContentTitle"
import { ContentTitleAbout } from "../ContentTitleAbout/ContentTitleAbout"
import { ContentAbout } from "../ContentAbout/ContentAbout"
export const BookkeepingContent = () => {
return (
<div className="content__info">
<ContentTitle title="Документы" description="# Описание" />
<ContentTitleAbout descriptionTitle="SVM - сервис выездных менеджеров для банка ПСБ" />
<ContentAbout contentAbout="Вы можете создать еще 4 документа. Чтобы создавать неограниченное число документов, укажите ИНН и получите 30 дней Эльбы на тарифе «Премиум» бесплатно. А для ИП младше 3 месяцев 0 год." />
<div className="content__label-list">
<div className="content-label">
<DocumentCard img={Act} title="Акт" link="/documents/act" className="content-item__text" />
<DocumentCard img={Tax} title="Налог" className="content-item__text"/>
</div>
<div className="content-label">
<DocumentCard img={Contract} title="Договор" link="/documents/contract" className="content-item__text"/>
<DocumentCard img={Money} title="Деньги" className="content-item__text"/>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,59 @@
.content__info {
padding-left: 45px;
width: 66%;
}
.content__info-main {
display: grid;
}
.content__label-list {
display: flex;
}
.content-label {
margin-right: 148px;
}
@media (max-width: 1240px) {
.content__info {
margin: 30px auto;
padding-left: 0px;
width: 100%;
}
.content__label-list {
display: flex;
justify-content: space-evenly;
}
.content-label {
margin: 0;
}
.content__info-title {
display: flex;
justify-content: center;
text-align: center;
}
.content__description {
text-align: center;
}
.content__description-title {
text-align: center;
}
.content__about {
text-align: center;
margin: 0 auto;
}
}
@media (max-width: 780px) {
.content__label-list {
display: grid;
justify-content: center;
}
.content__about {
margin: 10px auto;
}
}

View File

@ -0,0 +1,12 @@
import React from "react";
import "./bookkeepingFormField.css"
export const BookkeepingFormField = ({ title, Component, innerComponentProps, action }) => {
return (
<div className="bookkeeping-form-field">
<div className="bookkeeping-form-field__title">{title}</div>
<div className="bookkeeping-form-field__input">{<Component {...innerComponentProps} />}</div>
{action && <div className="bookkeeping-form-field__action" onClick={action.method}>{action.text}</div>}
</div>
)
}

View File

@ -0,0 +1,52 @@
.bookkeeping-form-field {
display:flex;
margin-top: 35px;
width: 768px;
}
.bookkeeping-form-field__title {
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 300;
display: flex;
align-items: center;
width: 200px;
}
.bookkeeping-form-field__action {
display: flex;
align-items: center;
font-size: 18px;
font-family: 'GT Eesti Pro Display';
color: #59b520;
text-decoration: underline;
cursor: pointer;
font-weight: 300;
margin-left: 20px;
}
@media (max-width: 800px) {
.bookkeeping-form-field {
display: grid;
justify-items: center;
}
.bookkeeping-form-field__title {
justify-content: center;
margin: 10px;
}
.bookkeeping-form-field__action {
margin-top: 10px;
}
.bookkeeping-form-field {
width: 100%;
}
}
@media (max-width: 380px) {
.bookkeping-Input {
max-width: 280px;
}
}

View File

@ -0,0 +1,22 @@
import React, { Children } from "react"
import "./bookkeepingTemplete.css"
import { Breadcrumps } from "../Breadcrumps/Breadcrumps"
import { Sidebar } from "../Sidebar/Sidebar"
export const BookkeepingTemplete = ({ showBreadcrumps, nameBreeadcrumps, children }) => {
return(
<div className="size">
<div className="title">
<h1>Аутстаффинг <span>онлайн-бухгалтерия</span></h1>
</div>
{showBreadcrumps && <Breadcrumps nameBreeadcrumps={nameBreeadcrumps}/>}
<div className="main-content">
<Sidebar />
<div className="">
{children}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,54 @@
.title {
width: 100%;
display: flex;
justify-content: center;
margin-top: 135px;
}
.title h1 {
font-family: 'GT Eesti Pro Display';
font-weight: 700;
font-size: 53px;
letter-spacing: 0.56px;
display: block;
}
.title span {
color: #52b709;
}
.size {
width: 1230px;
margin: 0 auto;
}
.main-content {
display: flex;
margin-bottom: 300px;
position: relative;
top: 90px;
}
@media (max-width: 1240px) {
.main-content {
display: grid;
top: 30px;
}
.size {
width: 100%;
}
.title {
text-align: center;
}
}
@media (max-width: 450px) {
.title h1{
font-size: 40px;
letter-spacing: 0.27px;
}
.content__info-title h2 {
font-size: 20px;
}
}

View File

@ -0,0 +1,8 @@
import React from "react";
import "./bookkepingInput.css"
export const BookkepingInput = (props) => {
return (
<input className="bookkeping-Input" placeholder={props.placeholder} />
)
}

View File

@ -0,0 +1,13 @@
.bookkeping-Input {
display: flex;
width: 358px;
height: 33px;
border-radius: 17px;
background-color: #f9f9f9;
border: none;
outline: none;
padding-left: 25px;
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 18px;
}

View File

@ -0,0 +1,22 @@
import React, {useState} from "react";
import "./bookkepingSelect.css"
export const BookkepingSelect = (props) => {
const handleChange = (e) => {
props.onSelect(e.target.value)
}
return (
<div>
<select className="bookkeping-select__selected" onChange={handleChange} >
{props.options.map( (documentTypeItem, index)=> {
return (
<option className="bookkeping-select__options-item" key={index} value={documentTypeItem.id}>{documentTypeItem[props.textField]}</option>
)
})}
</select>
</div>
)
}

View File

@ -0,0 +1,24 @@
.bookkeping-select__selected{
width: 358px;
height: 33px;
background-color: #54b611;
border-radius: 17px;
font-family: 'GT Eesti Pro Display';
color: #ffffff;
font-size: 18px;
font-weight: 400;
padding-left: 15px;
display: flex;
align-items: center;
position: relative;
text-transform: lowercase;
cursor: pointer;
border: none;
outline: none;
}
@media (max-width: 380px) {
.bookkeping-select__selected {
width: 280px;
}
}

View File

@ -0,0 +1,13 @@
import React from "react"
import "./breadcrumps.css"
import { Link } from "react-router-dom"
export const Breadcrumps = (props) => {
return (
<div className="page__breadcrumps">
<Link to="/documents"><div className="page__last">Документы</div></Link>
<div className="page__current">{props.nameBreeadcrumps}</div>
</div>
)
}

View File

@ -0,0 +1,38 @@
.page__breadcrumps {
margin-top: 40px;
margin-bottom: 40px;
display: flex;
position: absolute;
}
.page__last {
font-family: 'GT Eesti Pro Display';
font-size: 12px;
font-weight: 300;
color: #c4c4c4;
cursor: pointer;
position: relative;
margin-right: 19px;
}
.page__last:before {
position: absolute;
width: 8px;
height: 8px;
content: '';
left: 106%;
top: 28%;
border-top: 2px solid #c4c4c4;;
border-left: 2px solid #c4c4c4;;
transform: rotate(135deg);
cursor: auto;
}
.page__current {
font-family: 'GT Eesti Pro Display';
font-size: 12px;
font-weight: 300;
color: #000000;
cursor: pointer;
}

View File

@ -0,0 +1,6 @@
.content__about {
font-weight: 300;
font-size: 18px;
max-width: 660px;
padding-bottom: 42px;
}

View File

@ -0,0 +1,12 @@
import React from "react";
import "./ContentAbout.css"
export const ContentAbout = (props) => {
return (
<div>
<div className="content__about">
<p>{props.contentAbout}</p>
</div>
</div>
)
}

View File

@ -0,0 +1,10 @@
import React from "react";
import "./contentButton.css"
export const ContentButton = (props) => {
return (
<div>
<button className="content-button" style={props.styles} >{props.children}</button>
</div>
)
}

View File

@ -0,0 +1,22 @@
.content-button {
font-family: "Muller";
font-size: 22px;
letter-spacing: 1.1px;
cursor: pointer;
font-weight: 600;
}
@media (max-width: 800px) {
.content-button {
max-width: 230px;
max-height: 65px;
}
}
@media (max-width: 450px) {
.content-button {
max-width: 175px;
max-height: 60px;
font-size: 18px;
}
}

View File

@ -0,0 +1,19 @@
import React from "react"
import "./contentTitle.css"
export const ContentTitle = (props) => {
return (
<div>
<div>
<div className="content__info-title">
<h2>{props.title}</h2>
</div>
<div className="content__description">
<h2>{props.description}</h2>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,31 @@
.content__info-title h2 {
font-family: 'GT Eesti Pro Display';
font-weight: 700;
font-size: 25px;
color: #000000;
}
.content__info-title h2:after {
content: "";
display: flex;
width: 315px;
height: 5px;
border-radius: 3px;
background-color: #54b611;
margin-top: 25px;
margin-bottom: 30px;
}
.content__description h2 {
font-family: 'GT Eesti Pro Display';
font-weight: 300;
font-size: 12px;
margin-bottom: 30px;
}
@media (max-width: 450px) {
.content__info-title h2:after {
width: 200px;
}
}

View File

@ -0,0 +1,12 @@
import React from "react";
import "./contentTitleAbout.css"
export const ContentTitleAbout = (props) => {
return (
<div>
<div className="content__description-title">
<h2>{props.descriptionTitle}</h2>
</div>
</div>
)
}

View File

@ -0,0 +1,7 @@
.content__description-title h2 {
font-family: 'GT Eesti Pro Display';
font-weight: 400;
font-size: 22px;
line-height: 36px;
padding-bottom: 29px;
}

View File

@ -0,0 +1,97 @@
import React, { useEffect, useState } from "react";
import { ContentTitle } from "../ContentTitle/ContentTitle"
import { ContentButton } from "../ContentButton/ContentButton"
import { BookkeepingFormField } from "../BookkeepingFormField/BookkeepingFormField"
import { BookkepingSelect } from '../BookkepingSelect/BookkepingSelect';
import { BookkepingInput } from '../BookkepingInput/BookkepingInput';
import { fetchGet } from '../../../../server/server'
import { Link } from "react-router-dom"
export const ContractContent = () => {
const [templates, setTemplates] = useState([])
const [selectedTemplate, setSelectedTemplate] = useState()
const [templatedFields, setTemplatedFields] = useState([])
useEffect(() => {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/document/get-document-list`,
}).then((res) => {
setTemplates(res)
})
}, [])
useEffect(() => {
if (selectedTemplate === undefined) {
return
}
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/document/get-document?document_id=${selectedTemplate}`,
})
.then((res) => {
setTemplatedFields(res[0].templateDocumentFields)
})
}, [selectedTemplate])
return (
<div>
<div className="content__info">
<ContentTitle title="Создание договора" description="# Описание" />
<div className="content__info-main">
<form className='contract'>
<div className="contract__create">
<div className="contract__title">Создание договора </div>
<input type="text" className="contract__number" placeholder="#" />
<span>от</span>
<input type="date" className="contract__date" />
</div>
<BookkeepingFormField title="Шаблон документа"
Component={BookkepingSelect}
innerComponentProps={{
onSelect: setSelectedTemplate,
textField: "title",
options: templates,
defaultIndexSelected: 0,
}}
action={{
text: "Добавить свой шаблон",
method: () => {}
}}
/>
{templatedFields.map((field, index ) =>
<BookkeepingFormField title={field.field.title} key={index}
Component={BookkepingInput}
innerComponentProps={{
placeholder: "Введите данные",
}}
/>
)}
<div className="content__btn-list">
<ContentButton styles={{ width: "290px",
height: "75px",
boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
borderRadius: "38px",
backgroundColor: "#b64b3e",
border: "none",
color: "#ffffff",
}}>Сохранить</ContentButton>
<Link to="/documents" className="link-act-button">
<div className='act-Button'>
<ContentButton styles={{color: "#282828",
marginLeft: "40px",
background: "none",
border: "none"
}}>Отменить</ContentButton>
</div>
</Link>
</div>
</form>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,17 @@
import React from "react"
import "./documentCard.css"
import { Link } from "react-router-dom"
export const DocumentCard = (props) => {
return (
<div className="content-label-item">
<div className="content-label-item__img">
<img src={props.img} alt="" className="content-label-item-img__pic" />
</div>
<div className="content-label-item__info">
<Link to={props.link ? props.link : ""}><div className="content-label-item__add">Добавить</div></Link>
<div className="content-label-item__title">{props.title}</div>
</div>
</div>
)
}

View File

@ -0,0 +1,68 @@
.content-label-item {
display: flex;
width: 258px;
height: 112px;
border-radius: 20px;
background-color: #f9f9f9;
padding: 20px 20px;
margin-bottom: 94px;
}
.content-label-item__img {
width: 60%;
}
.content-label-item-img__pic {
position: relative;
top:7%;
left: -18%;
}
.content-label-item__info {
width: 40%;
}
.content-label-item__add {
line-height: 36px;
font-size: 12px;
font-family: 'GT Eesti Pro Display';
font-weight: 300;
display: flex;
justify-content: end;
cursor: pointer;
position: relative;
color: black;
}
.content-label-item__add:before,
.content-label-item__add:after {
cursor: pointer;
content: "";
width: 10%;
height: 1px;
background-color: #54b611;
border-radius: 3px;
position: absolute;
z-index: 1;
}
.content-label-item__add:before {
transform-origin: left top;
transform: rotate(90deg)
translate3d(0px, -2px, 0);
top: 14px;
left: 24px;
}
.content-label-item__add:after {
transform-origin: left bottom;
bottom: 17px;
left: 21px;
}
.content-label-item__title {
font-size: 18px;
font-family: 'GT Eesti Pro Display';
font-weight: 400;
color: black;
}

View File

@ -0,0 +1,67 @@
.nav {
width: 34%;
border: 1px solid #efefef;
background-color: rgb(249, 249, 249, 0.87);
height: 700px;
border-radius: 0 0 20px;
display: block;
width: 373px;
}
.nav__btn-list {
display:grid;
justify-content: center;
margin-top: 55px;
}
.nav__btn-item {
width: 280px;
height: 62px;
border-radius: 31px;
border-style: none;
cursor: pointer;
font-size: 16px;
font-family: "Muller";
font-weight: 700;
letter-spacing: 0.8px;
color: #5cb42c;
border: 2px solid #54b611;
background-color: #fdfdfd;
margin-top: 20px;
}
.nav__btn-item--selected {
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%), linear-gradient(36deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.16) 47%, rgba(255, 255, 255, 0.17) 50%, rgba(255, 255, 255, 0) 100%);
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
color: #ffffff;
}
.adaptive {
display: none;
}
@media (max-width: 1240px) {
.nav {
width: 100%;
max-height: 450px;
background: none;
border:none;
height: auto;
}
.nav__btn-list {
margin-top: 0;
}
.nav__btn-item {
display: none;
}
.adaptive {
display: block;
}
}
@media (max-width: 450px) {
.nav__btn-item {
width: 210px;
font-size: 14px;
}
}

View File

@ -0,0 +1,26 @@
import React from "react"
import "./Sidebar.css"
import { Link } from "react-router-dom"
import { useLocation } from 'react-router-dom'
const getActiveStatus = ({ pathName, location }) => {
const pathNameRegex = new RegExp(pathName,'g')
return (
location.pathname.match(pathNameRegex) ? "nav__btn-item nav__btn-item--selected" : "nav__btn-item")
}
export const Sidebar = () => {
const location = useLocation();
return(
<div className="nav">
<div className="nav__btn-list">
<Link to="/documents"><button className={getActiveStatus({pathName:"/documents", location })} >Документы</button></Link>
<Link to="/tax"><button className={getActiveStatus({pathName:"/tax", location })}>Налоги</button></Link>
<Link to="/money"><button className={getActiveStatus({pathName:"/money", location })}>Деньги</button></Link>
<button className="nav__btn-item">Реквизиты</button>
<button className="nav__btn-item adaptive">Меню</button>
</div>
</div>
)
}

BIN
src/images/Contract.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/images/Reporting-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/images/Reporting-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src/images/Reporting-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
src/images/Tax.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/images/Taxes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
src/images/act.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/images/money.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

13
src/pages/ActPage.js Normal file
View File

@ -0,0 +1,13 @@
import React from 'react';
import { ActContent } from "../components/features/bookkeeping/ActContent/ActContent"
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
export const ActPage = () => {
return (
<div>
<BookkeepingTemplete showBreadcrumps nameBreeadcrumps="Создание акта">
<ActContent></ActContent>
</BookkeepingTemplete>
</div>
)
}

18
src/pages/Bookkeeping.js Normal file
View File

@ -0,0 +1,18 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { BookkeepingContent } from "../components/features/bookkeeping/BookkeepingContent/BookkeepingContent"
const Bookkeeping = () => {
return(
<div>
<BookkeepingTemplete>
<BookkeepingContent></BookkeepingContent>
</BookkeepingTemplete>
</div>
)
}
export default Bookkeeping

13
src/pages/ContractPage.js Normal file
View File

@ -0,0 +1,13 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { ContractContent } from "../components/features/bookkeeping/ContractContent/ContractContent"
export const ContractPage = () => {
return (
<div>
<BookkeepingTemplete showBreadcrumps nameBreeadcrumps="Создание договора">
<ContractContent />
</BookkeepingTemplete>
</div>
)
}

13
src/pages/MoneyPage.js Normal file
View File

@ -0,0 +1,13 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { MoneyContent } from "../components/features/Money/MoneyContent"
export const MoneyPage = () => {
return (
<div>
<BookkeepingTemplete>
<MoneyContent></MoneyContent>
</BookkeepingTemplete>
</div>
)
}

11
src/pages/TaxPage.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react';
import { BookkeepingTemplete } from "../components/features/bookkeeping/BookkeepingTemplete/BookkeepingTemplete"
import { TaxContent } from "../components/features/Taxes/TaxContent/TaxContent"
export const TaxPage = () => {
return (
<BookkeepingTemplete>
<TaxContent></TaxContent>
</BookkeepingTemplete>
)
}

View File

@ -35,10 +35,13 @@ export const outstaffingSlice = createSlice({
setPositionId: (state, action) => { setPositionId: (state, action) => {
state.positionId = action.payload; state.positionId = action.payload;
}, },
setUserInfo: (state, action) => {
state.userInfo = action.payload;
}
}, },
}); });
export const { tags, profiles, selectedItems, auth, currentCandidate, filteredCandidates, setPositionId } = outstaffingSlice.actions; export const { tags, profiles, selectedItems, auth, currentCandidate, filteredCandidates, setPositionId, setUserInfo } = outstaffingSlice.actions;
export const selectProfiles = (state) => state.outstaffing.profiles; export const selectProfiles = (state) => state.outstaffing.profiles;
export const selectTags = (state) => state.outstaffing.tags; export const selectTags = (state) => state.outstaffing.tags;
@ -47,5 +50,6 @@ export const selectItems = (state) => state.outstaffing.selectedItems;
export const selectCurrentCandidate = (state) => state.outstaffing.currentCandidate; export const selectCurrentCandidate = (state) => state.outstaffing.currentCandidate;
export const selectAuth = (state) => state.outstaffing.auth; export const selectAuth = (state) => state.outstaffing.auth;
export const getPositionId = (state) => state.outstaffing.positionId; export const getPositionId = (state) => state.outstaffing.positionId;
export const selectUserInfo = (state) => state.outstaffing.userInfo;
export default outstaffingSlice.reducer; export default outstaffingSlice.reducer;

View File

@ -49,11 +49,12 @@ export const fetchAuth = async ({
response.json().then((resJSON) => { response.json().then((resJSON) => {
localStorage.setItem('auth_token', resJSON.access_token) localStorage.setItem('auth_token', resJSON.access_token)
localStorage.setItem('id', resJSON.id)
localStorage.setItem( localStorage.setItem(
'access_token_expired_at', 'access_token_expired_at',
resJSON.access_token_expired_at resJSON.access_token_expired_at
) )
dispatch() dispatch(resJSON)
}) })
} catch (error) { } catch (error) {
console.error('Error occured: ', error) console.error('Error occured: ', error)