-
{formatTime()}
+ {!isEmailCodeVerified && (
+
{formatTime(emailTimer)}
+ )}
확인
@@ -168,8 +431,17 @@ export default function MyProfile() {
)}
- {isCodeVerified &&
다음}
+ {name !== "" || email !== "" || phoneNumber !== "" || !isEditing ? (
+
+ {isEditing ? "저장하기" : "수정하기"}
+
+ ) : (
+ <>>
+ )}
);
}
diff --git a/src/pages/mypage/timeselect/timeselect.styles.tsx b/src/pages/mypage/timeselect/timeselect.styles.tsx
index 2c0935d..650b239 100644
--- a/src/pages/mypage/timeselect/timeselect.styles.tsx
+++ b/src/pages/mypage/timeselect/timeselect.styles.tsx
@@ -113,6 +113,35 @@ export const StyledCalendarWrapper = styled.div`
border-radius: 5rem;
}
}
+ /* 날짜만 선택되고 시간대는 선택되지 않은 경우 */
+ .date-selected-no-time abbr {
+ display: flex;
+ width: 3.6rem;
+ height: 3.6rem;
+ align-items: center;
+ justify-content: center;
+ margin: auto;
+ font-weight: 600;
+ border: 1px solid black;
+ border-radius: 5rem;
+ color: black !important;
+ background-color: transparent !important;
+ }
+
+ /* 시간대가 선택된 날짜 */
+ .date-selected-with-time abbr {
+ display: flex;
+ width: 3.6rem;
+ height: 3.6rem;
+ align-items: center;
+ justify-content: center;
+ margin: auto;
+ font-weight: 600;
+ border-radius: 5rem;
+ color: white !important;
+ background-color: black !important;
+ }
+
`;
// 캘린더를 불러옴
@@ -134,8 +163,23 @@ export const Title = styled.h1`
`;
export const Subtitle = styled.p`
- font-size: 1.6rem;
- color: #aeaeb2;
+ font-size: 1.8rem;
+ color: #5e5e5e;
+ margin: 0 0 -0.5rem 0.5rem;
+`;
+
+export const CircleSubtitle = styled.p`
+ font-size: 1.9rem;
+ font-weight: 500;
+ color: white;
+ background-color: black;
+ width: 6.5rem;
+ height: 6.5rem;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
margin-bottom: 1rem;
`;
@@ -176,10 +220,6 @@ export const ButtonContainer = styled.div`
column-gap: 1.5rem;
margin-top: 1rem;
margin-bottom: 2rem;
-
- & > :nth-last-child(1):nth-child(odd) {
- grid-column: span 2; // 마지막 요소가 가득 차도록 설정
- }
`;
export const TimeButton = styled.button<{ isSelected?: boolean }>`
@@ -261,4 +301,10 @@ export const NavFirst = styled(Link)`
color: #AEAEB2;
text-decoration: underline;
cursor: pointer;
-`;
\ No newline at end of file
+`;
+
+export const NoDatesMessage = styled.p`
+ width: 100%;
+ font-size: 1.5rem;
+ font-weight: 500;
+`
\ No newline at end of file
diff --git a/src/pages/mypage/timeselect/timeselect.tsx b/src/pages/mypage/timeselect/timeselect.tsx
index f7463da..28d853a 100644
--- a/src/pages/mypage/timeselect/timeselect.tsx
+++ b/src/pages/mypage/timeselect/timeselect.tsx
@@ -1,11 +1,4 @@
-import { useState } from "react";
-import {
- clubState,
- nameState,
- partState,
- userTypeState,
-} from "../../../atoms/authState";
-import { useRecoilValue } from "recoil";
+import { useEffect, useMemo, useState } from "react";
import * as S from "./timeselect.styles";
import {
Container,
@@ -16,10 +9,28 @@ import {
} from "../../../components/global.styles";
import BackButton from "../../../components/button/backButton";
import moment from "moment";
-import { useNavigate } from "react-router-dom";
+import axiosInstance from "../../../apis/axiosInstance";
+
+interface PossibleDate {
+ possible_date_id: number;
+ date: string;
+ start_time: string;
+ end_time: string;
+}
-type ValuePiece = Date | null;
-type Value = ValuePiece | [ValuePiece, ValuePiece];
+interface UpdatedDate {
+ date: string;
+ start_time: string;
+ end_time: string;
+}
+
+interface TimeSlot {
+ start_time: string;
+ end_time: string;
+}
+
+type PossibleDatesData = PossibleDate[];
+type UpdatedDatesData = UpdatedDate[];
export default function TimeSelect() {
const today = new Date();
@@ -28,93 +39,176 @@ export default function TimeSelect() {
today.getMonth(),
today.getDate()
);
- const [dates, setDates] = useState
([todayWithoutTime]);
- const [selectedDates, setSelectedDates] = useState([
- todayWithoutTime,
- ]);
const maxDate = moment(todayWithoutTime).add(13, "days").toDate();
- const attendDay = ["2023-12-03", "2023-12-13"]; // 예시
- const navigate = useNavigate();
+ const [possibleDates, setPossibleDates] = useState([]);
+ const [isEditing, setIsEditing] = useState(false);
+ const [mentorId, setMentorId] = useState("");
+ const [timesPerDate, setTimesPerDate] = useState<{
+ [key: string]: TimeSlot[];
+ }>({});
+ const [currentSelectedDate, setCurrentSelectedDate] = useState(null);
- const [timesPerDate, setTimesPerDate] = useState<{ [key: string]: string[] }>(
- {}
- );
+ const fetchPossibleDates = async () => {
+ try {
+ const response = await axiosInstance.get(`/possibleDates/${mentorId}`);
+ console.log("possibleDates get: ", response.data.content);
+ setPossibleDates(response.data.content || []);
+ } catch (error) {
+ console.error("멘토아이디 조회 실패: ", error);
+ alert("데이터를 가져오는 데 실패했습니다.");
+ setPossibleDates([]);
+ }
+ };
+
+ useEffect(() => {
+ axiosInstance
+ .get(`/users`)
+ .then((response) => {
+ console.log(response.data.content);
+ setMentorId(response.data.content.mentorId);
+ })
+ .catch((error) => {
+ console.error("Failed to fetch user data:", error);
+ });
+ }, []);
+
+ useEffect(() => {
+ if (mentorId) {
+ fetchPossibleDates();
+ }
+ }, [mentorId]);
const handleDateChange = (value: Date) => {
const dateString = moment(value).format("YYYY-MM-DD");
- if (selectedDates.some((date) => date.getTime() === value.getTime())) {
- // 이미 선택된 날짜라면 제거
- setSelectedDates(
- selectedDates.filter((date) => date.getTime() !== value.getTime())
- );
- // 해당 날짜의 시간 정보도 함께 제거
- const updatedTimes = { ...timesPerDate };
- delete updatedTimes[dateString];
- setTimesPerDate(updatedTimes);
- } else {
- // 선택되지 않은 날짜라면 추가
- setSelectedDates([...selectedDates, value]);
- // 날짜를 추가할 때 해당 날짜의 시간 배열 초기화
+ // 현재 클릭된 날짜를 업데이트
+ setCurrentSelectedDate(value);
+ };
+
+ const handleTimeClick = (date: Date, option: string) => {
+ const dateString = moment(date).format("YYYY-MM-DD");
+ const [start_time, end_time] = option.split(" ~ ").map((s) => s.trim());
+
+ if (!timesPerDate[dateString]) {
setTimesPerDate({
...timesPerDate,
- [dateString]: [],
+ [dateString]: [{ start_time, end_time }],
});
+ return;
}
- };
- const handleTimeClick = (date: Date, option: string) => {
- const dateString = moment(date).format("YYYY-MM-DD");
+ const existingIndex = timesPerDate[dateString].findIndex(
+ (slot) => slot.start_time === start_time && slot.end_time === end_time
+ );
- if (timesPerDate[dateString]?.includes(option)) {
+ if (existingIndex !== -1) {
// 이미 선택된 시간대라면 배열에서 제거
- const updatedTimes = timesPerDate[dateString].filter(
- (time) => time !== option
- );
+ const updatedSlots = [...timesPerDate[dateString]];
+ updatedSlots.splice(existingIndex, 1);
- // 시간대 배열이 비어 있으면 해당 날짜도 삭제
- if (updatedTimes.length === 0) {
- setSelectedDates(
- selectedDates.filter((d) => d.getTime() !== date.getTime())
- );
+ if (updatedSlots.length === 0) {
+ // 시간대 배열이 비어 있으면 해당 날짜도 삭제
const updatedTimesPerDate = { ...timesPerDate };
delete updatedTimesPerDate[dateString];
setTimesPerDate(updatedTimesPerDate);
} else {
setTimesPerDate({
...timesPerDate,
- [dateString]: updatedTimes,
+ [dateString]: updatedSlots,
});
}
} else {
// 선택되지 않은 시간대라면 배열에 추가
setTimesPerDate({
...timesPerDate,
- [dateString]: [...(timesPerDate[dateString] || []), option],
+ [dateString]: [...timesPerDate[dateString], { start_time, end_time }],
});
}
};
+ console.log("timesPerDate: ", timesPerDate);
const optionsToDisplay = [
- "10: 00 ~ 11: 00",
- "11: 00 ~ 12: 00",
- "12: 00 ~ 13: 00",
- "13: 00 ~ 14: 00",
- "14: 00 ~ 15: 00",
- "15: 00 ~ 16: 00",
- "16: 00 ~ 17: 00",
- "17: 00 ~ 18: 00",
- "18: 00 ~ 19: 00",
- "19: 00 ~ 20: 00",
- "20: 00 ~ 21: 00",
- "21: 00 ~ 22: 00",
+ "10:00 ~ 11:00",
+ "11:00 ~ 12:00",
+ "12:00 ~ 13:00",
+ "13:00 ~ 14:00",
+ "14:00 ~ 15:00",
+ "15:00 ~ 16:00",
+ "16:00 ~ 17:00",
+ "17:00 ~ 18:00",
+ "18:00 ~ 19:00",
+ "19:00 ~ 20:00",
+ "20:00 ~ 21:00",
+ "21:00 ~ 22:00",
];
- // 날짜를 오름차순으로 정렬
- const sortedDates = selectedDates.sort((a, b) => a.getTime() - b.getTime());
+ // possibleDates를 날짜별로 그룹화하고, 각 그룹 내의 시간대를 정렬
+ const groupedPossibleDates = useMemo(() => {
+ if (!possibleDates || possibleDates.length === 0) {
+ return { grouped: {}, sortedDates: [] };
+ }
+
+ // 그룹화
+ const grouped = possibleDates.reduce((acc, curr) => {
+ if (!acc[curr.date]) {
+ acc[curr.date] = [];
+ }
+ acc[curr.date].push(curr);
+ return acc;
+ }, {} as { [key: string]: PossibleDate[] });
+
+ // 날짜 오름차순 정렬
+ const sortedDates = Object.keys(grouped).sort(
+ (a, b) => new Date(a).getTime() - new Date(b).getTime()
+ );
+
+ // 각 날짜 내의 시간대 정렬 (오름차순)
+ sortedDates.forEach((date) => {
+ grouped[date].sort((a, b) => a.start_time.localeCompare(b.start_time));
+ });
+
+ return { grouped, sortedDates };
+ }, [possibleDates]);
+
+ const handleEditBtn = async () => {
+ if (!possibleDates) {
+ alert("사용자 데이터를 불러오는 중입니다.");
+ return;
+ }
- const handleNextButton = () => {
- navigate("/applyCogoMemo");
+ // API 요청에 사용할 데이터
+ const updatedDatesData: UpdatedDatesData = [];
+
+ Object.keys(timesPerDate).forEach((dateStr) => {
+ timesPerDate[dateStr].forEach((slot) => {
+ updatedDatesData.push({
+ date: dateStr,
+ start_time: slot.start_time,
+ end_time: slot.end_time,
+ });
+ });
+ });
+ console.log("updatedDatesData", updatedDatesData);
+
+ try {
+ const response = await axiosInstance.post(
+ `/possibleDates`,
+ updatedDatesData
+ );
+ console.log("사용자 정보 업데이트 성공:", response.data);
+ setIsEditing(false);
+ alert("정보가 성공적으로 저장되었습니다.");
+
+ // 최신 데이터를 다시 가져옵니다.
+ await fetchPossibleDates();
+ } catch (error) {
+ console.error("사용자 정보 업데이트 실패:", error);
+ alert("정보를 저장하는 데 실패했습니다.");
+ }
+ };
+
+ const toggleEditMode = () => {
+ setIsEditing(!isEditing);
};
return (
@@ -125,61 +219,103 @@ export default function TimeSelect() {
COGO 시간
COGO를 진행하기 편한 시간 대를 알려주세요.
-
-
- view === "month" && (date < todayWithoutTime || date > maxDate)
- }
- tileClassName={({ date, view }) => {
- if (
- view === "month" &&
- selectedDates.some((d) => d.getTime() === date.getTime())
- ) {
- return "selected-date"; // 선택된 날짜에 스타일을 적용
- }
- return null;
- }}
- formatDay={(locale, date) => moment(date).format("D")} // 일 제거 숫자만 보이게
- formatYear={(locale, date) => moment(date).format("YYYY")} // 네비게이션 눌렀을때 숫자 년도만 보이게
- formatMonthYear={(locale, date) => moment(date).format("YYYY. MM")} // 네비게이션에서 2023. 12 이렇게 보이도록 설정
- calendarType="gregory" // 일요일 부터 시작
- showNeighboringMonth={false} // 전달, 다음달 날짜 숨기기
- next2Label={null} // +1년 & +10년 이동 버튼 숨기기
- prev2Label={null} // -1년 & -10년 이동 버튼 숨기기
- minDetail="month" // 10년단위 년도 숨기기
- />
-
-
- {sortedDates.map((date) => {
- const dateString = moment(date).format("YYYY-MM-DD");
- return (
-
- {dateString}
-
- {optionsToDisplay.map((option) => (
- handleTimeClick(date, option)}
- >
- {option}
-
- ))}
-
-
- );
- })}
-
+ {isEditing ? (
+ <>
+
+
+ view === "month" &&
+ (date < todayWithoutTime || date > maxDate)
+ }
+ tileClassName={({ date, view }) => {
+ if (view === "month") {
+ const dateTime = date.getTime();
+ const dateString = moment(date).format("YYYY-MM-DD");
+
+ if (timesPerDate[dateString] && timesPerDate[dateString].length > 0) {
+ // 시간대가 선택된 날짜
+ return "date-selected-with-time";
+ } else if (currentSelectedDate && currentSelectedDate.getTime() === dateTime) {
+ // 현재 클릭된 날짜 (시간대 선택 없음)
+ return "date-selected-no-time";
+ }
+ }
+ return null;
+ }}
+ formatDay={(locale, date) => moment(date).format("D")} // 일 제거 숫자만 보이게
+ formatYear={(locale, date) => moment(date).format("YYYY")} // 네비게이션 눌렀을때 숫자 년도만 보이게
+ formatMonthYear={(locale, date) =>
+ moment(date).format("YYYY. MM")
+ } // 네비게이션에서 2023. 12 이렇게 보이도록 설정
+ calendarType="gregory" // 일요일 부터 시작
+ showNeighboringMonth={false} // 전달, 다음달 날짜 숨기기
+ next2Label={null} // +1년 & +10년 이동 버튼 숨기기
+ prev2Label={null} // -1년 & -10년 이동 버튼 숨기기
+ minDetail="month" // 10년단위 년도 숨기기
+ />
+
+ {currentSelectedDate && (
+
+
+ {moment(currentSelectedDate).format("YYYY-MM-DD")}
+
+ {optionsToDisplay.map((option) => (
+
+ `${slot.start_time} ~ ${slot.end_time}` ===
+ option
+ ) || false
+ }
+ onClick={() => handleTimeClick(currentSelectedDate, option)}
+ >
+ {option}
+
+ ))}
+
+
+
+ )}
+ >
+ ) : (
+ <>
+ {possibleDates.length > 0 ? (
+
+ {groupedPossibleDates.sortedDates.map(date => (
+
+ {moment(date).format("M/D")}
+
+ {groupedPossibleDates.grouped[date].map(slot => (
+
+ {`${slot.start_time.slice(0, 5)} ~ ${slot.end_time.slice(0, 5)}`}
+
+ ))}
+
+
+ ))}
+
+ ) : (
+
+ 코고를 진행 가능한 날짜가 없습니다.
+
+ )}
+ >
+ )}
- 다음
+ {isEditing ? "저장하기" : "수정하기"}
);
diff --git a/src/pages/search/search.styles.tsx b/src/pages/search/search.styles.tsx
index e49df0a..283239d 100644
--- a/src/pages/search/search.styles.tsx
+++ b/src/pages/search/search.styles.tsx
@@ -116,9 +116,39 @@ export const HeaderTitleContainer = styled.div`
`;
export const BodyContainer = styled.div`
+ width: 100%;
+ height: 90%;
display: flex;
flex-direction: column;
+ gap: 1.75rem;
+ margin: -1.5rem 0 0 0;
+ padding: 1rem 0.75rem 1rem 0.75rem;
+ overflow-y: auto;
+ /* 웹킷 기반 브라우저 */
+ ::-webkit-scrollbar {
+ width: 8px;
+ }
+ ::-webkit-scrollbar-track {
+ background-color: #f1f1f1;
+ }
+ ::-webkit-scrollbar-thumb {
+ background-color: #888;
+ border-radius: 5rem;
+ }
+ ::-webkit-scrollbar-thumb:hover {
+ background-color: #555;
+ }
+ /* 파이어폭스 */
+ scrollbar-width: thin;
+ scrollbar-color: #888 #f1f1f1;
+`;
+
+export const LoadingContainer = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
width: 100%;
+ height: 90%;
`;
export const ProfileCardContainer = styled.div`
diff --git a/src/pages/search/search.tsx b/src/pages/search/search.tsx
index ba680ea..85ba7f4 100644
--- a/src/pages/search/search.tsx
+++ b/src/pages/search/search.tsx
@@ -1,18 +1,11 @@
-import React, { useEffect, useState } from "react";
+import { useEffect } from "react";
import * as S from "./search.styles";
import { useNavigate } from "react-router-dom";
import { useRecoilState } from "recoil";
-import LeftButton from "../../assets/LeftButton.svg";
-import RightButton from "../../assets/RightButton.svg";
-import axios from "axios";
-import axiosInstance from "../../apis/axiosInstance";
import {
Container,
Header,
- Subtitle,
- Title,
} from "../../components/global.styles";
-import Logo from "../../assets/Logo.svg";
import SearchIcon from "../../assets/Search.svg";
import BackButton from "../../components/button/backButton";
import TagDelete from "../../assets/TagDelete.svg";
@@ -23,7 +16,6 @@ export default function Search() {
const [part, setPart] = useRecoilState(partSearchState);
const [club, setClub] = useRecoilState(clubSearchState);
- // 페이지가 로드될 때 localStorage에서 part와 club 값을 불러옴
useEffect(() => {
setPart("");
setClub("");
@@ -33,7 +25,14 @@ export default function Search() {
if (club === "" && part === "") {
alert("검색할 파트나 동아리를 선택해주세요.");
} else {
- navigate("/search/searchview");
+ const params = new URLSearchParams();
+ if (part !== "") {
+ params.append("part", part);
+ }
+ if (club !== "") {
+ params.append("club", club);
+ }
+ navigate(`/search/searchview?${params.toString()}`);
}
};
@@ -45,6 +44,14 @@ export default function Search() {
setClub("");
};
+ const togglePart = (option: string) => {
+ setPart(part === option ? "" : option);
+ };
+
+ const toggleClub = (option: string) => {
+ setClub(club === option ? "" : option);
+ };
+
return (
@@ -71,14 +78,14 @@ export default function Search() {
/>
-
+
파트
- {["FE", "BE", "기획", "디자인"].map((option) => (
+ {["FE", "BE", "PM", "DESIGN"].map((option) => (
setPart(option)}
+ onClick={() => togglePart(option)}
>
{option}
@@ -90,7 +97,7 @@ export default function Search() {
setClub(option)}
+ onClick={() => toggleClub(option)}
>
{option}
diff --git a/src/pages/search/searchView.tsx b/src/pages/search/searchView.tsx
index 952d840..04eaedc 100644
--- a/src/pages/search/searchView.tsx
+++ b/src/pages/search/searchView.tsx
@@ -1,21 +1,16 @@
-import React, { useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import * as S from "./search.styles";
-import { useNavigate } from "react-router-dom";
-import { authState, clubSearchState, partSearchState } from "../../atoms/authState";
-import { useRecoilState, useRecoilValue } from "recoil";
-import LeftButton from "../../assets/LeftButton.svg";
-import RightButton from "../../assets/RightButton.svg";
-import axios from "axios";
+import { useLocation, useNavigate } from "react-router-dom";
import axiosInstance from "../../apis/axiosInstance";
import {
Container,
Header,
- Subtitle,
- Title,
} from "../../components/global.styles";
-import Logo from "../../assets/Logo.svg";
import SearchIcon from "../../assets/Search.svg";
import BackButton from "../../components/button/backButton";
+import MentorCard from "../../components/mentorCard/mentorCard";
+import { MentorData } from "../../types/mentorData";
+import Loading from "../../components/loading/loading";
type MentorCategory = {
기획: "PM";
@@ -31,116 +26,61 @@ const mentorCategory: MentorCategory = {
BE: "BE",
};
-interface MentorData {
- picture: string;
- mentorName: string;
- clubName: string[];
- field: string;
- username: string;
-}
-
-const saveTokenToLocalStorage = (token: string) => {
- localStorage.setItem("token", token);
-};
-
-const getTokenFromLocalStorage = () => {
- return localStorage.getItem("token");
-};
-
-const saveCategoryToLocalStorage = (category: keyof MentorCategory) => {
- localStorage.setItem("activeCategory", category);
-};
-
-const getCategoryFromLocalStorage = () => {
- return localStorage.getItem("activeCategory") as keyof MentorCategory;
-};
-
export default function SearchView() {
- const [activeButtons, setActiveButtons] = useState(
- getCategoryFromLocalStorage() || "기획"
- );
- const [mentorData, setMentorData] = useState(null);
+ const [mentorData, setMentorData] = useState([]);
+ const [selectedMentorData, setSelectedMentorData] =
+ useState(null);
const navigate = useNavigate();
- const [currentIndex, setCurrentIndex] = useState(0);
- const part = useRecoilValue(partSearchState);
- const club = useRecoilValue(clubSearchState);
-
- const getMentorData = () => {
- const url = `/mentors/part?part=${mentorCategory[activeButtons]}`;
-
- axiosInstance
- .get(url)
- .then((response) => {
- setMentorData(response.data);
- console.log(response.data);
- console.log("토큰 아직 유효해용~");
- })
- .catch(async (error) => {
- console.error("Error:", error);
- // error.response가 undefined가 아닌지 확인
- if (error.response) {
- console.log("리이슈 요청 보내야 해용~");
- const originalRequest = error.config;
-
- // 액세스 토큰 만료 확인
- if (error.response.status === 401 && !originalRequest._retry) {
- originalRequest._retry = true;
-
- try {
- // 리이슈 요청
- const response = await axios.post(
- "https://cogo.life/reissue",
- {},
- {
- headers: {
- "Content-Type": "application/json",
- },
- withCredentials: true,
- }
- );
-
- // 새로운 액세스 토큰 저장
- const newToken = response.headers.access;
- saveTokenToLocalStorage(newToken);
- if (newToken) {
- console.log("리이슈 성공이용~", newToken);
- }
-
- // 원래 요청의 헤더 업데이트
- originalRequest.headers["Authorization"] = `Bearer ${newToken}`;
-
- // 원래 요청 재시도
- return axiosInstance(originalRequest);
- } catch (reissueError) {
- // 리이슈 요청 실패 시
- console.error("Failed to reissue access token:", reissueError);
- return Promise.reject(reissueError);
- }
-
- // if (error.response && error.response.status === 401) {
- // localStorage.removeItem("isLoggedIn");
- // navigate("/login");
- } else {
- console.log("An unexpected error occurred");
- }
- } else {
- // error.response가 없는 경우
- console.log("Network or unexpected error occurred");
- }
- });
- };
+ // const [partState, setPartState] = useRecoilValue(partSearchState);
+ // const [clubState, setClubState] = useRecoilValue(clubSearchState);
+ const location = useLocation();
+ const params = new URLSearchParams(location.search);
+ const part = params.get("part") || "";
+ const club = params.get("club") || "";
+ const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
- getMentorData();
- saveCategoryToLocalStorage(activeButtons);
- }, [activeButtons]);
+ // setPartState(part);
+ // setClubState(club);
+
+ const fetchMentors = async () => {
+ setIsLoading(true);
+ try {
+ let endpoint = "/mentors"; // 기본 엔드포인트
+ const params: Record = {};
+
+ if (part && club) {
+ endpoint = "/mentors/part/club";
+ params.part = part;
+ params.club = club;
+ } else if (part) {
+ endpoint = "/mentors/part";
+ params.part = part;
+ } else if (club) {
+ endpoint = "/mentors/club";
+ params.club = club;
+ }
- const handleButtonClick = (buttonName: keyof MentorCategory) => {
- setActiveButtons(buttonName);
+ const response = await axiosInstance.get(endpoint, { params });
+ setMentorData(response.data.content);
+ } catch (error) {
+ console.error("Failed to fetch mentor data:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchMentors();
+ }, [part, club]);
+
+ const handleProfileSelect = (mentor: MentorData) => {
+ setSelectedMentorData(mentor);
+ console.log("selectedData: ", selectedMentorData);
+ navigate(`/mentor-detail/${mentor.mentorId}`);
};
- const handleProfileClick = () => {
- navigate("/mentorprofile");
+ const handleSearchBarClick = () => {
+ navigate(`/search`);
};
return (
@@ -164,37 +104,21 @@ export default function SearchView() {
-
- {Array.from({ length: 3 }).map((_, index) => (
-
- GDSC
- 나는 교휘
-
-
- 아픈건 딱 질색이니까
-
- 오늘도 아침엔 입에 빵을 물고 똑같이 하루를 시작하고 온종일
- 한 손엔 아이스 아메리카노 피곤해 죽겠네
-
-
-
-
-
-
-
- {activeButtons}
- 직무직무
- 경력
-
-
- ))}
-
+ {isLoading ? (
+
+
+
+ ) : mentorData.length > 0 ? (
+ mentorData.map((mentor) => (
+ handleProfileSelect(mentor)}
+ />
+ ))
+ ) : (
+ 검색 결과가 없습니다.
+ )}
);
diff --git a/src/pages/signup/signup.styles.tsx b/src/pages/signup/signup.styles.tsx
index e669455..33f425b 100644
--- a/src/pages/signup/signup.styles.tsx
+++ b/src/pages/signup/signup.styles.tsx
@@ -65,6 +65,12 @@ export const Input = styled.input`
}
`;
+export const DisplayText = styled.div`
+ font-size: 2.2rem;
+ max-width: 24rem;
+ border-radius: 0;
+`;
+
export const NameInput = styled.input`
padding: 1rem 0;
width: 100%;
@@ -94,13 +100,18 @@ export const CertTime = styled.span`
`;
export const Button = styled.button`
+ position: absolute;
+ z-index: 10;
+ bottom: 12rem;
+ left: 50%;
+ transform: translate(-50%);
padding: 1.6rem;
background-color: #ededed;
color: black;
font-size: 2.2rem;
font-weight: 500;
- width: 50%;
- margin: 3rem auto;
+ width: 43%;
+ margin: 0 auto;
`;
export const CheckboxContainer = styled.div`
@@ -160,3 +171,10 @@ export const FireImg = styled.img`
height: auto;
margin: 8rem auto;
`;
+
+export const ErrorText = styled.div`
+ width: 100%;
+ color: red;
+ font-size: 1.5rem;
+ margin-top: -1rem;
+`;
\ No newline at end of file
diff --git a/src/pages/signup/signup.tsx b/src/pages/signup/signup.tsx
index 3944f5a..8178062 100644
--- a/src/pages/signup/signup.tsx
+++ b/src/pages/signup/signup.tsx
@@ -75,14 +75,15 @@ export default function SignUp() {
/>
);
case "part":
+ const nextStepAfterPart = userOption === "멘토" ? "club" : "complete";
return (
);
diff --git a/src/pages/signup/signup_complete.tsx b/src/pages/signup/signup_complete.tsx
index fad7d64..cfc08a4 100644
--- a/src/pages/signup/signup_complete.tsx
+++ b/src/pages/signup/signup_complete.tsx
@@ -10,19 +10,47 @@ import * as S from "./signup.styles";
import { useNavigate } from "react-router-dom";
import StartCogoFire from "../../assets/StartCogoFire.svg";
import { FullButton } from "../../components/global.styles";
+import axiosInstance from "../../apis/axiosInstance";
export default function CompleteStep() {
const name = useRecoilValue(nameState);
- const phonenum = useRecoilValue(phoneNumberState);
+ // const phonenum = useRecoilValue(phoneNumberState);
const userOption = useRecoilValue(userTypeState);
const part = useRecoilValue(partState);
const club = useRecoilValue(clubState);
const navigate = useNavigate();
const handleClear = () => {
- navigate("/");
+ completeSignup(part, club);
+ localStorage.setItem("isLoggedIn", "false");
+ localStorage.setItem("token", "");
+ navigate("/login");
};
+ const completeSignup = async (part: string, club: string) => {
+ const mentorData = {
+ part: part,
+ club: club,
+ };
+ const menteeData = {
+ part: part,
+ };
+
+ try {
+ if (userOption === "멘토") {
+ const response = await axiosInstance.post("/users/mentor", mentorData);
+ console.log(response.data);
+ } else if (userOption === "멘티") {
+ const response = await axiosInstance.post("/users/mentee", menteeData);
+ console.log(response.data);
+ }
+ alert("회원가입이 완료되었습니다.");
+ } catch (error) {
+ console.error("회원가입 실패: ", error);
+ alert("회원가입에 실패하셨습니다. 다시 처음부터 회원가입해주세요.");
+ // navigate("/login");
+ }
+ };
return (
<>
diff --git a/src/pages/signup/signup_name.tsx b/src/pages/signup/signup_name.tsx
index b95fba2..f3a95b6 100644
--- a/src/pages/signup/signup_name.tsx
+++ b/src/pages/signup/signup_name.tsx
@@ -1,6 +1,7 @@
-import { nameState } from "../../atoms/authState";
+import { nameState, phoneNumberState } from "../../atoms/authState";
import { useRecoilState } from "recoil";
import * as S from "./signup.styles";
+import axiosInstance from "../../apis/axiosInstance";
interface NameStepProps {
goToStep: (step: string) => void;
@@ -10,16 +11,29 @@ interface NameStepProps {
export default function NameStep({ goToStep }: NameStepProps) {
const [name, setName] = useRecoilState(nameState);
+ const [phoneNumber, setPhoneNumber] = useRecoilState(phoneNumberState);
const handleInputChange = (e: React.ChangeEvent) => {
setName(e.target.value);
};
const handleClear = () => {
- if (name === "") {
- alert("성함을 작성해 주세요.");
- } else {
- goToStep("usertype");
+ sendUserData(name, phoneNumber);
+ goToStep("usertype");
+ };
+
+ const sendUserData = async (name: string, phoneNumber: string) => {
+ const userData = {
+ phoneNum: phoneNumber,
+ name: name,
+ };
+
+ try {
+ const response = await axiosInstance.post("/users", userData);
+ console.log(response.data.content);
+ } catch (error) {
+ console.error("전화번호 인증 실패: ", error);
+ alert("전화번호를 다시 입력해주세요.");
}
};
diff --git a/src/pages/signup/signup_phoneNum.tsx b/src/pages/signup/signup_phoneNum.tsx
index 458f67a..ee450ae 100644
--- a/src/pages/signup/signup_phoneNum.tsx
+++ b/src/pages/signup/signup_phoneNum.tsx
@@ -13,8 +13,8 @@ interface PhoneNumberStepProps {
export default function PhoneNumStep({ goToStep }: PhoneNumberStepProps) {
const [phoneNumber, setPhoneNumber] = useRecoilState(phoneNumberState);
const [verificationCode, setVerificationCode] = useState("");
- const [isVerificationSent, setIsVerificationSent] = useState(false);
const [code, setCode] = useState("");
+ const [isVerificationSent, setIsVerificationSent] = useState(false);
const [isCodeVerified, setIsCodeVerified] = useState(false);
const [timer, setTimer] = useState(180);
@@ -25,11 +25,11 @@ export default function PhoneNumStep({ goToStep }: PhoneNumberStepProps) {
}
};
- const handleVerificationCodeChange = (
+ const handleCodeChange = (
e: React.ChangeEvent
) => {
if (e.target.value.length <= 4) {
- setVerificationCode(e.target.value);
+ setCode(e.target.value);
}
};
@@ -49,7 +49,6 @@ export default function PhoneNumStep({ goToStep }: PhoneNumberStepProps) {
// 인증번호 전송 함수
const sendVerification = async (phoneNumber: string) => {
if (phoneNumber.length < 10) return;
- setIsVerificationSent(true);
console.log(phoneNumber);
try {
@@ -59,9 +58,12 @@ export default function PhoneNumStep({ goToStep }: PhoneNumberStepProps) {
},
});
if (response.status === 200) {
- console.log(response.data);
+ console.log(response.data.content);
+ setVerificationCode(response.data.content.verificationCode);
+ setIsVerificationSent(true);
}
} catch (error) {
+ setIsVerificationSent(false);
console.error("전화번호 인증 실패: ", error);
alert("전화번호를 다시 입력해주세요.");
}
@@ -70,14 +72,18 @@ export default function PhoneNumStep({ goToStep }: PhoneNumberStepProps) {
// 인증번호 재전송 함수
const reSendVerification = () => {
setTimer(180);
- setVerificationCode("");
+ setCode("");
sendVerification(phoneNumber);
};
// 인증번호 확인 함수
const verifyCode = () => {
- // 인증번호 확인하는 로직
- setIsCodeVerified(true);
+ if (code === verificationCode) {
+ setIsCodeVerified(true);
+ } else {
+ setIsCodeVerified(false);
+ alert("인증번호가 일치하지 않습니다.");
+ }
};
//타이머 함수
@@ -146,21 +152,23 @@ export default function PhoneNumStep({ goToStep }: PhoneNumberStepProps) {
인증번호
- {formatTime()}
+ {!isCodeVerified && (
+ {formatTime()}
+ )}
확인
diff --git a/src/types/cogoData.ts b/src/types/cogoData.ts
new file mode 100644
index 0000000..d17f82f
--- /dev/null
+++ b/src/types/cogoData.ts
@@ -0,0 +1,11 @@
+export interface CogoData {
+ application_id: number;
+ mentor_id: number;
+ mentee_id: number;
+ mentor_name: string;
+ mentee_name: string;
+ application_memo: string;
+ application_date: string;
+ application_start_time: string;
+ application_end_time: string;
+}
diff --git a/src/types/mentorData.ts b/src/types/mentorData.ts
new file mode 100644
index 0000000..d24690c
--- /dev/null
+++ b/src/types/mentorData.ts
@@ -0,0 +1,10 @@
+export interface MentorData {
+ mentorId: number;
+ picture: string;
+ mentorName: string;
+ club: string;
+ part: string;
+ username: string;
+ title: string;
+ description: string;
+}
diff --git a/src/types/mentorDetail.ts b/src/types/mentorDetail.ts
new file mode 100644
index 0000000..21a7798
--- /dev/null
+++ b/src/types/mentorDetail.ts
@@ -0,0 +1,12 @@
+export interface MentorDetail {
+ mentorId: number,
+ mentorName: string;
+ part: string;
+ club: string;
+ username: string;
+ introductionTitle: string;
+ introductionDescription: string;
+ introductionAnswer1: string;
+ introductionAnswer2: string;
+ imageUrl: string;
+}
\ No newline at end of file
diff --git a/src/types/userData.ts b/src/types/userData.ts
new file mode 100644
index 0000000..140d50b
--- /dev/null
+++ b/src/types/userData.ts
@@ -0,0 +1,10 @@
+export interface UserData {
+ name: string;
+ email: string;
+ phoneNum: string;
+ role: string;
+ part: string;
+ club: string;
+ image: string;
+ picture: string;
+}
diff --git a/yarn.lock b/yarn.lock
index 281920d..fb64163 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6533,6 +6533,18 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
+lottie-react@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/lottie-react/-/lottie-react-2.4.0.tgz#f7249eee2b1deee70457a2d142194fdf2456e4bd"
+ integrity sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w==
+ dependencies:
+ lottie-web "^5.10.2"
+
+lottie-web@^5.10.2:
+ version "5.12.2"
+ resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.12.2.tgz#579ca9fe6d3fd9e352571edd3c0be162492f68e5"
+ integrity sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg==
+
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz"