Frontend Serverless with React and GraphQL

Start at the beginning and work your way through this project. The code for each step as well as the finished project can be found in the Github repository.


26. Add a Favorites Page

Objective: Add a recipes list page at the route `/favorites` that contain all the items that the user has liked.

Now that we have a way for users to like recipes, let's create a page that will list all of the user's favorite recipes. This favorites page will be similar to the home page except we pass different props into the <RecipesList> component- in particular the different queryType and options object.

Let's create a favorites page:

pages/favorites.tsx

import { MainLayout } from '../components/layout/MainLayout';
import styled from 'styled-components';
import { useFetchUser } from '../utils/user';
import * as _ from 'lodash';
import { Loading } from '../components/notify/Loading';
import Router from 'next/router';
import { RecipesList, queryEnum } from '../components/RecipesList';
const StyledHeader = styled.h1`
${({ theme }) => `
font-size: 2em;
text-align: left;
padding: ${theme['padding-small']} ${theme['padding-small']};
`}
`;
const Favorites = () => {
const { user, loading: isFetchUser } = useFetchUser();
const owner = _.get(user, 'sub');
const options = owner ? { variables: { where: { user: owner } } } : {};
if (isFetchUser) return <Loading />;
if (!user) {
Router.replace('/');
}
return (
<MainLayout title="Recipes">
<StyledHeader>My Favorites</StyledHeader>
<RecipesList
parentRoute="recipe"
queryType={queryEnum.userLikes}
options={options}
/>
</MainLayout>
);
};
export default Favorites;

Next, as a bit of cleanup, add the <Loading /> component to the my-recipes/index.tsx page. Just like the home page, we don't want to load any recipes lists on a page before the loading has completed.

pages/my-recipes/index.tsx

import styled from 'styled-components';
import { Layout, Menu } from 'antd';
import Link from 'next/link';
import { useFetchUser } from '../../utils/user';
const { Header } = Layout;
const TitleContainer = styled.div`
${({ theme }) => `
background-color: ${theme['header-color']};
width: 50%;
display: flex;
align-items: center;
@media (max-width: 890px){
visibility: hidden;
width: 0;
}
`}
`;
const Title = styled.div`
${({ theme }) => `
text-align: left;
display: flex;
line-height: 50px;
div {
width: 100%;
padding-left: ${theme['padding-small']};
}
h2 {
display: inline;
color: inherit;
}
img {
width: 64px;
}
p {
line-height: 0;
}
`}
`;
const StyledHeader = styled(Header)`
${({ theme }) => `
background-color: ${theme['header-color']};
border-bottom-color: ${theme['header-border-color']};
border-bottom-right: 1px;
border-bottom-style: solid;
text-align: right;
display: flex;
li {
font-size: ${theme['font-size-md']};
}
`}
`;
const StyledMenu = styled(Menu)`
border-bottom-width: 0px;
width: 50%;
@media (max-width: 890px) {
width: 100%;
}
`;
export const MainNavbar = () => {
const { user, loading } = useFetchUser();
return (
<StyledHeader>
<TitleContainer>
<Title>
<img src="/logo.svg" alt="Next Chop Logo" />
<div>
<h2>The Next Chop</h2>
<p>A recipe discovery app powered by Next.js</p>
</div>
</Title>
</TitleContainer>
<StyledMenu
theme="light"
mode="horizontal"
style={{ lineHeight: '64px' }}
>
<Menu.Item key="/">
<Link href="/">
<a>Home</a>
</Link>
</Menu.Item>
{user && !loading
? [
<Menu.Item key="/favorites">
<Link href="/favorites">
<a>Favorites</a>
</Link>
</Menu.Item>,
<Menu.Item key="/my-recipes">
<Link href="/my-recipes">
<a>My Recipes</a>
</Link>
</Menu.Item>,
<Menu.Item key="/api/logout">
<Link href="/api/logout">
<a>Logout</a>
</Link>
</Menu.Item>,
]
: [
<Menu.Item key="/api/login">
<Link href="/api/login">
<a>Login</a>
</Link>
</Menu.Item>,
]}
</StyledMenu>
</StyledHeader>
);
};