Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added public/images/kakao_login_medium_wide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 13 additions & 62 deletions src/pages/Login/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ const LoginPage: React.FC = () => {
// 카카오 로그인 버튼 클릭 시 실행될 함수
const kakaoLogin = () => {
if (!window.Kakao) {
return alert("카카오 SDK가 로드되지 않았습니다.");
alert("카카오 SDK가 로드되지 않았습니다.");
return;
}

window.Kakao.Auth.login({
Expand All @@ -64,9 +65,7 @@ const LoginPage: React.FC = () => {
body: JSON.stringify({ accessToken: authObj.access_token }),
}
);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
if (!res.ok) throw new Error(`HTTP ${res.status}`);

// 2) 백엔드가 내려준 JSON 파싱
const resp = (await res.json()) as KakaoUserInfoResponse;
Expand All @@ -86,22 +85,18 @@ const LoginPage: React.FC = () => {
},
}
);
if (!hasRes.ok) {
throw new Error(`hasSignedIn 호출 실패: ${hasRes.status}`);
}
if (!hasRes.ok) throw new Error(`hasSignedIn 호출 실패: ${hasRes.status}`);

const hasJson = await hasRes.json() as {
const hasJson = (await hasRes.json()) as {
success: boolean;
message: string;
data: { hasSignedIn: boolean };
};

// 5) 분기 처리
if (hasJson.data.hasSignedIn) {
// 이미 가입된 회원: 홈으로 이동
window.location.href = "/";
} else {
// 신규 회원: 닉네임/응원팀 설정 단계로 이동
navigate("/signup/1");
}
} catch (err) {
Expand Down Expand Up @@ -147,64 +142,20 @@ const LoginPage: React.FC = () => {
<div className="h-px flex-1 bg-gray-300" />
</div>

{/* 카카오 로그인 버튼 */}
<div className="w-full space-y-4">
<button
onClick={kakaoLogin}
className="flex h-12 w-full items-center justify-center rounded-lg bg-[#FEE500] shadow transition hover:opacity-90"
>
<img
src="/images/kakao_login.png"
alt="kakao"
className="mr-2 h-6 w-6"
/>
<span className="text-base font-medium text-gray-900">
카카오톡으로 계속하기
</span>
</button>
<button
onClick={() => navigate("/signup/1")}
className="flex h-12 w-full items-center justify-center rounded-lg bg-[#03C75A] shadow transition hover:opacity-90"
>
<img
src="/images/naver_login.png"
alt="naver"
className="mr-2 h-6 w-6"
/>
<span className="text-base font-medium text-white">
네이버로 계속하기
</span>
</button>
<button
onClick={() => navigate("/signup/1")}
className="flex h-12 w-full items-center justify-center rounded-lg border border-gray-200 bg-white shadow transition hover:bg-gray-50"
>
<button onClick={kakaoLogin} className="w-full">
<img
src="/images/google_login.png"
alt="google"
className="mr-2 h-6 w-6"
src="/images/kakao_login_medium_wide.png"
alt="카카오 로그인"
className="w-full h-auto"
/>
<span className="text-base font-medium text-gray-800">
구글로 계속하기
</span>
</button>
</div>

{/* plain text로된 약관 문구 */}
<p className="mt-6 px-2 text-center text-sm text-gray-500">
계속 진행 시{" "}
<button
onClick={() => window.open("/terms", "_blank")}
className="underline hover:text-gray-700"
>
이용약관
</button>{" "}
및{" "}
<button
onClick={() => window.open("/privacy", "_blank")}
className="underline hover:text-gray-700"
>
개인정보처리방침
</button>{" "}
에 동의한 것으로 간주됩니다.
계속 진행 시 이용약관 및 개인정보처리방침에 동의한 것으로 간주됩니다.
</p>
</div>
</div>
Expand All @@ -228,7 +179,7 @@ const LoginPage: React.FC = () => {
<TeamSelection
selectedTeam={selectedTeam}
setSelectedTeam={setSelectedTeam}
onNext={goStep3} // ← 여기서 goStep3를 넘겨줍니다
onNext={goStep3}
/>
);
case "3":
Expand Down
5 changes: 0 additions & 5 deletions src/pages/Matching/MatchingArticlePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,6 @@ export default function ArticleDetail() {
<span className="text-sm">뒤로</span>
</button>

{/* 점 3개 아이콘 */}
<button className="absolute top-4 right-0 text-xl text-gray-400 hover:text-black">
</button>

{/* 제목 */}
<div className="mb-2 border-t pt-4">
<h1 className="text-xl font-bold">{post.title}</h1>
Expand Down
76 changes: 36 additions & 40 deletions src/pages/Matching/MatchingWritePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ function WriteTitleInput({
<input
type="text"
placeholder="매칭 글 제목을 입력해 주세요."
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-pink-500 focus:ring-2 focus:ring-pink-500 focus:outline-none"
value={value}
onChange={(e) => onChange(e.target.value)}
onChange={e => onChange(e.target.value)}
/>
</div>
);
Expand All @@ -65,11 +65,11 @@ function WriteContentInput({
<textarea
rows={5}
placeholder="글 내용을 자유롭게 입력해 주세요."
className="w-full resize-none rounded border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
className="w-full resize-none rounded border border-gray-300 px-3 py-2 text-sm focus:border-pink-500 focus:ring-2 focus:ring-pink-500 focus:outline-none"
value={value}
onChange={(e) => onChange(e.target.value)}
onChange={e => onChange(e.target.value)}
/>
<div className="absolute bottom-1 right-2 text-xs text-gray-400">
<div className="absolute right-2 bottom-1 text-xs text-gray-400">
{value.length} / 300
</div>
</div>
Expand All @@ -79,20 +79,20 @@ function WriteContentInput({
function TicketSelector({
hasTicket,
setHasTicket,
onVerify,
}: {
hasTicket: boolean | null;
setHasTicket: (b: boolean) => void;
onVerify: () => void;
}) {
return (
<div>
<label className="mb-1 block font-medium">티켓 보유 여부</label>
<div className="flex gap-2 mb-2">
<div className="mb-2 flex gap-2">
<button
type="button"
className={`flex-1 rounded border border-gray-300 px-4 py-2 text-sm ${
hasTicket === true ? "bg-black text-white" : "bg-white text-gray-700"
hasTicket === true
? "bg-black text-white"
: "bg-white text-gray-700"
}`}
onClick={() => setHasTicket(true)}
>
Expand All @@ -101,20 +101,15 @@ function TicketSelector({
<button
type="button"
className={`flex-1 rounded border border-gray-300 px-4 py-2 text-sm ${
hasTicket === false ? "bg-black text-white" : "bg-white text-gray-700"
hasTicket === false
? "bg-black text-white"
: "bg-white text-gray-700"
}`}
onClick={() => setHasTicket(false)}
>
X
</button>
</div>
<button
type="button"
onClick={onVerify}
className="text-xs text-blue-600 hover:underline"
>
티켓 인증하기
</button>
</div>
);
}
Expand All @@ -131,9 +126,9 @@ function GameDatePicker({
<label className="mb-1 block font-medium">경기 날짜 선택</label>
<input
type="date"
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-pink-500 focus:ring-2 focus:ring-pink-500 focus:outline-none"
value={date}
onChange={(e) => setDate(e.target.value)}
onChange={e => setDate(e.target.value)}
/>
</div>
);
Expand All @@ -152,12 +147,12 @@ function GameSelector({
<div>
<label className="mb-1 block font-medium">경기 선택</label>
<select
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
className="w-full rounded border border-gray-300 px-3 py-2 text-sm focus:border-pink-500 focus:ring-2 focus:ring-pink-500 focus:outline-none"
value={game}
onChange={(e) => setGame(e.target.value)}
onChange={e => setGame(e.target.value)}
>
<option value="">예정 날짜의 경기를 선택해 주세요.</option>
{options.map((opt) => (
{options.map(opt => (
<option key={opt.id} value={opt.id}>
{opt.label}
</option>
Expand All @@ -175,7 +170,9 @@ export default function MatchingWritePage() {
const [hasTicket, setHasTicket] = useState<boolean | null>(null);
const [date, setDate] = useState<string>("");
const [game, setGame] = useState<string>("");
const [gameOptions, setGameOptions] = useState<{ id: string; label: string }[]>([]);
const [gameOptions, setGameOptions] = useState<
{ id: string; label: string }[]
>([]);

// 날짜 선택 시 경기 목록 불러오기
useEffect(() => {
Expand All @@ -189,27 +186,30 @@ export default function MatchingWritePage() {

(async () => {
try {
const resp = await axios.get<CalendarGamesResponse>("/home/calendar-games", {
params: { month: monthParam, day: dayParam },
});
const today = resp.data.days.find((d) => d.day === dayParam);
const resp = await axios.get<CalendarGamesResponse>(
"/home/calendar-games",
{
params: { month: monthParam, day: dayParam },
},
);
const today = resp.data.days.find(d => d.day === dayParam);
setGameOptions(
today
? today.games.map((g) => ({
? today.games.map(g => ({
id: String(g.gameIdx),
label: `${g.homeTeamName} vs ${g.awayTeamName} (${g.startTime})`,
}))
: []
: [],
);
} catch {
setGameOptions([]);
}
})();
}, [date]);

const handleVerifyTicket = () => {
alert("티켓 인증 요청(구현 필요)");
};
// const handleVerifyTicket = () => {
// alert("티켓 인증 요청(구현 필요)");
// };

const handleSubmit = async () => {
if (!title.trim()) return alert("제목을 입력해 주세요.");
Expand All @@ -235,7 +235,7 @@ export default function MatchingWritePage() {
Authorization: token ? `Bearer ${token}` : "",
"Content-Type": "application/json",
},
}
},
);
alert("매칭 글이 성공적으로 등록되었습니다.");
navigate(`/matching/articles/${res.data.postIdx}`);
Expand All @@ -246,21 +246,17 @@ export default function MatchingWritePage() {
};

return (
<div className="relative w-full min-h-screen bg-gray-50">
<div className="relative min-h-screen w-full bg-gray-50">
{/* 뒤로가기 버튼 (뷰포트 기준 좌상단 고정) */}
<div className="absolute top-4 left-4 z-10">
<BackHeader title="매칭 글 작성하기" />
</div>

{/* 중앙 폼 컨테이너: space-y-6으로 섹션 간격 균일하게 */}
<div className="mx-auto max-w-md pt-16 px-4 space-y-6">
<div className="mx-auto max-w-md space-y-6 px-4 pt-16">
<WriteTitleInput value={title} onChange={setTitle} />
<WriteContentInput value={content} onChange={setContent} />
<TicketSelector
hasTicket={hasTicket}
setHasTicket={setHasTicket}
onVerify={handleVerifyTicket}
/>
<TicketSelector hasTicket={hasTicket} setHasTicket={setHasTicket} />
<GameDatePicker date={date} setDate={setDate} />
<GameSelector game={game} setGame={setGame} options={gameOptions} />

Expand Down
3 changes: 2 additions & 1 deletion src/pages/Prediction/components/LivePrediction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ export default function LivePrediction() {
<div className="flex min-w-[140px] items-center justify-center">
<div className="rounded-full bg-[#d6d7e1] px-4 py-1">
<span className="t-h3 font-bold text-black">
{match.inning}회
{/* {match.inning}회 */}
경기 종료
</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Profile/components/ImageUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default function ImageUploader({
<button
type="button"
onClick={onUploadClick}
className="w-42 rounded bg-gray-400 px-4 py-2 text-white hover:bg-gray-700 text-sm lg:px-6 lg:py-3 lg:text-base"
className="w-46 rounded bg-gray-400 px-4 py-2 text-white hover:bg-gray-700 text-sm lg:px-6 lg:py-3 lg:text-base"
>
프로필 이미지 변경
</button>
Expand Down
1 change: 1 addition & 0 deletions src/types/Chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface ChatRoomCreateRequestDTO {

// 채팅방 생성 응답 DTO
export interface ChatRoomResponseDTO {
data(data: any): unknown;
chatRoomId: number;
withUserId: number;
}