Skip to content

Commit

Permalink
✨ add challenge API integration
Browse files Browse the repository at this point in the history
  • Loading branch information
w00khyung committed Dec 16, 2023
1 parent 2836a67 commit a04afc4
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 35 deletions.
17 changes: 12 additions & 5 deletions app/(route)/challenge/_components/ChallengeCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import Image from 'next/image'
import Link from 'next/link'

import RightArrowIcon from '@/app/_components/icons/RightArrowIcon'
import { ROUTE } from '@/app/_constants/route'
import { type Challenge } from '@/app/_service/challenge/challenge.types'

export default function ChallengeCard() {
interface ChallengeCardProps {
challenge: Challenge
}

export default function ChallengeCard({ challenge }: ChallengeCardProps) {
return (
<div className='flex items-center rounded-xl bg-[#D9D9D9] p-4'>
<Link href={ROUTE.CHALLENGE_DETAIL(challenge.id)} className='flex items-center rounded-xl bg-[#D9D9D9] p-4'>
<Image
className='mr-[10px] rounded-full bg-white'
src='https://dodals3.s3.ap-northeast-2.amazonaws.com/asset/profile_red.png'
Expand All @@ -14,13 +21,13 @@ export default function ChallengeCard() {
/>
<div className='flex flex-1 flex-col gap-1'>
<span className='w-fit rounded-sm bg-[#F6A0AB] p-1 text-center text-[10px]'>자기계발</span>
<span className='text-lg font-semibold text-[#595959]'>스페인어 공부</span>
<span className='text-[10px] text-[#595959]'>신상 텀블러</span>
<span className='text-lg font-semibold text-[#595959]'>{challenge.name}</span>
<span className='text-[10px] text-[#595959]'>{challenge.reward}</span>
<span>10%</span>
</div>
<button className='ml-[2px]'>
<RightArrowIcon />
</button>
</div>
</Link>
)
}
8 changes: 6 additions & 2 deletions app/(route)/challenge/_components/ChallengeFormDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ interface Inputs {
reward: string
}

export default function ChallengeFormDialog() {
const [isOpenDialog, setIsOpenDialog] = useState(false)
interface ChallengeFormDialogProps {
isOpenDialog: boolean
setIsOpenDialog: (isOpen: boolean) => void
}

export default function ChallengeFormDialog({ isOpenDialog, setIsOpenDialog }: ChallengeFormDialogProps) {
const [selectedCategory, setSelectedCategory] = useState<Category | '선택안함'>('선택안함')
const [isChecked, setIsChecked] = useState({
check1: false,
Expand Down
32 changes: 24 additions & 8 deletions app/(route)/challenge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,35 @@ import { cn } from '@/app/_styles/utils'

import ChallengeCard from './_components/ChallengeCard'
import ChallengeFormDialog from './_components/ChallengeFormDialog'
import { useChallengesQuery } from './queries'

export default function ChallengePage() {
const [tab, setTab] = useState<'ongoing' | 'completed'>('ongoing')
const [isOpenDialog, setIsOpenDialog] = useState(false)

const [tab, setTab] = useState<'inProgress' | 'completed'>('inProgress')

const challengesQuery = useChallengesQuery()

if (!challengesQuery.isSuccess) {
return null
}

const challenges = challengesQuery.data.data

const inProgressChallenges = challenges.filter((challenge) => challenge.challenge_status === 'PROGRESS')
const completedChallenges = challenges.filter((challenge) => challenge.challenge_status === 'SUCCESS')

const challengesByTab = tab === 'inProgress' ? inProgressChallenges : completedChallenges

return (
<div className='relative flex max-h-full flex-col gap-4 px-6 pt-[88px]'>
<div className='relative flex h-full max-h-full flex-col gap-4 px-6 pt-[88px]'>
<div className='flex w-full'>
<button
className={cn('flex h-12 w-1/2 items-center justify-center border-b border-white font-semibold', {
'border-b-[#595959] text-[#595959]': tab !== 'ongoing',
'border-b-[#595959] text-[#595959]': tab !== 'inProgress',
})}
onClick={() => {
setTab('ongoing')
setTab('inProgress')
}}
>
진행중
Expand All @@ -34,12 +50,12 @@ export default function ChallengePage() {
완료
</button>
</div>
<div className='flex flex-col gap-3 overflow-y-auto pb-20'>
{new Array(10).fill(0).map((_, index) => {
return <ChallengeCard key={index} />
<div className='flex flex-1 flex-col gap-3 overflow-y-auto pb-20'>
{challengesByTab.map((challenge) => {
return <ChallengeCard key={challenge.id} challenge={challenge} />
})}
</div>
<ChallengeFormDialog />
<ChallengeFormDialog isOpenDialog={isOpenDialog} setIsOpenDialog={setIsOpenDialog} />
</div>
)
}
15 changes: 13 additions & 2 deletions app/(route)/challenge/queries.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { useMutation } from '@tanstack/react-query'
import { useMutation, useQuery } from '@tanstack/react-query'

import { createChallenge } from '@/app/_service/challenge'
import { createChallenge, getChallenges } from '@/app/_service/challenge'

const QUERY_KEY = {
challenges: ['challenges'],
}

export const useCreateChallengeMutation = () => {
return useMutation({
mutationFn: createChallenge,
})
}

export const useChallengesQuery = () => {
return useQuery({
queryKey: QUERY_KEY.challenges,
queryFn: getChallenges,
})
}
43 changes: 40 additions & 3 deletions app/(route)/invitation/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
'use client'

import Image from 'next/image'
import { useRouter } from 'next/navigation'

import { ROUTE } from '@/app/_constants/route'

import { useApproveChallengeMutation, useUpcomingChallengeQuery } from './queries'

export default function InvitationPage({
params,
}: {
params: {
id: string
}
}) {
const router = useRouter()

const upcomingChallengeQuery = useUpcomingChallengeQuery({
challengeId: Number(params.id),
})

const approveChallengeMutation = useApproveChallengeMutation()

export default function InvitationPage() {
return (
<div className='flex min-h-[100dvh] flex-col items-center justify-center overflow-y-auto'>
<div className='relative aspect-[323/208] w-[323px]'>
<Image src='https://dodals3.s3.ap-northeast-2.amazonaws.com/asset/dodals.png' fill alt='' />
</div>
<div className='mt-8 flex flex-col justify-center'>
<div className='text-center'>
<span className='text-lg text-[#8A72FF]'>별빛우주</span>
<span className='text-lg text-[#8A72FF]'>{upcomingChallengeQuery.data?.data.nickname}</span>
<span className='text-lg'>님이 초대장을 보냈어요</span>
</div>
<span className='text-center text-lg'>목표를 위해 함께 달려볼까요?</span>
</div>
<div className='mt-20 flex w-full flex-col items-center gap-3'>
<button className='h-[56px] w-full max-w-[326px] rounded-[50px] bg-[#482BD9] text-lg font-bold'>좋아요!</button>
<button
className='h-[56px] w-full max-w-[326px] rounded-[50px] bg-[#482BD9] text-lg font-bold'
onClick={() => {
approveChallengeMutation.mutate(
{
challengeId: Number(params.id),
},
{
onSuccess: () => {
router.push(ROUTE.LOGIN)
},
}
)
}}
>
좋아요!
</button>
<button className='h-[56px] w-full max-w-[326px] rounded-[50px] bg-[#595959] text-lg font-semibold'>
아니요, 괜찮아요
</button>
Expand Down
22 changes: 22 additions & 0 deletions app/(route)/invitation/[id]/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useMutation, useQuery } from '@tanstack/react-query'

import { approveChallenge, getUpcomingChallenge } from '@/app/_service/challenge'

const QUERY_KEY = {
UPCOMING_CHALLENGE: (challengeId: number) => ['upcomingChallenge', challengeId],
}

export const useUpcomingChallengeQuery = ({ challengeId }: { challengeId: number }) => {
return useQuery({
queryKey: QUERY_KEY.UPCOMING_CHALLENGE(challengeId),
queryFn: () => {
return getUpcomingChallenge({ challengeId })
},
})
}

export const useApproveChallengeMutation = () => {
return useMutation({
mutationFn: approveChallenge,
})
}
1 change: 1 addition & 0 deletions app/_constants/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const ROUTE = {
LOGIN: '/login',
SIGN_UP: '/sign-up',
CHALLENGE: '/challenge',
CHALLENGE_DETAIL: (id: number) => `/challenge/${id}`,
ALARM: '/alarm',
ME: '/me',
OAUTH: {
Expand Down
23 changes: 8 additions & 15 deletions app/_service/challenge/challenge.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,19 @@ export interface GetUpcomingChallengeParams {
challengeId: number
}

export interface GetUpcomingChallengeResponse {
nickname: string
}

export type ChallengeStatus = 'WAITING' | 'PROGRESS' | 'FAILURE' | 'SUCCESS'

export interface Challenge {
id: number
name: string
category: Category
authentication_method: string
reward: string
is_deleted: 0
created_at: '2023-12-16T13:59:18.000Z'
updated_at: '2023-12-16T13:59:18.000Z'
target_count: 10
challenge_status: 'WAITING'
challenge_status: ChallengeStatus
}

export interface GetUpcomingChallengeResponse {
challengeId: number
name: string
category: Category
authenticationMethod: string
reward: string
champion: Champion
explorationCount: number
certificatedCount: number
}
export type GetChallengesResponse = Challenge[]
11 changes: 11 additions & 0 deletions app/_service/challenge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type GetTodayStatus,
type GetUpcomingChallengeParams,
type GetUpcomingChallengeResponse,
type GetChallengesResponse,
} from './challenge.types'

export const getTodayStatus = () => {
Expand All @@ -24,3 +25,13 @@ export const createChallenge = ({ name, category, authenticationMethod, reward }
export const getUpcomingChallenge = ({ challengeId }: GetUpcomingChallengeParams) => {
return api.get<GetUpcomingChallengeResponse>(`/challenges/upcoming/${challengeId}`)
}

export const approveChallenge = ({ challengeId }: { challengeId: number }) => {
return api.post(`/challenges/approve`, {
challengeId,
})
}

export const getChallenges = () => {
return api.get<GetChallengesResponse>('/challenges')
}

0 comments on commit a04afc4

Please sign in to comment.