feat: add inquiry types code option & filtering inquiry list by answer exist

This commit is contained in:
Dayoung 2025-05-22 14:26:51 +09:00
parent f74c0a6136
commit b0878c853b
12 changed files with 153 additions and 85 deletions

View File

@ -8,8 +8,8 @@ NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120
#1:1문의 api
# NEXT_PUBLIC_INQUIRY_API_URL=http://1.248.227.176:38080
# NEXT_PUBLIC_INQUIRY_API_URL=http://172.23.4.129:8110
NEXT_PUBLIC_INQUIRY_API_URL=http://172.30.1.93:8120
NEXT_PUBLIC_INQUIRY_API_URL=http://172.23.4.129:8110
# NEXT_PUBLIC_INQUIRY_API_URL=http://172.30.1.93:8120
#QPARTNER 로그인 api

View File

@ -13,7 +13,6 @@ export async function GET(request: Request) {
try {
const response = await axios.get(`${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/api/qna/detail?${queryStringFormatter(params)}`)
console.log('response.data detail:: ', response.data)
if (response.status === 200) {
return NextResponse.json(response.data)
}

View File

@ -9,13 +9,11 @@ export async function GET(request: Request) {
return NextResponse.json({ error: 'encodeFileNo is required' }, { status: 400 })
}
try {
const response = await axios.get(`${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/api/file/downloadFile`, {
const response = await axios.get(`${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/file/downloadFile`, {
params: {
encodeFileNo,
},
})
console.log('response.data:: ', response.data)
return NextResponse.json(response.data)
} catch (error: any) {
console.error(error.response)

View File

@ -17,7 +17,6 @@ export async function GET(request: Request) {
try {
const response = await axios.get(`${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/api/qna/list?${queryStringFormatter(params)}`)
console.log('response.data:: ', response.data)
if (response.status === 200) {
return NextResponse.json(response.data)
}

View File

@ -9,7 +9,6 @@ export async function POST(request: Request) {
'Content-Type': 'multipart/form-data',
},
})
console.log('response:: ', response)
if (response.status === 200) {
return NextResponse.json(response.data)
}

View File

@ -1,6 +1,5 @@
'use client'
import { useInquiry } from '@/hooks/useInquiry'
import { Inquiry } from '@/types/Inquiry'
export default function Answer({ inquiryDetail, downloadFile }: { inquiryDetail: Inquiry; downloadFile: (encodeFileNo: number) => Promise<File> }) {
@ -15,10 +14,7 @@ export default function Answer({ inquiryDetail, downloadFile }: { inquiryDetail:
</div>
<div className="inquiry-detail-data">
<div className="inquiry-answer-tit"></div>
<div className="inquiry-detail-txt">
, . ,
</div>
<div className="inquiry-detail-txt">{inquiryDetail?.ansContents}</div>
</div>
<div className="file-list-wrap">
<div className="file-list-tit"></div>

View File

@ -4,18 +4,13 @@ import { useInquiry } from '@/hooks/useInquiry'
import { useSessionStore } from '@/store/session'
import { InquiryRequest } from '@/types/Inquiry'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
export default function RegistForm() {
const { saveInquiry, isSavingInquiry } = useInquiry()
const { session } = useSessionStore()
const router = useRouter()
// TODO: 세션 정보 적용 | 현재는 test용 정보 적용
// useEffect(() => {
// setInquiryRequest({ ...inquiryRequest, regId: session?.userId ?? '', regUserNm: session?.userNm ?? '' })
// }, [session])
const [inquiryRequest, setInquiryRequest] = useState<InquiryRequest>({
compCd: '5200',
siteTpCd: 'QC',
@ -24,12 +19,26 @@ export default function RegistForm() {
qnaClsSmlCd: null,
title: '',
contents: '',
regId: 'X112',
regUserNm: 'TEST',
regId: '',
regUserNm: '',
regUserTelNo: null,
storeId: 'X112',
storeId: '',
qstMail: '',
})
const requiredFieldNames = [
{ id: 'qnaClsLrgCd', name: 'お問い合わせタイプ' },
{ id: 'qnaClsMidCd', name: 'お問い合わせタイプ' },
{ id: 'regUserNm', name: '名前' },
{ id: 'qstMail', name: 'E-mail' },
{ id: 'title', name: 'お問い合わせタイトル' },
{ id: 'contents', name: 'お問い合わせ内容' },
]
useEffect(() => {
if (session?.isLoggedIn) {
setInquiryRequest({ ...inquiryRequest, regId: session?.userId ?? '', regUserNm: session?.userNm ?? '', storeId: session?.storeId ?? '' })
}
}, [session])
const [attachedFiles, setAttachedFiles] = useState<File[]>([])
@ -45,7 +54,25 @@ export default function RegistForm() {
setAttachedFiles(attachedFiles.filter((_, i) => i !== index))
}
const focusOnRequiredField = (fieldId: string) => {
const element = document.getElementById(fieldId)
if (element) element.focus()
}
const handleSubmit = async () => {
const emptyField = requiredFieldNames.find((field) => inquiryRequest[field.id as keyof InquiryRequest] === '')
if (emptyField) {
alert(`${emptyField?.name}を入力してください。`)
focusOnRequiredField(emptyField?.id ?? '')
return
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(inquiryRequest.qstMail)) {
alert('有効なメールアドレスを入力してください。')
focusOnRequiredField('qstMail')
return
}
const formData = new FormData()
attachedFiles.forEach((file) => {
formData.append('files', file)
@ -54,12 +81,10 @@ export default function RegistForm() {
formData.append(key, value ?? '')
})
// FormData를 객체로 변환하여 확인
const formDataObj: Record<string, any> = {}
formData.forEach((value, key) => {
formDataObj[key] = value
})
console.log('formData contents:', formDataObj)
window.neoConfirm(
'お問い合わせを登録しますか? Hanwha Japanの担当者にお問い合わせメールが送信されます。',
@ -83,40 +108,40 @@ export default function RegistForm() {
<div className="data-input">
<select
className="select-form"
name=""
name="qnaClsLrgCd"
id="qnaClsLrgCd"
value={inquiryRequest.qnaClsLrgCd}
onChange={(e) => setInquiryRequest({ ...inquiryRequest, qnaClsLrgCd: e.target.value })}
>
<option value=""></option>
<option value="A01">A01</option>
<option value="A01"></option>
<option value="204200">204200</option>
</select>
</div>
<div className="data-input mt5">
<select
className="select-form"
name=""
name="qnaClsMidCd"
id="qnaClsMidCd"
value={inquiryRequest.qnaClsMidCd}
onChange={(e) => setInquiryRequest({ ...inquiryRequest, qnaClsMidCd: e.target.value })}
>
<option value=""></option>
<option value="B02">B02</option>
<option value="B02"></option>
<option value="204300">204300</option>
</select>
</div>
<div className="data-input mt5">
<select
className="select-form"
name=""
name="qnaClsSmlCd"
id="qnaClsSmlCd"
value={inquiryRequest.qnaClsSmlCd ?? ''}
onChange={(e) => setInquiryRequest({ ...inquiryRequest, qnaClsSmlCd: e.target.value })}
>
<option value=""></option>
<option value="C05">C05</option>
<option value="C05"></option>
<option value="204400">204400</option>
</select>
</div>
</div>
@ -130,6 +155,8 @@ export default function RegistForm() {
type="text"
placeholder="名前を書いてください"
onChange={(e) => setInquiryRequest({ ...inquiryRequest, regUserNm: e.target.value })}
value={inquiryRequest.regUserNm}
id="regUserNm"
/>
</div>
</div>
@ -141,6 +168,7 @@ export default function RegistForm() {
type="text"
placeholder="電話番号を書き留めてください"
onChange={(e) => setInquiryRequest({ ...inquiryRequest, regUserTelNo: e.target.value })}
id="regUserTelNo"
/>
</div>
</div>
@ -154,6 +182,7 @@ export default function RegistForm() {
type="text"
placeholder="E-mailを書いてください"
onChange={(e) => setInquiryRequest({ ...inquiryRequest, qstMail: e.target.value })}
id="qstMail"
/>
</div>
</div>
@ -167,6 +196,7 @@ export default function RegistForm() {
type="text"
placeholder="お問い合わせタイトルを記入してください"
onChange={(e) => setInquiryRequest({ ...inquiryRequest, title: e.target.value })}
id="title"
/>
</div>
</div>
@ -178,8 +208,7 @@ export default function RegistForm() {
<textarea
className="textarea-form"
rows={6}
name=""
id=""
id="contents"
placeholder="TextArea Filed"
onChange={(e) => setInquiryRequest({ ...inquiryRequest, contents: e.target.value })}
></textarea>

View File

@ -13,7 +13,6 @@ export default function ListForm() {
setInquiryListRequest({ ...inquiryListRequest, schTitle: searchKeyword })
}
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleSearch()

View File

@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'
import LoadMoreButton from '../../LoadMoreButton'
import { useInquiry } from '@/hooks/useInquiry'
import { InquiryList } from '@/types/Inquiry'
import { useRouter } from 'next/navigation'
import { usePathname, useRouter } from 'next/navigation'
import { useInquiryFilterStore } from '@/store/inquiryFilterStore'
import { useSessionStore } from '@/store/session'
import ListForm from './ListForm'
@ -22,48 +22,66 @@ const badgeStyle = [
},
]
export default function ListTable() {
const [offset, setOffset] = useState(0)
const [hasMore, setHasMore] = useState(false)
const router = useRouter()
const pathname = usePathname()
const { inquiryList } = useInquiry()
const { inquiryListRequest, setInquiryListRequest } = useInquiryFilterStore()
const { inquiryListRequest, setInquiryListRequest, reset } = useInquiryFilterStore()
const [heldInquiryList, setHeldInquiryList] = useState<typeof inquiryList>([])
const [offset, setOffset] = useState(inquiryListRequest.startRow)
const [hasMore, setHasMore] = useState(false)
const [heldInquiryList, setHeldInquiryList] = useState<InquiryList[]>([])
const { session } = useSessionStore()
useEffect(() => {
if (!inquiryList) return
setHeldInquiryList(inquiryList)
setHasMore(inquiryList.length > offset + 10)
}, [inquiryList, offset])
setInquiryListRequest({ ...inquiryListRequest, startRow: 1, endRow: 10 })
setHeldInquiryList([])
setOffset(1)
setHasMore(false)
}, [pathname])
console.log('heldInquiryList:: ', heldInquiryList)
console.log('inquiryList:: ', inquiryList)
useEffect(() => {
if (!session.isLoggedIn || !inquiryList) return
if (session.isLoggedIn) {
setInquiryListRequest({ ...inquiryListRequest, storeId: session.storeId ?? '', loginId: session.userId ?? '' })
// setInquiryListRequest({ ...inquiryListRequest, storeId: 'X112', loginId: 'x112' })
}
console.log('inquiryListRequest', inquiryListRequest)
if (inquiryList.length > 0 && inquiryList[0].totCnt > 0) {
if (inquiryListRequest.startRow > 1) {
const isDuplicate = inquiryList.every((newItem) => heldInquiryList.some((existingItem) => existingItem.qnaNo === newItem.qnaNo))
if (isDuplicate) return
setHeldInquiryList((prev) => [...prev, ...inquiryList])
} else {
setHeldInquiryList(inquiryList)
}
setHasMore(inquiryList[0].totCnt > inquiryListRequest.endRow)
} else {
setHeldInquiryList([])
setHasMore(false)
}
}, [session, inquiryList, inquiryListRequest.startRow])
const handleMyInquiry = () => {
setInquiryListRequest({ ...inquiryListRequest, schRegId: inquiryListRequest.schRegId ? null : session.userId })
}
const handleLoadMore = () => {
setOffset(offset + 10)
setInquiryListRequest({ ...inquiryListRequest, startRow: offset, endRow: offset + 10 })
setHeldInquiryList((prev) => [...prev, ...inquiryList])
}
const handleFilter = (e: React.ChangeEvent<HTMLSelectElement>) => {
console.log(e.target.value)
setHeldInquiryList(inquiryList.filter((inquiry: InquiryList) => inquiry.answerYn === e.target.value))
if (e.target.value === '') {
setHeldInquiryList(inquiryList)
switch (e.target.value) {
case 'N':
setInquiryListRequest({ ...inquiryListRequest, schAnswerYn: 'N' })
break
case 'Y':
setInquiryListRequest({ ...inquiryListRequest, schAnswerYn: 'Y' })
break
default:
reset()
break
}
}
console.log('heldInquiryList:: ', heldInquiryList)
return (
<>
<ListForm />
@ -87,23 +105,39 @@ export default function ListTable() {
<div className="inquiry-list-tit">
<span>{heldInquiryList.length > 0 ? heldInquiryList[0].totCnt : 0}</span>
</div>
<ul className="inquiry-list">
{heldInquiryList.length > 0 &&
heldInquiryList.map((inquiry: InquiryList) => (
<li className="inquiry-item" key={inquiry.qnaNo} onClick={() => router.push(`/inquiry/${inquiry.qnaNo}`)}>
<div className="inquiry-item-bx">
<div className="inquiry-item-category">{inquiry.qnaClsLrgCd}</div>
<div className="inquiry-item-tit">{inquiry.qstTitle}</div>
<div className="inquiry-item-date">{inquiry.regDt}</div>
<div className={`inquiry-badge badge ${badgeStyle.find((badge) => badge.id === inquiry.answerYn)?.color}`}>
{badgeStyle.find((badge) => badge.id === inquiry.answerYn)?.label}
{heldInquiryList.length === 0 || (heldInquiryList.length > 0 && heldInquiryList[0].totCnt === 0) ? (
<div className="inquiry-item-bx nodata">
<div className="inquiry-item-nodata"></div>
</div>
) : (
<ul className="inquiry-list">
{heldInquiryList.length > 0 &&
heldInquiryList.map((inquiry: InquiryList) => (
<li className="inquiry-item" key={inquiry.qnaNo} onClick={() => router.push(`/inquiry/${inquiry.qnaNo}`)}>
<div className="inquiry-item-bx">
<div className="inquiry-item-category">
<span>{inquiry.qnaClsLrgCd}</span>
<span>{inquiry.qnaClsMidCd}</span>
<span>{inquiry.qnaClsSmlCd}</span>
</div>
<div className="inquiry-item-tit">{inquiry.qstTitle}</div>
<div className="inquiry-item-date">{inquiry.regDt}</div>
<div className={`inquiry-badge badge ${badgeStyle.find((badge) => badge.id === inquiry.answerYn)?.color}`}>
{badgeStyle.find((badge) => badge.id === inquiry.answerYn)?.label}
</div>
</div>
</div>
</li>
))}
</ul>
</li>
))}
</ul>
)}
<div className="sale-edit-btn">
<LoadMoreButton hasMore={hasMore} onLoadMore={() => handleLoadMore()} />
<LoadMoreButton
hasMore={hasMore}
onLoadMore={() => {
setInquiryListRequest({ ...inquiryListRequest, startRow: offset + 10, endRow: offset + 19 })
setOffset(inquiryListRequest.startRow)
}}
/>
</div>
</div>
</div>

View File

@ -1,7 +1,9 @@
import { InquiryList, Inquiry, InquirySaveResponse } from '@/types/Inquiry'
import { axiosInstance } from '@/libs/axios'
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useInquiryFilterStore } from '@/store/inquiryFilterStore'
import { useMemo } from 'react'
import { useSessionStore } from '@/store/session'
export function useInquiry(
qnoNo?: number,
@ -17,6 +19,7 @@ export function useInquiry(
} {
const queryClient = useQueryClient()
const { inquiryListRequest } = useInquiryFilterStore()
const { session } = useSessionStore()
const { data: inquiryList, isLoading: isLoadingInquiryList } = useQuery({
queryKey: ['inquiryList', inquiryListRequest],
@ -31,16 +34,25 @@ export function useInquiry(
return []
}
},
placeholderData: (previousData) => previousData,
})
const inquriyListData = useMemo(() => {
if (isLoadingInquiryList) {
return { inquiryList: inquiryList ?? [] }
}
return {
inquiryList: inquiryList ?? [],
}
}, [inquiryList, isLoadingInquiryList])
const { data: inquiryDetail, isLoading: isLoadingInquiryDetail } = useQuery({
queryKey: ['inquiryDetail', qnoNo, compCd],
queryKey: ['inquiryDetail', qnoNo, compCd, session?.userId],
queryFn: async () => {
try {
const resp = await axiosInstance(null).get<{ data: Inquiry }>(`/api/qna/detail`, {
params: { qnoNo, compCd, langCd: 'JA', loginId: 'x112' },
params: { qnoNo, compCd, langCd: 'JA', loginId: session?.userId ?? '' },
})
console.log('resp.data.data:: ', resp.data.data)
return resp.data.data
} catch (error: any) {
console.error(error.response)
@ -66,7 +78,7 @@ export function useInquiry(
}
return {
inquiryList: inquiryList ?? [],
inquiryList: inquriyListData.inquiryList,
inquiryDetail: inquiryDetail ?? null,
isLoadingInquiryList,
isLoadingInquiryDetail,

View File

@ -11,15 +11,16 @@ export const useInquiryFilterStore = create<InquiryFilterState>((set) => ({
inquiryListRequest: {
compCd: '5200',
langCd: 'JA',
storeId: 'X112',
storeId: '',
siteTpCd: 'QC',
schTitle: null,
schRegId: null,
schFromDt: null,
schToDt: null,
startRow: 0,
schAnswerYn: null,
startRow: 1,
endRow: 10,
loginId: 'x112',
loginId: '',
},
setInquiryListRequest: (inquiryListRequest) => set({ inquiryListRequest }),
reset: () =>
@ -27,15 +28,16 @@ export const useInquiryFilterStore = create<InquiryFilterState>((set) => ({
inquiryListRequest: {
compCd: '5200',
langCd: 'JA',
storeId: 'X112',
storeId: '',
siteTpCd: 'QC',
schTitle: '',
schRegId: '',
schFromDt: '',
schToDt: '',
startRow: 0,
endRow: 50,
loginId: 'x112',
schAnswerYn: null,
startRow: 1,
endRow: 10,
loginId: '',
},
}),
}))

View File

@ -7,6 +7,7 @@ export type InquiryListRequest = {
schRegId: string | null //search regId
schFromDt: string | null //search start date
schToDt: string | null //search end date
schAnswerYn: string | null //search answer yn
startRow: number //start row
endRow: number //end row
loginId: string //login id