-
Notifications
You must be signed in to change notification settings - Fork 5
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
[FE] 행사 삭제 기능 구현 #903
base: fe-dev
Are you sure you want to change the base?
[FE] 행사 삭제 기능 구현 #903
Conversation
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.
복잡한 기능을 구현하느라 고생많았습니다 쿡키~~ 이제 이미 지난 행사를 삭제할 수 있게 되었네요 ! 👍
createdEvents: CreatedEvent[]; | ||
} | ||
|
||
const PageInner = () => { |
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.
이 파일에 꽤 많은 컴포넌트가 있는 것 같아서 분리해도 좋을 것 같아요..!
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.
CreatedEventList를 한 곳에서 밖에 쓰지 않아서 한 파일에 몰아뒀었는데 가독성 측면에서 분리하는 것이 좋겠네요
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.
저도 동의합니당~ 원래 CreatedEvent
에서는 CreatedEvent, CreatedEventList가 모두 있었는데, CreatedEventList가 없어져서 뭔가 했더니 일로 이사했군요
|
||
const handleMode = (mode: Mode) => setMode(mode); | ||
|
||
const has = (event: CreatedEvent) => { |
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.
더 의미를 포함해서 이름을 작성하는게 어떨까요..!? 다른 페이지에서 이 함수 자체만 처음 봤었는데 js의 내장 함수인가 라는 생각이 들었어요,,, !
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.
동의합니둥~
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.
그럴 수도 있겠네요;; 이미 선택 됐는지 판별하는 함수 의미에 맞게 변경했어요
...options, | ||
...coordinate, | ||
})} | ||
onAnimationStart={() => setCoordinate(prev => prev && {...prev, size: 800})} |
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.
사이즈가 800인 이유가 무엇인가요?!
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.
size가 300일 때
2025-01-07-13-50-22.mp4
size가 800일 때
2025-01-07-13-51-13.mp4
애니메이션이 차지하는 영역이 달라지도록 설정했어요.
그래서 전체 영역이 선택되도록 충분히 숫자를 크게 설정한 값이 800이었습니다
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.
어떤 디바이스로 확인하던 간에 size는 800인가용? 그렇다면 size가 화면 비율에 따라 채워지는 형식이 더 좋을 것 같아서요! 물론 width나 height에서 size계산이 좀 복잡할 것 같긴 하지만요...
return css({ | ||
position: 'absolute', | ||
top: `${remY - remSize / 2}rem`, | ||
left: `${remX - remSize / 2}rem`, | ||
width: `${remSize}rem`, | ||
height: `${remSize}rem`, | ||
background: theme.colors[animationColor], | ||
borderRadius: `50%`, | ||
transform: 'scale(1)', | ||
animation: `${animationFrame} ${animationTime}s ease-out forwards`, | ||
}); |
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.
오 혹시 이 css는 어떤 애니메이션인가요?
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.
ease-out은 처음은 빠르게 시작해서 마지막에 느리게 끝나는 애니메이션이고 forwards는 애니메이션 종료 후에 마지막 상태를 유지하기 위한 애니메이션이에요.
초기에는 scale 0이었다가 scale 1로 변하면서 opacity가 0이 되는 애니메이션을 위에 대입했다고 생각하면 됩니다
const {theme} = useTheme(); | ||
return ( | ||
<label css={checkboxStyle}> | ||
<div css={inputGroupStyle({theme, isChecked})}> | ||
{isChecked ? <Icon iconType="check" iconColor="onPrimary" className="check-icon" /> : null} | ||
<input type="checkbox" checked={isChecked} onChange={onChange} className="checkbox-input" /> | ||
</div> | ||
<Text size="bodyBold">{labelText}</Text> | ||
{!hideLabelText && <Text size="bodyBold">{labelText}</Text>} |
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.
labelText가 있는 경우에만 Text를 렌더링하도록 하면 hideLabelText를 없앨 수 있을 것 같아요!
{labelText && <Text>{labelText}</Text>}
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.
오~ 그렇네요!! 바로 수정해놓을게요
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.
고생 많았습니다~! 뭐 크게 변경할건 없을 것 같고, useLongPressAnimation
에 대한 제 생각만 구구절절 한번 늘어놔봤어요... ㅋㅋㅋ 한번 생각만 해 줘 보시면 감사하겠습니다~!
|
||
const handleMode = (mode: Mode) => setMode(mode); | ||
|
||
const has = (event: CreatedEvent) => { |
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.
동의합니둥~
createdEvents: CreatedEvent[]; | ||
} | ||
|
||
const PageInner = () => { |
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.
저도 동의합니당~ 원래 CreatedEvent
에서는 CreatedEvent, CreatedEventList가 모두 있었는데, CreatedEventList가 없어져서 뭔가 했더니 일로 이사했군요
|
||
return { | ||
handleTouchStart, | ||
handleTouchMove: handleTouchEnd, |
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.
TouchMove를 TouchEnd로 동일하게 또 내보내는 이유가 있나욘?
const LongPressAnimation = useMemo(() => { | ||
return coordinate ? ( | ||
<div | ||
css={animationStyle({ | ||
theme, | ||
...options, | ||
...coordinate, | ||
})} | ||
onAnimationStart={() => setCoordinate(prev => prev && {...prev, size: 800})} | ||
/> | ||
) : null; | ||
}, [coordinate]); | ||
|
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.
사실 이 useLongPressAnimation
hook 과 return들에 대해서 이렇게 복잡해야만 하나? 라는 의문이 드는 것은 사실이에요 ㅜㅜ
일단 이대로 구현한다고 했을 때, 예상되는 문제가 몇개를 말씀드려볼게요
-
LongPressAnimation을 어디에서나 어떤 component에서도 쉽게 재활용 하려고 hook으로 분리한 것으로 보이는데, 사실 이게 부모로서 children요소와 ref들을 받아 해당 Element에 변형을 주는게 아니라, 별도의 div를 그 위에 씌우는 방식으로 동작하고 있는것으로 이해돼요. 하지만 이런 작동방식을 구현한다면 단순히 div를 return하는 component 형식으로 만들 수 있을 것 같아요. 해당 div component 안에서도 좌표를 구할 수 있고, return하는 함수들도 callback을 인자로 받는 component prop을로 받을 수 있겠다는 생각... 아무튼. 다양한 상황에서 대응할 수 있을지 의문입니다! 예를 들어 쿠키가 지금
size:800
을 할당해주었지만, 전체화면에서 본다면 animation 속도로 인해서 어차피 800크기까지 가기전에 animation이 끝나더라구요 -
pc/mobile에서 touch와 click의 차이로 인해서 발생하는 문제가 있을 것으로 보여요. 예를 들면, pc에서는 아에 사용하지 못한다거나, mobile에서는 click이 존재하지 않기 때문에, 꾹 누르고 있는게 아니라, 터치를 하는 것 자체로도 터치된 순간만큼 animation이 발생할 것으로 보여집니다! PC는 우리가 크게 생각하지 않기 때문에 중요도를 낮다고 둬도, mobile 환경에서 클릭마다 애니메이션이 생기는건 적절하지 않을 것 같아요
-
2번에서 나온 추가적인 생각으로, 클릭만 해도 애니메이션이 나오고 있는데, 편집모드에 이미 들어간 상황에서도 이 longPress가 과연 필요할지 궁금해요. 이미 편집모드에서는 요소를 클릭하기만 해도, 체크가 되는데 longPress와 함께 있어야 하나 싶은 생각이 듭니다!
뭐 이대로 고쳐달라!! 라는 의미가 아니라, 당연히 쿠키가 구현한 부분이고, 더 많은 이해도를 가지고 깊은 고민을 했을 것이라고 생각합니다! 그냥 쿠키가 구현한 부분들에 대해서 제 생각들을 가지고 한번 더 생각해 봤으면 좋겠다는 의미에욘 :) 가볍게 생각만 해주셔도 좋을 것 같습니다
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.
구현해주신 행사 삭제하기 기능 잘 확인했어요 쿠키! 저번 회의때 말한 꾹- 눌렀을 때 편집 모드로 들어가는 것도 살려주셨구 각 행사 관리페이지에서 행사 삭제도 가능하게 해주셨네요! 고마워요ㅎㅎ
다만, 각 행사의 관리 페이지에서 행사를 삭제하는 서비스 로직에 대해 논의하고 싶은게 있어서 RC 남겨둬요! 회의때 다 같이 이야기해봐요 ㅎㅎ
> | ||
<Input inputType="search" value={eventName} onChange={onSearch} placeholder={placeholder} /> | ||
{createdEvents.length !== 0 && | ||
createdEvents.map(createdEvent => ( |
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.
나중에 생성된 이벤트들을 목록에서 보여줄때 정산중인 행사들이 시간순으로 상단에 오면 좋을 것 같아요!
...options, | ||
...coordinate, | ||
})} | ||
onAnimationStart={() => setCoordinate(prev => prev && {...prev, size: 800})} |
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.
어떤 디바이스로 확인하던 간에 size는 800인가용? 그렇다면 size가 화면 비율에 따라 채워지는 형식이 더 좋을 것 같아서요! 물론 width나 height에서 size계산이 좀 복잡할 것 같긴 하지만요...
@@ -49,6 +51,11 @@ const AdminPage = () => { | |||
navigate(`/event/${eventId}/admin/add-bill`); | |||
}; | |||
|
|||
const deleteEventAndNavigateLandingPage = async () => { | |||
navigate('/', {replace: true}); |
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.
이 생성된 행사를 편집. 즉, 삭제한다는 기능은 회원만 가능한 기능이에요. 그렇기 때문에 '관리'페이지에서 행사를 삭제한다면 마이페이지의 생성된 행사 목록 페이지
로 이동하는게 더 옳다고 생각이 듭니다!
@@ -60,6 +67,7 @@ const AdminPage = () => { | |||
<DropdownButton text="전체 참여자 관리" onClick={navigateEventMemberManage} /> | |||
<DropdownButton text="계좌번호 입력하기" onClick={navigateAccountInputPage} /> | |||
<DropdownButton text="사진 첨부하기" onClick={navigateAddImages} /> | |||
<DropdownButton text="행사 삭제하기" onClick={deleteEventAndNavigateLandingPage} /> |
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.
행사 삭제하기 기능은 회원만 사용기 가능하도록 제한을 두어야 할 것 같아요! 즉, 비회원은 사용 불가능하게 해당 dropdown 버튼이 보여서는 안된다고 생각합니당!
issue
구현 사항
행사 삭제 플로우 소개
행사를 지우는 방법은 크게 두 가지가 있습니다.
행사 목록에서 일괄 삭제는 사진과 같은 흐름을 거칩니다.
편집하기를 눌러 편집 모드로 화면을 바꾼 뒤 지우고 싶은 행사를 선택하여 삭제하기를 누르면 됩니다.
다른 방식으로는 리스트를 꾹 눌러 체크박스를 활성화하는 방식이 있습니다
2025-01-03.21-00-53.online-video-cutter.com.mp4
관리 페이지의 햄버거 버튼을 누르고 행사 삭제하기 버튼을 누르면 행사가 삭제되고 랜딩 페이지로 이동합니다.
2025-01-03.21-03-57.online-video-cutter.com.mp4
Long Press Animation 기능 구현
행사 목록에 적용한 기능으로 항목을 꾹 눌렀을 때 작동하는 애니메이션 useLongPressAnimation hook을 구현했습니다.
useLongPressAnimation 훅은 필수 인자인 onLongPress와 option을 받습니다.
onLongPress는 길게 눌렀을 때 실행시키고 싶은 콜백을 전달하면 됩니다.
option은 두 가지를 넣을 수 있습니다.
longPressTime은 애니메이션과 콜백이 실행될 때까지 누르고 있어야 하는 시간입니다. 기본 값은 0.5s입니다.
animationColor는 애니메이션 발생 시 나타나는 색입니다. 기본 값은 'grayContainer'입니다.
hook의 반환 값은 4가지가 있습니다.
handleTouchStart은 터치 이벤트가 시작할 때 실행되는 콜백입니다. 터치한 좌표 x, y 를 찾고 그 위치부터 애니메이션이 시작됩니다. 그리고 longPressTime 뒤에 onLongPress 콜백이 실행됩니다.
handleTouchEnd은 터치 이벤트가 끝날 때 실행되는 콜백입니다. setTimeout을 초기화하고 좌표 state를 초기화합니다.
handleTouchMove는 터치 이벤트 좌표가 움직일 때 실행되는 콜백입니다. 시간이 지나지 않을 때 터치가 움직인다면 콜백을 실행하지 않게 합니다.
이 3개의 콜백을 애니메이션을 발생 시키고 싶은 태그의 onTouchStart, onTouchEnd, onTouchMove에 넣어주면 됩니다.
LongPressAnimation은 애니메이션이 보이는 태그이며 위 태그 하위에 아래와 같이 호출하면 됩니다.
논의하고 싶은 점
행사 페이지 내부에서 행사 삭제 시 너무 한 번에 삭제되어 버리는 문제
행사 목록에서는 체크박스를 선택할 수 있고 삭제하기를 눌러서 삭제가 되는 흐름을 보면 여기서의 삭제는 충분히 사용자가 의도를 갖고 삭제를 한다고 가정할 수 있습니다. 그러나 이벤트 페이지 행사 삭제의 흐름(위의 영상)을 보면 햄버거를 누를 때 여러 보기가 나오고 "행사 삭제하기"를 누르게 되면 바로 삭제가 됩니다. 관리자가 다른 탭을 누르고 싶었지만 실수로 삭제를 눌러서 사라지는 참사가 일어날 수 있지 않을까 우려가 됩니다.
🫡 참고사항