قسمت بیست و سوم - تمرین عملی ساخت مدیریت کامل پستها در داشبورد ادمین
در این آموزش از شما مخاطبین عزیز دعوت میشه تا دست به کار بشید و تمرین عملی این بخش رو انجام بدید!
تمرین عملی ساخت مدیریت کامل پستها در داشبورد ادمین
تا اینجای مسیر، ما چند بخش مهم از پنل ادمین را کنار هم ساختهایم:
- احراز هویت ادمین
- محافظت از مسیرهای داشبورد
- ساختار کلی پنل مدیریت
- مدیریت کامل پروژهها در داشبورد
در قسمت قبل، ما ماژول Projects را بهصورت کامل پیادهسازی کردیم؛
یعنی ادمین میتوانست:
- لیست پروژهها را ببیند
- پروژه جدید بسازد
- پروژه موجود را ویرایش کند
- پروژهای را حذف کند
یعنی من در این پست:
- ساختار مسیرها را مشخص میکنم
- فایلهای لازم را مشخص میکنم
- مسئولیت هر بخش را توضیح میدهم
- نکات مهم پیادهسازی را میگویم
- و مسیر حل را کاملاً روشن میکنم
اما این بار، بخش اصلی پیادهسازی را شما انجام میدهید.
هدف این است که فقط کد را تکرار نکنید، بلکه الگوی ساخت یک ماژول مدیریتی کامل را واقعاً یاد بگیرید.
هدف این تمرین چیست؟
در پایان این تمرین، باید بتوانید ماژول کامل مدیریت پستها را در داشبورد ادمین بسازید.
یعنی این قابلیتها را داشته باشید:
- نمایش لیست پستها در داشبورد
- ساخت پست جدید
- ویرایش پست موجود
- حذف پست
- اتصال فرمها به API
- اعتبارسنجی دادهها
- محافظت از صفحات ادمین و endpointهای مدیریتی
اگر قسمت مدیریت پروژهها را کامل انجام داده باشید، این تمرین دقیقاً بهترین فرصت برای تثبیت همان الگو است.
قرار است چه مسیرهایی را بسازید؟
در این تمرین، باید این مسیرها را داشته باشید:
/admin/posts
/admin/posts/new
/admin/posts/[id]/edit
و در بخش API هم این endpointها را:
/api/posts
/api/posts/[id]
اگر به مدیریت پروژهها نگاه کنید، متوجه میشوید که ساختار تقریباً یکسان است.
تفاوت اصلی فقط در موجودیت داده است:
- آنجا با
projectکار میکردیم - اینجا با
postکار میکنیم
از ماژول پروژهها بهعنوان الگو استفاده کنید
بهترین روش برای انجام این تمرین این است که ماژول projects را الگوی مستقیم خودتان قرار دهید.
جدول زیر مسیر تبدیل را خیلی روشن میکند:
| ماژول پروژهها | ماژول پستها |
|---|---|
ProjectsTable | PostsTable |
ProjectForm | PostForm |
DeleteProjectButton | DeletePostButton |
/admin/projects | /admin/posts |
/admin/projects/new | /admin/posts/new |
/admin/projects/[id]/edit | /admin/posts/[id]/edit |
POST /api/projects | POST /api/posts |
PATCH /api/projects/[id] | PATCH /api/posts/[id] |
DELETE /api/projects/[id] | DELETE /api/posts/[id] |
اگر این جدول را جلوی خودتان بگذارید، عملاً مسیر پیادهسازی کاملاً مشخص میشود.
مدل داده پستها را بررسی کنید
قبل از اینکه وارد ساخت صفحه و فرم شوید، اول باید مدل Post خودتان را ببینید.
یک مدل رایج ممکن است چیزی شبیه این باشد:
prisma
model Post {
id String @id @default(cuid())
title String
slug String @unique
excerpt String?
content String
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
ممکن است در پروژه شما فیلدهای بیشتری هم وجود داشته باشد، مثل:
coverImagetagsreadingTimecategory
اما برای اصل این تمرین، کافی است روی فیلدهای پایه تمرکز کنید:
titleslugexcerptcontentpublished
قبل از شروع، به این سؤالها فکر کنید
برای اینکه این تمرین فقط تبدیل فایلها نباشد، قبل از شروع این سؤالها را از خودتان بپرسید:
- کدام فیلدها باید در جدول لیست پستها نمایش داده شوند؟
- کدام فیلدها باید داخل فرم باشند؟
- آیا
excerptاختیاری است؟ - آیا
slugباید unique باشد؟ - در صفحه ویرایش، اگر پست پیدا نشد چه باید اتفاقی بیفتد؟
- بعد از حذف موفق پست، چطور UI را بهروزرسانی میکنید؟
ساختار فایلهایی که باید بسازید
برای این تمرین، انتظار میرود ساختاری شبیه این داشته باشید:
app/
admin/
(dashboard)/
posts/
page.tsx
new/
page.tsx
[id]/
edit/
page.tsx
api/
posts/
route.ts
[id]/
route.ts
components/
admin/
posts-table.tsx
post-form.tsx
delete-post-button.tsx
lib/
validations/
post.ts
اگر ساختار پروژه شما کمی متفاوت است، ایرادی ندارد.
مهم این است که این مسئولیتها را در بخشهای مناسب نگه دارید.
مسئولیت هر فایل چیست؟
در این بخش، دقیقاً مشخص میکنیم که هر فایل چه کاری باید انجام دهد.
این قسمت مهمترین نقشه اجرایی تمرین شماست.
1. فایل app/admin/(dashboard)/posts/page.tsx
این فایل باید صفحه اصلی مدیریت پستها باشد.
وظایف آن:
- فقط برای ادمین قابل دسترسی باشد
- با Prisma لیست پستها را از دیتابیس بخواند
- دادهها را مرتبسازی کند
- کامپوننت جدول پستها را رندر کند
- یک لینک برای ساخت پست جدید داشته باشد
نکته
مثل صفحه /admin/projects عمل کنید.
بهتر است فقط فیلدهایی را select کنید که واقعاً برای جدول لازم هستند، مثلاً:
idtitleslugexcerptpublishedupdatedAt
2. فایل components/admin/posts-table.tsx
این کامپوننت مسئول نمایش دادهها در جدول است.
وظایف آن:
- اگر پستی وجود نداشت، یک empty state مناسب نمایش دهد
- برای هر پست، اطلاعات کلیدی را نشان دهد
- وضعیت انتشار را مشخص کند
- لینک
Editداشته باشد - دکمه
Deleteداشته باشد
پیشنهاد ستونها
برای شروع، این ستونها کافی هستند:
- Title
- Slug
- Excerpt
- Status
- Updated
- Actions
نکته
اگر excerpt طولانی است، آن را truncate کنید تا جدول مرتب بماند.
3. فایل components/admin/post-form.tsx
این مهمترین بخش تمرین شماست.
باید یک فرم مشترک بسازید که هم برای ساخت و هم برای ویرایش پست استفاده شود.
این فرم باید:
- در حالت
createکار کند - در حالت
editکار کند initialValuesبگیرد- وضعیت submit را مدیریت کند
- خطاها را نشان دهد
- بسته به mode درخواست
POSTیاPATCHبفرستد - بعد از موفقیت کاربر را به
/admin/postsبرگرداند
فیلدهای پایه فرم
برای شروع، این فیلدها را قرار دهید:
titleslugexcerptcontentpublished
نکته
همان الگوی mode: 'create' | 'edit' که در فرم پروژهها داشتید، اینجا هم کاملاً مناسب است.
4. فایل app/admin/(dashboard)/posts/new/page.tsx
این صفحه وظیفه ساخت پست جدید را دارد.
وظایف آن:
- ادمین بودن کاربر را بررسی کند
- عنوان صفحه را نمایش دهد
- لینک بازگشت به لیست پستها داشته باشد
PostFormرا در حالتcreateرندر کند
نکته
این صفحه باید بسیار ساده باشد، چون منطق اصلی داخل PostForm قرار میگیرد.
5. فایل app/admin/(dashboard)/posts/[id]/edit/page.tsx
این صفحه برای ویرایش یک پست موجود است.
وظایف آن:
- ادمین بودن کاربر را بررسی کند
idرا از مسیر بگیرد- پست را از دیتابیس بخواند
- اگر پست وجود نداشت،
notFound()را صدا بزند - فرم را با
initialValuesپر کند
نکته
در این صفحه فقط باید داده لازم برای ویرایش را بخوانید، نه همه فیلدهای ممکن مدل را.
6. فایل components/admin/delete-post-button.tsx
این کامپوننت باید Client Component باشد.
وظایف آن:
- کلیک کاربر را مدیریت کند
- قبل از حذف، تأیید بگیرد
- درخواست
DELETEبه API بفرستد - در صورت موفقیت، جدول را refresh کند
- در صورت خطا، پیام مناسب نمایش دهد
نکته
اگر در ماژول پروژهها از window.confirm() و router.refresh() استفاده کردهاید، اینجا هم همان الگو کاملاً مناسب است.
7. فایل app/api/posts/route.ts
این فایل مسئول ساخت پست جدید است.
وظایف آن:
- دسترسی ادمین را بررسی کند
request.json()را بخواند- داده را با
postSchemaاعتبارسنجی کند - تکراری نبودن
slugرا بررسی کند - پست را در دیتابیس ایجاد کند
- status code مناسب برگرداند
status codeهای پیشنهادی
201برای ساخت موفق400برای داده نامعتبر409برایslugتکراری500برای خطای سرور
8. فایل app/api/posts/[id]/route.ts
این فایل باید هم ویرایش و هم حذف را مدیریت کند.
برای PATCH
- دسترسی ادمین را بررسی کنید
- داده را اعتبارسنجی کنید
- وجود پست را بررسی کنید
slugتکراری را برای حالت ویرایش کنترل کنید- پست را بهروزرسانی کنید
برای DELETE
- دسترسی ادمین را بررسی کنید
- وجود پست را بررسی کنید
- پست را حذف کنید
- پاسخ مناسب برگردانید
status codeهای پیشنهادی
200برای موفقیت400برای داده نامعتبر404اگر پست پیدا نشد409برای تداخل اسلاگ500برای خطای سرور
schema اعتبارسنجی پستها
برای اینکه تمرکز تمرین روی CRUD و داشبورد باقی بماند، در این قسمت schema را بهصورت کامل در اختیار شما میگذارم.
ts import { z } from 'zod'
export const postSchema = z.object({ title: z.string().trim().min(1, 'Title is required').max(160), slug: z .string() .trim() .min(1, 'Slug is required') .regex(/^[a-z0-9-]+$/, 'Slug format is invalid'), excerpt: z.string().trim().max(300).optional().or(z.literal('')), content: z.string().trim().min(1, 'Content is required'), published: z.boolean(), })
export type PostInput = z.infer<typeof postSchema>
این schema چه چیزی را تضمین میکند؟
- عنوان خالی نباشد
- اسلاگ خالی نباشد
- اسلاگ فرمت قابلقبول داشته باشد
excerptدر صورت وجود بیش از حد طولانی نباشد- محتوا خالی نباشد
publishedمقدار بولی داشته باشد
ترتیب پیادهسازی پیشنهادی
اگر میخواهید این تمرین را بدون سردرگمی جلو ببرید، این ترتیب بهترین انتخاب است:
مرحله 1
ابتدا postSchema را بسازید.
مرحله 2
صفحه لیست پستها یعنی /admin/posts را پیادهسازی کنید.
مرحله 3
کامپوننت PostsTable را بسازید.
مرحله 4
صفحه ساخت پست جدید و فرم PostForm را آماده کنید.
مرحله 5
endpoint ساخت پست یعنی POST /api/posts را پیادهسازی کنید.
مرحله 6
صفحه ویرایش پست را بسازید.
مرحله 7
endpoint ویرایش یعنی PATCH /api/posts/[id] را کامل کنید.
مرحله 8
دکمه حذف و endpoint حذف را اضافه کنید.
این ترتیب باعث میشود از سادهتر به کاملتر حرکت کنید.
Hint برای صفحه لیست پستها
اگر نمیدانید از کجا شروع کنید، این نکتهها راه را روشن میکنند:
- از
requireAdmin()استفاده کنید - با Prisma از
findMany()کمک بگیرید - رکوردها را بر اساس
createdAtیاupdatedAtمرتب کنید - نتیجه را به
PostsTableپاس بدهید - یک دکمه یا لینک برای
New Postاضافه کنید
Hint برای جدول پستها
postsرا بهعنوان prop دریافت کنید- اگر آرایه خالی بود، empty state نمایش دهید
- برای هر ردیف،
EditوDeleteداشته باشید updatedAtرا فرمت کنید- وضعیت
publishedرا با یک badge ساده نشان دهید
Hint برای فرم پستها
- این کامپوننت باید
use clientباشد - از
useState()برای فیلدها استفاده کنید initialValuesرا برای حالت ویرایش پشتیبانی کنید- از یک prop مثل
modeاستفاده کنید - در حالت
createدرخواست را به/api/postsبفرستید - در حالت
editدرخواست را به/api/posts/[id]بفرستید - بعد از موفقیت از
router.push('/admin/posts')وrouter.refresh()استفاده کنید
Hint برای صفحه ویرایش
idرا ازparamsبگیرید- پست را با
findUnique()بخوانید - اگر وجود نداشت،
notFound()را اجرا کنید - داده اولیه را به فرم پاس بدهید
Hint برای حذف پست
DeletePostButtonباید Client Component باشد پاسخ قبل از حذف از کاربر تأیید بگیرید- درخواست
DELETEبفرستید - اگر پاسخ موفق بود،
router.refresh()را صدا بزنید - اگر خطا رخ داد، پیام خطا نمایش دهید
Hint برای ساخت پست در API
در handler مربوط به POST این ترتیب را رعایت کنید:
- بررسی session ادمین
- خواندن body
- اعتبارسنجی با
safeParse() - بررسی unique بودن
slug - ساخت پست با Prisma
- برگرداندن پاسخ با status مناسب
Hint برای ویرایش پست در API
در handler مربوط به PATCH به این نکات دقت کنید:
- اول مطمئن شوید پست موردنظر وجود دارد
- اگر
slugتغییر کرده، بررسی کنید پست دیگری از آن استفاده نکند - فقط بعد از این بررسیها عملیات update را انجام دهید
Hint برای حذف پست در API
در handler مربوط به DELETE:
- اول دسترسی ادمین را چک کنید
- بعد وجود پست را بررسی کنید
- اگر پست وجود داشت، آن را حذف کنید
- اگر وجود نداشت،
$404$برگردانید
چند سؤال مهم برای فکر کردن
قبل از اینکه سراغ کدنویسی بروید، این چند سؤال را برای خودتان پاسخ دهید:
1. چرا باید هم صفحههای ادمین و هم APIها محافظت شوند؟
اگر فقط UI محافظت شود ولی API آزاد باشد، چه اتفاقی میافتد؟
2. چرا بهتر است فرم ساخت و ویرایش مشترک باشد؟
اگر دو فرم جدا بنویسید، چه مشکلی در نگهداری کد پیش میآید؟
3. در حالت ویرایش، چرا بررسی slug باید با دقت بیشتری انجام شود؟
چه فرقی بین ساخت پست جدید و ویرایش یک پست موجود از نظر uniqueness وجود دارد؟
4. اگر کاربر مستقیم آدرس /admin/posts/[id]/edit را باز کند و id معتبر نباشد، چه باید نمایش داده شود؟
5. بعد از حذف موفق پست، چرا router.refresh() انتخاب مناسبی است؟
این سؤالها کمک میکنند فقط به خروجی نهایی فکر نکنید، بلکه منطق پشت پیادهسازی را هم بفهمید.
چکلیست تمرین
اگر این موارد را کامل کردهاید، یعنی تمرین را درست انجام دادهاید:
- مسیر
/admin/postsساخته شده است - لیست پستها از دیتابیس خوانده میشود
- empty state برای نبود پستها دارید
- صفحه
/admin/posts/newکار میکند - صفحه
/admin/posts/[id]/editداده پست را لود میکند - فرم مشترک برای create و edit ساختهاید
- endpoint ساخت پست پیادهسازی شده است
- endpoint ویرایش پست پیادهسازی شده است
- endpoint حذف پست پیادهسازی شده است
-
slugتکراری کنترل میشود - همه routeهای ادمین و APIها محافظت شدهاند
- بعد از عملیات موفق، UI درست بهروزرسانی میشود
اگر همه این موارد تیک خورده باشند، شما عملاً ماژول مدیریت پستها را کامل کردهاید.
این دقیقاً همان چیزی است که در پروژههای واقعی هم اتفاق میافتد.
این تمرین چرا مهم است؟
شاید در نگاه اول این قسمت فقط تکرار ماژول پروژهها برای پستها به نظر برسد.
اما در عمل، این یکی از مهمترین مراحل یادگیری شماست.
چون اگر بتوانید بدون کپیکردن مستقیم، همان الگو را روی یک موجودیت جدید پیاده کنید، یعنی:
- ساختار App Router را فهمیدهاید
- ارتباط بین page و component و API را فهمیدهاید
- منطق CRUD در داشبورد را یاد گرفتهاید
- و میتوانید بعداً ماژولهای دیگری مثل:
- تجربیات
- مقالات
- سرویسها
- مهارتها
- گواهینامهها
را هم با همین الگو بسازید
جمعبندی
در این قسمت، بهجای یک پیادهسازی کامل آماده، یک تمرین هدایتشده برای ساخت ماژول مدیریت پستها در داشبورد ادمین طراحی کردیم.
در این تمرین مشخص شد که:
- چه مسیرهایی باید ساخته شوند
- چه فایلهایی لازم هستند
- هر فایل چه مسئولیتی دارد
- فرم باید چگونه طراحی شود
- APIها چه رفتاری باید داشته باشند
- اعتبارسنجی چگونه انجام شود
- و چه نکاتی برای محافظت و مدیریت خطا مهم هستند
تمرین این قسمت
ماژول کامل مدیریت پستها را با استفاده از الگوی ماژول پروژهها پیادهسازی کنید.
اگر خواستید تمرین را کاملتر کنید، یکی از این قابلیتها را هم به آن اضافه کنید:
- تصویر شاخص
- تگها
- زمان مطالعه
- اطلاعات SEO
این بخش اختیاری است، اما برای عمیقتر شدن یادگیری شما بسیار مفید خواهد بود.