feat: implement survey-sale's read list & delete function
- 조사 매물 삭제, 리스트 조회 기능 구현
This commit is contained in:
parent
63750844ee
commit
fc93853601
3
.env
3
.env
@ -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"
|
||||
@ -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
|
||||
|
||||
@ -3,7 +3,7 @@ generator client {
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlserver"
|
||||
provider = "mysql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
|
||||
3
public/assets/images/common/btn_arr_up.svg
Normal file
3
public/assets/images/common/btn_arr_up.svg
Normal 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 |
@ -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
|
||||
// },
|
||||
}
|
||||
@ -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 } }) {
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import SurveyDetail from '@/components/survey-sales/SurveyDetail'
|
||||
|
||||
export default function SurveySalesDetailPage() {
|
||||
return <SurveyDetail />
|
||||
}
|
||||
|
||||
@ -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 />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import MainSurveyForm from '@/components/survey-sales/write-survey-sales/MainSurveyForm'
|
||||
|
||||
export default function SurveyWritePage() {
|
||||
return (
|
||||
<div>
|
||||
<MainSurveyForm />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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() })
|
||||
}
|
||||
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
111
src/types/Survey.ts
Normal 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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user