feat(blog): wire comment moderation and writers
This commit is contained in:
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { ArrowLeft, ArrowRight, FolderUp, ImageUp, Loader2, Save, Send, Trash2 } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Markdown from "@/components/Markdown";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
@@ -27,6 +28,7 @@ const emptyForm: Types.PostCreateSchema = {
|
||||
excerpt: "",
|
||||
category_id: null,
|
||||
tag_ids: [],
|
||||
writer_ids: [],
|
||||
status: "draft",
|
||||
is_featured: false,
|
||||
seo_title: "",
|
||||
@@ -40,12 +42,14 @@ const emptyForm: Types.PostCreateSchema = {
|
||||
|
||||
export default function AdminBlogEditor({ postId }: Props) {
|
||||
const router = useRouter();
|
||||
const { user } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const featuredInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [form, setForm] = useState<Types.PostCreateSchema>(emptyForm);
|
||||
const [post, setPost] = useState<Types.PostDetailSchema | null>(null);
|
||||
const [categories, setCategories] = useState<Types.CategorySchema[]>([]);
|
||||
const [tags, setTags] = useState<Types.TagSchema[]>([]);
|
||||
const [users, setUsers] = useState<NonNullable<Types.PostListSchema["writers"]>>([]);
|
||||
const [loading, setLoading] = useState(Boolean(postId));
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [uploadingFeatured, setUploadingFeatured] = useState(false);
|
||||
@@ -53,6 +57,7 @@ export default function AdminBlogEditor({ postId }: Props) {
|
||||
const isNew = postId == null;
|
||||
const featuredImage = post?.absolute_featured_image_preview_url || post?.absolute_featured_image_url || post?.featured_image;
|
||||
const canPersistPost = form.title.trim() && form.content.trim();
|
||||
const canAssignWriters = Boolean(user?.is_staff || user?.is_superuser || user?.can_review_blog_posts);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([api.getCategories(), api.getTags()])
|
||||
@@ -63,6 +68,13 @@ export default function AdminBlogEditor({ postId }: Props) {
|
||||
.catch(() => undefined);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canAssignWriters) return;
|
||||
api.listBlogWriters()
|
||||
.then((data) => setUsers(data))
|
||||
.catch(() => undefined);
|
||||
}, [canAssignWriters]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!postId) return;
|
||||
setLoading(true);
|
||||
@@ -75,6 +87,7 @@ export default function AdminBlogEditor({ postId }: Props) {
|
||||
excerpt: data.excerpt ?? "",
|
||||
category_id: data.category?.id ?? null,
|
||||
tag_ids: data.tags.map((tag) => tag.id),
|
||||
writer_ids: data.writers?.map((writer) => writer.id) ?? [data.author.id],
|
||||
status: data.status as Types.PostCreateSchema["status"],
|
||||
is_featured: data.is_featured,
|
||||
seo_title: data.seo_title ?? "",
|
||||
@@ -97,6 +110,7 @@ export default function AdminBlogEditor({ postId }: Props) {
|
||||
}, [postId, toast]);
|
||||
|
||||
const selectedTagIds = useMemo(() => form.tag_ids ?? [], [form.tag_ids]);
|
||||
const selectedWriterIds = useMemo(() => form.writer_ids ?? [], [form.writer_ids]);
|
||||
|
||||
const updateForm = <K extends keyof Types.PostCreateSchema>(key: K, value: Types.PostCreateSchema[K]) => {
|
||||
setForm((prev) => ({ ...prev, [key]: value }));
|
||||
@@ -322,6 +336,37 @@ export default function AdminBlogEditor({ postId }: Props) {
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{canAssignWriters ? (
|
||||
<div>
|
||||
<Label className="mb-2 block text-right">نویسندگان</Label>
|
||||
<div className="flex flex-wrap justify-end gap-2">
|
||||
{users.map((writer) => {
|
||||
const selected = selectedWriterIds.includes(writer.id);
|
||||
const fullName = [writer.first_name, writer.last_name].filter(Boolean).join(" ") || writer.username;
|
||||
return (
|
||||
<Button
|
||||
key={writer.id}
|
||||
type="button"
|
||||
size="sm"
|
||||
variant={selected ? "default" : "outline"}
|
||||
onClick={() => {
|
||||
updateForm(
|
||||
"writer_ids",
|
||||
selected ? selectedWriterIds.filter((id) => id !== writer.id) : [...selectedWriterIds, writer.id],
|
||||
);
|
||||
}}
|
||||
>
|
||||
{fullName}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className="mt-2 text-right text-xs text-muted-foreground">
|
||||
مالک اصلی نوشته تغییر نمیکند؛ این گزینه فقط لیست نویسندگان عمومی را تنظیم میکند.
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user