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}{" "} - -

-
- - )} -
-
- - - +
+
+ {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}{" "} + +

+
+ + )} +
+
+ + + - - - + + + +
+ {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

diff --git a/readCycle-client/src/pages/SignupPage.jsx b/readCycle-client/src/pages/SignupPage.jsx index b609162..f4b7bc3 100644 --- a/readCycle-client/src/pages/SignupPage.jsx +++ b/readCycle-client/src/pages/SignupPage.jsx @@ -4,7 +4,10 @@ import { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import axios from "axios"; -const API_URL = "http://localhost:4005"; +// local + // const API_URL = "http://localhost:4005"; + // deployment + const API_URL="https://mern-book-sharing-app.onrender.com" function SignupPage(props) { const [email, setEmail] = useState(""); diff --git a/readCycle-client/src/pages/UserProfilePage.jsx b/readCycle-client/src/pages/UserProfilePage.jsx index 3d7038a..104c028 100644 --- a/readCycle-client/src/pages/UserProfilePage.jsx +++ b/readCycle-client/src/pages/UserProfilePage.jsx @@ -11,7 +11,9 @@ function ProfilePage() { const [needsReloads, setNeedsReloads] = useState(true); const [booksOfferedScore, setBooksOfferedScore] = useState(0); - const API_URL = "http://localhost:4005"; + // const API_URL = "http://localhost:4005"; + // deployment + const API_URL = "https://mern-book-sharing-app.onrender.com"; const navigate = useNavigate(); //gamify @@ -139,18 +141,20 @@ function ProfilePage() {

Title: {book.title}

Author: {book.author}

- - Edit - - +
+ + Edit + + +
{" "}

@@ -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) => ( -
-
- book cover{" "} -

- Title: {book.title} -
- Author: {book.author} -
- {/* - Edit - */} - -
{" "} -

+ {user.userOfferedBooks + .filter((book) => book.booked === true) + .map((book, index) => ( +
+
+ book cover +
+ Title: {book.title} +
+ Author: {book.author} +
+
- )) - ) : ( -

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 = {