-
Notifications
You must be signed in to change notification settings - Fork 1
Fix/121 QA(1,2차 통합) 후 나온 메인페이지, 체험 등록/수정 페이지 이슈 해결 #133
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 7 commits
cddc2ac
f21c7a6
c44574a
777d90e
f4a197d
e9c62da
f926162
95fab3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| import Button from '@/components/Button'; | ||
| import { ACTIVITY_CATEGORIES, ActivityCategory } from '@/constants/categories'; | ||
| import cn from '@/lib/cn'; | ||
| import { useRef, useState, useEffect } from 'react'; | ||
|
|
||
| interface CategoryFilterProps { | ||
| selectedCategory: ActivityCategory; | ||
|
|
@@ -15,20 +16,78 @@ export default function CategoryFilter({ | |
| onChange, | ||
| className, | ||
| }: CategoryFilterProps) { | ||
| const scrollRef = useRef<HTMLDivElement | null>(null); | ||
| const [hasInteracted, setHasInteracted] = useState(false); | ||
| const [isDragging, setIsDragging] = useState(false); | ||
| const startX = useRef(0); | ||
| const scrollLeft = useRef(0); | ||
|
|
||
| const handleFirstInteraction = () => { | ||
| if (!hasInteracted) setHasInteracted(true); | ||
| }; | ||
|
|
||
| const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => { | ||
| setIsDragging(true); | ||
| startX.current = e.pageX - (scrollRef.current?.offsetLeft ?? 0); | ||
| scrollLeft.current = scrollRef.current?.scrollLeft ?? 0; | ||
| handleFirstInteraction(); | ||
| }; | ||
|
|
||
| const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => { | ||
| if (!isDragging || !scrollRef.current) return; | ||
| e.preventDefault(); | ||
| const x = e.pageX - scrollRef.current.offsetLeft; | ||
| const walk = (x - startX.current) * 1; | ||
| scrollRef.current.scrollLeft = scrollLeft.current - walk; | ||
| handleFirstInteraction(); | ||
| }; | ||
|
|
||
| const handleMouseUpOrLeave = () => { | ||
| setIsDragging(false); | ||
| }; | ||
|
|
||
| const handleScroll = () => { | ||
| handleFirstInteraction(); | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| const el = scrollRef.current; | ||
| if (!el) return; | ||
| el.addEventListener('scroll', handleScroll); | ||
| return () => el.removeEventListener('scroll', handleScroll); | ||
| }, []); | ||
|
Comment on lines
+19
to
+58
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. 🛠️ Refactor suggestion 드래그 스크롤 구현이 잘 되었지만 접근성을 개선하세요 드래그 스크롤 기능이 잘 구현되었고, 사용자 상호작용에 따른 그라데이션 오버레이 제어도 훌륭한 UX입니다. 하지만 접근성 측면에서 개선이 필요합니다. 정적 분석 도구의 경고를 해결하기 위해 다음과 같이 개선하세요: <div
ref={scrollRef}
className={cn(
'relative z-20 flex w-full gap-8 pr-15 overflow-x-auto whitespace-nowrap no-scrollbar cursor-grab active:cursor-grabbing select-none',
className,
)}
+ role="tablist"
+ tabIndex={0}
+ aria-label="카테고리 필터"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUpOrLeave}
onMouseLeave={handleMouseUpOrLeave}
+ onKeyDown={(e) => {
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
+ e.preventDefault();
+ const scrollAmount = 100;
+ if (scrollRef.current) {
+ scrollRef.current.scrollLeft += e.key === 'ArrowRight' ? scrollAmount : -scrollAmount;
+ handleFirstInteraction();
+ }
+ }
+ }}
>
🤖 Prompt for AI Agents |
||
|
|
||
| return ( | ||
| <div className={cn('relative flex w-full gap-8 overflow-x-auto whitespace-nowrap no-scrollbar', className)}> | ||
| {ACTIVITY_CATEGORIES.map((category) => ( | ||
| <Button | ||
| key={category} | ||
| className='flex-shrink-0 max-w-80 max-h-41 py-12 text-[16px] rounded-[15px]' | ||
| selected={selectedCategory === category} | ||
| variant='category' | ||
| onClick={() => onChange(category)} | ||
| > | ||
| {category} | ||
| </Button> | ||
| ))} | ||
| <div className='pointer-events-none absolute top-0 right-0 h-full w-100 bg-gradient-to-l from-white to-transparent' /> | ||
| <div className='relative w-full'> | ||
| {/* 스크롤 가능한 영역 */} | ||
| <div | ||
| ref={scrollRef} | ||
| className={cn( | ||
| 'relative z-20 flex w-full gap-8 pr-15 overflow-x-auto whitespace-nowrap no-scrollbar cursor-grab active:cursor-grabbing select-none', | ||
| className, | ||
| )} | ||
| onMouseDown={handleMouseDown} | ||
| onMouseMove={handleMouseMove} | ||
| onMouseUp={handleMouseUpOrLeave} | ||
| onMouseLeave={handleMouseUpOrLeave} | ||
| > | ||
| {ACTIVITY_CATEGORIES.map((category) => ( | ||
| <Button | ||
| key={category} | ||
| className='flex-shrink-0 max-w-80 max-h-41 py-12 text-[16px] rounded-[15px]' | ||
| selected={selectedCategory === category} | ||
| variant='category' | ||
| onClick={() => onChange(category)} | ||
| > | ||
| {category} | ||
| </Button> | ||
| ))} | ||
|
|
||
| {/* 그라데이션: 처음만 보이고 상호작용하면 사라짐 */} | ||
| {!hasInteracted && ( | ||
| <div className='md:hidden pointer-events-none absolute top-0 right-0 h-full w-[100px] bg-gradient-to-l from-white to-transparent z-10 transition-opacity duration-300' /> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -44,14 +44,14 @@ export default function ExperienceList({ keyword, isSearchMode }: ExperienceList | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const totalPage = Math.ceil(totalCount / 8); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <section className='max-w-1200 m-auto px-24 lg:px-0 pb-83'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <section className='max-w-1200 m-auto px-16 md:px-24 lg:px-0 pb-83'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 🔍 검색 모드일 때 문구 표시 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isSearchMode && keyword && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-left text-lg font-semibold ml-4 md:ml-0 mt-32"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="text-primary font-bold">"{keyword}"</span> (으)로 검색한 결과입니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="text-primary font-bold">{keyword}</span>(으)로 검색한 결과입니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-left text-sm font-normal ml-4 md:ml-0 mt-8 mb-16"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-left text-lg font-normal mt-8 mb-16"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 총 <span className="font-semibold">{totalCount}</span>개의 결과 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
56
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. 🧹 Nitpick (assertive) 검색 안내 문구에 접근성 힌트 추가 권장 검색 결과 변화가 동적으로 발생하므로, 스크린리더 사용자를 위해 -<p className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl">
+<p
+ className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl"
+ role="status"
+ aria-live="polite"
+>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {experiences.length === 0 && !isLoading && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -61,29 +61,34 @@ export default function ExperienceList({ keyword, isSearchMode }: ExperienceList | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {!isSearchMode && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className='flex justify-between items-center mb-40'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className='relative flex justify-between items-center mb-40 pr-120'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <CategoryFilter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectedCategory={selectedCategory} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(category) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSelectedCategory(category); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setCurrentPage(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Dropdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className='w-200' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder='가격' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options={SORT_OPTIONS} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const value = SORT_VALUE_MAP[label]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSortOption(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setCurrentPage(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* <div className=''> */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Dropdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className='absolute right-0 md:w-130' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buttonClassName='flex flex-row items-center justify-between gap-0 border-nomad border rounded-[15px] text-md py-9 px-15' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listboxClassName='px-0 py-0' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| optionClassName='pl-10 pr-0 py-9 text-md' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder='가격' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options={SORT_OPTIONS} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const value = SORT_VALUE_MAP[label]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSortOption(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setCurrentPage(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* </div> */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+64
to
88
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. 🧹 Nitpick (assertive) 절대 위치 Dropdown이 다른 요소를 가릴 가능성
플렉스 레이아웃을 그대로 활용하면 여유 padding 없이도 동일한 정렬이 가능합니다. -<div className='relative flex justify-between items-center mb-40 pr-120'>
+<div className='flex justify-between items-center mb-40'>
…
- <Dropdown
- className='absolute right-0 md:w-130'
+ <Dropdown
+ className='ml-auto w-[130px]'덧붙여, Lines 72 / 87의 주석 처리된 div 는 불필요하니 정리해주세요. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className='m-0'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className='m-0 pb-30 md:pb-150 lg:pb-100'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {!isSearchMode && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h2 className='text-xl md:text-3xl font-bold'>🛼 모든 체험</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
🧹 Nitpick (assertive)
애니메이션 구현 승인 및 접근성 개선 제안
framer-motion을 사용한 스태거드 애니메이션이 잘 구현되었습니다. 사용자 경험을 향상시키는 좋은 개선사항입니다.
다만, 접근성을 위해
prefers-reduced-motion미디어 쿼리를 고려하는 것이 좋겠습니다.📝 Committable suggestion
🤖 Prompt for AI Agents