Skip to content

Commit

Permalink
feat: 설질투표 백엔드 연결 (#25)
Browse files Browse the repository at this point in the history
* feat: Connect vote data api

* feat: Add post vote api

* fix: Fix rewrite api source

* fix: Add weski proxy to fix build error
  • Loading branch information
Najeong-Kim authored Aug 21, 2024
1 parent 5f33f22 commit 36478d6
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 68 deletions.
7 changes: 7 additions & 0 deletions src/entities/discovery/api/discovery.queries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { queryOptions } from '@tanstack/react-query';
import { getDiscoveries } from './get-discoveries';
import { getVote } from './get-vote';

export const discoveryQueries = {
all: () => ['discovery'],
Expand All @@ -10,4 +11,10 @@ export const discoveryQueries = {
queryKey: [...discoveryQueries.listQueryKey()],
queryFn: () => getDiscoveries(),
}),
voteQueryKey: (key: string) => [...discoveryQueries.all(), 'vote', key],
vote: (key: string) =>
queryOptions({
queryKey: discoveryQueries.voteQueryKey(key),
queryFn: () => getVote(key),
}),
};
8 changes: 8 additions & 0 deletions src/entities/discovery/api/get-vote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { apiClient } from '@/shared/api/base';
import type { Vote } from '../model';

export const getVote = async (key: string): Promise<Vote> => {
const result = await apiClient.get<Vote>(`/ski/${key}/snowmaking`);

return result;
};
1 change: 1 addition & 0 deletions src/entities/discovery/api/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { discoveryQueries } from './discovery.queries';
export { postVote } from './post-vote';
7 changes: 7 additions & 0 deletions src/entities/discovery/api/post-vote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { apiClient } from '@/shared/api/base';
import type { PostVoteRequest } from '../model';

export const postVote = async (key: string, body: PostVoteRequest) => {
const res = await apiClient.post<PostVoteRequest>(`/ski/${key}/snowmaking`, body);
return res;
};
15 changes: 15 additions & 0 deletions src/entities/discovery/api/use-post-vote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { discoveryApi } from '..';

export const usePostVote = (key: string) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: ({ isLike }: { isLike: boolean }) => discoveryApi.postVote(key, { isLike }),
async onSettled() {
await queryClient.invalidateQueries({
queryKey: discoveryApi.discoveryQueries.voteQueryKey(key),
});
},
});
};
96 changes: 48 additions & 48 deletions src/entities/discovery/model/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,6 @@ export const WeeklyWeatherData: WeeklyWeather[] = [
export const DiscoveryData: Discovery[] = [
{
id: 1,
name: '용평스키장 모나',
map: 'yongpyong',
slope: null,
url: {
bus: 'https://skibus.purplebus.co.kr/Yp/',
homepage: 'https://www.yongpyong.co.kr/kor/skiNboard/introduce.do',
},
weather: {
weather: 'sun',
temperature: -5,
description: '맑음',
},
weeklyWeather: WeeklyWeatherData,
},
{
id: 2,
name: '지산 리조트',
map: 'jisan',
slope: 8,
Expand All @@ -100,14 +84,30 @@ export const DiscoveryData: Discovery[] = [
},
weeklyWeather: WeeklyWeatherData,
},
{
id: 2,
name: '곤지암스키장',
map: 'gonjiam',
slope: 10,
url: {
bus: 'https://m.konjiamresort.co.kr/ski/pickupService.dev',
homepage: 'https://www.konjiamresort.co.kr/main.dev',
},
weather: {
weather: 'rain',
temperature: -4,
description: '비',
},
weeklyWeather: WeeklyWeatherData,
},
{
id: 3,
name: '에덴밸리 리조트',
map: 'eden',
name: '비발디파크',
map: 'vivaldipark',
slope: 10,
url: {
bus: 'http://m.newbusantour.co.kr/goods/goods_detail.asp?g_cd=2183',
homepage: 'https://www.edenvalley.co.kr/ski/View.asp?location=01',
bus: 'https://www.busline.co.kr/busline_22K28/reservation_input.html?title=%EB%85%B8%EC%84%A0%EC%84%A0%ED%83%9D%20%EB%B0%8F%20%EC%9D%B4%EC%9A%A9%EC%9E%90%EB%93%B1%EB%A1%9D',
homepage: 'https://www.sonohotelsresorts.com/complex_vp',
},
weather: {
weather: 'rain',
Expand All @@ -118,12 +118,12 @@ export const DiscoveryData: Discovery[] = [
},
{
id: 4,
name: '웰리힐리파크',
map: 'wellihilli',
name: '엘리시안 강촌',
map: 'elysian-gangchon',
slope: 10,
url: {
bus: 'https://www.wellihillipark.com/home/guide/map/shuttle',
homepage: 'https://www.wellihillipark.com/snowpark',
bus: 'https://gs.elysian.co.kr/gangchon/enjoyElysian/roadMap_free_230210_pop.asp',
homepage: 'https://www.elysian.co.kr/',
},
weather: {
weather: 'rain',
Expand All @@ -134,12 +134,12 @@ export const DiscoveryData: Discovery[] = [
},
{
id: 5,
name: '하이원스키장',
map: 'high1',
name: '웰리힐리파크',
map: 'wellihilli',
slope: 10,
url: {
bus: 'https://skibus.purplebus.co.kr/Hi/',
homepage: 'https://www.high1.com/ski/index.do',
bus: 'https://www.wellihillipark.com/home/guide/map/shuttle',
homepage: 'https://www.wellihillipark.com/snowpark',
},
weather: {
weather: 'rain',
Expand All @@ -150,12 +150,12 @@ export const DiscoveryData: Discovery[] = [
},
{
id: 6,
name: '곤지암스키장',
map: 'gonjiam',
name: '휘닉스파크',
map: 'phoenix',
slope: 10,
url: {
bus: 'https://m.konjiamresort.co.kr/ski/pickupService.dev',
homepage: 'https://www.konjiamresort.co.kr/main.dev',
bus: 'https://skibus.purplebus.co.kr/Pp/',
homepage: 'https://phoenixhnr.co.kr/page/main/pyeongchang?q%5BhmpgDivCd%5D=PP&page=1&size=4',
},
weather: {
weather: 'rain',
Expand All @@ -166,12 +166,12 @@ export const DiscoveryData: Discovery[] = [
},
{
id: 7,
name: '비발디파크',
map: 'vivaldipark',
name: '하이원스키장',
map: 'high1',
slope: 10,
url: {
bus: 'https://www.busline.co.kr/busline_22K28/reservation_input.html?title=%EB%85%B8%EC%84%A0%EC%84%A0%ED%83%9D%20%EB%B0%8F%20%EC%9D%B4%EC%9A%A9%EC%9E%90%EB%93%B1%EB%A1%9D',
homepage: 'https://www.sonohotelsresorts.com/complex_vp',
bus: 'https://skibus.purplebus.co.kr/Hi/',
homepage: 'https://www.high1.com/ski/index.do',
},
weather: {
weather: 'rain',
Expand All @@ -182,17 +182,17 @@ export const DiscoveryData: Discovery[] = [
},
{
id: 8,
name: '엘리시안 강촌',
map: 'elysian-gangchon',
slope: 10,
name: '용평스키장 모나',
map: 'yongpyong',
slope: null,
url: {
bus: 'https://gs.elysian.co.kr/gangchon/enjoyElysian/roadMap_free_230210_pop.asp',
homepage: 'https://www.elysian.co.kr/',
bus: 'https://skibus.purplebus.co.kr/Yp/',
homepage: 'https://www.yongpyong.co.kr/kor/skiNboard/introduce.do',
},
weather: {
weather: 'rain',
temperature: -4,
description: '',
weather: 'sun',
temperature: -5,
description: '맑음',
},
weeklyWeather: WeeklyWeatherData,
},
Expand All @@ -214,12 +214,12 @@ export const DiscoveryData: Discovery[] = [
},
{
id: 10,
name: '휘닉스파크',
map: 'phoenix',
name: '에덴밸리 리조트',
map: 'eden',
slope: 10,
url: {
bus: 'https://skibus.purplebus.co.kr/Pp/',
homepage: 'https://phoenixhnr.co.kr/page/main/pyeongchang?q%5BhmpgDivCd%5D=PP&page=1&size=4',
bus: 'http://m.newbusantour.co.kr/goods/goods_detail.asp?g_cd=2183',
homepage: 'https://www.edenvalley.co.kr/ski/View.asp?location=01',
},
weather: {
weather: 'rain',
Expand Down
2 changes: 1 addition & 1 deletion src/entities/discovery/model/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { DiscoveryData } from './constants';
export type { Weather, WeeklyWeather, Discovery } from './model';
export type { Weather, WeeklyWeather, Discovery, Vote, PostVoteRequest } from './model';
9 changes: 9 additions & 0 deletions src/entities/discovery/model/model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ export type Discovery = {
};
weeklyWeather: WeeklyWeather[];
};

export type Vote = {
totalNum: number;
likeNum: number;
};

export type PostVoteRequest = {
isLike: boolean;
};
31 changes: 21 additions & 10 deletions src/features/discovery-detail/ui/vote-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { useQuery } from '@tanstack/react-query';
import { useCallback, useState } from 'react';
import { toast } from 'sonner';
import { discoveryApi } from '@/entities/discovery';
import { usePostVote } from '@/entities/discovery/api/use-post-vote';
import { CheckIcon } from '@/shared/icons';
import { cn } from '@/shared/lib';
import {
Expand All @@ -13,19 +17,25 @@ import {
} from '@/shared/ui/dialog';

interface VoteDialogProps {
id: number;
trigger: React.ReactNode;
count: {
total: number;
voted: number;
};
}

const VoteDialog = ({ trigger, count }: VoteDialogProps) => {
const VoteDialog = ({ id, trigger }: VoteDialogProps) => {
const [isGood, setIsGood] = useState<boolean>(true);
const { data: voteData } = useQuery(discoveryApi.discoveryQueries.vote(id.toString()));

const handleVote = useCallback(() => {
console.log(isGood);
}, [isGood]);
const { mutateAsync } = usePostVote(id.toString());

const handleVote = useCallback(async () => {
try {
await mutateAsync({ isLike: isGood });
} catch (error) {
console.log(error);
} finally {
toast.success('투표가 완료되었습니다.');
}
}, [isGood, mutateAsync]);

return (
<Dialog>
Expand All @@ -36,8 +46,9 @@ const VoteDialog = ({ trigger, count }: VoteDialogProps) => {
<div className={cn('flex flex-col gap-1')}>
<DialogTitle>상태가 좋아요</DialogTitle>
<p className={cn('body1-semibold text-gray-60')}>
{count.total}명 중 <span className={cn('body1-bold text-main-1')}>{count.voted}</span>
명이 설질에 대해 투표했어요
{voteData?.totalNum}명 중{' '}
<span className={cn('body1-bold text-main-1')}>{voteData?.likeNum}</span>
명이 긍정적으로 투표했어요.
</p>
</div>
<DialogDescription>오늘같은 현장은 설질 괜찮을까요?</DialogDescription>
Expand Down
19 changes: 19 additions & 0 deletions src/pages/api/weski/[...path].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { API_URL } from '@/shared/config';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { path } = req.query;
const url = `${API_URL}/${(path as string[]).join('/')}`;

const response = await fetch(url, {
method: req.method,
headers: {
...(req.headers as Record<string, string>),
},
body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined,
});

const data = await response.json();

res.status(response.status).json(data);
}
3 changes: 1 addition & 2 deletions src/pages/discovery-detail/ui/discovery-detail-page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use client';

import Image from 'next/image';
import { useState } from 'react';
import { useCallback } from 'react';
import { useState, useCallback } from 'react';
import blind1 from '@public/blinds/blind_01.png';
import { DiscoveryContentTabList } from '@/widgets/discovery-detail/model/constants';
import DiscoverySummary from '@/widgets/discovery-detail/ui/discovery-summary';
Expand Down
10 changes: 5 additions & 5 deletions src/shared/api/base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { API_URL } from '@/shared/config';

export class ApiClient {
private baseUrl: string;

Expand All @@ -24,7 +22,7 @@ export class ApiClient {
endpoint: string,
queryParams?: Record<string, string | number>
): Promise<TResult> {
const url = new URL(endpoint, this.baseUrl);
const url = new URL('/api/weski' + endpoint, window.location.origin);

if (queryParams) {
Object.entries(queryParams).forEach(([key, value]) => {
Expand All @@ -46,7 +44,9 @@ export class ApiClient {
endpoint: string,
body: TData
): Promise<TResult> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
const url = new URL('/api/weski' + endpoint, window.location.origin);

const response = await fetch(url.toString(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand All @@ -58,4 +58,4 @@ export class ApiClient {
}
}

export const apiClient = new ApiClient(API_URL);
export const apiClient = new ApiClient('');
4 changes: 2 additions & 2 deletions src/widgets/discovery-detail/ui/discovery-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Card from '@/shared/ui/card';
import { DiscoverySummaryActionList } from '../model/constants';
import DiscoverySummaryAction from './discovery-summary-action';

const DiscoverySummary = ({ name, slope, url, weather }: Discovery) => {
const DiscoverySummary = ({ id, name, slope, url, weather }: Discovery) => {
return (
<div className={cn('flex w-full gap-[26px] px-[30px] pb-[34px] pt-[10px]')}>
<Card className={cn('flex h-[123px] flex-1 items-center justify-between pl-[30px] pr-6')}>
Expand All @@ -30,10 +30,10 @@ const DiscoverySummary = ({ name, slope, url, weather }: Discovery) => {
return (
<VoteDialog
key={action.name}
id={id}
trigger={
<DiscoverySummaryAction key={action.name} {...action} icon={<VoteIcon />} />
}
count={{ total: 100, voted: 50 }}
/>
);
} else {
Expand Down

0 comments on commit 36478d6

Please sign in to comment.