This commit is contained in:
omkieit 2024-09-04 06:22:26 +05:30
parent 45ac17338c
commit 2773225ff9
18 changed files with 2215 additions and 128 deletions

View File

@ -0,0 +1,77 @@
import dotenv from "dotenv";
import UserModal from "../models/user.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
dotenv.config();
const secret = process.env.SECRET_KEY;
// This is signup
export const signup = async (req, res) => {
const { email, password, firstName, lastName } = req.body;
try {
// Check if user already exists
const oldUser = await UserModal.findOne({ email });
if (oldUser) {
return res.status(400).json({ message: "User already exists" });
}
// Hash the password
const hashedPassword = await bcrypt.hash(password, 12);
// Create new user
const result = await UserModal.create({
email,
password: hashedPassword,
firstName,
lastName,
});
// Generate JWT (if needed)
const token = jwt.sign({ email: result.email, id: result._id }, secret, { expiresIn: "7h" });
// Save the token in the user's tokens array
result.tokens.push({ token });
// Save the user
await result.save();
// console.log("ss", result)
// Send the user info and token back to the client
res.status(201).json({ result, token });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Something went wrong" });
}
};
export const signin = async (req, res) => {
const { email, password } = req.body;
try {
const oldUser = await UserModal.findOne({ email });
if (!oldUser)
return res.status(404).json({ message: "User doesn't exist" });
const isPasswordCorrect = await bcrypt.compare(password, oldUser.password);
if (!isPasswordCorrect)
return res.status(400).json({ message: "Invalid credentials" });
// if (!oldUser.verified) {
// return res.status(401).json({ message: "User is not verified" });
// }
const token = jwt.sign({ email: oldUser.email, id: oldUser._id }, secret, {
expiresIn: "8h",
});
res.status(200).json({ result: oldUser, token });
} catch (error) {
res.status(500).json({ message: "Something went wrong" });
console.log(error);
}
};

57
ef-api/index.js Normal file
View File

@ -0,0 +1,57 @@
import express from "express";
import mongoose from "mongoose";
import cors from "cors";
import morgan from "morgan";
import session from "express-session";
import userRouter from "./routes/user.js";
import dotenv from "dotenv";
const app = express();
dotenv.config();
app.use(morgan("dev"));
app.use(express.json({ limit: "30mb", extended: true }));
app.use(express.urlencoded({ limit: "30mb", extended: true }));
app.use(cors());
app.use(
session({
secret: process.env.SECRET_KEY,
resave: false,
saveUninitialized: true,
})
);
app.use("/users", userRouter);
app.get("/", (req, res) => {
res.send("Welcome to EF-API");
});
const port = process.env.PORT || 5000;
// Connect to the database
const dbConnectionPromise = mongoose.connect(process.env.DB_ACCESS, {
// useNewUrlParser: true,
// useUnifiedTopology: true,
});
// Start HTTP server
const startHttpServer = () => {
app.listen(port, () => {
console.log(`Server is running on port ${port} (HTTP)`);
});
}; startHttpServer();
// Handle database connection success/failure
dbConnectionPromise
.then(() => {
console.log("Connected to the database");
})
.catch((error) => {
console.log(`${error} did not connect`);
});

18
ef-api/models/user.js Normal file
View File

@ -0,0 +1,18 @@
import mongoose from "mongoose";
const userSchema = new mongoose.Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
tokens:[
{
token:{
type:String,
required:true
}
}
],
});
export default mongoose.model("User", userSchema);

1271
ef-api/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
ef-api/package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "ef-api",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-session": "^1.18.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.6.0",
"morgan": "^1.10.0",
"nodemailer": "^6.9.14"
}
}

11
ef-api/routes/user.js Normal file
View File

@ -0,0 +1,11 @@
import express from "express";
const router = express.Router();
import { signup, signin } from "../controllers/user.js";
router.post("/signin", signin);
router.post("/signup", signup);
export default router;

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

@ -27,7 +27,7 @@
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css">
<!-- fonts -->
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;800&family=Sen:wght@400;700;800&display=swap" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-BWSzxPyA.js"></script>
<script type="module" crossorigin src="/assets/index-Ckttlw5i.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CyAHZLBw.css">
</head>

362
ef-ui/package-lock.json generated
View File

@ -9,12 +9,19 @@
"version": "0.0.0",
"dependencies": {
"@fortawesome/react-fontawesome": "^0.2.2",
"@reduxjs/toolkit": "^2.2.7",
"axios": "^1.7.7",
"dotenv": "^16.4.5",
"ef-ui": "file:",
"mdb-react-ui-kit": "^8.0.0",
"primeicons": "^7.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.1"
"react-loading-icons": "^1.1.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
@ -375,6 +382,23 @@
"node": ">=6.9.0"
}
},
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
"license": "MIT",
"optional": true,
"dependencies": {
"@emotion/memoize": "0.7.4"
}
},
"node_modules/@emotion/memoize": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"license": "MIT",
"optional": true
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"cpu": [
@ -620,6 +644,40 @@
"node": ">= 8"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.5",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@reduxjs/toolkit": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.7.tgz",
"integrity": "sha512-faI3cZbSdFb8yv9dhDTmGwclW0vk0z5o1cia+kf7gCbaCwHI5e+7tP57mJUv22pNcNbeA62GSrPpfrUfdXcQ6g==",
"license": "MIT",
"dependencies": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@remix-run/router": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
@ -685,12 +743,10 @@
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
"dev": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.4",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@ -699,12 +755,17 @@
},
"node_modules/@types/react-dom": {
"version": "18.3.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-react": {
"version": "4.3.1",
"dev": true,
@ -907,6 +968,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"dev": true,
@ -921,6 +988,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"dev": true,
@ -1026,6 +1104,15 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"dev": true,
@ -1042,6 +1129,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"dev": true,
@ -1067,7 +1166,6 @@
},
"node_modules/csstype": {
"version": "3.1.3",
"dev": true,
"license": "MIT"
},
"node_modules/data-view-buffer": {
@ -1171,6 +1269,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/doctrine": {
"version": "2.1.0",
"dev": true,
@ -1182,6 +1289,18 @@
"node": ">=0.10.0"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/ef-ui": {
"resolved": "",
"link": true
@ -1651,6 +1770,26 @@
"dev": true,
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": {
"version": "0.3.3",
"dev": true,
@ -1659,6 +1798,44 @@
"is-callable": "^1.1.3"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/framer-motion": {
"version": "10.18.0",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz",
"integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==",
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
},
"optionalDependencies": {
"@emotion/is-prop-valid": "^0.8.2"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"dev": true,
@ -1864,6 +2041,16 @@
"node": ">= 4"
}
},
"node_modules/immer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": {
"version": "3.3.0",
"dev": true,
@ -2360,6 +2547,54 @@
"yallist": "^3.0.2"
}
},
"node_modules/mdb-react-ui-kit": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/mdb-react-ui-kit/-/mdb-react-ui-kit-8.0.0.tgz",
"integrity": "sha512-4XnfUv/SkMGhKbSUcEqNgNBYz009YEPHuweodher7ba70NlbnKlfgCxXolZPX5WUPYtsV2IjDRMxu3+oLnjzTQ==",
"license": "SEE LICENSE IN <License.txt>",
"dependencies": {
"@popperjs/core": "2.11.5",
"clsx": "1.1.1",
"framer-motion": "^10.16.4",
"react-popper": "2.3.0"
},
"peerDependencies": {
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.3",
"react": "^18.1.0",
"react-dom": "^18.1.0"
}
},
"node_modules/mdb-react-ui-kit/node_modules/clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"dev": true,
@ -2631,6 +2866,12 @@
"react-is": "^16.13.1"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"dev": true,
@ -2683,6 +2924,12 @@
"react": "^18.3.1"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
"license": "MIT"
},
"node_modules/react-icons": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
@ -2696,6 +2943,53 @@
"version": "16.13.1",
"license": "MIT"
},
"node_modules/react-loading-icons": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-loading-icons/-/react-loading-icons-1.1.0.tgz",
"integrity": "sha512-Y9eZ6HAufmUd8DIQd6rFrx5Bt/oDlTM9Nsjvf8YpajTa3dI8cLNU8jUN5z7KTANU+Yd6/KJuBjxVlrU2dMw33g==",
"license": "MIT",
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/react-popper": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
"integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
"license": "MIT",
"dependencies": {
"react-fast-compare": "^3.0.1",
"warning": "^4.0.2"
},
"peerDependencies": {
"@popperjs/core": "^2.0.0",
"react": "^16.8.0 || ^17 || ^18",
"react-dom": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-redux": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
"integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.3",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^18.2.25",
"react": "^18.0",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": {
"version": "0.14.2",
"dev": true,
@ -2736,6 +3030,34 @@
"react": ">=16.8"
}
},
"node_modules/react-toastify": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz",
"integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==",
"license": "MIT",
"dependencies": {
"clsx": "^2.1.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.6",
"dev": true,
@ -2773,6 +3095,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": {
"version": "2.0.0-next.5",
"dev": true,
@ -3123,6 +3451,12 @@
"node": ">=4"
}
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD"
},
"node_modules/type-check": {
"version": "0.4.0",
"dev": true,
@ -3254,6 +3588,15 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
"integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/vite": {
"version": "5.4.2",
"dev": true,
@ -3312,6 +3655,15 @@
}
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"dev": true,

View File

@ -11,12 +11,19 @@
},
"dependencies": {
"@fortawesome/react-fontawesome": "^0.2.2",
"@reduxjs/toolkit": "^2.2.7",
"axios": "^1.7.7",
"dotenv": "^16.4.5",
"ef-ui": "file:",
"mdb-react-ui-kit": "^8.0.0",
"primeicons": "^7.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.1"
"react-loading-icons": "^1.1.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",

View File

@ -1,11 +1,46 @@
import { useState, useEffect } from "react";
import Footer from "./Footer";
import Navbar from "./Navbar";
import { toast } from "react-toastify";
import LoadingIcons from "react-loading-icons";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { login } from "../redux/features/authSlice";
const initialState = {
email: "",
password: "",
};
const Login = () => {
const [formValue, setFormValue] = useState(initialState);
const { loading, error } = useSelector((state) => ({ ...state.auth }));
const { email, password } = formValue;
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
error && toast.error(error);
}, [error]);
const handleSubmit = (e) => {
e.preventDefault();
if (email && password) {
dispatch(login({ formValue, navigate, toast }));
}
};
const onInputChange = (e) => {
let { name, value } = e.target;
setFormValue({ ...formValue, [name]: value });
};
return (
<>
<Navbar />
<br /><br /><br /><br /><br />
<Navbar />
<br />
<br />
<br />
<br />
<br />
<section
className="py-19 py-md-5 py-xl-8"
style={{ minHeight: "100vh", backgroundColor: "#FFFFFF" }}
@ -13,17 +48,13 @@ const Login = () => {
<div className="container-fluid px-0">
<div className="row gy-4 align-items-center justify-content-center">
<div className="col-12 col-md-6 col-xl-20 text-center text-md-start">
<div className="text-bg-primary">
<div className="px-4">
<hr className="border-primary-subtle mb-4" />
<p className="lead mb-5">
A beautiful, easy-to-use, and secure Investor Portal that gives your investors everything they may need
A beautiful, easy-to-use, and secure Investor Portal that
gives your investors everything they may need
</p>
<div className="text-endx">
<svg
@ -41,32 +72,32 @@ const Login = () => {
</div>
</div>
<div className="col-12 col-md-6 col-xl-5">
<div className="card border-0 rounded-4 shadow-lg" style={{ width: "100%" }}>
<div
className="card border-0 rounded-4 shadow-lg"
style={{ width: "100%" }}
>
<div className="card-body p-3 p-md-4 p-xl-5">
<div className="row">
<div className="col-12">
<div className="mb-4">
<h2 className="h3">Please Login</h2>
</div>
</div>
</div>
<form action="#!">
<form method="POST">
<div className="row gy-3 overflow-hidden">
<div className="col-12">
</div>
<div className="col-12">
</div>
<div className="col-12"></div>
<div className="col-12"></div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="email"
className="form-control"
name="email"
id="email"
placeholder="name@example.com"
value={email}
name="email"
onChange={onInputChange}
required
/>
<label htmlFor="email" className="form-label">
@ -79,9 +110,11 @@ const Login = () => {
<input
type="password"
className="form-control"
name="password"
id="password"
placeholder="Password"
value={password}
name="password"
onChange={onInputChange}
required
/>
<label htmlFor="password" className="form-label">
@ -103,7 +136,6 @@ const Login = () => {
htmlFor="iAgree"
>
Remember me{" "}
</label>
</div>
</div>
@ -112,9 +144,15 @@ const Login = () => {
<button
className="btn btn-primary btn-lg"
type="submit"
style={{ backgroundColor: "#fda417", border:"#fda417" }}
name="signin"
value="Sign in"
onClick={handleSubmit}
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
>
{loading && <LoadingIcons.Bars />}
Sign In
</button>
</div>
@ -146,9 +184,8 @@ const Login = () => {
</div>
</section>
<Footer />
</>
)
}
);
};
export default Login
export default Login;

View File

@ -1,36 +1,89 @@
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { toast } from "react-toastify";
import LoadingIcons from "react-loading-icons";
import { register } from "../redux/features/authSlice";
import "../register.css";
import Footer from "./Footer";
import Navbar from "./Navbar";
import 'primeicons/primeicons.css';
import "primeicons/primeicons.css";
const initialState = {
firstName: "",
lastName: "",
email: "",
password: "",
confirmPassword: "",
};
const Register = () => {
const [formValue, setFormValue] = useState(initialState);
const [isFormValid, setIsFormValid] = useState(false); // New state variable
const { loading, error } = useSelector((state) => ({ ...state.auth }));
const { email, password, firstName, lastName, confirmPassword } = formValue;
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
error && toast.error(error);
}, [error]);
useEffect(() => {
// Check if all fields are filled and all three checkboxes are selected
const isValid =
email && password && firstName && lastName && confirmPassword;
setIsFormValid(isValid);
}, [email, password, firstName, lastName, confirmPassword]);
const handleSubmit = (e) => {
e.preventDefault();
if (password !== confirmPassword) {
return toast.error("Password should match");
}
if (isFormValid) {
dispatch(register({ formValue, navigate, toast }));
} else {
toast.error("Please fill in all fields and select all checkboxes");
}
};
const onInputChange = (e) => {
let { name, value } = e.target;
setFormValue({ ...formValue, [name]: value });
};
return (
<>
<Navbar />
<br /><br /><br /><br /><br />
<br />
<br />
<br />
<br />
<br />
<section
className="py-19 py-md-5 py-xl-8"
className="card"
style={{ minHeight: "100vh", backgroundColor: "#FFFFFF" }}
>
<div className="container-fluid px-0">
<div className="row gy-4 align-items-center justify-content-center">
<div className="col-12 col-md-6 col-xl-20 text-center text-md-start">
{/* <span className="pi pi-dollar" style={{ fontSize: '5rem', color:"#fda417" }} /> */}
<i className="fa fa-dollar" style={{ fontSize: '5rem', color:"#fda417" }} aria-hidden="true" />
{/* <span className="pi pi-dollar" style={{ fontSize: '5rem', color:"#fda417" }} /> */}
<i
className="fa fa-dollar"
style={{ fontSize: "2.5rem", color: "#fda417" }}
aria-hidden="true"
/>
<div className="text-bg-primary">
<div className="px-4">
<hr className="border-primary-subtle mb-4" />
<h2 className="h1 mb-4">
Empower your investors
</h2>
<h2 className="h1 mb-4">Empower your investors</h2>
<p className="lead mb-5">
A beautiful, easy-to-use, and secure Investor Portal that gives your investors everything they may need
A beautiful, easy-to-use, and secure Investor Portal that
gives your investors everything they may need
</p>
<div className="text-endx">
<svg
@ -48,7 +101,10 @@ const Register = () => {
</div>
</div>
<div className="col-12 col-md-6 col-xl-5">
<div className="card border-0 rounded-4 shadow-lg" style={{ width: "100%" }}>
<div
className="card border-0 rounded-4 shadow-lg"
style={{ width: "100%" }}
>
<div className="card-body p-3 p-md-4 p-xl-5">
<div className="row">
<div className="col-12">
@ -60,18 +116,20 @@ const Register = () => {
</div>
</div>
</div>
<form action="#!">
<form onSubmit={handleSubmit}>
<div className="row gy-3 overflow-hidden">
<div className="col-12">
<div className="form-floating mb-3">
<input
type="text"
className="form-control"
value={firstName}
name="firstName"
id="firstName"
onChange={onInputChange}
placeholder="First Name"
required
required="required"
/>
<label htmlFor="firstName" className="form-label">
First Name
</label>
@ -82,11 +140,13 @@ const Register = () => {
<input
type="text"
className="form-control"
value={lastName}
name="lastName"
id="lastName"
onChange={onInputChange}
placeholder="Last Name"
required
required="required"
/>
<label htmlFor="lastName" className="form-label">
Last Name
</label>
@ -97,10 +157,11 @@ const Register = () => {
<input
type="email"
className="form-control"
value={email}
name="email"
id="email"
onChange={onInputChange}
placeholder="name@example.com"
required
required="required"
/>
<label htmlFor="email" className="form-label">
Email
@ -112,16 +173,36 @@ const Register = () => {
<input
type="password"
className="form-control"
value={password}
name="password"
id="password"
onChange={onInputChange}
placeholder="Password"
required
required="required"
/>
<label htmlFor="password" className="form-label">
Password
</label>
</div>
</div>
<div className="col-12">
<div className="form-floating mb-3">
<input
type="password"
className="form-control"
value={confirmPassword}
name="confirmPassword"
onChange={onInputChange}
placeholder="confirmPassword"
required="required"
/>
<label htmlFor="password" className="form-label">
Retype Password
</label>
</div>
</div>
<div className="col-12">
<div className="form-check">
<input
@ -150,9 +231,12 @@ const Register = () => {
<button
className="btn btn-primary btn-lg"
type="submit"
style={{ backgroundColor: "#fda417", border:"#fda417" }}
style={{
backgroundColor: "#fda417",
border: "#fda417",
}}
>
{loading && <LoadingIcons.Bars />}
Sign up
</button>
</div>

View File

@ -1,10 +1,21 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from "react-redux";
import store from "./redux/store";
import App from './App.jsx'
import './index.css'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
)

20
ef-ui/src/redux/api.js Normal file
View File

@ -0,0 +1,20 @@
import axios from "axios";
const BASE_URL = import.meta.env.REACT_APP_SECRET;
// const API = axios.create({baseURL:"http://localhost:3001"});
// const API = axios.create({ baseURL: `${BASE_URL}` });
const API = axios.create({ baseURL: BASE_URL });
API.interceptors.request.use((req) => {
if (localStorage.getItem("profile")) {
req.headers.Authorization = `Bearer ${
JSON.parse(localStorage.getItem("profile")).token
}`;
}
return req;
});
export const signUp = (formData) => API.post("/users/signup", formData);
export const signIn = (formData) => API.post("/users/signin", formData);

View File

@ -0,0 +1,85 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import * as api from "../api";
export const register = createAsyncThunk(
"auth/register",
async ({ formValue, navigate, toast }, { rejectWithValue }) => {
try {
const response = await api.signUp(formValue);
toast.success("Register Successfully");
navigate("/");
return response.data;
} catch (err) {
return rejectWithValue(err.response.data);
}
}
);
export const login = createAsyncThunk(
"auth/login",
async ({ formValue, navigate, toast }, { rejectWithValue }) => {
try {
const response = await api.signIn(formValue);
toast.success("Login Successfully");
navigate("/register");
return response.data;
} catch (err) {
return rejectWithValue(err.response.data);
}
}
);
const authSlice = createSlice({
name: "auth",
initialState: {
user: null,
error: null,
loading: false,
},
reducers: {
setUser: (state, action) => {
state.user = action.payload;
},
setLogout: (state) => {
localStorage.clear();
state.user = null;
},
setUserDetails: (state, action) => {
state.user = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(register.pending, (state, action) => {
state.loading = true;
state.user = action.payload;
})
.addCase(register.fulfilled, (state, action) => {
state.loading = false;
state.error = null;
localStorage.setItem("profile", JSON.stringify({ ...action.payload }));
state.user = action.payload;
})
.addCase(register.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
})
.addCase(login.pending, (state) => {
state.loading = true;
})
.addCase(login.fulfilled, (state, action) => {
state.loading = false;
localStorage.setItem("profile", JSON.stringify({ ...action.payload }));
state.user = action.payload;
})
.addCase(login.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
})
},
});
export const { setUser, setLogout } = authSlice.actions;
export default authSlice.reducer;

10
ef-ui/src/redux/store.js Normal file
View File

@ -0,0 +1,10 @@
import { configureStore } from "@reduxjs/toolkit";
import AuthReducer from "./features/authSlice";
export default configureStore({
reducer: {
auth: AuthReducer,
},
});

View File

@ -1,7 +1,17 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/users/signup': 'http://localhost:3001', // Proxy requests to /users to the backend server
'/users/signin': 'http://localhost:3001',
},
},
})