almost done
This commit is contained in:
+22
@@ -1,8 +1,30 @@
|
||||
import logo from "./logo.svg";
|
||||
import { Main } from "./components/Main/Main";
|
||||
import "./App.css";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { calcSlice } from "./store/reducers/calcSlice";
|
||||
import { calculationSlice } from "./store/reducers/calculationSlice";
|
||||
import { useEffect } from "react";
|
||||
|
||||
function App() {
|
||||
const state = useSelector((state) => state.calcReducer);
|
||||
const dispatch = useDispatch();
|
||||
const { handleCalculation, handleResultUpdated, handleResultDefault } = calculationSlice.actions;
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
handleCalculation({
|
||||
squareApartment: state.squareApartment,
|
||||
priceAvarage: state.priceAvarage,
|
||||
squareRC: state.squareRC,
|
||||
consultation: state.consultation,
|
||||
})
|
||||
);
|
||||
dispatch(handleResultUpdated())
|
||||
dispatch(handleResultDefault())
|
||||
|
||||
}, [state]);
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<Main></Main>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const AveragePriceApartment = () => {
|
||||
const { priceAvarage, total } = useSelector((state) => state.calcReducer);
|
||||
|
||||
console.log(priceAvarage)
|
||||
const name = "priceApartment";
|
||||
const name = "priceAvarage";
|
||||
const min = 30000;
|
||||
const max = 200000;
|
||||
const large = true
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import './InputComponent.css'
|
||||
import edit from './edit.svg'
|
||||
import "./InputComponent.css";
|
||||
import edit from "./edit.svg";
|
||||
import { useEffect, useState } from "react";
|
||||
import CurrencyInput from "react-currency-input-field";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { calcSlice } from "../../store/reducers/calcSlice";
|
||||
|
||||
export const InputComponent = ({ value, name, min, max, inputClass, large }) => {
|
||||
export const InputComponent = ({
|
||||
value,
|
||||
name,
|
||||
min,
|
||||
max,
|
||||
inputClass,
|
||||
large,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { handleValue, handleTotal } = calcSlice.actions;
|
||||
const { handleValue } = calcSlice.actions;
|
||||
const [valid, setValid] = useState(false);
|
||||
const [valueInput, setValueInput] = useState(value);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setValueInput(value)
|
||||
}, [value])
|
||||
setValueInput(value);
|
||||
}, [value]);
|
||||
|
||||
const handleNumber = (number) => {
|
||||
if (!number) {
|
||||
@@ -25,13 +31,9 @@ export const InputComponent = ({ value, name, min, max, inputClass, large }) =>
|
||||
return num;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(handleTotal());
|
||||
}, []);
|
||||
|
||||
const handleTotalValues = (value, name) => {
|
||||
dispatch(handleValue({ name: name, value: value }));
|
||||
dispatch(handleTotal());
|
||||
const toNum = parseInt(value)
|
||||
dispatch(handleValue({ name: name, value: toNum }));
|
||||
};
|
||||
|
||||
const handleState = (e) => {
|
||||
@@ -83,7 +85,6 @@ export const InputComponent = ({ value, name, min, max, inputClass, large }) =>
|
||||
const handleFocusLeft = (e) => {
|
||||
const { name, value } = e.target;
|
||||
const number = handleNumber(value);
|
||||
console.log(number, value, "valuie");
|
||||
if (number === 0 && number === undefined) {
|
||||
handleTotalValues(min, name);
|
||||
return;
|
||||
@@ -116,14 +117,14 @@ export const InputComponent = ({ value, name, min, max, inputClass, large }) =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='number-container'>
|
||||
<div className="number-container">
|
||||
<CurrencyInput
|
||||
className="number-input"
|
||||
decimalsLimit={3}
|
||||
allowNegativeValue={false}
|
||||
allowDecimals={false}
|
||||
groupSeparator={" "}
|
||||
style={large ? {width: '78px'} : {width: '45px'}}
|
||||
style={large ? { width: "78px" } : { width: "45px" }}
|
||||
min={min}
|
||||
max={max}
|
||||
name={name}
|
||||
@@ -131,7 +132,7 @@ export const InputComponent = ({ value, name, min, max, inputClass, large }) =>
|
||||
onBlur={(e) => handleFocusLeft(e)}
|
||||
onValueChange={(value, name) => handleOnValueChange(value, name)}
|
||||
/>
|
||||
<img className='edit-icon' alt='edit/изменить' src={edit} />
|
||||
<img className="edit-icon" alt="edit/изменить" src={edit} />
|
||||
</div>
|
||||
|
||||
<div className="input-container">
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
import './InputNumber.css'
|
||||
import { useEffect } from 'react';
|
||||
import CurrencyInput from 'react-currency-input-field';
|
||||
import useFormWithValidation from "../../hooks/useFormWithValidation";
|
||||
import "./InputNumber.css";
|
||||
import CurrencyInput from "react-currency-input-field";
|
||||
import { calcSlice } from "../../store/reducers/calcSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
export const InputNumber = ({ number, handleState }) => {
|
||||
export const InputNumber = ({ number }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { handleValue } = calcSlice.actions;
|
||||
|
||||
const { values, handleValidity, resetForm, isTrigger } =
|
||||
useFormWithValidation(0, 5000);
|
||||
const onValueChange = (value, name) => {
|
||||
const toNum = parseInt(value)
|
||||
dispatch(handleValue({ name: name, value: toNum }));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isTrigger) {
|
||||
handleState(prevState => ({ ...prevState, rcNumber: parseInt(values) }))
|
||||
|
||||
}
|
||||
}, [values])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
handleValidity(number)
|
||||
}, [])
|
||||
|
||||
|
||||
return (<div>
|
||||
<span className="input-number-caption">
|
||||
Кв. м жилья в жилом комплексе
|
||||
</span>
|
||||
<CurrencyInput
|
||||
value={values}
|
||||
onValueChange={(value, name) => handleValidity(value)}
|
||||
className="input-number"
|
||||
name='squareRC'
|
||||
decimalSeparator=' '
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<span className="input-number-caption">
|
||||
Кв. м жилья в жилом комплексе
|
||||
</span>
|
||||
<CurrencyInput
|
||||
value={number}
|
||||
onValueChange={(value, name) => onValueChange(value, name)}
|
||||
className="input-number"
|
||||
name="squareRC"
|
||||
decimalSeparator=" "
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
width: 68%;
|
||||
background: #1C1D22;
|
||||
border-radius: 4px;
|
||||
padding: 56px;
|
||||
padding: 40px;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
gap: 32px;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
padding-top: 74px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.map-main-component {
|
||||
|
||||
+15
-19
@@ -3,35 +3,33 @@ import { useEffect } from "react";
|
||||
import { calcSlice } from "../../store/reducers/calcSlice";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
||||
|
||||
import { InputSelect } from "../InputSelect/InputSelect";
|
||||
import { InputNumber } from "../InputNumber/InputNumber";
|
||||
import { AveragePriceApartment } from '../AveragePriceApartment/AveragePriceApartment';
|
||||
import { AverageSquareApartment } from '../AverageSquareApartment/AverageSquareApartment'
|
||||
import { AveragePriceApartment } from "../AveragePriceApartment/AveragePriceApartment";
|
||||
import { AverageSquareApartment } from "../AverageSquareApartment/AverageSquareApartment";
|
||||
import { ConsultationOffice } from "../ConsultationOffice/ConsultationOffice";
|
||||
import { ConsultationReserv } from "../ConsultationReserv/ConsultationReserv";
|
||||
import { Sales } from "../Sales/Sales";
|
||||
import { ResultBlock } from "../ResultBlock/ResultBlock";
|
||||
|
||||
const INITIAL_REGION = "e5b7edfb-17ec-475f-8631-bc796ad19909";
|
||||
|
||||
export const Main = ({ }) => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const { handleSelectRegion, handleOptions, handleValue } = calcSlice.actions
|
||||
const { selectedRegion, options, squareRC } = useSelector((state) => state.calcReducer);
|
||||
|
||||
export const Main = ({}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { handleSelectRegion, handleOptions, handleValue } = calcSlice.actions;
|
||||
const { selectedRegion, options, squareRC } = useSelector(
|
||||
(state) => state.calcReducer
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(handleSelectRegion(INITIAL_REGION))
|
||||
dispatch(handleOptions())
|
||||
}, [])
|
||||
|
||||
dispatch(handleSelectRegion(INITIAL_REGION));
|
||||
dispatch(handleOptions());
|
||||
}, []);
|
||||
|
||||
const handleSelect = (element) => {
|
||||
dispatch(handleSelectRegion(element.id))
|
||||
dispatch(handleSelectRegion(element.id));
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="main">
|
||||
<div className="input-container-main">
|
||||
@@ -45,12 +43,10 @@ export const Main = ({ }) => {
|
||||
<AveragePriceApartment></AveragePriceApartment>
|
||||
</div>
|
||||
<div className="input-container-second">
|
||||
<ConsultationReserv></ConsultationReserv>
|
||||
<ConsultationOffice></ConsultationOffice>
|
||||
<ConsultationReserv></ConsultationReserv>
|
||||
<Sales></Sales>
|
||||
</div>
|
||||
<div className="result-block">
|
||||
|
||||
<ResultBlock></ResultBlock>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
.result-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.rub-container {
|
||||
}
|
||||
|
||||
.numbers-container {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.result-block {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 46px;
|
||||
}
|
||||
|
||||
.result-number {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 54px;
|
||||
line-height: 100%;
|
||||
/* identical to box height, or 58px */
|
||||
margin: 0;
|
||||
color: #219653;
|
||||
}
|
||||
|
||||
.result-caption {
|
||||
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 22px;
|
||||
line-height: 120%;
|
||||
/* identical to box height, or 29px */
|
||||
margin: 0;
|
||||
color: #219653;
|
||||
}
|
||||
|
||||
.result-number_one {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
line-height: 100%;
|
||||
/* identical to box height, or 24px */
|
||||
|
||||
/* Gray/Accent */
|
||||
|
||||
color: #393c46;
|
||||
}
|
||||
|
||||
.result-caption_one {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 10px;
|
||||
line-height: 120%;
|
||||
/* identical to box height, or 12px */
|
||||
|
||||
/* Gray/Accent */
|
||||
|
||||
color: #393c46;
|
||||
}
|
||||
|
||||
.result-diff-caption {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 140%;
|
||||
/* or 20px */
|
||||
|
||||
/* White */
|
||||
|
||||
color: #f7f7f7;
|
||||
}
|
||||
|
||||
.diff {
|
||||
color: #d375ff;
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import "./ResultBlock.css";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export const ResultBlock = () => {
|
||||
const { timeUpdated, timeDefalut, priceDefault, priceUpdated } = useSelector(
|
||||
(state) => state.calculationReducer
|
||||
);
|
||||
|
||||
const handleMouthPostifx = (num) => {
|
||||
num = num % 100;
|
||||
|
||||
if (num > 19) {
|
||||
num = num % 10;
|
||||
}
|
||||
|
||||
switch (num) {
|
||||
case 1:
|
||||
return 0;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMounth = (num, type) => {
|
||||
const monthesPostfixes = [" месяц", " месяца", " месяцев"];
|
||||
|
||||
let postfix = handleMouthPostifx(num);
|
||||
let value1 = monthesPostfixes[postfix];
|
||||
|
||||
return handleNode(num, value1, type);
|
||||
};
|
||||
|
||||
const shortenNumRu = (num, type) => {
|
||||
if (isNaN(num)) throw new Error(num + " is not a Number!");
|
||||
let currency = {
|
||||
0: "",
|
||||
1: " тыс. руб.",
|
||||
2: " млн. руб",
|
||||
3: " млрд. руб",
|
||||
};
|
||||
let thousands = Math.floor((("" + num).length - 1) / 3);
|
||||
let coef = 1000 ** thousands;
|
||||
|
||||
console.log(thousands, "COEF");
|
||||
|
||||
let value = (num / coef).toFixed(1);
|
||||
let value1 = currency[thousands];
|
||||
|
||||
return handleNode(value, value1, type);
|
||||
};
|
||||
|
||||
const handleNode = (value, value1, type) => {
|
||||
if (type === 1) {
|
||||
return (
|
||||
<>
|
||||
<span className="result-number">{value}</span>
|
||||
<span className="result-caption">{value1}</span>
|
||||
</>
|
||||
);
|
||||
} else if (type === 0) {
|
||||
return (
|
||||
<>
|
||||
<span className="result-number_one">{value}</span>
|
||||
<span className="result-caption_one">{value1}</span>
|
||||
</>
|
||||
);
|
||||
} else if (type === 3) {
|
||||
return (
|
||||
<span className="result-diff-caption">
|
||||
На <span className="diff">{`${value} ${value1}`}</span> вы сократили
|
||||
срок реализации проекта
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span className="result-diff-caption">
|
||||
На <span className="diff">{`${value} ${value1}`}</span> в месяц вы
|
||||
заработали больше с помощью нашего инструмента продаж
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="result-block">
|
||||
<div className="result-container">
|
||||
<div className="numbers-container">
|
||||
<div className="rub-container">
|
||||
{handleMounth(Math.round(timeDefalut), 1)}
|
||||
</div>
|
||||
<div className="rub-container padding">
|
||||
{handleMounth(Math.round(timeUpdated), 0)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{handleMounth(Math.round(timeDefalut) - Math.round(timeUpdated), 3)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="result-container">
|
||||
<div className="numbers-container">
|
||||
<div className="rub-container">
|
||||
{shortenNumRu(Math.round(priceUpdated), 1)}
|
||||
</div>
|
||||
<div className="rub-container padding">
|
||||
{shortenNumRu(Math.round(priceDefault), 0)}
|
||||
</div>
|
||||
</div>
|
||||
<div>{shortenNumRu(Math.floor(priceUpdated - priceDefault), 4)}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,15 +3,16 @@ import { regions } from "../../utils/array";
|
||||
|
||||
const initialState = {
|
||||
squareRC: 1500,
|
||||
squareApartment: 100,
|
||||
squareApartment: 45,
|
||||
priceAvarage: 100000,
|
||||
consultation: 100,
|
||||
consultationReserv: 30,
|
||||
sales: 10,
|
||||
total: null,
|
||||
averagePriceApartment: null,
|
||||
regions: regions,
|
||||
selectedRegion: {},
|
||||
options: []
|
||||
options: [],
|
||||
averageNumberApartment: null,
|
||||
};
|
||||
|
||||
export const calcSlice = createSlice({
|
||||
@@ -20,16 +21,18 @@ export const calcSlice = createSlice({
|
||||
reducers: {
|
||||
handleOptions(state) {
|
||||
const options = state.regions.filter((i) => i.name);
|
||||
return { ...state, options: options }
|
||||
|
||||
return { ...state, options: options };
|
||||
},
|
||||
handleSelectRegion(state, action) {
|
||||
console.log(action)
|
||||
const region = state.regions.find((i) => i.id === action.payload);
|
||||
return { ...state, selectedRegion: region, priceAvarage: region.price }
|
||||
return {
|
||||
...state,
|
||||
selectedRegion: region,
|
||||
priceAvarage: parseInt(region.price),
|
||||
};
|
||||
},
|
||||
handleValue(state, action) {
|
||||
console.log(action.payload.value)
|
||||
console.log(action.payload.value, "ss");
|
||||
return { ...state, [action.payload.name]: action.payload.value };
|
||||
},
|
||||
handleInitalState(state, action) {
|
||||
@@ -39,8 +42,9 @@ export const calcSlice = createSlice({
|
||||
};
|
||||
},
|
||||
handleTotal(state) {
|
||||
const number = Number(state.squareApartment)
|
||||
state.total = number + 20;
|
||||
console.log(state.priceAvarage, "price");
|
||||
state.averagePriceApartment = state.squareApartment * state.priceAvarage;
|
||||
state.averageNumberApartment = state.squareRC / state.squareApartment;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const initialState = {
|
||||
averageNumberApartment: 0,
|
||||
averagePriceApartment: 0,
|
||||
priceDefault: 0,
|
||||
priceUpdated: 0,
|
||||
timeDefalut: 0,
|
||||
timeUpdated: 0,
|
||||
consultationNumber: 0,
|
||||
};
|
||||
|
||||
export const calculationSlice = createSlice({
|
||||
name: "calculation",
|
||||
initialState,
|
||||
reducers: {
|
||||
handleCalculation(state, action) {
|
||||
state.averagePriceApartment =
|
||||
action.payload.squareApartment * action.payload.priceAvarage;
|
||||
state.averageNumberApartment =
|
||||
action.payload.squareRC / action.payload.squareApartment;
|
||||
state.consultationNumber = action.payload.consultation;
|
||||
console.log(action.payload);
|
||||
},
|
||||
handleResultDefault(state) {
|
||||
|
||||
const reserved = (state.consultationNumber / 100) * 30;
|
||||
const sales = (reserved / 100) * 10;
|
||||
state.timeDefalut = state.averageNumberApartment / sales;
|
||||
state.priceDefault = sales * state.averagePriceApartment;
|
||||
console.log(state.averageNumberApartment, sales, 'reducer')
|
||||
},
|
||||
|
||||
handleResultUpdated(state) {
|
||||
const reserved = (state.consultationNumber / 100) * 45;
|
||||
const sales = (reserved / 100) * 20;
|
||||
state.timeUpdated = state.averageNumberApartment / sales;
|
||||
state.priceUpdated = sales * state.averagePriceApartment;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default calculationSlice.reducer;
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
import { combineReducers, configureStore } from "@reduxjs/toolkit";
|
||||
import calcReducer from "./reducers/calcSlice";
|
||||
import calculationReducer from './reducers/calculationSlice'
|
||||
|
||||
const rootReducer = combineReducers({ calcReducer });
|
||||
const rootReducer = combineReducers({ calcReducer, calculationReducer});
|
||||
|
||||
export const setupStore = () => {
|
||||
return configureStore({
|
||||
|
||||
Reference in New Issue
Block a user