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

[김은재] Week19 #489

Merged
Show file tree
Hide file tree
Changes from all 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
100 changes: 96 additions & 4 deletions api/folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { BASE_URL } from "@/constants/url";

export const getFolderNavInfo = async (userId: number) => {
try {
const response = await fetch(`${BASE_URL}/api/users/${userId}/folders`);
const response = await fetch(`${BASE_URL}/users/${userId}/folders`);
if (!response.ok) {
throw new Error(`${response.status}`);
}

const result = await response.json();

return result;
} catch (error) {
if (error instanceof Error) alert(`${error.message}에러 발생!`);
Expand All @@ -19,9 +19,9 @@ export const getFolderListInfo = async (
navId: string | string[] | undefined,
userToken: string | undefined
) => {
let query = "/api/links";
let query = "/links";
if (navId) {
query += `?folderId=${navId}`;
query = `/folders/${navId}${query}`;
}

try {
Expand All @@ -44,3 +44,95 @@ export const getFolderListInfo = async (
return false;
}
};

export const addFolder = async (folderName: string, userToken: string) => {
const response = await fetch(`${BASE_URL}/folders`, {
method: "POST",
headers: {
Authorization: `Bearer ${userToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: folderName,
}),
});

if (!response.ok) {
throw new Error("Failed to add Folder!!");
}
};

export const changeFolderName = async (
pageNavId: string | string[],
folderName: string,
userToken: string
) => {
const response = await fetch(`${BASE_URL}/folders/${pageNavId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${userToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: folderName,
}),
});

if (!response.ok) {
throw new Error("Failed to add Folder!!");
}
};

export const deleteFolder = async (
pageNavId: string | string[],
userToken: string
) => {
const response = await fetch(`${BASE_URL}/folders/${pageNavId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${userToken}`,
},
});

if (!response.ok) {
throw new Error("Failed to add Folder!!");
}
};

export const addLink = async (
linkUrl: string,
folderId: number,
userToken: string
) => {
const response = await fetch(`${BASE_URL}/links`, {
method: "POST",
Copy link
Collaborator

Choose a reason for hiding this comment

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

HTTP메소드들도 함수 util로 분리한다면 fetch 함수를 사용하는데 더 확장성 있게 사용할 수 있을거에요!

export const post = async (url, body) => {
  return await fetch(`${BASE_URL}{url}`, {
    method: 'POST',
    headers: {},
    body: body
  })
}

export const addLink = () => {
  const body = JSON.stringify({
      url: linkUrl,
      folderId: folderId,
  })
  return post('/links', body)
}

headers: {
Authorization: `Bearer ${userToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: linkUrl,
folderId: folderId,
}),
});

if (!response.ok) {
if (response.status === 400) {
throw new Error("이미 존재하는 URL입니다.");
}
throw new Error("Failed to add Folder!!");
}
};

export const deleteLink = async (linkId: number, userToken: string) => {
const response = await fetch(`${BASE_URL}/links/${linkId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${userToken}`,
},
});

if (!response.ok) {
throw new Error("Failed to add Folder!!");
}
};
6 changes: 4 additions & 2 deletions api/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ export const getSharedFolderInfo = async (
folderId: string | string[] | undefined
) => {
try {
const response = await fetch(`${BASE_URL}/api/folders/${folderId}`);
const response = await fetch(`${BASE_URL}/folders/${folderId}`);
if (!response.ok) {
throw new Error(`${response.status}`);
}

const result = await response.json();

return result;
} catch (error) {
if (error instanceof Error) alert(`${error.message}에러 발생!!`);
Expand All @@ -23,13 +24,14 @@ export const getSharedLinkList = async (
) => {
try {
const response = await fetch(
`${BASE_URL}/api/users/${userId}/links?folderId=${folderId}`
`${BASE_URL}/users/${userId}/links?folderId=${folderId}`
);
if (!response.ok) {
throw new Error(`${response.status}`);
}

const result = await response.json();

return result;
} catch (error) {
if (error instanceof Error) alert(`${error.message}에러 발생2!`);
Expand Down
9 changes: 4 additions & 5 deletions api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ interface SignUserData {

export const getSignInProfile = async (userToken: string) => {
let result = null;

try {
const response = await fetch(`${BASE_URL}/api/users`, {
const response = await fetch(`${BASE_URL}/users`, {
headers: {
Authorization: `Bearer ${userToken}`,
},
Expand All @@ -34,7 +33,7 @@ export const getUserInfo = async (userId: string) => {
let result = null;

try {
const response = await fetch(`${BASE_URL}/api/users/${userId}`);
const response = await fetch(`${BASE_URL}/users/${userId}`);
if (!response.ok) {
throw new Error(`${response.status}`);
}
Expand All @@ -51,7 +50,7 @@ export const postSign = async (apiUrl: string, userData: SignUserData) => {
let result = null;

try {
const response = await fetch(`${BASE_URL}/api/${apiUrl}`, {
const response = await fetch(`${BASE_URL}/${apiUrl}`, {
method: "POST",
headers: POST_HEADER,
body: JSON.stringify(userData),
Expand All @@ -72,7 +71,7 @@ export const postCheckEmail = async (email: string) => {
let result = null;

try {
const response = await fetch(`${BASE_URL}/api/check-email`, {
const response = await fetch(`${BASE_URL}/users/check-email`, {
method: "POST",
headers: POST_HEADER,
body: JSON.stringify({ email: email }),
Expand Down
15 changes: 9 additions & 6 deletions components/common/KebabList.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import * as S from "./KebabList.styled";
import useModal from "@/hooks/useModal";
import { ModalParam } from "@/types/Modal";
import LinkDeleteContent from "./modal/modalContent/LinkDeleteContent";
import LinkAddContent from "./modal/modalContent/LinkAddContent";

interface IKebabList {
linkId: number;
linkUrl: string;
setKebabOpen: (isOpen: boolean) => void;
}

const KebabList = ({ linkUrl, setKebabOpen }: IKebabList) => {
const KebabList = ({ linkId, linkUrl, setKebabOpen }: IKebabList) => {
const { openModal } = useModal();

const handleOpenModal = (
e: React.MouseEvent,
{ type, props }: ModalParam
{ props, component }: ModalParam
) => {
e.preventDefault();
setKebabOpen(false);
openModal({ type, props });
openModal({ props, component });
};

return (
<S.KebabList>
<S.KebabListItem
onClick={(e) => {
handleOpenModal(e, {
type: "linkDelete",
props: { title: "링크 삭제", subTitle: linkUrl },
component: <LinkDeleteContent linkId={linkId} />,
});
}}
>
Expand All @@ -34,8 +37,8 @@ const KebabList = ({ linkUrl, setKebabOpen }: IKebabList) => {
<S.KebabListItem
onClick={(e) => {
handleOpenModal(e, {
type: "linkAdd",
props: { title: "폴더에 추가", subTitle: "링크 주소" },
props: { title: "폴더에 추가", subTitle: linkUrl },
component: <LinkAddContent linkUrl={linkUrl} />,
});
}}
>
Expand Down
6 changes: 5 additions & 1 deletion components/common/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ const Link = ({ linkInfo, isSetting }: LinkItemParam) => {
</button>
)}
{isKebabOpen && (
<KebabList linkUrl={linkInfo.url} setKebabOpen={setIsKebabOpen} />
<KebabList
linkId={linkInfo.id}
linkUrl={linkInfo.url}
setKebabOpen={setIsKebabOpen}
/>
)}
</S.AgoBox>
<S.ContentParagraph>{linkInfo.description}</S.ContentParagraph>
Expand Down
22 changes: 3 additions & 19 deletions components/common/modal/ModalPortal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,20 @@ import { useContext } from "react";
import { createPortal } from "react-dom";
import { ModalStateContext } from "@/context/Modal";
import ModalLayOut from "./ModalLayout";
import ShareModalContent from "./modalContent/ShareModalContent";
import FolderAddModalContent from "./modalContent/FolderAddModalContent";
import FolderNameChangeContent from "./modalContent/FolderNameChangeContent";
import FolderDeleteContent from "./modalContent/FolderDeleteContent";
import LinkDeleteContent from "./modalContent/LinkDeleteContent";
import LinkAddContent from "./modalContent/LinkAddContent";
import { TModalParam, ModalType } from "@/types/Modal";

const MODAL_COMPONENTS: Record<ModalType, () => JSX.Element> = {
share: ShareModalContent,
folderAdd: FolderAddModalContent,
folderNameChange: FolderNameChangeContent,
folderDelete: FolderDeleteContent,
linkDelete: LinkDeleteContent,
linkAdd: LinkAddContent,
};
import { TModalParam } from "@/types/Modal";

function MoadlPortal() {
const modalParam: TModalParam = useContext(ModalStateContext);
if (!modalParam) {
return null;
}

const { type, props } = modalParam;
const { props, component } = modalParam;

const Modal = MODAL_COMPONENTS[type];
return createPortal(
<>
<ModalLayOut title={props.title} subTitle={props.subTitle}>
<Modal />
{component}
</ModalLayOut>
</>,
document.getElementById("modal") as HTMLElement
Expand Down
45 changes: 43 additions & 2 deletions components/common/modal/modalContent/FolderAddModalContent.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,52 @@
import { useContext, useState } from "react";
import * as S from "./FolderAddModalContent.styled";
import Button from "@/components/common/Button";
import { useMutation } from "@tanstack/react-query";
import { addFolder } from "@/api/folder";
import { UserInfoContext } from "@/context/User";

const FolderAddModalContent = () => {
const [folderName, setFolderName] = useState("");
const userInfo = useContext(UserInfoContext);

const changeInputFolderName = (e: React.ChangeEvent<HTMLInputElement>) => {
setFolderName(e.target.value);
Copy link
Collaborator

Choose a reason for hiding this comment

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

e.target vs e.currentTarget의 차이점을 아시나요?!

👇🏻혹시 모르신다면 아래를 참고해주세요!
https://velog.io/@sunkim/Javascript-e.target%EA%B3%BC-e.currentTarget%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90

};

const handleFolderAdd = useMutation({
mutationFn: () => {
if (!userInfo || !userInfo.token) {
return Promise.reject(new Error("UserToken Error!"));
}
return addFolder(folderName, userInfo.token);
},
onSuccess: () => {
alert("폴더 추가 성공");
window.location.reload();
},
});

const handleFolderAddBtnClick = () => {
if (folderName.trim() === "") {
alert("폴더 이름 입력");
return;
}
handleFolderAdd.mutate();
};

return (
<S.FolderAddContent>
<input type="text" placeholder="내용 입력" />
<Button btnType="button" type="folderAdd_modal">
<input
type="text"
value={folderName}
placeholder="내용 입력"
onChange={changeInputFolderName}
/>
<Button
btnType="button"
type="folderAdd_modal"
handleButtonClick={handleFolderAddBtnClick}
>
추가하기
</Button>
</S.FolderAddContent>
Expand Down
40 changes: 38 additions & 2 deletions components/common/modal/modalContent/FolderDeleteContent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
import { deleteFolder } from "@/api/folder";
import Button from "@/components/common/Button";
import { UserInfoContext } from "@/context/User";
import useModal from "@/hooks/useModal";
import { useMutation } from "@tanstack/react-query";
import { useRouter } from "next/router";
import { useContext } from "react";

interface IFolderDeleteContentProps {
pageNavId: string | string[];
}

const FolderDeleteContent = ({ pageNavId }: IFolderDeleteContentProps) => {
const userInfo = useContext(UserInfoContext);
const router = useRouter();
const { closeModal } = useModal();

const handleFolderDeleteBtnClick = () => {
handleFolderDelete.mutate();
};

const handleFolderDelete = useMutation({
mutationFn: () => {
if (!userInfo || !userInfo.token) {
return Promise.reject(new Error("UserToken Error!"));
}
return deleteFolder(pageNavId, userInfo.token);
},
onSuccess: () => {
alert("삭제 성공");
closeModal();
router.replace("/folder");
},
});

const FolderDeleteContent = () => {
return (
<Button btnType="button" type="folderDelete_modal">
<Button
btnType="button"
type="folderDelete_modal"
handleButtonClick={handleFolderDeleteBtnClick}
>
삭제하기
</Button>
);
Expand Down
Loading
Loading