Skip to content

Commit

Permalink
Merge pull request #29 from eunji-0623/feature-황은지
Browse files Browse the repository at this point in the history
Feat: 체험 등록 form 구현
  • Loading branch information
eunji-0623 authored Jul 15, 2024
2 parents 26efffc + 2be8c1e commit 6f24b6b
Show file tree
Hide file tree
Showing 17 changed files with 494 additions and 242 deletions.
12 changes: 7 additions & 5 deletions components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@ const primaryStyleClasses = {

/* 기본 버튼 - 로그인하기, 신청 불가 등 기본적인 형태의 버튼
size, style, onClick, children 필수 입력, disabled 선택 입력
size, style 필수 입력 onClick, children, disabled 선택 입력
size 옵션 - small - width auto | medium - width 144px | large - width 100%
style 옵션 - dark-어두운 배경에 흰글씨 | bright - 흰 배경에 어두운 글씨 | disabled - 회색 배경에 흰 글씨
사용 예시 <Button size="small" style="dark" onClick={handleClick}>로그인하기</Button> */
export function PrimaryButton({
type = 'button',
size,
style,
children,
onClick,
onClick = () => {},
disabled,
}: PrimaryButtonProps) {
return (
<button
type={type}
className={`flex items-center justify-center rounded-md ${primarySizeClasses[size]} ${primaryStyleClasses[style]} ${disabled ? 'cursor' : ''}`}
onClick={onClick}
disabled={disabled ?? false}
Expand Down Expand Up @@ -185,7 +187,7 @@ export function MeatballButton({ onClick }: SimpleButtonProps) {
/* 추가하기 버튼 - 항목 추가 시 사용 */
export function PlusButton({ onClick }: SimpleButtonProps) {
return (
<button type="button" onClick={onClick}>
<button type="button" onClick={onClick} className="shrink-0">
<Image src="/icon/btn_plus.svg" width={56} height={56} alt="추가하기" />
</button>
);
Expand All @@ -194,7 +196,7 @@ export function PlusButton({ onClick }: SimpleButtonProps) {
/* 제거하기 버튼 - 항목 제거 시 사용 */
export function MinusButton({ onClick }: SimpleButtonProps) {
return (
<button type="button" onClick={onClick}>
<button type="button" onClick={onClick} className="shrink-0">
<Image src="/icon/btn_minus.svg" width={56} height={56} alt="제거하기" />
</button>
);
Expand All @@ -217,7 +219,7 @@ export function ArrowCircleButton({ onClick }: SimpleButtonProps) {
/* 이미지 등록하기 버튼 */
export function ImageUploadButton() {
return (
<div className="flex flex-col gap-[40px] items-center justify-center w-[180px] h-[180px] border border-dashed border-var-gray8 rounded-lg">
<div className="flex flex-col gap-[40px] items-center justify-center w-[180px] h-[180px] t:w-[206px] t:h-[206px] m:w-[167px] m:h-[167px] border border-dashed border-var-gray8 rounded-lg">
<Image src="/icon/icon_plus.svg" width={30} height={30} alt="추가" />
<p className="text-[24px]">이미지 등록</p>
</div>
Expand Down
3 changes: 2 additions & 1 deletion components/Button/Button.types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export interface PrimaryButtonProps {
type?: 'button' | 'submit' | 'reset' | undefined;
size: 'small' | 'medium' | 'large';
style: 'dark' | 'bright' | 'disabled' | 'enabled';
children?: string;
disabled?: boolean;
onClick: () => void;
onClick?: () => void;
}

export interface PaginationButtonProps {
Expand Down
17 changes: 9 additions & 8 deletions components/KategorieDropdown/KategorieDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { KategoriedDropState } from '@/states/KategorieDropState';
import useClickOutside from '@/hooks/useClickOutside';

const Kategories: { [key: string]: string } = {
'문화 예술': '문화 예술',
'문화 예술': '문화 · 예술',
식음료: '식음료',
스포츠: '스포츠',
투어: '투어',
Expand All @@ -31,7 +31,7 @@ function Kategorie({ name, setIsOpen }: KategorieDropdownProps) {
const textColor = isSelected ? 'text-white' : 'text-black';
return (
<li
className={`w-[784px] h-[40px] flex items-center pl-[36px] ${backgroundColor} ${textColor} relative rounded-md hover:bg-gray-100`}
className={`w-[784px] h-[40px] flex items-center pl-[36px] ${backgroundColor} ${textColor} relative rounded-md hover:bg-gray-100 t:w-full m:w-full`}
onClick={changeKateogireInfo}
style={{ pointerEvents: isSelected ? 'none' : 'auto' }}
>
Expand Down Expand Up @@ -64,9 +64,12 @@ function KategorieDropdown() {
};

return (
<div className="w-[800px] h-[56px] relative z-10" ref={KateDropdownElement}>
<div
className="w-[800px] h-[56px] relative z-10 t:w-full m:w-full"
ref={KateDropdownElement}
>
<div
className={`w-[800px] h-[56px] border-solid border border-var-gray7 rounded flex items-center pl-[16px] text-[16px] font-[400] font-sans ${isSelected} bg-white`}
className={`w-[800px] h-[56px] border-solid border border-var-gray7 rounded flex items-center pl-[16px] text-[16px] font-[400] font-sans ${isSelected} bg-white t:w-full m:w-full`}
onClick={handleOpen}
>
{SelectedKateogorie.name ? SelectedKateogorie.name : '카테고리'}
Expand All @@ -88,14 +91,12 @@ function KategorieDropdown() {
></Image>
)}
</div>
{isOpen ? (
<ul className="w-[800px] h-[260px] rounded-md bg-white absolute animate-slideDown bottom-[-266px] flex flex-col items-center justify-center shadow-kategorieDropdown">
{isOpen && (
<ul className="w-[800px] h-[260px] rounded-md bg-white absolute animate-slideDown bottom-[-266px] flex flex-col items-center justify-center shadow-kategorieDropdown t:w-full m:w-full">
{Object.values(Kategories).map((category) => (
<Kategorie key={category} name={category} setIsOpen={setIsOpen} />
))}
</ul>
) : (
<div></div>
)}
</div>
);
Expand Down
14 changes: 12 additions & 2 deletions components/MyActivity/Register/DateInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useState } from 'react';
import CustomCalendar from '@/components/CustomCalendar/CustomCalendar';
import useClickOutside from '@/hooks/useClickOutside';
import { useRecoilState } from 'recoil';
import { selectedDateState } from '@/states/registerState';
import { TimeSlotGroupProps } from './TimeSlot.types';

function Date() {
function Date({ index }: TimeSlotGroupProps) {
const [date, setDate] = useState<Date | null>(null);
const [showCalendar, setShowCalendar] = useState(false);
const [selectedDate, setSelectedDate] = useRecoilState(selectedDateState);

const handleInputClick = () => {
setShowCalendar(true);
Expand All @@ -13,6 +17,12 @@ function Date() {
const handleDateSelect = (date: Date | null) => {
setDate(date);
setShowCalendar(false);

if (date) {
const updatedDate = [...selectedDate];
updatedDate[index] = date.toString();
setSelectedDate(updatedDate);
}
};

const closeCalendar = () => {
Expand All @@ -23,7 +33,7 @@ function Date() {

return (
<div className="relative">
<div className="w-[374px]">
<div className="w-[374px] t:w-[149px] m:w-[130px]">
<input
type="text"
value={date ? date.toDateString() : ''}
Expand Down
37 changes: 28 additions & 9 deletions components/MyActivity/Register/TimeDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
import useClickOutside from '@/hooks/useClickOutside';
import Image from 'next/image';
import { useState } from 'react';
import { TimeDropdownProps } from './TimeSlot.types';
import { useRecoilState, useRecoilValue } from 'recoil';
import { endTimeState, startTimeState } from '@/states/registerState';

function generateTimeOptions() {
function generateTimeOptions(startTime = 0, type: 'start' | 'end') {
const times = [];
for (let hour = 0; hour <= 24; hour++) {
const timeString = `${hour}:00`;
const endTime = type === 'start' ? 23 : 24;
for (let hour = startTime; hour <= endTime; hour++) {
const timeString = `${hour < 10 ? '0' + hour : hour}:00`;
times.push(timeString);
}
return times;
}

function TimeDropdown() {
function TimeDropdown({ type, index }: TimeDropdownProps) {
const [isOpen, setIsOpen] = useState(false);
const [selectedTime, setSelectedTime] = useState<string | null>('0:00');
const [selectedTime, setSelectedTime] = useState<string>('00:00');
const [startTime, setStartTime] = useRecoilState(startTimeState);
const [endTime, setEndTime] = useRecoilState(endTimeState);

const handleSelectTime = (time: string | null) => {
const handleSelectTime = (time: string) => {
setSelectedTime(time);
setIsOpen(false);
if (type === 'start') {
const updatedStartTime = [...startTime];
updatedStartTime[index] = time;
setStartTime(updatedStartTime);
} else {
const updatedEndTime = [...endTime];
updatedEndTime[index] = time;
setEndTime(updatedEndTime);
}
};

const handleClickDropdown = () => {
Expand All @@ -29,13 +44,17 @@ function TimeDropdown() {
};

const dropdownRef = useClickOutside<HTMLDivElement>(closeDropdown);
const times = generateTimeOptions();
let timeLimit = 0;
if (type === 'end') {
timeLimit = Number(startTime[index].substring(0, 2)) + 1;
}
const times = generateTimeOptions(timeLimit, type);

return (
<div ref={dropdownRef} className="relative">
<button
type="button"
className="flex justify-between items-center w-[140px] h-[56px] py-[4px] px-[16px] rounded-md border border-var-gray6 bg-white text-var-gray7 text-left"
className="flex justify-between items-center w-[140px] h-[56px] py-[4px] px-[16px] m:px-[7px] rounded-md border border-var-gray6 bg-white text-var-gray7 text-left t:w-[104px] m:w-[79px]"
onClick={handleClickDropdown}
>
<p>{selectedTime}</p>
Expand All @@ -52,7 +71,7 @@ function TimeDropdown() {
<li
key={time}
onClick={() => handleSelectTime(time)}
className="h-[56px] text-var-black py-[18px] px-[16px] hover:bg-gray-100 cursor-pointer bg-white"
className="h-[56px] text-var-black py-[18px] px-[16px] m:px-[12px] hover:bg-gray-100 cursor-pointer bg-white"
>
{time}
</li>
Expand Down
64 changes: 39 additions & 25 deletions components/MyActivity/Register/TimeSlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,86 @@ import DateInput from './DateInput';
import TimeDropdown from './TimeDropdown';
import { TimeSlotGroupProps } from './TimeSlot.types';
import { useState } from 'react';
import { useRecoilState } from 'recoil';
import { timeSlotCountState } from '@/states/registerState';

function TimeSlotGroup({
isDefault = false,
handleClickPlus = () => {},
handleClickMinus = (id: number) => {},
id = -1,
index,
}: TimeSlotGroupProps) {
return (
<div className="flex gap-[20px]">
<DateInput />
<div className="flex gap-[12px] items-center">
<TimeDropdown />
<p className="text-[20px] font-[700]">~</p>
<TimeDropdown />
<div className="flex items-center t:justify-between m:justify-between gap-[20px] t:gap-[4px] m:gap-[4px]">
<div className="flex items-center gap-[20px] t:gap-[4px] m:gap-[4px]">
<DateInput index={index} />
<div className="flex gap-[12px] items-center t:gap-[4px] m:gap-[4px]">
<TimeDropdown type="start" index={index} />
<p className="text-[20px] font-[700] t:hidden m:hidden">~</p>
<TimeDropdown type="end" index={index} />
</div>
</div>
<div className="flex items-center">
{isDefault ? (
<PlusButton onClick={handleClickPlus} />
) : (
<MinusButton
onClick={() => {
handleClickMinus(id);
}}
/>
)}
</div>
{isDefault ? (
<PlusButton onClick={handleClickPlus} />
) : (
<MinusButton
onClick={() => {
handleClickMinus(id);
}}
/>
)}
</div>
);
}

function TimeSlot() {
const [timeSlots, setTimeSlots] = useState<{ id: number }[]>([]);
const [timeSlotCount, setTimeSlotCount] = useRecoilState(timeSlotCountState);

const handleClickPlus = () => {
const newTimeSlot = {
id: Date.now(),
};
setTimeSlots((prevTimeSlots) => [...prevTimeSlots, newTimeSlot]);
setTimeSlotCount(timeSlotCount + 1);
};
const handleClickMinus = (id: number) => {
setTimeSlots((prevTimeSlots) =>
prevTimeSlots.filter((timeSlot) => timeSlot.id !== id)
);
setTimeSlotCount(timeSlotCount - 1);
};

return (
<div>
<label className="text-[24px] font-[700] block mb-[24px] text-var-black">
예약 가능한 시간대
</label>
<div className="flex gap-[20px]">
<label className="w-[374px] text-[20px] font-[500] block mb-[10px] text-var-gray8">
<div className="flex gap-[20px] t:gap-[4px] m:gap-[4px]">
<label className="w-[374px] t:w-[149px] m:w-[130px] text-[20px] m:text-[16px] font-[500] block mb-[10px] text-var-gray8 ">
날짜
</label>
<label className="w-[158px] text-[20px] font-[500] block mb-[10px] text-var-gray8">
시작 시간
</label>
<label className="w-[140px] text-[20px] font-[500] block mb-[10px] text-var-gray8">
종료 시간
</label>
<div className="flex gap-[12px] items-center t:gap-[4px] m:gap-[4px]">
<label className="w-[140px] t:w-[104px] m:w-[79px] text-[20px] m:text-[16px] font-[500] block mb-[10px] text-var-gray8">
시작 시간
</label>
<label className="w-[140px] t:w-[104px] m:w-[79px] text-[20px] m:text-[16px] font-[500] block mb-[10px] text-var-gray8">
종료 시간
</label>
</div>
<label className="text-transparent w-[56px] shrink-0">blank</label>
</div>
<TimeSlotGroup isDefault handleClickPlus={handleClickPlus} />
<TimeSlotGroup isDefault handleClickPlus={handleClickPlus} index={0} />
<hr className="mt-[20px] mb-[20px]" />
<div className="space-y-[20px]">
{timeSlots.map((timeSlot) => (
{timeSlots.map((timeSlot, index) => (
<TimeSlotGroup
key={timeSlot.id}
id={timeSlot.id}
index={index + 1}
handleClickMinus={() => {
handleClickMinus(timeSlot.id);
}}
Expand Down
6 changes: 6 additions & 0 deletions components/MyActivity/Register/TimeSlot.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ export interface TimeSlotGroupProps {
handleClickPlus?: () => void;
handleClickMinus?: (id: number) => void;
id?: number;
index: number;
}

export interface TimeDropdownProps {
type: 'start' | 'end';
index: number;
}
Loading

0 comments on commit 6f24b6b

Please sign in to comment.