Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes
File renamed without changes
Binary file removed public/images/introduce1.png
Binary file not shown.
Binary file removed public/images/introduce2.png
Binary file not shown.
Binary file removed public/images/introduce3.png
Binary file not shown.
Binary file added public/images/landing/landing-create.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/landing/landing-create2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/landing/landing-moim.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/landing/landing-signup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
Binary file removed public/images/long-card.png
Binary file not shown.
Binary file removed public/images/rainbow.webp
Binary file not shown.
Binary file removed public/images/running.png
Binary file not shown.
Binary file added public/video/main-video.webm
Binary file not shown.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import FAQItem from '@/components/landing/FAQ/FAQItem';
import FAQItem from '@/components/FAQ/FAQItem';
import { FAQS } from '@/constants/faq';

const DEFAULT_OPEN_IDX = 0;
Expand Down
66 changes: 66 additions & 0 deletions src/components/Landing/LandingFeature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useCallback, useRef, useState } from 'react';
import { useInView } from 'framer-motion';
import * as m from 'framer-motion/m';
import Image from 'next/image';
import { LANDING_BUTTONS } from '@/constants/landing';

export default function LandingFeature() {
const [selectedImage, setSelectedImage] = useState<string>(LANDING_BUTTONS[0].image);

const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true });

const handleButtonClick = useCallback((image: string) => {
setSelectedImage(image);
}, []);

return (
<section className="relative min-h-screen select-none">
<div className="absolute left-1/2 top-1/2 w-full -translate-x-1/2 -translate-y-1/2">
<m.article
ref={ref}
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 1.2, delay: 0.2 }}
>
<header className="text-center">
<h2 className="text-landing-title font-bold leading-[100%] text-black">
만취에서 쉽고 <span className="text-primary-400">빠르게 모임</span>을 만들고,
<br />
<span className="text-primary-400">다양한 활동</span>에 참여하세요.
</h2>
<p className="mt-4 text-balance text-center text-16-20-response font-medium">몇번의 클릭만으로 당신의 관심사를 함께할 사람들을 만나보세요.</p>
</header>
</m.article>
<m.section
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 1.2, delay: 0.5 }}
className="mx-auto mt-5 flex w-full max-w-[1200px] justify-center mobile:mt-10 tablet:mt-20"
>
<nav className="relative flex h-[350px] max-w-[510px] flex-1 flex-col divide-y">
{LANDING_BUTTONS.map(({ number, title, description, image }) => (
<button
type="button"
key={title}
onClick={() => handleButtonClick(image)}
className={`flex-1 px-7 py-5 text-16-20-response font-bold duration-300 before:absolute before:left-0 before:top-0 before:z-10 before:h-full before:w-1 before:bg-black ${
selectedImage === image ? 'bg-black text-white' : ''
} hover:bg-gray-50`}
>
<div className="flex items-center gap-2">
<span>{number}</span>
<h3>{title}</h3>
</div>
<div className="mt-2 text-left text-13-16-response font-medium">{description}</div>
</button>
))}
</nav>
<figure className="relative hidden flex-1 bg-white tablet:block">
<Image src={selectedImage} alt="main-image" fill className="absolute size-full object-contain" />
</figure>
</m.section>
</div>
</section>
);
}
40 changes: 40 additions & 0 deletions src/components/Landing/LandingMain.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useRef } from 'react';
import { useInView } from 'framer-motion';
import * as m from 'framer-motion/m';
import Link from 'next/link';

export default function LandingMain() {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true });

return (
<header className="relative min-h-screen select-none">
<div className="absolute left-1/2 top-1/2 size-full -translate-x-1/2 -translate-y-1/2">
<figure className="h-full">
<video muted autoPlay loop playsInline className="h-full object-cover">
<source src="/video/main-video.webm" type="video/webm" />
<source src="/video/main-video.mp4" type="video/mp4" />
</video>
</figure>
</div>
<m.article
ref={ref}
initial={{ x: -10, opacity: 0 }}
animate={isInView ? { x: 0, opacity: 1 } : { x: -10, opacity: 0 }}
transition={{ duration: 0.9, ease: 'easeInOut', delay: 0.4 }}
className="absolute left-5 top-1/3 -translate-y-1/2 font-bold text-white mobile:left-10 tablet:left-20"
>
<h1 className="text-landing-title leading-[100%]">
취미에 만취하다
<br />
일상에 즐거움을 더하다
</h1>
<p className="mt-6 text-16-20-response font-medium">다양한 취미와 함게, 즐거움에 만취하세요!</p>
<p className="text-balance text-16-20-response font-medium">새로운 사람들과 모여 당신만의 특별한 시간을 만들어 보세요.</p>
<nav className="mt-10 w-fit rounded-lg border border-gray-400 bg-blue-900 px-6 py-3 duration-300 hover:bg-blue-800">
<Link href="/main">만취 모임 참여하기</Link>
</nav>
</m.article>
</header>
);
}
68 changes: 68 additions & 0 deletions src/components/Landing/LandingPopularList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useRef } from 'react';
import { useInView } from 'framer-motion';
import * as m from 'framer-motion/m';
import Image from 'next/image';
import Link from 'next/link';
import { POPULAR_MEETINGS } from '@/constants/landing';
import { cn } from '@/utils/classNames';

export default function LandingPopularList() {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true });

return (
<section className="relative min-h-screen select-none bg-black text-white">
<div className="absolute left-1/2 top-1/2 w-full max-w-[1200px] -translate-x-1/2 -translate-y-1/2">
<m.article
ref={ref}
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 1.2, delay: 0.2 }}
className="flex flex-col items-center"
>
<header className="flex flex-col items-center gap-3">
<p className="text-16-20-response font-medium">만취 모임 LIST</p>
<h2 className="text-24-40-response font-bold">가장 인기 있는 모임 TOP 5</h2>
<p className="mx-5 text-wrap text-13-16-response font-medium">
다양한 취미와 관심사를 가진 사람들이 함께하는 인기 모임들을 만나고, 당신만의 특별한 경험을 만들어 보세요.
</p>
</header>
<div className="mt-10 w-fit rounded-lg border border-gray-400 bg-blue-900 px-6 py-3 duration-300 hover:bg-blue-800">
<Link href="/main">만취 모임 참여하기</Link>
</div>
</m.article>
<m.section
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 1.2, delay: 0.5 }}
className="mt-14 grid min-h-[500px] grid-cols-6 gap-4 text-black"
>
{POPULAR_MEETINGS.map(({ title, description, colSpan, image }, i) => (
<article
key={title}
className={cn('flex flex-col justify-between rounded-lg bg-white px-8 py-6', {
'col-span-2': colSpan === 2,
'col-span-3': colSpan === 3,
})}
>
<header>
<h3 className="text-16-20-response font-bold">{title}</h3>
<p className="mt-4 font-medium">{description}</p>
</header>
<div className="mt-4 flex items-end justify-between">
<nav className="font-medium text-gray-600 underline decoration-gray-600 underline-offset-8">
<Link href="/main">자세히보기</Link>
</nav>
{(i === 0 || i === POPULAR_MEETINGS.length - 1) && image && (
<figure>
<Image src={image} alt={`${title} 이미지`} width={120} height={100} className="relative top-3" />
</figure>
)}
</div>
</article>
))}
</m.section>
</div>
</section>
);
}
11 changes: 11 additions & 0 deletions src/components/Landing/LandingPrimary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function LandingPrimary() {
return (
<section className="relative h-landing-intro bg-primary-400">
<div className="absolute left-1/2 top-1/2 w-full max-w-[1200px] -translate-x-1/2 -translate-y-1/2 text-center text-16-20-response font-semibold text-black">
<h2 className="mb-5 text-24-40-response font-bold">혼자가 아닌 함께, 취미를 더 즐겁게</h2>
<p>관심사와 취미를 공유할 사람들을 만나보세요.</p>
<p>참여하기 쉽고, 새로운 경험이 기다리고 있습니다.</p>
</div>
</section>
);
}
57 changes: 57 additions & 0 deletions src/components/Landing/LandingReviewSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useRef } from 'react';
import { useInView } from 'framer-motion';
import * as m from 'framer-motion/m';
import Image from 'next/image';
import Link from 'next/link';
import { REVIEW_DATA } from '@/constants/landing';

export default function LandingReviewSection() {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true });

return (
<section className="select-none bg-black text-white">
<div className="mx-auto flex min-h-screen max-w-screen-pc flex-col justify-center gap-20 px-8">
<m.div
ref={ref}
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 1.2, delay: 0.2 }}
className="flex flex-col gap-4"
>
<h2 className="text-24-40-response font-bold">만취와 함께한 경험을 공유합니다</h2>
<p className="text-16-20-response">참여자들이 남긴 생생한 후기를 통해 다양한 모임의 실제 경험을 확인해보세요.</p>
<nav>
<Link href="/review" className="w-fit rounded-lg bg-white px-6 py-3 text-13-16-response font-bold text-black duration-300 hover:bg-gray-100">
후기 보러가기
</Link>
</nav>
</m.div>
<m.div
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 1.2, delay: 0.5 }}
className="flex flex-col gap-5 tablet:flex-row"
>
{REVIEW_DATA.map(({ name, title, content, image }) => (
<article key={name} className="flex-1 space-y-7 rounded-lg bg-white/10 p-8">
<header className="flex gap-4">
<figure>
<Image src="/icons/profile.svg" alt="프로필 사진" width={40} height={40} />
</figure>
<div className="flex flex-col">
<h3 className="text-13-16-response font-semibold">{name}</h3>
<p className="text-xs text-white/40">{title}</p>
</div>
</header>
<p className="text-13-15-response">{content}</p>
<figure>
<Image src={image} alt={title} />
</figure>
</article>
))}
</m.div>
</div>
</section>
);
}
43 changes: 43 additions & 0 deletions src/components/Landing/LandingScrollToTop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from 'react';
import * as m from 'framer-motion/m';
import { IS_SERVER } from '@/constants/server';

export default function LandingScrollToTop() {
const [showScrollTop, setShowScrollTop] = useState<boolean>(false);

const scrollToTop = () => {
if (!IS_SERVER) {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
};

useEffect(() => {
const handleScroll = () => {
if (!IS_SERVER) {
setShowScrollTop(window.scrollY > 350);
}
};

window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);

function ShowScrollTop() {
if (!showScrollTop) return null;

return (
<m.button
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={scrollToTop}
transition={{ duration: 0.5 }}
className="fixed bottom-10 left-1/2 z-10 -translate-x-1/2 transform rounded-2xl bg-white px-10 py-2 shadow-lg transition-colors duration-300 hover:bg-black hover:text-white"
>
</m.button>
);
}

return ShowScrollTop();
}
36 changes: 0 additions & 36 deletions src/components/landing/CardList/index.tsx

This file was deleted.

Loading
Loading