Skip to content

feat: add zod validation and character counters for forum posts (closes #19)#57

Merged
rishabhx29 merged 6 commits into
rishabhx29:mainfrom
arcgod-design:feat/issue-19-forum-validation
Jun 28, 2026
Merged

feat: add zod validation and character counters for forum posts (closes #19)#57
rishabhx29 merged 6 commits into
rishabhx29:mainfrom
arcgod-design:feat/issue-19-forum-validation

Conversation

@arcgod-design

@arcgod-design arcgod-design commented Jun 23, 2026

Copy link
Copy Markdown

Summary

Adds input validation and character limits to the forum post creation form, both frontend and backend.

Changes

New Files

  • �pp/src/lib/validators.ts — CreatePostSchema using zod with:
    • Title: required, 10–150 characters
    • Content: required, 20–10,000 characters
    • Category: defaults to 'general'

Modified Files

  • �pp/src/sections/CommunityForum.tsx:

    • Replaced raw useState with
      eact-hook-form + zodResolver
    • Added inline validation errors under each field
    • Added character counters (e.g., 143 / 150 for title, 1,234 / 10,000 for content)
    • Form submission blocked when invalid
    • mode: 'onChange' for real-time validation
  • �ackend/src/controllers/forumController.ts:

    • Server-side validation for title (10–150 chars), content (20–10,000 chars), and tags (0–5, each ≤30 chars)
    • Returns 400 Bad Request with descriptive error messages
    • Trims whitespace before validation

Acceptance Criteria

  • Empty or whitespace-only title/content is rejected with an inline error
  • Title over 150 characters is rejected
  • Content over 10,000 characters is rejected
  • Character counters are visible in the creation form
  • Backend also validates and returns 400 for invalid input

Testing

  • Frontend: Form blocks submission when invalid, shows real-time errors
  • Backend: Returns 400 for invalid input, 201 for valid input

Closes #19

Summary by CodeRabbit

  • New Features

    • Added stronger validation when creating forum posts, including title/content length limits and category defaults.
    • The post creation form now shows live validation feedback and character counts, with improved submit behavior.
  • Bug Fixes

    • Posting is now trimmed and validated consistently before save, reducing invalid or empty submissions.
    • Forum tag values are now cleaned and limited before being stored.

rishabhx29#19)

- app/src/lib/validators.ts: CreatePostSchema with title (10-150), content (20-10000), tags validation
- CommunityForum.tsx: Replace raw useState with react-hook-form + zodResolver, add inline errors and character counters
- forumController.ts: Add server-side validation for title, content, and tags with proper 400 responses
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the rishabhjtripathi2903-3434's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@arcgod-design, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 51 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7fab2862-6905-4ec9-b528-fa098fd04621

📥 Commits

Reviewing files that changed from the base of the PR and between 7e7f8d6 and 58533b1.

📒 Files selected for processing (1)
  • app/src/sections/Problems.tsx
📝 Walkthrough

Walkthrough

Adds schema-backed validation for forum post creation in the forum UI and backend, including inline errors, character counters, and request trimming/length checks. Separately, it applies ESLint/TypeScript annotation updates across multiple UI primitives and learning sections, plus a sidebar skeleton state tweak.

Changes

Forum Post Creation Validation

Layer / File(s) Summary
Schema and inferred form type
app/src/lib/validators.ts
Defines TITLE_MAX, CONTENT_MAX, createPostSchema, and CreatePostFormData for post creation validation.
CommunityForum create-post form
app/src/sections/CommunityForum.tsx
Uses react-hook-form with zodResolver(createPostSchema), shows field errors and counters, and submits validated title/content/category values.
Backend createPost validation
backend/src/controllers/forumController.ts
Trims and validates title, content, and tags, returns 400 on invalid input, and persists the validated payload through Prisma.

TypeScript and ESLint Compliance Cleanup

Layer / File(s) Summary
UI export suppressions
app/src/components/ui/{badge.tsx,button-group.tsx,button.tsx,form.tsx,navigation-menu.tsx,toggle.tsx}
Adds react-refresh/only-export-components comments before existing export blocks.
Sidebar skeleton state
app/src/components/ui/sidebar.tsx
Stores the skeleton width with a useState initializer and adds a useSidebar export suppression comment.
Problem page lint updates
app/src/sections/ProblemWorkspace.tsx, app/src/sections/Problems.tsx
Adds no-explicit-any suppressions around existing any usage and changes several catch (e) handlers to catch {}.
Roadmap and topic detail lint updates
app/src/sections/Roadmaps.tsx, app/src/sections/TopicDetail.tsx
Adds typed intermediates and no-explicit-any suppressions around loading and render paths, and changes several catch (e) handlers to catch {}.
UserHero annotation pass
app/src/sections/UserHero.tsx
Adds no-explicit-any suppressions across progress, activity, continue-learning, SVG, and next-goals mappings, and removes one bound error variable.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CommunityForum
  participant zodResolver
  participant forumController
  participant Prisma

  User->>CommunityForum: submit title, content, category
  CommunityForum->>zodResolver: validate with createPostSchema
  zodResolver-->>CommunityForum: errors or valid form data
  CommunityForum->>forumController: createPost(validated payload)
  forumController->>forumController: trim and validate title, content, tags
  alt invalid input
    forumController-->>CommunityForum: 400 Bad Request
  else valid input
    forumController->>Prisma: forumPost.create(validated payload)
    Prisma-->>forumController: created forum post
    forumController-->>CommunityForum: 201 Created
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

feature

Poem

A bunny typed by moonlit glow 🐇
"Title too short?" — the errors say no.
Counters now hop, neat and bright,
Tags stay trimmed just right tonight.
Forum posts leap safe and sound!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also changes many unrelated UI files with lint suppressions and a sidebar state refactor that are outside issue #19. Move the unrelated lint suppressions and UI refactors into a separate PR so this change stays focused on forum post validation.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding forum post validation and character counters with Zod.
Linked Issues check ✅ Passed The changes satisfy the forum post validation requirements on the frontend and backend, including Zod/react-hook-form and tag limits.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/src/lib/validators.ts (1)

3-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the exported max constants inside the schema to prevent drift.

TITLE_MAX and CONTENT_MAX are exported, but the schema still uses literal values. Keeping a single source avoids subtle contract drift later.

♻️ Proposed refactor
 import { z } from 'zod';
 
+export const TITLE_MAX = 150;
+export const CONTENT_MAX = 10000;
+
 export const createPostSchema = z.object({
   title: z
     .string()
     .trim()
     .min(1, 'Title is required')
     .min(10, 'Title must be at least 10 characters')
-    .max(150, 'Title must be less than 150 characters'),
+    .max(TITLE_MAX, 'Title must be less than 150 characters'),
   content: z
     .string()
     .trim()
     .min(1, 'Content is required')
     .min(20, 'Content must be at least 20 characters')
-    .max(10000, 'Content must be less than 10,000 characters'),
+    .max(CONTENT_MAX, 'Content must be less than 10,000 characters'),
   category: z.string().default('general'),
 });
 
 export type CreatePostFormData = z.infer<typeof createPostSchema>;
-
-export const TITLE_MAX = 150;
-export const CONTENT_MAX = 10000;

Also applies to: 21-22

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/validators.ts` around lines 3 - 17, The createPostSchema object
is using hardcoded literal values (150 for title and 10000 for content) in the
max validation calls instead of referencing the exported TITLE_MAX and
CONTENT_MAX constants. Replace the literal 150 in the title field's max()
validation with TITLE_MAX and the literal 10000 in the content field's max()
validation with CONTENT_MAX. Also check lines 21-22 mentioned in the comment for
any other occurrences that need the same treatment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/controllers/forumController.ts`:
- Around line 151-164: Add type guards to verify that `title` and `content` are
strings before calling the `.trim()` method on them in the code where
`trimmedTitle` and `trimmedContent` are assigned. Without these type checks,
non-string truthy values (like numbers or objects) will cause `.trim()` to throw
an error, resulting in a 500 response instead of a proper 400 validation error.
Use typeof checks to ensure both variables are strings before proceeding with
the trim operations.
- Around line 175-178: In the forumController.ts file, the current logic for
handling the postTags variable silently converts any non-array tags value to an
empty array, which masks invalid requests instead of rejecting them. Replace the
current conditional logic that assigns postTags to first check if tags is
provided and is not an array, and if so, return a 400 status response with an
appropriate error message indicating that tags must be an array. Only proceed
with the postTags assignment if tags is either undefined/null or is a valid
array, ensuring invalid tag types are properly rejected rather than silently
dropped.

---

Nitpick comments:
In `@app/src/lib/validators.ts`:
- Around line 3-17: The createPostSchema object is using hardcoded literal
values (150 for title and 10000 for content) in the max validation calls instead
of referencing the exported TITLE_MAX and CONTENT_MAX constants. Replace the
literal 150 in the title field's max() validation with TITLE_MAX and the literal
10000 in the content field's max() validation with CONTENT_MAX. Also check lines
21-22 mentioned in the comment for any other occurrences that need the same
treatment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3257e421-332e-4ab5-b979-5b0803deaf9b

📥 Commits

Reviewing files that changed from the base of the PR and between e38b451 and efbb132.

📒 Files selected for processing (3)
  • app/src/lib/validators.ts
  • app/src/sections/CommunityForum.tsx
  • backend/src/controllers/forumController.ts

Comment thread backend/src/controllers/forumController.ts Outdated
Comment thread backend/src/controllers/forumController.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/sections/AdminPanel.tsx (1)

136-145: 📐 Maintainability & Code Quality | 🔵 Trivial

Define typed response contracts in admin and content API modules to replace any suppressions

The explicit-any suppressions in AdminPanel cascade from untyped admin and content API functions. admin.ts functions like getUsers, editUser, addProblem, and editForumPost return response.data without type annotations, forcing AdminPanel state to use any. The forum API (forum.ts) shows the correct pattern: define interfaces like ForumPostSummary and PostsResponse, then annotate functions with Promise<Type> return types.

Apply the same pattern to admin.ts (define types for user lists, problem/topic responses, admin stats) and content.ts (define types for paths, topics, problems), then update AdminPanel to use these typed APIs. This eliminates suppressions incrementally and prevents payload shape drift from going undetected.

Lines affected across AdminPanel:
- 136–145 (users, editingUser, editForm)
- 329–340 (paths, topics, problems, editingProblem)  
- 548–554 (posts, editingPost)
Also requires changes to app/src/api/admin.ts and app/src/api/content.ts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/sections/AdminPanel.tsx` around lines 136 - 145, The state variables
in AdminPanel at lines 136-145 (users, editingUser, editForm) and 329-340
(paths, topics, problems, editingProblem) and 548-554 (posts, editingPost) use
`any` type due to untyped API functions. Follow the pattern used in forum.ts by
defining TypeScript interfaces in admin.ts for responses from getUsers,
editUser, addProblem, and editForumPost functions, and in content.ts for
responses from functions returning paths, topics, and problems. Add proper
Promise<Type> return type annotations to these API functions, then update
AdminPanel to type its state variables with these interfaces instead of `any`,
removing the eslint-disable-next-line suppressions as they become unnecessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/sections/Problems.tsx`:
- Line 323: In the Problems.tsx file, the line assigning problemMongoId uses a
redundant fallback expression where both sides reference the same field
(problem.id || problem.id), making it a no-op that provides no actual fallback.
Replace this with a proper fallback that checks an alternative field for the
mongo ID; commonly this would be problem._id as an alternative identifier when
problem.id may not be present. Ensure the fallback expression covers the case
where records come back with different id field naming so that action handlers
and list keys work correctly.

---

Nitpick comments:
In `@app/src/sections/AdminPanel.tsx`:
- Around line 136-145: The state variables in AdminPanel at lines 136-145
(users, editingUser, editForm) and 329-340 (paths, topics, problems,
editingProblem) and 548-554 (posts, editingPost) use `any` type due to untyped
API functions. Follow the pattern used in forum.ts by defining TypeScript
interfaces in admin.ts for responses from getUsers, editUser, addProblem, and
editForumPost functions, and in content.ts for responses from functions
returning paths, topics, and problems. Add proper Promise<Type> return type
annotations to these API functions, then update AdminPanel to type its state
variables with these interfaces instead of `any`, removing the
eslint-disable-next-line suppressions as they become unnecessary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b720773-30b8-42e1-a56d-d606c896e14e

📥 Commits

Reviewing files that changed from the base of the PR and between efbb132 and 84f324f.

📒 Files selected for processing (30)
  • app/src/api/admin.ts
  • app/src/components/custom/AlgoBot.tsx
  • app/src/components/custom/AuthModal.tsx
  • app/src/components/custom/Navigation.tsx
  • app/src/components/ui/badge.tsx
  • app/src/components/ui/button-group.tsx
  • app/src/components/ui/button.tsx
  • app/src/components/ui/form.tsx
  • app/src/components/ui/navigation-menu.tsx
  • app/src/components/ui/sidebar.tsx
  • app/src/components/ui/toggle.tsx
  • app/src/contexts/AuthContext.tsx
  • app/src/lib/validators.ts
  • app/src/sections/AdminPanel.tsx
  • app/src/sections/CTA.tsx
  • app/src/sections/CommunityForum.tsx
  • app/src/sections/CommunityHub.tsx
  • app/src/sections/DailyChallenges.tsx
  • app/src/sections/Dashboard.tsx
  • app/src/sections/Footer.tsx
  • app/src/sections/Hero.tsx
  • app/src/sections/Leaderboard.tsx
  • app/src/sections/Notes.tsx
  • app/src/sections/PathDetail.tsx
  • app/src/sections/ProblemWorkspace.tsx
  • app/src/sections/Problems.tsx
  • app/src/sections/Roadmaps.tsx
  • app/src/sections/TopicDetail.tsx
  • app/src/sections/UserHero.tsx
  • backend/src/controllers/forumController.ts
💤 Files with no reviewable changes (1)
  • app/src/sections/Footer.tsx
✅ Files skipped from review due to trivial changes (20)
  • app/src/components/ui/button.tsx
  • app/src/components/ui/badge.tsx
  • app/src/components/ui/toggle.tsx
  • app/src/components/ui/button-group.tsx
  • app/src/sections/Hero.tsx
  • app/src/components/custom/Navigation.tsx
  • app/src/components/ui/navigation-menu.tsx
  • app/src/sections/Leaderboard.tsx
  • app/src/sections/DailyChallenges.tsx
  • app/src/sections/CommunityHub.tsx
  • app/src/components/custom/AlgoBot.tsx
  • app/src/components/ui/form.tsx
  • app/src/sections/PathDetail.tsx
  • app/src/sections/Notes.tsx
  • app/src/components/custom/AuthModal.tsx
  • app/src/sections/Dashboard.tsx
  • app/src/sections/TopicDetail.tsx
  • app/src/contexts/AuthContext.tsx
  • app/src/sections/ProblemWorkspace.tsx
  • app/src/api/admin.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/src/controllers/forumController.ts
  • app/src/lib/validators.ts
  • app/src/sections/CommunityForum.tsx

Comment thread app/src/sections/Problems.tsx Outdated
@rishabhx29 rishabhx29 added enhancement New feature or request good first issue Good for newcomers SSoC26 Easy labels Jun 28, 2026
@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
algo-forge-2-0 Ready Ready Preview, Comment Jun 28, 2026 10:47am

@rishabhx29 rishabhx29 merged commit 676d113 into rishabhx29:main Jun 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Easy enhancement New feature or request good first issue Good for newcomers SSoC26

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Forum Post Creation Has No Input Validation or Character Limits

2 participants