Перейти к содержимому

Rest API

Подключение данных чреез API/JSON Server

./app/page.tsx
'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

./app/page.tsx
import PostList from '../components/PostList'
// CARDS
async 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}
/>
)
}
./components/PostList.tsx
'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>
</>
)
}