diff --git a/src/apis/custom.ts b/src/apis/custom.ts index 4db687b..e412175 100644 --- a/src/apis/custom.ts +++ b/src/apis/custom.ts @@ -1,6 +1,5 @@ export function defaultResponseHandler(res: Response) { if (!res.ok) { - console.log(res); //res.json().code=="token_not_valid" /*res.json().then((data)=>{ console.log; if(data.code=="token_not_valid"){ diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 88a8b1d..f60163b 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -63,8 +63,6 @@ function ContentPanel({ const { setCurrentModal: setLayoutCurrentModal } = useOutletContext(); - const [myRate, setMyRate] = useState(content.my_rate ?? null); - const { isLogined, accessToken } = useAuthContext(); const myState = content.my_state ?? null; @@ -119,13 +117,13 @@ function ContentPanel({
- {rateStr[myRate ? myRate.my_rate * 2 : 0]} + {rateStr[content.my_rate ? content.my_rate.my_rate * 2 : 0]}
@@ -202,7 +200,7 @@ function ContentPanel({ { setCurrentModal(null); }} diff --git a/src/components/SignupModal.tsx b/src/components/SignupModal.tsx index 7bcdcee..284013a 100644 --- a/src/components/SignupModal.tsx +++ b/src/components/SignupModal.tsx @@ -23,8 +23,8 @@ export default function SignupModal({ setCurrentModal }: SignupModalProps) { const { setAccessToken } = useAuthContext(); const error = { name: - nameInput.length < 2 || nameInput.length > 20 || nameInput.includes(" ") - ? "정확하지 않은 이름입니다." + nameInput.length < 3 || nameInput.length > 20 + ? "세 글자 이상 20글자 이하의 닉네임을 입력해주세요" : "", id: idInput.length < 2 || idInput.length > 20 || idInput.includes(" ") @@ -47,11 +47,13 @@ export default function SignupModal({ setCurrentModal }: SignupModalProps) { }) .then((data) => { if ("access" in data) { + alert("회원가입 성공!"); setIsSignupSuccess(true); - return; - } - if ("username" in data && data.username[0].includes("already exists")) { - setAuthErrorMessage("이미 존재하는 아이디입니다."); + } else if ( + "username" in data && + data.username[0].includes("already exists") + ) { + return setAuthErrorMessage("이미 존재하는 아이디입니다."); } else if ( "non_field_errors" in data && data.non_field_errors[0].includes("didn't match.") @@ -62,6 +64,11 @@ export default function SignupModal({ setCurrentModal }: SignupModalProps) { data.non_field_errors[0].includes("too similar") ) { setAuthErrorMessage("아이디와 비밀번호가 매우 유사합니다."); + } else if ( + "password1" in data && + data.password1[0].includes("common") + ) { + setAuthErrorMessage("너무 흔한 비밀번호입니다."); } else { setAuthErrorMessage("예상치 못한 오류가 발생하였습니다."); } @@ -291,7 +298,7 @@ export default function SignupModal({ setCurrentModal }: SignupModalProps) { window.open( "/auth/toKakao", "_blank", - "width=350,height=600", + "width=350,height=600" ); }} > diff --git a/src/components/StarRating.tsx b/src/components/StarRating.tsx index 9485f41..696d6f2 100644 --- a/src/components/StarRating.tsx +++ b/src/components/StarRating.tsx @@ -52,25 +52,20 @@ function Star(props: StarProps) { } type StarRatingProps = { - myRate: { + movieCD: string; + refetchContent: () => void; + myRateData: { id: number; my_rate: number; } | null; - setMyRate: ( - newRate: { - id: number; - my_rate: number; - } | null, - ) => void; - movieCD: string; }; export default function StarRating({ - myRate, - setMyRate, movieCD, + refetchContent, + myRateData, }: StarRatingProps) { - const savedRating = myRate ? myRate.my_rate : 0; + const savedRating = myRateData ? myRateData.my_rate : 0; const [selectedRating, setSelectedRating] = useState(savedRating); const [hover, setHover] = useState(false); const { isLogined, accessToken } = useAuthContext(); @@ -88,20 +83,20 @@ export default function StarRating({ if (!isLogined) { setCurrentModal("login"); } else { - if (myRate) { + if (myRateData) { if (rating === savedRating) { - deleteRatingRequest(myRate.id, accessToken ?? "") + deleteRatingRequest(myRateData.id, accessToken ?? "") .then(() => { - setMyRate(null); setSelectedRating(0); + refetchContent(); }) .catch((e) => console.log(e)); } else { - updateRatingRequest(myRate.id, rating, accessToken ?? "") + updateRatingRequest(myRateData.id, rating, accessToken ?? "") .then(defaultResponseHandler) .then((data) => { - setMyRate({ ...data, my_rate: data.rate }); setSelectedRating(data.rate); + refetchContent(); }) .catch((e) => console.log(e)); } @@ -109,8 +104,8 @@ export default function StarRating({ createRatingRequest(movieCD, rating, accessToken ?? "") .then(defaultResponseHandler) .then((data) => { - setMyRate({ ...data, my_rate: data.rate }); setSelectedRating(data.rate); + refetchContent(); }) .catch((e) => console.log(e)); } diff --git a/src/components/user/SettingModal.tsx b/src/components/user/SettingModal.tsx index 70d68bc..aeb4e9b 100644 --- a/src/components/user/SettingModal.tsx +++ b/src/components/user/SettingModal.tsx @@ -82,7 +82,7 @@ export default function SettingModal({ setCurrentModal }: SettingModalProps) { type ButtonBoxProps = { setAlertMessage: ( - alertMessage: "logoutAlert" | "withdrawalAlert" | "clipboard" | null, + alertMessage: "logoutAlert" | "withdrawalAlert" | "clipboard" | null ) => void; }; @@ -166,9 +166,8 @@ function WithdrawalAlertBoxContainer({ setAlertMessage }: ButtonBoxProps) { alert("회원탈퇴 성공"); window.location.href = "/"; }) - .catch((e) => { - console.log(e); - alert("로그아웃 실패"); + .catch(() => { + window.location.href = "/"; }); }} > diff --git a/src/components/user/User.tsx b/src/components/user/User.tsx index 6c8cd99..83f2581 100644 --- a/src/components/user/User.tsx +++ b/src/components/user/User.tsx @@ -1,7 +1,7 @@ // import { useEffect } from "react"; import { useAuthContext } from "../../contexts/authContext"; import styles from "./User.module.scss"; -import { Link, useParams } from "react-router-dom"; +import { Link, useNavigate, useParams } from "react-router-dom"; import { OutletContextType } from "../../pages/Layout"; import { useOutletContext } from "react-router-dom"; import { useEffect, useState } from "react"; @@ -23,8 +23,8 @@ export default function User() { const pageMode = !loginUserId ? "notLoggedIn" : pageUserId === loginUserId.toString() - ? "myPage" - : "otherPage"; + ? "myPage" + : "otherPage"; // myPage : 팔로우 버튼 보여주지 않는다 / 좋아요 섹션 보여준다 // otherPage : 팔로우 버튼 보여준다(팔로우or언팔로우) / 좋아요 섹션 보여주지 않는다. // isLoggedIn : 팔로우 버튼 보여준다(무조건 팔로우) / 좋아요 섹션 보여주지 않는다. @@ -34,9 +34,11 @@ export default function User() { const [isMyFollowing, setIsMyFollowing] = useState(false); const [isMyFollowingLoading, setIsMyFollowingLoading] = useState(true); const loading = pageUserloading || isMyFollowingLoading; - + const navigate = useNavigate(); + const [refetch, setRefetch] = useState(false); + const refetchPageUser = () => setRefetch(!refetch); const followButtonClickHandler = ( - e: React.MouseEvent, + e: React.MouseEvent ) => { e.preventDefault(); if (!pageUser) return; @@ -46,11 +48,13 @@ export default function User() { .then(defaultResponseHandler) .then(() => { setIsMyFollowing(false); + refetchPageUser(); }) : postAddFollow(accessToken, pageUser.id) .then(defaultResponseHandler) .then(() => { setIsMyFollowing(true); + refetchPageUser(); }); }; @@ -63,14 +67,18 @@ export default function User() { setTitle( data.nickname ? `${data.nickname}님의 프로필 페이지 - 와플피디아` - : "와플피디아 - 영화 평가 서비스", + : "와플피디아 - 영화 평가 서비스" ); setPageUser(data); }) + .catch(() => { + alert("잘못된 요청입니다."); + navigate(-1); + }) .finally(() => { setPageUserLoading(false); }); - }, [pageUserId, myUserData]); + }, [pageUserId, myUserData, , refetch]); // 유저데이터에 팔로잉 리스트가 없어서 추가로 가져와야 함 diff --git a/src/components/user/UserEditModal.module.scss b/src/components/user/UserEditModal.module.scss index 70b6d8b..f1c5168 100644 --- a/src/components/user/UserEditModal.module.scss +++ b/src/components/user/UserEditModal.module.scss @@ -77,3 +77,7 @@ } } } +.errorMessage { + color: red; + font-size: 12px; +} diff --git a/src/components/user/UserEditModal.tsx b/src/components/user/UserEditModal.tsx index 6f66003..945cc0a 100644 --- a/src/components/user/UserEditModal.tsx +++ b/src/components/user/UserEditModal.tsx @@ -5,12 +5,13 @@ import profileDefault from "../../assets/user_default.jpg"; import useUserEdit from "../../hooks/useUserEdit"; import usePreventScroll from "../../hooks/usePreventScroll"; import useHandlePopState from "../../hooks/useHandlePopState"; - +import { useState } from "react"; type UserEditModalProps = { setCurrentModal: (currentModal: CurrentModalType) => void; }; export default function UserEditModal({ setCurrentModal }: UserEditModalProps) { + const [nicknameError, setNicknameError] = useState(null); const { nickname, bio, @@ -59,7 +60,11 @@ export default function UserEditModal({ setCurrentModal }: UserEditModalProps) { 취소

프로필 변경

- @@ -95,8 +100,17 @@ export default function UserEditModal({ setCurrentModal }: UserEditModalProps) { type="text" id="nicknameEdit" value={nickname} - onChange={handleNickname} + onChange={(e) => { + if (e.target.value.replace(/\s/g, "").length) { + setNicknameError(null); + } else { + setNicknameError("공백을 제외한 한글자 이상을 입력해주세요."); + } + + handleNickname(e); + }} /> +

{nicknameError}

diff --git a/src/hooks/useUserEdit.ts b/src/hooks/useUserEdit.ts index 0b81b6a..39d808f 100644 --- a/src/hooks/useUserEdit.ts +++ b/src/hooks/useUserEdit.ts @@ -8,20 +8,24 @@ export default function useUserEdit() { const [nickname, setNickname] = useState(myUserData?.nickname ?? ""); const [bio, setBio] = useState(myUserData?.bio ?? ""); const [backgroundPhotoFile, setBackgroundPhotoFile] = useState( - null, + null ); const [profilePhotoFile, setProfilePhotoFile] = useState(null); const [backgroundPhotoUrl, setBackgroundPhotoUrl] = useState( - myUserData?.background_photo ?? "", + myUserData?.background_photo ?? "" ); const [profilePhotoUrl, setProfilePhotoUrl] = useState( - myUserData?.profile_photo ?? "", + myUserData?.profile_photo ?? "" ); const handleBackgroundPhoto = (e: ChangeEvent) => { const file = e.target.files?.[0]; const reader = new FileReader(); + if (!file) return; + if (file["type"].split("/")[0] !== "image") + return alert("이미지 파일만 업로드 가능합니다."); + reader.onload = (e) => { const previewPhotoUrl = e.target?.result as string | null | undefined; setBackgroundPhotoFile(file ?? null); @@ -34,6 +38,10 @@ export default function useUserEdit() { const file = e.target.files?.[0]; const reader = new FileReader(); + if (!file) return; + if (file["type"].split("/")[0] !== "image") + return alert("이미지 파일만 업로드 가능합니다."); + reader.onload = (e) => { const previewPhotoUrl = e.target?.result as string | null | undefined; setProfilePhotoFile(file ?? null); diff --git a/src/main.tsx b/src/main.tsx index 786e7b6..c4868c1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -19,6 +19,7 @@ import { AuthContextProvider } from "./contexts/authContext.tsx"; import UserStorageSubPage from "./pages/user/UserStorageSubPage.tsx"; import UserFollowerPage from "./pages/user/UserFollowerPage.tsx"; import AuthToKaKao from "./pages/AuthToKakao.tsx"; +import { Navigate } from "react-router-dom"; const router = createBrowserRouter([ { path: "/", @@ -81,6 +82,10 @@ const router = createBrowserRouter([ path: "/search", element: , }, + { + path: "/*", + element: , + }, ], }, { path: "/auth/callback/kakao", element: }, @@ -90,5 +95,5 @@ const router = createBrowserRouter([ ReactDOM.createRoot(document.getElementById("root")!).render( - , + ); diff --git a/src/pages/CommentPage.tsx b/src/pages/CommentPage.tsx index 251bc31..e6372c4 100644 --- a/src/pages/CommentPage.tsx +++ b/src/pages/CommentPage.tsx @@ -2,7 +2,7 @@ import styles from "./CommentPage.module.scss"; import CommentInfo from "../components/CommentInfo"; import { useEffect, useState } from "react"; import { getCommentRequest } from "../apis/comment"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { defaultResponseHandler } from "../apis/custom"; import { CommentType } from "../type"; import { useAuthContext } from "../contexts/authContext"; @@ -18,6 +18,7 @@ export default function CommentPage() { const { setTitle } = useChangeTitle(); const refetchComment = () => setRefetch(!refetch); useMoveScrollToTop(); + const navigate = useNavigate(); useEffect(() => { id && getCommentRequest(parseInt(id), accessToken ?? undefined) @@ -28,11 +29,12 @@ export default function CommentPage() { data.created_by.nickname + `님이 "${ data.movie.title_ko ?? "영화" - }"에 남긴 코멘트 - 와플피디아`, + }"에 남긴 코멘트 - 와플피디아` ); }) .catch(() => { alert("잘못된 요청입니다"); + navigate(-1); }) .finally(() => { setCommentDataLoading(false); diff --git a/src/pages/ContentPage.tsx b/src/pages/ContentPage.tsx index c1e84a6..a8f2e24 100644 --- a/src/pages/ContentPage.tsx +++ b/src/pages/ContentPage.tsx @@ -1,4 +1,4 @@ -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import Content from "../components/Content"; import styles from "./ContentPage.module.scss"; import { useEffect, useState } from "react"; @@ -19,7 +19,7 @@ export default function ContentPage() { const [refetch, setRefetch] = useState(false); const refetchContent = () => setRefetch(!refetch); const { addRecentContent } = useRecentContents(); - + const navigate = useNavigate(); useMoveScrollToTop(); useEffect(() => { @@ -32,6 +32,7 @@ export default function ContentPage() { }) .catch(() => { alert("잘못된 요청입니다"); + navigate(-1); }); }, [id, accessToken, setTitle, refetch]); diff --git a/src/pages/SearchPage.module.scss b/src/pages/SearchPage.module.scss index 848c4e3..91de36a 100644 --- a/src/pages/SearchPage.module.scss +++ b/src/pages/SearchPage.module.scss @@ -2,6 +2,7 @@ margin: 62px 0 0 0; .searchHeader { + width: 100%; display: flex; align-items: center; background: rgb(247, 247, 247); @@ -10,13 +11,14 @@ font-weight: 700; letter-spacing: -0.3px; line-height: 26px; - width: 100%; - height: 43px; div { margin: 0 20px; + width: 100%; max-width: 1320px; + word-wrap: break-word; + white-space: pre-wrap; @media (min-width: 760px) { margin: 0 3.5%;