This commit is contained in:
ruchi 2021-04-07 16:06:34 -04:00
commit 9374984732
38 changed files with 1687 additions and 20674 deletions

View file

@ -1,8 +1,10 @@
# SEG3125-LAB8
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/shared-components/contact-us/index.js" in order to set up the backend of the contact form.
# Authors
- Batuhan Basoglu, 300001274 - ArcticHawk1

20927
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,16 +6,38 @@
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"cors": "^2.8.5",
"email-validator": "^2.0.4",
"express": "^4.17.1",
"flag-icon-css": "^3.5.0",
"formik": "^2.2.6",
"i18next": "^20.2.1",
"i18next-browser-languagedetector": "^6.1.0",
"i18next-http-backend": "^1.2.1",
"joi-browser": "^13.4.0",
"js-cookie": "^2.2.1",
"nodemailer": "^6.5.0",
"react": "^17.0.2",
"react-alert": "^7.0.2",
"react-alert-template-basic": "^1.0.0",
"react-bootstrap": "^1.5.2",
"react-bootstrap-carousel": "^4.1.1",
"react-dom": "^17.0.2",
"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-hook-form": "^7.0.0",
"react-i18next": "^11.8.12",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-select": "^4.3.0",
"reactstrap": "^8.9.0",
"spectre.css": "^0.5.9",
"web-vitals": "^1.1.1"
"web-vitals": "^1.1.1",
"yup": "^0.32.9"
},
"scripts": {
"start": "react-scripts start",
@ -43,5 +65,18 @@
},
"devDependencies": {
"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

@ -0,0 +1,56 @@
{
"Welcome_to_React": "Welcome to React and react-i18next",
"Login_header": "Kene's Cribs",
"Login_message": "Welcome please back, please sign in!",
"Login_email": "Email address",
"Login_password": "Password",
"Login_remember_me": "remember me",
"Login_submit_button": "Submit",
"Account_header": "Personal Information",
"Account_message": "You can update/edit your account information here",
"Account_first_name": "First Name",
"Account_last_name": "Last Name",
"Account_email": "Email",
"Account_phone_number": "Phone Number",
"Account_current_password": "Current Password",
"Account_new_password": "New password",
"Account_update_button": "Update",
"Footer_message": "Copyright SEG3125 - Group 3",
"Nav_brand": "Kene's Cribs",
"Nav_Home": "Home",
"Nav_Listings": "Listings",
"Nav_Agents": "Agents",
"Nav_Contact_Us": "Contact Us",
"Nav_listing_page": "listing-page",
"Nav_Login": "Login",
"Slogan_1": "YOU ARE NOT BUYING A HOUSE, ",
"Slogan_2": "YOU ARE BUYING A LIFESTYLE. ",
"Info": "Click for Details",
"Home1": "Welcome to Kene's Cribs. Kene's Cribs is a real estator company which provides the clients with the houses.",
"Home2": "The houses limited to the clients are only limited to the clients' dreams. In order to further navigate the",
"Home3": "website, use the navigation bar to switch between pages. Listings page is for browsing houses, Agents page",
"Home4": "is for browsing the estators, the Contact Us page is for the contacting us and the login page is for our",
"Home5": "members who want to benefit from our deals. Above are some houses which can interest you. Feel free to",
"Home6": "click the buttons above to explore the houses you want.",
"Desc": "Description",
"Rooms": "1 Bedroom, 2 Bathroom",
"Danger1": "Message could not send.",
"Danger2": "There are some errors in your contact form.",
"Success1": "The message is successfully sent.",
"Success2": "You contact form will be delivered to our support team.",
"Contact-Title": "Contact to Us",
"Contact-House": "Select an Agent",
"Contact-Agent": "Select a House",
"No-Pref": "No preference",
"Contact-First": "First Name",
"Contact-Last": "Last Name",
"Contact-Mail": "Email Address",
"Contact-Message": "Message",
"Contact-Submit": "Submit",
"Contact-Error1": "Please select an item in the list.",
"Contact-Error2": "Please fill out this field.",
"Contact-Error3": "Invalid email address."
}

View file

@ -0,0 +1,53 @@
{
"Welcome_to_React": "Bienvenue a react et react-i18next",
"Login_header": "Berceaux de Kene",
"Login_message": "Bienvenue, merci de vous connecter!",
"Login_email": "Adresse e-mail",
"Login_password": "Mot de passe",
"Login_remember_me": "souviens-toi de moi",
"Login_submit_button": "Nous faire parvenir",
"Account_header": "Informations personnelles",
"Account_message": "Vous pouvez mettre à jour / modifier les informations de votre compte ici",
"Account_first_name": "Prénom",
"Account_last_name": "Nom de famille",
"Account_email": "E-mail",
"Account_phone_number": "Numéro de téléphone",
"Account_current_password": "Mot de passe actuel",
"Account_new_password": "Nouveau mot de passe",
"Account_update_button": "Mettre à jour",
"Footer_message": "Copyright SEG3125 - Groupe 3",
"Nav_brand": "Berceaux de Kene",
"Nav_Home": "Accueil",
"Nav_Listings": "Annonces",
"Nav_Agents": "Agents",
"Nav_Contact_Us": "Nous contacter",
"Nav_listing_page": "page de liste",
"Nav_Login": "Connexion",
"Slogan_1": "VOUS N'ACHETEZ PAS UNE MAISON, ",
"Slogan_2": "VOUS ACHETEZ UN STYLE DE VIE. ",
"Info": "Cliquer pour les détails",
"Home1": "Bienvenue chez Kene's Cribs. Kene's Cribs est une véritable société d'estateur qui fournit les maisons aux clients.",
"Home2": "Les maisons limitées aux clients ne se limitent qu'aux rêves des clients. Afin de mieux naviguer dans site Web, utilisez",
"Home3": "la barre de navigation pour basculer entre les pages. La page des listes sert à parcourir les maisons, la page Agents",
"Home4": "est pour parcourir les estators, la page Contactez-nous est pour nous contacter et la page de connexion est pour notre",
"Home5": "membres qui souhaitent bénéficier de nos offres. Ci-dessus, quelques maisons qui peuvent vous intéresser. N'hésitez",
"Home6": "pas à cliquez sur les boutons ci-dessus pour explorer les maisons que vous souhaitez.",
"Desc": "La Description",
"Rooms": "1 chambre, 2 salles de bains",
"Danger1": "Le message n'a pas pu être envoyé.",
"Danger2": "Il y a des erreurs dans votre formulaire de contact.",
"Success1": "Le message a été envoyé avec succès.",
"Success2": "Votre formulaire de contact sera envoyé à notre équipe d'assistance.",
"Contact-Title": "Contactez-Nous",
"Contact-House": "Sélectionnez un Agent",
"Contact-Agent": "Sélectionnez une Maison",
"No-Pref": "Aucune préférence",
"Contact-First": "Prénom",
"Contact-Last": "Nom de Famille",
"Contact-Mail": "Adresse E-mail",
"Contact-Message": "Le Message",
"Contact-Submit": "Soumettre",
"Contact-Error1": "Veuillez sélectionner un élément dans la liste.",
"Contact-Error2": "Veuillez remplir ce champ.",
"Contact-Error3": "Adresse e-mail invalide."
}

View file

@ -57,7 +57,7 @@
<title>Kene's Cribs</title>
</head>
<body>
<body dir="ltr">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--

View file

@ -1,26 +1,90 @@
import { Component } from "react";
import React, { useEffect } from "react";
import Footer from "./shared-components/footer-component/Footer";
import Navbar from "./shared-components/navbar-component/Navbar";
import { BrowserRouter, Route } from "react-router-dom";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import i18next from "i18next";
import { useTranslation } from "react-i18next";
import Cookies from "js-cookie";
import HomePage from "./homePage/Homepage";
import ListingsPage from "./listings-page/ListingsPage";
import AgentPage from "./agent-page/AgentPage";
// import ContactUs from "./shared-components/Contact-us/Contact-us";
import ListingPage from "./listings-page/single-listing/listing-page";
import Login from "./login-page/Login";
import Account from "./login-page/account/Account";
import ContactUs from "./shared-components/contact-us/contact-us";
class App extends Component {
render() {
return (
<div className="App">
<BrowserRouter>
<Navbar></Navbar>
const langauges = [
{
code: "fr",
name: "Français",
country_code: "fr",
},
{
code: "en",
name: "English",
country_code: "gb",
},
];
function App() {
const currentLanguageCode = Cookies.get("i18next") || "en";
const currentLanguage = langauges.find(
(lang) => lang.code === currentLanguageCode
);
const { t } = useTranslation();
useEffect(() => {
document.body.dir = currentLanguage.dir || "ltr";
}, [currentLanguage]);
return (
<div className="App">
{/* create the translations button */}
<div className="container">
<div className="d-flex justify-content-end">
{/* <!-- Example single danger button --> */}
<div class="btn-group">
<button
type="button"
class="btn btn-link dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<i class="fas fa-globe"></i>
</button>
<div class="dropdown-menu">
{langauges.map(({ code, name, country_code }) => (
<button
class="dropdown-item"
key={code}
onClick={() => i18next.changeLanguage(code)}
disabled={code === currentLanguageCode}
>
<span
className={`flag-icon flag-icon-${country_code} mx-2`}
></span>
{name}
</button>
))}
</div>
</div>
</div>
</div>
<BrowserRouter>
<Navbar></Navbar>
<Switch>
<Route exact path="/" component={HomePage}></Route>
<Route path="/listings" component={ListingsPage}></Route>
<Route path="/agents" component={AgentPage}></Route>
</BrowserRouter>
<Footer></Footer>
</div>
);
}
<Route path="/listing-page" component={ListingPage}></Route>
<Route path="/login" component={Login}></Route>
<Route path="/contact-us" component={ContactUs}></Route>
<Route path="/account" component={Account}></Route>
</Switch>
</BrowserRouter>
<Footer></Footer>
</div>
);
}
export default App;

View file

@ -4,25 +4,30 @@
white-space: nowrap;
}
.white-section {
background-color: #ffffff;
padding-top: 10rem;
padding-bottom: 5rem;
padding-left: 18%;
}
#title .container-fluid {
padding: 4% 34% 6%;
padding: 5% 34% 6%;
text-align: left;
font-family: "Georgia";
}
#features .container-fluid {
padding: 2% 48% 2%;
padding: 2% 14% 4%;
text-align: left;
font-family: "Georgia";
}
.housesIntro{
padding: 4% 5%;
width: 1200px;
height: 800px;
}
.contactIntro{
@ -31,23 +36,9 @@
padding-bottom: 80px;
}
.carouselSection {
margin: 2% 8%;
}
.headerImg{
width: 2000px;
height: 600px;
}
.heading-1{
padding-left: 40%;
padding-top: 10%;
padding-bottom: 20px;
}
.formhelper{
padding-bottom: 15px;
width: 1200px;
height: 800px;
}
.button-1 {
@ -67,3 +58,69 @@
padding-left: 45%;
padding-top: 15px;
}
.buttonka{
padding-top: 2px;
}
.prev-icon,
.next-icon {
height: 210px;
width: 100px;
outline: black;
background-size: 100%, 100%;
border-radius: 50%;
background-image: none;
text-shadow: 2px 2px 5px black;
}
.next-icon:after
{
content: '>';
font-size: 140px;
color: rgb(255, 255, 255);
text-shadow: 2px 2px 5px black;
}
.prev-icon:after {
content: '<';
font-size: 140px;
color: rgb(255, 255, 255);
text-shadow: 2px 2px 5px black;
}
ol.carousel-indicators {
position: absolute;
bottom: 5px;
margin: 0;
left: 0;
right: 0;
width: auto;
}
ol.carousel-indicators li,
ol.carousel-indicators li.active {
width: 1rem;
height: 1rem;
margin: 0;
border-radius: 50%;
border: 0;
background: transparent;
box-shadow: 0 0 1px 1px black;
}
ol.carousel-indicators li {
background: rgb(255, 255, 255);
margin-left: .5rem;
margin-right: .5rem;
box-shadow: 0 0 1px 1px black;
}
ol.carousel-indicators li.active {
background: #17a2b8;
box-shadow: 0 0 1px 1px black;
}
.homePage{
padding-bottom: 15%;
}

View file

@ -3,124 +3,101 @@ import "./Homepage.css";
import house1 from "../images/house1.jpg";
import house2 from "../images/house2.jpg";
import house3 from "../images/house3.jpg";
import house4 from "../images/house4.jpg";
import Carousel from "react-bootstrap/Carousel";
import Button from "react-bootstrap/Button";
import { withTranslation } from "react-i18next";
class Homepage extends Component {
constructor(props) {
super(props);
this.state = {
firstname: "",
lastname: "",
email: "",
message: "",
nextIcon: <span className="next-icon"></span>,
prevIcon: <span className="prev-icon"></span>,
};
}
render() {
const { t } = this.props;
const { nextIcon, prevIcon } = this.state;
return (
<div className="homePage">
<section className="colored-section" id="title">
<div className="container-fluid">
<div className="row">
<div className="col-12">
<h1 className="big-heading">YOU ARE NOT BUYING A HOUSE, </h1>
<h1 className="big-heading">YOU ARE BUYING A LIFESTYLE. </h1>
<h1 className="big-heading">{t("Slogan_1")}</h1>
<h1 className="big-heading">{t("Slogan_2")}</h1>
</div>
</div>
</div>
</section>
<section className="white-section" id="white-section">
<section className="white-section">
<div className="housesIntro">
<Carousel>
<Carousel nextIcon={nextIcon} prevIcon={prevIcon}>
<Carousel.Item>
<img className="headerImg" src={house1} alt="First House" />
<Carousel.Caption>
<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" }}>{t("Rooms")}</h2>
<div className="buttonka">
<Button href="/listings" variant="info">{t("Info")}</Button>
</div>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item>
<img className="headerImg" src={house2} alt="Second House" />
<Carousel.Caption>
<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" }}>{t("Rooms")}</h2>
<div className="buttonka">
<Button href="/listings" variant="info">{t("Info")}</Button>
</div>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item>
<img className="headerImg" src={house3} alt="Third House" />
<Carousel.Caption>
<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" }}>{t("Rooms")}</h2>
<div className="buttonka">
<Button href="/listings" variant="info">{t("Info")}</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" }}>{t("Rooms")}</h2>
<div className="buttonka">
<Button href="/listings" variant="info">{t("Info")}</Button>
</div>
</Carousel.Caption>
</Carousel.Item>
</Carousel>
</div>
</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)}
/>
<section className="colored-section" id="features">
<div className="container-fluid">
<div className="row">
<div className="col-12">
<h2 style={{ paddingLeft: "22rem", paddingBottom: "1rem", fontFamily: "Trebuchet MS" }}>{t("Desc")}</h2>
<p style={{ fontSize: "110%" }}>{t("Home1")}<br></br>
{t("Home2")}<br></br>
{t("Home3")}<br></br>
{t("Home4")}<br></br>
{t("Home5")}<br></br>
{t("Home6")}</p>
</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>
</section>
</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) {}
}
export default Homepage;
export default withTranslation()(Homepage);

View file

@ -1,10 +0,0 @@
import React from "react";
import { shallow } from "enzyme";
import HomePage from "./homePage";
describe("HomePage", () => {
test("matches snapshot", () => {
const wrapper = shallow(<HomePage />);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -1 +1 @@
export { default } from "./homePage";
export { default } from "./homePage";

BIN
src/images/house1-2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

BIN
src/images/house1-3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 253 KiB

BIN
src/images/house2-2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

BIN
src/images/house2-3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 245 KiB

BIN
src/images/house3-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 KiB

BIN
src/images/house3-3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 288 KiB

BIN
src/images/house4-2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

BIN
src/images/house4-3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

BIN
src/images/house4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,14 +1,49 @@
import React from "react";
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import "bootstrap/dist/css/bootstrap.min.css";
import "flag-icon-css/css/flag-icon.min.css";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
import "./index.css";
import App from "./App";
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.use(LanguageDetector)
.use(HttpApi)
.init({
// lng: "en",
supportedLngs: ["en", "fr"],
fallbackLng: "en",
detection: {
// "querystring",
order: ["cookie", "htmlTag", "localStorage", "path", "subdomain"],
caches: ["cookie"],
},
backend: {
loadPath: "/assets/locales/{{lng}}/translations.json",
},
react: {
useSuspense: false,
},
});
const loadingMsg = (
<div className="py-4 text-center">
<h2>Loading......</h2>
</div>
);
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
<Suspense fallback={loadingMsg}>
<React.StrictMode>
<App />
</React.StrictMode>
</Suspense>,
document.getElementById("root")
);

View file

@ -7,15 +7,17 @@
"ADDRESS": "8720 Russell Road",
"PRICE": "$500000",
"IMAGES":[
],
"AVAILABLE_AGENTS":[
],
"DESC":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui.",
"FULL_DESCRIPTION": "RUN DON'T WALK to this beautiful former Jayman show home. This home has every feature and convenience you could want in a home. When you enter you will be greeted with a large entry way, open concept main floor with a very spacious chef-like kitchen, the big eat in dinning area is an entertainers dream and plenty of room in the living room as to cozy up to the fireplace. When you head upstairs the owner 's suite will impress with a gorgeous ensuite consisting of a large soaker tub, oversized shower, separate vanities, in-floor heating and 2 separate closest. You will also find a nice sized flex room, spacious laundry room up stairs, 4 piece main bathroom and 2 more bedrooms. Then head downstairs where you will find another bedroom, bathroom and Theatre room with wet bar and beverage station that is perfect for any movie lover. Must not forget that this home is air conditioned, has front and back irrigation outside and speakers throughout the house. All of this can be yours in the lake community of Auburn Bay close to all amenities and year round lake access just a short distance away",
"FOR_SALE": true,
"coordinates": [-75.3372987731628, 45.383321536272049]
"coordinates": [-75.3372987731628, 45.383321536272049],
"BEDROOM": "2",
"BATHROOM": "4"
},
@ -32,8 +34,11 @@
],
"DESC":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui.",
"FULL_DESCRIPTION": "Nature at its Finest The Shores of Toney Bay are located at the mouth of River Phillip where it meets the Northumberland Straight with direct access to the incredible recreational waterways of River Phillip and nature rich Toney Bay. This nature lovers paradise boasts a plethora of migratory bird life and the rich fish stock of River Philip. The warm waters of the Northumberland Straight are swimmable from your shorefront, easy access to year-around activities and close proximity to the quaint community of Pugwash make this an ideal spot for a permeant residence or cottage. There are also ocean view / deeded access lots available. Large lots with unobstructed views and well away from highway noise. Swim in the warm waters at beautiful Heather's Beach just 4km away, fly fishing in the spring on River Phillip, book at tee time at Northumberland links, 16km away or enjoy a day at the Luxury Fox Harb'r Resort and Spa less then 30 mins away, and in winter enjoy skiing at Ski Wentworth a short 30 minutes drive. Lots are flat, cleared and ready for your dream home. All septic and building lot approvals are in place, good roads, and power to the lot line are included. Please take a few minutes to watch the video of the stunning Shores at Toney Bay",
"FOR_SALE": true,
"coordinates": [-75.546518086577947, 45.467134581917357]
"coordinates": [-75.546518086577947, 45.467134581917357],
"BEDROOM": "7",
"BATHROOM": "4"
@ -52,9 +57,11 @@
],
"DESC":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui.",
"FULL_DESCRIPTION": "Custom, Hi-Ranch home located in sought after neighborhood in the Heart of Petawawa. This home features 2+1 bedrooms and 4 bathrooms. Main level features spacious foyer, hardwood staircase, gleaming hardwood floors and open concept living. Large windows provide lots of natural light, vaulted ceilings in living room, a spectacular spacious kitchen with stainless steel appliances and large island. Master bedroom with walk through closet and 3 piece en-suite with custom tile shower and heated tile floor. Lower level features a finished rec room with cozy gas fireplace, bedroom and 3 piece bathroom. Patio door in foyer leads to large private backyard with no rear neighbours, spacious wood deck and hot tub. Double attached garage completes the package. Pack your backs and move right in! No Conveyances of offers until Friday April 9th at 3PM however the seller has the right to view and may accept pre-emptive offers",
"FOR_SALE": true,
"coordinates": [-75.898610599532319, 45.295014379864874]
"coordinates": [-75.898610599532319, 45.295014379864874],
"BEDROOM": "2",
"BATHROOM": "4"
@ -73,9 +80,12 @@
],
"DESC":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui.",
"FULL_DESCRIPTION": "Nestled in a quiet cul-de-sac in the family-friendly Ski Club/Widdifield neighbourhood, is 62 Riddle Court, a hidden gem that has everything you've been searching for - inside and out! Look up and take in the picturesque views of the escarpment, take a short walk or drive to the Laurentian Ski Hill with the family, or simply savour your very own property by having dinner on the patio, a glass of wine on the deck/balcony, and admire the breathtaking landscape (designed by an award-winning landscape artist) that wraps around your home. Lush perennials, tall grasses, interlocking brick pathways, in-ground sprinkler system and outdoor lighting make this property standout from the rest! Step inside to relish in all of the updated features this home has to offer - gleaming maple hardwood floors & stairs, updated maple kitchen cabinets, ceramic tile with in-floor radiant heat in bathrooms and office, built-in entertainment units, finished laundry room, low-maintenance composite deck off dining area, recessed lighting, updated windows, doors, insulation... the list goes on! Working from home these days? Enter the private & professional home office right off the recreation room through french doors. Enjoy the under-cabinet lighting, as well as the natural light beaming through the window with views of the backyard. Pride of ownership is obvious. Nothing left to do, but move-in! Pre-inspected for your peace of mind.",
"FOR_SALE": true,
"coordinates": [-75.468561642270757, 45.23032561834377]
"coordinates": [-75.468561642270757, 45.23032561834377],
"BEDROOM": "4",
"BATHROOM": "3"
}
]

View file

@ -0,0 +1,62 @@
.single-listing{
padding: 7% 15%;
/* background-color: darksalmon; */
}
.listing-header{
border: 1px solid black;
text-align: left;
padding: 1%;
}
.listing-title{
font-size: 2.5rem;
/* font-style: italic; */
}
.title-icon{
}
.listing-carousel{
margin: 5% 15%;
width: 70%;
/* padding-right: 10; */
}
.carousel-img{
/* width: 70%; */
}
.amentities{
border: 1px solid black;
/* padding: 5% 0; */
margin: 3% 40%;
padding: 1% 0%;
}
.amentities-text{
}
.amentities-icon{
margin: 0% 5%;
}
.description-box{
border: 1px solid black;
padding: 5%;
text-align: left;
}
.description-title{
padding: 2% 0%;
}
.listing-description{
}
.booking-btn{
padding: 2% 0%;
text-align: center;
}

View file

@ -1,9 +1,139 @@
import React, { Component } from "react";
import React from "react";
import "./listing-page.css";
import { Link } from "react-router-dom";
class ListingPage extends Component {
render() {
return <div>ListingPage</div>;
}
}
// import house images
import house_img1 from "../../images/house1-2.jpg";
const ListingPage = () => {
return (
<div className="single-listing">
{/* Listing header */}
<div className="row listing-header">
<div className="col-12">
<h2 className="listing-title">
<i class="fas fa-home title-icon"></i> My title
</h2>
</div>
</div>
{/* Carousel for images of the listings */}
<div className="listing-carousel">
<div
id="carouselExampleControls"
class="carousel slide"
data-ride="carousel"
>
<div class="carousel-inner">
<div class="carousel-item active carousel-img">
<img
src={house_img1}
class="d-block w-100"
alt="listing-img"
></img>
</div>
<div class="carousel-item carousel-img">
<img
src={house_img1}
class="d-block w-100"
alt="listing-img"
></img>
</div>
<div class="carousel-item">
<img
src={house_img1}
class="d-block w-100"
alt="listing-img"
></img>
</div>
</div>
<a
class="carousel-control-prev"
href="#carouselExampleControls"
role="button"
data-slide="prev"
>
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a
class="carousel-control-next"
href="#carouselExampleControls"
role="button"
data-slide="next"
>
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
{/* Text box for listing amenities */}
<div className="amentities">
<span className="amentities-text">1</span>
<i class="fas fa-bed fa-2x amentities-icon"></i>
<span className="amentities-text">2</span>
<i class="fas fa-bath fa-2x amentities-icon"></i>
<span className="amentities-text">3</span>
<i class="fas fa-wifi fa-2x amentities-icon"></i>
</div>
{/* Description for the listing */}
<div className="description-box">
<h2 className="description-title">Description</h2>
<p className="listing-description">
MOVE IN READY I WALKING DISTANCE TO SHOPPING, SCHOOLS AND TRANSIT I
Conveniently located in Haysboro, you have Restaurants, Groceries,
Coffee shops, Schools and a Library all within walking distance. This
Updated Bungalow offers over 2000 square feet of developed living
space, 3 Bedrooms upstairs to accommodate almost any family and 2 down
stairs in the fully developed basement. Having a separate entrance
downstairs through the backyard makes it much easier if you ever want
to convert the basement into a suite. The double garage is even heated
with 220v plus 2 additional spaces that could be used for RV parking
or for any additional vehicles in the house. You will notice the home
has been tastefully updated over the years including a soaker tub in
the bathroom, in the kitchen your cabinetry goes right to the ceiling
for a nice finished look, granite counters and Kitchenaid appliances
and a new garburator. The entire property has a fresh coat of paint
inside and out, Newer windows and roof along with the Plumbing,
Electrical (Cat 5 connection throughout and 200amp service) has been
updated. Last but not least, the back deck already has gas and ready
for the BBQ season. With the BIG updates being done, all that is left
to do is enjoy Summer on the deck or enjoy a bonfire in your sunny
West facing backyard!
</p>
<div className="booking-btn">
<Link to="/contact-us">
<button type="button" class="btn btn-lg btn-info">
Book Showing
</button>
</Link>
</div>
</div>
</div>
);
};
// sources for the images used
//images.adsttc.com/media/images/524c/2511/e8e4/4e67/bf00/03c3/large_jpg/Tetris_House_03.jpg?1380721927
//House description
// ->Link 1 -https://www.realtor.ca/real-estate/23018057/9811-elbow-drive-calgary-haysboro
// images
// 1 ->house1.jpg
// 2 ->//images.adsttc.com/media/images/524c/2511/e8e4/4e67/bf00/03c3/large_jpg/Tetris_House_03.jpg?1380721927
// 3 ->https://s3.amazonaws.com/on1-wp/wp-content/uploads/2018/08/3191-NW-Jenna-22-of-49v2-1024x683.jpg
//-> Link2 -> https://www.realtor.ca/real-estate/23020015/lot-14-67-seastone-drive-port-howe-port-howe
// 1 ->house2.jpg
// 2 ->https://static.photocdn.pt/images/articles/2019/12/24/How_to_Take_Gorgeous_Interior_Real_Estate_Photos.jpg
// 3 ->https://i.pinimg.com/originals/f3/72/87/f372870f4f99d3278193a3970aa394ce.jpg
//->Link3 -> https://www.realtor.ca/real-estate/23020040/1035-butler-boulevard-petawawa-laurentian-highlands
// 1 ->house3.jpg
// 2 ->https://miro.medium.com/max/1200/1*EyGqOwy0wQwKHUgi3xpQZA.png
// 3 ->https://i.pinimg.com/originals/9b/fe/76/9bfe76b23f7ed9dcb35f7553b8255f48.jpg
//->Link4 -> https://www.realtor.ca/real-estate/23020103/62-riddle-court-north-bay
// 1 ->house4.jpg
// 2 ->https://pictureitsoldfl.com/wp-content/uploads/2016/07/305-Rudder-Cay-Way-Jupiter-FL-print-014-28-Family-Room-4200x2800-300dpi-1170x780.jpg
// 3 ->https://www.tlcinteriors.com.au/wp-content/uploads/2018/06/hamptons-style-kitchen-from-metricon-bayville-display-home.jpg
export default ListingPage;

26
src/login-page/Login.css Normal file
View file

@ -0,0 +1,26 @@
.login-page{
padding: 10% 30%;
text-align: center;
background-color: pink;
}
.signin-Form{
border: 1px solid blanchedalmond;
border-radius: 2%;
}
.hr{
border-top: 2px dashed beige;
width: 30%;
}
.app-logo{
width: 15%;
}
.login-box{
padding: 0% 30%;
}
.login-btn{
margin: 10% 2%;
text-align: center;
}

77
src/login-page/Login.js Normal file
View file

@ -0,0 +1,77 @@
import React, { Component } from "react";
import Logo from "../images/logo1.png";
import "./Login.css";
import { Link } from "react-router-dom";
// import { useTranslation } from "react-i18next";
import { withTranslation } from "react-i18next";
class Login extends Component {
state = {
Account: {
firstName: "Kevon",
lastName: "Green",
email: "kevon.green@kcribs.com",
password: "Kcribs123",
},
};
handleLogin = (e) => {
console.log(e);
};
render() {
const { t } = this.props;
return (
<div className="login-page">
<form action="" className="signin-Form" onClick={this.handleLogin}>
<img className="app-logo" src={Logo} alt="app-logo" />
<h1 className="form-title">{t("Login_header")} </h1>
<p>{t("Login_message")}</p>
<hr className="hr" />
<div className="login-box">
<div class="form-group">
<label for="email">{t("Login_email")}</label>
<input
type="email"
class="form-control"
id="login-email"
// value={email}
aria-describedby="emailHelp"
placeholder="Enter your email"
required
/>
</div>
<div class="form-group">
<label for="password">{t("Login_password")}</label>
<input
type="password"
class="form-control"
id="login-password"
// value={password}
placeholder="Enter your password"
required
/>
</div>
<div class="form-group form-check">
<input
type="checkbox"
class="form-check-input"
id="exampleCheck1"
/>
<label class="form-check-label" for="exampleCheck1">
{t("Login_remember_me")}
</label>
</div>
<div className="login-btn">
<Link to="/account">
<button type="submit" class="btn btn-lg btn-primary">
{t("Login_submit_button")}
</button>
</Link>
</div>
</div>
</form>
</div>
);
}
}
export default withTranslation()(Login);

View file

@ -0,0 +1,37 @@
.account-info{
padding: 7%;
text-align: center;
background-color: #e7dec8;
}
.account-header{
/* margin: 0% 2%; */
padding: 0% 2%;
}
.account-img{
width: 90%;
margin: 10% 12%;
}
.update-account{
text-align: left;
padding: 4% 7%;
}
.info-group{
padding: 1%;
}
.label-text{
font-weight: bold;
margin: 2%;
}
.update-input{
padding: 0% 10%;
}
.update-btn{
padding: 4% 2%;
text-align: left;
}

View file

@ -0,0 +1,154 @@
import React, { Component } from "react";
import "./Account.css";
import Profile from "../../images/profile-picture.png";
import { withTranslation } from "react-i18next";
// import { useTranslation } from "react-i18next";
class Account extends Component {
state = {
Account: {
firstName: "Kevon",
lastName: "Green",
email: "kevon.green@kcribs.com",
password: "Kcribs123",
},
};
render() {
const { t } = this.props;
return (
<div className="account-info">
{/* // send information to my login component */}
<div className="account-header">
<h2 className="account-title">{t("Account_header")}</h2>
<p className="account-text">{t("Account_message")}</p>
</div>
<div className="row account-box">
<div className="col-4">
<img src={Profile} alt="profile-img" className="account-img" />
</div>
<div className="col-8">
<form action="" className="update-account">
<div class="row form-group info-group">
<div className="col-4">
<label class="label-text" for="email">
{t("Account_first_name")}
</label>
</div>
<div className="col-8 update-input">
<input
type="text"
class="form-control"
id="account-fname"
placeholder={this.state.Account.firstName}
required
disabled={true}
/>
</div>
</div>
<div class="row form-group info-group">
<div className="col-4">
<label class="label-text" for="email">
{t("Account_last_name")}
</label>
</div>
<div className="col-8 update-input">
<input
type="text"
class="form-control"
id="account-lname"
placeholder={this.state.Account.lastName}
required
disabled={true}
/>
</div>
</div>
<div class="row form-group info-group">
<div className="col-4">
<label class="label-text" for="email">
{t("Account_email")}
</label>
</div>
<div className="col-8 update-input">
<input
type="email"
class="form-control"
id="account-email"
aria-describedby="emailHelp"
placeholder={this.state.Account.email}
required
disabled={true}
/>
</div>
</div>
<div class="row form-group info-group">
<div className="col-4">
<label class="label-text" for="email">
{t("Account_phone_number")}
</label>
</div>
<div className="col-8 update-input">
<input
type="text"
class="form-control"
id="account-phoneNumber"
placeholder={this.state.Account.email}
required
disabled={true}
/>
</div>
</div>
<div class="row form-group info-group">
<div className="col-4">
<label class="label-text" for="email">
{t("Account_current_password")}
</label>
</div>
<div className="col-8 update-input">
<input
type="text"
class="form-control"
id="account-existPassword"
placeholder={"**************"}
required
disabled={true}
/>
</div>
</div>
{/* <div class="row form-group info-group">
<div className="col-4">
<label class="label-text" for="email">
{t("Account_new_password")}
</label>
</div>
<div className="col-8 update-input">
<input
type="text"
class="form-control"
id="account-newPassword"
required
/>
</div>
</div> */}
<div className="update-btn">
<button
type="button"
class="btn btn-lg btn-danger"
disabled={true}
>
{t("Account_update_button")}
</button>
</div>
</form>
</div>
</div>
</div>
);
}
}
//links
// https://www.pngitem.com/pimgs/m/146-1468479_my-profile-icon-blank-profile-picture-circle-hd.png
export default withTranslation()(Account);

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: 36%;
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,233 @@
import React, { Component } from "react";
import "./Contact-us.css";
import axios from 'axios'
import {
FieldFeedback,
FieldFeedbacks,
FormWithConstraints
} from 'react-form-with-constraints';
import Alert from 'react-bootstrap/Alert';
import { withTranslation } from "react-i18next";
class ContactUs extends Component {
constructor(props) {
super(props);
this.state = {
firstname: "",
lastname: "",
email: "",
message: "",
agent: "",
house: "",
alertBad: false,
alertSucess: false,
time: false
};
}
render() {
return <div>ContactUs</div>;
const { t } = this.props;
return (
<div className="contact-us">
<Alert variant="danger" style={{ display: this.state.alertBad ? "block" : "none" }} onClose={() => this.setState({ alertBad: false })} dismissible>
<Alert.Heading>{t("Danger1")}</Alert.Heading>
<p>
{t("Danger2")}
</p>
</Alert>
<Alert variant="success" style={{ display: this.state.alertSucess ? "block" : "none" }} onClose={() => this.setState({ alertSucess: false })} dismissible>
<Alert.Heading>{t("Success1")}</Alert.Heading>
<p>
{t("Success2")}
</p>
</Alert>
<div className="container">
<section class="colored-section" id="contact">
<div className="container-fluid">
<div className="contactIntro">
<h2 className="heading-1">{t("Contact-Title")}</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" required onChange={this.onAgentChange.bind(this)} value={this.state.agent}>
<option value="">{t("Contact-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">{t("No-Pref")}</option>
</select>
<FieldFeedbacks for="agent">
<FieldFeedback when="*">
{t("Contact-Error1")}
</FieldFeedback>
</FieldFeedbacks>
</div>
<div className="col-6">
<select className="form-group" name="house" id="dropdown" required onChange={this.onHouseChange.bind(this)} value={this.state.house}>
<option value="">{t("Contact-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">{t("No-Pref")}</option>
</select>
<FieldFeedbacks for="agent">
<FieldFeedback when="*">
{t("Contact-Error1")}
</FieldFeedback>
</FieldFeedbacks>
</div>
</div>
<div className="formhelper row">
<div className="col-6">
<input name="firstname"
type="text"
className="form-control"
placeholder={t("Contact-First")}
value={this.state.firstname}
required onChange={this.onFirstNameChange.bind(this)}
/>
<FieldFeedbacks for="agent">
<FieldFeedback when="*">
{t("Contact-Error2")}
</FieldFeedback>
</FieldFeedbacks>
</div>
<div className="col-6">
<input name="lastname"
type="text"
className="form-control"
placeholder={t("Contact-Last")}
value={this.state.lastname}
required onChange={this.onLastNameChange.bind(this)}
/>
<FieldFeedbacks for="agent">
<FieldFeedback when="*">
{t("Contact-Error2")}
</FieldFeedback>
</FieldFeedbacks>
</div>
</div>
<div className="form-group">
<input name="email"
type="email"
className="form-control"
placeholder={t("Contact-Mail")}
aria-describedby="emailHelp"
value={this.state.email}
required onChange={this.onEmailChange.bind(this)}
/>
<FieldFeedbacks for="email">
<FieldFeedback when={value => value.length === 0}>{t("Contact-Error2")}</FieldFeedback>
<FieldFeedback when={value => !/\S+@\S+/.test(value)}>{t("Contact-Error3")}</FieldFeedback>
</FieldFeedbacks>
</div>
<div className="form-group">
<textarea name="message"
className="form-control"
placeholder={t("Contact-Message")}
rows="5"
value={this.state.message}
required onChange={this.onMessageChange.bind(this)}
/>
<FieldFeedbacks for="agent">
<FieldFeedback when="*">
{t("Contact-Error1")}
</FieldFeedback>
</FieldFeedbacks>
</div>
<div className="buttonhelper">
<button type="submit" className="button-1">
{t("Contact-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()) {
this.alertBad();
return;
} else {
this.alertSuccess();
axios({
method: "POST",
url: "http://localhost:3002/send",
data: this.state
}).then((response) => {
if (response.data.status === 'success') {
this.resetForm();
} else if (response.data.status === 'fail') {
return;
}
})
}
}
alertSuccess() {
this.setState({ alertBad: false, alertSucess: true })
}
alertBad() {
this.setState({ alertSucess: false, alertBad: true })
}
resetForm() {
this.setState({ firstname: '', lastname: '', email: '', message: '', agent: '', house: '' })
}
}
export default ContactUs;
export default withTranslation()(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 agent = req.body.agent
var house = req.body.house
var message = req.body.message
var content = `First Name: ${firstname} \n Last Name: ${lastname} \n E-mail: ${email} \n Agent: ${agent} \n House: ${house} \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)

View file

@ -1,6 +1,9 @@
import React from "react";
import "./Footer.css";
import { useTranslation } from "react-i18next";
const Footer = () => {
const { t } = useTranslation();
return (
<footer>
<section id="footer">
@ -9,7 +12,7 @@ const Footer = () => {
<i class="fab fa-facebook social-icon"></i>
<i class="fab fa-instagram social-icon"></i>
<i class="fas fa-envelope social-icon"></i>
<p class="">© Copyright SEG3125 - Group 3</p>
<p class="">© {t("Footer_message")}</p>
</div>
</section>
</footer>

View file

@ -3,14 +3,16 @@ import { Link, withRouter } from "react-router-dom";
import "./Navbar.css";
import logo from "./logo2.png";
/* eslint-disable jsx-a11y/anchor-is-valid */
import { useTranslation } from "react-i18next";
const Navbar = () => {
const { t } = useTranslation();
return (
/* Nav Bar */
<div className="Navbar">
<nav class="navbar navbar-expand-lg navbar-dark">
<img src={logo} alt="" width="40" height="40"></img>
<a href="/" class="navbar-brand">
Kene's Cribs
{t("Nav_brand")}
</a>
<button
className="navbar-toggler"
@ -27,29 +29,36 @@ const Navbar = () => {
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<Link className="nav-link" exact to="/">
Home
{t("Nav_Home")}
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/listings">
Listings
{t("Nav_Listings")}
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/agents">
Agents
{t("Nav_Agents")}
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/">
Contact Us
<Link className="nav-link" to="/contact-us">
{t("Nav_Contact_Us")}
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/">
Login
<Link className="nav-link" to="/listing-page">
{t("Nav_listing_page")}
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/login">
{t("Nav_Login")}
</Link>
</li>
</ul>