-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 모임상세 정보 바인딩 작업 / date 유틸함수 refactor / 디폴트 이미지 상수 파일 생성 #180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ae6bfeb
f642d6d
4ca4667
062eeae
9d6465e
659cf3e
1210a75
fc146a6
94344ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // 나중에 회원 프로필, 모임 디폴트 시안 나오면 각각 '/public/default-images/' 경로에서 지정 | ||
| export const DEFAULT_PROFILE_IMAGE = | ||
| 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'; | ||
|
|
||
| export const DEFAULT_GROUP_IMAGE = | ||
| 'https://images.unsplash.com/photo-1705599359461-f99dc9e80efa?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,3 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { notFound, redirect } from 'next/navigation'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import axios from 'axios'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { CommonErrorResponse, CommonSuccessResponse } from '@/types/service/common'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -37,16 +35,6 @@ baseAPI.interceptors.response.use( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| return response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async (error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const status = error.response?.status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === 401) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirect('/signin?error=unauthorized'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === 404) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| notFound(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const errorResponse: CommonErrorResponse = error.response?.data || { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'about:blank', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: 'Network Error', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -56,6 +44,28 @@ baseAPI.interceptors.response.use( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| errorCode: 'NETWORK_ERROR', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const status = error.response?.status ?? errorResponse.status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isServer = typeof window === 'undefined'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === 401) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isServer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { redirect } = await import('next/navigation'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirect('/login'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.location.pathname === '/login') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw errorResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const currentPath = window.location.pathname + window.location.search; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+50
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 클라이언트 측 401 처리 시 코드 흐름 문제가 있습니다.
} else {
if (window.location.pathname === '/login') {
throw errorResponse;
}
const currentPath = window.location.pathname + window.location.search;
window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`;
+ return; // 리다이렉트 후 추가 실행 방지
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === 404) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isServer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { notFound } = await import('next/navigation'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| notFound(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw errorResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,27 +2,33 @@ | |
|
|
||
| import { useRouter } from 'next/navigation'; | ||
|
|
||
| import { useState } from 'react'; | ||
| import { useEffect, useState } from 'react'; | ||
|
|
||
| import Cookies from 'js-cookie'; | ||
|
|
||
| import { MeetupModal } from '@/components/pages/meetup/meetup-modal'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { useModal } from '@/components/ui/modal'; | ||
| import { GetGroupDetailsResponse } from '@/types/service/group'; | ||
|
|
||
| interface Props { | ||
| progress: { | ||
| current: number; | ||
| max: number; | ||
| }; | ||
| ownerInfo: { | ||
| name: string; | ||
| }; | ||
| members: { | ||
| name: string; | ||
| }[]; | ||
| conditions: Pick< | ||
| GetGroupDetailsResponse, | ||
| 'userStatus' | 'createdBy' | 'participantCount' | 'maxParticipants' | ||
| >; | ||
| groupId: string; | ||
| } | ||
|
|
||
| export const MeetupButtons = ({ progress: { current, max }, ownerInfo }: Props) => { | ||
| const [isJoined, _] = useState(true); | ||
| export const MeetupButtons = ({ | ||
| conditions: { | ||
| userStatus: { isJoined }, | ||
| createdBy, | ||
| participantCount, | ||
| maxParticipants, | ||
| }, | ||
| groupId, | ||
| }: Props) => { | ||
| const [isHost, setIsHost] = useState<boolean | null>(null); | ||
| const { open } = useModal(); | ||
| const { push } = useRouter(); | ||
|
|
||
|
|
@@ -31,36 +37,41 @@ export const MeetupButtons = ({ progress: { current, max }, ownerInfo }: Props) | |
| push('/message/id'); | ||
| }; | ||
|
|
||
| // 방 주인이냐 | ||
| const isOwner = ownerInfo.name === '본인 계정 닉네임 ㅇㅇ'; | ||
| useEffect(() => { | ||
| const sessionId = Number(Cookies.get('userId')); | ||
| // eslint-disable-next-line react-hooks/set-state-in-effect | ||
| setIsHost(sessionId === createdBy.userId); | ||
| }, [createdBy]); | ||
|
Comment on lines
+40
to
+44
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
useEffect(() => {
- const sessionId = Number(Cookies.get('userId'));
- // eslint-disable-next-line react-hooks/set-state-in-effect
- setIsHost(sessionId === createdBy.userId);
+ const userIdCookie = Cookies.get('userId');
+ if (userIdCookie) {
+ const sessionId = Number(userIdCookie);
+ setIsHost(sessionId === createdBy.userId);
+ } else {
+ setIsHost(false);
+ }
}, [createdBy]);🤖 Prompt for AI Agents |
||
|
|
||
| // 모임의 참가자라면 -> (모임 탈퇴 + 채팅 입장) 버튼 | ||
| // 본인이 생성한 방이면 -> (모임 취소 + 채팅 입장) 버튼 | ||
| if (isJoined) { | ||
| return ( | ||
| <div className='sticky bottom-[56px] border-t-1 border-gray-200 bg-white px-4 py-3'> | ||
| return ( | ||
| <div className='sticky bottom-[56px] border-t-1 border-gray-200 bg-white px-4 py-3'> | ||
| {isJoined ? ( | ||
| <div className='flex gap-[10px]'> | ||
| <Button | ||
| className='flex-[1.2]' | ||
| variant='tertiary' | ||
| onClick={() => open(<MeetupModal type={isOwner ? 'cancel' : 'leave'} />)} | ||
| onClick={() => | ||
| open(<MeetupModal groupId={groupId} type={isHost ? 'delete' : 'cancel'} />) | ||
| } | ||
| > | ||
| {isOwner ? '모임 취소' : '모임 탈퇴'} | ||
| {isHost ? '모임 취소' : '모임 탈퇴'} | ||
| </Button> | ||
| <Button className='flex-2' disabled={current >= max} onClick={onEnterChatClick}> | ||
| <Button | ||
| className='flex-2' | ||
| disabled={participantCount >= maxParticipants} | ||
| onClick={onEnterChatClick} | ||
| > | ||
| 채팅 입장 | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| // 방 주인 아니고 참가자도 아닐때 -> 참여 버튼 | ||
| return ( | ||
| <div className='sticky bottom-[56px] border-t-1 border-gray-200 bg-white px-4 py-3'> | ||
| <Button disabled={current >= max} onClick={() => open(<MeetupModal type='join' />)}> | ||
| 참여하기 | ||
| </Button> | ||
| ) : ( | ||
| <Button | ||
| disabled={participantCount >= maxParticipants} | ||
| onClick={() => open(<MeetupModal groupId={groupId} type='attend' />)} | ||
| > | ||
| 참여하기 | ||
| </Button> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { GetGroupDetailsResponse } from '@/types/service/group'; | ||
|
|
||
| interface Props { | ||
| detail: GetGroupDetailsResponse['description']; | ||
| } | ||
|
|
||
| export const DescriptionDetail = ({ detail }: Props) => { | ||
| return ( | ||
| <div className='mt-6'> | ||
| <p className='text-text-md-regular break-keep text-gray-800'>{detail}</p> | ||
| </div> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
외부 URL 의존성에 대한 리스크를 추적하세요.
현재 Unsplash 외부 URL을 사용하고 있어, 해당 이미지가 삭제되거나 변경될 경우 문제가 발생할 수 있습니다. 주석에서 언급한 대로 로컬 이미지로 교체하는 작업을 이슈로 추적하는 것을 권장합니다.
이 작업을 추적하기 위한 이슈를 생성해 드릴까요?
🤖 Prompt for AI Agents