Skip to content

Comments

feat: add modern sign-in page#94

Merged
0xdevcollins merged 4 commits intoboundlessfi:mainfrom
DioChuks:feature/auth-shadcn
Feb 21, 2026
Merged

feat: add modern sign-in page#94
0xdevcollins merged 4 commits intoboundlessfi:mainfrom
DioChuks:feature/auth-shadcn

Conversation

@DioChuks
Copy link
Contributor

@DioChuks DioChuks commented Feb 20, 2026

Description - Modern Sign-In Page

I have successfully modernized the sign-in page using the shadcn login-04 block.

Changes Made

components/login/sign-in.tsx

  • Updated the component to use the login-04 layout 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 sonner toasts for success and error messages.

  • Build Fixes:

Verification

  • i manually checked the ui & integration.
  • visit /auth to verify.

code Review

  • Verified authClient integration for all providers.
  • Verified component responsiveness and layout consistency with login-04.
  • Verified redirects and external links.

Note

I added a placeholder.svg but it can be updated to match your theme design though.


Closes #92

Summary by CodeRabbit

  • New Features

    • Magic-link email sign-in
    • Social sign-in with Google and GitHub
    • Global toast notifications for feedback
  • Refactor

    • Redesigned authentication page and Sign In UI with a responsive, constrained layout, two-column card presentation, and updated form flows and messaging

@vercel
Copy link

vercel bot commented Feb 20, 2026

@DioChuks is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The 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

Cohort / File(s) Summary
Page & Layout
app/(auth)/auth/page.tsx, app/layout.tsx
Auth page layout changed to a muted background with constrained inner width; global <Toaster /> added to RootLayout for app-wide toasts.
Sign-in Component
components/login/sign-in.tsx
Rewrote SignIn as a prop-accepting functional component with local state, email magic-link flow, Google & GitHub social sign-ins, loading/validation/toasts, two-column Card UI, and updated export signature to accept className/div props.
Auth client
lib/auth-client.ts
Initialized authClient with plugins: [magicLinkClient()], enabling Better Auth magic-link flows.
Dependencies
package.json
Added radix-ui dependency (^1.4.3).

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through JSX leaves tonight,
Magic links aglow and OAuth bright,
Toasts that cheer and inputs neat,
Two-column cards on cozy seat,
Hop in — sign in, the world’s alight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add modern sign-in page' clearly describes the main change, accurately reflecting the implementation of a modernized sign-in page with updated UI and authentication features.
Linked Issues check ✅ Passed The pull request successfully implements all coding requirements from issue #92: modern sign-in page using shadcn login-04, GitHub authentication, Google sign-in, magic-link flow, correct Sign Up redirect URL, and proper component placement.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to implementing the modern sign-in page: component restructuring, authentication client configuration, layout updates for toast notifications, and dependency additions necessary for the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 4

🧹 Nitpick comments (2)
app/(auth)/auth/page.tsx (1)

3-3: Unnecessary async on the page component.

Home contains no await expressions; the async keyword 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: Use next/image instead of a bare <img> tag; fix the non-descriptive alt.

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 or alt="" 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.

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: 3

🧹 Nitpick comments (2)
components/login/sign-in.tsx (2)

117-122: External "Sign up" link navigates away from the page without rel or target attributes.

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 / rel pair 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: Prefer next/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-element ESLint 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"
+ />

fill replaces the absolute inset-0 h-full w-full positioning; the parent already has relative set. 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.

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.

🧹 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 isSocialLoading state (or a single isLoading that 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-button disabled={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: Prefer next/image over bare <img> for the decorative panel.

The <img> tag bypasses Next.js image optimization (lazy loading, format negotiation, LCP hints). Since the parent already carries relative, the fill variant 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.

@0xdevcollins
Copy link
Contributor

image The Build check is not passing. @DioChuks

@DioChuks
Copy link
Contributor Author

image The Build check is not passing. @DioChuks

will look into it now, i had to sleep 🙏

@DioChuks
Copy link
Contributor Author

@0xdevcollins done, the issue happened cos of shadcn cmd didnt write to package.json correctly.

@0xdevcollins 0xdevcollins merged commit 4322602 into boundlessfi:main Feb 21, 2026
2 of 3 checks 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.

Implement Sign-in Page using shadcn login-04

2 participants