Skip to content

Commit

Permalink
Merge pull request #152 from prgrms-web-devcourse-final-project/113-f…
Browse files Browse the repository at this point in the history
…eature/register-dog-profile-api

[Feature] 반려견 정보 수정, 패밀리코드 초대 기능
  • Loading branch information
wonill authored Dec 9, 2024
2 parents c4e7329 + bfb19f3 commit 0d987d2
Show file tree
Hide file tree
Showing 42 changed files with 1,932 additions and 987 deletions.
1,474 changes: 737 additions & 737 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { Suspense, useState } from 'react'
import { Helmet, HelmetProvider } from 'react-helmet-async'
import { RouterProvider } from 'react-router-dom'
Expand All @@ -7,8 +9,6 @@ import { router } from '~/router'
import GlobalStyle from '~/styles/globalStyle'
import { darkTheme, lightTheme } from '~/styles/theme'
import PageLoader from '~components/PageLoader'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const queryClient = new QueryClient()
function App() {
Expand Down
46 changes: 25 additions & 21 deletions src/apis/dog/createDogProfile.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'
import { DogProfileType } from '~types/dogProfile'

interface CreateDogProfileResponse {
dogId: number
name: string
breed: string
birthDate: string
weight: number
gender: 'MALE' | 'FEMALE'
profileImg: File
isNeutered: 'TRUE' | 'FALSE'
familyId: number
comment: string
}
export type CreateDogProfileRequest = FormData

export type CreateDogProfileResponse = DogProfileType

export const createDogProfile = async (formData: FormData): Promise<APIResponse<CreateDogProfileResponse>> => {
/**
* 새로운 반려견 프로필을 생성합니다.
*/
export const createDogProfile = async (
req: CreateDogProfileRequest
): Promise<APIResponse<CreateDogProfileResponse>> => {
try {
const { data } = await axiosInstance.post<APIResponse<CreateDogProfileResponse>>('/dogs/create', formData, {
const { data } = await axiosInstance.post<APIResponse<CreateDogProfileResponse>>('/dogs/create', req, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>
const { response } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 등록 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
Expand Down
37 changes: 26 additions & 11 deletions src/apis/dog/deleteDogProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,42 @@ import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface DeleteDogProfileResponse {
export type DeleteDogProfileRequest = {
id: number
}

export type DeleteDogProfileResponse = {
data: Record<string, never>
}

export const deleteDogProfile = async (id: number): Promise<APIResponse<DeleteDogProfileResponse>> => {
/**
* 반려견 프로필을 삭제합니다.
*/
export const deleteDogProfile = async (
req: DeleteDogProfileRequest
): Promise<APIResponse<DeleteDogProfileResponse>> => {
try {
const { data } = await axiosInstance.delete<APIResponse<DeleteDogProfileResponse>>(`/dogs/${id}`)
const { data } = await axiosInstance.delete<APIResponse<DeleteDogProfileResponse>>(`/dogs/${req.id}`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>
const { response } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 삭제 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
Expand Down
52 changes: 28 additions & 24 deletions src/apis/dog/fetchDogProfile.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { APIResponse, CommonAPIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface DogProfileDetail {
dogId: number
name: string
breed: string
birthDate: string
weight: number
gender: 'MALE' | 'FEMALE'
profileImg: string
isNeutered: 'TRUE' | 'FALSE'
familyId: number
comment: string
type DogProfileDetail = Pick<
CommonAPIResponse,
'dogId' | 'name' | 'breed' | 'birthDate' | 'weight' | 'gender' | 'profileImg' | 'isNeutered' | 'familyId' | 'comment'
>

export type FetchDogProfileRequest = {
id: number
}

export interface FetchDogProfileResponse {
export type FetchDogProfileResponse = {
data: DogProfileDetail
}

export const fetchDogProfile = async (id: number): Promise<APIResponse<FetchDogProfileResponse>> => {
/**
* 반려견의 상세 프로필 정보를 조회합니다.
*/
export const fetchDogProfile = async (req: FetchDogProfileRequest): Promise<APIResponse<FetchDogProfileResponse>> => {
try {
const { data } = await axiosInstance.get<APIResponse<FetchDogProfileResponse>>(`/dogs/${id}`)
const { data } = await axiosInstance.get<APIResponse<FetchDogProfileResponse>>(`/dogs/${req.id}`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>
const { response } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 조회 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
Expand Down
48 changes: 48 additions & 0 deletions src/apis/dog/patchDogProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'
import { DogProfileType } from '~types/dogProfile'

export type PatchDogProfileRequest = FormData

export type PatchDogProfileResponse = DogProfileType

/**
* 반려견 프로필 정보를 수정합니다.
*/
export const patchDogProfile = async (
id: number,
req: PatchDogProfileRequest
): Promise<APIResponse<PatchDogProfileResponse>> => {
try {
const { data } = await axiosInstance.patch<APIResponse<PatchDogProfileResponse>>(`/dogs/${id}`, req, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}

throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
62 changes: 62 additions & 0 deletions src/apis/dog/useDogProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { createDogProfile } from '~apis/dog/createDogProfile'
import { useNavigate } from 'react-router-dom'
import { useModalStore } from '~stores/modalStore'
import { useToastStore } from '~stores/toastStore'
import { useDogProfileStore } from '~stores/dogProfileStore'
import { useSuspenseQuery } from '@tanstack/react-query'
import { fetchDogProfile } from '~apis/dog/fetchDogProfile'
import { queryKey } from '~constants/queryKey'
import { patchDogProfile, PatchDogProfileRequest } from '~apis/dog/patchDogProfile'
import ConfirmModal from '~modals/ConfirmModal'

export function useCreateDogProfile() {
const { setDogProfile } = useDogProfileStore()
const { pushModal, clearModal } = useModalStore()
const { showToast } = useToastStore()
const navigate = useNavigate()

const completeRegistration = () => {
navigate('/')
clearModal()
}

return useMutation({
mutationFn: (formData: FormData) => createDogProfile(formData),
onSuccess: response => {
setDogProfile({ ...response.data })
pushModal(<ConfirmModal content='반려견 등록이 완료되었습니다' onClick={completeRegistration} />)
},
onError: (error: Error) => {
showToast(error.message)
},
})
}

export function useFetchDogProfile(id: number) {
return useSuspenseQuery({
queryKey: queryKey.dog.profile(id),
queryFn: () => fetchDogProfile({ id }).then(res => res.data),
})
}

export function usePatchDogProfile(id: number) {
const queryClient = useQueryClient()
const { clearModal, pushModal } = useModalStore()
const { showToast } = useToastStore()

const completeRegistration = () => {
queryClient.invalidateQueries({ queryKey: queryKey.dog.profile(id) })
clearModal()
}

return useMutation({
mutationFn: (data: PatchDogProfileRequest) => patchDogProfile(id, data),
onSuccess: () => {
pushModal(<ConfirmModal content='반려견 정보가 수정되었습니다' onClick={completeRegistration} />)
},
onError: (error: Error) => {
showToast(error.message)
},
})
}
31 changes: 31 additions & 0 deletions src/apis/family/fetchInviteCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { AxiosError } from 'axios'
import { APIResponse, CommonAPIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

export type InviteCodeResponse = Pick<CommonAPIResponse, 'familyId' | 'inviteCode' | 'expiresInSeconds'>

export const fetchInviteCode = async (): Promise<APIResponse<InviteCodeResponse>> => {
try {
const { data } = await axiosInstance.get<APIResponse<InviteCodeResponse>>('/family/invite-code')
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<APIResponse<ErrorResponse>>
if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다')
case 401:
throw new Error(message || '인증에 실패했습니다')
case 500:
throw new Error(message || '서버 오류가 발생했습니다')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다')
}
}
throw new Error('네트워크 연결을 확인해주세요')
}
throw new Error('알 수 없는 오류가 발생했습니다')
}
}
36 changes: 36 additions & 0 deletions src/apis/family/fetchfamilyDogs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { AxiosError } from 'axios'
import { APIResponse, CommonAPIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'
import { DogProfileType } from '~types/dogProfile'

type FetchFamilyDogsRequest = Pick<CommonAPIResponse, 'inviteCode'>

export const fetchFamilyDogs = async (request: FetchFamilyDogsRequest): Promise<APIResponse<DogProfileType[]>> => {
try {
const { data } = await axiosInstance.post<APIResponse<DogProfileType[]>>('/family/dogs', request)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '유효하지 않은 코드입니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}

throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
Loading

0 comments on commit 0d987d2

Please sign in to comment.