This commit is contained in:
omkieit 2024-11-05 17:01:20 +05:30
parent e66b5fc26d
commit 6b0be193a5
20 changed files with 455 additions and 88 deletions

View File

@ -0,0 +1,32 @@
import dotenv from "dotenv";
import mongoose from 'mongoose';
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import adminModal from "../models/admin.js"
dotenv.config();
const secret = process.env.SECRET_KEY;
export const login = async (req, res) => {
const { username, password } = req.body;
try {
const user = await adminModal.findOne({ username });
if (!user) return res.status(400).json({ message: "User not found" });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: "Invalid password" });
const token = jwt.sign({ userId: user._id }, secret, { expiresIn: "1h" });
res.json({ token });
} catch (error) {
res.status(500).json({ message: "Server error" });
}
};

View File

@ -241,3 +241,28 @@ export const updateUser = async (req, res) => {
res.status(500).json({ message: "Error updating user", error }); res.status(500).json({ message: "Error updating user", error });
} }
}; };
export const investFunds = async (req, res) => {
const { userId,fundAmount } = req.body;
// console.log("userId", userId, fundAmount);
if (fundAmount <= 0) {
return res.status(400).json({ message: "Investment amount must be greater than 0." });
}
try {
const updatedUser = await UserModal.findOneAndUpdate(
{ userId }, // Query by custom userId, not ObjectId
{ fundAmount},
{ new: true } // Return the updated document
);
if (!updatedUser) {
return res.status(404).json({ message: "User not found." });
}
res.status(200).json(updatedUser);
} catch (error) {
res.status(500).json({ message: "Something went wrong." });
}
};

View File

@ -7,7 +7,10 @@ import session from "express-session";
import userRouter from "./routes/user.js"; import userRouter from "./routes/user.js";
import propertyRouter from "./routes/property.js"; import propertyRouter from "./routes/property.js";
import mysqlRouter from "./routes/mysqlproperty.js"; import mysqlRouter from "./routes/mysqlproperty.js";
import adminRouter from "./routes/admin.js"
import dotenv from "dotenv"; import dotenv from "dotenv";
import bcrypt from "bcryptjs";
import adminModal from "./models/admin.js";
const app = express(); const app = express();
dotenv.config(); dotenv.config();
@ -34,6 +37,7 @@ app.use(
app.use("/users", userRouter); app.use("/users", userRouter);
app.use("/properties", propertyRouter); app.use("/properties", propertyRouter);
app.use("/mysql", mysqlRouter); // Use MySQL routes app.use("/mysql", mysqlRouter); // Use MySQL routes
app.use("/admin", adminRouter);
@ -65,3 +69,14 @@ dbConnectionPromise
console.log(`${error} did not connect`); console.log(`${error} did not connect`);
}); });
const createAdminUser = async () => {
const hashedPassword = await bcrypt.hash("Uer$99#KKma-hi19", 10);
const admin = new adminModal({ username: "admin", password: hashedPassword });
await admin.save();
console.log("Admin user created.", admin);
mongoose.disconnect();
};
createAdminUser();

11
ef-api/models/admin.js Normal file
View File

@ -0,0 +1,11 @@
import mongoose from "mongoose";
const adminSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
export default mongoose.model("Admin", adminSchema);

View File

@ -30,6 +30,7 @@ const userSchema = new mongoose.Schema({
} }
} }
], ],
fundAmount: { type: String },
}); });
export default mongoose.model("User", userSchema); export default mongoose.model("User", userSchema);

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

@ -0,0 +1,10 @@
import express from "express";
const router = express.Router();
import { login } from "../controllers/admin.js"
router.post("/login", login);
export default router;

View File

@ -1,7 +1,7 @@
import express from "express"; import express from "express";
const router = express.Router(); const router = express.Router();
import { signup, signin, verifyUser, showUser, forgotPassword, resetPassword, updateUser } from "../controllers/user.js"; import { signup, signin, verifyUser, showUser, forgotPassword, resetPassword, updateUser,investFunds } from "../controllers/user.js";
router.post("/signup", signup); router.post("/signup", signup);
router.post("/signin", signin); router.post("/signin", signin);
@ -10,6 +10,7 @@ router.get('/:userId', showUser);
router.post("/forgotpassword", forgotPassword); router.post("/forgotpassword", forgotPassword);
router.post("/resetpassword/:id/:token", resetPassword); router.post("/resetpassword/:id/:token", resetPassword);
router.put('/update', updateUser); router.put('/update', updateUser);
router.patch("/invest", investFunds);

File diff suppressed because one or more lines are too long

View File

@ -45,7 +45,7 @@
<script type="module" crossorigin src="/assets/index-CFxhPaJf.js"></script> <script type="module" crossorigin src="/assets/index-Cw_Jz1o9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DUngj0jf.css"> <link rel="stylesheet" crossorigin href="/assets/index-DUngj0jf.css">
</head> </head>

View File

@ -19,7 +19,9 @@ import PropertyMysqlView from "./components/PropertyMysqlView";
import EditProperty from "./components/EditProperty"; import EditProperty from "./components/EditProperty";
import SearchProperties from "./components/SearchProperties"; import SearchProperties from "./components/SearchProperties";
import ProfileView from "./components/ProfileView"; import ProfileView from "./components/ProfileView";
import AdminLogin from "./components/admin/AdminLogin";
import ProtectedRoute from "./components/admin/ProtectedRoute";
import AdminDashboard from "./components/admin/Dashboard";
const App = () => { const App = () => {
return ( return (
@ -32,7 +34,6 @@ const App = () => {
<Route path="/contact" element={<Contact />}></Route> <Route path="/contact" element={<Contact />}></Route>
<Route path="/register" element={<Register />}></Route> <Route path="/register" element={<Register />}></Route>
<Route <Route
path="/registrationsuccess" path="/registrationsuccess"
element={ element={
@ -66,10 +67,29 @@ const App = () => {
<Route path="/properties/:house_id" element={<PropertyMysqlView />} /> <Route path="/properties/:house_id" element={<PropertyMysqlView />} />
<Route path="/searchmyproperties" element={<SearchMysqlProperties />} /> <Route path="/searchmyproperties" element={<SearchMysqlProperties />} />
<Route path="/searchproperties" element={<SearchProperties />} /> <Route path="/searchproperties" element={<SearchProperties />} />
<Route path="/editproperty/:id" element={<PrivateRoute><EditProperty /></PrivateRoute>} /> <Route
path="/editproperty/:id"
element={
<PrivateRoute>
<EditProperty />
</PrivateRoute>
}
/>
<Route path="/profile/:userId" element={<ProfileView />} /> <Route path="/profile/:userId" element={<ProfileView />} />
<Route path="/AdminPLogin" element={<AdminLogin />} />
<Route
path="/AdminPLogin/dashboard"
element={
<ProtectedRoute>
<AdminDashboard />
</ProtectedRoute>
}
/>
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
); );

View File

@ -10,6 +10,7 @@ import UserProfile from "./UserProfile";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import FundingsReceived from "./FundingsReceived"; import FundingsReceived from "./FundingsReceived";
import OffersSubmitted from "./OffersSubmitted"; import OffersSubmitted from "./OffersSubmitted";
import FundToInvest from "./FundToInvest";
const Dashboard = () => { const Dashboard = () => {
const [activeTab, setActiveTab] = useState("dashboard"); const [activeTab, setActiveTab] = useState("dashboard");
@ -19,6 +20,8 @@ const Dashboard = () => {
switch (activeTab) { switch (activeTab) {
case "Userdetails": case "Userdetails":
return <UserProfile />; return <UserProfile />;
case "FundToInvest":
return <FundToInvest />;
case "addProperty": case "addProperty":
return <Addproperty />; return <Addproperty />;
case "activeProperties": case "activeProperties":
@ -89,6 +92,17 @@ const Dashboard = () => {
<span>User Profile</span> <span>User Profile</span>
</button> </button>
<button
className={`btn mt-3 ${
activeTab === "FundToInvest" ? "active" : ""
}`}
onClick={() => setActiveTab("FundToInvest")}
>
<span className="fa fa-home" style={{ color: "#F74B02" }} />
<span>Fund To Invest</span>
</button>
<button <button
className={`btn mt-3 ${ className={`btn mt-3 ${
activeTab === "addProperty" ? "active" : "" activeTab === "addProperty" ? "active" : ""

View File

@ -0,0 +1,76 @@
import { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { WillingToInvest } from "../redux/features/authSlice";
import { toast } from "react-toastify";
const FundToInvest = () => {
const { user } = useSelector((state) => ({ ...state.auth }));
console.log("user", user);
const dispatch = useDispatch();
const [formData, setFormData] = useState({
userId: user?.result?.userId || "",
fundAmount: user?.result?.fundAmount || "",
});
useEffect(() => {
if (user) {
setFormData({
userId: user?.result?.userId,
fundAmount: user?.result?.fundAmount,
});
}
}, [user]);
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
if (formData.fundAmount <= 0) {
return toast.error("Please enter a valid funding amount greater than 0");
}
dispatch(WillingToInvest({formData, toast }));
};
return (
<>
<form onSubmit={handleSubmit}>
<div className="col-4">
<div className="form-floating mb-3">
Willing to Invest
<input
type="text"
className="form-control"
placeholder="Enter Fund Amount"
name="fundAmount"
value={formData.fundAmount}
onChange={handleChange}
/>
</div>
</div>
<div className="col-12">
<div className="d-grid">
<button
className="btn btn-primary btn-lg"
type="submit"
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
>
submit
</button>
</div>
</div>
</form>
</>
);
};
export default FundToInvest;

View File

@ -40,14 +40,16 @@ const ProfileView = () => {
<div className="card"> <div className="card">
<div className="card-body"> <div className="card-body">
<div className="d-flex flex-column align-items-center text-center"> <div className="d-flex flex-column align-items-center text-center">
<img
<img className="img-fluid"
// src="https://bootdey.com/img/Content/avatar/avatar7.png" src={user.profileImage || profilepic}
src={user.profileImage} alt="ProfileImage"
alt="Admin" style={{
className="rounded-circle" marginTop: "0px",
width={150} maxWidth: "300px",
/> maxHeight: "300px",
}}
/>
<h4> <h4>
{user.title}. {user.firstName} {user.middleName}{" "} {user.title}. {user.firstName} {user.middleName}{" "}
@ -186,7 +188,7 @@ const ProfileView = () => {
<div className="col-md-4"> <div className="col-md-4">
<div className="card mb-3"> <div className="card mb-3">
<div className="card-body"> <div className="card-body">
<h1 <h1
className="d-flex align-items-center mb-3" className="d-flex align-items-center mb-3"
style={{ color: "#fda417", fontSize: "26px" }} style={{ color: "#fda417", fontSize: "26px" }}
> >
@ -194,25 +196,23 @@ const ProfileView = () => {
className="material-icons text-info mr-2" className="material-icons text-info mr-2"
style={{ color: "#fda417", fontSize: "26px" }} style={{ color: "#fda417", fontSize: "26px" }}
> >
About About
</i> </i>
me : me :
</h1> <hr /> </h1>{" "}
{user.aboutme} <hr />
{user.aboutme}
</div> </div>
</div> </div>
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<div className="card"> <div className="card">
<div className="card-body"> <div className="card-body">
<h1
<h1
className="d-flex align-items-center mb-3" className="d-flex align-items-center mb-3"
style={{ color: "#fda417", fontSize: "26px" }} style={{ color: "#fda417", fontSize: "26px" }}
> >
@ -220,24 +220,25 @@ const ProfileView = () => {
className="material-icons text-info mr-2" className="material-icons text-info mr-2"
style={{ color: "#fda417", fontSize: "26px" }} style={{ color: "#fda417", fontSize: "26px" }}
> >
Willing to Willing to
</i> </i>
invest: invest:
</h1> </h1>
<hr /> <hr />
<h2 className="d-flex flex-column align-items-center text-center" <h2
style={{ className="d-flex flex-column align-items-center text-center"
color: "#fda417", style={{
border: "#fda417", color: "#fda417",
fontSize: "60px", border: "#fda417",
fontWeight: "normal", fontSize: "60px",
}} fontWeight: "normal",
> }}
$ 500,000 >
</h2> $ {user.fundAmount}
</h2>
<span className="d-flex flex-column align-items-center text-center">
{/* <span className="d-flex flex-column align-items-center text-center">
<button <button
className="btn btn-primary btn-lg " className="btn btn-primary btn-lg "
type="submit" type="submit"
@ -248,38 +249,29 @@ const ProfileView = () => {
> >
Request Request
</button> </button>
</span> </span> */}
{/* <button className="btn btn-outline-primary">Message</button> */}
<hr /> <hr />
<div className="card-body"> <div className="card-body">
<div className="row"> <div className="row">
<div className="col-sm-3"> <div className="col-sm-3">
<span className="mb-0">Email</span> <span className="mb-0">Email</span>
</div> </div>
{user.email} {user.email}
<div className="col-sm-9 text-secondary"></div> <div className="col-sm-9 text-secondary"></div>
</div> </div>
<hr /> <hr />
<div className="row"> <div className="row">
<div className="col-sm-3"> <div className="col-sm-3">
<span className="mb-0">Phone</span> <span className="mb-0">Phone</span>
</div> </div>
67656584687 67656584687
<div className="col-sm-9 text-secondary"></div> <div className="col-sm-9 text-secondary"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="col-md-4 mb-3"> <div className="col-md-4 mb-3">
<div className="card"> <div className="card">

View File

@ -0,0 +1,72 @@
import axios from "axios";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
function AdminLogin() {
const BASE_URL = import.meta.env.VITE_REACT_APP_SECRET;
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const { data } = await axios.post(`${BASE_URL}/admin/login`, {
username,
password,
});
localStorage.setItem("token", data.token);
navigate("/AdminPLogin/dashboard");
} catch (error) {
alert("Invalid login credentials", error);
}
};
return (
<>
<section
className="d-flex justify-content-center align-items-center col-12"
style={{
minHeight: "100vh",
backgroundColor: "#FFFFFF",
width: "100%",
paddingLeft: "500px",
}}
>
<div className="col-md-6 col-lg-4">
<div className="container-fluid px-0">
<div className="row gy-4 align-items-center justify-content-center">
<div className="col-12 text-center">
<input
autoComplete="off"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<br /> <br />
<input
autoComplete="off"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<div className="mb-3">
<button onClick={handleSubmit} className="btn btn-dark w-70">
Login
</button>
</div>
</div>
</div>
</div>
</div>
</section>
</>
);
}
export default AdminLogin;

View File

@ -0,0 +1,20 @@
import { useNavigate } from "react-router-dom";
const AdminDashboard = () => {
const navigate = useNavigate();
const handleLogout = () => {
localStorage.removeItem("token");
navigate("/AdminPLogin");
};
return (
<div>
<h1>Welcome to the Sample Page!</h1>
<button onClick={handleLogout}>Logout</button>
</div>
);
};
export default AdminDashboard;

View File

@ -0,0 +1,23 @@
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
const Loadingredirect = () => {
const [count, setCount] = useState(3);
const navigate = useNavigate();
useEffect(() => {
const interval = setInterval(() => {
setCount((currentCount) => --currentCount);
}, 1000);
count === 0 && navigate("/login");
return () => clearInterval(interval);
}, [count, navigate]);
return (
<div style={{ marginTop: "100px" }}>
<h5>Redirecting you in {count} seconds</h5>
</div>
);
}
export default Loadingredirect

View File

@ -0,0 +1,23 @@
import { useNavigate } from "react-router-dom";
import { useEffect } from "react";
import PropTypes from "prop-types";
function ProtectedRoute({ children }) {
const navigate = useNavigate();
const token = localStorage.getItem("token");
console.log("token", token)
useEffect(() => {
if (!token) {
navigate("/AdminPLogin"); // Redirect to homepage if token is missing
}
}, [token, navigate]);
return token ? children : null;
}
ProtectedRoute.propTypes = {
children: PropTypes.node.isRequired, // Validate the presence of children
};
export default ProtectedRoute;

View File

@ -0,0 +1 @@
body{background: #000}.card{border: none;height: 320px}.forms-inputs{position: relative}.forms-inputs span{position: absolute;top:-18px;left: 10px;background-color: #fff;padding: 5px 10px;font-size: 15px}.forms-inputs input{height: 50px;border: 2px solid #eee}.forms-inputs input:focus{box-shadow: none;outline: none;border: 2px solid #000}.btn{height: 50px}.success-data{display: flex;flex-direction: column}.bxs-badge-check{font-size: 90px}

View File

@ -30,6 +30,8 @@ export const updateProperty = (id, propertyData) => API.put(`/properties/${id}`,
export const showUser = (userId) => API.get(`/users/${userId}`); export const showUser = (userId) => API.get(`/users/${userId}`);
export const investFunds = (formData) => API.patch(`/users/invest`, formData);

View File

@ -52,6 +52,20 @@ export const updateUser = createAsyncThunk(
} }
); );
export const WillingToInvest = createAsyncThunk(
"auth/WillingToInvest",
async ({formData, toast }, { rejectWithValue }) => {
try {
const response = await api.investFunds(formData); // Call the backend API
toast.success("Investment amount updated successfully!");
return response.data;
} catch (error) {
toast.error(error.response.data.message);
return rejectWithValue(error.response.data);
}
}
);
const authSlice = createSlice({ const authSlice = createSlice({
name: "auth", name: "auth",
@ -113,7 +127,22 @@ const authSlice = createSlice({
.addCase(updateUser.rejected, (state, action) => { .addCase(updateUser.rejected, (state, action) => {
state.isLoading = false; state.isLoading = false;
state.error = action.payload; state.error = action.payload;
}); })
.addCase(WillingToInvest.pending, (state) => {
state.loading = true;
})
.addCase(WillingToInvest.fulfilled, (state, action) => {
state.loading = false;
state.user = { ...state.user, result: action.payload }; // Update the user result with the new data
})
.addCase(WillingToInvest.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
})
;
}, },
}); });