mirror of
https://github.com/ershisan99/www.git
synced 2026-01-26 05:12:07 +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 { Label } from '@/components/ui/label'
|
||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
import { api } from '@/trpc/react'
|
import { api } from '@/trpc/react'
|
||||||
import { useParams } from 'next/navigation'
|
import { useParams } from 'next/navigation'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
@@ -35,12 +42,16 @@ export default function EditBlogPostPage() {
|
|||||||
const [excerpt, setExcerpt] = useState('')
|
const [excerpt, setExcerpt] = useState('')
|
||||||
const [published, setPublished] = useState(false)
|
const [published, setPublished] = useState(false)
|
||||||
const [updateSlug, setUpdateSlug] = useState(false)
|
const [updateSlug, setUpdateSlug] = useState(false)
|
||||||
|
const [authorId, setAuthorId] = useState<string | undefined>(undefined)
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
// Fetch post data
|
// Fetch post data
|
||||||
const { data: posts, isLoading: isFetching } = api.blog.getAll.useQuery()
|
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(() => {
|
useEffect(() => {
|
||||||
if (posts) {
|
if (posts) {
|
||||||
const currentPost = posts.find((p) => p.id === id)
|
const currentPost = posts.find((p) => p.id === id)
|
||||||
@@ -49,6 +60,7 @@ export default function EditBlogPostPage() {
|
|||||||
setContent(currentPost.content)
|
setContent(currentPost.content)
|
||||||
setExcerpt(currentPost.excerpt || '')
|
setExcerpt(currentPost.excerpt || '')
|
||||||
setPublished(currentPost.published)
|
setPublished(currentPost.published)
|
||||||
|
setAuthorId(currentPost.authorId)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
} else {
|
} else {
|
||||||
toast.error('Blog post not found')
|
toast.error('Blog post not found')
|
||||||
@@ -104,6 +116,7 @@ export default function EditBlogPostPage() {
|
|||||||
excerpt: excerpt || undefined,
|
excerpt: excerpt || undefined,
|
||||||
published,
|
published,
|
||||||
updateSlug,
|
updateSlug,
|
||||||
|
authorId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +185,31 @@ export default function EditBlogPostPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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'>
|
<div className='space-y-2'>
|
||||||
<Label htmlFor='content'>Content</Label>
|
<Label htmlFor='content'>Content</Label>
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ import { Textarea } from '@/components/ui/textarea'
|
|||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { MarkdownEditor } from '@/components/markdown-editor'
|
import { MarkdownEditor } from '@/components/markdown-editor'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select'
|
||||||
|
|
||||||
export default function NewBlogPostPage() {
|
export default function NewBlogPostPage() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -17,8 +24,12 @@ export default function NewBlogPostPage() {
|
|||||||
const [content, setContent] = useState('')
|
const [content, setContent] = useState('')
|
||||||
const [excerpt, setExcerpt] = useState('')
|
const [excerpt, setExcerpt] = useState('')
|
||||||
const [published, setPublished] = useState(false)
|
const [published, setPublished] = useState(false)
|
||||||
|
const [authorId, setAuthorId] = useState<string | undefined>(undefined)
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
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({
|
const createPost = api.blog.create.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success('Blog post created successfully')
|
toast.success('Blog post created successfully')
|
||||||
@@ -51,6 +62,7 @@ export default function NewBlogPostPage() {
|
|||||||
content,
|
content,
|
||||||
excerpt: excerpt || undefined,
|
excerpt: excerpt || undefined,
|
||||||
published,
|
published,
|
||||||
|
authorId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +93,31 @@ export default function NewBlogPostPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="content">Content</Label>
|
<Label htmlFor="content">Content</Label>
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
|
|||||||
@@ -20,6 +20,19 @@ function generateSlug(title: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const blogRouter = createTRPCRouter({
|
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)
|
// Get all published blog posts (public)
|
||||||
getAllPublished: publicProcedure.query(async () => {
|
getAllPublished: publicProcedure.query(async () => {
|
||||||
const posts = await db.query.blogPosts.findMany({
|
const posts = await db.query.blogPosts.findMany({
|
||||||
@@ -90,6 +103,7 @@ export const blogRouter = createTRPCRouter({
|
|||||||
content: z.string().min(1),
|
content: z.string().min(1),
|
||||||
excerpt: z.string().optional(),
|
excerpt: z.string().optional(),
|
||||||
published: z.boolean().default(false),
|
published: z.boolean().default(false),
|
||||||
|
authorId: z.string().optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
@@ -115,7 +129,7 @@ export const blogRouter = createTRPCRouter({
|
|||||||
content: input.content,
|
content: input.content,
|
||||||
excerpt: input.excerpt || null,
|
excerpt: input.excerpt || null,
|
||||||
published: input.published,
|
published: input.published,
|
||||||
authorId: ctx.session.user.id,
|
authorId: input.authorId || ctx.session.user.id,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
|
|
||||||
@@ -132,6 +146,7 @@ export const blogRouter = createTRPCRouter({
|
|||||||
excerpt: z.string().optional(),
|
excerpt: z.string().optional(),
|
||||||
published: z.boolean(),
|
published: z.boolean(),
|
||||||
updateSlug: z.boolean().default(false),
|
updateSlug: z.boolean().default(false),
|
||||||
|
authorId: z.string().optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
@@ -171,6 +186,7 @@ export const blogRouter = createTRPCRouter({
|
|||||||
content: input.content,
|
content: input.content,
|
||||||
excerpt: input.excerpt || null,
|
excerpt: input.excerpt || null,
|
||||||
published: input.published,
|
published: input.published,
|
||||||
|
...(input.authorId && { authorId: input.authorId }),
|
||||||
})
|
})
|
||||||
.where(eq(blogPosts.id, input.id))
|
.where(eq(blogPosts.id, input.id))
|
||||||
.returning()
|
.returning()
|
||||||
|
|||||||
Reference in New Issue
Block a user