Closed jellyjoji closed 1 year ago
요소에서 입력받은 날짜 포멧과 PocketBase의 날짜(date) 포멧이 다르기 때문입니다. 🤔
날짜 및 시간 정보를 받을 수 있도록 DatePicker 컴포넌트 코드를 아래와 같이 수정합니다.
DatePicker.jsx
import { forwardRef, useId, useState } from 'react';
function DatePicker({ title, className, labelClassName, ...restProps }, ref) {
const id = useId();
return (
<>
<div>
<label htmlFor={id} className={labelClassName}>
{title}
</label>
<input
**type="datetime-local"**
id={id}
ref={ref}
className={className}
{...restProps}
/>
</div>
</>
);
}
export default forwardRef(DatePicker);
사용자로부터 입력 받은 날짜, 시간 정보를 PocketBase 날짜 포멧을 변환하여 폼 데이터에 추가합니다.
CreateRoom.jsx
const handleCreate = async (e) => {
e.preventDefault();
// ...
// Input (날짜, 시간 정보) -> PocketBase (날짜, 시간 정보) 변환
const dateValue = **new Date(dateRef.current.value).toISOString()**;
const data = new FormData();
// ...
**data.append('pickup', dateValue);**
// ...
};
Date.prototype.toISOString()
매서드를 사용하여 시간데이터를 커스텀하여 해결하였습니다.
toISOString() 메서드는 단순화한 확장 ISO 형식([ISO 8601](https://ko.wikipedia.org/wiki/ISO_8601))의 문자열을 반환합니다. 반환값은 언제나 24글자 또는 27글자(각각 YYYY-MM-DDTHH:mm:ss.sssZ 또는 ±YYYYYY-MM-DDTHH:mm:ss.sssZ)입니다.시간대는 언제나 UTC이며 접미어 Z로 표현합니다.
해결된 코드 첨부
const dateValue = new Date(createRoomForm.pickUp).toISOString()
import { AppContext } from '@/App';
import FormInput from '@/components/FormInput';
import { useState, useContext, useEffect } from 'react';
function PickUp({ value = null, label, title, className, labelClassName, ...restProps }) {
const { updateCreateRoomForm } = useContext(AppContext);
const [data, setData] = useState(value);
useEffect(() => {
updateCreateRoomForm('pickUp', data)
}, [data])
const handleInputChange = (e) => {
setData(e.target.value);
}
return (
<div>
<FormInput
title={title}
value={data || ''}
onChange={handleInputChange}
type="datetime-local"
placeholder="픽업 날짜와 시간을 선택헤주세요"
labelClassName="product name"
inputClassName="defaultInput w-full"
label={label}
{...restProps}
/>
</div>
)
}
export default PickUp;
import { AppContext } from '@/App';
import { pb } from '@/api/pocketbase';
import { ClientResponseError } from 'pocketbase';
import { useContext, useRef } from 'react';
import { Helmet } from 'react-helmet-async';
import { useNavigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import Button from '@/components/Button';
import CreateHeader from '@/layout/CreateHeader';
import Category from '@/parts/create/Category';
import Content from '@/parts/create/Content';
import Creator from '@/parts/create/Creator';
import PickUp from '@/parts/create/PickUp';
import UploadImage from '@/parts/create/UploadImage';
import MeetingPoint from '@/parts/create/MeetingPoint';
import ParticipateCounter from '@/parts/create/ParticipateCounter';
import PaymentToggleButton from '@/parts/create/PaymentToggleButton';
import Status from '@/parts/create/Status';
import Price from '@/parts/create/Price';
import Title from '@/parts/create/title';
function CreateRoom() {
const { createRoomForm } = useContext(AppContext);
const navigate = useNavigate();
const formRef = useRef(null);
const handleCreate = async (e) => {
e.preventDefault();
const categoryValue = createRoomForm.category;
const titleValue = createRoomForm.title;
const contentValue = createRoomForm.content;
const priceValue = createRoomForm.price;
// createRoomForm.pickUp
const dateValue = new Date(createRoomForm.pickUp).toISOString();
// const paymentValue = paymentRef.current.dataset.payment;
const paymentValue = createRoomForm.payment;
const ParticipateCounterValue = Number(
// createRoomForm.current.textContent
createRoomForm.participateNumber
);
const meetingPointValue = createRoomForm.meetingPoint;
const creatorValue = createRoomForm.creator.id;
// const uploadImageValue = uploadImageRef.current.files[0];
const uploadImageValue = createRoomForm.uploadImage;
const statusValue = createRoomForm.status;
const data = new FormData();
data.append('category', categoryValue);
data.append('title', titleValue);
data.append('content', contentValue);
data.append('price', priceValue);
data.append('pickUp', dateValue);
data.append('payment', paymentValue);
data.append('participateNumber', ParticipateCounterValue);
data.append('meetingPoint', meetingPointValue);
data.append('creator', creatorValue);
data.append('participate', creatorValue);
if (uploadImageValue) {
data.append('uploadImage', uploadImageValue);
}
data.append('status', statusValue);
try {
await pb.collection('products').create(data);
toast.success('등록되었습니다.', {
position: 'top-center',
ariaProps: {
role: 'status',
'aria-live': 'polite',
},
});
navigate(`/products`);
} catch (error) {
if (!(error instanceof ClientResponseError)) {
toast.error('등록에 실패하였습니다. 다시 시도해 주세요.', {
position: 'top-center',
ariaProps: {
role: 'status',
'aria-live': 'polite',
},
});
}
}
};
return (
<>
<Helmet>
<title>방만들기</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
property="og:title"
content="합리적인 소비를 위한 공동구매 서비스 R09M 공동구매 방 만들기 페이지"
/>
<meta
property="twitter:title"
content="합리적인 소비를 위한 공동구매 서비스 R09M 공동구매 방 만들기 페이지"
/>
<meta property="og:type" content="web application" />
<meta property="og:url" content="https://r09m.vercel.app/createRoom" />
<meta
property="og:description"
content="공동구매 채소 상품을 확인할 수 있는 페이지입니다. 카테고리, 상품명, 상품 이미지, 상품 가격, 내용, 픽업 날짜, 상태, 생성자, 지불 방법, 픽업 위치 등을 입력하면 방이 생성됩니다."
/>
<meta
name="description"
content="공동구매 채소 상품을 확인할 수 있는 페이지입니다. 카테고리, 상품명, 상품 이미지, 상품 가격, 내용, 픽업 날짜, 상태, 생성자, 지불 방법, 픽업 위치 등을 입력하면 방이 생성됩니다."
></meta>
<meta property="og:image" content="favicon.png" />
<meta property="og:article:author" content="Ready! Act" />
</Helmet>
<h1 className="sr-only">R09M</h1>
<div className="py-2">
<div className="px-4">
<CreateHeader />
<h2 className="pageTitle">방만들기</h2>
</div>
</div>
<div>
<form
encType="multipart/form-data"
ref={formRef}
onSubmit={handleCreate}
>
<div className="flex flex-col gap-4 p-4 relative"
>
<Category
title="카테고리"
className="w-full defaultInput"
label="카테고리"
value={createRoomForm.category}
/>
<Title
value={createRoomForm.title}
/>
<Price
value={createRoomForm.price}
/>
<Content
title="내용"
placeholder="공구 모임 주요내용을 알려주세요."
className="w-full defaultInput"
labelClassName="product content"
label="내용"
value={createRoomForm.content}
/>
<PickUp
title="픽업 날짜"
label="픽업 날짜"
className="w-full defaultInput"
labelClassName="date Picker"
value={createRoomForm.pickUp}
/>
<Status
title="상태"
label="상태"
className="w-full defaultInput "
labelClassName="status"
/>
<Creator />
<PaymentToggleButton
title="정산 방법"
label="정산 방법"
labelClassName="payment"
value={createRoomForm.payment}
/>
<ParticipateCounter value={createRoomForm.participateCounter} labelClassName="participateCounter" label="참여자 인원" title="참여자 인원" />
<MeetingPoint
value={createRoomForm.meetingPoint}
title="만날 장소" labelClassName="meetingPoint" />
<UploadImage
// value={createRoomForm.uploadImage}
title="파일 업로드"
label="파일 업로드"
className="bg-[#EBF8E8] p-4 rounded-lg text-primary-500"
/>
</div >
<Button
type="submit"
className="fixed bottom-3 py-4 activeButton lgFontButton mx-3 w-[93vw] max-w-[544px]"
>
방 만들기
</Button>
</form >
</div >
</>
);
}
export default CreateRoom;
내용
pocketHost 에 시간날짜 form 을 Create 하는 중입니다. DatePicker 에 대한 양식은
<input type="date">
로 구현하여 form 에 맞게 전송하는데 성공했으나 포켓호스트 양식에 맞는 시간 형식을 동시에 입력할수 없는 상황입니다.<input type="datetime-local">
형식으로 구현할 경우2018-06-12T19:30
형식으로 입력되어 포켓호스트 양식과 일치하지 않아 form에 올라가지 않는 문제가 있습니다.DatePicker.jsx 현재 구현한 날짜 구현 코드입니다.
시간 데이터를 "00:00:00" 형식으로 다음과 같은 방법 구현하여 사용하려해도 한 입력폼에 date 와 time 이 동시에 들어가야하는 문제점이 있습니다.
TimePicker.jsx
참고 이미지 (선택)
문제 상황
아래는 데이터가 입력되어 생성되는 포켓호스트 폼 형식입니다.
※ 댓글에 이슈 해결 완료 후 결과 또는 해결 과정 이미지 첨부