Skip to content

Commit cceda98

Browse files
committed
feat: 사용자 등록 위치 확인 페이지 추가
1 parent a290f83 commit cceda98

File tree

4 files changed

+221
-3
lines changed

4 files changed

+221
-3
lines changed

app/pullup/[id]/pullup-client.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,15 @@ const PullupClient = ({
191191
<span className="mr-1 mb-[3px]">
192192
<StarIcon />
193193
</span>
194-
<Text typography="t7" className="">
195-
정보 제공자: {marker.username}
196-
</Text>
194+
<button
195+
onClick={() => {
196+
router.push(`/user-info/${marker.username}`);
197+
}}
198+
>
199+
<Text typography="t7" className="">
200+
정보 제공자: {marker.username}
201+
</Text>
202+
</button>
197203
</div>
198204
)}
199205
</div>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
"use client";
2+
3+
import Section from "@/components/common/section";
4+
import userMarkers, {
5+
type UserMarker,
6+
type UserMarkerRes,
7+
} from "@api/marker/user-marker";
8+
import GrowBox from "@common/grow-box";
9+
import Skeleton from "@common/skeleton";
10+
import Text from "@common/text";
11+
import { useRouter } from "next/navigation";
12+
import { useCallback, useEffect, useRef, useState } from "react";
13+
import { BsPinMapFill } from "react-icons/bs";
14+
15+
interface Props {
16+
data: UserMarkerRes;
17+
userName: string;
18+
}
19+
20+
const PageClient = ({ data, userName }: Props) => {
21+
const router = useRouter();
22+
23+
const [markers, setMarkers] = useState<UserMarker[]>(data.markers);
24+
const [currentPage, setCurrentPage] = useState(data.currentPage);
25+
26+
const [isLoading, setIsLoading] = useState(false);
27+
28+
const observerRef = useRef<IntersectionObserver | null>(null);
29+
const loadMoreRef = useRef<HTMLDivElement | null>(null);
30+
31+
const loadMoreMarkers = useCallback(async () => {
32+
if (isLoading || currentPage >= data.totalPages) return;
33+
34+
setIsLoading(true);
35+
const newData = await userMarkers({
36+
userName: userName,
37+
page: currentPage + 1,
38+
});
39+
40+
setMarkers((prevMarkers) => [...prevMarkers, ...newData.markers]);
41+
setCurrentPage(newData.currentPage);
42+
43+
setIsLoading(false);
44+
}, [currentPage, isLoading, data.totalPages]);
45+
46+
useEffect(() => {
47+
observerRef.current = new IntersectionObserver(
48+
(entries) => {
49+
if (entries[0].isIntersecting) {
50+
loadMoreMarkers();
51+
}
52+
},
53+
{
54+
rootMargin: "100px",
55+
}
56+
);
57+
58+
if (loadMoreRef.current) {
59+
observerRef.current.observe(loadMoreRef.current);
60+
}
61+
62+
return () => {
63+
if (observerRef.current) {
64+
observerRef.current.disconnect();
65+
}
66+
};
67+
}, [loadMoreMarkers]);
68+
69+
if (!markers || markers.length === 0) {
70+
return (
71+
<Section>
72+
<p>{userName}님이 등록한 위치가 없습니다.</p>
73+
</Section>
74+
);
75+
}
76+
return (
77+
<div>
78+
<ul>
79+
{markers.map((marker) => {
80+
return (
81+
<li
82+
key={marker.markerId}
83+
className="border-b border-solid dark:border-grey-dark active:bg-grey-light dark:active:bg-grey-dark"
84+
>
85+
<button
86+
className="flex items-center p-2 px-4 text-left w-full h-full"
87+
onClick={() => {
88+
router.push(`/pullup/${marker.markerId}`);
89+
}}
90+
>
91+
<div className="w-[90%] flex flex-col">
92+
<Text typography="t6" className="break-all">
93+
{marker.address}
94+
</Text>
95+
<Text
96+
typography="t7"
97+
className="break-all text-grey dark:text-grey"
98+
>
99+
{marker.description || "설명이 없습니다."}
100+
</Text>
101+
</div>
102+
<GrowBox />
103+
<div className="shrink-0 w-[10%] flex items-center justify-center">
104+
<BsPinMapFill className="fill-primary" />
105+
</div>
106+
</button>
107+
</li>
108+
);
109+
})}
110+
</ul>
111+
{isLoading && (
112+
<div className="p-4">
113+
<Skeleton className="w-full h-16 rounded-lg" />
114+
</div>
115+
)}
116+
{data.totalPages > currentPage && (
117+
<div ref={loadMoreRef} className="w-full h-20" />
118+
)}
119+
</div>
120+
);
121+
};
122+
123+
export default PageClient;

app/user-info/[user]/page.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import userMarkers from "@api/marker/user-marker";
2+
import Section from "@common/section";
3+
import SideMain from "@common/side-main";
4+
import Text from "@common/text";
5+
import { cookies } from "next/headers";
6+
import PageClient from "./page-client";
7+
8+
type Params = {
9+
user: string;
10+
};
11+
12+
const UserInfoPage = async ({ params }: { params: Params }) => {
13+
const { user } = params;
14+
15+
const cookieStore = cookies();
16+
const decodeCookie = decodeURIComponent(cookieStore.toString());
17+
const decodeUserName = decodeURIComponent(user);
18+
19+
const data = await userMarkers({
20+
userName: decodeUserName,
21+
cookie: decodeCookie,
22+
});
23+
24+
return (
25+
<SideMain hasBackButton headerTitle="대한민국 철봉 지도">
26+
<Section>
27+
<div>
28+
<Text typography="t4" fontWeight="bold" className="mr-1">
29+
{decodeUserName}
30+
</Text>
31+
</div>
32+
<Text typography="t6">님이 등록한 위치</Text>
33+
</Section>
34+
35+
<PageClient data={data} userName={decodeUserName} />
36+
</SideMain>
37+
);
38+
};
39+
40+
export default UserInfoPage;

lib/api/marker/user-marker.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import fetchData from "@lib/fetchData";
2+
3+
export interface UserMarker {
4+
latitude: number;
5+
longitude: number;
6+
markerId: number;
7+
description: string;
8+
address: string;
9+
}
10+
11+
export interface UserMarkerRes {
12+
markers: UserMarker[];
13+
currentPage: number;
14+
totalPages: number;
15+
totalMarkers: number;
16+
}
17+
18+
const userMarkers = async ({
19+
userName,
20+
page = 1,
21+
pageSize = 10,
22+
cookie,
23+
}: {
24+
userName: string;
25+
page?: number;
26+
pageSize?: number;
27+
cookie?: string;
28+
}): Promise<UserMarkerRes> => {
29+
const isServer = typeof window === "undefined";
30+
31+
const url = isServer ? process.env.NEXT_PUBLIC_BASE_URL : "/api/v1";
32+
33+
const response = await fetchData(
34+
`${url}/markers/user/${userName}?page=${page}&pageSize=${pageSize}`,
35+
{
36+
headers: {
37+
Cookie: cookie || "",
38+
},
39+
cache: "no-store",
40+
credentials: "include",
41+
}
42+
);
43+
44+
const data = response.json();
45+
46+
return data;
47+
};
48+
49+
export default userMarkers;

0 commit comments

Comments
 (0)