diff --git a/frontend/public/mac.png b/frontend/public/mac.png new file mode 100644 index 00000000..6107ff2d Binary files /dev/null and b/frontend/public/mac.png differ diff --git a/frontend/public/worldoscollage.mp4 b/frontend/public/worldoscollage.mp4 new file mode 100644 index 00000000..4a43771f Binary files /dev/null and b/frontend/public/worldoscollage.mp4 differ diff --git a/frontend/src/app/favicon.ico b/frontend/src/app/favicon.ico index 718d6fea..78aaa965 100644 Binary files a/frontend/src/app/favicon.ico and b/frontend/src/app/favicon.ico differ diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index c1c5051c..8fa76021 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -4,13 +4,15 @@ import { createClient } from "@/lib/supabase/server"; import { redirect } from "next/navigation"; import { SubmitButton } from "@/components/ui/submit-button"; import { Input } from "@/components/ui/input"; -import { ChevronDown } from "lucide-react"; +import GoogleSignIn from "@/components/GoogleSignIn"; export default function Login({ searchParams, }: { - searchParams: { message: string, returnUrl?: string }; + searchParams: { message: string, returnUrl?: string, mode?: 'signin' | 'signup' }; }) { + const isSignUp = searchParams.mode === 'signup'; + const signIn = async (prevState: any, formData: FormData) => { "use server"; @@ -45,6 +47,7 @@ export default function Login({ const origin = headers().get("origin"); const email = formData.get("email") as string; const password = formData.get("password") as string; + const confirmPassword = formData.get("confirmPassword") as string; if (!email || !email.includes('@')) { return { message: "Please enter a valid email address" }; @@ -54,6 +57,10 @@ export default function Login({ return { message: "Password must be at least 6 characters" }; } + if (password !== confirmPassword) { + return { message: "Passwords do not match" }; + } + const supabase = createClient(); const { error } = await supabase.auth.signUp({ @@ -82,63 +89,40 @@ export default function Login({ }; return ( -
-
-
-

- Your ideas,
amplified -

-

- Privacy-first AI that helps you create in confidence. -

-
- +
+ {/* Left side - Login Form */} +
-
- +
+

+ {isSignUp ? "Create an account" : "Welcome back"} +

+

+ Privacy-first AI that helps you create in confidence. +

-
+
+ +
+ +
- - OR - + OR
-
+
@@ -149,54 +133,106 @@ export default function Login({ name="password" type="password" placeholder="Password" - className="w-full h-14 px-4 rounded-full border border-gray-300 dark:border-gray-700 bg-white dark:bg-background-secondary text-black dark:text-white" + className="w-full h-12 px-4 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-background-secondary text-black dark:text-white" required />
+ + {isSignUp && ( +
+ +
+ )} -
- - Sign in - - - - Create new account - +
+ {!isSignUp ? ( + <> + + Sign in + + + + Create new account + + + ) : ( + <> + + Sign up + + + + Back to sign in + + + )}
- -
- - Forgot password? - -
- + + {!isSignUp && ( +
+ + Forgot password? + +
+ )} + {searchParams?.message && ( -
+
{searchParams.message}
)} - -
- By continuing, you acknowledge our{' '} + +
+ By continuing, you agree to our{' '} - Privacy Policy - . + Terms of Service + {' '} + and{' '}Privacy Policy
- -
- +
+
+ {/* Right side - Video Background */} +
+
+ + Mac +
+
+

General AI Agent,
That do tasks for you

diff --git a/frontend/src/components/GoogleSignIn.tsx b/frontend/src/components/GoogleSignIn.tsx new file mode 100644 index 00000000..536f0e4b --- /dev/null +++ b/frontend/src/components/GoogleSignIn.tsx @@ -0,0 +1,145 @@ +'use client'; + +import { useEffect, useCallback } from 'react'; +import Script from 'next/script'; +import { createClient } from '@/lib/supabase/client'; + +// Add type declarations for Google One Tap +declare global { + interface Window { + handleGoogleSignIn?: (response: GoogleSignInResponse) => void; + google: { + accounts: { + id: { + initialize: (config: GoogleInitializeConfig) => void; + renderButton: (element: HTMLElement, options: GoogleButtonOptions) => void; + prompt: (callback?: (notification: GoogleNotification) => void) => void; + cancel: () => void; + }; + }; + }; + } +} + +// Define types for Google Sign-In +interface GoogleSignInResponse { + credential: string; + clientId?: string; + select_by?: string; +} + +interface GoogleInitializeConfig { + client_id: string | undefined; + callback: ((response: GoogleSignInResponse) => void) | undefined; + nonce?: string; + use_fedcm?: boolean; + context?: string; + itp_support?: boolean; +} + +interface GoogleButtonOptions { + type?: string; + theme?: string; + size?: string; + text?: string; + shape?: string; +} + +interface GoogleNotification { + isNotDisplayed: () => boolean; + getNotDisplayedReason: () => string; + isSkippedMoment: () => boolean; + getSkippedReason: () => string; + isDismissedMoment: () => boolean; + getDismissedReason: () => string; +} + +interface GoogleSignInProps { + returnUrl?: string; +} + +export default function GoogleSignIn({ returnUrl }: GoogleSignInProps) { + const googleClientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID; + + const handleGoogleSignIn = useCallback(async (response: GoogleSignInResponse) => { + try { + const supabase = createClient(); + const { error } = await supabase.auth.signInWithIdToken({ + provider: 'google', + token: response.credential, + }); + + if (error) throw error; + window.location.href = returnUrl || "/dashboard"; + } catch (error) { + console.error('Error signing in with Google:', error); + } + }, [returnUrl]); + + useEffect(() => { + // Assign the callback to window object so it can be called from the Google button + window.handleGoogleSignIn = handleGoogleSignIn; + + if (window.google && googleClientId) { + window.google.accounts.id.initialize({ + client_id: googleClientId, + callback: handleGoogleSignIn, + use_fedcm: true, + context: 'signin', + itp_support: true + }); + } + + return () => { + // Cleanup + delete window.handleGoogleSignIn; + if (window.google) { + window.google.accounts.id.cancel(); + } + }; + }, [googleClientId, handleGoogleSignIn]); + + if (!googleClientId) { + return ( + + ); + } + + return ( + <> + {/* Hidden div for One Tap popup */} +
+ {/* The actual sign-in button that will be shown */} +
+