@@ -234,7 +259,7 @@ export default function MatchingListPage() {
작성자
|
- 매칭글 제목
+ 제목
|
티켓
@@ -255,7 +280,7 @@ export default function MatchingListPage() {
{item.authorNickname}
|
- {item.title}
+ {highlightText(item.title, searchTerm)}
|
{item.haveTicket ? "O" : "X"}
@@ -267,14 +292,14 @@ export default function MatchingListPage() {
)}
- {/* 페이지네이션 UI */}
{!isLoading && !errorMsg && totalPages > 1 && (
- {/* 이전 버튼 */}
-
- {/* 페이지 번호 버튼들 */}
{pageNumbers.map((page) => (
-
))}
- {/* 다음 버튼 */}
-
- {hasTicket && (
-
- )}
+
);
}
@@ -174,102 +170,64 @@ function GameSelector({
export default function MatchingWritePage() {
const navigate = useNavigate();
- // ────────────────────────────────────────────────────────────
- // 폼 상태
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [hasTicket, setHasTicket] = useState(null);
- const [date, setDate] = useState(""); // "YYYY-MM-DD"
- const [game, setGame] = useState(""); // 선택된 gameIdx(문자열)
-
- // 실제 백엔드에서 받아온 gameIdx 목록
+ const [date, setDate] = useState("");
+ const [game, setGame] = useState("");
const [gameOptions, setGameOptions] = useState<{ id: string; label: string }[]>([]);
- // ────────────────────────────────────────────────────────────
- // 1) “날짜(date)”가 선택될 때마다 백엔드에서 해당 날짜의 모든 경기 목록을 가져오기
+ // 날짜 선택 시 경기 목록 불러오기
useEffect(() => {
if (!date) {
- setGameOptions([]); // 날짜가 비어 있으면 옵션 초기화
+ setGameOptions([]);
return;
}
-
const [yyyy, mm, dd] = date.split("-");
const monthParam = `${yyyy}-${mm}`;
const dayParam = parseInt(dd, 10);
- const fetchGames = async () => {
+ (async () => {
try {
- // cheeringTeamIdx 파라미터 없이 호출 → 해당 날짜에 열리는 모든 경기 반환
const resp = await axios.get("/home/calendar-games", {
- params: {
- month: monthParam,
- day: dayParam
- },
+ params: { month: monthParam, day: dayParam },
});
- const todayObj = resp.data.days.find((d) => d.day === dayParam);
- if (todayObj) {
- const opts = todayObj.games.map((g) => ({
- id: String(g.gameIdx),
- label: `${g.homeTeamName} vs ${g.awayTeamName} (${g.startTime})`,
- }));
- setGameOptions(opts);
- } else {
- setGameOptions([]);
- }
- } catch (e) {
- console.error("달력 API 호출 실패:", e);
+ const today = resp.data.days.find((d) => d.day === dayParam);
+ setGameOptions(
+ today
+ ? today.games.map((g) => ({
+ id: String(g.gameIdx),
+ label: `${g.homeTeamName} vs ${g.awayTeamName} (${g.startTime})`,
+ }))
+ : []
+ );
+ } catch {
setGameOptions([]);
}
- };
-
- fetchGames();
+ })();
}, [date]);
- // ────────────────────────────────────────────────────────────
- // 티켓 인증 (더미)
const handleVerifyTicket = () => {
alert("티켓 인증 요청(구현 필요)");
};
- // ────────────────────────────────────────────────────────────
- // 매칭 글 등록 제출 처리
const handleSubmit = async () => {
- // 1) 유효성 검사
- if (!title.trim()) {
- alert("제목을 입력해 주세요.");
- return;
- }
- if (!content.trim()) {
- alert("내용을 입력해 주세요.");
- return;
- }
- if (hasTicket === null) {
- alert("티켓 보유 여부를 선택해 주세요.");
- return;
- }
- if (!date) {
- alert("경기 날짜를 선택해 주세요.");
- return;
- }
- if (!game) {
- alert("경기를 선택해 주세요.");
- return;
- }
+ if (!title.trim()) return alert("제목을 입력해 주세요.");
+ if (!content.trim()) return alert("내용을 입력해 주세요.");
+ if (hasTicket === null) return alert("티켓 보유 여부를 선택해 주세요.");
+ if (!date) return alert("경기 날짜를 선택해 주세요.");
+ if (!game) return alert("경기를 선택해 주세요.");
- // 2) 요청 바디 생성 (스펙에 맞춰 정확히 보내기)
const body: CreateMatchingPostRequest = {
title: title.trim(),
context: content.trim(),
haveTicket: hasTicket,
- gameIdx: parseInt(game, 10), // 반드시 숫자 타입(정수)로
+ gameIdx: parseInt(game, 10),
};
try {
- // (예시) 로컬 스토리지에 저장된 JWT 토큰 꺼내기
const token = localStorage.getItem("jwtToken") || "";
-
- // 3) POST /matching-post API 호출 (Authorization 헤더 포함)
- const response = await axios.post(
+ const res = await axios.post(
"/matching-post",
body,
{
@@ -279,41 +237,30 @@ export default function MatchingWritePage() {
},
}
);
-
- // 4) 성공 시 반환된 postIdx로 상세 페이지로 이동
- const { postIdx } = response.data;
alert("매칭 글이 성공적으로 등록되었습니다.");
- navigate(`/matching/articles/${postIdx}`);
- } catch (error) {
- console.error("매칭 글 등록 실패:", error);
- alert("매칭 글 등록 중 오류가 발생했습니다. 서버 로그를 확인해 주세요.");
+ navigate(`/matching/articles/${res.data.postIdx}`);
+ } catch (err) {
+ console.error(err);
+ alert("매칭 글 등록 중 오류가 발생했습니다.");
}
};
return (
-
- {/* 헤더 */}
-
-
-
+
+ {/* 뒤로가기 버튼 (뷰포트 기준 좌상단 고정) */}
+
+
+
- {/* 폼 */}
-
+ {/* 중앙 폼 컨테이너: space-y-6으로 섹션 간격 균일하게 */}
+
-
-
diff --git a/src/pages/Profile/Profile.tsx b/src/pages/Profile/Profile.tsx
index aac5417..523b001 100644
--- a/src/pages/Profile/Profile.tsx
+++ b/src/pages/Profile/Profile.tsx
@@ -1,10 +1,11 @@
// src/pages/Profile/Profile.tsx
+
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
getTeamLogoByIdx,
getTeamNameByIdx,
-} from "../../hooks/TeamNameChanger"; // 실제 경로에 맞게 수정해주세요
+} from "../../hooks/TeamNameChanger";
interface ProfileData {
userIdx: number;
@@ -25,15 +26,13 @@ interface MyMatchingPostDto {
context: string;
haveTicket: boolean;
createdAt: string;
- isMatched: boolean; // 매칭 완료 여부
+ isMatched: boolean;
}
export default function Profile() {
const navigate = useNavigate();
- // 프로필 정보 상태
const [profile, setProfile] = useState (null);
- // 내가 쓴 매칭 글 상태
const [myPosts, setMyPosts] = useState([]);
const [isLoadingPosts, setIsLoadingPosts] = useState(false);
const [postsError, setPostsError] = useState(null);
@@ -44,8 +43,8 @@ export default function Profile() {
const token = localStorage.getItem("jwtToken");
if (!token) throw new Error("토큰이 없습니다.");
- // 1) 프로필 정보 조회
- const profileRes = await fetch(
+ // 프로필 조회
+ const pr = await fetch(
`${import.meta.env.VITE_API_URL}/mypage/myTemp`,
{
method: "GET",
@@ -56,16 +55,14 @@ export default function Profile() {
},
}
);
- if (!profileRes.ok)
- throw new Error(`프로필 조회 실패: ${profileRes.status}`);
- const profileData: ProfileData = await profileRes.json();
+ if (!pr.ok) throw new Error(`프로필 조회 실패: ${pr.status}`);
+ const profileData: ProfileData = await pr.json();
setProfile(profileData);
- // 2) 내가 쓴 매칭 글 목록 조회 (/mypage/myPost)
+ // 내 매칭 글 조회
setIsLoadingPosts(true);
setPostsError(null);
-
- const postsRes = await fetch(
+ const ps = await fetch(
`${import.meta.env.VITE_API_URL}/mypage/myPost`,
{
method: "GET",
@@ -76,12 +73,11 @@ export default function Profile() {
},
}
);
- if (!postsRes.ok)
- throw new Error(`내 글 조회 실패: ${postsRes.status}`);
- const postsData: MyMatchingPostDto[] = await postsRes.json();
+ if (!ps.ok) throw new Error(`내 글 조회 실패: ${ps.status}`);
+ const postsData: MyMatchingPostDto[] = await ps.json();
setMyPosts(postsData);
} catch (err: any) {
- console.error("프로필 또는 매칭 글 불러오기 실패:", err);
+ console.error(err);
setPostsError("내가 쓴 매칭 글을 불러오는 중 오류가 발생했습니다.");
} finally {
setIsLoadingPosts(false);
@@ -89,7 +85,6 @@ export default function Profile() {
})();
}, []);
- // 팀명/로고 파생 값
const cheeringTeamName =
profile?.cheeringTeamId != null
? getTeamNameByIdx(profile.cheeringTeamId)
@@ -99,34 +94,24 @@ export default function Profile() {
? getTeamLogoByIdx(profile.cheeringTeamId)
: "/images/default_team_emb.png";
- /**
- * 매칭 완료 토글 함수
- * @param postId {number} - 토글할 matchingPostIdx
- */
const handleToggleMatched = async (postId: number) => {
try {
const token = localStorage.getItem("jwtToken");
if (!token) throw new Error("토큰이 없습니다.");
- // PATCH 요청: is_matched 상태 토글
const res = await fetch(
`${import.meta.env.VITE_API_URL}/mypage/${postId}/toggle-matched`,
{
method: "PATCH",
credentials: "include",
headers: {
- "Content-Type": "application/json",
+ Accept: "application/json",
Authorization: `Bearer ${token}`,
},
}
);
- if (!res.ok) {
- throw new Error(`매칭 토글 실패: ${res.status}`);
- }
- const updated = await res.json();
- // 예시 응답: { matchingPostIdx: 3, matched: true }
-
- // 로컬 상태에서도 해당 게시글의 isMatched 값을 반전시켜서 업데이트
+ if (!res.ok) throw new Error(`매칭 토글 실패: ${res.status}`);
+ const updated = await res.json();
setMyPosts((prev) =>
prev.map((p) =>
p.matchingPostIdx === updated.matchingPostIdx
@@ -135,196 +120,180 @@ export default function Profile() {
)
);
} catch (error: any) {
- console.error("매칭 토글 중 오류 발생:", error);
+ console.error(error);
alert(`오류: ${error.message}`);
}
};
+ // userTemp 그대로 %로 사용 (36.5℃ → 36.5%)
+ const fillPercent =
+ profile?.userTemp != null ? Math.min(profile.userTemp, 100) : 0;
+
return (
- <>
-
- {/* 헤더 배너 */}
-
-
-
- {/* 프로필 + 소개 */}
-
-  0
- ? profile.profileImageUrl
- : "/images/user_avatar.png"
- }
- alt="프로필"
- className="h-24 w-24 rounded-full border-4 border-white object-cover"
- onError={(e) => {
- e.currentTarget.src = "/images/user_avatar.png";
- }}
- />
-
-
-
- {profile?.nickname || "로딩 중..."}
-
-
- {cheeringTeamName}
-
-
-
- {profile?.bio || "자기소개가 없습니다."}
-
- {/* 수정 / 설정 버튼 */}
-
-
-
-
+
+ {/* 헤더 배너 */}
+
+
+
+
+  0
+ ? profile.profileImageUrl
+ : "/images/user_avatar.png"
+ }
+ alt="프로필"
+ className="h-24 w-24 rounded-full border-4 border-white object-cover"
+ onError={(e) => {
+ e.currentTarget.src = "/images/user_avatar.png";
+ }}
+ />
+
+
+
+ {profile?.nickname || "로딩 중..."}
+
+
+ {cheeringTeamName}
+
+
+
+ {profile?.bio || "자기소개가 없습니다."}
+
+
+
+
- {/* 팀 엠블럼 */}
-
-  {
- e.currentTarget.src = "/images/default_team_emb.png";
- }}
- />
-
-
-
-
- {/* 직관 온도 */}
-
-
-
- 직관 온도
-
-
- {profile?.userTemp != null
- ? `${profile.userTemp.toFixed(1)}℃`
- : "–"}
-
-
-
+  {
+ e.currentTarget.src = "/images/default_team_emb.png";
}}
/>
-
+
+
+
+ {/* 직관 온도 */}
+
+
+
+ 직관 온도
+
+
+ {profile?.userTemp != null
+ ? `${profile.userTemp.toFixed(1)}℃`
+ : "–"}
+
+
+
+
- {/* 작성한 매칭 글 (버튼 추가) */}
-
-
- 작성한 매칭 글
-
- {isLoadingPosts && (
- 불러오는 중...
- )}
- {postsError && (
- {postsError}
- )}
- {!isLoadingPosts && !postsError && myPosts.length === 0 && (
-
- 작성한 매칭 글이 없습니다.
-
- )}
- {!isLoadingPosts && !postsError && myPosts.length > 0 && (
-
- {myPosts.map((post) => (
+ {/* 작성한 매칭 글 */}
+
+
+ 작성한 매칭 글
+
+ {isLoadingPosts && (
+ 불러오는 중...
+ )}
+ {postsError && {postsError} }
+ {!isLoadingPosts && !postsError && myPosts.length === 0 && (
+
+ 작성한 매칭 글이 없습니다.
+
+ )}
+ {!isLoadingPosts && !postsError && myPosts.length > 0 && (
+
+ {myPosts.map((post) => (
+
+ navigate(`/matching/articles/${post.matchingPostIdx}`)
+ }
>
-
- navigate(`/matching/articles/${post.matchingPostIdx}`)
- }
- >
-
-
-
-
- {profile?.nickname || ""} ·{" "}
- {new Date(post.createdAt).toLocaleDateString(
- "ko-KR"
- )}
-
-
-
- {post.title}
-
-
- {post.context.length > 30
- ? post.context.slice(0, 30) + "…"
- : post.context}
-
-
-
-
-
- {cheeringTeamName}
-
-
- {post.haveTicket ? "티켓 있음" : "티켓 없음"}
-
-
-
+
+
+
+
+ {profile?.nickname} ·{" "}
{new Date(post.createdAt).toLocaleDateString("ko-KR")}
+
+ {post.title}
+
+
+ {post.context.length > 30
+ ? post.context.slice(0, 30) + "…"
+ : post.context}
+
+
+
+
+
+ {cheeringTeamName}
+
+
+ {post.haveTicket ? "티켓 있음" : "티켓 없음"}
+
+
+
+ {new Date(post.createdAt).toLocaleDateString("ko-KR")}
+
-
- {/**
- * ▷ 매칭 완료 토글 버튼
- * - post.isMatched === false → “매칭 완료”
- * - post.isMatched === true → “다시 매칭하기”
- */}
-
- ))}
-
- )}
-
-
- >
+
+
+ ))}
+
+ )}
+
+
);
}
|