-
Notifications
You must be signed in to change notification settings - Fork 1
Refactor/#79 코드 가독성을 위한 리팩토링 #80
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
6226276
bf13677
d5908da
70e4b5f
924b77f
f28aa78
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,29 @@ | ||
| 'use client' | ||
|
|
||
| import { Skeleton } from '@heroui/react' | ||
| import { Column, Flex } from '@repo/ui/components/Layout' | ||
| import { cn } from '@repo/ui/utils/cn' | ||
|
|
||
| type Props = { | ||
| count?: number | ||
| } | ||
|
|
||
| export const PlaceListItemSkeleton = ({ count = 3 }: Props) => { | ||
| return ( | ||
| <div className={cn('w-full', 'flex flex-col gap-4')}> | ||
| {Array.from({ length: count }).map((_, index) => ( | ||
| <Column | ||
| key={index} | ||
| className='border-b-1 w-full gap-3 border-gray-50 pb-4 pt-2.5' | ||
| > | ||
| <Skeleton className='w-18 h-5 rounded-full' /> | ||
| <Skeleton className='w-26 h-4 rounded-full' /> | ||
| <Flex className={'gap-1'}> | ||
| <Skeleton className='w-18 h-6 rounded-full' /> | ||
| <Skeleton className='w-18 h-6 rounded-full' /> | ||
| </Flex> | ||
| </Column> | ||
| ))} | ||
| </div> | ||
| ) | ||
| } |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,51 @@ | ||
| 'use client' | ||
|
|
||
| import { Suspense } from 'react' | ||
| import { useSuspenseQuery } from '@tanstack/react-query' | ||
| import { useCampusStore } from '@/_store/campus' | ||
| import { usePlaceQueries } from '@/_apis/queries/place' | ||
| import type { IconType } from '@repo/ui/components/Icon' | ||
| import type { BasePlace } from '@/_apis/schemas/place' | ||
| import type { RankingPlaceSort } from '@/_apis/schemas/place' | ||
| import { Column } from '@repo/ui/components/Layout' | ||
| import { SubTitle } from '@/_components/SubTitle' | ||
| import { PlaceListItem } from '@/_components/PlaceListItem' | ||
|
|
||
| type Props = { | ||
| title: string | ||
| icon: IconType | ||
| places: BasePlace[] | ||
| rankingPlaceSort: RankingPlaceSort | ||
| } | ||
|
|
||
| export const RankingPlaceList = ({ title, icon, places }: Props) => { | ||
| export const RankingPlaceList = ({ title, icon, rankingPlaceSort }: Props) => { | ||
| return ( | ||
| <Column className={'gap-1.5 px-5'}> | ||
| <SubTitle title={title} icon={icon} /> | ||
| <ul className={'px-3'}> | ||
| {places.map((place, index) => ( | ||
| <PlaceListItem | ||
| key={place.placeId} | ||
| {...place} | ||
| showBorder={index !== places.length - 1} | ||
| /> | ||
| ))} | ||
| </ul> | ||
| <Suspense fallback={<PlaceListItem.Skeleton />}> | ||
| <PlaceList rankingPlaceSort={rankingPlaceSort} /> | ||
| </Suspense> | ||
| </Column> | ||
| ) | ||
| } | ||
|
|
||
| const PlaceList = ({ | ||
| rankingPlaceSort, | ||
| }: { | ||
| rankingPlaceSort: RankingPlaceSort | ||
| }) => { | ||
| const { campus } = useCampusStore() | ||
| const { data: places } = useSuspenseQuery( | ||
| usePlaceQueries.byRanking(rankingPlaceSort, campus), | ||
| ) | ||
|
|
||
| return ( | ||
| <ul className={'px-3'}> | ||
| {places.map((place, index) => ( | ||
| <PlaceListItem | ||
| key={place.placeId} | ||
| {...place} | ||
| showBorder={index !== places.length - 1} | ||
| /> | ||
| ))} | ||
| </ul> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1 @@ | ||
| export { RankingPlaceList } from './RankingPlaceList' | ||
| export { MostViewsPlaces } from './MostViewsPlaces' | ||
| export { MostLikesPlaces } from './MostLikesPlaces' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,74 +1,36 @@ | ||
| import { useSuspenseQuery } from '@tanstack/react-query' | ||
| import { motion, PanInfo } from 'motion/react' | ||
| import { useCampusStore } from '@/_store/campus' | ||
| import { usePlaceQueries } from '@/_apis/queries/place' | ||
| import { PlaceListItem } from '@/_components/PlaceListItem' | ||
| import { VerticalScrollArea } from '@repo/ui/components/Layout' | ||
| import { EmptyPlaces } from './EmptyPlaces' | ||
|
|
||
| type Props = { | ||
| id: string | ||
| setIdFunc: (id: string) => void | ||
| categoryId: string | ||
| } | ||
|
|
||
| // 스와이프 감도 | ||
| const SWIPE_CONFIDENCE_THRESHOLD = 20 | ||
|
|
||
| export const Places = ({ id, setIdFunc }: Props) => { | ||
| export const Places = ({ categoryId }: Props) => { | ||
| const { campus } = useCampusStore() | ||
| const { data: places } = useSuspenseQuery( | ||
| usePlaceQueries.byCategory(id, campus), | ||
| usePlaceQueries.byCategory(categoryId, campus), | ||
| ) | ||
|
|
||
| const currentCategoryId = Number(id) | ||
| const onDragEnd = ( | ||
| _e: MouseEvent | TouchEvent | PointerEvent, | ||
| { offset, velocity }: PanInfo, | ||
| ) => { | ||
| const swipePower = Math.abs(offset.x) * velocity.x | ||
|
|
||
| if (swipePower < -SWIPE_CONFIDENCE_THRESHOLD) { | ||
| if (currentCategoryId < 15) { | ||
| setIdFunc(String(currentCategoryId + 1)) | ||
| } | ||
| } else if (swipePower > SWIPE_CONFIDENCE_THRESHOLD) { | ||
| if (currentCategoryId > 1) { | ||
| setIdFunc(String(currentCategoryId - 1)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const content = | ||
| places.length === 0 ? ( | ||
| <EmptyPlaces /> | ||
| ) : ( | ||
| <VerticalScrollArea as={'ul'} className={'px-8'}> | ||
| {places.map((place, index) => ( | ||
| <PlaceListItem | ||
| key={place.placeId} | ||
| {...place} | ||
| showCategory={false} | ||
| showBorder={index !== places.length - 1} | ||
| /> | ||
| ))} | ||
| </VerticalScrollArea> | ||
| ) | ||
|
|
||
| return ( | ||
| <div className='relative h-full w-full overflow-hidden'> | ||
| <motion.div | ||
| key={id} | ||
| drag='x' | ||
| dragConstraints={{ | ||
| right: currentCategoryId <= 1 ? 0 : undefined, | ||
| left: currentCategoryId >= 15 ? 0 : undefined, | ||
| }} | ||
| dragElastic={0.2} | ||
| onDragEnd={onDragEnd} | ||
| className='relative h-full w-full bg-white' | ||
| > | ||
| {content} | ||
| </motion.div> | ||
| </div> | ||
| <> | ||
| {places.length === 0 ? ( | ||
| <EmptyPlaces /> | ||
| ) : ( | ||
| <VerticalScrollArea as={'ul'}> | ||
| {places.map((place, index) => ( | ||
| <PlaceListItem | ||
| key={place.placeId} | ||
| {...place} | ||
| showCategory={false} | ||
| showBorder={index !== places.length - 1} | ||
| /> | ||
| ))} | ||
| </VerticalScrollArea> | ||
| )} | ||
| </> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { motion, PanInfo } from 'motion/react' | ||
|
|
||
| type Props = { | ||
| categoryId: string | ||
| setCategoryId: (id: string) => void | ||
| children: React.ReactNode | ||
| } | ||
|
|
||
| const SWIPE_CONFIDENCE_THRESHOLD = 20 | ||
|
|
||
| export const SwipeableArea = ({ | ||
| categoryId, | ||
| setCategoryId, | ||
| children, | ||
| }: Props) => { | ||
| const NumberToCategoryId = Number(categoryId) | ||
|
|
||
| const onDragEnd = ( | ||
| _e: MouseEvent | TouchEvent | PointerEvent, | ||
| { offset, velocity }: PanInfo, | ||
| ) => { | ||
| const swipePower = Math.abs(offset.x) * velocity.x | ||
|
|
||
| if (swipePower < -SWIPE_CONFIDENCE_THRESHOLD) { | ||
| if (NumberToCategoryId < 15) { | ||
| setCategoryId(String(NumberToCategoryId + 1)) | ||
| } | ||
| } else if (swipePower > SWIPE_CONFIDENCE_THRESHOLD) { | ||
| if (NumberToCategoryId > 1) { | ||
| setCategoryId(String(NumberToCategoryId - 1)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <div className='relative h-full w-full overflow-hidden px-8'> | ||
| <motion.div | ||
| key={categoryId} | ||
| drag='x' | ||
| dragConstraints={{ | ||
| right: NumberToCategoryId <= 1 ? 0 : undefined, | ||
| left: NumberToCategoryId >= 15 ? 0 : undefined, | ||
| }} | ||
| dragElastic={0.2} | ||
| onDragEnd={onDragEnd} | ||
| className='relative h-full w-full bg-white' | ||
| > | ||
| {children} | ||
| </motion.div> | ||
| </div> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { SwipeableArea } from './SwipeableArea' |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { usePathname } from 'next/navigation' | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { CLIENT_PATH } from '@/_constants/path' | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| export const useCategoryIdFromUrl = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const categoryId = usePathname().split('/')[2] || '0' | ||||||||||||||||||||||||||||||||||||||||||||||||
| const setCategoryId = (id: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||
| window.history.replaceState(null, '', CLIENT_PATH.CATEGORY_DETAIL(id)) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return [categoryId, setCategoryId] as const | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+11
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. 🧩 Analysis chain🏁 Script executed: # Find usages of useCategoryIdFromUrl hook
rg "useCategoryIdFromUrl" --type ts --type tsx -B 2 -A 2Repository: knu-matzip/Matzip-Client Length of output: 95 🏁 Script executed: # Find SwipeableArea and RowCategories components mentioned in the review
fd "SwipeableArea|RowCategories" --type f -e ts -e tsxRepository: knu-matzip/Matzip-Client Length of output: 213 🏁 Script executed: # Check the actual file content and its context
cat -n "apps/web/app/categories/[id]/_hooks/useCategoryIdFromUrl.ts"Repository: knu-matzip/Matzip-Client Length of output: 509 🏁 Script executed: # Search for setCategoryId usage specifically
rg "setCategoryId" --type ts --type tsx -B 3 -A 3Repository: knu-matzip/Matzip-Client Length of output: 95 🏁 Script executed: # Correct search for useCategoryIdFromUrl usage with proper file types
rg "useCategoryIdFromUrl" --type ts --type js -B 2 -A 5Repository: knu-matzip/Matzip-Client Length of output: 2334 🏁 Script executed: # Read RowCategories component
cat -n "apps/web/app/categories/[id]/_components/RowCategories/RowCategories.tsx"Repository: knu-matzip/Matzip-Client Length of output: 1487 🏁 Script executed: # Read SwipeableArea component
cat -n "apps/web/app/categories/[id]/_components/SwipeableArea/SwipeableArea.tsx"Repository: knu-matzip/Matzip-Client Length of output: 1713 🏁 Script executed: # Read full CategoryDetailPage.tsx to see the useEffect
cat -n "apps/web/app/categories/[id]/CategoryDetailPage.tsx"Repository: knu-matzip/Matzip-Client Length of output: 2258 🏁 Script executed: # Search for router.replace usage patterns in the codebase
rg "router\.replace" --type ts --type js -B 2 -A 2Repository: knu-matzip/Matzip-Client Length of output: 50 🏁 Script executed: # Check if there's any useEffect with pathname dependency in CategoryDetailPage or similar files
rg "useEffect.*pathname|pathname.*useEffect" --type ts --type js -B 3 -A 5Repository: knu-matzip/Matzip-Client Length of output: 50 🏁 Script executed: # Double-check: search for useRouter import to see if router is used elsewhere in the app
rg "useRouter" --type ts --type js -l | head -20Repository: knu-matzip/Matzip-Client Length of output: 389 🏁 Script executed: # Verify CLIENT_PATH.CATEGORY_DETAIL function to understand the path format
rg "CATEGORY_DETAIL" --type ts --type js -B 2 -A 2Repository: knu-matzip/Matzip-Client Length of output: 1800 Use The hook computes
When -import { usePathname } from 'next/navigation'
+import { usePathname, useRouter } from 'next/navigation'
import { CLIENT_PATH } from '@/_constants/path'
export const useCategoryIdFromUrl = () => {
+ const router = useRouter()
const categoryId = usePathname().split('/')[2] || '0'
const setCategoryId = (id: string) => {
- window.history.replaceState(null, '', CLIENT_PATH.CATEGORY_DETAIL(id))
+ router.replace(CLIENT_PATH.CATEGORY_DETAIL(id))
}
return [categoryId, setCategoryId] as const
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
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.
activeCategory계산 시 숫자/문자열 비교로 매칭이 항상 실패합니다.useCategoryIdFromUrl가 문자열을 반환하는 반면, 카테고리 데이터의id는 mock 기준 number입니다. 현재처럼category.id === categoryId로 strict 비교하면 어떤 카테고리도 매칭되지 않아activeCategory가 항상undefined가 됩니다(문서 제목과 헤더 아이콘/이름 모두 깨짐).문자열로 맞춰 비교하도록 수정하는 쪽이 안전해 보입니다.
🤖 Prompt for AI Agents