Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 165 additions & 11 deletions apps/web/app/(org)/login/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AnimatePresence, motion } from "framer-motion";
import Cookies from "js-cookie";
import { LucideArrowUpRight } from "lucide-react";
import { KeyRound, LucideArrowUpRight } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
Expand Down Expand Up @@ -40,6 +40,8 @@ export function LoginForm() {
const [lastEmailSentTime, setLastEmailSentTime] = useState<number | null>(
null,
);
const [password, setPassword] = useState("");
const [showCredientialLogin,setShowCredientialLogin]=useState(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo and formatting.

The variable name has a typo ("Crediential" should be "Credential") and is missing spaces around operators.

Apply this diff:

-	const [showCredientialLogin,setShowCredientialLogin]=useState(false);
+	const [showCredentialLogin, setShowCredentialLogin] = useState(false);

Note: This change will require updating all references to these identifiers throughout the file (lines 253, 368, 376, 387, 467, 475, 522, 553, 562, 619).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/app/(org)/login/form.tsx around line 44, the state variable
declaration contains a typo and formatting issues: rename showCredientialLogin
to showCredentialLogin, and rename setShowCredientialLogin to
setShowCredentialLogin, adding spaces around the = and after commas; then update
all usages of these identifiers throughout the file (lines 253, 368, 376, 387,
467, 475, 522, 553, 562, 619) to the corrected names so references remain
consistent.

const theme = Cookies.get("theme") || "light";

useEffect(() => {
Expand Down Expand Up @@ -248,6 +250,60 @@ export function LoginForm() {
e.preventDefault();
if (!email) return;

if (showCredientialLogin) {
if (!email || !password) {
toast.error("Please enter email and password");
return;
}

try {
setLoading(true);
trackEvent("auth_started", { method: "password", is_signup: false });

const res = await signIn("credentials", {
email,
password,
redirect: false,
...(next && next.length > 0 ? { callbackUrl: next } : {}),
});

setLoading(false);

if (res?.ok && !res?.error) {
trackEvent("auth_success", { method: "password", is_signup: false });
router.push(next || "/");
return;
}


// Handle specific known errors first
if (res?.error?.toLowerCase().includes("verify")) {
toast.error("Please verify your email before logging in.");
const res = await fetch("/api/signup/resend", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});

const data = await res.json();
if (!data?.status) {
throw "Something went wrong,try other login methods";
}
router.push(`/verify-otp?email=${encodeURIComponent(email)}&type=credientials`);
return;
}

// Otherwise generic error
toast.error("Invalid credentials – try again?");
} catch (error) {
console.error("Credential login error:", error);
toast.error("Invalid credentials – try again?");
} finally {
setLoading(false);
}
return;
}

// Check if we're rate limited on the client side
if (lastEmailSentTime) {
const timeSinceLastRequest =
Expand Down Expand Up @@ -309,15 +365,28 @@ export function LoginForm() {
}}
className="flex flex-col space-y-3"
>
<NormalLogin
setShowOrgInput={setShowOrgInput}
email={email}
emailSent={emailSent}
setEmail={setEmail}
loading={loading}
oauthError={oauthError}
handleGoogleSignIn={handleGoogleSignIn}
/>
{showCredientialLogin ? (
<LoginWithEmailAndPassword
email={email}
emailSent={emailSent}
setEmail={setEmail}
password={password}
setPassword={setPassword}
loading={loading}
setShowCredientialLogin={setShowCredientialLogin}
/>
) : (
<NormalLogin
setShowOrgInput={setShowOrgInput}
email={email}
emailSent={emailSent}
setEmail={setEmail}
loading={loading}
oauthError={oauthError}
handleGoogleSignIn={handleGoogleSignIn}
setShowCredientialLogin={setShowCredientialLogin}
/>
)}
</motion.form>
)}
</motion.div>
Expand Down Expand Up @@ -388,6 +457,79 @@ const LoginWithSSO = ({
);
};

const LoginWithEmailAndPassword = ({
email,
emailSent,
setEmail,
loading,
password,
setPassword,
setShowCredientialLogin
}: {
email: string;
emailSent: boolean;
setEmail: (email: string) => void;
password: string,
setPassword: (password: string) => void;
loading: boolean;
setShowCredientialLogin : (show : boolean) => void

}) => {
return (
<motion.div>
<motion.div layout className="flex flex-col space-y-3">
<MotionInput
id="email"
name="email"
autoFocus
type="email"
placeholder={emailSent ? "" : "[email protected]"}
autoComplete="email"
required
value={email}
disabled={emailSent || loading}
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<MotionInput
id="password"
name="password"
autoFocus
type="password"
placeholder={emailSent ? "" : "password"}
autoComplete="password"
required
value={password}
disabled={emailSent || loading}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<MotionButton
variant="dark"
type="submit"
disabled={loading || emailSent}
icon={<FontAwesomeIcon className="mr-1 size-4" icon={faEnvelope} />}
>
Login with email
</MotionButton>
<MotionButton
variant="gray"
type="button"
className="w-full"
layout
onClick={() => setShowCredientialLogin(false)}
disabled={loading || emailSent}
>
<LucideArrowUpRight size={20} />
Login with OTP
</MotionButton>
</motion.div>
</motion.div>
);
};

const NormalLogin = ({
setShowOrgInput,
email,
Expand All @@ -396,6 +538,7 @@ const NormalLogin = ({
loading,
oauthError,
handleGoogleSignIn,
setShowCredientialLogin
}: {
setShowOrgInput: (show: boolean) => void;
email: string;
Expand All @@ -404,6 +547,7 @@ const NormalLogin = ({
loading: boolean;
oauthError: boolean;
handleGoogleSignIn: () => void;
setShowCredientialLogin : (show : boolean) => void
}) => {
const publicEnv = usePublicEnv();

Expand Down Expand Up @@ -455,7 +599,17 @@ const NormalLogin = ({
Sign up here
</Link>
</motion.p>

<MotionButton
variant="gray"
type="button"
className="w-full"
layout
onClick={() => setShowCredientialLogin(true)}
disabled={loading || emailSent}
>
<KeyRound size={18}/>
Login with Password
</MotionButton>
{(publicEnv.googleAuthAvailable || publicEnv.workosAuthAvailable) && (
<>
<div className="flex gap-4 items-center mt-4 mb-4">
Expand Down
Loading