diff --git a/src/features/discovery-detail/lib/formatDate.ts b/src/features/discovery-detail/lib/formatDate.ts new file mode 100644 index 0000000..1b4ec19 --- /dev/null +++ b/src/features/discovery-detail/lib/formatDate.ts @@ -0,0 +1,6 @@ +export const formatDate = (date: Date) => { + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, '0'); + return `${year}-${month}-${day}`; +} \ No newline at end of file diff --git a/src/features/discovery-detail/lib/vote.ts b/src/features/discovery-detail/lib/vote.ts new file mode 100644 index 0000000..d51b3fe --- /dev/null +++ b/src/features/discovery-detail/lib/vote.ts @@ -0,0 +1,21 @@ +import { formatDate } from "./formatDate"; + +export const getVoteData = () => { + const voteData = localStorage.getItem('vote_data'); + return voteData ? JSON.parse(voteData) : {}; +} + +export const saveVoteData = (voteData: Record[]) => { + localStorage.setItem('vote_data', JSON.stringify(voteData)); +} + +export const canVote = (resortId: string) => { + const voteData = getVoteData(); + const todayDate = formatDate(new Date()); + + if (voteData[resortId] === todayDate) { + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/features/discovery-detail/ui/vote-dialog.tsx b/src/features/discovery-detail/ui/vote-dialog.tsx index ccbda26..bf02311 100644 --- a/src/features/discovery-detail/ui/vote-dialog.tsx +++ b/src/features/discovery-detail/ui/vote-dialog.tsx @@ -15,7 +15,9 @@ import { DialogTitle, DialogTrigger, } from '@/shared/ui/dialog'; +import { formatDate } from '../lib/formatDate'; import { getVoteText } from '../lib/getVoteText'; +import { canVote, getVoteData, saveVoteData } from '../lib/vote'; interface VoteDialogProps { id: number; @@ -29,14 +31,21 @@ const VoteDialog = ({ id, trigger }: VoteDialogProps) => { const { mutateAsync } = usePostVote(id.toString()); const handleVote = useCallback(async () => { + if (!canVote(id.toString())) { + toast.error('하루에 한 번만 투표할 수 있어요'); + return; + } try { await mutateAsync({ isLike: isGood }); } catch (error) { console.log(error); } finally { + const voteData = getVoteData(); + voteData[id.toString()] = formatDate(new Date()); + saveVoteData(voteData); toast.success('고마워요! 투표의 결과가 반영되었어요'); } - }, [isGood, mutateAsync]); + }, [id, isGood, mutateAsync]); return ( diff --git a/src/views/discovery-detail/ui/discovery-detail-page.tsx b/src/views/discovery-detail/ui/discovery-detail-page.tsx index dcd6372..69223e2 100644 --- a/src/views/discovery-detail/ui/discovery-detail-page.tsx +++ b/src/views/discovery-detail/ui/discovery-detail-page.tsx @@ -9,7 +9,9 @@ import { DiscoveryContentTabList } from '@/widgets/discovery-detail/model/consta import DiscoverySummary from '@/widgets/discovery-detail/ui/discovery-summary'; import { Header } from '@/widgets/header/ui'; import { WebcamMap, WebcamSlopList } from '@/widgets/webcam/ui'; +import { formatDate } from '@/features/discovery-detail/lib/formatDate'; import { getVoteText } from '@/features/discovery-detail/lib/getVoteText'; +import { canVote, getVoteData, saveVoteData } from '@/features/discovery-detail/lib/vote'; import AppDownloadDialog from '@/features/discovery-detail/ui/app-download-dialog'; import useMapPinch from '@/features/slop/hooks/useMapPinch'; import calculateWebcamPosition from '@/features/slop/lib/calculateWebcamPosition'; @@ -55,14 +57,21 @@ const DiscoveryDetailPage = ({ params }: { params: { resortId: string } }) => { }; const handleVote = useCallback(async () => { + if (!canVote(params?.resortId)) { + toast.error('하루에 한 번만 투표할 수 있어요'); + return; + } try { await mutateAsync({ isLike: isGood }); } catch (error) { console.log(error); } finally { + const voteData = getVoteData(); + voteData[params?.resortId] = formatDate(new Date()); + saveVoteData(voteData); toast.success('고마워요! 투표의 결과가 반영되었어요'); } - }, [isGood, mutateAsync]); + }, [isGood, mutateAsync, params?.resortId]); if (!discovery) return;