import { useContext, useEffect } from "react";
import { AuthContext } from "../components/AuthContext";
import { useNavigate } from "react-router-dom";

const LandingPage = () => {
    const { userDetails } = useContext(AuthContext);
    const navigate = useNavigate();
    useEffect(() => {
        userDetails ? navigate('/dashboard') : null
    }, [userDetails])

    return (
        <>
            <section className="hero">
                <div className="hero-content">
                    <h2>Welcome to Bloggy</h2>
                    <p>Your space to share your thoughts and read amazing blogs.</p>
                    <a href="/signup" className="cta-btn">Get Started</a>
                </div>
            </section>
            <section className="features">
                <div className="feature">
                    <h3>Easy Blogging</h3>
                    <p>Start writing in minutes with our intuitive editor.</p>
                </div>
                <div className="feature">
                    <h3>Read and Explore</h3>
                    <p>Discover blogs on a variety of topics from around the world.</p>
                </div>
                <div className="feature">
                    <h3>Join the Community</h3>
                    <p>Engage with other bloggers and readers.</p>
                </div>
            </section>
        </>
    )
}

export default LandingPage;


import { useContext, useEffect, useState } from "react";
import { AuthContext } from "./AuthContext";
import { useNavigate } from "react-router-dom";

const Header = () => {
    const navigate = useNavigate();

    const { user, setUser, userDetails } = useContext(AuthContext);


    const handleLogout = async () => {
        const res = await fetch('http://localhost:3000/logout', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            credentials: 'include'
        })
        if (res.ok) {
            setUser(false)
            navigate('/login');
        }
    }


    return (
        <header>
            <h1>
                <a href="/">
                    Bloggy
                </a>
            </h1>
            <nav>
                {user && userDetails ? 
                <ul>
                    <li className="nav-btns"><a href="/dashboard">Dashboard</a></li>
                    <li className="nav-btns">
                        <a href="/profile">
                        {userDetails.firstName}
                        </a></li>
                    <li className="nav-btns"><button onClick={handleLogout}>Logout</button></li>
                </ul> :
                <ul>
                    <li  className="nav-btns"><a href="/signup">Sign up</a></li>
                    <li  className="nav-btns"><a href="/login">Login</a></li>
                </ul>}
            </nav>
        </header>
    )
}

export default Header;


const Footer = () => {
    return (
        <footer>
            <p>&copy; 2024 Bloggy.com | <a href="/privacy">Privacy Policy</a> | <a href="/terms">Terms of Service</a></p>
        </footer>
    )
}

export default Footer;






import { useContext, useEffect, useState } from "react"
import { AuthContext } from "../components/AuthContext";
import { useNavigate } from "react-router-dom";

const Login = () => {

    const { user, loading, setUser } = useContext(AuthContext);

    const navigate = useNavigate();

    useEffect(() => {
        console.log('log', user);
        if (user) {
            navigate('/dashboard')
        }
    }, [user, loading, navigate])

    const [userInfo, setUserInfo] = useState({
        email: '',
        password: '',
    });
    const [error, setError] = useState(null);

    const handleChange = (e) => {
        const { name, value } = e.target;
        setUserInfo({
            ...userInfo,
            [name]: value
        })
    }

    const handleSubmit = async (e) => {
        e.preventDefault()
        const res = await fetch('http://localhost:3000/login', {
            method: 'POST',
            credentials: 'include',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(userInfo)
        })
        if (res.ok) {
            const data = await res.json();
            setUser(data);
            setError(null);
        } else {
            const e = await res.json();
            setError(e);
            console.log(e);
        }

    }
    
    return (
        <section className="login-section">
            <div className="login-form-container">
                <h2>Login</h2>
                <form className="login-form" autoComplete="true" onSubmit={handleSubmit}>

                    <label htmlFor="email">Email</label>
                    <input type="email" id="email" name="email" value={userInfo.email} onChange={handleChange} required />
                    {error && error.path === 'email' ? <p className="error">{error.message}</p> : null}

                    <label htmlFor="password">Password</label>
                    <input type="password" id="password" name="password" value={userInfo.password} onChange={handleChange} required />
                    {error && error.path === 'password' ? <p className="error">{error.message}</p> : null}

                    <button type="submit" className="submit-btn">Login</button>
                </form>
            </div>
        </section>
    )
}

export default Login;


import { useEffect, useState } from "react"

const Signup = () => {
    const [userInfo, setUserInfo] = useState({
        firstName: '',
        lastName: '',
        email: '',
        password: '',
        confirmPassword: '',
    })
    const [success, setSuccess] = useState(null);
    const [error, setError] = useState(null);

    const handleChange = (e) => {
        const { name, value } = e.target;
        setUserInfo({
            ...userInfo,
            [name]: value
        })
    }

    const handleSubmission = async (e) => {
        e.preventDefault();
        try {
            if (userInfo.password === userInfo.confirmPassword) {

                const res = await fetch('http://localhost:3000/signup', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    credentials: 'include',
                    body: JSON.stringify({
                        firstName: userInfo.firstName,
                        lastName: userInfo.lastName,
                        email: userInfo.email,
                        password: userInfo.password,
                    }),
                })
                if (res.ok) {
                    const data = await res.json();
                    setSuccess({message: 'User created successfully'});
                    console.log('33', data);
                    console.log('34', success);
                    setError(null);
                } else {
                    const data = await res.json();
                    setError(data);
                    console.log('38', data);
                    console.log('39', error);
                    setSuccess(null);
                }
            } else {
                setError({
                    errors: [{
                        path: 'confirmPassword',
                        msg: 'Passwords do not match'
                    }]
                })
            }
        } catch (err) {
            console.log('44', err);
            setError(err);
            console.log('46', error);
            
        }
    }

    useEffect(() => {
        console.log('69', error);
    })

    return (
        <section className="signup-section">
            <div className="signup-form-container">
                <h2>Create Your Account</h2>
                <p>Join our community of bloggers today!</p>
                <form className="signup-form" onSubmit={handleSubmission}>
                    <label htmlFor="firstName">First name</label>
                    <input type="text" id="firstName" name="firstName" value={userInfo.firstName} onChange={handleChange} required />
                    {error && error.errors 
                    ? error.errors.map(err => (
                        err.path === 'firstName'? <p className="error"> {err.msg} </p> : null
                        )) 
                    : null}

                    <label htmlFor="lastName">Last name</label>
                    <input type="text" id="lastName" name="lastName" value={userInfo.lastName} onChange={handleChange} required />
                    {error && error.errors 
                    ? error.errors.map(err => (
                        err.path === 'lastName'? <p className="error"> {err.msg} </p> : null
                        )) 
                    : null}

                    <label htmlFor="email">Email</label>
                    <input type="email" id="email" name="email" value={userInfo.email} onChange={handleChange} required />
                    {error && error.errors 
                    ? error.errors.map(err => (
                        err.path === 'email'? <p className="error"> {err.msg} </p> : null
                        )) 
                    : null}
                    {error && error.message ? <p className="error">{error.message}</p>: null}

                    <label htmlFor="password">Password</label>
                    <input type="password" id="password" name="password" value={userInfo.password} onChange={handleChange} required />
                    {error && error.errors 
                    ? error.errors.map(err => (
                        err.path === 'password'? <p className="error"> {err.msg} </p> : null
                        )) 
                    : null}

                    <label htmlFor="confirmPassword">Confirm Password</label>
                    <input type="password" id="confirmPassword" name="confirmPassword" value={userInfo.confirmPassword} onChange={handleChange} required />
                    {error && error.errors 
                    ? error.errors.map(err => (
                        err.path === 'confirmPassword'? <p className="error"> {err.msg} </p> : null
                        )) 
                    : null}

                    <button type="submit" className="submit-btn">Sign Up</button>
                </form>
            </div>
        </section>
    )
}

export default Signup;



import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../components/AuthContext";
import { useNavigate } from "react-router-dom";
import Posts from "../components/Posts";
import Aside from "../components/Aside";

const Dashboard = () => {
    const navigate = useNavigate();
    const { user, loading, userDetails } = useContext(AuthContext);

    useEffect(() => {
        if (!user) {
            navigate('/login');
        }
    }, [])

    if (loading) {
        return <h2>Loading...</h2>;  // Show loading indicator while fetching user data
    }

    return (
        <section>
            {user && userDetails ? (
                <div className="dashboard-con">
                    <Aside />
                    <div>
                        
                    </div>
                    <h1>Hello {userDetails.firstName}</h1>
                    <Posts />
                </div>
            ) : null}
        </section>
    );
};

export default Dashboard;




import { useContext, useEffect, useRef, useState } from 'react';
import { Editor } from '@tinymce/tinymce-react';
import { AuthContext } from './AuthContext';
import { useNavigate } from 'react-router-dom';

const CreateBlog = () => {

    const navigate = useNavigate();
    const { user } = useContext(AuthContext);
    
    const [blogData, setBlogData] = useState({
        title: '',
        content: '',
    });

    const handleChange = (e) => {
        const { name, value } = e.target;
        setBlogData({
            ...blogData,
            [name]: value,
        })
    }

    const handleEditorChange = (content, editor) => {
        setBlogData(p_state => ({
            ...p_state,
            content: content
        }))
    }

    const [isEditorReady, setEditorReady] = useState(false);
    const [loading, setLoading] = useState(true);
    const [isBlogCreated, setBlogCreated] = useState(false);

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const res = await fetch(`http://localhost:3000/posts/${user.user.id}/create`, {
                method: 'POST',
                credentials: 'include',
                body: JSON.stringify(blogData),
                headers: {
                    'Content-Type': 'application/json',
                }
            })
            if (res.ok) {
                const data = await res.json();
                console.log(data);
                setBlogCreated(true);
            } else {
                setBlogCreated(false);
            }
        } catch (err) {
            console.log(err);
        }
    }

    function handleEditorLoading() {
        setEditorReady(true);
        setLoading(false);
    }


    useEffect(() => {
        if (!loading) {
            if (!user) {
                navigate('/login');
                return;
            }
        }
        if (isBlogCreated) {
            navigate('/myblogs');
        }
    }, [user, loading, isBlogCreated])

    const TINY_API = import.meta.env.VITE_TINYMCE_API_KEY
    return (
        <>
        {user ? 
            <div className='blog-editor-container'>
                <form className='blog-form' onSubmit={handleSubmit}> 
                    {!isEditorReady ? 
                        <h2>Loading editor...</h2>
                    :
                    <div className='blog-title'>
                        <label htmlFor="title">Title for the blog</label>
                        <input type="text" name='title' id='title' value={blogData.title} onChange={handleChange} />
                    </div>
                    }
                    <Editor 
                        
                        apiKey={TINY_API}
                        value={blogData.content}
                        init={{
                            height: 500,
                            menubar: false,
                            plugins: [
                                'advlist', 'autolink', 'lists', 'link',
                                'charmap', 'preview', 'anchor', 'searchreplace',
                                'visualblocks', 'code', 'fullscreen', 
                                'insertdatetime', 'media', 'table', 'code',
                                'help', 'wordcount',
                            ],
                            toolbar: 'undo redo | blocks | ' + 
                            'bold italic forecolor | alignleft aligncenter ' +
                            'alignright alignjustify | bullist numlist outdent indent | ' +
                            'removeformat | help',
                            content_style: 'body { fontfamily: sans-serif; font-size: 14px }'
                        }}
                        onInit={handleEditorLoading}
                        onEditorChange={handleEditorChange}
                        
                        />
                    <button type='submit' className='submit-btn'>Publish blog</button>
                </form>
            </div>
            : null}
        
        </>
    )
}

export default CreateBlog;




import { useContext, useEffect, useState } from "react";
import { AuthContext } from "../components/AuthContext";
import { useNavigate } from "react-router-dom";
import SBlog from "../components/SBlog";

const MyBlogs = () => {
    const { user, userDetails ,loading } = useContext(AuthContext);
    const [allBlogs, setAllBlogs] = useState([]);
    const [error, setError] = useState(false);
    const navigate = useNavigate();

    useEffect(() => {
        console.log('all', allBlogs);
        console.log('user', user);
        console.log('userdetails', userDetails);
        if (loading) return;
        if (user) {
            getBlogs();
        } else if (!user) {
            navigate('/login')
        }
    }, [user, userDetails]);

    const getBlogs = async () => {
        try {
            const res = await fetch(`http://localhost:3000/posts/${user.user.id}`, {
                method: 'GET',
                credentials: 'include',
                headers: {
                    'Content-Type': 'application/json'
                }
            })
            if (res.ok) {
                const data = await res.json();
                setError(false);
                console.log(data);
                setAllBlogs(data);
            }
        } catch (err) {
            console.log(err);
            setError(true);
        }
    }

    if (loading) {
        return <h2>Loading your blogs...</h2>
    }

    if (error) {
        return <h2 className="error">Error fetching blogs. Try again</h2>
    }
    return (
        <div className="my-all-blogs">
            <h2>My blogs</h2>
            {allBlogs && allBlogs.length > 0 ? 
                allBlogs.map(blog => <SBlog key={blog.id}
                    postTitle={blog.postTitle}
                    authorName={blog.author.firstName}
                    createdAt={blog.createdAt}
                    updatedAt={blog.updatedAt}
                />)
                :
                <h2 className="error">No blogs to show <a href="/create">Create new</a></h2>
            }
        </div>

    )
}

export default MyBlogs;




const Aside = () => {
    return (
        <div className="side-menu">
            <div className="links"><a href="/create" className="aside-links">Create Blog</a></div>
            <div className="links"><a href="/myblogs" className="aside-links">Show my Blogs</a></div>
            <div className="links"><a href="/allblogs" className="aside-links">Read blogs</a></div>
            <div className="links"><a href="/editprofile" className="aside-links">Edit Profile</a></div>
        </div>
    )
}

export default Aside;




const SBlog = ({postTitle, createdAt, updatedAt, authorName}) => {
    return (
        <div className="small-blog-card">
            <h2>{postTitle}</h2>
            <h5>{createdAt}</h5>
            {createdAt !== updatedAt ?
            <h5>{updatedAt}</h5> :
            null
            }
            <h2>{authorName}</h2>
        </div>
    )
}

export default SBlog;


style these for me make it fantastic which looks attractive