diff --git a/package-lock.json b/package-lock.json index a9c13b12..0551b06e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "sakura-js": "^1.1.1", "sharp": "^0.33.5", "slick-carousel": "^1.8.1", - "sweetalert2": "^11.10.8", + "sweetalert2": "^11.14.1", "swiper": "^11.1.14", "tailwindcss-animate": "^1.0.7", "zustand": "^4.5.2" @@ -6342,9 +6342,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.0.tgz", - "integrity": "sha512-kF1Q/+GtZZXr+rYVcBNwlEsnxP089CpDbck+MYjvLaQj9x4fzHqN9UhlkHOIR0k09LVu2sx8cU9BnvRlxWIZqg==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.1.tgz", + "integrity": "sha512-xadhfcA4STGMh8nC5zHFFWURhRpWc4zyI3GdMDFH/m3hGWZeQQNWhX9xcG4lI9gZYsi/IlazKbwvvje3juL3Xg==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" diff --git a/package.json b/package.json index 555192c3..536d48e7 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "sakura-js": "^1.1.1", "sharp": "^0.33.5", "slick-carousel": "^1.8.1", - "sweetalert2": "^11.10.8", + "sweetalert2": "^11.14.1", "swiper": "^11.1.14", "tailwindcss-animate": "^1.0.7", "zustand": "^4.5.2" diff --git a/public/lef b/public/lef new file mode 100644 index 00000000..e69de29b diff --git a/src/app/edit/[id]/page.tsx b/src/app/edit/[id]/page.tsx index d30fee4c..1090d79e 100644 --- a/src/app/edit/[id]/page.tsx +++ b/src/app/edit/[id]/page.tsx @@ -14,22 +14,7 @@ import { fetchWeather } from '@/services/ootd.ts/weather'; import { getCoordinatesFromAddress } from '@/constants/geocode'; import ImageChanger from '@/components/ootd/ImageChanger'; import Header from '@/components//shared/header/Header'; - -const weatherOptions = [ - { value: 'rain', label: '비' }, - { value: 'snow', label: '눈' }, - { value: 'mostly_cloudy', label: '구름많음' }, - { value: 'cloudy', label: '흐림' }, - { value: 'sunny', label: '맑음' }, - { value: 'unknown', label: '기억 안남' }, -]; - - -const temperatureOptions = Array.from({ length: 71 }, (_, i) => i - 20).map(temp => ({ - value: temp === -100 ? 'unknown' : temp.toString(), - label: temp === -100 ? '기억 안남' : `${temp}°C`, -})); - +import WeatherModal from '@/components/ootd/WeatherModal'; const EditOotd: React.FC = () => { const router = useRouter(); @@ -45,9 +30,10 @@ const EditOotd: React.FC = () => { const [weather, setWeather] = useState(null); const [postId, setPostId] = useState(null); const [ootdId, setOotdId] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); // 모달 상태 - // 상태 변경 여부를 추적하는 상태 const [hasChanges, setHasChanges] = useState(false); + const [isFirstLoad, setIsFirstLoad] = useState(true); const { data, isLoading, error } = useQuery(['ootdPostDetail', id], () => fetchOotdPostDetail(Number(id)) @@ -64,7 +50,6 @@ const EditOotd: React.FC = () => { } return dateString; // 형식이 다를 경우 원본 문자열 반환 }; - const formatDateForApi = (dateString: string | null) => { if (!dateString) return ''; // null이나 undefined일 경우 빈 문자열 반환 @@ -75,15 +60,12 @@ const EditOotd: React.FC = () => { const day = dateString.slice(6, 8); return `${year}-${month}-${day}`; }; - useEffect(() => { if (data && data.isSuccess) { const postData = data.result.post; const ootdData = data.result.ootd; - console.log(postData.images); - if (postData && ootdData) { setPostId(postData.id); setOotdId(ootdData.id); @@ -98,8 +80,6 @@ const EditOotd: React.FC = () => { avgTemp: ootdData.weatherTemp, }); - console.log(formatDateString(ootdData.date)); // 변환된 날짜 확인 - // 주소를 좌표로 변환하여 latitude와 longitude를 설정합니다. getCoordinatesFromAddress(postData.location).then(coordinates => { if (coordinates) { @@ -125,19 +105,15 @@ const EditOotd: React.FC = () => { }, [images]); useEffect(() => { - if (hasChanges && latitude !== null && longitude !== null && date) { + // 조건: latitude, longitude, date 값이 유효할 때만 날씨 정보를 불러옵니다. + if (!isFirstLoad && hasChanges && latitude !== null && longitude !== null && date && date.length === 8) { handleFetchWeather(); } - }, [hasChanges, latitude, longitude, date]); + }, [hasChanges, latitude, longitude, date]); // hasChanges 대신 date와 좌표 값을 직접 감시 - // useEffect(() => { - // if (data && data.isSuccess) { - // const postData = data.result.post; - // if (postData) { - // setImages(postData.images); // 상태 업데이트 - // } - // } - // }, [data]); + useEffect(() => { + setIsFirstLoad(false); // 첫 로드가 완료되면 상태를 false로 설정하여 이후에는 정상 동작 + }, []); const weatherMutation = useMutation( (variables: { latitude: number; longitude: number; date: string }) => @@ -146,67 +122,23 @@ const EditOotd: React.FC = () => { onSuccess: (data) => { setWeather(data.result); }, - onError: async (error) => { - console.error('Error fetching weather:', error); - - // 클라이언트에서만 실행되도록 useEffect 사용 - if (typeof window !== 'undefined') { - Swal.fire({ - icon: 'error', - title: '날씨 정보를 불러올 수 없습니다.', - text: '날씨와 온도를 직접 선택해주세요.', - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - allowOutsideClick: false, - preConfirm: async () => { - // 요소에 접근하려면 useEffect 안에서 지정합니다 - return new Promise((resolve) => { - Swal.fire({ - title: '날씨와 온도를 선택하세요', - html: ` - - - - - `, - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - cancelButtonText: '취소', - showCancelButton: true, - allowOutsideClick: false, - preConfirm: () => { - const weatherSelect = document.getElementById('weather-select') as HTMLSelectElement; - const temperatureSelect = document.getElementById('temperature-select') as HTMLSelectElement; - return { - weather: weatherSelect.value, - temperature: temperatureSelect.value, - }; - }, - }).then((result) => { - if (result.isConfirmed) { - const selected = result.value; - if (selected) { - setWeather({ - status: selected.weather, - avgTemp: selected.temperature === 'unknown' ? '정보 없음' : selected.temperature, - }); - } - resolve(); - } - }); - }); - }, - }); - } + onError: async () => { + // Swal로 먼저 경고 메시지를 띄우고 나서 커스텀 모달을 엽니다. + Swal.fire({ + icon: 'error', + title: '날씨 정보를 불러올 수 없습니다.', + text: '날씨와 온도를 직접 선택해주세요.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', + allowOutsideClick: false, + }).then(() => { + // Swal이 닫힌 후 커스텀 모달을 엽니다. + setIsModalOpen(true); + }); }, } ); - const handleFetchWeather = () => { if (latitude === null || longitude === null || !date) { Swal.fire({ @@ -321,7 +253,6 @@ const EditOotd: React.FC = () => { updatePostMutation.mutate({ postRequest, ootdRequest }); }; - const handleLocationChange = (locationData: { address: string; lat: number; lng: number }) => { setLocation(locationData.address); @@ -332,7 +263,13 @@ const EditOotd: React.FC = () => { const handleDateChange = (newDate: string) => { setDate(formatDateString(newDate)); - setHasChanges(true); + setHasChanges(true); // 날짜가 변경되었을 때 상태를 업데이트 + }; + const handleSaveWeather = (selectedWeather: string, temperature: string | null) => { + setWeather({ + status: selectedWeather, + avgTemp: temperature ? `${temperature}` : '정보 없음', + }); }; if (isLoading) { @@ -344,53 +281,63 @@ const EditOotd: React.FC = () => { } return ( - <>
-
- -
-
- -
- -
- - - {weather ? ( -
-
- {weather.avgTemp === '정보 없음' - ? '정보 없음' - : `${weather.avgTemp}°C, ${getWeatherStatusInKorean( - weather.status - )}`} -
+ <> +
+
+
+ +
+
+ +
+ +
+ + + {weather ? ( +
+
+ {weather.avgTemp === '정보 없음' + ? '정보 없음' + : `${weather.avgTemp}°C, ${getWeatherStatusInKorean( + weather.status + )}`} +
+
+ ) : ( + + )}
- ) : ( - - )} +
-
-
- ); -}; - -export default EditOotd; + + {/* Weather Modal 컴포넌트 */} + setIsModalOpen(false)} + onSave={handleSaveWeather} + /> + + ); + }; + + export default EditOotd; \ No newline at end of file diff --git a/src/components/ootd/CalendarModal.tsx b/src/components/ootd/CalendarModal.tsx index 4c474343..591a0803 100644 --- a/src/components/ootd/CalendarModal.tsx +++ b/src/components/ootd/CalendarModal.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { ko } from 'date-fns/locale'; @@ -18,99 +18,157 @@ const CalendarModal: React.FC = ({ }) => { if (!isOpen) return null; - const today = new Date(); // 오늘 날짜를 기준으로 + const today = new Date(); + const years = Array.from(new Array(50), (_, i) => today.getFullYear() - i); // 최근 50년간의 연도 + const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']; + + const [isYearDropdownOpen, setIsYearDropdownOpen] = useState(false); + const [isMonthDropdownOpen, setIsMonthDropdownOpen] = useState(false); + + // 선택된 날짜를 임시로 저장할 상태 + const [tempSelectedDate, setTempSelectedDate] = useState(selectedDate); + + // 날짜를 선택했을 때 tempSelectedDate에 저장 + const handleDateChange = (date: Date | null) => { + setTempSelectedDate(date); + }; + + // 선택 버튼을 눌렀을 때 모달을 닫고 최종적으로 상위 컴포넌트로 데이터를 전달 + const handleConfirm = () => { + onDateChange(tempSelectedDate); // 최종 선택된 날짜 전달 + onClose(); // 모달 닫기 + }; return (
-
- { - // 선택된 날짜에만 스타일 적용 - return selectedDate && date.getTime() === selectedDate.getTime() - ? 'react-datepicker__day--selected-custom' - : ''; - }} - renderCustomHeader={({ - date, - decreaseMonth, - increaseMonth, - prevMonthButtonDisabled, - nextMonthButtonDisabled, - }) => { - // 다음 달로 넘어갈 수 없도록 제어 - const isNextMonthDisabled = - date.getMonth() >= today.getMonth() && - date.getFullYear() >= today.getFullYear(); +
+ { + return tempSelectedDate && date.getTime() === tempSelectedDate.getTime() + ? 'react-datepicker__day--selected-custom' + : ''; + }} + renderCustomHeader={({ + date, + changeYear, + changeMonth, + decreaseMonth, + increaseMonth, + prevMonthButtonDisabled, + nextMonthButtonDisabled, + }) => { + const isNextMonthDisabled = + date.getMonth() >= today.getMonth() && date.getFullYear() >= today.getFullYear(); - return ( -
-
- + return ( +
+
+ + + {/* 커스텀 연도 선택 드롭다운 */} +
+ + {isYearDropdownOpen && ( +
+ {years.map((year) => ( +
{ + setIsYearDropdownOpen(false); + changeYear(year); // 여기서 changeYear 호출 + }} + > + {year}년 +
+ ))} +
+ )} +
-
- {date.getFullYear()}. {String(date.getMonth() + 1).padStart(2, '0')} -
+ {/* 커스텀 월 선택 드롭다운 */} +
+ + {isMonthDropdownOpen && ( +
+ {months.map((month, index) => ( +
{ + setIsMonthDropdownOpen(false); + changeMonth(index); // 여기서 changeMonth 호출 + }} + > + {month} +
+ ))} +
+ )} +
- +
+
+ ); + }} + /> + + {/* 선택 및 취소 버튼 */} +
+ +
- ); - }} -/> - -
); }; diff --git a/src/components/ootd/DateInput.tsx b/src/components/ootd/DateInput.tsx index e491ea63..fad61cec 100644 --- a/src/components/ootd/DateInput.tsx +++ b/src/components/ootd/DateInput.tsx @@ -12,12 +12,14 @@ interface DateInputProps { const DateInput: React.FC = ({ onDateChange, initialDate }) => { const [selectedDate, setSelectedDate] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); // 모달 상태 관리 + const [inputValue, setInputValue] = useState(''); // 입력된 값 관리 useEffect(() => { if (initialDate) { const parsedDate = parse(initialDate, 'yyyyMMdd', new Date()); if (isValid(parsedDate)) { setSelectedDate(parsedDate); + setInputValue(initialDate); // 초기 날짜 값 설정 } else { console.error('Invalid date format:', initialDate); } @@ -28,6 +30,7 @@ const DateInput: React.FC = ({ onDateChange, initialDate }) => { if (date) { const formattedDate = format(date, 'yyyyMMdd'); setSelectedDate(date); + setInputValue(formattedDate); // 입력 필드에 포맷된 날짜 설정 onDateChange(formattedDate); // 상위 컴포넌트에 변경된 날짜 전달 setIsModalOpen(false); // 날짜 선택 후 모달 닫기 } @@ -37,20 +40,40 @@ const DateInput: React.FC = ({ onDateChange, initialDate }) => { setIsModalOpen(true); // 모달 열기 }; + // 사용자가 직접 입력할 때 8자리 유효성 검사 및 처리 + const handleInputChange = (e: React.ChangeEvent) => { + const input = e.target.value; + // 숫자만 입력 가능하고 최대 8자리로 제한 + if (/^\d{0,8}$/.test(input)) { + setInputValue(input); + + if (input.length === 8) { + const parsedDate = parse(input, 'yyyyMMdd', new Date()); + if (isValid(parsedDate)) { + setSelectedDate(parsedDate); + onDateChange(input); // 유효한 8자리 날짜를 상위 컴포넌트에 전달 + } else { + console.error('Invalid date entered:', input); + } + } + } + }; + return ( -
+
Calendar Icon {/* 모달 컴포넌트 */} diff --git a/src/components/ootd/LocationInput.tsx b/src/components/ootd/LocationInput.tsx index feeda0b9..bd29e6ce 100644 --- a/src/components/ootd/LocationInput.tsx +++ b/src/components/ootd/LocationInput.tsx @@ -102,9 +102,9 @@ const LocationInput: React.FC = ({ onLocationChange, selecte
i - 20).map(temp => ({ - value: temp === -100 ? 'unknown' : temp.toString(), - label: temp === -100 ? '기억 안남' : `${temp}°C`, -})); +import Cookies from 'js-cookie'; +import Swal from 'sweetalert2'; // Swal 추가 +import WeatherModal from './WeatherModal'; // WeatherModal 컴포넌트 사용 const PostOotd: React.FC = () => { const [images, setImages] = useState([]); @@ -37,18 +26,19 @@ const PostOotd: React.FC = () => { const [longitude, setLongitude] = useState(0); const [date, setDate] = useState(''); const [weather, setWeather] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열림 상태 관리 const router = useRouter(); const userInfo = useUserStore((state) => state.userInfo); const userMemberId = userInfo?.memberId; useEffect(() => { - // Redirect to login if not authenticated - if (!userInfo) { - router.push("/login"); + const accessToken = Cookies.get('accessToken'); + if (!accessToken) { + router.push('/login'); } - }, [userInfo, router]); + }, [router]); - const dateInputRef = useRef(null); + const dateInputRef = useRef(null); const weatherMutation = useMutation( (variables: { latitude: number; longitude: number; date: string }) => @@ -57,69 +47,23 @@ const PostOotd: React.FC = () => { onSuccess: (data) => { setWeather(data.result); }, - onError: async (error) => { - console.error('Error fetching weather:', error); - - // 클라이언트에서만 실행되도록 useEffect 사용 - if (typeof window !== 'undefined') { - Swal.fire({ - icon: 'error', - title: '날씨 정보를 불러올 수 없습니다.', - text: '날씨와 온도를 직접 선택해주세요.', - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - allowOutsideClick: false, - preConfirm: async () => { - // 요소에 접근하려면 useEffect 안에서 지정합니다 - return new Promise((resolve) => { - Swal.fire({ - title: '날씨와 온도를 선택하세요', - html: ` - - - - - `, - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - cancelButtonText: '취소', - showCancelButton: true, - allowOutsideClick: false, - preConfirm: () => { - const weatherSelect = document.getElementById('weather-select') as HTMLSelectElement; - const temperatureSelect = document.getElementById('temperature-select') as HTMLSelectElement; - return { - weather: weatherSelect.value, - temperature: temperatureSelect.value, - }; - }, - }).then((result) => { - if (result.isConfirmed) { - const selected = result.value; - if (selected) { - setWeather({ - status: selected.weather, - avgTemp: selected.temperature === 'unknown' ? '정보 없음' : selected.temperature, - }); - } - resolve(); - } - }); - }) - }, - }); - } + onError: () => { + // Swal로 먼저 경고 메시지를 띄우고 나서 커스텀 모달을 엽니다. + Swal.fire({ + icon: 'error', + title: '날씨 정보를 불러올 수 없습니다.', + text: '날씨와 온도를 직접 선택해주세요.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', + allowOutsideClick: false, + }).then(() => { + // Swal이 닫힌 후 커스텀 모달을 엽니다. + setIsModalOpen(true); + }); }, } ); - console.log(latitude); - console.log(longitude); - const postMutation = useMutation( (variables: { postRequest: PostRequest; ootdRequest: OotdRequest }) => createPost(variables.postRequest, variables.ootdRequest), @@ -129,23 +73,18 @@ const PostOotd: React.FC = () => { icon: 'success', title: 'OOTD 게시글을 올렸습니다.', confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - customClass: { - popup: 'swal-custom-popup', - icon: 'swal-custom-icon' - } - }).then((result) => { - if (result.isConfirmed) { - router.push('/ootd'); - } + confirmButtonColor: '#FB3463', + }).then(() => { + router.push('/ootd'); }); }, onError: (error) => { - console.error('Error creating post:', error); Swal.fire({ icon: 'error', title: '게시글 올리기 오류', text: '게시글을 올리는 중 오류가 발생했습니다.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', }); }, } @@ -159,16 +98,13 @@ const PostOotd: React.FC = () => { const handleFetchWeather = () => { if (!location || date.length !== 8) { - if (typeof window !== 'undefined') { - Swal.fire({ - icon: 'error', - iconColor: '#FB3463', - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - title: '입력 오류', - text: '지역과 날짜를 모두 입력해주세요.', - }); - } + Swal.fire({ + icon: 'error', + title: '입력 오류', + text: '지역과 날짜를 모두 입력해주세요.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', + }); return; } setLatitude(latitude); @@ -184,53 +120,35 @@ const PostOotd: React.FC = () => { const handleCreatePost = () => { if (images.length === 0) { - if (typeof window !== 'undefined') { - Swal.fire({ - icon: 'error', - title: '이미지 오류', - text: '이미지를 업로드해주세요.', - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - customClass: { - popup: 'swal-custom-popup', - icon: 'swal-custom-icon' - } - }); - } + Swal.fire({ + icon: 'error', + title: '이미지 오류', + text: '이미지를 업로드해주세요.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', + }); return; } if (post.trim() === '') { - if (typeof window !== 'undefined') { - Swal.fire({ - icon: 'error', - title: '문구 작성 오류', - text: 'OOTD 문구를 작성해주세요.', - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - customClass: { - popup: 'swal-custom-popup', - icon: 'swal-custom-icon' - } - }); - } + Swal.fire({ + icon: 'error', + title: '문구 작성 오류', + text: 'OOTD 문구를 작성해주세요.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', + }); return; } if (tags.length < 3) { - if (typeof window !== 'undefined') { - Swal.fire({ - icon: 'error', - title: '태그 오류', - text: '태그를 3개 이상 등록해주세요.', - confirmButtonText: '확인', - confirmButtonColor: '#FB3463', - customClass: { - popup: 'swal-custom-popup', - icon: 'swal-custom-icon' - } - }); - } + Swal.fire({ + icon: 'error', + title: '태그 오류', + text: '태그를 3개 이상 등록해주세요.', + confirmButtonText: '확인', + confirmButtonColor: '#FB3463', + }); return; } @@ -247,7 +165,7 @@ const PostOotd: React.FC = () => { authenticateId: image.authenticateId, })), tags, - memberId: userMemberId + memberId: userMemberId, }; const ootdRequest: OotdRequest = { @@ -258,9 +176,6 @@ const PostOotd: React.FC = () => { date: formattedDate, }; - console.log('Sending postRequest:', JSON.stringify(postRequest, null, 2)); - console.log('Sending ootdRequest:', JSON.stringify(ootdRequest, null, 2)); - postMutation.mutate({ postRequest, ootdRequest }); }; @@ -275,14 +190,18 @@ const PostOotd: React.FC = () => { setDate(date); setWeather(null); }; + + // 모달에서 날씨와 온도 저장 + const handleSaveWeather = (selectedWeather: string, temperature: string | null) => { + setWeather({ + status: selectedWeather, + avgTemp: temperature ? `${temperature}°C` : '정보 없음', + }); + }; + return ( -
+
- {/* */} )}
+ + {/* 커스텀 모달 */} + setIsModalOpen(false)} onSave={handleSaveWeather} />
); }; -export default PostOotd; +export default PostOotd; \ No newline at end of file diff --git a/src/components/ootd/WeatherModal.tsx b/src/components/ootd/WeatherModal.tsx new file mode 100644 index 00000000..3caf1e25 --- /dev/null +++ b/src/components/ootd/WeatherModal.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; + +interface WeatherModalProps { + isOpen: boolean; + onClose: () => void; + onSave: (weather: string, temperature: string | null) => void; +} + +const weatherOptions = [ + { value: 'sunny', label: '맑음' }, + { value: 'rain', label: '비' }, + { value: 'snow', label: '눈' }, + { value: 'mostly_cloudy', label: '구름많음' }, + { value: 'cloudy', label: '흐림' }, +]; + +const WeatherModal: React.FC = ({ isOpen, onClose, onSave }) => { + const [selectedWeather, setSelectedWeather] = useState(weatherOptions[0].value); + const [temperature, setTemperature] = useState(null); + + if (!isOpen) return null; + + const handleSave = () => { + onSave(selectedWeather, temperature); + onClose(); + }; + + return ( +
+
+
날씨와 온도를 선택하세요
+ + {/* 날씨 상태 및 온도 입력란을 세로로 배치 */} +
+ + +
+ +
+ +
+ setTemperature(e.target.value)} + placeholder="" + /> + °C +
+
+ + {/* 버튼을 하단에 배치 */} +
+ + +
+
+
+ ); +}; + +export default WeatherModal; \ No newline at end of file diff --git a/src/styles/globals.css b/src/styles/globals.css index 1f49e28e..23bfc7b6 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -552,7 +552,8 @@ body { } .react-datepicker__day-name, .react-datepicker__day, .react-datepicker__time-name { - margin-right: 16px !important; /* 날짜와 시간 간격 조정 */ + margin-right: 8px !important; /* 날짜와 시간 간격 조정 */ + margin-left: 8px !important; margin-top: 6px !important; } @@ -629,4 +630,111 @@ body { .react-datepicker__day--keyboard-selected, .react-datepicker__month-text--keyboard-selected, .react-datepicker__quarter-text--keyboard-selected, .react-datepicker__year-text--keyboard-selected { background-color: transparent !important; color: inherit !important; +} + +.react-datepicker__day-names { + padding-bottom: 10px; +} + +.react-datepicker__day-name { + font-weight: 700; + font-size: 10px; +} + +.custom-icon { + color: #FB3463 !important; /* 아이콘의 색상을 변경 */ +} + +.swal2-error { + color: #FB3463 !important; + border-color: #FB3463 !important; +} + +.swal2-error [class^=swal2-x-mark-line] { + background-color: #FB3463 !important; +} + +/* WeatherModal.css */ +.weather-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.weather-modal-content { + background-color: white; + padding: 20px; + border-radius: 10px; + width: 350px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + text-align: center; +} + +.weather-modal-content h2 { + font-size: 1.2em; + font-weight: 600; + margin-bottom: 20px; +} + +.weather-modal-content .form-row { + width: 60%; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + margin: 10px auto; /* 중앙에 배치 */ +} + +.weather-modal-content label { + font-size: 1em; + font-weight: 500; + margin-right: 10px; +} + +.weather-modal-content select, +.weather-modal-content input { + padding: 8px; + font-size: 1em; + border: 1px solid #ddd; + border-radius: 4px; + width: 74px; /* 드롭다운과 입력 필드의 너비를 동일하게 설정 */ +} +.weather-modal-content .button-group { + display: flex; + justify-content: center; /* 버튼들을 중앙으로 정렬 */ + align-items: center; + gap: 20px; /* 버튼들 간의 간격을 20px로 설정 */ + margin: 0 auto; /* 버튼 그룹을 중앙에 배치 */ + margin-top: 20px; +} + +.weather-modal-content .button-group button { + padding: 10px 20px; + font-size: 1em; + border-radius: 4px; + border: none; + cursor: pointer; + min-width: 60px; /* 최소 너비 설정 */ +} + +.weather-modal-content .button-group button:first-child { + background-color: #fb3463; + color: white; +} + +.weather-modal-content .button-group button:last-child { + border: #cfcfcf; + border: 1px solid #cfcfcf; + color: #cfcfcf; +} + +#temperature-input { + width: 60px !important; /* 온도 입력 필드와 드롭다운 필드 크기를 동일하게 맞춤 */ } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ae6a20a5..b9f982a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -92,7 +92,7 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz" integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== -"@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.15", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.8", "@babel/runtime@^7.24.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.8", "@babel/runtime@^7.24.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.24.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz" integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== @@ -2376,11 +2376,6 @@ json5@^1.0.2: object.assign "^4.1.4" object.values "^1.1.6" -kakao.maps.d.ts@^0.1.39: - version "0.1.40" - resolved "https://registry.npmjs.org/kakao.maps.d.ts/-/kakao.maps.d.ts-0.1.40.tgz" - integrity sha512-nX69MB1ok04epe3OqS+/tEeWBbU31GSQbvDPJmQRRltzzqn6t4jBsO5v1nzalUjCKzwcH2CptOc767NZ7Hbu3g== - kdbush@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz" @@ -3068,14 +3063,6 @@ react-is@^17.0.2: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-kakao-maps-sdk@^1.1.27: - version "1.1.27" - resolved "https://registry.npmjs.org/react-kakao-maps-sdk/-/react-kakao-maps-sdk-1.1.27.tgz" - integrity sha512-1EwYkYsjTDRFqysKStDasFMrFTXcLx2AyRlqMoWD7ONWhRqpjx9M874hkhEEHrnypP2eSIhhDLe0EiSKp3bd2Q== - dependencies: - "@babel/runtime" "^7.22.15" - kakao.maps.d.ts "^0.1.39" - react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz" @@ -3589,15 +3576,10 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -sweetalert2-react-content@^5.0.7: - version "5.0.7" - resolved "https://registry.npmjs.org/sweetalert2-react-content/-/sweetalert2-react-content-5.0.7.tgz" - integrity sha512-8Fk82Mpk45lFXpJWKIFF/lq8k/dJKDDQGFcuqVosaL/qRdViyAs5+u37LoTGfnOIvf+rfQB3PAXcp1XLLn+0ew== - -sweetalert2@^11.0.0, sweetalert2@^11.10.8: - version "11.14.0" - resolved "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.0.tgz" - integrity sha512-kF1Q/+GtZZXr+rYVcBNwlEsnxP089CpDbck+MYjvLaQj9x4fzHqN9UhlkHOIR0k09LVu2sx8cU9BnvRlxWIZqg== +sweetalert2@^11.14.1: + version "11.14.1" + resolved "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.1.tgz" + integrity sha512-xadhfcA4STGMh8nC5zHFFWURhRpWc4zyI3GdMDFH/m3hGWZeQQNWhX9xcG4lI9gZYsi/IlazKbwvvje3juL3Xg== swiper@^11.1.14: version "11.1.14"