Horje
Social Networking Platform with MERN Stack

Social networking platforms are an important part of our lives, connecting people from all over the world. Building such platforms requires a robust technology stack that can handle a large number of users and real-time interactions. This article will guide you through the process of building a social networking platform using the MERN stack.

The social networking platform will allow users to register, log in, create posts, like and comment on posts, and follow other users. The platform will have a user-friendly interface and real-time updates

Project Preview:

jpeg-optimizer_Screenshot-2024-07-25-161318

Social Networking Platform with MERN Stack

Prerequisites

Functionalities

  • User Authentication: Registration, login, and logout
  • User Profiles: View and edit profile details
  • Posts: Create, edit, delete posts
  • Interactions: Like and comment on posts
  • Follow System: Follow and unfollow users
  • Real-Time Updates: Real-time notifications for new likes, comments, and follows

Steps to Create Social Networking Platform

Step 1: Create a new directory for the project.

mkdir backend
cd backend

Step 2 : Initialize a new Node.js project.

npm init -y

Step 3 : Install required dependencies

npm install bcryptjs cors dotenv express jsonwebtoken mongoose multer

Step 4: Create and Open your .env file and paste below code

MONGO_URI=mongodb://127.0.0.1:27017/socialnetwork
JWT_SECRET=gfg@socialNetworkProject
PORT=5000

Dependencies

"dependencies": {
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.19.2",
    "jsonwebtoken": "^9.0.2",
    "mongoose": "^8.4.4",
    "multer": "^1.4.5-lts.1"
  }

Project Structure (Backend)

Screenshot-2024-07-29-155905

project structure (backend)

Example: Backend code for Social Networking Platform

JavaScript
//backend/server.js

const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const cors = require('cors');
const path = require('path');
const authMiddleware = require('./middleware/authMiddleware');
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
const postRoutes = require('./routes/posts');
const commentRoutes = require('./routes/comments');

dotenv.config();

const app = express();
app.use(express.json());
app.use(cors({
    origin: '*'
}))

mongoose.connect(process.env.MONGO_URI, {
}).then(() => console.log('MongoDB connected'))
    .catch(err => console.error(err));
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
app.use('/api/auth', authRoutes);
app.use('/api/users', authMiddleware, userRoutes);
app.use('/api/posts', authMiddleware, postRoutes);
app.use('/api/post/comments', authMiddleware, commentRoutes);

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
JavaScript
//backend/controllers/authController.js

const User = require('../models/User');
const jwt = require('jsonwebtoken');

const generateToken = (id) => {
    return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d' });
};

exports.registerUser = async (req, res) => {
    const { username, email, password } = req.body;
    try {
        const user = new User({ username, email, password });
        await user.save();
        res.status(201).json({ message: 'Register successfully...please log-in ' });
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
};

exports.loginUser = async (req, res) => {
    const { email, password } = req.body;
    try {
        const user = await User.findOne({ email });
        if (user && await user.matchPassword(password)) {
            res.json({ token: generateToken(user._id), user_id: user._id });
        } else {
            res.status(401).json({ message: 'Invalid email or password' });
        }
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};
JavaScript
//backend/controllers/commentController.js

const Comment = require("../models/Comment");
const Post = require("../models/Post");
const User = require("../models/User");

exports.createComment = async (req, res) => {
    try {
        const { postId, content } = req.body;
        const user_data = await User.findById(req.user.id);

        const comment = new Comment({
            user: req.user.id,
            username: user_data.username,
            post: postId,
            content,
        });
        await comment.save();

        const post = await Post.findById(postId);
        post.comments.push(comment._id);
        await post.save();

        res.status(201).json(comment);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
};

exports.getComments = async (req, res) => {
    try {
        const comments = await Comment.find({ post: req.params.postId })
            .populate("user", "username")
            .sort({ createdAt: -1 });
        res.json(comments);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};

exports.deleteComment = async (req, res) => {
    try {
        const comment = await Comment.findById(req.params.id);
        if (comment.user.toString() === req.user.id) {
            await comment.remove();
            res.json({ message: "Comment removed" });
        } else {
            res.status(401).json({ message: "Not authorized" });
        }
    } catch (error) {
        res.status(404).json({ message: "Comment not found" });
    }
};
JavaScript
//backend/controllers/postController.js

const Post = require('../models/Post');
const User = require('../models/User');
const multer = require('multer');
const path = require('path');

// Set up multer for file uploads
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/');
    },
    filename: function (req, file, cb) {
        cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
    }
});

const upload = multer({ storage: storage }).single('image');

exports.createPost = async (req, res) => {
    upload(req, res, async function (err) {
        if (err instanceof multer.MulterError) {
            return res.status(500).json({ message: err.message });
        } else if (err) {
            return res.status(500).json({ message: err.message });
        }
        let Userdata = await User.findById(req.user.id);
        try {
            const post = new Post({
                user: req.user.id,
                username: Userdata.username,
                content: req.body.content,
                image: req.file ? req.file.filename : null

            });
            await post.save();
            res.status(201).json(post);
        } catch (error) {
            res.status(400).json({ message: error.message });
        }
    });
};

exports.getPosts = async (req, res) => {
    try {
        const posts = await Post.find()
            .populate('user', 'username')
            .populate('comments')
            .sort({ createdAt: -1 });
        res.json(posts);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};
exports.getUserPost = async (req, res) => {
    const userId = req.user.id;

    try {
        const posts = await Post.find({ user: userId })
            .populate('user', 'username')
            .populate('comments')
            .sort({ createdAt: -1 });

        res.json(posts);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
};

exports.updatePost = async (req, res) => {
    try {
        const post = await Post.findById(req.params.id);
        if (!post) {
            return res.status(404).json({ message: 'Post not found' });
        }
        if (post.user.toString() !== req.user.id) {
            return res.status(401).json({ message: 'Not authorized' });
        }
        post.content = req.body.content;
        await post.save();
        res.json(post);
    } catch (error) {
        res.status(500).json({ message: 'Internal Server Error' });
    }
};

exports.deletePost = async (req, res) => {
    try {
        const post = await Post.findById(req.params.id);
        if (!post) {
            return res.status(404).json({ message: 'Post not found' });
        }
        if (post.user.toString() !== req.user.id) {
            return res.status(401).json({ message: 'Not authorized' });
        }
        await post.deleteOne();
        res.json({ message: 'Post removed' });
    } catch (error) {
        res.status(500).json({ message: 'Internal Server Error' });
    }
};

exports.likePost = async (req, res) => {
    const postId = req.params.id;
    const userId = req.user.id;

    try {
        const post = await Post.findById(postId);

        if (!post) {
            return res.status(404).json({ message: 'Post not found' });
        }

        if (post.likedBy.includes(userId)) {
            return res.status(400).json({ message: 'You have already liked this post' });
        }

        post.likes++;
        post.likedBy.push(userId);
        await post.save();

        res.status(200).json({ message: 'Post liked successfully', post });
    } catch (error) {
        console.error(error);
        res.status(500).json({ message: 'Internal Server Error' });
    }
};
JavaScript
//backend/controllers/userController.js

const User = require("../models/User");

exports.getUserProfile = async (req, res) => {
    try {
        const user = await User.findById(req.user.id).select("-password");
        console.log(user);
        res.json(user);
    } catch (error) {
        res.status(404).json({ message: "User not found" });
    }
};
exports.getAllUserProfile = async (req, res) => {
    try {
        const users = await User.find({ _id: { $ne: req.user.id } }).select(
            "-password"
        );
        console.log("users");
        res.json(users);
    } catch (error) {
        res.status(500).json({ message: "Server error" });
    }
};

exports.updateUserProfile = async (req, res) => {
    const { username, email } = req.body;
    try {
        const user = await User.findById(req.user.id);
        if (user) {
            user.username = username || user.username;
            user.email = email || user.email;
            await user.save();

            res.json(user);
        } else {
            res.status(404).json({ message: "User not found" });
        }
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
};
JavaScript
//backend/middleware/authMiddleware.js

const jwt = require("jsonwebtoken");
const User = require("../models/User");

const authMiddleware = async (req, res, next) => {
    const token = req.header("Authorization")?.replace("Bearer ", "");
    if (!token) {
        return res.status(401).json({ message: "No token, authorization denied" });
    }

    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = await User.findById(decoded.id).select("-password");
        next();
    } catch (error) {
        res.status(401).json({ message: "Token is not valid" });
    }
};

module.exports = authMiddleware;
JavaScript
//backend/models/Comment.js

const mongoose = require("mongoose");

const CommentSchema = new mongoose.Schema(
    {
        user: { type: mongoose.Schema.Types.ObjectId, required: true },
        post: { type: mongoose.Schema.Types.ObjectId, required: true },
        username: { type: String, required: true },
        content: { type: String, required: true },
    },
    { timestamps: true }
);

const Comment = mongoose.model("Comment", CommentSchema);
module.exports = Comment;
JavaScript
//backend/models/Post.js

const mongoose = require("mongoose");

const postSchema = new mongoose.Schema({
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User",
        required: true,
    },
    username: {
        type: String,
        required: true,
    },
    content: {
        type: String,
        required: true,
    },
    likes: {
        type: Number,
        default: 0,
    },
    likedBy: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: "User",
        },
    ],
    image: { type: String },
    comments: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Comment",
        },
    ],
    createdAt: {
        type: Date,
        default: Date.now,
    },
});

const Post = mongoose.model("Post", postSchema);
module.exports = Post;
JavaScript
//backend/models/User.js

const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");

const UserSchema = new mongoose.Schema({
    username: { type: String, required: true, unique: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
    token: { type: String, required: true },
});

UserSchema.pre("save", async function (next) {
    if (!this.isModified("password")) {
        return next();
    }
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
});

UserSchema.methods.matchPassword = async function (enteredPassword) {
    return await bcrypt.compare(enteredPassword, this.password);
};

const User = mongoose.model("User", UserSchema);
module.exports = User;
JavaScript
//backend/routes/auth.js
const express = require('express');
const { registerUser, loginUser } = require('../controllers/authController');

const router = express.Router();

router.post('/register', registerUser);
router.post('/login', loginUser);

module.exports = router;
JavaScript
//backend/routes/comments.js

const express = require("express");
const {
    createComment,
    getComments,
    deleteComment,
} = require("../controllers/commentController");
const authMiddleware = require("../middleware/authMiddleware");

const router = express.Router();

router.post("/:postId/comment", authMiddleware, createComment);
router.get("/:postId", getComments);
router.delete("/:id", authMiddleware, deleteComment);

module.exports = router;
JavaScript
//backend/routes/posts.js

const express = require("express");
const {
    createPost,
    getPosts,
    getUserPost,
    updatePost,
    deletePost,
    likePost,
} = require("../controllers/postController");
const authMiddleware = require("../middleware/authMiddleware");

const router = express.Router();

router.post("/", authMiddleware, createPost);
router.get("/", getPosts);
router.get("/userpost", getUserPost);
router.put("/:id", authMiddleware, updatePost);
router.delete("/:id", authMiddleware, deletePost);

router.post("/:id/like", authMiddleware, likePost);

module.exports = router;
JavaScript
//backend/routes/users.js

const express = require("express");
const {
    getUserProfile,
    updateUserProfile,
    getAllUserProfile,
} = require("../controllers/userController");
const authMiddleware = require("../middleware/authMiddleware");

const router = express.Router();

router.get("/profile", authMiddleware, getUserProfile);
router.get("/allusers/profile", authMiddleware, getAllUserProfile);
router.put("/profile", authMiddleware, updateUserProfile);

module.exports = router;


Frontend Development

  • Initialize a new React project
  • Set up Redux for state management
  • Create components for registration, login, user profile, post feed, and post details
  • Implement API calls using Axios

Step 1 : Initialize a new React project

npx create-react-app frontend
cd frontend 

Step 2 : Install required dependencies

npm install react-redux redux redux-thunk redux-devtools-extension @headlessui/react @heroicons/react axios date-fns –legacy-peer-deps

npm install -D tailwindcss

npx tailwindcss init

Dependencies

"dependencies": {
    "@headlessui/react": "^2.1.1",
    "@heroicons/react": "^2.1.4",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.2",
    "date-fns": "^3.6.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-redux": "^9.1.2",
    "react-router-dom": "^6.23.1",
    "react-scripts": "5.0.1",
    "redux": "^5.0.1",
    "redux-devtools-extension": "^2.13.9",
    "redux-thunk": "^3.1.0",
    "web-vitals": "^2.1.4"
  }

Updated tailwind.config.js


/** @type {import('tailwindcss').Config} */
module.exports = {
    content: [
        "./src/**/*.{js,jsx,ts,tsx}",
  ],
    theme: {
        extend: {},
  },
    plugins: [],
} 

Project Structure (Frontend)

Screenshot-2024-07-02-150050

project structure (frontend)

Integrate Frontend and Backend

  • Ensure the frontend communicates with the backend using REST APIs
  • Handle user authentication and protected routes in React

Styling and UI Enhancements

  • Use CSS, Bootstrap, Tailwind and Material-UI for styling
  • Create a responsive design for different screen sizes

Example: Complete code for frontend

CSS
/* frontend/src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}
JavaScript
//frontend/src/App.js
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./components/Login";
import Register from "./components/Register";
import Profile from "./components/Profile";
import Feed from "./components/Feed";
import PrivateRoute from "./components/PrivateRoute";
import Nav from "./components/Nav";
import Sidebarprofile from "./components/Sidebarprofile";
import AllProfileShow from "./components/AllProfileShow";
import ErrorNotification from "./components/ErrorNotification";
const FeedPage = () => {
    return (
        <>
            <Nav />
            <div className=" grid grid-cols-1 sm:flex flex-row p-4 justify-between ">
                <Sidebarprofile />
                <Feed />
                <AllProfileShow />
            </div>
        </>
    );
};

export default function App() {
    return (
        <>

            <ErrorNotification />
            <div className="bg-[#f4f2ee]">
                <Router>
                    <Routes>
                        {/* Public routes */}

                        <Route path="/login" element={<Login />} />
                        <Route path="/register" element={<Register />} />

                        {/* Private routes */}

                        <Route path="/" element={<PrivateRoute />}>
                            <Route path="/profile" element={<Profile />} />
                            <Route path="/feed" element={<FeedPage />} />
                        </Route>
                    </Routes>
                </Router>
            </div>
        </>
    );
}
JavaScript
//frontend/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux/store';
import App from './App';
import './index.css';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
);
JavaScript
//frontend/src/redux/actions/authActions.js
import axios from "axios";
import { setError, clearError } from "./errorActions";

export const registerUser = (userData) => async (dispatch) => {
    try {
        dispatch({ type: "USER_REGISTER_REQUEST" });

        const config = {
            headers: { "Content-Type": "application/json" },
        };

        const { data } = await axios.post(
            "http://localhost:5000/api/auth/register",
            userData,
            config
        );
        dispatch(setError(data.message));
    } catch (error) {
        dispatch({
            type: "USER_REGISTER_FAIL",
            payload:
                error.response && error.response.data.message
                    ? error.response.data.message
                    : error.message,
        });
    }
};

export const loginUser = (userData) => async (dispatch) => {
    try {
        dispatch({ type: "USER_LOGIN_REQUEST" });

        const config = {
            headers: { "Content-Type": "application/json" },
        };

        const { data } = await axios.post(
            "http://localhost:5000/api/auth/login",
            userData,
            config
        );

        dispatch({ type: "USER_LOGIN_SUCCESS", payload: data });
        localStorage.setItem("userInfo", JSON.stringify(data));
        dispatch(clearError());
    } catch (error) {
        dispatch({
            type: "USER_LOGIN_FAIL",
            payload:
                error.response && error.response.data.message
                    ? error.response.data.message
                    : error.message,
        });
        dispatch(setError(error.response?.data?.message || error.message));
    }
};

export const logoutUser = () => (dispatch) => {
    localStorage.removeItem("userInfo");
    dispatch({ type: "USER_LOGOUT" });
};
JavaScript
//frontend/src/redux/actions/errorActions.js
import { SET_ERROR, CLEAR_ERROR } from './types';

export const setError = (error) => ({
    type: SET_ERROR,
    payload: error,
});

export const clearError = () => ({
    type: CLEAR_ERROR,
});
JavaScript
//frontend/src/redux/actions/postActions.js
import axios from "axios";
import { setError, clearError } from "./errorActions";
// Action Types
export const GET_POSTS = "GET_POSTS";
export const CREATE_POST = "CREATE_POST";
export const DELETE_POST = "DELETE_POST";
export const LIKE_POST = "LIKE_POST";
export const COMMENT_ON_POST = "COMMENT_ON_POST";

const getTokenConfig = () => {
    const tokenObj = localStorage.getItem("userInfo");
    return {
        headers: {
            Authorization: `Bearer ${JSON.parse(tokenObj).token}`,
        },
    };
};

export const getPosts = () => async (dispatch) => {
    try {
        const response = await axios.get(
            "http://localhost:5000/api/posts",
            getTokenConfig()
        );
        dispatch({ type: GET_POSTS, payload: response.data });
        dispatch(clearError());
    } catch (error) {
        console.error("Error fetching posts:", error);
        dispatch(setError(error.response?.data?.message || error.message));
    }
};
export const getUserPost = () => async (dispatch) => {
    try {
        const response = await axios.get(
            "http://localhost:5000/api/posts/userpost",
            getTokenConfig()
        );
        dispatch({ type: GET_POSTS, payload: response.data });
        dispatch(clearError());
    } catch (error) {
        console.error("Error fetching posts:", error);
        dispatch(setError(error.response?.data?.message || error.message));
    }
};
export const createPost = (formData) => async (dispatch) => {
    try {
        const config = {
            headers: {
                "Content-Type": "multipart/form-data",
                Authorization: `Bearer ${JSON.parse(localStorage.getItem("userInfo")).token
                    }`,
            },
        };
        const response = await axios.post(
            "http://localhost:5000/api/posts",
            formData,
            config
        );
        dispatch({ type: CREATE_POST, payload: response.data });
        dispatch(setError("Post added!!!!"));
    } catch (error) {
        console.error("Error creating post:", error);
        dispatch(setError(error.response?.data?.message || error.message));
    }
};

export const deletePost = (id) => async (dispatch) => {
    try {
        const response = await axios.delete(
            `http://localhost:5000/api/posts/${id}`,
            getTokenConfig()
        );
        dispatch({ type: DELETE_POST, payload: id });
        dispatch(setError(response.data.message));
    } catch (error) {
        console.error("Error deleting post:", error);
        dispatch(setError(error.response?.data?.message || error.message));
    }
};

export const likePost = (id) => async (dispatch) => {
    try {
        const response = await axios.post(
            `http://localhost:5000/api/posts/${id}/like`,
            {},
            getTokenConfig()
        );
        dispatch({ type: LIKE_POST, payload: response.data });
        console.log(response.data);
        dispatch(setError(response.data.message));
    } catch (error) {
        console.error("Error liking post:", error);
        dispatch(setError(error.response?.data?.message || error.message));
    }
};

export const commentOnPost = (postId, content) => async (dispatch) => {
    try {
        const response = await axios.post(
            `http://localhost:5000/api/post/comments/${postId}/comment`,
            { postId, content },
            getTokenConfig()
        );
        dispatch({ type: COMMENT_ON_POST, payload: response.data });
        dispatch(setError("Comment added!!!"));
    } catch (error) {
        console.error("Error commenting on post:", error);
        dispatch(setError(error.response?.data?.message || error.message));
    }
};
JavaScript
//frontend/src/redux/actions/types.js
// Post action types
export const GET_POSTS = 'GET_POSTS';
export const CREATE_POST = 'CREATE_POST';
export const DELETE_POST = 'DELETE_POST';
export const LIKE_POST = 'LIKE_POST';
export const COMMENT_ON_POST = 'COMMENT_ON_POST';

// User action types
export const FETCHING_USER = 'FETCHING_USER';
export const FETCH_USER_PROFILE_FAIL='FETCH_USER_PROFILE_FAIL';
export const FETCH_USER_PROFILE_SUCCESS='FETCH_USER_PROFILE_SUCCESS';
export const FETCH_USER_PROFILE_REQUEST='FETCH_USER_PROFILE_REQUEST';
export const UPDATE_USER_PROFILE = 'UPDATE_USER_PROFILE';
export const UPDATE_USER_PROFILE_SUCCESS='UPDATE_USER_PROFILE_SUCCESS';
export const USER_REGISTER_FAIL='USER_REGISTER_FAIL';
export const USER_REGISTER_SUCCESS='USER_REGISTER_SUCCESS';
export const USER_REGISTER_REQUEST='USER_REGISTER_REQUEST';
export const USER_LOGIN_SUCCESS='USER_LOGIN_SUCCESS';
export const USER_LOGIN_FAIL='USER_LOGIN_FAIL';
export const USER_LOGIN_REQUEST='USER_LOGIN_REQUEST';
export const FETCHING_ALL_PROFILE_REQUEST='FETCHING_ALL_PROFILE_REQUEST';
export const FETCHING_ALL_PROFILE_FAIL='FETCHING_ALL_PROFILE_FAIL';
export const FETCHING_ALL_PROFILE_SUCCESS='FETCHING_ALL_PROFILE_SUCCESS';
export const FETCHING_ALL_PROFILE='FETCHING_ALL_PROFILE';
export const SET_ERROR = 'SET_ERROR';
export const CLEAR_ERROR = 'CLEAR_ERROR';
JavaScript
//frontend/src/redux/actions/userActions.js
import axios from "axios";
import {
    FETCH_USER_PROFILE_REQUEST,
    FETCH_USER_PROFILE_SUCCESS,
    FETCH_USER_PROFILE_FAIL,
    UPDATE_USER_PROFILE_SUCCESS,
    FETCHING_ALL_PROFILE_REQUEST,
    FETCHING_ALL_PROFILE_FAIL,
    FETCHING_ALL_PROFILE_SUCCESS,
} from "./types";

// Action to fetch user profile
export const fetchUserProfile = () => async (dispatch) => {
    const token = JSON.parse(localStorage.getItem("userInfo"))?.token;

    try {
        dispatch({ type: FETCH_USER_PROFILE_REQUEST });

        const config = {
            headers: {
                Authorization: `Bearer ${token}`,
            },
        };

        const res = await axios.get(
            "http://localhost:5000/api/users/profile",
            config
        );

        dispatch({
            type: FETCH_USER_PROFILE_SUCCESS,
            payload: res.data,
        });
    } catch (err) {
        dispatch({
            type: FETCH_USER_PROFILE_FAIL,
            payload: err.response?.data?.message || err.message,
        });
    }
};

// Action to update user profile
export const updateUserProfile = (userData) => async (dispatch) => {
    const token = JSON.parse(localStorage.getItem("userInfo"))?.token;

    try {
        const config = {
            headers: {
                Authorization: `Bearer ${token}`,
            },
        };

        const res = await axios.put(
            "http://localhost:5000/api/users/profile",
            userData,
            config
        );

        dispatch({
            type: UPDATE_USER_PROFILE_SUCCESS,
            payload: res.data,
        });
    } catch (err) {
        console.error(err);
    }
};

//Action to fetch all user profile
export const fetchAllUserProfile = () => async (dispatch) => {
    const token = JSON.parse(localStorage.getItem("userInfo"))?.token;

    try {
        dispatch({ type: FETCHING_ALL_PROFILE_REQUEST });

        const config = {
            headers: {
                Authorization: `Bearer ${token}`,
            },
        };

        const res = await axios.get(
            "http://localhost:5000/api/users/allusers/profile",
            config
        );
        console.log(res);
        dispatch({
            type: FETCHING_ALL_PROFILE_SUCCESS,
            payload: res.data,
        });
    } catch (err) {
        dispatch({
            type: FETCHING_ALL_PROFILE_FAIL,
            payload: err.response?.data?.message || err.message,
        });
    }
};
JavaScript
//frontend/src/redux/reducers/allProfileReducer.js
import {
    FETCHING_ALL_PROFILE_REQUEST,
    FETCHING_ALL_PROFILE_SUCCESS,
    FETCHING_ALL_PROFILE_FAIL,
} from "../actions/types";

const initialState = {
    profile: null,
    loading: false,
    error: null,
};

const userProfileReducer = (state = initialState, action) => {
    switch (action.type) {
        case FETCHING_ALL_PROFILE_REQUEST:
            return { ...state, loading: true, error: null };
        case FETCHING_ALL_PROFILE_SUCCESS:
            console.log(action.payload);
            return { ...state, loading: false, profile: action.payload };
        case FETCHING_ALL_PROFILE_FAIL:
            return { ...state, loading: false, error: action.payload };
        default:
            return state;
    }
};

export default userProfileReducer;
JavaScript
//frontend/src/redux/reducers/authReducer.js
import {
    USER_REGISTER_REQUEST,
    USER_REGISTER_SUCCESS,
    USER_REGISTER_FAIL,
    FETCH_USER_PROFILE_REQUEST,
    FETCH_USER_PROFILE_SUCCESS,
    FETCH_USER_PROFILE_FAIL,
    UPDATE_USER_PROFILE_SUCCESS,
    USER_LOGIN_REQUEST,
    USER_LOGIN_SUCCESS,
} from "../actions/types";

const initialState = {
    userInfo: JSON.parse(localStorage.getItem("userInfo")) || null,
    loading: false,
    error: null,
};

const authReducer = (state = initialState, action) => {
    switch (action.type) {
        case USER_REGISTER_REQUEST:
        case FETCH_USER_PROFILE_REQUEST:
            return { ...state, loading: true };

        case FETCH_USER_PROFILE_SUCCESS:
            return { ...state, loading: false, userInfo: action.payload };
        case USER_REGISTER_FAIL:
        case FETCH_USER_PROFILE_FAIL:
            return { ...state, loading: false, error: action.payload };
        case UPDATE_USER_PROFILE_SUCCESS:
            return { ...state, userInfo: action.payload };
        case USER_LOGIN_SUCCESS:
            return { ...state, userInfo: action.payload };
        case "USER_LOGOUT":
            return {};
        default:
            return state;
    }
};

export default authReducer;
JavaScript
//frontend/src/redux/reducers/errorReducer.js
import { SET_ERROR, CLEAR_ERROR } from "../actions/types";

const initialState = null;

const errorReducer = (state = initialState, action) => {
    switch (action.type) {
        case SET_ERROR:
            return action.payload;
        case CLEAR_ERROR:
            return null;
        default:
            return state;
    }
};

export default errorReducer;
JavaScript
// frontend/src/redux/reducers/index.js
import { combineReducers } from "redux";
import postReducer from "./postReducer";
import authReducer from "./authReducer";
import allProfileReducer from "./allProfileReducer";
import errorReducer from "./errorReducer";

const rootReducer = combineReducers({
    posts: postReducer,
    auth: authReducer,
    allUserProfile: allProfileReducer,
    error: errorReducer,
});

export default rootReducer;
JavaScript
// frontend/src/redux/reducers/postReducer.js
import {
    GET_POSTS,
    CREATE_POST,
    DELETE_POST,
    LIKE_POST,
    COMMENT_ON_POST,
} from "../actions/postActions";

const initialState = [];

const postReducer = (state = initialState, action) => {
    switch (action.type) {
        case GET_POSTS:
            return action.payload;
        case CREATE_POST:
            return [action.payload, ...state];
        case DELETE_POST:
            return state.filter((post) => post._id !== action.payload);
        case LIKE_POST:
            return state.map((post) =>
                post._id === action.payload._id ? action.payload : post
            );
        case COMMENT_ON_POST:
            return state.map((post) =>
                post._id === action.payload._id ? action.payload : post
            );
        default:
            return state;
    }
};

export default postReducer;
JavaScript
//frontend/src/redux/store.js
import { createStore, applyMiddleware, compose } from "redux";
import { thunk } from "redux-thunk";
import rootReducer from "./reducers"; 

const middleware = [thunk];

const composeEnhancers =
    (typeof window !== "undefined" &&
        window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
    compose;

const store = createStore(
    rootReducer,
    composeEnhancers(applyMiddleware(...middleware))
);

export default store;
JavaScript
//frontend/src/components/AllProfileShow.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchAllUserProfile } from "../redux/actions/userActions"; 

const AllProfileShow = () => {
    const dispatch = useDispatch();
    const userProfile = useSelector((state) => state.allUserProfile.profile);

    useEffect(() => {
        dispatch(fetchAllUserProfile());
    }, [dispatch]);
    if (!Array.isArray(userProfile)) {
        return <div className="mt-20">No connection available</div>;
    }

    return (
        <div className="bg-white my-20  pb-6 rounded-lg shadow-sm grid 
        grid-cols-2 w-[30%]  h-[15%]  overflow-y-scroll items-baseline ">
            {userProfile.map((profiles) => (
                <div>
                    <UsersCard key={profiles._id} userName={profiles.username} />
                </div>
            ))}
        </div>
    );
};
const UsersCard = ({ key, userName }) => {
    return (
        <div className="mt-4">
            <div className=" shadow  h-24 w-24 mx-auto border-white 
            rounded-full overflow-hidden border-4">
                <img
                    className="object-cover w-full h-full"
                    src="https://images.unsplash.com/photo-1438761681033-
                    6461ffad8d80?ixlib=rb-1.2.
                    1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=80"
                />
            </div>
            <div className="flex flex-col justify-center gap-3">
                <h1 className="text-lg text-center font-semibold">
                    <p className="uppercase    ">{userName}</p>
                </h1>
                <p className="text-sm text-gray-600 text-center">
                    <a
                        class="px-6 py-2 min-w-[120px] text-center text-white
                        bg-violet-600 border border-violet-600 
                        rounded active:text-violet-500 hover:bg-transparent 
                        hover:text-violet-600 focus:outline-none focus:ring"
                        href="#"
                    >
                        Follow
                    </a>
                </p>
            </div>
        </div>
    );
};

export default AllProfileShow;
JavaScript
//frontend/src/components/ErrorNotification.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { clearError } from "../redux/actions/errorActions";

const ErrorNotification = () => {
    const error = useSelector((state) => state.error);
    const dispatch = useDispatch();

    if (!error) return null;

    return (
        <>
            <div class="bg-indigo-900 text-center py-4 lg:px-4 fixed bottom-10 z-30 right-0">
                <div
                    class="p-2 bg-indigo-800 items-center text-indigo-100 leading-none
                     lg:rounded-full flex lg:inline-flex"
                    role="alert"
                >
                    <span class="flex rounded-full bg-indigo-500 uppercase
                     px-2 py-1 text-xs font-bold mr-3">
                        New
                    </span>
                    <span class="font-semibold mr-2 text-left flex-auto">{error}</span>
                    <svg
                        onClick={() => dispatch(clearError())}
                        class="fill-current opacity-75 h-4 w-4 cursor-pointer"
                        xmlns="http://www.w3.org/2000/svg"
                        viewBox="0 0 20 20"
                    >
                        <path
                            d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 
                        10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"
                        />
                    </svg>
                </div>
            </div>
        </>
    );
};

export default ErrorNotification;
JavaScript
//frontend/src/components/Feed.js
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { formatDistanceToNow } from "date-fns";
import {
    getPosts,
    createPost,
    deletePost,
    likePost,
    commentOnPost,
} from "../redux/actions/postActions";

const Feed = () => {
    const dispatch = useDispatch();
    const posts = useSelector((state) => state.posts) || [];

    useEffect(() => {
        dispatch(getPosts());
    }, [dispatch]);

    const handleCreatePost = (formData) => {
        dispatch(createPost(formData));
    };

    const handleDeletePost = (id) => {
        dispatch(deletePost(id));
    };

    const handleLikePost = (id) => {
        dispatch(likePost(id));
    };

    const handleCommentOnPost = (postId, content) => {
        dispatch(commentOnPost(postId, content));
    };

    return (
        <div>
            <PostForm onSubmit={handleCreatePost} />
            <PostList
                posts={posts}
                onDeletePost={handleDeletePost}
                onLikePost={handleLikePost}
                onCommentOnPost={handleCommentOnPost}
            />
        </div>
    );
};

const PostForm = ({ onSubmit }) => {
    const [content, setContent] = useState("");
    const [image, setImage] = useState(null);

    const handleSubmit = (e) => {
        e.preventDefault();
        const formData = new FormData();
        formData.append("content", content);
        if (image) {
            formData.append("image", image);
        }
        onSubmit(formData);
        setContent("");
        setImage(null);
    };

    return (
        <>
            <div className="flex flex-col justify-center font-[sans-serif] p-4 mt-14">
                <div class="min-w-[40rem] w-full mx-auto border border-gray-300
                 rounded-2xl p-4  bg-white text-black shadow-lg">
                    <form onSubmit={handleSubmit}>
                        <textarea
                            value={content}
                            onChange={(e) => setContent(e.target.value)}
                            placeholder="What's on your mind?"
                            style={{ resize: "none" }}
                            required
                            class="resize-none rounded-md w-full p-2"
                        ></textarea>
                        <div className="flex flex-row justify-between">
                            <img
                                onClick={() => {
                                    document.getElementById("post_media").click();
                                }}
                                src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCA
                                YAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFTElEQVR4nO1YaVOTVx
                                TmR1jrvleJC4Iii4ILiIIoVlAQAQEXUEjasYvaTqtCsVrDvgQChDXsJLhXrd1EaxfwQ/
                                ux34SxmuTN8r7wRZ15OufesIxMKQYl2ObMnHlzz53MfZ4nzzn3nbi5ucIVrvjvxmZ5V3
                                iwvKs3WNGN0TJI3oWN6b9gw9GfEJjaiXUpP8D/0LfwO3gLPslfwzvxKlbtvwyv+A6sjG
                                vHitgWLItpxNLoerjvrsHiKA0W7SrHwp2lmB9RjLnbCzE7PA+zwrJ7poWcD3OYQLC8q+
                                ffwG9K/xUb0u5j/ZG7CEj9EWsPfwe/g9/A98BNrEm6jtWJV+CVcBGecTp47GvF8r1NWBq
                                jhWxPLZZEVeGdyAosfFeNBREqzNtRhDnh+Zi1LQczQy9g+pZzmLo566HjBEZV/TdsTPsZ64/
                                eQ2DqnUHVfQ8MV/0SPOP1WLmvDStim7EspgGy6DqueiRXfcGg6gWYvS0XM8OUmLH1PKZtOY
                                upmzMxJegMXjGBLmxidrmPQFI9ZUh1n+QbWJN0DavJLgncLh6xLVz1aC3cB1TfRaqXYX5E
                                CebtKMQcbhfMsKv+dkgW3grOwJSg0yxfGYHhqgeQ6oe/h/+h24N28U68glUJXHUPUn2vX
                                fU9dViyuxqLIyuxiOyyk9tl7oDqoUpMJ9VDBlTnwB0m4K4zhMv0T3plHQZMhnTveNI
                                j0xvG3szsC5MAuGw4Cf2TsTezs8HK/iEdIvBAeIqB6DY9fe0gH4xynkMEXozXTeDF
                                cBHoNg39pF0TYKHuUc77fzaxbBKlm4tAx+tTd1mHAR56Azx1Rqwall46Izz0RrY/
                                Ke+BlToj1rQb4d9mQmCrCX+Yng2e97vxGTa08jrtO0RgtLk8XuC+7UYGLqjFhC3NA
                                sKahBHnbW8UsK1JwNZmYXIQICt4txsR0GpCcIuJgY5oFBClFRCtNY84L7bejJh6M9t3+
                                j2wXG+AT7uR2YIUjWgUsEdrxr46MxJrzThYY8afj4csRJ9Tqi2snlRrdu4UIvBkmU0t
                                JmaJyAaBqUvACGR6lQXvayw4prHiw8qh/EBjZXV5lcV5BJZ2GFijbrSDJzvE15lxqI
                                YDP6ax4ONKKz6psOKzcitOldsGk9afVlhxvNLqPAI0Dte3cr8T+IQ6rvp7Ggs+quQA
                                T6ttyFLbcK7Mhq9KKUX2pHVWmQ1n1DbnEFihN2BtG58yuxoExNWZcdgO/nilFZ+Xc+
                                AEOFclokAlorBERFEJfxaU8PqFUtE594C33fc0DvfW80ZVVHHwp8pt+LLMhpxSDlZVLOL
                                xo+eD5/316DnURbxO+xM+RpcPU5+adn+tGWlVFtac5O2zBF7F1S4rEqEplEacV1MgsToRmX
                                AC9BpAIzO8UWCznJqWJgo1a6baBmUpB0/gqgslaPNHEmjO4/XqAmni7wGaPHRZ7WzgU+dI
                                NVeffE/Nma8SUVYsoqpQQkO+hNY8CabeIQvRZ11uH6vTvkMExpN+bUaENJuYfeiiktu9n2
                                FXv7hEREWRiPoCCS15EvS5fbic04er2f24nt2Pa9n9bK3P4SQm9G8VemUg/9ONu1srILnW
                                zCYP2ecLtY1NFpVd/cZ8Ce25Ei7l9DHgN5VDeV3Zz+ptudJL/K2iN4SNlwQ18Lo2E0Kb+Os
                                CTR/yP818at48lYjSYpF5uylPYla5kt2HG8p+3FL24/YF/qT1VaX0UJfTFzpmAq5wxThDcS
                                LzrvxkRqfbm4pHcTITlG6TJBQvi+fFL8hPZtwbrsBErxWvgECn4kTmHWetFeMl4OxQvDSBN
                                72JXeEKtzHH3yinOXsdJo8UAAAAAElFTkSuQmCC"
                            ></img>

                            <input
                                type="file"
                                accept="image/*"
                                onChange={(e) => setImage(e.target.files[0])}
                                style={{ display: "none" }}
                                id="post_media"
                            />
                            <button
                                type="submit"
                                class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
                            >
                                Post
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </>
    );
};

const PostList = ({ posts, onDeletePost, onLikePost, onCommentOnPost }) => {
    if (!Array.isArray(posts)) {
        return <div>No posts available</div>;
    }

    return (
        <div>
            {posts.map((post) => (
                <Post
                    key={post._id}
                    post={post}
                    username={post.username}
                    onDelete={() => onDeletePost(post._id)}
                    onLike={() => onLikePost(post._id)}
                    onComment={(content) => onCommentOnPost(post._id, content)}
                />
            ))}
        </div>
    );
};

const Post = ({ post, username, onDelete, onLike, onComment }) => {
    const [commentContent, setCommentContent] = React.useState("");
    const [isViewerOpen, setIsViewerOpen] = useState(false);
    const [isShowCommentBox, setShowCommentBox] = useState(false);
    const [comments, setComments] = useState(post.comments || []);

    const handleCommentSubmit = (e) => {
        e.preventDefault();
        if (!commentContent.trim()) return;

        // Update the local state immediately
        const newComment = {
            createdAt: new Date(),
            content: commentContent,
            username,
            user: {
                username: JSON.parse(localStorage.getItem("userInfo")).user_id,
            },
        };
        setComments([...comments, newComment]);

        // Dispatch the action to update the Redux store
        onComment(commentContent);
        setCommentContent("");
    };
    const handlePostClick = () => {
        setIsViewerOpen(true);
    };

    const handleCloseViewer = () => {
        setIsViewerOpen(false);
    };
    function classNames(...classes) {
        return classes.filter(Boolean).join(" ");
    }
    return (
        <div>
            <div className="flex flex-col justify-center font-[sans-serif] p-4">
                <div class="min-w-[40rem] w-full mx-auto border border-gray-300 rounded-2xl p-4  bg-white text-black shadow-lg">
                    <div className="grid grid-cols-2  gap-4 ">
                        <div className="flex flex-row justify-stat gap-4 ">
                            <div className="rounded-lg  h-12 w-12 flex flex-row justify-center font-bold">
                                <img
                                    className="object-cover w-full h-full"
                                    src="https://media.geeksforgeeks.org/wp-content/
                                    uploads/20240725110755/user-profile_5675059.png"
                                />
                            </div>
                            <div>
                                <p className="uppercase    ">{post.user.username || username}</p>
                                <p>
                                    {formatDistanceToNow(new Date(post.createdAt), {
                                        addSuffix: true,
                                    })}
                                </p>
                            </div>
                        </div>
                        <div className="cursor-pointer flex flex-row justify-end">
                            <Menu as="div" className="relative ml-3">
                                <div>
                                    <MenuButton className="relative flex rounded-full  text-sm focus:outline-none focus:ring-2
                                     focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
                                        <span className="absolute -inset-1.5" />
                                        <span className="sr-only">Open user menu</span>
                                        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAA
                                        ADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAQElEQVR4nGNgGAVUBEcZGBj+Q/Fh
                                        BhqA/2h46FlwGMnwQ7SwYBQQBEdHkykhcHg0mQ44ODqaTAmBw6PJlIEWAACAfy0E9A9
                                        56gAAAABJRU5ErkJggg==" />
                                    </MenuButton>
                                </div>
                                <MenuItems
                                    transition
                                    className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md
                                     bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 transition 
                                     focus:outline-none data-[closed]:scale-95 data-[closed]:transform 
                                     data-[closed]:opacity-0 data-[enter]:duration-100 data-[leave]:duration-75
                                      data-[enter]:ease-out data-[leave]:ease-in"
                                >
                                    {JSON.parse(localStorage.getItem("userInfo"))?.user_id ===
                                        post.user._id ? (
                                        <MenuItem>
                                            {({ focus }) => (
                                                <a
                                                    href="#"
                                                    className={classNames(
                                                        focus ? "bg-gray-100" : "",
                                                        "block px-4 py-2 text-sm"
                                                    )}
                                                    onClick={onDelete}
                                                >
                                                    Delete
                                                </a>
                                            )}
                                        </MenuItem>
                                    ) : null}
                                </MenuItems>
                            </Menu>
                        </div>
                    </div>
                    <div className="mt-2">
                        <hr></hr>
                    </div>
                    <p className=" p-2 overflow-hidden text-nowrap text-ellipsis
                     whitespace-break-spaces max-w-[100%]">
                        {post.content}
                    </p>
                    <div className="flex justify-center">

                        {post.image ? (
                            <img
                                onClick={handlePostClick}
                                src={`http://localhost:5000/uploads/${post.image}`}
                                alt="Post"
                                className="object-cover h-[220px] w-[220px]"
                            />
                        ) : null}
                    </div>

                    <div className="mt-4 mb-4">
                        <hr></hr>
                    </div>
                    <div
                        className="flex flex-row justify-between p-4 bg-gray-100 rounded-md"
                        style={{ cursor: "pointer" }}
                    >
                        <div className="flex flex-row justify-between items-center ">
                            <div>
                                <img
                                    onClick={onLike}
                                    src="https://media.geeksforgeeks.org/wp-content/uploads/
                                    20240725145948/like_10950198.png"
                                    className="h-6 w-6" />
                            </div>
                            <div>
                                <p className="font-bold">{post.likes}</p>
                            </div>
                        </div>
                        <div className="flex flex-row justify-between items-center">
                            <div
                                onClick={() => {
                                    setShowCommentBox(!isShowCommentBox);
                                }}
                            >
                                <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240725150621
                                /bubble-chat_8345612.png" className="h-6 w-6" />

                            </div>
                            <div>
                                <p className="font-bold">{post.comments.length}</p>
                            </div>
                        </div>
                        <div>
                            <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240725151348
                            /share_5949688.png" className="h-6 w-6" />

                        </div>
                    </div>

                    {isShowCommentBox ? (
                        <div className="max-h-[14rem] overflow-y-scroll bg-gray-100">
                            <div>
                                <hr></hr>
                            </div>
                            {comments.map((comment, index) => (
                                <>
                                    <div key={index}>
                                        <div className="p-2">
                                            <div className="flex justify-start">
                                                <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA
                                                AAAyCAYAAAAeP4ixAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHV0lEQVR4nO2Ze
                                                1BU5xnGt216ybRNp/2jgWnadKbpGKeddjLTNtPWEKntGDspE20YhYmKxmgSRG
                                                BZFuQuuMhFuUREWA2CDIHdFVBuEXbhnEUuC7vLZbkJCJZzvsOaSgzgjMoiPp2z
                                                sivXugcW8w/PzDN7zpnd731/vN/77tmDSLSuda1rXc5QE8M8rwS+JeQziBZ9c1
                                                Ltuulu7Yu/GVe7aic0Lm0TGpdtomeh7u7u79RxzFaKsIkUR2ppjhCKsI9ojmD
                                                W9ynC9tAcUdEc619LyIZlQSjRc5Ma13ceQ7hi1uP89TUDuMZxv6AJSaUJe3tO
                                                0g6ZIqSVIsTHYDB8e6m1J9QuHTaQcbWrBU0vPe90AN3Y2AsUIWkUR6aEAiwG
                                                YocpjntnEYjGZRtfCR5iQuMa4HSIulHmTX7rrBZgCavUQ0M/mhuL306gXv6e
                                                0yG0hPjShH24BhA2D9aOjPxqYVyprvMlqbFro1MgaI4NW0OAJybsLXp0dF7S
                                                IXqTJkRvuuepVAqagIukZdlDQpKpZUagaryGvIpylLTohPcNxzIahvmZLX5
                                                wS9fbIXqT3+ogGOZPjjZ1eWc7Uk7JIPf7F0rFW1AZtBmF/v9ApM92ZORe4B
                                                v7qWtonxzrlptoK/pCozly46kV+M9NJEUFoShwK2pD3FEXutilgVsQsncHZ
                                                LJY5FVVLLvW5NQUbt+/PzvRSIRTQCiOPeZIJeKDP4RmieSXskbqjmS/ncuu
                                                NTg+jr47d2a3GLlHmc2/XG01fkIRdvJpEKqGeij9tzgEYfPJcF8BzU/kq63
                                                GUUcCJSXEQjO7nZR+bqgUv/n/KxLijtSsMwIan0xpbt16ccUgNGH7HAkUE+
                                                yH2lB3lPi7QZsVBm12BC74/ROlR9wWQSgPP76WFCMROsUCVlYNM/NbR4PER
                                                0mtDV4YtAMzgxqrH/bXwFiYiOKYAyiQeCIvcDsUUftQlSbFlQA3fBLxkTAQ
                                                wtavDIRj/BwNcjb3PColf0Nlkh/6q3JQlhKByvRoTLSXWaHUWTKUp0WhOT8
                                                FDwfUOLd/E9KPBQmsCJmqNpu/LxiE5tgcR4OUd3ZAfmgL2pWnrIl3FmdivO
                                                2KvTq82y+dwcN+tfX49N7NSEk/KQiE5j3K/FE4CGGvObJ4RWcbjvv6IPXQu7
                                                jdorImWiSTQFeQaoeY7lcj3NMDY60l1vNzYi8kHtkPxdVKYVUZZd9bs0YvadW
                                                hqzIHRcc/tm4ba3/Mvs615Xq1/bjhYgJMxanIyMpY+4anOJZpNI9iYmoK/713
                                                b+5tw/zFWQYV+RnouJy5KPnlfP3zHNRdTIaiXiuw4Ynwb3n+dtr05RhsajKPLh
                                                sgN/88Oq5kL0p4tEGBYfXFRRXqqTqPNFmk4B6hCJEIByFEz1dheHICfV89vl1
                                                YznXMCFKSZfaE+UZn6M/woOdza3+MNiqtUDaQ+KNilJs6hYNwzAHBIBTHKoQE
                                                sSZWcs6e7IPeqxisvoC+yvP4ynjZfr23Jh/nigqETyyO38bsZuEghEQKDZSj
                                                /AwjLU+SnuwoxxfNl+znX7ZX4ERclLWvhG8r9lGj2fxTQRAwef/4TqckZ6g/F
                                                w3khqCAucUKUHnJ1m02F0SnyoQsNgrq4aEVVYMmpFtwNSyGXeppoxd43+6OEV
                                                h+BqoUCdoLotGhSkNHcTr0eVEoT/gAxbqmlUFwvNkk4SDGXZM2kAft+xwKVD3Q
                                                j7Py04g7vBsjxaGwULFWP6iLsb6OlYcjyX835PJ01NwYFAxSz7K/EwwybfSS20
                                                DY3rS5TwsXOa+sFFLxYfgHBSJSUYKrzRUY056yg9h8lz6BhiYF4ksuIyAsFGGh
                                                YuRXXHHopy/FkTrRSgSIvjHdtnMbb4pjlpxe/F81VBoASaYcJ1q6kGDsszvR0I
                                                uLrQ2obi5HTXMZilooJBt65r0nvrUbwfJcBAWL8amywPnTaqG0hPycJuzdhYun
                                                nklB3DXjvORW6lDZMVztv75MNViFyFmiCPloYYCYUwlOgeAdVVaDC6rCpSaVmfr
                                                ipovTQGZhCmwB1EM3EJaR4TSQ2NomZH+aOb+5yZCFZll3kbNVNTDwXZpjNXyQUn
                                                0rwvOLnAYia2zDJ+mJdgjjzYYZi9FrxmLc9aHTQewwhFzinyCGKy47DSRB34OEx
                                                DjbhJoyDdf6WQxeGovR2020VqKA57ILC3PC5oDE8YkIrYKhd955dGKC9TFpHcf9
                                                VfSs5LFhww93e3vXB2VkT58w9GKvVicYZO5n4nUmHBGLB/nnaKKvQzve+PPW/e
                                                +/P+B9+uwMn4xQkMiSKvgeDZ94b+fOyr9v3PjrZ5Z4iK7r91J957sLr7/1+uub9n
                                                h7074hobckJ9OnwgpUiNU04Hi9we44WoeI4gpIM7IefSyWTO4+8MGgl8fbwW+9
                                                8soLomctqd5UJm3tnInu7v7Bcu/Z+uqrrjve+Mu/D/rsSTx08GDBQR+fin1eu6
                                                r3e3kp9mz3CPR47bU/eHqKVvc/jtUqyNjzcoihY9OqF1rXuta1LtEK9T8tSk8+
                                                5an1CAAAAABJRU5ErkJggg==" />
                                                <div className="block ml-2">
                                                    <div>{comment.username}</div>
                                                    <div>
                                                        {formatDistanceToNow(new Date(comment.createdAt), {
                                                            addSuffix: true,
                                                        })}
                                                    </div>
                                                </div>
                                            </div>
                                            <div>
                                                <hr></hr>
                                            </div>
                                            <div className="flex justify-start">
                                                <p key={comment._id}>{comment.content}</p>
                                            </div>
                                        </div>
                                    </div>
                                </>
                            ))}

                            <div class="w-full ">
                                <form onSubmit={handleCommentSubmit}>
                                    <div className="">
                                        <div>
                                            <textarea
                                                cols="30"
                                                className="p-2 mt-5 w-full resize-none border-gray-400 border-2"
                                                required
                                                value={commentContent}
                                                onChange={(e) => setCommentContent(e.target.value)}
                                                placeholder="Write your comment...."
                                            ></textarea>
                                        </div>
                                        <div className="p-2 flex justify-end">
                                            <button
                                                type="submit"
                                                class="bg-black hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
                                            >
                                                Add
                                            </button>
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    ) : null}
                </div>
            </div>
        </div>
    );
};

export default Feed;
JavaScript
//frontend/src/components/Login.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { loginUser } from "../redux/actions/authActions";
import { useNavigate } from "react-router-dom";
import { Link } from "react-router-dom";

const Login = () => {
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");

    const dispatch = useDispatch();
    const auth = useSelector((state) => state.auth);
    const navigate = useNavigate();

    const handleSubmit = (e) => {
        e.preventDefault();
        dispatch(loginUser({ email, password }));
    };

    useEffect(() => {
        if (auth.userInfo) {
            // console.log(auth.userInfo)
            navigate("/feed");
        }
    }, [auth.userInfo, navigate]);

    return (
        <div class="flex flex-col justify-center font-[sans-serif] sm:h-screen p-4
        mt-14 bg-white">
            <div class="max-w-md w-full mx-auto border border-gray-300 rounded-2xl p-8">
                <div class="sm:mx-auto sm:w-full sm:max-w-sm">
                    <h2 class="mt-10 text-center text-2xl font-bold leading-9 
                    tracking-tight text-gray-900">
                        Sign in to your account
                    </h2>
                </div>

                <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
                    <form class="space-y-6" onSubmit={handleSubmit}>
                        <div>
                            <label
                                for="email"
                                class="block text-sm font-medium leading-6 text-gray-900"
                            >
                                Email address
                            </label>
                            <div class="mt-2">
                                <input
                                    id="email"
                                    name="email"
                                    type="email"
                                    autocomplete="email"
                                    value={email}
                                    onChange={(e) => setEmail(e.target.value)}
                                    required
                                    class="block w-full p-2 rounded-md border-0 py-1.5
                                    text-gray-900
                                     shadow-sm ring-1 ring-inset ring-gray-300 
                                     placeholder:text-gray-400
                                      focus:ring-2 focus:ring-inset focus:ring-indigo-600
                                      sm:text-sm sm:leading-6"
                                />
                            </div>
                        </div>

                        <div>
                            <div class="flex items-center justify-between">
                                <label
                                    for="password"
                                    class="block text-sm font-medium leading-6 text-gray-900"
                                >
                                    Password
                                </label>
                                <div class="text-sm">
                                    <a
                                        href="#"
                                        class="font-semibold text-indigo-600
                                        hover:text-indigo-500"
                                    >
                                        Forgot password?
                                    </a>
                                </div>
                            </div>
                            <div class="mt-2">
                                <input
                                    id="password"
                                    name="password"
                                    type="password"
                                    value={password}
                                    onChange={(e) => setPassword(e.target.value)}
                                    autocomplete="current-password"
                                    required
                                    class="block w-full p-2 rounded-md border-0 py-1.5 
                                    text-gray-900 
                                    shadow-sm ring-1 ring-inset ring-gray-300 
                                    placeholder:text-gray-400
                                     focus:ring-2 focus:ring-inset focus:ring-indigo-600
                                     sm:text-sm sm:leading-6"
                                />
                            </div>
                        </div>

                        <div>
                            <button
                                type="submit"
                                class="flex w-full justify-center rounded-md bg-indigo-600 px-3 
                                py-1.5 text-sm font-semibold leading-6 text-white shadow-sm
                                hover:bg-indigo-500
                                 focus-visible:outline focus-visible:outline-2 
                                 focus-visible:outline-offset-2
                                  focus-visible:outline-indigo-600"
                            >
                                Sign in
                            </button>
                        </div>
                        <p class="text-gray-800 text-sm mt-6 text-center">
                            Don't have an account?{" "}
                            <Link
                                to="/register"
                                href="javascript:void(0);"
                                class="text-blue-600 font-semibold hover:underline ml-1"
                            >
                                Register here
                            </Link>
                        </p>
                    </form>
                </div>
            </div>
        </div>
    );
};

export default Login;
JavaScript
//frontend/src/components/Nav.js
import React from "react";
import { Link } from "react-router-dom";
import {
    Disclosure,
    DisclosureButton,
    DisclosurePanel,
    Menu,
    MenuButton,
    MenuItem,
    MenuItems,
} from "@headlessui/react";
import { Bars3Icon, BellIcon, XMarkIcon } from "@heroicons/react/24/outline";
import { useDispatch } from "react-redux";
import { logoutUser } from "../redux/actions/authActions";
import { useNavigate } from "react-router-dom";
export default function Nav() {
    const dispatch = useDispatch();
    const navigate = useNavigate();

    const navigation = [
        { name: "Dashboard", href: "#", current: true },

    ];
    function classNames(...classes) {
        return classes.filter(Boolean).join(" ");
    }
    const handleLogout = () => {
        dispatch(logoutUser());
        navigate('/login');
    };
    return (
        <Disclosure as="nav" className="bg-gray-800 fixed w-full top-0 z-20">
            {({ open }) => (
                <>
                    <div className="mx-auto max-w-7xl px-2 sm:px-6 lg:px-8">
                        <div className="relative flex h-16 items-center justify-between">
                            <div className="absolute inset-y-0 left-0 flex items-center
                            sm:hidden">
                                {/* Mobile menu button*/}
                                <DisclosureButton className="relative inline-flex items-center
                                 justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700
                                  hover:text-white focus:outline-none focus:ring-2 
                                  focus:ring-inset
                                   focus:ring-white">
                                    <span className="absolute -inset-0.5" />
                                    <span className="sr-only">Open main menu</span>
                                    {open ? (
                                        <XMarkIcon className="block h-6 w-6" 
                                        aria-hidden="true" />
                                    ) : (
                                        <Bars3Icon className="block h-6 w-6"
                                        aria-hidden="true" />
                                    )}
                                </DisclosureButton>
                            </div>
                            <div className="flex flex-1 items-center justify-center
                             sm:items-stretch sm:justify-start">
                                <div className="flex flex-shrink-0 items-center">
                                    <img
                                        className="h-8 w-auto"
                                        src="https://media.geeksforgeeks.org/auth-
                                        dashboard-uploads/
                                        gfgFooterLogoDark.png"
                                        alt="Your Company"
                                    />
                                </div>
                                <div className="hidden sm:ml-6 sm:block">
                                    <div className="flex space-x-4">
                                        {navigation.map((item) => (
                                            <a
                                                key={item.name}
                                                href={item.href}
                                                className={classNames(
                                                    item.current
                                                        ? "bg-gray-900 text-white"
                                                        : "text-gray-300 hover:bg-gray-700
                                                         hover:text-white",
                                                    "rounded-md px-3 py-2 text-sm font-medium"
                                                )}
                                                aria-current={item.current ? "page" : undefined}
                                            >
                                                {item.name}
                                            </a>
                                        ))}
                                    </div>
                                </div>
                            </div>
                            <div className="absolute inset-y-0 right-0 flex items-center pr-2 
                            sm:static sm:inset-auto sm:ml-6 sm:pr-0">
                                <button
                                    type="button"
                                    className="relative rounded-full bg-gray-800 p-1 
                                    text-gray-400
                                     hover:text-white focus:outline-none focus:ring-2 
                                     focus:ring-white
                                      focus:ring-offset-2 focus:ring-offset-gray-800"
                                >
                                    <span className="absolute -inset-1.5" />
                                    <span className="sr-only">View notifications</span>
                                    <BellIcon className="h-6 w-6" aria-hidden="true" />
                                </button>

                                {/* Profile dropdown */}
                                <Menu as="div" className="relative ml-3">
                                    <div>
                                        <MenuButton className="relative flex rounded-full
                                        bg-gray-800
                                         text-sm focus:outline-none focus:ring-2 focus:ring-white
                                          focus:ring-offset-2 focus:ring-offset-gray-800">
                                            <span className="absolute -inset-1.5" />
                                            <span className="sr-only">Open user menu</span>
                                            <img
                                                className="h-8 w-8 rounded-full"
                                                src="https://media.geeksforgeeks.org/
                                                wp-content/uploads/
                                                20240725110755/user-profile_5675059.png"

                                                alt=""
                                            />
                                        </MenuButton>
                                    </div>
                                    <MenuItems
                                        transition
                                        className="absolute right-0 z-10 mt-2 w-48
                                        origin-top-right rounded-md
                                         bg-white py-1 shadow-lg ring-1 ring-black
                                         ring-opacity-5 transition
                                          focus:outline-none data-[closed]:scale-95
                                          data-[closed]:transform
                                           data-[closed]:opacity-0 data-[enter]:duration-100 
                                           data-[leave]:duration-75 data-[enter]:ease-out 
                                           data-[leave]:ease-in"
                                    >
                                        <MenuItem>
                                            {({ focus }) => (
                                                <Link
                                                    to='/profile'
                                                    className={classNames(
                                                        focus ? "bg-gray-100" : "",
                                                        "block px-4 py-2 text-sm text-gray-700"
                                                    )}
                                                >
                                                    Your Profile
                                                </Link>
                                            )}
                                        </MenuItem>
                                        <MenuItem>
                                            {({ focus }) => (
                                                <a
                                                    href="#"
                                                    className={classNames(
                                                        focus ? "bg-gray-100" : "",
                                                        "block px-4 py-2 text-sm text-gray-700"
                                                    )}
                                                >
                                                    Settings
                                                </a>
                                            )}
                                        </MenuItem>
                                        <MenuItem>
                                            {({ focus }) => (
                                                <a
                                                    href="#"
                                                    onClick={handleLogout}
                                                    className={classNames(
                                                        focus ? "bg-gray-100" : "",
                                                        "block px-4 py-2 text-sm text-gray-700"
                                                    )}
                                                >
                                                    Sign out
                                                </a>
                                            )}
                                        </MenuItem>
                                    </MenuItems>
                                </Menu>
                            </div>
                        </div>
                    </div>

                    <DisclosurePanel className="sm:hidden">
                        <div className="space-y-1 px-2 pb-3 pt-2">
                            {navigation.map((item) => (
                                <DisclosureButton
                                    key={item.name}
                                    as="a"
                                    href={item.href}
                                    className={classNames(
                                        item.current
                                            ? "bg-gray-900 text-white"
                                            : "text-gray-300 hover:bg-gray-700 hover:text-white",
                                        "block rounded-md px-3 py-2 text-base font-medium"
                                    )}
                                    aria-current={item.current ? "page" : undefined}
                                >
                                    {item.name}
                                </DisclosureButton>
                            ))}
                        </div>
                    </DisclosurePanel>
                </>
            )}
        </Disclosure>
    );
}
JavaScript
//frontend/src/components/PosttViewer.js
import { useState } from "react";
import {
    Dialog,
    DialogBackdrop,
    DialogPanel,
    DialogTitle,
} from "@headlessui/react";
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";

export default function PosttViewer({
    img_url,
    content,
    isViewerOpen,
    onClose,
}) {
    return (
        <Dialog className="relative z-10" open={isViewerOpen} onClose={onClose}>
            <DialogBackdrop
                transition
                className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity
         data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:
         duration-200 data-[enter]:ease-out data-[leave]:ease-in"
            />

            <div className="fixed inset-0 z-10 w-screen overflow-y-auto p-10">
                <div className="flex min-h-full items-end justify-center p-4
         text-center sm:items-center sm:p-0">
                    <DialogPanel
                        transition
                        className="relative transform overflow-hidden rounded-lg
                         bg-white text-left shadow-xl transition-all data-[closed]:
                         translate-y-4 data-[closed]:opacity-0 data-[enter]:
                         duration-300 data-[leave]:duration-200 data-[enter]:
                         ease-out data-[leave]:ease-in sm:my-8 sm:w-full 
                         sm:max-w-lg data-[closed]:sm:translate-y-0 data-[closed]:
                         sm:scale-95"
                    >
                        <div className="bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
                            <div className="sm:flex sm:items-start">
                                <div className="mx-auto flex h-12 w-12 flex-shrink-0
                                 items-center justify-center rounded-full bg-red-100
                                  sm:mx-0 sm:h-10 sm:w-10">
                                    <ExclamationTriangleIcon
                                        className="h-6 w-6 text-red-600"
                                        aria-hidden="true"
                                    />
                                </div>
                                <div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
                                    <div className="mt-2">
                                        <p className="text-sm text-gray-500">{content}</p>
                                    </div>
                                    <div>
                                        {img_url && (
                                            <img
                                                src={`http://localhost:5000/uploads/${img_url}`}
                                                alt="Post"
                                                style={{ maxWidth: "100%", maxHeight: "100%" }}
                                            />
                                        )}
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div className="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse 
                        sm:px-6">
                        </div>
                    </DialogPanel>
                </div>
            </div>
        </Dialog>
    );
}
JavaScript
//frontend/src/components/PrivateRoute.js
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useSelector } from 'react-redux';

const PrivateRoute = () => {
    const user = useSelector((state) => state.auth.userInfo);

    return user ? <Outlet /> : <Navigate to="/login" />;
};
export default PrivateRoute;
JavaScript
//frontend/src/components/Profile.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import { formatDistanceToNow } from "date-fns";
import {
    fetchUserProfile,
    updateUserProfile,
} from "../redux/actions/userActions";
import { getUserPost } from "../redux/actions/postActions";
const Profile = () => {
    const dispatch = useDispatch();
    const userInfo = useSelector((state) => state.auth.userInfo);
    const loading = useSelector((state) => state.auth.loading);
    const posts = useSelector((state) => state.posts) || [];
    const [username, setUsername] = useState("");
    const [email, setEmail] = useState("");

    useEffect(() => {
        dispatch(fetchUserProfile());
        dispatch(getUserPost());
    }, [dispatch]);

    useEffect(() => {
        if (userInfo) {
            setUsername(userInfo.username);
            setEmail(userInfo.email);
            console.log("Username:", userInfo.username, "Email:", userInfo.email);
        }
    }, [userInfo]);

    const handleSubmit = (e) => {
        e.preventDefault();
        if (userInfo) {
            dispatch(updateUserProfile({ username, email }));
        }
    };

    if (loading) {
        return <div>Loading...</div>;
    }

    return (
        <>
            <div class="h-screen dark:bg-gray-700 bg-gray-200 pt-12">
                <h1 className="p-2 ml-5">
                    <Link to="/feed">Go back</Link>
                </h1>
                <div class="max-w-sm mx-auto bg-white dark:bg-gray-900 
                rounded-lg overflow-hidden shadow-lg">
                    <div class="border-b px-4 pb-6">
                        <form onSubmit={handleSubmit}>
                            <div class="text-center my-4">
                                <img
                                    class="h-32 w-32 rounded-full border-4 border-white
                                     dark:border-gray-800 mx-auto my-4"
                                    src="https://media.geeksforgeeks.org/wp-content/\
                                    uploads/20240725103944/user-sign-icon-front-side-with-white
                                    -background_187299-40022.png"
                                    alt=""
                                />
                                <div class="py-2">
                                    <input
                                        type="text"
                                        value={username}
                                        onChange={(e) => setUsername(e.target.value)}
                                        placeholder="Username"
                                        required
                                        class="font-bold text-2xl text-gray-800 
                                        dark:text-white mb-1 text-center"
                                    />
                                    <h3 class="font-bold text-2xl text-gray-800 
                                    dark:text-white mb-1">
                                        {email}
                                    </h3>

                                    <div class="inline-flex text-gray-700 
                                    dark:text-gray-300 items-center">
                                        <svg
                                            class="h-5 w-5 text-gray-400 
                                            dark:text-gray-600 mr-1"
                                            fill="currentColor"
                                            xmlns="http://www.w3.org/2000/svg"
                                            viewBox="0 0 24 24"
                                            width="24"
                                            height="24"
                                        >
                                            <path
                                                class=""
                                                d="M5.64 16.36a9 9 0 1 1 12.72 0l-5.65 5.66a1
                                                 1 0 0 1-1.42 0l-5.65-5.66zm11.31-1.41a7 7 0 
                                                 1 0-9.9 0L12 19.9l4.95-4.95zM12 14a4 4 0 1 
                                                 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2
                                                  0 0 0 0 4z"
                                            />
                                        </svg>
                                        New York, NY
                                    </div>
                                </div>
                            </div>
                            <div class="flex gap-2 px-2">
                                <button
                                    type="submit"
                                    class="flex-1 rounded-full bg-blue-600 dark:bg-blue-800
                                     text-white dark:text-white antialiased font-bold
                                      hover:bg-blue-800 dark:hover:bg-blue-900 px-4 py-2"
                                >
                                    Update Profile
                                </button>
                            </div>
                        </form>
                    </div>
                    <div class="px-4 py-4">
                        <div class="flex gap-2 items-center text-gray-800 
                        dark:text-gray-300 mb-4">
                            <svg
                                class="h-6 w-6 text-gray-600 dark:text-gray-400"
                                fill="currentColor"
                                xmlns="http://www.w3.org/2000/svg"
                                viewBox="0 0 24 24"
                                width="24"
                                height="24"
                            >
                                <path
                                    class=""
                                    d="M12 12a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm0-2a3 
                                    3 0 1 0 0-6 3 3 0 0 0 0 6zm9 11a1 1 0 0 1-2 0v-2a3
                                     3 0 0 0-3-3H8a3 3 0 0 0-3 3v2a1 1 0 0 1-2 0v-2a5
                                      5 0 0 1 5-5h8a5 5 0 0 1 5 5v2z"
                                />
                            </svg>
                            <span>
                                <strong class="text-black dark:text-white">
                                    12</strong> Followers
                                you know
                            </span>
                        </div>
                        <div class="flex">
                            <div class="flex justify-end mr-2">
                                <img
                                    class="border-2 border-white dark:border-gray-800
                                     rounded-full h-10 w-10 -mr-2"
                                    src="https://media.geeksforgeeks.org/wp-content
                                    /uploads/20240729160824/32.jpg"
                                    alt=""
                                />
                                <img
                                    class="border-2 border-white dark:border-gray-800 
                                    rounded-full h-10 w-10 -mr-2"
                                    src="https://media.geeksforgeeks.org/wp-content/
                                    uploads/20240729160919/31.jpg"
                                    alt=""
                                />
                                <img
                                    class="border-2 border-white dark:border-gray-800
                                     rounded-full h-10 w-10 -mr-2"
                                    src="https://media.geeksforgeeks.org/wp-content/
                                    uploads/20240729161005/33.jpg"
                                    alt=""
                                />
                                <img
                                    class="border-2 border-white dark:border-gray-800 
                                    rounded-full h-10 w-10 -mr-2"
                                    src="https://media.geeksforgeeks.org/wp-content/
                                    uploads/20240729161119/32-w.jpg"
                                    alt=""
                                />
                                <img
                                    class="border-2 border-white dark:border-gray-800
                                     rounded-full h-10 w-10 -mr-2"
                                    src="https://media.geeksforgeeks.org/wp-content/
                                    uploads/20240729161214/44.jpg"
                                    alt=""
                                />
                                <img
                                    class="border-2 border-white dark:border-gray-800
                                     rounded-full h-10 w-10 -mr-2"
                                    src="https://media.geeksforgeeks.org/wp-content/
                                    uploads/20240729161356/42.jpg"
                                    alt=""
                                />
                                <span class="flex items-center justify-center bg-white
                                 dark:bg-gray-800 text-sm text-gray-800 dark:text-white
                                  font-semibold border-2 border-gray-200 dark:border-gray-700
                                   rounded-full h-10 w-10">
                                    +999
                                </span>
                            </div>
                        </div>
                    </div>
                </div>

                <PostList posts={posts} />
            </div>

            {/* all post */}
        </>
    );
};
const PostList = ({ posts }) => {
    if (!Array.isArray(posts)) {
        return <div>No posts available</div>;
    }

    return (
        <div className=" dark:bg-gray-700 bg-gray-200 ">
            <div className="bg-black p-4 mt-8 text-white font-bold ">
                <h1 className="flex justify-center">All posts</h1>
            </div>
            <div>
                {posts.map((post) => (
                    <div className="flex justify-center">
                        <Post key={post._id} post={post} />
                    </div>
                ))}
            </div>
        </div>
    );
};
const Post = ({ post }) => {
    return (
        <div className="">
            <div className="flex flex-col justify-center 
            font-[sans-serif] p-4">
                <div class="min-w-[40rem] w-full mx-auto border 
                border-gray-300 rounded-2xl p-4  bg-white 
                text-black shadow-lg">
                    <div className="grid grid-cols-2  gap-4 ">
                        <div className="flex flex-row  gap-4 ">
                            <div className="rounded-lg  h-8 w-8 
                            flex flex-row justify-center font-bold">
                                <img
                                    className="object-cover w-full h-full"
                                    src="https://media.geeksforgeeks.org/
                                    wp-content/uploads/20240725110755/user-profile_5675059.png"
                                />
                            </div>
                            <div>
                                <p className="uppercase    ">{post.user.username}</p>
                                <p>
                                    {formatDistanceToNow(new Date(post.createdAt), {
                                        addSuffix: true,
                                    })}
                                </p>
                            </div>
                        </div>
                    </div>
                    <div className="mt-2">
                        <hr></hr>
                    </div>
                    <p className=" p-2 overflow-hidden text-nowrap text-ellipsis
                     whitespace-break-spaces max-w-[100%]">
                        {post.content}
                    </p>
                    <div className="flex justify-center">
                        {post.image ? (
                            <img
                                src={`http://localhost:5000/uploads/${post.image}`}
                                alt="Post"
                                className="object-cover h-[220px] w-[220px]"
                            />
                        ) : null}
                    </div>
                    <div className="mt-4 mb-4">
                        <hr></hr>
                    </div>
                    <div
                        className="flex flex-row justify-between p-2 
                        bg-gray-100 rounded-md"
                        style={{ cursor: "pointer" }}
                    >
                        <div className="flex flex-row justify-between items-center ">
                            <div>
                                <img
                                    src="https://media.geeksforgeeks.org/
                                    wp-content/uploads/20240725145948/like_10950198.png"
                                    className="h-6 w-6"
                                />
                            </div>
                            <div>
                                <span className="h-6 w-6">{post.likes}</span>
                            </div>
                        </div>
                        <div className="flex flex-row justify-between items-center">
                            <div>
                                <img
                                    src="https://media.geeksforgeeks.org/
                                    wp-content/uploads/20240725150621/bubble-chat_8345612.png"
                                    className="h-6 w-6"
                                />
                            </div>
                            <div>
                                <span className="h-6 w-6">
                                    {post.comments.length}</span>
                            </div>
                        </div>
                        <div>
                            <img
                                src="https://media.geeksforgeeks.org/
                                wp-content/uploads/20240725151348/share_5949688.png"
                                className="h-6 w-6"
                            />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default Profile;
JavaScript
//frontend/src/components/PublicRoute.js
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useSelector } from 'react-redux';

const PublicRoute = () => {
    const user = useSelector((state) => state.auth.userInfo);

    return user ? <Navigate to="/" /> : <Outlet />;
};

export default PublicRoute;
JavaScript
//frontend/src/components/Register.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { registerUser } from "../redux/actions/authActions";
import { useNavigate } from "react-router-dom";
import { Link } from "react-router-dom";

const Register = () => {
    const [username, setUsername] = useState("");
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");

    const dispatch = useDispatch();
    const auth = useSelector((state) => state.auth);
    const navigate = useNavigate();

    const handleSubmit = (e) => {
        e.preventDefault();
        dispatch(registerUser({ username, email, password }));
        navigate("/login");
    };

    // useEffect(() => {
    //   if (auth.userInfo) {
    //     navigate("/login");
    //   }
    // }, [auth.userInfo, navigate]);

    return (
        <div class="flex flex-col justify-center font-[sans-serif] 
        sm:h-screen p-4 mt-14 bg-white">
            <div class="max-w-md w-full mx-auto border border-gray-300
             rounded-2xl p-8">
                <div class="text-center mb-12">
                    <h2 class="mt-10 text-center text-2xl font-bold
                     leading-9 tracking-tight text-gray-900">
                        Register your account
                    </h2>
                </div>

                <form onSubmit={handleSubmit}>
                    <div class="space-y-6">
                        <div>
                            <label class="text-gray-800 text-sm mb-2 block">
                                Name</label>
                            <input
                                name="name"
                                type="text"
                                class="text-gray-800 bg-white border 
                                border-gray-300 w-full text-sm px-4 py-3
                                 rounded-md outline-blue-500"
                                placeholder="Enter name"
                                required
                                value={username}
                                onChange={(e) => setUsername(e.target.value)}
                            />
                        </div>
                        <div>
                            <label class="text-gray-800 text-sm mb-2 block">
                                Email</label>
                            <input
                                name="email"
                                type="email"
                                class="text-gray-800 bg-white border border-gray-300
                                 w-full text-sm px-4 py-3 rounded-md
                                  outline-blue-500"
                                placeholder="Enter email"
                                required
                                value={email}
                                onChange={(e) => setEmail(e.target.value)}
                            />
                        </div>
                        <div>
                            <label class="text-gray-800 text-sm mb-2 block">
                                Password</label>
                            <input
                                name="password"
                                type="password"
                                class="text-gray-800 bg-white border border-gray-300
                                 w-full text-sm px-4 py-3 rounded-md outline-blue-500"
                                placeholder="Enter password"
                                required
                                value={password}
                                onChange={(e) => setPassword(e.target.value)}
                            />
                        </div>

                        <div class="flex items-center">
                            <input
                                id="remember-me"
                                name="remember-me"
                                type="checkbox"
                                class="h-4 w-4 shrink-0 text-blue-600 
                                focus:ring-blue-500 border-gray-300 rounded"
                            />
                            <label for="remember-me" class="text-gray-800
                             ml-3 block text-sm">
                                I accept the{" "}
                                <a
                                    href="javascript:void(0);"
                                    class="text-blue-600 font-semibold
                                     hover:underline ml-1"
                                >
                                    Terms and Conditions
                                </a>
                            </label>
                        </div>
                    </div>

                    <div class="!mt-12">
                        <button
                            type="submit"
                            class="w-full py-3 px-4 text-sm tracking-wider 
                            font-semibold rounded-md text-white bg-blue-600
                             hover:bg-blue-700 focus:outline-none"
                        >
                            Create an account
                        </button>
                    </div>
                    <p class="text-gray-800 text-sm mt-6 text-center">
                        Already have an account?{" "}
                        <Link
                            to="/login"
                            href="javascript:void(0);"
                            class="text-blue-600 font-semibold 
                            hover:underline ml-1"
                        >
                            Login here
                        </Link>
                    </p>
                </form>
            </div>
        </div>
    );
};

export default Register;
JavaScript
//frontend/src/components/Sidebarprofile.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchUserProfile } from "../redux/actions/userActions";

export default function Sidebarprofile() {
    const dispatch = useDispatch();
    const userInfo = useSelector((state) => state.auth.userInfo);
    const loading = useSelector((state) => state.auth.loading);
    const [username, setUsername] = useState("");
    useEffect(() => {
        dispatch(fetchUserProfile());
    }, [dispatch]);

    useEffect(() => {
        if (userInfo) {
            setUsername(userInfo.username);
        }
    }, [userInfo]);
    if (loading) {
        return <div>Loading...</div>;
    }

    return (
        <div className="bg-white my-20 pb-6 w-[20%] h-[20%]
         justify-center items-center overflow-hidden 
          rounded-lg shadow-lg ">
            <div className="relative h-40">
                <img
                    className="absolute h-full w-full object-cover"
                    src="https://media.geeksforgeeks.org/
                    wp-content/uploads/20240725153703/profile_4260134.png"
                />
            </div>
            <div className="relative shadow mx-auto h-24 w-24
             -my-12 border-white rounded-full overflow-hidden border-4">
                <img
                    className="object-cover w-full h-full"
                    src="https://media.geeksforgeeks.org/wp-content/
                    uploads/20240725103944/user-sign-icon-front-side-
                    with-white-background_187299-40022.png"
                />
            </div>
            <div className="mt-16">
                <h1 className="text-lg text-center font-semibold">
                    <p className="uppercase    ">{userInfo.username}</p>
                </h1>
                <p className="text-sm text-gray-600 text-center">
                    13 connections in common
                </p>
            </div>
        </div>
    );
}
JavaScript
//src/components/Home.js
// src/components/Home.js
import React from "react";
import { Link } from "react-router-dom";

const Home = () => {
    return (
        <div style={{ padding: "20px" }}>
            <header style={{ marginBottom: "20px" }}>
                <h1>Welcome to the Social Networking Platform</h1>
                <nav>
                    <Link to="/login" style={{ marginRight: "10px" }}>
                        Login
                    </Link>
                    <Link to="/register">Register</Link>
                </nav>
            </header>
            <main>
                <section>
                    <h2>About Our Platform</h2>
                    <p>
                        This platform allows you to connect with friends, share updates, and
                        stay informed about what's happening in your community.
                    </p>
                </section>
            </main>
            <footer style={{ marginTop: "20px" }}>
                <p>&copy; 2024 Social Networking Platform. All rights reserved.</p>
            </footer>
        </div>
    );
};

export default Home;


Steps to Run the Application

Backend

  • Navigate to the backend directory
  • Install dependencies: npm install
  • Start the server: npm start
cd backend
npm server.js / nodemon server.js

Frontend

  • Navigate to the frontend directory
  • Install dependencies: npm install
  • Create a .env file for environment variables (e.g., API base URL)
  • Start the React app: npm start
cd frontend
npm start

Output




Reffered: https://www.geeksforgeeks.org


MERN Stack

Related
Authentication and Authorization with OAuth Authentication and Authorization with OAuth
How To Use MERN Stack: A Complete Guide How To Use MERN Stack: A Complete Guide
Error Handling in MERN Applications Error Handling in MERN Applications
Monitoring and Logging in MERN Stack Monitoring and Logging in MERN Stack
Optimizing Your MERN Stack Application Performance Optimizing Your MERN Stack Application Performance

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
21