diff --git a/src/assets/images/buddyApproval.svg b/src/assets/images/buddyApproval.svg new file mode 100644 index 0000000..cb4fdd9 --- /dev/null +++ b/src/assets/images/buddyApproval.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/buddyWaiting.svg b/src/assets/images/buddyWaiting.svg new file mode 100644 index 0000000..0a84dce --- /dev/null +++ b/src/assets/images/buddyWaiting.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/left-controller.svg b/src/assets/images/left-controller.svg new file mode 100644 index 0000000..e90b3ee --- /dev/null +++ b/src/assets/images/left-controller.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/notice_black.svg b/src/assets/images/notice_black.svg new file mode 100644 index 0000000..9f8f0cb --- /dev/null +++ b/src/assets/images/notice_black.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/right-controller.svg b/src/assets/images/right-controller.svg new file mode 100644 index 0000000..5dd1917 --- /dev/null +++ b/src/assets/images/right-controller.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/buddy/buddyButton.tsx b/src/components/buddy/buddyButton.tsx index 7a70c40..2516f6d 100644 --- a/src/components/buddy/buddyButton.tsx +++ b/src/components/buddy/buddyButton.tsx @@ -1,70 +1,59 @@ import styled from "styled-components"; import { COLORS } from "../../theme"; -import checked from '../../assets/images/filledCheck.png'; -import unchecked from '../../assets/images/unfilledCheck.png'; +import checked from "../../assets/images/filledCheck.png"; +import unchecked from "../../assets/images/unfilledCheck.png"; interface ButtonProps { - text?: string; - ischecked?: boolean; - onClick?: () => void; + text?: string; + ischecked?: boolean; + onClick?: () => void; } -export const BuddyButton = ({ - text='오류', - ischecked=false, - onClick +export const BuddyButton = ({ + text = "오류", + ischecked = false, + onClick, }: ButtonProps) => { - return ( - (ischecked ? - - {text} - - : - - {text} - - ) - ) -} + return ischecked ? ( + {text} + ) : ( + {text} + ); +}; const ButtonWrapper = styled.button` - width: 100%; - height: 56px; - color: ${COLORS.font1}; - background-color: #FFF; - border-radius: 50px; - border: none; - cursor: pointer; - display: inline-block; - text-align: left; - padding: 16px 24px; - box-shadow: 0px 0px 12px -4px rgba(0, 0, 0, 0.20); - background-image: url(${unchecked}); - background-size: 28px; - background-repeat: no-repeat; - background-position: right 20px center; + width: 100%; + height: 56px; + color: ${COLORS.font1}; + background-color: #fff; + border-radius: 50px; + border: none; + cursor: pointer; + display: inline-block; + text-align: left; + padding: 16px 24px; + box-shadow: 0px 0px 12px -4px rgba(0, 0, 0, 0.2); + background-image: url(${unchecked}); + background-size: 28px; + background-repeat: no-repeat; + background-position: right 20px center; `; const ButtonWrapperChecked = styled.button` - width: 100%; - height: 56px; - color: ${COLORS.font1}; - background-color: #FCECEE; - border-radius: 50px; - border: 1px solid ${COLORS.main}; - cursor: pointer; - display: inline-block; - text-align: left; - padding: 16px 24px; - box-shadow: 0px 0px 12px -4px rgba(0, 0, 0, 0.20); - font-weight: bold; - background-image: url(${checked}); - background-size: 28px; - background-repeat: no-repeat; - background-position: right 20px center; + width: 100%; + height: 56px; + color: ${COLORS.font1}; + background-color: #fcecee; + border-radius: 50px; + border: 1px solid ${COLORS.main}; + cursor: pointer; + display: inline-block; + text-align: left; + padding: 16px 24px; + box-shadow: 0px 0px 12px -4px rgba(0, 0, 0, 0.2); + font-weight: bold; + background-image: url(${checked}); + background-size: 28px; + background-repeat: no-repeat; + background-position: right 20px center; `; - diff --git a/src/components/buddy/buddyStart.tsx b/src/components/buddy/buddyStart.tsx index 6227131..60c596c 100644 --- a/src/components/buddy/buddyStart.tsx +++ b/src/components/buddy/buddyStart.tsx @@ -1,122 +1,169 @@ -import styled from "styled-components"; -import { COLORS } from "../../theme"; +import { useEffect, useState } from "react"; + +// api +import { getBuddyUsers } from "../../services/apis/buddy.service"; // 컴포넌트 import { ConfirmButton } from "../button/confirmButton"; import { BuddyHeader } from "../header/buddyHeader"; import { useNavigate } from "react-router-dom"; -// styled-components -import { BuddyContainer, BuddyContainer2, ApplicationContainer } from "../../styles/buddy-styles"; +// style +import styled from "styled-components"; +import { + BuddyContainer, + BuddyContainer2, + ApplicationContainer, +} from "../../styles/buddy-styles"; +import { COLORS } from "../../theme"; // 이미지 -import startImg from '../../assets/images/buddyStart.svg'; -import nugul from '../../assets/images/nugul.svg'; -import { useEffect } from "react"; +import startImg from "../../assets/images/buddyStart.svg"; +import nugul from "../../assets/images/nugul.svg"; +import { ConfirmModal } from "../modals/confirmModal"; export const BuddyStart = () => { - const navigate = useNavigate(); - - // localStorage에서 토큰 가져오기 - // const token = localStorage.getItem("accessToken"); - - // 조건 선택 페이지 이동 - const navigateHandler = () => { - navigate('/buddy?step=1'); - // 로그인 하면 이동할 수 있도록 했는데 테스트 끝나면 주석해제 예정입니다~ - // if(token) { - // navigate('/buddy?step=1'); - // } else { - // alert('로그인이 필요한 서비스 입니다!'); - // navigate('/login'); - // } + const [isModalOpen, setIsModalOpen] = useState(false); + + const openModal = () => { + setIsModalOpen(true); // 모달 열기 + }; + + const closeModal = () => { + setIsModalOpen(false); // 모달 닫기 + }; + + const navigate = useNavigate(); + + // localStorage에서 토큰 가져오기 + // const token = localStorage.getItem("accessToken"); + + // 조건 선택 페이지 이동 + const navigateHandler = () => { + navigate("/buddy?step=1"); + // 로그인 하면 이동할 수 있도록 했는데 테스트 끝나면 주석해제 예정입니다~ + // if(token) { + // navigate('/buddy?step=1'); + // } else { + // alert('로그인이 필요한 서비스 입니다!'); + // navigate('/login'); + // } + }; + + const [buddyCount, setBuddyCount] = useState(0); + useEffect(() => { + const fetchUserCount = async () => { + const result = await getBuddyUsers(); + if (result) { + setBuddyCount(result); + localStorage.setItem('buddyCount', `${result}`); + } else { + localStorage.setItem('buddyCount', `0`); + } }; - useEffect(() => { - - }, []) - - return ( - - - - - - - 세종버디란? - 세종버디(Buddy)는 - - 맞춤형 캠퍼스 짝꿍을 찾는 서비스 입니다. - - 한 명의 학우와 한 학기 동안 버디로 매칭 되며, - 다음 학기에 새로운 버디를 찾을 수 있습니다. - - - - - - 216명의 학생들이 버디를 찾고 있어요! - - - - - - ); -} + fetchUserCount(); + }, []); + + return ( + + {isModalOpen && ( + { + console.log("확인 버튼 클릭"); + closeModal(); // 모달 닫기 + }} + onClose={closeModal} // 백드롭 또는 취소 버튼 클릭 시 모달 닫기 + /> + )} + + + + + + 세종버디란? + 세종버디(Buddy)는 + + 맞춤형 캠퍼스 짝꿍을 찾는 서비스 + 입니다. + + + 한 명의 학우와 한 학기 동안 버디로 매칭 되며, + + + 다음 학기에 새로운 버디를 찾을 수 있습니다. + + + + + + + + {buddyCount}명의 학생들이 버디를 찾고 있어요! + + + + + + + ); +}; const StartImg = styled.img` - width: 100%; - height: 132px; - text-align: center; - margin: 20% 0 17%; + width: 100%; + height: 132px; + text-align: center; + margin: 20% 0 17%; `; const BuddyStartTextContainer = styled.div` - padding: 0 6px; - @media (min-width: 430px) { - max-width: 398px; - margin: 0 auto; - } + padding: 0 6px; + @media (min-width: 430px) { + max-width: 398px; + margin: 0 auto; + } `; const BuddyStartTitle = styled.h3` - color: #111; - font-weight: bold; - font-size: 24px; - margin-bottom: 12px; + color: #111; + font-weight: bold; + font-size: 24px; + margin-bottom: 12px; `; const BuddyStartText1 = styled.p` - color: ${COLORS.font1}; - font-weight: 700; - line-height: 1.5; + color: ${COLORS.font1}; + font-weight: 700; + line-height: 1.5; `; const BuddyStartTextB = styled.b` - color: ${COLORS.main}; + color: ${COLORS.main}; `; const BuddyStartText2 = styled.p` - color: #555; - line-height: 1.5; + color: #555; + line-height: 1.5; `; const ApplicationNum = styled.div` - display: flex; - align-items: center; - gap: 5px; + display: flex; + align-items: center; + gap: 5px; `; const ApplicationImg = styled.img` - object-fit:cover; - border-raidus: 12px; + object-fit: cover; + border-raidus: 12px; `; const ApplicationText = styled.p` -color: #111; -font-weight: 700; -`; \ No newline at end of file + color: #111; + font-weight: 700; +`; diff --git a/src/components/buddy/buddyStatus.tsx b/src/components/buddy/buddyStatus.tsx new file mode 100644 index 0000000..48dc589 --- /dev/null +++ b/src/components/buddy/buddyStatus.tsx @@ -0,0 +1,85 @@ +import styled from "styled-components"; +import { COLORS } from "../../theme"; + +import unchecked from "../../assets/images/unfilledCheck.png"; +import checked from "../../assets/images/filledCheck.png"; + +interface BuddyMatchingStep { + step: number; +} + +export const BuddyStatus = ({ step }: BuddyMatchingStep) => { + return ( + + + + {step >= 2 ? : } + = 2 ? checked : unchecked} /> + {step >= 3 ? : } + = 3 ? checked : unchecked} /> + + + + 버디 찾는 중 + {step >= 2 ? ( + 버디 수락/거절 + ) : ( + 버디 수락/거절 + )} + {step >= 3 ? ( + 버디 정보확인 + ) : ( + 버디 정보확인 + )} + + + ); +}; + +const StatusContainer = styled.div` + width: 301px; + height: 78px; + background-color: ${COLORS.sub3}; + border-radius: 16px; + padding: 14px 0; + margin: 0 auto; + margin-top: 4px; +`; +const StatusIconContainer = styled.div` + display: flex; + align-items: center; + gap: 8px; + justify-content: center; +`; +const StatusIcon = styled.img` + width: 24px; + height: 24px; +`; +const StatusBar = styled.div` + width: 48px; + height: 0; + border: 1px solid ${COLORS.line1}; +`; +const StatusBarProgress = styled.div` + width: 48px; + height: 0; + border: 1px solid ${COLORS.main}; +`; +const StatusTextContainer = styled.div` + display: flex; + align-items: center; + gap: 16px; + margin-top: 6px; + justify-content: center; +`; +const StatusTextTextNow = styled.p` + font-size: 14px; + font-weight: 600; + color: ${COLORS.main}; +`; +const StatusTextText = styled.p` + font-size: 14px; + font-weight: 400; + color: ${COLORS.font3}; + letter-spacing: -0.35px; +`; diff --git a/src/components/buddy/matching/buddyApproval.tsx b/src/components/buddy/matching/buddyApproval.tsx new file mode 100644 index 0000000..b325748 --- /dev/null +++ b/src/components/buddy/matching/buddyApproval.tsx @@ -0,0 +1,103 @@ +import { useNavigate } from "react-router-dom"; + +// Components +import { LoginHeader } from "../../loginHeader"; +import { BuddyStatus } from "../buddyStatus"; +import { NoticeBox } from "../../noticeBox"; +import { ConfirmButton } from "../../button/confirmButton"; + +// style +import styled from "styled-components"; +import { COLORS } from "../../../theme"; +import { MatchingContainer, MatchingTitle, MatchingText } from "./buddyWaiting"; + +// Img +import BuddyApprovalImg from "../../../assets/images/buddyApproval.svg"; +import { useEffect, useState } from "react"; +import { ConfirmModal } from "../../modals/confirmModal"; +//import { getBuddyInfoFirst } from "../../../services/apis/buddy.service"; + +export const BuddyApproval = () => { + //const [buddyInfo, setBuddyInfo] = useState(''); + const navigate = useNavigate(); + const HandleGoHome = () => { + navigate("/"); + }; + + useEffect(() => { + // const info = getBuddyInfoFirst(); + // setBuddyInfo(info); + }, []); + + const [isModalOpen, setIsModalOpen] = useState(false); + + const openModal = () => { + setIsModalOpen(true); // 모달 열기 + }; + + const closeModal = () => { + setIsModalOpen(false); // 모달 닫기 + }; + + return ( + + {isModalOpen && ( + { + console.log("확인 버튼 클릭"); + closeModal(); // 모달 닫기 + }} + onClose={closeModal} // 백드롭 또는 취소 버튼 클릭 시 모달 닫기 + /> + )} + + + + 버디를 찾았습니다 + 24시간 내로 수락여부를 선택해주세요! + + 미디어커뮤니케이션 · 4학년 + + + + + + + + ); +}; + +const MatchingBuddyInfo = styled.p` + font-size: 18px; + font-weight: 600; + color: ${COLORS.font1}; + margin-bottom: 21px; +`; +const MatchingImg = styled.img` + margin: 16px 0 12px; +`; +const ButtonContainer = styled.div` + display: flex; + gap: 7px; + margin-top: auto; +`; diff --git a/src/components/buddy/matching/buddyFinal5.tsx b/src/components/buddy/matching/buddyFinal5.tsx index f5dc8e5..563aac2 100644 --- a/src/components/buddy/matching/buddyFinal5.tsx +++ b/src/components/buddy/matching/buddyFinal5.tsx @@ -1,9 +1,20 @@ import { useNavigate } from "react-router-dom"; +//Zustand import { BuddyStore } from "../../../store/useBuddyStore"; +// API +import { registerBuddy } from "../../../services/apis/buddy.service"; + +// Components +import { BuddyHeader } from "../../header/buddyHeader"; +import { ConfirmButton } from "../../button/confirmButton"; +import { toast } from "sonner"; + +// style +import styled from "styled-components"; import { COLORS } from "../../../theme"; -import { +import { BuddyContainer, BuddyContainer2, InfoContainer, @@ -12,49 +23,79 @@ import { ButtonContainer, } from "../../../styles/buddy-styles"; -import { BuddyHeader } from "../../header/buddyHeader"; -import { ConfirmButton } from "../../button/confirmButton"; -import styled from "styled-components"; export const BuddyFinal = () => { + // 값 가져오기 const { gender, major, subMajor, type, grade } = BuddyStore(); // 해당 값을 누르면 그 페이지로 이동 const navigate = useNavigate(); - const navigateHandler = ( step:string ): void => { + const navigateHandler = (step: string): void => { navigate(`/buddy?step=${step}`); - } + }; console.log(gender, major, subMajor, type, grade); // 텍스트 변환 // 버디 범위 텍스트 변환 const majorText: { [key: string]: string } = { - "SAME_COLLEGE": "같은 단과대 버디", - "SAME_DEPARTMENT": "같은 학과 버디", - "NO_MATTER": "상관없음" - } + SAME_COLLEGE: "같은 단과대 버디", + SAME_DEPARTMENT: "같은 학과 버디", + NO_MATTER: "상관없음", + }; // 버디 타입 텍스트 변환 const getTypeText: { [key: string]: string } = { - "MATE": "동기", - "SENIOR": "선배", - "JUNIOR": "후배" - } + MATE: "동기", + SENIOR: "선배", + JUNIOR: "후배", + }; const TypeText = type - .filter((t) => getTypeText[t]) // TypeText에 존재하는 값만 필터링 - .map((t) => getTypeText[t]) // 해당 값을 매핑 - .join(", "); // 문자열로 나열 - + .filter((t) => getTypeText[t]) // TypeText에 존재하는 값만 필터링 + .map((t) => getTypeText[t]) // 해당 값을 매핑 + .join(", "); // 문자열로 나열 + // 버디 학년 텍스트 변환 const getGradeText: { [key: string]: string } = { - "GRADE_1": "1학년", - "GRADE_2": "2학년", - "GRADE_3": "3학년", - "GRADE_4": "4학년(이상)" - } + GRADE_1: "1학년", + GRADE_2: "2학년", + GRADE_3: "3학년", + GRADE_4: "4학년(이상)", + }; const gradeText = grade - .filter((g) => getGradeText[g]) // TypeText에 존재하는 값만 필터링 - .map((g) => getGradeText[g]) // 해당 값을 매핑 - .join(", "); // 문자열로 나열 + .sort() // 정렬(오름차순) + .filter((g) => getGradeText[g]) // TypeText에 존재하는 값만 필터링 + .map((g) => getGradeText[g]) // 해당 값을 매핑 + .join(", "); // 문자열로 나열 + + // 카카오톡 아이디 + const kakaoId: string | null = localStorage.getItem("kakaoAccount"); + // 핸드폰 번호 + let phoneNumber: string | null = localStorage.getItem("phoneNumber"); + // 폰번호에 하이픈 넣기 + if (phoneNumber) { + phoneNumber = phoneNumber.replace(/(\d{3})(\d{4})(\d{4})/, "$1-$2-$3"); + } + + const buddyRegisterHandler = async () => { + // 전달 데이터 + const buddyData = { + genderOption: gender, + classTypeOption: type, + gradeOption: grade, + collegeMajorOption: major, + isSubMajor: subMajor, + }; + console.log(buddyData); + + const response = await registerBuddy(buddyData); + + //버디등록 성공 시 + if (response) { + toast.success("버디 등록이 완료되었습니다."); + navigate("/"); + } else { + toast.success("버디 등록을 실패했습니다."); + } + }; return ( @@ -68,8 +109,7 @@ export const BuddyFinal = () => { 버디 성별 - navigateHandler('1')}> + navigateHandler("1")}> {gender === "SAME" ? "동성" : "상관없음"} @@ -77,22 +117,21 @@ export const BuddyFinal = () => { 버디 범위 - navigateHandler('2')}> + navigateHandler("2")}> {majorText[major] || "오류"} - + 버디 관계 - navigateHandler('3')}> + navigateHandler("3")}> {TypeText} - + 버디 학년 - navigateHandler('4')}> + navigateHandler("4")}> {gradeText} @@ -100,23 +139,24 @@ export const BuddyFinal = () => { 카카오톡 아이디 - aaaaaaaa + {kakaoId} 전화번호 - 010-1111-2222 + {phoneNumber} - ); -} +}; const BuddyConditionContainer = styled.div` width: 100%; @@ -131,7 +171,7 @@ const BuddyConditionInput = styled.button` width: 100%; border-radius: 8px; border: 1px solid ${COLORS.line1}; - background-color: #FFF; + background-color: #fff; padding: 13px 16px; margin-top: 8px; text-align: left; @@ -144,4 +184,4 @@ const BuddyConditionContainer2 = styled.div` display: flex; align-items: center; gap: 15px; -`; \ No newline at end of file +`; diff --git a/src/components/buddy/matching/buddyGender1.tsx b/src/components/buddy/matching/buddyGender1.tsx index b65ab56..e8e187a 100644 --- a/src/components/buddy/matching/buddyGender1.tsx +++ b/src/components/buddy/matching/buddyGender1.tsx @@ -1,22 +1,25 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; +// Zustand import { BuddyStore } from "../../../store/useBuddyStore"; +// Components +import { BuddyHeader } from "../../header/buddyHeader"; +import { BuddyButton } from "../buddyButton"; +import { ConfirmButton } from "../../button/confirmButton"; + +// style import { COLORS } from "../../../theme"; -import { +import { BuddyContainer, BuddyContainer2, InfoContainer, MatchingTitle, MatchingInfo, - ButtonContainer + ButtonContainer, } from "../../../styles/buddy-styles"; -import { BuddyHeader } from "../../header/buddyHeader"; -import { BuddyButton } from "../buddyButton"; -import { ConfirmButton } from "../../button/confirmButton"; - export const BuddyGender = () => { const { gender, setGender } = BuddyStore(); const [isSame, setIsSame] = useState(false); @@ -25,7 +28,7 @@ export const BuddyGender = () => { // 값 유지 useEffect(() => { if (gender === "SAME") { - setIsSame(true) + setIsSame(true); } else if (gender === "NO_MATTER") { setIsAny(true); } @@ -35,31 +38,30 @@ export const BuddyGender = () => { const ClickSameHandler = () => { setIsSame(!isSame); setGender("SAME"); - if(isAny) { + if (isAny) { setIsAny(false); } - } + }; // 상관없음 선택 const ClickAnyHandler = () => { setIsAny(!isAny); setGender("NO_MATTER"); - if(isSame) { + if (isSame) { setIsSame(false); } - } + }; console.log("성별: ", gender); // 다음 단계 const navigate = useNavigate(); const NextStepHandler = () => { - navigate("/buddy?step=2"); - } + navigate("/buddy?step=2"); + }; return ( - 버디 성별 선택 이성과의 매칭이 부담스러우신 분들은 @@ -67,25 +69,24 @@ export const BuddyGender = () => { - - - {}} + {}} /> - ); -} \ No newline at end of file +}; diff --git a/src/components/buddy/matching/buddyGrade4.tsx b/src/components/buddy/matching/buddyGrade4.tsx index cd42e39..516db67 100644 --- a/src/components/buddy/matching/buddyGrade4.tsx +++ b/src/components/buddy/matching/buddyGrade4.tsx @@ -1,10 +1,17 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; +// Zustand import { BuddyStore } from "../../../store/useBuddyStore"; +// Components +import { BuddyHeader } from "../../header/buddyHeader"; +import { BuddyButton } from "../buddyButton"; +import { ConfirmButton } from "../../button/confirmButton"; + +// style import { COLORS } from "../../../theme"; -import { +import { BuddyContainer, BuddyContainer2, InfoContainer, @@ -13,10 +20,6 @@ import { ButtonContainer, } from "../../../styles/buddy-styles"; -import { BuddyHeader } from "../../header/buddyHeader"; -import { BuddyButton } from "../buddyButton"; -import { ConfirmButton } from "../../button/confirmButton"; - export const BuddyGrade = () => { const { grade, setGrade } = BuddyStore(); const [is1st, setIs1st] = useState(false); @@ -27,16 +30,16 @@ export const BuddyGrade = () => { useEffect(() => { if (grade.includes("GRADE_1")) { setIs1st(true); - } + } if (grade.includes("GRADE_2")) { setIs2nd(true); - } + } if (grade.includes("GRADE_3")) { setIs3rd(true); - } + } if (grade.includes("GRADE_4")) { setIs4th(true); - } + } }, [grade]); // 범위 선택 핸들러 @@ -46,77 +49,84 @@ export const BuddyGrade = () => { const typeArr: string[] = [...grade, "GRADE_1"]; setGrade(typeArr); } - } + }; const secondHandler = () => { setIs2nd(!is2nd); if (!grade.includes("GRADE_2")) { const typeArr: string[] = [...grade, "GRADE_2"]; setGrade(typeArr); } - } + }; const thirdHandler = () => { setIs3rd(!is3rd); if (!grade.includes("GRADE_3")) { const typeArr: string[] = [...grade, "GRADE_3"]; setGrade(typeArr); } - } + }; const fourthHandler = () => { setIs4th(!is4th); if (!grade.includes("GRADE_4")) { const typeArr: string[] = [...grade, "GRADE_4"]; setGrade(typeArr); } - } - console.log(grade) + }; + console.log(grade); // 다음 단계 const navigate = useNavigate(); const NextStepHandler = () => { - navigate("/buddy?step=5"); - } + navigate("/buddy?step=5"); + }; return ( - + 버디 학년 선택(복수선택 가능) 선호하는 버디의 학년을 선택해주세요! - 초과학기, 졸업유예 등은 4학년에 포함됩니다. + + 초과학기, 졸업유예 등은 4학년에 포함됩니다. + - - - - - {}} + {} + } /> - ); -} \ No newline at end of file +}; diff --git a/src/components/buddy/matching/buddyMajor2.tsx b/src/components/buddy/matching/buddyMajor2.tsx index 1ccc271..9e1a3c0 100644 --- a/src/components/buddy/matching/buddyMajor2.tsx +++ b/src/components/buddy/matching/buddyMajor2.tsx @@ -1,24 +1,27 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; +// Zustand import { BuddyStore } from "../../../store/useBuddyStore"; +// Components +import { BuddyHeader } from "../../header/buddyHeader"; +import { BuddyButton } from "../buddyButton"; +import { ConfirmButton } from "../../button/confirmButton"; + +// style +import styled from "styled-components"; import { COLORS } from "../../../theme"; -import { +import { BuddyContainer, BuddyContainer2, InfoContainer, MatchingTitle, MatchingInfo, ButtonContainer, - ApplicationContainer + ApplicationContainer, } from "../../../styles/buddy-styles"; -import { BuddyHeader } from "../../header/buddyHeader"; -import { BuddyButton } from "../buddyButton"; -import { ConfirmButton } from "../../button/confirmButton"; -import styled from "styled-components"; - export const BuddyMajor = () => { const { major, setMajor, subMajor, setSubMajor } = BuddyStore(); const [isMajor, setIsMajor] = useState(false); @@ -28,7 +31,7 @@ export const BuddyMajor = () => { // 값 유지 useEffect(() => { if (major === "SAME_COLLEGE") { - setIsCollege(true) + setIsCollege(true); } else if (major === "SAME_DEPARTMENT") { setIsMajor(true); } else if (major === "NO_MATTER") { @@ -40,43 +43,43 @@ export const BuddyMajor = () => { const majorHandler = () => { setIsMajor(!isMajor); setMajor("SAME_DEPARTMENT"); - if(isCollege || isAll) { + if (isCollege || isAll) { setIsCollege(false); setIsAll(false); } - } + }; const collegeHandler = () => { setIsCollege(!isCollege); setMajor("SAME_COLLEGE"); - if(isMajor || isAll) { + if (isMajor || isAll) { setIsMajor(false); setIsAll(false); } - } + }; const allHandler = () => { setIsAll(!isAll); setMajor("NO_MATTER"); - if(isMajor || isCollege) { + if (isMajor || isCollege) { setIsMajor(false); setIsCollege(false); } - } - + }; + const subCheckedHandler = (e: React.ChangeEvent) => { setSubMajor(e.target.checked); - } + }; console.log("학과: ", major); console.log("복수 전공: ", subMajor); - + // 다음 단계 const navigate = useNavigate(); const NextStepHandler = () => { navigate("/buddy?step=3"); - } + }; return ( - + @@ -86,18 +89,18 @@ export const BuddyMajor = () => { - - - @@ -106,19 +109,19 @@ export const BuddyMajor = () => { *복수/부전공 학과 기준으로 찾기 - TIP. 신규 학과는 단과대 단위로 선택해주세요! - {}} /> @@ -126,7 +129,7 @@ export const BuddyMajor = () => { ); -} +}; const TipText = styled.p` color: ${COLORS.font1}; @@ -158,22 +161,22 @@ const ToggleLabel = styled.label` border: 1px solid ${COLORS.line1}; position: relative; cursor: pointer; - background-color: #FFF; + background-color: #fff; &::after { - content: ''; - width: 20px; - height: 20px; - border-radius: 50%; - background-color: ${COLORS.line1}; - position: absolute; - top: 3.5px; - left: 4px; - transition: transform 0.3s; + content: ""; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: ${COLORS.line1}; + position: absolute; + top: 3.5px; + left: 4px; + transition: transform 0.3s; } ${Toggle}:checked + &::after { - background-color: ${COLORS.main}; - transform: translateX(20px); + background-color: ${COLORS.main}; + transform: translateX(20px); } -`; \ No newline at end of file +`; diff --git a/src/components/buddy/matching/buddySuccess.tsx b/src/components/buddy/matching/buddySuccess.tsx new file mode 100644 index 0000000..da4e43f --- /dev/null +++ b/src/components/buddy/matching/buddySuccess.tsx @@ -0,0 +1,204 @@ +import { useEffect, useState } from "react"; + +import { getBuddyInfo } from "../../../services/apis/buddy.service"; + +// Components +import { LoginHeader } from "../../loginHeader"; +import { BuddyStatus } from "../buddyStatus"; +import { ConfirmButton } from "../../button/confirmButton"; + +// style +import { COLORS } from "../../../theme"; +import styled from "styled-components"; +import { MatchingContainer, MatchingText } from "./buddyWaiting"; + +// Img +import left from "../../../assets/images/left-controller.svg"; +import right from "../../../assets/images/right-controller.svg"; + +interface BuddyInfoWrapperProps { + activeIndex: number; +} + +export const BuddySuccess = () => { + const [activeIndex, setActiveIndex] = useState(0); + + useEffect(() => { + //getBuddyInfo(); + }, []); + + const buddyInfoList = [ + { + name: "장유진", + department: "전자정보통신공학과", + grade: "4학년", + kakaoId: "yujinyujin", + }, + { + name: "장유진", + department: "전자정보통신공학과", + grade: "4학년", + kakaoId: "yujinyujin", + }, + { + name: "장유진", + department: "전자정보통신공학과", + grade: "4학년", + kakaoId: "yujinyujin", + }, + ]; + + const handleNext = () => { + if (activeIndex < buddyInfoList.length - 1) { + setActiveIndex(activeIndex + 1); + } + }; + + const handlePrev = () => { + if (activeIndex > 0) { + setActiveIndex(activeIndex - 1); + } + }; + return ( + + + + 버디 매칭 성공! + 버디와 연락해 보세요! + + + + + + {buddyInfoList.map((buddy, index) => ( + + {index + 1} 번째 버디 정보 + + {buddy.name} + {buddy.department} + {buddy.grade} + + 카카오톡 아이디 + {buddy.kakaoId} + + + ))} + + + + + + + *옆으로 넘겨 다른 버디를 확인해주세요. + + + + ); +}; + +const BuddyTitle = styled.h3` + color: ${COLORS.main}; + font-size: 24px; + font-weight: 700; + margin: 20px 0 4px; + line-height: 1.3; +`; +const Slider = styled.div` + display: flex; + align-items: center; +`; +const BuddyInfoWrapper = styled.div` + display: flex; + transition: transform 0.3s ease; + transform: translateX(${(props) => -props.activeIndex * 140}px); + margin-top: 16px; +`; +const BuddyInfoControllerL = styled.button` + background-image: url(${left}); + background-repeat: no-repeat; + width: 20px; + height: 200px; + padding: 0; + background-color: transparent; + background-position: center; +`; +const BuddyInfoControllerR = styled.button` + background-image: url(${right}); + background-repeat: no-repeat; + width: 20px; + height: 200px; + padding: 0; + background-color: transparent; + background-position: center; +`; +const BuddyInfoContainer = styled.div` + overflow: hidden; + display: flex; + align-items: center; + width: 280px; + gap: 50px; +`; +const BuddyInfo = styled.div` + border-radius: 16px; + border: 1px solid ${COLORS.line2}; + padding: 14px 16px; + width: 280px; + flex-shrink: 0; +`; +const InfoTitle = styled.h4` + font-size: 18px; + font-weight: 600; + color: ${COLORS.font1}; +`; +const Line = styled.div` + width: 100%; + height: 0; + border: 1px solid ${COLORS.regular}; + margin: 10px 0; +`; +const BuddyName = styled.p` + font-size: 16px; + font-weight: 600; + color: #000; + line-height: 1.5; +`; +const BuddyInfoText = styled.p` + color: ${COLORS.font2}; + font-size: 16px; + font-weight: 600; + line-height: 1.5; +`; +const BuddyKakao = styled.div` + width: 100%; + border-radius: 8px; + border: 1px solid ${COLORS.line2}; + padding: 13px 16px; + margin-top: 16px; + display: flex; + align-items: center; + justify-content: space-between; +`; +const BuddyKakaoTitle = styled.p` + font-size: 15px; + font-weight: 600; + color: #000; + line-height: 1.5; +`; +const BuddyKakaoText = styled.p` + color: ${COLORS.font2}; + font-size: 15px; + font-weight: 600; + line-height: 1.5; +`; +const Notice = styled.p` + font-size: 14px; + font-weight: 400; + color: ${COLORS.main}; + text-align: center; + margin: 8px 0 63px; +`; diff --git a/src/components/buddy/matching/buddyType3.tsx b/src/components/buddy/matching/buddyType3.tsx index 3127d81..0db64c8 100644 --- a/src/components/buddy/matching/buddyType3.tsx +++ b/src/components/buddy/matching/buddyType3.tsx @@ -1,10 +1,17 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; +// Zustand import { BuddyStore } from "../../../store/useBuddyStore"; +// Components +import { BuddyHeader } from "../../header/buddyHeader"; +import { BuddyButton } from "../buddyButton"; +import { ConfirmButton } from "../../button/confirmButton"; + +// style import { COLORS } from "../../../theme"; -import { +import { BuddyContainer, BuddyContainer2, InfoContainer, @@ -13,10 +20,6 @@ import { ButtonContainer, } from "../../../styles/buddy-styles"; -import { BuddyHeader } from "../../header/buddyHeader"; -import { BuddyButton } from "../buddyButton"; -import { ConfirmButton } from "../../button/confirmButton"; - export const BuddyType = () => { const { type, setType } = BuddyStore(); const [isSenior, setIsSenior] = useState(false); @@ -26,13 +29,13 @@ export const BuddyType = () => { useEffect(() => { if (type.includes("SENIOR")) { setIsSenior(true); - } + } if (type.includes("MATE")) { setIsPeer(true); - } + } if (type.includes("JUNIOR")) { setIsJunior(true); - } + } }, [type]); // 범위 선택 핸들러 @@ -42,64 +45,66 @@ export const BuddyType = () => { const typeArr: string[] = [...type, "SENIOR"]; setType(typeArr); } - } + }; const peerHandler = () => { setIsPeer(!isPeer); if (!type.includes("MATE")) { const typeArr: string[] = [...type, "MATE"]; setType(typeArr); } - } + }; const juniorHandler = () => { setIsJunior(!isJunior); if (!type.includes("JUNIOR")) { const typeArr: string[] = [...type, "JUNIOR"]; setType(typeArr); } - } + }; console.log("버디 종류", type); // 다음 단계 const navigate = useNavigate(); const NextStepHandler = () => { navigate("/buddy?step=4"); - } + }; return ( - + 버디 관계 선택(복수선택 가능) 입학년도(학번 앞 두자리)를 기준으로 - 선배·후배·동기 중 원하는 조건을 선택하세요! + + 선배·후배·동기 중 원하는 조건을 선택하세요! + - - - + - {}} + {}} /> ); -} \ No newline at end of file +}; diff --git a/src/components/buddy/matching/buddyWaiting.tsx b/src/components/buddy/matching/buddyWaiting.tsx new file mode 100644 index 0000000..c0ecd9c --- /dev/null +++ b/src/components/buddy/matching/buddyWaiting.tsx @@ -0,0 +1,83 @@ +import { useNavigate } from "react-router-dom"; + +import { cancelBuddy } from "../../../services/apis/buddy.service"; + +// style +import styled from "styled-components"; +import { COLORS } from "../../../theme"; + +// Components +import { LoginHeader } from "../../loginHeader"; +import { BuddyStatus } from "../buddyStatus"; +import { NoticeBox } from "../../noticeBox"; +import { ConfirmButton } from "../../button/confirmButton"; + +// Img +import buddyWaitingImg from "../../../assets/images/buddyWaiting.svg"; + +export const BuddyWaiting = () => { + const navigate = useNavigate(); + const HandleGoHome = () => { + navigate("/"); + }; + + return ( + + + + + 세종버디 신청 완료 + + {buddyCount}명의 학생들이 버디를 찾고 있어요! + + + + + 버디 신청 취소 + + ); +}; + +export const MatchingContainer = styled.div` + display: flex; + flex-direction: column; + padding: 10px 16px; + align-items: center; + height: 100vh; +`; +export const MatchingTitle = styled.h2` + font-size: 24px; + font-weight: 700; + color: ${COLORS.font1}; + margin: 20px 0 4px; +`; +export const MatchingText = styled.p` + font-size: 15px; + font-weight: 400; + color: ${COLORS.font1}; +`; +const BuddyCount = styled.b` + font-weight: 600; +`; +const MatchingImg = styled.img` + margin: 4px 0 39px; +`; +export const Cancel = styled.button` + background-color: transparent; + color: ${COLORS.font2}; + margin-top: 5px; + cursor: pointer; +`; diff --git a/src/components/modals/alertModal.tsx b/src/components/modals/alertModal.tsx deleted file mode 100644 index 39b9cf3..0000000 --- a/src/components/modals/alertModal.tsx +++ /dev/null @@ -1,5 +0,0 @@ -// import styled from "styled-components" - -// export const AlertModal = () => { -// return -// } \ No newline at end of file diff --git a/src/components/modals/confirmModal.tsx b/src/components/modals/confirmModal.tsx new file mode 100644 index 0000000..1d06007 --- /dev/null +++ b/src/components/modals/confirmModal.tsx @@ -0,0 +1,65 @@ +import styled from "styled-components"; +import { Modal } from "./modal"; +import { COLORS } from "../../theme"; + +interface ConfirmModalElements { + text: string; + subText?: string; + onClick: () => void; + onClose: () => void; +} + +const ModalContainer = styled.div` + min-width: 279px; + display: flex; + flex-direction: column; + align-items: center; +`; + +const ModalTitle = styled.h3` + color: ${COLORS.font1}; + font-size: 18px; + font-weight: 600; + margin-top: 16px; + line-height: 1.4; +`; +const ModalSubTitle = styled.p` + color: ${COLORS.font2}; + font-size: 15px; + font-weight: 400; + margin: 4px 0 20px; + line-height: 1.4; + padding: 0 22px; + text-align: center; +`; + +const ControllerContainer = styled.div` + display: flex; + align-items: center; + gap: 7px; +`; +const CancelBtn = styled.button` + padding: 10px 36px; + background-color: ${COLORS.sub4}; + color: ${COLORS.main}; + border-radius: 50px; +`; +const ConfirmBtn = styled.button` + padding: 10px 36px; + background-color: ${COLORS.main}; + color: ${COLORS.white}; + border-radius: 50px; +`; + +export const ConfirmModal = ({ text, subText, onClick, onClose }: ConfirmModalElements) => { + return + + {text} + {subText} + + 취소하기 + 거절하기 + + + ; +}; diff --git a/src/components/modals/modal.tsx b/src/components/modals/modal.tsx new file mode 100644 index 0000000..331ea6e --- /dev/null +++ b/src/components/modals/modal.tsx @@ -0,0 +1,45 @@ +import { ReactNode } from 'react'; +import ReactDOM from 'react-dom'; +import styled from 'styled-components'; +import { COLORS } from '../../theme'; + +interface ModalProps { + children: ReactNode; + onClose: () => void; +} + +const Backdrop = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(17, 17, 17, 0.3); + z-index: 999; +`; + +const ModalContent = styled.div` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: ${COLORS.white}; + padding: 16px; + z-index: 1000; + border-radius: 32px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +`; + +export const Modal = ({ children, onClose }: ModalProps) => { + const modal = document.getElementById('modal'); + + if (!modal) return null; + + return ReactDOM.createPortal( + <> + + {children} + , + modal + ); +}; diff --git a/src/components/noticeBox.tsx b/src/components/noticeBox.tsx new file mode 100644 index 0000000..22e5f80 --- /dev/null +++ b/src/components/noticeBox.tsx @@ -0,0 +1,31 @@ +import styled from "styled-components"; +import notice from "../assets/images/notice_black.svg"; +import { COLORS } from "../theme"; + +interface NoticeTextType { + text: string; +} + +export const NoticeBox = ({text}:NoticeTextType) => { + return ( + + 경고 아이콘 + {text} + + ) +}; + +const NoticeContainer = styled.div` + margin: 0 8px 16px 8px; + padding: 10px 16px; + display: flex; + gap: 12px; + border-radius: 8px; + border: 1px solid ${COLORS.regular}; +`; +const NoticeText = styled.p` + font-size: 14px; + font-weight: 400; + line-height: 1.4; + color: ${COLORS.font1}; +`; \ No newline at end of file diff --git a/src/components/progressBar.tsx b/src/components/progressBar.tsx index afc974d..fe4b64a 100644 --- a/src/components/progressBar.tsx +++ b/src/components/progressBar.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import Box from '@mui/material/Box'; import LinearProgress from '@mui/material/LinearProgress'; import styled from 'styled-components'; diff --git a/src/index.html b/src/index.html index 0318ad2..7516221 100644 --- a/src/index.html +++ b/src/index.html @@ -10,6 +10,7 @@
+ diff --git a/src/pages/buddy-page.tsx b/src/pages/buddy-page.tsx index 5dfc793..4d41112 100644 --- a/src/pages/buddy-page.tsx +++ b/src/pages/buddy-page.tsx @@ -8,41 +8,54 @@ import { BuddyMajor } from "../components/buddy/matching/buddyMajor2"; import { BuddyType } from "../components/buddy/matching/buddyType3"; import { BuddyGrade } from "../components/buddy/matching/buddyGrade4"; import { BuddyFinal } from "../components/buddy/matching/buddyFinal5"; +import { BuddyWaiting } from "../components/buddy/matching/buddyWaiting"; +import { BuddyApproval } from "../components/buddy/matching/buddyApproval"; +import { BuddySuccess } from "../components/buddy/matching/buddySuccess"; export const BuddyPage = () => { - const [searchParams] = useSearchParams(); - const step = searchParams.get("step") || "0"; + const [searchParams] = useSearchParams(); + const step = searchParams.get("step") || "0"; - useEffect(() => { - // 페이지가 로드될 때 body 배경색 변경 - document.body.style.backgroundColor = "#FAF5F5"; + useEffect(() => { + // 페이지가 로드될 때 body 배경색 변경 + if (Number(step) < 6) { + document.body.style.backgroundColor = "#FAF5F5"; + } else { + document.body.style.backgroundColor = "#FFF"; + } - // 컴포넌트가 언마운트될 때 배경색 원래대로 복구 - return () => { - document.body.style.backgroundColor = ""; - }; - }, []); + // 컴포넌트가 언마운트될 때 배경색 원래대로 복구 + return () => { + document.body.style.backgroundColor = ""; + }; + }, []); - const buddyStep = () => { - switch (step) { - case "0": - return ; - case "1": - return ; - case "2": - return ; - case "3": - return ; - case "4": - return ; - case "5": - return ; - } + const buddyStep = () => { + switch (step) { + case "0": + return ; + case "1": + return ; + case "2": + return ; + case "3": + return ; + case "4": + return ; + case "5": + return ; + case "6": + return ; + case "7": + return ; + case "8": + return ; } + } - return( -
- {buddyStep()} -
- ) + return( +
+ {buddyStep()} +
+ ) }; \ No newline at end of file diff --git a/src/services/apis/buddy.service.ts b/src/services/apis/buddy.service.ts index 84a11c9..8b0c13b 100644 --- a/src/services/apis/buddy.service.ts +++ b/src/services/apis/buddy.service.ts @@ -1,2 +1,132 @@ -// import { axiosInstance } from "../../services/axios-instance"; -// import { toast } from "sonner"; \ No newline at end of file +import { axiosInstance } from "../../services/axios-instance"; +import { AxiosResponseData } from "../../types/buddy/buddyType.ts"; +import { toast } from "sonner"; + +// 버디 신청 유저 수 확인하기 +export const getBuddyUsers = async (): Promise => { + try { + const response = await axiosInstance.get( + "/buddy/active-count" + ); + + console.log(response.data.data.count); + + const result = response.data.data.count; + return result ?? null; + } catch (error) { + console.error("이용자 수 불러오기 실패", error); + toast.error("버디 신청자 수를 불러오는데 실패했습니다."); + return null; + } +}; + +// 버디 신청 상태 확인하기 +export const getStatusBuddy = async () => { + try { + const response = await axiosInstance.get("/buddy/matching-status"); + + return response.data; + } catch (error) { + console.error(error); + toast.error("버디 신청 상태를 확인할 수 없습니다."); + return null; + } +}; + +// 버디 신청하기 +export const registerBuddy = async (buddyData: object) => { + try { + const response = await axiosInstance.post("/buddy/register", buddyData); + + if (response.status === 200) { + toast.success("버디 등록이 완료되었습니다."); + return response.data; + } else { + toast.error("버디 등록에 실패했습니다."); + return null; + } + } catch (error) { + console.error("버디 등록에 실패했습니다.", error); + toast.error("버디 등록에 실패했습니다."); + return null; + } +}; + +// 버디 신청 취소하기 +export const cancelBuddy = async () => { + try { + const response = await axiosInstance.get("/buddy/cancel"); + + if (response.status === 200) { + toast.success("버디 신청 취소가 완료되었습니다."); + return true; + } else { + toast.error("버디 신청 취소에 실패했습니다."); + } + } catch (error) { + console.error(error); + toast.error("버디 신청 취소에 실패했습니다."); + return null; + } +}; + +// 버디 수락/거절 창에서의 상대방 정보 요청 +export const getBuddyInfoFirst = async () => { + try { + const response = await axiosInstance.get("/buddy/partner/details"); + + console.log(response.data); + return response.data; + } catch (error) { + console.error(error); + toast.error("버디 정보를 가져오지 못했습니다."); + return null; + } +}; + +// 버디 매칭 수락/거절 +export const approvalBuddy = async (approvalData: string) => { + const approval = { + isAccept: approvalData, + }; + try { + const response = await axiosInstance.post( + "/buddy/matching/status", + approval + ); + + if (response.status === 200) { + if (approvalData) { + toast.success( + "매칭이 수락되었습니다! \n상대방이 매칭을 수락할 때까지 기다려주세요." + ); + } else { + toast.success( + "매칭이 거절되었습니다. \n거절 패널티로 1시간동안 매칭을 등록할 수 없습니다." + ); + } + return true; + } else { + toast.error("오류가 발생했습니다."); + return null; + } + } catch (error) { + console.error(error); + toast.error("버디 정보를 가져오지 못했습니다."); + return null; + } +}; + +// 매칭 수락 완료 후 상대방 정보요청 +export const getBuddyInfo = async () => { + try { + const response = await axiosInstance.get("/buddy/matched-partner/details"); + + console.log(response.data); + return response.data; + } catch (error) { + console.error(error); + toast.error("버디 정보를 가져오지 못했습니다."); + return null; + } +}; diff --git a/src/store/useBuddyStore.tsx b/src/store/useBuddyStore.tsx index b7fde23..39efb52 100644 --- a/src/store/useBuddyStore.tsx +++ b/src/store/useBuddyStore.tsx @@ -1,19 +1,7 @@ import { create } from "zustand"; +import { SetBuddyMatchingType } from "../types/buddy/buddyType"; -interface Buddy { - gender: string; - major: string; - subMajor: boolean; - type: string[]; - grade: string[]; - setGender: (gender: string) => void; - setMajor: (major: string) => void; - setSubMajor: (subMajor: boolean) => void; - setType: (type: string[]) => void; - setGrade: (grade: string[]) => void; -} - -export const BuddyStore = create(set => ({ +export const BuddyStore = create(set => ({ gender: '', major: '', subMajor: false, diff --git a/src/theme.ts b/src/theme.ts index 101afc8..d6aa0d2 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -4,6 +4,7 @@ export const COLORS = { sub1: "#D31D1D", sub2: "#FFE9E9", sub3: "#FAF4F4", + sub4: "#FCECEE", sub: "#FF7474", back1: "#FAFAFA", back2: "#FFFFFF", diff --git a/src/types/buddy/buddyType.ts b/src/types/buddy/buddyType.ts new file mode 100644 index 0000000..a3c7349 --- /dev/null +++ b/src/types/buddy/buddyType.ts @@ -0,0 +1,27 @@ +// 버디 신청자 수 타입 +interface BuddyCount { + count: number; +} + +// 버디 신청자 수 axios 응답 타입 +export interface AxiosResponseData { + data: BuddyCount; +} + +// 버디 신청 타입 +export interface BuddyMatchingType { + gender: string; + major: string; + subMajor: boolean; + type: string[]; + grade: string[]; +} + +// set 버디 타입 +export interface SetBuddyMatchingType extends BuddyMatchingType { + setGender: (gender: string) => void; + setMajor: (major: string) => void; + setSubMajor: (subMajor: boolean) => void; + setType: (type: string[]) => void; + setGrade: (grade: string[]) => void; +}