Contact form UI

This commit is contained in:
Edin Dazdarevic
2017-04-12 13:08:06 +02:00
parent 8792abcd9f
commit 6a4c02d01a
6 changed files with 257 additions and 31 deletions

69
web/dist/main.css vendored
View File

@@ -757,7 +757,8 @@ html {
padding: 15px 0;
}
.ld-check-availability button {
.ld-check-availability button,
.contact-form button {
font-size: 18px;
padding: 15px 0;
background-color: #51bc6a;
@@ -858,7 +859,7 @@ h5 {
.modal h3 {
color: #575a60;
font-size: 16px;
font-size: 18px;
font-weight: 400;
letter-spacing: .3px;
}
@@ -875,6 +876,66 @@ h5 {
font-size: 1.2em;
}
.close:hover {
/*background: #00d9ff;*/
.contact-form input, textarea {
padding: 10px;
border: 1px solid #e2e2e6;
width: 420px;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
font-size: 14px;
color: #212126;
letter-spacing: .2px;
}
.contact-form-email-phone input {
}
.contact-form-email-phone input:first-child {
margin-right: 5px;
width: 250px;
}
.contact-form-email-phone input:last-child {
margin-left: 5px;
width: 160px;
}
.contact-form-name,
.contact-form-message,
.contact-form-email-phone {
margin-top: 15px;
margin-bottom: 15px;
}
.contact-form-footer {
text-align: center;
padding-top: 20px;
padding-bottom: 15px;
}
.contact-form-alert input {
width: 15px;
}
.contact-form-alert span {
cursor: pointer;
}
.contact-form-alert {
font-size: 0.9em;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
input.validation-failed {
border: 1px solid red;
}

View File

@@ -1,30 +1,124 @@
import React from 'react';
import React from 'react'
export default class ContactModal extends React.Component {
onContactCloseClick () {
}
onContactCloseClick (e) {
e.preventDefault()
this.props.dispatch({
type: 'CLOSE_CONTACT'
});
e.preventDefault();
})
}
onSubmit (e) {
e.preventDefault()
const {name, email} = this.props.contact;
if (!name || !email) {
this.props.dispatch({
type: 'INVALID_CONTACT'
})
} else {
this.props.dispatch({
type: 'SUBMIT_CONTACT'
})
}
}
onFieldChange (field, e) {
this.props.dispatch({
type: 'UPDATE_CONTACT_INFO',
action: {
field,
value: e.target.value
}
})
}
onAlertToggle (e) {
const alert = this.props.contact.alert
this.props.dispatch({
type: 'UPDATE_CONTACT_INFO',
action: {
field: 'alert',
value: !alert
}
})
}
render () {
const {
message,
email,
phone,
name,
alert: doAlert,
nameInvalid,
emailInvalid
} = this.props.contact
const nameValidationClass = nameInvalid ? 'validation-failed' : ''
const emailValidationClass = emailInvalid ? 'validation-failed' : ''
render() {
return (
<div className="modal">
<div className="modal contact-form">
<div>
<a href="#close" title="Zatvori" className="close" onClick={this.onContactCloseClick.bind(this)}>
<i className="fa fa-times" aria-hidden="true"></i>
<a
href="#close"
title="Zatvori"
className="close"
onClick={this.onContactCloseClick.bind(this)}
>
<i className="fa fa-times" aria-hidden="true" />
</a>
<h3>Kontaktirajte prodavca</h3>
<form onSubmit={this.onSubmit.bind(this)}>
<h3>Kontaktirajte prodavca</h3>
<div className="contact-form-name">
<input
value={name}
className={nameValidationClass}
onChange={this.onFieldChange.bind(this, 'name')}
placeholder="Ime i prezime"
type="text"
/>
</div>
<div className="contact-form-email-phone">
<input
value={email}
className={emailValidationClass}
onChange={this.onFieldChange.bind(this, 'email')}
placeholder="Email adresa"
type="text"
/>
<input
value={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')}
value={message}
rows="14"
/>
</div>
<div className="contact-form-alert noselect">
<input
type="checkbox"
onChange={this.onAlertToggle.bind(this)}
checked={doAlert}
/>
<span onClick={this.onAlertToggle.bind(this)}>
Obavjesti me ukoliko se slična nekretnine pojavi
</span>
</div>
<div className="contact-form-footer">
<button>Pošalji poruku</button>
</div>
</form>
</div>
</div>
);
)
}
}

View File

@@ -116,7 +116,9 @@ export default class ListingDetails extends React.Component {
<div className="ld-footer" />
</div>
{contactFormOpen ? <ContactModal dispatch={this.props.dispatch} /> : null}
{contactFormOpen ? <ContactModal
contact={this.props.contact}
dispatch={this.props.dispatch} /> : null}
</div>
)
}

View File

@@ -20,6 +20,13 @@ class Main extends React.Component {
filters: {
rooms: {},
category: {}
},
contact: {
message: '',
name: '',
email: '',
phone: '',
valid: true
}
}
@@ -516,6 +523,7 @@ class Main extends React.Component {
children.push(
<ListingDetails
contactFormOpen={this.state.contactFormOpen}
contact={this.state.contact}
listing={listing}
imageIndex={this.state.imageIndex}
dispatch={this.dispatch.bind(this)}

View File

@@ -1,4 +1,5 @@
import {markSeen} from './api'
import {defaultContactMessage, listingUrl} from './helpers'
const setMaxPrice = ({type, action}, component) => {
const maxPrice = parseFloat(action.maxPrice)
@@ -52,8 +53,6 @@ const viewListingDetails = ({type, action}, component) => {
const scrollElem = document.querySelector('.right-content')
component.savedScrollTop = scrollElem.scrollTop
//component.router.listingId = action.id;
component.setState(
{
listingDetails: true,
@@ -63,7 +62,6 @@ const viewListingDetails = ({type, action}, component) => {
listing: action.listing
},
() => {
//component.router.update();
markSeen(action.id)
const m = component.findMarker(action.id)
if (m) {
@@ -221,8 +219,6 @@ const backToResults = ({type, action}, component) => {
listingDetails: false
},
() => {
//component.router.update();
if (prevSelected) {
prevSelected.marker.setIcon(component.visitedMarkerIcon())
}
@@ -268,7 +264,6 @@ const sortChange = ({type, action}, component) => {
page: 0
},
() => {
//component.router.update();
component.refreshListings()
}
)
@@ -280,14 +275,59 @@ const updateRoute = ({type, action}, component) => {
const openContact = ({type, action}, component) => {
component.setState({
contactFormOpen: true
});
contactFormOpen: true,
contact: {
...component.state.contact,
message: defaultContactMessage(listingUrl(component.state.listingId)),
emailInvalid: false,
nameInvalid: false,
name: '',
email: '',
phone: ''
}
})
}
const closeContact = ({type, action}, component) => {
component.setState({
contactFormOpen: false
});
})
}
const updateContactInfo = ({type, action}, component) => {
let nameInvalid = component.state.contact.nameInvalid
let emailInvalid = component.state.contact.emailInvalid
if (action.field === 'name') {
nameInvalid = !action.value
}
if (action.field === 'email') {
emailInvalid = !action.value
}
component.setState({
contact: {
...component.state.contact,
[action.field]: action.value,
...{nameInvalid, emailInvalid}
}
})
}
const invalidContact = ({type, action}, component) => {
const {name, email} = component.state.contact
component.setState({
contact: {
...component.state.contact,
...{nameInvalid: !name, emailInvalid: !email}
}
})
}
const submitContact = ({type, action}, component) => {
}
const handlers = {
@@ -313,7 +353,10 @@ const handlers = {
SORT_CHANGE: sortChange,
UPDATE_ROUTE: updateRoute,
OPEN_CONTACT: openContact,
CLOSE_CONTACT: closeContact
CLOSE_CONTACT: closeContact,
UPDATE_CONTACT_INFO: updateContactInfo,
SUBMIT_CONTACT: submitContact,
INVALID_CONTACT: invalidContact
}
export const handleMessage = ({type, action}, component) => {
@@ -321,6 +364,6 @@ export const handleMessage = ({type, action}, component) => {
throw new `Unhandled message: ${type}`()
}
console.log(type);
console.log(type, action);
return handlers[type]({type, action}, component)
}

View File

@@ -18,3 +18,21 @@ export const galleryImageUrl = img =>
export const listingImageUrl = img =>
img && img.replace('upload/', 'upload/w_205/')
export const defaultContactMessage = (url) => {
return `Pozdrav,
Našao/Našla sam vaš oglas na portalu Kivi za sljedeću nekretninu:
${url}
Želim da me kontaktirate kako bih dobio/dobila više informacija.
S poštovanjem
`
}
export const listingUrl = (id) => {
// TODO: fix this once removing hardcoded values
return `http://localhost:8080/?listingId=${id}`
}