Added the contact form page and updated the form.

This commit is contained in:
Batuhan Berk Başoğlu 2021-04-03 21:01:15 -04:00
parent 836fb80bbd
commit c8cd281c5e
13 changed files with 366 additions and 176 deletions

View file

@ -3,7 +3,7 @@
The Real Estate Website using React made by Ruchira Perrera, Sam Oyediran, Batuhan Basoglu, and Kene Ojukwu. The Real Estate Website using React made by Ruchira Perrera, Sam Oyediran, Batuhan Basoglu, and Kene Ojukwu.
After npm start execute the command "node src/homePage/index.js" in order to set up the backend of the contact form. After npm start execute the command "node src/shared-components/contact-us/index.js" in order to set up the backend of the contact form.
# Authors # Authors

32
package-lock.json generated
View file

@ -4466,6 +4466,15 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"cosmiconfig": { "cosmiconfig": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz",
@ -9830,6 +9839,11 @@
} }
} }
}, },
"joi-browser": {
"version": "13.4.0",
"resolved": "https://registry.npmjs.org/joi-browser/-/joi-browser-13.4.0.tgz",
"integrity": "sha512-TfzJd2JaJ/lg/gU+q5j9rLAjnfUNF9DUmXTP9w+GfmG79LjFOXFeM7hIFuXCBcZCivUDFwd9l1btTV9rhHumtQ=="
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -10807,6 +10821,11 @@
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz",
"integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg=="
}, },
"nodemailer": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.5.0.tgz",
"integrity": "sha512-Tm4RPrrIZbnqDKAvX+/4M+zovEReiKlEXWDzG4iwtpL9X34MJY+D5LnQPH/+eghe8DLlAVshHAJZAZWBGhkguw=="
},
"normalize-package-data": { "normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@ -12927,6 +12946,19 @@
"prop-types": "^15.5.8" "prop-types": "^15.5.8"
} }
}, },
"react-form-with-constraints": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/react-form-with-constraints/-/react-form-with-constraints-0.16.0.tgz",
"integrity": "sha512-xt6geBao/+i3pcfNKDHYIJCHI+Ii6mhNN5rhL5l7BnKnQ04KPIRkWmrBNrhfptwV2LIc21hLH6lYZMN5BaKkvQ==",
"requires": {
"prop-types": "^15.7.2"
}
},
"react-form-with-constraints-bootstrap4": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/react-form-with-constraints-bootstrap4/-/react-form-with-constraints-bootstrap4-0.16.0.tgz",
"integrity": "sha512-eauxGmqAdLEFCAwZxWWO6QtAP10rS/mvZRWC9qHqGeRj1qIlVZ73egi5pJSgnpDUDeqScmwADQvmn8GsUyNdXg=="
},
"react-google-maps": { "react-google-maps": {
"version": "9.4.5", "version": "9.4.5",
"resolved": "https://registry.npmjs.org/react-google-maps/-/react-google-maps-9.4.5.tgz", "resolved": "https://registry.npmjs.org/react-google-maps/-/react-google-maps-9.4.5.tgz",

View file

@ -8,11 +8,17 @@
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1", "axios": "^0.21.1",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"joi-browser": "^13.4.0",
"nodemailer": "^6.5.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-bootstrap": "^1.5.2", "react-bootstrap": "^1.5.2",
"react-bootstrap-carousel": "^4.1.1", "react-bootstrap-carousel": "^4.1.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-fa": "^5.0.0", "react-fa": "^5.0.0",
"react-form-with-constraints": "^0.16.0",
"react-form-with-constraints-bootstrap4": "^0.16.0",
"react-google-maps": "^9.4.5", "react-google-maps": "^9.4.5",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
@ -45,5 +51,18 @@
}, },
"devDependencies": { "devDependencies": {
"create-react-component-folder": "^0.3.7" "create-react-component-folder": "^0.3.7"
} },
"description": "The Real Estate Website using React made by Ruchira Perrera, Sam Oyediran, Batuhan Basoglu, and Kene Ojukwu.",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/ArcticHawk1/SEG3125-LAB8.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/ArcticHawk1/SEG3125-LAB8/issues"
},
"homepage": ""
} }

View file

@ -5,7 +5,7 @@ import { BrowserRouter, Route } from "react-router-dom";
import HomePage from "./homePage/Homepage"; import HomePage from "./homePage/Homepage";
import ListingsPage from "./listings-page/ListingsPage"; import ListingsPage from "./listings-page/ListingsPage";
import AgentPage from "./agent-page/AgentPage"; import AgentPage from "./agent-page/AgentPage";
// import ContactUs from "./shared-components/Contact-us/Contact-us"; import ContactUs from "./shared-components/contact-us/contact-us";
class App extends Component { class App extends Component {
render() { render() {
@ -16,6 +16,7 @@ class App extends Component {
<Route exact path="/" component={HomePage}></Route> <Route exact path="/" component={HomePage}></Route>
<Route path="/listings" component={ListingsPage}></Route> <Route path="/listings" component={ListingsPage}></Route>
<Route path="/agents" component={AgentPage}></Route> <Route path="/agents" component={AgentPage}></Route>
<Route path="/contact-us" component={ContactUs}></Route>
</BrowserRouter> </BrowserRouter>
<Footer></Footer> <Footer></Footer>
</div> </div>

View file

@ -41,16 +41,6 @@
height: 800px; height: 800px;
} }
.heading-1{
padding-left: 40%;
padding-top: 10%;
padding-bottom: 20px;
}
.formhelper{
padding-bottom: 15px;
}
.button-1 { .button-1 {
background-color: rgb(0, 0, 0); background-color: rgb(0, 0, 0);
border-color: transparent; border-color: transparent;

View file

@ -3,9 +3,9 @@ import "./Homepage.css";
import house1 from "../images/house1.jpg"; import house1 from "../images/house1.jpg";
import house2 from "../images/house2.jpg"; import house2 from "../images/house2.jpg";
import house3 from "../images/house3.jpg"; import house3 from "../images/house3.jpg";
import house4 from "../images/house4.jpg";
import Carousel from "react-bootstrap/Carousel"; import Carousel from "react-bootstrap/Carousel";
import Button from "react-bootstrap/Button"; import Button from "react-bootstrap/Button";
import axios from 'axios'
@ -13,10 +13,6 @@ class Homepage extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
firstname: "",
lastname: "",
email: "",
message: "",
nextIcon: <span className="next-icon"></span>, nextIcon: <span className="next-icon"></span>,
prevIcon: <span className="prev-icon"></span>, prevIcon: <span className="prev-icon"></span>,
}; };
@ -42,7 +38,8 @@ class Homepage extends Component {
<Carousel.Item> <Carousel.Item>
<img className="headerImg" src={house1} alt="First House" /> <img className="headerImg" src={house1} alt="First House" />
<Carousel.Caption> <Carousel.Caption>
<h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>360 Michawashkode St, Ottawa, Ontario K4A 3N6</h3> <h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>100 Charlie Rogers, Kanata, ON K2V 1A7</h3>
<h2 style={{ fontSize: "150%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>1 Bedroom, 2 Bathroom</h2>
<div className="buttonka"> <div className="buttonka">
<Button href="/listings" variant="info">Click for Details</Button> <Button href="/listings" variant="info">Click for Details</Button>
</div> </div>
@ -51,7 +48,8 @@ class Homepage extends Component {
<Carousel.Item> <Carousel.Item>
<img className="headerImg" src={house2} alt="Second House" /> <img className="headerImg" src={house2} alt="Second House" />
<Carousel.Caption> <Carousel.Caption>
<h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>6865 PEBBLE TRAIL WAY, Ottawa, Ontario K4P 0B7</h3> <h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>1490 Youville Drive, Orléans, ON K1C 2X8</h3>
<h2 style={{ fontSize: "150%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>1 Bedroom, 2 Bathroom</h2>
<div className="buttonka"> <div className="buttonka">
<Button href="/listings" variant="info">Click for Details</Button> <Button href="/listings" variant="info">Click for Details</Button>
</div> </div>
@ -60,7 +58,18 @@ class Homepage extends Component {
<Carousel.Item> <Carousel.Item>
<img className="headerImg" src={house3} alt="Third House" /> <img className="headerImg" src={house3} alt="Third House" />
<Carousel.Caption> <Carousel.Caption>
<h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>5284 Knott Crescent, Ottawa, Ontario K4M 0A2</h3> <h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>8720 Russell Road, Navan, ON K4B 1J1</h3>
<h2 style={{ fontSize: "150%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>1 Bedroom, 2 Bathroom</h2>
<div className="buttonka">
<Button href="/listings" variant="info">Click for Details</Button>
</div>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item>
<img className="headerImg" src={house4} alt="Fourth House" />
<Carousel.Caption>
<h3 style={{ fontSize: "250%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>2785 8th Line Road, Metcalfe, ON K0A 2P0</h3>
<h2 style={{ fontSize: "150%", textShadow: "-2px 0 black, 0 2px black, 2px 0 black, 0 -2px black" }}>1 Bedroom, 2 Bathroom</h2>
<div className="buttonka"> <div className="buttonka">
<Button href="/listings" variant="info">Click for Details</Button> <Button href="/listings" variant="info">Click for Details</Button>
</div> </div>
@ -69,102 +78,9 @@ class Homepage extends Component {
</Carousel> </Carousel>
</div> </div>
</section> </section>
<section class="colored-section" id="contact">
<div className="container-fluid">
<div className="contactIntro">
<h2 className="heading-1">Contact Us</h2>
<form
id="contact-form"
onSubmit={this.handleSubmit.bind(this)}
method="POST"
>
<div className="formhelper row">
<div className="col-6">
<input
type="text"
className="form-control"
placeholder="First Name"
value={this.state.firstname}
onChange={this.onFirstNameChange.bind(this)}
/>
</div>
<div className="col-6">
<input
type="text"
className="form-control"
placeholder="Last Name"
value={this.state.lastname}
onChange={this.onLastNameChange.bind(this)}
/>
</div>
</div>
<div className="form-group">
<input
type="email"
className="form-control"
placeholder="Email Address"
aria-describedby="emailHelp"
value={this.state.email}
onChange={this.onEmailChange.bind(this)}
/>
</div>
<div className="form-group">
<textarea
className="form-control"
placeholder="Message"
rows="5"
value={this.state.message}
onChange={this.onMessageChange.bind(this)}
/>
</div>
<div className="buttonhelper">
<button type="submit" className="button-1">
Submit
</button>
</div>
</form>
</div>
</div>
</section>
</div> </div>
); );
} }
onFirstNameChange(event) {
this.setState({ firstname: event.target.value });
}
onLastNameChange(event) {
this.setState({ lastname: event.target.value });
}
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onMessageChange(event) {
this.setState({ message: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
axios({
method: "POST",
url: "http://localhost:3002/send",
data: this.state
}).then((response) => {
if (response.data.status === 'success') {
alert("Message Sent.");
this.resetForm()
} else if (response.data.status === 'fail') {
alert("Message failed to send.")
}
})
}
resetForm() {
this.setState({ firstname: '', lastname: '', email: '', message: '' })
}
} }
export default Homepage; export default Homepage;

View file

@ -1,59 +1 @@
var express = require('express');
var router = express.Router();
var nodemailer = require('nodemailer');
var cors = require('cors');
const creds = require('./config');
var transport = {
host: 'smtp.mailtrap.io', // Dont forget to replace with the SMTP host of your provider
port: 2525,
auth: {
user: creds.USER,
pass: creds.PASS
}
}
var transporter = nodemailer.createTransport(transport)
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log('Server is ready to take messages');
}
});
router.post('/send', (req, res, next) => {
var firstname = req.body.firstname
var lastname = req.body.lastname
var email = req.body.email
var message = req.body.message
var content = `firstname: ${firstname} \n lastname: ${lastname} \n email: ${email} \n message: ${message} `
var mail = {
from: email,
to: 'kenes@cribs.com', // Change to email address that you want to receive messages on
subject: 'New Message from Contact Form',
text: content
}
transporter.sendMail(mail, (err, data) => {
if (err) {
res.json({
status: 'fail'
})
} else {
res.json({
status: 'success'
})
}
})
})
const app = express()
app.use(cors())
app.use(express.json())
app.use('/', router)
app.listen(3002)
export { default } from "./homePage"; export { default } from "./homePage";

BIN
src/images/house4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View file

@ -0,0 +1,48 @@
.colored-section {
background-color: #e7dec8;
color: #000000;
white-space: nowrap;
}
#contact .container-fluid {
padding-top: 50px;
text-align: left;
font-family: "Georgia";
}
.contactIntro{
padding-left: 10%;
padding-right: 10%;
padding-bottom: 80px;
}
.heading-1{
padding-left: 38%;
padding-top: 10px;
padding-bottom: 20%;
font-size: 40px;
}
.formhelper{
padding-bottom: 15px;
}
#dropdown{
width: 100%;
height: 40px;
}
input, textarea {
margin-bottom: 10px;
}
[data-feedback] {
margin-bottom: 10px;
}
[data-feedback].error {
color: red;
}

View file

@ -0,0 +1,4 @@
module.exports = {
USER: 'a0f17fd29c4ac0',
PASS: '671f8cbe2bd5e2'
}

View file

@ -1,9 +1,188 @@
import React, { Component } from "react"; import React, { Component } from "react";
import "./Contact-us.css";
import axios from 'axios'
import {
FieldFeedback,
FieldFeedbacks,
FormWithConstraints
} from 'react-form-with-constraints';
class ContactUs extends Component { class ContactUs extends Component {
constructor(props) {
super(props);
this.state = {
firstname: "",
lastname: "",
email: "",
message: "",
agent: "",
house: ""
};
}
render() { render() {
return <div>ContactUs</div>; return (
<div className="contact-us">
<div className="container">
<section class="colored-section" id="contact">
<div className="container-fluid">
<div className="contactIntro">
<h2 className="heading-1">Contact Us</h2>
<FormWithConstraints ref={form => this.form = form}
id="contact-form"
onSubmit={this.handleSubmit.bind(this)}
method="POST"
noValidate
>
<div className="row">
<div className="col-6">
<select className="form-group" name="agent" id="dropdown" onChange={this.onAgentChange.bind(this)} value={this.state.agent}>
<option selected>Select an Agent</option>
<option value="Michael">Michael</option>
<option value="Jin">Jin </option>
<option value="Anita">Anita</option>
<option value="Alex">Alex</option>
<option value="Xuan">Xuan</option>
<option value="Walter">Walter</option>
<option value="No preference">No preference</option>
</select>
</div>
<div className="col-6">
<select className="form-group" name="house" id="dropdown" onChange={this.onHouseChange.bind(this)} value={this.state.house}>
<option selected>Select a House</option>
<option value="100 Charlie Rogers">100 Charlie Rogers</option>
<option value="1490 Youville Drive">1490 Youville Drive </option>
<option value="8720 Russell Road">8720 Russell Road</option>
<option value="2785 8th Line Road">2785 8th Line Road</option>
<option value="No preference">No preference</option>
</select>
</div>
</div>
<div className="formhelper row">
<div className="col-6">
<input name="firstname"
type="text"
className="form-control"
placeholder="First Name"
value={this.state.firstname}
required onChange={this.onFirstNameChange.bind(this)}
/>
<FieldFeedbacks for="firstname">
<FieldFeedback when="*" />
</FieldFeedbacks>
</div>
<div className="col-6">
<input name="lastname"
type="text"
className="form-control"
placeholder="Last Name"
value={this.state.lastname}
required onChange={this.onLastNameChange.bind(this)}
/>
<FieldFeedbacks for="lastname">
<FieldFeedback when="*" />
</FieldFeedbacks>
</div>
</div>
<div className="form-group">
<input name="email"
type="email"
className="form-control"
placeholder="Email Address"
aria-describedby="emailHelp"
value={this.state.email}
required onChange={this.onEmailChange.bind(this)}
/>
<FieldFeedbacks for="email">
<FieldFeedback when="*" />
</FieldFeedbacks>
</div>
<div className="form-group">
<textarea name="message"
className="form-control"
placeholder="Message"
rows="5"
value={this.state.message}
required onChange={this.onMessageChange.bind(this)}
/>
<FieldFeedbacks for="message">
<FieldFeedback when="*" />
</FieldFeedbacks>
</div>
<div className="buttonhelper">
<button type="submit" className="button-1">
Submit
</button>
</div>
</FormWithConstraints>
</div>
</div>
</section>
</div>
</div>
)
}
onAgentChange(event) {
this.setState({ agent: event.target.value });
}
onHouseChange(event) {
this.setState({ house: event.target.value });
}
onFirstNameChange(event) {
this.setState({ firstname: event.target.value });
}
onLastNameChange(event) {
this.setState({ lastname: event.target.value });
}
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onMessageChange(event) {
this.setState({ message: event.target.value });
}
handleChange = e => {
this.form.validateFields(e.target);
}
handleSubmit(event) {
event.preventDefault();
this.form.validateFields();
if (!this.form.isValid()) {
console.log('form is invalid: do not submit');
} else {
console.log('form is valid: submit');
}
axios({
method: "POST",
url: "http://localhost:3002/send",
data: this.state
}).then((response) => {
if (response.data.status === 'success') {
alert("Message Sent.");
this.resetForm()
} else if (response.data.status === 'fail') {
alert("Message failed to send.")
}
})
}
resetForm() {
this.setState({ firstname: '', lastname: '', email: '', message: '' })
} }
} }
export default ContactUs; export default ContactUs;

View file

@ -1 +1,60 @@
export { default } from "./Contact-us"; var express = require('express');
var router = express.Router();
var nodemailer = require('nodemailer');
var cors = require('cors');
const creds = require('./config');
var transport = {
host: 'smtp.mailtrap.io', // Dont forget to replace with the SMTP host of your provider
port: 2525,
auth: {
user: creds.USER,
pass: creds.PASS
}
}
var transporter = nodemailer.createTransport(transport)
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log('Server is ready to take messages');
}
});
router.post('/send', (req, res, next) => {
var firstname = req.body.firstname
var lastname = req.body.lastname
var email = req.body.email
var message = req.body.message
var agent = req.body.agent
var house = req.body.house
var content = `firstname: ${firstname} \n lastname: ${lastname} \n email: ${email} \n message: ${message} \n agent: ${agent} \n house: ${house}`
var mail = {
from: email,
to: 'kenes@cribs.com', // Change to email address that you want to receive messages on
subject: 'New Message from Contact Form',
text: content
}
transporter.sendMail(mail, (err, data) => {
if (err) {
res.json({
status: 'fail'
})
} else {
res.json({
status: 'success'
})
}
})
})
const app = express()
app.use(cors())
app.use(express.json())
app.use('/', router)
app.listen(3002)

View file

@ -43,7 +43,7 @@ const Navbar = () => {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<Link className="nav-link" to="/"> <Link className="nav-link" to="/contact-us">
Contact Us Contact Us
</Link> </Link>
</li> </li>