Skip to content

Commit a72a00b

Browse files
depak7Deepak S
authored andcommitted
"Implement password sign-in option (#1036)"
1 parent 3cfb332 commit a72a00b

File tree

7 files changed

+422
-22
lines changed

7 files changed

+422
-22
lines changed

apps/web/app/(org)/login/form.tsx

Lines changed: 165 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
1111
import { AnimatePresence, motion } from "framer-motion";
1212
import Cookies from "js-cookie";
13-
import { LucideArrowUpRight } from "lucide-react";
13+
import { KeyRound, LucideArrowUpRight } from "lucide-react";
1414
import Image from "next/image";
1515
import Link from "next/link";
1616
import { useRouter, useSearchParams } from "next/navigation";
@@ -40,6 +40,8 @@ export function LoginForm() {
4040
const [lastEmailSentTime, setLastEmailSentTime] = useState<number | null>(
4141
null,
4242
);
43+
const [password, setPassword] = useState("");
44+
const [showCredientialLogin,setShowCredientialLogin]=useState(false);
4345
const theme = Cookies.get("theme") || "light";
4446

4547
useEffect(() => {
@@ -248,6 +250,60 @@ export function LoginForm() {
248250
e.preventDefault();
249251
if (!email) return;
250252

253+
if (showCredientialLogin) {
254+
if (!email || !password) {
255+
toast.error("Please enter email and password");
256+
return;
257+
}
258+
259+
try {
260+
setLoading(true);
261+
trackEvent("auth_started", { method: "password", is_signup: false });
262+
263+
const res = await signIn("credentials", {
264+
email,
265+
password,
266+
redirect: false,
267+
...(next && next.length > 0 ? { callbackUrl: next } : {}),
268+
});
269+
270+
setLoading(false);
271+
272+
if (res?.ok && !res?.error) {
273+
trackEvent("auth_success", { method: "password", is_signup: false });
274+
router.push(next || "/");
275+
return;
276+
}
277+
278+
279+
// Handle specific known errors first
280+
if (res?.error?.toLowerCase().includes("verify")) {
281+
toast.error("Please verify your email before logging in.");
282+
const res = await fetch("/api/signup/resend", {
283+
method: "POST",
284+
headers: { "Content-Type": "application/json" },
285+
body: JSON.stringify({ email }),
286+
});
287+
288+
const data = await res.json();
289+
if (!data?.status) {
290+
throw "Something went wrong,try other login methods";
291+
}
292+
router.push(`/verify-otp?email=${encodeURIComponent(email)}&type=credientials`);
293+
return;
294+
}
295+
296+
// Otherwise generic error
297+
toast.error("Invalid credentials – try again?");
298+
} catch (error) {
299+
console.error("Credential login error:", error);
300+
toast.error("Invalid credentials – try again?");
301+
} finally {
302+
setLoading(false);
303+
}
304+
return;
305+
}
306+
251307
// Check if we're rate limited on the client side
252308
if (lastEmailSentTime) {
253309
const timeSinceLastRequest =
@@ -309,15 +365,28 @@ export function LoginForm() {
309365
}}
310366
className="flex flex-col space-y-3"
311367
>
312-
<NormalLogin
313-
setShowOrgInput={setShowOrgInput}
314-
email={email}
315-
emailSent={emailSent}
316-
setEmail={setEmail}
317-
loading={loading}
318-
oauthError={oauthError}
319-
handleGoogleSignIn={handleGoogleSignIn}
320-
/>
368+
{showCredientialLogin ? (
369+
<LoginWithEmailAndPassword
370+
email={email}
371+
emailSent={emailSent}
372+
setEmail={setEmail}
373+
password={password}
374+
setPassword={setPassword}
375+
loading={loading}
376+
setShowCredientialLogin={setShowCredientialLogin}
377+
/>
378+
) : (
379+
<NormalLogin
380+
setShowOrgInput={setShowOrgInput}
381+
email={email}
382+
emailSent={emailSent}
383+
setEmail={setEmail}
384+
loading={loading}
385+
oauthError={oauthError}
386+
handleGoogleSignIn={handleGoogleSignIn}
387+
setShowCredientialLogin={setShowCredientialLogin}
388+
/>
389+
)}
321390
</motion.form>
322391
)}
323392
</motion.div>
@@ -388,6 +457,79 @@ const LoginWithSSO = ({
388457
);
389458
};
390459

460+
const LoginWithEmailAndPassword = ({
461+
email,
462+
emailSent,
463+
setEmail,
464+
loading,
465+
password,
466+
setPassword,
467+
setShowCredientialLogin
468+
}: {
469+
email: string;
470+
emailSent: boolean;
471+
setEmail: (email: string) => void;
472+
password: string,
473+
setPassword: (password: string) => void;
474+
loading: boolean;
475+
setShowCredientialLogin : (show : boolean) => void
476+
477+
}) => {
478+
return (
479+
<motion.div>
480+
<motion.div layout className="flex flex-col space-y-3">
481+
<MotionInput
482+
id="email"
483+
name="email"
484+
autoFocus
485+
type="email"
486+
placeholder={emailSent ? "" : "tim@apple.com"}
487+
autoComplete="email"
488+
required
489+
value={email}
490+
disabled={emailSent || loading}
491+
onChange={(e) => {
492+
setEmail(e.target.value);
493+
}}
494+
/>
495+
<MotionInput
496+
id="password"
497+
name="password"
498+
autoFocus
499+
type="password"
500+
placeholder={emailSent ? "" : "password"}
501+
autoComplete="password"
502+
required
503+
value={password}
504+
disabled={emailSent || loading}
505+
onChange={(e) => {
506+
setPassword(e.target.value);
507+
}}
508+
/>
509+
<MotionButton
510+
variant="dark"
511+
type="submit"
512+
disabled={loading || emailSent}
513+
icon={<FontAwesomeIcon className="mr-1 size-4" icon={faEnvelope} />}
514+
>
515+
Login with email
516+
</MotionButton>
517+
<MotionButton
518+
variant="gray"
519+
type="button"
520+
className="w-full"
521+
layout
522+
onClick={() => setShowCredientialLogin(false)}
523+
disabled={loading || emailSent}
524+
>
525+
<LucideArrowUpRight size={20} />
526+
Login with OTP
527+
</MotionButton>
528+
</motion.div>
529+
</motion.div>
530+
);
531+
};
532+
391533
const NormalLogin = ({
392534
setShowOrgInput,
393535
email,
@@ -396,6 +538,7 @@ const NormalLogin = ({
396538
loading,
397539
oauthError,
398540
handleGoogleSignIn,
541+
setShowCredientialLogin
399542
}: {
400543
setShowOrgInput: (show: boolean) => void;
401544
email: string;
@@ -404,6 +547,7 @@ const NormalLogin = ({
404547
loading: boolean;
405548
oauthError: boolean;
406549
handleGoogleSignIn: () => void;
550+
setShowCredientialLogin : (show : boolean) => void
407551
}) => {
408552
const publicEnv = usePublicEnv();
409553

@@ -455,7 +599,17 @@ const NormalLogin = ({
455599
Sign up here
456600
</Link>
457601
</motion.p>
458-
602+
<MotionButton
603+
variant="gray"
604+
type="button"
605+
className="w-full"
606+
layout
607+
onClick={() => setShowCredientialLogin(true)}
608+
disabled={loading || emailSent}
609+
>
610+
<KeyRound size={18}/>
611+
Login with Password
612+
</MotionButton>
459613
{(publicEnv.googleAuthAvailable || publicEnv.workosAuthAvailable) && (
460614
<>
461615
<div className="flex gap-4 items-center mt-4 mb-4">

0 commit comments

Comments
 (0)