-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: fsd로 마이그레이션 * refactor: 전반적인 코드 분리 * chore: typescript lint 추가 * chore: fsd eslint 추가 * chore: tanstack query 세팅
- Loading branch information
1 parent
f0dd33a
commit 4df3a70
Showing
36 changed files
with
539 additions
and
526 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
{ | ||
"extends": ["next", "prettier"], | ||
"plugins": ["unused-imports", "simple-import-sort"], | ||
"parser": "@typescript-eslint/parser", | ||
"extends": ["next", "prettier", "plugin:@typescript-eslint/recommended", "@feature-sliced"], | ||
"plugins": ["@typescript-eslint", "unused-imports"], | ||
|
||
"rules": { | ||
"unused-imports/no-unused-imports": "error", | ||
"simple-import-sort/imports": "error", | ||
"simple-import-sort/exports": "error" | ||
"unused-imports/no-unused-imports": "error", // 사용하지 않는 import 제거 | ||
"@typescript-eslint/consistent-type-imports": "error", // type 가져오기 경우 import type { Type } from 'module' 사용 | ||
"@typescript-eslint/no-unused-expressions": "off", // 사용하지 않는 표현식 허용 | ||
"import/no-internal-modules": "off" // 내부 모듈 사용 허용 (fsd index 파일을 사용하기 위함) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
'use client'; | ||
|
||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; | ||
import type { PropsWithChildren } from 'react'; | ||
import React from 'react'; | ||
|
||
const Providers = ({ children }: PropsWithChildren) => { | ||
const queryClient = new QueryClient(); | ||
|
||
return ( | ||
<QueryClientProvider client={queryClient}> | ||
{children} | ||
<ReactQueryDevtools initialIsOpen={false} /> | ||
</QueryClientProvider> | ||
); | ||
}; | ||
|
||
export default Providers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1 @@ | ||
import Image from 'next/image'; | ||
|
||
import { cn } from '@/lib/utils'; | ||
|
||
const Page = () => { | ||
return ( | ||
<div className={cn('h-[calc(100%-154px)] overflow-scroll scrollbar-hide')}> | ||
{Array.from({ length: 4 }).map((_, index) => ( | ||
<Image | ||
key={index} | ||
src={`/congestion/mock${(index % 3) + 1}.png`} | ||
alt={`${index}`} | ||
width={376} | ||
height={461} | ||
/> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Page; | ||
export { default } from '@/pages/congestion/ui/congestion-page'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,161 +1 @@ | ||
'use client'; | ||
|
||
import { animated, useSpring } from '@react-spring/web'; | ||
import { createUseGesture, dragAction, pinchAction } from '@use-gesture/react'; | ||
import Image from 'next/image'; | ||
import React from 'react'; | ||
|
||
import Summary from '@/components/ui/main/summary'; | ||
import { cn } from '@/lib/utils'; | ||
|
||
import { ResortList, Spot } from './data'; | ||
|
||
const useGesture = createUseGesture([pinchAction, dragAction]); | ||
|
||
const Page = () => { | ||
const [selectedTab, setSelectedTab] = React.useState(ResortList[0]); | ||
const [selectedSpot, setSelectedSpot] = React.useState<Spot | null>(null); | ||
const [style, api] = useSpring(() => ({ scale: 1, x: 0, y: 0 })); | ||
const ref = React.useRef<HTMLDivElement>(null); | ||
|
||
useGesture( | ||
{ | ||
onPinch: ({ origin: [ox, oy], first, movement: [ms], offset: [s], memo }) => { | ||
if (first) { | ||
const { width, height, x, y } = ref.current!.getBoundingClientRect(); | ||
const tx = ox - (x + width / 2); | ||
const ty = oy - (y + height / 2); | ||
memo = [style.x.get(), style.y.get(), tx, ty]; | ||
} | ||
|
||
const x = memo[0] - (ms - 1) * memo[2]; | ||
const y = memo[1] - (ms - 1) * memo[3]; | ||
api.start({ scale: s, x, y }); | ||
return memo; | ||
}, | ||
onPinchEnd: () => { | ||
if (style.scale.get() < 1) { | ||
api.start({ scale: 1, x: 0, y: 0 }); | ||
} | ||
}, | ||
onDrag: ({ pinching, cancel, offset: [x, y] }) => { | ||
if (pinching) return cancel(); | ||
api.start({ x, y }); | ||
}, | ||
onDragEnd: () => { | ||
const [boundedX, boundedY] = getBoundedPositions( | ||
style.x.get(), | ||
style.y.get(), | ||
style.scale.get() | ||
); | ||
api.start({ x: boundedX, y: boundedY }); | ||
}, | ||
}, | ||
{ | ||
target: ref, | ||
drag: { from: () => [style.x.get(), style.y.get()] }, | ||
pinch: { scaleBounds: { min: 1, max: 6 }, rubberband: true }, | ||
} | ||
); | ||
|
||
const getBoundedPositions = (x: number, y: number, scale: number): [number, number] => { | ||
const CONTAINER = { width: 376, height: 200 }; | ||
const IMAGE = { width: 376, height: 357 }; | ||
const OFFSET_Y = IMAGE.height - CONTAINER.height; | ||
|
||
const scaledSize = { | ||
width: IMAGE.width * scale, | ||
height: IMAGE.height * scale, | ||
}; | ||
|
||
const bounds = { | ||
x: { | ||
min: (CONTAINER.width - scaledSize.width) / 2, | ||
max: -(CONTAINER.width - scaledSize.width) / 2, | ||
}, | ||
y: { | ||
min: -((scaledSize.height - CONTAINER.height + OFFSET_Y) / 2), | ||
max: Math.max((scaledSize.height - CONTAINER.height - OFFSET_Y) / 2, 0), | ||
}, | ||
}; | ||
|
||
const boundedPosition = { | ||
x: Math.max(bounds.x.min, Math.min(bounds.x.max, x)), | ||
y: Math.max(bounds.y.min, Math.min(bounds.y.max, y)), | ||
}; | ||
|
||
return [boundedPosition.x, boundedPosition.y]; | ||
}; | ||
|
||
return ( | ||
<div className={cn('size-full')}> | ||
<div className={cn('mb-1 flex w-full overflow-scroll scrollbar-hide')}> | ||
{ResortList.map((tab) => ( | ||
<div | ||
key={tab.name} | ||
className={cn( | ||
'flex shrink-0 cursor-pointer items-center justify-center border-b-4 p-3 pb-2 font-bold', | ||
selectedTab.name === tab.name ? 'border-black' : 'border-white opacity-20' | ||
)} | ||
onClick={() => { | ||
setSelectedTab(tab); | ||
setSelectedSpot(null); | ||
}} | ||
> | ||
{tab.name} | ||
</div> | ||
))} | ||
</div> | ||
<Summary {...ResortList.find((tab) => tab.name === selectedTab.name)!} /> | ||
<div className={cn('relative h-[200px] w-full overflow-hidden')}> | ||
<animated.div | ||
ref={ref} | ||
style={{ | ||
touchAction: 'none', | ||
display: 'inline-block', | ||
...style, | ||
}} | ||
> | ||
<Image | ||
className={cn('object-cover')} | ||
width={376} | ||
height={357} | ||
src={`/map/${selectedTab.map}`} | ||
alt={`${selectedTab.name}`} | ||
/> | ||
<div | ||
className={cn('absolute left-0 top-3 h-2 w-3 bg-black opacity-20')} | ||
onClick={() => console.log('hi')} | ||
/> | ||
</animated.div> | ||
{selectedSpot && ( | ||
<div className={cn('absolute left-0 top-0 size-full')}> | ||
<video src={`/video/${selectedTab.tag}/${selectedSpot.tag}.mov`} muted autoPlay loop /> | ||
</div> | ||
)} | ||
</div> | ||
<div className={cn('flex h-[296px] flex-col gap-0.5 overflow-scroll scrollbar-hide')}> | ||
{selectedTab.spots?.map((spot) => ( | ||
<div | ||
key={spot.name} | ||
className={cn( | ||
'flex h-20 w-full items-center justify-between bg-gray-100 p-6 font-bold', | ||
spot.isAvailable ? 'cursor-pointer' : 'cursor-not-allowed opacity-20' | ||
)} | ||
onClick={() => { | ||
spot.isAvailable && setSelectedSpot(spot); | ||
}} | ||
> | ||
<p className={cn('text-lg')}>{spot.name}</p> | ||
<div className={cn('flex items-center gap-3')}> | ||
<p className={cn('text-sm')}>{spot.level}</p> | ||
<p className={cn('text-lg')}>헤라1,2</p> | ||
</div> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Page; | ||
export { default } from '@/pages/webcam/ui/web-cam-map-page'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1 @@ | ||
import CloudIcon from '@/components/icons/cloud'; | ||
import SunIcon from '@/components/icons/sun'; | ||
import { cn } from '@/lib/utils'; | ||
|
||
const WEATHER_DATA = [ | ||
{ | ||
time: '오전 8시', | ||
icon: <SunIcon />, | ||
temperature: '22°', | ||
humidity: '20%', | ||
}, | ||
{ | ||
time: '오전 11시', | ||
icon: <CloudIcon />, | ||
temperature: '24°', | ||
humidity: '30%', | ||
}, | ||
{ | ||
time: '오후 2시', | ||
icon: <SunIcon />, | ||
temperature: '26°', | ||
humidity: '40%', | ||
}, | ||
{ | ||
time: '오후 5시', | ||
icon: <CloudIcon />, | ||
temperature: '25°', | ||
humidity: '30%', | ||
}, | ||
]; | ||
|
||
const Page = () => { | ||
return ( | ||
<div className={cn('flex h-[650px] flex-col items-center gap-3 overflow-auto')}> | ||
{Array.from({ length: 3 }).map((_, index) => ( | ||
<WeatherCard key={index} /> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Page; | ||
|
||
const WeatherCard = () => { | ||
return ( | ||
<div className={cn('h-[461px] w-[335px] rounded-lg bg-gray-100 p-6')}> | ||
<div className={cn('mb-[130px] flex items-center justify-between')}> | ||
<h4 className={cn('font-bold')}>하이원 스키장</h4> | ||
<div className={cn('rounded-lg bg-white px-3 py-1 text-sm font-bold')}>설질 GOOD 56%</div> | ||
</div> | ||
|
||
<p className={cn('text-4xl font-semibold')}>25°</p> | ||
<p className={cn('mb-4 text-sm text-gray-600')}>흐리고 비</p> | ||
|
||
<p className={cn('text-lg font-semibold')}>흐리고 비오는 날씨가 계속 돼요</p> | ||
<p className={cn('mb-4 text-sm')}>최고:28° 최저 24° 미세먼지 좋음</p> | ||
|
||
<hr className={cn('mb-4')} /> | ||
|
||
<ul className={cn('flex gap-5')}> | ||
{WEATHER_DATA.map((data) => ( | ||
<li key={data.time} className={cn('flex flex-col items-center')}> | ||
<div className={cn('mb-1 text-sm font-medium')}>{data.time}</div> | ||
{data.icon} | ||
<p className={cn('mt-1')}>{data.temperature}</p> | ||
<p className={cn('text-xs')}>{data.humidity}</p> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
}; | ||
export { default } from '@/pages/weather/ui/weather-page'; |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.