Skip to content

Conversation

nickventon
Copy link
Collaborator

@nickventon nickventon commented Sep 26, 2025

Description:
This PR refactors the form components to improve code consistency and maintainability:

  • Removed all default exports from FormInput and FormCheckbox, switching to named exports only.
  • Updated all imports to use named imports for these components.
  • Removed manual type definitions for form values in SigninForm and SignupForm, importing types directly from their respective schema files.
  • Cleaned up unused imports and ensured type consistency across authentication forms.

These changes help prevent import/export confusion, reduce code duplication, and align with modern ES6 best practices.

Summary by CodeRabbit

  • New Features

    • Added dedicated Sign In and Sign Up forms with real-time validation, clearer terms validation, and improved actionable error messages.
    • Success shows a toast and redirects to the home page; errors display actionable toasts.
    • Forms include loading/disabled states, email/password inputs, terms checkbox, and links to Sign In/Up, Terms, and Privacy.
  • Refactor

    • Sign-in and sign-up pages now delegate to the new form components for a consistent, streamlined experience.

Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds exported SigninForm and SignupForm components using react-hook-form + Zod and action execution; page containers now delegate to these components. Changes remove default exports from FormInput/FormCheckbox and update signup schema's terms validation to a boolean refine.

Changes

Cohort / File(s) Summary
Auth Forms (new components)
src/components/models/auth/SigninForm.tsx, src/components/models/auth/SignupForm.tsx
New exported React components implementing sign-in and sign-up flows with react-hook-form, zod validation, action execution (signinAction/signupAction), toast notifications, loading/disabled states, navigation on success, and composed error handling from server/field errors.
Page Containers delegating to forms
src/components/pages/signin/SigninPageContainer.tsx, src/components/pages/signup/SignupPageContainer.tsx
Replaced inline form logic/UI with minimal wrappers that import and return SigninForm / SignupForm. Export names unchanged.
Shared Form exports (named only)
src/components/shared/Form/FormCheckbox.tsx, src/components/shared/Form/FormInput.tsx
Removed default exports; retained named exports FormCheckbox and FormInput. Import style must change; implementation unchanged.
Signup schema tweak
src/actions/auth/signup/schema.ts
Changed terms validation from z.literal(true, { errorMap: ... }) to z.boolean().refine(val => val === true, { message: 'You must accept the terms' }) (semantics unchanged, error reporting path simplified).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant SF as SigninForm
  participant SA as signinAction
  participant T as Toast
  participant R as Router

  U->>SF: Submit email/password
  SF->>SF: Validate with Zod
  alt Valid
    SF->>SA: execute(credentials)
    alt Success
      SA-->>SF: OK
      SF->>T: Show success toast
      SF->>R: Navigate to "/"
    else Error
      SA-->>SF: serverError/fieldErrors
      SF->>SF: Compose error message
      SF->>T: Show error toast
    end
  else Invalid
    SF->>T: Show validation errors
  end
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant SUF as SignupForm
  participant UA as signupAction
  participant T as Toast
  participant R as Router

  U->>SUF: Submit signup fields
  SUF->>SUF: Validate with Zod
  alt Valid
    SUF->>UA: execute(payload)
    alt Success
      UA-->>SUF: OK
      SUF->>SUF: Reset form
      SUF->>T: Success toast
      SUF->>R: Navigate to "/"
    else Error
      UA-->>SUF: serverError/fieldErrors
      SUF->>SUF: Compose error message
      SUF->>T: Error toast
    end
  else Invalid
    SUF->>T: Show validation errors
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • tasin2610

Poem

A nibble of Zod, a hop of form state,
I thump "submit!" and await my fate.
Toasts pop warm, routes change with glee,
Signin, Signup — neat as can be.
Named exports tidy the burrowed tree. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly conveys the primary refactor of switching to named exports for form components and importing types from schemas, directly reflecting the main changes and aligning with the PR objectives.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/consistent-form-imports

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

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 (12)
src/components/models/auth/SigninForm.tsx (5)

35-38: Prefer replace() over push() after successful sign-in.

Prevents the user from navigating back to the sign-in page post-auth.

-    onSuccess: () => {
+    onSuccess: () => {
       toast.success("Signed in successfully!");
-      router.push("/");
+      router.replace("/");
     },

65-65: Disable native browser validation to avoid double validation UX.

Rely on Zod/RHF errors only.

-          <form onSubmit={form.handleSubmit(execute)}>
+          <form noValidate onSubmit={form.handleSubmit(execute)}>

Based on learnings


67-74: Add email autocomplete for better UX.

Requires FormInput to forward autoComplete.

Proposed usage here (after updating FormInput to accept autoComplete):

               <FormInput
                 control={form.control}
                 name="email"
                 label="Email"
                 placeholder="john@example.com"
                 type="email"
+                autoComplete="email"
                 required
               />

Update FormInput component (outside this file) to support autoComplete:

 export function FormInput<T extends FieldValues>({
   placeholder,
   control,
   name,
   label,
   type = "text",
   required = false,
   helperText,
   endComponent,
   min,
   step,
   max,
+  autoComplete,
 }: {
   max?: number;
   step?: number;
   label: string;
   placeholder: string;
   control: Control<T>;
   name: Path<T>;
   type?: React.HTMLInputTypeAttribute;
   required?: boolean;
   helperText?: string;
   endComponent?: React.ReactNode;
   min?: number;
+  autoComplete?: string;
 }) {
   return (
     <FormField
       control={control}
       name={name}
       render={({ field }) => (
         <FormItem>
           <FormLabel>
             {label} {required && <span className="text-red-500">*</span>}
           </FormLabel>
           <div className="flex flex-row gap-2">
             <FormControl>
               <Input
                 placeholder={placeholder}
                 {...field}
                 min={min}
                 max={max}
                 step={step}
+                autoComplete={autoComplete}
                 onChange={(e) => {

75-82: Add password autocomplete for better UX.

After extending FormInput as above:

               <FormInput
                 control={form.control}
                 name="password"
                 label="Password"
                 placeholder="Enter your password"
                 type="password"
+                autoComplete="current-password"
                 required
               />

40-49: Harden error aggregation to handle arrays safely.

Prevents “[a,b]” stringification quirks and non-array cases.

-      const fieldErrors = error.error.validationErrors?.fieldErrors;
-      const errorMessage =
-        error.error.serverError ??
-        (fieldErrors
-          ? Object.entries(fieldErrors)
-              .map(([key, value]) => `${key}: ${value}`)
-              .join(", ")
-          : "An unknown error occurred");
+      const fieldErrors = error.error.validationErrors?.fieldErrors;
+      const errorMessage =
+        error.error.serverError ??
+        (fieldErrors
+          ? Object.entries(fieldErrors)
+              .map(([key, value]) => {
+                const messages = Array.isArray(value) ? value.join(", ") : String(value);
+                return `${key}: ${messages}`;
+              })
+              .join(", ")
+          : "An unknown error occurred");
src/components/models/auth/SignupForm.tsx (7)

39-42: Prefer replace() over push() after successful signup.

Prevents navigating back to the signup page post-account creation.

-      toast.success("Account created successfully!");
-      form.reset();
-      router.push("/");
+      toast.success("Account created successfully!");
+      form.reset();
+      router.replace("/");

66-66: Disable native browser validation to avoid mixed HTML/RHF validation.

Use Zod/RHF messages only.

-          <form onSubmit={form.handleSubmit(execute)}>
+          <form noValidate onSubmit={form.handleSubmit(execute)}>

Based on learnings


69-74: Add autocomplete for name.

Requires FormInput to support autoComplete as suggested in SigninForm comment.

               <FormInput
                 control={form.control}
                 name="name"
                 label="Full Name"
                 placeholder="John Doe"
+                autoComplete="name"
                 required
               />

76-82: Add autocomplete for email.

               <FormInput
                 control={form.control}
                 name="email"
                 label="Email"
                 placeholder="john@example.com"
                 type="email"
+                autoComplete="email"
                 required
               />

84-90: Add autocomplete for password fields (new-password).

Improves password manager behavior.

               <FormInput
                 control={form.control}
                 name="password"
                 label="Password"
                 placeholder="Create a strong password"
                 type="password"
+                autoComplete="new-password"
                 required
               />

92-98: Add autocomplete for confirm password (new-password).

               <FormInput
                 control={form.control}
                 name="confirmPassword"
                 label="Confirm Password"
                 placeholder="Confirm your password"
                 type="password"
+                autoComplete="new-password"
                 required
               />

44-53: Harden error aggregation to join arrays safely.

Same improvement as SigninForm.

-      const fieldErrors = error.error.validationErrors?.fieldErrors;
-      const errorMessage =
-        error.error.serverError ??
-        (fieldErrors
-          ? Object.entries(fieldErrors)
-              .map(([key, value]) => `${key}: ${value}`)
-              .join(", ")
-          : "An unknown error occurred");
+      const fieldErrors = error.error.validationErrors?.fieldErrors;
+      const errorMessage =
+        error.error.serverError ??
+        (fieldErrors
+          ? Object.entries(fieldErrors)
+              .map(([key, value]) => {
+                const messages = Array.isArray(value) ? value.join(", ") : String(value);
+                return `${key}: ${messages}`;
+              })
+              .join(", ")
+          : "An unknown error occurred");
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 126b5f8 and e9c1b32.

📒 Files selected for processing (6)
  • src/components/models/auth/SigninForm.tsx (1 hunks)
  • src/components/models/auth/SignupForm.tsx (1 hunks)
  • src/components/pages/signin/SigninPageContainer.tsx (1 hunks)
  • src/components/pages/signup/SignupPageContainer.tsx (1 hunks)
  • src/components/shared/Form/FormCheckbox.tsx (0 hunks)
  • src/components/shared/Form/FormInput.tsx (0 hunks)
💤 Files with no reviewable changes (2)
  • src/components/shared/Form/FormInput.tsx
  • src/components/shared/Form/FormCheckbox.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
src/components/pages/signup/SignupPageContainer.tsx (1)
src/components/models/auth/SignupForm.tsx (1)
  • SignupForm (23-151)
src/components/models/auth/SigninForm.tsx (4)
src/actions/auth/signin/schema.ts (2)
  • SigninInput (8-8)
  • signinSchema (3-6)
src/actions/auth/signin/action.ts (1)
  • signinAction (7-32)
src/components/shared/Form/FormInput.tsx (1)
  • FormInput (11-81)
src/components/shared/Form/FormButton.tsx (1)
  • FormButton (11-27)
src/components/models/auth/SignupForm.tsx (5)
src/actions/auth/signup/schema.ts (2)
  • SignupInput (22-22)
  • signupSchema (3-20)
src/actions/auth/signup/action.ts (1)
  • signupAction (7-31)
src/components/shared/Form/FormInput.tsx (1)
  • FormInput (11-81)
src/components/shared/Form/FormCheckbox.tsx (1)
  • FormCheckbox (17-51)
src/components/shared/Form/FormButton.tsx (1)
  • FormButton (11-27)
src/components/pages/signin/SigninPageContainer.tsx (1)
src/components/models/auth/SigninForm.tsx (1)
  • SigninForm (23-112)
🔇 Additional comments (5)
src/components/pages/signin/SigninPageContainer.tsx (1)

1-5: LGTM: Minimal wrapper with named import is correct.

Container delegates cleanly to SigninForm and aligns with the named‑export refactor.

src/components/pages/signup/SignupPageContainer.tsx (1)

1-5: LGTM: Wrapper pattern and named import look good.

Consistent with the refactor objectives.

src/components/models/auth/SigninForm.tsx (2)

17-19: Good: Uses named exports and imports types from schema.

This aligns with the PR goals and improves type consistency.


1-22: No lingering default imports/exports or manual form-type definitions detected

src/components/models/auth/SignupForm.tsx (1)

16-19: Good: Named imports and schema-derived types.

Matches the PR goals and reduces duplication.

Comment on lines 32 to 33
terms: true,
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not pre-check the Terms checkbox (compliance risk).

Pre-checked consent can violate GDPR/consent best practices; require explicit user action.

-      terms: true,
+      terms: false,
🤖 Prompt for AI Agents
In src/components/models/auth/SignupForm.tsx around lines 32-33 the terms
checkbox is pre-checked (terms: true), which violates consent best practices;
change the initial form value to false (terms: false), ensure the checkbox is
controlled to reflect that initial value, and add or ensure existing validation
blocks submission unless terms is explicitly checked by the user.

Copy link

@coderabbitai coderabbitai bot left a comment

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 (2)
src/actions/auth/signup/schema.ts (1)

5-6: Normalize name/email inputs (trim and lowercase email).

Improves validation UX and reduces backend normalization work.

Apply:

-        name: z.string().min(1, 'Name is required'),
-        email: z.string().email('Invalid email format'),
+        name: z.string().trim().min(1, 'Name is required'),
+        email: z.string().trim().toLowerCase().email('Invalid email format'),
src/components/models/auth/SignupForm.tsx (1)

44-51: Handle fieldErrors as arrays for clearer toasts.

Zod/next-safe-action fieldErrors are typically string[]. Join them explicitly for readability.

-      const errorMessage =
-        error.error.serverError ??
-        (fieldErrors
-          ? Object.entries(fieldErrors)
-              .map(([key, value]) => `${key}: ${value}`)
-              .join(", ")
-          : "An unknown error occurred");
+      const errorMessage =
+        error.error.serverError ??
+        (fieldErrors
+          ? Object.entries(fieldErrors)
+              .map(([key, msgs]) => `${key}: ${(Array.isArray(msgs) ? msgs : [msgs]).join("; ")}`)
+              .join(" | ")
+          : "An unknown error occurred");
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9c1b32 and c464f48.

📒 Files selected for processing (2)
  • src/actions/auth/signup/schema.ts (1 hunks)
  • src/components/models/auth/SignupForm.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/models/auth/SignupForm.tsx (5)
src/actions/auth/signup/schema.ts (2)
  • SignupInput (22-22)
  • signupSchema (3-20)
src/actions/auth/signup/action.ts (1)
  • signupAction (7-31)
src/components/shared/Form/FormInput.tsx (1)
  • FormInput (11-81)
src/components/shared/Form/FormCheckbox.tsx (1)
  • FormCheckbox (17-51)
src/components/shared/Form/FormButton.tsx (1)
  • FormButton (11-27)
🔇 Additional comments (4)
src/actions/auth/signup/schema.ts (2)

13-15: LGTM: boolean + refine for terms is appropriate with RHF checkboxes.

Using z.boolean().refine(...) keeps UX consistent and provides a clear message.


22-22: LGTM: exporting SignupInput from schema.

Removes duplicated form value types and ensures type-source-of-truth.

src/components/models/auth/SignupForm.tsx (2)

27-33: Good: Terms default to false (addresses prior compliance concern).

Prevents pre-checked consent; aligns with best practices.


16-18: No remaining default imports for FormInput/FormCheckbox Verified across all TS/TSX files; all usages migrated to named imports.

Comment on lines +99 to +121
<FormCheckbox
control={form.control}
name="terms"
label={
<span>
I agree to the{" "}
<Link
href="/terms"
className="text-blue-600 hover:underline"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="text-blue-600 hover:underline"
>
Privacy Policy
</Link>
</span>
}
required
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Checkbox onChange likely sets non-boolean values — fix in FormCheckbox.

Current FormCheckbox uses onChange={field.onChange} on a native checkbox. Controller expects you to pass checked boolean; otherwise RHF may store "on" or the event, breaking z.boolean() and preventing valid submission.

Update FormCheckbox:

-              <input
+              <input
                 id={name}
                 type="checkbox"
                 className="h-4 w-4 text-blue-600 border-gray-300 rounded"
-                checked={!!field.value}
-                onChange={field.onChange}
+                checked={!!field.value}
+                onChange={(e) => field.onChange((e.target as HTMLInputElement).checked)}
                 ref={field.ref}
                 required={required}
               />

File: src/components/shared/Form/FormCheckbox.tsx

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<FormCheckbox
control={form.control}
name="terms"
label={
<span>
I agree to the{" "}
<Link
href="/terms"
className="text-blue-600 hover:underline"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="text-blue-600 hover:underline"
>
Privacy Policy
</Link>
</span>
}
required
/>
<input
id={name}
type="checkbox"
className="h-4 w-4 text-blue-600 border-gray-300 rounded"
checked={!!field.value}
onChange={(e) => field.onChange((e.target as HTMLInputElement).checked)}
ref={field.ref}
required={required}
/>
🤖 Prompt for AI Agents
In src/components/shared/Form/FormCheckbox.tsx (update this file), the component
currently wires the native checkbox directly to field.onChange which passes the
event (or "on") into react-hook-form and causes non-boolean values to be stored;
change the input to call field.onChange(e.target.checked) and bind
checked={!!field.value} (defaulting to false) instead of spreading the whole
field onto the input for onChange/checked, and keep other props (onBlur, name,
ref) from field; this ensures RHF receives a boolean and zod/z.boolean()
validation works correctly.

@tasin2610 tasin2610 merged commit 485f190 into main Sep 26, 2025
1 check passed
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.

2 participants