Welcome page
This commit is contained in:
@@ -1,28 +1,27 @@
|
||||
import React from 'react'
|
||||
import {saveContactRequest} from '../lib/api'
|
||||
import React from "react";
|
||||
import { saveContactRequest } from "../lib/api";
|
||||
|
||||
export default class ContactModal extends React.Component {
|
||||
onContactCloseClick (e) {
|
||||
e.preventDefault()
|
||||
onContactCloseClick(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'CLOSE_CONTACT'
|
||||
})
|
||||
type: "CLOSE_CONTACT"
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit (e) {
|
||||
e.preventDefault()
|
||||
const {name, email, message, phone, alert} = this.props.contact;
|
||||
onSubmit(e) {
|
||||
e.preventDefault();
|
||||
const { name, email, message, phone, alert } = this.props.contact;
|
||||
|
||||
if (!name || !email) {
|
||||
this.props.dispatch({
|
||||
type: 'INVALID_CONTACT'
|
||||
})
|
||||
type: "INVALID_CONTACT"
|
||||
});
|
||||
} else {
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'SUBMIT_CONTACT_START'
|
||||
})
|
||||
type: "SUBMIT_CONTACT_START"
|
||||
});
|
||||
|
||||
saveContactRequest(this.props.listingId, {
|
||||
name,
|
||||
@@ -30,39 +29,42 @@ export default class ContactModal extends React.Component {
|
||||
phone,
|
||||
message,
|
||||
alert
|
||||
}).then(l => l.text()).then(res => {
|
||||
this.props.dispatch({
|
||||
type: 'SUBMIT_CONTACT_END'
|
||||
})
|
||||
.then(l => l.text())
|
||||
.then(res => {
|
||||
this.props.dispatch({
|
||||
type: "SUBMIT_CONTACT_END"
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
// TODO: should we have a global view for rendering errors
|
||||
console.error(e);
|
||||
});
|
||||
}).catch(e => {
|
||||
// TODO: should we have a global view for rendering errors
|
||||
console.error(e)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onFieldChange (field, e) {
|
||||
onFieldChange(field, e) {
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_CONTACT_INFO',
|
||||
type: "UPDATE_CONTACT_INFO",
|
||||
action: {
|
||||
field,
|
||||
value: e.target.value
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
onAlertToggle (e) {
|
||||
const alert = this.props.contact.alert
|
||||
onAlertToggle(e) {
|
||||
const alert = this.props.contact.alert;
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_CONTACT_INFO',
|
||||
type: "UPDATE_CONTACT_INFO",
|
||||
action: {
|
||||
field: 'alert',
|
||||
field: "alert",
|
||||
value: !alert
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const {
|
||||
message,
|
||||
email,
|
||||
@@ -72,10 +74,10 @@ export default class ContactModal extends React.Component {
|
||||
nameInvalid,
|
||||
emailInvalid,
|
||||
sending
|
||||
} = this.props.contact
|
||||
} = this.props.contact;
|
||||
|
||||
const nameValidationClass = nameInvalid ? 'validation-failed' : ''
|
||||
const emailValidationClass = emailInvalid ? 'validation-failed' : ''
|
||||
const nameValidationClass = nameInvalid ? "validation-failed" : "";
|
||||
const emailValidationClass = emailInvalid ? "validation-failed" : "";
|
||||
|
||||
return (
|
||||
<div className="modal contact-form">
|
||||
@@ -94,7 +96,7 @@ export default class ContactModal extends React.Component {
|
||||
<input
|
||||
value={name}
|
||||
className={nameValidationClass}
|
||||
onChange={this.onFieldChange.bind(this, 'name')}
|
||||
onChange={this.onFieldChange.bind(this, "name")}
|
||||
placeholder="Ime i prezime"
|
||||
type="text"
|
||||
/>
|
||||
@@ -103,32 +105,32 @@ export default class ContactModal extends React.Component {
|
||||
<input
|
||||
value={email}
|
||||
className={emailValidationClass}
|
||||
onChange={this.onFieldChange.bind(this, 'email')}
|
||||
onChange={this.onFieldChange.bind(this, "email")}
|
||||
placeholder="Email adresa"
|
||||
type="email"
|
||||
name="email"
|
||||
/>
|
||||
<input
|
||||
value={phone}
|
||||
onChange={this.onFieldChange.bind(this, 'phone')}
|
||||
onChange={this.onFieldChange.bind(this, "phone")}
|
||||
placeholder="Telefon (opcionalno)"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div className="contact-form-message">
|
||||
<textarea
|
||||
onChange={this.onFieldChange.bind(this, 'message')}
|
||||
onChange={this.onFieldChange.bind(this, "message")}
|
||||
value={message}
|
||||
/>
|
||||
</div>
|
||||
<div className="contact-form-alert noselect">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
onChange={this.onAlertToggle.bind(this)}
|
||||
checked={doAlert}
|
||||
/>
|
||||
Javi mi kada se objavi sličan oglas
|
||||
<input
|
||||
type="checkbox"
|
||||
onChange={this.onAlertToggle.bind(this)}
|
||||
checked={doAlert}
|
||||
/>
|
||||
Javi mi kada se objavi sličan oglas
|
||||
</label>
|
||||
</div>
|
||||
<div className="contact-form-footer">
|
||||
@@ -137,6 +139,6 @@ export default class ContactModal extends React.Component {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
import React from 'react'
|
||||
import {formatFilterNumber} from '../lib/helpers'
|
||||
import React from "react";
|
||||
import { formatFilterNumber } from "../lib/helpers";
|
||||
import {
|
||||
CATEGORY_FLAT,
|
||||
CATEGORY_HOUSE,
|
||||
CATEGORY_OFFICE,
|
||||
CATEGORY_LAND
|
||||
} from '../../../crawler/enums'
|
||||
} from "../../../crawler/enums";
|
||||
|
||||
export default class Filters extends React.Component {
|
||||
onCloseClick (e) {
|
||||
onCloseClick(e) {
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose()
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
onMaxPriceChange (e) {
|
||||
const maxPrice = e.target.value
|
||||
onMaxPriceChange(e) {
|
||||
const maxPrice = e.target.value;
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'SET_MAX_PRICE',
|
||||
action: {maxPrice}
|
||||
})
|
||||
type: "SET_MAX_PRICE",
|
||||
action: { maxPrice }
|
||||
});
|
||||
}
|
||||
|
||||
onMinPriceChange (e) {
|
||||
const minPrice = e.target.value
|
||||
onMinPriceChange(e) {
|
||||
const minPrice = e.target.value;
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'SET_MIN_PRICE',
|
||||
action: {minPrice}
|
||||
})
|
||||
type: "SET_MIN_PRICE",
|
||||
action: { minPrice }
|
||||
});
|
||||
}
|
||||
|
||||
onMaxSizeChange (e) {
|
||||
onMaxSizeChange(e) {
|
||||
this.props.dispatch({
|
||||
type: 'SET_MAX_SIZE',
|
||||
action: {maxSize: e.target.value}
|
||||
})
|
||||
type: "SET_MAX_SIZE",
|
||||
action: { maxSize: e.target.value }
|
||||
});
|
||||
}
|
||||
|
||||
onMinSizeChange (e) {
|
||||
onMinSizeChange(e) {
|
||||
this.props.dispatch({
|
||||
type: 'SET_MIN_SIZE',
|
||||
action: {minSize: e.target.value}
|
||||
})
|
||||
type: "SET_MIN_SIZE",
|
||||
action: { minSize: e.target.value }
|
||||
});
|
||||
}
|
||||
|
||||
onRoomsClick (rooms) {
|
||||
onRoomsClick(rooms) {
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_ROUTE',
|
||||
type: "UPDATE_ROUTE",
|
||||
action: {
|
||||
params: {rooms}
|
||||
params: { rooms }
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.props.dispatch({type: 'SET_ROOMS', action: {rooms}})
|
||||
this.props.dispatch({ type: "SET_ROOMS", action: { rooms } });
|
||||
}
|
||||
|
||||
onCategoryClick (category) {
|
||||
onCategoryClick(category) {
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_ROUTE',
|
||||
type: "UPDATE_ROUTE",
|
||||
action: {
|
||||
params: {category}
|
||||
params: { category }
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.props.dispatch({type: 'SET_CATEGORY', action: {category}})
|
||||
this.props.dispatch({ type: "SET_CATEGORY", action: { category } });
|
||||
}
|
||||
|
||||
onRefreshClick (closeFilters) {
|
||||
this.updateSearch()
|
||||
onRefreshClick(closeFilters) {
|
||||
this.updateSearch();
|
||||
|
||||
if (closeFilters) {
|
||||
this.props.dispatch({
|
||||
type: 'CLOSE_FILTERS'
|
||||
})
|
||||
type: "CLOSE_FILTERS"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onKeyPress (e) {
|
||||
if (e.key === 'Enter') {
|
||||
this.updateSearch()
|
||||
onKeyPress(e) {
|
||||
if (e.key === "Enter") {
|
||||
this.updateSearch();
|
||||
}
|
||||
}
|
||||
|
||||
updateSearch () {
|
||||
const {minPrice, maxPrice, minSize, maxSize} = this.props.filters
|
||||
updateSearch() {
|
||||
const { minPrice, maxPrice, minSize, maxSize } = this.props.filters;
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'UPDATE_ROUTE',
|
||||
type: "UPDATE_ROUTE",
|
||||
action: {
|
||||
params: {
|
||||
minPrice,
|
||||
@@ -97,20 +97,20 @@ export default class Filters extends React.Component {
|
||||
maxSize
|
||||
}
|
||||
}
|
||||
})
|
||||
this.props.dispatch({type: 'UPDATE_SEARCH'})
|
||||
});
|
||||
this.props.dispatch({ type: "UPDATE_SEARCH" });
|
||||
}
|
||||
|
||||
onResetSearch (e) {
|
||||
onResetSearch(e) {
|
||||
this.props.dispatch({
|
||||
type: 'RESET_FILTERS'
|
||||
})
|
||||
type: "RESET_FILTERS"
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const {filters} = this.props
|
||||
const selectedRooms = val => filters.rooms[val] ? 'selected' : ''
|
||||
const selectedCategory = val => filters.category[val] ? 'selected' : ''
|
||||
render() {
|
||||
const { filters } = this.props;
|
||||
const selectedRooms = val => filters.rooms[val] ? "selected" : "";
|
||||
const selectedCategory = val => filters.category[val] ? "selected" : "";
|
||||
|
||||
return (
|
||||
<div className="filters">
|
||||
@@ -135,7 +135,7 @@ export default class Filters extends React.Component {
|
||||
onChange={this.onMinPriceChange.bind(this)}
|
||||
value={formatFilterNumber(filters.minPrice)}
|
||||
/>
|
||||
{' '}
|
||||
{" "}
|
||||
DO
|
||||
|
||||
<input
|
||||
@@ -206,7 +206,7 @@ export default class Filters extends React.Component {
|
||||
/>
|
||||
|
||||
DO
|
||||
{' '}
|
||||
{" "}
|
||||
<input
|
||||
onKeyPress={this.onKeyPress.bind(this)}
|
||||
value={formatFilterNumber(filters.maxSize)}
|
||||
@@ -247,8 +247,8 @@ export default class Filters extends React.Component {
|
||||
3
|
||||
</div>
|
||||
<div
|
||||
onClick={this.onRoomsClick.bind(this, '4+')}
|
||||
className={`filter-btn property-rooms-btn ${selectedRooms('4+')}`}
|
||||
onClick={this.onRoomsClick.bind(this, "4+")}
|
||||
className={`filter-btn property-rooms-btn ${selectedRooms("4+")}`}
|
||||
>
|
||||
4+
|
||||
</div>
|
||||
@@ -262,12 +262,18 @@ export default class Filters extends React.Component {
|
||||
</div>
|
||||
<div className="clear-both" />
|
||||
<div className="filter-bottom">
|
||||
<div onClick={this.onResetSearch.bind(this)} className="filter-btn">Poništi</div>
|
||||
<div onClick={this.onRefreshClick.bind(this, true)}
|
||||
className="filter-btn confirm">Potvrdi</div>
|
||||
|
||||
<div onClick={this.onResetSearch.bind(this)} className="filter-btn">
|
||||
Poništi
|
||||
</div>
|
||||
<div
|
||||
onClick={this.onRefreshClick.bind(this, true)}
|
||||
className="filter-btn confirm"
|
||||
>
|
||||
Potvrdi
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +630,7 @@ class Main extends React.Component {
|
||||
</div>
|
||||
|
||||
<div id="left" style={leftStyle} className={leftClass}>
|
||||
{this.state.mobileView === 'LIST' && !this.state.listingDetails &&
|
||||
{this.state.mobileView === 'LIST' && !this.state.listingDetails &&
|
||||
<div className={ this.state.filtersOpen ? "map-list-view hide": "map-list-view" }>
|
||||
<Listings
|
||||
sort={this.state.sort}
|
||||
|
||||
81
web/src/components/Welcome.js
Normal file
81
web/src/components/Welcome.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import React from 'react'
|
||||
import { pacSelectFirst } from '../helpers/googleMaps'
|
||||
|
||||
export default class Welcome extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
type: 'SALE'
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const options = {
|
||||
componentRestrictions: { country: 'BA' },
|
||||
types: ['geocode']
|
||||
}
|
||||
|
||||
const input = document.getElementById('gmaps-places-input-welcome')
|
||||
const searchBox = new google.maps.places.Autocomplete(input, options)
|
||||
|
||||
pacSelectFirst(input)
|
||||
input.addEventListener('focus', e => {
|
||||
e.target.value = ''
|
||||
})
|
||||
searchBox.addListener('place_changed', () => {
|
||||
const place = searchBox.getPlace()
|
||||
if (place.geometry.viewport) {
|
||||
const bounds = place.geometry.viewport.toUrlValue()
|
||||
this.props.onSearch({
|
||||
bounds,
|
||||
type: this.state.type
|
||||
})
|
||||
} else {
|
||||
const location = place.geometry.location
|
||||
this.props.onSearch({
|
||||
location,
|
||||
type: this.state.type
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onSaleClick () {
|
||||
this.setState({
|
||||
type: 'SALE'
|
||||
})
|
||||
}
|
||||
|
||||
onRentClick () {
|
||||
this.setState({
|
||||
type: 'RENT'
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='welcome-container-bg'>
|
||||
</div>
|
||||
<div className='welcome-container'>
|
||||
|
||||
<div className='welcome-content'>
|
||||
<h1>KIVI</h1>
|
||||
<h2>Pronađi svoj novi dom!</h2>
|
||||
<button
|
||||
onClick={this.onSaleClick.bind(this)}>Kupovina</button>
|
||||
<button onClick={this.onRentClick.bind(this)}>Iznajmljivanje</button>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='Unesite adresu, naselje ili grad'
|
||||
className='where-to'
|
||||
id='gmaps-places-input-welcome'
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user