-
Notifications
You must be signed in to change notification settings - Fork 4
Integrate end-to-end forgot/reset password and logout flows (RHF + Zod, TanStack Query) #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate end-to-end forgot/reset password and logout flows (RHF + Zod, TanStack Query) #55
Conversation
…to relevant pages
…rgot and /password/reset
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (2)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughAdds comprehensive documentation (README overhaul and new docs). Implements full forgot/reset password client flow with new forms, hooks, and pages, plus password strength UI. Updates validation utilities and schemas. Adjusts middleware/public routes and sign-out behavior. Server adds a check preventing reuse of the current password. Minor UI text and footer link updates. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant ForgotPage as Forgot Password Page
participant Form as ForgotPasswordForm
participant Hook as useForgotPasswordMutation
participant API as POST /api/auth/password/forgot
participant Router as Next Router
User->>ForgotPage: Visit /password/forgot
ForgotPage->>Form: Render
User->>Form: Enter email + Submit
Form->>Hook: mutate({ email })
Hook->>API: POST { email }
API-->>Hook: { token? }
Hook-->>Form: result
Note over Form: On success, derive redirect target
alt token returned
Form->>Router: replace(/password/reset?token=...)
else no token (dev-only)
Form->>Router: replace(/password/reset)
end
Note over User,Form: Errors surfaced via toast
sequenceDiagram
autonumber
actor User
participant ResetPage as Reset Password Page
participant Form as ResetPasswordForm
participant Mut as useResetPasswordMutation
participant API as POST /api/auth/password/reset
participant Service as auth.service.resetPassword
participant DB as DB
participant Query as QueryClient
participant Router as Next Router
User->>ResetPage: Visit /password/reset?token=...
ResetPage->>Form: Render (reads token)
User->>Form: Enter new/confirm password + Submit
Form->>Mut: mutate({ token, newPassword, confirmPassword })
Mut->>API: POST { token, newPassword, confirmPassword }
API->>Service: resetPassword(...)
Service->>DB: Fetch user (include hashedPassword)
Service-->>Service: verify(newPassword != current)
alt new == current
Service-->>API: 422 error
API-->>Mut: error
Mut-->>Form: error
Form-->>User: toast(error)
else new != current
Service->>DB: Update hashed password
Service-->>API: success
API-->>Mut: { ok: true }
Mut-->>Form: result
Form->>Query: invalidate(auth.session)
Form->>Router: replace(/login?reset=done)
Form-->>User: toast(success)
end
sequenceDiagram
autonumber
actor User
participant MW as middleware.ts
participant Routes as publicRoutes.ts
User->>MW: Request /password/forgot or /password/reset
MW->>Routes: Check PUBLIC_ROUTES/PREFIXES
alt Authenticated and visiting auth page
MW-->>User: Redirect to dashboard
else Not authenticated or public route
MW-->>User: Continue
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
✅ Actions performedReview triggered.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 23
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/app/(client)/(auth)/components/RegisterForm.tsx (1)
44-46
: Use onSuccess(data) param instead of reading registerMutation.data inside the callbackPrevents potential stale reads and aligns with TanStack Query best practices.
- const message = - registerMutation.data?.message ?? 'Account created. Redirecting to your dashboard…'; + const message = + // `data` is the first param of onSuccess from the register mutation + // Ensure useFormSubmission forwards it through. + (data as any)?.message ?? 'Account created. Redirecting to your dashboard…';If
useFormSubmission
doesn’t currently passonSuccess(data)
, extend its types to forward the mutation result. As per coding guidelines.src/app/(client)/hooks/forms/useFormSubmission.ts (3)
137-139
: Bug: surfaces a phantom error string when there is no mutation error
toMessage(undefined)
returns "An unexpected error occurred.", soerror
is non-empty even when no error occurred.Apply:
- const loading = mutation ? mutation.isPending : localLoading; - const error = mutation ? toMessage(mutation.error as unknown) : localError; + const loading = mutation ? mutation.isPending : localLoading; + const error = mutation?.error ? toMessage(mutation.error as unknown) : localError;
168-173
: Guard against double-submitsSubmitting while
isPending
/localLoading
risks duplicate requests.Apply:
const handleSubmit = async (e: FormEvent) => { e.preventDefault(); if (validate && !validate()) return; + // Prevent re-entrancy + if (mutation ? mutation.isPending : localLoading) return;
172-183
: Reset legacy local error before a new attemptKeeps error UI accurate between attempts in legacy mode.
Apply:
try { + if (!mutation) setLocalError(''); let result: R | undefined;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (9)
.coderabbit.yaml
is excluded by none and included by noneCODEOWNERS
is excluded by none and included by noneCODE_OF_CONDUCT.md
is excluded by none and included by noneCONTRIBUTING.md
is excluded by none and included by noneSECURITY.md
is excluded by none and included by nonepackage.json
is excluded by none and included by nonepublic/assets/icons/general/CheckCircleIcon.tsx
is excluded by!public/assets/**
and included by nonepublic/assets/icons/general/XCircleIcon.tsx
is excluded by!public/assets/**
and included by nonepublic/assets/icons/index.ts
is excluded by!public/assets/**
and included by none
📒 Files selected for processing (28)
README.md
(1 hunks)docs/architecture.md
(1 hunks)docs/development-guide.md
(1 hunks)docs/getting-started.md
(1 hunks)src/app/(client)/(auth)/components/ForgotPasswordForm.tsx
(1 hunks)src/app/(client)/(auth)/components/RegisterForm.tsx
(3 hunks)src/app/(client)/(auth)/components/ResetPasswordForm.tsx
(1 hunks)src/app/(client)/(auth)/password/forgot/page.tsx
(1 hunks)src/app/(client)/(auth)/password/reset/page.tsx
(1 hunks)src/app/(client)/components/auth/GoogleAuthButton.tsx
(3 hunks)src/app/(client)/components/form/PasswordValidation.tsx
(1 hunks)src/app/(client)/components/index.ts
(1 hunks)src/app/(client)/hooks/data/auth/useForgotPasswordMutation.ts
(1 hunks)src/app/(client)/hooks/data/auth/useResetPasswordMutation.ts
(1 hunks)src/app/(client)/hooks/data/index.ts
(1 hunks)src/app/(client)/hooks/data/useSignOutMutation.ts
(3 hunks)src/app/(client)/hooks/forms/index.ts
(1 hunks)src/app/(client)/hooks/forms/useForgotPasswordForm.ts
(1 hunks)src/app/(client)/hooks/forms/useFormSubmission.ts
(3 hunks)src/app/(client)/hooks/forms/useResetPasswordForm.ts
(1 hunks)src/app/(client)/page.tsx
(1 hunks)src/app/(server)/api/auth/password/forgot/route.ts
(1 hunks)src/app/(server)/api/auth/password/reset/route.ts
(1 hunks)src/app/(server)/services/auth/service.ts
(2 hunks)src/lib/middleware/publicRoutes.ts
(2 hunks)src/lib/validation/auth.schemas.ts
(3 hunks)src/lib/validation/validationUtils.ts
(1 hunks)src/middleware.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
src/app/[(]server[)]/api/**/route.ts
⚙️ CodeRabbit configuration file
src/app/[(]server[)]/api/**/route.ts
: Keep route handlers thin: validate with Zod (shared schemas), call a Service, then return a typed response. Do NOT import prisma/redis or perform business logic here. Example: parse with a schema from src/lib/validation/** and call a function in src/app/(server)/services/** that returns typed data.
Files:
src/app/(server)/api/auth/password/reset/route.ts
src/app/(server)/api/auth/password/forgot/route.ts
src/app/[(]client[)]/**
⚙️ CodeRabbit configuration file
src/app/[(]client[)]/**
: Use TanStack Query hooks for server data (no inline fetches in components). Import query keys from src/lib/queryKeys.ts (canonical). Keep components presentational; Tailwind-first styling. Example: import { keys } from 'src/lib/queryKeys' and call a typed hook rather than fetch in the component.
Files:
src/app/(client)/hooks/forms/useForgotPasswordForm.ts
src/app/(client)/components/index.ts
src/app/(client)/hooks/data/index.ts
src/app/(client)/(auth)/password/reset/page.tsx
src/app/(client)/hooks/forms/useResetPasswordForm.ts
src/app/(client)/page.tsx
src/app/(client)/hooks/forms/index.ts
src/app/(client)/components/form/PasswordValidation.tsx
src/app/(client)/(auth)/components/ForgotPasswordForm.tsx
src/app/(client)/(auth)/components/ResetPasswordForm.tsx
src/app/(client)/hooks/forms/useFormSubmission.ts
src/app/(client)/hooks/data/auth/useResetPasswordMutation.ts
src/app/(client)/components/auth/GoogleAuthButton.tsx
src/app/(client)/hooks/data/auth/useForgotPasswordMutation.ts
src/app/(client)/(auth)/components/RegisterForm.tsx
src/app/(client)/(auth)/password/forgot/page.tsx
src/app/(client)/hooks/data/useSignOutMutation.ts
src/lib/validation/**
⚙️ CodeRabbit configuration file
src/lib/validation/**
: Define Zod schemas and infer types via z.infer. Avoid duplicate shapes; export schemas for reuse in Services and API routes. Co-locate schema files by domain.
Files:
src/lib/validation/auth.schemas.ts
src/lib/validation/validationUtils.ts
src/lib/middleware/**
⚙️ CodeRabbit configuration file
src/lib/middleware/**
: Shared middleware helpers. Keep lean; no business logic or side effects.
Files:
src/lib/middleware/publicRoutes.ts
docs/**
⚙️ CodeRabbit configuration file
docs/**
: Keep Development Guide aligned with current directory structure and conventions. Update docs when APIs/routes/schemas change.
Files:
docs/development-guide.md
docs/getting-started.md
docs/architecture.md
src/middleware.ts
⚙️ CodeRabbit configuration file
src/middleware.ts
: Global middleware for auth/guards/request shaping only. No business logic.
Files:
src/middleware.ts
src/app/[(]server[)]/services/**
⚙️ CodeRabbit configuration file
src/app/[(]server[)]/services/**
: All business logic lives here. No Request/Response/cookies; accept plain inputs and return typed results. Reuse shared types and Zod schemas from src/lib/validation/**. Keep services framework-agnostic and unit-testable.
Files:
src/app/(server)/services/auth/service.ts
🪛 markdownlint-cli2 (0.18.1)
docs/development-guide.md
14-14: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
16-16: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
19-19: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
21-21: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
24-24: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
25-25: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
44-44: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
46-46: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
53-53: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
56-56: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
62-62: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
67-67: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
72-72: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
README.md
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
6-6: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above
(MD022, blanks-around-headings)
22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
26-26: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
30-30: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
34-34: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
38-38: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
41-41: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
45-45: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
51-51: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above
(MD022, blanks-around-headings)
51-51: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
57-57: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
63-63: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
67-67: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
72-72: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
77-77: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
82-82: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
88-88: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
93-93: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
docs/architecture.md
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
6-6: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above
(MD022, blanks-around-headings)
22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
26-26: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
30-30: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
34-34: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
38-38: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
41-41: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
45-45: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
51-51: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above
(MD022, blanks-around-headings)
51-51: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
57-57: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
63-63: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
67-67: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
72-72: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
77-77: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
82-82: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
88-88: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
93-93: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🔇 Additional comments (13)
src/app/(client)/(auth)/password/reset/page.tsx (1)
3-4
: Import paths are correct; no updates needed — ‘../../components/...’ already resolves to src/app/(client)/(auth)/components.src/lib/middleware/publicRoutes.ts (1)
14-16
: Route updates LGTM. No stale references to/forgot-password
or/reset-password
found across the repository.src/app/(client)/hooks/data/auth/useResetPasswordMutation.ts (1)
14-18
: Avoid sending confirmPassword if server doesn’t require it.If the server validates equality itself from two fields, keep it. If not required, drop confirmPassword from the payload to minimize surface.
Do you want me to scan the server route to confirm whether confirmPassword is used before trimming it here?
src/app/(client)/hooks/data/index.ts (1)
8-9
: LGTM: new auth mutations are properly re-exported.Public surface looks consistent with existing pattern.
docs/development-guide.md (1)
75-89
: Confirm MD022/031 fixes manually
markdownlint-cli2 --fix didn’t apply the blank-line changes around headings and fenced code blocks. Please manually add missing blank lines in docs/development-guide.md at lines 14–16, 32–34, 52–54, 55–56, 75–89 and 112–116.src/app/(client)/hooks/forms/useFormSubmission.ts (1)
114-114
: Good change: pass mutation result to onSuccessAllowing
onSuccess
to receive(result?: R)
enables result-aware flows (e.g., navigation with returned token). Looks correct.src/app/(client)/(auth)/components/ForgotPasswordForm.tsx (3)
24-42
: Integration with mutation+form hook looks solidGood use of adapter mode (
getVariables
,validate
, manual error toast). Redirects on success are handled inonSuccess
.
29-35
: Security: avoid passing raw reset token in the URL in non-devQuery strings leak via logs/referrers. If this is dev-only, guard with an env check and prefer server-delivered reset links in production.
Would you like a follow-up patch that gates this branch with
process.env.NEXT_PUBLIC_ENV === 'development'
and uses a toast-only path otherwise?
28-36
: No changes required:ForgotPasswordResult
already definestoken?: string
, sores?.token
is type-safe.src/app/(client)/hooks/data/auth/useForgotPasswordMutation.ts (1)
13-18
: Double-check API does not return raw reset tokens in productionReturning a reset token to the client is typically unsafe. Ensure the endpoint only returns a generic success in prod.
Would you like a conditional mock/dev implementation and production-safe response contract?
src/app/(client)/hooks/data/useSignOutMutation.ts (1)
21-27
: Good alignment with app architectureUsing
useRouter
+@tanstack/react-query
and importing canonicalqueryKeys
matches the client app guidelines.src/app/(client)/(auth)/components/ResetPasswordForm.tsx (2)
10-10
: Nice reusePulling in
PasswordValidation
here is a solid UX touch and keeps validation logic DRY.
32-55
: I’ve initiated a script to locate and display theuseFormSubmission
definition. Once we have its signature and implementation details, we can confirm whetheronSuccess
receives the mutation result payload.
## Table of Contents | ||
- [Table of Contents](#table-of-contents) | ||
- [Overview](#overview) | ||
- [Features](#features) | ||
- [Architecture](#architecture) | ||
- [Tech stack](#tech-stack) | ||
- [Quick start](#quick-start) | ||
- [Prerequisites](#prerequisites) | ||
- [Environment](#environment) | ||
- [Commands](#commands) | ||
- [Project structure](#project-structure) | ||
- [Configuration and scripts](#configuration-and-scripts) | ||
- [Testing](#testing) | ||
- [CI/CD and deployment](#cicd-and-deployment) | ||
- [Conventions](#conventions) | ||
- [Community](#community) | ||
- [Security](#security) | ||
- [Troubleshooting / FAQ](#troubleshooting--faq) | ||
- [License](#license) | ||
|
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
## License | ||
No license file is currently included (TODO). Ownership and licensing terms are undecided. | ||
|
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
<span className='text-muted-foreground'> | ||
[TODO: Insert external docs site link when available] | ||
</span> | ||
<span className='text-muted-foreground'> | ||
[TODO: Insert support contact when available] | ||
</span> |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
@SepidehShahbazi Please review and resolve comments from CodeRabbit
|
…luewave-labs/LangRoute into feat/29-forgot-reset-password-integration
…luewave-labs/LangRoute into feat/29-forgot-reset-password-integration
…ithub.com/bluewave-labs/LangRoute into feat/29-forgot-reset-password-integration
@mahid797 I reviewed the CodeRabbit comments and resolved all applicable ones. |
6dfe4f5
into
feat/29-auth-pages-integration
Refs #29
✨ Summary
This PR integrates the authentication flow (forgot/reset password, logout) end to end. It wires RHF + Zod forms to TanStack Query, introduces reusable hooks (forms, data) and a live
PasswordValidation
UI, implements accessible inline/server error handling, and sets up cache invalidation.✅ Implemented
🔐 Auth data layer
useForgotPasswordMutation
anduseResetPasswordMutation
(TanStack Query) under hooks/data/auth.useSignOutMutation
to disable automatic redirect and manually navigate withrouter.replace(callbackUrl ?? '/login')
for consistent post-logout UX.🧾 Forms (RHF + Zod)
Implemented reusable form hooks in hooks/forms:
useForgotPasswordForm
anduseResetPasswordForm
— composed hooks used by the pages.🖥️ Pages
Moved
forgot
andreset
password from SSRpage.tsx
to client components (ForgotPasswordForm.tsx
,ResetPasswordForm.tsx
).app/(client)/(auth)/password/forgot/page.tsx
+ForgotPasswordForm.tsx
):useForgotPasswordMutation
.app/(client)/(auth)/password/reset/page.tsx
+ResetPasswordForm.tsx
):useResetPasswordMutation
.['auth', 'session']
, and redirected to/login
.token
fromsearchParams
.🧩 Reusable components & 🎨 Icons
PasswordValidation
component and Applied it to all pages with password fields onRegister
andReset
Password forms.XCircleIcon
andCheckCircleIcon
SVGs and converted them to React components for use insidePasswordValidation
.🛠️ Backend & API
/api/auth/password/reset
endpoint to reject cases where the new password matches the current password.🛣️ Public Routes & Middleware
publicRoutes.ts
andMiddleware.ts
to include/password/forgot
and/password/reset
.📸 UI Changes
Summary by CodeRabbit