From 3f429fcd9f8def6b840bb5db51cdd4fb643fbeae Mon Sep 17 00:00:00 2001 From: rina1201 Date: Sat, 2 May 2026 20:26:17 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=BA=A1=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C,=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=A7=81=EC=A0=91=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/router/index.tsx | 2 + .../reels/InpersonSearchResultCard.tsx | 53 +++++ src/pages/RegisterPlaceInpersonPage.tsx | 182 ++++++++++++++++++ src/store/inpersonPlaceStore.ts | 21 ++ 4 files changed, 258 insertions(+) create mode 100644 src/components/reels/InpersonSearchResultCard.tsx create mode 100644 src/pages/RegisterPlaceInpersonPage.tsx create mode 100644 src/store/inpersonPlaceStore.ts diff --git a/src/app/router/index.tsx b/src/app/router/index.tsx index 200bb23..a932898 100644 --- a/src/app/router/index.tsx +++ b/src/app/router/index.tsx @@ -14,6 +14,7 @@ import { mapHomeLoader } from "@/pages/map/map-home-loader"; import NicknamePage from "@/pages/onboarding/NicknamePage"; import TermsAgreementPage from "@/pages/onboarding/TermsAgreementPage"; import ReelsPlaceSelectPage from "@/pages/ReelsPlaceSelectPage"; +import RegisterPlaceInpersonPage from "@/pages/RegisterPlaceInpersonPage"; import RegisterSelectRoomPage from "@/pages/RegisterSelectRoomPage"; import SplashScreenPage from "@/pages/SplashScreenPage"; @@ -34,6 +35,7 @@ export const router = createBrowserRouter([ { path: "dev/SelectOption", element: }, { path: "dev/register_place", element: }, { path: "edit_place", element: }, + { path: "register-place-inperson", element: }, { path: "register-select-room", element: }, { path: "login", element: }, { path: "app", element: }, diff --git a/src/components/reels/InpersonSearchResultCard.tsx b/src/components/reels/InpersonSearchResultCard.tsx new file mode 100644 index 0000000..0404e0b --- /dev/null +++ b/src/components/reels/InpersonSearchResultCard.tsx @@ -0,0 +1,53 @@ +import { Check } from "lucide-react"; + +import { cn } from "@/lib/utils"; +import type { SavedPlace } from "@/shared/types/map-home"; + +type InpersonSearchResultCardProps = { + place: SavedPlace; + selected: boolean; + onSelect: () => void; +}; + +export function InpersonSearchResultCard({ + place, + selected, + onSelect, +}: InpersonSearchResultCardProps) { + return ( +
  • + +
  • + ); +} diff --git a/src/pages/RegisterPlaceInpersonPage.tsx b/src/pages/RegisterPlaceInpersonPage.tsx new file mode 100644 index 0000000..fce0241 --- /dev/null +++ b/src/pages/RegisterPlaceInpersonPage.tsx @@ -0,0 +1,182 @@ +import { ChevronLeft, Search } from "lucide-react"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; + +import { InpersonSearchResultCard } from "@/components/reels/InpersonSearchResultCard"; +import { cn } from "@/lib/utils"; +import { SAVED_PLACE_MOCKS } from "@/pages/map/map-home-mock"; +import { useInpersonPlaceStore } from "@/store/inpersonPlaceStore"; +import { useRegisterRoomStore } from "@/store/registerRoomStore"; + +const REELS_LINK_MOCK = + "https://www.instagram.com/reel/DNp9tqSz6rT/?igsh=MW4yOGd6aGNzMmRsYw=="; + +export default function RegisterPlaceInpersonPage() { + const navigate = useNavigate(); + const keyword = useInpersonPlaceStore((state) => state.keyword); + const selectedPlaceId = useInpersonPlaceStore((state) => state.selectedPlaceId); + const setKeyword = useInpersonPlaceStore((state) => state.setKeyword); + const setSelectedPlace = useInpersonPlaceStore((state) => state.setSelectedPlace); + const reset = useInpersonPlaceStore((state) => state.reset); + const setSelectedPlacesForRegister = useRegisterRoomStore((state) => state.setSelectedPlaces); + const [copyLabel, setCopyLabel] = useState("복사"); + const [isSearchMode, setIsSearchMode] = useState(false); + + useEffect(() => { + reset(); + }, [reset]); + + const trimmedKeyword = keyword.trim(); + const canConfirm = selectedPlaceId !== null; + + const searchResults = useMemo(() => { + if (!trimmedKeyword) { + return []; + } + + return SAVED_PLACE_MOCKS.filter((place) => place.name.includes(trimmedKeyword)); + }, [trimmedKeyword]); + + const selectedPlace = useMemo( + () => SAVED_PLACE_MOCKS.find((place) => place.id === selectedPlaceId) ?? null, + [selectedPlaceId], + ); + + const handleCopy = useCallback(async () => { + try { + await navigator.clipboard.writeText(REELS_LINK_MOCK); + setCopyLabel("복사됨"); + window.setTimeout(() => setCopyLabel("복사"), 1500); + } catch { + setCopyLabel("실패"); + window.setTimeout(() => setCopyLabel("복사"), 1500); + } + }, []); + + const returnToInitialScreen = () => { + setKeyword(""); + setSelectedPlace(null); + setIsSearchMode(false); + }; + + const handleCancel = () => { + reset(); + setIsSearchMode(false); + navigate(-1); + }; + + const handleConfirm = () => { + if (!selectedPlaceId) { + return; + } + + setSelectedPlacesForRegister([selectedPlaceId]); + navigate("/register-select-room", { + state: { + selectedPlaceIds: [selectedPlaceId], + selectedPlaceCount: 1, + selectedPlace, + }, + }); + }; + + return ( +
    +
    +
    +
    +

    + 장소 인식에 실패했습니다. +

    +

    + 해당 장소를 직접 입력해주세요. +

    +
    + + {!isSearchMode ? ( +
    +

    {REELS_LINK_MOCK}

    + +
    + ) : null} + + +
    +
    + +
    + {isSearchMode ? ( +
      + {searchResults.map((place) => ( + setSelectedPlace(place.id)} + /> + ))} +
    + ) : null} +
    + +
    +
    + + +
    +
    +
    + ); +} diff --git a/src/store/inpersonPlaceStore.ts b/src/store/inpersonPlaceStore.ts new file mode 100644 index 0000000..f1804f8 --- /dev/null +++ b/src/store/inpersonPlaceStore.ts @@ -0,0 +1,21 @@ +import { create } from "zustand"; + +type InpersonPlaceState = { + keyword: string; + selectedPlaceId: string | null; + setKeyword: (keyword: string) => void; + setSelectedPlace: (placeId: string | null) => void; + reset: () => void; +}; + +export const useInpersonPlaceStore = create((set) => ({ + keyword: "", + selectedPlaceId: null, + setKeyword: (keyword) => set({ keyword }), + setSelectedPlace: (placeId) => set({ selectedPlaceId: placeId }), + reset: () => + set({ + keyword: "", + selectedPlaceId: null, + }), +})); From aded09c9daeb33aef5b67f10c824d886cf0d0076 Mon Sep 17 00:00:00 2001 From: 1000hyehyang Date: Sat, 2 May 2026 21:54:11 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=EC=A7=81=EC=A0=91=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20EditPlace?= =?UTF-8?q?=EC=99=80=20UI=C2=B7=EA=B2=80=EC=83=89=20=ED=8C=A8=ED=84=B4=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reels/InpersonSearchResultCard.tsx | 53 ------- src/pages/RegisterPlaceInpersonPage.tsx | 130 ++++++++---------- 2 files changed, 59 insertions(+), 124 deletions(-) delete mode 100644 src/components/reels/InpersonSearchResultCard.tsx diff --git a/src/components/reels/InpersonSearchResultCard.tsx b/src/components/reels/InpersonSearchResultCard.tsx deleted file mode 100644 index 0404e0b..0000000 --- a/src/components/reels/InpersonSearchResultCard.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Check } from "lucide-react"; - -import { cn } from "@/lib/utils"; -import type { SavedPlace } from "@/shared/types/map-home"; - -type InpersonSearchResultCardProps = { - place: SavedPlace; - selected: boolean; - onSelect: () => void; -}; - -export function InpersonSearchResultCard({ - place, - selected, - onSelect, -}: InpersonSearchResultCardProps) { - return ( -
  • - -
  • - ); -} diff --git a/src/pages/RegisterPlaceInpersonPage.tsx b/src/pages/RegisterPlaceInpersonPage.tsx index fce0241..81cafea 100644 --- a/src/pages/RegisterPlaceInpersonPage.tsx +++ b/src/pages/RegisterPlaceInpersonPage.tsx @@ -1,10 +1,10 @@ -import { ChevronLeft, Search } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { InpersonSearchResultCard } from "@/components/reels/InpersonSearchResultCard"; -import { cn } from "@/lib/utils"; -import { SAVED_PLACE_MOCKS } from "@/pages/map/map-home-mock"; +import { SearchField } from "@/components/common/SearchField"; +import { EditPlaceResultCard } from "@/components/reels/EditPlaceResultCard"; +import { PillButton } from "@/components/ui/PillButton"; +import { SAVED_PLACE_MOCKS } from "@/shared/mocks/place-mocks"; import { useInpersonPlaceStore } from "@/store/inpersonPlaceStore"; import { useRegisterRoomStore } from "@/store/registerRoomStore"; @@ -20,13 +20,13 @@ export default function RegisterPlaceInpersonPage() { const reset = useInpersonPlaceStore((state) => state.reset); const setSelectedPlacesForRegister = useRegisterRoomStore((state) => state.setSelectedPlaces); const [copyLabel, setCopyLabel] = useState("복사"); - const [isSearchMode, setIsSearchMode] = useState(false); useEffect(() => { reset(); }, [reset]); const trimmedKeyword = keyword.trim(); + const canSearch = trimmedKeyword.length > 0; const canConfirm = selectedPlaceId !== null; const searchResults = useMemo(() => { @@ -34,7 +34,9 @@ export default function RegisterPlaceInpersonPage() { return []; } - return SAVED_PLACE_MOCKS.filter((place) => place.name.includes(trimmedKeyword)); + return SAVED_PLACE_MOCKS.filter( + (place) => place.name.includes(trimmedKeyword) || place.address.includes(trimmedKeyword), + ); }, [trimmedKeyword]); const selectedPlace = useMemo( @@ -53,15 +55,8 @@ export default function RegisterPlaceInpersonPage() { } }, []); - const returnToInitialScreen = () => { - setKeyword(""); - setSelectedPlace(null); - setIsSearchMode(false); - }; - const handleCancel = () => { reset(); - setIsSearchMode(false); navigate(-1); }; @@ -82,7 +77,7 @@ export default function RegisterPlaceInpersonPage() { return (
    -
    +

    - {!isSearchMode ? ( -
    -

    {REELS_LINK_MOCK}

    - -
    - ) : null} +
    +

    {REELS_LINK_MOCK}

    + +
    - + + {trimmedKeyword ? ( +
      + {searchResults.length === 0 ? ( +
    • + 검색 결과가 없습니다 +
    • + ) : ( + searchResults.map((place) => ( + setSelectedPlace(place.id)} + /> + )) + )} +
    + ) : null}
    -
    - -
    - {isSearchMode ? ( -
      - {searchResults.map((place) => ( - setSelectedPlace(place.id)} - /> - ))} -
    - ) : null}
    -
    - - +
    From a3655550f6d9876c03827d701dfe820d682a47dd Mon Sep 17 00:00:00 2001 From: 1000hyehyang Date: Sat, 2 May 2026 21:55:22 +0900 Subject: [PATCH 3/3] fix: npm run format --- src/pages/RegisterPlaceInpersonPage.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/RegisterPlaceInpersonPage.tsx b/src/pages/RegisterPlaceInpersonPage.tsx index 81cafea..5839de9 100644 --- a/src/pages/RegisterPlaceInpersonPage.tsx +++ b/src/pages/RegisterPlaceInpersonPage.tsx @@ -8,8 +8,7 @@ import { SAVED_PLACE_MOCKS } from "@/shared/mocks/place-mocks"; import { useInpersonPlaceStore } from "@/store/inpersonPlaceStore"; import { useRegisterRoomStore } from "@/store/registerRoomStore"; -const REELS_LINK_MOCK = - "https://www.instagram.com/reel/DNp9tqSz6rT/?igsh=MW4yOGd6aGNzMmRsYw=="; +const REELS_LINK_MOCK = "https://www.instagram.com/reel/DNp9tqSz6rT/?igsh=MW4yOGd6aGNzMmRsYw=="; export default function RegisterPlaceInpersonPage() { const navigate = useNavigate(); @@ -125,7 +124,7 @@ export default function RegisterPlaceInpersonPage() { {trimmedKeyword ? ( -
      +
        {searchResults.length === 0 ? (
      • 검색 결과가 없습니다