refactoring and implementing refreshing items list every 2 seconds
This commit is contained in:
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import Select from "react-select";
|
||||
import { connect } from "react-redux";
|
||||
import { CATEGORY_SELECT, ITEMS_CHANGED } from "constants/actionTypes";
|
||||
import { hoc } from "utils/hoc";
|
||||
import { hoc, areObjectEqual } from "utils/helpers";
|
||||
import { createOlxLink } from "utils/createOlxLink";
|
||||
import axios from "axios";
|
||||
|
||||
@@ -29,21 +29,43 @@ const mapDispatchToProps = dispatch => ({
|
||||
onItemsChanged: items => dispatch({ type: ITEMS_CHANGED, items })
|
||||
});
|
||||
|
||||
let lastUpdateTime = null;
|
||||
let interval = null;
|
||||
class App extends React.Component {
|
||||
componentDidMount() {
|
||||
interval = setInterval(() => {
|
||||
if (lastUpdateTime && Date.now() - lastUpdateTime > 2000) {
|
||||
const { category, options, subcategory, onItemsChanged } = this.props;
|
||||
let url = createOlxLink(category, subcategory, options);
|
||||
url = encodeURI(url);
|
||||
if (url) {
|
||||
axios
|
||||
.get(`/api/${url}`)
|
||||
.then(response => onItemsChanged(response.data))
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
lastUpdateTime = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(interval);
|
||||
}
|
||||
componentWillReceiveProps(newProps) {
|
||||
const { subcategory, category, options } = this.props;
|
||||
if (
|
||||
newProps.subcategory !== subcategory ||
|
||||
newProps.category !== category ||
|
||||
!areObjectEqual(newProps.options, options)
|
||||
) {
|
||||
lastUpdateTime = Date.now();
|
||||
}
|
||||
}
|
||||
handleChange = selectedOption => {
|
||||
this.props.onCategoryChanged(selectedOption);
|
||||
};
|
||||
|
||||
getDataFromOlx = () => {
|
||||
const { category, options, subcategory, onItemsChanged } = this.props;
|
||||
let url = createOlxLink(category, subcategory, options);
|
||||
url = encodeURI(url);
|
||||
axios
|
||||
.get(`/api/${url}`)
|
||||
.then(response => onItemsChanged(response.data))
|
||||
.catch(error => console.log(error));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { category } = this.props;
|
||||
|
||||
@@ -58,7 +80,6 @@ class App extends React.Component {
|
||||
Vozila: <Vozila />,
|
||||
Nekretnine: <Nekretnine />
|
||||
})}
|
||||
<button onClick={this.getDataFromOlx}>Get Data from OLX </button>
|
||||
<ItemsContainer />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import React from "react";
|
||||
import Select from "react-select";
|
||||
import { subcategorywrapper } from "utils/subcategorywrapper";
|
||||
import { hoc } from "utils/hoc";
|
||||
import { hoc } from "utils/helpers";
|
||||
|
||||
import Stanovi from "../subcategories/nekretnine/Stanovi";
|
||||
import Kuce from "../subcategories/nekretnine/Kuce";
|
||||
|
||||
const options = [
|
||||
{ value: "Stanovi", label: "Stanovi" },
|
||||
{ value: "Kuce", label: "Kuce" }
|
||||
];
|
||||
const options = [{ value: 23, label: "Stanovi" }, { value: 24, label: "Kuce" }];
|
||||
|
||||
class Nekretnine extends React.Component {
|
||||
handleChange = selectedOption => {
|
||||
@@ -27,8 +24,8 @@ class Nekretnine extends React.Component {
|
||||
options={options}
|
||||
/>
|
||||
{hoc(subcategory && subcategory.value, {
|
||||
Stanovi: <Stanovi />,
|
||||
Kuce: <Kuce />
|
||||
23: <Stanovi />,
|
||||
24: <Kuce />
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,35 +3,35 @@ import Select from "react-select";
|
||||
import Automobili from "../subcategories/vozila/Automobili";
|
||||
import Motocikli from "../subcategories/vozila/Motocikli";
|
||||
import { subcategorywrapper } from "utils/subcategorywrapper";
|
||||
import { hoc } from "utils/hoc";
|
||||
import { hoc } from "utils/helpers";
|
||||
|
||||
const options = [
|
||||
{ value: "Automobili", label: "Automobili" },
|
||||
{ value: "Motocikli", label: "Motocikli" }
|
||||
{ value: 18, label: "Automobili" },
|
||||
{ value: 21, label: "Motocikli" }
|
||||
];
|
||||
|
||||
class Vozila extends React.Component {
|
||||
handleChange = selectedOption => {
|
||||
this.props.onSubCategoryChanged(selectedOption);
|
||||
};
|
||||
handleChange = selectedOption => {
|
||||
this.props.onSubCategoryChanged(selectedOption);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { subcategory } = this.props;
|
||||
render() {
|
||||
const { subcategory } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
value={subcategory}
|
||||
onChange={this.handleChange}
|
||||
options={options}
|
||||
/>
|
||||
{hoc(subcategory && subcategory.value, {
|
||||
Automobili: <Automobili />,
|
||||
Motocikli: <Motocikli />
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
value={subcategory}
|
||||
onChange={this.handleChange}
|
||||
options={options}
|
||||
/>
|
||||
{hoc(subcategory && subcategory.value, {
|
||||
18: <Automobili />,
|
||||
21: <Motocikli />
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default subcategorywrapper(Vozila);
|
||||
|
||||
@@ -2,5 +2,6 @@ export const rangeOptions = {
|
||||
min: 0,
|
||||
max: 100000,
|
||||
defaultValues: [0, 100000],
|
||||
step: 100
|
||||
step: 100,
|
||||
optionNames: ["od", "do"]
|
||||
};
|
||||
|
||||
@@ -5,42 +5,50 @@ const elements = [
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Uknjizeno",
|
||||
optionName: "uknjizeno"
|
||||
optionName: "uknjizeno-zk_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Namjesteno",
|
||||
optionName: "namjesteno"
|
||||
optionName: "namjestena_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Nedavno adaptirano",
|
||||
optionName: "nedavno_adaptirano"
|
||||
optionName: "nedavno-adaptirana_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Garaza",
|
||||
optionName: "garaza"
|
||||
optionName: "gara-a_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Balkon",
|
||||
optionName: "balkon"
|
||||
optionName: "balkon_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Voda",
|
||||
optionName: "voda"
|
||||
optionName: "voda_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Plin",
|
||||
optionName: "plin"
|
||||
optionName: "plin_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Bazen",
|
||||
optionName: "bazen"
|
||||
optionName: "bazen_checkbox",
|
||||
value: "on"
|
||||
}
|
||||
];
|
||||
class KuceFilter extends React.Component {
|
||||
|
||||
@@ -5,47 +5,56 @@ const elements = [
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Novogradnja",
|
||||
optionName: "novogradnja"
|
||||
optionName: "novogradnja_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Namjesten",
|
||||
optionName: "namjesten"
|
||||
optionName: "namjesten_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Nedavno adaptiran",
|
||||
optionName: "Nedavno_adaptiran"
|
||||
optionName: "nedavno-adaptiran_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Uknjizeno",
|
||||
optionName: "uknjizeno"
|
||||
optionName: "uknjizeno-zk_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Lift",
|
||||
optionName: "lift"
|
||||
optionName: "lift_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Balkon",
|
||||
optionName: "balkon"
|
||||
optionName: "balkon_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Parking",
|
||||
optionName: "parking"
|
||||
optionName: "parking_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Plin",
|
||||
optionName: "plin"
|
||||
optionName: "plin_checkbox",
|
||||
value: "on"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "kablovska",
|
||||
optionName: "kablovska"
|
||||
optionName: "kablovska-tv_checkbox",
|
||||
value: "on"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@ export const rangeOptions = {
|
||||
min: 0,
|
||||
max: 1000,
|
||||
defaultValues: [0, new Date().getFullYear()],
|
||||
step: 1
|
||||
step: 1,
|
||||
optionNames: ["kvadrata_min", "kvadrata_max"]
|
||||
};
|
||||
|
||||
@@ -2,5 +2,6 @@ export const rangeOptions = {
|
||||
min: 0,
|
||||
max: 100000,
|
||||
defaultValues: [0, 100000],
|
||||
step: 100
|
||||
step: 100,
|
||||
optionNames: ["od", "do"]
|
||||
};
|
||||
|
||||
@@ -2,5 +2,6 @@ export const rangeOptions = {
|
||||
min: 1960,
|
||||
max: new Date().getFullYear(),
|
||||
defaultValues: [1960, new Date().getFullYear()],
|
||||
step: 1
|
||||
step: 1,
|
||||
optionNames: ["godiste_min", "godiste_max"]
|
||||
};
|
||||
|
||||
@@ -2,26 +2,31 @@ export const elements = [
|
||||
{
|
||||
name: "Dizel",
|
||||
optionName: "gorivo_select_dizel",
|
||||
type: "checkbox"
|
||||
type: "checkbox",
|
||||
value: "Dizel"
|
||||
},
|
||||
{
|
||||
name: "Benzin",
|
||||
optionName: "gorivo_select_benzin",
|
||||
type: "checkbox"
|
||||
type: "checkbox",
|
||||
value: "Benzin"
|
||||
},
|
||||
{
|
||||
name: "Plin",
|
||||
optionName: "gorivo_select_plin",
|
||||
type: "checkbox"
|
||||
type: "checkbox",
|
||||
value: "Plin"
|
||||
},
|
||||
{
|
||||
name: "Hibrid",
|
||||
optionName: "gorivo_select_hibrid",
|
||||
type: "checkbox"
|
||||
type: "checkbox",
|
||||
value: "Hibrid"
|
||||
},
|
||||
{
|
||||
name: "Elektro",
|
||||
optionName: "gorivo_select_elektro",
|
||||
type: "checkbox"
|
||||
type: "checkbox",
|
||||
value: "Elektro"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
export const kilometrazaOptions = {
|
||||
kilometraMin: {
|
||||
choices: [{ value: 5000, label: "5000" }, { value: 10000, label: "10000" }],
|
||||
value: "kilometrazaMin",
|
||||
optionName: "kilometrazaMin"
|
||||
value: "kilometra-a_min",
|
||||
optionName: "kilometra-a_min"
|
||||
},
|
||||
kilometraMax: {
|
||||
choices: [
|
||||
{ value: 15000, label: "15000" },
|
||||
{ value: 200000, label: "200000" }
|
||||
],
|
||||
value: "kilometrazaMax",
|
||||
optionName: "kilometrazaMax"
|
||||
value: "kilometra-a_max",
|
||||
optionName: "kilometra-a_max"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const proizvodacOptions = {
|
||||
choices: [{ value: "1900", label: "Audi" }, { value: "9000", label: "Ford" }],
|
||||
value: "proizvodac",
|
||||
optionName: "proizvodac"
|
||||
value: "v_b",
|
||||
optionName: "v_b"
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ export const elements = [
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "Udarena vozila",
|
||||
optionName: "udaren_checkbox"
|
||||
optionName: "udaren_checkbox",
|
||||
value: "on"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -19,9 +19,9 @@ class VozilaFilter extends React.Component {
|
||||
elements={Stanje.elements}
|
||||
/>
|
||||
<SelectWrapper {...Proizvodac.proizvodacOptions} />
|
||||
<RangeWrapper {...Cijena.rangeOptions} optionName="cijena" />
|
||||
<RangeWrapper {...Cijena.rangeOptions} />
|
||||
<SelectWrapper {...Lokacija.lokacijaOptions} />
|
||||
<RangeWrapper {...Godiste.rangeOptions} optionName="godiste" />
|
||||
<RangeWrapper {...Godiste.rangeOptions} />
|
||||
<SelectWrapper {...Kilometraza.kilometrazaOptions.kilometraMin} />
|
||||
<SelectWrapper {...Kilometraza.kilometrazaOptions.kilometraMax} />
|
||||
<CheckboxAndRadioWrapper
|
||||
|
||||
@@ -5,7 +5,7 @@ class CheckboxAndRadioWrapper extends React.Component {
|
||||
optionChange = (option, optionName, type) => {
|
||||
const optionTypePicker = {
|
||||
radio: option.target.value,
|
||||
checkbox: option.target.checked
|
||||
checkbox: option.target.checked ? option.target.value : false
|
||||
};
|
||||
const { onOptionChanged } = this.props;
|
||||
onOptionChanged({
|
||||
@@ -15,10 +15,9 @@ class CheckboxAndRadioWrapper extends React.Component {
|
||||
};
|
||||
isChecked = (type, value, optionName) => {
|
||||
const { options } = this.props;
|
||||
return type === "checkbox"
|
||||
? value
|
||||
: options.hasOwnProperty(optionName) &&
|
||||
options[optionName] === String(value);
|
||||
return options.hasOwnProperty(optionName) && type === "checkbox"
|
||||
? options[optionName]
|
||||
: options[optionName] === String(value);
|
||||
};
|
||||
renderElements = (elements, componentName) => {
|
||||
return elements.map(({ type, value, name, optionName } = {}) => (
|
||||
|
||||
@@ -4,22 +4,28 @@ import { optionchangewrapper } from "utils/optionchangewrapper";
|
||||
import "rc-slider/assets/index.css";
|
||||
|
||||
class RangeWrapper extends React.Component {
|
||||
sendAction = (optionName, optionValue) => {
|
||||
this.props.onOptionChanged({
|
||||
optionName,
|
||||
optionValue
|
||||
});
|
||||
};
|
||||
handleRangeChange = ([min, max] = this.props.defaultValues) => {
|
||||
this.inputMin.value = min;
|
||||
this.inputMax.value = max;
|
||||
const { onOptionChanged, optionName } = this.props;
|
||||
onOptionChanged({
|
||||
optionName,
|
||||
optionValue: [min, max]
|
||||
});
|
||||
const { optionNames } = this.props;
|
||||
const optionValues = [min, max];
|
||||
optionNames.forEach((optionName, index) =>
|
||||
this.sendAction(optionName, optionValues[index])
|
||||
);
|
||||
};
|
||||
|
||||
handleInputChange = () => {
|
||||
const { onOptionChanged, optionName } = this.props;
|
||||
onOptionChanged({
|
||||
optionName,
|
||||
optionValue: [this.inputMin.value, this.inputMax.value]
|
||||
});
|
||||
const { optionNames } = this.props;
|
||||
const optionValues = [this.inputMin.value, this.inputMax.value];
|
||||
optionNames.forEach((optionName, index) =>
|
||||
this.sendAction(optionName, optionValues[index])
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,63 +1,15 @@
|
||||
/*category: {value: "Vozila", label: "Vozila"}
|
||||
options:
|
||||
cijena: (2) [6500, 70500]
|
||||
godiste: (2) [2004, 2017]
|
||||
gorivo_select_benzin: true
|
||||
gorivo_select_dizel: true
|
||||
kanton: {value: "9", label: "Sarajevo"}
|
||||
kilometrazaMax: {value: 20000, label: "20000"}
|
||||
kilometrazaMin: {value: 5000, label: "5000"}
|
||||
proizvodac: {value: "1900", label: "Audi"}
|
||||
stanje: ""
|
||||
subcategory: {value: "Automobili", label: "Automobili"}
|
||||
const isObject = obj => obj === Object(obj);
|
||||
const mapOptionToLink = (options, option) =>
|
||||
options[option] !== false
|
||||
? `${option}=${
|
||||
isObject(options[option]) ? options[option].value : options[option]
|
||||
}&`
|
||||
: "";
|
||||
|
||||
|
||||
https://www.olx.ba/pretraga?
|
||||
kategorija=18&stanje=&v_b=1900
|
||||
&od=6500&do=70500
|
||||
&kanton=9&
|
||||
godiste_min=2004&godiste_max=2017
|
||||
&kilometra-a_min=5000&kilometra-a_max=50000
|
||||
&gorivo_select_dizel=Dizel&gorivo_select_benzin=Benzin
|
||||
|
||||
{
|
||||
"Automobili": 18,
|
||||
"v_b": "proizvodac",
|
||||
|
||||
}*/
|
||||
function AutomobiliLinkCreator(options) {
|
||||
const [od, do_] = options.cijena;
|
||||
const [godiste_min, godiste_max] = options.godiste;
|
||||
const goriva = [
|
||||
"gorivo_select_benzin",
|
||||
"gorivo_select_dizel",
|
||||
"gorivo_select_plin",
|
||||
"gorivo_select_hibrid",
|
||||
"gorivo_selector_elektro"
|
||||
]
|
||||
.filter(gorivo => options.hasOwnProperty(gorivo))
|
||||
.reduce(
|
||||
(izborGoriva, gorivo) =>
|
||||
izborGoriva +
|
||||
"" +
|
||||
options[gorivo] +
|
||||
"=" +
|
||||
options[gorivo] +
|
||||
"&",
|
||||
""
|
||||
);
|
||||
return `kategorija=18&stanje=${options.stanje}&v_b=${
|
||||
options.proizvodac.value
|
||||
}&od=${od}&do=${do_}&kanton=${
|
||||
options.kanton.value
|
||||
}&godiste_min=${godiste_min}&godiste_max=${godiste_max}&kilometra-a_min=${
|
||||
options.kilometrazaMin.value
|
||||
}&kilometra-a_max=${options.kilometrazaMax.value}&${goriva}`;
|
||||
}
|
||||
const mappingFunctios = {
|
||||
Automobili: AutomobiliLinkCreator
|
||||
};
|
||||
|
||||
export const createOlxLink = (category, subcategory, options) => {
|
||||
return mappingFunctios[subcategory.value](options);
|
||||
};
|
||||
export const createOlxLink = (category, subcategory, options) =>
|
||||
subcategory.value
|
||||
? Object.keys(options).reduce(
|
||||
(link, option) => link + mapOptionToLink(options, option),
|
||||
`kategorija=${subcategory.value}&`
|
||||
)
|
||||
: "";
|
||||
|
||||
11
frontend-react/src/utils/helpers.js
Normal file
11
frontend-react/src/utils/helpers.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export const hoc = (option, componentList) => componentList[option] || null;
|
||||
export const areObjectEqual = function checkEquality(objectA, objectB) {
|
||||
return (
|
||||
Object.keys(objectA).length === Object.keys(objectB).length &&
|
||||
Object.keys(objectA).every(property =>
|
||||
objectA[property] === Object(objectA[property])
|
||||
? checkEquality(objectA[property], objectB[property])
|
||||
: objectA[property] === objectB[property]
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export const hoc = (option, componentList) => componentList[option] || null;
|
||||
Reference in New Issue
Block a user