From ee95e80eff748be91e6ef8e1aaae9d4e0c4fa9a2 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 18:09:45 +0900 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=93=A6[Chore]=20React-datepicker=20?= =?UTF-8?q?=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 45 +++++++++++++++++++++++++++++++++++++++------ package.json | 4 +++- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41cdbc4a..36ce19e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,11 @@ "@hookform/resolvers": "^3.9.1", "@tanstack/react-query": "^5.61.3", "@tanstack/react-query-devtools": "^5.61.3", + "@types/react-datepicker": "^6.2.0", "axios": "^1.7.8", "next": "15.0.3", "react": "^18.3.1", + "react-datepicker": "^7.5.0", "react-dom": "^18.3.1", "react-hook-form": "^7.53.2", "tailwind-merge": "^2.5.5", @@ -5509,20 +5511,29 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-datepicker": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-6.2.0.tgz", + "integrity": "sha512-+JtO4Fm97WLkJTH8j8/v3Ldh7JCNRwjMYjRaKh4KHH0M3jJoXtwiD3JBCsdlg3tsFIw9eQSqyAPeVDN2H2oM9Q==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.2", + "@types/react": "*", + "date-fns": "^3.3.1" + } + }, "node_modules/@types/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", @@ -8791,7 +8802,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -8870,6 +8880,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -14529,7 +14549,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -15668,7 +15687,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -15847,6 +15865,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-datepicker": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.5.0.tgz", + "integrity": "sha512-6MzeamV8cWSOcduwePHfGqY40acuGlS1cG//ePHT6bVbLxWyqngaStenfH03n1wbzOibFggF66kWaBTb1SbTtQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.23", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-docgen": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.0.tgz", @@ -15912,7 +15946,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-refresh": { diff --git a/package.json b/package.json index 11e07d75..8209897d 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,14 @@ "@hookform/resolvers": "^3.9.1", "@tanstack/react-query": "^5.61.3", "@tanstack/react-query-devtools": "^5.61.3", + "@types/react-datepicker": "^6.2.0", "axios": "^1.7.8", "next": "15.0.3", "react": "^18.3.1", + "react-datepicker": "^7.5.0", "react-dom": "^18.3.1", - "tailwind-merge": "^2.5.5", "react-hook-form": "^7.53.2", + "tailwind-merge": "^2.5.5", "zod": "^3.23.8", "zustand": "^5.0.1" }, From bfb7a5f07b065ca67d8d759ce5abb42f87022b62 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 18:10:18 +0900 Subject: [PATCH 2/8] =?UTF-8?q?=E2=9C=A8[Feat]=20datepicker=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8=20=ED=8F=BC=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 104 +++++++++++++++++- .../club-create/types/bookClubSchema.ts | 10 +- 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index 7e5086cc..f686ea30 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useForm } from 'react-hook-form'; +import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useState } from 'react'; import Button from '@/components/button/Button'; @@ -10,12 +10,16 @@ import { RadioButtonGroup, } from '@/features/club-create/components'; import { BookClubForm, bookClubSchema } from '@/features/club-create/types'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; export default function CreateBookClub() { const [selectedFileName, setSelectedFileName] = useState(''); const { register, handleSubmit, + control, + setValue, formState: { errors }, watch, } = useForm({ @@ -25,12 +29,55 @@ export default function CreateBookClub() { // TODO:: 컨테이너별로 비즈니스 로직 작업 후 훅 분리 const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; - setSelectedFileName(file ? file.name : ''); + if (file) { + console.log('선택된 파일:', file); + setSelectedFileName(file.name); + setValue('image', file); // 이 줄 추가 + } else { + setSelectedFileName(''); + } }; // TODO: API 연동, 훅 분리 const onSubmit = (data: BookClubForm) => { + const formData = new FormData(); + console.log(data); + // 이미지 파일 추가 + const imageFile = data.image instanceof File ? data.image : null; // File 타입 체크로 변경 + if (imageFile) { + formData.append('image', imageFile); + } + + // 일반 데이터 추가 + formData.append('title', data.title); + formData.append('description', data.description); + formData.append('bookType', data.bookType); + formData.append('location', data.location); + formData.append('startDate', data.startDate.toISOString()); + formData.append('endDate', data.endDate.toISOString()); + formData.append('maxParticipants', String(data.maxParticipants)); + + // 객체 형태로 데이터 확인 + const formDataObject = { + image: imageFile + ? { + name: imageFile.name, + type: imageFile.type, + size: `${(imageFile.size / 1024).toFixed(2)}KB`, + lastModified: new Date(imageFile.lastModified).toLocaleString(), + } + : null, + title: data.title, + description: data.description, + booktype: data.bookType, + location: data.location, + startDate: data.startDate, + endDate: data.endDate, + maxParticipants: data.maxParticipants, + }; + + console.log('폼 데이터:', formDataObject); }; return ( @@ -62,7 +109,10 @@ export default function CreateBookClub() { /> - +
- + ( + { + const hours = time.getHours(); + return hours > 12 ? 'text-success' : 'text-error'; + }} + placeholderText="만나는 날짜를 선택해주세요!" + customInput={} + /> + )} + /> - + ( + { + const hours = time.getHours(); + return hours > 12 ? 'text-success' : 'text-error'; + }} + placeholderText="모임의 모집 마감 날짜를 선택해주세요!" + customInput={} + /> + )} + /> ); } + +// diff --git a/src/features/club-create/types/bookClubSchema.ts b/src/features/club-create/types/bookClubSchema.ts index 881ce69a..1ae09a21 100644 --- a/src/features/club-create/types/bookClubSchema.ts +++ b/src/features/club-create/types/bookClubSchema.ts @@ -9,12 +9,16 @@ export const bookClubSchema = z.object({ .string() .min(1, '상세 설명을 입력해주세요') .max(30, '상세 설명은 최대 30자까지 가능합니다'), - image: z.string().optional(), + image: z.any().optional(), bookType: z.enum(['자유책', '지정책']), location: z.enum(['온라인', '오프라인']), place: z.string().optional(), - startDate: z.string().min(1, '시작 날짜를 선택해주세요'), - endDate: z.string().min(1, '종료 날짜를 선택해주세요'), + startDate: z.date().refine((date) => !isNaN(date.getTime()), { + message: '유효한 날짜를 선택해주세요', + }), + endDate: z.date().refine((date) => !isNaN(date.getTime()), { + message: '유효한 날짜를 선택해주세요', + }), maxParticipants: z .number() .min(3, '최소 3명 이상 입력해주세요') From fa1e5691c694c9810b54e7890549878b8ad47ac6 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 18:15:40 +0900 Subject: [PATCH 3/8] =?UTF-8?q?=E2=9C=A8[Feat]=20=ED=8F=BC=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80,=20=EB=B6=81?= =?UTF-8?q?=ED=81=B4=EB=9F=BD=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index f686ea30..501db375 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -42,42 +42,42 @@ export default function CreateBookClub() { const onSubmit = (data: BookClubForm) => { const formData = new FormData(); - console.log(data); - // 이미지 파일 추가 - const imageFile = data.image instanceof File ? data.image : null; // File 타입 체크로 변경 + // 이미지 파일 추가 (Multipart) + const imageFile = data.image instanceof File ? data.image : null; if (imageFile) { formData.append('image', imageFile); } - // 일반 데이터 추가 - formData.append('title', data.title); - formData.append('description', data.description); - formData.append('bookType', data.bookType); - formData.append('location', data.location); - formData.append('startDate', data.startDate.toISOString()); - formData.append('endDate', data.endDate.toISOString()); - formData.append('maxParticipants', String(data.maxParticipants)); - - // 객체 형태로 데이터 확인 - const formDataObject = { - image: imageFile - ? { - name: imageFile.name, - type: imageFile.type, - size: `${(imageFile.size / 1024).toFixed(2)}KB`, - lastModified: new Date(imageFile.lastModified).toLocaleString(), - } - : null, + // 나머지 데이터는 JSON으로 묶기 + const bookClubData = { title: data.title, description: data.description, - booktype: data.bookType, + bookType: data.bookType, location: data.location, startDate: data.startDate, endDate: data.endDate, maxParticipants: data.maxParticipants, }; - console.log('폼 데이터:', formDataObject); + // JSON 데이터 추가 + formData.append( + 'bookClub', + new Blob([JSON.stringify(bookClubData)], { + type: 'application/json', + }), + ); + + // 확인용 로그 + console.log('전송될 데이터:', { + 이미지: imageFile + ? { + 이름: imageFile.name, + 타입: imageFile.type, + 크기: `${(imageFile.size / 1024).toFixed(2)}KB`, + } + : null, + 북클럽_데이터: bookClubData, + }); }; return ( From 7fbe919488ea6d4fb46fd72b451a167f045f700f Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 18:26:43 +0900 Subject: [PATCH 4/8] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EA=B0=92=20=EB=B0=B1=EC=97=94=EB=93=9C=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=8C=80=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 35 ++++++++++--------- .../club-create/types/bookClubSchema.ts | 10 +++--- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index 501db375..d6db0e1d 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -52,11 +52,12 @@ export default function CreateBookClub() { const bookClubData = { title: data.title, description: data.description, - bookType: data.bookType, - location: data.location, - startDate: data.startDate, + bookClubType: data.bookClubType, + meetingType: data.meetingType, + town: data.town, + targetDate: data.targetDate, endDate: data.endDate, - maxParticipants: data.maxParticipants, + memberLimit: data.memberLimit, }; // JSON 데이터 추가 @@ -152,39 +153,39 @@ export default function CreateBookClub() { options={[ { label: '자유책', - value: '자유책', + value: 'FREE', description: '읽고 싶은 책을 자유롭게 선택하고 각자의 생각을 나눠요.', }, { label: '지정책', - value: '지정책', + value: 'FIXED', description: '한 권의 책을 선정해 깊이 있는 토론을 진행해요.', }, ]} - selectedValue={watch('bookType')} - register={register('bookType')} + selectedValue={watch('bookClubType')} + register={register('bookClubType')} /> ( diff --git a/src/features/club-create/types/bookClubSchema.ts b/src/features/club-create/types/bookClubSchema.ts index 1ae09a21..3b8e4fff 100644 --- a/src/features/club-create/types/bookClubSchema.ts +++ b/src/features/club-create/types/bookClubSchema.ts @@ -10,16 +10,16 @@ export const bookClubSchema = z.object({ .min(1, '상세 설명을 입력해주세요') .max(30, '상세 설명은 최대 30자까지 가능합니다'), image: z.any().optional(), - bookType: z.enum(['자유책', '지정책']), - location: z.enum(['온라인', '오프라인']), - place: z.string().optional(), - startDate: z.date().refine((date) => !isNaN(date.getTime()), { + bookClubType: z.enum(['FREE', 'FIXED']), + meetingType: z.enum(['ONLINE', 'OFFLINE']), + town: z.string().optional(), + targetDate: z.date().refine((date) => !isNaN(date.getTime()), { message: '유효한 날짜를 선택해주세요', }), endDate: z.date().refine((date) => !isNaN(date.getTime()), { message: '유효한 날짜를 선택해주세요', }), - maxParticipants: z + memberLimit: z .number() .min(3, '최소 3명 이상 입력해주세요') .max(20, '최대 20명까지 가능합니다'), From 4988c723c240b97f96ba2e79018316f610863267 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 18:36:12 +0900 Subject: [PATCH 5/8] =?UTF-8?q?=E2=9C=A8[Feat]=20=ED=95=9C=EA=B5=AD=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EC=9C=BC=EB=A1=9C=20=EB=82=A0=EC=A7=9C=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index d6db0e1d..42795589 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -48,6 +48,14 @@ export default function CreateBookClub() { formData.append('image', imageFile); } + // 날짜 형식 변환 함수 (KST) + const formatDate = (date: Date) => { + const kstDate = new Date( + date.getTime() - date.getTimezoneOffset() * 60000, + ); + return kstDate.toISOString().slice(0, 19); // yyyy-MM-dd'T'HH:mm:ss 형식 + }; + // 나머지 데이터는 JSON으로 묶기 const bookClubData = { title: data.title, @@ -55,8 +63,8 @@ export default function CreateBookClub() { bookClubType: data.bookClubType, meetingType: data.meetingType, town: data.town, - targetDate: data.targetDate, - endDate: data.endDate, + targetDate: formatDate(data.targetDate), + endDate: formatDate(data.endDate), memberLimit: data.memberLimit, }; From f3f7625944f39642cbddae865dfc8939a63b5550 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 18:58:18 +0900 Subject: [PATCH 6/8] =?UTF-8?q?=E2=9C=A8[Feat]=20=ED=9B=85=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 51 ++----------------- .../club-create/hooks/useCreateBookClub.ts | 47 +++++++++++++++++ src/lib/utils/dateUtils.ts | 4 ++ 3 files changed, 55 insertions(+), 47 deletions(-) create mode 100644 src/features/club-create/hooks/useCreateBookClub.ts create mode 100644 src/lib/utils/dateUtils.ts diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index 42795589..47652632 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -12,9 +12,11 @@ import { import { BookClubForm, bookClubSchema } from '@/features/club-create/types'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; +import { useCreateBookClub } from '@/features/club-create/hooks/useCreateBookClub'; export default function CreateBookClub() { const [selectedFileName, setSelectedFileName] = useState(''); + const { createBookClub } = useCreateBookClub(); const { register, handleSubmit, @@ -40,53 +42,8 @@ export default function CreateBookClub() { // TODO: API 연동, 훅 분리 const onSubmit = (data: BookClubForm) => { - const formData = new FormData(); - - // 이미지 파일 추가 (Multipart) - const imageFile = data.image instanceof File ? data.image : null; - if (imageFile) { - formData.append('image', imageFile); - } - - // 날짜 형식 변환 함수 (KST) - const formatDate = (date: Date) => { - const kstDate = new Date( - date.getTime() - date.getTimezoneOffset() * 60000, - ); - return kstDate.toISOString().slice(0, 19); // yyyy-MM-dd'T'HH:mm:ss 형식 - }; - - // 나머지 데이터는 JSON으로 묶기 - const bookClubData = { - title: data.title, - description: data.description, - bookClubType: data.bookClubType, - meetingType: data.meetingType, - town: data.town, - targetDate: formatDate(data.targetDate), - endDate: formatDate(data.endDate), - memberLimit: data.memberLimit, - }; - - // JSON 데이터 추가 - formData.append( - 'bookClub', - new Blob([JSON.stringify(bookClubData)], { - type: 'application/json', - }), - ); - - // 확인용 로그 - console.log('전송될 데이터:', { - 이미지: imageFile - ? { - 이름: imageFile.name, - 타입: imageFile.type, - 크기: `${(imageFile.size / 1024).toFixed(2)}KB`, - } - : null, - 북클럽_데이터: bookClubData, - }); + const formData = createBookClub(data); + console.log(formData); }; return ( diff --git a/src/features/club-create/hooks/useCreateBookClub.ts b/src/features/club-create/hooks/useCreateBookClub.ts new file mode 100644 index 00000000..1ad4217e --- /dev/null +++ b/src/features/club-create/hooks/useCreateBookClub.ts @@ -0,0 +1,47 @@ +import { toKoreanTime } from '@/lib/utils/dateUtils'; +import { BookClubForm } from '../types'; + +export const useCreateBookClub = () => { + const createBookClub = (data: BookClubForm) => { + const formData = new FormData(); + + const imageFile = data.image instanceof File ? data.image : null; + if (imageFile) { + formData.append('image', imageFile); + } + + const bookClubData = { + title: data.title, + description: data.description, + bookClubType: data.bookClubType, + meetingType: data.meetingType, + town: data.town, + targetDate: toKoreanTime(data.targetDate), + endDate: toKoreanTime(data.endDate), + memberLimit: data.memberLimit, + }; + + formData.append( + 'bookClub', + new Blob([JSON.stringify(bookClubData)], { + type: 'application/json', + }), + ); + + // TODO: API 호출 로직 추가 + console.log('전송될 데이터:', { + 이미지: imageFile + ? { + 이름: imageFile.name, + 타입: imageFile.type, + 크기: `${(imageFile.size / 1024).toFixed(2)}KB`, + } + : null, + 북클럽_데이터: bookClubData, + }); + + return formData; + }; + + return { createBookClub }; +}; diff --git a/src/lib/utils/dateUtils.ts b/src/lib/utils/dateUtils.ts new file mode 100644 index 00000000..34915223 --- /dev/null +++ b/src/lib/utils/dateUtils.ts @@ -0,0 +1,4 @@ +export const toKoreanTime = (date: Date): string => { + const kstDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000); + return kstDate.toISOString().slice(0, 19); +}; From d61397fa2e2ead63d9e92223db4f9bbf71a697ed Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 19:05:14 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=E2=9C=A8[Feat]=20=EB=82=A0=EC=A7=9C=20Ui?= =?UTF-8?q?=20=ED=8F=AC=EB=A7=B7=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index 47652632..466cee41 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -13,6 +13,7 @@ import { BookClubForm, bookClubSchema } from '@/features/club-create/types'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { useCreateBookClub } from '@/features/club-create/hooks/useCreateBookClub'; +import { ko } from 'date-fns/locale'; export default function CreateBookClub() { const [selectedFileName, setSelectedFileName] = useState(''); @@ -34,7 +35,7 @@ export default function CreateBookClub() { if (file) { console.log('선택된 파일:', file); setSelectedFileName(file.name); - setValue('image', file); // 이 줄 추가 + setValue('image', file); } else { setSelectedFileName(''); } @@ -157,14 +158,11 @@ export default function CreateBookClub() { onChange={field.onChange} showTimeSelect timeIntervals={10} - dateFormat="yyyy/MM/dd h:mm aa" + dateFormat="yyyy-MM-dd a HH:mm" timeFormat="HH:mm" + locale={ko} showTimeSelectOnly={false} - timeCaption="Time" - timeClassName={(time) => { - const hours = time.getHours(); - return hours > 12 ? 'text-success' : 'text-error'; - }} + timeCaption="시간" placeholderText="만나는 날짜를 선택해주세요!" customInput={} /> @@ -185,14 +183,11 @@ export default function CreateBookClub() { onChange={field.onChange} showTimeSelect timeIntervals={10} - dateFormat="yyyy/MM/dd h:mm aa" + dateFormat="yyyy-MM-dd a HH:mm" timeFormat="HH:mm" + locale={ko} showTimeSelectOnly={false} - timeCaption="Time" - timeClassName={(time) => { - const hours = time.getHours(); - return hours > 12 ? 'text-success' : 'text-error'; - }} + timeCaption="시간" placeholderText="모임의 모집 마감 날짜를 선택해주세요!" customInput={} /> From fb0ee440cb81fbfa23d554ba5992d748b4b50e31 Mon Sep 17 00:00:00 2001 From: cloud0406 Date: Wed, 11 Dec 2024 19:12:09 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=E2=9C=A8[Feat]=20datepicker=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bookclub/create/page.tsx | 61 ++++--------------- .../components/DatePickerField.tsx | 46 ++++++++++++++ src/features/club-create/components/index.ts | 1 + 3 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 src/features/club-create/components/DatePickerField.tsx diff --git a/src/app/bookclub/create/page.tsx b/src/app/bookclub/create/page.tsx index 466cee41..3c91a71e 100644 --- a/src/app/bookclub/create/page.tsx +++ b/src/app/bookclub/create/page.tsx @@ -1,19 +1,18 @@ 'use client'; -import { useForm, Controller } from 'react-hook-form'; +import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useState } from 'react'; import Button from '@/components/button/Button'; import { CreateClubFormField, + DatePickerContainer, InputField, RadioButtonGroup, } from '@/features/club-create/components'; import { BookClubForm, bookClubSchema } from '@/features/club-create/types'; -import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { useCreateBookClub } from '@/features/club-create/hooks/useCreateBookClub'; -import { ko } from 'date-fns/locale'; export default function CreateBookClub() { const [selectedFileName, setSelectedFileName] = useState(''); @@ -145,55 +144,21 @@ export default function CreateBookClub() { /> - - ( - } - /> - )} - /> - + placeholder="만나는 날짜를 선택해주세요!" + /> - - ( - } - /> - )} - /> - + placeholder="모임의 모집 마감 날짜를 선택해주세요!" + /> ); } - -// diff --git a/src/features/club-create/components/DatePickerField.tsx b/src/features/club-create/components/DatePickerField.tsx new file mode 100644 index 00000000..daf95367 --- /dev/null +++ b/src/features/club-create/components/DatePickerField.tsx @@ -0,0 +1,46 @@ +import { Control, Controller } from 'react-hook-form'; +import DatePicker from 'react-datepicker'; +import { ko } from 'date-fns/locale'; +import { BookClubForm } from '../types'; +import CreateClubFormField from '@/features/club-create/components/CreateClubFormField'; +import InputField from '@/features/club-create/components/InputField'; + +interface DatePickerContainerProps { + control: Control; + name: 'targetDate' | 'endDate'; + label: string; + error?: string; + placeholder: string; +} + +export const DatePickerContainer = ({ + control, + name, + label, + error, + placeholder, +}: DatePickerContainerProps) => { + return ( + + ( + } + /> + )} + /> + + ); +}; diff --git a/src/features/club-create/components/index.ts b/src/features/club-create/components/index.ts index f1d0d7c9..b4fff908 100644 --- a/src/features/club-create/components/index.ts +++ b/src/features/club-create/components/index.ts @@ -1,3 +1,4 @@ export { default as CreateClubFormField } from './CreateClubFormField'; export { default as InputField } from './InputField'; export { default as RadioButtonGroup } from './RadioButtonGroup'; +export { DatePickerContainer } from './DatePickerField';