complete frontend restyling

This commit is contained in:
lion
2019-01-29 20:30:43 +01:00
parent c7aa9ca499
commit 2559afc1e8
34 changed files with 1235 additions and 145 deletions

View File

@@ -1,26 +1,20 @@
import React from "react";
import Select from "react-select";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import { connect } from "react-redux";
import {
CATEGORY_SELECT,
ITEMS_CHANGED,
USER_DATA_CHANGED
} from "constants/actionTypes";
import { hoc, areObjectEqual } from "utils/helpers";
import { ITEMS_CHANGED, USER_DATA_CHANGED } from "constants/actionTypes";
import { areObjectEqual } from "utils/helpers";
import { createOlxLink } from "utils/createOlxLink";
import axios from "axios";
import * as Vozila from "./categories/Vozila";
import * as Nekretnine from "./categories/Nekretnine";
import image from "assets/img/sidebar-1.jpg";
import logo from "assets/img/reactlogo.png";
import DeepCategoryWrapper from "components/widgets/DeepCategoryWrapper";
import Sidebar from "components/Sidebar.js";
import ItemsContainer from "./items/itemscontainer/ItemsContainer";
import NotificationModal from "./NotificationModal";
const options = [
{ value: "Vozila", label: "Vozila" },
{ value: "Nekretnine", label: "Nekretnine" }
];
import dashboardStyle from "assets/dashboardStyle.js";
const mapStateToProps = state => {
return {
@@ -33,7 +27,6 @@ const mapStateToProps = state => {
};
const mapDispatchToProps = dispatch => ({
onCategoryChanged: option => dispatch({ type: CATEGORY_SELECT, option }),
onItemsChanged: items => dispatch({ type: ITEMS_CHANGED, items }),
onUserDataChange: change => dispatch({ type: USER_DATA_CHANGED, ...change })
});
@@ -84,32 +77,29 @@ class App extends React.Component {
lastUpdateTime = Date.now();
}
}
handleChange = selectedOption => {
this.props.onCategoryChanged(selectedOption);
};
render() {
const { category, items } = this.props;
const { items, classes } = this.props;
return (
<div>
<Select
value={category}
onChange={this.handleChange}
options={options}
/>
{hoc(category && category.value, {
Vozila: <DeepCategoryWrapper {...Vozila.properties} />,
Nekretnine: <DeepCategoryWrapper {...Nekretnine.properties} />
})}
<ItemsContainer />
{items.length ? <NotificationModal /> : null}
<div className={classes.wrapper}>
<Sidebar logoText={"Market Alarm"} logo={logo} image={image} />
<div className={classes.mainPanel}>
<ItemsContainer />
{items.length ? <NotificationModal /> : null}
</div>
</div>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default withStyles(dashboardStyle)(
connect(
mapStateToProps,
mapDispatchToProps
)(App)
);

View File

@@ -19,6 +19,22 @@ const modalStyle = function() {
};
};
const buttonStyle = function() {
return {
width: "100px",
margin: "0 auto",
position: "relative",
borderRadius: "8px",
display: "block",
padding: "10px 10px",
textAlign: "center",
marginBottom: "20px",
color: "white",
backgroundColor: "#e91e63",
border: "1px solid #e91e63"
};
};
class NotificationModal extends React.Component {
handleOpen = () => {
this.props.onModalOpen();
@@ -50,7 +66,9 @@ class NotificationModal extends React.Component {
return (
<div>
<button onClick={this.handleOpen}>Open Modal</button>
<button style={buttonStyle()} onClick={this.handleOpen}>
Open Modal
</button>
<div style={modalStyle()} aria-labelledby="modal-label" hidden={!modal}>
<div>

View File

@@ -0,0 +1,135 @@
import React from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Dashboard from "@material-ui/icons/Dashboard";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
import Collapse from "@material-ui/core/Collapse";
import StarBorder from "@material-ui/icons/StarBorder";
import sidebarStyle from "assets/sidebarStyle.js";
import CollapseWrapperStyled from "components/widgets/CollapseWrapperStyled";
import DeepCategoryWrapper from "./widgets/DeepCategoryWrapper";
import { hoc } from "utils/helpers";
import * as Vozila from "./categories/Vozila";
import * as Nekretnine from "./categories/Nekretnine";
import { connect } from "react-redux";
import { CATEGORY_SELECT } from "constants/actionTypes";
const options = [
{ value: "Vozila", label: "Vozila" },
{ value: "Nekretnine", label: "Nekretnine" }
];
const mapStateToProps = state => {
return {
category: state.category
};
};
const mapDispatchToProps = dispatch => ({
onCategoryChanged: option => dispatch({ type: CATEGORY_SELECT, option })
});
class Sidebar extends React.Component {
handleCategoryChange = selectedOption => {
this.props.onCategoryChanged(selectedOption);
};
render() {
const { classes, logo, image, logoText, category } = this.props;
return (
<div className={classes.drawerPaper}>
<div className={classes.logo}>
<a href="https://www.creative-tim.com" className={classes.logoLink}>
<div className={classes.logoImage}>
<img src={logo} alt="logo" className={classes.img} />
</div>
{logoText}
</a>
</div>
<div className={classes.sidebarWrapper}>
<List role="menu">
<ListItem className={classes.whiteText} onClick={this.handleClick}>
<ListItemIcon className={classes.whiteText}>
<Dashboard />
</ListItemIcon>
<ListItemText
className={classes.whiteText}
inset
primary="Kategorija"
disableTypography={true}
/>
{true ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={true} timeout="auto" unmountOnExit>
<List disablePadding>
{options.map(({ label, value }, index) => (
<ListItem
onClick={() => this.handleCategoryChange({ label, value })}
className={
classes.nested +
" " +
classes.collapsedItemStyle +
" " +
(category && category.value === value
? classes.checkedItem
: "")
}
key={index}
>
<ListItemIcon className={classes.whiteText}>
<StarBorder />
</ListItemIcon>
<ListItemText
className={classes.whiteText}
primary={label}
disableTypography={true}
/>
</ListItem>
))}
</List>
</Collapse>
{hoc(category && category.value, {
Vozila: (
<CollapseWrapperStyled componentName="Podkategorija">
<DeepCategoryWrapper {...Vozila.properties} />
</CollapseWrapperStyled>
),
Nekretnine: (
<CollapseWrapperStyled componentName="Podkategorija">
<DeepCategoryWrapper {...Nekretnine.properties} />
</CollapseWrapperStyled>
)
})}
</List>
</div>
{image !== undefined ? (
<div
className={classes.background}
style={{ backgroundImage: "url(" + image + ")" }}
/>
) : null}
</div>
);
}
}
Sidebar.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(sidebarStyle)(
connect(
mapStateToProps,
mapDispatchToProps
)(Sidebar)
);

View File

@@ -1,5 +1,6 @@
import React from "react";
import CheckboxAndRadioWrapper from "components/widgets/CheckboxAndRadioWrapper";
import CollapseWrapperStyled from "components/widgets/CollapseWrapperStyled";
const elements = [
{
@@ -54,13 +55,12 @@ const elements = [
class KuceFilter extends React.Component {
render() {
return (
<div>
<span>Dodatno za kuce</span>
<CollapseWrapperStyled componentName="Dodatno za kuce">
<CheckboxAndRadioWrapper
componentName="dodatno-za-kuce"
elements={elements}
/>
</div>
</CollapseWrapperStyled>
);
}
}

View File

@@ -1,5 +1,6 @@
import React from "react";
import CheckboxAndRadioWrapper from "components/widgets/CheckboxAndRadioWrapper";
import CollapseWrapperStyled from "components/widgets/CollapseWrapperStyled";
const elements = [
{
@@ -61,13 +62,12 @@ const elements = [
class StanoviFilter extends React.Component {
render() {
return (
<div>
<span>Dodatno za stan</span>
<CollapseWrapperStyled componentName="Dodatno za stan">
<CheckboxAndRadioWrapper
componentName="dodatno-za-stan"
elements={elements}
/>
</div>
</CollapseWrapperStyled>
);
}
}

View File

@@ -2,21 +2,49 @@ import React from "react";
import * as Filters from "./AllFiltersDefined";
import CheckboxAndRadioWrapper from "components/widgets/CheckboxAndRadioWrapper";
import SelectDisplayCheckboxWrapper from "components/widgets/SelectDisplayCheckboxWrapper";
import CollapseWrapperStyled from "components/widgets/CollapseWrapperStyled";
import RangeWrapper from "components/widgets/RangeWrapper";
import SelectWrapper from "components/widgets/SelectWrapper";
import DropDownWrapper from "components/widgets/DropDownWrapper";
class NekretnineFilter extends React.Component {
render() {
return (
<div>
<CheckboxAndRadioWrapper
componentName="vrsta"
elements={Filters.vrstaElements}
/>
<SelectWrapper {...Filters.lokacijaOptions} />
<SelectDisplayCheckboxWrapper {...Filters.gradoviOptions} />
<RangeWrapper {...Filters.cijenaRangeOptions} optionName="cijena" />
<RangeWrapper {...Filters.velicinaRangeOptions} optionName="velicina" />
{[
<CheckboxAndRadioWrapper
componentName="Vrsta"
elements={Filters.vrstaElements}
/>,
<DropDownWrapper
componentName="Lokacija"
{...Filters.lokacijaOptions}
/>,
<SelectDisplayCheckboxWrapper
componentName="Grad"
{...Filters.gradoviOptions}
/>,
<RangeWrapper
componentName="Cijena"
{...Filters.cijenaRangeOptions}
optionName="cijena"
/>,
<RangeWrapper
componentName="Velicina"
{...Filters.velicinaRangeOptions}
optionName="velicina"
/>
].map(comp => (
<CollapseWrapperStyled
key={comp.props.componentName}
componentName={comp.props.componentName}
>
{comp}
</CollapseWrapperStyled>
))}
</div>
);
}

View File

@@ -2,26 +2,54 @@ import React from "react";
import * as Filters from "./AllFiltersDefined";
import CheckboxAndRadioWrapper from "components/widgets/CheckboxAndRadioWrapper";
import RangeWrapper from "components/widgets/RangeWrapper";
import SelectWrapper from "components/widgets/SelectWrapper";
import CollapseWrapperStyled from "components/widgets/CollapseWrapperStyled";
import DropDownWrapper from "components/widgets/DropDownWrapper";
class VozilaFilter extends React.Component {
render() {
return (
<div>
<CheckboxAndRadioWrapper
componentName="stanje"
elements={Filters.stanjeElements}
/>
<SelectWrapper {...Filters.proizvodacOptions} />
<RangeWrapper {...Filters.cijenaRangeOptions} />
<SelectWrapper {...Filters.lokacijaOptions} />
<RangeWrapper {...Filters.godisteRangeOptions} />
<SelectWrapper {...Filters.kilometrazaOptions.kilometraMin} />
<SelectWrapper {...Filters.kilometrazaOptions.kilometraMax} />
<CheckboxAndRadioWrapper
componentName="gorivo"
elements={Filters.gorivoElements}
/>
{[
<CheckboxAndRadioWrapper
componentName="Stanje"
elements={Filters.stanjeElements}
/>,
<DropDownWrapper
componentName="Proizvodac"
{...Filters.proizvodacOptions}
/>,
<RangeWrapper
componentName="Cijena"
{...Filters.cijenaRangeOptions}
/>,
<DropDownWrapper
componentName="Lokacija"
{...Filters.lokacijaOptions}
/>,
<RangeWrapper
componentName="Godiste"
{...Filters.godisteRangeOptions}
/>,
<DropDownWrapper
componentName="Min kilometraza"
{...Filters.kilometrazaOptions.kilometraMin}
/>,
<DropDownWrapper
componentName="Max kilometraza"
{...Filters.kilometrazaOptions.kilometraMax}
/>,
<CheckboxAndRadioWrapper
componentName="Gorivo"
elements={Filters.gorivoElements}
/>
].map(comp => (
<CollapseWrapperStyled
key={comp.props.componentName}
componentName={comp.props.componentName}
>
{comp}
</CollapseWrapperStyled>
))}
</div>
);
}

View File

@@ -1,36 +1,40 @@
.item {
display: grid;
border: solid #877ad2 1px;
border-radius: 10px;
max-width: 360px;
min-height: 300px;
border: solid #ffc600 1px;
border-radius: 5px;
max-width: 200px;
min-height: 150px;
grid-template-areas:
"image"
"title";
margin: 3%;
background-color: #dbdbd7;
background-color: #272727;
justify-items: center;
align-items: center;
}
.item > a > img {
width: 70%;
height: 60%;
max-height: 200px;
width: 90%;
height: 90%;
min-height: 100px !important;
min-width: 150px !important;
grid-area: image;
padding: 8% 8% 0 8%;
}
.item > a {
text-decoration: none;
}
.basic-info {
grid-area: title;
width: 85%;
width: 100%;
margin: 0 auto;
text-align: center;
}
.basic-info > h3 {
font-size: 90%;
margin: 5%;
margin-bottom: 0px;
color: #332390;
color: #ffc600;
padding-bottom: 10px;
overflow-wrap: break-word;
}

View File

@@ -3,6 +3,6 @@
margin-top: 8%;
display: grid;
grid-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
grid-template-rows: repeat(2, 320px);
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
justify-items: center;
}

View File

@@ -19,11 +19,7 @@ class ItemContainer extends Component {
}
render() {
return (
<div>
<div className="items-list">{this.renderItems()}</div>
</div>
);
return <div className="items-list">{this.renderItems()}</div>;
}
}
export default connect(mapStateToProps)(ItemContainer);

View File

@@ -2,14 +2,13 @@ import React from "react";
import NekretnineFilter from "components/filters/NekretnineFilter/index";
import KuceFilter from "components/filters/NekretnineFilter/KuceFilter/index";
class Kuce extends React.Component {
render() {
return (
<div>
<span>Kuce</span>
<NekretnineFilter />
<KuceFilter />
</div>
);
}
render() {
return (
<div>
<NekretnineFilter />
<KuceFilter />
</div>
);
}
}
export default Kuce;

View File

@@ -3,14 +3,13 @@ import NekretnineFilter from "components/filters/NekretnineFilter/index";
import StanoviFilter from "components/filters/NekretnineFilter/StanoviFilter/index";
class Stanovi extends React.Component {
render() {
return (
<div>
<span>Stanovi</span>
<NekretnineFilter />
<StanoviFilter />
</div>
);
}
render() {
return (
<div>
<NekretnineFilter />
<StanoviFilter />
</div>
);
}
}
export default Stanovi;

View File

@@ -2,13 +2,12 @@ import React from "react";
import VozilaFilter from "components/filters/VozilaFilter/index";
class Automobili extends React.Component {
render() {
return (
<div>
<span>Auto</span>
<VozilaFilter />
</div>
);
}
render() {
return (
<div>
<VozilaFilter />
</div>
);
}
}
export default Automobili;

View File

@@ -2,13 +2,12 @@ import React from "react";
import VozilaFilter from "components/filters/VozilaFilter/index";
class Motocikli extends React.Component {
render() {
return (
<div>
<span>Motocikli</span>
<VozilaFilter />
</div>
);
}
render() {
return (
<div>
<VozilaFilter />
</div>
);
}
}
export default Motocikli;

View File

@@ -1,5 +1,8 @@
import React from "react";
import { optionchangewrapper } from "utils/optionchangewrapper";
import Checkbox from "@material-ui/core/Checkbox";
import Radio from "@material-ui/core/Radio";
import "assets/checkboxAndRadioStyle.css";
class CheckboxAndRadioWrapper extends React.Component {
optionChange = (option, optionName, type) => {
@@ -22,26 +25,33 @@ class CheckboxAndRadioWrapper extends React.Component {
renderElements = (elements, componentName) => {
return elements.map(({ type, value, name, optionName } = {}) => (
<label key={name + type}>
<input
name={type === "radio" ? `${componentName}-radio` : ""}
type={type}
value={value}
checked={this.isChecked(type, value, optionName)}
onChange={option => this.optionChange(option, optionName, type)}
/>
{name}
{type === "radio" ? (
<Radio
className="radio-style"
name={type === "radio" ? `${componentName}-radio` : ""}
type={type}
value={value}
checked={this.isChecked(type, value, optionName)}
onChange={option => this.optionChange(option, optionName, type)}
/>
) : (
<Checkbox
className="checkbox-style"
type={type}
value={value}
checked={this.isChecked(type, value, optionName)}
onChange={option => this.optionChange(option, optionName, type)}
/>
)}
<span className="label-style">{name}</span>
<br />
</label>
));
};
render() {
const { elements, componentName } = this.props;
return (
<div>
<span>{componentName}</span>
{this.renderElements(elements, componentName)}
</div>
);
return <div>{this.renderElements(elements, componentName)}</div>;
}
}
export default optionchangewrapper(CheckboxAndRadioWrapper);

View File

@@ -0,0 +1,71 @@
import React from "react";
import { connect } from "react-redux";
import { OPTION_EXPAND_CHANGE } from "constants/actionTypes";
import withStyles from "@material-ui/core/styles/withStyles";
import sidebarStyle from "assets/sidebarStyle.js";
import Collapse from "@material-ui/core/Collapse";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
import Dashboard from "@material-ui/icons/Dashboard";
const mapStateToProps = state => {
return {
uiexpand: state.uiexpand
};
};
const mapDispatchToProps = dispatch => ({
onUIExpandChanged: uiExpandOption =>
dispatch({ type: OPTION_EXPAND_CHANGE, ...uiExpandOption })
});
class CollapseWrapperStyled extends React.Component {
handleClick = componentName => {
this.props.onUIExpandChanged({
controlName: componentName,
value: !this.props.uiexpand[componentName]
});
};
isopen = componentName => this.props.uiexpand[componentName];
render() {
let { classes, componentName } = this.props;
return (
<div>
<ListItem
className={classes.whiteText}
onClick={() => this.handleClick(componentName)}
>
<ListItemIcon className={classes.whiteText}>
<Dashboard />
</ListItemIcon>
<ListItemText
className={classes.whiteText}
inset
primary={componentName}
disableTypography={true}
/>
{this.isopen(componentName) ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse
className={classes.subOptionIndent}
in={this.isopen(componentName)}
timeout="auto"
unmountOnExit
>
{this.props.children}
</Collapse>
</div>
);
}
}
export default withStyles(sidebarStyle)(
connect(
mapStateToProps,
mapDispatchToProps
)(CollapseWrapperStyled)
);

View File

@@ -1,8 +1,15 @@
import React from "react";
import Select from "react-select";
import { subcategorywrapper } from "utils/subcategorywrapper";
import { hoc } from "utils/helpers";
import withStyles from "@material-ui/core/styles/withStyles";
import sidebarStyle from "assets/sidebarStyle.js";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import StarBorder from "@material-ui/icons/StarBorder";
class DeepCategoryWrapper extends React.Component {
handleOptionChange = selectedOption => {
const { depth, onSubCategoryChanged } = this.props;
@@ -10,21 +17,45 @@ class DeepCategoryWrapper extends React.Component {
};
render() {
const { options, depth, childrenComponents } = this.props;
const { options, depth, childrenComponents, classes } = this.props;
const {
subcategory: { [depth]: deepSubCategory }
} = this.props;
return (
<div>
<Select
value={deepSubCategory || null}
onChange={this.handleOptionChange}
options={options}
/>
<List disablePadding>
{options.map(({ label, value }, index) => (
<ListItem
onClick={() => this.handleOptionChange({ label, value })}
className={
classes.nested +
" " +
classes.collapsedItemStyle +
" " +
(deepSubCategory && deepSubCategory.value === value
? classes.checkedItem
: "")
}
key={index}
>
<ListItemIcon className={classes.whiteText}>
<StarBorder />
</ListItemIcon>
<ListItemText
className={classes.whiteText}
primary={label}
disableTypography={true}
/>
</ListItem>
))}
</List>
{hoc(deepSubCategory && deepSubCategory.value, childrenComponents)}
</div>
);
}
}
export default subcategorywrapper(DeepCategoryWrapper);
export default withStyles(sidebarStyle)(
subcategorywrapper(DeepCategoryWrapper)
);

View File

@@ -0,0 +1,54 @@
import React from "react";
import { optionchangewrapper } from "utils/optionchangewrapper";
import withStyles from "@material-ui/core/styles/withStyles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import sidebarStyle from "assets/sidebarStyle.js";
import StarBorder from "@material-ui/icons/StarBorder";
class DropdownWrapper extends React.Component {
handleOptionChange = selectedOption => {
const { onOptionChanged, optionName } = this.props;
onOptionChanged({
optionName,
optionValue: selectedOption
});
};
render() {
let { value, options, choices, classes } = this.props;
let optionName = value;
return (
<List disablePadding>
{choices.map(({ label, value }, index) => (
<ListItem
onClick={() => this.handleOptionChange({ label, value })}
className={
classes.nested +
" " +
classes.collapsedItemStyle +
" " +
(options[optionName] && options[optionName].value === value
? classes.checkedItem
: "")
}
key={index}
>
<ListItemIcon className={classes.whiteText}>
<StarBorder />
</ListItemIcon>
<ListItemText
className={classes.whiteText}
primary={label}
disableTypography={true}
/>
</ListItem>
))}
</List>
);
}
}
export default withStyles(sidebarStyle)(optionchangewrapper(DropdownWrapper));

View File

@@ -1,7 +1,9 @@
import React from "react";
import { Range } from "rc-slider";
import { optionchangewrapper } from "utils/optionchangewrapper";
import "assets/rangeStyle.css";
import "rc-slider/assets/index.css";
import Input from "@material-ui/core/Input";
class RangeWrapper extends React.Component {
sendAction = (optionName, optionValue) => {
@@ -39,14 +41,24 @@ class RangeWrapper extends React.Component {
step={step}
onAfterChange={this.handleRangeChange}
/>
<input
ref={node => {
<Input
placeholder="Min"
className="input-style"
inputProps={{
"aria-label": "Min"
}}
inputRef={node => {
this.inputMin = node;
}}
onChange={this.handleInputChange}
/>
<input
ref={node => {
<Input
placeholder="Max"
className="input-style"
inputProps={{
"aria-label": "Max"
}}
inputRef={node => {
this.inputMax = node;
}}
onChange={this.handleInputChange}