From d038fc87fb52a3bfbc17b36e5f8e8035fefef1a6 Mon Sep 17 00:00:00 2001 From: KimSehyeoun Date: Sun, 18 Feb 2024 21:29:55 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feature-022=20:=20=ED=95=B4=EC=89=AC?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EB=91=90=EC=A4=84=EB=A1=9C=EB=A7=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SearchPage.tsx | 21 +++++++++++++++++---- src/styles/SearchPage.ts | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/pages/SearchPage.tsx b/src/pages/SearchPage.tsx index 16f57b5..88d60ea 100644 --- a/src/pages/SearchPage.tsx +++ b/src/pages/SearchPage.tsx @@ -22,12 +22,13 @@ const SearchPage = () => { setUserHashTag(shuffleData); } catch(e) { - setUserHashTag(['A','B','C','D','E','F','G','H','I','J']); + setUserHashTag([]); } } handleTagAPI(); }, []); + const handleHashtagBox = (value : string) => { const isSelected = selectedHashtags.includes(value); setSelectedHashtags(prev => @@ -40,7 +41,9 @@ const SearchPage = () => { const sortShuffle = (arr : string[]) => { return arr.sort(() => Math.random() - 0.5); } - + const firstHalf = userHashTag.slice(0, 5); + const secondHalf = userHashTag.slice(5); + return (
@@ -74,13 +77,23 @@ const SearchPage = () => { }
-
+
+
{ - userHashTag.map((value : string, idx : number) => { + firstHalf.map((value : string, idx : number) => { + return( handleHashtagBox(value)} + className={selectedHashtags.includes(value) ? 'toggle' : ''}>{'#' + value}) + }) + } +
+
+ { + secondHalf.map((value : string, idx : number) => { return( handleHashtagBox(value)} className={selectedHashtags.includes(value) ? 'toggle' : ''}>{'#' + value}) }) } +
diff --git a/src/styles/SearchPage.ts b/src/styles/SearchPage.ts index 764273c..ec8c85a 100644 --- a/src/styles/SearchPage.ts +++ b/src/styles/SearchPage.ts @@ -110,6 +110,7 @@ const Container = styled.div` } & div.hashtag { + width : 100%; display: flex; flex-direction: row; flex-wrap : wrap; From b8a040a455190eb0b3a4ad04ca290d052d1f534e Mon Sep 17 00:00:00 2001 From: KimSehyeoun Date: Sun, 18 Feb 2024 21:30:28 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EA=B3=B5=EB=B0=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SearchResultPage.tsx | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/pages/SearchResultPage.tsx b/src/pages/SearchResultPage.tsx index b3215f7..c6dd4d9 100644 --- a/src/pages/SearchResultPage.tsx +++ b/src/pages/SearchResultPage.tsx @@ -60,10 +60,9 @@ const SearchResult = () => { ) => { try { const keywords = inputValues.split(splittype); - const requests = keywords.map((value) => { if (type === 'hashtag') { - value = value.replace('#', ''); + value = value.replace(/^#/, '').replace(/\s/g, ''); } const searchData = searchAPI(type, value); return searchData.then((value) => value.data.result); @@ -80,24 +79,27 @@ const SearchResult = () => { }; const formatContent = (content: string, keyword: string) => { - if (keyword.trim() !== '') { - content = content - .split(keyword) - .map((s) => escapeHTML(s)) - .join(`${escapeHTML(keyword)}`); - } else { - content = escapeHTML(content); - } - - content = content.replace(/\n/g, '
'); - - return content; + let result = escapeHTML(content); + const keywordArr = keyword.split(' '); + + keywordArr.forEach((keyword) => { + if (keyword.trim() !== '') { + result = result + .split(keyword) + .join(`${escapeHTML(keyword)}`); + } + }); + + result = result.replace(/\n/g, '
'); + + return result; }; const dataDuplicateHandler = (videos: IVideo[], check: string) => { const uniqueData = videos.filter((v, index, arr) => arr.findIndex(t => t.video_id === v.video_id) === index ); + const mappingData = uniqueData.map((video) => { return { ...video, From 1338944dc4dd0dbb1ce0a2558f3626c76eab4aaa Mon Sep 17 00:00:00 2001 From: KimSehyeoun Date: Sun, 18 Feb 2024 21:30:58 +0900 Subject: [PATCH 3/9] =?UTF-8?q?=ED=82=A4=EC=9B=8C=EB=93=9C=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20mark=20=EA=B3=B5=EB=B0=B1=20=EC=88=9C=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SearchPage/SearchComponent.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/SearchPage/SearchComponent.tsx b/src/components/SearchPage/SearchComponent.tsx index b81d63f..7eb7b03 100644 --- a/src/components/SearchPage/SearchComponent.tsx +++ b/src/components/SearchPage/SearchComponent.tsx @@ -54,18 +54,18 @@ const SearchComponent : React.FC = ({tags, input, searchType, sel if(!searchType){ if ((event.code === 'Comma' || event.code === 'Space') && !isComposing) { - event.preventDefault(); + event.preventDefault(); if (input) { tags.length > 0 && !input.startsWith('#') ? setTags([...tags, '#' + input]) : setTags([...tags, input]) if(selectedHashtags && setSelectedHashtags && !selectedHashtags.includes(input)) - input.startsWith('#') ? setSelectedHashtags([...selectedHashtags, input.substring(1)]) : setSelectedHashtags([...selectedHashtags, input]) + input.startsWith('#') ? setSelectedHashtags([...selectedHashtags, input.substring(1).replace(/\s/g, '')]) : setSelectedHashtags([...selectedHashtags, input.replace(/\s/g, '')]) setInput(''); } } else if ((event.key === 'Backspace' || event.code === 'Backspace') && !input) { if(tags.length > 0){ const lastValue = tags[tags.length - 1] - if(selectedHashtags && setSelectedHashtags && selectedHashtags.includes(lastValue.substring(1))) - setSelectedHashtags(selectedHashtags.filter((prev : string) => prev !== lastValue.substring(1))); + if(selectedHashtags && setSelectedHashtags && selectedHashtags.includes(lastValue.substring(1).replace(/\s/g, ''))) + setSelectedHashtags(selectedHashtags.filter((prev : string) => prev !== lastValue.substring(1).replace(/\s/g, ''))); setRemovingTagIndex(tags.length - 1); setTimeout(() => { From ace64538cbdd05b0628dfca583d0f561d5cb451e Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 00:17:18 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feature-077:=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=ED=98=B8=EB=B2=84=20=EC=8B=9C=20=EC=83=89=EC=83=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/category/EmptyCard.style.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/styles/category/EmptyCard.style.ts b/src/styles/category/EmptyCard.style.ts index 547027d..c51ff1e 100644 --- a/src/styles/category/EmptyCard.style.ts +++ b/src/styles/category/EmptyCard.style.ts @@ -26,4 +26,8 @@ export const Button = styled(Link)` border-radius: 100px; padding: 12px 32px; ${theme.typography.Subheader2} + + &:hover { + color: ${theme.color.green400}; + } `; From 5acb7a05179ac1cf059409ac4ab8484807b859c4 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 00:28:46 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feature-077:=20=EC=9D=B4=EB=A9=94=EC=9D=BC?= =?UTF-8?q?=20=EC=A0=84=EC=86=A1=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A7=80=EB=8A=94=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/success-mail.png | Bin 0 -> 874 bytes src/components/layout/footer/SendEmail.tsx | 48 ++++++++++++++------- src/styles/layout/footer/index.ts | 12 ++++++ 3 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 src/assets/success-mail.png diff --git a/src/assets/success-mail.png b/src/assets/success-mail.png new file mode 100644 index 0000000000000000000000000000000000000000..e82cc8f1ce51eec44d6cac2b29da1313aad2edb4 GIT binary patch literal 874 zcmV-w1C{)VP)-al!pxMiI2Pm0KWOB_BvBG$V)TLs4J2MXDT!yngC|YUlZj|TyvU6s zEMys&NE8)fOiX|SMGfOm;-(u}fzJBgMi=&{Tib1TfKSr2eZRE*yuSCoEwImCBoGMn zj*X4=q70Z&0>NOAlO$=F9JXyYjWZ0>?eqBr7{ss;k>L}GL{OGxKI4WalVCQReMY6K zU;w}0f0fA0WXn)!8+mIpqobquU;x!Obf!=0BE99a+g-L;qu0LH1r5xG4-Ni|*%q_g zWs@v?e83Dsk5J1)d4XKYqcR9ny;V*53QUm;(cA#oSZE?90&~lmZB7p8Y)SQdXi7#X zQurQ4g?cWUaFT~6rUl^6YdL*hM>_KSmMJMkX>KrO)C~#s`(@Z_)$6f15Z%aM^E4nW zL!lj`l2H-*glN9>O>S{bm5XkhZl z&}BQ!l8dO&z*`y4FHmJ}MZHEJe)ENl&aA0}DC zJ(+GrSkyS1bz-j(asSl2FDTqs?^fK7z%}Wm&+BjSS%cN5t8ctBO@cca-K0{ zcDo%`s}+$*1eJtkwp435pN{J4YB(GYJifM$_T~c1!62>9AGm#DTD7ja>>YIp-1Ysz zjnnIR^T~?9Txs*kT_48vW8c8Bg*#a^hjxlR8?VRLB@6UGMJ^m&Koi+(p{USKu}4?J zcrVo8)uat8Q8UUZqFYGe9KVs=0<$cJK3j9d>@{&*=I3$z;2MmeTS%eZD<(HZ?05Vx z+K&i0@BEHZFf5cUwz%{QVss6gn<$Brg%lbOA0RR|tgfyi9M(5)c4FukRt=hAB<FMO(%(EnS zRY)OQjQkKHM~IAZL=?qt>>?b;rJs#`_9+wp07s@6n8R-B<^TWy07*qoM6N<$f=k(x A9smFU literal 0 HcmV?d00001 diff --git a/src/components/layout/footer/SendEmail.tsx b/src/components/layout/footer/SendEmail.tsx index 5516af5..08520a0 100644 --- a/src/components/layout/footer/SendEmail.tsx +++ b/src/components/layout/footer/SendEmail.tsx @@ -2,10 +2,12 @@ import * as FooterStyle from '@/styles/layout/footer'; import { useState } from 'react'; import SendEmailImage from '@/assets/mail.png'; +import SuccessSendEmailImage from '@/assets/success-mail.png'; import { postFeedback } from '@/apis/feedback'; const SendEmail = () => { const [feedback, setFeedback] = useState(''); + const [successSend, setSuccessSend] = useState(false); const handleInputFeedback = (e: React.ChangeEvent) => setFeedback(e.target.value); @@ -14,27 +16,43 @@ const SendEmail = () => { const res = await postFeedback(feedback); if (res.data.success) { setFeedback(''); + setSuccessSend(true); return; } alert('피드백을 전송하는 과정에서 오류가 발생했습니다.'); }; return ( - - - - - 보내기 - + + {successSend && ( + <> + + + 소중한 피드백이 전달되었어요! + + + )} + {!successSend && ( + <> + + + + 보내기 + + + )} ); }; diff --git a/src/styles/layout/footer/index.ts b/src/styles/layout/footer/index.ts index 2410db9..a8c279e 100644 --- a/src/styles/layout/footer/index.ts +++ b/src/styles/layout/footer/index.ts @@ -18,6 +18,18 @@ export const SendEmailWrap = styled.div` margin: 40px 0px; border-radius: 12px; background-color: ${theme.color.white}; + + &.success-send { + justify-content: flex-start; + padding: 0; + background-color: ${theme.color.gray100}; + } +`; + +export const SuccessSendEmail = styled.span` + margin-left: 20px; + ${theme.typography.Subheader2} + color: ${theme.color.blue}; `; export const SendEmailImage = styled.img` From b14763e110391c1139165511276e9934782b73dd Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 00:32:09 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feature-077:=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EB=93=9C=20=EB=B0=94=20=EB=8B=AB=ED=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/HomePage.tsx | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index aaa76f0..a4f855e 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { IVideoProps } from 'types/videos'; import { @@ -19,6 +19,7 @@ import { HomePageContainer } from '@/styles/HomepageStyle'; import { userTokenState } from '@/stores/user'; import { recommendationModalState } from '@/stores/modal'; import { toastListState } from '@/stores/toast'; +import { isSideBarOpenState } from '@/stores/ui'; export interface Video { id: string; @@ -30,6 +31,7 @@ export interface Video { const HomePage: React.FC = () => { const userToken = useRecoilValue(userTokenState); + const setIsSideBarOpen = useSetRecoilState(isSideBarOpenState); const isOpenModal = useRecoilValue(recommendationModalState); const [recentVideos, setRecentVideos] = useState([]); const [dummyVideos, setDummyVideos] = useState([]); @@ -68,7 +70,9 @@ const HomePage: React.FC = () => { setRecentVideos([]); setDummyVideos(res.result.videos); }); - }, [userToken]); + + setIsSideBarOpen(false); + }, [setIsSideBarOpen, userToken]); return ( <> @@ -76,16 +80,24 @@ const HomePage: React.FC = () => { {userToken ? ( -
- - -
- ) : ( -
- - -
- )} +
+ + +
+ ) : ( +
+ + +
+ )} {isOpenModal && } From 1732b6711fd9155e7253ec6d82257759555872b0 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 01:00:30 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feature-077:=20=ED=95=98=EC=9C=84=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=9B=84=20=EB=B3=B4=EB=9F=AC=EA=B0=80=EA=B8=B0=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20=EC=8B=9C=20=ED=95=B4=EB=8B=B9=20=ED=95=98=EC=9C=84?= =?UTF-8?q?=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/sideBar/UserMode.tsx | 5 +++-- src/components/modals/AddCategoryModal.tsx | 10 +++++----- src/components/modals/SuccessAddCategoryModal.tsx | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/layout/sideBar/UserMode.tsx b/src/components/layout/sideBar/UserMode.tsx index 3c7446c..a183e3d 100644 --- a/src/components/layout/sideBar/UserMode.tsx +++ b/src/components/layout/sideBar/UserMode.tsx @@ -21,6 +21,7 @@ const UserMode = () => { const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [categoryName, setCategoryName] = useState(''); const [categoryId, setCategoryId] = useState(null); + const [to, setTo] = useState(''); const categories = useRecoilValue(categoryState); const [isSubCategoryModalOpen, setIsSubCategoryModalOpen] = useState(false); const grabedCategory = useRef(undefined); @@ -105,7 +106,7 @@ const UserMode = () => { setCategoryName={setCategoryName} setIsSuccessAddCategoryModalOpen={setIsSuccessAddCategoryModalOpen} topCategoryId={topId} - setCategoryId={setCategoryId} + setTo={setTo} /> )} {isDeleteModalOpen && ( @@ -119,7 +120,7 @@ const UserMode = () => { categoryName={categoryName} setCategoryName={setCategoryName} setIsSuccessAddCategoryModalOpen={setIsSuccessAddCategoryModalOpen} - categoryId={categoryId} + to={to} /> )} diff --git a/src/components/modals/AddCategoryModal.tsx b/src/components/modals/AddCategoryModal.tsx index 3efb0fa..9883254 100644 --- a/src/components/modals/AddCategoryModal.tsx +++ b/src/components/modals/AddCategoryModal.tsx @@ -19,7 +19,7 @@ interface IAddTopCategoryModalProps extends ICommonModalProps { isTopCategoryModalOpen: boolean; setIsSubCategoryModalOpen: React.Dispatch>; topCategoryId: number; - setCategoryId: React.Dispatch>; + setTo: React.Dispatch>; } const AddCategoryModal = ({ @@ -29,7 +29,7 @@ const AddCategoryModal = ({ setCategoryName, setIsSuccessAddCategoryModalOpen, topCategoryId, - setCategoryId, + setTo, }: IAddTopCategoryModalProps) => { const setIsTopCategoryModalOpen = useSetRecoilState(topCategoryModalState); const { updateCategories } = useUpdateCategories(); @@ -58,10 +58,10 @@ const AddCategoryModal = ({ : await postSubCategroy(categoryName, topCategoryId); if (response.isSuccess) { updateCategories(); - setCategoryId( + setTo( isTopCategoryModalOpen - ? response.result.categoryId - : response.result.topCategoryId, + ? `${response.result.categoryId}` + : `${response.result.topCategoryId}/${response.result.categoryId}`, ); setIsSuccessAddCategoryModalOpen(true); } diff --git a/src/components/modals/SuccessAddCategoryModal.tsx b/src/components/modals/SuccessAddCategoryModal.tsx index b3d12c2..ac746f4 100644 --- a/src/components/modals/SuccessAddCategoryModal.tsx +++ b/src/components/modals/SuccessAddCategoryModal.tsx @@ -10,14 +10,14 @@ import { ICommonModalProps } from 'types/modal'; import FileImage from '@/assets/file.png'; interface ISuccessAddCategory extends ICommonModalProps { - categoryId: number | null; + to: string; } const SuccessAddCategoryModal = ({ categoryName, setCategoryName, setIsSuccessAddCategoryModalOpen, - categoryId, + to, }: ISuccessAddCategory) => { const onCloseModal = () => { setIsSuccessAddCategoryModalOpen(false); @@ -40,7 +40,7 @@ const SuccessAddCategoryModal = ({ 생성 완료! 보러가기 From d30c3035ede6eb903ace285cf1de39e229a52d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8A=B9=EC=9A=B0?= Date: Mon, 19 Feb 2024 01:26:07 +0900 Subject: [PATCH 8/9] =?UTF-8?q?Feature-052~054:=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=B0=BE=EA=B8=B0&=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EB=AA=A8=EB=8B=AC=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/user.ts | 11 +- src/components/FindPassword.tsx | 32 +++ src/components/NicknameModal.tsx | 19 +- src/models/user.ts | 11 + src/pages/FindPasswordPage.tsx | 328 ++++---------------------- src/pages/SignUpPage.tsx | 12 +- src/styles/FindEmail.ts | 1 + src/styles/PhoneCheck.ts | 2 + src/styles/modals/NotFindUserModal.ts | 111 +++++---- src/styles/signup/SignuppageStyle.ts | 4 + 10 files changed, 198 insertions(+), 333 deletions(-) create mode 100644 src/components/FindPassword.tsx diff --git a/src/apis/user.ts b/src/apis/user.ts index d9b4446..769cb65 100644 --- a/src/apis/user.ts +++ b/src/apis/user.ts @@ -12,7 +12,9 @@ import { NickNameRequest, NickNameResponse, FindEmailResponse, - FindEmailRequest + FindEmailRequest, + FindPasswordResponse, + FindPasswordRequest } from '@/models/user'; import { AlarmResponse, @@ -81,4 +83,11 @@ export const findEmailAPI = (data : FindEmailRequest) => { PREFIX + '/findEmail', data ); +} + +export const findPasswordAPI = (data : FindPasswordRequest) => { + return axios.post( + PREFIX + '/findPassword', + data + ); } \ No newline at end of file diff --git a/src/components/FindPassword.tsx b/src/components/FindPassword.tsx new file mode 100644 index 0000000..be12638 --- /dev/null +++ b/src/components/FindPassword.tsx @@ -0,0 +1,32 @@ +import React from "react" +import MailPng from '@/assets/mail.png'; +import Container from "@/styles/FindEmail"; +import { useNavigate } from "react-router-dom"; + +interface FindEmailProp { + email : string; +} + +const FindPassword : React.FC = ({email}) => { + const navigate = useNavigate(); + + const handleLoginBtn = () => { + navigate('/sign-in'); + } + return( + +
+
+ +
{email}로
회원님의 임시 비밀번호를 전송하였습니다!
+ 로그인하고 나만의 영상 아카이빙을 시작해요 +
+
+ +
+
+
+ ); +} + +export default FindPassword; \ No newline at end of file diff --git a/src/components/NicknameModal.tsx b/src/components/NicknameModal.tsx index 36e8975..7a45e92 100644 --- a/src/components/NicknameModal.tsx +++ b/src/components/NicknameModal.tsx @@ -1,13 +1,27 @@ import styled from 'styled-components'; import theme from '@/styles/theme'; import React, { useState } from 'react'; -import { nickNameAPI } from '@/apis/user'; +import { getMyInfoAPI, nickNameAPI } from '@/apis/user'; import nameImg from '@/assets/name.png'; import { BlurBackground } from '@/styles/modals/common.style'; +import { userInfoState } from '@/stores/user'; +import { useSetRecoilState } from 'recoil'; const NicknameModal = () => { const [inputCount, setInputCount] = useState(0); const [name, setName] = useState(""); + + const setUserInfo = useSetRecoilState(userInfoState); + + const refreshMyInfo = async () => { + try { + const { result } = (await getMyInfoAPI()).data; + + setUserInfo(result); + } catch (e) { + console.error(e); + } + } const onChangeName = (e: React.ChangeEvent) => { const target = e.currentTarget; @@ -15,7 +29,6 @@ const NicknameModal = () => { target.value = target.value.slice(0, 7); } setName(target.value); - console.log(name); setInputCount( target.value.replace(/[\0-\x7f]|([0-\u07ff]|(.))/g, "$&$1$2").length ); @@ -36,6 +49,7 @@ const NicknameModal = () => { const response = (await nickNameAPI({ nick_name : name, })).data + refreshMyInfo(); console.log(response); } catch (err) { console.log(err); @@ -156,6 +170,7 @@ const SucButton = styled.button` color: #fff; text-align: center; ${theme.typography.Body1}; + cursor: pointer; `; const Button = styled.button` diff --git a/src/models/user.ts b/src/models/user.ts index 3b1c319..ea65996 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -60,4 +60,15 @@ export interface FindEmailResponse { export interface FindEmailRequest { name : string; phone_number : string; +} + +export interface FindPasswordResponse { + success : boolean; + message : string; +} + +export interface FindPasswordRequest { + name : string; + phone_number : string; + email: string; } \ No newline at end of file diff --git a/src/pages/FindPasswordPage.tsx b/src/pages/FindPasswordPage.tsx index 0e19ae7..659d42d 100644 --- a/src/pages/FindPasswordPage.tsx +++ b/src/pages/FindPasswordPage.tsx @@ -1,162 +1,60 @@ -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useState} from 'react'; import styled from 'styled-components'; import { Link } from 'react-router-dom'; -import { useNavigate } from 'react-router-dom'; -import { sendSMSAPI, checkSMSAPI } from '@/apis/sms'; -import ImageSlider from "@/components/ImageSlider"; import smallLogo from "../assets/logo.png"; -import mail from "../assets/mail.png"; import theme from '@/styles/theme'; +import NotFindUserModal from '@/components/modals/NotFindUserModal'; +import PhoneCheck from '@/components/PhoneCheck'; +import { findPasswordAPI } from '@/apis/user'; +import FindPassword from '@/components/FindPassword'; +import ImageSlider from '@/components/ImageSlider'; -const FindPwPage = () => { +const FindPasswordPage = () => { const [name, setName] = useState(""); - const [errMessage, setErrMessage] = useState(''); - const [tel, setTel] = useState(""); - const [isTel, setIsTel] = useState(false); - const [email, setEmail] = useState(""); - const [isSend, setIsSend] = useState(false); + const [tel, setTel] = useState(''); + const [email, setEmail] = useState(''); + const [isModal, setIsModal] = useState(false); + const [isFind, setIsFind] = useState(false); + const [allChecked, setAllChecked] = useState(false); - const [certifyNum, setCertifyNum] = useState(''); - const [isCertify, setIsCertify] = useState(false); - const [time, setTime] = useState(60*5); - const [isTimer, setIsTimer] = useState(true); - const [token,setToken] =useState(""); - - const [showResult, setShowResult] = useState(false); - const [isEmail, setIsEmail] = useState(false); - - const [emailMessage, setEmailMessage] = useState(""); - - useEffect(() => { - if (isTimer) { - const intervalId = setInterval(() => { - setTime(prevTime => { - if (prevTime <= 1) { - clearInterval(intervalId); - setIsTimer(false); - return 0; - } else { - return prevTime - 1; - } - }); - }, 1000); - - return () => clearInterval(intervalId); - } - }, [isTimer]); - - - const onChangeName = useCallback((e: React.ChangeEvent) => { + const onChangeName = (e: React.ChangeEvent) => { setName(e.target.value); - console.log(name); - }, []); + } - const onChangeEmail = useCallback((e: React.ChangeEvent) => { - const emailRegex = - /([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/; + const onChangeEmail = (e: React.ChangeEvent) => { const emailCurrent = e.target.value; setEmail(emailCurrent); - if (!emailRegex.test(emailCurrent)) { - setEmailMessage("올바른 이메일 형식이 아닙니다!"); - setIsEmail(false); - } else { - setEmailMessage(""); - setIsEmail(true); - } - console.log(email); - }, []); - - const onChangeTel = useCallback( - (e: React.ChangeEvent) => { - const telRegex = /^01(?:0|1|[6-9])(?:\d{3}|\d{4})\d{4}$/; - const telCurrent = e.target.value; - setTel(telCurrent); - if (!telRegex.test(telCurrent)) { - setErrMessage('올바른 전화번호 형식이 아닙니다.'); - setIsTel(false); - } else { - setErrMessage(''); - setIsTel(true); - } - }, - [tel], - ); - - const onChangeCertifyInput = (e : React.ChangeEvent) => { - const certifyRegex = /^\d{7}$/; - setCertifyNum(e.target.value); - if(certifyRegex.test(e.target.value)){ - setIsCertify(true); - } else { - setIsCertify(false); - } - } - - - const onResultHandler = useCallback(() => { - setShowResult(true); - }, []); - - const navigate = useNavigate(); - const navigateToLogin = () => { - navigate('/sign-in'); }; - const onSubmit = useCallback( - (e: React.FormEvent) => { - e.preventDefault(); - console.log(tel); - }, - [tel], - ); - - const handleCertifyNum = async () => { - const response = (await sendSMSAPI({ - phone_number : tel - })) - console.log(response) - if(response.data.success){ - setIsSend(true); - setToken(response.data.result.token); - } - startTimer(); - } - const handleCheckCertify = async () => { - stopTimer(); - const response = (await checkSMSAPI({ - verification_code : Number(certifyNum), - },token)) - console.log(response); + const findBtnHandler = async () => { + try { + const {data} = await findPasswordAPI({ + name: name, + phone_number: tel, + email: email + }); + if(data.success){ + setIsFind(true); + } + else{ + setIsModal(true); + } + } catch (error) { } +} - const startTimer = () => setTime(5 * 60); // 타이머 시작 - const stopTimer = () => { - setIsTimer(false); - }; - - const minutes = Math.floor(time / 60); // 분 - const seconds = time % 60; - - +if(isFind){ + return ( + + + + ); +} return ( - {showResult ? ( - - 메일 이미지 -

- yejin2174@naver.com

회원님의 임시 비밀번호를 전송하였습니다! -

-

로그인하고 나만의 영상 아카이빙을 시작해요

- - 로그인 하러가기 - -
- ) : ( - - - + 로고 이미지 @@ -164,7 +62,6 @@ return (

비밀번호가 기억나지 않으시나요?

-
- 전화번호 - - - {isSend? '인증번호 재전송' : '인증번호 받기'} - - {!isTel && {errMessage}} - - {isSend ? - onChangeCertifyInput(e)} - style={{width : '326px'}}/> - 인증번호 확인 - : ''} - - {!isTel && {errMessage}} - {isSend && 인증번호가 발송되었어요 (유효시간 {minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')})} - -
- + + + 찾아보기 @@ -237,87 +106,16 @@ return (
- )} + {isModal && }
); }; -export default FindPwPage; +export default FindPasswordPage; -const Container = styled.div` - display: flex; -`; -const ResultWrapper = styled.div` +const Container = styled.div` display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-width: 1440px; - width: 100%; - min-height: 100vh; - h4 { - color:#1E1E1E; - font-size: 32px; - font-weight: 500; - line-height: 160%; - margin-top: 41px; - margin-bottom: 8px; - text-align: center; - span { - color: ${({ theme }) => theme.color.gray500}; - font-weight: bold; - } - } - img{ - display: flex; - width: 130.67px; - height: 121.67px; - } - p{ - color:#BBBBBB; - font-size: 16px; - font-weight: 500; - line-height: 160%; - margin-top: 8px; - } -`; - -const UserDiv = styled.div` - display : flex; - flex-direction : row; - gap : 8px; -` -const UserButton = styled.button` - width : 160px; - height : 56px; - color : #1E1E1E; - background-color : #E9FF3F; - border : none; - border-radius : 12px; - ${theme.typography.Body1}; - &:disabled { - background-color : #F3F3F3; - color : #BBBBBB; - } -` - - -const LoginButton = styled.button<{ bgColor?: boolean }>` - width: 494px; - height: 56px; - background: #FFFFFF; - color: #787878; - font-size: 16px; - font-weight: 500; - line-height: 160%; - border-radius: 12px; - border: 1.5px solid var(--gray-200, #e8e8e8); - margin-top:12px; - font-family: Pretendard; - &:hover { - cursor: pointer; - } `; const Wrapper = styled.div` @@ -330,13 +128,6 @@ const Wrapper = styled.div` gap: 124px; `; -const LogoSection = styled.div` - img{ - display: flex; - width: auto; - height: 840px; - } -`; const MainSection = styled.div` display: flex; @@ -382,13 +173,6 @@ const InputSection = styled.div` height: auto; `; -const Form = styled.form` - display: flex; - flex-direction: column; - align-items: center; - width: 494px; - height: auto; -`; const Label = styled.label` margin-bottom: 20px; @@ -408,7 +192,6 @@ const TwoLabel = styled.label` margin-bottom: 8px; span { font-size: 16px; - color: #787878; font-family: Pretendard; margin-bottom: 8px; font-weight: 500; @@ -419,6 +202,7 @@ const TwoLabel = styled.label` const InputBox = styled.input` display: flex; align-items: center; + margin-bottom : 8px; justify-content: center; width: 494px; height: 56px; @@ -433,7 +217,6 @@ const InputBox = styled.input` line-height: 160%; border-radius: 12px; border: 1.5px solid var(--gray-200, #e8e8e8); - margin-top: 8px; outline: none; &:hover { @@ -450,21 +233,7 @@ const InputBox = styled.input` } `; -const Error = styled.p` - color: #eb5353; - font-size: 0.9rem !important; - font-weight: 500; - margin: 0; - padding-left: 4px; - padding-top: 12px; -`; - -const SendMsg = styled.span` - color : #FF4A4A !important; - ${theme.typography.Body3}; -` - -const FindButton = styled.button<{ bgColor?: boolean }>` +const FindButton = styled.button` width: 494px; height: 56px; background: #1E1E1E; @@ -507,5 +276,4 @@ const StyledLink = styled(Link)` text-align: center; text-decoration: none; margin : 0px 0px 0px 10px; -`; - +`; \ No newline at end of file diff --git a/src/pages/SignUpPage.tsx b/src/pages/SignUpPage.tsx index 1308aee..ff745c4 100644 --- a/src/pages/SignUpPage.tsx +++ b/src/pages/SignUpPage.tsx @@ -83,7 +83,7 @@ const SignUp = () => { setPassword(passwordCurrent); if (!passwordRegex.test(passwordCurrent)) { setPasswordMessage( - '*8자 이상으로 입력 *대문자 사용 *숫자 사용 *특수문자 사용', + '*8자 이상으로 입력 *대문자 사용 *숫자 사용 *특수문자 사용' ); setIsPassword(false); } else { @@ -96,12 +96,14 @@ const SignUp = () => { setPasswordCheck(e.target.value); if (e.target.value === '') { setPasswordCheckMessage('비밀번호를 재입력해주세요'); - } else if (password && e.target.value !== password) { + } + else if (password && e.target.value !== password) { setMismatchError(true); setPasswordCheckMessage( '비밀번호가 일치하지 않습니다. 다시 확인해주세요.', ); - } else { + } + else { setMismatchError(false); setPasswordCheckMessage('비밀번호가 일치합니다.'); } @@ -109,9 +111,6 @@ const SignUp = () => { const onSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (!mismatchError) { - console.log('서버로 회원가입하기'); - } }; const navigate = useNavigate(); @@ -119,7 +118,6 @@ const SignUp = () => { if (name && year && month && date && isCertify && selectedSex && email && password && passwordCheck && !mismatchError) { // 서버에 데이터 전송 onRegisterUserInfo(); - console.log('정보 등록 완료'); navigate('/sign-up/success'); } else { alert('입력값을 확인해주세요.'); diff --git a/src/styles/FindEmail.ts b/src/styles/FindEmail.ts index 5af45ac..e7c4cf8 100644 --- a/src/styles/FindEmail.ts +++ b/src/styles/FindEmail.ts @@ -36,6 +36,7 @@ const Container = styled.div` border : none; border-radius : 12px; ${theme.typography.Body1}; + cursor: pointer; } & img { width : 87px; diff --git a/src/styles/PhoneCheck.ts b/src/styles/PhoneCheck.ts index f9bf679..a8f7354 100644 --- a/src/styles/PhoneCheck.ts +++ b/src/styles/PhoneCheck.ts @@ -37,9 +37,11 @@ const Cotainer = styled.div` border : none; border-radius : 12px; ${theme.typography.Body1}; + cursor: pointer; &:disabled { background-color : ${theme.color.gray100}; color : ${theme.color.gray300}; + cursor: default; } } & div.inputwrap { diff --git a/src/styles/modals/NotFindUserModal.ts b/src/styles/modals/NotFindUserModal.ts index d0ac081..56c1ee0 100644 --- a/src/styles/modals/NotFindUserModal.ts +++ b/src/styles/modals/NotFindUserModal.ts @@ -1,58 +1,83 @@ import styled from "styled-components"; -import theme from '@/styles/theme'; +import theme from "../theme"; -const Cotainer = styled.div` - width : 494px; +const Container = styled.div` + position : fixed; display : flex; - flex-direction : column; - gap : 5px; - & div.label { - color : ${theme.color.gray400}; - ${theme.typography.Body1}; + z-index: 1; + background-color: rgba(0,0,0,0.4); + height : 100%; + width : 100%; + justify-content : center; + align-items : center; + + & div.contentWrap { + width : 700px; + height : 452px; + padding : 40px 50px 40px 50px; + background-color : ${theme.color.white}; + border-radius: 20px; + display : flex; + gap: 20px; + flex-direction : column; + justify-content: space-between; + } + & div.closeWrap { + width : 600px; + display: flex; + flex-direction: row-reverse; + cursor: pointer; + } + & div.textContent { + height : 200px; + display: flex; + flex-direction : column; + justify-content: space-between; + align-items : center; + gap : 10px; } - & input { - width : 326px; + & div.subtitleWrap { + display : flex; + flex-direction : column; + justify-content : center; + align-items : center; + gap : 5px; + } + & img { + width : 56px; height : 56px; - padding: 0px 0px 0px 20px; - border : 1.5px solid ${theme.color.gray200}; - border-radius : 12px; - outline : none; + } + & span.title { + ${theme.typography.Header6} color : ${theme.color.gray500}; + } + & span.subtitle { ${theme.typography.Body1}; - &:focus { - border: 1.5px solid ${theme.color.gray500}; - }; - &:hover { - border: 1.5px solid ${theme.color.gray500}; - } - &::placeholder { - color : ${theme.color.gray300}; - } + color : ${theme.color.gray300}; + } + & div.btnContent { + display : flex; + flex-direction : column; + gap: 12px; } & button { - width : 160px; - height : 56px; - background-color : ${theme.color.green400}; - color : ${theme.color.gray500}; - border : none; + width : 600px; + height : 58px; border-radius : 12px; + border: none; ${theme.typography.Body1}; - &:disabled { - background-color : ${theme.color.gray100}; - color : ${theme.color.gray300}; - } } - & div.inputwrap { - display : flex; - gap : 10px; + & button.b_btn { + color : ${theme.color.white}; + background-color : ${theme.color.gray500}; + cursor: pointer; } - & span.msg { - margin-left : 10px; - ${theme.typography.Body3}; - color : ${theme.color.red}; + & button.w_btn { + color : ${theme.color.gray400}; + background-color : ${theme.color.white}; + border : 1.5px solid ${theme.color.gray200}; + cursor: pointer; } -`; - - +` -export default Cotainer; \ No newline at end of file +export default Container; \ No newline at end of file diff --git a/src/styles/signup/SignuppageStyle.ts b/src/styles/signup/SignuppageStyle.ts index d660080..d302af7 100644 --- a/src/styles/signup/SignuppageStyle.ts +++ b/src/styles/signup/SignuppageStyle.ts @@ -279,6 +279,7 @@ export const SexButton = styled.button<{ selected: boolean }>` font-weight: 500; line-height: 160%; margin-right: 10px; + cursor: pointer; ${(props) => props.selected && ` background: #1e1e1e; @@ -346,6 +347,7 @@ export const SucButton = styled.button` justify-content: center; align-items: center; border: none; + cursor: pointer; `; export const DupSucButton = styled.button` @@ -365,9 +367,11 @@ export const DupSucButton = styled.button` justify-content: center; align-items: center; border: none; + cursor: pointer; &:disabled { background-color : ${theme.color.gray100}; color : ${theme.color.gray300}; + cursor: default; } `; From ccf3999a93f99a6767c93f4a5b6cf79ddee3bd5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=89=E1=85=A5=E1=86=BC?= =?UTF-8?q?=E1=84=92=E1=85=B1?= Date: Mon, 19 Feb 2024 02:41:05 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feature-074:=20=EB=AA=A8=EB=8D=B8=EB=A7=81?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20=EC=A7=84=ED=96=89=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 7 +- package.json | 1 + src/assets/icons/pause.svg | 5 + src/components/Home/SearchYoutube.tsx | 9 +- .../SummaryDetailBox/SummaryDetailBox.tsx | 104 +++++++++++++++--- .../ScriptViewer/ScriptViewer.tsx | 50 +++++++-- .../SummaryScriptBox/ToolBox/ToolBox.tsx | 5 +- .../ModelController/ModelController.tsx | 34 ++++-- .../layout/header/alarm/AlarmItem.tsx | 30 ++++- .../layout/header/alarm/AlarmList.tsx | 4 +- src/components/layout/header/alarm/index.tsx | 22 +++- src/models/video.ts | 5 +- src/stores/summary.ts | 10 ++ src/styles/SummaryPage.ts | 31 +++++- src/utils/date.ts | 19 ++++ yarn.lock | 5 + 16 files changed, 292 insertions(+), 49 deletions(-) create mode 100644 src/assets/icons/pause.svg diff --git a/index.html b/index.html index 8769ef3..5b45a52 100644 --- a/index.html +++ b/index.html @@ -17,6 +17,11 @@
- + + diff --git a/package.json b/package.json index 0a19650..70b7584 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/jest": "^29.5.11", "@types/node": "^20.11.17", "@types/react-modal": "^3.16.3", + "@types/youtube": "^0.0.50", "axios": "^1.6.4", "lodash": "^4.17.21", "react": "^18.2.0", diff --git a/src/assets/icons/pause.svg b/src/assets/icons/pause.svg new file mode 100644 index 0000000..d30f950 --- /dev/null +++ b/src/assets/icons/pause.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/Home/SearchYoutube.tsx b/src/components/Home/SearchYoutube.tsx index 0ef4b36..779cb9b 100644 --- a/src/components/Home/SearchYoutube.tsx +++ b/src/components/Home/SearchYoutube.tsx @@ -91,6 +91,13 @@ const SearchYoutube = ({ searchRef }: Props) => { } }; + const handleChangeInput: React.ChangeEventHandler = (e) => { + setInputLink(e.target.value); + setVideoLink(null); + setStatus('NONE'); + setProgress(0); + }; + const handleClickCreateVideoButton = async () => { if (!modelingData) return; @@ -153,7 +160,7 @@ const SearchYoutube = ({ searchRef }: Props) => { type="text" value={inputLink} disabled={status === 'CONTINUE'} - onChange={(e) => setInputLink(e.target.value)} + onChange={handleChangeInput} placeholder="https://youtube.com/..." /> diff --git a/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx b/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx index 4714388..8358d15 100644 --- a/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx +++ b/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx @@ -1,4 +1,5 @@ -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useEffect, useRef } from 'react'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { updateVideoCategoryIdAPI } from '@/apis/videos'; @@ -6,8 +7,10 @@ import { IVideo } from '@/models/video'; import { summaryIsEditingViewState, + summaryPlaySubHeadingIdState, summaryUpdateVideoState, summaryVideoState, + summaryVideoTimeState, } from '@/stores/summary'; import { toastListState } from '@/stores/toast'; @@ -24,9 +27,15 @@ type Props = { }; const SummaryDetailBox = ({ onRefresh }: Props) => { + const player = useRef(); + const summaryVideo = useRecoilValue(summaryVideoState) as IVideo; const summaryUpdateVideo = useRecoilValue(summaryUpdateVideoState); + const setSummaryVideoTime = useSetRecoilState(summaryVideoTimeState); const isEditingView = useRecoilValue(summaryIsEditingViewState); + const [playSubHeadingId, setPlaySubHeadingId] = useRecoilState( + summaryPlaySubHeadingIdState, + ); const [toastList, setToastList] = useRecoilState(toastListState); const subHeading = isEditingView @@ -50,6 +59,75 @@ const SummaryDetailBox = ({ onRefresh }: Props) => { } }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleMessage = (e: any) => { + if (e.origin === 'https://www.youtube.com') { + try { + const { info } = JSON.parse(e.data); + + if (!info) return; + + setPlaySubHeadingId((id) => { + const item = subHeading.find((s) => s.id === id); + + // END or PAUSE + if ([0, 2].includes(info.playerState)) { + return -1; + } + + if (item) { + if ( + item.start_time > info.currentTime || + info.currentTime > item.end_time + ) + return -1; + } + + return id; + }); + + setSummaryVideoTime(info.currentTime); + } catch (e) { + console.error(e); + } + } + }; + + useEffect(() => { + if (player.current) return; + + player.current = new YT.Player('player', { + videoId: summaryVideo.youtube_id, + }); + + window.onmessage = handleMessage; + + return () => { + setSummaryVideoTime(0); + setPlaySubHeadingId(-1); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [summaryVideo]); + + useEffect(() => { + if (player.current) { + if (playSubHeadingId > -1) { + const item = subHeading.find((s) => s.id === playSubHeadingId); + + if (item) { + player.current.seekTo(item.start_time, true); + player.current.playVideo(); + } + } else if (playSubHeadingId === -2) { + // 영상 멈추기 + player.current.pauseVideo(); + + setPlaySubHeadingId(-1); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [playSubHeadingId]); + return (
{ ))}
-