diff --git a/src/App.tsx b/src/App.tsx
index 18daf2e..60ccefd 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,26 +1,61 @@
+
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { BrowserRouter, Routes, Route } from "react-router-dom";
+import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
+import { AuthProvider, useAuth } from "@/contexts/AuthContext";
import Index from "./pages/Index";
+import Auth from "./pages/Auth";
import NotFound from "./pages/NotFound";
const queryClient = new QueryClient();
+// Protected route component
+const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
+ const { user, loading } = useAuth();
+
+ if (loading) {
+ return
Loading...
;
+ }
+
+ if (!user) {
+ return ;
+ }
+
+ return <>{children}>;
+};
+
const App = () => (
-
-
-
-
-
- } />
- {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
- } />
-
-
-
+
+
+
+
+
+
+ } />
+ } />
+ {/* Protected routes example (Dashboard will be implemented later) */}
+
+
+
+
Dashboard
+
This is a protected route. Dashboard implementation coming soon.
+
+
+
+ }
+ />
+ {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
+ } />
+
+
+
+
);
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index b4290ab..b913eea 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -2,13 +2,24 @@
import { useState, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Button } from "@/components/ui/button";
-import { ShieldCheck, Menu, X } from 'lucide-react';
+import { ShieldCheck, Menu, X, User, LogOut } from 'lucide-react';
+import { useAuth } from '@/contexts/AuthContext';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Avatar, AvatarFallback } from "@/components/ui/avatar";
const Header = () => {
const [isScrolled, setIsScrolled] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const location = useLocation();
const isHome = location.pathname === '/';
+ const { user, profile, signOut } = useAuth();
useEffect(() => {
const handleScroll = () => {
@@ -18,6 +29,17 @@ const Header = () => {
return () => window.removeEventListener('scroll', handleScroll);
}, []);
+ const getInitials = () => {
+ if (profile?.full_name) {
+ return profile.full_name
+ .split(' ')
+ .map((n: string) => n[0])
+ .join('')
+ .toUpperCase();
+ }
+ return user?.email?.substring(0, 2).toUpperCase() || 'U';
+ };
+
return (
@@ -46,13 +68,38 @@ const Header = () => {
{/* Desktop CTA */}
- {isHome ? (
+ {user ? (
+
+
+
+
+
+ My Account
+
+
+ Dashboard
+
+
+ Profile
+
+
+ signOut()} className="text-destructive">
+
+ Log out
+
+
+
+ ) : isHome ? (
) : (
-
@@ -80,9 +127,28 @@ const Header = () => {
setMobileMenuOpen(false)}>
Pricing
- {isHome && (
+ {user ? (
+ <>
+
setMobileMenuOpen(false)}>
+ Profile
+
+
{
+ signOut();
+ setMobileMenuOpen(false);
+ }}
+ >
+
+ Log out
+
+ >
+ ) : (
- setMobileMenuOpen(false)}>Try for Free
+ setMobileMenuOpen(false)}>
+ {isHome ? 'Try for Free' : 'Sign In'}
+
)}
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
index 991f56e..1b502d1 100644
--- a/src/components/ui/avatar.tsx
+++ b/src/components/ui/avatar.tsx
@@ -1,3 +1,4 @@
+
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
index 769ff7a..30d5d91 100644
--- a/src/components/ui/dropdown-menu.tsx
+++ b/src/components/ui/dropdown-menu.tsx
@@ -1,3 +1,4 @@
+
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
@@ -81,7 +82,7 @@ const DropdownMenuItem = React.forwardRef<
Promise;
+ signUp: (email: string, password: string, fullName: string) => Promise;
+ signOut: () => Promise;
+};
+
+const AuthContext = createContext(undefined);
+
+export const AuthProvider = ({ children }: { children: ReactNode }) => {
+ const [session, setSession] = useState(null);
+ const [user, setUser] = useState(null);
+ const [profile, setProfile] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const { toast } = useToast();
+
+ useEffect(() => {
+ // Get the initial session
+ supabase.auth.getSession().then(({ data: { session } }) => {
+ setSession(session);
+ setUser(session?.user ?? null);
+ if (session?.user) {
+ fetchProfile(session.user.id);
+ }
+ setLoading(false);
+ });
+
+ // Listen for auth changes
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(
+ (_event, session) => {
+ setSession(session);
+ setUser(session?.user ?? null);
+ if (session?.user) {
+ fetchProfile(session.user.id);
+ } else {
+ setProfile(null);
+ }
+ setLoading(false);
+ }
+ );
+
+ return () => {
+ subscription.unsubscribe();
+ };
+ }, []);
+
+ const fetchProfile = async (userId: string) => {
+ try {
+ const { data, error } = await supabase
+ .from('profiles')
+ .select('*')
+ .eq('id', userId)
+ .single();
+
+ if (error) {
+ console.error('Error fetching profile:', error);
+ return;
+ }
+
+ setProfile(data);
+ } catch (error) {
+ console.error('Error fetching profile:', error);
+ }
+ };
+
+ const signIn = async (email: string, password: string) => {
+ try {
+ setLoading(true);
+ const { error } = await supabase.auth.signInWithPassword({ email, password });
+
+ if (error) throw error;
+
+ toast({
+ title: "Success!",
+ description: "You are now signed in.",
+ });
+ } catch (error: any) {
+ toast({
+ title: "Error signing in",
+ description: error.message,
+ variant: "destructive",
+ });
+ console.error('Error signing in:', error);
+ throw error;
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const signUp = async (email: string, password: string, fullName: string) => {
+ try {
+ setLoading(true);
+ const { error } = await supabase.auth.signUp({
+ email,
+ password,
+ options: {
+ data: {
+ full_name: fullName,
+ }
+ }
+ });
+
+ if (error) throw error;
+
+ toast({
+ title: "Account created!",
+ description: "Please check your email to confirm your account.",
+ });
+ } catch (error: any) {
+ toast({
+ title: "Error creating account",
+ description: error.message,
+ variant: "destructive",
+ });
+ console.error('Error signing up:', error);
+ throw error;
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const signOut = async () => {
+ try {
+ setLoading(true);
+ const { error } = await supabase.auth.signOut();
+ if (error) throw error;
+ toast({
+ title: "Signed out",
+ description: "You have been successfully signed out.",
+ });
+ } catch (error: any) {
+ toast({
+ title: "Error signing out",
+ description: error.message,
+ variant: "destructive",
+ });
+ console.error('Error signing out:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useAuth = () => {
+ const context = useContext(AuthContext);
+ if (context === undefined) {
+ throw new Error('useAuth must be used within an AuthProvider');
+ }
+ return context;
+};
diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts
index a4d65a8..e768ae4 100644
--- a/src/integrations/supabase/types.ts
+++ b/src/integrations/supabase/types.ts
@@ -9,7 +9,33 @@ export type Json =
export type Database = {
public: {
Tables: {
- [_ in never]: never
+ profiles: {
+ Row: {
+ company: string | null
+ created_at: string
+ full_name: string | null
+ id: string
+ role: string | null
+ updated_at: string
+ }
+ Insert: {
+ company?: string | null
+ created_at?: string
+ full_name?: string | null
+ id: string
+ role?: string | null
+ updated_at?: string
+ }
+ Update: {
+ company?: string | null
+ created_at?: string
+ full_name?: string | null
+ id?: string
+ role?: string | null
+ updated_at?: string
+ }
+ Relationships: []
+ }
}
Views: {
[_ in never]: never
diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx
new file mode 100644
index 0000000..a06961a
--- /dev/null
+++ b/src/pages/Auth.tsx
@@ -0,0 +1,194 @@
+
+import { useState } from 'react';
+import { Navigate } from 'react-router-dom';
+import { useAuth } from '@/contexts/AuthContext';
+import { ShieldCheck, Mail, Lock, User } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+
+const Auth = () => {
+ const { user, signIn, signUp, loading } = useAuth();
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [fullName, setFullName] = useState('');
+ const [authError, setAuthError] = useState(null);
+
+ // If user is already logged in, redirect to home page
+ if (user) {
+ return ;
+ }
+
+ const handleSignIn = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setAuthError(null);
+ try {
+ await signIn(email, password);
+ } catch (error: any) {
+ setAuthError(error.message);
+ }
+ };
+
+ const handleSignUp = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setAuthError(null);
+ try {
+ await signUp(email, password, fullName);
+ } catch (error: any) {
+ setAuthError(error.message);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
SecuPolicy
+
Sign in to access your account
+
+
+
+
+ Login
+ Register
+
+
+
+
+
+ Welcome back
+
+ Enter your credentials to access your account
+
+
+
+
+
+
+
+
+
+ Create an account
+
+ Enter your details to create your account
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Auth;