mirror of
https://github.com/ershisan99/www.git
synced 2025-12-28 20:59:27 +00:00
add author selection to blog create/edit pages and backend improvements for author handling
This commit is contained in:
@@ -17,6 +17,13 @@ import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { api } from '@/trpc/react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useRouter } from 'next/navigation'
|
||||
@@ -35,12 +42,16 @@ export default function EditBlogPostPage() {
|
||||
const [excerpt, setExcerpt] = useState('')
|
||||
const [published, setPublished] = useState(false)
|
||||
const [updateSlug, setUpdateSlug] = useState(false)
|
||||
const [authorId, setAuthorId] = useState<string | undefined>(undefined)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
// Fetch post data
|
||||
const { data: posts, isLoading: isFetching } = api.blog.getAll.useQuery()
|
||||
|
||||
// Fetch all users for author selection
|
||||
const { data: users, isLoading: isLoadingUsers } = api.blog.getAllUsers.useQuery()
|
||||
|
||||
useEffect(() => {
|
||||
if (posts) {
|
||||
const currentPost = posts.find((p) => p.id === id)
|
||||
@@ -49,6 +60,7 @@ export default function EditBlogPostPage() {
|
||||
setContent(currentPost.content)
|
||||
setExcerpt(currentPost.excerpt || '')
|
||||
setPublished(currentPost.published)
|
||||
setAuthorId(currentPost.authorId)
|
||||
setIsLoading(false)
|
||||
} else {
|
||||
toast.error('Blog post not found')
|
||||
@@ -104,6 +116,7 @@ export default function EditBlogPostPage() {
|
||||
excerpt: excerpt || undefined,
|
||||
published,
|
||||
updateSlug,
|
||||
authorId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -172,6 +185,31 @@ export default function EditBlogPostPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='author'>Author</Label>
|
||||
<Select
|
||||
value={authorId}
|
||||
onValueChange={setAuthorId}
|
||||
>
|
||||
<SelectTrigger id='author'>
|
||||
<SelectValue placeholder='Select an author' />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{isLoadingUsers ? (
|
||||
<SelectItem value='loading' disabled>
|
||||
Loading...
|
||||
</SelectItem>
|
||||
) : (
|
||||
users?.map((user) => (
|
||||
<SelectItem key={user.id} value={user.id}>
|
||||
{user.name}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='content'>Content</Label>
|
||||
<MarkdownEditor
|
||||
|
||||
@@ -10,6 +10,13 @@ import { Textarea } from '@/components/ui/textarea'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { MarkdownEditor } from '@/components/markdown-editor'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
|
||||
export default function NewBlogPostPage() {
|
||||
const router = useRouter()
|
||||
@@ -17,8 +24,12 @@ export default function NewBlogPostPage() {
|
||||
const [content, setContent] = useState('')
|
||||
const [excerpt, setExcerpt] = useState('')
|
||||
const [published, setPublished] = useState(false)
|
||||
const [authorId, setAuthorId] = useState<string | undefined>(undefined)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
// Fetch all users for author selection
|
||||
const { data: users, isLoading: isLoadingUsers } = api.blog.getAllUsers.useQuery()
|
||||
|
||||
const createPost = api.blog.create.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success('Blog post created successfully')
|
||||
@@ -33,31 +44,32 @@ export default function NewBlogPostPage() {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
|
||||
if (!title.trim()) {
|
||||
toast.error('Title is required')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!content.trim()) {
|
||||
toast.error('Content is required')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
setIsSubmitting(true)
|
||||
|
||||
|
||||
createPost.mutate({
|
||||
title,
|
||||
content,
|
||||
excerpt: excerpt || undefined,
|
||||
published,
|
||||
authorId,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container py-10">
|
||||
<h1 className="mb-8 text-4xl font-bold">Create New Blog Post</h1>
|
||||
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="title">Title</Label>
|
||||
@@ -69,7 +81,7 @@ export default function NewBlogPostPage() {
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="excerpt">Excerpt (optional)</Label>
|
||||
<Textarea
|
||||
@@ -80,7 +92,32 @@ export default function NewBlogPostPage() {
|
||||
className="h-24"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="author">Author</Label>
|
||||
<Select
|
||||
value={authorId}
|
||||
onValueChange={setAuthorId}
|
||||
>
|
||||
<SelectTrigger id="author">
|
||||
<SelectValue placeholder="Select an author (defaults to you)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{isLoadingUsers ? (
|
||||
<SelectItem value="loading" disabled>
|
||||
Loading...
|
||||
</SelectItem>
|
||||
) : (
|
||||
users?.map((user) => (
|
||||
<SelectItem key={user.id} value={user.id}>
|
||||
{user.name}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="content">Content</Label>
|
||||
<MarkdownEditor
|
||||
@@ -90,7 +127,7 @@ export default function NewBlogPostPage() {
|
||||
minHeight="500px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="published"
|
||||
@@ -99,7 +136,7 @@ export default function NewBlogPostPage() {
|
||||
/>
|
||||
<Label htmlFor="published">Publish immediately</Label>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
type="submit"
|
||||
@@ -118,4 +155,4 @@ export default function NewBlogPostPage() {
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,19 @@ function generateSlug(title: string): string {
|
||||
}
|
||||
|
||||
export const blogRouter = createTRPCRouter({
|
||||
// Get all users that can be authors (admin only)
|
||||
getAllUsers: adminProcedure.query(async () => {
|
||||
const allUsers = await db.query.users.findMany({
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
image: true,
|
||||
},
|
||||
orderBy: (users, { asc }) => [asc(users.name)],
|
||||
})
|
||||
return allUsers
|
||||
}),
|
||||
|
||||
// Get all published blog posts (public)
|
||||
getAllPublished: publicProcedure.query(async () => {
|
||||
const posts = await db.query.blogPosts.findMany({
|
||||
@@ -90,6 +103,7 @@ export const blogRouter = createTRPCRouter({
|
||||
content: z.string().min(1),
|
||||
excerpt: z.string().optional(),
|
||||
published: z.boolean().default(false),
|
||||
authorId: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
@@ -115,7 +129,7 @@ export const blogRouter = createTRPCRouter({
|
||||
content: input.content,
|
||||
excerpt: input.excerpt || null,
|
||||
published: input.published,
|
||||
authorId: ctx.session.user.id,
|
||||
authorId: input.authorId || ctx.session.user.id,
|
||||
})
|
||||
.returning()
|
||||
|
||||
@@ -132,6 +146,7 @@ export const blogRouter = createTRPCRouter({
|
||||
excerpt: z.string().optional(),
|
||||
published: z.boolean(),
|
||||
updateSlug: z.boolean().default(false),
|
||||
authorId: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
@@ -171,6 +186,7 @@ export const blogRouter = createTRPCRouter({
|
||||
content: input.content,
|
||||
excerpt: input.excerpt || null,
|
||||
published: input.published,
|
||||
...(input.authorId && { authorId: input.authorId }),
|
||||
})
|
||||
.where(eq(blogPosts.id, input.id))
|
||||
.returning()
|
||||
|
||||
Reference in New Issue
Block a user