Skip to content

Commit

Permalink
✨ feat: 회의 만들기 페이지 step2(회의 날짜 및 시간) UI 작성 (#47)
Browse files Browse the repository at this point in the history
* feat: 회의실 만들기 step2 UI 틀 작성

* feat: 특정 날짜에 대한 요일 계산 함수 작성

* refactor: Input 컴포넌트의 interface 작성

* feat: DatePicker 컴포넌트 적용

* feat: Date Picker 컴포넌트의 Open, Close 액션 및 애니메이션 적용

* feat: 썸네일 state react-hook-form 적용

* feat: Date Picker & Time Picker 컴포넌트 hook 적용

* feat: 회의실 만들기 step2 UI 틀 작성

* feat: 특정 날짜에 대한 요일 계산 함수 작성

* refactor: Input 컴포넌트의 interface 작성

* feat: DatePicker 컴포넌트 적용

* feat: Date Picker 컴포넌트의 Open, Close 액션 및 애니메이션 적용

* feat: 썸네일 state react-hook-form 적용

* feat: Date Picker & Time Picker 컴포넌트 hook 적용

* fix: Date Picker type 에러 수정

* refactor: console.log 코드 제거

* refactor: eslint rule 주석 제거
  • Loading branch information
shubug1015 authored Apr 10, 2024
1 parent e074be6 commit fde345e
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 62 deletions.
1 change: 0 additions & 1 deletion src/components/common/TimePicker/TimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export const TimePicker = ({
case 'time': {
const periodOfDay = swiper1?.slides[swiper1?.activeIndex].innerText;
return { periodOfDay, hour, minute };
// return `${periodOfDay} ${hour}시 ${minute}분`;
}
}
};
Expand Down
2 changes: 2 additions & 0 deletions src/components/createMeetingRoom/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './step1';
export * from './step2';
41 changes: 20 additions & 21 deletions src/components/createMeetingRoom/step1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,34 @@
import { thumbnailList } from '@/assets/MeetingRoom/thumbnailList';
import { Flex, Space } from '@/components/Wrapper';
import { Input, SvgIcon } from '@/components/common';
import { FormType } from '@/pages/createMeetingroom';
import { theme } from '@/styles';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { FieldErrors, UseFormRegister, UseFormWatch } from 'react-hook-form';
import {
FieldErrors,
UseFormRegister,
UseFormSetValue,
UseFormWatch
} from 'react-hook-form';

interface Step1Props {
register: UseFormRegister<{
meetingRoomName: string;
meetingRoomNotice: string;
}>;
watch: UseFormWatch<{ meetingRoomName: string; meetingRoomNotice: string }>;
register: UseFormRegister<FormType>;
watch: UseFormWatch<FormType>;
errors?: FieldErrors;
thumbnailNumber: number | null;
setThumbnailNumber: React.Dispatch<React.SetStateAction<number | null>>;
setValue: UseFormSetValue<FormType>;
}

const Step1 = ({
register,
watch,
errors,
thumbnailNumber,
setThumbnailNumber
}: Step1Props) => {
export const Step1 = ({ register, watch, errors, setValue }: Step1Props) => {
return (
<Flex direction="column" align="flex-start">
<div
css={css`
width: 100%;
`}>
<StyledLabel>
회의 이름을 알려주세요 <SvgIcon id="star_orange" size={18} />
회의 이름을 알려주세요
<SvgIcon id="star_orange" size={18} />
</StyledLabel>
<Input
{...register('meetingRoomName', {
Expand Down Expand Up @@ -73,16 +70,20 @@ const Step1 = ({
<Space height={4} />

<StyledLabel>
썸네일을 골라주세요 <SvgIcon id="star_orange" size={18} />
썸네일을 골라주세요
<SvgIcon id="star_orange" size={18} />
</StyledLabel>

<StyledThumbnailList>
{thumbnailList.map((thumbnail) => (
<button
key={thumbnail.id}
onClick={() => setThumbnailNumber(thumbnail.id + 1)}
onClick={() =>
setValue('meetingThumbnail', String(thumbnail.id + 1))
}
css={css`
border: ${thumbnail.id + 1 === thumbnailNumber &&
border: ${thumbnail.id + 1 ===
Number(watch('meetingThumbnail')) &&
`2px solid ${theme.palette.main_blue}`};
border-radius: 14px;
`}>
Expand Down Expand Up @@ -110,5 +111,3 @@ const StyledThumbnailList = styled.div`
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1rem;
`;

export default Step1;
223 changes: 223 additions & 0 deletions src/components/createMeetingRoom/step2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/** @jsxImportSource @emotion/react */
import { Flex, Space } from '@/components/Wrapper';
import { DatePicker, Input, SvgIcon, TimePicker } from '@/components/common';
import { useDatePicker } from '@/hooks/useDatePicker';
import { useTimePicker } from '@/hooks/useTimePicker';
import { FormType } from '@/pages/createMeetingroom';
import { getDayOfWeek } from '@/utils/getDayOfWeek';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { AnimatePresence, motion } from 'framer-motion';
import { useEffect } from 'react';
import {
FieldErrors,
UseFormRegister,
UseFormSetValue,
UseFormWatch
} from 'react-hook-form';

interface Step2Props {
register: UseFormRegister<FormType>;
watch: UseFormWatch<FormType>;
errors?: FieldErrors;
setValue: UseFormSetValue<FormType>;
}

const pickerVariants = {
invisible: {
opacity: 0
},
visible: {
opacity: 1
}
};

export const Step2 = ({ register, watch, errors, setValue }: Step2Props) => {
const { datePicker, setDate, openDatePicker, closeDatePicker } =
useDatePicker();
const {
timePicker: timePicker1,
setTime: setTime1,
openTimePicker: openTimePicker1,
closeTimePicker: closeTimePicker1
} = useTimePicker();
const {
timePicker: timePicker2,
setTime: setTime2,
openTimePicker: openTimePicker2,
closeTimePicker: closeTimePicker2
} = useTimePicker();

useEffect(() => {
const { year, month, date } = datePicker.date;
setValue(
'meetingRoomDate',
`${month}${date}${getDayOfWeek(`${year}-${month}-${date}`)}요일`
);
}, [datePicker.date, setValue]);

useEffect(() => {
const { periodOfDay, hour, minute } = timePicker1.time;
if (periodOfDay && hour && minute) {
setValue('meetingRoomTime', `${periodOfDay} ${hour}${minute}분`);
}
}, [timePicker1.time, setValue]);

useEffect(() => {
const { hour, minute } = timePicker2.time;
if (hour && minute) {
setValue('meetingRoomDuration', `${hour}시간 ${minute}분`);
}
}, [timePicker2.time, setValue]);
return (
<Flex direction="column" align="flex-start">
<div
css={css`
width: 100%;
`}>
<StyledLabel>
회의가 진행되는 날짜를 알려주세요
<SvgIcon id="star_orange" size={18} />
</StyledLabel>
<div
css={css`
display: flex;
gap: 1rem;
`}>
<Input
{...register('meetingRoomDate', {
required: '회의 날짜를 입력해주세요'
})}
value={watch('meetingRoomDate')}
type="default"
placeholder="0월 0일 00일"
isError={errors?.meetingRoomDate ? true : false}
errorText={errors?.meetingRoomDate?.message as string}
readOnly
onClick={() => {
if (timePicker1.isOpen) {
closeTimePicker1();
}
if (timePicker2.isOpen) {
closeTimePicker2();
}
openDatePicker();
}}
/>
<Input
{...register('meetingRoomTime', {
required: '회의 시간을 입력해주세요'
})}
value={watch('meetingRoomTime')}
type="default"
placeholder="오후 00시 00분"
isError={errors?.meetingRoomTime ? true : false}
errorText={errors?.meetingRoomTime?.message as string}
readOnly
onClick={() => {
if (datePicker.isOpen) {
closeDatePicker();
}
if (timePicker2.isOpen) {
closeTimePicker2();
}
openTimePicker1();
}}
/>
</div>

<AnimatePresence>
{datePicker.isOpen && (
<motion.div
variants={pickerVariants}
initial="invisible"
animate="visible">
<DatePicker
date={datePicker.date}
setDate={setDate}
onClose={closeDatePicker}
/>
<Space height={30} />
</motion.div>
)}
</AnimatePresence>

<AnimatePresence>
{timePicker1.isOpen && (
<motion.div
variants={pickerVariants}
initial="invisible"
animate="visible">
<TimePicker
type="time"
setTime={setTime1}
onClose={closeTimePicker1}
/>
<Space height={30} />
</motion.div>
)}
</AnimatePresence>

<StyledLabel>
회의 예상 소요시간을 알려주세요
<SvgIcon id="star_orange" size={18} />
</StyledLabel>
<div
css={css`
display: flex;
gap: 1rem;
`}>
<Input
{...register('meetingRoomDuration', {
required: '회의 예상 소요시간을 입력해주세요'
})}
value={watch('meetingRoomDuration')}
type="default"
placeholder="00시간 00분"
isError={errors?.meetingRoomDuration ? true : false}
errorText={errors?.meetingRoomDuration?.message as string}
readOnly
onClick={() => {
if (datePicker.isOpen) {
closeDatePicker();
}
if (timePicker1.isOpen) {
closeTimePicker1();
}
openTimePicker2();
}}
/>
<div
css={css`
width: 100%;
`}
/>
</div>

<AnimatePresence>
{timePicker2.isOpen && (
<motion.div
variants={pickerVariants}
initial="invisible"
animate="visible">
<TimePicker
type="duration"
setTime={setTime2}
onClose={closeTimePicker2}
/>
</motion.div>
)}
</AnimatePresence>
</div>
</Flex>
);
};

const StyledLabel = styled.label`
${(props) => props.theme.typo.T5}
color: ${(props) => props.theme.palette.dark_gray2};
display: flex;
align-items: center;
gap: 0.2rem;
margin-bottom: 1rem;
`;
Loading

0 comments on commit fde345e

Please sign in to comment.