Skip to content

Commit eef657a

Browse files
committed
Merge branch 'main' of https://github.com/2025-LOCO/Loco-FE
2 parents e6695c7 + d8e922d commit eef657a

34 files changed

Lines changed: 1376 additions & 304 deletions

src/App.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,20 @@ import RoutePanel from "./components/map/RoutePanel";
2323

2424
import TalkEdit from "@/pages/LocoTalk/TalkEdit";
2525
import TalkLayout from "@/pages/LocoTalk/TalkLayout";
26+
import { useAuthStore } from "./stores/authStore";
27+
import { useEffect, useState } from "react";
2628

2729
function App() {
30+
const setLoggedIn = useAuthStore((s) => s.setLoggedIn);
31+
const [authChecked, setAuthChecked] = useState(false);
32+
useEffect(() => {
33+
const token = localStorage.getItem("accessToken");
34+
setLoggedIn(!!token);
35+
setAuthChecked(true);
36+
}, [setLoggedIn]);
37+
38+
if (!authChecked) return null;
39+
2840
return (
2941
<>
3042
<Routes>
@@ -69,7 +81,7 @@ function App() {
6981
</Route>
7082
{/* 2. 다른 사람들이 보는 map 페이지 (readOnly)*/}
7183
{/* <Route path="u/:nickname/map" element={<MapLayout mapType="public" />}> */}
72-
<Route path="public-map" element={<MapLayout mapType="public" />}>
84+
<Route path="u/:user_id/map" element={<MapLayout mapType="public" />}>
7385
<Route index element={<Navigate to="profile" replace />} />
7486
<Route path="profile" element={<ProfilePanel />} />
7587
<Route path="place" element={<PlacePanel />} />

src/apis/auth/getMyInformation.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import apiInstance from "@/apis/apiInstance";
2+
3+
// 요청 타입
4+
export type GetMyInformationRequest = void;
5+
6+
// 응답 타입
7+
export interface GetMyInformationResponse {
8+
id: number;
9+
email: string;
10+
nickname: string;
11+
intro: string | null;
12+
image_url: string | null;
13+
city_id: string | null;
14+
created_at: string;
15+
ranking: number | null;
16+
points: number;
17+
grade: string;
18+
my_places_count: number;
19+
my_routes_count: number;
20+
my_answers_count: number;
21+
my_places_liked_count: number;
22+
my_routes_liked_count: number;
23+
places_loco_count: number[];
24+
routes_loco_count: number[];
25+
}
26+
27+
export async function getMyInformation(): Promise<GetMyInformationResponse> {
28+
const res = await apiInstance.get<GetMyInformationResponse>(
29+
"/api/v1/users/me"
30+
);
31+
return res.data;
32+
}

src/apis/auth/getUserProfile.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import apiInstance from "@/apis/apiInstance";
2+
3+
export interface UserProfileResponse {
4+
id: number;
5+
nickname: string;
6+
city_name: string;
7+
intro: string;
8+
avatar_url: string;
9+
created_at: string;
10+
ranking: number;
11+
points: number;
12+
grade: string;
13+
ranking_percentile: number;
14+
liked: number;
15+
}
16+
17+
// user_id 기반 사용자 정보 조회
18+
export async function getUserProfile(
19+
userId: number
20+
): Promise<UserProfileResponse> {
21+
const res = await apiInstance.get(`/api/v1/users/${userId}`);
22+
return res.data;
23+
}

src/apis/auth/login.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// src/apis/auth/login.ts
2+
import apiInstance from "../apiInstance";
3+
4+
interface LoginRequest {
5+
username: string;
6+
password: string;
7+
}
8+
9+
interface LoginResponse {
10+
user_id: number;
11+
nickname: string;
12+
access_token: string;
13+
token_type: string;
14+
}
15+
16+
export async function loginUser({
17+
username,
18+
password,
19+
}: LoginRequest): Promise<LoginResponse> {
20+
const formData = new URLSearchParams();
21+
formData.append("username", username);
22+
formData.append("password", password);
23+
24+
const res = await apiInstance.post<LoginResponse>(
25+
"/api/v1/auth/login",
26+
formData,
27+
{
28+
headers: {
29+
"Content-Type": "application/x-www-form-urlencoded",
30+
},
31+
}
32+
);
33+
34+
return res.data;
35+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import apiInstance from "@/apis/apiInstance";
2+
3+
/** 요청 타입 */
4+
export type GetExploreMembersRequest = void;
5+
6+
/** 로코지기(회원) 개별 타입 */
7+
export interface ExploreMemberItem {
8+
id: number;
9+
nickname: string;
10+
city_name: string;
11+
intro: string;
12+
avatar_url: string;
13+
created_at: string;
14+
ranking: number;
15+
points: number;
16+
grade: string;
17+
ranking_percentile: number;
18+
liked: number;
19+
}
20+
21+
/** 응답 타입 */
22+
export interface GetExploreMembersResponse {
23+
best_users: ExploreMemberItem[];
24+
new_local_users: ExploreMemberItem[];
25+
}
26+
27+
/**
28+
* [GET] /api/v1/users/loco-explore
29+
* 로코지기 탐색 - 베스트/신규 로코지기 목록 조회
30+
*/
31+
export async function getExploreMembers(): Promise<GetExploreMembersResponse> {
32+
const res = await apiInstance.get<GetExploreMembersResponse>(
33+
"/api/v1/users/loco-explore"
34+
);
35+
return res.data;
36+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import apiInstance from "@/apis/apiInstance";
2+
3+
/**
4+
* [GET] /api/v1/places/explore
5+
* - 인기 장소 / 신규 장소 리스트 조회
6+
*/
7+
8+
export type GetExplorePlaceListRequest = void;
9+
10+
export interface ExplorePlaceItem {
11+
member_id: number;
12+
place_id: number;
13+
name: string;
14+
image_url: string | null;
15+
liked: number;
16+
short_location: string;
17+
intro: string;
18+
}
19+
20+
export interface GetExplorePlaceListResponse {
21+
ranked_places: ExplorePlaceItem[];
22+
new_places: ExplorePlaceItem[];
23+
}
24+
25+
export async function getExplorePlaceList(): Promise<GetExplorePlaceListResponse> {
26+
const res = await apiInstance.get<GetExplorePlaceListResponse>(
27+
"/api/v1/places/explore"
28+
);
29+
return res.data;
30+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import apiInstance from "@/apis/apiInstance";
2+
3+
// 요청 타입
4+
export interface GetExploreRouteListRequest {
5+
tag_period?: number;
6+
tag_env?: string;
7+
tag_with?: string;
8+
tag_move?: string;
9+
tag_atmosphere?: string;
10+
tag_place_count?: number;
11+
}
12+
13+
// 태그 타입
14+
export interface ExploreRouteTag {
15+
period: string;
16+
env: string;
17+
with: string;
18+
move: string;
19+
atmosphere: string;
20+
place_count: string;
21+
}
22+
23+
// 경로 내 장소 타입
24+
export interface ExploreRoutePlace {
25+
id: number;
26+
name: string;
27+
category: string;
28+
day: number;
29+
order: number;
30+
}
31+
32+
// 교통수단 타입
33+
export interface ExploreRouteTransportation {
34+
id: number;
35+
name: string;
36+
day: number;
37+
order: number;
38+
}
39+
40+
// 전체 경로 아이템 타입
41+
export interface ExploreRouteItem {
42+
userId: number;
43+
id: number;
44+
name: string;
45+
imageUrl: string;
46+
location: string;
47+
intro: string;
48+
liked: number;
49+
tags: ExploreRouteTag;
50+
places: ExploreRoutePlace[];
51+
transportations: ExploreRouteTransportation[];
52+
countReal: number;
53+
countSoso: number;
54+
countBad: number;
55+
created_at: string;
56+
}
57+
58+
export type GetExploreRouteListResponse = ExploreRouteItem[];
59+
60+
/**
61+
* [GET] /api/v1/routes/search
62+
* 탐색 탭에서 루트 목록 조회 (태그 검색 포함)
63+
*/
64+
export async function getExploreRouteList(
65+
params?: GetExploreRouteListRequest
66+
): Promise<GetExploreRouteListResponse> {
67+
const res = await apiInstance.get<GetExploreRouteListResponse>(
68+
"/api/v1/routes/search",
69+
{ params } // 쿼리 파라미터 전달
70+
);
71+
return res.data;
72+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// src/apis/favorite/getFavoritePlaces.ts
2+
import apiInstance from "@/apis/apiInstance";
3+
4+
// 요청 타입
5+
export type GetFavoritePlacesRequest = {
6+
user_id: number;
7+
};
8+
9+
// 응답 타입
10+
export interface FavoritePlaceItem {
11+
id: number;
12+
user_id: number;
13+
place_id: number;
14+
created_at: string;
15+
place: {
16+
place_id: number;
17+
name: string;
18+
type: string;
19+
is_frequent: boolean;
20+
atmosphere: string;
21+
pros: string;
22+
cons: string;
23+
image_url: string;
24+
count_real: number;
25+
count_normal: number;
26+
count_bad: number;
27+
latitude: number;
28+
longitude: number;
29+
kakao_place_id: string;
30+
intro: string;
31+
phone: string;
32+
address_name: string;
33+
link: string;
34+
liked: number;
35+
user_id: number;
36+
city_name: string;
37+
};
38+
}
39+
40+
// 전체 응답 타입
41+
export type GetFavoritePlacesResponse = FavoritePlaceItem[];
42+
43+
// API 함수
44+
export async function getFavoritePlaces(
45+
user_id: number
46+
): Promise<GetFavoritePlacesResponse> {
47+
const res = await apiInstance.get<GetFavoritePlacesResponse>(
48+
`/api/v1/favorites/places/${user_id}`
49+
);
50+
return res.data;
51+
}

src/apis/qna/getQnaDetail.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import apiInstance from "@/apis/apiInstance";
2+
3+
export async function getQnADetail(questionId: number) {
4+
const res = await apiInstance.get(`/api/v1/qna/questions/${questionId}`);
5+
return res.data;
6+
}

src/apis/qna/questions.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import apiInstance from "../apiInstance";
2+
3+
/** QnA 질문*/
4+
export interface QnaQuestion {
5+
question_id: number;
6+
user_id: number;
7+
title: string;
8+
content: string;
9+
created_at: string;
10+
view_count: number;
11+
answer_count: number;
12+
}
13+
14+
/** 질문 목록 조회: GET /api/v1/qna/questions */
15+
export async function getQnaQuestions(): Promise<QnaQuestion[]> {
16+
const res = await apiInstance.get<QnaQuestion[]>("/api/v1/qna/questions", {
17+
headers: { Accept: "application/json" },
18+
});
19+
return res.data;
20+
}

0 commit comments

Comments
 (0)