Styling and structure of main recipes page.

This commit is contained in:
Jesse Brault 2024-08-01 10:11:55 -05:00
parent 159f0177eb
commit a983d49f22
12 changed files with 199 additions and 69 deletions

View File

@ -0,0 +1,11 @@
import './footer.module.css'
const Footer = () => {
return (
<footer>
<p>Copyright 2024 Jesse R. Brault. All rights reserved.</p>
</footer>
)
}
export default Footer

View File

@ -0,0 +1,3 @@
footer {
padding: 20px;
}

View File

@ -0,0 +1,27 @@
import { useNavigate, useRouter } from '@tanstack/react-router'
import { useAuth } from '../../auth'
import classes from './header.module.css'
const Header = () => {
const auth = useAuth()
const router = useRouter()
const navigate = useNavigate()
const onLogout = async () => {
auth.clearToken(async () => {
await router.invalidate()
await navigate({ to: '/login' })
})
}
return (
<header>
<h1 className={classes.mealsMadeEasy}>Meals Made Easy</h1>
<nav>
<button onClick={onLogout}>Logout</button>
</nav>
</header>
)
}
export default Header

View File

@ -0,0 +1,11 @@
header {
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--primary-red);
padding: 20px;
}
.meals-made-easy {
color: var(--primary-white);
}

View File

@ -38,7 +38,9 @@ const RecipeCard = ({
/> />
</Link> </Link>
<div className={classes.infoContainer}> <div className={classes.infoContainer}>
<div className={classes.infoRow}>
<Link <Link
className={classes.titleLink}
to="/recipes/$username/$slug" to="/recipes/$username/$slug"
params={{ params={{
username: ownerUsername, username: ownerUsername,
@ -47,12 +49,21 @@ const RecipeCard = ({
> >
<h1 className={classes.title}>{title}</h1> <h1 className={classes.title}>{title}</h1>
</Link> </Link>
<span> <span className={classes.starInfo}>
<FontAwesomeIcon icon="star" size="sm" /> <FontAwesomeIcon
icon="star"
className={classes.star}
size="sm"
/>
{starCount} {starCount}
</span> </span>
<span> </div>
<FontAwesomeIcon icon="user" /> <div className={classes.infoRow}>
<span className={classes.userInfo}>
<FontAwesomeIcon
icon="user"
className={classes.userIcon}
/>
{ownerUsername} {ownerUsername}
</span> </span>
{isPublic ? ( {isPublic ? (
@ -61,6 +72,7 @@ const RecipeCard = ({
<FontAwesomeIcon icon="lock" size="sm" /> <FontAwesomeIcon icon="lock" size="sm" />
)} )}
</div> </div>
</div>
</article> </article>
) )
} }

View File

@ -1,18 +1,44 @@
.recipe-card { .recipe-card {
max-width: 400px; justify-self: stretch;
border: 1px solid black;
} }
.recipe-image { .recipe-image {
max-width: 100%; width: 100%;
max-height: 200px;
object-fit: cover;
} }
.info-container { .info-container {
display: grid; display: flex;
grid-template-columns: repeat(2, 1fr); flex-direction: column;
align-items: baseline; row-gap: 10px;
}
.info-row {
display: flex;
justify-content: space-between;
} }
.title { .title {
margin-block: 0; margin-block: 0;
font-size: 18px;
}
.star-info {
display: flex;
column-gap: 5px;
align-items: center;
}
.star {
color: var(--primary-yellow);
}
.user-info {
display: flex;
column-gap: 5px;
}
.user-icon {
color: var(--primary-red);
} }

24
src/main.css Normal file
View File

@ -0,0 +1,24 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
:root {
--primary-white: #ffffff;
--primary-red: #91351d;
--primary-yellow: #ffb61d;
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
}
a {
color: var(--primary-red);
}
a:visited {
color: var(--primary-red);
}
a:hover {
color: hsl(from var(--primary-red) h s l / 0.9);
}

View File

@ -6,6 +6,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { AuthProvider, useAuth } from './auth' import { AuthProvider, useAuth } from './auth'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons' import { fas } from '@fortawesome/free-solid-svg-icons'
import './main.css'
// Font-Awesome: load icons // Font-Awesome: load icons
library.add(fas) library.add(fas)

View File

@ -4,6 +4,7 @@ import { useState } from 'react'
import { useAuth } from '../../auth' import { useAuth } from '../../auth'
import { ApiError } from '../../api/ApiError' import { ApiError } from '../../api/ApiError'
import RecipeCard from '../../components/recipe-card/RecipeCard' import RecipeCard from '../../components/recipe-card/RecipeCard'
import classes from './recipes.module.css'
const Recipes = () => { const Recipes = () => {
const [pageNumber, setPageNumber] = useState(0) const [pageNumber, setPageNumber] = useState(0)
@ -35,7 +36,11 @@ const Recipes = () => {
return <p>Error: {error.message}</p> return <p>Error: {error.message}</p>
} }
} else { } else {
return data.content.map(view => ( return (
<>
<h1>Recipes</h1>
<section className={classes.recipeList}>
{data.content.map(view => (
<RecipeCard <RecipeCard
key={view.id} key={view.id}
title={view.title} title={view.title}
@ -43,12 +48,17 @@ const Recipes = () => {
slug={view.slug} slug={view.slug}
mainImageUrl={view.mainImage.url} mainImageUrl={view.mainImage.url}
mainImageAlt={ mainImageAlt={
view.mainImage.alt ? view.mainImage.alt : undefined view.mainImage.alt
? view.mainImage.alt
: undefined
} }
starCount={view.starCount} starCount={view.starCount}
isPublic={view.isPublic} isPublic={view.isPublic}
/> />
)) ))}
</section>
</>
)
} }
} }

View File

@ -0,0 +1,23 @@
.recipe-list {
display: grid;
justify-content: space-evenly;
gap: 20px;
}
@media screen and (min-width: 900px) {
.recipe-list {
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (min-width: 1300px) {
.recipe-list {
grid-template-columns: repeat(3, 1fr);
}
}
@media screen and (min-width: 1700px) {
.recipe-list {
grid-template-columns: repeat(4, 1fr);
}
}

View File

@ -0,0 +1,3 @@
main {
padding: 20px;
}

View File

@ -1,39 +1,18 @@
import { import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
Outlet,
createRootRouteWithContext,
useNavigate,
useRouter
} from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools' import { TanStackRouterDevtools } from '@tanstack/router-devtools'
import RouterContext from '../RouterContext' import RouterContext from '../RouterContext'
import { useAuth } from '../auth' import Header from '../components/header/Header'
import Footer from '../components/footer/Footer'
import './__root.module.css'
const RootLayout = () => { const RootLayout = () => {
const auth = useAuth()
const router = useRouter()
const navigate = useNavigate()
const onLogout = async () => {
auth.clearToken(async () => {
await router.invalidate()
await navigate({ to: '/login' })
})
}
return ( return (
<> <>
<header> <Header />
<h1>Meals Made Easy</h1>
<nav>
<button onClick={onLogout}>Logout</button>
</nav>
</header>
<main> <main>
<Outlet /> <Outlet />
</main> </main>
<footer> <Footer />
<p>Copyright 2024 Jesse R. Brault. All rights reserved.</p>
</footer>
<TanStackRouterDevtools position="bottom-right" /> <TanStackRouterDevtools position="bottom-right" />
</> </>
) )