diff --git a/.DS_Store b/.DS_Store
index 05c4b75..803ae7b 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/readCycle-client/src/App.css b/readCycle-client/src/App.css
index 7916f19..4b23abc 100644
--- a/readCycle-client/src/App.css
+++ b/readCycle-client/src/App.css
@@ -36,7 +36,9 @@ body {
padding: 15px;
border-radius: 20px;
font-size: 1.7em;
- text-shadow: 2px 2px 3px #000;
+
+ text-shadow: 2px 2px 3px #1f1e1e;
+
line-height: 1.5;
color: aliceblue;
}
@@ -69,10 +71,11 @@ body {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
- min-height: 95vh;
+ min-height: 100vh;
display: flex;
+ justify-content: center;
+ align-items: center;
flex-direction: column;
- max-width: 100%;
padding: 0;
}
@@ -87,12 +90,9 @@ body {
color: rgb(92, 106, 106);
}
+.SignupInner,
.LoginInner {
- margin: 50px 0 0 50px;
-}
-
-.SignupInner {
- margin: 30px 0 0 50px;
+ text-align: center;
}
input {
@@ -176,11 +176,11 @@ textarea {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
- padding-left: 20px;
min-height: 100vh;
- padding-top: 20px;
+ padding-top: 100px;
padding-bottom: 90px;
overflow: scroll;
+ text-align: center;
}
.form-group {
@@ -220,8 +220,9 @@ textarea {
}
.bookDetails {
- display: inline-block;
- margin: 20px;
+ display: flex;
+ padding-top: 80px;
+ margin: 40px;
}
.libraryPage {
@@ -242,17 +243,22 @@ textarea {
.headerText {
color: aliceblue;
font-size: 1.7em;
- text-shadow: 2px 2px 5px #000;
+ text-shadow: 2px 2px 4px #000;
+ padding-top: 10px;
}
/* about page style */
.about {
background-image: url(./assets/book.png);
background-size: cover;
+ background-position: center;
background-repeat: no-repeat;
overflow: scroll;
height: 100vh;
padding-bottom: 80px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
.aboutContainer {
@@ -271,8 +277,7 @@ textarea {
flex-direction: column;
margin: auto;
text-align: center;
- /* text-shadow: 2px 2px 5px #000; */
- font-size: 1.1em;
+ font-size: 1.2em;
line-height: 1.5;
color: aliceblue;
}
@@ -284,6 +289,9 @@ textarea {
align-items: center;
background-color: #c5c0c0;
padding: 15px 20px;
+ position: fixed;
+ top: 0;
+ width: 100%;
}
.nav-button-container {
@@ -342,12 +350,6 @@ textarea {
box-shadow: 0 4px 6px #0000001a;
transition: box-shadow 0.6s ease-in-out;
line-height: 1.3em;
-
- /* Add a media query for smaller screens */
- @media (max-width: 768px) {
- /* Change the number of columns to adjust for smaller screens */
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
- }
}
.link-button,
@@ -362,7 +364,7 @@ textarea {
border-radius: 10px;
cursor: pointer;
margin: 10px 10px 30px;
- min-width: 200px;
+ max-width: 260px;
}
.link-button:hover,
@@ -371,24 +373,24 @@ textarea {
}
/* CSS for book details page */
-.bookDetails {
- display: flex;
- padding-top: 40px;
+.details-page {
+ min-height: 100vh;
}
.bookDetailCover {
margin-right: 20px;
}
-.bookInfo {
- font-size: 1.1em;
- max-width: 30%;
+.book-info {
+ font-size: 1rem;
+ max-width: 60%;
line-height: 1.3em;
}
.bookOwner a {
text-decoration: none;
}
+
.hidden {
display: none;
}
@@ -411,23 +413,24 @@ textarea {
.bookDetailCover img {
margin: 15px;
}
+
.confirmation-text {
- margin: 20px 20px;
+ margin: 15px 15px;
border: 2px solid #777;
max-width: 400px;
background-color: #555;
color: aliceblue;
text-align: center;
- font-size: 1.1em;
+ font-size: 1rem;
border-radius: 7px;
}
/* CSS for user profile */
-
.profile-container {
display: flex;
justify-content: space-evenly;
- padding-bottom: 70px;
+ padding: 70px 0;
+ min-height: 100vh;
}
.book-details {
padding: 10px 20px 15px 30px;
@@ -453,7 +456,6 @@ textarea {
}
.userAvatar {
border-radius: 10px;
- /* padding-left: 10px; */
display: block;
margin-left: auto;
margin-right: auto;
@@ -514,7 +516,7 @@ textarea {
font-size: 16px;
border-radius: 7px;
}
-/* Add margin to create space between buttons */
+
.bookEditButton + .bookEditButton {
margin-left: 10px;
}
@@ -530,7 +532,6 @@ textarea {
position: fixed;
width: 100%;
bottom: 0;
- /* height: 60px; */
}
.social-links {
@@ -553,3 +554,176 @@ textarea {
.rounded-image {
border-radius: 10px;
}
+.book-buttons {
+ margin: auto;
+}
+
+@media screen and (max-width: 768px) {
+ .booksContainer,
+ .booksShared {
+ grid-gap: 1rem;
+ grid-template-columns: repeat(auto-fill, minmax(115px, 1fr));
+ }
+ .bookContainer,
+ .bookShared {
+ line-height: 1rem;
+ }
+ .BookCard a,
+ .BookCard p {
+ font-size: 0.8rem;
+ }
+ .homePage {
+ max-height: 100vh;
+ }
+ .nav {
+ padding: 15px 5px;
+ }
+ .nav-left,
+ .nav-right {
+ display: flex;
+ }
+ .nav-button {
+ font-size: 0.7rem;
+ padding: 5px 7px;
+ margin: 0 4px;
+ }
+ .homeContent {
+ max-width: 300px;
+ font-size: 1rem;
+ }
+ .headerText {
+ font-size: 1.2rem;
+ }
+ .libraryHeader {
+ padding-top: 50px;
+ }
+ .libraryPage {
+ margin: 20px 20px 80px;
+ }
+ .link-button,
+ .genre-select {
+ font-size: 0.8rem;
+ max-width: 170px;
+ margin: 0 5px 15px;
+ display: inline;
+ padding: 6px;
+ }
+ .footer {
+ height: 50px;
+ }
+ .copyright {
+ font-size: 0.7rem;
+ }
+ .social-links {
+ gap: 15px;
+ margin-right: 10px;
+ }
+ .footer .fab {
+ font-size: 1.2rem;
+ }
+ .aboutContainer {
+ margin-top: 4rem;
+ }
+ .aboutBox {
+ font-size: 0.8rem;
+ line-height: 0.9rem;
+ width: 70%;
+ }
+ .Login,
+ .Signup {
+ width: 70%;
+ min-height: 430px;
+ }
+ input,
+ #genre,
+ textarea {
+ font-size: 0.8rem;
+ margin: 5px 0;
+ font-size: inherit;
+ }
+ .bookDetails {
+ display: flex;
+ padding-top: 40px;
+ margin: 25px 15px;
+ }
+ .bookDetailCover img {
+ width: 130px;
+ }
+ .book-info {
+ font-size: 0.8rem;
+ line-height: 1rem;
+ }
+ .confirmation-text {
+ margin: 15px auto;
+ max-width: 80%;
+ font-size: 0.8rem;
+ }
+ .bookButtons {
+ max-width: 80%;
+ margin: auto;
+ display: flex;
+ justify-content: space-around;
+ }
+ .bookButtons button {
+ margin: 0;
+ font-size: 0.8rem;
+ padding: 5px 10px;
+ }
+ .AddBookPage,
+ .bookEditPage,
+ .profileEdit {
+ padding-top: 60px;
+ }
+ .form-group input[type="text"],
+ .form-group input[type="email"],
+ .form-group input[type="file"],
+ .form-group textarea,
+ .form-group select,
+ #location,
+ #genreEdit {
+ width: 80%;
+ }
+ .profile-container {
+ padding: 50px 0;
+ }
+ .book-details {
+ padding: 0 20px 20px 20px;
+ text-align: center;
+ margin-bottom: 20px;
+ }
+ .profile-container {
+ flex-direction: column;
+ }
+ .bookEditButton {
+ display: inline;
+ }
+ .bookEditButton + .bookEditButton {
+ margin-left: 4px;
+ }
+ .bookEditButton button {
+ padding: 4px 10px;
+ }
+ .book-buttons {
+ margin: auto;
+ }
+ .userAvatar {
+ width: 80px;
+ }
+ .user-avatar {
+ padding: 0;
+ }
+ .user-details {
+ display: flex;
+ align-items: center;
+ justify-content: space-evenly;
+ gap: 1rem;
+ padding: 15px;
+ }
+ .profile-list li,
+ .profile-list {
+ margin-top: 0;
+ }
+ .profile-list li {
+ margin: 10px 0;
+ }
+}
diff --git a/readCycle-client/src/assets/bookshelf3.jpg b/readCycle-client/src/assets/bookshelf3.jpg
deleted file mode 100644
index 6d0ee09..0000000
Binary files a/readCycle-client/src/assets/bookshelf3.jpg and /dev/null differ
diff --git a/readCycle-client/src/assets/bookshelf4.png b/readCycle-client/src/assets/bookshelf4.png
deleted file mode 100644
index 8beba85..0000000
Binary files a/readCycle-client/src/assets/bookshelf4.png and /dev/null differ
diff --git a/readCycle-client/src/components/AddBook.jsx b/readCycle-client/src/components/AddBook.jsx
index d64c9a4..9315d19 100644
--- a/readCycle-client/src/components/AddBook.jsx
+++ b/readCycle-client/src/components/AddBook.jsx
@@ -2,7 +2,10 @@ import { useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
-const API_URL = "http://localhost:4005";
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL="https://mern-book-sharing-app.onrender.com"
function AddBook(props) {
const [title, setTitle] = useState("");
diff --git a/readCycle-client/src/components/BookEdit.jsx b/readCycle-client/src/components/BookEdit.jsx
index b866ec4..08a715e 100644
--- a/readCycle-client/src/components/BookEdit.jsx
+++ b/readCycle-client/src/components/BookEdit.jsx
@@ -2,7 +2,10 @@ import { useState, useEffect } from "react";
import axios from "axios";
import { useParams, useNavigate } from "react-router-dom";
-const API_URL = "http://localhost:4005";
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL="https://mern-book-sharing-app.onrender.com"
function BookEdit(props) {
const { bookId } = useParams();
diff --git a/readCycle-client/src/components/Navbar.jsx b/readCycle-client/src/components/Navbar.jsx
index 80affd2..aad157f 100644
--- a/readCycle-client/src/components/Navbar.jsx
+++ b/readCycle-client/src/components/Navbar.jsx
@@ -23,6 +23,7 @@ function Navbar() {
)}
+
{isLoggedIn && (
<>
diff --git a/readCycle-client/src/components/ProfileEdit.jsx b/readCycle-client/src/components/ProfileEdit.jsx
index e3b8e8e..a909bf1 100644
--- a/readCycle-client/src/components/ProfileEdit.jsx
+++ b/readCycle-client/src/components/ProfileEdit.jsx
@@ -4,7 +4,10 @@ import { useNavigate } from "react-router-dom";
import { useContext } from "react";
import { AuthContext } from "../context/auth.context";
-const API_URL = "http://localhost:4005";
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL="https://mern-book-sharing-app.onrender.com"
function ProfileEdit(props) {
const { user } = useContext(AuthContext);
diff --git a/readCycle-client/src/context/auth.context.jsx b/readCycle-client/src/context/auth.context.jsx
index e3b6c25..f5c96d2 100644
--- a/readCycle-client/src/context/auth.context.jsx
+++ b/readCycle-client/src/context/auth.context.jsx
@@ -2,7 +2,12 @@
import React, { useState, useEffect } from "react";
import axios from "axios";
-const API_URL = "http://localhost:4005";
+
+
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL="https://mern-book-sharing-app.onrender.com"
const AuthContext = React.createContext();
@@ -35,6 +40,7 @@ function AuthProviderWrapper(props) {
.then((response) => {
// If the server verifies that the JWT token is valid
const user = response.data;
+ console.log('Value of user:', user);
// Update state variables
setIsLoggedIn(true);
setIsLoading(false);
diff --git a/readCycle-client/src/pages/BookDetailsPage.jsx b/readCycle-client/src/pages/BookDetailsPage.jsx
index 6fe7179..0f13dde 100644
--- a/readCycle-client/src/pages/BookDetailsPage.jsx
+++ b/readCycle-client/src/pages/BookDetailsPage.jsx
@@ -3,7 +3,11 @@ import { useParams, Link, useNavigate } from "react-router-dom";
import axios from "axios";
import { useContext } from "react";
import { AuthContext } from "../context/auth.context";
-const API_URL = "http://localhost:4005";
+
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL = "https://mern-book-sharing-app.onrender.com";
function BookDetailsPage(props) {
const navigate = useNavigate();
@@ -59,7 +63,7 @@ function BookDetailsPage(props) {
}
)
.then((updateResponse) => {
- console.log("Update Response:", updateResponse);
+ console.log("Update Response:", updateResponse);
// Update the 'book' state with the updated book data
setBook({ ...book, booked: true });
})
@@ -93,56 +97,60 @@ function BookDetailsPage(props) {
return (
<>
-
- {book && (
- <>
-
-
-
-
-
{book.title}
-
- by {book.author}
-
-
- Genre:
- {book.genre}
-
-
- Description: {book.description}
-
-
- Language: {book.language}
-
-
- Owner review: {book.review}
-
-
- Book Owner: {book.offeredBy.name}
-
-
-
- Contact e-mail: {book.offeredBy.email}{" "}
-
-
-
- >
- )}
-
-
-
-
handleEmailSent()}>Contact Book Owner
-
+
+
+ {book && (
+ <>
+
+
+
+
+
{book.title}
+
+ by {book.author}
+
+
+ Genre:
+ {book.genre}
+
+
+ Description: {book.description}
+
+
+ Language: {book.language}
+
+
+ Owner review: {book.review}
+
+
+ Book Owner: {book.offeredBy.name}
+
+
+
+ Contact e-mail: {book.offeredBy.email}{" "}
+
+
+
+ >
+ )}
+
+
+
+ handleEmailSent()}>
+ Contact Book Owner
+
+
-
- Back to Library
-
+
+ Back to Library
+
+
+ {success}
- {success}
>
);
}
diff --git a/readCycle-client/src/pages/LibraryPage.jsx b/readCycle-client/src/pages/LibraryPage.jsx
index 19aa137..d28f993 100644
--- a/readCycle-client/src/pages/LibraryPage.jsx
+++ b/readCycle-client/src/pages/LibraryPage.jsx
@@ -7,7 +7,10 @@ import { AuthContext } from "../context/auth.context";
import BookCard from "../components/BookCard";
-const API_URL = "http://localhost:4005";
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL="https://mern-book-sharing-app.onrender.com"
function LibraryPage() {
const [books, setBooks] = useState([]);
diff --git a/readCycle-client/src/pages/LoginPage.jsx b/readCycle-client/src/pages/LoginPage.jsx
index 9ac7875..7ef4dfb 100644
--- a/readCycle-client/src/pages/LoginPage.jsx
+++ b/readCycle-client/src/pages/LoginPage.jsx
@@ -5,11 +5,10 @@ import axios from "axios";
import { Link, useNavigate } from "react-router-dom";
import { AuthContext } from "../context/auth.context"; // <== IMPORT
-const API_URL =
- // when working on local version
- "http://localhost:4005";
-// when working on deployment version
-// link of backend deployment server?
+// when working on local version
+// const API_URL = "http://localhost:4005";
+// deployment
+const API_URL = "https://mern-book-sharing-app.onrender.com";
function LoginPage(props) {
const [email, setEmail] = useState("");
@@ -51,7 +50,7 @@ function LoginPage(props) {
-
readCycle
+
readCycle
Login
{" "}
@@ -167,48 +171,42 @@ function ProfilePage() {
Pending Share Requests
-
+ {user &&
+ user.userOfferedBooks &&
+ user.userOfferedBooks.filter((book) => book.booked === true).length >
+ 0 ? (
- {user ? (
- user.userOfferedBooks
- .filter((book) => book.booked === true) // Filter books with booked property equal to true
- .map((book, index) => (
-
-
-
{" "}
-
-
Title: {book.title}
-
-
Author: {book.author}
-
- {/*
- Edit
- */}
-
handleDeleteBook(book._id)}
- className="bookEditButton"
- >
- Delete
-
-
{" "}
-
+ {user.userOfferedBooks
+ .filter((book) => book.booked === true)
+ .map((book, index) => (
+
+
+
+
+
Title: {book.title}
+
+
Author: {book.author}
+
+
handleDeleteBook(book._id)}
+ className="bookEditButton"
+ >
+ Delete
+
- ))
- ) : (
-
Loading...
- )}
+
+
+ ))}
-
+ ) : (
+
No request yet
+ )}
- {/*
*/}
);
diff --git a/readCycle-server/app.js b/readCycle-server/app.js
index 7af1558..fdf65c7 100644
--- a/readCycle-server/app.js
+++ b/readCycle-server/app.js
@@ -19,6 +19,7 @@ require("./config")(app);
//initialize Nodemailer
const nodemailer = require("nodemailer");
+
// đ Start handling routes here
const indexRoutes = require("./routes/index.routes");
app.use("/api", indexRoutes);
diff --git a/readCycle-server/config/index.js b/readCycle-server/config/index.js
index db1247e..22eba14 100644
--- a/readCycle-server/config/index.js
+++ b/readCycle-server/config/index.js
@@ -17,12 +17,16 @@ const cors = require("cors"); // <== IMPORT
// Middleware configuration
module.exports = (app) => {
// Because this is a server that will accept requests from outside and it will be hosted ona server with a `proxy`, express needs to know that it should trust that setting.
- // Services like heroku use something called a proxy and you need to add this to your server
app.set("trust proxy", 1);
app.use(
cors({
- origin: [process.env.CLIENT_URL],
+ origin: "https://readcycle.netlify.app",
+ methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
+ credentials: true,
+ optionsSuccessStatus: 204,
+ // origin:'*',
+ // origin: [process.env.CLIENT_URL],
})
);
@@ -37,6 +41,7 @@ module.exports = (app) => {
// In development environment the app logs
app.use(logger("dev"));
+
// Handles access to the public folder
app.use(express.static(path.join(__dirname, "..", "public")));
@@ -44,4 +49,8 @@ module.exports = (app) => {
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
+
+ app.options('*', cors());
+
};
+
diff --git a/readCycle-server/db/index.js b/readCycle-server/db/index.js
index 981a456..52309d9 100644
--- a/readCycle-server/db/index.js
+++ b/readCycle-server/db/index.js
@@ -5,16 +5,35 @@ const mongoose = require("mongoose");
// âšī¸ Sets the MongoDB URI for our app to have access to it.
// If no env has been set, we dynamically set it to whatever the folder name was upon the creation of the app
-const MONGO_URI =
- process.env.MONGODB_URI || "mongodb://127.0.0.1:27017/readcycle-server";
-
-mongoose
- .connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
- .then((x) => {
- console.log(
- `Connected to Mongo! Database name: "${x.connections[0].name}"`
- );
- })
- .catch((err) => {
- console.error("Error connecting to mongo: ", err);
- });
+const uri =
+ process.env.MONGODB_URI || "mongodb+srv://test_user:n0PgwB3ipaZWCYJh@cluster0.dmxynb3.mongodb.net/readCycle-server?retryWrites=true&w=majority"
+
+ // "mongodb://127.0.0.1:27017/readcycle-server";
+
+ // mongodb+srv://test_user:n0PgwB3ipaZWCYJh@cluster0.dmxynb3.mongodb.net/readCycle-server?retryWrites=true&w=majority
+
+ // mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
+
+// mongoose
+// .connect(uri, { useNewUrlParser: true, useUnifiedTopology: true })
+// .then((x) => {
+// console.log(
+// `Connected to Mongo! Database name: "${x.connections[0].name}"`
+// );
+// })
+// .catch((err) => {
+// console.error("Error connecting to mongo: ", err);
+// });
+
+
+mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
+
+const dbConnection = mongoose.connection;
+
+dbConnection.once("open", () => {
+ console.log(`Connected to Mongo! Database name: "${dbConnection.name}"`);
+});
+
+dbConnection.on("error", (err) => {
+ console.error("Error connecting to MongoDB:", err);
+});
diff --git a/readCycle-server/models/User.model.js b/readCycle-server/models/User.model.js
index b8ebdff..f875f78 100644
--- a/readCycle-server/models/User.model.js
+++ b/readCycle-server/models/User.model.js
@@ -10,6 +10,7 @@ const userSchema = new Schema({
email: {
type: String,
lowercase: true,
+ index: true,
required: [true, "â Email can't be blank"],
match: [/\S+@\S+\.\S+/, "is invalid"],
},
diff --git a/readCycle-server/routes/auth.routes.js b/readCycle-server/routes/auth.routes.js
index 1fef82f..a829ebb 100644
--- a/readCycle-server/routes/auth.routes.js
+++ b/readCycle-server/routes/auth.routes.js
@@ -4,7 +4,7 @@ const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/User.model");
-const {isAuthenticated} = require("../middleware/jwt.middleware"); // <== IMPORT
+const { isAuthenticated } = require("../middleware/jwt.middleware"); // <== IMPORT
const saltRounds = 10;
// POST /auth/signup - Creates a new user in the database
@@ -37,6 +37,7 @@ router.post("/signup", (req, res, next) => {
// Check the users collection if a user with the same email already exists
User.findOne({ email })
.then((foundUser) => {
+ console.log(`Found User Query: ${foundUser}`);
// If the user with the same email already exists, send an error response
if (foundUser) {
res.status(400).json({ message: "User already exists." });
@@ -63,7 +64,7 @@ router.post("/signup", (req, res, next) => {
res.status(201).json({ user: user });
})
.catch((err) => {
- console.log(err);
+ console.error("Error finding user:", err);
res.status(500).json({ message: "Internal Server Error" });
});
});
diff --git a/readCycle-server/routes/email.routes.js b/readCycle-server/routes/email.routes.js
index 6dcf40b..48b0d1f 100644
--- a/readCycle-server/routes/email.routes.js
+++ b/readCycle-server/routes/email.routes.js
@@ -21,9 +21,9 @@ const sendVerificationEmail = (
res
) => {
// When working on local version
- const baseUrl = "http://localhost:4005";
+ // const baseUrl = "http://localhost:4005";
// When working on deployment version
- // ??
+ const baseURL = "https://mern-book-sharing-app.onrender.com"
// mail options !! no need token !
const mailOptions = {