feat: implement survey-sale's read list & delete function

- 조사 매물 삭제, 리스트 조회 기능 구현
This commit is contained in:
Dayoung 2025-05-02 15:41:37 +09:00
parent 63750844ee
commit fc93853601
25 changed files with 375 additions and 265 deletions

3
.env
View File

@ -5,7 +5,8 @@
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
# DATABASE_URL="sqlserver://3team.devgrr.kr:1433;database=onsitesurvey;user=sa;password=1q2w3e4r!;encrypt=true;trustServerCertificate=true;"
DATABASE_URL="sqlserver://3team.devgrr.kr:1433;database=onsitesurvey;user=sa;password=1q2w3e4r!;encrypt=true;trustServerCertificate=true;"
# DATABASE_URL="sqlserver://3team.devgrr.kr:1433;database=onsitesurvey;user=sa;password=1q2w3e4r!;encrypt=true;trustServerCertificate=true;"
DATABASE_URL="mysql://root:root@localhost:3306/onsitesurvey"
# SESSION_PASSWORD="QWERASDFZXCV1234567890REWQFDSAVCXZ"
SESSION_PASSWORD="This application is for mobile field research"

View File

@ -6,14 +6,14 @@ const nextConfig: NextConfig = {
sassOptions: {
includePaths: [path.join(__dirname, './src/styles')],
},
async rewrites() {
return [
{
source: '/api/:path*',
destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
},
]
},
// async rewrites() {
// return [
// {
// source: '/api/:path*',
// destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
// },
// ]
// },
}
export default nextConfig

View File

@ -3,7 +3,7 @@ generator client {
}
datasource db {
provider = "sqlserver"
provider = "mysql"
url = env("DATABASE_URL")
}

View File

@ -0,0 +1,3 @@
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 5L5 1L9 5" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 207 B

View File

@ -1,127 +0,0 @@
import { axiosInstance } from '@/libs/axios'
export interface SurveySalesBasicInfo {
id?: number
representative: string
store: string | null
construction_point: string | null
investigation_date: string | null
building_name: string | null
customer_name: string | null
post_code: string | null
address: string | null
address_detail: string | null
submission_status: boolean
submission_date?: string | null
detail_info?: SurveySalesDetailInfo | null
}
export interface SurveySalesDetailInfo {
id?: number
contract_capacity: string | null
retail_company: string | null
supplementary_facilities: number | null
supplementary_facilities_etc: string | null
installation_system: number | null
installation_system_etc: string | null
construction_year: number | null
construction_year_etc: string | null
roof_material: number | null
roof_material_etc: string | null
roof_shape: number | null
roof_shape_etc: string | null
roof_slope: string | null
house_structure: number | null
house_structure_etc: string | null
rafter_material: number | null
rafter_material_etc: string | null
rafter_size: number | null
rafter_size_etc: string | null
rafter_pitch: number | null
rafter_pitch_etc: string | null
rafter_direction: number | null
open_field_plate_kind: number | null
open_field_plate_kind_etc: string | null
open_field_plate_thickness: string | null
leak_trace: boolean | null
waterproof_material: number | null
waterproof_material_etc: string | null
insulation_presence: number | null
insulation_presence_etc: string | null
structure_order: number | null
structure_order_etc: string | null
installation_availability: number | null
installation_availability_etc: string | null
memo: string | null
}
export const surveySalesApi = {
create: async (data: SurveySalesBasicInfo): Promise<number> => {
try {
const response = await axiosInstance.post<SurveySalesBasicInfo>('/api/survey-sales', data)
return response.data.id ?? 0
} catch (error) {
console.error(error)
return 0
}
},
getList: async (): Promise<SurveySalesBasicInfo[] | []> => {
try {
const response = await axiosInstance.get<SurveySalesBasicInfo[]>('/api/survey-sales')
return response.data
} catch (error) {
console.error(error)
return []
}
},
getDetail: async (id: number): Promise<SurveySalesBasicInfo | null> => {
try {
const response = await axiosInstance.get<SurveySalesBasicInfo>(`/api/survey-sales/${id}`)
return response.data
} catch (error) {
console.error(error)
return null
}
},
update: async (id: number, data: SurveySalesBasicInfo): Promise<SurveySalesBasicInfo | null> => {
try {
const response = await axiosInstance.put<SurveySalesBasicInfo>(`/api/survey-sales/${id}`, data)
return response.data
} catch (error) {
console.error(error)
return null
}
},
delete: async (id: number, isDetail: boolean = false): Promise<boolean> => {
try {
await axiosInstance.delete(`/api/survey-sales/${id}`, {
params: {
detail_id: isDetail ? id : undefined,
},
})
return true
} catch (error) {
throw error
}
},
createDetail: async (surveyId: number, data: SurveySalesDetailInfo): Promise<boolean> => {
try {
await axiosInstance.post<SurveySalesDetailInfo>(`/api/survey-sales/${surveyId}`, data)
return true
} catch (error) {
throw error
}
},
confirm: async (id: number): Promise<boolean> => {
try {
await axiosInstance.patch<SurveySalesBasicInfo>(`/api/survey-sales/${id}`)
return true
} catch (error) {
throw error
}
},
// update: async (data: SurveySalesBasicInfo): Promise<SurveySalesBasicInfo> => {
// const response = await axiosInstance.put<SurveySalesBasicInfo>(`/api/survey-sales`, data)
// return response.data
// },
}

View File

@ -44,20 +44,36 @@ export async function PUT(request: Request, context: { params: { id: string } })
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) },
export async function DELETE(request: Request, context: { params: { id: string } }) {
const { id } = await context.params
try {
//@ts-ignore
await prisma.$transaction(async (tx) => {
// @ts-ignore
const detailData = await tx.SD_SERVEY_SALES_BASIC_INFO.findUnique({
where: { id: Number(id) },
select: {
detail_info: true,
},
})
console.log('detailData:: ', detailData)
if (detailData?.detail_info?.id) {
// @ts-ignore
await tx.SD_SERVEY_SALES_DETAIL_INFO.delete({
where: { id: Number(detailData?.detail_info?.id) },
})
}
// @ts-ignore
await tx.SD_SERVEY_SALES_BASIC_INFO.delete({
where: { id: Number(id) },
})
})
return NextResponse.json({ message: 'Survey deleted successfully' })
} catch (error) {
console.error(error)
return NextResponse.json({ message: 'Survey deletion failed' }, { status: 500 })
}
return NextResponse.json({ message: 'Survey deleted successfully' })
}
export async function PATCH(request: Request, context: { params: { id: string } }) {

View File

@ -1,6 +0,0 @@
import SurveyDetail from '@/components/survey-sales/SurveyDetail'
export default function SurveySalesDetailPage() {
return <SurveyDetail />
}

View File

@ -1,12 +0,0 @@
import SurveySales from '@/components/SurveySales'
import SurveySaleList from '@/components/survey-sales/SurveySaleList'
export default function page() {
return (
<>
<h1 className="text-2xl font-bold my-4 flex justify-center"> </h1>
{/* <SurveySales /> */}
<SurveySaleList />
</>
)
}

View File

@ -1,9 +0,0 @@
import MainSurveyForm from '@/components/survey-sales/write-survey-sales/MainSurveyForm'
export default function SurveyWritePage() {
return (
<div>
<MainSurveyForm />
</div>
)
}

View File

@ -7,5 +7,19 @@ interface LoadMoreButtonProps {
}
export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: LoadMoreButtonProps) {
return <div>{hasMore ? <button onClick={onLoadMore}>Load More</button> : <button onClick={onScrollToTop}>Scroll to Top</button>}</div>
return (
<>
{hasMore ? (
<button onClick={onLoadMore} className="btn-frame n-blue icon">
<i className="btn-edit"></i>
</button>
) : (
<button onClick={onScrollToTop} className="btn-frame n-blue icon">
<i className="btn-arr-up"></i>
</button>
)}
</>
)
}

View File

@ -1,21 +1,52 @@
'use client'
import { SurveyBasicRequest } from '@/types/Survey'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
const defaultBasicInfoForm: SurveyBasicRequest = {
representative: '',
store: null,
construction_point: null,
investigation_date: null,
building_name: null,
customer_name: null,
post_code: null,
address: null,
address_detail: null,
submission_status: false,
submission_date: null,
}
export default function BasicForm() {
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(defaultBasicInfoForm)
const handleChange = (key: keyof SurveyBasicRequest, value: string) => {
setBasicInfoData({ ...basicInfoData, [key]: value })
}
const router = useRouter()
const handleSave = () => {
console.log('save')
}
const handleDelete = () => {
console.log('delete')
}
return (
<>
<div className="sale-frame">
<div className="data-form-wrap">
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" defaultValue={'HG'} />
<input type="text" className="input-frame" id='representative' value={basicInfoData.representative} onChange={(e) => handleChange('representative', e.target.value)} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" defaultValue={'HWJ(T01)'} />
<input type="text" className="input-frame" id='store' value={basicInfoData.store ?? ''} onChange={(e) => handleChange('store', e.target.value)} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" defaultValue={'施工点名表示'} />
<input type="text" className="input-frame" id='construction_point' value={basicInfoData.construction_point ?? ''} onChange={(e) => handleChange('construction_point', e.target.value)} />
</div>
</div>
</div>
@ -28,19 +59,19 @@ export default function BasicForm() {
<button className="date-btn">
<i className="date-icon"></i>
</button>
<input type="text" className="date-frame" defaultValue={'2025.04.16'} />
<input type="date" className="date-frame" id='investigation_date' value={basicInfoData.investigation_date ?? ''} onChange={(e) => handleChange('investigation_date', e.target.value)} />
</div>
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" defaultValue={'ギルドン'} />
<input type="text" className="input-frame" id='building_name' value={basicInfoData.building_name ?? ''} onChange={(e) => handleChange('building_name', e.target.value)} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" defaultValue={'HWJ Building'} />
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" id='customer_name' value={basicInfoData.customer_name ?? ''} onChange={(e) => handleChange('customer_name', e.target.value)} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<div className="data-input-form-tit"></div>
<div className="form-flex">
<div className="form-bx">
<input type="text" className="input-frame" defaultValue={'1050013'} disabled />
@ -68,17 +99,17 @@ export default function BasicForm() {
</div>
<div className="btn-flex-wrap">
<div className="btn-bx">
<button className="btn-frame n-blue icon">
<button className="btn-frame n-blue icon" onClick={handleSave}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame red icon">
<button className="btn-frame red icon" onClick={handleDelete}>
<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={() => router.push('/survey-sale')}>
<i className="btn-arr"></i>
</button>
</div>

View File

@ -1,6 +1,18 @@
'use client'
import { useServey } from '@/hooks/useSurvey'
import { useParams } from 'next/navigation'
export default function DataTable() {
const params = useParams()
const id = params.id
const { surveyDetail, isLoadingSurveyDetail } = useServey(Number(id))
if (isLoadingSurveyDetail) {
return <div>Loading...</div>
}
return (
<>
<div className="sale-frame">
@ -12,19 +24,23 @@ export default function DataTable() {
<tbody>
<tr>
<th></th>
<td>0000000020</td>
<td>{surveyDetail?.id}</td>
</tr>
<tr>
<th></th>
<td>2025.04.11</td>
<td>{surveyDetail?.created_at ? new Date(surveyDetail?.created_at).toLocaleString() : ''}</td>
</tr>
<tr>
<th></th>
<td>2025.04.11 15:06:29</td>
<td>{surveyDetail?.updated_at ? new Date(surveyDetail?.updated_at).toLocaleString() : ''}</td>
</tr>
<tr>
<th></th>
<td>2025.04.12 10:00:00 (INTERPLUG )</td>
<td>
{surveyDetail?.submission_status && surveyDetail?.submission_date
? new Date(surveyDetail.submission_date).toLocaleString()
: '未提出'}
</td>
</tr>
<tr>
<th></th>

View File

@ -1,21 +1,53 @@
'use client'
import { useServey } from '@/hooks/useSurvey'
import { useParams, useRouter } from 'next/navigation'
export default function DetailForm() {
const router = useRouter()
const params = useParams()
const id = params.id
const { surveyDetail, deleteSurvey, submitSurvey, isLoadingSurveyDetail } = useServey(Number(id))
if (isLoadingSurveyDetail) {
return <div>Loading...</div>
}
const handleSubmit = async () => {
if (confirm('submit?')) {
if (surveyDetail?.id) {
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">
<div className="data-form-wrap">
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" disabled defaultValue={'HG'} />
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.representative} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" disabled defaultValue={'HWJ(T01)'} />
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.store ?? ''} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" disabled defaultValue={'施工点名表示'} />
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.construction_point ?? ''} />
</div>
</div>
</div>
@ -24,35 +56,35 @@ export default function DetailForm() {
<div className="data-form-wrap">
<div className="data-input-form-bx">
<div className="data-input-form-tit">調</div>
<input type="text" className="input-frame" disabled defaultValue={'2025/04/05'} />
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.investigation_date ?? ''} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" disabled defaultValue={'ハンファジャパンビル'} />
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.building_name ?? ''} />
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" disabled defaultValue={'Hong gi'} />
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.customer_name ?? ''} />
</div>
</div>
<div className="btn-flex-wrap">
<div className="btn-bx">
<button className="btn-frame n-blue icon">
<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">
<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">
<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">
<button className="btn-frame n-blue icon" onClick={handleDelete}>
<i className="btn-arr"></i>
</button>
</div>

View File

@ -1,31 +1,53 @@
'use client'
import LoadMoreButton from '@/components/LoadMoreButton'
import { useServey } from '@/hooks/useSurvey'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
export default function ListTable() {
const router = useRouter()
const { surveyList, isLoadingSurveyList } = useServey()
const [hasMore, setHasMore] = useState(surveyList.length > 5)
const [visibleItems, setVisibleItems] = useState(5)
const handleLoadMore = () => {
const newVisibleItems = Math.min(visibleItems + 5, surveyList.length)
setVisibleItems(newVisibleItems)
setHasMore(newVisibleItems < surveyList.length)
}
const handleScrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
const handleDetail = (id: number) => {
router.push(`/survey-sale/${id}`)
}
return (
<>
<div className="sale-frame">
<ul className="sale-list-wrap">
{Array.from({ length: 4 }).map((_, idx) => (
<li className="sale-list-item" key={idx}>
{surveyList.slice(0, visibleItems).map((survey) => (
<li className="sale-list-item cursor-pointer" key={survey.id} onClick={() => handleDetail(survey.id)}>
<div className="sale-item-bx">
<div className="sale-item-date-bx">
<div className="sale-item-num">0000000021</div>
<div className="sale-item-date">2025.04.22</div>
<div className="sale-item-num">{survey.id}</div>
<div className="sale-item-date">{survey.investigation_date}</div>
</div>
<div className="sale-item-tit">Hanwha Building</div>
<div className="sale-item-customer">Gil dong</div>
<div className="sale-item-tit">{survey.building_name}</div>
<div className="sale-item-customer">{survey.customer_name}</div>
<div className="sale-item-update-bx">
<div className="sale-item-name">Hong Gildong</div>
<div className="sale-item-update">2025.04.22 10:00:21</div>
<div className="sale-item-name">{survey.representative}</div>
<div className="sale-item-update">{new Date(survey.updated_at).toLocaleString()}</div>
</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={handleLoadMore} onScrollToTop={handleScrollToTop} />
</div>
</div>
</>

View File

@ -1,10 +1,13 @@
'use client'
import { useRouter } from 'next/navigation'
export default function SearchForm() {
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('/survey-sale/basic-info')}>
<i className="btn-arr"></i>
</button>
</div>

View File

@ -1,17 +1,17 @@
'use client'
import { useState } from "react"
import { SurveySalesDetailInfo } from "@/api/surveySales"
import { SurveyDetailRequest } from "@/types/Survey"
interface EtcCheckboxProps {
formName: keyof SurveySalesDetailInfo
formName: keyof SurveyDetailRequest
label: string
detailInfoForm: SurveySalesDetailInfo
setDetailInfoForm: (form: SurveySalesDetailInfo) => void
detailInfoForm: SurveyDetailRequest
setDetailInfoForm: (form: SurveyDetailRequest) => void
}
export default function EtcCheckbox({ formName, label, detailInfoForm, setDetailInfoForm }: EtcCheckboxProps) {
const [showEtcInput, setShowEtcInput] = useState(false)
const etcFieldName = `${formName}_etc` as keyof SurveySalesDetailInfo
const etcFieldName = `${formName}_etc` as keyof SurveyDetailRequest
return (
<div className="space-y-2">

View File

@ -1,7 +1,6 @@
'use client'
import { useServey } from '@/hooks/useSurvey'
import { SurveySalesBasicInfo } from '@/api/surveySales'
import { useParams, useRouter } from 'next/navigation'
export default function SurveyDetail() {
@ -56,6 +55,7 @@ export default function SurveyDetail() {
back
</button>
</div>
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.store ?? ''} />
</div>
)
}

View File

@ -1,15 +1,15 @@
'use client'
import { SurveySalesBasicInfo } from '@/api/surveySales'
import { SurveyBasicRequest } from '@/types/Survey'
export default function BasicWriteForm({
basicInfoData,
setBasicInfoData,
}: {
basicInfoData: SurveySalesBasicInfo
setBasicInfoData: (basicInfoData: SurveySalesBasicInfo) => void
basicInfoData: SurveyBasicRequest
setBasicInfoData: (basicInfoData: SurveyBasicRequest) => void
}) {
const handleChange = (key: keyof SurveySalesBasicInfo, value: string) => {
const handleChange = (key: keyof SurveyBasicRequest, value: string) => {
setBasicInfoData({ ...basicInfoData, [key]: value.toString() })
}

View File

@ -1,24 +1,24 @@
'use client'
import React from 'react'
import EtcCheckbox from '../EtcCheckbox'
import { SurveySalesDetailInfo } from '@/api/surveySales'
import { SurveyDetailRequest } from '@/types/Survey'
interface DetailWriteFormProps {
detailInfoForm: SurveySalesDetailInfo
setDetailInfoForm: (form: SurveySalesDetailInfo) => void
detailInfoForm: SurveyDetailRequest
setDetailInfoForm: (form: SurveyDetailRequest) => void
}
export default function DetailWriteForm({ detailInfoForm, setDetailInfoForm }: DetailWriteFormProps) {
const handleNumberInput = (field: keyof SurveySalesDetailInfo, value: string) => {
const handleNumberInput = (field: keyof SurveyDetailRequest, value: string) => {
const numberValue = value === '' ? null : Number(value)
setDetailInfoForm({ ...detailInfoForm, [field]: numberValue })
}
const handleTextInput = (field: keyof SurveySalesDetailInfo, value: string) => {
const handleTextInput = (field: keyof SurveyDetailRequest, value: string) => {
setDetailInfoForm({ ...detailInfoForm, [field]: value || null })
}
const handleBooleanInput = (field: keyof SurveySalesDetailInfo, checked: boolean) => {
const handleBooleanInput = (field: keyof SurveyDetailRequest, checked: boolean) => {
setDetailInfoForm({ ...detailInfoForm, [field]: checked })
}

View File

@ -6,10 +6,12 @@ import DetailWriteForm from './DetailWriteForm'
import { SurveySalesBasicInfo, SurveySalesDetailInfo } from '@/api/surveySales'
import { useRouter, useSearchParams } from 'next/navigation'
import { useServey } from '@/hooks/useSurvey'
import { SurveyBasicRequest, SurveyDetailRequest } from '@/types/Survey'
type TabType = 'basic' | 'detail'
const defaultDetailInfoForm: SurveySalesDetailInfo = {
const defaultDetailInfoForm: SurveyDetailRequest = {
basic_info_id: 0,
contract_capacity: null,
retail_company: null,
supplementary_facilities: null,
@ -47,7 +49,7 @@ const defaultDetailInfoForm: SurveySalesDetailInfo = {
memo: null,
}
const defaultBasicInfoForm: SurveySalesBasicInfo = {
const defaultBasicInfoForm: SurveyBasicRequest = {
representative: '',
store: null,
construction_point: null,
@ -58,6 +60,7 @@ const defaultBasicInfoForm: SurveySalesBasicInfo = {
address: null,
address_detail: null,
submission_status: false,
submission_date: null,
}
export default function MainSurveyForm() {
@ -74,8 +77,8 @@ export default function MainSurveyForm() {
const router = useRouter()
const { createSurvey, isCreatingSurvey, createSurveyDetail, surveyDetail, updateSurvey } = useServey(Number(id))
const [detailInfoForm, setDetailInfoForm] = useState<SurveySalesDetailInfo>(defaultDetailInfoForm)
const [basicInfoData, setBasicInfoData] = useState<SurveySalesBasicInfo>(defaultBasicInfoForm)
const [detailInfoForm, setDetailInfoForm] = useState<SurveyDetailRequest>(defaultDetailInfoForm)
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(defaultBasicInfoForm)
useEffect(() => {
if (surveyDetail) {
@ -94,8 +97,8 @@ export default function MainSurveyForm() {
if (id) {
updateSurvey({
...basicInfoData,
detail_info: detailInfoForm,
submission_status: isSubmit,
submission_date: isSubmit ? new Date().toISOString() : null,
})
router.push('/survey-sales')
return

View File

@ -1,26 +1,29 @@
import { SurveySalesBasicInfo, surveySalesApi, SurveySalesDetailInfo } from '@/api/surveySales'
import { axiosInstance } from '@/libs/axios'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import type { SurveyBasicInfo, SurveyBasicRequest, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
export function useServey(id?: number): {
surveyList: SurveySalesBasicInfo[] | []
surveyDetail: SurveySalesBasicInfo | null
surveyList: SurveyBasicInfo[] | []
surveyDetail: SurveyBasicInfo | null
isLoadingSurveyList: boolean
isLoadingSurveyDetail: boolean
isCreatingSurvey: boolean
isUpdatingSurvey: boolean
isDeletingSurvey: boolean
createSurvey: (survey: SurveySalesBasicInfo) => Promise<number>
createSurveyDetail: (params: { surveyId: number; surveyDetail: SurveySalesDetailInfo }) => void
updateSurvey: (survey: SurveySalesBasicInfo) => void
deleteSurvey: (params: { id: number; isDetail: boolean }) => void
confirmSurvey: (id: number) => void
createSurvey: (survey: SurveyBasicRequest) => Promise<number>
createSurveyDetail: (params: { surveyId: number; surveyDetail: SurveyDetailRequest }) => void
updateSurvey: (survey: SurveyBasicRequest) => void
deleteSurvey: () => Promise<boolean>
submitSurvey: () => void
} {
const queryClient = useQueryClient()
const { data: surveyList, isLoading: isLoadingSurveyList } = useQuery({
queryKey: ['survey', 'list'],
queryFn: () => surveySalesApi.getList(),
queryFn: async () => {
const resp = await axiosInstance.get<SurveyBasicInfo[]>('/api/survey-sales')
return resp.data
},
})
const { data: surveyDetail, isLoading: isLoadingSurveyDetail } = useQuery({
@ -28,15 +31,15 @@ export function useServey(id?: number): {
queryFn: async () => {
if (id === undefined) throw new Error('id is required')
if (id === null) return null
const resp = await axiosInstance.get<SurveySalesBasicInfo>(`/api/survey-sales/${id}`)
const resp = await axiosInstance.get<SurveyBasicInfo>(`/api/survey-sales/${id}`)
return resp.data
},
enabled: id !== undefined,
})
const { mutateAsync: createSurvey, isPending: isCreatingSurvey } = useMutation({
mutationFn: async (survey: SurveySalesBasicInfo) => {
const resp = await axiosInstance.post<SurveySalesBasicInfo>('/api/survey-sales', survey)
mutationFn: async (survey: SurveyBasicRequest) => {
const resp = await axiosInstance.post<SurveyBasicInfo>('/api/survey-sales', survey)
return resp.data.id ?? 0
},
onSuccess: (data) => {
@ -46,9 +49,9 @@ export function useServey(id?: number): {
})
const { mutate: updateSurvey, isPending: isUpdatingSurvey } = useMutation({
mutationFn: async (survey: SurveySalesBasicInfo) => {
mutationFn: async (survey: SurveyBasicRequest) => {
if (id === undefined) throw new Error('id is required')
const resp = await axiosInstance.put<SurveySalesBasicInfo>(`/api/survey-sales/${id}`, survey)
const resp = await axiosInstance.put<SurveyBasicInfo>(`/api/survey-sales/${id}`, survey)
return resp.data
},
onSuccess: () => {
@ -58,19 +61,9 @@ export function useServey(id?: number): {
})
const { mutateAsync: deleteSurvey, isPending: isDeletingSurvey } = useMutation({
mutationFn: async ({ id, isDetail }: { id: number; isDetail: boolean }) => {
if (id === undefined) throw new Error('id is required')
const resp = await axiosInstance.delete<boolean>(`/api/survey-sales/${id}`, { params: { isDetail } })
return resp.data
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['survey', 'list'] })
},
})
const { mutateAsync: createSurveyDetail, isPending: isCreatingSurveyDetail } = useMutation({
mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveySalesDetailInfo }) => {
const resp = await axiosInstance.post<SurveySalesDetailInfo>(`/api/survey-sales/${surveyId}`, surveyDetail)
mutationFn: async () => {
if (id === null) throw new Error('id is required')
const resp = await axiosInstance.delete<boolean>(`/api/survey-sales/${id}`)
return resp.data
},
onSuccess: () => {
@ -79,8 +72,19 @@ export function useServey(id?: number): {
},
})
const { mutateAsync: confirmSurvey, isPending: isConfirmingSurvey } = useMutation({
mutationFn: async (id: number) => {
const { mutateAsync: createSurveyDetail, isPending: isCreatingSurveyDetail } = useMutation({
mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveyDetailRequest }) => {
const resp = await axiosInstance.post<SurveyDetailInfo>(`/api/survey-sales/${surveyId}`, surveyDetail)
return resp.data
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['survey', 'list'] })
queryClient.invalidateQueries({ queryKey: ['survey', id] })
},
})
const { mutateAsync: submitSurvey } = useMutation({
mutationFn: async () => {
if (id === undefined) throw new Error('id is required')
const resp = await axiosInstance.patch<boolean>(`/api/survey-sales/${id}`)
return resp.data
@ -103,6 +107,6 @@ export function useServey(id?: number): {
updateSurvey,
deleteSurvey,
createSurveyDetail,
confirmSurvey,
submitSurvey,
}
}

View File

@ -49,6 +49,14 @@
background-size: cover;
margin-left: 12px;
}
.btn-arr-up{
display: block;
width: 10px;
height: 6px;
background: url(/assets/images/common/btn_arr_up.svg)no-repeat center;
background-size: cover;
margin-left: 12px;
}
.btn-edit{
display: block;
width: 10px;

111
src/types/Survey.ts Normal file
View File

@ -0,0 +1,111 @@
export type SurveyBasicInfo = {
id: number
representative: string
store: string | null
construction_point: string | null
investigation_date: string | null
building_name: string | null
customer_name: string | null
post_code: string | null
address: string | null
address_detail: string | null
submission_status: boolean
submission_date: string | null
detail_info: SurveyDetailInfo | null
created_at: Date
updated_at: Date
}
export type SurveyDetailInfo = {
id: number
contract_capacity: string | null
retail_company: string | null
supplementary_facilities: number | null
supplementary_facilities_etc: string | null
installation_system: number | null
installation_system_etc: string | null
construction_year: number | null
construction_year_etc: string | null
roof_material: number | null
roof_material_etc: string | null
roof_shape: number | null
roof_shape_etc: string | null
roof_slope: string | null
house_structure: number | null
house_structure_etc: string | null
rafter_material: number | null
rafter_material_etc: string | null
rafter_size: number | null
rafter_size_etc: string | null
rafter_pitch: number | null
rafter_pitch_etc: string | null
rafter_direction: number | null
open_field_plate_kind: number | null
open_field_plate_kind_etc: string | null
open_field_plate_thickness: string | null
leak_trace: boolean | null
waterproof_material: number | null
waterproof_material_etc: string | null
insulation_presence: number | null
insulation_presence_etc: string | null
structure_order: number | null
structure_order_etc: string | null
installation_availability: number | null
installation_availability_etc: string | null
memo: string | null
created_at: Date
updated_at: Date
}
export type SurveyBasicRequest = {
representative: string
store: string | null
construction_point: string | null
investigation_date: string | null
building_name: string | null
customer_name: string | null
post_code: string | null
address: string | null
address_detail: string | null
submission_status: boolean
submission_date: string | null
}
export type SurveyDetailRequest = {
contract_capacity: string | null
retail_company: string | null
supplementary_facilities: number | null
supplementary_facilities_etc: string | null
installation_system: number | null
installation_system_etc: string | null
construction_year: number | null
construction_year_etc: string | null
roof_material: number | null
roof_material_etc: string | null
roof_shape: number | null
roof_shape_etc: string | null
roof_slope: string | null
house_structure: number | null
house_structure_etc: string | null
rafter_material: number | null
rafter_material_etc: string | null
rafter_size: number | null
rafter_size_etc: string | null
rafter_pitch: number | null
rafter_pitch_etc: string | null
rafter_direction: number | null
open_field_plate_kind: number | null
open_field_plate_kind_etc: string | null
open_field_plate_thickness: string | null
leak_trace: boolean | null
waterproof_material: number | null
waterproof_material_etc: string | null
insulation_presence: number | null
insulation_presence_etc: string | null
structure_order: number | null
structure_order_etc: string | null
installation_availability: number | null
installation_availability_etc: string | null
memo: string | null
basic_info_id: number
}