8 Commits

Author SHA1 Message Date
GotPPay
9d9110bf63 new web-app.bundle 2017-11-13 21:13:34 +01:00
GotPPay
50b737d5ac url port removal 2017-11-13 21:06:00 +01:00
GotPPay
02410a8c3c css fix, url fix 2017-11-13 19:46:26 +01:00
GotPPay
1c25fe6502 css fix; url fix 2017-11-13 19:40:30 +01:00
Bilal
7f3f08c65c izmjene na serveru 2017-11-13 18:34:43 +00:00
GotPPay
2974480b83 css fix ; url fix 2017-11-13 19:26:43 +01:00
Bilal
2e2551f3c3 changed localhost to public ip 2017-11-06 19:52:57 +00:00
GotPPay
ec9a29e04d welcome popeup + mobile friendly 2017-11-06 14:38:47 +01:00
21 changed files with 27863 additions and 153 deletions

View File

@@ -752,7 +752,7 @@ router.get('/search/listings', function () {
//Get only ads with location //Get only ads with location
query = Object.assign(query, { query = Object.assign(query, {
has_map: true hasMap: true
}); });
//AND //AND

View File

@@ -97,7 +97,7 @@ router.get ('/search/listings', async (req, res, next) => {
//Get only ads with location //Get only ads with location
query = Object.assign (query, { query = Object.assign (query, {
has_map: true, hasMap: true,
}); });
//AND //AND

1
common/config.js Normal file
View File

@@ -0,0 +1 @@
export const BASE_URL = '138.68.67.31';

View File

@@ -0,0 +1 @@
RENTAL_FROM_PAGE=1 RENTAL_TO_PAGE=45 PROSTOR_FROM_PAGE=1 PROSTOR_TO_PAGE=26 MONGO_URL=mongodb://localhost:27017/kivi node /home/bilal/kivi/crawler/build/crawler.js > /home/bilal/crawler.log

27652
web/dist/app.bundle.js vendored

File diff suppressed because one or more lines are too long

BIN
web/dist/static/images/rent_0.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
web/dist/static/images/rent_0_mobile.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
web/dist/static/images/rent_1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
web/dist/static/images/rent_1_mobile.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
web/dist/static/images/sale_0.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
web/dist/static/images/sale_0_mobile.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
web/dist/static/images/sale_1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
web/dist/static/images/sale_1_mobile.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

170
web/dist/welcome.css vendored
View File

@@ -5,25 +5,25 @@
/*}*/ /*}*/
.welcome-container h1 { .welcome-container h1 {
font-size: 2em; font-size: 1.2em;
text-align: center; text-align: center;
} }
.welcome-container h2 { .welcome-container h2 {
padding-bottom: 25px; /*padding-bottom: 25px;*/
color: #2d3138; color: #2d3138;
font-size: 26px; font-size: 1em;
font-weight: 200; font-weight: 200;
text-align: center; text-align: center;
letter-spacing: .59px; letter-spacing: .59px;
} }
.welcome-container-bg { .welcome-container-bg {
/*background-color: rgb(92, 192, 99);*/ /*background-color: rgb(92, 192, 99);*/
background-image: url('static/map.jpg'); /*background-image: url('static/map.jpg');*/
/*background-image: url('static/images/sa-bg.jpg');*/ background-image: url('static/images/sa-bg.jpg');
background-size: auto 100%;
/*background-position: center;*/ /*background-position: center;*/
-moz-filter: blur(5px); -moz-filter: blur(5px);
-o-filter: blur(5px); -o-filter: blur(5px);
@@ -40,26 +40,154 @@
} }
.welcome-container { .welcome-container {
position: fixed; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
top:0;
bottom:0;
z-index: 0; z-index: 0;
margin-left: 20px; /*margin-left: 20px;
margin-right: 20px; margin-right: 20px;*/
height: 100%; height: 100%;
padding: 100px; /*padding: 100px;*/
} }
.welcome-content { .welcome-content {
/*height: 100%;*/ /*height: 100%;*/
margin: 0 auto; /*margin: 0 auto;*/
width: 600px; width: 240px;
background-color: hsla(0,0%,100%,.95); background-color: hsla(0,0%,100%,.95);
box-shadow: 0 2px 4px 0 rgba(73,73,73,.1); box-shadow: 0 2px 4px 0 rgba(73,73,73,.1);
padding: 50px; margin-left: auto;
margin-right: auto;
margin-top:40%;
/*padding: 50px;*/
} }
.welcome-content .gmaps-places-input-welcome { .buy-button-active {
height: 80px;
width: 100%; width: 80px;
background:url('static/images/sale_1_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 10%;
margin-right: 5%;
}
.buy-button-inactive {
height: 80px;
width: 80px;
background:url('static/images/sale_0_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 10%;
margin-right: 5%;
}
.rent-button-active{
height: 80px;
width: 80px;
background:url('static/images/rent_1_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 5%;
}
.rent-button-inactive{
height: 80px;
width: 80px;
background:url('static/images/rent_0_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 5%;
}
.search-button{
background-color: #b6d53b;
margin: 10px;
border: none;
text-align: center;
font-size: 1em;
width: 90%;
margin-left:5%;
margin-right: 5%;
}
@media (min-width: 550px) {
.welcome-container h1 {
font-size: 2em;
text-align: center;
}
.welcome-container h2 {
/*padding-bottom: 25px;*/
color: #2d3138;
font-size: 1.4em;
font-weight: 200;
text-align: center;
letter-spacing: .59px;
}
.welcome-content {
/*height: 100%;*/
/*margin: 0 auto;*/
width: 500px;
background-color: hsla(0,0%,100%,.95);
box-shadow: 0 2px 4px 0 rgba(73,73,73,.1);
margin-left: auto;
margin-right: auto;
margin-top:10%;
/*padding: 50px;*/
}
.buy-button-active {
height: 150px;
width: 150px;
background:url('static/images/sale_1_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 15%;
margin-right: 5%;
}
.buy-button-inactive {
height: 150px;
width: 150px;
background:url('static/images/sale_0_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 15%;
margin-right: 5%;
}
.rent-button-active{
height: 150px;
width: 150px;
background:url('static/images/rent_1_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 5%;
}
.rent-button-inactive{
height: 150px;
width: 150px;
background:url('static/images/rent_0_mobile.png') no-repeat;
background-size: contain;
border: none;
margin-left: 5%;
}
.search-button{
background-color: #b6d53b;
margin: 10px;
border: none;
text-align: center;
font-size: 1.4em;
width: 90%;
margin-left:5%;
margin-right: 5%;
}
} }

View File

@@ -22,7 +22,8 @@ class Main extends React.Component {
filters: { filters: {
rooms: {}, rooms: {},
category: {}, category: {},
status : {} status : {},
adType: 0
}, },
mobileView: 'MAP', mobileView: 'MAP',
contact: { contact: {
@@ -34,6 +35,8 @@ class Main extends React.Component {
} }
} }
console.log("Props : ");
console.log(props.initialState);
if (props.initialState) { if (props.initialState) {
props.initialState.sort = props.initialState.sort || state.sort props.initialState.sort = props.initialState.sort || state.sort
state.filters.rooms = props.initialState.rooms state.filters.rooms = props.initialState.rooms
@@ -49,6 +52,7 @@ class Main extends React.Component {
state.filters.maxSize = props.initialState.maxSize state.filters.maxSize = props.initialState.maxSize
state.filters.minPrice = props.initialState.minPrice state.filters.minPrice = props.initialState.minPrice
state.filters.maxPrice = props.initialState.maxPrice state.filters.maxPrice = props.initialState.maxPrice
state.filters.adType = props.initialState.adType
} }
this.state = state this.state = state
@@ -234,7 +238,8 @@ class Main extends React.Component {
maxSize, maxSize,
minPrice, minPrice,
maxPrice, maxPrice,
category category,
adType
} = this.state.filters } = this.state.filters
const bounds = map.getBounds() const bounds = map.getBounds()
@@ -246,6 +251,7 @@ class Main extends React.Component {
minPrice, minPrice,
maxPrice, maxPrice,
category, category,
adType,
page: this.state.page, page: this.state.page,
pins: true pins: true
}) })
@@ -404,7 +410,8 @@ class Main extends React.Component {
maxSize, maxSize,
minPrice, minPrice,
maxPrice, maxPrice,
category category,
adType
} = this.state.filters } = this.state.filters
const bounds = map.getBounds() const bounds = map.getBounds()
@@ -416,6 +423,7 @@ class Main extends React.Component {
minPrice, minPrice,
maxPrice, maxPrice,
category, category,
adType,
page: this.state.page, page: this.state.page,
sort: this.state.sort sort: this.state.sort
}) })

View File

@@ -1,55 +1,25 @@
import React from 'react' import React from 'react'
import { pacSelectFirst } from '../helpers/googleMaps' import {AD_TYPE_SALE, AD_TYPE_RENT} from '../../../common/enums';
export default class Welcome extends React.Component { export default class Welcome extends React.Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.state = { this.state = {
type: 'SALE' type: AD_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 () { onSaleClick () {
this.setState({ this.setState({
type: 'SALE' type: AD_TYPE_SALE,
}) })
} }
onRentClick () { onRentClick () {
this.setState({ this.setState({
type: 'RENT' type: AD_TYPE_RENT,
}) })
} }
@@ -63,15 +33,11 @@ export default class Welcome extends React.Component {
<div className='welcome-content'> <div className='welcome-content'>
<h1>KIVI</h1> <h1>KIVI</h1>
<h2>Pronađi svoj novi dom!</h2> <h2>Pronađi svoj novi dom!</h2>
<button <div>
onClick={this.onSaleClick.bind(this)}>Kupovina</button> <button className={this.state.type===AD_TYPE_SALE?'buy-button-active':'buy-button-inactive'} onClick={this.onSaleClick.bind(this)}></button>
<button onClick={this.onRentClick.bind(this)}>Iznajmljivanje</button> <button className={this.state.type===AD_TYPE_RENT?'rent-button-active':'rent-button-inactive'} onClick={this.onRentClick.bind(this)}></button>
<input </div>
type='text' <button className='search-button' onClick={()=>this.props.onSearch({adType: this.state.type})} >TRAŽI</button>
placeholder='Unesite adresu, naselje ili grad'
className='where-to'
id='gmaps-places-input-welcome'
/>
</div> </div>
</div> </div>

View File

@@ -1,84 +1,84 @@
import React from 'react' import React from 'react';
import {render} from 'react-dom' import {render} from 'react-dom';
import Main from './components/Main' import Main from './components/Main';
import Welcome from './components/Welcome' import Welcome from './components/Welcome';
const getInitialState = url => { const getInitialState = url => {
const params = window.location.search.substr(1).split('&') const params = window.location.search.substr (1).split ('&');
const initialState = { const initialState = {
rooms: {}, rooms: {},
category: {} category: {},
} };
for (const param of params) { for (const param of params) {
const [key, value] = param.split('=') const [key, value] = param.split ('=');
if (key === 'rooms' && value !== '') { if (key === 'rooms' && value !== '') {
initialState.rooms = {} initialState.rooms = {};
value.split(',').forEach(k => { value.split (',').forEach (k => {
initialState.rooms[parseInt(k)] = true initialState.rooms[parseInt (k)] = true;
}) });
} }
if (key === 'category' && value !== '') { if (key === 'category' && value !== '') {
initialState.category = {} initialState.category = {};
value.split(',').forEach(k => { value.split (',').forEach (k => {
initialState.category[parseInt(k)] = true initialState.category[parseInt (k)] = true;
}) });
} }
if (key === 'sort') { if (key === 'sort') {
initialState.sort = value initialState.sort = value;
} }
if (key === 'bounds') { if (key === 'bounds') {
initialState.bounds = value initialState.bounds = value;
} }
if (key === 'listingId') { if (key === 'listingId') {
initialState.listingId = value initialState.listingId = value;
} }
if (key === 'type') { if (key === 'adType') {
initialState.type = value initialState.adType = value;
} }
if (key === 'zoom') { if (key === 'zoom') {
initialState.zoom = parseInt(value) initialState.zoom = parseInt (value);
} }
if (['minSize', 'maxSize', 'minPrice', 'maxPrice'].includes(key)) { if (['minSize', 'maxSize', 'minPrice', 'maxPrice'].includes (key)) {
initialState[key] = parseFloat(value) initialState[key] = parseFloat (value);
} }
} }
return initialState return initialState;
} };
const root = document.getElementById('root') const root = document.getElementById ('root');
const initialState = getInitialState(window.location) const initialState = getInitialState (window.location);
const renderMain = (additionalState = {}) => { const renderMain = (additionalState = {}) => {
const main = <Main initialState={{...initialState, ...additionalState}} /> const main = <Main initialState={{...initialState, ...additionalState}} />;
render(main, root) render (main, root);
} };
renderMain() //renderMain ();
// disable temp // disable temp
/*
if (Object.keys(initialState).length === 2 && if (
window.localStorage.getItem('lastLoad') == null) { Object.keys (initialState).length === 2 &&
const onSearch = ({bounds, type, location}) => { window.localStorage.getItem ('lastLoad') == null
window.location = `/?bounds=${bounds}&type=${type}` ) {
//renderMain({ const onSearch = ({adType}) => {
//bounds,
//type console.log("onSearch()");
//}) //window.location = `/?adType=${adType}`;
} renderMain({adType})
const welcome = <Welcome onSearch={onSearch} /> };
render(welcome, root) const welcome = <Welcome onSearch={onSearch} />;
render (welcome, root);
} else { } else {
renderMain() renderMain ();
} }
*/

View File

@@ -1,11 +1,9 @@
import fetch from 'isomorphic-fetch' import fetch from 'isomorphic-fetch'
import {BASE_URL} from '../../../common/config'
const BASE_URL = 'localhost';
//const BASE_URL = '192.168.0.13';
export const saveContactRequest = (listingId, params) => { export const saveContactRequest = (listingId, params) => {
let url = `http://${BASE_URL}:3001/api/contact/${listingId}` let url = `http://${BASE_URL}/api/contact/${listingId}`
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',
@@ -18,7 +16,7 @@ export const saveContactRequest = (listingId, params) => {
} }
export const loadListing = id => { export const loadListing = id => {
let url = `http://${BASE_URL}:3001/api/search/listings/${id}` let url = `http://${BASE_URL}/api/search/listings/${id}`
return fetch( return fetch(
url, url,
@@ -37,6 +35,7 @@ export const loadProperties = (
maxSize = '', maxSize = '',
rooms = {}, rooms = {},
category = {}, category = {},
adType=1,
page = 1, page = 1,
pins = false, pins = false,
sort = '' sort = ''
@@ -50,7 +49,7 @@ export const loadProperties = (
// TODO: handle errors // TODO: handle errors
//return fetch(process.env.API_URL + '/api/search', { //return fetch(process.env.API_URL + '/api/search', {
let url = `http://${BASE_URL}:3001/api/search/listings?bounds=${bounds}&minPrice=${minPrice}&maxPrice=${maxPrice}&rooms=${allRooms}&minSize=${minSize}&maxSize=${maxSize}&category=${allCategories}&page=${page}&pins=${pins}&sort=${sort}` let url = `http://${BASE_URL}/api/search/listings?bounds=${bounds}&minPrice=${minPrice}&maxPrice=${maxPrice}&rooms=${allRooms}&minSize=${minSize}&maxSize=${maxSize}&adType=${adType}&category=${allCategories}&page=${page}&pins=${pins}&sort=${sort}`
return fetch( return fetch(
url, url,

View File

@@ -1,3 +1,5 @@
import {BASE_URL} from '../../../common/config'
export const formatPrice = p => { export const formatPrice = p => {
if (isNaN(p)) { if (isNaN(p)) {
return 'Po dogovoru' return 'Po dogovoru'
@@ -33,8 +35,7 @@ S poštovanjem
} }
export const listingUrl = (id) => { export const listingUrl = (id) => {
// TODO: fix this once removing hardcoded values return `http://${BASE_URL}/?listingId=${id}`
return `http://localhost:8080/?listingId=${id}`
} }
export const isMobile = () => window.matchMedia("(max-width: 768px)").matches export const isMobile = () => window.matchMedia("(max-width: 768px)").matches

View File

@@ -59,13 +59,15 @@ export default class Router {
sort, sort,
rooms = {}, rooms = {},
category = {}, category = {},
zoom zoom,
adType
} = this.state } = this.state
if (listingId) { if (listingId) {
params.push(`listingId=${listingId}`) params.push(`listingId=${listingId}`)
} }
params.push(`adType=${adType}`);
params.push(`sort=${sort}`) params.push(`sort=${sort}`)
params.push(`bounds=${bounds}`) params.push(`bounds=${bounds}`)
params.push(`zoom=${zoom}`) params.push(`zoom=${zoom}`)

View File

@@ -3,7 +3,13 @@ module.exports = {
output: { output: {
path: __dirname + "/dist", path: __dirname + "/dist",
filename: "app.bundle.js", filename: "app.bundle.js",
publicPath: "http://0.0.0.0:8080/" publicPath: "http://138.68.67.31:8080/"
},
devServer: {
// .. rest of devserver options
host: '0.0.0.0',
disableHostCheck: true
}, },
module: { module: {
loaders: [ loaders: [