Commit 98808fe0 by Manivasagam S

refactor the code

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