ساخت اسکلت اولیه‌ی DevNotes

در این فصل با صفحه‌ها، Layoutها، Route Groups، پوشه‌های خصوصی و فایل‌های ویژه آشنا شدیم. حالا همه‌ی آن‌ها را کنار هم می‌گذاریم و اسکلت کامل DevNotes را می‌سازیم: مسیرهای عمومی، صفحات ورود و ثبت‌نام و بخش داشبورد. هنوز داده‌ی واقعی، فرم و استایل نداریم؛ هدف این است که ساختار پروژه از همین حالا درست و ثابت باشد تا در فصل‌های بعد فقط محتوا و منطق را به آن اضافه کنیم.

پیش‌نیاز

  • تمام درس‌های فصل دوم تا «فایل‌های ویژه‌ی App Router»
  • پروژه‌ی devnotes با ساختاری که در درس «پوشه‌های خصوصی و ساختاردهی فایل‌ها» ساختیم

نقشه‌ی مسیرهای DevNotes

اسکلت اولیه‌ی پروژه این مسیرها را دارد:

آدرس گروه محتوا
/ (public) صفحه‌ی اصلی
/about (public) درباره‌ی پروژه
/posts (public) فهرست نوشته‌ها (فعلاً موقت)
/login (auth) صفحه‌ی ورود (فعلاً بدون فرم)
/register (auth) صفحه‌ی ثبت‌نام (فعلاً بدون فرم)
/dashboard (dashboard) صفحه‌ی اصلی داشبورد
/dashboard/posts (dashboard) فهرست نوشته‌های مدیریت (فعلاً موقت)
/dashboard/posts/new (dashboard) صفحه‌ی ایجاد نوشته (فعلاً بدون فرم)

صفحه‌ی جزئیات هر نوشته (/posts/[slug]) را در درس «ساخت فهرست و صفحه‌ی جزئیات نوشته‌ها» از فصل «Routing و Navigation» اضافه می‌کنیم، چون به مسیرهای پویا نیاز دارد.

تغییراتی نسبت به ساختار قبلی

در درس «Route Groups و سازمان‌دهی مسیرها» گفتیم محل نهایی صفحه‌ی ایجاد نوشته را اینجا مشخص می‌کنیم. ایجاد نوشته کار مدیریت است و باید پشت ورود کاربر قرار بگیرد، پس آن را از بخش عمومی به داشبورد منتقل می‌کنیم:

  • حذف: پوشه‌ی app/(public)/posts/new (آدرس /posts/new دیگر وجود ندارد).
  • جدید: app/(dashboard)/dashboard/posts/new/page.tsx با آدرس /dashboard/posts/new.
  • فایل‌های دیگر تغییرکرده: components/SiteHeader.tsx، app/(public)/page.tsx، app/(dashboard)/layout.tsx و app/(dashboard)/dashboard/page.tsx.
  • فایل‌های جدید: گروه (auth) و صفحه‌ی dashboard/posts.

یادآوری: قرارگرفتن در گروه (dashboard) هنوز هیچ محافظتی ایجاد نمی‌کند. کنترل دسترسی واقعی را در فصل «Authentication و Security» و «Proxy و کنترل دسترسی» می‌سازیم.

یک ابزار جدید: کامپوننت Link

برای اسکلت به لینک بین صفحات نیاز داریم. در Next.js برای لینک داخلی به‌جای تگ <a> از کامپوننت Link استفاده می‌کنیم:

tsx
import Link from "next/link";

<Link href="/posts">نوشته‌ها</Link>

Link ناوبری سریع بدون بارگذاری کامل صفحه انجام می‌دهد. فعلاً همین مقدار کافی است؛ کارکرد کامل آن، از جمله Prefetch، را در درس «لینک‌سازی با Link» می‌بینیم.

گام ۱: هدر با ناوبری

فایل components/SiteHeader.tsx را این‌طور بازنویسی کنید:

tsx
// components/SiteHeader.tsx
import Link from "next/link";

export default function SiteHeader() {
  return (
    <header>
      <strong>DevNotes</strong>
      <nav aria-label="ناوبری اصلی">
        <ul>
          <li><Link href="/">خانه</Link></li>
          <li><Link href="/posts">نوشته‌ها</Link></li>
          <li><Link href="/about">درباره</Link></li>
          <li><Link href="/login">ورود</Link></li>
        </ul>
      </nav>
    </header>
  );
}

گام ۲: صفحه‌ی اصلی

tsx
// app/(public)/page.tsx
import Link from "next/link";

export default function HomePage() {
  return (
    <main>
      <h1>DevNotes</h1>
      <p>
        وب‌سایتی برای انتشار و مدیریت نوشته‌های فنی. در طول دوره‌ی
        Next.js 16 این پروژه را قدم‌به‌قدم کامل می‌کنیم.
      </p>
      <Link href="/posts">مشاهده‌ی نوشته‌ها</Link>
    </main>
  );
}

صفحه‌های about و posts را بدون تغییر نگه دارید.

گام ۳: گروه (auth) برای ورود و ثبت‌نام

صفحات ورود و ثبت‌نام نه به هدر و فوتر عمومی و نه به منوی کناری داشبورد نیاز دارند، پس گروه جداگانه‌ای برایشان می‌سازیم. پوشه‌ی app/(auth) را بسازید:

tsx
// app/(auth)/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";

export default function AuthLayout({
  children,
}: Readonly<{ children: ReactNode }>) {
  return (
    <div>
      <p>
        <Link href="/">بازگشت به DevNotes</Link>
      </p>
      {children}
    </div>
  );
}
tsx
// app/(auth)/login/page.tsx
import Link from "next/link";

export default function LoginPage() {
  return (
    <main>
      <h1>ورود</h1>
      <p>فرم ورود در فصل‌های بعد ساخته می‌شود.</p>
      <p>
        حساب کاربری ندارید؟ <Link href="/register">ثبت‌نام</Link>
      </p>
    </main>
  );
}
tsx
// app/(auth)/register/page.tsx
import Link from "next/link";

export default function RegisterPage() {
  return (
    <main>
      <h1>ثبت‌نام</h1>
      <p>فرم ثبت‌نام در فصل‌های بعد ساخته می‌شود.</p>
      <p>
        قبلاً ثبت‌نام کرده‌اید؟ <Link href="/login">ورود</Link>
      </p>
    </main>
  );
}

گام ۴: داشبورد

ابتدا Layout داشبورد را با لینک‌های ناوبری به‌روزرسانی کنید:

tsx
// app/(dashboard)/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";

export default function DashboardLayout({
  children,
}: Readonly<{ children: ReactNode }>) {
  return (
    <div>
      <aside>
        <strong>داشبورد DevNotes</strong>
        <nav aria-label="ناوبری داشبورد">
          <ul>
            <li><Link href="/dashboard">نمای کلی</Link></li>
            <li><Link href="/dashboard/posts">نوشته‌های من</Link></li>
            <li><Link href="/dashboard/posts/new">نوشته‌ی جدید</Link></li>
            <li><Link href="/">بازگشت به سایت</Link></li>
          </ul>
        </nav>
      </aside>
      {children}
    </div>
  );
}
tsx
// app/(dashboard)/dashboard/page.tsx
export default function DashboardPage() {
  return (
    <main>
      <h1>داشبورد مدیریت</h1>
      <p>در این بخش نوشته‌های خودتان را مدیریت خواهید کرد.</p>
    </main>
  );
}
tsx
// app/(dashboard)/dashboard/posts/page.tsx
export default function DashboardPostsPage() {
  return (
    <main>
      <h1>نوشته‌های من</h1>
      <p>فهرست نوشته‌های شما بعد از اتصال به داده در اینجا نمایش داده می‌شود.</p>
    </main>
  );
}
tsx
// app/(dashboard)/dashboard/posts/new/page.tsx
export default function NewPostPage() {
  return (
    <main>
      <h1>نوشته‌ی جدید</h1>
      <p>فرم ایجاد نوشته در فصل «فرم‌ها و Server Actions» ساخته می‌شود.</p>
    </main>
  );
}

حالا پوشه‌ی قدیمی app/(public)/posts/new را حذف کنید.

ساختار نهایی پروژه در پایان فصل دوم

text
devnotes/
├── app/
│   ├── layout.tsx                      ← Root Layout
│   ├── not-found.tsx                   ← ۴۰۴ فارسی
│   ├── globals.css
│   ├── favicon.ico
│   ├── (public)/
│   │   ├── layout.tsx                  ← هدر و فوتر
│   │   ├── page.tsx                    ← /
│   │   ├── about/
│   │   │   └── page.tsx                ← /about
│   │   └── posts/
│   │       ├── layout.tsx
│   │       ├── page.tsx                ← /posts
│   │       └── _components/
│   │           └── PostsIntro.tsx
│   ├── (auth)/
│   │   ├── layout.tsx
│   │   ├── login/
│   │   │   └── page.tsx                ← /login
│   │   └── register/
│   │       └── page.tsx                ← /register
│   └── (dashboard)/
│       ├── layout.tsx                  ← منوی کناری
│       └── dashboard/
│           ├── page.tsx                ← /dashboard
│           └── posts/
│               ├── page.tsx            ← /dashboard/posts
│               └── new/
│                   └── page.tsx        ← /dashboard/posts/new
└── components/
    ├── SiteHeader.tsx
    └── SiteFooter.tsx

توضیح طراحی

  • سه گروه، سه Layout: بخش عمومی هدر و فوتر دارد، بخش ورود فقط یک لینک بازگشت، و داشبورد منوی کناری. Root Layout فقط ساختار HTML و تنظیمات سراسری را نگه می‌دارد.
  • همه Server Component هستند: هیچ فایلی "use client" ندارد و هنوز هم نیازی نیست. نیاز به Client Component را در فصل «Server Components و Client Components» می‌بینیم.
  • HTML معنایی: از nav با ویژگی aria-label، ul، main، header و aside استفاده کرده‌ایم تا ساختار برای صفحه‌خوان‌ها و موتورهای جست‌وجو معنادار باشد.
  • بدون استایل: چیدمان فعلاً ساده و عمودی است. استایل‌دهی راست‌چین را در درس «طراحی رابط کاربری فارسی و راست‌چین DevNotes» انجام می‌دهیم.

خروجی و رفتار مورد انتظار

  1. سرور را با npm run dev اجرا کنید (اگر قبلاً اجرا بود و صفحه‌ای ۴۰۴ داد، یک‌بار آن را متوقف و دوباره اجرا کنید).
  2. در / هدر با چهار لینک و دکمه‌ی «مشاهده‌ی نوشته‌ها» را ببینید.
  3. از هدر به /posts و /about بروید؛ هدر و فوتر ثابت می‌مانند.
  4. از هدر روی «ورود» بزنید؛ در /login هدر و فوتر عمومی نیستند و فقط لینک بازگشت دیده می‌شود.
  5. آدرس /dashboard را باز کنید؛ منوی کناری داشبورد را می‌بینید و با لینک‌های آن به /dashboard/posts و /dashboard/posts/new می‌روید.
  6. آدرس /posts/new را باز کنید؛ حالا صفحه‌ی ۴۰۴ فارسی نمایش داده می‌شود.

نکته‌های مهم

  • لینک داخلی را با Link بنویسید. اگر برای لینک داخلی از تگ <a> استفاده کنید، ESLint در Next.js هشدار می‌دهد و ناوبری سریع هم از بین می‌رود.
  • برای داشبورد هنوز محافظتی نداریم. هر کسی می‌تواند /dashboard را باز کند. این محدودیت آموزشی و موقتی است و در فصل‌های امنیتی برطرف می‌شود.
  • مسیرهای پویا را هنوز نساخته‌ایم. صفحه‌ی جزئیات نوشته به params نیاز دارد و موضوع فصل بعد است.
  • در تغییرات بعدی ساختار، دلیل و فایل‌های متأثر را بررسی کنید. مثل همین درس که به‌خاطر انتقال صفحه‌ی ایجاد نوشته، چند فایل را هم‌زمان تغییر دادیم.

جمع‌بندی

  • اسکلت DevNotes از سه Route Group ساخته شده: (public) برای سایت، (auth) برای ورود و ثبت‌نام و (dashboard) برای مدیریت.
  • هر گروه Layout مخصوص خودش را دارد و Root Layout فقط ساختار HTML را نگه می‌دارد.
  • صفحه‌ی ایجاد نوشته به /dashboard/posts/new منتقل شد، چون به کاربر واردشده تعلق دارد.
  • برای لینک داخلی از Link استفاده کردیم و جزئیات آن در درس بعدی فصل «Routing و Navigation» می‌آید.
  • هنوز داده، فرم، استایل، مسیر پویا و کنترل دسترسی نداریم؛ این‌ها در فصل‌های بعد به همین ساختار اضافه می‌شوند.
  • در درس بعد، «آزمون فصل دوم»، دانسته‌های این فصل را می‌سنجیم.