Skip to content

Commit 1382fdf

Browse files
authored
feat/connect slope api (#42)
* feat: add slope api * feat: apply slope status in slope page * feat: update webcam mobile page with resortId
1 parent 934b400 commit 1382fdf

File tree

12 files changed

+153
-27
lines changed

12 files changed

+153
-27
lines changed
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import WebcamMobileMapPage from '@/views/slop-status/ui/slop-status-page';
3+
4+
const Page = ({ params }: { params: { resortId: string } }) => {
5+
return <WebcamMobileMapPage resortId={+params.resortId} />;
6+
};
7+
8+
export default Page;

src/app/mobile/webcam/[key]/page.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
import { redirect } from 'next/navigation';
21
import React from 'react';
32
import WebcamMobileMapPage from '@/views/webcam/ui/webcam-mobile-map-page';
3+
import { DiscoveryData } from '@/entities/discovery';
44
import { RESORT_DOMAIN } from '@/entities/slop/model';
55

66
const Page = ({ params }: { params: { key: string } }) => {
7-
if (!(params?.key in RESORT_DOMAIN)) {
8-
redirect('/not-found');
7+
if (!(params.key in RESORT_DOMAIN)) {
8+
const key = DiscoveryData.find((discovery) => discovery.id === +params.key)?.map;
9+
return (
10+
<WebcamMobileMapPage
11+
resortId={+params.key}
12+
data={RESORT_DOMAIN[key as keyof typeof RESORT_DOMAIN]}
13+
/>
14+
);
915
}
1016

1117
const domain = RESORT_DOMAIN[params?.key as keyof typeof RESORT_DOMAIN];

src/entities/slope/api/get-slope.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { apiClient } from '@/shared/api/base';
2+
import type { SlopeResponse } from '../model';
3+
4+
export const getSlopes = async (resortId: number): Promise<SlopeResponse> => {
5+
const result = await apiClient.get<SlopeResponse>(`/api/slopes/${resortId}`);
6+
7+
return result;
8+
};

src/entities/slope/api/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { slopeQueries } from './query/slope.queries';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { queryOptions } from '@tanstack/react-query';
2+
import { getSlopes } from '../get-slope';
3+
4+
export const slopeQueries = {
5+
all: () => ['slope'],
6+
7+
slopeQueryKey: (resortId: number) => [...slopeQueries.all(), resortId],
8+
slope: (resortId: number) =>
9+
queryOptions({
10+
queryKey: slopeQueries.slopeQueryKey(resortId),
11+
queryFn: () => getSlopes(resortId),
12+
}),
13+
};

src/entities/slope/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * as slopeApi from './api';
2+
export * from './model';

src/entities/slope/model/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type { Level, Slope, Webcam, SlopeResponse } from './model';

src/entities/slope/model/model.d.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export type Level =
2+
| '초급'
3+
| '초중급'
4+
| '중급'
5+
| '중상급'
6+
| '상급'
7+
| '최상급';
8+
9+
export type Slope = {
10+
name: string,
11+
difficulty: Level,
12+
isDayOperating: true,
13+
isNightOperating: true,
14+
isLateNightOperating: true,
15+
isDawnOperating: true,
16+
isMidnightOperating: true
17+
}
18+
19+
export type Webcam = {
20+
name: string,
21+
number: 0,
22+
description: string,
23+
url: string
24+
}
25+
26+
export type SlopeResponse = {
27+
dayOperatingHours: string,
28+
nightOperatingHours: string,
29+
lateNightOperatingHours: string,
30+
dawnOperatingHours: string,
31+
midnightOperatingHours: string,
32+
slopes: Slope[],
33+
webcams: Webcam[]
34+
}

src/entities/slope/ui/level-chip.tsx

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import { cn } from '../../../shared/lib';
3+
import type { Level } from '../model';
4+
5+
const LEVEL: Record<Level, { text: string; color: string }> = {
6+
초급: {
7+
text: '초급',
8+
color: 'bg-sub-2',
9+
},
10+
초중급: {
11+
text: '초중급',
12+
color: 'bg-sub-2',
13+
},
14+
중급: {
15+
text: '중급',
16+
color: 'bg-sub-4',
17+
},
18+
중상급: {
19+
text: '중상급',
20+
color: 'bg-sub-4',
21+
},
22+
상급: {
23+
text: '상급',
24+
color: 'bg-gray-70',
25+
},
26+
최상급: {
27+
text: '최상급',
28+
color: 'bg-gray-70',
29+
},
30+
};
31+
32+
interface LevelChipProps {
33+
className?: string;
34+
level: Level;
35+
}
36+
37+
const LevelChip = ({ level, className }: LevelChipProps) => {
38+
return (
39+
<div
40+
className={cn(
41+
'flex h-[25px] w-[56px] items-center justify-center rounded-[6px] border-[1px] border-white border-opacity-10',
42+
LEVEL[level].color,
43+
className
44+
)}
45+
>
46+
<p className="body1-semibold text-white">{LEVEL[level].text}</p>
47+
</div>
48+
);
49+
};
50+
51+
export default LevelChip;

src/features/slop/ui/slop-status-list.tsx

+13-20
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
import React from 'react';
2-
import type { Level } from '@/entities/slop/model/model';
3-
import LevelChip from '@/entities/slop/ui/level-chip';
2+
import type { Slope } from '@/entities/slope';
3+
import LevelChip from '@/entities/slope/ui/level-chip';
44
import CheckIcon from '@/shared/icons/check';
55
import CloseIcon from '@/shared/icons/close';
66
import { cn } from '@/shared/lib';
77
import useSlopStore from '../hooks/useSlopStore';
88

99
interface SlopStatusListProps {
10-
list: {
11-
key: string;
12-
name: string;
13-
level: Level;
14-
isOpenDuringDay: boolean;
15-
isOpenDuringWeek: boolean;
16-
isOpenDuringNight: boolean;
17-
}[];
10+
slopes?: Slope[];
1811
}
1912

20-
const SlopStatusList = ({ list }: SlopStatusListProps) => {
13+
const SlopStatusList = ({ slopes }: SlopStatusListProps) => {
2114
const { selectedSlop, setSelectedSlop } = useSlopStore();
2215

2316
const handleSlopClick = ({ id }: { id: string }) => {
@@ -47,26 +40,26 @@ const SlopStatusList = ({ list }: SlopStatusListProps) => {
4740
</tr>
4841
</thead>
4942
<tbody>
50-
{list.map((item) => (
43+
{slopes?.map((slope) => (
5144
<tr
52-
key={item.key}
53-
className={cn(selectedSlop === item.key && 'bg-main-5')}
54-
onClick={() => handleSlopClick({ id: item.key })}
45+
key={slope.name}
46+
className={cn(selectedSlop === slope.name && 'bg-main-5')}
47+
onClick={() => handleSlopClick({ id: slope.name })}
5548
>
5649
<td className={cn('body1-semibold py-[12px] pl-5 text-left text-gray-80')}>
57-
{item.name}
50+
{slope.name}
5851
</td>
5952
<td className={cn('text-center')}>
60-
<LevelChip level={item.level} className={cn('m-auto')} />
53+
<LevelChip level={slope.difficulty} className={cn('m-auto')} />
6154
</td>
6255
<td className={cn('text-center')}>
63-
<StatusIcon isOpen={item.isOpenDuringDay} className={cn('m-auto')} />
56+
<StatusIcon isOpen={slope.isDayOperating} className={cn('m-auto')} />
6457
</td>
6558
<td className={cn('text-center')}>
66-
<StatusIcon isOpen={item.isOpenDuringWeek} className={cn('m-auto')} />
59+
<StatusIcon isOpen={slope.isNightOperating} className={cn('m-auto')} />
6760
</td>
6861
<td className={cn('pr-5 text-center')}>
69-
<StatusIcon isOpen={item.isOpenDuringNight} className={cn('m-auto')} />
62+
<StatusIcon isOpen={slope.isLateNightOperating} className={cn('m-auto')} />
7063
</td>
7164
</tr>
7265
))}

src/views/slop-status/ui/slop-status-page.tsx

+12-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ import SlopMap from '@/features/slop/ui/slop-map';
77
import SlopStatusList from '@/features/slop/ui/slop-status-list';
88
import { slopQueries } from '@/entities/slop/api';
99
import { RESORT_DOMAIN } from '@/entities/slop/model';
10+
import { slopeApi } from '@/entities/slope';
1011
import { cn } from '@/shared/lib';
1112

12-
const SlopStatusPage = ({ params }: { params: { key: keyof typeof RESORT_DOMAIN } }) => {
13+
const SlopStatusPage = ({
14+
params,
15+
resortId,
16+
}: {
17+
params?: { key: keyof typeof RESORT_DOMAIN };
18+
resortId?: number;
19+
}) => {
1320
const { ref, style, containerRef } = useMapPinch();
1421

15-
const { data } = useQuery(slopQueries.list(params.key));
22+
const { data: slopeData } = useQuery(slopeApi.slopeQueries.slope(resortId ?? 1));
23+
24+
const { data } = useQuery(slopQueries.list(params?.key ?? 'jisan'));
1625

1726
if (!data) return;
1827

@@ -27,7 +36,7 @@ const SlopStatusPage = ({ params }: { params: { key: keyof typeof RESORT_DOMAIN
2736
slops={RESORT_DOMAIN[data.key].slops}
2837
/>
2938
</section>
30-
<SlopStatusList list={data.slopes} />
39+
<SlopStatusList slopes={slopeData?.slopes} />
3140
</main>
3241
);
3342
};

src/views/webcam/ui/webcam-mobile-map-page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import calculateWebcamPosition from '@/features/slop/lib/calculateWebcamPosition
77
import type { Position, ResortInfo } from '@/entities/slop/model/model';
88
import { cn } from '@/shared/lib';
99

10-
const WebCamMobileMapPage = ({ data }: { data: ResortInfo }) => {
10+
const WebCamMobileMapPage = ({ data }: { resortId?: number; data: ResortInfo }) => {
1111
const [cameraPositions, setCameraPositions] = useState<{
1212
[key: string]: Position;
1313
}>({});

0 commit comments

Comments
 (0)