Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions backend/src/controllers/blog.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ export const createBlog = asyncHandler(async (req, res) => {
throw new ApiError(400, "Title, excerpt, content, and category are required");
}

// Validate minimum lengths
if (title.trim().length < 5) {
throw new ApiError(400, "Title must be at least 5 characters long");
}

if (content.trim().length < 20) {
throw new ApiError(400, "Content must be at least 20 characters long");
}

if (!req.file) {
throw new ApiError(400, "Blog image is required");
}
Expand Down Expand Up @@ -158,9 +167,27 @@ export const updateBlogById = asyncHandler(async (req, res) => {
}

const updateData = {};
if (title) updateData.title = title.trim();
if (title !== undefined && title !== null) {
const trimmedTitle = title.trim();
if (!trimmedTitle) {
throw new ApiError(400, "Title cannot be empty");
}
if (trimmedTitle.length < 5) {
throw new ApiError(400, "Title must be at least 5 characters long");
}
updateData.title = trimmedTitle;
}
if (excerpt) updateData.excerpt = excerpt.trim();
if (content) updateData.content = content.trim();
if (content !== undefined && content !== null) {
const trimmedContent = content.trim();
if (!trimmedContent) {
throw new ApiError(400, "Content cannot be empty");
}
if (trimmedContent.length < 20) {
throw new ApiError(400, "Content must be at least 20 characters long");
}
updateData.content = trimmedContent;
}
if (category) updateData.category = category.trim();
if (tags) updateData.tags = parseTags(tags).map((tag) => tag.trim());

Expand Down
51 changes: 45 additions & 6 deletions src/Pages/AddBlogPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ export default function AddBlogPage() {
category: "",
});

const [errors, setErrors] = useState({
title: "",
content: "",
});

const validateForm = () => {
const newErrors = {
title: "",
content: "",
};

const trimmedTitle = newBlog.title.trim();
const trimmedContent = newBlog.content.trim();

if (trimmedTitle.length < 5) {
newErrors.title = "Title must be at least 5 characters long";
}

if (trimmedContent.length < 20) {
newErrors.content = "Content must be at least 20 characters long";
}

setErrors(newErrors);
return !newErrors.title && !newErrors.content;
};

const handleImageChange = (e) => {
const file = e.target.files[0];
if (!file) return;
Expand Down Expand Up @@ -72,6 +98,11 @@ export default function AddBlogPage() {
return;
}

// Validate form before submission
if (!validateForm()) {
return;
}

const title = newBlog.title.trim();
const excerpt = newBlog.excerpt.trim();
const content = newBlog.content.trim();
Expand Down Expand Up @@ -156,10 +187,14 @@ export default function AddBlogPage() {
placeholder="Enter your blog title..."
className="w-full px-3 py-2 rounded-xl border-2 border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-800 focus:border-blue-500 focus:ring-4 focus:ring-blue-500/20 transition-all"
value={newBlog.title}
onChange={(e) =>
setNewBlog({ ...newBlog, title: e.target.value })
}
onChange={(e) => {
setNewBlog({ ...newBlog, title: e.target.value });
if (errors.title) setErrors((prev) => ({ ...prev, title: "" }));
}}
/>
{errors.title && (
<p className="text-red-500 text-sm mt-1">{errors.title}</p>
)}
</div>

<div className="space-y-1">
Expand Down Expand Up @@ -190,10 +225,14 @@ export default function AddBlogPage() {
placeholder="Write your full blog content (Markdown supported)..."
className="w-full px-3 py-2 rounded-xl border-2 border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-800 focus:border-rose-500 focus:ring-4 focus:ring-rose-500/20 transition-all resize-none"
value={newBlog.content}
onChange={(e) =>
setNewBlog({ ...newBlog, content: e.target.value })
}
onChange={(e) => {
setNewBlog({ ...newBlog, content: e.target.value });
if (errors.content) setErrors((prev) => ({ ...prev, content: "" }));
}}
/>
{errors.content && (
<p className="text-red-500 text-sm mt-1">{errors.content}</p>
)}
</div>

<div className="grid md:grid-cols-2 gap-3">
Expand Down