Fetching/query of RecipeInfo working at a basic level.
This commit is contained in:
parent
c7f9a83cca
commit
501069e3af
9
src/api/ApiError.ts
Normal file
9
src/api/ApiError.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export class ApiError extends Error {
|
||||||
|
status: number
|
||||||
|
|
||||||
|
constructor(status: number, message?: string) {
|
||||||
|
super(message)
|
||||||
|
this.status = status
|
||||||
|
Object.setPrototypeOf(this, ApiError.prototype)
|
||||||
|
}
|
||||||
|
}
|
43
src/api/getRecipeInfos.ts
Normal file
43
src/api/getRecipeInfos.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { ApiError } from './ApiError'
|
||||||
|
|
||||||
|
export interface RecipeInfoView {
|
||||||
|
id: number
|
||||||
|
updated: Date
|
||||||
|
title: string
|
||||||
|
ownerId: number
|
||||||
|
ownerUsername: string
|
||||||
|
isPublic: boolean
|
||||||
|
starCount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecipeInfosView {
|
||||||
|
pageNumber: number
|
||||||
|
pageSize: number
|
||||||
|
content: RecipeInfoView[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRecipeInfos = async (
|
||||||
|
token: string | null,
|
||||||
|
pageNumber: number,
|
||||||
|
pageSize: number
|
||||||
|
): Promise<RecipeInfosView> => {
|
||||||
|
const headers = new Headers()
|
||||||
|
if (token !== null) {
|
||||||
|
headers.set('Authorization', `Bearer ${token}`)
|
||||||
|
}
|
||||||
|
const response = await fetch(
|
||||||
|
import.meta.env.VITE_MME_API_URL +
|
||||||
|
`/recipes?page=${pageNumber}&size=${pageSize}`,
|
||||||
|
{
|
||||||
|
headers,
|
||||||
|
mode: 'cors'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (response.ok) {
|
||||||
|
return (await response.json()) as RecipeInfosView
|
||||||
|
} else {
|
||||||
|
throw new ApiError(response.status, response.statusText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getRecipeInfos
|
@ -2,20 +2,36 @@ export interface RecipeCardProps {
|
|||||||
title: string
|
title: string
|
||||||
owner?: string
|
owner?: string
|
||||||
imgUrl?: string
|
imgUrl?: string
|
||||||
rating?: number
|
starCount?: number
|
||||||
|
isPublic?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const RecipeCard = ({ title, owner, imgUrl, rating }: RecipeCardProps) => {
|
const RecipeCard = ({
|
||||||
|
title,
|
||||||
|
owner,
|
||||||
|
imgUrl,
|
||||||
|
starCount,
|
||||||
|
isPublic
|
||||||
|
}: RecipeCardProps) => {
|
||||||
return (
|
return (
|
||||||
<article>
|
<article>
|
||||||
{imgUrl ? <img src={imgUrl} /> : null}
|
{imgUrl ? <img src={imgUrl} /> : null}
|
||||||
<div className="title-rating-container">
|
<div className="title-rating-container">
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
{rating ? <span>{Math.round(rating)}/5</span> : null}
|
{starCount ? <span>{starCount}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="owner-container">
|
<div className="owner-container">
|
||||||
{owner ? <p>@{owner}</p> : null}
|
{owner ? <p>@{owner}</p> : null}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="is-public-container">
|
||||||
|
{isPublic !== undefined ? (
|
||||||
|
isPublic ? (
|
||||||
|
<p>public</p>
|
||||||
|
) : (
|
||||||
|
<p>not public</p>
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
44
src/pages/recipes/Recipes.tsx
Normal file
44
src/pages/recipes/Recipes.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import getRecipeInfos from '../../api/getRecipeInfos'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useAuth } from '../../auth'
|
||||||
|
import { ApiError } from '../../api/ApiError'
|
||||||
|
import RecipeCard from '../../components/recipe-card/RecipeCard'
|
||||||
|
|
||||||
|
const Recipes = () => {
|
||||||
|
const [pageNumber, setPageNumber] = useState(0)
|
||||||
|
const [pageSize, setPageSize] = useState(20)
|
||||||
|
|
||||||
|
const { token } = useAuth()
|
||||||
|
|
||||||
|
const { data, isPending, error } = useQuery({
|
||||||
|
queryKey: ['recipeInfos'],
|
||||||
|
queryFn: () => getRecipeInfos(token, pageNumber, pageSize)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isPending) {
|
||||||
|
return <p>Loading...</p>
|
||||||
|
} else if (error) {
|
||||||
|
if (error instanceof ApiError) {
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
ApiError: {error.status} {error.message}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return <p>Error: {error.message}</p>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return data.content.map(view => (
|
||||||
|
<RecipeCard
|
||||||
|
key={view.id}
|
||||||
|
title={view.title}
|
||||||
|
owner={view.ownerUsername}
|
||||||
|
starCount={view.starCount}
|
||||||
|
isPublic={view.isPublic}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Recipes
|
@ -11,12 +11,18 @@
|
|||||||
// Import Routes
|
// Import Routes
|
||||||
|
|
||||||
import { Route as rootRoute } from './routes/__root'
|
import { Route as rootRoute } from './routes/__root'
|
||||||
|
import { Route as RecipesImport } from './routes/recipes'
|
||||||
import { Route as LoginImport } from './routes/login'
|
import { Route as LoginImport } from './routes/login'
|
||||||
import { Route as AuthImport } from './routes/_auth'
|
import { Route as AuthImport } from './routes/_auth'
|
||||||
import { Route as AuthIndexImport } from './routes/_auth/index'
|
import { Route as AuthIndexImport } from './routes/_auth/index'
|
||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
|
|
||||||
|
const RecipesRoute = RecipesImport.update({
|
||||||
|
path: '/recipes',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const LoginRoute = LoginImport.update({
|
const LoginRoute = LoginImport.update({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
@ -50,6 +56,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof LoginImport
|
preLoaderRoute: typeof LoginImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/recipes': {
|
||||||
|
id: '/recipes'
|
||||||
|
path: '/recipes'
|
||||||
|
fullPath: '/recipes'
|
||||||
|
preLoaderRoute: typeof RecipesImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/_auth/': {
|
'/_auth/': {
|
||||||
id: '/_auth/'
|
id: '/_auth/'
|
||||||
path: '/'
|
path: '/'
|
||||||
@ -65,6 +78,7 @@ declare module '@tanstack/react-router' {
|
|||||||
export const routeTree = rootRoute.addChildren({
|
export const routeTree = rootRoute.addChildren({
|
||||||
AuthRoute: AuthRoute.addChildren({ AuthIndexRoute }),
|
AuthRoute: AuthRoute.addChildren({ AuthIndexRoute }),
|
||||||
LoginRoute,
|
LoginRoute,
|
||||||
|
RecipesRoute,
|
||||||
})
|
})
|
||||||
|
|
||||||
/* prettier-ignore-end */
|
/* prettier-ignore-end */
|
||||||
@ -76,7 +90,8 @@ export const routeTree = rootRoute.addChildren({
|
|||||||
"filePath": "__root.tsx",
|
"filePath": "__root.tsx",
|
||||||
"children": [
|
"children": [
|
||||||
"/_auth",
|
"/_auth",
|
||||||
"/login"
|
"/login",
|
||||||
|
"/recipes"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/_auth": {
|
"/_auth": {
|
||||||
@ -88,6 +103,9 @@ export const routeTree = rootRoute.addChildren({
|
|||||||
"/login": {
|
"/login": {
|
||||||
"filePath": "login.tsx"
|
"filePath": "login.tsx"
|
||||||
},
|
},
|
||||||
|
"/recipes": {
|
||||||
|
"filePath": "recipes.tsx"
|
||||||
|
},
|
||||||
"/_auth/": {
|
"/_auth/": {
|
||||||
"filePath": "_auth/index.tsx",
|
"filePath": "_auth/index.tsx",
|
||||||
"parent": "/_auth"
|
"parent": "/_auth"
|
||||||
|
6
src/routes/recipes.tsx
Normal file
6
src/routes/recipes.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
import Recipes from '../pages/recipes/Recipes'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/recipes')({
|
||||||
|
component: Recipes
|
||||||
|
})
|
@ -14,7 +14,8 @@ export const Primary: Story = {
|
|||||||
args: {
|
args: {
|
||||||
title: 'My Recipe',
|
title: 'My Recipe',
|
||||||
owner: 'JesseBrault',
|
owner: 'JesseBrault',
|
||||||
rating: 5,
|
starCount: 7,
|
||||||
|
isPublic: true,
|
||||||
imgUrl: 'https://www.simplyrecipes.com/thmb/L4nBpdZCubnbGtVbOW90JQSBVWc=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc():format(webp)/Simply-Recipes-Easy-Banana-Bread-LEAD-2-2-63dd39af009945d58f5bf4c2ae8d6070.jpg'
|
imgUrl: 'https://www.simplyrecipes.com/thmb/L4nBpdZCubnbGtVbOW90JQSBVWc=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc():format(webp)/Simply-Recipes-Easy-Banana-Bread-LEAD-2-2-63dd39af009945d58f5bf4c2ae8d6070.jpg'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user