From 8a13ceeb9f0e2fa6dde9879403d5b93fc8b98823 Mon Sep 17 00:00:00 2001 From: Misbah374 Date: Mon, 19 Jan 2026 23:01:49 +0530 Subject: [PATCH] Routes are set to upload blog and research paper by self but DELETE method is not set --- src/app/api/blogs/[id]/route.ts | 27 ++ src/app/api/blogs/route.ts | 25 ++ src/app/api/paper/[id]/route.ts | 27 ++ src/app/api/paper/route.ts | 25 ++ src/app/u/[id]/edit/page.tsx | 2 +- src/components/EditProfileForm.tsx | 461 ++++++++++++++++++++++++++++- 6 files changed, 564 insertions(+), 3 deletions(-) create mode 100644 src/app/api/blogs/[id]/route.ts create mode 100644 src/app/api/paper/[id]/route.ts diff --git a/src/app/api/blogs/[id]/route.ts b/src/app/api/blogs/[id]/route.ts new file mode 100644 index 0000000..7b9a0d2 --- /dev/null +++ b/src/app/api/blogs/[id]/route.ts @@ -0,0 +1,27 @@ +/*import { NextRequest, NextResponse } from "next/server"; +import { auth } from "@/lib/auth"; + +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const session = await auth(); + + if (!session?.user?.id) { + return NextResponse.json( + { error: "Unauthorized" }, + { status: 401 } + ); + } + + const { id } = await params; + + return NextResponse.json({ success: true }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Failed to delete blog" }, + { status: 400 } + ); + } +}*/ diff --git a/src/app/api/blogs/route.ts b/src/app/api/blogs/route.ts index 549c388..9baa46d 100644 --- a/src/app/api/blogs/route.ts +++ b/src/app/api/blogs/route.ts @@ -1,14 +1,39 @@ import { NextResponse } from "next/server"; import { CreateBlogSchema } from "@/schemas/CreateBlogSchema"; import { createBlogs, getBlogs } from "@/actions/blogs"; +import { auth } from "@/lib/auth"; export async function POST(request: Request) { try { + // Check authentication + const session = await auth(); + + if (!session?.user?.id) { + return NextResponse.json( + { error: "Unauthorized. Please log in to create blogs." }, + { status: 401 }, + ); + } + const body = await request.json(); // Validate request body const validatedData = CreateBlogSchema.parse(body); + // Validate that all blogs have the authenticated user as author + for (const blog of validatedData) { + if (blog.authorId !== session.user.id) { + return NextResponse.json( + { + error: "Forbidden. You can only create blogs as yourself.", + providedAuthorId: blog.authorId, + authenticatedUserId: session.user.id + }, + { status: 403 }, + ); + } + } + // Create the blogs const blogs = await createBlogs(validatedData); diff --git a/src/app/api/paper/[id]/route.ts b/src/app/api/paper/[id]/route.ts new file mode 100644 index 0000000..6043d38 --- /dev/null +++ b/src/app/api/paper/[id]/route.ts @@ -0,0 +1,27 @@ +/*import { NextRequest, NextResponse } from "next/server"; +import { auth } from "@/lib/auth"; + +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const session = await auth(); + + if (!session?.user?.id) { + return NextResponse.json( + { error: "Unauthorized" }, + { status: 401 } + ); + } + + const { id } = await params; + + return NextResponse.json({ success: true }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : "Failed to delete research paper" }, + { status: 400 } + ); + } +}*/ diff --git a/src/app/api/paper/route.ts b/src/app/api/paper/route.ts index 623a3fe..b176a76 100644 --- a/src/app/api/paper/route.ts +++ b/src/app/api/paper/route.ts @@ -1,14 +1,39 @@ import { NextRequest, NextResponse } from "next/server"; import { CreatePaperSchema } from "@/schemas/CreatePaperSchema"; import { createPapers, fetchPapers } from "@/actions/papers"; +import { auth } from "@/lib/auth"; export async function POST(request: NextRequest) { try { + // Check authentication + const session = await auth(); + + if (!session?.user?.id) { + return NextResponse.json( + { error: "Unauthorized. Please log in to create research papers." }, + { status: 401 }, + ); + } + const body = await request.json(); // Validate request body const validatedData = CreatePaperSchema.parse(body); + // Validate that authenticated user is included as an author in all papers + for (const paper of validatedData) { + if (!paper.authorIds.includes(session.user.id)) { + return NextResponse.json( + { + error: "Forbidden. You must include yourself as an author.", + providedAuthorIds: paper.authorIds, + authenticatedUserId: session.user.id + }, + { status: 403 }, + ); + } + } + // Create the paper const paper = await createPapers(validatedData); diff --git a/src/app/u/[id]/edit/page.tsx b/src/app/u/[id]/edit/page.tsx index c03211e..ffd172f 100644 --- a/src/app/u/[id]/edit/page.tsx +++ b/src/app/u/[id]/edit/page.tsx @@ -59,7 +59,7 @@ const EditProfilePage = async ({ params }: PageProps) => { Edit Profile

- Update your profile information and social links + Update your profile, information, social links, publish blogs, and add research papers

diff --git a/src/components/EditProfileForm.tsx b/src/components/EditProfileForm.tsx index 18821db..cd09de5 100644 --- a/src/components/EditProfileForm.tsx +++ b/src/components/EditProfileForm.tsx @@ -7,9 +7,11 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { ImageUpload } from "@/components/ImageUpload"; +import { Badge } from "@/components/ui/badge"; import { updateUserProfile } from "@/actions/profile"; -import { Loader2, Save, X } from "lucide-react"; +import { Loader2, Save, X, Plus, User, BookOpen, FileText } from "lucide-react"; import { toast } from "sonner"; +import Image from "next/image"; interface EditProfileFormProps { user: { @@ -28,6 +30,9 @@ interface EditProfileFormProps { export function EditProfileForm({ user }: EditProfileFormProps) { const router = useRouter(); + const [activeSection, setActiveSection] = useState<"profile" | "blog" | "paper">("profile"); + + // Profile states const [name, setName] = useState(user.name || ""); const [imageUrl, setImageUrl] = useState(user.image || ""); const [designation, setDesignation] = useState(user.designation || ""); @@ -38,6 +43,28 @@ export function EditProfileForm({ user }: EditProfileFormProps) { const [github, setGithub] = useState(user.github || ""); const [isLoading, setIsLoading] = useState(false); + // Blog states + const [blogTitle, setBlogTitle] = useState(""); + //const [blogSlug, setBlogSlug] = useState(""); + //const [blogTldr, setBlogTldr] = useState(""); + const [blogContent, setBlogContent] = useState(""); + const [blogReadTime, setBlogReadTime] = useState(5); + const [blogPoster, setBlogPoster] = useState(""); + const [blogBanner, setBlogBanner] = useState(""); + const [blogTags, setBlogTags] = useState([]); + const [blogCategories, setBlogCategories] = useState([]); + const [tagInput, setTagInput] = useState(""); + const [categoryInput, setCategoryInput] = useState(""); + + // Research Paper states + const [paperTitle, setPaperTitle] = useState(""); + const [paperAbstract, setPaperAbstract] = useState(""); + const [paperDoi, setPaperDoi] = useState(""); + const [paperPublishedAt, setPaperPublishedAt] = useState(""); + const [paperConference, setPaperConference] = useState(""); + const [paperAuthorIds, setPaperAuthorIds] = useState([user.id]); + const [authorIdInput, setAuthorIdInput] = useState(""); + const getInitials = (name?: string) => { if (!name) return "U"; return name @@ -82,8 +109,160 @@ export function EditProfileForm({ user }: EditProfileFormProps) { router.push(`/u/${user.email?.split("@")[0]}`); }; + const handleBlogSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(true); + + try { + const blogData = { + //slug: blogSlug, + title: blogTitle, + //tldr: blogTldr, + content: blogContent, + readTime: blogReadTime, + tags: blogTags, + categories: blogCategories, + poster: blogPoster, + banner: blogBanner, + authorId: user.id, + }; + + const response = await fetch("/api/blogs", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify([blogData]), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || "Failed to create blog"); + } + + const result = await response.json(); + toast.success("Blog created successfully!"); + router.push(`/blogs/${result.data[0].slug}`); + router.refresh(); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Failed to create blog"); + } finally { + setIsLoading(false); + } + }; + + const handlePaperSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(true); + + try { + const publishedAtDate = new Date(paperPublishedAt); + if (isNaN(publishedAtDate.getTime())) { + throw new Error("Invalid date format"); + } + + const paperData = { + title: paperTitle, + abstract: paperAbstract, + doi: paperDoi || undefined, + publishedAt: publishedAtDate.toISOString(), + conference: paperConference || undefined, + authorIds: paperAuthorIds, + }; + + const response = await fetch("/api/paper", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify([paperData]), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || "Failed to create research paper"); + } + + const result = await response.json(); + toast.success("Research paper created successfully!"); + router.push(`/research/${result.data[0].id}`); + router.refresh(); + } catch (error) { + toast.error(error instanceof Error ? error.message : "Failed to create research paper"); + } finally { + setIsLoading(false); + } + }; + + const addTag = () => { + if (tagInput.trim() && !blogTags.includes(tagInput.trim())) { + setBlogTags([...blogTags, tagInput.trim()]); + setTagInput(""); + } + }; + + const removeTag = (tagToRemove: string) => { + setBlogTags(blogTags.filter((tag) => tag !== tagToRemove)); + }; + + const addCategory = () => { + if (categoryInput.trim() && !blogCategories.includes(categoryInput.trim())) { + setBlogCategories([...blogCategories, categoryInput.trim()]); + setCategoryInput(""); + } + }; + + const removeCategory = (categoryToRemove: string) => { + setBlogCategories(blogCategories.filter((cat) => cat !== categoryToRemove)); + }; + + const addAuthorId = () => { + if (authorIdInput.trim() && !paperAuthorIds.includes(authorIdInput.trim())) { + setPaperAuthorIds([...paperAuthorIds, authorIdInput.trim()]); + setAuthorIdInput(""); + } + }; + + const removeAuthorId = (authorToRemove: string) => { + if (paperAuthorIds.length > 1) { + setPaperAuthorIds(paperAuthorIds.filter((id) => id !== authorToRemove)); + } else { + toast.error("At least one author is required"); + } + }; + return ( -
+
+ {/* Section Switcher */} +
+ + + +
+ + {/* Profile Section */} + {activeSection === "profile" && ( + {/* Image Upload */}
@@ -254,5 +433,283 @@ export function EditProfileForm({ user }: EditProfileFormProps) {
+ )} + + {/* Blog Section */} + {activeSection === "blog" && ( +
+
+
+ + setBlogTitle(e.target.value)} + placeholder="Enter blog title" + required + maxLength={200} + /> +
+ + {/*
+ + setBlogSlug(e.target.value)} + placeholder="blog-url-slug" + required + maxLength={100} + /> +

URL-friendly version of the title

+
+ +
+ +