diff --git a/public/icons/medal.svg b/public/icons/medal.svg new file mode 100644 index 00000000..d650c401 --- /dev/null +++ b/public/icons/medal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/pages/boards/BestItems.tsx b/src/components/pages/boards/BestItems.tsx index 38f4c7f4..f44e3c17 100644 --- a/src/components/pages/boards/BestItems.tsx +++ b/src/components/pages/boards/BestItems.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { useMediaQuery } from "@/hooks/useMediaQuery"; +import { useMedia } from "@/hooks/useMedia"; import { getArticles } from "@/apis/apis"; import { GetArticlesParams, GetArticlesRes } from "@/apis/apis.type"; import { useQuery } from "@/hooks/useQuery"; @@ -9,24 +9,27 @@ import styles from "./BestItems.module.css"; const pageSizeTable = { PC: 3, TABLET: 2, MOBILE: 1 }; export default function BestItems() { - const media = useMediaQuery(); - const [paramObj, setParamObj] = useState({ - page: 1, - pageSize: pageSizeTable[media], - orderBy: "like", - }); - const { isLoading, error, data } = useQuery< + const media = useMedia(); + const [paramObj, setParamObj] = useState(); + const { isLoading, error, data, query } = useQuery< GetArticlesParams, GetArticlesRes - >(getArticles, paramObj); + >(getArticles); useEffect(() => { - setParamObj((prevObj) => ({ - ...prevObj, - pageSize: pageSizeTable[media], - })); + if (!media) return; + setParamObj((prevObj) => + !prevObj + ? { page: 1, pageSize: pageSizeTable[media], orderBy: "like" } + : { ...prevObj, pageSize: pageSizeTable[media] } + ); }, [media]); + useEffect(() => { + if (!paramObj) return; + query(paramObj); + }, [query, paramObj]); + return (
@@ -34,8 +37,8 @@ export default function BestItems() {
{!isLoading && !error && data && (
- {data.list.map((item) => ( - + {data.list.map((article) => ( + ))}
)} diff --git a/src/components/pages/boards/componnets/BestItem.module.css b/src/components/pages/boards/componnets/BestItem.module.css index 85e3a17f..aea1fb14 100644 --- a/src/components/pages/boards/componnets/BestItem.module.css +++ b/src/components/pages/boards/componnets/BestItem.module.css @@ -2,6 +2,39 @@ display: flex; flex-direction: column; gap: 16px; + padding: 0 24px 16px; + border-radius: 8px; + background-color: var(--gray-50); +} + +.tag { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + width: 100px; + height: 30px; + border-radius: 0 0 15px 15px; + background-color: var(--blue-100); + font-size: var(--size-lg); + font-weight: var(--semibold); + color: white; +} + +.content { + display: flex; + align-items: center; + justify-content: space-between; +} + +.title { + font-size: var(--size-xl); + font-weight: var(--semibold); +} +@media (max-width: 1199px) { + .title { + font-size: var(--size-2gl); + } } .imageWrapper { @@ -16,29 +49,30 @@ object-fit: contain; } -.content { +.info { display: flex; - flex-direction: column; - gap: 6px; -} - -.title { + align-items: center; + gap: 8px; font-size: var(--size-md); } -.price { - font-size: var(--size-lg); - font-weight: bold; +.nickname { + color: var(--gray-600); } .like { + flex-grow: 1; display: flex; align-items: center; gap: 4px; - font-size: var(--size-xs); + color: var(--gray-500); } .likeIcon { width: 16px; height: 16px; } + +.date { + color: var(--gray-400); +} diff --git a/src/components/pages/boards/componnets/BestItem.tsx b/src/components/pages/boards/componnets/BestItem.tsx index 1a82c1a8..98e286a9 100644 --- a/src/components/pages/boards/componnets/BestItem.tsx +++ b/src/components/pages/boards/componnets/BestItem.tsx @@ -1,39 +1,43 @@ import Link from "next/link"; import Image from "next/image"; +import { ArticleProps } from "@/apis/apis.type"; +import medalIcon from "#/icons/medal.svg"; import heartIcon from "#/icons/heart_inactive.svg"; import styles from "./BestItem.module.css"; -import { ArticleProps } from "@/apis/apis.type"; interface ItemProps { - data: ArticleProps; + article: ArticleProps; } -export default function BestItem({ data }: ItemProps) { - const { - id, - title, - writer: { nickname }, - updatedAt, - image, - likeCount, - } = data; +export default function BestItem({ article }: ItemProps) { + const date = new Date(article.updatedAt).toLocaleDateString(); + return ( - -
- {"이미지"} + +
+ + Best
- {title} - {updatedAt} +

{article.title}

+
+ {article.image ? ( + {"이미지"} + ) : undefined} +
+
+
+ {article.writer.nickname}
- 좋아요 수 - {likeCount} + + {article.likeCount}
+ {date}
); diff --git a/src/hooks/useMedia.ts b/src/hooks/useMedia.ts new file mode 100644 index 00000000..2d7b3459 --- /dev/null +++ b/src/hooks/useMedia.ts @@ -0,0 +1,7 @@ +import { useContext } from "react"; +import { MediaContext } from "@/store/MediaContext"; + +export function useMedia() { + const media = useContext(MediaContext); + return media; +} diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts deleted file mode 100644 index 38c9205c..00000000 --- a/src/hooks/useMediaQuery.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useState, useEffect } from "react"; - -export type MediaType = "PC" | "TABLET" | "MOBILE"; - -function checkMedia(width: number): MediaType { - if (width >= 1200) return "PC"; - if (width >= 768) return "TABLET"; - return "MOBILE"; -} - -export function useMediaQuery() { - const [media, setMedia] = useState("PC"); - - useEffect(() => { - setMedia(checkMedia(window.innerWidth)); - - const handleWindowResize = () => { - setMedia(checkMedia(window.innerWidth)); - }; - - window.addEventListener("resize", handleWindowResize); - return () => { - window.removeEventListener("resize", handleWindowResize); - }; - }, []); - - return media; -} diff --git a/src/hooks/useQuery.ts b/src/hooks/useQuery.ts index d506cdd1..85ec8730 100644 --- a/src/hooks/useQuery.ts +++ b/src/hooks/useQuery.ts @@ -1,14 +1,13 @@ -import { useState, useCallback, useEffect } from "react"; +import { useState, useCallback } from "react"; export function useQuery( - fetchFunc: (paramObj: Params) => Promise, - paramObj: Params + fetchFunc: (paramObj: Params) => Promise ) { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [data, setData] = useState(null); - const wrappedFunc = useCallback( + const query = useCallback( async (paramObj: Params) => { try { setIsLoading(true); @@ -27,11 +26,5 @@ export function useQuery( [fetchFunc] ); - const update = () => wrappedFunc(paramObj); - - useEffect(() => { - wrappedFunc(paramObj); - }, [wrappedFunc, paramObj]); - - return { isLoading, error, data, update }; + return { isLoading, error, data, query }; } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index f0671c05..6b3d2cd6 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,6 +1,7 @@ import type { AppProps } from "next/app"; import localFont from "next/font/local"; import Head from "next/head"; +import { MediaProvider } from "@/store/MediaContext"; import Layout from "@/components/layout/Layout"; import "@/styles/reset.css"; import "@/styles/variable.css"; @@ -23,13 +24,15 @@ export default function App({ Component, pageProps }: MyAppProps) { - {Component.isNotLayout ? ( - - ) : ( - + + {Component.isNotLayout ? ( - - )} + ) : ( + + + + )} + ); } diff --git a/src/store/MediaContext.tsx b/src/store/MediaContext.tsx new file mode 100644 index 00000000..f27717d2 --- /dev/null +++ b/src/store/MediaContext.tsx @@ -0,0 +1,36 @@ +import { createContext, useState, useEffect, ReactNode } from "react"; + +export type MediaType = "PC" | "TABLET" | "MOBILE"; + +export const MediaContext = createContext(undefined); + +interface MediaProviderProps { + children: ReactNode; +} + +export function MediaProvider({ children }: MediaProviderProps) { + const [media, setMedia] = useState(); + + useEffect(() => { + const checkMedia = (width: number): MediaType => { + if (width >= 1200) return "PC"; + if (width >= 768) return "TABLET"; + return "MOBILE"; + }; + + const handleWindowResize = () => { + setMedia(checkMedia(window.innerWidth)); + }; + + handleWindowResize(); + + window.addEventListener("resize", handleWindowResize); + return () => { + window.removeEventListener("resize", handleWindowResize); + }; + }, []); + + return ( + {children} + ); +} diff --git a/src/styles/variable.css b/src/styles/variable.css index 5da0cdb9..78c29188 100644 --- a/src/styles/variable.css +++ b/src/styles/variable.css @@ -44,4 +44,7 @@ --line-md: 24px; --line-sm: 22px; --line-xs: 18px; + + /* Font - Weight */ + --semibold: 600; }