Skip to content

Commit

Permalink
Merge pull request #247 from myong39/Next-서미영-sprint9
Browse files Browse the repository at this point in the history
[서미영] Sprint9
  • Loading branch information
dev-kjy authored Jul 16, 2024
2 parents 8d163dd + 8bbe271 commit 34a748e
Show file tree
Hide file tree
Showing 63 changed files with 1,774 additions and 473 deletions.
24 changes: 24 additions & 0 deletions components/NavLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getLinkStyle } from "@/utils/Utils";
import Link from "next/link";
import { useRouter } from "next/router";
import { CSSProperties } from "react";

interface NavLinkProps {
href: string;
children: React.ReactNode;
style?: CSSProperties;
}

const NavLink: React.FC<NavLinkProps> = ({ href, children }) => {
const router = useRouter();
const isActive = router.pathname === href;
const style: CSSProperties = isActive ? getLinkStyle({ isActive: true }) : {};

return (
<Link href={href} style={style}>
{children}
</Link>
);
};

export default NavLink;
39 changes: 39 additions & 0 deletions components/boards/AllArticleItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ArticleProp } from "@/types/articleTypes";
import styles from "./Freeboard.module.scss";
import favoriteImg from "@/public/images/icons/ic_heart.svg";
import { getFormatTime } from "@/utils/Utils";

export default function AllArticleItem({
article: { createdAt, image, likeCount, title, writer },
}: ArticleProp) {
return (
<div className={styles["all-card"]}>
<div className={styles["content-wrapper"]}>
<div className={styles["title-wrapper"]}>
<h2 className={styles["card-title"]}>{title}</h2>
{image && (
<div className={styles["item-box"]}>
<img className={styles.item} src={image} alt="대표 이미지" />
</div>
)}
</div>
<div className={styles["writer-wrapper"]}>
<div className={styles["nickname-wrapper"]}>
<span className={styles["nickname"]}>{writer.nickname}</span>
<span className={styles["date"]}>
{getFormatTime(createdAt, false)}
</span>
</div>
<div className={styles["favorite-wrapper"]}>
<img
className={styles["favorite"]}
src={favoriteImg.src}
alt="즐겨찾기"
/>
<span className={styles["favorite-count"]}>{likeCount}</span>
</div>
</div>
</div>
</div>
);
}
76 changes: 76 additions & 0 deletions components/boards/AllArticleList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useEffect, useState } from "react";
import styles from "./Freeboard.module.scss";
import AllArticleItem from "./AllArticleItem";
import { Article, ArticleApiData } from "@/types/articleTypes";
import SearchInput from "@/components/layout/SearchInput";
import Button from "@/components/layout/Button";
import Dropdown from "@/components/layout/Dropdown";
import Link from "next/link";
import { getArticle } from "@/lib/articleApi";
import { useRouter } from "next/router";

export default function AllArticleList({
initialArticles,
}: {
initialArticles: Article[];
}) {
const [articles, setArticles] = useState(initialArticles);
const [orderBy, setOrderBy] = useState<ArticleApiData["orderBy"]>("recent");

const router = useRouter();
const keyword = router.query.q;

const handleOrderChange = (option: string) => {
if (option === "recent" || option === "like") setOrderBy(option);
};

const fetchData = async ({ orderBy, pageSize, keyword }: ArticleApiData) => {
try {
const searchKeyword = Array.isArray(keyword) ? keyword[0] : keyword;
const result = await getArticle({
orderBy,
pageSize,
keyword: searchKeyword,
});

setArticles(() => result.list);
} catch (error) {
console.log(error);
}
};

const handleSortBySearch = (value: string) => {
const searchValue = value.trim();

if (!searchValue) {
router.push("/boards");
return;
}

router.push(`/boards?q=${searchValue}`);
};

useEffect(() => {
fetchData({ orderBy: orderBy, keyword: keyword });
}, [orderBy, keyword]);

return (
<section className={styles["all-article"]}>
<div className={styles["all-title-wrapper"]}>
<h1 className={styles.title}>게시글</h1>
<Button>글쓰기</Button>
</div>
<div className={styles["search-wrapper"]}>
<SearchInput onSortBySearch={handleSortBySearch} />
<Dropdown onOrderChange={handleOrderChange} />
</div>
{articles.length
? articles.map((article) => (
<Link href={`/board/${article.id}`} key={article.id}>
<AllArticleItem article={article} />
</Link>
))
: keyword && <div>{`'${keyword}'(으)로 검색된 결과가 없습니다.`}</div>}
</section>
);
}
41 changes: 41 additions & 0 deletions components/boards/BestArticleItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import styles from "./Freeboard.module.scss";
import badgeImg from "@/public/images/icons/img_badge.svg";
import favoriteImg from "@/public/images/icons/ic_heart.svg";
import { ArticleProp } from "@/types/articleTypes";
import { getFormatTime } from "@/utils/Utils";

export default function BestArticleItem({
article: { createdAt, image, likeCount, title, writer },
}: ArticleProp) {
return (
<div className={styles["best-card"]}>
<img className={styles.badge} src={badgeImg.src} alt="Best 뱃지" />
<div className={styles["content-wrapper"]}>
<div className={styles["title-wrapper"]}>
<h2 className={styles["card-title"]}>{title}</h2>
{image && (
<div className={styles["item-box"]}>
<img className={styles.item} src={image} alt="대표 이미지" />
</div>
)}
</div>
<div className={styles["writer-wrapper"]}>
<div className={styles["nickname-wrapper"]}>
<span className={styles["nickname"]}>{writer.nickname}</span>
<div className={styles["favorite-wrapper"]}>
<img
className={styles["favorite"]}
src={favoriteImg.src}
alt="즐겨찾기"
/>
<span className={styles["favorite-count"]}>{likeCount}</span>
</div>
</div>
<span className={styles["date"]}>
{getFormatTime(createdAt, false)}
</span>
</div>
</div>
</div>
);
}
50 changes: 50 additions & 0 deletions components/boards/BestArticleList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useEffect, useState } from "react";
import styles from "./Freeboard.module.scss";
import BestArticleItem from "./BestArticleItem";
import { getArticle } from "@/lib/articleApi";
import { Article, ArticleApiData } from "@/types/articleTypes";
import Link from "next/link";
import useDeviceType from "@/hooks/useDeviceType";
import { DeviceTypePageSize } from "@/types/articleTypes";

const ORDERBY = "like";

export default function BestArticleList() {
const [articles, setArticles] = useState<Article[]>([]);
const { isMobile, isTablet } = useDeviceType();
const { MOBILE_PAGE_SIZE, TABLET_PAGE_SIZE, DESKTOP_PAGE_SIZE } =
DeviceTypePageSize;

const pageSize = isTablet
? TABLET_PAGE_SIZE
: isMobile
? MOBILE_PAGE_SIZE
: DESKTOP_PAGE_SIZE;

const fetchData = async ({ orderBy, pageSize }: ArticleApiData) => {
try {
const result = await getArticle({ orderBy, pageSize });

setArticles(() => result.list);
} catch (error) {
console.log(error);
}
};

useEffect(() => {
fetchData({ orderBy: ORDERBY, pageSize: pageSize });
}, [pageSize]);

return (
<section className={styles["best-article"]}>
<h1 className={styles.title}>베스트 게시글</h1>
<div className={styles["card-container"]}>
{articles.map((article) => (
<Link href={`/board/${article.id}`} key={article.id}>
<BestArticleItem article={article} />
</Link>
))}
</div>
</section>
);
}
Loading

0 comments on commit 34a748e

Please sign in to comment.