mirror of
https://github.com/FranP-code/inbox-negotiator.git
synced 2025-10-13 00:42:26 +00:00
Add authentication system with landing page
This commit is contained in:
committed by
GitHub
parent
1bb5fcd022
commit
7349ae477c
164
src/components/AuthForm.tsx
Normal file
164
src/components/AuthForm.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import React, { useState } from 'react';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Loader2, Mail, Lock, User } from 'lucide-react';
|
||||
|
||||
interface AuthFormProps {
|
||||
mode: 'login' | 'signup';
|
||||
}
|
||||
|
||||
export function AuthForm({ mode }: AuthFormProps) {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [fullName, setFullName] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError('');
|
||||
setMessage('');
|
||||
|
||||
try {
|
||||
if (mode === 'signup') {
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
data: {
|
||||
full_name: fullName,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
setMessage('Check your email for the confirmation link!');
|
||||
} else {
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
// Redirect to dashboard on successful login
|
||||
window.location.href = '/dashboard';
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-full max-w-md mx-auto">
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-2xl font-bold text-center">
|
||||
{mode === 'login' ? 'Welcome back' : 'Create account'}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-center">
|
||||
{mode === 'login'
|
||||
? 'Enter your credentials to access your dashboard'
|
||||
: 'Enter your details to create your account'
|
||||
}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{mode === 'signup' && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="fullName">Full Name</Label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-3 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
id="fullName"
|
||||
type="text"
|
||||
placeholder="John Doe"
|
||||
value={fullName}
|
||||
onChange={(e) => setFullName(e.target.value)}
|
||||
className="pl-10"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-3 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="john@example.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="pl-10"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-3 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="pl-10"
|
||||
required
|
||||
minLength={6}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{message && (
|
||||
<Alert>
|
||||
<AlertDescription>{message}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={loading}>
|
||||
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{mode === 'login' ? 'Sign In' : 'Create Account'}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="mt-6 text-center text-sm">
|
||||
{mode === 'login' ? (
|
||||
<p>
|
||||
Don't have an account?{' '}
|
||||
<a href="/signup" className="text-primary hover:underline font-medium">
|
||||
Sign up
|
||||
</a>
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
Already have an account?{' '}
|
||||
<a href="/login" className="text-primary hover:underline font-medium">
|
||||
Sign in
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { supabase, type Debt } from '../lib/supabase';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DebtCard } from './DebtCard';
|
||||
import { DebtTimeline } from './DebtTimeline';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -13,7 +14,8 @@ import {
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
RefreshCw,
|
||||
BarChart3
|
||||
BarChart3,
|
||||
LogOut
|
||||
} from 'lucide-react';
|
||||
|
||||
export function Dashboard() {
|
||||
@@ -96,6 +98,11 @@ export function Dashboard() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await supabase.auth.signOut();
|
||||
window.location.href = '/';
|
||||
};
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
@@ -125,14 +132,16 @@ export function Dashboard() {
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 flex items-center gap-3">
|
||||
<BarChart3 className="h-8 w-8 text-primary" />
|
||||
InboxNegotiator Dashboard
|
||||
</h1>
|
||||
<p className="text-gray-600 mt-2">
|
||||
AI-powered debt resolution platform with real-time updates
|
||||
</p>
|
||||
<div className="mb-8 flex justify-between items-start">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 flex items-center gap-3">
|
||||
<BarChart3 className="h-8 w-8 text-primary" />
|
||||
InboxNegotiator Dashboard
|
||||
</h1>
|
||||
<p className="text-gray-600 mt-2">
|
||||
AI-powered debt resolution platform with real-time updates
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
|
||||
102
src/components/Navbar.tsx
Normal file
102
src/components/Navbar.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import type { User } from '@supabase/supabase-js';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { BarChart3, LogOut, User as UserIcon } from 'lucide-react';
|
||||
|
||||
export function Navbar() {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
supabase.auth.getSession().then(({ data: { session } }) => {
|
||||
setUser(session?.user ?? null);
|
||||
});
|
||||
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange(
|
||||
(event, session) => {
|
||||
setUser(session?.user ?? null);
|
||||
}
|
||||
);
|
||||
|
||||
return () => subscription.unsubscribe();
|
||||
}, []);
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await supabase.auth.signOut();
|
||||
window.location.href = '/';
|
||||
};
|
||||
|
||||
const getInitials = (email: string) => {
|
||||
return email.substring(0, 2).toUpperCase();
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className="border-b bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<div className="flex items-center gap-3">
|
||||
<BarChart3 className="h-8 w-8 text-primary" />
|
||||
<a href="/" className="text-xl font-bold text-gray-900">
|
||||
InboxNegotiator
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
{user ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
|
||||
<Avatar className="h-8 w-8">
|
||||
<AvatarFallback className="text-xs">
|
||||
{getInitials(user.email || '')}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56" align="end">
|
||||
<div className="flex items-center justify-start gap-2 p-2">
|
||||
<div className="flex flex-col space-y-1 leading-none">
|
||||
<p className="font-medium text-sm">{user.user_metadata?.full_name || 'User'}</p>
|
||||
<p className="w-[200px] truncate text-xs text-muted-foreground">
|
||||
{user.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<a href="/dashboard" className="flex items-center">
|
||||
<UserIcon className="mr-2 h-4 w-4" />
|
||||
Dashboard
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={handleSignOut}>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
Sign out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" asChild>
|
||||
<a href="/login">Sign In</a>
|
||||
</Button>
|
||||
<Button asChild>
|
||||
<a href="/signup">Get Started</a>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
52
src/components/ProtectedRoute.tsx
Normal file
52
src/components/ProtectedRoute.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import type { User } from '@supabase/supabase-js';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function ProtectedRoute({ children }: ProtectedRouteProps) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Get initial session
|
||||
supabase.auth.getSession().then(({ data: { session } }) => {
|
||||
setUser(session?.user ?? null);
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
// Listen for auth changes
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange(
|
||||
(event, session) => {
|
||||
setUser(session?.user ?? null);
|
||||
setLoading(false);
|
||||
}
|
||||
);
|
||||
|
||||
return () => subscription.unsubscribe();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="flex items-center gap-2 text-lg">
|
||||
<Loader2 className="h-5 w-5 animate-spin" />
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
// Redirect to login if not authenticated
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
15
src/pages/dashboard.astro
Normal file
15
src/pages/dashboard.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
import '@/styles/globals.css'
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { Dashboard } from '../components/Dashboard';
|
||||
import { ProtectedRoute } from '../components/ProtectedRoute';
|
||||
import { Navbar } from '../components/Navbar';
|
||||
---
|
||||
|
||||
<Layout title="Dashboard - InboxNegotiator">
|
||||
<Navbar client:load />
|
||||
|
||||
<ProtectedRoute client:load>
|
||||
<Dashboard client:load />
|
||||
</ProtectedRoute>
|
||||
</Layout>
|
||||
@@ -1,9 +1,137 @@
|
||||
---
|
||||
import '@/styles/globals.css'
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { Dashboard } from '../components/Dashboard';
|
||||
import { Navbar } from '../components/Navbar';
|
||||
---
|
||||
|
||||
<Layout title="InboxNegotiator - AI-Powered Debt Resolution">
|
||||
<Dashboard client:load />
|
||||
<Navbar client:load />
|
||||
|
||||
<main class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
|
||||
<!-- Hero Section -->
|
||||
<section class="relative overflow-hidden">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-20 pb-16">
|
||||
<div class="text-center">
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
|
||||
AI-Powered
|
||||
<span class="text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-purple-600">
|
||||
Debt Resolution
|
||||
</span>
|
||||
</h1>
|
||||
<p class="text-xl text-gray-600 mb-8 max-w-3xl mx-auto">
|
||||
Forward your debt emails and let our AI negotiate FDCPA-compliant payment plans automatically.
|
||||
Save time, reduce stress, and potentially save thousands on your debts.
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a href="/signup" class="inline-flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 transition-colors">
|
||||
Get Started Free
|
||||
</a>
|
||||
<a href="#how-it-works" class="inline-flex items-center justify-center px-8 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 transition-colors">
|
||||
Learn More
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section id="how-it-works" class="py-16 bg-white">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-4">How It Works</h2>
|
||||
<p class="text-lg text-gray-600">Simple, automated, and compliant debt resolution</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Forward Emails</h3>
|
||||
<p class="text-gray-600">Simply forward your debt collection emails to our secure processing address.</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">AI Analysis</h3>
|
||||
<p class="text-gray-600">Our AI analyzes the debt and generates FDCPA-compliant negotiation strategies.</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-2">Track Progress</h3>
|
||||
<p class="text-gray-600">Monitor negotiations in real-time and track your potential savings.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Benefits Section -->
|
||||
<section class="py-16 bg-gray-50">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-4">Why Choose InboxNegotiator?</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
<div class="bg-white p-6 rounded-lg shadow-sm">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">FDCPA Compliant</h3>
|
||||
<p class="text-gray-600">All responses follow Fair Debt Collection Practices Act guidelines.</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-lg shadow-sm">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">Save Money</h3>
|
||||
<p class="text-gray-600">Potentially reduce your debt by up to 40% through strategic negotiations.</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-lg shadow-sm">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">Real-time Updates</h3>
|
||||
<p class="text-gray-600">Track your negotiations with live dashboard updates.</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-lg shadow-sm">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">Secure & Private</h3>
|
||||
<p class="text-gray-600">Your financial information is encrypted and protected.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="py-16 bg-blue-600">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h2 class="text-3xl font-bold text-white mb-4">Ready to Take Control of Your Debt?</h2>
|
||||
<p class="text-xl text-blue-100 mb-8">Join thousands who have successfully negotiated their debts with AI assistance.</p>
|
||||
<a href="/signup" class="inline-flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-blue-600 bg-white hover:bg-gray-50 transition-colors">
|
||||
Start Your Free Account
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-gray-900 text-white py-12">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="text-center">
|
||||
<div class="flex items-center justify-center gap-3 mb-4">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
|
||||
</svg>
|
||||
<span class="text-xl font-bold">InboxNegotiator</span>
|
||||
</div>
|
||||
<p class="text-gray-400 mb-4">AI-powered debt resolution platform</p>
|
||||
<p class="text-sm text-gray-500">© 2025 InboxNegotiator. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</Layout>
|
||||
21
src/pages/login.astro
Normal file
21
src/pages/login.astro
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import '@/styles/globals.css'
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { AuthForm } from '../components/AuthForm';
|
||||
import { Navbar } from '../components/Navbar';
|
||||
---
|
||||
|
||||
<Layout title="Sign In - InboxNegotiator">
|
||||
<Navbar client:load />
|
||||
|
||||
<main class="min-h-screen bg-gray-50 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="w-full max-w-md">
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2">Sign In</h1>
|
||||
<p class="text-gray-600">Access your debt resolution dashboard</p>
|
||||
</div>
|
||||
|
||||
<AuthForm mode="login" client:load />
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
21
src/pages/signup.astro
Normal file
21
src/pages/signup.astro
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import '@/styles/globals.css'
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { AuthForm } from '../components/AuthForm';
|
||||
import { Navbar } from '../components/Navbar';
|
||||
---
|
||||
|
||||
<Layout title="Sign Up - InboxNegotiator">
|
||||
<Navbar client:load />
|
||||
|
||||
<main class="min-h-screen bg-gray-50 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div class="w-full max-w-md">
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2">Create Account</h1>
|
||||
<p class="text-gray-600">Start resolving your debts with AI assistance</p>
|
||||
</div>
|
||||
|
||||
<AuthForm mode="signup" client:load />
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
Reference in New Issue
Block a user