Compare commits
No commits in common. "5feef03187185d21f15f83f8e308f9f98c684878" and "b36844303355504af49af3ca7634969b989d79e2" have entirely different histories.
5feef03187
...
b368443033
@ -65,11 +65,9 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
const suitable: Suitable[] = await prisma.$queryRawUnsafe(query, pageNumber, itemPerPage)
|
const suitable: Suitable[] = await prisma.$queryRawUnsafe(query, pageNumber, itemPerPage)
|
||||||
|
|
||||||
return NextResponse.json(suitable, {
|
// console.log(`검색 조건 :::: 카테고리: ${category}, 키워드: ${keyword}`)
|
||||||
headers: {
|
|
||||||
'spinner-state': 'true',
|
return NextResponse.json(suitable)
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
||||||
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import SuitableDetailPopupButton from './SuitableDetailPopupButton'
|
|||||||
import { useSuitable } from '@/hooks/useSuitable'
|
import { useSuitable } from '@/hooks/useSuitable'
|
||||||
import { usePopupController } from '@/store/popupController'
|
import { usePopupController } from '@/store/popupController'
|
||||||
import { useSuitableStore } from '@/store/useSuitableStore'
|
import { useSuitableStore } from '@/store/useSuitableStore'
|
||||||
import { useSpinnerStore } from '@/store/spinnerStore'
|
|
||||||
import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable'
|
import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable'
|
||||||
|
|
||||||
export default function SuitableDetailPopup() {
|
export default function SuitableDetailPopup() {
|
||||||
@ -16,7 +15,7 @@ export default function SuitableDetailPopup() {
|
|||||||
|
|
||||||
const [openItems, setOpenItems] = useState<Set<number>>(new Set())
|
const [openItems, setOpenItems] = useState<Set<number>>(new Set())
|
||||||
|
|
||||||
/* 아이템 열기/닫기 */
|
// 아이템 열기/닫기
|
||||||
const toggleItemOpen = useCallback((itemId: number) => {
|
const toggleItemOpen = useCallback((itemId: number) => {
|
||||||
setOpenItems((prev) => {
|
setOpenItems((prev) => {
|
||||||
const newOpenItems = new Set(prev)
|
const newOpenItems = new Set(prev)
|
||||||
@ -25,11 +24,6 @@ export default function SuitableDetailPopup() {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/* 데이터 로딩 상태 처리 */
|
|
||||||
useEffect(() => {
|
|
||||||
useSpinnerStore.getState().setIsShow(isSelectedSuitablesLoading)
|
|
||||||
}, [isSelectedSuitablesLoading])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedItemsSearching(true)
|
setSelectedItemsSearching(true)
|
||||||
}, [])
|
}, [])
|
||||||
@ -51,7 +45,10 @@ export default function SuitableDetailPopup() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
<div className="compliance-check-pop-wrap">
|
<div className="compliance-check-pop-wrap">
|
||||||
{selectedSuitables?.map((item: Suitable) => (
|
{isSelectedSuitablesLoading ? (
|
||||||
|
<div>Loading...</div>
|
||||||
|
) : (
|
||||||
|
selectedSuitables?.map((item: Suitable) => (
|
||||||
<div className={`compliance-check-bx ${openItems.has(item.id) ? 'act' : ''}`} key={item.id}>
|
<div className={`compliance-check-bx ${openItems.has(item.id) ? 'act' : ''}`} key={item.id}>
|
||||||
<div className="check-name-wrap">
|
<div className="check-name-wrap">
|
||||||
<div className="check-name">{item.productName}</div>
|
<div className="check-name">{item.productName}</div>
|
||||||
@ -100,7 +97,8 @@ export default function SuitableDetailPopup() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<SuitableDetailPopupButton />
|
<SuitableDetailPopupButton />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import SuitableButton from './SuitableButton'
|
|||||||
import SuitableNoData from './SuitableNoData'
|
import SuitableNoData from './SuitableNoData'
|
||||||
import { useSuitable } from '@/hooks/useSuitable'
|
import { useSuitable } from '@/hooks/useSuitable'
|
||||||
import { useSuitableStore } from '@/store/useSuitableStore'
|
import { useSuitableStore } from '@/store/useSuitableStore'
|
||||||
import { useSpinnerStore } from '@/store/spinnerStore'
|
|
||||||
import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable'
|
import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable'
|
||||||
|
|
||||||
export default function SuitableList() {
|
export default function SuitableList() {
|
||||||
@ -25,7 +24,7 @@ export default function SuitableList() {
|
|||||||
const [openItems, setOpenItems] = useState<Set<number>>(new Set())
|
const [openItems, setOpenItems] = useState<Set<number>>(new Set())
|
||||||
const observerTarget = useRef<HTMLDivElement>(null)
|
const observerTarget = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
/* 선택된 아이템 확인 - 메인 하위 아이템 indeterminate 확인 */
|
// 선택된 아이템 확인 - 메인 하위 아이템 indeterminate 확인
|
||||||
const isMainIndeterminate = useMemo(
|
const isMainIndeterminate = useMemo(
|
||||||
() => (mainId: number, detailCnt: number) => {
|
() => (mainId: number, detailCnt: number) => {
|
||||||
const mainItem = selectedItems.get(mainId)
|
const mainItem = selectedItems.get(mainId)
|
||||||
@ -35,7 +34,7 @@ export default function SuitableList() {
|
|||||||
[selectedItems],
|
[selectedItems],
|
||||||
)
|
)
|
||||||
|
|
||||||
/* 선택된 아이템 확인 */
|
// 선택된 아이템 확인
|
||||||
const isItemSelected = useCallback(
|
const isItemSelected = useCallback(
|
||||||
(mainId: number, detailId?: number): boolean => {
|
(mainId: number, detailId?: number): boolean => {
|
||||||
const mainItem = selectedItems.get(mainId)
|
const mainItem = selectedItems.get(mainId)
|
||||||
@ -46,7 +45,7 @@ export default function SuitableList() {
|
|||||||
[selectedItems],
|
[selectedItems],
|
||||||
)
|
)
|
||||||
|
|
||||||
/* 아이템 클릭 */
|
// 아이템 클릭
|
||||||
const handleItemClick = useCallback(
|
const handleItemClick = useCallback(
|
||||||
(mainId: number, detailId?: number, detailIds?: Set<number>): void => {
|
(mainId: number, detailId?: number, detailIds?: Set<number>): void => {
|
||||||
setSelectedItemsSearching(false)
|
setSelectedItemsSearching(false)
|
||||||
@ -55,7 +54,7 @@ export default function SuitableList() {
|
|||||||
[isItemSelected, addSelectedItem, removeSelectedItem],
|
[isItemSelected, addSelectedItem, removeSelectedItem],
|
||||||
)
|
)
|
||||||
|
|
||||||
/* 아이템 열기/닫기 */
|
// 아이템 열기/닫기
|
||||||
const toggleItemOpen = useCallback((itemId: number) => {
|
const toggleItemOpen = useCallback((itemId: number) => {
|
||||||
setOpenItems((prev) => {
|
setOpenItems((prev) => {
|
||||||
const newOpenItems = new Set(prev)
|
const newOpenItems = new Set(prev)
|
||||||
@ -64,7 +63,7 @@ export default function SuitableList() {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/* 아이템 렌더링 */
|
// 아이템 렌더링
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(item: Suitable) => {
|
(item: Suitable) => {
|
||||||
return (
|
return (
|
||||||
@ -116,10 +115,10 @@ export default function SuitableList() {
|
|||||||
[isItemSelected, openItems, handleItemClick, toggleItemOpen, toCodeName, toSuitableDetail],
|
[isItemSelected, openItems, handleItemClick, toggleItemOpen, toCodeName, toSuitableDetail],
|
||||||
)
|
)
|
||||||
|
|
||||||
/* 조회 데이터 리스트 */
|
// 아이템 리스트
|
||||||
const suitableList = useMemo(() => suitables?.pages.flat() ?? [], [suitables?.pages])
|
const suitableList = useMemo(() => suitables?.pages.flat() ?? [], [suitables?.pages])
|
||||||
|
|
||||||
/* Intersection Observer 설정 */
|
// Intersection Observer 설정
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
@ -129,7 +128,7 @@ export default function SuitableList() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
rootMargin: `${window.innerHeight * 0.2}px`,
|
rootMargin: '100px',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -140,18 +139,15 @@ export default function SuitableList() {
|
|||||||
return () => observer.disconnect()
|
return () => observer.disconnect()
|
||||||
}, [hasNextPage, isFetchingNextPage, fetchNextPage])
|
}, [hasNextPage, isFetchingNextPage, fetchNextPage])
|
||||||
|
|
||||||
/* 데이터 로딩 상태 처리 */
|
if (isLoading) return <div>Loading...</div>
|
||||||
useEffect(() => {
|
|
||||||
useSpinnerStore.getState().setIsShow(isLoading || isFetchingNextPage)
|
|
||||||
}, [isLoading, isFetchingNextPage])
|
|
||||||
|
|
||||||
/* 조회 데이터 없는 경우 */
|
|
||||||
if (!suitableList.length) return <SuitableNoData />
|
if (!suitableList.length) return <SuitableNoData />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{suitableList.map(renderItem)}
|
{suitableList.map(renderItem)}
|
||||||
<div ref={observerTarget} />
|
<div ref={observerTarget} className="loading-indicator">
|
||||||
|
{isFetchingNextPage && <div className="loading-more">데이터를 불러오는 중...</div>}
|
||||||
|
</div>
|
||||||
<SuitableButton />
|
<SuitableButton />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -111,7 +111,7 @@ export default function Header() {
|
|||||||
</div>
|
</div>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
<SwiperSlide>
|
<SwiperSlide>
|
||||||
<div className="side-swiper-card" onClick={() => router.push('/suitable')}>
|
<div className="side-swiper-card">
|
||||||
<div className="side-swiper-icon icon01"></div>
|
<div className="side-swiper-icon icon01"></div>
|
||||||
<div className="side-swiper-infor">私は作成したお問 い合わせ</div>
|
<div className="side-swiper-infor">私は作成したお問 い合わせ</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,10 +9,9 @@ export function useAxios() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const responseHandler = (response: AxiosResponse) => {
|
const responseHandler = (response: AxiosResponse) => {
|
||||||
/* spinner 조작 커스텀이 필요한 경우 api 응답 헤더에 spinner-state: true 추가 */
|
// if (response.headers['spinner-state'] === undefined) {
|
||||||
if (!response.headers['spinner-state']) {
|
|
||||||
useSpinnerStore.getState().setIsShow(false)
|
useSpinnerStore.getState().setIsShow(false)
|
||||||
}
|
// }
|
||||||
response.data = transferResponse(response)
|
response.data = transferResponse(response)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user