Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions components/Folder/AddFolderButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FolderData } from "@/types/folderTypes";
import useModalStore from "@/store/useModalStore";
import useRerenderFolderList from "@/hooks/useRerenderFolderList";

interface AddFolderButtonProps {
setFolderList: React.Dispatch<React.SetStateAction<FolderData[]>>;
}

export const AddFolderButton = ({ setFolderList }: AddFolderButtonProps) => {
const { isOpen, openModal } = useModalStore();

useRerenderFolderList(isOpen, setFolderList);

return (
<button
className="w-[79px] h-[19px] text-purple100"
onClick={() => openModal("AddFolderModal")}
>
폴더 추가 +
</button>
);
};
export default AddFolderButton;
51 changes: 51 additions & 0 deletions components/Folder/FolderActionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { FolderData } from "@/types/folderTypes";
import Image from "next/image";
import useModalStore from "@/store/useModalStore";
import useRerenderFolderList from "../../hooks/useRerenderFolderList";

interface FolderActionsMenuProps {
setFolderList: React.Dispatch<React.SetStateAction<FolderData[]>>;
}

const FolderActionsMenu = ({ setFolderList }: FolderActionsMenuProps) => {
const { isOpen, openModal } = useModalStore();

const handleModalOpen = (text: string) => {
switch (text) {
case "공유":
openModal("SNSModal");
break;
case "이름 변경":
openModal("EditModal");
break;
case "삭제":
openModal("DeleteFolderModal");
break;
default:
break;
}
};

useRerenderFolderList(isOpen, setFolderList);

return (
<div className="w-[192px] h-[18px] flex justify-between gap-[12px] text-gray400">
{[
{ src: "/icons/share.svg", alt: "공유", text: "공유" },
{ src: "/icons/pen.svg", alt: "이름 변경", text: "이름 변경" },
{ src: "/icons/delete.svg", alt: "삭제", text: "삭제" },
].map(({ src, alt, text }) => (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버튼 구현 이렇게 하니까 깔끔하네요

<button
key={text}
className="flex items-center gap-[4px] text-sm"
onClick={() => handleModalOpen(text)}
>
<Image width={18} height={18} src={src} alt={alt} />
<span>{text}</span>
</button>
))}
</div>
);
};

export default FolderActionsMenu;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const FolderTag = ({ folderList }: FolderListData) => {
const handleSubmit = (id: number | string) => {
router.push({
pathname: router.pathname,
query: { ...router.query, folder: id },
query: id ? { folder: id } : {},
});
};

Expand Down
18 changes: 0 additions & 18 deletions components/Link/ActionButtons.tsx

This file was deleted.

9 changes: 7 additions & 2 deletions components/Link/AddLinkInput.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { ChangeEvent, KeyboardEvent, useState } from "react";
import { ChangeEvent, KeyboardEvent, useEffect, useState } from "react";
import { FolderListData } from "@/types/folderTypes";
import { Modal } from "../modal/modalManager/ModalManager";
import Image from "next/image";
import SubmitButton from "../SubMitButton";
import useModalStore from "@/store/useModalStore";

const AddLinkInput = ({ folderList }: FolderListData) => {
const { isOpen, openModal } = useModalStore();
const [link, setLink] = useState("");

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setLink(e.target.value);
};

const handleClick = () => {
// Addmodal 띄우면서 link 전달
openModal("AddModal", { list: folderList, link: link });
setLink("");
};

const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
Expand All @@ -34,6 +38,7 @@ const AddLinkInput = ({ folderList }: FolderListData) => {
<div onClick={handleClick}>
<SubmitButton className="w-[80px] h-[37px]">추가하기</SubmitButton>
</div>
{isOpen && <Modal />}
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion components/LinkCard.tsx → components/Link/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import timeAgo from "@/util/timAgo";
import Image from "next/image";
import Dropdown from "./Dropdown";
import Dropdown from "../Dropdown";
import useModalStore from "@/store/useModalStore";

interface LinkCardProps {
Expand Down
37 changes: 18 additions & 19 deletions components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";

interface PaginationProps {
page: number;
pageSize: number;
totalCount: number;
}

const Pagination: React.FC<PaginationProps> = ({
page,
pageSize,
totalCount,
}) => {
const Pagination: React.FC<PaginationProps> = ({ totalCount }) => {
const router = useRouter();
const LiStyle = "relative w-12 h-12 rounded-lg bg-gray900";
const buttonStyle = "flex justify-center items-center h-full text-black400";
const { page, pageSize } = router.query;

const totalPages = Math.ceil(totalCount / pageSize);
const currentPage = Number(page);
const currentPageSize = Number(pageSize);
const totalPages = Math.ceil(totalCount / currentPageSize);
const [maxPagesToShow, setMaxPagesToShow] = useState(2);

// 화면 크기 변화에 따라 pageSize와 maxPagesToShow를 설정
Expand Down Expand Up @@ -45,12 +44,12 @@ const Pagination: React.FC<PaginationProps> = ({
} else {
// 첫 페이지와 마지막 페이지는 항상 표시
pages.push(1);
let start = Math.max(2, page - 1);
let end = Math.min(totalPages - 1, page + 1);
let start = Math.max(2, currentPage - 1);
let end = Math.min(totalPages - 1, currentPage + 1);

if (page > 3) pages.push("...");
if (currentPage > 3) pages.push("...");
for (let i = start; i <= end; i++) pages.push(i);
if (page < totalPages - 2) pages.push("...");
if (currentPage < totalPages - 2) pages.push("...");
pages.push(totalPages);
}

Expand All @@ -61,12 +60,12 @@ const Pagination: React.FC<PaginationProps> = ({
<ul className="flex justify-center gap-[10px] my-10">
<li className={LiStyle}>
<Link
href={`/link?page=${page - 1}&pageSize=${pageSize}`}
className={`${buttonStyle} ${page > 1 ? "text-black500" : "pointer-events-none"}`}
href={`/link?page=${currentPage - 1}&pageSize=${currentPageSize}`}
className={`${buttonStyle} ${currentPage > 1 ? "text-black500" : "pointer-events-none"}`}
>
<Image
src={
page > 1
currentPage > 1
? "/icons/pagination-left-active.png"
: "/icons/pagination-left.png"
}
Expand All @@ -83,7 +82,7 @@ const Pagination: React.FC<PaginationProps> = ({
<li key={index} className={LiStyle}>
<Link
href={`/link?page=${pageNum}&pageSize=${pageSize}`}
className={`${buttonStyle} ${pageNum === page ? "text-black500" : "text-black400"}`}
className={`${buttonStyle} ${pageNum === currentPage ? "text-black500" : "text-black400"}`}
>
{pageNum}
</Link>
Expand All @@ -100,12 +99,12 @@ const Pagination: React.FC<PaginationProps> = ({

<li className={LiStyle}>
<Link
href={`/link?page=${page + 1}&pageSize=${pageSize}`}
className={`${buttonStyle} ${page < totalPages ? "text-black500" : "pointer-events-none"}`}
href={`/link?page=${currentPage + 1}&pageSize=${pageSize}`}
className={`${buttonStyle} ${currentPage < totalPages ? "text-black500" : "pointer-events-none"}`}
>
<Image
src={
page < totalPages
currentPage < totalPages
? "/icons/pagination-right-active.png"
: "/icons/pagination-right.png"
}
Expand Down
1 change: 1 addition & 0 deletions components/Search/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const SearchInput = () => {
pathname: router.pathname,
query: { ...router.query, search: value },
});
setValue("");
};

return (
Expand Down
14 changes: 14 additions & 0 deletions components/Search/SearchResultMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface SearchResultMessageProps {
message: string | string[];
}

const SearchResultMessage = ({ message }: SearchResultMessageProps) => {
return (
<div className="text-[32px] text-gray400 mt-[40px]">
<span className="text-black300">&quot;{message}&quot;</span>으로 검색한
결과입니다.
</div>
);
};

export default SearchResultMessage;
32 changes: 32 additions & 0 deletions hooks/useFetchLinks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect } from "react";
import { proxy } from "@/lib/api/axiosInstanceApi";
import { LinkData } from "@/types/linkTypes";
import useViewport from "./useViewport";

// 링크페이지의 query가 바뀌면 새로운 리스트로 업데이트 해주는 훅
const useFetchLinks = (
query: {
page?: number;
search?: string;
},
setLinkCardList: (list: LinkData[]) => void
) => {
const { isTablet } = useViewport();

useEffect(() => {
const fetchLinks = async () => {
const res = await proxy.get("/api/links", {
params: {
page: query.page,
pageSize: isTablet ? 6 : 10,
search: query.search,
},
});
setLinkCardList(res.data.list);
};

if (query) fetchLinks();
}, [query, setLinkCardList, isTablet]);
};

export default useFetchLinks;
29 changes: 29 additions & 0 deletions hooks/useRerenderFolderList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useEffect, useRef } from "react";
import { getFolders } from "@/lib/api/folder";
import { FolderData } from "@/types/folderTypes";

// 모달이 닫혔을 때 새로운 FolderList를 보여주는 커스텀 훅
const useRerenderFolderList = (
isOpen: boolean,
setFolderList: React.Dispatch<React.SetStateAction<FolderData[]>>
) => {
const isFirstRender = useRef(true);

useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return; // 최초 로드 시에 불필요한 fetch 요청을 막아줌.
}

const fetchNewFolderList = async () => {
const res = await getFolders();
setFolderList(res);
};

if (!isOpen) {
fetchNewFolderList(); // 드랍다운이 한번 열리고 닫혔을 때 데이터 fetch
}
}, [isOpen, setFolderList]);
};

export default useRerenderFolderList;
32 changes: 32 additions & 0 deletions hooks/useViewport.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잘 사용하겠습니다 😭

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useState, useEffect } from "react";

const breakpoints = {
PC: { min: 1200 },
Tablet: { min: 768, max: 1199 },
Mobile: { min: 343, max: 767 },
};

// 현재 브라우저의 innerWidth와 반응형 상태를 반환하는 훅
function useViewport(initialWidth = 0) {
const [width, setWidth] = useState(initialWidth);

const handleResize = () => {
setWidth(window.innerWidth);
};

useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);

const isPC = width >= breakpoints.PC.min;
const isTablet =
width >= breakpoints.Tablet.min && width <= breakpoints.Tablet.max;
const isMobile =
width >= breakpoints.Mobile.min && width <= breakpoints.Mobile.max;

return { width, isPC, isTablet, isMobile };
}

export default useViewport;
4 changes: 2 additions & 2 deletions lib/api/axiosInstanceApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import axios from "axios";

const axiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000",
timeout: 5000,
timeout: 10000,
withCredentials: true,
});

export const proxy = axios.create({
// 배포 이후에는 배포된 URL로 변경해야 함.
baseURL: "http://localhost:3000",
timeout: 5000,
timeout: 10000,
withCredentials: true,
});

Expand Down
10 changes: 10 additions & 0 deletions lib/api/fetchProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { proxy } from "./axiosInstanceApi";

// SSR에서 proxy로 요청 보낼 때 사용하는 로직 ㅜㅊ상화
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별건 아니지만.. 오타가 났네욥

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하겟습니다~

const fetchProxy = async (endpoint: string, req: any) => {
const headers = req ? { Cookie: req.headers.cookie } : undefined;
const response = await proxy.get(endpoint, { headers });
return response.data;
};

export default fetchProxy;
Loading
Loading