feat: add modern sign-in page#94
Conversation
|
@DioChuks is attempting to deploy a commit to the Threadflow Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe PR replaces the SignIn UI with a prop-compatible, stateful component that implements email magic-link and Google/GitHub social sign-ins, enables the magic-link plugin in the auth client, injects a global Toaster into RootLayout, and restructures the auth page layout to a muted, responsive container. Changes
Sequence DiagramsequenceDiagram
participant User
participant SignIn as SignIn Component
participant AuthClient as authClient
participant MagicLink as MagicLink Plugin
participant Social as Social Provider
participant Toast as Toaster
User->>SignIn: Enter email + submit
SignIn->>AuthClient: handleMagicLinkSignIn(email)
AuthClient->>MagicLink: request magic link
MagicLink-->>AuthClient: success / error
AuthClient-->>SignIn: result
SignIn->>Toast: show success/error
SignIn-->>User: update UI (loading state)
User->>SignIn: Click social sign-in (Google/GitHub)
SignIn->>AuthClient: handleSocialSignIn(provider)
AuthClient->>Social: initiate OAuth
Social-->>AuthClient: auth response
AuthClient-->>SignIn: result
alt success
SignIn->>User: redirect
else error
SignIn->>Toast: show error
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
app/(auth)/auth/page.tsx (1)
3-3: Unnecessaryasyncon the page component.
Homecontains noawaitexpressions; theasynckeyword adds nothing here and may mislead readers into thinking there is async server-side work.♻️ Proposed fix
-export default async function Home() { +export default function Home() {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/`(auth)/auth/page.tsx at line 3, The Home page component is marked async but contains no await usage; remove the unnecessary async modifier from the Home function declaration (export default function Home()) to avoid implying server-side async work, and double-check there are no awaited calls inside the Home function or any nested helpers before removing it.components/login/sign-in.tsx (1)
129-133: Usenext/imageinstead of a bare<img>tag; fix the non-descriptivealt.Next.js's
<Image>component provides automatic lazy loading, size optimization, and prevents layout shift — important for a decorative image occupying half the sign-in card.
alt="Image"is also non-descriptive; a screen reader would announce "image, image" — use a short meaningful phrase oralt=""if purely decorative.♻️ Proposed fix
+import Image from "next/image"; ... -<img - src="/placeholder.svg" - alt="Image" - className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale" -/> +<Image + src="/placeholder.svg" + alt="" + fill + className="object-cover dark:brightness-[0.2] dark:grayscale" +/>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/login/sign-in.tsx` around lines 129 - 133, Replace the bare <img> in the sign-in card with Next.js's Image component: import Image from "next/image" (in components/login/sign-in.tsx), swap the <img src="/placeholder.svg" ... /> usage for <Image> with appropriate width/height or layout props to avoid layout shift and enable optimization/lazy-loading, and update the alt from the non-descriptive "Image" to either a short meaningful description (e.g., "sign-in illustration") or an empty string alt="" if the image is purely decorative; ensure className/styles are preserved when converting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/login/sign-in.tsx`:
- Around line 138-139: The Terms of Service and Privacy Policy anchor tags in
the SignIn component currently use placeholder href="#" which are no-ops; update
the SignIn component (the anchor elements in components/login/sign-in.tsx) to
either use the real policy URLs (e.g., accept termsUrl and privacyUrl props or
pull from config/constants) or conditionally render those links only when valid
URLs exist (e.g., if (termsUrl) render <a href={termsUrl}>Terms of Service</a>,
otherwise omit or render plain text). Ensure the anchors include target and rel
attributes if linking off-site and validate that any prop or config names you
add (termsUrl, privacyUrl) are documented/typed in the component's props or
defaulted appropriately.
- Around line 45-55: The handleSocialSignIn function calls
authClient.signIn.social but ignores the returned { data, error } object (so
server-side rejections won't trigger the catch); update handleSocialSignIn to
await the response from authClient.signIn.social into a const (e.g., const {
data, error } = await authClient.signIn.social(...)), check if error is non-null
and show a toast.error with the error message and console.error(error), and only
proceed/return on success (or early-return after showing the error) so the user
gets feedback for server-side auth failures.
- Around line 26-43: handleMagicLinkSignIn currently awaits authClient.signIn
via a thrown-exception pattern and uses an unnecessary as any cast, causing the
success toast to fire even when the API returns an error; change the call to use
the proper typed method authClient.signIn.magicLink and destructure its response
(const { data, error } = await authClient.signIn.magicLink({...})), remove the
as any cast, check if error is truthy and call toast.error with the error
message (or a fallback) and only call toast.success when error is falsy, while
keeping setIsMagicLinkLoading(false) in the finally block.
In `@package.json`:
- Line 61: The package.json now contains the unified "radix-ui" package
alongside many individual "@radix-ui/react-*" packages which cause duplicate
primitives and conflict with "cmdk"; remove all individual "@radix-ui/react-*"
entries from dependencies/devDependencies, update component imports in the
codebase to import from "radix-ui" instead of the individual packages (search
for "@radix-ui/react-" imports), then run your package manager to
reinstall/update the lockfile and run a dedupe (npm/yarn/pnpm) to ensure only
one `@radix-ui/react-primitive` version is installed and verify cmdk works
correctly.
---
Nitpick comments:
In `@app/`(auth)/auth/page.tsx:
- Line 3: The Home page component is marked async but contains no await usage;
remove the unnecessary async modifier from the Home function declaration (export
default function Home()) to avoid implying server-side async work, and
double-check there are no awaited calls inside the Home function or any nested
helpers before removing it.
In `@components/login/sign-in.tsx`:
- Around line 129-133: Replace the bare <img> in the sign-in card with Next.js's
Image component: import Image from "next/image" (in
components/login/sign-in.tsx), swap the <img src="/placeholder.svg" ... /> usage
for <Image> with appropriate width/height or layout props to avoid layout shift
and enable optimization/lazy-loading, and update the alt from the
non-descriptive "Image" to either a short meaningful description (e.g., "sign-in
illustration") or an empty string alt="" if the image is purely decorative;
ensure className/styles are preserved when converting.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
components/login/sign-in.tsx (2)
117-122: External "Sign up" link navigates away from the page withoutrelortargetattributes.All three external links (sign-up at line 118, terms at line 136, privacy at line 137) point to a different origin. If the intent is to open them in a new tab, add
target="_blank" rel="noopener noreferrer". If they are meant to replace the current page, this is fine, but the user loses any half-entered email — worth confirming the intended behavior.♻️ Proposed fix (new-tab variant)
<a href="https://www.boundlessfi.xyz/auth?mode=signup" className="underline underline-offset-4" + target="_blank" + rel="noopener noreferrer" > Sign up </a>Apply the same
target/relpair to the Terms of Service and Privacy Policy anchors at lines 136–137.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/login/sign-in.tsx` around lines 117 - 122, The external anchors for "Sign up", "Terms of Service", and "Privacy Policy" currently navigate to a different origin without specifying target or rel; update the three anchor elements (the "Sign up" anchor and the Terms/Privacy anchors) to open in a new tab by adding target="_blank" and rel="noopener noreferrer" to each anchor to prevent navigation from replacing the current page and to mitigate security/privacy issues.
127-131: Prefernext/image<Image>over a bare<img>tag.Next.js's
<Image>component provides automatic lazy loading, size optimization, and format negotiation. The@next/next/no-img-elementESLint rule flags raw<img>usage in Next.js projects.♻️ Proposed refactor
Add the import at the top of the file:
+ import Image from "next/image";Replace the
<img>element:- <img - src="/placeholder.svg" - alt="Image" - className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale" - /> + <Image + src="/placeholder.svg" + alt="" + fill + className="object-cover dark:brightness-[0.2] dark:grayscale" + />
fillreplaces theabsolute inset-0 h-full w-fullpositioning; the parent already hasrelativeset.alt=""is correct for decorative images.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/login/sign-in.tsx` around lines 127 - 131, Replace the raw <img> with Next.js's Image component: add an import for Image from 'next/image' at the top of the file, then replace the <img src="/placeholder.svg" ... /> JSX inside the SignIn component with <Image src="/placeholder.svg" alt="" fill className="object-cover dark:brightness-[0.2] dark:grayscale" /> so the parent relative positioning remains, automatic optimization/ lazy loading are enabled, and the decorative image uses an empty alt; ensure you remove the absolute/inset/h-full/w-full classes from the wrapper since Image's fill handles sizing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/login/sign-in.tsx`:
- Line 129: The img in the SignIn component currently has alt="Image", which is
non-descriptive; update that <img ... alt="Image"> to be an empty alt (alt="")
for purely decorative images so screen readers ignore it, and optionally add
role="presentation" or aria-hidden="true" on the same element if you want to
reinforce that it is decorative.
- Around line 44-53: handleSocialSignIn lacks a try/catch so network exceptions
from authClient.signIn.social become unhandled; wrap the await
authClient.signIn.social(...) call inside a try/catch in the handleSocialSignIn
function, keep the existing handling of the returned { error } (toast.error and
console.error(error)) in the try block, and in the catch block call
toast.error(`Failed to sign in with ${provider}.`) and console.error(err) so
both API-level errors and thrown exceptions are surfaced similarly to the
magic-link handler.
- Around line 30-41: The loading state reset is not guaranteed because
authClient.signIn.magicLink can throw; wrap the sign-in call in a try/finally so
setIsMagicLinkLoading(false) always runs—use try { const { error } = await
authClient.signIn.magicLink({ email, callbackURL: "/bounty" }); if (error) {
toast.error(...); console.error(error); } else { toast.success(...); } } catch
(err) { toast.error(err?.message ?? "Failed to send magic link. Please try
again."); console.error(err); } finally { setIsMagicLinkLoading(false); }—apply
this change around the handler that uses authClient.signIn.magicLink and
setIsMagicLinkLoading to ensure the button is re-enabled on exceptions.
---
Nitpick comments:
In `@components/login/sign-in.tsx`:
- Around line 117-122: The external anchors for "Sign up", "Terms of Service",
and "Privacy Policy" currently navigate to a different origin without specifying
target or rel; update the three anchor elements (the "Sign up" anchor and the
Terms/Privacy anchors) to open in a new tab by adding target="_blank" and
rel="noopener noreferrer" to each anchor to prevent navigation from replacing
the current page and to mitigate security/privacy issues.
- Around line 127-131: Replace the raw <img> with Next.js's Image component: add
an import for Image from 'next/image' at the top of the file, then replace the
<img src="/placeholder.svg" ... /> JSX inside the SignIn component with <Image
src="/placeholder.svg" alt="" fill className="object-cover dark:brightness-[0.2]
dark:grayscale" /> so the parent relative positioning remains, automatic
optimization/ lazy loading are enabled, and the decorative image uses an empty
alt; ensure you remove the absolute/inset/h-full/w-full classes from the wrapper
since Image's fill handles sizing.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
components/login/sign-in.tsx (3)
50-64: LGTM on error handling; consider a loading guard for social buttons.Past issue addressed. Optionally, tracking a
isSocialLoadingstate (or a singleisLoadingthat covers both flows) would disable the social buttons during the async call and prevent duplicate invocations on slower networks.♻️ Suggested approach
const [email, setEmail] = useState(""); const [isMagicLinkLoading, setIsMagicLinkLoading] = useState(false); + const [isSocialLoading, setIsSocialLoading] = useState<"github" | "google" | null>(null); const handleSocialSignIn = async (provider: "github" | "google") => { + setIsSocialLoading(provider); try { const { error } = await authClient.signIn.social({ provider, callbackURL: "/bounty", }); if (error) { toast.error(`Failed to sign in with ${provider}.`); console.error(error); } } catch (err) { toast.error(`Failed to sign in with ${provider}.`); console.error(err); + } finally { + setIsSocialLoading(null); } };Then disable the social buttons with
disabled={isSocialLoading !== null}(or per-buttondisabled={isSocialLoading === "google"}/"github").🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/login/sign-in.tsx` around lines 50 - 64, Add a loading guard to prevent duplicate social sign-ins: introduce a state (e.g., isSocialLoading | null or "google" | "github") and set it at the start and clear it at the end of handleSocialSignIn; wrap the async call to authClient.signIn.social inside this guard so double-clicks are ignored while isSocialLoading is set, and use that state to disable the social buttons (e.g., disabled={isSocialLoading !== null} or per-button disabled={isSocialLoading === "google"/"github"}), ensuring you clear the state in both success/error branches and the catch block.
146-149: Terms of Service / Privacy Policy links navigate away from the sign-in page.Opening these in the same tab loses any partially filled state and exits the auth flow. Add
target="_blank"+rel="noopener noreferrer"so users can read the policies without leaving the page.♻️ Proposed fix
<FieldDescription className="px-6 text-center"> By clicking continue, you agree to our{" "} - <a href="https://www.boundlessfi.xyz/terms">Terms of Service</a>{" "} - and <a href="https://www.boundlessfi.xyz/privacy">Privacy Policy</a>. + <a href="https://www.boundlessfi.xyz/terms" target="_blank" rel="noopener noreferrer">Terms of Service</a>{" "} + and <a href="https://www.boundlessfi.xyz/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>. </FieldDescription>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/login/sign-in.tsx` around lines 146 - 149, The policy links inside the FieldDescription component currently open in the same tab and can disrupt the sign-in flow; update the two anchor elements in the FieldDescription (the Terms of Service and Privacy Policy links) to include target="_blank" and rel="noopener noreferrer" so they open in a new tab safely without losing form state. Locate the anchors in the sign-in component (inside FieldDescription) and add those attributes to both <a> elements.
138-143: Prefernext/imageover bare<img>for the decorative panel.The
<img>tag bypasses Next.js image optimization (lazy loading, format negotiation, LCP hints). Since the parent already carriesrelative, thefillvariant drops in cleanly.♻️ Proposed refactor
+ import Image from "next/image"; // … <div className="bg-muted relative hidden md:block"> - <img - src="/placeholder.svg" - alt="Image" - className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale" - /> + <Image + src="/placeholder.svg" + alt="" + fill + className="object-cover dark:brightness-[0.2] dark:grayscale" + /> </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/login/sign-in.tsx` around lines 138 - 143, Replace the bare <img> in the decorative panel with Next.js's Image component: import Image from "next/image" at the top of components/login/sign-in.tsx, then swap the <img src="/placeholder.svg" ... /> inside the relative parent for <Image src="/placeholder.svg" alt="Image" fill className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale" /> (use the fill prop instead of width/height so it layers with the parent's positioning and preserves the existing classes/behavior), removing the original <img> and ensuring the import is added.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@components/login/sign-in.tsx`:
- Line 140: The img element in the SignIn component currently uses alt="Image",
which is non-descriptive; update the <img> used in the SignIn
(components/login/sign-in.tsx) to provide an appropriate, meaningful alt text
that describes the image's content or purpose (e.g., "Company logo" or "Sign-in
illustration"), or if the image is purely decorative set alt="" so screen
readers ignore it; modify the alt attribute on that img element accordingly.
---
Nitpick comments:
In `@components/login/sign-in.tsx`:
- Around line 50-64: Add a loading guard to prevent duplicate social sign-ins:
introduce a state (e.g., isSocialLoading | null or "google" | "github") and set
it at the start and clear it at the end of handleSocialSignIn; wrap the async
call to authClient.signIn.social inside this guard so double-clicks are ignored
while isSocialLoading is set, and use that state to disable the social buttons
(e.g., disabled={isSocialLoading !== null} or per-button
disabled={isSocialLoading === "google"/"github"}), ensuring you clear the state
in both success/error branches and the catch block.
- Around line 146-149: The policy links inside the FieldDescription component
currently open in the same tab and can disrupt the sign-in flow; update the two
anchor elements in the FieldDescription (the Terms of Service and Privacy Policy
links) to include target="_blank" and rel="noopener noreferrer" so they open in
a new tab safely without losing form state. Locate the anchors in the sign-in
component (inside FieldDescription) and add those attributes to both <a>
elements.
- Around line 138-143: Replace the bare <img> in the decorative panel with
Next.js's Image component: import Image from "next/image" at the top of
components/login/sign-in.tsx, then swap the <img src="/placeholder.svg" ... />
inside the relative parent for <Image src="/placeholder.svg" alt="Image" fill
className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2]
dark:grayscale" /> (use the fill prop instead of width/height so it layers with
the parent's positioning and preserves the existing classes/behavior), removing
the original <img> and ensuring the import is added.
The Build check is not passing. @DioChuks
|
will look into it now, i had to sleep 🙏 |
|
@0xdevcollins done, the issue happened cos of shadcn cmd didnt write to |


Description - Modern Sign-In Page
I have successfully modernized the sign-in page using the shadcn
login-04block.Changes Made
components/login/sign-in.tsx
Updated the component to use the
login-04layout with a split-screen design.GitHub Auth: Restored and integrated the GitHub login flow.
Google Auth: Added new support for Google sign-in.
Magic Link: Added an email input and "Login with Magic Link" button using Better Auth.
Sign Up Link: Updated the "Sign Up" link to redirect to
https://www.boundlessfi.xyz/auth?mode=signup.Loading States: Added a loading spinner for the Magic Link submission.
User Feedback: Integrated
sonnertoasts for success and error messages.Build Fixes:
LoginForm.magicLinkClientplugin in lib/auth-client.ts.Toastercomponent in app/layout.tsx.Verification
/authto verify.code Review
authClientintegration for all providers.login-04.Note
I added a
placeholder.svgbut it can be updated to match your theme design though.Closes #92
Summary by CodeRabbit
New Features
Refactor