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
10 changes: 4 additions & 6 deletions apps/admin/src/app/requests/[id]/RequestDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Header } from '@repo/ui/components/Header'
import { Icon } from '@repo/ui/components/Icon'
import { Text } from '@repo/ui/components/Text'
import { Column, VerticalScrollArea } from '@repo/ui/components/Layout'
import { Banner } from '@repo/ui/components/Banner'
import { Carousel } from '@repo/ui/components/Carousel'

import type { RequestDetail } from './_api/types'
import { CLIENT_PATH } from '@/consts/path'
Expand Down Expand Up @@ -56,8 +56,8 @@ export const RequestDetailPage = ({ data }: Props) => {
/>
<VerticalScrollArea className={'flex-1 py-5'}>
{photos.length > 0 && (
<Banner
contents={photos.map((photo) => (
<Carousel minHeight={180} showIndicator={true}>
{photos.map((photo) => (
<Image
key={photo.displayOrder}
src={photo.photoUrl}
Expand All @@ -67,9 +67,7 @@ export const RequestDetailPage = ({ data }: Props) => {
className={'max-h-[180px] object-contain'}
/>
))}
minHeight={180}
showIndicator={true}
/>
</Carousel>
)}
<Column className={'flex-1 justify-around gap-4 px-5'}>
<Section icon={'pin'} title={'위치'}>
Expand Down
14 changes: 6 additions & 8 deletions apps/web/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { Flex, VerticalScrollArea } from '@repo/ui/components/Layout'
import { Icon } from '@repo/ui/components/Icon'
import { Text } from '@repo/ui/components/Text'
import { Divider } from '@repo/ui/components/Divider'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { Banner } from '@repo/ui/components/Banner'
import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage'
import { Carousel } from '@repo/ui/components/Carousel'
import { Categories } from '@/_components/Categories'
import { BottomNavigation } from '@/_components/BottomNavigation'
import { RankingSection } from './_components/RankingSection'
Expand Down Expand Up @@ -47,12 +47,10 @@ export default function Page() {
>
<Categories />
</HydrationBoundaryPage>
<Banner
contents={[
<FoodSlotMachineBanner key='banner-1' />,
<LuckyDrawBanner key='banner-2' />,
]}
/>
<Carousel>
<FoodSlotMachineBanner />
<LuckyDrawBanner />
</Carousel>
<RankingSection
title={'찜많은 맛집'}
icon={'fireHeart'}
Expand Down
53 changes: 53 additions & 0 deletions apps/web/app/_hooks/useDebounced.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useCallback, useEffect, useRef } from 'react'

/**
* 함수 실행을 지정된 시간만큼 지연시키는 훅 (Debounce)
*
* @template T - 실행할 함수의 타입
* @param func - 실행할 함수 (콜백)
* @param delay - 지연 시간 (ms)
* @returns 디바운스 처리된 실행 함수
*/

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const useDebounced = <T extends (...args: any[]) => any>(
func: T,
delay: number = 300,
) => {
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)

const funcRef = useRef(func)

useEffect(() => {
funcRef.current = func
}, [func])

const trigger = useCallback(
(...args: Parameters<T>) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}

timeoutRef.current = setTimeout(async () => {
try {
// 최신 함수 호출
await funcRef.current(...args)
} catch (error) {
console.error('Debounced function failed:', error)
}
}, delay)
},
[delay],
)

// 언마운트 시 타이머 클리어
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
}
}, [])

return trigger
}
File renamed without changes.
2 changes: 1 addition & 1 deletion apps/web/app/categories/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata } from 'next'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage'
import { useCategoryQueries } from '@/_apis/queries/category'
import { CategoryDetailPage } from '@/categories/[id]/CategoryDetailPage'
import { getCategories } from '@/_apis/services/category'
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/events/food-slot/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata } from 'next'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage'
import { useCategoryQueries } from '@/_apis/queries/category'
import FoodSlotMachine from './FoodSlotMachine'
import { Header } from '@repo/ui/components/Header'
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/events/lucky-draw/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Flex } from '@repo/ui/components/Layout'
import { Icon } from '@repo/ui/components/Icon'
import { Text } from '@repo/ui/components/Text'
import { LuckyDraw } from './LuckyDraw'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage'
import { useEventQueries } from '@/_apis/queries/event'
import { InfoPopover } from './_components/InfoPopover'

Expand Down
14 changes: 7 additions & 7 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import '@repo/ui/styles.css'
import './globals.css'
import { Suspense } from 'react'
import Script from 'next/script'
import type { Metadata } from 'next'
import { Suspense } from 'react'
import QueryProvider from './QueryClientProvider'
import localFont from 'next/font/local'
import { GoogleAnalytics } from '@next/third-parties/google'
import QueryProvider from '@/_providers/QueryClientProvider'
import { NaverMapProvider } from '@/_providers/NaverMapProvider'
import { HeroProvider } from '@/_providers/HeroProvider'
import { CampusInitializer } from '@/_components/CampusInitializer'
import { Column } from '@repo/ui/components/Layout'
// import { initServerMSW } from '@/_mocks/initMSW'
// import { MSWProvider } from '@/_mocks/MSWProvider'
import { Column } from '@repo/ui/components/Layout'
import { NaverMapProvider } from '@/NaverMapProvider'
import { HeroProvider } from '@/HeroProvider'
import { CampusInitializer } from '@/CampusInitializer'
import { GoogleAnalytics } from '@next/third-parties/google'

const SITE_URL = new URL(process.env.NEXT_PUBLIC_CLIENT_URL || '')

Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/likes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Metadata } from 'next'
import { usePlaceQueries } from '@/_apis/queries/place'
import { OnlyLeftHeader } from '@repo/ui/components/Header'
import { VerticalScrollArea } from '@repo/ui/components/Layout'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage'
import { LikePlacesList } from './_components/LikePlacesList'
import { BottomNavigation } from '@/_components/BottomNavigation'

Expand Down
44 changes: 18 additions & 26 deletions apps/web/app/map/MapComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Container, NaverMap } from 'react-naver-maps'
import { CAMPUS_LOCATION } from '@/_constants/campus'
import { useCampusStore } from '@/_store/campus'
import { useLastMapCenterStore } from '@/_store/lastMapCenter'
import { BOTTOM_OFFSET } from './constants/CurrentLocationButton'
import { usePlaceQueries } from '@/_apis/queries/place'
import type { MapBounds } from '@/_apis/schemas/place'

Expand All @@ -17,14 +18,15 @@ import { PlaceList } from './_components/PlaceList'
import { CampusButtonBox } from './_components/CampusButtom'
import { UserMarker, PlaceMarker } from './_components/Marker'
import { CurrentLocationButton } from './_components/CurrentLocationButton'
import { PreviewPlace } from './_components/PreviewPlace'
import { PlaceSummaryCard } from './_components/PlaceSummaryCard'
import { RefreshButton } from './_components/RefreshButton'
import { useDebounced } from '@/_hooks/useDebounced'

const MapComponent = () => {
const [map, setMap] = useState<naver.maps.Map | null>(null)
const [isCenteredOnUser, setIsCenteredOnUser] = useState(false)
const [currentBounds, setCurrentBounds] = useState<MapBounds | null>(null)
const [previewPlaceId, setPreviewPlaceId] = useState<string | null>(null)
const [selectedPlaceId, setSelectedPlaceId] = useState<string | null>(null)
const [showUpdateButton, setShowUpdateButton] = useState(false)

const { campus } = useCampusStore()
Expand All @@ -33,8 +35,8 @@ const MapComponent = () => {
const { data = [] } = useQuery(usePlaceQueries.byMap(currentBounds))

const defaultCenter = toLatLng(lastMapCenter || CAMPUS_LOCATION[campus])
const previewPlace = previewPlaceId
? data.find((place) => place.placeId === previewPlaceId)!
const selectedPlace = selectedPlaceId
? data.find((place) => place.placeId === selectedPlaceId)!
: null

const refreshMapBounds = useCallback(() => {
Expand Down Expand Up @@ -66,18 +68,11 @@ const MapComponent = () => {
setIsCenteredOnUser(false)
}

const onCenterChanged = () => {
const onCenterChanged = useDebounced(() => {
setIsCenteredOnUser(false)
setShowUpdateButton(true)
}

const handlePreviewPlace = (placeId: string) => {
setPreviewPlaceId(placeId)
}

const resetPreviewPlace = () => {
setPreviewPlaceId(null)
}
setSelectedPlaceId(null)
}, 200)

useEffect(refreshMapBounds, [refreshMapBounds])
useEffect(() => {
Expand All @@ -97,37 +92,34 @@ const MapComponent = () => {
<CurrentLocationButton
onClick={centerMapToUserLocation}
isCenteredOnUser={isCenteredOnUser}
previewPlaceId={previewPlaceId}
bottomOffset={
selectedPlaceId ? BOTTOM_OFFSET.WITH_SUMMARY_CARD : undefined
}
/>
<CampusButtonBox map={map} centerMapToCampus={centerMapToCampus} />
<Container
className={cn('map-wrapper', 'w-full', 'h-full')}
onClick={resetPreviewPlace}
onTouchEnd={onCenterChanged}
onMouseUp={onCenterChanged}
>
<Container className={cn('map-wrapper', 'w-full', 'h-full')}>
<NaverMap
defaultZoom={15}
minZoom={12}
ref={setMap}
defaultCenter={defaultCenter}
onZoomChanged={onCenterChanged}
onCenterChanged={onCenterChanged}
>
{userLocation && <UserMarker position={userLocation} />}
{data.map((place) => (
<PlaceMarker
key={place.placeId}
position={place.location}
icon={place.categories[0]?.iconKey || 'logo'}
handlePreviewPlace={() => {
handlePreviewPlace(place.placeId)
onClick={() => {
setSelectedPlaceId(place.placeId)
}}
/>
))}
</NaverMap>
</Container>
{previewPlace ? (
<PreviewPlace place={previewPlace} />
{selectedPlace ? (
<PlaceSummaryCard place={selectedPlace} />
) : (
<PlaceList places={data} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
import { motion } from 'motion/react'
import { Icon } from '@repo/ui/components/Icon'
import { cn } from '@repo/ui/utils/cn'
import { BOTTOM_OFFSET } from '@/map/constants/CurrentLocationButton'

type Props = {
onClick: VoidFunction
isCenteredOnUser: boolean
previewPlaceId: string | null
bottomOffset?: number
}
const windowHeight = Math.floor(window.innerHeight * 0.2) + 10

export const CurrentLocationButton = ({
onClick,
isCenteredOnUser,
previewPlaceId,
bottomOffset,
}: Props) => {
return (
<motion.button
initial={{ bottom: windowHeight }}
animate={{ bottom: previewPlaceId ? 220 : windowHeight }}
initial={{ bottom: BOTTOM_OFFSET.WITH_BOTTOM_SHEET }}
animate={{ bottom: bottomOffset ?? BOTTOM_OFFSET.WITH_BOTTOM_SHEET }}
transition={{
duration: 0.5,
ease: 'easeOut',
Expand Down
6 changes: 3 additions & 3 deletions apps/web/app/map/_components/Marker/Marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ export const UserMarker = ({ position }: { position: Coord }) => {
export const PlaceMarker = ({
icon,
position,
handlePreviewPlace = () => {},
onClick = () => {},
}: {
icon: IconType
position: Coord
handlePreviewPlace?: VoidFunction
onClick?: VoidFunction
}) => {
const naverMaps = useNavermaps()
const MarkerIcon = ReactDOMServer.renderToString(
Expand All @@ -54,7 +54,7 @@ export const PlaceMarker = ({
<Marker
onClick={(e) => {
e.pointerEvent.stopPropagation()
handlePreviewPlace()
onClick()
}}
position={new naverMaps.LatLng(toLatLng(position))}
icon={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Text } from '@repo/ui/components/Text'
import { Icon } from '@repo/ui/components/Icon'
import { Column, Flex } from '@repo/ui/components/Layout'

export const PreviewPlace = ({ place }: { place: PlaceByMap }) => {
export const PlaceSummaryCard = ({ place }: { place: PlaceByMap }) => {
const { placeId, placeName, categories, address, photos } = place
const mainCategoryIcon = categories[0]?.iconKey || 'logo'

Expand Down
1 change: 1 addition & 0 deletions apps/web/app/map/_components/PlaceSummaryCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PlaceSummaryCard } from './PlaceSummaryCard'
1 change: 0 additions & 1 deletion apps/web/app/map/_components/PreviewPlace/index.tsx

This file was deleted.

7 changes: 7 additions & 0 deletions apps/web/app/map/constants/CurrentLocationButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const SPACE_FROM_BOTTOM_SHEET = 10

export const BOTTOM_OFFSET = {
WITH_BOTTOM_SHEET:
Math.floor(window.innerHeight * 0.2) + SPACE_FROM_BOTTOM_SHEET,
WITH_SUMMARY_CARD: 220,
} as const
10 changes: 4 additions & 6 deletions apps/web/app/places/[id]/PlaceDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Image from 'next/image'
import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useCampusStore } from '@/_store/campus'
import { PlaceQueryKeys, usePlaceQueries } from '@/_apis/queries/place'
import { Banner } from '@repo/ui/components/Banner'
import { Carousel } from '@repo/ui/components/Carousel'
import { HeaderBackButton } from '@/_components/HeaderBackButton'
import { Text } from '@repo/ui/components/Text'
import { Header } from '@repo/ui/components/Header'
Expand Down Expand Up @@ -47,8 +47,8 @@ export const PlaceDetailPage = ({ id }: { id: string }) => {
right={<LikeButton placeId={placeId} initIsLiked={isLiked} />}
/>
<VerticalScrollArea className={'flex-1'}>
<Banner
contents={photos.map((photo, index) => (
<Carousel minHeight={180} showIndicator={true}>
{photos.map((photo, index) => (
<Image
key={photo.displayOrder}
src={photo.photoUrl}
Expand All @@ -59,9 +59,7 @@ export const PlaceDetailPage = ({ id }: { id: string }) => {
priority={index === 0}
/>
))}
minHeight={180}
showIndicator={true}
/>
</Carousel>
<Column className={'flex-1 justify-around gap-4 p-5'}>
<Section icon={'pin'} title={'위치'}>
<Location location={location} />
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/places/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Metadata } from 'next'
import { usePlaceQueries } from '@/_apis/queries/place'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { HydrationBoundaryPage } from '@/_components/HydrationBoundaryPage'
import { PlaceDetailPage } from './PlaceDetailPage'
import { getPlaceDetail } from '@/_apis/services/place'

Expand Down
Loading