diff --git a/app/auth/login/page.tsx b/app/auth/login/page.tsx new file mode 100644 index 0000000..8e11b9e --- /dev/null +++ b/app/auth/login/page.tsx @@ -0,0 +1,8 @@ +'use client'; + +import LoginForm from "@/components/auth/LoginForm"; + + +export default function LoginPage() { + return ; +} diff --git a/app/auth/page.tsx b/app/auth/page.tsx deleted file mode 100644 index 0398d04..0000000 --- a/app/auth/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import AuthPage from '../../components/auth/AuthPage'; - -export default function Auth() { - return ; -} diff --git a/app/auth/signup/page.tsx b/app/auth/signup/page.tsx new file mode 100644 index 0000000..aa7c43f --- /dev/null +++ b/app/auth/signup/page.tsx @@ -0,0 +1,8 @@ +'use client'; + +import SignupForm from "@/components/auth/SignupForm"; + + +export default function SignupPage() { + return ; +} diff --git a/app/auth/verify/page.tsx b/app/auth/verify/page.tsx new file mode 100644 index 0000000..fab0c10 --- /dev/null +++ b/app/auth/verify/page.tsx @@ -0,0 +1,29 @@ +'use client' + +import { useEffect, useState } from 'react' +import { useSearchParams, useRouter } from 'next/navigation' +import VerifyForm from '@/components/auth/VerifyForm' + +export default function VerifyPage() { + const router = useRouter() + const searchParams = useSearchParams() + const [email, setEmail] = useState(null) + + useEffect(() => { + const param = searchParams.get('email') + if (param) { + setEmail(param) + router.replace('/auth/verify') // hides ?email= from address bar + } + }, [searchParams, router]) + + if (!email) { + return ( +
+

Missing email parameter. Please go back and sign up again.

+
+ ) + } + + return +} diff --git a/app/blog/page.tsx b/app/blog/page.tsx index f879c73..cd7ab29 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -111,6 +111,8 @@ export default function BlogPage() { >
{post.title}} {activeTab === "Logout" && } {activeTab === "Receive funds" && } - {activeTab === "sign up" && } + {/* {activeTab === "sign up" && } */} {activeTab === "History" && } {activeTab === "Notification" && } {activeTab === "Help" && } diff --git a/components/auth/AuthPage.tsx b/components/auth/AuthPage.tsx index e22b10d..0b9108d 100644 --- a/components/auth/AuthPage.tsx +++ b/components/auth/AuthPage.tsx @@ -1,90 +1,90 @@ -"use client"; +// "use client"; -import React, { useState } from "react"; -import LoginForm from "./LoginForm"; -import SignupForm from "./SignupForm"; -import VerifyForm from "./VerifyForm"; -import { Card } from "@/components/ui/Card"; -import Link from "next/link"; +// import React, { useState } from "react"; +// import LoginForm from "./LoginForm"; +// import SignupForm from "./SignupForm"; +// import VerifyForm from "./VerifyForm"; +// import { Card } from "@/components/ui/Card"; +// import Link from "next/link"; -export type AuthTab = "login" | "signup" | "verify"; +// export type AuthTab = "login" | "signup" | "verify"; -export interface EncryptedWalletData { - encryptedMnemonic: string; - encryptedWallets: { - ethereum: string; - bitcoin: string; - solana: string; - starknet: string; - }; - publicAddresses: { - ethereum: string; - bitcoin: string; - solana: string; - starknet: string; - }; -} +// export interface EncryptedWalletData { +// encryptedMnemonic: string; +// encryptedWallets: { +// ethereum: string; +// bitcoin: string; +// solana: string; +// starknet: string; +// }; +// publicAddresses: { +// ethereum: string; +// bitcoin: string; +// solana: string; +// starknet: string; +// }; +// } -interface AuthPageProps { - initialTab?: AuthTab; -} +// interface AuthPageProps { +// initialTab?: AuthTab; +// } -export default function AuthPage({ initialTab = "login" }: AuthPageProps) { - const [activeTab, setActiveTab] = useState(initialTab); - const [email, setEmail] = useState(""); - const [walletData, setWalletData] = useState( - null - ); +// export default function AuthPage({ initialTab = "login" }: AuthPageProps) { +// const [activeTab, setActiveTab] = useState(initialTab); +// const [email, setEmail] = useState(""); +// const [walletData, setWalletData] = useState( +// null +// ); - // Enhanced setActiveTab to accept optional email and wallet data - const handleSetActiveTab = ( - tab: AuthTab, - emailArg?: string, - walletDataArg?: EncryptedWalletData - ) => { - setActiveTab(tab); - if (emailArg) setEmail(emailArg); - if (walletDataArg) setWalletData(walletDataArg); - }; +// // Enhanced setActiveTab to accept optional email and wallet data +// const handleSetActiveTab = ( +// tab: AuthTab, +// emailArg?: string, +// walletDataArg?: EncryptedWalletData +// ) => { +// setActiveTab(tab); +// if (emailArg) setEmail(emailArg); +// if (walletDataArg) setWalletData(walletDataArg); +// }; - return ( -
- {/* Left side - Branding */} -
-
+// return ( +//
+// {/* Left side - Branding */} +//
+//
-

- Welcome to - VELO - -

-

- The fastest way to manage your crypto payments and splits -

-
-
+//

+// Welcome to +// VELO +// +//

+//

+// The fastest way to manage your crypto payments and splits +//

+//
+//
- {/* Right side - Auth forms */} -
- - {activeTab === "login" && ( - - )} - {activeTab === "signup" && ( - - )} - {activeTab === "verify" && ( - - )} - -
-
- ); -} +// {/* Right side - Auth forms */} +//
+// +// {activeTab === "login" && ( +// +// )} +// {activeTab === "signup" && ( +// +// )} +// {activeTab === "verify" && ( +// +// )} +// +//
+//
+// ); +// } diff --git a/components/auth/LoginForm.tsx b/components/auth/LoginForm.tsx index 6065b05..74c904c 100644 --- a/components/auth/LoginForm.tsx +++ b/components/auth/LoginForm.tsx @@ -1,187 +1,171 @@ -// components/auth/LoginForm.tsx -'use client'; +'use client' -import React, { useState } from 'react'; -import { Eye, EyeOff, Mail, Lock } from 'lucide-react'; -import { AuthTab } from './AuthPage'; -import { useAuth } from '@/components/context/AuthContext'; -import { useRouter } from 'next/navigation'; +import React, { useState } from 'react' +import Link from 'next/link' +import { useRouter } from 'next/navigation' +import { Mail, Lock, Eye, EyeOff, ArrowLeft } from 'lucide-react' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Button } from '@/components/ui/buttons' +import { useAuth } from '@/components/context/AuthContext' -interface LoginFormProps { - setActiveTab: (tab: AuthTab) => void; -} - -export default function LoginForm({ setActiveTab }: LoginFormProps) { - const router = useRouter(); - const [showPassword, setShowPassword] = useState(false); - const [formData, setFormData] = useState({ - email: '', - password: '', - }); - const [apiMessage, setApiMessage] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const { login } = useAuth(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setApiMessage(null); - setIsLoading(true); +export default function LoginForm() { + const router = useRouter() + const { login } = useAuth() - console.log('Starting login with:', { - email: formData.email, - password: '***', - }); + const [showPassword, setShowPassword] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [apiMessage, setApiMessage] = useState(null) + const [formData, setFormData] = useState({ + email: '', + password: '', + }) - try { - const success = await login(formData.email, formData.password); - console.log('Login result:', success); + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setApiMessage(null) + setIsLoading(true) - if (success) { - console.log('Login successful, redirecting to dashboard...'); - router.push('/dashboard'); - } else { - console.log('Login failed - success was false'); - setApiMessage('Login failed. Please check your credentials.'); - } - } catch (err) { - console.error('Login error caught:', err); - const errorMessage = - err instanceof Error ? err.message : String(err); - console.log('Setting error message:', errorMessage); - setApiMessage(errorMessage); - } finally { - setIsLoading(false); - } - }; + try { + const success = await login(formData.email, formData.password) - return ( -
-

- Login to your account -

+ if (success) { + router.push('/dashboard') + } else { + setApiMessage('Login failed. Please check your credentials.') + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err) + setApiMessage(message) + } finally { + setIsLoading(false) + } + } -
- {apiMessage && ( -
- {apiMessage} -
- )} + return ( +
+
+ {/* Back button */} + + + Back to home + -
-
- -
- - - setFormData({ - ...formData, - email: e.target.value, - }) - } - placeholder="Enter your email" - className="w-full pl-10 pr-4 py-3 bg-background border border-border rounded-[7px] outline-none focus:border-[#2F80ED] text-foreground" - required - /> -
-
- -
- -
- - - setFormData({ - ...formData, - password: e.target.value, - }) - } - placeholder="Enter your password" - className="w-full pl-10 pr-12 py-3 bg-background border border-border rounded-[7px] outline-none focus:border-[#2F80ED] text-foreground" - required - /> - -
-
-
+ {/* Header */} +
+

+ Welcome to VELO +

+

+ The fastest way to manage your crypto payments and splits +

+
-
-
- - -
+ {/* Login form */} + + {apiMessage && ( +
{apiMessage}
+ )} - -
+
+ {/* Email */} +
+ +
+ + + setFormData({ ...formData, email: e.target.value }) + } + required + className="pl-10 bg-background/50" + /> +
+
+ {/* Password */} +
+
+ + + Forgot password? + +
+
+ + + setFormData({ ...formData, password: e.target.value }) + } + required + className="pl-10 pr-10 bg-background/50" + /> +
+
-
-

- Don't have an account?{' '} - -

-
- -
- ); + {/* Remember me */} +
+ + +
+
+ + + +

+ Don’t have an account?{' '} + + Sign up + +

+ +
+
+ ) } diff --git a/components/auth/SignupForm.tsx b/components/auth/SignupForm.tsx index 11dcfa7..13aef8d 100644 --- a/components/auth/SignupForm.tsx +++ b/components/auth/SignupForm.tsx @@ -1,175 +1,189 @@ -// components/auth/SignupForm.tsx -'use client'; - -import React, { useState } from 'react'; -import { Eye, EyeOff, Mail, Lock } from 'lucide-react'; -import { AuthTab } from './AuthPage'; -// import { EncryptedWalletData } from '@/components/lib/utils/walletGenerator'; -import { useAuth } from '@/components/context/AuthContext'; - -interface SignupFormProps { - setActiveTab: ( - tab: AuthTab, - email?: string, - // walletData?: EncryptedWalletData - ) => void; -} +'use client' + +import React, { useState } from 'react' +import Link from 'next/link' +import { Mail, Lock, Eye, EyeOff, ArrowLeft } from 'lucide-react' +import { useAuth } from '@/components/context/AuthContext' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Button } from '@/components/ui/buttons' +import { useRouter } from 'next/navigation' + +export default function SignupForm() { + const router = useRouter() + const { register } = useAuth() + + const [showPassword, setShowPassword] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [apiMessage, setApiMessage] = useState(null) + const [formData, setFormData] = useState({ + firstName: '', + lastName: '', + email: '', + password: '', + agreeToTerms: false, + }) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setApiMessage(null) + + if (!formData.agreeToTerms) { + setApiMessage('You must agree to the terms and conditions') + return + } + + setIsLoading(true) + + try { + const result = await register(formData.email, formData.password) + + if (result?.success) { + // Navigate to verification step or dashboard depending on your flow + router.push('/verify?email=' + encodeURIComponent(formData.email)) + } else { + setApiMessage('Registration failed. Please try again.') + } + } catch (error) { + const message = error instanceof Error ? error.message : 'Something went wrong.' + setApiMessage(message) + } finally { + setIsLoading(false) + } + } + + return ( +
+
+ {/* Back button */} + + + Back to home + -export default function SignupForm({ setActiveTab }: SignupFormProps) { - const [showPassword, setShowPassword] = useState(false); - const [formData, setFormData] = useState({ - firstName: '', - lastName: '', - email: '', - password: '', - agreeToTerms: false, - }); - const [isGenerating, setIsGenerating] = useState(false); - const { register } = useAuth(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (!formData.agreeToTerms) { - alert('You must agree to the terms and conditions'); - return; - } - - setIsGenerating(true); - try { - const result = await register(formData.email, formData.password); - if (result.success) { - setActiveTab('verify', formData.email); - } - } catch (error) { - console.error('Registration error:', error); - alert(error instanceof Error ? error.message : 'Registration failed. Please try again.'); - } finally { - setIsGenerating(false); - } - }; - - return ( -
-

- Create an account -

- -
-
- -
- - - setFormData({ - ...formData, - email: e.target.value, - }) - } - placeholder="Enter your email" - className="w-full pl-10 pr-4 py-3 bg-background border border-border rounded-[7px] outline-none focus:border-[#2F80ED] text-foreground" - required - /> -
-
- -
- -
- - - setFormData({ - ...formData, - password: e.target.value, - }) - } - placeholder="Create a password" - className="w-full pl-10 pr-12 py-3 bg-background border border-border rounded-[7px] outline-none focus:border-[#2F80ED] text-foreground" - required - /> - -
-
- -
- - setFormData({ - ...formData, - agreeToTerms: e.target.checked, - }) - } - className="w-4 h-4 text-[#2F80ED] bg-background border-border rounded focus:ring-[#2F80ED]" - required - /> - -
+ {/* Header */} +
+

+ Create your VELO account +

+

+ Start accepting crypto payments in minutes +

+
+ + {/* Signup form */} + + {apiMessage && ( +
{apiMessage}
+ )} + +
+ + {/* Email */} +
+ +
+ + + setFormData({ ...formData, email: e.target.value }) + } + required + className="pl-10 pr-10 bg-background/50" + /> +
+
+ + {/* Password */} +
+ +
+ + + setFormData({ ...formData, password: e.target.value }) + } + required + className="pl-10 pr-10 bg-background/50" + /> +
+
-
-

- Already have an account?{' '} - -

-
- -
- ); -} \ No newline at end of file + {/* Terms */} +
+ + setFormData({ ...formData, agreeToTerms: e.target.checked }) + } + className="rounded border-border mt-1" + /> + +
+
+ + + +

+ Already have an account?{' '} + + Log in + +

+ +
+
+ ) +} diff --git a/components/auth/VerifyForm.tsx b/components/auth/VerifyForm.tsx index 120a877..6dbb428 100644 --- a/components/auth/VerifyForm.tsx +++ b/components/auth/VerifyForm.tsx @@ -1,135 +1,152 @@ -// components/auth/VerifyForm.tsx -'use client'; - -import React, { useState } from 'react'; -import { ArrowLeft } from 'lucide-react'; -import { AuthTab, EncryptedWalletData } from './AuthPage'; -import { useAuth } from '@/components/context/AuthContext'; - -interface VerifyFormProps { - setActiveTab: (tab: AuthTab, email?: string) => void; - email: string; - walletData?: EncryptedWalletData | null; +'use client' + +import React, { useState } from 'react' +import Link from 'next/link' +import { useRouter} from 'next/navigation' +import { useAuth } from '@/components/context/AuthContext' +import { Input } from '@/components/ui/input' +import { Button } from '@/components/ui/buttons' +import { ArrowLeft } from 'lucide-react' + +type VerifyFormProps = { + email: string } -export default function VerifyForm({ - setActiveTab, - email, -}: VerifyFormProps) { - const [apiMessage, setApiMessage] = useState(null); - const [otp, setOtp] = useState(['', '', '', '', '', '']); - const { verifyOtp, resendOtp } = useAuth(); - - const handleChange = (element: HTMLInputElement, index: number) => { - if (isNaN(Number(element.value))) return false; - - setOtp([...otp.map((d, idx) => (idx === index ? element.value : d))]); - - // Focus next input - if (element.nextSibling && element.value !== '') { - (element.nextSibling as HTMLInputElement).focus(); - } - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setApiMessage(null); - - const verificationCode = otp.join(''); - try { - const result = await verifyOtp(email, verificationCode); - if (result.success) { - setApiMessage(result.message || 'Verification successful!'); - // Redirect to login after successful verification - setTimeout(() => { - setActiveTab('login', email); - }, 1000); - } - } catch (err) { - setApiMessage(err instanceof Error ? err.message : 'Verification failed.'); - } - }; - - const [resendLoading, setResendLoading] = useState(false); - const handleResend = async () => { - setApiMessage(null); - setResendLoading(true); - try { - const result = await resendOtp(email); - if (result.success) { - setApiMessage(result.message || 'OTP resent successfully!'); - } - } catch (err) { - setApiMessage(err instanceof Error ? err.message : 'Failed to resend OTP.'); - } - setResendLoading(false); - }; +export default function VerifyForm({ email }: VerifyFormProps) { + const router = useRouter() + const { verifyOtp, resendOtp } = useAuth() + + const [otp, setOtp] = useState(['', '', '', '', '', '']) + const [apiMessage, setApiMessage] = useState(null) + const [isLoading, setIsLoading] = useState(false) + const [resending, setResending] = useState(false) + if (!email) { return ( -
+
+
+

Missing email address.

+ + Go back to Signup + +
+
+ ) + } + + const handleChange = (index: number, value: string) => { + if (!/^[0-9]?$/.test(value)) return + const newOtp = [...otp] + newOtp[index] = value + setOtp(newOtp) + + // Auto-focus next input + if (value && index < 5) { + const nextInput = document.getElementById(`otp-${index + 1}`) + nextInput?.focus() + } + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setApiMessage(null) + setIsLoading(true) + + try { + const code = otp.join('') + const result = await verifyOtp(email, code) + + if (result?.success) { + setApiMessage('✅ Email verified successfully!') + setTimeout(() => { + router.push('/dashboard') + }, 1500) + } else { + setApiMessage(result?.message || 'Verification failed. Try again.') + } + } catch (err) { + setApiMessage(err instanceof Error ? err.message : 'Verification failed.') + } finally { + setIsLoading(false) + } + } + + const handleResend = async () => { + setApiMessage(null) + setResending(true) + try { + const result = await resendOtp(email) + setApiMessage(result?.message || 'A new code has been sent to your email.') + } catch { + setApiMessage('Failed to resend verification code.') + } + setResending(false) + } + + return ( +
+
+ {/* Back button */} + + + Back to signup + + + {/* Header */} +
+

Verify your email

+

+ Enter the 6-digit code sent to {email} +

+
+ + {/* Verify Form */} +
+ {apiMessage && ( +
{apiMessage}
+ )} + +
+ {otp.map((digit, index) => ( + handleChange(index, e.target.value)} + className="w-12 h-12 text-center text-lg font-semibold tracking-widest border border-border/50" + /> + ))} +
+ + + +

+ Didn’t receive the code?{' '} - -

- Verify your email -

-

- We've sent a 6-digit code to your email. Enter it below to - continue. -

- - - {apiMessage && ( -
- {apiMessage} -
- )} -
- {otp.map((data, index) => ( - - handleChange( - e.target as HTMLInputElement, - index - ) - } - onFocus={(e) => e.target.select()} - className="w-12 h-12 text-center bg-background border border-border rounded-[7px] outline-none focus:border-[#2F80ED] text-foreground text-custom-xl" - /> - ))} -
- - - -
-

- Didn't receive the code?{' '} - -

-
-
-
- ); -} \ No newline at end of file +

+ +
+
+ ) +} diff --git a/components/dashboard/side-nav.tsx b/components/dashboard/side-nav.tsx index f1668d2..6cc172c 100644 --- a/components/dashboard/side-nav.tsx +++ b/components/dashboard/side-nav.tsx @@ -80,7 +80,7 @@ export function SideNav({ activeTab, setTab }: SideNavProps) { "w-full justify-start gap-3 h-11 text-sm font-medium transition-all duration-200", activeTab === item.name ? "bg-primary text-primary-foreground hover:bg-primary/90 shadow-lg shadow-primary/25" - : "text-sidebar-foreground hover:bg-sidebar-accent/80 hover:text-sidebar-accent-foreground", + : "text-sidebar-foreground hover:bg-velo-gradient/50 hover:text-sidebar-accent-foreground", )} onClick={() => { setTab(item.name) diff --git a/components/landingpage/landing/navigation.tsx b/components/landingpage/landing/navigation.tsx index 3b25493..34cc02b 100644 --- a/components/landingpage/landing/navigation.tsx +++ b/components/landingpage/landing/navigation.tsx @@ -185,7 +185,7 @@ export function Navigation() { {/* Pricing Link */} - + @@ -287,12 +287,12 @@ export function Navigation() {
- + - +
diff --git a/next.config.ts b/next.config.ts index e9ffa30..112ebaa 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,14 @@ -import type { NextConfig } from "next"; +/** @type {import('next').NextConfig} */ +const nextConfig = { + async redirects() { + return [ + { + source: '/verify', + destination: '/auth/verify', + permanent: false, + }, + ] + }, +} -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; +module.exports = nextConfig