@@ -25,59 +28,50 @@ export default function Detail() {
| 登録日 |
- 2025.04.10 |
+ {inquiryDetail?.regDt} |
| 作者 |
- Hong gi |
+ {inquiryDetail?.regNm} |
| 販売店 |
- interplug |
+ {inquiryDetail?.storeNm} |
| 施工店 |
- interplugs |
+ {inquiryDetail?.compCd} |
| E-mail |
- Hong@interplug.co.kr |
+ {inquiryDetail?.regEmail} |
屋根適合
-
屋根材適合性確認依頼
-
- 入力した内容が表示されます.
-
- インストール可能であることを確認してください.
-
- 屋根の写真を添付しました.
-
+
{inquiryDetail?.qstTitle}
+
{inquiryDetail?.qstContent}
ファイル添付
- -
-
-
- -
-
-
+ {inquiryDetail?.listFile?.map((file) => (
+ -
+
+
+ ))}
- {inquiry &&
}
+ {inquiryDetail?.answerYn === 'Y' &&
}
-
diff --git a/src/components/inquiry/InquiryDetail.tsx b/src/components/inquiry/InquiryDetail.tsx
deleted file mode 100644
index b153f35..0000000
--- a/src/components/inquiry/InquiryDetail.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-'use client'
-
-import { useParams } from 'next/navigation'
-
-const inquiryDummyData = {
- writer: {
- name: 'writer',
- email: 'writer@example.com',
- },
- title: 'title',
- content: 'content',
- files: ['file1.jpg', 'file2.jpg', 'file3.jpg'],
- createdAt: '2021-01-01',
- answer: {
- writer: '佐藤一貴',
- content:
- '一次側接続は、自動切替開閉器と住宅分電盤主幹ブレーカの間に蓄電システムブレーカを配線する方法です。\n二次側接続は、住宅分電盤主幹ブレ―カの二次側に蓄電システムブレーカを接続する',
- createdAt: '2021-01-01 12:00:00',
- files: ['file4.jpg', 'file5.jpg', 'file6.jpg'],
- },
-}
-
-export default function InquiryDetail() {
- const params = useParams()
- const id = params.id
- return (
-
-
InquiryDetail
-
{id}
-
-
-
writer
-
{inquiryDummyData.writer.name}
-
-
-
email
-
{inquiryDummyData.writer.email}
-
-
-
title
-
{inquiryDummyData.title}
-
-
-
content
-
{inquiryDummyData.content}
-
-
-
files
-
- {inquiryDummyData.files.map((file) => (
- {file}
- ))}
-
-
- {inquiryDummyData.answer && (
-
-
Reply: Hanwha Japan
-
-
{inquiryDummyData.answer.writer}
-
{inquiryDummyData.answer.createdAt}
-
{inquiryDummyData.answer.content}
-
- {inquiryDummyData.answer.files.map((file) => (
- {file}
- ))}
-
-
-
- )}
-
-
- )
-}
diff --git a/src/components/inquiry/InquiryFilter.tsx b/src/components/inquiry/InquiryFilter.tsx
deleted file mode 100644
index c3911a2..0000000
--- a/src/components/inquiry/InquiryFilter.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-'use client'
-
-import { Search } from 'lucide-react'
-import { useRouter } from 'next/navigation'
-
-
-export default function InquiryFilter({ handleSearch }: { handleSearch: (e: React.ChangeEvent
) => void }) {
- const router = useRouter()
- return (
-
-
router.push('/inquiry/write')}>write 1:1 Inquiry {'>'}
-
-
-
-
-
-
-
- )
-}
diff --git a/src/components/inquiry/InquiryItems.tsx b/src/components/inquiry/InquiryItems.tsx
deleted file mode 100644
index bc38ad6..0000000
--- a/src/components/inquiry/InquiryItems.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-'use client'
-
-import { useRouter } from 'next/navigation'
-
-export default function InquiryItems({ inquiryData }: { inquiryData: any }) {
- const router = useRouter()
- return (
-
- {inquiryData.map((item: any) => (
-
router.push(`/inquiry/${item.id}`)}>
-
{item.title}
-
{item.content}
-
{item.createdAt}
-
{item.writer}
-
{item.category}
- {item.file &&
{item.file}
}
-
- ))}
-
- )
-}
diff --git a/src/components/inquiry/InquiryList.tsx b/src/components/inquiry/InquiryList.tsx
deleted file mode 100644
index f65478b..0000000
--- a/src/components/inquiry/InquiryList.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-'use client'
-import { useState } from 'react'
-import InquiryItems from './InquiryItems'
-import InquiryFilter from './InquiryFilter'
-import LoadMoreButton from '../LoadMoreButton'
-
-const inquiryDummyData = [
- {
- id: 1,
- title: 'post',
- content: 'content',
- file: 'file.png',
- createdAt: '2024-01-01',
- writer: 'writer',
- category: 'A',
- },
- {
- id: 2,
- title: 'post',
- content: 'content',
- file: 'file.png',
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'B',
- },
- {
- id: 3,
- title: 'post',
- content: 'content',
- file: null,
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'C',
- },
- {
- id: 4,
- title: 'post',
- content: 'content',
- file: null,
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'A',
- },
- {
- id: 5,
- title: 'post',
- content: 'content',
- file: null,
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'B',
- },
- {
- id: 6,
- title: 'post',
- content: 'content',
- file: null,
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'C',
- },
- {
- id: 7,
- title: 'post',
- content: 'content',
- file: 'file.png',
- createdAt: '2024-01-01',
- writer: 'writer',
- category: 'A',
- },
- {
- id: 8,
- title: 'post',
- content: 'content',
- file: 'file.png',
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'B',
- },
- {
- id: 9,
- title: 'post',
- content: 'content',
- file: null,
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'C',
- },
-
- {
- id: 10,
- title: 'post',
- content: 'content',
- file: 'file.png',
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'A',
- },
- {
- id: 11,
- title: 'post',
- content: 'content',
- file: 'file.png',
- createdAt: '2024-01-01',
- writer: 'writer',
- category: 'B',
- },
- {
- id: 12,
- title: 'post',
- content: 'content',
- file: null,
- createdAt: '2024-01-01',
- writer: 'writer1',
- category: 'C',
- },
-]
-
-export default function InquiryList() {
- const [visibleItems, setVisibleItems] = useState(5)
- const [isMyPostsOnly, setIsMyPostsOnly] = useState(false)
- const [category, setCategory] = useState('')
- const [search, setSearch] = useState('')
- const [hasMore, setHasMore] = useState(inquiryDummyData.length > 5)
-
- const inquriyData = () => {
- if (isMyPostsOnly) {
- return inquiryDummyData.filter((item) => item.writer === 'writer')
- }
- if (category.trim().length > 0) {
- return inquiryDummyData.filter((item) => item.category === category)
- }
- if (search.trim().length > 0) {
- return inquiryDummyData.filter((item) => item.title.includes(search))
- }
- return inquiryDummyData
- }
-
- const handleLoadMore = () => {
- const newVisibleItems = Math.min(visibleItems + 5, inquriyData().length)
- setVisibleItems(newVisibleItems)
- setHasMore(newVisibleItems < inquriyData().length)
- }
-
- const handleSearch = (e: React.ChangeEvent) => {
- setSearch(e.target.value)
- }
-
- const handleScrollToTop = () => {
- window.scrollTo({ top: 0, behavior: 'smooth' })
- }
-
- return (
-
-
-
- setIsMyPostsOnly(e.target.checked)} />
-
-
-
-
total {inquriyData().length}
-
-
-
- )
-}
diff --git a/src/components/inquiry/InquiryWriteForm.tsx b/src/components/inquiry/InquiryWriteForm.tsx
deleted file mode 100644
index 868f3da..0000000
--- a/src/components/inquiry/InquiryWriteForm.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-'use client'
-
-import { useState } from 'react'
-import { useRouter } from 'next/navigation'
-
-export interface InquiryFormData {
- category: string
- title: string
- content: string
- file: File[]
-}
-
-export default function InquiryWriteForm() {
- const router = useRouter()
- const [formData, setFormData] = useState({
- category: 'A',
- title: '',
- content: '',
- file: [],
- })
-
- const handleFileChange = (e: React.ChangeEvent) => {
- const file = Array.from(e.target.files || [])
- setFormData({ ...formData, file: [...formData.file, ...file] })
- }
- const handleSubmit = () => {
- console.log('submit: ', formData)
- // router.push(`/inquiry`)
- }
-
- return (
-
-
-
-
-
-
-
- setFormData({ ...formData, title: e.target.value })} />
-
-
-
-
-
-
-
-
-
file count: {formData.file.length}
- {formData.file.map((f) => (
-
-
{f.name}
-
- delete
-
-
- ))}
-
-
-
submit
-
- )
-}
diff --git a/src/components/inquiry/ListTable.tsx b/src/components/inquiry/ListTable.tsx
deleted file mode 100644
index a2ac091..0000000
--- a/src/components/inquiry/ListTable.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-'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 (
- <>
-
-
-
-
-
-
-
-
-
- 合計 98個
-
-
- {inquiryList.map((inquiry) => (
- -
-
-
{inquiry.category}
-
{inquiry.title}
-
{inquiry.date}
-
badge.id === inquiry.status)?.color}`}>
- {badgeStyle.find((badge) => badge.id === inquiry.status)?.label}
-
-
-
- ))}
-
-
- setOffset(offset + 10)} />
-
-
-
- >
- )
-}
diff --git a/src/components/inquiry/ListForm.tsx b/src/components/inquiry/list/ListForm.tsx
similarity index 100%
rename from src/components/inquiry/ListForm.tsx
rename to src/components/inquiry/list/ListForm.tsx
diff --git a/src/components/inquiry/list/ListTable.tsx b/src/components/inquiry/list/ListTable.tsx
new file mode 100644
index 0000000..16314aa
--- /dev/null
+++ b/src/components/inquiry/list/ListTable.tsx
@@ -0,0 +1,76 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import LoadMoreButton from '../../LoadMoreButton'
+import { useInquiry } from '@/hooks/useInquiry'
+import { InquiryList } from '@/types/Inquiry'
+
+const badgeStyle = [
+ {
+ id: 'Y',
+ label: '回答完了',
+ color: 'blue',
+ },
+ {
+ id: 'N',
+ label: '回答待ち',
+ color: 'orange',
+ },
+]
+export default function ListTable() {
+ const [offset, setOffset] = useState(0)
+ const [hasMore, setHasMore] = useState(true)
+
+ const { inquiryList } = useInquiry()
+ useEffect(() => {
+ if (inquiryList.length > offset + 10) {
+ setHasMore(true)
+ } else {
+ setHasMore(false)
+ }
+ }, [inquiryList])
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ 合計 {inquiryList.length}個
+
+
+ {inquiryList.map((inquiry: InquiryList) => (
+ -
+
+
{inquiry.qnaTpCd}
+
{inquiry.qstTitle}
+
{inquiry.regDt}
+
badge.id === inquiry.answerYn)?.color}`}>
+ {badgeStyle.find((badge) => badge.id === inquiry.answerYn)?.label}
+
+
+
+ ))}
+
+
+ setOffset(offset + 10)} />
+
+
+
+ >
+ )
+}
diff --git a/src/hooks/useInquiry.ts b/src/hooks/useInquiry.ts
new file mode 100644
index 0000000..07e74fe
--- /dev/null
+++ b/src/hooks/useInquiry.ts
@@ -0,0 +1,60 @@
+import { InquiryList, Inquiry, InquiryRequest } from '@/types/Inquiry'
+import { axiosInstance } from '@/libs/axios'
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
+import { useInquiryFilterStore } from '@/store/inquiryFilterStore'
+import { useSessionStore } from '@/store/session'
+export function useInquiry(
+ qnoNo?: number,
+ compCd?: string,
+): {
+ inquiryList: InquiryList[]
+ isLoadingInquiryList: boolean
+ inquiryDetail: Inquiry | null
+ isLoadingInquiryDetail: boolean
+ isSavingInquiry: boolean
+ saveInquiry: (inquiryRequest: InquiryRequest) => Promise
+} {
+ const { session } = useSessionStore()
+ const queryClient = useQueryClient()
+ const { inquiryListRequest } = useInquiryFilterStore()
+
+ const { data: inquiryList, isLoading: isLoadingInquiryList } = useQuery({
+ queryKey: ['inquiryList', qnoNo, compCd, inquiryListRequest],
+ queryFn: async () => {
+ const resp = await axiosInstance(null).get('/api/qna/list', {
+ params: { inquiryListRequest },
+ })
+ return resp.data
+ },
+ })
+
+ const { data: inquiryDetail, isLoading: isLoadingInquiryDetail } = useQuery({
+ queryKey: ['inquiryDetail', qnoNo, compCd],
+ queryFn: async () => {
+ const resp = await axiosInstance(null).get(`/api/qna/detail`, {
+ params: { qnoNo, compCd, loginId: session?.userNm },
+ })
+ return resp.data
+ },
+ enabled: qnoNo !== undefined && compCd !== undefined,
+ })
+
+ const { mutateAsync: saveInquiry, isPending: isSavingInquiry } = useMutation({
+ mutationFn: async (inquiryRequest: InquiryRequest) => {
+ const resp = await axiosInstance(null).post('/api/qna/save', inquiryRequest)
+ return resp.data
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['inquiryList'] })
+ },
+ })
+
+ return {
+ inquiryList: inquiryList ?? [],
+ inquiryDetail: inquiryDetail ?? null,
+ isLoadingInquiryList,
+ isLoadingInquiryDetail,
+ isSavingInquiry,
+ saveInquiry,
+ }
+}
diff --git a/src/store/inquiryFilterStore.ts b/src/store/inquiryFilterStore.ts
index 0960c85..edfadc4 100644
--- a/src/store/inquiryFilterStore.ts
+++ b/src/store/inquiryFilterStore.ts
@@ -1,41 +1,41 @@
+import { InquiryListRequest } from '@/types/Inquiry'
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
+ inquiryListRequest: InquiryListRequest
+ setInquiryListRequest: (inquiryListRequest: InquiryListRequest) => void
reset: () => void
}
export const useInquiryFilterStore = create((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 }),
+ inquiryListRequest: {
+ compCd: '',
+ langCd: '',
+ storeId: '',
+ siteTpCd: '',
+ schTitle: '',
+ schRegId: '',
+ schFromDt: '',
+ schToDt: '',
+ startRow: 0,
+ endRow: 0,
+ loginId: '',
+ },
+ setInquiryListRequest: (inquiryListRequest) => set({ inquiryListRequest }),
+ reset: () =>
+ set({
+ inquiryListRequest: {
+ compCd: '',
+ langCd: '',
+ storeId: '',
+ siteTpCd: '',
+ schTitle: '',
+ schRegId: '',
+ schFromDt: '',
+ schToDt: '',
+ startRow: 0,
+ endRow: 0,
+ loginId: '',
+ },
+ }),
}))
diff --git a/src/types/Inquiry.ts b/src/types/Inquiry.ts
new file mode 100644
index 0000000..96a6fff
--- /dev/null
+++ b/src/types/Inquiry.ts
@@ -0,0 +1,89 @@
+export type InquiryListRequest = {
+ compCd: string //company code
+ langCd: string //language code
+ storeId: string //store id
+ siteTpCd: string //site type code (QC: QCast, QR: QRead)
+ schTitle: string | null //search title
+ schRegId: string | null //search regId
+ schFromDt: string | null //search start date
+ schToDt: string | null //search end date
+ startRow: number //start row
+ endRow: number //end row
+ loginId: string //login id
+}
+
+export type InquiryList = {
+ totCnt: number //total count
+ rowNumber: number //row number
+ compCd: string //company code
+ qnaNo: number //qna number
+ qstTitle: string //title
+ regDt: string //registration date
+ regId: string //registration Userid
+ regNm: string //registration User name
+ answerYn: string //answer yn - Y / N
+ attachYn: string | null //attach yn - Y / N
+ qnaClsLrgCd: string //qna CLS large Code
+ qnaClsMidCd: string //qna CLS Mid Code
+ qnaClsSmlCd: string | null //qna CLS Small Code
+}
+
+export type InquiryDetailRequest = {
+ compCd: string //company code
+ langCd: string //language code
+ qnaNo: number //qna number
+ loginId: string //login id
+}
+
+export type Inquiry = {
+ compCd: string //company code
+ qnaNo: number //qna number
+ qstTitle: string //title
+ qstContents: string //content
+ regDt: string //registration date
+ regId: string //registration Userid
+ regNm: string //registration User name
+ regEmail: string //registration User email
+ answerYn: string //answer yn - Y / N
+ ansContents: string | null //answer content
+ ansRegDt: string | null //answer registration date
+ ansRegNm: string | null //answer registration User name
+ storeId: string | null //store id
+ storeNm: string | null //store name
+ regUserNm: string //registration User name
+ regUserTelNo: string | null //registration User tel number
+ qnaClsLrgCd: string //qna CLS large Code
+ qnaClsMidCd: string //qna CLS Mid Code
+ qnaClsSmlCd: string | null //qna CLS Small Code
+ listFile: listFile[] | null //Question list file
+ ansListFile: listFile[] | null //Answer list file
+}
+
+export type listFile = {
+ fileNo: number //file number
+ encodeFileNo: string //encode file number
+ srcFileNm: string //source file name
+ fileCours: string //file course
+ fileSize: number //file size(Byte)
+ regDt: string //registration date
+}
+
+export type InquiryRequest = {
+ compCd: string //company code
+ siteTpCd: string //site type code(QC: QCast, QR: QRead)
+ qnaClsLrgCd: string //qna CLS large Code
+ qnaClsMidCd: string //qna CLS Mid Code
+ qnaClsSmlCd: string | null //qna CLS Small Code
+ title: string //title
+ contents: string | null //contents
+ regId: string //registration Userid
+ storeId: string | null //store id
+ regUserNm: string //registration User name
+ regUserTelNo: string | null //registration User tel number
+}
+
+export type InquirySaveResponse = {
+ cnt: number | null //count
+ qnaNo: number //qna number
+ mailYn: string //mail yn - Y / N
+}