feat: remove unused survey sale API routes & add address popup at Survey BasicForm
This commit is contained in:
parent
555e6f3b4a
commit
95d971b198
@ -1,72 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(request: Request, context: { params: { id: string } }) {
|
||||
const body = await request.json()
|
||||
const { id } = await context.params
|
||||
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
detail_info: {
|
||||
create: body,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
}
|
||||
export async function GET(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.findUnique({
|
||||
where: { id: Number(id) },
|
||||
include: {
|
||||
detail_info: true,
|
||||
},
|
||||
})
|
||||
return NextResponse.json(survey)
|
||||
}
|
||||
|
||||
export async function PUT(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
const body = await request.json()
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
...body,
|
||||
detail_info: {
|
||||
update: body.detail_info,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json(survey)
|
||||
}
|
||||
|
||||
export async function DELETE(request: Request, context: { params: { id: string; detail_id: string } }) {
|
||||
const { id, detail_id } = await context.params
|
||||
if (detail_id) {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_DETAIL_INFO.delete({
|
||||
where: { id: Number(detail_id) },
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.delete({
|
||||
where: { id: Number(id) },
|
||||
})
|
||||
}
|
||||
return NextResponse.json({ message: 'Survey deleted successfully' })
|
||||
}
|
||||
|
||||
export async function PATCH(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
submission_status: true,
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey confirmed successfully' })
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json()
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.create({
|
||||
data: body,
|
||||
})
|
||||
return NextResponse.json(res)
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany()
|
||||
return NextResponse.json(res)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
const body = await request.json()
|
||||
console.log('🚀 ~ PUT ~ body:', body)
|
||||
const detailInfo = { ...body.detail_info, basic_info_id: body.id }
|
||||
console.log('🚀 ~ PUT ~ detailInfo:', detailInfo)
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_DETAIL_INFO.create({
|
||||
data: detailInfo,
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey sales updated successfully' })
|
||||
}
|
||||
@ -1,21 +1,5 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
// export async function POST(request: Request, context: { params: { id: string } }) {
|
||||
// const body = await request.json()
|
||||
// const { id } = await context.params
|
||||
|
||||
// // @ts-ignore
|
||||
// const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
// where: { id: Number(id) },
|
||||
// data: {
|
||||
// detail_info: {
|
||||
// create: body,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
// }
|
||||
|
||||
export async function GET(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
|
||||
@ -16,6 +16,7 @@ export async function GET(request: Request) {
|
||||
const searchOption = searchParams.get('searchOption')
|
||||
const isMySurvey = searchParams.get('isMySurvey')
|
||||
const sort = searchParams.get('sort')
|
||||
const offset = searchParams.get('offset')
|
||||
|
||||
const searchOptions = ['building_name', 'representative', 'store', 'construction_point', 'customer_name', 'post_code', 'address', 'address_detail']
|
||||
try {
|
||||
@ -27,7 +28,7 @@ export async function GET(request: Request) {
|
||||
|
||||
if (keyword && keyword.trim() !== '' && searchOption) {
|
||||
if (searchOption === 'all') {
|
||||
where.OR = [];
|
||||
where.OR = []
|
||||
if (keyword.match(/^\d+$/) || !isNaN(Number(keyword))) {
|
||||
where.OR.push({
|
||||
id: {
|
||||
@ -40,7 +41,7 @@ export async function GET(request: Request) {
|
||||
[field]: {
|
||||
contains: keyword,
|
||||
},
|
||||
}))
|
||||
})),
|
||||
)
|
||||
} else if (searchOptions.includes(searchOption)) {
|
||||
where[searchOption] = {
|
||||
@ -53,12 +54,22 @@ export async function GET(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany({
|
||||
where,
|
||||
orderBy: sort === 'created' ? { created_at: 'desc' } : { updated_at: 'desc' },
|
||||
})
|
||||
return NextResponse.json(res)
|
||||
if (offset) {
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany({
|
||||
where,
|
||||
orderBy: sort === 'created' ? { created_at: 'desc' } : { updated_at: 'desc' },
|
||||
skip: Number(offset),
|
||||
take: 10,
|
||||
})
|
||||
return NextResponse.json(res)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const count = await prisma.SD_SERVEY_SALES_BASIC_INFO.count({
|
||||
where,
|
||||
})
|
||||
return NextResponse.json(count)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
|
||||
@ -2,7 +2,8 @@ import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { getIronSession } from 'iron-session'
|
||||
import { cookies } from 'next/headers'
|
||||
import { SessionData, sessionOptions } from '@/libs/session'
|
||||
import { sessionOptions } from '@/libs/session'
|
||||
import type { SessionData } from '@/types/Auth'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const { username, password } = await request.json()
|
||||
@ -25,8 +26,8 @@ export async function POST(request: Request) {
|
||||
const cookieStore = await cookies()
|
||||
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||
console.log('start session edit!')
|
||||
session.username = user.username!
|
||||
session.email = user.email!
|
||||
session.userNm = user.username!
|
||||
// session.email = user.email!
|
||||
session.isLoggedIn = true
|
||||
console.log('end session edit!')
|
||||
await session.save()
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import InquiryWriteForm from '@/components/inquiry/InquiryWriteForm'
|
||||
|
||||
export default function InquiryWrite() {
|
||||
return (
|
||||
<div>
|
||||
<InquiryWriteForm />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -22,7 +22,8 @@ interface RootLayoutProps {
|
||||
export default async function RootLayout({ children, header, footer, floatBtn }: RootLayoutProps): Promise<ReactNode> {
|
||||
return (
|
||||
<ReactQueryProviders>
|
||||
<html lang="en">
|
||||
{/* 일본어로 렌더링 */}
|
||||
<html lang="ja" suppressHydrationWarning>
|
||||
<body>
|
||||
<div className="wrap">
|
||||
{header}
|
||||
|
||||
@ -5,7 +5,7 @@ export default function page() {
|
||||
return (
|
||||
<>
|
||||
<DataTable />
|
||||
<DetailForm />
|
||||
{/* <DetailForm /> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import ListTable from '@/components/survey-sale/list/ListTable'
|
||||
import SearchForm from '@/components/survey-sale/list/SearchForm'
|
||||
|
||||
export default function page() {
|
||||
return (
|
||||
|
||||
@ -3,10 +3,9 @@
|
||||
interface LoadMoreButtonProps {
|
||||
hasMore: boolean
|
||||
onLoadMore: () => void
|
||||
onScrollToTop: () => void
|
||||
}
|
||||
|
||||
export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: LoadMoreButtonProps) {
|
||||
export default function LoadMoreButton({ hasMore, onLoadMore }: LoadMoreButtonProps) {
|
||||
return (
|
||||
<>
|
||||
{hasMore ? (
|
||||
@ -15,10 +14,7 @@ export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: L
|
||||
<i className="btn-edit"></i>
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={onScrollToTop} className="btn-frame n-blue icon">
|
||||
トップシフト
|
||||
<i className="btn-arr-up"></i>
|
||||
</button>
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
@ -23,11 +23,6 @@ export default function InquiryWriteForm() {
|
||||
const file = Array.from(e.target.files || [])
|
||||
setFormData({ ...formData, file: [...formData.file, ...file] })
|
||||
}
|
||||
|
||||
const handleFileDelete = (fileToDelete: File) => {
|
||||
setFormData({ ...formData, file: formData.file.filter((f) => f !== fileToDelete) })
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log('submit: ', formData)
|
||||
// router.push(`/inquiry`)
|
||||
@ -60,7 +55,7 @@ export default function InquiryWriteForm() {
|
||||
<div key={f.name}>
|
||||
<div>{f.name}</div>
|
||||
<div>
|
||||
<button onClick={() => handleFileDelete(f)}>delete</button>
|
||||
<button>delete</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
'use client'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
export default function ListForm() {
|
||||
const router = useRouter()
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
<div className="sale-form-bx">
|
||||
<button className="btn-frame n-blue icon">
|
||||
<button className="btn-frame n-blue icon" onClick={() => router.push('/inquiry/regist')}>
|
||||
お問い合わせ<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,52 @@
|
||||
'use client'
|
||||
|
||||
import { use, useEffect, useState } from 'react'
|
||||
import LoadMoreButton from '../LoadMoreButton'
|
||||
|
||||
const inquiryDummy = [
|
||||
{ id: 1, category: '屋根', title: '屋根材適合性確認依頼', date: '2025.04.02', status: 'completed' },
|
||||
{ id: 2, category: '外壁', title: '外壁仕上げ材確認', date: '2025.04.03', status: 'completed' },
|
||||
{ id: 3, category: '設備', title: '換気システム図面確認', date: '2025.04.04', status: 'completed' },
|
||||
{ id: 4, category: '基礎', title: '基礎配筋検査依頼', date: '2025.04.05', status: 'completed' },
|
||||
{ id: 5, category: '内装', title: 'クロス仕様確認', date: '2025.04.06', status: 'waiting' },
|
||||
{ id: 6, category: '構造', title: '耐震壁位置変更申請', date: '2025.04.07', status: 'completed' },
|
||||
{ id: 7, category: '屋根', title: '雨樋取付方法確認', date: '2025.04.08', status: 'completed' },
|
||||
{ id: 8, category: '外構', title: 'フェンス高さ変更相談', date: '2025.04.09', status: 'completed' },
|
||||
{ id: 9, category: '設備', title: '給湯器設置位置確認', date: '2025.04.10', status: 'completed' },
|
||||
{ id: 10, category: '外壁', title: 'タイル割付案確認依頼', date: '2025.04.11', status: 'waiting' },
|
||||
{ id: 11, category: '内装', title: '照明配置図面確認', date: '2025.04.12', status: 'completed' },
|
||||
{ id: 12, category: '構造', title: '梁補強案確認', date: '2025.04.13', status: 'completed' },
|
||||
{ id: 13, category: '基礎', title: '杭長設計確認依頼', date: '2025.04.14', status: 'completed' },
|
||||
{ id: 14, category: '屋根', title: '断熱材施工方法確認', date: '2025.04.15', status: 'completed' },
|
||||
{ id: 15, category: '外構', title: '駐車場勾配図確認', date: '2025.04.16', status: 'completed' },
|
||||
]
|
||||
|
||||
const badgeStyle = [
|
||||
{
|
||||
id: 'completed',
|
||||
label: '回答完了',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
id: 'waiting',
|
||||
label: '回答待ち',
|
||||
color: 'orange',
|
||||
},
|
||||
]
|
||||
export default function ListTable() {
|
||||
const [offset, setOffset] = useState(0)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
|
||||
const inquiryList = inquiryDummy.slice(0, offset + 10)
|
||||
|
||||
useEffect(() => {
|
||||
if (inquiryDummy.length > offset + 10) {
|
||||
setHasMore(true)
|
||||
} else {
|
||||
setHasMore(false)
|
||||
}
|
||||
}, [inquiryList])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
@ -13,10 +60,8 @@ export default function ListTable() {
|
||||
<div className="filter-select">
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">回答待ち</option>
|
||||
<option value="">回答完了</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,43 +70,21 @@ export default function ListTable() {
|
||||
合計 <span>98</span>個
|
||||
</div>
|
||||
<ul className="inquiry-list">
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">屋根</div>
|
||||
<div className="inquiry-item-tit">屋根材適合性確認依頼</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge blue">回答待ち</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">設計</div>
|
||||
<div className="inquiry-item-tit">設置可能ですか?</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge orange">回答完了</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">屋根</div>
|
||||
<div className="inquiry-item-tit">屋根材適合性確認依頼屋根材適合性確認依頼屋根材適合性確認依頼屋根材適合性確認依頼</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge blue">回答待ち</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">設計</div>
|
||||
<div className="inquiry-item-tit">設置可能ですか?</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge orange">回答完了</div>
|
||||
</div>
|
||||
</li>
|
||||
{inquiryList.map((inquiry) => (
|
||||
<li className="inquiry-item" key={inquiry.id}>
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">{inquiry.category}</div>
|
||||
<div className="inquiry-item-tit">{inquiry.title}</div>
|
||||
<div className="inquiry-item-date">{inquiry.date}</div>
|
||||
<div className={`inquiry-badge badge ${badgeStyle.find((badge) => badge.id === inquiry.status)?.color}`}>
|
||||
{badgeStyle.find((badge) => badge.id === inquiry.status)?.label}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="sale-edit-btn">
|
||||
<button className="btn-frame n-blue icon">
|
||||
もっと見る<i className="btn-edit"></i>
|
||||
</button>
|
||||
<LoadMoreButton hasMore={hasMore} onLoadMore={() => setOffset(offset + 10)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,18 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import { useAddressStore } from '@/store/addressStore'
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
import { useState } from 'react'
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
post_code: '123-567',
|
||||
pref: '東京都',
|
||||
city: '千代田区',
|
||||
detail: '永田町ハイツ101号室',
|
||||
},
|
||||
{
|
||||
post_code: '987-654',
|
||||
pref: '大阪府',
|
||||
city: '北区',
|
||||
detail: '梅田スカイビル23階',
|
||||
},
|
||||
{
|
||||
post_code: '456-789',
|
||||
pref: '福岡県',
|
||||
city: '博多区',
|
||||
detail: '中洲マンション305号',
|
||||
},
|
||||
]
|
||||
|
||||
export default function ZipCodePopup() {
|
||||
const [searchValue, setSearchValue] = useState('') //search 데이터 유무
|
||||
const [selected, setSelected] = useState('')
|
||||
|
||||
const { setAddressData } = useAddressStore()
|
||||
|
||||
//search 데이터 value 추가
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchValue(e.target.value)
|
||||
}
|
||||
|
||||
const popupController = usePopupController()
|
||||
|
||||
const handleApply = () => {
|
||||
const addressData = dummyData.find((item) => item.post_code === selected)
|
||||
setAddressData({
|
||||
post_code: addressData?.post_code || '',
|
||||
address: addressData?.pref || '',
|
||||
address_detail: addressData?.city + ' ' + addressData?.detail || '',
|
||||
})
|
||||
popupController.setZipCodePopup(false)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="modal-popup">
|
||||
@ -50,31 +83,23 @@ export default function ZipCodePopup() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>東京都</td>
|
||||
<td>浜松</td>
|
||||
<td>浜松町</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>東京都</td>
|
||||
<td>浜松</td>
|
||||
<td>浜松町</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>東京都</td>
|
||||
<td>浜松</td>
|
||||
<td>浜松町</td>
|
||||
</tr>
|
||||
{dummyData.map((item, index) => (
|
||||
<tr key={`${item.post_code}-${index}`} onClick={() => setSelected(item.post_code)}>
|
||||
<td>{item.pref}</td>
|
||||
<td>{item.city}</td>
|
||||
<td>{item.detail}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="btn-flex-wrap">
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame red icon">
|
||||
<button className="btn-frame red icon" onClick={handleApply}>
|
||||
住所の適用<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon">
|
||||
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(false)}>
|
||||
閉じる<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -33,7 +33,7 @@ export default function NavTab() {
|
||||
return
|
||||
}
|
||||
if (detailId) {
|
||||
router.push(`/survey-sale/${detailId}?tab=basic-info`)
|
||||
router.push(`/survey-sale/${detailId}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,34 @@
|
||||
'use client'
|
||||
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useParams, useSearchParams } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import DetailForm from './DetailForm'
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import RoofDetailForm from './RoofDetailForm'
|
||||
|
||||
export default function DataTable() {
|
||||
const params = useParams()
|
||||
const id = params.id
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const tab = searchParams.get('tab')
|
||||
|
||||
const { surveyDetail, isLoadingSurveyDetail } = useServey(Number(id))
|
||||
const [isTemporary, setIsTemporary] = useState(false)
|
||||
const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState()
|
||||
|
||||
useEffect(() => {
|
||||
if (!surveyDetail?.representative || !surveyDetail?.store || !surveyDetail?.construction_point) {
|
||||
setIsTemporary(true)
|
||||
}
|
||||
}, [surveyDetail])
|
||||
if (tab === 'roof-info') {
|
||||
setRoofInfoSelected()
|
||||
} else {
|
||||
setBasicInfoSelected()
|
||||
}
|
||||
}, [surveyDetail, tab, setBasicInfoSelected, setRoofInfoSelected])
|
||||
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
@ -73,6 +85,11 @@ export default function DataTable() {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{tab === 'roof-info' ? (
|
||||
<RoofDetailForm surveyDetail={surveyDetail} isLoadingSurveyDetail={isLoadingSurveyDetail} />
|
||||
) : (
|
||||
<DetailForm surveyDetail={surveyDetail} isLoadingSurveyDetail={isLoadingSurveyDetail} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
65
src/components/survey-sale/detail/DetailButton.tsx
Normal file
65
src/components/survey-sale/detail/DetailButton.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
|
||||
export default function DetailButton({ isTemporary, surveyId }: { isTemporary: boolean, surveyId: number }) {
|
||||
const router = useRouter()
|
||||
|
||||
const { submitSurvey, deleteSurvey } = useServey(surveyId)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isTemporary) {
|
||||
alert('SAVE FIRST')
|
||||
return
|
||||
}
|
||||
if (confirm('submit?')) {
|
||||
if (surveyId) {
|
||||
// TODO: 제출 페이지 추가
|
||||
alert('SUBMIT POPUP')
|
||||
await submitSurvey()
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleUpdate = () => {
|
||||
router.push(`/survey-sale/basic-info?id=${surveyId}`)
|
||||
}
|
||||
const handleDelete = async () => {
|
||||
if (confirm('delete?')) {
|
||||
if (surveyId) {
|
||||
await deleteSurvey()
|
||||
router.push('/survey-sale')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="btn-flex-wrap">
|
||||
{isTemporary ? (
|
||||
<></>
|
||||
) : (
|
||||
<>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
|
||||
リスト<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame red icon" onClick={handleSubmit}>
|
||||
提出<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleUpdate}>
|
||||
修正<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleDelete}>
|
||||
削除<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,64 +1,27 @@
|
||||
'use client'
|
||||
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useParams, useRouter, useSearchParams } from 'next/navigation'
|
||||
import DetailButton from './DetailButton'
|
||||
import { SurveyBasicInfo } from '@/types/Survey'
|
||||
|
||||
export default function DetailForm() {
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const id = params.id
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const tab = searchParams.get('tab')
|
||||
|
||||
const { surveyDetail, deleteSurvey, submitSurvey, isLoadingSurveyDetail } = useServey(Number(id))
|
||||
const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState()
|
||||
export default function DetailForm({
|
||||
surveyDetail,
|
||||
isLoadingSurveyDetail,
|
||||
}: {
|
||||
surveyDetail: SurveyBasicInfo | null
|
||||
isLoadingSurveyDetail: boolean
|
||||
}) {
|
||||
const [isTemporary, setIsTemporary] = useState(true)
|
||||
|
||||
// TODO: 조사매물 지붕정보 퍼블 구현되면 탭 화면 변경 추가
|
||||
useEffect(() => {
|
||||
if (tab === 'basic-info') {
|
||||
setBasicInfoSelected()
|
||||
}
|
||||
if (tab === 'roof-info') {
|
||||
setRoofInfoSelected()
|
||||
}
|
||||
if (surveyDetail?.representative && surveyDetail?.store && surveyDetail?.construction_point) {
|
||||
setIsTemporary(false)
|
||||
}
|
||||
}, [tab, setBasicInfoSelected, setRoofInfoSelected, surveyDetail])
|
||||
}, [surveyDetail])
|
||||
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isTemporary) {
|
||||
alert('SAVE FIRST')
|
||||
return
|
||||
}
|
||||
if (confirm('submit?')) {
|
||||
if (surveyDetail?.id) {
|
||||
// TODO: 제출 페이지 추가
|
||||
alert('SUBMIT POPUP')
|
||||
await submitSurvey()
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleUpdate = () => {
|
||||
router.push(`/survey-sale/basic-info?id=${id}`)
|
||||
}
|
||||
const handleDelete = async () => {
|
||||
if (confirm('delete?')) {
|
||||
if (surveyDetail?.id) {
|
||||
await deleteSurvey()
|
||||
router.push('/survey-sale')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
@ -93,34 +56,7 @@ export default function DetailForm() {
|
||||
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.customer_name ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="btn-flex-wrap">
|
||||
{isTemporary ? (
|
||||
<></>
|
||||
) : (
|
||||
<>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
|
||||
リスト<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame red icon" onClick={handleSubmit}>
|
||||
提出<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleUpdate}>
|
||||
修正<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleDelete}>
|
||||
削除<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<DetailButton isTemporary={isTemporary} surveyId={Number(surveyDetail?.id)} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
26
src/components/survey-sale/detail/RoofDetailForm.tsx
Normal file
26
src/components/survey-sale/detail/RoofDetailForm.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { SurveyBasicInfo } from '@/types/Survey'
|
||||
import DetailButton from './DetailButton'
|
||||
|
||||
export default function RoofDetailForm({
|
||||
surveyDetail,
|
||||
isLoadingSurveyDetail,
|
||||
}: {
|
||||
surveyDetail: SurveyBasicInfo | null
|
||||
isLoadingSurveyDetail: boolean
|
||||
}) {
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
<div className="data-form-wrap">
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">屋根材</div>
|
||||
</div>
|
||||
</div>
|
||||
<DetailButton isTemporary={false} surveyId={Number(surveyDetail?.id)} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -5,6 +5,8 @@ import { SurveyBasicRequest } from '@/types/Survey'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
import { useAddressStore } from '@/store/addressStore'
|
||||
|
||||
const defaultBasicInfoForm: SurveyBasicRequest = {
|
||||
representative: '',
|
||||
@ -32,16 +34,25 @@ export default function BasicForm() {
|
||||
|
||||
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(defaultBasicInfoForm)
|
||||
|
||||
const { addressData } = useAddressStore()
|
||||
|
||||
const popupController = usePopupController()
|
||||
|
||||
useEffect(() => {
|
||||
if (surveyDetail) {
|
||||
const { id, updated_at, created_at, detail_info, ...rest } = surveyDetail
|
||||
setBasicInfoData(rest)
|
||||
}
|
||||
}, [surveyDetail])
|
||||
|
||||
useEffect(() => {
|
||||
if (addressData) {
|
||||
setBasicInfoData({
|
||||
...basicInfoData,
|
||||
post_code: addressData.post_code,
|
||||
address: addressData.address,
|
||||
address_detail: addressData.address_detail,
|
||||
})
|
||||
}
|
||||
setBasicInfoSelected()
|
||||
}, [])
|
||||
}, [surveyDetail, addressData])
|
||||
|
||||
const focusInput = (input: keyof SurveyBasicRequest) => {
|
||||
const inputElement = document.getElementById(input)
|
||||
@ -192,7 +203,7 @@ export default function BasicForm() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-btn">
|
||||
<button className="btn-frame n-blue icon">
|
||||
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(true)}>
|
||||
郵便番号<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -2,46 +2,50 @@
|
||||
|
||||
import LoadMoreButton from '@/components/LoadMoreButton'
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import SearchForm from './SearchForm'
|
||||
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
|
||||
|
||||
export default function ListTable() {
|
||||
const router = useRouter()
|
||||
const { surveyList, isLoadingSurveyList } = useServey()
|
||||
const { surveyList, isLoadingSurveyList, surveyListCount } = useServey()
|
||||
const { offset, setOffset } = useSurveyFilterStore()
|
||||
|
||||
const [visibleItems, setVisibleItems] = useState(10)
|
||||
const [heldSurveyList, setHeldSurveyList] = useState<typeof surveyList>([])
|
||||
const [hasMore, setHasMore] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setHasMore(surveyList.length > visibleItems)
|
||||
}, [surveyList, visibleItems])
|
||||
|
||||
const handleLoadMore = () => {
|
||||
const newVisibleItems = Math.min(visibleItems + 10, surveyList.length)
|
||||
setVisibleItems(newVisibleItems)
|
||||
setHasMore(newVisibleItems < surveyList.length)
|
||||
}
|
||||
|
||||
const handleScrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
if (surveyList && surveyList.length > 0) {
|
||||
if (offset === 0) {
|
||||
setHeldSurveyList(surveyList)
|
||||
} else {
|
||||
const remainingList = heldSurveyList.slice(offset, offset + 10)
|
||||
if (JSON.stringify(remainingList) !== JSON.stringify(surveyList)) {
|
||||
setHeldSurveyList((prev) => [...prev, ...surveyList])
|
||||
}
|
||||
}
|
||||
setHasMore(surveyListCount > offset + 10)
|
||||
}
|
||||
}, [surveyList, surveyListCount, offset])
|
||||
|
||||
const handleDetailClick = (id: number) => {
|
||||
router.push(`/survey-sale/${id}`)
|
||||
}
|
||||
|
||||
if (isLoadingSurveyList) {
|
||||
return <div>Loading...</div>
|
||||
const handleItemsInit = () => {
|
||||
setHeldSurveyList([])
|
||||
setOffset(0)
|
||||
}
|
||||
|
||||
// TODO: 로딩 처리 필요
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchForm onItemsInit={() => setVisibleItems(10)} />
|
||||
{surveyList.length > 0 ? (
|
||||
<SearchForm onItemsInit={handleItemsInit} />
|
||||
{heldSurveyList.length > 0 ? (
|
||||
<div className="sale-frame">
|
||||
<ul className="sale-list-wrap">
|
||||
{surveyList.slice(0, visibleItems).map((survey) => (
|
||||
{heldSurveyList.map((survey) => (
|
||||
<li className="sale-list-item cursor-pointer" key={survey.id} onClick={() => handleDetailClick(survey.id)}>
|
||||
<div className="sale-item-bx">
|
||||
<div className="sale-item-date-bx">
|
||||
@ -59,7 +63,7 @@ export default function ListTable() {
|
||||
))}
|
||||
</ul>
|
||||
<div className="sale-edit-btn">
|
||||
<LoadMoreButton hasMore={hasMore} onLoadMore={handleLoadMore} onScrollToTop={handleScrollToTop} />
|
||||
<LoadMoreButton hasMore={hasMore} onLoadMore={() => setOffset(offset + 10)} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@ -6,6 +6,7 @@ import { useSurveyFilterStore } from '@/store/surveyFilterStore'
|
||||
export function useServey(id?: number): {
|
||||
surveyList: SurveyBasicInfo[] | []
|
||||
surveyDetail: SurveyBasicInfo | null
|
||||
surveyListCount: number
|
||||
isLoadingSurveyList: boolean
|
||||
isLoadingSurveyDetail: boolean
|
||||
isCreatingSurvey: boolean
|
||||
@ -19,14 +20,13 @@ export function useServey(id?: number): {
|
||||
validateSurveyDetail: (surveyDetail: SurveyDetailRequest) => string
|
||||
} {
|
||||
const queryClient = useQueryClient()
|
||||
const { keyword, searchOption, isMySurvey, sort } = useSurveyFilterStore()
|
||||
const { keyword, searchOption, isMySurvey, sort, offset } = useSurveyFilterStore()
|
||||
|
||||
const { data: surveyList, isLoading: isLoadingSurveyList } = useQuery({
|
||||
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort],
|
||||
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort, offset],
|
||||
queryFn: async () => {
|
||||
console.log('keyword, searchOption, isMySurvey, sort:: ', keyword, searchOption, isMySurvey, sort)
|
||||
const resp = await axiosInstance(null).get<SurveyBasicInfo[]>('/api/survey-sales', {
|
||||
params: { keyword, searchOption, isMySurvey, sort },
|
||||
params: { keyword, searchOption, isMySurvey, sort, offset },
|
||||
})
|
||||
return resp.data
|
||||
},
|
||||
@ -43,6 +43,16 @@ export function useServey(id?: number): {
|
||||
enabled: id !== undefined,
|
||||
})
|
||||
|
||||
const { data: surveyListCount } = useQuery({
|
||||
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort],
|
||||
queryFn: async () => {
|
||||
const resp = await axiosInstance(null).get<number>('/api/survey-sales', {
|
||||
params: { keyword, searchOption, isMySurvey, sort },
|
||||
})
|
||||
return resp.data
|
||||
},
|
||||
})
|
||||
|
||||
const { mutateAsync: createSurvey, isPending: isCreatingSurvey } = useMutation({
|
||||
mutationFn: async (survey: SurveyBasicRequest) => {
|
||||
const resp = await axiosInstance(null).post<SurveyBasicInfo>('/api/survey-sales', survey)
|
||||
@ -80,7 +90,7 @@ export function useServey(id?: number): {
|
||||
},
|
||||
})
|
||||
|
||||
const { mutateAsync: createSurveyDetail, isPending: isCreatingSurveyDetail } = useMutation({
|
||||
const { mutateAsync: createSurveyDetail } = useMutation({
|
||||
mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => {
|
||||
const resp = await axiosInstance(null).patch<SurveyDetailInfo>(`/api/survey-sales/${surveyId}`, surveyDetail)
|
||||
return resp.data
|
||||
@ -137,6 +147,7 @@ export function useServey(id?: number): {
|
||||
return {
|
||||
surveyList: surveyList || [],
|
||||
surveyDetail: surveyDetail || null,
|
||||
surveyListCount: surveyListCount || 0,
|
||||
isLoadingSurveyList,
|
||||
isLoadingSurveyDetail,
|
||||
isCreatingSurvey,
|
||||
|
||||
19
src/store/addressStore.ts
Normal file
19
src/store/addressStore.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
type AddressData = {
|
||||
post_code: string
|
||||
address: string
|
||||
address_detail: string
|
||||
}
|
||||
|
||||
interface AddressState {
|
||||
addressData: AddressData | null
|
||||
setAddressData: (address: AddressData) => void
|
||||
resetAddressData: () => void
|
||||
}
|
||||
|
||||
export const useAddressStore = create<AddressState>((set) => ({
|
||||
addressData: null,
|
||||
setAddressData: (address) => set({ addressData: address }),
|
||||
resetAddressData: () => set({ addressData: null }),
|
||||
}))
|
||||
41
src/store/inquiryFilterStore.ts
Normal file
41
src/store/inquiryFilterStore.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
export const FILTER_OPTIONS = [
|
||||
{
|
||||
id: 'all',
|
||||
label: '全体',
|
||||
},
|
||||
{
|
||||
id: 'completed',
|
||||
label: '回答完了',
|
||||
},
|
||||
{
|
||||
id: 'waiting',
|
||||
label: '回答待ち',
|
||||
},
|
||||
]
|
||||
export type FILTER_OPTIONS_ENUM = (typeof FILTER_OPTIONS)[number]['id']
|
||||
|
||||
type InquiryFilterState = {
|
||||
keyword: string
|
||||
filter: FILTER_OPTIONS_ENUM
|
||||
isMySurvey: string | null
|
||||
offset: number
|
||||
setKeyword: (keyword: string) => void
|
||||
setFilter: (filter: FILTER_OPTIONS_ENUM) => void
|
||||
setIsMySurvey: (isMySurvey: string | null) => void
|
||||
setOffset: (offset: number) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
export const useInquiryFilterStore = create<InquiryFilterState>((set) => ({
|
||||
keyword: '',
|
||||
filter: 'all',
|
||||
isMySurvey: null,
|
||||
offset: 0,
|
||||
setKeyword: (keyword) => set({ keyword }),
|
||||
setFilter: (filter) => set({ filter }),
|
||||
setIsMySurvey: (isMySurvey) => set({ isMySurvey }),
|
||||
setOffset: (offset) => set({ offset }),
|
||||
reset: () => set({ keyword: '', filter: 'all', isMySurvey: null, offset: 0 }),
|
||||
}))
|
||||
@ -1,10 +1,10 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
export type SEARCH_OPTIONS_ENUM = 'all' | 'id' | 'building_name' | 'representative' | 'store' | 'construction_point'
|
||||
// export type SEARCH_OPTIONS_ENUM = 'all' | 'id' | 'building_name' | 'representative' | 'store' | 'construction_point'
|
||||
// | 'store_id' | 'construction_id'
|
||||
export type SEARCH_OPTIONS_PARTNERS_ENUM = 'all' | 'id' | 'building_name' | 'representative'
|
||||
// export type SEARCH_OPTIONS_PARTNERS_ENUM = 'all' | 'id' | 'building_name' | 'representative'
|
||||
|
||||
export type SORT_OPTIONS_ENUM = 'created' | 'updated'
|
||||
// export type SORT_OPTIONS_ENUM = 'created' | 'updated'
|
||||
|
||||
export const SEARCH_OPTIONS = [
|
||||
{
|
||||
@ -27,18 +27,18 @@ export const SEARCH_OPTIONS = [
|
||||
id: 'store',
|
||||
label: '販売店名',
|
||||
},
|
||||
// {
|
||||
// id: 'store_id',
|
||||
// label: '販売店ID',
|
||||
// },
|
||||
// {
|
||||
// id: 'store_id',
|
||||
// label: '販売店ID',
|
||||
// },
|
||||
{
|
||||
id: 'construction_point',
|
||||
label: '施工店名',
|
||||
},
|
||||
// {
|
||||
// id: 'construction_id',
|
||||
// label: '施工店ID',
|
||||
// },
|
||||
// {
|
||||
// id: 'construction_id',
|
||||
// label: '施工店ID',
|
||||
// },
|
||||
]
|
||||
|
||||
export const SEARCH_OPTIONS_PARTNERS = [
|
||||
@ -60,15 +60,21 @@ export const SEARCH_OPTIONS_PARTNERS = [
|
||||
},
|
||||
]
|
||||
|
||||
export type SEARCH_OPTIONS_ENUM = (typeof SEARCH_OPTIONS)[number]['id']
|
||||
export type SEARCH_OPTIONS_PARTNERS_ENUM = (typeof SEARCH_OPTIONS_PARTNERS)[number]['id']
|
||||
export type SORT_OPTIONS_ENUM = 'created' | 'updated'
|
||||
|
||||
type SurveyFilterState = {
|
||||
keyword: string
|
||||
searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM
|
||||
isMySurvey: string | null
|
||||
sort: SORT_OPTIONS_ENUM
|
||||
offset: number
|
||||
setKeyword: (keyword: string) => void
|
||||
setSearchOption: (searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM) => void
|
||||
setIsMySurvey: (isMySurvey: string | null) => void
|
||||
setSort: (sort: SORT_OPTIONS_ENUM) => void
|
||||
setOffset: (offset: number) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
@ -77,9 +83,11 @@ export const useSurveyFilterStore = create<SurveyFilterState>((set) => ({
|
||||
searchOption: 'all',
|
||||
isMySurvey: null,
|
||||
sort: 'created',
|
||||
offset: 0,
|
||||
setKeyword: (keyword: string) => set({ keyword }),
|
||||
setSearchOption: (searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM) => set({ searchOption }),
|
||||
setIsMySurvey: (isMySurvey: string | null) => set({ isMySurvey }),
|
||||
setSort: (sort: SORT_OPTIONS_ENUM) => set({ sort }),
|
||||
reset: () => set({ keyword: '', searchOption: 'all', isMySurvey: null, sort: 'created' }),
|
||||
setOffset: (offset: number) => set({ offset }),
|
||||
reset: () => set({ keyword: '', searchOption: 'all', isMySurvey: null, sort: 'created', offset: 0 }),
|
||||
}))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user