feat: improve auth

This commit is contained in:
2026-04-26 09:23:35 -05:00
parent a49e0d8ff0
commit 3f955e63c3
13 changed files with 128 additions and 119 deletions
@@ -1 +0,0 @@
{"type":"server-started","port":49813,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:49813","screen_dir":"/Users/adrianbonpin/Documents/Code/personal/deckyvault/.superpowers/brainstorm/44094-1777186296/content","state_dir":"/Users/adrianbonpin/Documents/Code/personal/deckyvault/.superpowers/brainstorm/44094-1777186296/state"}
@@ -0,0 +1 @@
{"reason":"idle timeout","timestamp":1777190496647}
@@ -13,3 +13,4 @@
{"source":"user-event","type":"click","text":"A\n Featured Row + Asymmetric Grid\n Top chart gets a larger container with summary stats beside it. Remaining charts in a flowing grid.\n\n \n \n \n \n 📈 Historical AVG FPS FEATURED\n [Stacked Area Chart — full width of container]\n \n \n \n Overall Avg\n 54 FPS\n \n \n Best Device\n OLED\n \n \n Entries\n 42\n \n \n \n\n \n \n 🎮 FPS by Upscaler & Frame Gen FULL\n [Grouped Bar — FSR version × Frame Gen method]\n \n\n \n \n \n 📊 Boxplot\n [FPS distribution per device]\n \n \n 📉 FPS Range\n [Low → High per entry]\n \n \n 🖥️ Devices\n [Donut: entries per device]\n \n \n\n \n \n ✅ Community Trust FULL\n [Horizontal bar: verified + vote ratio per entry]","choice":"a","id":null,"timestamp":1777187409898} {"source":"user-event","type":"click","text":"A\n Featured Row + Asymmetric Grid\n Top chart gets a larger container with summary stats beside it. Remaining charts in a flowing grid.\n\n \n \n \n \n 📈 Historical AVG FPS FEATURED\n [Stacked Area Chart — full width of container]\n \n \n \n Overall Avg\n 54 FPS\n \n \n Best Device\n OLED\n \n \n Entries\n 42\n \n \n \n\n \n \n 🎮 FPS by Upscaler & Frame Gen FULL\n [Grouped Bar — FSR version × Frame Gen method]\n \n\n \n \n \n 📊 Boxplot\n [FPS distribution per device]\n \n \n 📉 FPS Range\n [Low → High per entry]\n \n \n 🖥️ Devices\n [Donut: entries per device]\n \n \n\n \n \n ✅ Community Trust FULL\n [Horizontal bar: verified + vote ratio per entry]","choice":"a","id":null,"timestamp":1777187409898}
{"type":"screen-added","file":"/Users/adrianbonpin/Documents/Code/personal/deckyvault/.superpowers/brainstorm/44094-1777186296/content/search-badge.html"} {"type":"screen-added","file":"/Users/adrianbonpin/Documents/Code/personal/deckyvault/.superpowers/brainstorm/44094-1777186296/content/search-badge.html"}
{"type":"screen-added","file":"/Users/adrianbonpin/Documents/Code/personal/deckyvault/.superpowers/brainstorm/44094-1777186296/content/search-card-final.html"} {"type":"screen-added","file":"/Users/adrianbonpin/Documents/Code/personal/deckyvault/.superpowers/brainstorm/44094-1777186296/content/search-card-final.html"}
{"type":"server-stopped","reason":"idle timeout"}
+13 -8
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next" import type { Metadata } from "next"
import { Lock } from "lucide-react" import Image from "next/image"
import Link from "next/link" import Link from "next/link"
import logo from "@/app/icon.png"
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Authentication", title: "Authentication",
@@ -12,20 +13,24 @@ export default function AuthLayout({
children: React.ReactNode children: React.ReactNode
}) { }) {
return ( return (
<div className="flex-1 flex items-center justify-center px-4 py-8 relative overflow-hidden"> <div className="min-h-[calc(100vh-3.6rem)] flex items-center justify-center px-4 relative overflow-hidden">
{/* Background decorative orbs */} {/* Background decorative orbs */}
<div className="absolute top-10 left-10 w-32 h-32 rounded-full bg-[#eb3779]/10 blur-3xl pointer-events-none" /> <div className="absolute top-20 left-10 w-48 h-48 rounded-full bg-primary/10 blur-3xl pointer-events-none" />
<div className="absolute bottom-16 right-16 w-24 h-24 rounded-full bg-[#571b8b]/15 blur-2xl pointer-events-none" /> <div className="absolute bottom-20 right-10 w-36 h-36 rounded-full bg-secondary/15 blur-2xl pointer-events-none" />
{/* Auth card */} {/* Auth card */}
<div className="w-full max-w-[400px] bg-white/[0.03] border border-white/[0.08] rounded-xl p-7 backdrop-blur-sm relative z-10"> <div className="w-full max-w-md bg-text/5 border border-border rounded-xl p-8 relative z-10">
{/* Logo */} {/* Logo */}
<Link <Link
href="/" href="/"
className="flex items-center justify-center gap-2 mb-6" className="flex flex-col items-center gap-2 mb-8"
> >
<Lock className="h-5 w-5 text-[#eb3779]" /> <Image
<span className="text-lg font-bold text-[#ebe4f1]"> src={logo}
alt="DeckyVault Logo"
className="h-10 w-auto"
/>
<span className="text-lg font-bold text-text">
DeckyVault DeckyVault
</span> </span>
</Link> </Link>
+11 -11
View File
@@ -1,7 +1,7 @@
"use client" "use client"
import { useState } from "react" import { useState } from "react"
import { Lock, Loader2, ArrowLeft } from "lucide-react" import { KeyRound, Loader2, ArrowLeft } from "lucide-react"
import { authClient } from "@/lib/auth-client" import { authClient } from "@/lib/auth-client"
import { import {
forgotPasswordSchema, forgotPasswordSchema,
@@ -43,26 +43,26 @@ export default function ForgotPasswordForm() {
} }
return ( return (
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-5">
<div className="text-center mb-2"> <div className="text-center mb-2">
<Lock className="h-10 w-10 text-[#eb3779] mx-auto mb-3" /> <KeyRound className="h-10 w-10 text-primary mx-auto mb-3" />
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Forgot your password? Forgot your password?
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50 mt-1 leading-relaxed"> <p className="text-sm text-text/50 mt-2 leading-relaxed">
Enter your email and we&apos;ll send you a verification code to Enter your email and we&apos;ll send you a verification code to
reset your password. reset your password.
</p> </p>
</div> </div>
{error && ( {error && (
<div className="text-red-400 text-xs text-center bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2"> <div className="text-red-400 text-sm text-center bg-red-500/10 border border-red-500/20 rounded-lg px-4 py-3">
{error} {error}
</div> </div>
)} )}
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Email Email
</label> </label>
<input <input
@@ -70,14 +70,14 @@ export default function ForgotPasswordForm() {
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com" placeholder="you@example.com"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
</div> </div>
<button <button
type="submit" type="submit"
disabled={isLoading} disabled={isLoading}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading && <Loader2 className="h-4 w-4 animate-spin" />} {isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
Send verification code Send verification code
@@ -85,9 +85,9 @@ export default function ForgotPasswordForm() {
<Link <Link
href="/login" href="/login"
className="flex items-center justify-center gap-1.5 text-xs text-[#ebe4f1]/50 hover:text-[#ebe4f1] transition-colors" className="flex items-center justify-center gap-2 text-sm text-text/50 hover:text-text transition-colors"
> >
<ArrowLeft className="h-3 w-3" /> <ArrowLeft className="h-4 w-4" />
Back to sign in Back to sign in
</Link> </Link>
</form> </form>
+3 -3
View File
@@ -107,16 +107,16 @@ export default function OtpInput({
onPaste={handlePaste} onPaste={handlePaste}
disabled={disabled} disabled={disabled}
aria-label={`Digit ${i + 1} of ${length}`} aria-label={`Digit ${i + 1} of ${length}`}
className={`w-11 h-[52px] text-center text-xl font-bold rounded-lg border bg-white/[0.03] text-[#ebe4f1] outline-none transition-colors ${ className={`w-12 h-14 text-center text-xl font-bold rounded-lg border bg-text/5 text-text outline-none transition-colors ${
error error
? "border-red-500 focus:border-red-500 focus:ring-2 focus:ring-red-500/50" ? "border-red-500 focus:border-red-500 focus:ring-2 focus:ring-red-500/50"
: "border-white/10 focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50" : "border-border focus:border-primary focus:ring-2 focus:ring-primary/50"
} ${disabled ? "opacity-50 cursor-not-allowed" : ""}`} } ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
/> />
))} ))}
</div> </div>
{error && ( {error && (
<p className="text-red-400 text-xs mt-2 text-center"> <p className="text-red-400 text-sm mt-2 text-center">
{error} {error}
</p> </p>
)} )}
+13 -13
View File
@@ -75,20 +75,20 @@ export default function OtpVerificationStep({
} }
return ( return (
<div className="space-y-4"> <div className="space-y-5">
<div className="text-center mb-2"> <div className="text-center mb-2">
<Mail className="h-10 w-10 text-[#eb3779] mx-auto mb-3" /> <Mail className="h-10 w-10 text-primary mx-auto mb-3" />
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Verify your email Verify your email
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50 mt-1"> <p className="text-sm text-text/50 mt-2">
We sent a 6-digit code to{" "} We sent a 6-digit code to{" "}
<strong className="text-[#ebe4f1]">{email}</strong> <strong className="text-text">{email}</strong>
</p> </p>
</div> </div>
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-2 text-center"> <label className="text-sm text-text/60 block mb-2 text-center">
Verification code Verification code
</label> </label>
<OtpInput <OtpInput
@@ -104,18 +104,18 @@ export default function OtpVerificationStep({
/> />
</div> </div>
<div className="text-center text-xs"> <div className="text-center text-sm">
{canResend ? ( {canResend ? (
<button <button
onClick={handleResend} onClick={handleResend}
className="text-[#eb3779] hover:underline" className="text-primary hover:underline"
> >
Resend code Resend code
</button> </button>
) : ( ) : (
<span className="text-[#ebe4f1]/40"> <span className="text-text/40">
Resend code in{" "} Resend code in{" "}
<span className="text-[#eb3779] font-semibold"> <span className="text-primary font-semibold">
{formatTime(resendTimer)} {formatTime(resendTimer)}
</span> </span>
</span> </span>
@@ -125,7 +125,7 @@ export default function OtpVerificationStep({
<button <button
onClick={() => handleVerify(otp)} onClick={() => handleVerify(otp)}
disabled={isLoading || otp.length !== 6} disabled={isLoading || otp.length !== 6}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading && <Loader2 className="h-4 w-4 animate-spin" />} {isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
Verify Verify
@@ -133,9 +133,9 @@ export default function OtpVerificationStep({
<button <button
onClick={onBack} onClick={onBack}
className="flex items-center justify-center gap-1.5 w-full text-xs text-[#ebe4f1]/50 hover:text-[#ebe4f1] transition-colors" className="flex items-center justify-center gap-2 w-full text-sm text-text/50 hover:text-text transition-colors"
> >
<ArrowLeft className="h-3 w-3" /> <ArrowLeft className="h-4 w-4" />
Back to signup Back to signup
</button> </button>
</div> </div>
+16 -16
View File
@@ -42,36 +42,36 @@ export default function PasskeySetupStep({
} }
return ( return (
<div className="space-y-4"> <div className="space-y-5">
<div className="text-center mb-2"> <div className="text-center mb-2">
<KeyRound className="h-10 w-10 text-[#eb3779] mx-auto mb-3" /> <KeyRound className="h-10 w-10 text-primary mx-auto mb-3" />
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Set up a passkey Set up a passkey
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50 mt-1 leading-relaxed"> <p className="text-sm text-text/50 mt-2 leading-relaxed">
Sign in faster with biometrics or your device&apos;s security Sign in faster with biometrics or your device&apos;s security
key. No password needed. key. No password needed.
</p> </p>
</div> </div>
{/* Benefits list */} {/* Benefits list */}
<div className="bg-white/[0.02] rounded-lg p-3.5 space-y-2"> <div className="bg-text/5 rounded-lg p-4 space-y-3">
<div className="flex items-center gap-2 text-xs text-[#ebe4f1]/60"> <div className="flex items-center gap-3 text-sm text-text/60">
<Check className="h-3.5 w-3.5 text-[#22c55e] shrink-0" /> <Check className="h-4 w-4 text-green-400 shrink-0" />
Faster sign-in with fingerprint or face Faster sign-in with fingerprint or face
</div> </div>
<div className="flex items-center gap-2 text-xs text-[#ebe4f1]/60"> <div className="flex items-center gap-3 text-sm text-text/60">
<Check className="h-3.5 w-3.5 text-[#22c55e] shrink-0" /> <Check className="h-4 w-4 text-green-400 shrink-0" />
More secure than passwords More secure than passwords
</div> </div>
<div className="flex items-center gap-2 text-xs text-[#ebe4f1]/60"> <div className="flex items-center gap-3 text-sm text-text/60">
<Check className="h-3.5 w-3.5 text-[#22c55e] shrink-0" /> <Check className="h-4 w-4 text-green-400 shrink-0" />
Works across your devices Works across your devices
</div> </div>
</div> </div>
{error && ( {error && (
<div className="text-red-400 text-xs text-center bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2"> <div className="text-red-400 text-sm text-center bg-red-500/10 border border-red-500/20 rounded-lg px-4 py-3">
{error} {error}
</div> </div>
)} )}
@@ -81,7 +81,7 @@ export default function PasskeySetupStep({
<button <button
onClick={handleAddPasskey} onClick={handleAddPasskey}
disabled={isLoading} disabled={isLoading}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading ? ( {isLoading ? (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
@@ -92,19 +92,19 @@ export default function PasskeySetupStep({
</button> </button>
<button <button
onClick={onSkip} onClick={onSkip}
className="w-full py-2.5 rounded-lg border border-white/10 text-[#ebe4f1]/60 text-sm hover:bg-white/[0.03] transition-colors" className="w-full py-3 rounded-lg border border-border text-text/60 text-sm hover:bg-text/5 transition-colors cursor-pointer"
> >
Skip for now Skip for now
</button> </button>
</> </>
) : ( ) : (
<div className="text-center"> <div className="text-center">
<p className="text-xs text-[#ebe4f1]/40 mb-3"> <p className="text-sm text-text/40 mb-4">
Passkeys are not supported on this device. Passkeys are not supported on this device.
</p> </p>
<button <button
onClick={onSkip} onClick={onSkip}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer"
> >
Continue Continue
</button> </button>
+27 -27
View File
@@ -88,18 +88,18 @@ export default function ResetPasswordForm({
if (isSuccess) { if (isSuccess) {
return ( return (
<div className="text-center space-y-4"> <div className="text-center space-y-5">
<CheckCircle2 className="h-10 w-10 text-[#22c55e] mx-auto" /> <CheckCircle2 className="h-10 w-10 text-green-400 mx-auto" />
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Password reset successful Password reset successful
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50"> <p className="text-sm text-text/50">
Your password has been updated. You can now sign in with Your password has been updated. You can now sign in with
your new password. your new password.
</p> </p>
<Link <Link
href="/login" href="/login"
className="inline-block w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors text-center" className="inline-block w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors text-center cursor-pointer"
> >
Sign in Sign in
</Link> </Link>
@@ -108,20 +108,20 @@ export default function ResetPasswordForm({
} }
return ( return (
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-5">
<div className="text-center mb-2"> <div className="text-center mb-2">
<Mail className="h-10 w-10 text-[#eb3779] mx-auto mb-3" /> <Mail className="h-10 w-10 text-primary mx-auto mb-3" />
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Check your email Check your email
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50 mt-1"> <p className="text-sm text-text/50 mt-2">
We sent a 6-digit code to{" "} We sent a 6-digit code to{" "}
<strong className="text-[#ebe4f1]">{email}</strong> <strong className="text-text">{email}</strong>
</p> </p>
</div> </div>
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-2 text-center"> <label className="text-sm text-text/60 block mb-2 text-center">
Verification code Verification code
</label> </label>
<OtpInput <OtpInput
@@ -131,19 +131,19 @@ export default function ResetPasswordForm({
/> />
</div> </div>
<div className="text-center text-xs"> <div className="text-center text-sm">
{canResend ? ( {canResend ? (
<button <button
type="button" type="button"
onClick={handleResend} onClick={handleResend}
className="text-[#eb3779] hover:underline" className="text-primary hover:underline"
> >
Resend code Resend code
</button> </button>
) : ( ) : (
<span className="text-[#ebe4f1]/40"> <span className="text-text/40">
Resend code in{" "} Resend code in{" "}
<span className="text-[#eb3779] font-semibold"> <span className="text-primary font-semibold">
{formatTime(resendTimer)} {formatTime(resendTimer)}
</span> </span>
</span> </span>
@@ -151,21 +151,21 @@ export default function ResetPasswordForm({
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex-1 h-px bg-white/[0.08]" /> <div className="flex-1 h-px bg-border" />
<span className="text-[11px] text-[#ebe4f1]/40"> <span className="text-xs text-text/40">
then set new password then set new password
</span> </span>
<div className="flex-1 h-px bg-white/[0.08]" /> <div className="flex-1 h-px bg-border" />
</div> </div>
{error && ( {error && (
<div className="text-red-400 text-xs text-center bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2"> <div className="text-red-400 text-sm text-center bg-red-500/10 border border-red-500/20 rounded-lg px-4 py-3">
{error} {error}
</div> </div>
)} )}
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
New password New password
</label> </label>
<input <input
@@ -173,13 +173,13 @@ export default function ResetPasswordForm({
value={newPassword} value={newPassword}
onChange={(e) => setNewPassword(e.target.value)} onChange={(e) => setNewPassword(e.target.value)}
placeholder="Min. 10 characters" placeholder="Min. 10 characters"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
<PasswordStrengthMeter password={newPassword} /> <PasswordStrengthMeter password={newPassword} />
</div> </div>
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Confirm new password Confirm new password
</label> </label>
<input <input
@@ -187,11 +187,11 @@ export default function ResetPasswordForm({
value={confirmPassword} value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)} onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Re-enter password" placeholder="Re-enter password"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
{confirmPassword && {confirmPassword &&
newPassword !== confirmPassword && ( newPassword !== confirmPassword && (
<p className="text-red-400 text-xs mt-1"> <p className="text-red-400 text-sm mt-1">
Passwords do not match Passwords do not match
</p> </p>
)} )}
@@ -200,7 +200,7 @@ export default function ResetPasswordForm({
<button <button
type="submit" type="submit"
disabled={isLoading || otp.length !== 6} disabled={isLoading || otp.length !== 6}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading && <Loader2 className="h-4 w-4 animate-spin" />} {isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
Reset password Reset password
@@ -208,9 +208,9 @@ export default function ResetPasswordForm({
<Link <Link
href="/login" href="/login"
className="flex items-center justify-center gap-1.5 text-xs text-[#ebe4f1]/50 hover:text-[#ebe4f1] transition-colors" className="flex items-center justify-center gap-2 text-sm text-text/50 hover:text-text transition-colors"
> >
<ArrowLeft className="h-3 w-3" /> <ArrowLeft className="h-4 w-4" />
Back to sign in Back to sign in
</Link> </Link>
</form> </form>
+20 -20
View File
@@ -57,32 +57,32 @@ export default function SignupFormStep({ onSuccess }: SignupFormStepProps) {
return ( return (
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div className="text-center mb-2"> <div className="text-center mb-2">
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Create your account Create your account
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50 mt-1"> <p className="text-sm text-text/50 mt-1">
Choose how you&apos;d like to sign up Join DeckyVault and start exploring
</p> </p>
</div> </div>
<SocialButtons callbackURL="/signup?step=passkey" /> <SocialButtons callbackURL="/signup?step=passkey" />
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex-1 h-px bg-white/[0.08]" /> <div className="flex-1 h-px bg-border" />
<span className="text-[11px] text-[#ebe4f1]/40 uppercase"> <span className="text-xs text-text/40 uppercase">
or use email or use email
</span> </span>
<div className="flex-1 h-px bg-white/[0.08]" /> <div className="flex-1 h-px bg-border" />
</div> </div>
{serverError && ( {serverError && (
<div className="text-red-400 text-xs text-center bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2"> <div className="text-red-400 text-sm text-center bg-red-500/10 border border-red-500/20 rounded-lg px-4 py-3">
{serverError} {serverError}
</div> </div>
)} )}
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Name Name
</label> </label>
<input <input
@@ -90,15 +90,15 @@ export default function SignupFormStep({ onSuccess }: SignupFormStepProps) {
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
placeholder="Your name" placeholder="Your name"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
{errors.name && ( {errors.name && (
<p className="text-red-400 text-xs mt-1">{errors.name}</p> <p className="text-red-400 text-sm mt-1">{errors.name}</p>
)} )}
</div> </div>
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Email Email
</label> </label>
<input <input
@@ -106,15 +106,15 @@ export default function SignupFormStep({ onSuccess }: SignupFormStepProps) {
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com" placeholder="you@example.com"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
{errors.email && ( {errors.email && (
<p className="text-red-400 text-xs mt-1">{errors.email}</p> <p className="text-red-400 text-sm mt-1">{errors.email}</p>
)} )}
</div> </div>
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Password Password
</label> </label>
<input <input
@@ -122,10 +122,10 @@ export default function SignupFormStep({ onSuccess }: SignupFormStepProps) {
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
placeholder="Min. 10 characters" placeholder="Min. 10 characters"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
{errors.password && ( {errors.password && (
<p className="text-red-400 text-xs mt-1"> <p className="text-red-400 text-sm mt-1">
{errors.password} {errors.password}
</p> </p>
)} )}
@@ -135,15 +135,15 @@ export default function SignupFormStep({ onSuccess }: SignupFormStepProps) {
<button <button
type="submit" type="submit"
disabled={isLoading} disabled={isLoading}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading && <Loader2 className="h-4 w-4 animate-spin" />} {isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
Continue Create account
</button> </button>
<p className="text-center text-xs text-[#ebe4f1]/50"> <p className="text-center text-sm text-text/50">
Already have an account?{" "} Already have an account?{" "}
<Link href="/login" className="text-[#eb3779] hover:underline"> <Link href="/login" className="text-primary hover:underline">
Sign in Sign in
</Link> </Link>
</p> </p>
+4 -4
View File
@@ -35,12 +35,12 @@ export default function SocialButtons({
const isLoading = loadingProvider !== null const isLoading = loadingProvider !== null
return ( return (
<div className="flex gap-2"> <div className="flex gap-3">
<button <button
type="button" type="button"
onClick={() => handleSocialLogin("google")} onClick={() => handleSocialLogin("google")}
disabled={disabled || isLoading} disabled={disabled || isLoading}
className="flex-1 flex items-center justify-center gap-2 px-4 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm font-medium hover:bg-white/[0.06] transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm font-medium hover:bg-text/10 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
> >
{loadingProvider === "google" ? ( {loadingProvider === "google" ? (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
@@ -71,13 +71,13 @@ export default function SocialButtons({
type="button" type="button"
onClick={() => handleSocialLogin("discord")} onClick={() => handleSocialLogin("discord")}
disabled={disabled || isLoading} disabled={disabled || isLoading}
className="flex-1 flex items-center justify-center gap-2 px-4 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm font-medium hover:bg-white/[0.06] transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="flex-1 flex items-center justify-center gap-2 px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm font-medium hover:bg-text/10 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
> >
{loadingProvider === "discord" ? ( {loadingProvider === "discord" ? (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
) : ( ) : (
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="#5865F2"> <svg className="h-4 w-4" viewBox="0 0 24 24" fill="#5865F2">
<path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03z" /> <path d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.09.09 0 00-.07.03c-.18.33-.39.76-.53 1.09a16.09 16.09 0 00-4.8 0c-.14-.34-.35-.76-.54-1.09-.01-.01-.04-.03-.07-.03-1.5.26-2.93.71-4.27 1.33-.01 0-.02.01-.03.02-2.72 4.07-3.47 8.03-3.1 11.95 0 .01.01.03.02.04 1.69 1.24 3.33 1.99 4.95 2.49.03.01.06 0 .07-.02.38-.52.72-1.07 1.01-1.65.02-.04 0-.08-.04-.09-.55-.2-1.08-.45-1.59-.73-.04-.02-.04-.08 0-.1.11-.08.22-.17.33-.26.02-.02.05-.02.07-.01 3.44 1.57 7.15 1.57 10.55 0 .02-.01.05-.01.07.01.11.09.22.17.33.26.04.02.04.08 0 .1-.51.28-1.04.53-1.59.73-.04.01-.05.06-.04.09.29.58.64 1.13 1.01 1.65.03.01.06.02.09.01 1.62-.5 3.27-1.25 4.96-2.49.01-.01.02-.03.02-.04.44-4.53-.73-8.46-3.1-11.95-.01-.01-.02-.02-.04-.02zM8.52 14.91c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12 0 1.17-.84 2.12-1.89 2.12zm6.97 0c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12 0 1.17-.83 2.12-1.89 2.12z" />
</svg> </svg>
)} )}
Discord Discord
+4 -4
View File
@@ -16,15 +16,15 @@ export default function StepIndicator({
{Array.from({ length: totalSteps }).map((_, i) => ( {Array.from({ length: totalSteps }).map((_, i) => (
<motion.div <motion.div
key={i} key={i}
className="h-1 w-8 rounded-full" className="h-1 w-12 rounded-full"
initial={false} initial={false}
animate={{ animate={{
backgroundColor: backgroundColor:
i < currentStep - 1 i < currentStep - 1
? "#22c55e" // completed ? "#22c55e" // completed - green
: i === currentStep - 1 : i === currentStep - 1
? "#eb3779" // current ? "#eb3779" // current - primary
: "rgba(255,255,255,0.1)", // upcoming : "rgba(235,228,241,0.1)", // upcoming - border color
}} }}
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
/> />
+15 -12
View File
@@ -7,20 +7,23 @@ export const routes = [
title: "Search", title: "Search",
href: "/search", href: "/search",
}, },
{ // TODO: Add back when games page is implemented
title: "Games", // {
href: "/games", // title: "Games",
}, // href: "/games",
{ // },
title: "Compare", // TODO: Add back when compare page is implemented
href: "/compare", // {
}, // title: "Compare",
// href: "/compare",
// },
{ {
title: "Devices", title: "Devices",
href: "/devices", href: "/devices",
}, },
{ // TODO: Add back when about page is implemented
title: "About", // {
href: "/about", // title: "About",
} // href: "/about",
// }
] ]