This commit is contained in:
omkieit 2024-09-15 13:41:03 +05:30
parent 5f9f99b7a0
commit 195cdcc6c0
18 changed files with 468 additions and 320 deletions

View File

@ -4,25 +4,23 @@ import mongoose from "mongoose";
import { v4 as uuidv4 } from "uuid";
export const createProperty = async (req, res) => {
const property = req.body;
function generateRandomNumber() {
return Math.floor(Math.random() * 90000) + 10000;
}
const randomNumber = generateRandomNumber().toString();
const propertyId = `EFP${randomNumber}`;
const newProperty = new PropertyModal({
...property,
creator: req.userId,
createdAt: new Date().toISOString(),
publishedAt: new Date().toISOString(),
currentYear: new Date().getFullYear(),
propertyId: propertyId,
});
const propertyData = req.body;
// console.log('Property received:', propertyData);
const newProperty = new PropertyModal({
...propertyData,
});
try {
await newProperty.save();
res.status(201).json(newProperty);
} catch (error) {
res.status(404).json({ message: "Something went wrong" });
}
};
try {
await newProperty.save();
res.status(201).json(newProperty);
} catch (error) {
res.status(404).json({ message: "Something went wrong" });
}
};

View File

@ -8,6 +8,12 @@ const secret = process.env.SECRET_KEY
const auth = async (req, res, next) => {
try {
// Check if the authorization header exists
if (!req.headers.authorization) {
return res.status(401).json({ message: "Authorization header missing" });
}
const token = req.headers.authorization.split(" ")[1];
const isCustomAuth = token.length < 500;
let decodedData;

View File

@ -5,20 +5,7 @@ const propertySchema = mongoose.Schema({
title: {type: String, required: true },
yearBuild: {type: String, required: true },
totalSqft: {type: String, required: true },
propertyId: String,
creator: String,
createdAt: {
type: Date,
default: new Date(),
},
publishedAt: {
type: Date,
default: new Date(),
},
currentYear: {
type: Number,
default: new Date().getFullYear(),
},
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
ef-ui/dist/assets/index-BrT2kTNU.css vendored Normal file

File diff suppressed because one or more lines are too long

80
ef-ui/dist/assets/index-CzD7WLXM.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -42,8 +42,8 @@
<!-- <script src="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" crossorigin src="/assets/index-BdFlv2f8.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B4N2BY33.css">
<script type="module" crossorigin src="/assets/index-CzD7WLXM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BrT2kTNU.css">
</head>

View File

@ -1,4 +1,5 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import Home from "./components/Home";
import About from "./components/About";
import Contact from "./components/Contact";
@ -10,15 +11,23 @@ import VerifyUser from "./components/EmailVerify";
import ForgotPassword from "./components/ForgotPassword";
import ResetPassword from "./components/ResetPassword";
import Addproperty from "./components/Addproperty";
import Registrationsuccess from "./components/Registrationsuccess";
const App = () => {
return (
<BrowserRouter>
<ToastContainer />
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/contact" element={<Contact/>}></Route>
<Route path="/register" element={<Register/>}></Route>
<Route path="/registrationsuccess" element={<PrivateRoute><Registrationsuccess/></PrivateRoute>}></Route>
<Route path="/login" element={<Login/>}></Route>
<Route
path="/dashboard"
@ -37,7 +46,7 @@ const App = () => {
element={<ResetPassword />}
/>
<Route path="/addproperty" element={<Addproperty />}></Route>
<Route path="/addproperty" element={ <PrivateRoute><Addproperty /></PrivateRoute>}></Route>

View File

@ -1,122 +1,214 @@
import { useState } from "react";
import { useDispatch } from "react-redux";
import { createProperty } from "../redux/features/propertySlice";
import { useDispatch, useSelector } from "react-redux";
import { submitProperty } from "../redux/features/propertySlice";
import { useState, useEffect } from "react";
import Navbar from "./Navbar";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import "../addproperty.css";
const Addproperty = () => {
const [activeTab, setActiveTab] = useState("propertydetails");
const [formData, setFormData] = useState({
propertyType: "",
title: "",
yearBuild: "",
totalSqft: "",
const handleContinue = () => {
if (activeTab === "propertydetails") setActiveTab("shipping");
// else if (activeTab === 'shipping') setActiveTab('review');
};
const handleBack = () => {
// if (activeTab === 'review') setActiveTab('shipping');
// else if (activeTab === 'shipping') setActiveTab('propertydetails');
if (activeTab === "shipping") setActiveTab("propertydetails");
};
const dispatch = useDispatch();
const { status, error } = useSelector((state) => state.property); // Assuming propertySlice handles status and error
const [formData, setFormData] = useState({
propertyType: "",
title: "",
yearBuild: "",
totalSqft: "", // This will now be used in the shipping tab
});
const [submitted, setSubmitted] = useState(false); // Track if the form was submitted
const handleInputChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
const dispatch = useDispatch();
const handleSubmit = () => {
if (
formData.propertyType &&
formData.title &&
formData.yearBuild &&
formData.totalSqft
) {
// Dispatch the form data only if all fields are filled
dispatch(submitProperty(formData));
setSubmitted(true); // Mark the form as submitted
} else {
toast.error("Please fill all fields before submitting", {
position: "top-right",
autoClose: 3000,
});
}
};
const handleInputChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
// Show toast after form submission, based on status and error from Redux
useEffect(() => {
if (submitted) {
if (status === "succeeded") {
toast.success("Property submitted successfully!", {
position: "top-right",
autoClose: 3000,
});
setSubmitted(false); // Reset after showing toast
} else if (status === "failed") {
toast.error(`Failed to submit: ${error}`, {
position: "top-right",
autoClose: 3000,
});
setSubmitted(false); // Reset after showing toast
}
}
}, [status, error, submitted]);
return (
<>
<div className="container tabs-wrap">
<Navbar />
const handlePlaceOrder = (e) => {
e.preventDefault(); // Prevent form from refreshing the page
dispatch(createProperty(formData));
};
<ul className="nav nav-tabs" role="tablist">
<li
role="presentation"
className={activeTab === "propertydetails" ? "active tab" : "tab"}
>
<a onClick={() => setActiveTab("propertydetails")} role="tab">
Property Details
</a>
</li>
<li
role="presentation"
className={activeTab === "shipping" ? "active tab" : "tab"}
>
<a onClick={() => setActiveTab("shipping")} role="tab">
Delivery Address
</a>
</li>
</ul>
return (
<div className="container tabs-wrap">
<form onSubmit={handlePlaceOrder}>
<div className="tab-content">
<div role="tabpanel" className="tab-pane active">
<h3>Submit the Property Details</h3>
<br />
<div className="row gy-3 overflow-hidden">
<div className="col-12">
<select
className="form-floating mb-3 form-control"
name="propertyType"
value={formData.propertyType}
onChange={handleInputChange}
required
>
<option value="">Please Select Property Type</option>
<option value="Residential">Residential</option>
<option value="Land">Land</option>
<option value="Commercial">Commercial</option>
<option value="Industrial">Industrial</option>
<option value="Water">Water</option>
</select>
</div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
name="title"
placeholder="Property title"
value={formData.title}
onChange={handleInputChange}
required
/>
<label htmlFor="title" className="form-label">
Property Title
</label>
</div>
</div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
name="yearBuild"
placeholder="Year build"
value={formData.yearBuild}
onChange={handleInputChange}
required
/>
<label htmlFor="yearBuild" className="form-label">
Year Build
</label>
</div>
</div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
name="totalSqft"
placeholder="Total SQFT"
value={formData.totalSqft}
onChange={handleInputChange}
required
/>
<label htmlFor="totalSqft" className="form-label">
Total SQFT
</label>
</div>
</div>
</div>
<button
type="button"
className="btn btn-primary continue"
style={{ backgroundColor: "#fda417", border: "#fda417" }}
>
Submit
</button>
</div>
<div className="tab-content">
{activeTab === "propertydetails" && (
<div role="tabpanel" className="tab-pane active">
<h3>Property Details</h3>
<form>
<div className="row gy-3 overflow-hidden">
<div className="col-12">
<select
className="form-floating mb-3 form-control"
name="propertyType"
value={formData.propertyType}
onChange={handleInputChange}
required
>
<option value="">Please Select Property Type</option>
<option value="Residential">Residential</option>
<option value="Land">Land</option>
<option value="Commercial">Commercial</option>
<option value="Industrial">Industrial</option>
<option value="Water">Water</option>
</select>
</div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
name="title"
value={formData.title}
onChange={handleInputChange}
placeholder="Property title"
required
/>
<label htmlFor="title" className="form-label">
Property Title
</label>
</div>
</div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
name="yearBuild"
value={formData.yearBuild}
onChange={handleInputChange}
placeholder="Year build"
required
/>
<label htmlFor="yearBuild" className="form-label">
Year Build
</label>
</div>
</div>
</div>
</form>
</form>
<button
className="btn btn-primary continue"
onClick={handleContinue}
style={{ backgroundColor: "#fda417", border: "#fda417" }}
>
Continue
</button>
</div>
)}
{activeTab === "shipping" && (
<div role="tabpanel" className="tab-pane active">
<h3>Shipping Address</h3>
<form>
<div className="row gy-3 overflow-hidden">
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
name="totalSqft"
value={formData.totalSqft}
onChange={handleInputChange}
placeholder="Total SQFT"
required
/>
<label htmlFor="totalSqft" className="form-label">
Total SQFT
</label>
</div>
</div>
</div>
</form>
<button
className="btn btn-primary back"
onClick={handleBack}
style={{ backgroundColor: "#fda417", border: "#fda417" }}
>
Go Back
</button>{" "}
<button
type="button"
className="btn btn-primary continue"
style={{ backgroundColor: "#fda417", border: "#fda417" }}
onClick={handleSubmit}
>
Submit
</button>
</div>
)}
</div>
);
</div>
</>
);
};
export default Addproperty;

View File

@ -0,0 +1,61 @@
import { useState } from 'react';
import "../addproperty.css";
const Addproperty = () => {
const [activeTab, setActiveTab] = useState('billing');
const handleContinue = () => {
if (activeTab === 'billing') setActiveTab('shipping');
else if (activeTab === 'shipping') setActiveTab('review');
};
const handleBack = () => {
if (activeTab === 'review') setActiveTab('shipping');
else if (activeTab === 'shipping') setActiveTab('billing');
};
return (
<div className="container tabs-wrap">
<ul className="nav nav-tabs" role="tablist">
<li role="presentation" className={activeTab === 'billing' ? 'active tab' : 'tab'}>
<a onClick={() => setActiveTab('billing')} role="tab">Billing Address</a>
</li>
<li role="presentation" className={activeTab === 'shipping' ? 'active tab' : 'tab'}>
<a onClick={() => setActiveTab('shipping')} role="tab">Delivery Address</a>
</li>
<li role="presentation" className={activeTab === 'review' ? 'active tab' : 'tab'}>
<a onClick={() => setActiveTab('review')} role="tab">Review &amp; Payment</a>
</li>
</ul>
<div className="tab-content">
{activeTab === 'billing' && (
<div role="tabpanel" className="tab-pane active">
<h3>Billing Address</h3>
<p>Billing Address Form</p>
<button className="btn btn-primary continue" onClick={handleContinue}>Continue</button>
</div>
)}
{activeTab === 'shipping' && (
<div role="tabpanel" className="tab-pane active">
<h3>Shipping Address</h3>
<p>Shipping Address Form</p>
<button className="btn btn-primary back" onClick={handleBack}>Go Back </button>{" "}
<button className="btn btn-primary continue" onClick={handleContinue}>Continue</button>
</div>
)}
{activeTab === 'review' && (
<div role="tabpanel" className="tab-pane active">
<h3>Review &amp; Place Order</h3>
<p>Review &amp; Payment Tab</p>
<button className="btn btn-primary back" onClick={handleBack}>Go Back</button>{" "}
<button className="btn btn-primary continue">Place Order</button>
</div>
)}
</div>
</div>
);
};
export default Addproperty;

View File

@ -1,56 +0,0 @@
import { NavLink } from "react-router-dom";
const Header = () => {
return (
<>
<div className="container-fluid p-0 m-0">
<nav
className="navbar navbar-expand-lg w-100"
style={{
backgroundColor: "#000000",
position: "fixed", // Make the navbar fixed at the top
top: 0,
left: 0,
right: 0,
zIndex: 1000, // Ensure the navbar is above other elements
}}
>
<div className="container-fluid d-flex align-items-center justify-content-between">
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<form className="form-inline my-2 my-lg-0">
<div className="login_text">
<ul>
<li>
<NavLink to="/login" className="nav-link">
Login
</NavLink>
</li>
<li>
<a href="#">
<i className="fa fa-user" aria-hidden="true" />
</a>
</li>
</ul>
</div>
<div className="quote_btn">
<NavLink to="/register" className="nav-link">
Register
</NavLink>
</div>
</form>
</div>
</div>
</nav>
</div>
</>
)
}
export default Header

View File

@ -77,9 +77,24 @@ const Navbar = () => {
<li className="nav-item">
<NavLink to="/contact" className="nav-link">
Contact Us
Contact
</NavLink>
</li>
{/* <div className="btn_main">
<div className="started_text active">
<NavLink to="/addproperty" className="nav-link">Add Property</NavLink>
</div>
</div> */}
</ul>
{user?.result?._id ? (

View File

@ -0,0 +1,37 @@
import Footer from "./Footer"
import Navbar from "./Navbar"
const Registrationsuccess = () => {
return (
<>
<Navbar />
<br /> <br /> <br /> <br />
<div className="col-md-18">
<br /><br /><br /><br />
<h1 className="card-title text-center">
{" "}
Thank you for joining the world's most trusted realtor investment and borrowers portal.
</h1>
<h2>We reqest you to kindly <span style={{ fontSize: "2rem", color: "#fda417" }}>check your email inbox </span> and click on the <span style={{ fontSize: "2rem", color: "#fda417" }}>verification link </span>to access the dashboard.</h2>
</div>
<br /> <br /> <br />
<Footer />
</>
)
}
export default Registrationsuccess

View File

@ -19,5 +19,5 @@ API.interceptors.request.use((req) => {
export const signUp = (formData) => API.post("/users/signup", formData);
export const signIn = (formData) => API.post("/users/signin", formData);
export const verifyEmail = (id, token, data) => API.get(`/users/${id}/verify/${token}`, data);
export const createProperty = (propertyData) => API.post("/property", propertyData);
export const submitProperty = (propertyData) => API.post("/property", propertyData);

View File

@ -4,10 +4,9 @@ import * as api from "../api";
export const login = createAsyncThunk(
"auth/login",
async ({ formValue, navigate, toast }, { rejectWithValue }) => {
async ({ formValue, navigate }, { rejectWithValue }) => {
try {
const response = await api.signIn(formValue);
toast.success("Login Successfully");
navigate("/dashboard");
return response.data;
} catch (err) {
@ -22,7 +21,7 @@ export const register = createAsyncThunk(
try {
const response = await api.signUp(formValue);
toast.success("Register Successfully");
navigate("/");
navigate("/registrationsuccess");
return response.data;
} catch (err) {
return rejectWithValue(err.response.data);

View File

@ -1,41 +1,41 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// import axios from 'axios';
import * as api from "../api";
// Async thunk for submitting the form
export const createProperty = createAsyncThunk(
"property/createProperty",
async (formData, { rejectWithValue }) => {
try {
const response = await api.createProperty(formData);
return response.data;
} catch (error) {
return rejectWithValue(error.response.data);
}
export const submitProperty = createAsyncThunk(
'property/submitProperty',
async (propertyData, { rejectWithValue }) => {
try {
const response = await api.submitProperty(propertyData);
return response.data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
const propertySlice = createSlice({
name: "property",
initialState: {
data: {},
loading: false,
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(createProperty.pending, (state) => {
state.loading = true;
})
.addCase(createProperty.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(createProperty.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
},
name: 'property',
initialState: {
property: {},
status: 'idle',
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(submitProperty.pending, (state) => {
state.status = 'loading';
})
.addCase(submitProperty.fulfilled, (state, action) => {
state.status = 'succeeded';
state.property = action.payload;
})
.addCase(submitProperty.rejected, (state, action) => {
state.status = 'failed';
state.error = action.payload;
});
},
});
export default propertySlice.reducer;

View File

@ -14,7 +14,7 @@ export default defineConfig({
'/users/signup': 'http://67.225.129.127:3002/',
'/users/signin': 'http://67.225.129.127:3002/',
'/property': 'http://67.225.129.127:3002/',
// '/property': 'http://67.225.129.127:3002/',
// '/users/forgotpassword': 'http://67.225.129.127:3002/',
// '/users/:id/verify/:token': 'http://67.225.129.127:3002/',
},