Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 3 additions & 38 deletions src/apis/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,10 @@ import { CareCode } from './interceptor'
import {
PostLoginBody,
PostLoginResponse,
PostKakaoLoginBody,
PostKakaoLoginResponse,
PostRegisterBody,
PostRegisterResponse,
PostKakaoRegisterBody,
PostRefreshTokenResponse,
postKakaoLoginBodySchema,
postKakaoLoginResponseSchema,
postKakaoRegisterBodySchema,
postKakaoRegisterResponseSchema,
postLoginBodySchema,
postLoginResponseSchema,
postRefreshTokenResponseSchema,
postRegisterBodySchema,
postRegisterResponseSchema,
getKakaoAuthUrlResponseSchema,
GetKakaoAuthUrlResponse,
postKakaoAuthBodySchema,
Expand All @@ -36,29 +25,6 @@ export const postLogin = async (body: PostLoginBody): Promise<PostLoginResponse>
return postLoginResponseSchema.parse(res.data)
}

// /auth/kakao/login
export const postKakaoLogin = async (body: PostKakaoLoginBody): Promise<PostKakaoLoginResponse> => {
const parsedBody = postKakaoLoginBodySchema.parse(body)
const res = await CareCode.post('/auth/kakao/login', parsedBody)
return postKakaoLoginResponseSchema.parse(res.data)
}

// /auth/register
export const PostRegister = async (body: PostRegisterBody): Promise<PostRegisterResponse> => {
const parsedBody = postRegisterBodySchema.parse(body)
const res = await CareCode.post('/auth/register', parsedBody)
return postRegisterResponseSchema.parse(res.data)
}

// /auth/kakao/register
export const PostKakaoRegister = async (
body: PostKakaoRegisterBody,
): Promise<PostRegisterResponse> => {
const parsedBody = postKakaoRegisterBodySchema.parse(body)
const res = await CareCode.post('/auth/kakao/register', parsedBody)
return postKakaoRegisterResponseSchema.parse(res.data)
}

let refreshTimer: NodeJS.Timeout | null = null

/**
Expand Down Expand Up @@ -139,10 +105,9 @@ export async function refreshAccessToken(): Promise<PostRefreshTokenResponse> {
return parsed
}

// GET /oauth2/kakao/auth-url - 카카오 인증 URL 요청
export const getKakaoAuthUrl = async (redirectUri?: string): Promise<GetKakaoAuthUrlResponse> => {
const params = redirectUri ? { redirectUri } : {}
const res = await CareCode.get('/oauth2/kakao/auth-url', { params })
// GET /auth/kakao/login-url - 카카오 인증 URL 요청
export const getKakaoAuthUrl = async (): Promise<GetKakaoAuthUrlResponse> => {
const res = await CareCode.get('/auth/kakao/login-url')
return getKakaoAuthUrlResponseSchema.parse(res.data)
}

Expand Down
20 changes: 0 additions & 20 deletions src/apis/health.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {
HealthRecord,
healthRecordListSchema,
healthRecordSchema,
HealthStats,
healthStatsSchema,
RecordType,
UpdateHealthRecordBody,
updateHealthRecordBodySchema,
Expand Down Expand Up @@ -47,18 +45,6 @@ export const getHealthRecordsByType = async (
return healthRecordListSchema.parse(res.data)
}

// GET /health/records/date-range-asc - 기간 조회 (성장 기록 확인용)
export const getHealthRecordsByDateRange = async (
childId: number,
startDate: string,
endDate: string,
): Promise<HealthRecord[]> => {
const res = await CareCode.get('/health/records/date-range-asc', {
params: { childId, startDate, endDate },
})
return healthRecordListSchema.parse(res.data)
}

// PUT /health/records/{recordId}
export const putHealthRecord = async (
recordId: number,
Expand Down Expand Up @@ -125,12 +111,6 @@ export const getHealthAlerts = async (userId: string): Promise<HealthAlert[]> =>
return healthAlertListSchema.parse(res.data)
}

// GET /health/statistics
export const getHealthStatistics = async (userId: string): Promise<HealthStats> => {
const res = await CareCode.get('/health/statistics', { params: { userId } })
return healthStatsSchema.parse(res.data)
}

// GET /health/recommendations - 아이 월령 기준 추천
export const getHealthRecommendations = async (): Promise<HealthRecommendation> => {
const res = await CareCode.get('/health/recommendations')
Expand Down
7 changes: 0 additions & 7 deletions src/apis/hospital.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { z } from 'zod'
import { CareCode } from './interceptor'
import {
CreateHospitalReviewBody,
Expand Down Expand Up @@ -56,12 +55,6 @@ export const getPopularHospitals = async (limit = 10): Promise<Hospital[]> => {

// ==================== 찜 ====================

// GET /health/hospitals/{id}/likes - 총 개수만 필요할 때 (비로그인 화면)
export const getHospitalLikeCount = async (id: number): Promise<number> => {
const res = await CareCode.get(`/health/hospitals/${id}/likes`)
return z.number().parse(res.data)
}

// GET /health/hospitals/{id}/like-status - 내 찜 여부 + 총 개수
export const getHospitalLikeStatus = async (id: number): Promise<HospitalLikeStatus> => {
const res = await CareCode.get(`/health/hospitals/${id}/like-status`)
Expand Down
9 changes: 0 additions & 9 deletions src/apis/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,6 @@ export const getProfileCompletion = async (): Promise<GetProfileCompletionRespon
return getProfileCompletionResponseSchema.parse(res.data)
}

// PUT /users/{userId}/location - 위치 갱신 (주변 시설 추천에 사용)
export const putUserLocation = async (
userId: string,
latitude: number,
longitude: number,
): Promise<void> => {
await CareCode.put(`/users/${userId}/location`, null, { params: { latitude, longitude } })
}

// POST /auth/logout - 서버의 리프레시 토큰 세션 폐기
export const postLogout = async (): Promise<void> => {
await CareCode.post('/auth/logout')
Expand Down
23 changes: 17 additions & 6 deletions src/app/(without-tabs)/community/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
useToggleCommunityLike,
} from '@/queries/community'
import { useBlockUser, useReport } from '@/queries/moderation'
import { PostCommunityCommentBody } from '@/types/apis/community'
import { PostComment, PostCommunityCommentBody } from '@/types/apis/community'
import { formatDate } from '@/utils/date'

const CommunityDetail = (): JSX.Element => {
Expand Down Expand Up @@ -184,11 +184,7 @@ const CommunityDetail = (): JSX.Element => {
return (
<Comment
key={comment.commentId}
comment={{
author: comment.authorName,
content: comment.content,
timestamp: formatDate(comment.createdAt, 'MM/dd HH:mm'),
}}
comment={toCommentData(comment)}
className="w-full"
isSaving={isUpdatingComment}
onEdit={
Expand Down Expand Up @@ -317,3 +313,18 @@ const CommunityDetail = (): JSX.Element => {
}

export default CommunityDetail

/** 답글까지 화면용 모양으로 바꾼다. 서버는 답글을 부모 댓글의 replies 에 트리로 담아 준다. */
function toCommentData(comment: PostComment): {
author: string
content: string
timestamp: string
replies: ReturnType<typeof toCommentData>[]
} {
return {
author: comment.authorName,
content: comment.content,
timestamp: formatDate(comment.createdAt, 'MM/dd HH:mm'),
replies: comment.replies.map(toCommentData),
}
}
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function Home(): JSX.Element {
const handleKakaoLogin = () => {
getKakaoAuthUrl(undefined, {
onSuccess: (data) => {
window.location.href = data.authUrl
window.location.href = data.loginUrl
},
onError: (err) => {
console.error('카카오 인증 URL을 가져오지 못했습니다:', err)
Expand Down
24 changes: 22 additions & 2 deletions src/components/features/facility/BookingDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ interface BookingDialogProps {
/** date-time-local 값(yyyy-MM-ddTHH:mm)을 서버 LocalDateTime 형식으로 맞춘다. */
const toLocalDateTime = (value: string): string => (value.length === 16 ? `${value}:00` : value)

/**
* 방문 상담은 1시간으로 잡는다. 서버는 종료 시각이 필수(겹침 계산에 쓴다)인데 폼에는 종료 입력이 없어,
* 예약이 "예약 시작/종료 시간은 필수입니다" 로 항상 실패했다.
*/
const DEFAULT_VISIT_MINUTES = 60

/** 시간대 없는 LocalDateTime 문자열에 분을 더한다. 브라우저 로컬 시각 기준으로 계산하고 그대로 돌려준다. */
const addMinutes = (localDateTime: string, minutes: number): string => {
const date = new Date(localDateTime)
date.setMinutes(date.getMinutes() + minutes)
const pad = (n: number) => String(n).padStart(2, '0')
return (
`${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
`T${pad(date.getHours())}:${pad(date.getMinutes())}:00`
)
}

const BookingDialog = ({
isOpen,
facilityName,
Expand Down Expand Up @@ -49,11 +66,14 @@ const BookingDialog = ({
})

const submit = (values: PostFacilityBookBody) => {
const startTime = toLocalDateTime(values.startTime)
onSubmit({
...values,
childAge: Number(values.childAge),
startTime: toLocalDateTime(values.startTime),
endTime: values.endTime ? toLocalDateTime(values.endTime) : undefined,
startTime,
endTime: values.endTime
? toLocalDateTime(values.endTime)
: addMinutes(startTime, DEFAULT_VISIT_MINUTES),
})
}

Expand Down
7 changes: 6 additions & 1 deletion src/components/features/health/HealthRecordCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ interface HealthRecordCardProps {
onClick?: () => void
}

const TYPE_COLOR: Record<string, 'green' | 'blue' | 'purple' | 'yellow' | 'white'> = {
const TYPE_COLOR: Record<string, 'green' | 'blue' | 'purple' | 'yellow' | 'red' | 'white'> = {
VACCINATION: 'green',
CHECKUP: 'blue',
GROWTH: 'blue',
DENTAL: 'blue',
EYE: 'blue',
MEDICATION: 'purple',
ILLNESS: 'yellow',
SYMPTOM: 'yellow',
EMERGENCY: 'red',
OTHER: 'white',
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ describe('HealthRecordCard', () => {
expect(screen.getByText('예방접종')).toBeInTheDocument()
})

it('서버가 모르는 기록 종류는 원본 값이라도 보여준다', () => {
// 라벨 매핑에 없다고 빈칸이 되면 사용자가 무슨 기록인지 알 수 없다.
it('서버에만 있던 기록 종류(치과·응급 등)도 한글 라벨로 보여준다', () => {
render(<HealthRecordCard record={{ ...baseRecord, recordType: 'DENTAL' }} />)

expect(screen.getByText('DENTAL')).toBeInTheDocument()
expect(screen.getByText('치과')).toBeInTheDocument()
})

it('프런트가 모르는 새 기록 종류는 원본 값이라도 보여준다', () => {
// 라벨 매핑에 없다고 빈칸이 되면 사용자가 무슨 기록인지 알 수 없다.
render(<HealthRecordCard record={{ ...baseRecord, recordType: 'SOMETHING_NEW' }} />)

expect(screen.getByText('SOMETHING_NEW')).toBeInTheDocument()
})

it('측정값이 있으면 요약해서 보여준다', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/queries/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import {
export const useGetKakaoAuthUrlMutation = (): UseMutationResult<
GetKakaoAuthUrlResponse,
Error,
string | undefined
void
> => {
return useMutation({
mutationFn: (redirectUri?: string) => getKakaoAuthUrl(redirectUri),
mutationFn: () => getKakaoAuthUrl(),
})
}

Expand Down
Loading
Loading