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
30 changes: 14 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@heroui/theme": "^2.4.12",
"@stomp/stompjs": "^7.0.0",
"axios": "^1.8.3",
"framer-motion": "^12.23.22",
"lucide-react": "^0.508.0",
"next": "^15.1.4",
"react": "^18",
Expand Down
11 changes: 10 additions & 1 deletion src/app/layouts/service.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FC, ReactNode } from 'react';
import { tv } from 'tailwind-variants';

import Menu from '@/features/service-menu';
import Header from '@/widgets/header';
Expand All @@ -9,11 +10,19 @@ interface Props {
children: ReactNode;
}

const style = tv({
base: [
'relative overflow-y-hidden',
'h-0 grow', // mobile
'md:grid md:grid-cols-[auto,1fr] md:bg-default-100', // desktop
],
});

const ServiceLayout: FC<Props> = ({ children }) => {
return (
<AuthProvider requireAuth={process.env.NODE_ENV !== 'development'}>
<Header className='max-w-none' />
<main className='relative grow overflow-y-hidden md:grid md:grid-cols-[auto,1fr] md:bg-default-100'>
<main className={style()}>
<Menu />
{children}
</main>
Expand Down
4 changes: 2 additions & 2 deletions src/features/drawer/ui/styles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { tv } from 'tailwind-variants';

export const drawer = tv({
base: 'absolute right-0 top-0 z-50 flex h-full w-[300px] flex-col bg-white transition-transform',
base: 'absolute right-0 top-0 z-50 flex h-screen w-[300px] flex-col bg-white transition-transform',
variants: {
open: {
true: 'translate-x-0',
Expand All @@ -11,7 +11,7 @@ export const drawer = tv({
});

export const backdrop = tv({
base: 'fixed left-0 top-0 z-40 size-full bg-black bg-opacity-50 md:hidden',
base: 'fixed left-0 top-0 z-40 h-screen w-screen bg-black bg-opacity-50 md:hidden',
});

export const button = tv({
Expand Down
9 changes: 6 additions & 3 deletions src/pages/landing/ui/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ import SupportForm from './support/form';
import TutorialChatting from './tutorial-chatting/chatting';
import TutorialChattingDescription from './tutorial-chatting/description';

const NAV_ID = 'nav-menu';

const LandingPage = () => {
return (
<>
<Header position='sticky'>
<nav id='nav-menu' className='flex gap-8 max-md:hidden' />
<nav id={NAV_ID} className='flex gap-8 max-md:hidden' />
</Header>
<main>
<AnchorPoint label='소개' anchorPortalId='nav-menu' />
<AnchorPoint label='소개' anchorPortalId={NAV_ID} />
<Hero
viewport={{ once: true }}
className='bg-[url(/images/background-landing.svg)] bg-cover bg-center bg-no-repeat'
>
<TutorialChattingDescription />
<TutorialChatting />
</Hero>
<AnchorPoint label='문의하기' anchorPortalId='nav-menu' />
<AnchorPoint label='문의하기' anchorPortalId={NAV_ID} />
<Hero className='bg-[url(/images/background-support.svg)] bg-cover bg-center bg-no-repeat'>
<SupportDescription />
<SupportForm />
Expand Down
21 changes: 19 additions & 2 deletions src/pages/landing/ui/support/description.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
'use client';

import { motion } from 'framer-motion';

import { description } from '../styles';
import { CheckCircle } from './check-circle';

const SupportDescription = () => {
const styles = description();

return (
<div className={styles.wrapper({ class: 'max-lg:items-center' })}>
<motion.div
variants={{
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
delay: 0.2,
duration: 0.5,
},
},
}}
className={styles.wrapper({ class: 'max-lg:items-center' })}
>
<h1 className={styles.title({ class: 'max-xs:text-center' })}>
{'지금 영업팀에 '}
<br className='xs:max-lg:hidden' />
Expand All @@ -30,7 +47,7 @@ const SupportDescription = () => {
),
)}
</ul>
</div>
</motion.div>
);
};

Expand Down
50 changes: 32 additions & 18 deletions src/pages/landing/ui/tutorial-chatting/chatting.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use client';

import { Card } from '@heroui/react';
import { motion } from 'framer-motion';

import { ChattingRoomProvider } from '@/entities/chatting-room';
import { TUTORIAL_CHAT_MEMBER_ID } from '@/shared/config';
Expand All @@ -17,24 +20,35 @@ const createMember = (

const TutorialChatting = () => {
return (
<Mockup>
<Card className='h-full p-2'>
<ChattingHeader>
<ChattingTitle title='직접 입력하고 테스트해보세요' />
</ChattingHeader>
<ChattingRoomProvider
chattingMemberMap={{
[TUTORIAL_CHAT_MEMBER_ID.ai]: createMember(
TUTORIAL_CHAT_MEMBER_ID.ai,
'CAMUS',
'/images/logo.svg', // TODO: 이미지 변경
),
}}
>
<TutorialChattingBody />
</ChattingRoomProvider>
</Card>
</Mockup>
<motion.section
variants={{
hidden: { opacity: 0, y: 10 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5 },
},
}}
>
<Mockup>
<Card className='h-full p-2'>
<ChattingHeader>
<ChattingTitle title='직접 입력하고 테스트해보세요' />
</ChattingHeader>
<ChattingRoomProvider
chattingMemberMap={{
[TUTORIAL_CHAT_MEMBER_ID.ai]: createMember(
TUTORIAL_CHAT_MEMBER_ID.ai,
'CAMUS',
'/images/logo.svg', // TODO: 이미지 변경
),
}}
>
<TutorialChattingBody />
</ChattingRoomProvider>
</Card>
</Mockup>
</motion.section>
);
};

Expand Down
19 changes: 17 additions & 2 deletions src/pages/landing/ui/tutorial-chatting/description.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
'use client';

import { motion } from 'framer-motion';
import { tv } from 'tailwind-variants';

import { description } from '../styles';
Expand All @@ -18,7 +21,19 @@ const TutorialChattingDescription = () => {
const styles = createStyle();

return (
<div className={styles.wrapper()}>
<motion.div
variants={{
hidden: { opacity: 0, y: 10 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
},
},
}}
className={styles.wrapper()}
>
<h1 className={styles.title()}>
{'문맥까지 이해하는 '}
<br className='sm:max-lg:hidden xl:hidden' />
Expand All @@ -29,7 +44,7 @@ const TutorialChattingDescription = () => {
<br className='max-xs:hidden xl:hidden' />
누구나 안심하고 대화할 수 있는 커뮤니케이션 공간을 만듭니다.
</p>
</div>
</motion.div>
);
};

Expand Down
28 changes: 23 additions & 5 deletions src/shared/ui/component/hero.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
'use client';

import { type MotionProps, motion, stagger } from 'framer-motion';
import { tv } from 'tailwind-variants';

const createStyle = tv({
Expand All @@ -18,19 +21,34 @@ const createStyle = tv({
},
});

interface Props {
type Props = {
children: ReactNode;
className?: string;
isFirst?: boolean;
}
} & Pick<MotionProps, 'viewport'>;

const Hero = ({ children, className, isFirst }: Props) => {
const Hero = ({ children, className, isFirst, viewport }: Props) => {
const styles = createStyle({ isFirst });

return (
<article className={styles.base({ class: className })}>
<motion.article
viewport={viewport}
variants={{
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
duration: 1.5,
delayChildren: stagger(0.5),
},
},
}}
initial='hidden'
whileInView='visible'
className={styles.base({ class: className })}
>
<div className={styles.wrapper()}>{children}</div>
</article>
</motion.article>
);
};

Expand Down
7 changes: 4 additions & 3 deletions src/widgets/header/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NAVIGATIONS } from '../config/navigation';
import DrawerLink from './drawer-item/link';
import NavigationBelt from './nav-belt';
import Login from './nav-login';
import HeaderWrapper from './wrapper';

interface Props {
business?: boolean;
Expand All @@ -25,7 +26,7 @@ const createStyle = tv({
variants: {
position: {
fixed: { base: 'fixed' },
sticky: { base: 'sticky top-0' },
sticky: { base: 'sticky top-0 bg-transparent backdrop-blur-lg' },
},
},
});
Expand All @@ -39,7 +40,7 @@ const Header = async ({ business, className, position, children }: Props) => {
const styles = createStyle({ position });

return (
<header className={styles.base()}>
<HeaderWrapper className={styles.base()} animated={position === 'sticky'}>
<NavigationBelt business={isBusiness} className={className} />
<div className={styles.wrapper({ className })}>
<Logo business={isBusiness} />
Expand All @@ -60,7 +61,7 @@ const Header = async ({ business, className, position, children }: Props) => {
</div>
<DrawerLink path={ROUTE.signup}>회원가입</DrawerLink>
</Drawer>
</header>
</HeaderWrapper>
);
};

Expand Down
29 changes: 29 additions & 0 deletions src/widgets/header/ui/wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';

import { motion } from 'framer-motion';

interface Props {
children: React.ReactNode;
className?: string;
animated?: boolean;
}

const HeaderWrapper = ({ animated, ...props }: Props) => {
if (!animated) {
return <header {...props} />;
}

return (
<motion.header
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.7,
delay: 0.2,
}}
{...props}
/>
);
};

export default HeaderWrapper;