From e6d0e151ef7bc14e8ee3a3337112311e4ffa2813 Mon Sep 17 00:00:00 2001 From: karnelll <165611407+karnelll@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:25:53 +0900 Subject: [PATCH 1/5] Feature/detail fixes (#43) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] #40 핑 업데이티를 제외한 페이지 연결 및 api 연동 구현 * [feat] #40 eslint 해결 * #40 Update API connection * Fix build errors * Fix minor UI details and code formatting * [style] 스타일 수정 --------- Co-authored-by: choihooo --- .../[id]/load-mappin/components/Form.tsx | 2 +- .../components/EventNameInput.tsx | 29 +++++++------- .../components/LocationInput.tsx | 38 ++++++++++++------- .../components/SearchResultsItem.tsx | 2 +- fe/src/app/eventcreate-page/page.tsx | 5 ++- 5 files changed, 43 insertions(+), 33 deletions(-) diff --git a/fe/src/app/event-maps/[id]/load-mappin/components/Form.tsx b/fe/src/app/event-maps/[id]/load-mappin/components/Form.tsx index 1d080b6..741f6ff 100644 --- a/fe/src/app/event-maps/[id]/load-mappin/components/Form.tsx +++ b/fe/src/app/event-maps/[id]/load-mappin/components/Form.tsx @@ -36,7 +36,7 @@ export default function Form({ uuid }: FormProps) { try { const response = await fetch( - "http://110.165.17.236:8081/api/v1/nonmembers/pings", + `${process.env.NEXT_PUBLIC_API_BASE_URL}/nonmembers/pings`, { method: "POST", headers: { diff --git a/fe/src/app/eventcreate-page/components/EventNameInput.tsx b/fe/src/app/eventcreate-page/components/EventNameInput.tsx index c6409e8..314e12a 100644 --- a/fe/src/app/eventcreate-page/components/EventNameInput.tsx +++ b/fe/src/app/eventcreate-page/components/EventNameInput.tsx @@ -15,8 +15,8 @@ function EventNameInput({ className, selectedLocation, onChange, + value, }: EventNameInputProps) { - const [eventName, setEventName] = useState(""); const [isFocused, setIsFocused] = useState(false); const [hasUserEdited, setHasUserEdited] = useState(false); const [isLoading, setIsLoading] = useState(true); @@ -27,41 +27,38 @@ function EventNameInput({ const newEventName = selectedLocation ? `${currentDate} ${selectedLocation} 모임` : `${currentDate} 모임`; - setEventName(newEventName); onChange(newEventName); } - setIsLoading(false); // 로딩 완료 상태로 변경 + setIsLoading(false); }, [selectedLocation, currentDate, onChange, hasUserEdited]); const handleInputChange = (e: React.ChangeEvent) => { const newValue = e.target.value; - setEventName(newValue); setHasUserEdited(true); onChange(newValue); }; const handleClear = () => { - setEventName(""); setHasUserEdited(true); onChange(""); }; const borderClass = isFocused ? "border-[#2C2C2C]" : "border-transparent"; const textColorClass = - eventName === `${currentDate} 모임` || - eventName === `${currentDate} ${selectedLocation} 모임` + value === `${currentDate} 모임` || + value === `${currentDate} ${selectedLocation} 모임` ? "text-mediumGray" - : "text-text-default"; + : "text-[#8e8e8e]"; - const charCount = eventName.length; + const charCount = value.length; const showWarning = charCount < 1 || charCount > 20; const isDefaultValue = - eventName === `${currentDate} 모임` || - eventName === `${currentDate} ${selectedLocation} 모임`; + value === `${currentDate} 모임` || + value === `${currentDate} ${selectedLocation} 모임`; return (
-
+
이벤트 이름
@@ -72,14 +69,14 @@ function EventNameInput({ > setIsFocused(true)} onBlur={() => setIsFocused(false)} /> - {eventName && !isDefaultValue && ( + {value && !isDefaultValue && (
)} diff --git a/fe/src/app/eventcreate-page/components/LocationInput.tsx b/fe/src/app/eventcreate-page/components/LocationInput.tsx index a4a58a4..f945338 100644 --- a/fe/src/app/eventcreate-page/components/LocationInput.tsx +++ b/fe/src/app/eventcreate-page/components/LocationInput.tsx @@ -1,13 +1,24 @@ "use client"; import React, { useState } from "react"; -import { LocationInputProps, Place } from "@/app/eventcreate-page/types/types"; // 최상단으로 이동 import Image from "next/image"; import SearchResults from "./SearchResults"; +interface LocationInputProps { + className?: string; + onSelect: (place: { + name: string; + address: string; + px?: number; + py?: number; + }) => void; +} + function LocationInput({ className, onSelect }: LocationInputProps) { const [location, setLocation] = useState(""); - const [results, setResults] = useState([]); + const [results, setResults] = useState< + { name: string; address: string; px?: number; py?: number }[] + >([]); const [isFetching, setIsFetching] = useState(false); const fetchPlacesBySearch = async (query: string) => { @@ -45,7 +56,12 @@ function LocationInput({ className, onSelect }: LocationInputProps) { } }; - const handleSelectPlace = (place: Place) => { + const handleSelectPlace = (place: { + name: string; + address: string; + px?: number; + py?: number; + }) => { setLocation(place.name); setResults([]); onSelect(place); @@ -73,7 +89,7 @@ function LocationInput({ className, onSelect }: LocationInputProps) { const data = await response.json(); if (data.data && data.data.length > 0) { - const selectedPlace: Place = { ...data.data[0], px, py }; + const selectedPlace = { ...data.data[0], px, py }; setResults(data.data); setLocation(selectedPlace.name); handleSelectPlace(selectedPlace); @@ -93,15 +109,9 @@ function LocationInput({ className, onSelect }: LocationInputProps) { ); }; - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter" || e.key === " ") { - handleCurrentLocation(); - } - }; - return (
-
+
어떤 공간을 찾고 계신가요?
@@ -117,7 +127,7 @@ function LocationInput({ className, onSelect }: LocationInputProps) { value={location} onChange={handleSearch} placeholder="장소를 입력해주세요" - className="bg-transparent border-none grow text-base placeholder-mediumGray outline-none" + className="bg-transparent border-none grow text-base text-[#8e8e8e] placeholder-[#8e8e8e] outline-none font-['Pretendard']" />
{ + if (e.key === "Enter") handleCurrentLocation(); + }} > -
+
{highlightText(place.name, searchTerm)}
diff --git a/fe/src/app/eventcreate-page/page.tsx b/fe/src/app/eventcreate-page/page.tsx index e8a38d7..e5d4e1e 100644 --- a/fe/src/app/eventcreate-page/page.tsx +++ b/fe/src/app/eventcreate-page/page.tsx @@ -18,7 +18,6 @@ function EventCreatePage() { const [uuid, setUuid] = useState(null); const router = useRouter(); - // Adjust coordinates by dividing by 10^7 to match standard map coordinates const adjustedPx = px ? px / 1e7 : null; const adjustedPy = py ? py / 1e7 : null; @@ -26,6 +25,8 @@ function EventCreatePage() { setIsFormComplete( selectedLocation.trim() !== "" && eventName.trim() !== "" && + eventName.length >= 1 && + eventName.length <= 20 && adjustedPx !== null && adjustedPy !== null ); @@ -109,7 +110,7 @@ function EventCreatePage() { label={isSubmitting ? "처리 중..." : "다음"} type="start" onClick={createEvent} - className="w-[328px] h-[60px] py-[17px] rounded-lg" + className="w-[328px] h-[60px] py-[17px] rounded-lg text-base font-medium font-['Pretendard']" disabled={!isFormComplete || isSubmitting} />
From e198360df574d2fbf69725cfcbdbedfae511fa86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=ED=98=B8?= <67588757+choihooo@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:17:15 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[fix]=20api=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fe/src/app/event-maps/[id]/components/BottomDrawer.tsx | 2 +- fe/src/app/layout.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index cea2095..cd19635 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -102,7 +102,7 @@ export default function BottomDrawer({ // 특정 nonMemberId에 대한 핑 요청 try { const response = await fetch( - `http://110.165.17.236:8081/api/v1/nonmembers/pings/${nonMemberId}`, + `${process.env.NEXT_PUBLIC_API_BASE_URL}/nonmembers/pings/${nonMemberId}`, { method: "GET", headers: { "Content-Type": "application/json" } } ); diff --git a/fe/src/app/layout.tsx b/fe/src/app/layout.tsx index 6521acb..d6d650f 100644 --- a/fe/src/app/layout.tsx +++ b/fe/src/app/layout.tsx @@ -2,8 +2,8 @@ import "@/styles/globals.css"; import { ReactNode } from "react"; export const metadata = { - title: "My App", - description: "This is my Next.js app", + title: "Moping!", + description: "Moping", }; export default function RootLayout({ children }: { children: ReactNode }) { From 94501ecb5d8bd5c658c4729d9b4866ca2bbe0697 Mon Sep 17 00:00:00 2001 From: karnelll <165611407+karnelll@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:33:43 +0900 Subject: [PATCH 3/5] [HOTFIX] --- fe/src/app/event-maps/[id]/components/BottomDrawer.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index cd19635..2f4f341 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -43,6 +43,8 @@ export default function BottomDrawer({ "/profile/profil4.svg", ]); + const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL; // 환경 변수로부터 API URL 가져오기 + useEffect(() => { // 프로필 이미지 랜덤 할당 const profiles = nonMembers.reduce( @@ -63,9 +65,7 @@ export default function BottomDrawer({ // 전체 pings 데이터를 처음 로드할 때 가져옴 const fetchAllPings = async () => { try { - const response = await fetch( - `http://110.165.17.236:8081/api/v1/nonmembers/pings?uuid=${id}` - ); + const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); setAllPings(data.pings || []); @@ -76,7 +76,7 @@ export default function BottomDrawer({ } }; fetchAllPings(); - }, [id, setCustomMarkers]); + }, [apiUrl, id, setCustomMarkers]); const handleLocationClick = () => { if (navigator.geolocation) { @@ -102,7 +102,7 @@ export default function BottomDrawer({ // 특정 nonMemberId에 대한 핑 요청 try { const response = await fetch( - `${process.env.NEXT_PUBLIC_API_BASE_URL}/nonmembers/pings/${nonMemberId}`, + `${apiUrl}/nonmembers/pings/${nonMemberId}`, { method: "GET", headers: { "Content-Type": "application/json" } } ); From c6da24922f25ac6ab48bcea60cc61b989ef6d5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=ED=98=B8?= <67588757+choihooo@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:45:27 +0900 Subject: [PATCH 4/5] Feature/update api (#45) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] 업데이트 api 연결 * [feat] build 오류 해결 --- .../components/PincheckInput.tsx | 17 +++- .../load-mappin-edit/components/Form.tsx | 79 ++++++++++++++----- .../load-mappin-edit/components/LinkField.tsx | 13 ++- .../[nonMemberId]/load-mappin-edit/page.tsx | 5 +- .../[nonMemberId]/stores/useUserDataStore.ts | 23 ++++++ fe/src/app/event-maps/[id]/page.tsx | 1 - 6 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 fe/src/app/event-maps/[id]/[nonMemberId]/stores/useUserDataStore.ts diff --git a/fe/src/app/event-maps/[id]/[nonMemberId]/components/PincheckInput.tsx b/fe/src/app/event-maps/[id]/[nonMemberId]/components/PincheckInput.tsx index 9428341..8e8f30e 100644 --- a/fe/src/app/event-maps/[id]/[nonMemberId]/components/PincheckInput.tsx +++ b/fe/src/app/event-maps/[id]/[nonMemberId]/components/PincheckInput.tsx @@ -3,6 +3,7 @@ import React, { useRef, useState, useEffect, useCallback } from "react"; import { useRouter, useParams } from "next/navigation"; import { v4 as uuidv4 } from "uuid"; +import { useUserDataStore } from "../stores/useUserDataStore"; // zustand store import export default function PasswordInput() { const [password, setPassword] = useState(["", "", "", ""]); @@ -11,8 +12,8 @@ export default function PasswordInput() { const inputRefs = useRef<(HTMLInputElement | null)[]>([]); const router = useRouter(); const { id, nonMemberId } = useParams(); + const setUserData = useUserDataStore((state) => state.setUserData); // Use zustand's setUserData - // Define submitPassword before useEffect const submitPassword = useCallback(async () => { const fullPassword = password.join(""); @@ -38,6 +39,16 @@ export default function PasswordInput() { ); if (response.ok) { + const data = await response.json(); // Get the JSON response + + // Save data in zustand store + setUserData({ + nonMemberId: data.nonMemberId, + name: data.name, + bookmarkUrls: data.bookmarkUrls || [], + storeUrls: data.storeUrls || [], + }); + router.push(`/event-maps/${id}/${nonMemberId}/load-mappin-edit`); } else { setHasError(true); @@ -47,7 +58,7 @@ export default function PasswordInput() { } catch (error) { setHasError(true); } - }, [id, nonMemberId, password, router]); + }, [id, nonMemberId, password, router, setUserData]); useEffect(() => { if (password.every((digit) => digit !== "")) { @@ -98,7 +109,7 @@ export default function PasswordInput() {
{password.map((_, i) => (
([]); + const [storeLinks, setStoreLinks] = useState([]); const [isTooltipVisible, setIsTooltipVisible] = useState(true); - const [isFormComplete, setIsFormComplete] = useState(false); // isFormComplete 추가 + const [isFormComplete, setIsFormComplete] = useState(false); + const router = useRouter(); + const { id } = useParams(); // Retrieve `id` from the route parameters + + // Load mapLinks and storeLinks from the store when the component mounts + useEffect(() => { + if (userData) { + setMapLinks( + userData.bookmarkUrls?.filter((link) => link.trim() !== "") || [] + ); + setStoreLinks( + userData.storeUrls?.filter((link) => link.trim() !== "") || [] + ); + } + }, [userData]); - // mapLinks와 storeLinks가 모두 입력되었을 때만 isFormComplete를 true로 설정 + // Check if form is complete whenever mapLinks or storeLinks change useEffect(() => { setIsFormComplete( - mapLinks.some((link) => link.trim() !== "") && - storeLinks.some((link) => link.trim() !== "") + mapLinks.length > 0 && storeLinks.length > 0 // Check for non-empty lists ); }, [mapLinks, storeLinks]); - const handleSubmit = (e: FormEvent) => { + const handleSubmit = async (e: FormEvent) => { e.preventDefault(); + + // Filter out any empty strings just before submitting + const filteredMapLinks = mapLinks.filter((link) => link.trim() !== ""); + const filteredStoreLinks = storeLinks.filter((link) => link.trim() !== ""); + const formData = { - userName, - mapLinks, - storeLinks, + nonMemberId: userData?.nonMemberId, // Assuming nonMemberId is available in userData + bookmarkUrls: filteredMapLinks, + storeUrls: filteredStoreLinks, }; - console.log("폼 데이터:", formData); + + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_BASE_URL}/nonmembers/pings`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(formData), + } + ); + + if (response.ok) { + console.log("데이터가 성공적으로 전송되었습니다."); + router.push(`/event-maps/${id}`); // Redirect to the specified route on success + } else { + console.error("데이터 전송 실패:", response.status); + } + } catch (error) { + console.error("서버 오류 발생:", error); + } }; return ( @@ -38,7 +77,9 @@ export default function Form({ userName }: FormProps) { label="맵핀 모음 링크" placeholder="링크 붙여넣기" value={mapLinks} - onChange={setMapLinks} + onChange={(links) => + setMapLinks(links.filter((link) => link.trim() !== "")) + } showTooltip={isTooltipVisible} onInfoClick={() => setIsTooltipVisible(true)} /> @@ -47,7 +88,9 @@ export default function Form({ userName }: FormProps) { label="가게 정보 링크" placeholder="링크 붙여넣기" value={storeLinks} - onChange={setStoreLinks} + onChange={(links) => + setStoreLinks(links.filter((link) => link.trim() !== "")) + } />
)} -
+
{isModalOpen && ( diff --git a/fe/src/app/event-maps/[id]/[nonMemberId]/stores/useUserDataStore.ts b/fe/src/app/event-maps/[id]/[nonMemberId]/stores/useUserDataStore.ts new file mode 100644 index 0000000..a2ea29f --- /dev/null +++ b/fe/src/app/event-maps/[id]/[nonMemberId]/stores/useUserDataStore.ts @@ -0,0 +1,23 @@ +import { create } from "zustand"; + +interface UserData { + nonMemberId: number | null; + name: string; + bookmarkUrls: string[]; + storeUrls: string[]; +} + +interface UserDataStore { + userData: UserData; + setUserData: (data: UserData) => void; +} + +export const useUserDataStore = create((set) => ({ + userData: { + nonMemberId: null, + name: "", + bookmarkUrls: [], + storeUrls: [], + }, + setUserData: (data) => set({ userData: data }), +})); diff --git a/fe/src/app/event-maps/[id]/page.tsx b/fe/src/app/event-maps/[id]/page.tsx index ef3f8c6..a095f6e 100644 --- a/fe/src/app/event-maps/[id]/page.tsx +++ b/fe/src/app/event-maps/[id]/page.tsx @@ -65,7 +65,6 @@ export default function Page() { const result = await response.json(); setData(result); console.log("API Response Data:", JSON.stringify(result, null, 2)); - if (result.px && result.py) { console.log("Moving to location:", result.py, result.px); moveToLocation(result.py, result.px); From 7562a4942be20640f2173643088ab97bcf3ed234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=ED=98=B8?= <67588757+choihooo@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:22:41 +0900 Subject: [PATCH 5/5] Hotfix/map add profile (#46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] 업데이트 api 연결 * [feat] build 오류 해결 * [hotfix] 지도랑 프로필 추가 수정 * 빌드 오류 수정 * 리프레시 함수 추가 --- .../load-mappin-edit/components/Form.tsx | 15 +-- .../load-mappin-edit/components/LinkField.tsx | 58 +++++++-- .../[nonMemberId]/load-mappin-edit/page.tsx | 3 +- .../[id]/components/BottomDrawer.tsx | 122 +++++++++++++----- .../[id]/components/MapComponent.tsx | 2 +- .../[id]/load-mappin/components/Form.tsx | 10 +- .../[id]/load-mappin/components/NameField.tsx | 60 +++++++-- .../[id]/load-mappin/components/PinField.tsx | 4 + 8 files changed, 197 insertions(+), 77 deletions(-) diff --git a/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/Form.tsx b/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/Form.tsx index e970d80..38726a5 100644 --- a/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/Form.tsx +++ b/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/Form.tsx @@ -10,7 +10,6 @@ export default function Form() { const [mapLinks, setMapLinks] = useState([]); const [storeLinks, setStoreLinks] = useState([]); const [isTooltipVisible, setIsTooltipVisible] = useState(true); - const [isFormComplete, setIsFormComplete] = useState(false); const router = useRouter(); const { id } = useParams(); // Retrieve `id` from the route parameters @@ -26,13 +25,6 @@ export default function Form() { } }, [userData]); - // Check if form is complete whenever mapLinks or storeLinks change - useEffect(() => { - setIsFormComplete( - mapLinks.length > 0 && storeLinks.length > 0 // Check for non-empty lists - ); - }, [mapLinks, storeLinks]); - const handleSubmit = async (e: FormEvent) => { e.preventDefault(); @@ -94,13 +86,8 @@ export default function Form() { /> diff --git a/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/LinkField.tsx b/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/LinkField.tsx index d9b3477..76a82a5 100644 --- a/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/LinkField.tsx +++ b/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/components/LinkField.tsx @@ -1,3 +1,5 @@ +"use client"; + import React, { useState, useEffect } from "react"; import { nanoid } from "nanoid"; import Image from "next/image"; @@ -20,24 +22,37 @@ export default function LinkField({ showTooltip = true, onInfoClick, }: LinkFieldProps) { - const userData = useUserDataStore((state) => state.userData); // Use zustand's userData + const userData = useUserDataStore((state) => state.userData); const [inputFields, setInputFields] = useState( - value.map((val) => ({ id: nanoid(), text: val })) + value.length > 0 + ? value.map((val) => ({ id: nanoid(), text: val })) + : [{ id: nanoid(), text: "" }] ); useEffect(() => { - // Update input fields with data from zustand if available const initialData = label === "맵핀 모음 링크" ? userData.bookmarkUrls : userData.storeUrls; - setInputFields(initialData.map((val) => ({ id: nanoid(), text: val }))); + setInputFields( + initialData.length > 0 + ? initialData.map((val) => ({ id: nanoid(), text: val })) + : [{ id: nanoid(), text: "" }] + ); }, [label, userData]); + useEffect(() => { + if (inputFields.length === 0) { + setInputFields([{ id: nanoid(), text: "" }]); + } + }, [inputFields]); + const handleInputChange = (id: string, inputValue: string) => { const newInputs = inputFields.map((field) => field.id === id ? { ...field, text: inputValue } : field ); setInputFields(newInputs); - onChange(newInputs.map((field) => field.text)); + onChange( + newInputs.map((field) => field.text).filter((text) => text.trim() !== "") + ); }; const clearInput = (id: string) => { @@ -45,14 +60,24 @@ export default function LinkField({ field.id === id ? { ...field, text: "" } : field ); setInputFields(newInputs); - onChange(newInputs.map((field) => field.text)); + onChange( + newInputs.map((field) => field.text).filter((text) => text.trim() !== "") + ); }; const addInputField = () => { const newField = { id: nanoid(), text: "" }; const updatedInputs = [...inputFields, newField]; setInputFields(updatedInputs); - onChange(updatedInputs.map((field) => field.text)); + onChange( + updatedInputs + .map((field) => field.text) + .filter((text) => text.trim() !== "") + ); + }; + + const handleNaverMove = () => { + window.open("https://m.place.naver.com/my/place"); }; return ( @@ -76,15 +101,28 @@ export default function LinkField({ /> {showTooltip && (
즐겨찾기 링크 복사 방법을 확인해보세요 -
+
)}
)} +
{inputFields.map((field) => ( @@ -94,7 +132,7 @@ export default function LinkField({ value={field.text} onChange={(e) => handleInputChange(field.id, e.target.value)} placeholder={placeholder} - className="w-full p-3 bg-gray-50 rounded-md focus:outline-none focus:ring-2 focus:ring-grayscale-80" + className="w-full p-3 pr-10 bg-gray-50 rounded-md focus:outline-none focus:ring-2 focus:ring-grayscale-80" style={{ border: "none", }} diff --git a/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/page.tsx b/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/page.tsx index 00a8312..ab03631 100644 --- a/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/page.tsx +++ b/fe/src/app/event-maps/[id]/[nonMemberId]/load-mappin-edit/page.tsx @@ -8,7 +8,8 @@ import Form from "./components/Form"; import { useUserDataStore } from "../stores/useUserDataStore"; export default function Page() { - const userName = useUserDataStore((state) => state.userData.name); // Get userName from zustand + const userName = useUserDataStore((state) => state.userData.name); + const [isModalOpen, setIsModalOpen] = useState(false); const router = useRouter(); const { id } = useParams(); diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index 2f4f341..5c74ede 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -24,16 +24,18 @@ interface BottomDrawerProps { } export default function BottomDrawer({ - nonMembers, - eventName, + nonMembers: initialNonMembers, + eventName: initialEventName, id, }: BottomDrawerProps) { + const [eventName, setEventName] = useState(initialEventName); const [selectedButton, setSelectedButton] = useState(null); + const [nonMembers, setNonMembers] = useState(initialNonMembers); const [memberProfiles, setMemberProfiles] = useState<{ [key: number]: string; }>({}); const [allPings, setAllPings] = useState([]); - const { setCustomMarkers } = useMarkerStore(); // useMarkerStore에서 setCustomMarkers 가져오기 + const { setCustomMarkers } = useMarkerStore(); const moveToLocation = useLocationStore((state) => state.moveToLocation); const router = useRouter(); const profileImagesRef = useRef([ @@ -43,10 +45,9 @@ export default function BottomDrawer({ "/profile/profil4.svg", ]); - const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL; // 환경 변수로부터 API URL 가져오기 + const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL; useEffect(() => { - // 프로필 이미지 랜덤 할당 const profiles = nonMembers.reduce( (acc, member) => { const randomImage = @@ -62,14 +63,13 @@ export default function BottomDrawer({ }, [nonMembers]); useEffect(() => { - // 전체 pings 데이터를 처음 로드할 때 가져옴 const fetchAllPings = async () => { try { const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); setAllPings(data.pings || []); - setCustomMarkers(data.pings || []); // 모든 핑을 setCustomMarkers에 설정 + setCustomMarkers(data.pings || []); } } catch (error) { console.log("Error:", error); @@ -91,15 +91,12 @@ export default function BottomDrawer({ }; const handleButtonClick = async (nonMemberId: number) => { - // 선택된 버튼 토글 const isDeselect = selectedButton === nonMemberId; setSelectedButton(isDeselect ? null : nonMemberId); if (isDeselect) { - // 선택 해제 시 전체 핑 표시 setCustomMarkers(allPings); } else { - // 특정 nonMemberId에 대한 핑 요청 try { const response = await fetch( `${apiUrl}/nonmembers/pings/${nonMemberId}`, @@ -110,10 +107,9 @@ export default function BottomDrawer({ const data = await response.json(); const filteredPings = data.pings.map((ping: Ping) => ({ ...ping, - iconLevel: 1, // 선택된 nonMember에 대한 핑을 level1로 설정 + iconLevel: 1, })); - console.log(filteredPings); - setCustomMarkers(filteredPings); // 필터링된 핑을 setCustomMarkers에 설정 + setCustomMarkers(filteredPings); } else { console.log("Failed to fetch data:", response.status); } @@ -141,8 +137,64 @@ export default function BottomDrawer({ } }; + const handleRefresh = async () => { + try { + const response = await fetch( + `${apiUrl}/nonmembers/pings/refresh-all?uuid=${id}`, + { + method: "GET", + headers: { "Content-Type": "application/json" }, + } + ); + + if (response.ok) { + const data = await response.json(); + console.log(data); + setEventName(data.eventName); + setNonMembers(data.nonMembers); + setAllPings(data.pings || []); + setCustomMarkers(data.pings || []); + } else { + console.log("Failed to fetch refreshed data:", response.status); + } + } catch (error) { + console.log("Error refreshing data:", error); + } + }; + + const handleDrawerClick = ( + event: + | React.MouseEvent + | React.KeyboardEvent + ) => { + if (event.type === "keydown") { + const keyboardEvent = event as React.KeyboardEvent; + if (keyboardEvent.key === "Enter" || keyboardEvent.key === " ") { + setSelectedButton(null); + setCustomMarkers(allPings); + } + } else if (event.type === "click") { + const mouseEvent = event as React.MouseEvent; + const target = mouseEvent.target as HTMLElement; + if (!target.closest("button")) { + setSelectedButton(null); + setCustomMarkers(allPings); + } + } + }; + return ( -
+
{ + if (e.key === "Enter" || e.key === " ") { + handleDrawerClick(e); + } + }} + >
+ {selectedButton !== null ? ( + + ) : ( + + )}
@@ -210,11 +270,7 @@ export default function BottomDrawer({