Creating the Dashboard and Interacting with Backend | LinkTree Project
This tutorial is a part of the Series ‘Complete FullStack LinkTree Project with MERN (opens in a new tab)‘. This Project covers beginner to Intermediate level implementation of MongoDB, Express, ReactJS, NodeJS, and NextJS.
Project Walkthrough
Users can signup on the LinkTree website and start creating their LinkTree immediately. They get a user profile editing page, an edit or customize links page, a Dashboard to edit and track the links, and many more.
I’ve created a YouTube series on this Project in 6 parts. This Code snippet is from the 4th part where we create a Dashboard and interact with the backend.
Frontend
import React, { useEffect, useState, useContext } from "react";
import LinkBox from "../components/LinkBox";
import UserHeader from "../components/UserHeader";
import { toast } from "react-toastify";
import UserContext from "../context/userContext";
const dashboard = () => {
const [data, setData] = useState({});
const { setUserData } = useContext(UserContext);
useEffect(() => {
if (!localStorage.getItem("LinkTreeToken"))
return (window.location.href = "/login");
fetch("http://localhost:8080/data/dashboard", {
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify({
tokenMail: localStorage.getItem("LinkTreeToken")
})
})
.then((res) => res.json())
.then((data) => {
if (data.status === "error") return toast.error("Error happened");
setData(data.userData);
setUserData(data.userData);
localStorage.setItem("userHandle", data.userData.handle);
// toast.success(data.message)
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<>
<div className="">
<UserHeader />
<main>
<section className="grid md:grid-cols-2 xl:grid-cols-4 gap-5">
<LinkBox
lbTitle="Links"
lbNumber={data.links}
lbSvg="url"
lbTheme="red"
/>
<LinkBox
lbTitle="Growth"
lbNumber="30%"
lbSvg="growth"
lbTheme="blue"
/>
<LinkBox
lbTitle="Links"
lbNumber="12"
lbSvg="email"
lbTheme="red"
/>
<LinkBox
lbTitle="Growth"
lbNumber="30%"
lbSvg="ig"
lbTheme="blue"
/>
</section>
<section></section>
</main>
</div>
</>
);
};
export default dashboard;
import React, {useContext, useEffect} from 'react'
import { useRouter } from 'next/router';
import Link from 'next/link';
import UserContext from '../context/userContext';
const UserHeader = () => {
// const {name, role, avatar, handle, links} = data;
const router = useRouter();
const handleLogout = ()=>{
localStorage.removeItem('LinkTreeToken');
router.push('/login');
}
const {userData, setUserData} = useContext(UserContext);
const {role, avatar, handle} = userData;
useEffect(()=>{
if(!localStorage.getItem('LinkTreeToken')) return window.location.href = "/login";
fetch('http://localhost:8080/data/dashboard', {
method: 'POST',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
tokenMail: localStorage.getItem('LinkTreeToken')
})
}).then(res=>res.json())
.then(data=>{
if(data.status==='error') return toast.error('Error happened');
// setData(data.userData);
console.log('logging from userHeader', data.userData);
setUserData(data.userData);
localStorage.setItem('userHandle', data.userData.handle);
// toast.success(data.message)
}).catch(err=>{
console.log(err);
})
}, [])
return (
<>
<header className='flex flex-row justify-between items-center'>
<div className="flex flex-col md:flex-row p-5">
<Link href="/edit/links">
<button className='inline-flex w-full md:w-auto px-5 py-3 text-purple-500 font-bold hover:text-purple-700 hover:bg-purple-100 rounded-md mb-3 border-2 border-purple-500'>
<img src="/svg/url.svg" className='w-6 mr-3'/>
Edit Links
</button>
</Link>
<Link href="/edit/profile">
<button className='inline-flex w-full md:w-auto px-5 py-3 text-red-500 font-bold hover:text-red-700 hover:bg-red-100 rounded-md mb-3 border-2 border-red-500 md:ml-4'>
<img src="/svg/user.svg" className='w-6 mr-3'/>
Edit profile
</button>
</Link>
</div>
<Link href={`http://localhost:3000/${handle}`}>
<div className="flex flex-row">
<div className='inline-flex mr-5 text-right items-center bg-gray-200 px-5 py-2 rounded-lg'>
<div className="text-xs md:text-md flex flex-col flex-wrap">
<span className='font-bold'>{handle}</span>
<span>{role} Pack</span>
</div>
<div className="user-img">
<img className='w-10 ml-5 rounded-full' src={avatar}/>
</div>
</div>
<img className='w-6 mr-5 cursor-pointer' src="/svg/notify.svg" alt="" />
<img className='w-6 mr-5 cursor-pointer' src="/svg/logout.svg" alt="" onClick={handleLogout}/>
</div>
</Link>
</header>
</>
)
}
export default UserHeader
components/LinkBox.js
import React from 'react'
const LinkBox = ({lbTitle, lbNumber, lbSvg, lbTheme}) => {
return (
<div className="flex items-center p-8 bg-white shadow border rounded-lg">
<div className={`bg-${lbTheme}-500` + " inline-flex flex-shrink-0 items-center justify-center h-16 w-16 rounded-full mr-6"}>
<img src={`/svg/${lbSvg}.svg`} className='w-6' />
</div>
<div className="">
<span className="inline-block text-2xl font-bold">{lbNumber}</span>
<span className='block text-gray-500'>{lbTitle}</span>
</div>
</div>
)
}
export default LinkBox
Backend
// Added dashboard data logic path to previous code in index.js
app.post("/data/dashboard", dashBoardData);
// Backend's dashboard.js controller
const User = require("../models/user");
const jwt_decode = require("jwt-decode");
const dashBoardData = async (req, res) => {
const { tokenMail } = req.body;
console.log(tokenMail);
try {
const decodedTokenMail = jwt_decode(tokenMail, process.env.SECRET_JWT);
const email = decodedTokenMail.email;
console.log("decoded email", email);
const user = await User.findOne({ email: email });
const userData = {
name: user.name,
role: user.role,
bio: user.bio,
avatar: user.avatar,
handle: user.handle,
links: user.links.length
};
return res.json({ message: "User loaded", userData, status: "Okay" });
} catch (err) {
return res.json({ status: "error", error: err.message });
}
};
module.exports = { dashBoardData };
Summing up
I hope this part of the tutorial on the Fullstack LinkTree Project was helpful to you. If you faced any problems while following the video or the source code snippets, comment below and I’ll reply as soon as possible. See you soon.