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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-vercel.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy to Vercel with Redeploy-on-Failure
name: Deploy to Vercel

on:
push:
Expand Down
81 changes: 57 additions & 24 deletions apps/web/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,69 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# LinkyBoard Web

## Getting Started
Next.js 기반 웹 플랫폼입니다.

First, run the development server:
## 기술 스택

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
- **Framework**: Next.js 15 (App Router)
- **Language**: TypeScript
- **Styling**: Tailwind CSS 4
- **State**: Zustand, TanStack Query

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## 설치 방법

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
1. 프로젝트 루트에서 의존성 설치 (이미 설치되어 있다면 생략)

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More
```bash
pnpm install
```

To learn more about Next.js, take a look at the following resources:
## 개발 서버 실행

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
```bash
# 루트에서 실행
pnpm dev:web

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
# 또는 web 디렉토리에서 직접 실행
cd apps/web
pnpm dev
```

## Deploy on Vercel
개발 서버는 [http://localhost:3000](http://localhost:3000)에서 실행됩니다.

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
## 프로젝트 구조

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
```plaintext
web/
├── public/ # 정적 파일
│ ├── example/ # 예제 비디오 파일
│ └── static/ # 정적 이미지
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── (with-side-bar)/ # 사이드바가 있는 레이아웃 그룹
│ │ │ ├── dashboard/ # 대시보드 페이지
│ │ │ ├── library/ # 라이브러리 페이지
│ │ │ └── topic/ # 토픽 페이지
│ │ ├── login/ # 로그인 페이지
│ │ ├── redirect/ # 로그인 리다이렉트 페이지
│ │ ├── layout.tsx # 루트 레이아웃
│ │ └── page.tsx # 랜딩 페이지
│ ├── assets/ # 폰트, SVG 등
│ ├── components/ # React 컴포넌트
│ │ ├── (with-side-bar)/ # 사이드바 관련 컴포넌트
│ │ ├── common/ # 공통 컴포넌트
│ │ ├── landing/ # 랜딩 페이지 컴포넌트
│ │ └── provider/ # Provider 컴포넌트
│ ├── constants/ # 상수 정의
│ ├── hooks/ # 커스텀 훅
│ ├── lib/ # 라이브러리 설정
│ │ ├── tanstack/ # TanStack Query 설정
│ │ └── zustand/ # Zustand 스토어
│ ├── models/ # 데이터 모델
│ ├── page/ # 경로에 해당하는 페이지 컴포넌트
│ ├── schemas/ # Zod 스키마
│ ├── services/ # API 서비스
│ ├── types/ # TypeScript 타입 정의
│ └── utils/ # 유틸리티 함수
├── next.config.ts # Next.js 설정
└── postcss.config.mjs # PostCSS 설정
```
Binary file added apps/web/public/static/gradient-bg.webp
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 apps/web/public/static/landing-bg.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions apps/web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,53 @@ div.customHandle {
.node-toggleListItem:has(+ .bn-block-group) svg {
fill: var(--foreground);
}

@utility bg-glass {
background-color: transparent;
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 60px;
}

@utility bg-radial-gradient-blue {
background: radial-gradient(
50% 50% at 50% 50%,
#c0d9ff 0%,
rgba(192, 217, 255, 0.8) 46.63%,
rgba(192, 217, 255, 0) 100%
);
}

@utility bg-radial-gradient-purple {
background: radial-gradient(
50% 50% at 50% 50%,
rgba(225, 217, 253, 0.8) 0%,
rgba(225, 217, 253, 0.8) 46.63%,
rgba(225, 216, 255, 0) 100%
);
/* background: radial-gradient(
50% 50% at 50% 50%,
#ccbeff 0%,
rgba(204, 190, 255, 0.8) 46.63%,
rgba(225, 216, 255, 0) 100%
); */
}

@utility bg-radial-gradient-duo {
background:
radial-gradient(
50% 50% at 50% 50%,
#ccbeff 0%,
rgba(204, 190, 255, 0.8) 46.63%,
rgba(225, 216, 255, 0) 100%
),
radial-gradient(
55.95% 57.37% at 29.97% 32%,
rgba(163, 137, 255, 0.7) 0%,
rgba(163, 137, 255, 0.35) 38.72%,
rgba(255, 0, 4, 0.5) 100%
);
filter: blur(120px);
opacity: 0.9;
}
34 changes: 7 additions & 27 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import "./globals.css";

import type { Metadata } from "next";
import { JetBrains_Mono, Merriweather, Poppins } from "next/font/google";
import localFont from "next/font/local";

import RootProvider from "@/components/provider/root-provider";
import ThirdPartyProvider from "@/components/provider/third-party-provider";
import { cn } from "@linkyboard/utils";

const poppins = Poppins({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
variable: "--font-poppins",
});

const merriweather = Merriweather({
subsets: ["latin"],
weight: ["300", "400", "700"],
variable: "--font-merriweather",
});

const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
weight: ["400", "500"],
variable: "--font-jetbrains-mono",
const pretendard = localFont({
src: "../assets/fonts/PretendardVariable.woff2",
variable: "--font-pretendard",
display: "swap",
});

export const metadata: Metadata = {
Expand Down Expand Up @@ -74,16 +62,8 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="ko" className={cn(poppins.variable, merriweather.variable, jetbrainsMono.variable)}>
<head>
<meta
name="google-site-verification"
content="QCEJjFcIzUi4_pR-QJw5g8OdYabndorX1YORsRauKnw"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
</head>
<body className={cn(poppins.className, "antialiased")}>
<html lang="ko">
<body className={cn(pretendard.className, "antialiased")}>
<ThirdPartyProvider />
<RootProvider>{children}</RootProvider>
</body>
Expand Down
8 changes: 3 additions & 5 deletions apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import BenefitsSection from "@/components/landing/benefits-section";
import DemoSection from "@/components/landing/demo-section";
import FeaturesSection from "@/components/landing/features-section";
import Footer from "@/components/landing/footer";
import FourStepsSection from "@/components/landing/four-steps-section";
import Header from "@/components/landing/header";
import HeroSection from "@/components/landing/hero-section";
import HowItWorksSection from "@/components/landing/how-it-works-section";
import ProblemSolutionSection from "@/components/landing/problem-solution-section";
import { getCookie } from "@/utils/cookie";

export default async function Home() {
Expand All @@ -18,14 +17,13 @@ export default async function Home() {
}

return (
<main className="min-h-screen">
<main className="min-h-screen overflow-x-hidden">
<Header />
<HeroSection />
<DemoSection />
<ProblemSolutionSection />
<FeaturesSection />
<BenefitsSection />
<HowItWorksSection />
<FourStepsSection />
<Footer />
</main>
);
Expand Down
19 changes: 5 additions & 14 deletions apps/web/src/components/landing/benefits-section.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Database, Gem, Heart, Lightbulb, Rocket, Users } from "lucide-react";
import { Database, Gem, Lightbulb, Rocket } from "lucide-react";

const benefits = [
{
Expand All @@ -16,29 +16,19 @@ const benefits = [
title: "체계적 지식 축적",
description: "체계적인 지식 축적과 관리",
},
{
icon: Heart,
title: "스트레스 감소",
description: "정보 과부하로 인한 스트레스 해소",
},
{
icon: Gem,
title: "개인 지식 자산",
description: "나만의 가치 있는 지식 자산 구축",
},
{
icon: Users,
title: "협업 가능",
description: "팀원과 지식 공유 및 협업",
},
];

export default function BenefitsSection() {
return (
<section id="benefits" className="py-20">
<div className="mx-auto max-w-6xl px-4">
<section id="benefits" className="relative overflow-hidden py-20">
<div className="relative z-10 mx-auto max-w-6xl px-4">
<div className="mb-16 text-center">
<h2 className="mb-4 text-4xl font-bold">LinkyBoard의 혜택</h2>
<h2 className="mb-4 text-4xl font-bold">LinkyBoard의 가치</h2>
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
지식 관리의 새로운 패러다임을 경험하세요
</p>
Expand All @@ -59,6 +49,7 @@ export default function BenefitsSection() {
))}
</div>
</div>
<div className="bg-radial-gradient-purple max-size-96 absolute bottom-0 left-1/2 z-0 size-[90vw] -translate-x-1/2 translate-y-1/2 rounded-full" />
</section>
);
}
27 changes: 20 additions & 7 deletions apps/web/src/components/landing/demo-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,35 @@ const DEMO_VIDEOS = [
type: "extension",
icon: <NotebookPen size={12} />,
title: "요약",
description: "웹 서칭 중 발견한 지식들을 요약하세요.",
description: "웹 서칭 중 발견한 지식들을 저장하세요.",
video: extensionVideo,
button: "Chrome 확장프로그램 다운로드",
},
{
type: "dashboard",
icon: <FolderArchive size={12} />,
title: "정리",
description: "발견한 지식들을 한 곳에서 관리하세요.",
description: "저장한 지식들을 한 곳에서 관리하세요.",
video: dashboardVideo,
button: "정리하러 가기",
},
{
type: "topic",
icon: <Link size={12} />,
title: "연결",
description: "지식들을 연결하여 하나의 토픽을 생성하세요.",
description: "한 곳에 모은 지식들을 자유롭게 연결하세요.",
video: topicVideo,
button: "연결하러 가기",
},
// TODO: 서버 복구되면 영상 찍기
// {
// type: "dashboard",
// icon: <FolderArchive size={12} />,
// title: "정리",
// description: "연결된 지식들을 하나로 요약하세요.",
// video: topicVideo,
// button: "요약하러 가기",
// },
];

export default function DemoSection() {
Expand All @@ -55,13 +64,13 @@ export default function DemoSection() {
};

return (
<section className="bg-card px-4 py-20">
<div className="mx-auto max-w-6xl space-y-8 sm:space-y-12 lg:space-y-24">
<section className="bg-card relative px-4 py-20">
<div className="relative z-10 mx-auto max-w-6xl space-y-8 sm:space-y-12 lg:space-y-24">
{DEMO_VIDEOS.map((demo, idx) => (
<div
key={demo.title}
key={`${demo.title}-${idx}`}
className={cn(
"animate-fade-in-up flex flex-col items-center gap-6 rounded-2xl lg:flex-row lg:gap-10",
"animate-fade-in-up z-1 flex flex-col items-center gap-6 rounded-2xl lg:flex-row lg:gap-10",
idx % 2 && "lg:flex-row-reverse"
)}
>
Expand Down Expand Up @@ -95,6 +104,10 @@ export default function DemoSection() {
</div>
))}
</div>
<div className="bg-radial-gradient-blue z-1 absolute left-32 top-0 size-[600px] -translate-y-1/2 rounded-full" />
<div className="bg-radial-gradient-purple absolute left-0 top-0 z-0 size-[600px] -translate-x-1/3 -translate-y-1/4 rounded-full" />
<div className="bg-radial-gradient-duo absolute right-0 top-1/2 z-0 size-[600px] translate-x-1/4 rounded-full" />
<div className="bg-radial-gradient-blue z-1 absolute bottom-0 left-0 size-[600px] -translate-x-1/3 translate-y-1/2 rounded-full" />
</section>
);
}
2 changes: 1 addition & 1 deletion apps/web/src/components/landing/features-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const features = [
export default function FeaturesSection() {
return (
<section id="features" className="bg-card py-20">
<div className="mx-auto max-w-6xl px-4">
<div className="relative z-10 mx-auto max-w-6xl px-4">
<div className="mb-16 text-center">
<h2 className="mb-4 text-4xl font-bold">강력한 기능들</h2>
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
Expand Down
Loading
Loading