diff --git a/components/FolderTag.tsx b/components/FolderTag.tsx index 9745c83..33e91b4 100644 --- a/components/FolderTag.tsx +++ b/components/FolderTag.tsx @@ -1,22 +1,39 @@ -interface FolderData { - id: number; - createdAt: string; - name: string; - linkCount: number; -} +import { useRouter } from "next/router"; +import { FolderListData } from "@/types/folderTypes"; -const FolderTag = (list: FolderData[]) => { - const folderStyle = "py-[8px] px-[12px] border border-purple-100 rounded-md"; +const FolderTag = ({ folderList }: FolderListData) => { + const router = useRouter(); + const { folder: currentFolderId } = router.query; + + const folderStyle = + "py-[8px] px-[12px] border border-purple100 rounded-md hover:bg-purple100 hover:text-white"; + + const handleSubmit = (id: number | string) => { + router.push({ + pathname: router.pathname, + query: { ...router.query, folder: id }, + }); + }; return ( -
-
전체
- {list.map((folder) => ( -
- {folder.name} -
+
+ ); }; diff --git a/components/Link/ActionButtons.tsx b/components/Link/ActionButtons.tsx new file mode 100644 index 0000000..b498677 --- /dev/null +++ b/components/Link/ActionButtons.tsx @@ -0,0 +1,18 @@ +import Image from "next/image"; + +const ActionButtons = () => ( +
+ {[ + { src: "/icons/share.svg", alt: "공유", text: "공유" }, + { src: "/icons/pen.svg", alt: "이름 변경", text: "이름 변경" }, + { src: "/icons/delete.svg", alt: "삭제", text: "삭제" }, + ].map(({ src, alt, text }) => ( + + ))} +
+); + +export default ActionButtons; diff --git a/components/Link/AddLinkInput.tsx b/components/Link/AddLinkInput.tsx index 2c684fc..add6904 100644 --- a/components/Link/AddLinkInput.tsx +++ b/components/Link/AddLinkInput.tsx @@ -1,37 +1,40 @@ -import { ChangeEvent, FormEvent, useState } from "react"; -import { postLink } from "@/lib/api/link"; +import { ChangeEvent, KeyboardEvent, useState } from "react"; +import { FolderListData } from "@/types/folderTypes"; import Image from "next/image"; import SubmitButton from "../SubMitButton"; -const AddLinkInput = (folderId: number) => { - const [value, setValue] = useState(""); +const AddLinkInput = ({ folderList }: FolderListData) => { + const [link, setLink] = useState(""); const handleChange = (e: ChangeEvent) => { - setValue(e.target.value); + setLink(e.target.value); }; - const handleSubmit = async (e: FormEvent) => { - e.preventDefault(); - await postLink({ url: value, folderId }); - // postLink 하고 추가된 link가 보이도록 하는 로직 구현해야 함. + const handleClick = () => { + // Addmodal 띄우면서 link 전달 + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Enter") { + e.preventDefault(); + handleClick(); + } }; return ( -
+
link icon - - 추가하기 - - +
+ 추가하기 +
+
); }; diff --git a/components/LinkCard.tsx b/components/LinkCard.tsx index e84edb3..dbb2e77 100644 --- a/components/LinkCard.tsx +++ b/components/LinkCard.tsx @@ -2,21 +2,20 @@ import { useState } from "react"; import timeAgo from "@/util/timAgo"; import Image from "next/image"; -interface linkDataType { - id: number; - title: string; - description: string; - favorite?: boolean; - imageSource: string; - url: string; - createdAt: string; +interface LinkCardProps { + info: { + id: number; + title: string; + description: string; + favorite?: boolean; + imageSource: string; + url: string; + createdAt: string; + }; + isFavoritePage?: boolean; } -interface CardItemProps extends linkDataType { - isFavoritePage?: boolean; // 즐겨찾기 페이지 여부를 판별하는 flag -} - -const LinkCard = ({ isFavoritePage, ...info }: CardItemProps) => { +const LinkCard = ({ isFavoritePage, info }: LinkCardProps) => { const [isSubscribed, setIsSubscribed] = useState(false); const [isOpen, setIsOpen] = useState(false); diff --git a/pages/api/folders/index.ts b/pages/api/folders/index.ts index 1b52708..e7b7f12 100644 --- a/pages/api/folders/index.ts +++ b/pages/api/folders/index.ts @@ -1,32 +1,41 @@ -import axiosInstance from "@/lib/api/axiosInstanceApi"; -import axios from "axios"; import { NextApiRequest, NextApiResponse } from "next"; +import { parse } from "cookie"; +import axiosInstance from "@/lib/api/axiosInstanceApi"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { - const token = req.cookies.accessToken; - console.log("Token:", token); // 쿠키 확인 + const cookies = parse(req.headers.cookie || ""); + const accessToken = cookies.accessToken; - if (!token) { - return res.status(401).json({ error: "사용자 정보를 찾을 수 없습니다." }); - } + switch (req.method) { + case "GET": + // 유저의 모든 폴더 조회 + try { + const response = await axiosInstance.get("/folders", { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.status(201).json(response.data); + } catch (err) { + console.error(err); + return res + .status(500) + .json({ message: "모든 폴저 조회에 실패했습니다." }); + } - if (req.method === "POST") { - try { - await axiosInstance.post("/folders", req.body, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - return res.status(200).json({ message: "폴더 생성 성공" }); - } catch (error) { - if (axios.isAxiosError(error) && error.response) { - const status = error.response.status; - const message = error.response.data?.message || "알 수 없는 오류 발생"; - return res.status(status).json({ message }); + case "POST": + // 유저의 폴더 생성 + try { + const response = await axiosInstance.post("/folders", req.body, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.status(201).json(response.data); + } catch (err) { + console.error(err); + return res.status(500).json({ message: "폴더 생성에 실패했습니다." }); } - } - } else { - res.status(405).json({ message: "허용되지 않은 접근 방법" }); + + default: + res.setHeader("Allow", ["GET", "POST"]); + return res.status(405).end(`메서드 ${req.method}는 허용되지 않습니다.`); } }; diff --git a/pages/api/links/index.ts b/pages/api/links/index.ts index 4bde147..0b8f68f 100644 --- a/pages/api/links/index.ts +++ b/pages/api/links/index.ts @@ -1,32 +1,41 @@ -import axiosInstance from "@/lib/api/axiosInstanceApi"; -import axios, { isAxiosError } from "axios"; import { NextApiRequest, NextApiResponse } from "next"; +import { parse } from "cookie"; +import axiosInstance from "@/lib/api/axiosInstanceApi"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { - const token = req.cookies.accessToken; - console.log("Token:", token); + const cookies = parse(req.headers.cookie || ""); + const accessToken = cookies.accessToken; - if (!token) { - return res.status(401).json({ error: "사용자 정보를 찾을 수 없습니다." }); - } + switch (req.method) { + case "GET": + // 유저의 전체 링크 조회 + try { + const response = await axiosInstance.get("/links", { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.status(201).json(response.data); + } catch (err) { + console.error(err); + return res + .status(500) + .json({ message: "전체 링크 조회에 실패했습니다." }); + } - if (req.method === "POST") { - try { - await axiosInstance.post("/links", req.body, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - return res.status(201).json({ message: "링크 추가 성공" }); - } catch (error) { - if (isAxiosError(error) && error.response) { - const status = error.response.status; - const message = error.response.data?.message || "알 수 없는 오류 발생"; - return res.status(status).json({ message }); + case "POST": + // 링크 생성 로직 + try { + const response = await axiosInstance.post("/links", req.body, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + return res.status(201).json(response.data); + } catch (err) { + console.error(err); + return res.status(500).json({ message: "링크 생성에 실패했습니다." }); } - } - } else { - res.status(405).json({ message: "허용되지 않은 접근 방법" }); + + default: + res.setHeader("Allow", ["GET", "POST"]); + return res.status(405).end(`메서드 ${req.method}는 허용되지 않습니다.`); } }; diff --git a/pages/favorite/index.tsx b/pages/favorite/index.tsx index d5a345a..5b8a46d 100644 --- a/pages/favorite/index.tsx +++ b/pages/favorite/index.tsx @@ -54,16 +54,7 @@ const FavoritePage = ({ favoriteList, totalCount }: FavoriteProps) => { {favoriteList.length > 0 ? favoriteList.map((favorite) => ( - + )) : null} diff --git a/pages/link/index.tsx b/pages/link/index.tsx new file mode 100644 index 0000000..11b9bca --- /dev/null +++ b/pages/link/index.tsx @@ -0,0 +1,75 @@ +import { GetServerSidePropsContext } from "next"; +import { proxy } from "@/lib/api/axiosInstanceApi"; +import { LinkData } from "@/types/linkTypes"; +import { FolderData } from "@/types/folderTypes"; +import { SearchInput } from "../../components/Search/SearchInput"; +import Container from "@/components/Layout/Container"; +import CardsLayout from "@/components/Layout/CardsLayout"; +import ActionButtons from "@/components/Link/ActionButtons"; +import AddLinkInput from "@/components/Link/AddLinkInput"; +import FolderTag from "../../components/FolderTag"; +import LinkCard from "../../components/LinkCard"; + +interface LinkPageProps { + linkList: LinkData[]; + folderList: FolderData[]; +} + +export const getServerSideProps = async ( + context: GetServerSidePropsContext +) => { + const { req } = context; + + const fetchData = async (endpoint: string) => { + const response = await proxy.get(endpoint, { + headers: { + Cookie: req.headers.cookie, + }, + }); + return response.data; + }; + + const [links, folders] = await Promise.all([ + fetchData("/api/links"), + fetchData("/api/folders"), + ]); + + return { + props: { + linkList: links.list || [], + folderList: folders || [], + }, + }; +}; + +const LinkPage = ({ linkList, folderList }: LinkPageProps) => { + return ( + <> +
+ +
+
+ + +
+ {folderList && } + +
+
+

유용한 글

+ +
+ + {linkList.map((link) => ( + + ))} + +
+
+ + ); +}; + +export default LinkPage; diff --git a/public/icons/delete.svg b/public/icons/delete.svg new file mode 100644 index 0000000..1923de9 --- /dev/null +++ b/public/icons/delete.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/icons/pen.svg b/public/icons/pen.svg new file mode 100644 index 0000000..fb75fc3 --- /dev/null +++ b/public/icons/pen.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/share.svg b/public/icons/share.svg new file mode 100644 index 0000000..da9d0c1 --- /dev/null +++ b/public/icons/share.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/types/folderTypes.ts b/types/folderTypes.ts new file mode 100644 index 0000000..399196f --- /dev/null +++ b/types/folderTypes.ts @@ -0,0 +1,10 @@ +export interface FolderData { + id: number; + createdAt: string; + name: string; + linkCount: number; +} + +export interface FolderListData { + folderList: FolderData[]; +} diff --git a/types/linkTypes.ts b/types/linkTypes.ts new file mode 100644 index 0000000..96088c6 --- /dev/null +++ b/types/linkTypes.ts @@ -0,0 +1,9 @@ +export interface LinkData { + id: number; + favorite: boolean; + url: string; + title: string; + imageSource: string; + description: string; + createdAt: string; +}