Appearance
Admin CRUD
This chapter builds the admin area for creating and managing posts, categories, and tags.
What you will build
/admin/admin/posts/admin/categories/admin/tags- server actions for create, update, and delete
Files you will create
txt
app/admin/layout.tsx
app/admin/page.tsx
app/admin/posts/page.tsx
app/admin/categories/page.tsx
app/admin/tags/page.tsx
app/admin/actions.tsThe finished example uses:
apps/blog-next/app/admin/page.tsxapps/blog-next/app/admin/posts/page.tsxapps/blog-next/app/admin/categories/page.tsxapps/blog-next/app/admin/tags/page.tsxapps/blog-next/app/admin/actions.ts
Admin dashboard data
ts
export async function getAdminDashboardData() {
const [postCount, publishedCount, categoryCount, tagCount] = await Promise.all([
Post.count(),
Post.where('status', 'published').count(),
Category.count(),
Tag.count(),
])
return {
postCount,
publishedCount,
categoryCount,
tagCount,
}
}Server action flow
Every admin write should:
- read the current auth state
- redirect guests to login
- authorize the action
- validate input
- call the server-domain function
- redirect back to the admin screen
ts
'use server'
import { redirect } from 'next/navigation'
import { auth } from '@holo-js/auth/next/server'
import { authorize } from '@holo-js/authorization'
import { validate } from '@holo-js/forms'
import { postForm } from '@/lib/schemas/blog'
import { createPost } from '@/server/lib/blog'
import Post from '@/server/models/Post'
export async function createPostAction(formData: FormData) {
const currentAuth = await auth()
if (!currentAuth.authenticated || !currentAuth.user) {
redirect('/login')
}
await authorize('create', Post)
const data = await validate(formData, postForm)
await createPost({
...data,
authorId: currentAuth.user.id,
tagIds: data.tagIds.join(','),
})
redirect('/admin/posts')
}Checkpoint
The admin area can create, update, and delete blog content. The next chapter adds strict form validation and upload validation.