Commit 98808fe0 by Manivasagam S

refactor the code

parent e802915f
import React from "react"; import React from "react";
import PropTypes from "prop-types";
import styles from "@/Component/Base/Card/Card.module.css"; import styles from "@/Component/Base/Card/Card.module.css";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { Icons } from "@/Component/Base/Icons/Icons.jsx"; import { Icons } from "@/Component/Base/Icons/Icons.jsx";
export const Card = ({ export const Card = ({
id, id,
title, title,
...@@ -39,7 +41,7 @@ export const Card = ({ ...@@ -39,7 +41,7 @@ export const Card = ({
height={author.image.height} height={author.image.height}
width={author.image.width} width={author.image.width}
loading="eager" loading="eager"
alt={author} alt={author.name}
/> />
</div> </div>
<div className={styles.authordescription}> <div className={styles.authordescription}>
...@@ -58,3 +60,24 @@ export const Card = ({ ...@@ -58,3 +60,24 @@ export const Card = ({
</div> </div>
); );
}; };
Card.propTypes = {
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.shape({
url: PropTypes.string,
height: PropTypes.number,
width: PropTypes.number,
}),
publishedDate: PropTypes.string,
author: PropTypes.shape({
name: PropTypes.string,
image: PropTypes.shape({
url: PropTypes.string,
height: PropTypes.number,
width: PropTypes.number,
}),
}),
readingTime: PropTypes.string,
};
.container { .container {
background-color: var(--card-bg); background-color: var(--card-bg);
border-radius: 12px; border-radius: 0.8rem;
overflow: hidden; overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease; transition: transform 0.2s ease;
...@@ -29,9 +29,9 @@ ...@@ -29,9 +29,9 @@
.card-img { .card-img {
width: 100%; width: 100%;
max-width: 27rem; max-width: 27rem;
height: 300px; height: 18.75rem;
border-top-left-radius: 12px; border-top-left-radius: 0.75rem;
border-top-right-radius: 12px; border-top-right-radius: 0.75rem;
object-fit: cover; object-fit: cover;
} }
...@@ -65,8 +65,8 @@ ...@@ -65,8 +65,8 @@
.img { .img {
border-radius: 50%; border-radius: 50%;
object-fit: cover; object-fit: cover;
width: 50px; width: 3.125rem;
height: 50px; height: 3.125rem;
margin-top: 5px; margin-top: 5px;
} }
...@@ -78,7 +78,7 @@ ...@@ -78,7 +78,7 @@
.authorName { .authorName {
font-weight: 600; font-weight: 600;
font-size: 1.1rem; font-size: 1.1rem;
color: var(--text-color); color: var(--light-text);
} }
.author-desc { .author-desc {
......
"use client"; "use client";
import React, { useContext } from "react"; import React, { useContext, useState } from "react";
import styles from "@/Component/Base/Header/Header.module.css";
import Link from "next/link"; import Link from "next/link";
import { Icons } from "@/Component/Base/Icons/Icons.jsx";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Search } from "@/Component/Base/Search/Search.jsx";
import { ThemeContext } from "@/Context/ThemeContext"; import { ThemeContext } from "@/Context/ThemeContext";
export const Header = ({ name, onchange }) => { import { Icons } from "@/Component/Base/Icons/Icons.jsx";
import { Search } from "@/Component/Base/Search/Search.jsx";
import styles from "@/Component/Base/Header/Header.module.css";
export const Header = ({ name, onSearch }) => {
const { theme, toggleTheme } = useContext(ThemeContext); const { theme, toggleTheme } = useContext(ThemeContext);
return ( return (
<header className={styles.header}> <header className={styles.header}>
...@@ -20,19 +21,21 @@ export const Header = ({ name, onchange }) => { ...@@ -20,19 +21,21 @@ export const Header = ({ name, onchange }) => {
/> />
<h3 className={styles["nav-text"]}>{name}</h3> <h3 className={styles["nav-text"]}>{name}</h3>
</Link> </Link>
<div className={`${styles.searchWrapper}`}>
<Search onChange={onchange} /> <div className={styles.searchWrapper}>
<Search onSearch={onSearch} />
</div> </div>
<div className={styles.headerIcons}>
<div className={styles.themelight} onClick={toggleTheme}> <div className={styles.themelight} onClick={toggleTheme}>
<Icons name={theme === "light" ? "sun" : "moon"} size="medium" /> <Icons name={theme === "light" ? "sun" : "moon"} size="medium" />
</div> </div>
</div>
</nav> </nav>
</div> </div>
</header> </header>
); );
}; };
Header.propTypes = { Header.propTypes = {
name: PropTypes.string, name: PropTypes.string.isRequired,
onSearch: PropTypes.func.isRequired,
}; };
...@@ -5,13 +5,17 @@ ...@@ -5,13 +5,17 @@
} }
.container { .container {
max-width: 1200px; /* width: 100%; */
max-width: 84rem;
margin: 0 auto; margin: 0 auto;
} }
.navbar { .navbar {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 1rem;
} }
.nav-brand { .nav-brand {
...@@ -19,65 +23,77 @@ ...@@ -19,65 +23,77 @@
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
text-decoration: none; text-decoration: none;
position: relative; order: 1;
right: 5rem;
} }
.nav-text { .nav-text {
margin: 0; margin: 0;
color: var(--text-color); color: var(--text-color);
font-weight: 600; font-weight: 600;
margin-bottom: 5px; font-size: 1.25rem;
} }
.searchWrapper { .searchWrapper {
flex: 1; order: 3;
flex: 1 1 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
display: none; width: 100%;
max-width: 25rem;
margin: 0 auto;
} }
.themelight { .themelight {
display: flex; display: flex;
align-items: center; align-items: center;
}
.header-ul {
display: flex;
gap: 2rem;
position: relative;
right: 0.8rem;
display: none;
}
.header-ul li {
list-style-type: none;
}
.headerIcons {
display: flex;
gap: 1rem;
cursor: pointer; cursor: pointer;
position: relative; order: 2;
left: 5rem;
} }
@media (min-width: 640px) { @media (min-width: 640px) {
.navbar { .navbar {
gap: 3rem; flex-wrap: nowrap;
} }
.nav-text { .nav-brand {
font-size: 1.5rem; order: 1;
}
.searchWrapper {
order: 2;
}
.themelight {
order: 3;
}
}
@media (min-width: 768px) {
.nav-brand {
order: 1;
}
.themelight {
order: 3;
}
.searchWrapper {
order: 2;
} }
} }
@media (min-width: 1024px) { @media (min-width: 1024px) {
/* .mobileMenu{ .navbar {
display: none; flex-wrap: nowrap;
} */ }
.header-ul {
display: inline-flex; .nav-brand {
order: 1;
} }
.searchWrapper { .searchWrapper {
display: inline-flex; order: 2;
flex: 1 1 auto;
margin: 0 auto;
}
.themelight {
order: 3;
} }
.menuIcon {
display: none; .nav-text {
font-size: 1.5rem;
} }
} }
import {Header} from "@/Component/Base/Header/Header.jsx"; import { Header } from "@/Component/Base/Header/Header.jsx";
import { action } from 'storybook/actions'; import { action } from "storybook/actions";
export default{ export default {
title:'Base/Header', title: "Layout/Header",
component:Header component: Header,
} };
export const Heading=(args)=><Header {...args}/> export const Heading = (args) => <Header {...args} />;
Heading.args={ Heading.args = {
name:"NewsBlog", name: "NewsBlog",
onchange: action("Header Search Changed"), onchange: action("Header Search Changed"),
} };
\ No newline at end of file
/* Icon size styles */ /* Icon size styles */
.small svg { .small svg {
height: 18px; height: 16px;
width: 18px; width: 16px;
color: var(--font-color-300); color: var(--font-color-300);
} }
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
width: 25x; width: 25x;
color: var(--font-color-300); color: var(--font-color-300);
} }
.large svg{ .large svg {
height: 35px; height: 35px;
width: 35px; width: 35px;
color: var(--font-color-300); color: var(--font-color-300);
...@@ -50,15 +50,13 @@ ...@@ -50,15 +50,13 @@
font-size: 0.75rem; font-size: 0.75rem;
gap: 0.5rem; gap: 0.5rem;
} }
@media(min-width:640px){ @media (min-width: 640px) {
.medium svg{ .medium svg {
width: 38px; width: 38px;
height: 38px; height: 38px;
} }
.large svg{ .large svg {
width: 42px; width: 42px;
height: 42px; height: 42px;
} }
} }
import React from 'react' import React from "react";
import clsx from 'clsx'; import clsx from "clsx";
import PropTypes from "prop-types";
import styles from "@/Component/Base/Input/Input.module.css"; import styles from "@/Component/Base/Input/Input.module.css";
export const Input =({type='text',placeholder='',isInvalid=false,onChange,value='',autoFocus,errorMessage="",...props}) => { export const Input = ({
type = "text",
placeholder = "",
isInvalid = false,
onChange,
value = "",
autoFocus,
errorMessage = "",
...props
}) => {
return ( return (
<div > <div>
<input className={clsx(styles.inputbox,isInvalid && styles.invalid) } type={type} placeholder={placeholder} onChange={onChange} autoFocus={autoFocus} {...props}/> <input
{errorMessage && <p className={styles.error} style={{ className={clsx(styles.inputbox, isInvalid && styles.invalid)}
color:'red' type={type}
}}>{errorMessage}</p>} placeholder={placeholder}
onChange={onChange}
autoFocus={autoFocus}
{...props}
/>
{errorMessage && (
<p
className={styles.error}
style={{
color: "red",
}}
>
{errorMessage}
</p>
)}
</div> </div>
);
) };
} Input.prototype = {
type: PropTypes.string,
isInvalid: PropTypes.bool,
placeholder: PropTypes.string,
};
...@@ -6,20 +6,20 @@ import { Icons } from "@/Component/Base/Icons/Icons.jsx"; ...@@ -6,20 +6,20 @@ import { Icons } from "@/Component/Base/Icons/Icons.jsx";
export const Search = ({ export const Search = ({
placeholder = "Discover news, articles and more", placeholder = "Discover news, articles and more",
onSearch, onSearch,
posts,
}) => { }) => {
const [text, setText] = useState(""); const [text, setText] = useState("");
const handleInputChange = (value) => { const handleInputChange = (value) => {
setText(value); setText(value);
onChange?.(value); onSearch?.(value);
}; };
const handleClear = (e) => { const handleClear = (e) => {
e.preventDefault(); e.preventDefault();
setText(""); setText("");
onSearch?.("");
}; };
console.log(text);
return ( return (
<div className={styles.searchContainer}> <div className={styles.searchContainer}>
<span className={styles.searchIcon}> <span className={styles.searchIcon}>
......
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
background-color: var(--search-color); background-color: var(--search-color);
border-radius: 8px; border-radius: 0.5rem;
padding: 0.5rem 0.5rem; padding: 0.5rem 0.5rem;
max-width: 25rem;
width: 100%; width: 100%;
/* gap: 0.75rem; */ /* gap: 0.75rem; */
position: relative; position: relative;
...@@ -25,11 +24,11 @@ ...@@ -25,11 +24,11 @@
align-items: center; align-items: center;
} }
.input-box { .input-box {
width: 20rem; min-width: 15rem;
border: none; border: none;
outline: none; outline: none;
background: transparent; background: transparent;
font-size: 1rem; font-size: 0.8rem;
color: var(--text-color); color: var(--text-color);
padding: 0.25rem 0; padding: 0.25rem 0;
margin-left: 1rem; margin-left: 1rem;
...@@ -43,5 +42,10 @@ ...@@ -43,5 +42,10 @@
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
color: #2b6cb0; color: #2b6cb0;
margin-top: 7px; margin-top: 0.5rem;
}
@media (min-width: 640px) {
.input-box {
font-size: 1rem;
}
} }
import {Search, search} from "@/Component/Base/Search/Search.jsx"; import { Search, search } from "@/Component/Base/Search/Search.jsx";
import { action } from 'storybook/actions'; import { action } from "storybook/actions";
export default{ import { background } from "storybook/internal/theming";
title:"Shared/Search", export default {
component:Search, title: "Shared/Search",
component: Search,
argsTypes: {
argsTypes:{ onchange: {
onchange:{ action: "changed",
action:"changed" },
} },
} };
} export const Searchbar = (args) => <Search {...args} />;
export const Searchbar=(args)=><Search {...args}/> Searchbar.args = {
Searchbar.args={ placeholder: "Discover news, articles and more",
placeholder:"Discover news, articles and more",
onChange: action("Input Changed"), onChange: action("Input Changed"),
} };
"use client"; "use client";
import React, { useState, useEffect } from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import styles from "@/Component/Top-level/CardList/CardList.module.css";
import { Pagination } from "@/Component/Top-level/Pagination/Pagination";
import { Card } from "@/Component/Base/Card/Card"; import { Card } from "@/Component/Base/Card/Card";
import styles from "@/Component/Top-level/CardList/CardList.module.css";
export const CardList = ({ title, description, blogs }) => { export const CardList = ({ title, description, blogs = [], slicedData }) => {
const itemsPerPage = 6; const displayData = slicedData && slicedData.length > 0 ? slicedData : blogs;
const [currentPage, setCurrentPage] = useState(1);
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
window.scrollTo({ top: 0, behavior: "smooth" });
};
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const slicedData = blogs.slice(startIndex, endIndex);
const totalPages = Math.ceil(blogs.length / itemsPerPage);
return ( return (
<div className={styles.cardListContainer}> <div className={styles.cardListContainer}>
{blogs.length === 0 ? (
<p className={styles.resultFound}>No result found</p>
) : (
<>
<div className={styles.heading}>
{title && <h1 className={styles.title}>{title}</h1>} {title && <h1 className={styles.title}>{title}</h1>}
{description && <p className={styles.description}>{description}</p>} {description && <p className={styles.description}>{description}</p>}
</div>
<div className={styles.blogRow}> <div className={styles.blogRow}>
{slicedData.map((blogdata, i) => ( {displayData.map((blogdata, i) => (
<div key={i} className={styles.blogCard}> <div key={i} className={styles.blogCard}>
<Card {...blogdata} /> <Card {...blogdata} />
</div> </div>
))} ))}
</div> </div>
</>
{totalPages > 1 && (
<div className={styles.pagination}>
<Pagination
size={totalPages}
currentPage={currentPage}
onChange={handlePageChange}
/>
</div>
)} )}
</div> </div>
); );
}; };
CardList.propTypes = { CardList.propTypes = {
title: PropTypes.string, title: PropTypes.string,
description: PropTypes.string, description: PropTypes.string,
blogs: PropTypes.arrayOf(PropTypes.object).isRequired, blogs: PropTypes.arrayOf(PropTypes.object),
slicedData: PropTypes.arrayOf(PropTypes.object),
}; };
.cardListContainer { .cardListContainer {
padding-left: 2rem; padding: 1rem 2rem;
padding-right: 2rem;
} }
.description { .description {
padding-left: 2rem; padding-left: 2rem;
...@@ -12,6 +11,11 @@ ...@@ -12,6 +11,11 @@
padding-top: 2rem; padding-top: 2rem;
color: var(--light-text); color: var(--light-text);
} }
.heading {
display: flex;
flex-direction: column;
justify-content: center;
}
.blogRow { .blogRow {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
...@@ -19,7 +23,17 @@ ...@@ -19,7 +23,17 @@
place-self: center; place-self: center;
margin-top: 2rem; margin-top: 2rem;
} }
.resultFound {
display: flex;
justify-content: center;
margin-top: 20rem;
font-size: 1.5rem;
text-transform: uppercase;
}
@media (min-width: 640px) { @media (min-width: 640px) {
.resultFound {
font-size: 2rem;
}
.blogRow { .blogRow {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
......
import { CardList } from "@/Component/Top-level/CardList/CardList.jsx"; import { CardList } from "@/Component/Top-level/CardList/CardList.jsx";
export default{ export default {
title:"Toplevel/CardList", title: "Toplevel/CardList",
component:CardList component: CardList,
} };
export const blogLists = { export const blogLists = {
args: { args: {
title: "Lifestyle",
description:
"Lorem ipsum dolor sit amet elit. Id quaerat amet ipsum sunt et quos dolorum",
blogs: [ blogs: [
{ {
id: 1, id: 1,
......
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import { Button } from "@/Component/Base/Button/Button"; import { Button } from "@/Component/Base/Button/Button";
import styles from "./Pagination.module.css"; import styles from "./Pagination.module.css";
import { ThemeContext } from "@/Context/ThemeContext";
export const Pagination = ({ size, currentPage, onChange }) => { export const Pagination = ({ size, currentPage, onChange }) => {
const [isShow, setIsShow] = useState(false); const [isShow, setIsShow] = useState(false);
......
...@@ -8,9 +8,8 @@ export default { ...@@ -8,9 +8,8 @@ export default {
}, },
}; };
export const Default =(args)=> <Pagination {...args}/> export const Default = (args) => <Pagination {...args} />;
Default.args= { Default.args = {
size: 7, size: 7,
currentPage: 3, currentPage: 3,
};
}
\ No newline at end of file
...@@ -75,8 +75,8 @@ ...@@ -75,8 +75,8 @@
} }
.tagItems { .tagItems {
border: 1px solid black; border: 1px solid black;
padding: 0.3rem; padding: 0.2rem;
border-radius: 0.5rem; border-radius: 1rem;
background-color: rgb(91, 90, 90); background-color: rgb(91, 90, 90);
color: white; color: white;
border-color: transparent; border-color: transparent;
......
...@@ -3,6 +3,7 @@ import React, { useState, useMemo } from "react"; ...@@ -3,6 +3,7 @@ import React, { useState, useMemo } from "react";
import { CardList } from "@/Component/Top-level/CardList/CardList"; import { CardList } from "@/Component/Top-level/CardList/CardList";
import { Header } from "@/Component/Base/Header/Header"; import { Header } from "@/Component/Base/Header/Header";
import { useThemeContext } from "@/Context/ThemeContext"; import { useThemeContext } from "@/Context/ThemeContext";
import { Pagination } from "@/Component/Top-level/Pagination/Pagination";
import "../globals.css"; import "../globals.css";
export default function Home({ blogs }) { export default function Home({ blogs }) {
...@@ -12,14 +13,30 @@ export default function Home({ blogs }) { ...@@ -12,14 +13,30 @@ export default function Home({ blogs }) {
const handleSearch = (value) => { const handleSearch = (value) => {
setSearchQuery(value.toLowerCase()); setSearchQuery(value.toLowerCase());
}; };
const filteredPosts = useMemo(() => { const filteredPosts = useMemo(() => {
return blogs.filter( return blogs.filter((post) => {
(post) => const title =
post.title.toLowerCase().includes(searchQuery) || typeof post.title === "string" ? post.title.toLowerCase() : "";
post.author.toLowerCase().includes(searchQuery) const author =
); typeof post.author === "string" ? post.author.toLowerCase() : "";
return title.includes(searchQuery) || author.includes(searchQuery);
});
}, [searchQuery, blogs]); }, [searchQuery, blogs]);
const itemsPerPage = 6;
const [currentPage, setCurrentPage] = useState(1);
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
window.scrollTo({ top: 0, behavior: "smooth" });
};
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const slicedData = filteredPosts.slice(startIndex, endIndex);
const totalPages = Math.ceil(filteredPosts.length / itemsPerPage);
return ( return (
<> <>
<Header <Header
...@@ -33,7 +50,18 @@ export default function Home({ blogs }) { ...@@ -33,7 +50,18 @@ export default function Home({ blogs }) {
title="Lifestyle" title="Lifestyle"
description="Lorem ipsum dolor sit amet elit. Id quaerat amet ipsum sunt et quos dolorum" description="Lorem ipsum dolor sit amet elit. Id quaerat amet ipsum sunt et quos dolorum"
blogs={filteredPosts} blogs={filteredPosts}
slicedData={slicedData}
/>
{totalPages > 1 && (
<div>
<Pagination
size={totalPages}
currentPage={currentPage}
onChange={handlePageChange}
/> />
</div>
)}
</> </>
); );
} }
@import url("https://fonts.googleapis.com/css2?family=Hind:wght@300;400;500;600;700&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Hind:wght@300;400;500;600;700&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");
body, body,
html { html {
font-family: "Hind", sans-serif; font-family: "Hind", sans-serif;
...@@ -20,42 +22,6 @@ h6 { ...@@ -20,42 +22,6 @@ h6 {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
/* .light {
--primary-bgcolor: #5a67d8;
--secondary-bgcolor: #fff;
--primary-color: #fff;
--secondary-color: #718096;
--font-color-900: #2d3748;
--font-color-300: #718096;
--title-color: #2d3748;
--bg-color: #f8f9fa;
--input-bg: #f4f4f4;
--card-bg: #fff;
--heading-color: #2d3748;
--border-radius: 6px;
--border-radius-full: 15px;
--border-color: transparent;
--bg-grey: #f4f4f4;
--bg-lightgrey: #f8f9fa;
--btn-bg: #e2e8f0;
--font-color-700: black;
--placeholder-color: #9f9f9f;
}
.dark {
--bg-color: black;
--input-bg: black;
--card-bg: #131617;
--font-color-300: #b7b7b7;
--placeholder-color: #b7b7b7;
--font-color-900: #fff;
--title-color: #fff;
--font-color-700: #fff;
--border-color: rgba(255, 255, 255, 0.2);
--primary-bgcolor: #5a67d8;
--secondary-bgcolor: #131617;
--primary-color: #fff;
--secondary-color: #fff;
} */
:root { :root {
/* --primary-bg:black; */ /* --primary-bg:black; */
--primary-bgcolor: #5a67d8; --primary-bgcolor: #5a67d8;
......
"use server";
import { getAllPosts } from "@/lib/post"; import { getAllPosts } from "@/lib/post";
import Home from "@/app/components/Home"; import Home from "@/app/components/Home";
......
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