diff --git a/app/join/[id]/page.tsx b/app/join/[id]/page.tsx index 56388a1..a804c85 100644 --- a/app/join/[id]/page.tsx +++ b/app/join/[id]/page.tsx @@ -1,166 +1,48 @@ -'use client'; - -import { useRouter, useParams } from 'next/navigation'; -import { useState, useEffect } from 'react'; -import { useEnterParticipant } from '@/hooks/api/mutation/useEnterParticipant'; -import { useToast } from '@/hooks/useToast'; -import Toast from '@/components/ui/toast'; -import { useIsLoggedIn } from '@/hooks/useIsLoggedIn'; -import { setMeetingUserId } from '@/lib/storage'; - -export default function Page() { - const params = useParams(); - const meetingId = params?.id as string; - const router = useRouter(); - - const { isLogin, isChecking } = useIsLoggedIn(meetingId); // ⭐ meetingId 전달 - - const [name, setName] = useState(''); - const [password, setPassword] = useState(''); - const [isRemembered, setIsRemembered] = useState(true); - const [errorMessage, setErrorMessage] = useState(''); - - const participantEnter = useEnterParticipant(); - const { isVisible, show } = useToast(); - - useEffect(() => { - if (isChecking) return; - - if (isLogin && meetingId) { - router.replace(`/meeting/${meetingId}`); - } - }, [isChecking, isLogin, meetingId, router]); - - if (isChecking || isLogin) { - return ( -
-
-

로그인 정보를 확인 중...

-
- ); - } - - const isFormValid = name.length > 0 && password.length === 4; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - if (!isFormValid || !meetingId) return; - - try { - const result = await participantEnter.mutateAsync({ - meetingId, - data: { - userId: name, - password, - }, - }); - - if (result.success) { - // ⭐ 모임별로 분리된 스토리지에 저장 - setMeetingUserId(meetingId, name, isRemembered); - - router.push(`/meeting/${meetingId}`); - } else { - setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.'); - show(); - } - } catch { - setErrorMessage('모임 참여에 실패했습니다. 이름과 비밀번호를 확인해주세요.'); - show(); - } +import type { Metadata, ResolvingMetadata } from 'next'; +import JoinForm from '@/components/join/joinForm'; + +type Props = { + params: Promise<{ id: string }>; + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}; + +export async function generateMetadata( + { params, searchParams }: Props, + parent: ResolvingMetadata +): Promise { + const { id } = await params; + const { view } = await searchParams; + + // 부모(layout.tsx)의 이미지 정보 가져오기 + const previousImages = (await parent).openGraph?.images || []; + + // 상황별 텍스트 및 이미지 설정 + const isNudge = view === 'nudge'; + + const title = isNudge ? '모임원들이 결과를 기다리고 있어요!' : '모임에 참여하세요!'; + + // 쿼리스트링에 따라 이미지 URL만 변경 + const imageUrl = + view === 'nudge' + ? '/images/og-image/nudge_meeting_card.jpg' // 재촉하기 이미지 + : '/images/og-image/share_meeting_card.jpg'; // 기본 초대 이미지 + + return { + title, + openGraph: { + title, + url: `https://www.mingling.kr/join/${id}`, // 실제 도메인으로 변경 권장 + images: [imageUrl, ...previousImages], + }, + twitter: { + card: 'summary_large_image', + title, + images: [imageUrl], + }, }; +} - return ( -
-

- 모임에 참여해 주세요. -

- -
-
- - setName(e.target.value)} - placeholder="최대 20자 이내로 입력해주세요" - maxLength={20} - className="border-gray-2 placeholder:text-gray-3 text-gray-10 focus:border-blue-5 w-full rounded-sm border py-2 pl-3 text-[15px] focus:bg-white focus:outline-none" - /> -
- -
- - { - const val = e.target.value.replace(/[^0-9]/g, ''); - if (val.length <= 4) setPassword(val); - }} - placeholder="숫자 4자리를 입력해주세요" - className={`border-gray-2 placeholder:text-gray-3 text-gray-10 focus:border-blue-5 w-full rounded-sm border py-2 pl-3 text-center text-[15px] focus:bg-white focus:outline-none ${password ? 'pl-0 text-center' : 'pl-3 text-left'}`} - /> - -
setIsRemembered(!isRemembered)} - className="flex cursor-pointer items-center gap-2" - > -
- {isRemembered && ( - - - - )} -
- - 내 정보 기억하기 - -
-
- -
- - -
-
-
- ); +export default async function Page({ params }: Props) { + const { id } = await params; + return ; } diff --git a/app/layout.tsx b/app/layout.tsx index f4273a8..b0bdf7e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -24,6 +24,7 @@ const pretendard = localFont({ }); export const metadata: Metadata = { + metadataBase: new URL(process.env.NEXT_PUBLIC_BASE_URL || 'https://www.mingling.kr'), title: '밍글링 - 어디서 만날지, 고민 시간을 줄여드려요', description: '퇴근 후 모임, 주말 약속까지. 서울 어디서든 모두가 비슷하게 도착하는 마법의 장소를 찾아드려요.', diff --git a/app/meeting/[id]/page.tsx b/app/meeting/[id]/page.tsx index e47b776..1ce386e 100644 --- a/app/meeting/[id]/page.tsx +++ b/app/meeting/[id]/page.tsx @@ -239,7 +239,7 @@ export default function Page() { )} @@ -282,7 +282,7 @@ export default function Page() { /> -
+
{allParticipants.length > 0 ? ( allParticipants.map((user) => ( @@ -314,7 +314,7 @@ export default function Page() { diff --git a/app/opengraph-image.jpg b/app/opengraph-image.jpg new file mode 100644 index 0000000..cb6c4e3 Binary files /dev/null and b/app/opengraph-image.jpg differ diff --git a/app/page.tsx b/app/page.tsx index 70ce479..b2c75ac 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -57,7 +57,7 @@ const PainPointTooltip = ({ text }: { text: string }) => ( // 2. 반복되는 특징 카드 컴포넌트 const FeatureCard = ({ title, desc, desc2 }: { title: string; desc: string; desc2: string }) => ( -
+

{title}

@@ -77,16 +77,19 @@ const FeatureCard = ({ title, desc, desc2 }: { title: string; desc: string; desc const HeroSection = () => (
-

+

모임 장소 선정, 출발역만 넣으면 끝!

-
-

- 참석자들이 지하철 출발역을 입력하면, -

-

- 이동시간과 편차를 분석해 서울 내 최적의 번화가를 - 추천합니다 + +

+

+ 참석자들이 지하철 출발역을 입력하면, +
+   + 이동시간과 편차를 고려해 +
+   + 서울 내 최적의 번화가를 추천합니다

@@ -97,7 +100,7 @@ const HeroSection = () => ( 모임 만들기 - {/* Responsive Images */} + {/* Responsive Images (기존 코드 유지) */}
-

- 모임이 만들어졌어요! -
- 링크를 공유해주세요 -

- -
- 모임이 만들어졌어요 -
- -
- - - -
+import type { Metadata } from 'next'; +import ShareContent from '@/components/share/shareContent'; + +type Props = { + params: Promise<{ id: string }>; +}; + +export async function generateMetadata({ params }: Props): Promise { + const { id } = await params; + + return { + title: '모임이 만들어졌어요! 🎉', + description: '친구들에게 링크를 공유하고 출발지를 받아보세요.', + openGraph: { + title: '모임이 만들어졌어요! 🎉', + description: '친구들에게 링크를 공유하고 출발지를 받아보세요.', + images: ['/images/og-image/create_meeting_card.jpg'], + url: `https://www.mingling.kr/share/${id}`, + }, + twitter: { + card: 'summary_large_image', + title: '모임이 만들어졌어요! 🎉', + description: '친구들에게 링크를 공유하고 출발지를 받아보세요.', + images: ['/images/og-image/create_meeting_card.jpg'], + }, + }; +} - - 내 출발지 등록하기 - -
- ); +export default async function Page({ params }: Props) { + const { id } = await params; + return ; } diff --git a/components/footer.tsx b/components/footer.tsx index ef61de3..f467239 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -24,26 +24,26 @@ const Footer = () => {
- + 문의하기 - + */}
- {/* 쓰레드, 인스타그램 아이콘 렌더링 */} -
+ {/* 쓰레드, 인스타그램 아이콘 렌더링 숨김 */} + {/*
{ICON.map((item, idx) => ( {`${item} ))} -
+
*/}
diff --git a/components/header.tsx b/components/header.tsx index 1db323e..c6021e2 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -12,7 +12,10 @@ const Header = () => {