feat: remove unused survey sale API routes & add address popup at Survey BasicForm

This commit is contained in:
Dayoung 2025-05-09 15:08:55 +09:00
parent 555e6f3b4a
commit 95d971b198
25 changed files with 398 additions and 336 deletions

View File

@ -1,72 +0,0 @@
import { NextResponse } from 'next/server'
export async function POST(request: Request, context: { params: { id: string } }) {
const body = await request.json()
const { id } = await context.params
// @ts-ignore
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
where: { id: Number(id) },
data: {
detail_info: {
create: body,
},
},
})
return NextResponse.json({ message: 'Survey detail created successfully' })
}
export async function GET(request: Request, context: { params: { id: string } }) {
const { id } = await context.params
// @ts-ignore
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.findUnique({
where: { id: Number(id) },
include: {
detail_info: true,
},
})
return NextResponse.json(survey)
}
export async function PUT(request: Request, context: { params: { id: string } }) {
const { id } = await context.params
const body = await request.json()
// @ts-ignore
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
where: { id: Number(id) },
data: {
...body,
detail_info: {
update: body.detail_info,
},
},
})
return NextResponse.json(survey)
}
export async function DELETE(request: Request, context: { params: { id: string; detail_id: string } }) {
const { id, detail_id } = await context.params
if (detail_id) {
// @ts-ignore
const survey = await prisma.SD_SERVEY_SALES_DETAIL_INFO.delete({
where: { id: Number(detail_id) },
})
} else {
// @ts-ignore
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.delete({
where: { id: Number(id) },
})
}
return NextResponse.json({ message: 'Survey deleted successfully' })
}
export async function PATCH(request: Request, context: { params: { id: string } }) {
const { id } = await context.params
// @ts-ignore
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
where: { id: Number(id) },
data: {
submission_status: true,
},
})
return NextResponse.json({ message: 'Survey confirmed successfully' })
}

View File

@ -1,33 +0,0 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma'
export async function POST(request: Request) {
const body = await request.json()
// @ts-ignore
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.create({
data: body,
})
return NextResponse.json(res)
}
export async function GET() {
try {
// @ts-ignore
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany()
return NextResponse.json(res)
} catch (error) {
console.error(error)
}
}
export async function PUT(request: Request) {
const body = await request.json()
console.log('🚀 ~ PUT ~ body:', body)
const detailInfo = { ...body.detail_info, basic_info_id: body.id }
console.log('🚀 ~ PUT ~ detailInfo:', detailInfo)
// @ts-ignore
const res = await prisma.SD_SERVEY_SALES_DETAIL_INFO.create({
data: detailInfo,
})
return NextResponse.json({ message: 'Survey sales updated successfully' })
}

View File

@ -1,21 +1,5 @@
import { NextResponse } from 'next/server'
// export async function POST(request: Request, context: { params: { id: string } }) {
// const body = await request.json()
// const { id } = await context.params
// // @ts-ignore
// const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
// where: { id: Number(id) },
// data: {
// detail_info: {
// create: body,
// },
// },
// })
// return NextResponse.json({ message: 'Survey detail created successfully' })
// }
export async function GET(request: Request, context: { params: { id: string } }) {
const { id } = await context.params
// @ts-ignore

View File

@ -16,6 +16,7 @@ export async function GET(request: Request) {
const searchOption = searchParams.get('searchOption')
const isMySurvey = searchParams.get('isMySurvey')
const sort = searchParams.get('sort')
const offset = searchParams.get('offset')
const searchOptions = ['building_name', 'representative', 'store', 'construction_point', 'customer_name', 'post_code', 'address', 'address_detail']
try {
@ -27,7 +28,7 @@ export async function GET(request: Request) {
if (keyword && keyword.trim() !== '' && searchOption) {
if (searchOption === 'all') {
where.OR = [];
where.OR = []
if (keyword.match(/^\d+$/) || !isNaN(Number(keyword))) {
where.OR.push({
id: {
@ -40,7 +41,7 @@ export async function GET(request: Request) {
[field]: {
contains: keyword,
},
}))
})),
)
} else if (searchOptions.includes(searchOption)) {
where[searchOption] = {
@ -53,12 +54,22 @@ export async function GET(request: Request) {
}
}
// @ts-ignore
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany({
where,
orderBy: sort === 'created' ? { created_at: 'desc' } : { updated_at: 'desc' },
})
return NextResponse.json(res)
if (offset) {
// @ts-ignore
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany({
where,
orderBy: sort === 'created' ? { created_at: 'desc' } : { updated_at: 'desc' },
skip: Number(offset),
take: 10,
})
return NextResponse.json(res)
} else {
// @ts-ignore
const count = await prisma.SD_SERVEY_SALES_BASIC_INFO.count({
where,
})
return NextResponse.json(count)
}
} catch (error) {
console.error(error)
throw error

View File

@ -2,7 +2,8 @@ import { NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma'
import { getIronSession } from 'iron-session'
import { cookies } from 'next/headers'
import { SessionData, sessionOptions } from '@/libs/session'
import { sessionOptions } from '@/libs/session'
import type { SessionData } from '@/types/Auth'
export async function POST(request: Request) {
const { username, password } = await request.json()
@ -25,8 +26,8 @@ export async function POST(request: Request) {
const cookieStore = await cookies()
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
console.log('start session edit!')
session.username = user.username!
session.email = user.email!
session.userNm = user.username!
// session.email = user.email!
session.isLoggedIn = true
console.log('end session edit!')
await session.save()

View File

@ -1,9 +0,0 @@
import InquiryWriteForm from '@/components/inquiry/InquiryWriteForm'
export default function InquiryWrite() {
return (
<div>
<InquiryWriteForm />
</div>
)
}

View File

@ -22,7 +22,8 @@ interface RootLayoutProps {
export default async function RootLayout({ children, header, footer, floatBtn }: RootLayoutProps): Promise<ReactNode> {
return (
<ReactQueryProviders>
<html lang="en">
{/* 일본어로 렌더링 */}
<html lang="ja" suppressHydrationWarning>
<body>
<div className="wrap">
{header}

View File

@ -5,7 +5,7 @@ export default function page() {
return (
<>
<DataTable />
<DetailForm />
{/* <DetailForm /> */}
</>
)
}

View File

@ -1,5 +1,4 @@
import ListTable from '@/components/survey-sale/list/ListTable'
import SearchForm from '@/components/survey-sale/list/SearchForm'
export default function page() {
return (

View File

@ -3,10 +3,9 @@
interface LoadMoreButtonProps {
hasMore: boolean
onLoadMore: () => void
onScrollToTop: () => void
}
export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: LoadMoreButtonProps) {
export default function LoadMoreButton({ hasMore, onLoadMore }: LoadMoreButtonProps) {
return (
<>
{hasMore ? (
@ -15,10 +14,7 @@ export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: L
<i className="btn-edit"></i>
</button>
) : (
<button onClick={onScrollToTop} className="btn-frame n-blue icon">
<i className="btn-arr-up"></i>
</button>
<></>
)}
</>
)

View File

@ -23,11 +23,6 @@ export default function InquiryWriteForm() {
const file = Array.from(e.target.files || [])
setFormData({ ...formData, file: [...formData.file, ...file] })
}
const handleFileDelete = (fileToDelete: File) => {
setFormData({ ...formData, file: formData.file.filter((f) => f !== fileToDelete) })
}
const handleSubmit = () => {
console.log('submit: ', formData)
// router.push(`/inquiry`)
@ -60,7 +55,7 @@ export default function InquiryWriteForm() {
<div key={f.name}>
<div>{f.name}</div>
<div>
<button onClick={() => handleFileDelete(f)}>delete</button>
<button>delete</button>
</div>
</div>
))}

View File

@ -1,10 +1,13 @@
'use client'
import { useRouter } from 'next/navigation'
export default function ListForm() {
const router = useRouter()
return (
<>
<div className="sale-frame">
<div className="sale-form-bx">
<button className="btn-frame n-blue icon">
<button className="btn-frame n-blue icon" onClick={() => router.push('/inquiry/regist')}>
<i className="btn-arr"></i>
</button>
</div>

View File

@ -1,5 +1,52 @@
'use client'
import { use, useEffect, useState } from 'react'
import LoadMoreButton from '../LoadMoreButton'
const inquiryDummy = [
{ id: 1, category: '屋根', title: '屋根材適合性確認依頼', date: '2025.04.02', status: 'completed' },
{ id: 2, category: '外壁', title: '外壁仕上げ材確認', date: '2025.04.03', status: 'completed' },
{ id: 3, category: '設備', title: '換気システム図面確認', date: '2025.04.04', status: 'completed' },
{ id: 4, category: '基礎', title: '基礎配筋検査依頼', date: '2025.04.05', status: 'completed' },
{ id: 5, category: '内装', title: 'クロス仕様確認', date: '2025.04.06', status: 'waiting' },
{ id: 6, category: '構造', title: '耐震壁位置変更申請', date: '2025.04.07', status: 'completed' },
{ id: 7, category: '屋根', title: '雨樋取付方法確認', date: '2025.04.08', status: 'completed' },
{ id: 8, category: '外構', title: 'フェンス高さ変更相談', date: '2025.04.09', status: 'completed' },
{ id: 9, category: '設備', title: '給湯器設置位置確認', date: '2025.04.10', status: 'completed' },
{ id: 10, category: '外壁', title: 'タイル割付案確認依頼', date: '2025.04.11', status: 'waiting' },
{ id: 11, category: '内装', title: '照明配置図面確認', date: '2025.04.12', status: 'completed' },
{ id: 12, category: '構造', title: '梁補強案確認', date: '2025.04.13', status: 'completed' },
{ id: 13, category: '基礎', title: '杭長設計確認依頼', date: '2025.04.14', status: 'completed' },
{ id: 14, category: '屋根', title: '断熱材施工方法確認', date: '2025.04.15', status: 'completed' },
{ id: 15, category: '外構', title: '駐車場勾配図確認', date: '2025.04.16', status: 'completed' },
]
const badgeStyle = [
{
id: 'completed',
label: '回答完了',
color: 'blue',
},
{
id: 'waiting',
label: '回答待ち',
color: 'orange',
},
]
export default function ListTable() {
const [offset, setOffset] = useState(0)
const [hasMore, setHasMore] = useState(true)
const inquiryList = inquiryDummy.slice(0, offset + 10)
useEffect(() => {
if (inquiryDummy.length > offset + 10) {
setHasMore(true)
} else {
setHasMore(false)
}
}, [inquiryList])
return (
<>
<div className="sale-frame">
@ -13,10 +60,8 @@ export default function ListTable() {
<div className="filter-select">
<select className="select-form" name="" id="">
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
</select>
</div>
</div>
@ -25,43 +70,21 @@ export default function ListTable() {
<span>98</span>
</div>
<ul className="inquiry-list">
<li className="inquiry-item">
<div className="inquiry-item-bx">
<div className="inquiry-item-category"></div>
<div className="inquiry-item-tit"></div>
<div className="inquiry-item-date">2025.04.02</div>
<div className="inquiry-badge badge blue"></div>
</div>
</li>
<li className="inquiry-item">
<div className="inquiry-item-bx">
<div className="inquiry-item-category"></div>
<div className="inquiry-item-tit"></div>
<div className="inquiry-item-date">2025.04.02</div>
<div className="inquiry-badge badge orange"></div>
</div>
</li>
<li className="inquiry-item">
<div className="inquiry-item-bx">
<div className="inquiry-item-category"></div>
<div className="inquiry-item-tit"></div>
<div className="inquiry-item-date">2025.04.02</div>
<div className="inquiry-badge badge blue"></div>
</div>
</li>
<li className="inquiry-item">
<div className="inquiry-item-bx">
<div className="inquiry-item-category"></div>
<div className="inquiry-item-tit"></div>
<div className="inquiry-item-date">2025.04.02</div>
<div className="inquiry-badge badge orange"></div>
</div>
</li>
{inquiryList.map((inquiry) => (
<li className="inquiry-item" key={inquiry.id}>
<div className="inquiry-item-bx">
<div className="inquiry-item-category">{inquiry.category}</div>
<div className="inquiry-item-tit">{inquiry.title}</div>
<div className="inquiry-item-date">{inquiry.date}</div>
<div className={`inquiry-badge badge ${badgeStyle.find((badge) => badge.id === inquiry.status)?.color}`}>
{badgeStyle.find((badge) => badge.id === inquiry.status)?.label}
</div>
</div>
</li>
))}
</ul>
<div className="sale-edit-btn">
<button className="btn-frame n-blue icon">
<i className="btn-edit"></i>
</button>
<LoadMoreButton hasMore={hasMore} onLoadMore={() => setOffset(offset + 10)} />
</div>
</div>
</div>

View File

@ -1,18 +1,51 @@
'use client'
import { useAddressStore } from '@/store/addressStore'
import { usePopupController } from '@/store/popupController'
import { useState } from 'react'
const dummyData = [
{
post_code: '123-567',
pref: '東京都',
city: '千代田区',
detail: '永田町ハイツ101号室',
},
{
post_code: '987-654',
pref: '大阪府',
city: '北区',
detail: '梅田スカイビル23階',
},
{
post_code: '456-789',
pref: '福岡県',
city: '博多区',
detail: '中洲マンション305号',
},
]
export default function ZipCodePopup() {
const [searchValue, setSearchValue] = useState('') //search 데이터 유무
const [selected, setSelected] = useState('')
const { setAddressData } = useAddressStore()
//search 데이터 value 추가
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchValue(e.target.value)
}
const popupController = usePopupController()
const handleApply = () => {
const addressData = dummyData.find((item) => item.post_code === selected)
setAddressData({
post_code: addressData?.post_code || '',
address: addressData?.pref || '',
address_detail: addressData?.city + ' ' + addressData?.detail || '',
})
popupController.setZipCodePopup(false)
}
return (
<>
<div className="modal-popup">
@ -50,31 +83,23 @@ export default function ZipCodePopup() {
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
{dummyData.map((item, index) => (
<tr key={`${item.post_code}-${index}`} onClick={() => setSelected(item.post_code)}>
<td>{item.pref}</td>
<td>{item.city}</td>
<td>{item.detail}</td>
</tr>
))}
</tbody>
</table>
<div className="btn-flex-wrap">
<div className="btn-bx">
<button className="btn-frame red icon">
<button className="btn-frame red icon" onClick={handleApply}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame n-blue icon">
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(false)}>
<i className="btn-arr"></i>
</button>
</div>

View File

@ -33,7 +33,7 @@ export default function NavTab() {
return
}
if (detailId) {
router.push(`/survey-sale/${detailId}?tab=basic-info`)
router.push(`/survey-sale/${detailId}`)
return
}
}

View File

@ -1,22 +1,34 @@
'use client'
import { useServey } from '@/hooks/useSurvey'
import { useParams } from 'next/navigation'
import { useParams, useSearchParams } from 'next/navigation'
import { useEffect } from 'react'
import { useState } from 'react'
import DetailForm from './DetailForm'
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
import RoofDetailForm from './RoofDetailForm'
export default function DataTable() {
const params = useParams()
const id = params.id
const searchParams = useSearchParams()
const tab = searchParams.get('tab')
const { surveyDetail, isLoadingSurveyDetail } = useServey(Number(id))
const [isTemporary, setIsTemporary] = useState(false)
const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState()
useEffect(() => {
if (!surveyDetail?.representative || !surveyDetail?.store || !surveyDetail?.construction_point) {
setIsTemporary(true)
}
}, [surveyDetail])
if (tab === 'roof-info') {
setRoofInfoSelected()
} else {
setBasicInfoSelected()
}
}, [surveyDetail, tab, setBasicInfoSelected, setRoofInfoSelected])
if (isLoadingSurveyDetail) {
return <div>Loading...</div>
@ -73,6 +85,11 @@ export default function DataTable() {
</tbody>
</table>
</div>
{tab === 'roof-info' ? (
<RoofDetailForm surveyDetail={surveyDetail} isLoadingSurveyDetail={isLoadingSurveyDetail} />
) : (
<DetailForm surveyDetail={surveyDetail} isLoadingSurveyDetail={isLoadingSurveyDetail} />
)}
</>
)
}

View File

@ -0,0 +1,65 @@
'use client'
import { useRouter } from 'next/navigation'
import { useServey } from '@/hooks/useSurvey'
export default function DetailButton({ isTemporary, surveyId }: { isTemporary: boolean, surveyId: number }) {
const router = useRouter()
const { submitSurvey, deleteSurvey } = useServey(surveyId)
const handleSubmit = async () => {
if (isTemporary) {
alert('SAVE FIRST')
return
}
if (confirm('submit?')) {
if (surveyId) {
// TODO: 제출 페이지 추가
alert('SUBMIT POPUP')
await submitSurvey()
}
}
}
const handleUpdate = () => {
router.push(`/survey-sale/basic-info?id=${surveyId}`)
}
const handleDelete = async () => {
if (confirm('delete?')) {
if (surveyId) {
await deleteSurvey()
router.push('/survey-sale')
}
}
}
return (
<div className="btn-flex-wrap">
{isTemporary ? (
<></>
) : (
<>
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame red icon" onClick={handleSubmit}>
<i className="btn-arr"></i>
</button>
</div>
</>
)}
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={handleUpdate}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={handleDelete}>
<i className="btn-arr"></i>
</button>
</div>
</div>
)
}

View File

@ -1,64 +1,27 @@
'use client'
import { useServey } from '@/hooks/useSurvey'
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
import { useEffect, useState } from 'react'
import { useParams, useRouter, useSearchParams } from 'next/navigation'
import DetailButton from './DetailButton'
import { SurveyBasicInfo } from '@/types/Survey'
export default function DetailForm() {
const router = useRouter()
const params = useParams()
const id = params.id
const searchParams = useSearchParams()
const tab = searchParams.get('tab')
const { surveyDetail, deleteSurvey, submitSurvey, isLoadingSurveyDetail } = useServey(Number(id))
const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState()
export default function DetailForm({
surveyDetail,
isLoadingSurveyDetail,
}: {
surveyDetail: SurveyBasicInfo | null
isLoadingSurveyDetail: boolean
}) {
const [isTemporary, setIsTemporary] = useState(true)
// TODO: 조사매물 지붕정보 퍼블 구현되면 탭 화면 변경 추가
useEffect(() => {
if (tab === 'basic-info') {
setBasicInfoSelected()
}
if (tab === 'roof-info') {
setRoofInfoSelected()
}
if (surveyDetail?.representative && surveyDetail?.store && surveyDetail?.construction_point) {
setIsTemporary(false)
}
}, [tab, setBasicInfoSelected, setRoofInfoSelected, surveyDetail])
}, [surveyDetail])
if (isLoadingSurveyDetail) {
return <div>Loading...</div>
}
const handleSubmit = async () => {
if (isTemporary) {
alert('SAVE FIRST')
return
}
if (confirm('submit?')) {
if (surveyDetail?.id) {
// TODO: 제출 페이지 추가
alert('SUBMIT POPUP')
await submitSurvey()
}
}
}
const handleUpdate = () => {
router.push(`/survey-sale/basic-info?id=${id}`)
}
const handleDelete = async () => {
if (confirm('delete?')) {
if (surveyDetail?.id) {
await deleteSurvey()
router.push('/survey-sale')
}
}
}
return (
<>
<div className="sale-frame">
@ -93,34 +56,7 @@ export default function DetailForm() {
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.customer_name ?? ''} />
</div>
</div>
<div className="btn-flex-wrap">
{isTemporary ? (
<></>
) : (
<>
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame red icon" onClick={handleSubmit}>
<i className="btn-arr"></i>
</button>
</div>
</>
)}
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={handleUpdate}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={handleDelete}>
<i className="btn-arr"></i>
</button>
</div>
</div>
<DetailButton isTemporary={isTemporary} surveyId={Number(surveyDetail?.id)} />
</div>
</>
)

View File

@ -0,0 +1,26 @@
import { SurveyBasicInfo } from '@/types/Survey'
import DetailButton from './DetailButton'
export default function RoofDetailForm({
surveyDetail,
isLoadingSurveyDetail,
}: {
surveyDetail: SurveyBasicInfo | null
isLoadingSurveyDetail: boolean
}) {
if (isLoadingSurveyDetail) {
return <div>Loading...</div>
}
return (
<>
<div className="sale-frame">
<div className="data-form-wrap">
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
</div>
</div>
<DetailButton isTemporary={false} surveyId={Number(surveyDetail?.id)} />
</div>
</>
)
}

View File

@ -5,6 +5,8 @@ import { SurveyBasicRequest } from '@/types/Survey'
import { useRouter, useSearchParams } from 'next/navigation'
import { useState, useEffect } from 'react'
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
import { usePopupController } from '@/store/popupController'
import { useAddressStore } from '@/store/addressStore'
const defaultBasicInfoForm: SurveyBasicRequest = {
representative: '',
@ -32,16 +34,25 @@ export default function BasicForm() {
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(defaultBasicInfoForm)
const { addressData } = useAddressStore()
const popupController = usePopupController()
useEffect(() => {
if (surveyDetail) {
const { id, updated_at, created_at, detail_info, ...rest } = surveyDetail
setBasicInfoData(rest)
}
}, [surveyDetail])
useEffect(() => {
if (addressData) {
setBasicInfoData({
...basicInfoData,
post_code: addressData.post_code,
address: addressData.address,
address_detail: addressData.address_detail,
})
}
setBasicInfoSelected()
}, [])
}, [surveyDetail, addressData])
const focusInput = (input: keyof SurveyBasicRequest) => {
const inputElement = document.getElementById(input)
@ -192,7 +203,7 @@ export default function BasicForm() {
</div>
</div>
<div className="form-btn">
<button className="btn-frame n-blue icon">
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(true)}>
便<i className="btn-arr"></i>
</button>
</div>

View File

@ -2,46 +2,50 @@
import LoadMoreButton from '@/components/LoadMoreButton'
import { useServey } from '@/hooks/useSurvey'
import { useEffect, useState } from 'react'
import { useEffect, useState, useRef } from 'react'
import { useRouter } from 'next/navigation'
import SearchForm from './SearchForm'
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
export default function ListTable() {
const router = useRouter()
const { surveyList, isLoadingSurveyList } = useServey()
const { surveyList, isLoadingSurveyList, surveyListCount } = useServey()
const { offset, setOffset } = useSurveyFilterStore()
const [visibleItems, setVisibleItems] = useState(10)
const [heldSurveyList, setHeldSurveyList] = useState<typeof surveyList>([])
const [hasMore, setHasMore] = useState(false)
useEffect(() => {
setHasMore(surveyList.length > visibleItems)
}, [surveyList, visibleItems])
const handleLoadMore = () => {
const newVisibleItems = Math.min(visibleItems + 10, surveyList.length)
setVisibleItems(newVisibleItems)
setHasMore(newVisibleItems < surveyList.length)
}
const handleScrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
if (surveyList && surveyList.length > 0) {
if (offset === 0) {
setHeldSurveyList(surveyList)
} else {
const remainingList = heldSurveyList.slice(offset, offset + 10)
if (JSON.stringify(remainingList) !== JSON.stringify(surveyList)) {
setHeldSurveyList((prev) => [...prev, ...surveyList])
}
}
setHasMore(surveyListCount > offset + 10)
}
}, [surveyList, surveyListCount, offset])
const handleDetailClick = (id: number) => {
router.push(`/survey-sale/${id}`)
}
if (isLoadingSurveyList) {
return <div>Loading...</div>
const handleItemsInit = () => {
setHeldSurveyList([])
setOffset(0)
}
// TODO: 로딩 처리 필요
return (
<>
<SearchForm onItemsInit={() => setVisibleItems(10)} />
{surveyList.length > 0 ? (
<SearchForm onItemsInit={handleItemsInit} />
{heldSurveyList.length > 0 ? (
<div className="sale-frame">
<ul className="sale-list-wrap">
{surveyList.slice(0, visibleItems).map((survey) => (
{heldSurveyList.map((survey) => (
<li className="sale-list-item cursor-pointer" key={survey.id} onClick={() => handleDetailClick(survey.id)}>
<div className="sale-item-bx">
<div className="sale-item-date-bx">
@ -59,7 +63,7 @@ export default function ListTable() {
))}
</ul>
<div className="sale-edit-btn">
<LoadMoreButton hasMore={hasMore} onLoadMore={handleLoadMore} onScrollToTop={handleScrollToTop} />
<LoadMoreButton hasMore={hasMore} onLoadMore={() => setOffset(offset + 10)} />
</div>
</div>
) : (

View File

@ -6,6 +6,7 @@ import { useSurveyFilterStore } from '@/store/surveyFilterStore'
export function useServey(id?: number): {
surveyList: SurveyBasicInfo[] | []
surveyDetail: SurveyBasicInfo | null
surveyListCount: number
isLoadingSurveyList: boolean
isLoadingSurveyDetail: boolean
isCreatingSurvey: boolean
@ -19,14 +20,13 @@ export function useServey(id?: number): {
validateSurveyDetail: (surveyDetail: SurveyDetailRequest) => string
} {
const queryClient = useQueryClient()
const { keyword, searchOption, isMySurvey, sort } = useSurveyFilterStore()
const { keyword, searchOption, isMySurvey, sort, offset } = useSurveyFilterStore()
const { data: surveyList, isLoading: isLoadingSurveyList } = useQuery({
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort],
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort, offset],
queryFn: async () => {
console.log('keyword, searchOption, isMySurvey, sort:: ', keyword, searchOption, isMySurvey, sort)
const resp = await axiosInstance(null).get<SurveyBasicInfo[]>('/api/survey-sales', {
params: { keyword, searchOption, isMySurvey, sort },
params: { keyword, searchOption, isMySurvey, sort, offset },
})
return resp.data
},
@ -43,6 +43,16 @@ export function useServey(id?: number): {
enabled: id !== undefined,
})
const { data: surveyListCount } = useQuery({
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort],
queryFn: async () => {
const resp = await axiosInstance(null).get<number>('/api/survey-sales', {
params: { keyword, searchOption, isMySurvey, sort },
})
return resp.data
},
})
const { mutateAsync: createSurvey, isPending: isCreatingSurvey } = useMutation({
mutationFn: async (survey: SurveyBasicRequest) => {
const resp = await axiosInstance(null).post<SurveyBasicInfo>('/api/survey-sales', survey)
@ -80,7 +90,7 @@ export function useServey(id?: number): {
},
})
const { mutateAsync: createSurveyDetail, isPending: isCreatingSurveyDetail } = useMutation({
const { mutateAsync: createSurveyDetail } = useMutation({
mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => {
const resp = await axiosInstance(null).patch<SurveyDetailInfo>(`/api/survey-sales/${surveyId}`, surveyDetail)
return resp.data
@ -137,6 +147,7 @@ export function useServey(id?: number): {
return {
surveyList: surveyList || [],
surveyDetail: surveyDetail || null,
surveyListCount: surveyListCount || 0,
isLoadingSurveyList,
isLoadingSurveyDetail,
isCreatingSurvey,

19
src/store/addressStore.ts Normal file
View File

@ -0,0 +1,19 @@
import { create } from 'zustand'
type AddressData = {
post_code: string
address: string
address_detail: string
}
interface AddressState {
addressData: AddressData | null
setAddressData: (address: AddressData) => void
resetAddressData: () => void
}
export const useAddressStore = create<AddressState>((set) => ({
addressData: null,
setAddressData: (address) => set({ addressData: address }),
resetAddressData: () => set({ addressData: null }),
}))

View File

@ -0,0 +1,41 @@
import { create } from 'zustand'
export const FILTER_OPTIONS = [
{
id: 'all',
label: '全体',
},
{
id: 'completed',
label: '回答完了',
},
{
id: 'waiting',
label: '回答待ち',
},
]
export type FILTER_OPTIONS_ENUM = (typeof FILTER_OPTIONS)[number]['id']
type InquiryFilterState = {
keyword: string
filter: FILTER_OPTIONS_ENUM
isMySurvey: string | null
offset: number
setKeyword: (keyword: string) => void
setFilter: (filter: FILTER_OPTIONS_ENUM) => void
setIsMySurvey: (isMySurvey: string | null) => void
setOffset: (offset: number) => void
reset: () => void
}
export const useInquiryFilterStore = create<InquiryFilterState>((set) => ({
keyword: '',
filter: 'all',
isMySurvey: null,
offset: 0,
setKeyword: (keyword) => set({ keyword }),
setFilter: (filter) => set({ filter }),
setIsMySurvey: (isMySurvey) => set({ isMySurvey }),
setOffset: (offset) => set({ offset }),
reset: () => set({ keyword: '', filter: 'all', isMySurvey: null, offset: 0 }),
}))

View File

@ -1,10 +1,10 @@
import { create } from 'zustand'
export type SEARCH_OPTIONS_ENUM = 'all' | 'id' | 'building_name' | 'representative' | 'store' | 'construction_point'
// export type SEARCH_OPTIONS_ENUM = 'all' | 'id' | 'building_name' | 'representative' | 'store' | 'construction_point'
// | 'store_id' | 'construction_id'
export type SEARCH_OPTIONS_PARTNERS_ENUM = 'all' | 'id' | 'building_name' | 'representative'
// export type SEARCH_OPTIONS_PARTNERS_ENUM = 'all' | 'id' | 'building_name' | 'representative'
export type SORT_OPTIONS_ENUM = 'created' | 'updated'
// export type SORT_OPTIONS_ENUM = 'created' | 'updated'
export const SEARCH_OPTIONS = [
{
@ -27,18 +27,18 @@ export const SEARCH_OPTIONS = [
id: 'store',
label: '販売店名',
},
// {
// id: 'store_id',
// label: '販売店ID',
// },
// {
// id: 'store_id',
// label: '販売店ID',
// },
{
id: 'construction_point',
label: '施工店名',
},
// {
// id: 'construction_id',
// label: '施工店ID',
// },
// {
// id: 'construction_id',
// label: '施工店ID',
// },
]
export const SEARCH_OPTIONS_PARTNERS = [
@ -60,15 +60,21 @@ export const SEARCH_OPTIONS_PARTNERS = [
},
]
export type SEARCH_OPTIONS_ENUM = (typeof SEARCH_OPTIONS)[number]['id']
export type SEARCH_OPTIONS_PARTNERS_ENUM = (typeof SEARCH_OPTIONS_PARTNERS)[number]['id']
export type SORT_OPTIONS_ENUM = 'created' | 'updated'
type SurveyFilterState = {
keyword: string
searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM
isMySurvey: string | null
sort: SORT_OPTIONS_ENUM
offset: number
setKeyword: (keyword: string) => void
setSearchOption: (searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM) => void
setIsMySurvey: (isMySurvey: string | null) => void
setSort: (sort: SORT_OPTIONS_ENUM) => void
setOffset: (offset: number) => void
reset: () => void
}
@ -77,9 +83,11 @@ export const useSurveyFilterStore = create<SurveyFilterState>((set) => ({
searchOption: 'all',
isMySurvey: null,
sort: 'created',
offset: 0,
setKeyword: (keyword: string) => set({ keyword }),
setSearchOption: (searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM) => set({ searchOption }),
setIsMySurvey: (isMySurvey: string | null) => set({ isMySurvey }),
setSort: (sort: SORT_OPTIONS_ENUM) => set({ sort }),
reset: () => set({ keyword: '', searchOption: 'all', isMySurvey: null, sort: 'created' }),
setOffset: (offset: number) => set({ offset }),
reset: () => set({ keyword: '', searchOption: 'all', isMySurvey: null, sort: 'created', offset: 0 }),
}))