Styling and structure of main recipes page.
This commit is contained in:
parent
159f0177eb
commit
a983d49f22
11
src/components/footer/Footer.tsx
Normal file
11
src/components/footer/Footer.tsx
Normal 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
|
3
src/components/footer/footer.module.css
Normal file
3
src/components/footer/footer.module.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
footer {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
27
src/components/header/Header.tsx
Normal file
27
src/components/header/Header.tsx
Normal 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
|
11
src/components/header/header.module.css
Normal file
11
src/components/header/header.module.css
Normal 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);
|
||||||
|
}
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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
24
src/main.css
Normal 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);
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
src/pages/recipes/recipes.module.css
Normal file
23
src/pages/recipes/recipes.module.css
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
3
src/routes/__root.module.css
Normal file
3
src/routes/__root.module.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
@ -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" />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user