Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[길수진] week20 #1083

35 changes: 30 additions & 5 deletions components/common/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,43 @@ import styles from "./card.module.css";
import DeleteModal from "@/components/common/Modal/DeleteModal/DeleteModal";
import FolderModal from "@/components/common/Modal/FolderModal/FolderModal";

import { useDeleteLink } from "hooks/useDeleteLink";

import { formatDate, getTimeDifference } from "utils/date";
import { MODALS } from "constants/modals";
import type { LinkItem, Folder } from "types";
import noImage from "@/images/bg_noImage.png";

interface Props {
item: LinkItem;
folderId: number;
folderList: Folder[] | null;
}

export type Link = {
id: number;
favorite: boolean;
created_at: Date;
url: string;
title: string;
image_source: string;
description: string;
[key: string]: number | Date | string | boolean;
};

// TODO: Card 컴포넌트 분리
function Card({ item, folderList }: Props) {
const { createdAt, created_at, description, imageSource, image_source, url } =
item;
function Card({ item, folderId, folderList }: Props) {
// const { createdAt, created_at, description, imageSource, image_source, url } =
// item;

const date = createdAt || created_at;
const { id, created_at: date, url, image_source: imgUrl, description } = item;

const imgUrl = imageSource || image_source;
const {
mutateAsync: deleteLink,
isPending,
isError,
error,
} = useDeleteLink(folderId);

const absoluteImageUrl = imgUrl?.startsWith("//")
? `https:${imgUrl}`
Expand All @@ -38,6 +57,11 @@ function Card({ item, folderList }: Props) {
setCurrentModal(null);
};

const handleDelete = async () => {
await deleteLink(id);
closeModal();
};

const handleCardClick = (url: string) => {
window.open(url, "_blank");
};
Expand Down Expand Up @@ -137,6 +161,7 @@ function Card({ item, folderList }: Props) {
isOpen={currentModal === MODALS.deleteLink}
title="폴더 삭제"
deletion={url}
onClick={handleDelete}
onCloseClick={closeModal}
/>

Expand Down
5 changes: 3 additions & 2 deletions components/common/CardList/CardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import NoResults from "@/components/common/NoResults/NoResults";
import type { LinkItem, Folder } from "types";

interface Props {
folderId: number;
items: LinkItem[] | null;
folderList: Folder[] | null;
}

function CardList({ items, folderList }: Props) {
function CardList({ folderId, items, folderList }: Props) {
if (!items || items.length === 0) {
return <NoResults />;
}
Expand All @@ -19,7 +20,7 @@ function CardList({ items, folderList }: Props) {
<ul className={styles.list}>
{items.map((item) => (
<li key={item.id}>
<Card item={item} folderList={folderList} />
<Card item={item} folderId={folderId} folderList={folderList} />
</li>
))}
</ul>
Expand Down
17 changes: 10 additions & 7 deletions components/common/Modal/DeleteModal/DeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ type Props = {
isOpen: boolean;
title: string;
deletion: string;
onClick: MouseEventHandler<HTMLButtonElement>;
onCloseClick: MouseEventHandler<HTMLDivElement | HTMLButtonElement>;
};

const DeleteModal = ({ isOpen, title, deletion, onCloseClick }: Props) => {
const DeleteModal = ({
isOpen,
title,
deletion,
onClick,
onCloseClick,
}: Props) => {
return (
<BaseModal
title={title}
isOpen={isOpen}
onCloseClick={onCloseClick}
>
<BaseModal title={title} isOpen={isOpen} onCloseClick={onCloseClick}>
<p className={styles.deleted}>{deletion}</p>

<button type="button" className={styles.deletedBtn}>
<button type="button" className={styles.deletedBtn} onClick={onClick}>
삭제하기
</button>
</BaseModal>
Expand Down
4 changes: 3 additions & 1 deletion components/common/Modal/FolderModal/FolderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Props = {
onCloseClick: MouseEventHandler<HTMLDivElement | HTMLButtonElement>;
onKeyDown?: KeyboardEventHandler<HTMLDivElement>;
onChange?: ChangeEventHandler<HTMLInputElement>;
onClick: MouseEventHandler<HTMLDivElement | HTMLButtonElement>;
};

const FolderModal = ({
Expand All @@ -25,6 +26,7 @@ const FolderModal = ({
link,
folderList,
onCloseClick,
onClick,
}: Props) => {
return (
<BaseModal isOpen={isOpen} title={title} onCloseClick={onCloseClick}>
Expand All @@ -43,7 +45,7 @@ const FolderModal = ({
) : (
<input type="text" className={styles.input} placeholder="내용 입력" />
)}
<button type="button" className={styles.btn}>
<button type="button" className={styles.btn} onClick={onClick}>
{buttonText}
</button>
</BaseModal>
Expand Down
10 changes: 4 additions & 6 deletions components/common/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,20 @@ import styles from "./navbar.module.css";
import Profile from "./Profile/Profile";
import { ROUTE_PATHS } from "constants/route";
import Logo from "@/images/logo.svg";
import useGetUser from "hooks/useGetUser";
import { useGetUser } from "hooks/useGetUser";

const Navbar = () => {
const { data, isError, isPending } = useGetUser();
Copy link
Collaborator

Choose a reason for hiding this comment

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

api 요청을 hook으로 관리하려는 시도 너무 좋은 것 같아요👍🏻


if (isPending) {
return <></>;
}

return (
<nav className={styles.navbar}>
<div className={styles.wrap}>
<Link href={ROUTE_PATHS.home}>
<Logo width="133" height="24" alt="로고" priority />
</Link>
{isError ? (
{isPending ? (
<></>
) : isError ? (
<Link href={ROUTE_PATHS.login}>로그인</Link>
) : (
<Link href={ROUTE_PATHS.home}>
Expand Down
19 changes: 8 additions & 11 deletions components/folder/Category/Category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,21 @@ import { ALL } from "constants/etc";
import type { Folder } from "types";
import type { SelectedCategory } from "pages/folder";


interface Props {
buttonNames: Folder[];
selectedCategory: SelectedCategory;
onClick: (id: number | null, name: string) => void;
}

function Category({ buttonNames, selectedCategory, onClick }: Props) {
const Category = ({ buttonNames, selectedCategory, onClick }: Props) => {
return (
<div className={styles.buttons}>
<Link href={ROUTE_PATHS.folder}>
<FolderButton
isChecked={selectedCategory.name === ALL}
onClick={() => onClick(null, ALL)}
>
{ALL}
</FolderButton>
</Link>
<FolderButton
isChecked={selectedCategory.name === ALL}
onClick={() => onClick(null, ALL)}
>
{ALL}
</FolderButton>
{buttonNames.map(({ id, name }) => (
<FolderButton
key={id}
Expand All @@ -35,6 +32,6 @@ function Category({ buttonNames, selectedCategory, onClick }: Props) {
))}
</div>
);
}
};

export default Category;
24 changes: 22 additions & 2 deletions components/folder/FolderToolBar/FolderToolBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import FolderModal from "@/components/common/Modal/FolderModal/FolderModal";
import DeleteModal from "@/components/common/Modal/DeleteModal/DeleteModal";
import ShareModal from "@/components/common/Modal/ShareModal/ShareModal";

import { ALL } from "constants/etc";
import { useDeleteFolder } from "hooks/useDeleteFolder";

import { MODALS } from "constants/modals";
import AddIcon from "@/images/ic_add.svg";

Expand All @@ -27,10 +28,25 @@ const FolderToolBar = ({
}: Props) => {
const [currentModal, setCurrentModal] = useState<string | null>(null);

const { mutateAsync: deleteFolder } = useDeleteFolder();

const closeModal = () => {
setCurrentModal(null);
};

const handleFolderName = () => {
console.log("change name");
// useMutation
};

const handleDelete = async () => {
if (selectedCategory.id) {
await deleteFolder(selectedCategory.id);
onCategoryClick(null, "전체");
closeModal();
}
};

return (
<>
<div className={styles.category}>
Expand All @@ -54,14 +70,16 @@ const FolderToolBar = ({
isOpen={currentModal === MODALS.addFolder}
title="폴더 추가"
buttonText="추가하기"
onClick={() => console.log("추가")}
onCloseClick={closeModal}
/>

<div className={styles.bar}>
<div className={styles.categoryName}>{selectedCategory.name}</div>

<div
className={`${styles.barButtons} ${
selectedCategory.name === ALL ? styles.hidden : ""
selectedCategory.id === null ? styles.hidden : ""
}`}
>
<ActionButton
Expand Down Expand Up @@ -92,13 +110,15 @@ const FolderToolBar = ({
isOpen={currentModal === MODALS.edit}
title="폴더 이름 변경"
buttonText="변경하기"
onClick={handleFolderName}
onCloseClick={closeModal}
/>

<DeleteModal
isOpen={currentModal === MODALS.deleteFolder}
title="폴더 삭제"
deletion={selectedCategory.name}
onClick={handleDelete}
onCloseClick={closeModal}
/>
</>
Expand Down
22 changes: 22 additions & 0 deletions hooks/useDeleteFolder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import instance from "lib/axios";

export const useDeleteFolder = () => {
const deleteFolder = async (folderId: number) => {
try {
await instance.delete(`/folders/${folderId}`);
} catch (error) {
throw error;
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 부분도 api 폴더로 따로 빼서 관리해보면 좋을 것 같아요!


const queryClient = useQueryClient();

return useMutation({
mutationFn: deleteFolder,
onSuccess: () =>
queryClient.invalidateQueries({
queryKey: ["folders"],
Copy link
Collaborator

Choose a reason for hiding this comment

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

tanstack query 메인테이너인 tkdodo 블로그를 보면 queryKey를 관리하는 방식에 대해서 설명해주고 있어요!

해당 부분 참고해서 보시면 좋을 것 같아요!

📌 참고
effective query key

}),
});
};
22 changes: 22 additions & 0 deletions hooks/useDeleteLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import instance from "lib/axios";

export const useDeleteLink = (folderId: number) => {
const deleteLink = async (linkId: number) => {
try {
await instance.delete(`/links/${linkId}`);
} catch (error) {
throw error;
}
};

const queryClient = useQueryClient();

return useMutation({
mutationFn: deleteLink,
onSuccess: () =>
queryClient.invalidateQueries({
queryKey: ["links", folderId],
}),
});
};
30 changes: 22 additions & 8 deletions hooks/useGetFolders.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import useFetch from "hooks/useFetch";
import type { Folder } from "types";
import { useQuery } from "@tanstack/react-query";
import instance from "lib/axios";

export const useGetFolders = () => {
export type Folder = {
id: number;
created_at: Date;
favorite: boolean;
name: string;
link_count: number;
};

const { data, loading, error } = useFetch<{ data: { folder: Folder[] } }>(
`/folders`
);
const folderData = data?.data?.folder ?? [];
const fetchFolders = async () => {
try {
const { data } = await instance.get<Folder[]>("/folders");
return data;
} catch (error) {
throw error;
}
};

return { data: folderData, loading, error };
export const useGetFolders = () => {
return useQuery({
queryKey: ["folders"],
queryFn: fetchFolders,
});
};
Loading