Skip to content

Feature/form validation - #785

Merged
nafiuishaaq merged 4 commits into
MentoNest:mainfrom
Unclebaffa:feature/form-validation
Aug 2, 2026
Merged

nafiuishaaq merged 4 commits into
MentoNest:mainfrom
Unclebaffa:feature/form-validation

Conversation

@Unclebaffa

Copy link
Copy Markdown
Contributor

Detailed Implementation Report: Client-Side Form Validation

1. Executive Summary

We have implemented comprehensive client-side form validation for the Login and Register forms in SkillSync. Built using react-hook-form and zod, the implementation delivers real-time validation, a live 4-stage password strength indicator, accessible aria-describedby error announcements, disabled submit controls for invalid form states, and unit test coverage.


2. Core Features Implemented

A. Centralised Zod Validation Schemas (lib/validations/auth.ts)

  • loginSchema:
    • email: Required field validation + RFC 5322 email format validation.
    • password: Required field validation.
  • registerSchema:
    • name: Required full name (1 to 80 characters).
    • email: Required email format validation.
    • password: Length validation (8 to 128 characters).
    • confirmPassword: Validated via .superRefine() to ensure exact match with password.
  • getPasswordStrength(password):
    • Evaluates 4 rules: length ≥ 8, contains digits 0-9, contains lowercase a-z, contains uppercase/symbols.
    • Returns numerical score (0 to 4), level identifier (empty, weak, fair, good, strong), and boolean flags for each criteria.

B. Accessible Form Field Wrapper (components/auth/FormField.tsx)

  • Encapsulates <label>, the input control, error alert text, and optional field hints.
  • Uses React’s useId() hook to link inputs to labels and error text.
  • Generates aria-describedby={${id}-error} dynamically when errors occur so screen readers announce validation errors.
  • Sets aria-invalid={hasError} on the input element.
  • Dynamically applies red border styling (border-red-400 focus:ring-red-300) on validation failure.

C. Live Password Strength Meter (components/auth/PasswordStrengthMeter.tsx)

  • Renders a 4-bar progress meter that changes colour based on complexity score:
    • Score 1 (weak): Red (bg-red-500)
    • Score 2 (fair): Amber (bg-amber-400)
    • Score 3 (good): Yellow (bg-yellow-400)
    • Score 4 (strong): Emerald (bg-emerald-500)
  • Renders a checklist indicating requirement completion:
    • 8+ characters
    • Number (0–9)
    • Lowercase (a–z)
    • Uppercase or symbol

D. Refactored Form Components

1. components/auth/LoginForm.tsx

  • Validated via loginSchema with onTouched validation mode.
  • Submit button disabled when form is invalid (!isValid), submitting (isSubmitting), or loading (isLoading).
  • Displays animated SVG spinner when submission is in progress.
  • Single top-level API error alert banner.

2. components/auth/RegisterForm.tsx

  • Validated via registerSchema.
  • Displays real-time <PasswordStrengthMeter value={watch("password")} />.
  • Strips confirmPassword from registration payload before calling registerUser({ name, email, password }).
  • Submit button disabled until all fields (including password match) pass validation.

3. Testing & Code Quality Metrics

Unit Tests Created:

  1. __tests__/auth/LoginForm.test.tsx (16 Tests)

    • Form controls rendering
    • Initial disabled submit state
    • noValidate HTML5 attribute presence
    • "Email is required" on empty touch
    • "Enter a valid email address" on malformed email
    • "Password is required" on empty password touch
    • Submit button enabling when email and password are valid
    • Correct payload invocation on submit
    • Router navigation to /dashboard
    • API error rendering from AuthContext
    • Loading spinner state
    • aria-invalid and aria-describedby linking
  2. __tests__/auth/RegisterForm.test.tsx (18 Tests)

    • All 4 input fields rendering (Name, Email, Password, Confirm Password)
    • Initial disabled submit state
    • "Full name is required" validation
    • "Password must be at least 8 characters" validation
    • "Passwords don't match" validation on confirm password field
    • Dynamic strength meter rendering
    • "Strong" evaluation for complex passwords
    • Submit button enabling when all 4 fields pass
    • Exclusion of confirmPassword from register() call
    • Router navigation to /dashboard

Verification Summary:

  • Jest Test Suite: 187 / 187 tests passed (17 test suites)
  • TypeScript: npx tsc --noEmit — 0 errors
  • ESLint: npm run lint — 0 errors
  • Prettier: 100% formatted

Closes #613

- Add SocialAuthButtons component with Google and Facebook buttons
- Include inline SVG icons for both providers (no external deps)
- Add divider with configurable 'or ...' label
- Fully accessible: aria-label, role=group/separator, aria-hidden on decorative SVGs
- Dark mode support via Tailwind dark: variants
- No backend integration (UI only)
- Fix pre-existing broken login page (code inside return statement)
- Refactor register page to use shared SocialAuthButtons component
- Add 7 unit tests for SocialAuthButtons (all passing)
…uth UI

- feat: add SocialAuthButtons component (Google + Facebook, dark mode, SVG icons)
- feat: integrate SocialAuthButtons into login and register pages
- test: add 7 unit tests for SocialAuthButtons; all 153 tests pass across 15 suites

TypeScript (tsc --noEmit now clean):
- lib/api/client.ts: type headers as Record<string,string> to allow Authorization indexing
- lib/types.ts: add experienceYears? field to Mentor interface
- components/mentors/data.ts: re-export Mentor, ExperienceLevel; add EXPERIENCE_LEVELS array
- components/mentors/DiscoveryMentorCard.tsx: nullish coalesce all optional Mentor fields
- components/mentors/MentorComparisonDrawer.tsx: add rating ?? 0 and id || mentorId fallbacks
- components/DiscussionMetadata.tsx: add full TypeScript types + React.CSSProperties
- app/(public)/mentors/data/mockMentors.ts: type as Mentor[], add MENTORS + mentorSlug exports
- app/(public)/mentors/[id]/page.tsx: type existing param in filter callback
- app/(public)/mentors/components/MentorCard.tsx: replace @heroicons/react with lucide-react
- app/(public)/resources/tracks/LearningTracks.tsx: fix LearningTrackCard import path
- app/CategoryBadge.tsx: add ColorKey/ColorEntry types, typed COLOR_MAP and props
- components/community/CreateDiscussionForm.tsx: import missing Toast component
- components/icons.tsx: add named aliases (CodeIcon, GearIcon, ChevronRightIcon, etc.)
- components/resources/icons.ts: create re-export barrel for resource icon imports

ESLint (0 errors):
- components/community/RichTextEditor.tsx: move ToolbarButton to module level
- hooks/useDiscussions.ts: wrap setState in async IIFE to fix react-hooks/set-state-in-effect
- components/ResourceSearchBar.tsx: wrap setState in microtask
- lib/auth-context.tsx: use lazy useState initializer for localStorage reads

Prettier: all files formatted clean

Bug fixes:
- app/layout.tsx: remove duplicate AuthProvider import
- app/(dashboard)/community/[id]/page.tsx: fix duplicate content, unescaped JSX entities
- app/(public)/discover-mentors/MentorDiscoveryView.tsx: fix corruption, align Mentor fields
- app/(public)/mentors/page.tsx: fix duplicate component syntax and missing return wrapper
- components/ui/StarRating.tsx: fix 1-indexed partial-fill star calculation
- lib/utils.ts: replace missing clsx/tailwind-merge with pure cn() implementation
- feat: create centralised Zod schemas in lib/validations/auth.ts for login and register
- feat: add getPasswordStrength helper & PasswordStrengthMeter component with live 4-bar indicator & checklist
- feat: create reusable FormField component with accessibility (aria-describedby, aria-invalid, role=alert)
- feat: refactor LoginForm & RegisterForm to disable submit when form is invalid (!isValid)
- feat: strip confirmPassword from registration payload before calling authContext
- test: add unit test suites __tests__/auth/LoginForm.test.tsx (16 tests) and __tests__/auth/RegisterForm.test.tsx (18 tests)
- fix: resolve ESLint warnings & errors across modified components (impure Date.now, prefer-const, unused imports)
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Unclebaffa Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@nafiuishaaq

Copy link
Copy Markdown
Contributor

Please resolve conflict

@Unclebaffa

Copy link
Copy Markdown
Contributor Author

Please resolve conflict

Conflict resolved

@nafiuishaaq
nafiuishaaq merged commit 438e37b into MentoNest:main Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Form Validation (Client-Side)

2 participants