diff --git a/api/services/config.ts b/api/services/config.ts index c2b74f664..b3d3b4ced 100644 --- a/api/services/config.ts +++ b/api/services/config.ts @@ -1,46 +1,19 @@ -import { - getRefreshToken, - setAccessToken, - setRefreshToken, -} from "@/utils/localStorage"; import axios from "axios"; -const BASE_URL = "https://bootcamp-api.codeit.kr"; +const BASE_URL = "https://bootcamp-api.codeit.kr/api/linkbrary/v1"; const SAMPLE_USER_ENDPOINT = "/api/users/1"; const SAMPLE_FOLDER_ENDPOINT = "/api/sample/folder"; -const USERS_ENDPOINT = "/api/users"; -const SIGNIN_ENDPOINT = "/api/sign-in"; -const SIGNUP_ENDPOINT = "/api/sign-up"; +const USERS_ENDPOINT = "/users"; +const SIGNIN_ENDPOINT = "/auth/sign-in"; +const SIGNUP_ENDPOINT = "/auth/sign-up"; const FOLDER_ENDPOINT = `/api/folders`; const LINKS_ENDPOINT = `/api/links`; const instance = axios.create({ - baseURL: "https://bootcamp-api.codeit.kr", + baseURL: "https://bootcamp-api.codeit.kr/api/linkbrary/v1", timeout: 3000, }); -instance.interceptors.response.use( - (res) => res, - async (error) => { - const originalRequest = error.config; - let res; - if (error.response?.status === 401 && !originalRequest._retry) { - res = await instance.post( - "/api/refresh-token", - { - refresh_token: getRefreshToken(), - }, - { _retry: true } as any - ); - setAccessToken(res?.data.data.accessToken); - setRefreshToken(res?.data.data.refreshToken); - originalRequest._retry = true; - return instance(originalRequest); - } - return Promise.reject(error); - } -); - export { BASE_URL, SAMPLE_USER_ENDPOINT, diff --git a/components/addLinkInput/AddLinkInput.tsx b/components/addLinkInput/AddLinkInput.tsx index 2e5e326c5..195657a7e 100644 --- a/components/addLinkInput/AddLinkInput.tsx +++ b/components/addLinkInput/AddLinkInput.tsx @@ -41,7 +41,7 @@ const AddLinkInput = forwardRef( 폴더에 추가 {url} - {folders.map((folder) => ( + {folders?.map((folder) => ( {folder.name} 개 링크 diff --git a/components/card/Card.tsx b/components/card/Card.tsx index b0f7ccda3..27d1d0703 100644 --- a/components/card/Card.tsx +++ b/components/card/Card.tsx @@ -67,7 +67,7 @@ function Card({ linkInfo, folders }: CardProps) {
-

+

{formattedTime} -

+

{linkInfo?.description}

{`${year}. ${month}. ${date}`}

diff --git a/components/header/Header.tsx b/components/header/Header.tsx index e1550e236..ab7913f37 100644 --- a/components/header/Header.tsx +++ b/components/header/Header.tsx @@ -1,15 +1,14 @@ import styles from "./header.module.css"; import Image from "next/image"; -import useUserStore from "@/hooks/useStore"; +import { User } from "@/types/types"; interface HeaderProps { folderName: string; + user: User[]; } -export default function Header({ folderName }: HeaderProps) { - const { user } = useUserStore(); - const name = user[0]?.name; - +export default function Header({ folderName, user }: HeaderProps) { + const name = user?.length > 0 ? user[0].name : ""; return (
@@ -23,7 +22,6 @@ export default function Header({ folderName }: HeaderProps) { height={24} /> ))} - {name}
{folderName} diff --git a/components/layout/nav/Nav.tsx b/components/layout/nav/Nav.tsx index 953c898ce..2e284c79b 100644 --- a/components/layout/nav/Nav.tsx +++ b/components/layout/nav/Nav.tsx @@ -11,20 +11,24 @@ import { getAccessToken } from "@/utils/localStorage"; export default function Nav() { const router = useRouter(); const isFolderPage = router.pathname === "/folder"; - const [userProfile, setUserProfile] = useState([]); + const [user, setUser] = useState(); - const fetchUsers = async () => { - const res = await instance.get(USERS_ENDPOINT, { - headers: { - Authorization: `Bearer ${getAccessToken()}`, - }, - }); - const user = res?.data.data; - setUserProfile(user); + const getUser = async () => { + try { + const res = await instance.get(USERS_ENDPOINT, { + headers: { + Authorization: `Bearer ${getAccessToken()}`, + }, + }); + const nextUser = res?.data; + setUser(nextUser); + } catch (error) { + console.error(error); + } }; useEffect(() => { - fetchUsers(); + getUser(); }, []); return ( @@ -39,19 +43,19 @@ export default function Nav() { height={24} /> - {userProfile ? ( + {user ? (
userProfile - {userProfile[0]?.email} + {user[0]?.email}
) : ( - + diff --git a/components/modal/components/dialogButton/DialogButton.tsx b/components/modal/components/dialogButton/DialogButton.tsx index 2df058bf6..9ea65bf22 100644 --- a/components/modal/components/dialogButton/DialogButton.tsx +++ b/components/modal/components/dialogButton/DialogButton.tsx @@ -4,14 +4,17 @@ import styles from "./dialogButton.module.css"; interface DialogButtonProps { children?: ReactNode; isAddButton?: boolean; + onClick?: () => void; } export default function DialogButton({ children, isAddButton, + onClick, }: DialogButtonProps) { return (
) => void; } -export default function DialogInput({ value }: DialogInputProps) { +export default function DialogInput({ value, onChange }: DialogInputProps) { return ( ); } diff --git a/components/optionButton/OptionButton.tsx b/components/optionButton/OptionButton.tsx index 5b93206ec..a1be70bfe 100644 --- a/components/optionButton/OptionButton.tsx +++ b/components/optionButton/OptionButton.tsx @@ -1,6 +1,11 @@ import Image from "next/image"; import styles from "./optionButton.module.css"; import useModal from "@/hooks/useModal"; +import { instance } from "@/api/services/config"; +import { ChangeEvent, useState } from "react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { getAccessToken } from "@/utils/localStorage"; +import { QUERY_KEYS } from "@/constants/queryKeys"; interface OptionButtonProps { iconSrc: string; @@ -18,6 +23,58 @@ export default function OptionButton({ folderId, }: OptionButtonProps) { const { open, close, Dialog, isModalOpen } = useModal(); + const [newName, setNewName] = useState(""); + const queryClient = useQueryClient(); + + const handleChange = (e: ChangeEvent) => { + setNewName(e.target.value); + }; + + const putFolderName = async (folderId: number | string) => { + try { + const res = await instance.put( + `/folders/${folderId}`, + { + name: newName, + }, + { + headers: { + Authorization: `Bearer ${getAccessToken()}`, + }, + } + ); + } catch (error) { + console.error(error); + } + }; + + const deleteFolder = async (folderId: number | string) => { + try { + instance.delete(`/folders/${folderId}`, { + headers: { + Authorization: `Bearer ${getAccessToken()}`, + }, + }); + } catch (error) { + console.error(error); + } + }; + + const deleteFolderMutation = useMutation({ + mutationFn: () => deleteFolder(folderId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.FOLDER_NAMES] }); + }, + }); + + const putFolderNameMutation = useMutation({ + mutationFn: () => putFolderName(folderId), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [QUERY_KEYS.FOLDER_NAMES], + }); + }, + }); return (
@@ -31,15 +88,25 @@ export default function OptionButton({ {name === "이름 변경" ? ( 폴더 이름 변경 - - 변경하기 + + putFolderNameMutation.mutate()} + > + 변경하기 + ) : null} {name === "삭제" ? ( 폴더 삭제 {folderName} - 삭제하기 + deleteFolderMutation.mutate()} + > + 삭제하기 + ) : null} {alt} diff --git a/components/popover/Popover.tsx b/components/popover/Popover.tsx index 9d5318f3f..91dacb3de 100644 --- a/components/popover/Popover.tsx +++ b/components/popover/Popover.tsx @@ -60,7 +60,7 @@ export default function Popover({ url, folders }: PopoverProps) { {folder.name} - {folder.link.count}개 링크 + {folder.link_count}개 링크 ))} diff --git a/components/shareCard/ShareCard.tsx b/components/shareCard/ShareCard.tsx index c80777706..57287938b 100644 --- a/components/shareCard/ShareCard.tsx +++ b/components/shareCard/ShareCard.tsx @@ -7,21 +7,21 @@ import Image from "next/image"; interface ShareCardProps { linkInfo: { id: number; - createdAt: string; + created_at: string; url: string; title: string; description: string; - imageSource: string; + image_source: string; }; } function ShareCard({ linkInfo }: ShareCardProps) { - const createdAt = new Date(linkInfo?.createdAt); - const formattedTime = calcCreateTime(createdAt); + const created_at = new Date(linkInfo?.created_at); + const formattedTime = calcCreateTime(created_at); - const year = createdAt.getFullYear(); - const month = createdAt.getMonth() + 1; - const date = createdAt.getDate(); + const year = created_at.getFullYear(); + const month = created_at.getMonth() + 1; + const date = created_at.getDate(); return (
@@ -33,14 +33,14 @@ function ShareCard({ linkInfo }: ShareCardProps) { >
- {linkInfo?.imageSource ? ( + {linkInfo?.image_source ? ( cat({ mode: "onBlur" }); + const queryClient = new QueryClient(); const { setUser } = useUserStore(); const getUser = async () => { @@ -47,26 +50,35 @@ export function SigninForm() { Authorization: `Bearer ${getAccessToken()}`, }, }); - const nextUser = res?.data.data; + const nextUser = res?.data; setUser(nextUser); } catch (error) { console.error(error); } }; + const postSignin = async () => { + const res = await instance.post(`/auth/sign-in`, { + email: watch("email"), + password: watch("password"), + }); + const accessToken = res?.data.accessToken; + const refreshToken = res?.data.refreshToken; + setAccessToken(accessToken); + setRefreshToken(refreshToken); + await getUser(); + if (res.status === 200) { + router.push("/folder"); + } + }; + + const signinMutation = useMutation({ + mutationFn: postSignin, + }); + const onSubmit = handleSubmit(async (data: FormValues) => { - let res; try { - res = await instance.post(`${SIGNIN_ENDPOINT}`, { - email: data.email, - password: data.password, - }); - const accessToken = res?.data.data.accessToken; - const refreshToken = res?.data.data.refreshToken; - setAccessToken(accessToken); - setRefreshToken(refreshToken); - await getUser(); - res?.status === 200 && router.push("/folder"); + signinMutation.mutate(); } catch (error) { if (axios.isAxiosError(error) && error.response?.status === 400) { setError("email", { message: CHECK_EMAIL_TEXT }); @@ -78,7 +90,7 @@ export function SigninForm() { return ( <> -
+