Commit 45a48db0 by Madhankumar

blog app changes

parent a3c4ae7a
"use client";
import Header from "@components/base/header";
import { useAppContext } from "@context/index";
import { useRouter } from "next/navigation";
export default function PageHeader() {
const { theme, toggleTheme, setInput } = useAppContext();
const { theme, toggleTheme } = useAppContext();
const router = useRouter();
let currentPage = 1;
const handleSearch = (value) => {
setInput(value);
if (value) {
router.push(`/?page=${currentPage}&search=${value}`, undefined, {
shallow: true,
});
} else {
router.push(`/?page=${currentPage}`, undefined, { shallow: true });
}
};
const handleClose = () => {
setInput("");
router.push(`/?page=${currentPage}`, undefined, { shallow: true });
};
return (
......
import { ThemeProvider } from "@context/index";
import "@styles/global.css";
export default function App({ Component, pageProps }) {
return (
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
);
}
// api/search/index.js
import { getPosts } from "@lib/api";
import { NextResponse } from "next/server";
export async function GET(request) {
const { searchParams } = new URL(request.url);
const page = searchParams.get("page");
const search = searchParams.get("search");
const { blogs } = await getPosts("posts", 1);
const filterBlog = blogs.filter((e) => {
return e.title.toLowerCase().includes(search.toLowerCase());
});
let pageNo = filterBlog.length > 6 ? page : 1;
const itemsPerPage = 6;
// Calculate the start and end indices for the current page
const startIndex = (pageNo - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const slicedData = Array.isArray(filterBlog)
? filterBlog.slice(startIndex, endIndex)
: [];
const response = { data: slicedData, total: filterBlog.length };
return NextResponse.json(response);
}
import { getAllPosts } from "@lib/posts";
import { NextResponse } from "next/server";
export async function GET() {
const blogs = await getAllPosts("posts");
return NextResponse.json(blogs);
}
"use client";
import { useEffect, useState } from "react";
import BlogLists from "@components/top-level/blog-lists";
import Loading from "./loading";
import { useAppContext } from "@context/index";
import styles from "./container.module.css";
export default function BlogContainer({ blogs }) {
const { input } = useAppContext();
const [filteredBlogs, setFilteredBlogs] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
handleBlogs();
}, [input, blogs]); // Added 'blogs' as a dependency
const handleBlogs = () => {
setLoading(true); // Set loading state to true when starting to filter
const filtered = blogs.filter((blog) => {
const lowerCaseBlogTitle = blog.title.toLowerCase();
const lowerCaseInput = input.toLowerCase();
return lowerCaseBlogTitle.includes(lowerCaseInput);
});
setFilteredBlogs(filtered);
setLoading(false); // Set loading state to false when filtering is complete
};
return (
<>
{loading ? (
<Loading />
) : filteredBlogs.length ? (
<BlogLists
title="Lifestyle"
description="Lorem ipsum dolor sit amet elit. Id quaerat amet ipsum sunt et quos dolorum."
blogs={filteredBlogs}
/>
) : (
<h2 className={styles.center}>No data found</h2>
)}
</>
);
}
.center {
text-align: center;
}
"use client";
import { useState, useEffect } from "react";
import { ThemeProvider } from "@context/index";
import "./globals.css";
import "@styles/globals.css";
export default function RootLayout({ children, header }) {
// Check if localStorage is available (client-side)
const themeClass =
......
import styles from "./loading.module.css";
import LoadingComponent from "@components/base/loading";
const Loading = () => {
return <h2 className={styles.loading}>Loading ...</h2>;
return <LoadingComponent />;
};
export default Loading;
.loading {
display: flex;
align-items: center;
justify-content: center;
}
import styles from "./not-found.module.css";
import PageNotFoundComponent from "@components/base/pagenotfound";
const PageNotFound = () => {
return <h1 className={styles.notfound}>404 Not Found</h1>;
return <PageNotFoundComponent />;
};
export default PageNotFound;
.notfound {
display: grid;
place-content: center;
}
import BlogContainer from "./container";
import { getAllPosts } from "@lib/posts";
import BlogLists from "@components/top-level/blog-lists";
import { getPosts } from "@lib/api";
import styles from "./page.module.css";
import Loading from "./loading";
const Home = () => {
let blogs;
const fetchData = () => {
async function fetchBlogsByTitle(pageNo, search) {
try {
blogs = getAllPosts("posts");
} catch (error) {
console.error("Error fetching blogs:", error);
const searchResponse = await fetch(
`http://localhost:3000/api/blogs/?page=${pageNo}&search=${search}`
// {
// next: {
// revalidate: 100, //after 100sec
// },
// }
);
return await searchResponse.json();
} catch (err) {
console.log(er.message);
}
}
async function fetchBlogs(pageNo) {
try {
const response = await getPosts("posts", pageNo);
return response;
} catch (err) {
console.log(err.message);
}
}
const Home = async ({ searchParams }) => {
let blogs = [];
let blogsLength = 0;
let pageNo = searchParams.page || 1;
let search = searchParams.search || "";
let response;
if (!search) {
const { data, total } = await fetchBlogs(pageNo);
response = { data, total: total };
} else {
response = await fetchBlogsByTitle(pageNo, search);
}
};
fetchData();
blogs = response.data;
blogsLength = response.total;
pageNo = blogsLength > 6 ? pageNo : 1;
return (
<div className={styles.container}>
{blogs.length ? <BlogContainer blogs={blogs} /> : <Loading />}
{blogs?.length ? (
<BlogLists
title="Lifestyle"
description="Lorem ipsum dolor sit amet elit. Id quaerat amet ipsum sunt et quos dolorum."
blogs={blogs}
totalBlogLength={Number(blogsLength)}
currentPage={Number(pageNo)}
/>
) : (
<h2 className="center">No data found</h2>
)}
</div>
);
};
......
.container {
padding-top: 3rem;
padding-top: 1rem;
}
import SingleBlog from "@components/top-level/single-blog";
import { getPostById } from "@lib/posts";
import Loading from "app/loading";
import { getPostById } from "@lib/api";
const SingleBlogApp = ({ params }) => {
let blog;
const fetchBlogsBySlug = () => {
const fetchBlogById = async (id) => {
try {
const blogPost = getPostById("posts", params.id);
blog = blogPost[0];
} catch (err) {
console.log("error", err.message);
const blogPost = await getPostById("posts", id);
return blogPost[0];
} catch (error) {
console.error("Error fetching blog:", error.message);
return null;
}
};
};
const SingleBlogApp = async ({ params: { id } }) => {
const blog = await fetchBlogById(id);
fetchBlogsBySlug();
return (
<SingleBlog
title={blog.title}
publishedDate={blog.publishedDate}
author={blog.author}
category={blog.category}
blogImage={blog.blogImage}
readingTime={blog.readingTime}
categories={blog.categories}
markdown={blog.markdown}
/>
);
return <div>{blog ? <SingleBlog {...blog} /> : <Loading />}</div>;
};
export default SingleBlogApp;
import PropTypes from "prop-types";
import cn from "classnames";
import styles from "./styles.module.css";
const Button = ({ children, isDisabled, variant, className, ...props }) => {
const classNames = cn(styles["btn"], {
[styles[variant]]: variant,
[styles[className]]: className,
[styles.disabled]: isDisabled === true,
[styles.disabled]: isDisabled,
});
return (
<button className={`${classNames}`} {...props} disabled={isDisabled}>
<button className={classNames} disabled={isDisabled} {...props}>
{children}
</button>
);
......
......@@ -19,7 +19,7 @@ function Header({ name, currentTheme, onSearch, onClose, onThemeChange }) {
<header className={styles.header}>
<div className={styles.container}>
<nav className={styles.navbar}>
<Link className={styles["navbar-brand"]} href="/">
<Link className={styles["navbar-brand"]} href={`/?page=1`}>
<Icons name="newspaper" size="medium" classes="newspaper-icon" />
<h3>{name}</h3>
</Link>
......@@ -28,7 +28,7 @@ function Header({ name, currentTheme, onSearch, onClose, onThemeChange }) {
<Search onSearch={handleSearch} onClose={handleClose} />
</div>
<div onClick={handleTheme} className={styles.themelight}>
<div onClick={handleTheme} className={styles.themeLight}>
<Icons
name={currentTheme === "light" ? "sun" : "moon"}
size="medium"
......
.header {
position: sticky;
top: 57px;
top: 0;
z-index: 1000;
margin-bottom: 40px;
}
......@@ -12,7 +12,7 @@
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.7em 10%;
padding: 0.7em 12%;
}
.header .container .navbar .navbar-brand {
display: grid;
......@@ -24,10 +24,10 @@
.header .container .navbar .navbar-brand h2 {
color: var(--title-color);
}
.header .container .navbar .navbar-brand .themelight {
display: flex;
gap: 1rem;
.themeLight {
cursor: pointer;
}
.search {
display: none;
}
......
const LoadingComponent = () => {
return <h2 className="center">Loading ...</h2>;
};
export default LoadingComponent;
import Link from "next/link";
const PageNotFoundComponent = () => {
return (
<div className="center">
<div>
<h1>404 Page Not Found</h1>
<p style={{ textAlign: "center" }}>
Please redirect to <Link href="/">Home</Link>
</p>
</div>
</div>
);
};
export default PageNotFoundComponent;
"use client";
import { useState, useEffect } from "react";
import { useDebouncedCallback } from "use-debounce";
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import { useSearchParams } from "next/navigation";
import Icons from "@components/base/icons";
import styles from "./styles.module.css";
const Search = ({ onSearch, onClose }) => {
const [text, setText] = useState("");
const handleDebounceChange = useDebouncedCallback((value) => {
onSearch(value);
}, 1000);
const handleInputChange = (value) => {
setText(value);
handleDebounceChange(value);
};
const searchParams = useSearchParams();
const search = searchParams.get("search") || "";
const [inputValue, setInputValue] = useState(search);
useEffect(() => {
setInputValue(search);
}, [search]);
const handleCustomClear = (e) => {
e.preventDefault();
const handleCustomClear = () => {
onClose("");
setText("");
setInputValue(""); // Reset the input value
};
const handleKeyPress = (e) => {
if (e.key === "Enter") {
onSearch(inputValue);
}
};
const handleChange = (e) => {
setInputValue(e);
};
return (
......@@ -31,11 +37,12 @@ const Search = ({ onSearch, onClose }) => {
className={styles["search-input"]}
type="text"
autoFocus={true}
value={text}
onChange={(e) => handleInputChange(e.target.value)}
value={inputValue}
onChange={(e) => handleChange(e.target.value)}
onKeyUp={handleKeyPress}
placeholder="Discover news, articles and more"
/>
{text && (
{inputValue && (
<span className={`${styles["close-icon"]}`} onClick={handleCustomClear}>
<Icons name="close" size="small" />
</span>
......@@ -48,4 +55,5 @@ Search.propTypes = {
onSearch: PropTypes.func,
onClose: PropTypes.func,
};
export default Search;
"use client";
import React, { useEffect, useState } from "react";
import React from "react";
import PropTypes from "prop-types";
import Card from "@components/base/card";
import Pagination from "@components/top-level/pagination";
import { useSearchParams } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import styles from "./styles.module.css";
const BlogLists = ({ title, description, blogs }) => {
const BlogLists = ({
title,
description,
blogs,
currentPage,
totalBlogLength,
}) => {
const router = useRouter();
const searchParams = useSearchParams();
let searchPage = searchParams.get("page");
const length = blogs.length;
const itemsPerPage = 6;
const [currentPage, setCurrentPage] = useState(searchPage);
// Calculate the start and end indices for the current page
const startIndex = blogs.length > 6 ? (currentPage - 1) * itemsPerPage : 0;
const endIndex = startIndex + itemsPerPage;
const slicedData = blogs.slice(startIndex, endIndex);
useEffect(() => {
setCurrentPage(parseInt(searchPage) || 1);
}, [searchPage]);
// Handle page change
const redirect = (pageNumber) => {
const searchQuery = searchParams.get("search")
? `&search=${searchParams.get("search")}`
: "";
const newUrl = `?page=${pageNumber}${searchQuery}`;
return router.push(newUrl, undefined, { shallow: true });
};
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
redirect(pageNumber);
window.scrollTo({ top: 0, behavior: "smooth" });
};
......@@ -31,7 +34,7 @@ const BlogLists = ({ title, description, blogs }) => {
<h2 className={styles.title}>{title}</h2>
<p className={styles.description}>{description}</p>
<div className={styles.row}>
{slicedData.map((blogData, i) => (
{blogs.map((blogData, i) => (
<div key={i} className={styles.col}>
<Card {...blogData} />
</div>
......@@ -39,9 +42,9 @@ const BlogLists = ({ title, description, blogs }) => {
</div>
<div className={styles.pagination}>
<Pagination
currentPage={currentPage}
total={length}
perPage={itemsPerPage}
currentPage={Number(currentPage)}
total={Number(totalBlogLength)}
perPage={6}
onPageChange={handlePageChange}
/>
</div>
......@@ -53,6 +56,8 @@ BlogLists.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
blogs: PropTypes.array,
currentPage: PropTypes.number,
totalBlogLength: PropTypes.number,
};
export default BlogLists;
import { useState, useEffect } from "react";
import PropTypes from "prop-types";
import Button from "@components/base/button";
import { useRouter } from "next/navigation";
import styles from "./styles.module.css";
function Pagination({ total, currentPage, onPageChange, perPage }) {
const router = useRouter();
//Set number of pages
const numberOfPages = [];
const totalPages = Math.ceil(total / perPage);
const totalPage = totalPages <= 1 ? 1 : totalPages;
const numberOfPages = [];
for (let i = 1; i <= totalPage; i++) {
numberOfPages.push(i);
}
// Current active button number
const [currentButton, setCurrentButton] = useState(1);
const handleCurrentPage = (item) => {
onPageChange(item);
};
// Array of buttons what we see on the page
const [arrOfCurrButtons, setArrOfCurrButtons] = useState([]);
useEffect(() => {
let tempNumberOfPages = [...arrOfCurrButtons];
//adding dots
let dotsInitial = "...";
// Logic for displaying buttons
let tempNumberOfPages = [];
let dotsLeft = "...";
let dotsRight = "...";
if (numberOfPages.length < 6) {
tempNumberOfPages = numberOfPages;
} else if (currentButton >= 1 && currentButton <= 3) {
tempNumberOfPages = [1, 2, 3, 4, dotsInitial, numberOfPages.length];
} else if (currentButton === 4) {
} else if (currentPage >= 1 && currentPage <= 3) {
tempNumberOfPages = [1, 2, 3, 4, dotsRight, numberOfPages.length];
} else if (currentPage === 4) {
const sliced = numberOfPages.slice(0, 5);
tempNumberOfPages = [...sliced, dotsInitial, numberOfPages.length];
} else if (currentButton > 4 && currentButton < numberOfPages.length - 2) {
// from 5 to 8 -> (10 - 2)
const sliced1 = numberOfPages.slice(currentButton - 2, currentButton); // sliced1 (5-2, 5) -> [4,5]
const sliced2 = numberOfPages.slice(currentButton, currentButton + 1); // sliced1 (5, 5+1) -> [6]
tempNumberOfPages = [...sliced, dotsRight, numberOfPages.length];
} else if (currentPage > 4 && currentPage < numberOfPages.length - 2) {
const sliced1 = numberOfPages.slice(currentPage - 2, currentPage);
const sliced2 = numberOfPages.slice(currentPage, currentPage + 1);
tempNumberOfPages = [
1,
dotsLeft,
......@@ -45,63 +38,38 @@ function Pagination({ total, currentPage, onPageChange, perPage }) {
dotsRight,
numberOfPages.length,
];
} else if (currentButton > numberOfPages.length - 3) {
// > 7
const sliced = numberOfPages.slice(numberOfPages.length - 4); // slice(10-4)
} else if (currentPage > numberOfPages.length - 3) {
const sliced = numberOfPages.slice(numberOfPages.length - 4);
tempNumberOfPages = [1, dotsLeft, ...sliced];
}
setArrOfCurrButtons(tempNumberOfPages);
}, [total, currentButton, perPage]);
//update currentButton value if currentPageNo changes
useEffect(() => {
setCurrentButton(currentPage);
router.push(`/?page=${currentPage}`);
}, [currentPage]);
//onClick function
const handleCurrentPage = (item) => {
setCurrentButton(item);
onPageChange(item);
};
return (
<div className={styles["pagination-container"]}>
<span className={styles.previous}>
<Button
variant="secondary"
isDisabled={currentButton <= 1 ? true : false}
onClick={() =>
currentButton <= 1 ? "" : handleCurrentPage(currentButton - 1)
}
isDisabled={currentPage <= 1}
onClick={() => handleCurrentPage(currentPage - 1)}
>
&laquo; Prev
</Button>
</span>
{arrOfCurrButtons.map((item, index) => {
return (
{tempNumberOfPages.map((item, index) => (
<span key={index}>
<Button
variant={currentButton === item ? "primary" : "secondary"}
primary={currentButton === item ? true : false}
isDisabled={totalPage === 1}
variant={currentPage === item ? "primary" : "secondary"}
onClick={() => (item === "..." ? "" : handleCurrentPage(item))}
>
{item}
</Button>
</span>
);
})}
))}
<span className={styles.next}>
<Button
variant="secondary"
isDisabled={currentButton === numberOfPages.length ? true : false}
onClick={() =>
currentButton === numberOfPages.length
? ""
: handleCurrentPage(currentButton + 1)
}
isDisabled={currentPage === totalPage}
onClick={() => handleCurrentPage(currentPage + 1)}
>
Next &raquo;
</Button>
......@@ -115,4 +83,5 @@ Pagination.propTypes = {
currentPage: PropTypes.number,
onPageChange: PropTypes.func,
};
export default Pagination;
......@@ -3,6 +3,7 @@ import Image from "next/image";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import styles from "./styles.module.css";
function SingleBlog({
title,
publishedDate,
......@@ -14,38 +15,38 @@ function SingleBlog({
markdown,
}) {
return (
<div className={styles["container"]}>
<div className={styles.container}>
<div className={styles.headtitle}>
<h1 className={styles.title}>{title}</h1>
</div>
<div className={styles["blog-list"]}>
<div className={styles.blogList}>
<ul>
<li>
Posted on&nbsp;
<span className={styles["list-bold"]}>{publishedDate}</span>
<span className={styles.listBold}>{publishedDate}</span>
</li>
<li>
By&nbsp;
<a href="#" className={styles["list-bold"]}>
<a href="#" className={styles.listBold}>
{author.name}
</a>
</li>
<li>
Published in&nbsp;
<a href="#" className={styles["list-bold"]}>
<a href="#" className={styles.listBold}>
{category}
</a>
</li>
<li>
<a href="#" className={styles["list-bold"]}>
{readingTime}
<a href="#" className={styles.listBold}>
{readingTime} read
</a>
</li>
</ul>
</div>
<div className={styles["blog-container"]}>
<div className={styles.blogimage}>
<div className={styles.blogContainer}>
<div className={styles.blogImage}>
<Image
className={styles.img}
src={blogImage.url}
......@@ -54,12 +55,12 @@ function SingleBlog({
height={blogImage.height}
/>
</div>
<div className={styles["blog-content"]}>
<div className={styles.blogContent}>
<ReactMarkdown rehypePlugins={[rehypeRaw]}>{markdown}</ReactMarkdown>
<div className={styles.tag}>
<span>Tags:</span>
{categories.map((e, i) => (
<p key={i}>{e}</p>
{categories.map((category, index) => (
<p key={index}>{category}</p>
))}
</div>
</div>
......@@ -84,4 +85,5 @@ SingleBlog.propTypes = {
categories: PropTypes.array,
markdown: PropTypes.any,
};
export default SingleBlog;
......@@ -6,19 +6,20 @@
.blog-list ul {
padding: 0rem 2.3rem;
}
.blog-content {
.blogContent {
padding: 1rem;
border-radius: 15px;
margin-top: 2rem;
font-size: 18px;
line-height: 1.2;
color: var(--font-color-300);
background: var(--card-bg);
}
.blog-content p,
.blog-content ol li {
.blogContent p,
.blogContent ol li {
font-size: clamp(1rem, 0.8rem + 1vw, 1.2rem);
}
.blogimage {
.blogImage {
overflow: hidden;
border-radius: 15px;
width: 100%;
......@@ -30,25 +31,25 @@
object-fit: cover;
display: block;
}
.blog-content blockquote p {
.blogContent blockquote p {
font-size: 20px;
}
.blog-list ul {
.blogList ul {
display: flex;
flex-wrap: wrap;
list-style: none;
gap: 1em;
color: var(--font-color-300);
}
.blog-list ul li {
.blogList ul li {
font-size: clamp(1rem, 0.4rem + 0.5vw, 1.1rem);
line-height: 1;
}
.blog-list ul li a {
.blogList ul li a {
text-decoration: none;
color: var(--font-color-300);
}
.blog-container {
.blogContainer {
width: 90%;
margin: 0 auto;
}
......@@ -58,7 +59,7 @@
gap: 0.2rem;
}
.tag > p {
background: #e2e8f0;
background: var(--bg-grey);
padding: 0.3em 1em;
border-radius: 40px;
color: #718096;
......@@ -73,12 +74,12 @@
background: #5a67d8;
color: white;
}
.blog-content blockquote {
.blogContent blockquote {
padding: 0.66001rem 1rem 1rem;
border-left: 3px solid #5a67d8;
margin: 0;
}
.blog-content blockquote p::before {
.blogContent blockquote p::before {
content: "\2014\00A0";
}
.head-container {
......@@ -90,17 +91,17 @@
font-weight: 600;
color: var(--font-color-900);
}
.list-bold {
.listBold {
font-weight: bold;
}
@media screen and (min-width: 768px) {
.headtitle,
.blog-list ul {
.blogList ul {
width: min(80rem, 90%);
padding: 0rem 3.5rem;
}
.blog-content {
.blogContent {
padding: 3rem;
}
.tag {
......@@ -112,11 +113,11 @@
padding-inline: 40px;
}
.headtitle,
.blog-list ul {
.blogList ul {
width: min(70rem, 90%);
margin: 0 auto;
}
.blog-list ul {
.blogList ul {
margin-top: 1rem;
}
}
......@@ -6,7 +6,8 @@
"@img/*": ["public/images/*"],
"@posts/*": ["posts/*"],
"@context/*": ["context/*"],
"@lib/*": ["lib/*"]
"@lib/*": ["lib/*"],
"@styles/*": ["styles/*"]
},
"experimentalDecorators": true
}
......
import fs from "fs";
import fs from "fs/promises";
import path from "path";
import matter from "gray-matter";
import { remark } from "remark";
import html from "remark-html";
export function getPostById(fileDirectory, slug) {
export async function getPostById(fileDirectory, slug) {
const directory = path.join(process.cwd(), fileDirectory);
const fileNames = fs.readdirSync(directory);
const data = fileNames
.map((fileName) => {
const fileNames = await fs.readdir(directory);
const data = await Promise.all(
fileNames.map(async (fileName) => {
const fullPath = path.join(fileDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, "utf8");
const fileContents = await fs.readFile(fullPath, "utf8");
const { data, content } = matter(fileContents);
const processedContent = remark()
.use(html)
......@@ -22,23 +22,34 @@ export function getPostById(fileDirectory, slug) {
...data,
};
})
.filter((e) => e.id == slug);
);
return data;
return data.filter((e) => e.id == slug);
}
export function getAllPosts(fileDirectory) {
export async function getPosts(fileDirectory, page) {
const directory = path.join(process.cwd(), fileDirectory);
const fileNames = fs.readdirSync(directory);
const data = fileNames.map((fileName) => {
const fileNames = await fs.readdir(directory);
const dataPromises = fileNames.map(async (fileName) => {
const id = fileName.replace(/\.md$/, "");
const fullPath = path.join(fileDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, "utf8");
const matterResult = matter(fileContents);
const fileContents = await fs.readFile(fullPath, "utf8");
const { data, content } = matter(fileContents);
return {
id,
...matterResult.data,
...data,
};
});
return data;
const data = await Promise.all(dataPromises);
const itemsPerPage = 6;
// Calculate the start and end indices for the current page
const startIndex = (page - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const slicedData = Array.isArray(data)
? data.slice(startIndex, endIndex)
: [];
const response = { data: slicedData, total: data?.length, blogs: data };
return response;
}
......@@ -52,6 +52,7 @@
--secondary-bgcolor: #131617;
--primary-color: #fff;
--secondary-color: #fff;
--bg-grey: #2a2a2a;
}
body,
......@@ -107,8 +108,9 @@ p {
display: flex;
gap: 1em;
}
/* @media (prefers-color-scheme: dark) {
html {
background-color: var(--bg-color);
}
} */
.center {
display: flex;
align-items: center;
justify-content: center;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment