Rest API
Подключение данных чреез API/JSON Server
'use client'
import { useEffect, useState } from 'react'
export default function Page() { const [cards, setCards] = useState([])
// LoadMore const articlesShown = 2 const [loadMore, setLoadMore] = useState(articlesShown) const showMoreArticles = () => { setLoadMore(loadMore + articlesShown) }
useEffect(() => { fetch('https://website.com/cards', { cache: 'force-cache', }) .then((res) => res.json()) .then((data) => setCards(data)) }, [])
return ( <div className="max-w-2xl mx-auto py-4"> <div className="grid gap-4"> {cards.slice(0, loadMore).map((item: any) => ( <div key={item.id} className="p-4 border rounded-lg space-y-2"> <h2 className="tetx-xl font-bold">{item.title}</h2> </div> ))} </div>
<div className="text-center mt-8"> {loadMore < cards?.length && ( <div className="flex justify-center"> <button type="button" className="rounded-lg bg-blue-600 w-full py-4 text-white" onClick={showMoreArticles}> Load More </button> </div> )} </div> </div> )}
Posts Components
import PostList from '../components/PostList'
// CARDSasync function getCards() { const res = await fetch('https://website.com/cards', { cache: 'force-cache', }) if (!res.ok) { // This will activate the closest `error.js` Error Boundary throw new Error('Failed to fetch data') } return res.json()}
// Opt out of caching for all data requests in the route segment// export const dynamic = 'force-dynamic'
export default async function RestApi() { const cards = await getCards()
return ( <PostList cards={cards} /> )}
'use client'import React from 'react'import Link from 'next/link'import { useState } from 'react'import { Accordion, AccordionItem, Avatar } from '@nextui-org/react'import { IconWindowMaximize } from '@tabler/icons-react'
type Props = { cards: Cards[]}
type Cards = any
export default function PostList({ cards }: Props) { const articlesShown = 24 const [loadMore, setLoadMore] = useState(articlesShown) const showMoreArticles = () => { setLoadMore(loadMore + articlesShown) }
return ( <> <div className="mx-auto max-w-screen-md py-8 px-4 grid gap-4"> <h1 className="text-2xl font-bold text-blue-500">Rest API</h1> <hr />
{/* CATEGORIES */} <div className="grid gap-4"> <div className="flex flex-wrap justify-between sm:items-center"> <h2 className="text-lg font-bold">Categories</h2> <Link className="flex items-center hover:text-blue-500 gap-2" href="https://sp.thefubon.dev/categories" target="_blank"> View JSON <IconWindowMaximize size={18} /> </Link> </div> <div className="grid gap-4"> <Accordion variant="bordered"> {categories.map((сategory: any) => { return ( <AccordionItem key={сategory.id} aria-label={сategory.title} title={сategory.title}> <div className="grid gap-4 p-4"> <div>Id: {сategory.id}</div> <div>Title Id: {сategory.titleId}</div> <h3 className="font-bold">{сategory.title}</h3> <div>{сategory.meta}</div> <div>{сategory.price}</div> <div>{сategory.period}</div> <div>{сategory.button}</div> </div> </AccordionItem> ) })} </Accordion> </div> </div>
{/* CARDS */} <div className="grid gap-4"> <div className="flex flex-wrap justify-between sm:items-center"> <h2 className="text-lg font-bold">Cards</h2> <Link className="flex items-center hover:text-blue-500 gap-2" href="https://sp.thefubon.dev/cards" target="_blank"> View JSON <IconWindowMaximize size={18} /> </Link> </div> <div className="grid gap-4"> <Accordion variant="bordered"> {cards.slice(0, loadMore).map((item: any) => { return ( <AccordionItem key={item.id} aria-label={item.title} startContent={ <Avatar isBordered //color="primary" radius="lg" src={'https://sp.thefubon.dev' + item.images.logo} /> } subtitle={item.meta} title={item.title}> <div className="grid gap-4 p-4 text-balance"> <div>Id: {item.id}</div> <div>Type: {item.type}</div> <div>Category: {item.category}</div> <div>Title Id: {item.titleId}</div> <h3 className="font-bold">{item.title}</h3> {item.meta && <div>{item.meta}</div>} {item.descriptio && <div>{item.description}</div>}
{item.offers && ( <div className="flex flex-col gap-2"> {item.offers.map((offer: any, i: any) => { return ( <div key={i} className="flex flex-wrap gap-2"> <div>{offer.title}</div> <div> <img src={ 'https://sp.thefubon.dev' + offer.logo } width={64} height={64} alt="" /> </div> <div>{offer.benefit}</div> </div> ) })} </div> )} {item.benefits && ( <ul className="list-disc list-inside"> {item.benefits.map((benefit: any, i: any) => { return <li key={i}>{benefit.text}</li> })} </ul> )} {item.use && ( <div className="flex flex-col gap-2"> {item.use.map((list: any, i: any) => { return ( <div key={i} className="inline-flex items-center gap-x-2"> <div className="w-6 h-6 bg-blue-500 text-white rounded-full flex justify-center items-center text-sm"> {list.number} </div> <div className="flex-1">{list.text}</div> </div> ) })} </div> )} {item.term && <div>{item.term}</div>} {item.about && <div>{item.about}</div>} {item.website && ( <div className="flex flex-col"> <div>{item.website.name}</div> <div>{item.website.url}</div> </div> )} <div className="flex items-center gap-4"> <div> <img className={ item.images.border && 'border rounded-xl overflow-hidden' } src={'https://sp.thefubon.dev' + item.images.logo} width={48} height={48} alt="" /> </div> <div> <img src={ 'https://sp.thefubon.dev' + item.images.cover } width={200} height={200} alt="" /> </div> {item.images.thumb && ( <div> <img className="rounded-xl" src={ 'https://sp.thefubon.dev' + item.images.thumb } width={262} height={164} alt="" /> </div> )} </div> <div> <button className="py-2 px-6 bg-blue-500 rounded-xl text-white"> {item.button} </button> </div> </div> </AccordionItem> ) })} </Accordion> </div>
<div className="text-center"> {loadMore < cards?.length && ( <div className="flex justify-center"> <button type="button" className="rounded-lg bg-blue-500 hover:bg-opacity-75 duration-150 px-12 py-4 text-white w-full" onClick={showMoreArticles}> Загрузить </button> </div> )} </div> </div> </div> </div> </> )}