From fa7c25465cc7e97d2e3abae8e4c4e9b9a6ee4ef0 Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Mon, 6 Jul 2026 09:32:49 +0500 Subject: [PATCH] Add public DS-2 welcome page on root for unauthenticated visitors Second-brain scheme (logo + six cards with rays) reworked from the old platform's infographic. Root stays role-redirecting for signed-in users; middleware opens "/" as an exact match only. Co-Authored-By: Claude Fable 5 --- public/logo.svg | 66 +++++++ src/app/page.tsx | 3 +- src/components/welcome/scheme-connectors.tsx | 56 ++++++ src/components/welcome/welcome-page.tsx | 195 +++++++++++++++++++ src/middleware.ts | 4 +- 5 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 public/logo.svg create mode 100644 src/components/welcome/scheme-connectors.tsx create mode 100644 src/components/welcome/welcome-page.tsx diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..2e29416 --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/page.tsx b/src/app/page.tsx index 157d127..bf79b41 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,12 +1,13 @@ import { redirect } from "next/navigation"; import { headers } from "next/headers"; import { auth } from "@/lib/auth"; +import { WelcomePage } from "@/components/welcome/welcome-page"; export default async function HomePage() { const session = await auth.api.getSession({ headers: await headers() }); if (!session) { - redirect("/login"); + return ; } const role = session.user.role; diff --git a/src/components/welcome/scheme-connectors.tsx b/src/components/welcome/scheme-connectors.tsx new file mode 100644 index 0000000..12e48a5 --- /dev/null +++ b/src/components/welcome/scheme-connectors.tsx @@ -0,0 +1,56 @@ +"use client"; + +import { useEffect, useRef } from "react"; + +// Draws 2px rays from the edge of every [data-scheme-card] to the central +// [data-scheme-logo] circle. Redraws on container resize and after fonts load. +export function SchemeConnectors() { + const ref = useRef(null); + + useEffect(() => { + const svg = ref.current; + const scheme = svg?.parentElement; + if (!svg || !scheme) return; + + function draw() { + if (!svg || !scheme) return; + const logo = scheme.querySelector("[data-scheme-logo]"); + if (!logo) return; + const s = scheme.getBoundingClientRect(); + const l = logo.getBoundingClientRect(); + const cx = l.left + l.width / 2 - s.left; + const cy = l.top + l.height / 2 - s.top; + const r = l.width / 2 + 4; + svg.setAttribute("viewBox", `0 0 ${s.width} ${s.height}`); + let lines = ""; + scheme.querySelectorAll("[data-scheme-card]").forEach((card) => { + const c = card.getBoundingClientRect(); + const isLeft = card.dataset.schemeCard === "left"; + const x1 = (isLeft ? c.right : c.left) - s.left; + const y1 = c.top + c.height / 2 - s.top; + const dx = cx - x1; + const dy = cy - y1; + const d = Math.hypot(dx, dy) || 1; + const x2 = cx - (dx / d) * r; + const y2 = cy - (dy / d) * r; + lines += ``; + }); + svg.innerHTML = lines; + } + + draw(); + const ro = new ResizeObserver(draw); + ro.observe(scheme); + document.fonts?.ready.then(draw).catch(() => {}); + return () => ro.disconnect(); + }, []); + + return ( + + ); +} diff --git a/src/components/welcome/welcome-page.tsx b/src/components/welcome/welcome-page.tsx new file mode 100644 index 0000000..0722645 --- /dev/null +++ b/src/components/welcome/welcome-page.tsx @@ -0,0 +1,195 @@ +// Public landing shown on "/" for unauthenticated visitors. +// DS-2 rework of the old platform's "second brain" infographic. +import Link from "next/link"; +import { + StickyNote, + BookOpen, + Lightbulb, + Database, + GraduationCap, + Wrench, + type LucideIcon, +} from "lucide-react"; +import { SchemeConnectors } from "./scheme-connectors"; + +interface SchemeBlock { + icon: LucideIcon; + title: string; + items: string[]; +} + +const BLOCKS_LEFT: SchemeBlock[] = [ + { + icon: StickyNote, + title: "Заметки", + items: [ + "разбираемся, как создавать качественные заметки, а не просто писать всё подряд", + "изучаем самые эффективные подходы в заметкоделии и заметковедении", + ], + }, + { + icon: BookOpen, + title: "Знания", + items: [ + "превращаем сырые данные и разрозненную информацию в устойчивые знания, готовые к повторному применению", + ], + }, + { + icon: Lightbulb, + title: "Идеи", + items: [ + "фиксируем, развиваем и воплощаем гениальные идеи в жизнь", + "и не даём им сгинуть в процессе", + ], + }, +]; + +const BLOCKS_RIGHT: SchemeBlock[] = [ + { + icon: Database, + title: "Базы знаний", + items: [ + "создаём картотеку, строим структуру, наполняем её качественными заметками", + "связываем отдельные элементы в единую сеть идей и размышлений", + ], + }, + { + icon: GraduationCap, + title: "Обучение", + items: [ + "учимся правильно учиться, осваиваем эффективные стратегии", + "выстраиваем свой индивидуальный трек развития", + ], + }, + { + icon: Wrench, + title: "Инструменты", + items: [ + "разбираемся с особенностями инструментов и выбираем самые подходящие", + ], + }, +]; + +const ROW_START = ["md:row-start-1", "md:row-start-2", "md:row-start-3"]; + +function SchemeCard({ block, side, row }: { block: SchemeBlock; side: "left" | "right"; row: number }) { + const Icon = block.icon; + const colClass = side === "left" ? "md:col-start-1" : "md:col-start-3"; + return ( +
+
+ + + + + {block.title} + +
+
    + {block.items.map((item) => ( +
  • + + — + + {item} +
  • + ))} +
+
+ ); +} + +export function WelcomePage() { + return ( +
+
+ Second Brain + +
+ +
+

+ Образовательная платформа Second Brain +

+

+ Создаём и развиваем +
+ второй мозг +

+

+ Заметки, знания и идеи — в одной системе, которая работает на вас +

+ +
+ +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + Second Brain + + Second Brain + +
+ {BLOCKS_LEFT.map((block, i) => ( + + ))} + {BLOCKS_RIGHT.map((block, i) => ( + + ))} +
+ +
+ + Начать учиться → + + + У меня уже есть доступ + +
+
+ + +
+ ); +} diff --git a/src/middleware.ts b/src/middleware.ts index 9d16a6d..293b790 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -14,9 +14,11 @@ export function middleware(request: NextRequest) { } if ( + pathname === "/" || // public welcome page; "/" must stay an EXACT match (startsWith would open everything) PUBLIC_ROUTES.some((route) => pathname.startsWith(route)) || pathname.startsWith("/_next") || - pathname.startsWith("/favicon") + pathname.startsWith("/favicon") || + pathname === "/logo.svg" ) { return NextResponse.next(); }