From a1634ccacc3ed5a6c8f8c94f8fdc2c5afdf1d819 Mon Sep 17 00:00:00 2001 From: najitwo Date: Wed, 27 Nov 2024 02:07:20 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=92=84=20style:=20improve=20DatePicke?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/DatePicker.module.css | 99 ++++++++++++-- .../dashboard/[id]/components/DatePicker.tsx | 121 ++++++++++++------ src/utils/dateUtils.ts | 12 ++ 3 files changed, 185 insertions(+), 47 deletions(-) diff --git a/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.module.css b/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.module.css index a6ebc9d..2ea004b 100644 --- a/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.module.css +++ b/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.module.css @@ -24,7 +24,8 @@ font-size: 14px; font-weight: 400; line-height: 24px; - padding: 11px 14px; + padding: 12px 0 12px 46px; + color: var(--black-100); cursor: pointer; } @@ -36,11 +37,24 @@ color: var(--gray-400); } +.icon { + position: absolute; + top: 50%; + left: 16px; + transform: translateY(-50%); +} + +.icon path { + fill: var(--black-100); +} + .calendarWrapper { + display: flex; position: absolute; - top: 50px; + top: calc(100% + 4px); left: 0; - width: 100%; + width: auto; + max-height: 250px; background: var(--white); border: 1px solid var(--gray-200); border-radius: 4px; @@ -53,20 +67,20 @@ justify-content: space-between; align-items: center; padding: 10px; - background: var(--gray-200); + background: var(--violet-light); border-bottom: 1px solid var(--gray-200); } .daysOfWeek { display: grid; grid-template-columns: repeat(7, 1fr); - background: var(--gray-300); + background: var(--violet-light); text-align: center; padding: 5px 0; } .day { - font-weight: bold; + font-weight: 800; color: var(--black-100); } @@ -77,11 +91,13 @@ } .dayButton { - padding: 10px; + font-size: 14px; + padding: 7px; } -.dayButton:hover { +.dayButton:hover:not(.disabled):not(.selected) { background: var(--violet-light); + color: var(--violet); } .dayButton.selected { @@ -91,19 +107,59 @@ } .dayButton:disabled { - cursor: default; + color: var(--gray-300); + cursor: not-allowed; } .timePicker { - padding: 10px; border-top: 1px solid var(--gray-200); } +.timePicker h4 { + background-color: var(--violet-light); + text-align: center; + padding: 10px; + font-size: 16px; + font-weight: 500; + line-height: 18px; +} + .timeSelect { width: 100%; - padding: 8px; + max-height: 210px; border: 1px solid var(--gray-200); border-radius: 4px; + overflow-y: scroll; +} + +.timeSelect::-webkit-scrollbar { + width: 4px; +} + +.timeSelect::-webkit-scrollbar-thumb { + background: var(--purple); + border-radius: 5px; +} + +.timeList { + font-size: 14px; + padding: 7px; + cursor: pointer; +} + +.timeList:hover:not(.disabled):not(.selected) { + background-color: var(--violet-light); + color: var(--violet); +} + +.timeList.selected { + background: var(--violet); + color: var(--white); +} + +.timeList.disabled { + color: var(--gray-300); + cursor: not-allowed; } .image { @@ -119,4 +175,25 @@ font-size: 18px; line-height: 26px; } + + .input { + font-size: 16px; + line-height: 26px; + } + + .calendarWrapper { + max-height: 290px; + } + + .dayButton { + padding: 10px; + } + + .timeSelect { + max-height: 250px; + } + + .timeList { + padding: 10px; + } } diff --git a/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.tsx b/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.tsx index e6fd4f6..b990ef8 100644 --- a/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.tsx +++ b/src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.tsx @@ -1,14 +1,18 @@ 'use client'; -import React, { useState, ChangeEvent } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import Image from 'next/image'; +import { formatDateFormat } from '@/utils/dateUtils'; +import CalendarIcon from '/public/icons/calendar.svg'; import styles from './DatePicker.module.css'; -const DatePicker = () => { +export default function DatePicker() { const [selectedDate, setSelectedDate] = useState(''); const [selectedTime, setSelectedTime] = useState(''); + const [isCalendarVisible, setIsCalendarVisible] = useState(false); const [currentMonth, setCurrentMonth] = useState(new Date()); + const datePickerRef = useRef(null); const daysOfWeek = ['일', '월', '화', '수', '목', '금', '토']; @@ -28,10 +32,6 @@ const DatePicker = () => { return days; }; - const handleDateClick = (date: Date) => { - setSelectedDate(date.toISOString().split('T')[0]); - }; - const handleMonthChange = (direction: 'prev' | 'next') => { setCurrentMonth( (prev) => @@ -43,24 +43,54 @@ const DatePicker = () => { ); }; - const handleTimeChange = (event: ChangeEvent) => { - setSelectedTime(event.target.value); + const handleDateClick = (date: Date) => { + setSelectedDate(formatDateFormat(date)); + setSelectedTime('23:30'); + }; + + const handleTimeClick = (date: string) => { + setSelectedTime(date); + setIsCalendarVisible(false); }; - const daysInMonth = getDaysInMonth(currentMonth); + useEffect(() => { + const handleOutsideClick = (event: MouseEvent) => { + if ( + datePickerRef.current && + !datePickerRef.current.contains(event.target as Node) + ) { + setIsCalendarVisible(false); + } + }; + + document.addEventListener('mousedown', handleOutsideClick); + return () => { + document.removeEventListener('mousedown', handleOutsideClick); + }; + }, []); + + const daysInMonth = useMemo( + () => getDaysInMonth(currentMonth), + [currentMonth] + ); return (
-
+
setIsCalendarVisible((prev) => !prev)} /> + {isCalendarVisible && (
@@ -95,45 +125,64 @@ const DatePicker = () => { ))}
- {daysInMonth.map((date, index) => ( - - ))} + {daysInMonth.map((date, index) => { + const now = new Date(); + const todayStart = new Date(now.setHours(0, 0, 0, 0)); + const isToday = date?.getTime() === todayStart.getTime(); + const isPastDate = + (date?.getTime() ?? Infinity) < todayStart.getTime() || + (isToday && now.getHours() >= 23 && now.getMinutes() >= 30); + + return ( + + ); + })}
- +
)}
); -}; - -export default DatePicker; +} diff --git a/src/utils/dateUtils.ts b/src/utils/dateUtils.ts index 4d32f1b..3307db1 100644 --- a/src/utils/dateUtils.ts +++ b/src/utils/dateUtils.ts @@ -11,3 +11,15 @@ export function formatDateToCustomFormat(targetDate: string): string { return `${year}.${month}.${day} ${hours}:${minutes}`; } + +export function formatDateFormat(targetDate: string | Date): string { + if (!targetDate) return ''; + + const date = new Date(targetDate); + + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + + return `${year}. ${month}. ${day}`; +} From ea078256a6bb388c982ffcf4cceee59753d547ba Mon Sep 17 00:00:00 2001 From: najitwo Date: Wed, 27 Nov 2024 03:10:12 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20FileInput=20to=20?= =?UTF-8?q?task=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/CreateTaskModal.module.css | 5 +++++ .../[id]/components/CreateTaskModal.tsx | 20 +++++++++++++++++- .../mypage/_components/Label.module.css | 13 ------------ .../mypage/_components/Label.tsx | 15 ------------- .../mypage/_components/ProfileForm.tsx | 2 +- .../FileInput.module.css | 21 ++++++++++++++++--- .../_components => components}/FileInput.tsx | 20 ++++++++++++++---- .../Textarea.module.css} | 0 .../Input.tsx => components/Textarea.tsx} | 16 +++++++------- 9 files changed, 66 insertions(+), 46 deletions(-) delete mode 100644 src/app/(with-header-sidebar)/mypage/_components/Label.module.css delete mode 100644 src/app/(with-header-sidebar)/mypage/_components/Label.tsx rename src/{app/(with-header-sidebar)/mypage/_components => components}/FileInput.module.css (70%) rename src/{app/(with-header-sidebar)/mypage/_components => components}/FileInput.tsx (76%) rename src/{app/(with-header-sidebar)/mypage/_components/Input.module.css => components/Textarea.module.css} (100%) rename src/{app/(with-header-sidebar)/mypage/_components/Input.tsx => components/Textarea.tsx} (83%) diff --git a/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.module.css b/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.module.css index 757a334..723e81a 100644 --- a/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.module.css +++ b/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.module.css @@ -12,6 +12,11 @@ color: var(--black-100); } +.image.image { + width: 76px; + height: 76px; +} + .footer { display: flex; align-items: center; diff --git a/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.tsx b/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.tsx index 102689d..600a1f3 100644 --- a/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.tsx +++ b/src/app/(with-header-sidebar)/dashboard/[id]/components/CreateTaskModal.tsx @@ -5,19 +5,28 @@ import Input from '@/components/Input'; import styles from './CreateTaskModal.module.css'; import SearchDropdown from './SearchDropdown'; import DatePicker from './DatePicker'; +import Textarea from '@/components/Textarea'; +import FileInput from '@/components/FileInput'; export interface ManagerOption { id: number; name: string; } +export interface TaskFormValues { + image: File | null; + title: string; + description: string; +} + export default function CreateTaskModal() { const { closeModal } = useModalStore(); const { register, handleSubmit, formState: { errors, isValid }, - } = useForm<{ email: string }>({ mode: 'onChange' }); + setValue, + } = useForm({ mode: 'onChange' }); const onSubmit = () => { // closeModal(); @@ -38,7 +47,16 @@ export default function CreateTaskModal() {

할일 생성

+