This commit is contained in:
omkieit 2024-09-12 13:41:07 +05:30
parent a7727d10b7
commit 5f9f99b7a0
15 changed files with 293 additions and 88 deletions

View File

@ -0,0 +1,28 @@
import PropertyModal from "../models/property.js";
import UserModal from "../models/user.js";
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,
});
try {
await newProperty.save();
res.status(201).json(newProperty);
} catch (error) {
res.status(404).json({ message: "Something went wrong" });
}
};

View File

@ -4,6 +4,7 @@ import cors from "cors";
import morgan from "morgan"; import morgan from "morgan";
import session from "express-session"; import session from "express-session";
import userRouter from "./routes/user.js"; import userRouter from "./routes/user.js";
import propertyRouter from "./routes/property.js";
import dotenv from "dotenv"; import dotenv from "dotenv";
@ -32,6 +33,7 @@ app.use(
); );
app.use("/users", userRouter); app.use("/users", userRouter);
app.use("/property", propertyRouter);
app.get("/", (req, res) => { app.get("/", (req, res) => {
res.send("Welcome to EF-API"); res.send("Welcome to EF-API");

30
ef-api/middleware/auth.js Normal file
View File

@ -0,0 +1,30 @@
import jwt from "jsonwebtoken";
import UserModel from "../models/user.js"
import dotenv from "dotenv";
dotenv.config();
const secret = process.env.SECRET_KEY
const auth = async (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
const isCustomAuth = token.length < 500;
let decodedData;
if (token && isCustomAuth) {
decodedData = jwt.verify(token, secret);
req.userId = decodedData?.id;
} else {
decodedData = jwt.decode(token);
// const googleId = decodedData?.sub.toString();
// const user = await UserModel.findOne({ googleId });
// req.userId = user?._id;
req.userId = decodedData?.id;
}
next();
} catch (error) {
console.log(error);
}
};
export default auth;

27
ef-api/models/property.js Normal file
View File

@ -0,0 +1,27 @@
import mongoose from "mongoose";
const propertySchema = mongoose.Schema({
propertyType: {type: String, required: true },
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(),
},
});
const PropertyModal = mongoose.model("property", propertySchema);
export default PropertyModal;

View File

@ -18,7 +18,8 @@
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"mongoose": "^8.6.0", "mongoose": "^8.6.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"nodemailer": "^6.9.14" "nodemailer": "^6.9.14",
"uuid": "^10.0.0"
} }
}, },
"node_modules/@mongodb-js/saslprep": { "node_modules/@mongodb-js/saslprep": {
@ -1241,6 +1242,19 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@ -20,6 +20,7 @@
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"mongoose": "^8.6.0", "mongoose": "^8.6.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"nodemailer": "^6.9.14" "nodemailer": "^6.9.14",
"uuid": "^10.0.0"
} }
} }

10
ef-api/routes/property.js Normal file
View File

@ -0,0 +1,10 @@
import express from 'express';
const router = express.Router();
import auth from '../middleware/auth.js';
import { createProperty } from '../controllers/property.js';
router.post('/', auth, createProperty);
export default router;

File diff suppressed because one or more lines are too long

View File

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

View File

@ -41,6 +41,7 @@ const App = () => {
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
) )

View File

@ -1,75 +1,122 @@
import { useState } from 'react'; import { useState } from "react";
import "../addproperty.css" import { useDispatch } from "react-redux";
import { createProperty } from "../redux/features/propertySlice";
import "../addproperty.css";
const Addproperty = () => { const Addproperty = () => {
const [activeTab, setActiveTab] = useState('billing');
const handleContinue = () => { const [formData, setFormData] = useState({
if (activeTab === 'billing') setActiveTab('shipping'); propertyType: "",
else if (activeTab === 'shipping') setActiveTab('review'); title: "",
}; yearBuild: "",
totalSqft: "",
});
const handleBack = () => { const dispatch = useDispatch();
if (activeTab === 'review') setActiveTab('shipping');
else if (activeTab === 'shipping') setActiveTab('billing');
};
return ( const handleInputChange = (e) => {
<div className="container tabs-wrap"> setFormData({ ...formData, [e.target.name]: e.target.value });
<ul className="nav nav-tabs" role="tablist"> };
<li role="presentation" className={activeTab === 'billing' ? 'active tab' : 'tab'}>
<a onClick={() => setActiveTab('billing')} role="tab">Property Address</a>
</li>
<li role="presentation" className={activeTab === 'shipping' ? 'active tab' : 'tab'}>
<a onClick={() => setActiveTab('shipping')} role="tab">Request Investment Amount</a>
</li>
<li role="presentation" className={activeTab === 'review' ? 'active tab' : 'tab'}>
<a onClick={() => setActiveTab('review')} role="tab">Review &amp; Document</a>
</li>
</ul>
<div className="tab-content">
{activeTab === 'billing' && (
<div role="tabpanel" className="tab-pane active"> const handlePlaceOrder = (e) => {
<h3>Property Details</h3> e.preventDefault(); // Prevent form from refreshing the page
<p>Property Address Form</p> dispatch(createProperty(formData));
<button className="btn btn-primary continue" style={{ };
backgroundColor: "#fda417",
border: "#fda417", return (
}} onClick={handleContinue}>Continue</button> <div className="container tabs-wrap">
</div>
)}
{activeTab === 'shipping' && ( <form onSubmit={handlePlaceOrder}>
<div role="tabpanel" className="tab-pane active"> <div className="tab-content">
<h3>Investment Details</h3>
<p>Investment Details Form</p> <div role="tabpanel" className="tab-pane active">
<button className="btn btn-primary back" style={{ <h3>Submit the Property Details</h3>
backgroundColor: "#fda417", <br />
border: "#fda417", <div className="row gy-3 overflow-hidden">
}} onClick={handleBack}>Go Back</button>{" "} <div className="col-12">
<button className="btn btn-primary continue" style={{ <select
backgroundColor: "#fda417", className="form-floating mb-3 form-control"
border: "#fda417", name="propertyType"
}} onClick={handleContinue}>Continue</button> value={formData.propertyType}
</div> onChange={handleInputChange}
)} required
{activeTab === 'review' && ( >
<div role="tabpanel" className="tab-pane active"> <option value="">Please Select Property Type</option>
<h3>Review &amp; Document</h3> <option value="Residential">Residential</option>
<p>Review &amp; Document Tab</p> <option value="Land">Land</option>
<button className="btn btn-primary back" style={{ <option value="Commercial">Commercial</option>
backgroundColor: "#fda417", <option value="Industrial">Industrial</option>
border: "#fda417", <option value="Water">Water</option>
}} onClick={handleBack}>Go Back</button>{" "} </select>
<button className="btn btn-primary continue" style={{ </div>
backgroundColor: "#fda417", <div className="col-12">
border: "#fda417", <div className="form-floating mb-3">
}} >Place Order</button> <input
</div> type="text"
)} className="form-control"
</div> name="title"
</div> 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>
</form>
</div>
);
}; };
export default Addproperty; export default Addproperty;

View File

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

View File

@ -0,0 +1,41 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
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);
}
}
);
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;
});
},
});
export default propertySlice.reducer;

View File

@ -1,11 +1,13 @@
import { configureStore } from "@reduxjs/toolkit"; import { configureStore } from "@reduxjs/toolkit";
import AuthReducer from "./features/authSlice"; import AuthReducer from "./features/authSlice";
import userReducer from "./features/userSlice"; import userReducer from "./features/userSlice";
import propertyReducer from "./features/propertySlice";
export default configureStore({ export default configureStore({
reducer: { reducer: {
auth: AuthReducer, auth: AuthReducer,
user: userReducer, user: userReducer,
property: propertyReducer,
}, },
}); });

View File

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