Skip to content

Issue 340 - Implement Forget password on Login page#353

Open
Alien501 wants to merge 2 commits intodjangoindia:mainfrom
Alien501:issue_340
Open

Issue 340 - Implement Forget password on Login page#353
Alien501 wants to merge 2 commits intodjangoindia:mainfrom
Alien501:issue_340

Conversation

@Alien501
Copy link
Copy Markdown

@Alien501 Alien501 commented Apr 3, 2025

Closes #340

Implemented forgot password logic in login page from start to end.

Changes

  • Added forgot password link in login page
  • Created forgot password page to fill email
  • Created rest password page to fill new password

Type of change

  • Bug fix
  • [ x] Feature update
  • Breaking change
  • Documentation update

Demo

image

How has this been tested?

  • Checked manually

Summary by CodeRabbit

  • New Features

    • Added full Forgot Password flow (request reset via email).
    • Added Reset Password flow with link validation and password visibility toggles.
    • Login now shows a “Forgot Password?” link.
    • Improved Change Password form with visibility toggles; revamped Profile form layout.
    • Introduced supporting endpoints and form validations.
  • Documentation

    • Updated Privacy Policy: added “Comply with legal obligations” and revised contact link.
  • Style/Refactor

    • Polished modal and avatar UI; minor navbar and middleware formatting updates with no behavior changes.

@yanurag-dev
Copy link
Copy Markdown
Contributor

@CodeRabbit fullreview

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 28, 2025

@Yadavanurag13 I'll conduct a full review of the PR implementing the forgot password functionality. Let me examine all the changes and provide comprehensive feedback.

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 28, 2025

Walkthrough

Adds forgot/reset password feature: new containers, pages, types, schemas, and endpoints. Login now links to forgot password. User profile and change-password forms refactored (validation, visibility toggles, submission flow). Privacy policy content adjusted. Barrel exports updated. Several files received formatting/styling changes without behavioral impact.

Changes

Cohort / File(s) Summary of changes
Auth pages re-exports
frontend/src/app/(auth)/forgot-password/page.tsx, frontend/src/app/(auth)/reset-password/page.tsx
Added default re-exports to route pages, mapping to container components.
Forgot Password feature
frontend/src/containers/ForgotPassword/*
New container, type, and index barrel. Implements email form with Yup schema, submit via fetchData to /forgot-password, success/error snackbars, Google sign-in link.
Reset Password feature
frontend/src/containers/ResetPassword/*
New container, type, and index barrel. Validates URL params, shows loading, posts new password to /reset-password/{uidb64}/{token}, handles success/error, includes visibility toggles and Google sign-in.
Auth containers barrel
frontend/src/containers/index.ts
Exposes ForgotPassword and ResetPassword through barrel exports.
Validation schemas
frontend/src/constants/schema.tsx
Added FORGOT_PASSWORD_SCHEMA, RESET_PASSWORD_SCHEMA; tweaked confirm password logic across signup/change-password; message/style normalization.
API endpoints
frontend/src/constants/endpoints.ts
Added API_ENDPOINTS.forgotPassword = '/forgot-password'.
Login UI link
frontend/src/containers/Login/Login.tsx
Wrapped Password label with a right-aligned “Forgot Password?” link; no logic changes.
Users: Change Password form
frontend/src/containers/Users/ChangePasswordForm.tsx, .../ChangePasswordForm.config.ts, .../ChangePasswordForm.types.ts
Refactor to RHF with resolver, token fetch, submit handler, password visibility toggles; config uses type-only import and simplified array type; types formatting only.
Users: Profile form and User container
frontend/src/containers/Users/UserProfileForm.tsx, .../UserProfileForm.config.ts, .../UserProfileForm.types.ts, .../User.tsx
Profile form rewritten with RHF, PATCH via fetchData, per-field errors; config switched to type-only import and formatted; types re-indented; User now renders Profile and Change Password forms and adjusts layout.
UI/Styling-only tweaks
frontend/src/components/Modal/modal.tsx, frontend/src/sections/Navbar/Navbar.tsx, frontend/src/containers/Signup/Signup.tsx, frontend/src/containers/Users/UserAvatar.tsx, frontend/src/middleware.ts
Indentation/class reflow and avatar sizing updates; middleware baseUrl formatting only; no behavior changes.
Content update
frontend/src/app/(core)/privacy-policy/page.tsx
Text/markup adjustments, added bullet “Comply with legal obligations.” and link tweaks; no API changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant LoginPage as Login Page
  participant ForgotPage as ForgotPassword Container
  participant API as Backend API
  participant Notify as Snackbar

  User->>LoginPage: Click "Forgot Password?"
  LoginPage->>ForgotPage: Navigate to /forgot-password
  User->>ForgotPage: Enter email + Submit
  ForgotPage->>API: POST /forgot-password { email }
  alt success
    API-->>ForgotPage: 200 OK
    ForgotPage->>Notify: Show success "Check your email"
  else error
    API-->>ForgotPage: 4xx/5xx
    ForgotPage->>Notify: Show error message
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant ResetPage as ResetPassword Container
  participant API as Backend API
  participant Router as Next Router
  participant Notify as Snackbar

  User->>ResetPage: Open /reset-password?uidb64=...&token=...
  ResetPage->>ResetPage: Validate uidb64 & token (decode if needed)
  alt invalid link
    ResetPage->>Notify: Show invalid link error
    ResetPage->>Router: Redirect to "/"
  else valid
    User->>ResetPage: Enter new & confirm password + Submit
    ResetPage->>API: POST /reset-password/{uidb64}/{token} { new_password }
    alt success
      API-->>ResetPage: 200 OK
      ResetPage->>Notify: Show success
      ResetPage->>Router: Redirect to "/"
    else error
      API-->>ResetPage: 4xx/5xx
      ResetPage->>Notify: Show error
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump my paws—reset, reclaim!
A burrow of links, a password tamed.
Forgot? Fear not, dear hopper friend—
An email sent, the woes will end.
With twinkly eyes (•ᴗ•) and tidy forms,
We hop through flows, past error storms.
New keys in paws—onward we perform!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request contains multiple changes unrelated to the forgot password feature such as privacy policy content updates, modal styling adjustments, user profile and change password form refactors, schema modifications, and navbar indentation changes that are not part of issue #340. Separate the unrelated privacy policy, modal, user settings, and schema formatting changes into their own pull requests to keep the scope focused on the forgot password implementation.
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 (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly indicates the implementation of the forget password feature on the login page and directly relates to the main change described in the pull request.
Linked Issues Check ✅ Passed The pull request adds a "Forgot Password?" link on the login page, implements a dedicated forgot password page with email collection and validation, and includes a reset password page for setting a new password, fulfilling all coding requirements from issue #340.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link
Copy Markdown

@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: 10

🧹 Nitpick comments (9)
frontend/src/containers/Users/UserProfileForm.tsx (3)

74-82: Surface server-side field errors via setError instead of many snackbars.

Improves UX and aligns with <FormMessage> rendering.

-  const {
+  const {
     register,
     control,
     reset,
     handleSubmit,
-    formState: { errors, ...restFormState },
+    formState: { errors, ...restFormState },
+    setError,
     ...rest
   } = useForm<ProfileFormValues>({
-  } else {
-    // Handle nested error message object
-    const errorMessages = res.error?.message;
-
-    if (typeof errorMessages === 'object') {
-      Object.entries(errorMessages).forEach(([field, errors]) => {
-        if (Array.isArray(errors)) {
-          errors.forEach((error) =>
-            enqueueSnackbar(`${field}: ${error}`, { variant: 'error' }),
-          );
-        }
-      });
-    } else {
-      enqueueSnackbar('An error occurred', { variant: 'error' });
-    }
-  }
+  } else {
+    const errorMessages = res.error?.message;
+    if (errorMessages && typeof errorMessages === 'object') {
+      Object.entries(errorMessages).forEach(([field, errs]) => {
+        if (Array.isArray(errs)) {
+          errs.forEach((msg) =>
+            setError(field as any, { type: 'server', message: String(msg) }),
+          );
+        } else if (errs) {
+          setError(field as any, { type: 'server', message: String(errs) });
+        }
+      });
+      enqueueSnackbar('Please fix the highlighted fields', { variant: 'error' });
+    } else {
+      enqueueSnackbar('An error occurred', { variant: 'error' });
+    }
+  }

Also applies to: 119-133


116-117: Remove debug log before shipping.

- console.log('res after updated', res);

241-249: RHF props duplication: {...field} already supplies onChange and value.

Drop the extra onChange/value unless you need custom mapping.

Also applies to: 323-331

frontend/src/containers/ResetPassword/ResetPassword.tsx (6)

28-39: Centralize/reset-link param extraction and avoid top-level window access.

Refactor to parse from useSearchParams() and location.pathname, and run in an effect to sidestep hydration gotchas.

-// Retriving raw URL to handle potential HTML encoded parameters
-const url = new URL(window.location.href);
-// Replace HTML-encoded ampersands (&amp;) and other encoded characters
-const fixedSearch = url.search
-  .replace(/&amp%3B/g, '&')
-  .replace(/&amp;/g, '&');
-const params = new URLSearchParams(fixedSearch);
-const uidb64 = params.get("uidb64") || url.searchParams.get("uidb64") || "";
-const token = params.get("token") || url.searchParams.get("token") || "";
+const [uidb64, setUidb64] = useState('');
+const [token, setToken] = useState('');
+
+useEffect(() => {
+  const sp = new URLSearchParams(window.location.search.replace(/&amp;/g, '&'));
+  let u = sp.get('uidb64') ?? '';
+  let t = sp.get('token') ?? '';
+  if ((!u || !t)) {
+    const parts = window.location.pathname.split('/').filter(Boolean);
+    const idx = parts.indexOf('reset-password');
+    if (idx >= 0 && parts[idx + 1] && parts[idx + 2]) {
+      u = parts[idx + 1];
+      t = parts[idx + 2];
+    }
+  }
+  setUidb64(u);
+  setToken(t);
+}, []);

Also applies to: 53-87


99-105: Send JSON header with the POST.

Make payload/content-type explicit for backends that require it.

 const response = await fetchData(
   `/reset-password/${uidb64}/${token}`,
   {
     method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
     body: JSON.stringify({ new_password: data.new_password }),
   }
 );

116-117: Honor redirect after success.

Route users back to their intended destination (or login) instead of always /.

- router.push('/');
+ router.push(redirect || '/login');

Also applies to: 26-27


193-207: Accessibility: add aria-label and proper autoComplete for password fields and toggles.

 <Input
   {...register('new_password')}
   type={showPassword ? "text" : "password"}
   id="new_password"
   placeholder="Enter your new password"
+  autoComplete="new-password"
   className={`${errors.new_password ? 'text-red-500 !outline-red-500' : ''} pr-10`}
 />
 <button
   type="button"
   onClick={togglePassword}
+  aria-label={showPassword ? 'Hide password' : 'Show password'}
   className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500"
>
 <Input
   {...register('confirm_password')}
   type={showConfirmPassword ? "text" : "password"}
   id="confirm_password"
   placeholder="Confirm your new password"
+  autoComplete="new-password"
   className={`${errors.confirm_password ? 'text-red-500 !outline-red-500' : ''} pr-10`}
 />
 <button
   type="button"
   onClick={toggleConfirmPassword}
+  aria-label={showConfirmPassword ? 'Hide confirm password' : 'Show confirm password'}
   className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500"
>

Also applies to: 221-235


45-51: Disable submit while submitting to prevent duplicate requests.

-const {
-  register,
-  handleSubmit,
-  formState: { errors },
-} = useForm<ResetPasswordType>({
+const {
+  register,
+  handleSubmit,
+  formState: { errors, isSubmitting },
+} = useForm<ResetPasswordType>({
   resolver: yupResolver(RESET_PASSWORD_SCHEMA),
 });
-<Button className="mt-4" type="submit">Reset Password</Button>
+<Button className="mt-4" type="submit" disabled={isSubmitting}>
+  Reset Password
+</Button>

Also applies to: 241-242


134-142: Optional: “Validating reset link…” is misleading without a server check.

Either call a lightweight validate endpoint or change the copy to “Checking reset link…”.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b2a990 and 0029b21.

📒 Files selected for processing (25)
  • frontend/src/app/(auth)/forgot-password/page.tsx (1 hunks)
  • frontend/src/app/(auth)/reset-password/page.tsx (1 hunks)
  • frontend/src/app/(core)/privacy-policy/page.tsx (3 hunks)
  • frontend/src/components/Modal/modal.tsx (1 hunks)
  • frontend/src/constants/endpoints.ts (1 hunks)
  • frontend/src/constants/schema.tsx (1 hunks)
  • frontend/src/containers/ForgotPassword/ForgotPassword.tsx (1 hunks)
  • frontend/src/containers/ForgotPassword/ForgotPassword.types.ts (1 hunks)
  • frontend/src/containers/ForgotPassword/index.ts (1 hunks)
  • frontend/src/containers/Login/Login.tsx (1 hunks)
  • frontend/src/containers/ResetPassword/ResetPassword.tsx (1 hunks)
  • frontend/src/containers/ResetPassword/ResetPassword.types.ts (1 hunks)
  • frontend/src/containers/ResetPassword/index.ts (1 hunks)
  • frontend/src/containers/Signup/Signup.tsx (5 hunks)
  • frontend/src/containers/Users/ChangePasswordForm.config.ts (1 hunks)
  • frontend/src/containers/Users/ChangePasswordForm.tsx (1 hunks)
  • frontend/src/containers/Users/ChangePasswordForm.types.ts (1 hunks)
  • frontend/src/containers/Users/User.tsx (2 hunks)
  • frontend/src/containers/Users/UserAvatar.tsx (1 hunks)
  • frontend/src/containers/Users/UserProfileForm.config.ts (2 hunks)
  • frontend/src/containers/Users/UserProfileForm.tsx (1 hunks)
  • frontend/src/containers/Users/UserProfileForm.types.ts (1 hunks)
  • frontend/src/containers/index.ts (1 hunks)
  • frontend/src/middleware.ts (2 hunks)
  • frontend/src/sections/Navbar/Navbar.tsx (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
frontend/src/constants/schema.tsx

[error] 146-146: Do not add then to an object.

(lint/suspicious/noThenProperty)


[error] 212-212: Do not add then to an object.

(lint/suspicious/noThenProperty)

frontend/src/containers/Users/UserProfileForm.tsx

[error] 63-63: Shouldn't redeclare 'ProfileForm'. Consider to delete it or rename it.

'ProfileForm' is defined here:

(lint/suspicious/noRedeclare)

🔇 Additional comments (8)
frontend/src/sections/Navbar/Navbar.tsx (1)

293-301: Formatting tidy, no behavioral impact.

Avatar image/fallback layout is cleaner and keeps the same data flow, so nothing functionally changes.

frontend/src/containers/Users/UserProfileForm.types.ts (1)

2-36: Whitespace-only type formatting looks good.

The reindent keeps the type surface identical while improving readability.

frontend/src/middleware.ts (1)

16-38: Reflow preserves redirect logic.

Splitting the baseUrl construction across lines improves readability without altering the fallback order.

frontend/src/containers/ResetPassword/ResetPassword.types.ts (1)

1-4: Type surface looks good.

The reset-password form fields are captured cleanly with string typing; no issues spotted.

frontend/src/components/Modal/modal.tsx (1)

67-98: Layout tweak confirmed harmless.

Only indentation and spacing around the subscriber inputs changed; validation wiring and bindings remain intact.

frontend/src/containers/Login/Login.tsx (1)

121-134: Forgot-password link integration looks solid.

The link sits alongside the password label without affecting form validation or submission.

frontend/src/containers/Users/UserAvatar.tsx (1)

11-26: Avatar styling update LGTM.

Responsive size utilities and fallback styling are tidy and preserve previous behavior.

frontend/src/containers/ResetPassword/index.ts (1)

1-2: Export barrel is clean.

Container and related types are re-exported in line with the project pattern.

Comment on lines +120 to 124
If you have any questions about this Privacy Policy, please contact
us at:{' '}
<Link href='mailto:contact@djangoindia.org'>
<u>admin@djangoindia.org</u>
</Link>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix contact email href mismatch.

The visible email shows admin@djangoindia.org, but the href sends users to mailto:contact@djangoindia.org. That mismatch will route messages to the wrong inbox and confuse users who copy the address. Please align the href with the displayed address (or update the display text) so clicks and copy-paste go to the same destination. (djangoindia.org)

-            <Link href='mailto:contact@djangoindia.org'>
-              <u>admin@djangoindia.org</u>
-            </Link>
+            <Link href='mailto:admin@djangoindia.org'>
+              <u>admin@djangoindia.org</u>
+            </Link>
📝 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
If you have any questions about this Privacy Policy, please contact
us at:{' '}
<Link href='mailto:contact@djangoindia.org'>
<u>admin@djangoindia.org</u>
</Link>
If you have any questions about this Privacy Policy, please contact
us at:{' '}
<Link href='mailto:admin@djangoindia.org'>
<u>admin@djangoindia.org</u>
</Link>
🤖 Prompt for AI Agents
frontend/src/app/(core)/privacy-policy/page.tsx around lines 120 to 124: the
mailto href and displayed email are inconsistent (href uses
contact@djangoindia.org while visible text shows admin@djangoindia.org); fix by
making them identical—either change the href to mailto:admin@djangoindia.org to
match the visible text or change the visible text to
admin@contact@djangoindia.org (preferably set both to
mailto:admin@djangoindia.org and display admin@djangoindia.org) so clicks and
copy-paste go to the same address.

Comment on lines +144 to +151
confirmPassword: yup.string().when('newPassword', {
is: (newPassword: string) => newPassword && newPassword.length > 0,
then: (schema) =>
schema
.required('Confirm Password is required')
.oneOf([yup.ref('newPassword')], 'Passwords must match'),
otherwise: (schema) => schema.optional().nullable()
}),
otherwise: (schema) => schema.optional().nullable(),
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Resolve Biome noThenProperty lint errors in yup .when usage

Biome fails the build here because the object literal handed to .when exposes a callable then property, which the lint/suspicious/noThenProperty rule flags as a potential thenable. Switch to the callback overload so we keep the same validation while satisfying the linter.

-  confirmPassword: yup.string().when('newPassword', {
-    is: (newPassword: string) => newPassword && newPassword.length > 0,
-    then: (schema) =>
-      schema
-        .required('Confirm Password is required')
-        .oneOf([yup.ref('newPassword')], 'Passwords must match'),
-    otherwise: (schema) => schema.optional().nullable(),
-  }),
+  confirmPassword: yup
+    .string()
+    .when('newPassword', (newPassword, schema) =>
+      newPassword && newPassword.length > 0
+        ? schema
+            .required('Confirm Password is required')
+            .oneOf([yup.ref('newPassword')], 'Passwords must match')
+        : schema.optional().nullable(),
+    ),
-  confirmPassword: yup.string().when('newPassword', {
-    is: (newPassword: string) => newPassword && newPassword.length > 0,
-    then: (schema) =>
-      schema
-        .required('Confirm Password is required')
-        .oneOf([yup.ref('newPassword')], 'Passwords must match'),
-    otherwise: (schema) => schema.optional().nullable(),
-  }),
+  confirmPassword: yup
+    .string()
+    .when('newPassword', (newPassword, schema) =>
+      newPassword && newPassword.length > 0
+        ? schema
+            .required('Confirm Password is required')
+            .oneOf([yup.ref('newPassword')], 'Passwords must match')
+        : schema.optional().nullable(),
+    ),

As reported by Biome.

Also applies to: 210-217

🧰 Tools
🪛 Biome (2.1.2)

[error] 146-146: Do not add then to an object.

(lint/suspicious/noThenProperty)

🤖 Prompt for AI Agents
frontend/src/constants/schema.tsx around lines 144-151 (and similarly update
lines 210-217): the yup .when currently uses an object with a then property
which triggers Biome's noThenProperty lint rule; replace the object overload
with the callback overload signature: call .when('newPassword',
(newPasswordValue, schema) => { return newPasswordValue &&
newPasswordValue.length > 0 ? schema.required('Confirm Password is
required').oneOf([yup.ref('newPassword')], 'Passwords must match') :
schema.optional().nullable(); }); apply the same pattern to the other occurrence
on lines 210-217 to remove the callable then property and satisfy the linter.

Comment on lines +131 to 168
<div className='grid w-full grid-cols-2 items-center gap-1.5'>
<div className='grid items-center gap-1.5'>
<Label
htmlFor='firstName'
className={`${errors.firstName ? 'text-red-500' : ''}`}
>
First Name
</Label>
<Input
{...register('firstName', { required: true })}
type='text'
id='firstName'
placeholder='Enter your first name'
className={`${errors.firstName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.firstName?.message ?? ' '}
</p>
<Label
htmlFor='firstName'
className={`${errors.firstName ? 'text-red-500' : ''}`}
>
First Name
</Label>
<Input
{...register('firstName', { required: true })}
type='text'
id='firstName'
placeholder='Enter your first name'
className={`${errors.firstName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.firstName?.message ?? ' '}
</p>
</div>
<div className='grid items-center gap-1.5'>
<Label
htmlFor='lastName'
className={`${errors.lastName ? 'text-red-500' : ''}`}
>
Last Name
</Label>
<Input
{...register('lastName', { required: true })}
type='text'
id='lastName'
placeholder='Enter your last name'
className={`${errors.lastName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.lastName?.message ?? ' '}
</p>
<Label
htmlFor='lastName'
className={`${errors.lastName ? 'text-red-500' : ''}`}
>
Last Name
</Label>
<Input
{...register('lastName', { required: true })}
type='text'
id='lastName'
placeholder='Enter your last name'
className={`${errors.lastName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.lastName?.message ?? ' '}
</p>
</div>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore single-column layout for mobile viewports.

Locking this block to grid-cols-2 at the base breakpoint forces every screen (including ≤640px) into a two-column layout. On phones, each input drops below ~180 px width, which makes the form difficult to use and regresses the previous mobile-friendly stack. Please keep the form single-column on small screens and only split into two columns once there’s room.

-            <div className='grid w-full grid-cols-2 items-center gap-1.5'>
+            <div className='grid w-full grid-cols-1 items-center gap-1.5 sm:grid-cols-2'>
📝 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
<div className='grid w-full grid-cols-2 items-center gap-1.5'>
<div className='grid items-center gap-1.5'>
<Label
htmlFor='firstName'
className={`${errors.firstName ? 'text-red-500' : ''}`}
>
First Name
</Label>
<Input
{...register('firstName', { required: true })}
type='text'
id='firstName'
placeholder='Enter your first name'
className={`${errors.firstName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.firstName?.message ?? ' '}
</p>
<Label
htmlFor='firstName'
className={`${errors.firstName ? 'text-red-500' : ''}`}
>
First Name
</Label>
<Input
{...register('firstName', { required: true })}
type='text'
id='firstName'
placeholder='Enter your first name'
className={`${errors.firstName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.firstName?.message ?? ' '}
</p>
</div>
<div className='grid items-center gap-1.5'>
<Label
htmlFor='lastName'
className={`${errors.lastName ? 'text-red-500' : ''}`}
>
Last Name
</Label>
<Input
{...register('lastName', { required: true })}
type='text'
id='lastName'
placeholder='Enter your last name'
className={`${errors.lastName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.lastName?.message ?? ' '}
</p>
<Label
htmlFor='lastName'
className={`${errors.lastName ? 'text-red-500' : ''}`}
>
Last Name
</Label>
<Input
{...register('lastName', { required: true })}
type='text'
id='lastName'
placeholder='Enter your last name'
className={`${errors.lastName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.lastName?.message ?? ' '}
</p>
</div>
</div>
<div className='grid w-full grid-cols-1 items-center gap-1.5 sm:grid-cols-2'>
<div className='grid items-center gap-1.5'>
<Label
htmlFor='firstName'
className={`${errors.firstName ? 'text-red-500' : ''}`}
>
First Name
</Label>
<Input
{...register('firstName', { required: true })}
type='text'
id='firstName'
placeholder='Enter your first name'
className={`${errors.firstName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.firstName?.message ?? ' '}
</p>
</div>
<div className='grid items-center gap-1.5'>
<Label
htmlFor='lastName'
className={`${errors.lastName ? 'text-red-500' : ''}`}
>
Last Name
</Label>
<Input
{...register('lastName', { required: true })}
type='text'
id='lastName'
placeholder='Enter your last name'
className={`${errors.lastName ? 'text-red-500 !outline-red-500' : ''}`}
/>
<p className='h-[20px] text-sm text-red-500'>
{errors.lastName?.message ?? ' '}
</p>
</div>
</div>
🤖 Prompt for AI Agents
In frontend/src/containers/Signup/Signup.tsx around lines 131 to 168, the parent
grid is fixed to two columns at all breakpoints which forces a cramped
two-column layout on small screens; change the grid class to be responsive
(e.g., grid-cols-1 sm:grid-cols-2) so the form is single-column on mobile and
becomes two-column at the small breakpoint or larger, preserving existing item
alignment and gap classes.

} from '@/components/Card';
import { API_ENDPOINTS, EDIT_PROFILE_FORM_SCHEMA } from '@/constants';
import { fetchData } from '@/utils';
import { getAccessToken } from '@/utils/getAccesstoken';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify import path casing (getAccesstoken vs getAccessToken).

Case-sensitive filesystems will break if the filename/export don’t match.


🏁 Script executed:

#!/bin/bash
# Check for file and export casing mismatches
fd -IH --glob 'getAccessToken.*' -a
fd -IH --glob 'getAccesstoken.*' -a
rg -nP "export\s+(?:const|function)\s+getAccessToken\b" -C1
rg -nP "export\s+(?:const|function)\s+getAccesstoken\b" -C1

Length of output: 283


Rename util file to match exported function casing
The file frontend/src/utils/getAccesstoken.ts should be renamed to getAccessToken.ts (uppercase ‘T’) and all import paths (e.g. in UserProfileForm.tsx) updated to @/utils/getAccessToken to ensure consistency on case-sensitive filesystems.

🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around line 34, the import
references '@/utils/getAccesstoken' which mismatches the exported function
casing; rename the util file from frontend/src/utils/getAccesstoken.ts to
frontend/src/utils/getAccessToken.ts (uppercase 'T') and update this import to
'@/utils/getAccessToken' (and update any other imports across the codebase) so
file name and import casing match the exported function on case-sensitive
filesystems.

Comment on lines 39 to 41
import type { ProfileForm } from './UserProfileForm.types';
import { enqueueSnackbar } from 'notistack';
import { useState, useEffect } from 'react';
import type * as yup from 'yup';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Resolve identifier collision: component vs imported type ProfileForm (Biome noRedeclare).

Alias the imported type and use the alias in generics/handler to satisfy linter and avoid confusion.

-import type { ProfileForm } from './UserProfileForm.types';
+import type { ProfileForm as ProfileFormValues } from './UserProfileForm.types';

-} = useForm<ProfileForm>({
+} = useForm<ProfileFormValues>({

-const onSubmit: SubmitHandler<ProfileForm> = async (data: any) => {
+const onSubmit: SubmitHandler<ProfileFormValues> = async (data) => {

Also applies to: 63-63, 81-82, 104-104

🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around lines 39-41 (and
also apply at 63, 81-82, 104), the imported type ProfileForm collides with the
component identifier; rename the import to an alias (e.g. ProfileFormType) and
update all usages in generics and handler signatures to use that alias so the
linter noRedeclare error is resolved and the component identifier remains
unchanged.

Comment on lines +83 to +100
defaultValues: {
username: userData?.username || '',
email: userData?.email || '',
first_name: userData?.first_name || '',
last_name: userData?.last_name || '',
gender: userData?.gender || null,
bio: userData?.bio || '',
about: userData?.about || '',
website: userData?.website || null,
linkedin: userData?.linkedin || null,
instagram: userData?.instagram || null,
github: userData?.github || null,
twitter: userData?.twitter || null,
mastodon: userData?.mastodon || null,
organization: userData?.organization || null,
country: userData?.country || null,
user_timezone: userData?.user_timezone || '',
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid null in RHF defaultValues for text/selects (prevents controlled/uncontrolled warnings).

Use empty strings for text inputs and an empty string (or undefined) for selects.

-  gender: userData?.gender || null,
-  website: userData?.website || null,
-  linkedin: userData?.linkedin || null,
-  instagram: userData?.instagram || null,
-  github: userData?.github || null,
-  twitter: userData?.twitter || null,
-  mastodon: userData?.mastodon || null,
-  organization: userData?.organization || null,
-  country: userData?.country || null,
+  gender: userData?.gender || '',
+  website: userData?.website || '',
+  linkedin: userData?.linkedin || '',
+  instagram: userData?.instagram || '',
+  github: userData?.github || '',
+  twitter: userData?.twitter || '',
+  mastodon: userData?.mastodon || '',
+  organization: userData?.organization || '',
+  country: userData?.country || '',
📝 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
defaultValues: {
username: userData?.username || '',
email: userData?.email || '',
first_name: userData?.first_name || '',
last_name: userData?.last_name || '',
gender: userData?.gender || null,
bio: userData?.bio || '',
about: userData?.about || '',
website: userData?.website || null,
linkedin: userData?.linkedin || null,
instagram: userData?.instagram || null,
github: userData?.github || null,
twitter: userData?.twitter || null,
mastodon: userData?.mastodon || null,
organization: userData?.organization || null,
country: userData?.country || null,
user_timezone: userData?.user_timezone || '',
},
defaultValues: {
username: userData?.username || '',
email: userData?.email || '',
first_name: userData?.first_name || '',
last_name: userData?.last_name || '',
gender: userData?.gender || '',
bio: userData?.bio || '',
about: userData?.about || '',
website: userData?.website || '',
linkedin: userData?.linkedin || '',
instagram: userData?.instagram || '',
github: userData?.github || '',
twitter: userData?.twitter || '',
mastodon: userData?.mastodon || '',
organization: userData?.organization || '',
country: userData?.country || '',
user_timezone: userData?.user_timezone || '',
},
🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around lines 83 to 100,
several defaultValues use null for text/select fields which causes React Hook
Form controlled/uncontrolled warnings; change those nulls to empty strings (or
undefined for select if you prefer) — specifically replace gender, website,
linkedin, instagram, github, twitter, mastodon, organization, and country
defaultValues from null to '' (or undefined for selects) so all text
inputs/selects have string/undefined defaults and avoid control state warnings.

Comment on lines +104 to 114
const onSubmit: SubmitHandler<ProfileForm> = async (data: any) => {
try {
const res = await fetchData<{ message: string }>(API_ENDPOINTS.profile, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
mode: 'onChange'
});
body: JSON.stringify(data),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard API call on missing token and disable submit until ready.

Prevents sending Authorization: Bearer null and accidental double-submits.

 const onSubmit: SubmitHandler<ProfileFormValues> = async (data) => {
   try {
+    if (!accessToken) {
+      enqueueSnackbar('Please wait, preparing authentication…', { variant: 'info' });
+      return;
+    }
     const res = await fetchData<{ message: string }>(API_ENDPOINTS.profile, {
       method: 'PATCH',
       headers: {
         'Content-Type': 'application/json',
         Authorization: `Bearer ${accessToken}`,
       },
       body: JSON.stringify(data),
     });
-<Button type='submit'>Save Profile</Button>
+<Button type='submit' disabled={!accessToken || restFormState.isSubmitting}>
+  Save Profile
+</Button>

Also applies to: 345-346, 66-72

🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around lines 104-114 (and
also apply same fix at lines 345-346 and 66-72): the API call is currently sent
even when accessToken is null and the form allows double submits; guard the
submit handler so it returns early (or shows an error) if accessToken is
missing, avoid attaching an Authorization header with a null token, and prevent
double submissions by disabling the submit control while the request is
in-flight (use formState.isSubmitting or a local submitting flag). Ensure the UI
submit button is disabled when token is absent or submitting, and surface a
clear message/validation when the token is missing before calling fetchData.

Comment on lines +182 to +191
{type === 'checkbox' ? (
<div className='flex items-center gap-0'>
<FormControl>
<Input
type='checkbox'
onChange={field.onChange}
disabled={disabled}
value={field.value ?? undefined}
/>
</FormControl>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Checkbox wired as text input: use checked and map boolean values.

Current code passes value for a checkbox; RHF expects booleans.

-<Input
-  type='checkbox'
-  onChange={field.onChange}
-  disabled={disabled}
-  value={field.value ?? undefined}
-/>
+<Input
+  type='checkbox'
+  checked={!!field.value}
+  onChange={(e) => field.onChange(e.currentTarget.checked)}
+  disabled={disabled}
/>
📝 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
{type === 'checkbox' ? (
<div className='flex items-center gap-0'>
<FormControl>
<Input
type='checkbox'
onChange={field.onChange}
disabled={disabled}
value={field.value ?? undefined}
/>
</FormControl>
{type === 'checkbox' ? (
<div className='flex items-center gap-0'>
<FormControl>
<Input
type='checkbox'
checked={!!field.value}
onChange={(e) => field.onChange(e.currentTarget.checked)}
disabled={disabled}
/>
</FormControl>
🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around lines 182-191, the
checkbox input is incorrectly using the value prop (text input behavior) instead
of boolean checked handling; change the Input to use
checked={Boolean(field.value)} and update the onChange to pass the boolean state
(e.g. onChange={e => field.onChange(e.target.checked)}), ensuring disabled
remains, and map any stored string values to booleans before rendering so RHF
receives a true/false value.

Comment on lines +205 to +229
<Select
onValueChange={field.onChange}
defaultValue={String(field.value)}
disabled={disabled}
value={field.value ?? undefined}
>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={placeholder}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{options.map(({ label, value }) => (
<SelectItem
key={value}
value={value}
>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
) : (
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Select is both controlled and uncontrolled (value + defaultValue); remove defaultValue.

Also avoid String(field.value) which yields "null"/"undefined".

-<Select
-  onValueChange={field.onChange}
-  defaultValue={String(field.value)}
-  disabled={disabled}
-  value={field.value ?? undefined}
->
+<Select
+  onValueChange={field.onChange}
+  disabled={disabled}
+  value={field.value ?? undefined}
+>
📝 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
<Select
onValueChange={field.onChange}
defaultValue={String(field.value)}
disabled={disabled}
value={field.value ?? undefined}
>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={placeholder}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{options.map(({ label, value }) => (
<SelectItem
key={value}
value={value}
>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
) : (
<Select
onValueChange={field.onChange}
disabled={disabled}
value={field.value ?? undefined}
>
🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around lines 205 to 229,
the Select is being used as both controlled and uncontrolled (it has both value
and defaultValue) and also uses String(field.value) which can produce
"null"/"undefined"; remove the defaultValue prop so the component is purely
controlled, stop calling String(field.value), and ensure value is a proper
string or undefined (e.g., use field.value ?? undefined or String(field.value)
only after a null/undefined guard), and keep onValueChange wired to
field.onChange ensuring any necessary type conversion happens there.

Comment on lines +292 to +311
<Select
onValueChange={field.onChange}
defaultValue={String(field.value)}
disabled={disabled}
value={field.value ?? undefined}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={item.placeholder} />
</SelectTrigger>
</FormControl>
<SelectContent>
{item.options.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
) : (
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same select issue in single-field renderer.

Remove defaultValue and keep a single source of truth via value.

-<Select
-  onValueChange={field.onChange}
-  defaultValue={String(field.value)}
-  disabled={disabled}
-  value={field.value ?? undefined}
->
+<Select
+  onValueChange={field.onChange}
+  disabled={disabled}
+  value={field.value ?? undefined}
+>
📝 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
<Select
onValueChange={field.onChange}
defaultValue={String(field.value)}
disabled={disabled}
value={field.value ?? undefined}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={item.placeholder} />
</SelectTrigger>
</FormControl>
<SelectContent>
{item.options.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
) : (
<Select
onValueChange={field.onChange}
disabled={disabled}
value={field.value ?? undefined}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={item.placeholder} />
</SelectTrigger>
</FormControl>
<SelectContent>
{item.options.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
🤖 Prompt for AI Agents
In frontend/src/containers/Users/UserProfileForm.tsx around lines 292 to 311,
the Select is using both defaultValue and value which creates two sources of
truth; remove the defaultValue prop (defaultValue={String(field.value)}) and
rely solely on the controlled value prop (value={field.value ?? undefined}) with
onValueChange={field.onChange} so the component remains fully controlled and
consistent.

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.

[FEATURE] Implement Forget password on Login page

2 participants