From 08d99fb4e725b489950b6d031b963dd042b7e5df Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Tue, 29 Apr 2025 10:52:49 +0900 Subject: [PATCH] feat: Implement survey-sale, inquiry UI base components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 조사매물, 1:1 문의 목록 더보기 버튼 구현 - 조사매물, 1:1 문의 목록, 상세 페이지, 작성 페이지 샘플 구현 --- src/api/survey.ts | 66 +++++++++++++ src/app/api/survey/[id]/route.ts | 28 ++++++ src/app/api/survey/route.ts | 16 +++ src/app/survey/[id]/page.tsx | 9 ++ src/app/survey/page.tsx | 10 ++ src/app/survey/write/page.tsx | 9 ++ src/components/LoadMoreButton.tsx | 11 +++ .../{InquirySearch.tsx => InquiryFilter.tsx} | 3 +- src/components/inquiry/InquiryList.tsx | 17 ++-- src/components/inquiry/InquiryWriteForm.tsx | 65 ++++++------- src/components/survey/SurveyFilter.tsx | 20 ++++ src/components/survey/SurveySaleDetail.tsx | 4 + src/components/survey/SurveySaleList.tsx | 54 +++++++++++ src/components/survey/SurveySaleWriteForm.tsx | 97 +++++++++++++++++++ src/hooks/useSurvey.ts | 51 ++++++++++ 15 files changed, 416 insertions(+), 44 deletions(-) create mode 100644 src/api/survey.ts create mode 100644 src/app/api/survey/[id]/route.ts create mode 100644 src/app/api/survey/route.ts create mode 100644 src/app/survey/[id]/page.tsx create mode 100644 src/app/survey/page.tsx create mode 100644 src/app/survey/write/page.tsx create mode 100644 src/components/LoadMoreButton.tsx rename src/components/inquiry/{InquirySearch.tsx => InquiryFilter.tsx} (88%) create mode 100644 src/components/survey/SurveyFilter.tsx create mode 100644 src/components/survey/SurveySaleDetail.tsx create mode 100644 src/components/survey/SurveySaleList.tsx create mode 100644 src/components/survey/SurveySaleWriteForm.tsx create mode 100644 src/hooks/useSurvey.ts diff --git a/src/api/survey.ts b/src/api/survey.ts new file mode 100644 index 0000000..e004930 --- /dev/null +++ b/src/api/survey.ts @@ -0,0 +1,66 @@ +import { axiosInstance } from '@/libs/axios' + +export interface Survey { + id: number + title: string + content: string + created_at: string + updated_at: string + checked: string[] + otherText: string +} + +export const surveyApi = { + create: async (data: Survey): Promise => { + try { + const response = await axiosInstance.post('/api/survey', data) + return response.data + } catch (error) { + console.error('Error creating survey:', error) + throw error + } + }, + + getList: async (): Promise => { + try { + const response = await axiosInstance.get('/api/survey') + return response.data + } catch (error) { + console.error('Error fetching survey list:', error) + return [] + } + }, + + getDetail: async (id?: number): Promise => { + try { + if (id) { + const response = await axiosInstance.get(`/api/survey/${id}`) + return response.data + } else { + return {} as Survey + } + } catch (error) { + console.error('Error fetching survey detail:', error) + throw error + } + }, + + update: async (id: number, data: Survey): Promise => { + try { + const response = await axiosInstance.put(`/api/survey/${id}`, data) + return response.data + } catch (error) { + console.error('Error updating survey:', error) + throw error + } + }, + + delete: async (id: number): Promise => { + try { + await axiosInstance.delete(`/api/survey/${id}`) + } catch (error) { + console.error('Error deleting survey:', error) + throw error + } + }, +} diff --git a/src/app/api/survey/[id]/route.ts b/src/app/api/survey/[id]/route.ts new file mode 100644 index 0000000..b8a989a --- /dev/null +++ b/src/app/api/survey/[id]/route.ts @@ -0,0 +1,28 @@ +import { NextResponse } from 'next/server' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + const { id } = params + // @ts-ignore + const survey = await prisma.SD_SERVEY_SALES.findUnique({ + where: { id: parseInt(id) }, + }) + return NextResponse.json(survey) +} + +export async function PUT(request: Request, { params }: { params: { id: string } }) { + const { id } = params + const body = await request.json() + // @ts-ignore + const survey = await prisma.SD_SERVEY_SALES.update({ + where: { id: parseInt(id) }, + data: body, + }) + return NextResponse.json(survey) +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + const { id } = params + // @ts-ignore + await prisma.SD_SERVEY_SALES.delete({ where: { id: parseInt(id) } }) + return NextResponse.json({ message: 'Survey deleted successfully' }) +} diff --git a/src/app/api/survey/route.ts b/src/app/api/survey/route.ts new file mode 100644 index 0000000..a8b6b7e --- /dev/null +++ b/src/app/api/survey/route.ts @@ -0,0 +1,16 @@ +import { NextResponse } from 'next/server' + +export async function GET() { + // @ts-ignore + const surveys = await prisma.SD_SERVEY_SALES.findMany() + return NextResponse.json(surveys) +} + +export async function POST(request: Request) { + const body = await request.json() + // @ts-ignore + const survey = await prisma.SD_SERVEY_SALES.create({ + data: body, + }) + return NextResponse.json(survey) +} diff --git a/src/app/survey/[id]/page.tsx b/src/app/survey/[id]/page.tsx new file mode 100644 index 0000000..234ee63 --- /dev/null +++ b/src/app/survey/[id]/page.tsx @@ -0,0 +1,9 @@ +import SurveySaleDetail from '@/components/survey/SurveySaleDetail' + +export default function SurveyDetailPage() { + return ( +
+ +
+ ) +} diff --git a/src/app/survey/page.tsx b/src/app/survey/page.tsx new file mode 100644 index 0000000..d716d96 --- /dev/null +++ b/src/app/survey/page.tsx @@ -0,0 +1,10 @@ +import SurveySaleList from '@/components/survey/SurveySaleList' + +export default function SurveyPage() { + return ( +
+ +
+ ) +} + diff --git a/src/app/survey/write/page.tsx b/src/app/survey/write/page.tsx new file mode 100644 index 0000000..925b622 --- /dev/null +++ b/src/app/survey/write/page.tsx @@ -0,0 +1,9 @@ +import SurveySaleWriteForm from '@/components/survey/SurveySaleWriteForm' + +export default function SurveyWritePage() { + return ( +
+ +
+ ) +} diff --git a/src/components/LoadMoreButton.tsx b/src/components/LoadMoreButton.tsx new file mode 100644 index 0000000..0a92e1b --- /dev/null +++ b/src/components/LoadMoreButton.tsx @@ -0,0 +1,11 @@ +'use client' + +interface LoadMoreButtonProps { + hasMore: boolean + onLoadMore: () => void + onScrollToTop: () => void +} + +export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: LoadMoreButtonProps) { + return
{hasMore ? : }
+} diff --git a/src/components/inquiry/InquirySearch.tsx b/src/components/inquiry/InquiryFilter.tsx similarity index 88% rename from src/components/inquiry/InquirySearch.tsx rename to src/components/inquiry/InquiryFilter.tsx index 894962f..c3911a2 100644 --- a/src/components/inquiry/InquirySearch.tsx +++ b/src/components/inquiry/InquiryFilter.tsx @@ -3,7 +3,8 @@ import { Search } from 'lucide-react' import { useRouter } from 'next/navigation' -export default function InquirySearch({ handleSearch }: { handleSearch: (e: React.ChangeEvent) => void }) { + +export default function InquiryFilter({ handleSearch }: { handleSearch: (e: React.ChangeEvent) => void }) { const router = useRouter() return (
diff --git a/src/components/inquiry/InquiryList.tsx b/src/components/inquiry/InquiryList.tsx index ec886a9..f65478b 100644 --- a/src/components/inquiry/InquiryList.tsx +++ b/src/components/inquiry/InquiryList.tsx @@ -1,7 +1,8 @@ 'use client' import { useState } from 'react' import InquiryItems from './InquiryItems' -import InquirySearch from './InquirySearch' +import InquiryFilter from './InquiryFilter' +import LoadMoreButton from '../LoadMoreButton' const inquiryDummyData = [ { @@ -120,6 +121,7 @@ export default function InquiryList() { const [isMyPostsOnly, setIsMyPostsOnly] = useState(false) const [category, setCategory] = useState('') const [search, setSearch] = useState('') + const [hasMore, setHasMore] = useState(inquiryDummyData.length > 5) const inquriyData = () => { if (isMyPostsOnly) { @@ -135,7 +137,9 @@ export default function InquiryList() { } const handleLoadMore = () => { - setVisibleItems((prev) => Math.min(prev + 5, inquriyData().length)) + const newVisibleItems = Math.min(visibleItems + 5, inquriyData().length) + setVisibleItems(newVisibleItems) + setHasMore(newVisibleItems < inquriyData().length) } const handleSearch = (e: React.ChangeEvent) => { @@ -148,7 +152,7 @@ export default function InquiryList() { return (
- +
setIsMyPostsOnly(e.target.checked)} /> @@ -161,12 +165,7 @@ export default function InquiryList() { total {inquriyData().length} - {visibleItems < inquriyData().length ? ( - - ) : ( - - )} +
) } - diff --git a/src/components/inquiry/InquiryWriteForm.tsx b/src/components/inquiry/InquiryWriteForm.tsx index c2db5e7..047e4d0 100644 --- a/src/components/inquiry/InquiryWriteForm.tsx +++ b/src/components/inquiry/InquiryWriteForm.tsx @@ -28,48 +28,45 @@ export default function InquiryWriteForm() { setFormData({ ...formData, file: formData.file.filter((f) => f !== fileToDelete) }) } - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() + const handleSubmit = () => { console.log('submit: ', formData) - router.push(`/inquiry`) + // router.push(`/inquiry`) } return (
-
+
+ + +
+
+ + setFormData({ ...formData, title: e.target.value })} /> +
+
+ +