Email Sorter Beta
Ich habe soweit automatisiert the Emails sortieren aber ich muss noch schauen was es fur bugs es gibt wenn die app online ist deswegen wurde ich mit diesen Commit die website veroffentlichen obwohjl es sein konnte das es noch nicht fertig ist und verkaufs bereit
This commit is contained in:
119
client/src/components/landing/FAQ.tsx
Normal file
119
client/src/components/landing/FAQ.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown, HelpCircle } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
question: "Are my emails secure?",
|
||||
answer: "Yes! We use OAuth – we never see your password. Content is only analyzed briefly, never stored."
|
||||
},
|
||||
{
|
||||
question: "Which email providers work?",
|
||||
answer: "Gmail and Outlook. More coming soon."
|
||||
},
|
||||
{
|
||||
question: "Can I create custom rules?",
|
||||
answer: "Absolutely! You can set VIP contacts and define custom categories."
|
||||
},
|
||||
{
|
||||
question: "What about old emails?",
|
||||
answer: "The last 30 days are analyzed. You decide if they should be sorted too."
|
||||
},
|
||||
{
|
||||
question: "Can I cancel anytime?",
|
||||
answer: "Yes, with one click. No tricks, no long commitments."
|
||||
},
|
||||
{
|
||||
question: "Do I need a credit card?",
|
||||
answer: "No, the 14-day trial is completely free."
|
||||
},
|
||||
{
|
||||
question: "Does it work on mobile?",
|
||||
answer: "Yes! Sorting runs on our servers – works in any email app."
|
||||
},
|
||||
{
|
||||
question: "What if the AI sorts wrong?",
|
||||
answer: "Just correct it. The AI learns and gets better over time."
|
||||
},
|
||||
]
|
||||
|
||||
export function FAQ() {
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(0)
|
||||
|
||||
return (
|
||||
<section id="faq" className="py-24 bg-slate-50">
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Section header */}
|
||||
<div className="text-center mb-16">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-100 mb-6">
|
||||
<HelpCircle className="w-8 h-8 text-primary-600" />
|
||||
</div>
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-4">
|
||||
FAQ
|
||||
</h2>
|
||||
<p className="text-lg text-slate-600">
|
||||
Quick answers to common questions.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* FAQ items */}
|
||||
<div className="space-y-3">
|
||||
{faqs.map((faq, index) => (
|
||||
<FAQItem
|
||||
key={index}
|
||||
question={faq.question}
|
||||
answer={faq.answer}
|
||||
isOpen={openIndex === index}
|
||||
onClick={() => setOpenIndex(openIndex === index ? null : index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Contact CTA */}
|
||||
<div className="mt-12 text-center p-6 bg-white rounded-2xl border border-slate-200">
|
||||
<p className="text-slate-600 mb-2">Still have questions?</p>
|
||||
<a
|
||||
href="mailto:support@emailsorter.com"
|
||||
className="text-primary-600 font-semibold hover:text-primary-700"
|
||||
>
|
||||
Contact us →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface FAQItemProps {
|
||||
question: string
|
||||
answer: string
|
||||
isOpen: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
function FAQItem({ question, answer, isOpen, onClick }: FAQItemProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
<button
|
||||
className="w-full px-6 py-4 text-left flex items-center justify-between hover:bg-slate-50 transition-colors"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="font-semibold text-slate-900 pr-4">{question}</span>
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"w-5 h-5 text-slate-400 transition-transform duration-200 flex-shrink-0",
|
||||
isOpen && "rotate-180"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className={cn(
|
||||
"overflow-hidden transition-all duration-200",
|
||||
isOpen ? "max-h-40" : "max-h-0"
|
||||
)}
|
||||
>
|
||||
<p className="px-6 pb-4 text-slate-600">{answer}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
137
client/src/components/landing/Features.tsx
Normal file
137
client/src/components/landing/Features.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import {
|
||||
Brain,
|
||||
Zap,
|
||||
Shield,
|
||||
Clock,
|
||||
Tags,
|
||||
Settings,
|
||||
Inbox,
|
||||
Filter
|
||||
} from 'lucide-react'
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: Brain,
|
||||
title: "AI-powered categorization",
|
||||
description: "Our AI automatically recognizes whether an email is an invoice, newsletter, or important message.",
|
||||
color: "from-violet-500 to-purple-600"
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
title: "Real-time sorting",
|
||||
description: "New emails are categorized instantly. Your inbox arrives already sorted.",
|
||||
color: "from-amber-500 to-orange-600"
|
||||
},
|
||||
{
|
||||
icon: Tags,
|
||||
title: "Smart labels",
|
||||
description: "Automatic labels for VIP, clients, invoices, newsletters, social media and more.",
|
||||
color: "from-blue-500 to-cyan-600"
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: "GDPR compliant",
|
||||
description: "Your data stays secure. We only read email headers and metadata for sorting.",
|
||||
color: "from-green-500 to-emerald-600"
|
||||
},
|
||||
{
|
||||
icon: Clock,
|
||||
title: "Save time",
|
||||
description: "Average 2 hours per week less on email organization. More time for what matters.",
|
||||
color: "from-pink-500 to-rose-600"
|
||||
},
|
||||
{
|
||||
icon: Settings,
|
||||
title: "Fully customizable",
|
||||
description: "Define your own rules, VIP contacts, and categories based on your needs.",
|
||||
color: "from-indigo-500 to-blue-600"
|
||||
},
|
||||
]
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section id="features" className="py-24 bg-slate-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Section header */}
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-4">
|
||||
Everything you need for{' '}
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-accent-500">
|
||||
Inbox Zero
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
EmailSorter combines AI technology with proven email management methods
|
||||
for maximum productivity.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Features grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<FeatureCard key={index} {...feature} index={index} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bottom illustration */}
|
||||
<div className="mt-20 relative">
|
||||
<div className="bg-white rounded-3xl border border-slate-200 shadow-xl p-8 max-w-4xl mx-auto">
|
||||
<div className="grid md:grid-cols-3 gap-8 items-center">
|
||||
{/* Before */}
|
||||
<div className="text-center">
|
||||
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-red-100 flex items-center justify-center">
|
||||
<Inbox className="w-10 h-10 text-red-500" />
|
||||
</div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Before</h4>
|
||||
<p className="text-sm text-slate-500">Inbox chaos</p>
|
||||
<div className="mt-3 text-3xl font-bold text-red-500">847</div>
|
||||
<p className="text-xs text-slate-400">unread emails</p>
|
||||
</div>
|
||||
|
||||
{/* Arrow */}
|
||||
<div className="hidden md:flex justify-center">
|
||||
<div className="w-24 h-24 rounded-full bg-gradient-to-r from-primary-500 to-accent-500 flex items-center justify-center shadow-lg">
|
||||
<Filter className="w-10 h-10 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* After */}
|
||||
<div className="text-center">
|
||||
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-green-100 flex items-center justify-center">
|
||||
<Inbox className="w-10 h-10 text-green-500" />
|
||||
</div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">After</h4>
|
||||
<p className="text-sm text-slate-500">All sorted</p>
|
||||
<div className="mt-3 text-3xl font-bold text-green-500">12</div>
|
||||
<p className="text-xs text-slate-400">important emails</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface FeatureCardProps {
|
||||
icon: React.ElementType
|
||||
title: string
|
||||
description: string
|
||||
color: string
|
||||
index: number
|
||||
}
|
||||
|
||||
function FeatureCard({ icon: Icon, title, description, color, index }: FeatureCardProps) {
|
||||
return (
|
||||
<div
|
||||
className="group bg-white rounded-2xl p-6 border border-slate-200 hover:border-primary-200 hover:shadow-lg transition-all duration-300"
|
||||
style={{ animationDelay: `${index * 0.1}s` }}
|
||||
>
|
||||
<div className={`w-14 h-14 rounded-xl bg-gradient-to-br ${color} flex items-center justify-center mb-5 group-hover:scale-110 transition-transform duration-300`}>
|
||||
<Icon className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-slate-900 mb-2">{title}</h3>
|
||||
<p className="text-slate-600">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
185
client/src/components/landing/Footer.tsx
Normal file
185
client/src/components/landing/Footer.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Mail, Twitter, Linkedin, Github } from 'lucide-react'
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-slate-900 text-slate-300">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<div className="grid md:grid-cols-4 gap-12">
|
||||
{/* Brand */}
|
||||
<div className="md:col-span-1">
|
||||
<Link to="/" className="flex items-center gap-2 mb-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center">
|
||||
<Mail className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<span className="text-xl font-bold text-white">
|
||||
Email<span className="text-primary-400">Sorter</span>
|
||||
</span>
|
||||
</Link>
|
||||
<p className="text-sm text-slate-400 mb-6">
|
||||
AI-powered email sorting for more productivity and less stress.
|
||||
</p>
|
||||
{/* Social links */}
|
||||
<div className="flex gap-4">
|
||||
<a
|
||||
href="#"
|
||||
className="w-10 h-10 rounded-lg bg-slate-800 flex items-center justify-center hover:bg-slate-700 transition-colors"
|
||||
>
|
||||
<Twitter className="w-5 h-5" />
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="w-10 h-10 rounded-lg bg-slate-800 flex items-center justify-center hover:bg-slate-700 transition-colors"
|
||||
>
|
||||
<Linkedin className="w-5 h-5" />
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="w-10 h-10 rounded-lg bg-slate-800 flex items-center justify-center hover:bg-slate-700 transition-colors"
|
||||
>
|
||||
<Github className="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Product */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-white mb-4">Product</h4>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<button
|
||||
onClick={() => document.getElementById('features')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
Features
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
onClick={() => document.getElementById('pricing')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
Pricing
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
onClick={() => document.getElementById('faq')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
FAQ
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" className="hover:text-white transition-colors">
|
||||
Roadmap
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Company */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-white mb-4">Company</h4>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a
|
||||
href="https://webklar.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
About us
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://webklar.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://webklar.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
Careers
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="mailto:support@webklar.com"
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Legal */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-white mb-4">Legal</h4>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<Link to="/privacy" className="hover:text-white transition-colors">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/imprint" className="hover:text-white transition-colors">
|
||||
Impressum
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://webklar.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-white transition-colors"
|
||||
>
|
||||
webklar.com
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<div className="mt-12 pt-8 border-t border-slate-800">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 mb-4">
|
||||
<p className="text-sm text-slate-500">
|
||||
© {new Date().getFullYear()} EmailSorter. All rights reserved.
|
||||
</p>
|
||||
<p className="text-sm text-slate-500">
|
||||
Made with ❤️
|
||||
</p>
|
||||
</div>
|
||||
{/* webklar.com Verweis */}
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pt-4 border-t border-slate-800">
|
||||
<p className="text-sm text-slate-400">
|
||||
Need a website?
|
||||
</p>
|
||||
<a
|
||||
href="https://webklar.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm font-medium text-primary-400 hover:text-primary-300 transition-colors inline-flex items-center gap-1"
|
||||
>
|
||||
Visit webklar.com
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
179
client/src/components/landing/Hero.tsx
Normal file
179
client/src/components/landing/Hero.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { captureUTMParams } from '@/lib/analytics'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { ArrowRight, Mail, Inbox, Sparkles, Check, Zap } from 'lucide-react'
|
||||
|
||||
export function Hero() {
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleCTAClick = () => {
|
||||
// Capture UTM parameters before navigation
|
||||
captureUTMParams()
|
||||
navigate('/register')
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="relative min-h-screen flex items-center overflow-hidden">
|
||||
{/* Background */}
|
||||
<div className="absolute inset-0 gradient-hero" />
|
||||
<div className="absolute inset-0 gradient-mesh opacity-30" />
|
||||
|
||||
{/* Grid pattern overlay */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-10"
|
||||
style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.4'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-32">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left side - Text content */}
|
||||
<div className="text-center lg:text-left">
|
||||
<Badge className="mb-6 bg-primary-500/20 text-primary-200 border-primary-400/30">
|
||||
<Sparkles className="w-3 h-3 mr-1" />
|
||||
AI-powered email sorting
|
||||
</Badge>
|
||||
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold text-white leading-tight mb-6">
|
||||
Your inbox.
|
||||
<br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-accent-400 to-primary-300">
|
||||
Finally organized.
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-lg sm:text-xl text-slate-300 mb-8 max-w-xl mx-auto lg:mx-0">
|
||||
EmailSorter uses AI to automatically categorize your emails.
|
||||
Newsletters, invoices, important contacts – everything lands
|
||||
exactly where it belongs.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center lg:justify-start mb-8">
|
||||
<Button
|
||||
size="xl"
|
||||
onClick={handleCTAClick}
|
||||
className="group"
|
||||
>
|
||||
Start 14-day free trial
|
||||
<ArrowRight className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" />
|
||||
</Button>
|
||||
<Button
|
||||
size="xl"
|
||||
variant="outline"
|
||||
className="bg-white/10 border-white/20 text-white hover:bg-white/20"
|
||||
onClick={() => document.getElementById('how-it-works')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
>
|
||||
See how it works
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Trust badges */}
|
||||
<div className="flex flex-wrap gap-6 justify-center lg:justify-start text-slate-400 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<Check className="w-4 h-4 text-accent-400" />
|
||||
No credit card required
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Check className="w-4 h-4 text-accent-400" />
|
||||
Gmail & Outlook
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Check className="w-4 h-4 text-accent-400" />
|
||||
GDPR compliant
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right side - Visual */}
|
||||
<div className="relative hidden lg:block">
|
||||
<div className="relative">
|
||||
{/* Main card */}
|
||||
<div className="bg-white/10 backdrop-blur-xl rounded-3xl border border-white/20 p-6 shadow-2xl animate-float">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center">
|
||||
<Inbox className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-white">Your Inbox</h3>
|
||||
<p className="text-sm text-slate-400">Auto-sorted</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email categories preview */}
|
||||
<div className="space-y-3">
|
||||
<EmailPreview
|
||||
category="Important"
|
||||
color="bg-red-500"
|
||||
sender="John Smith"
|
||||
subject="Meeting tomorrow at 10"
|
||||
delay="stagger-1"
|
||||
/>
|
||||
<EmailPreview
|
||||
category="Invoice"
|
||||
color="bg-green-500"
|
||||
sender="Amazon"
|
||||
subject="Invoice for order #12345"
|
||||
delay="stagger-2"
|
||||
/>
|
||||
<EmailPreview
|
||||
category="Newsletter"
|
||||
color="bg-purple-500"
|
||||
sender="Tech Daily"
|
||||
subject="Latest AI trends"
|
||||
delay="stagger-3"
|
||||
/>
|
||||
<EmailPreview
|
||||
category="Social"
|
||||
color="bg-cyan-500"
|
||||
sender="LinkedIn"
|
||||
subject="3 new connection requests"
|
||||
delay="stagger-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating badge */}
|
||||
<div className="absolute -right-4 top-1/4 bg-accent-500 text-white px-4 py-2 rounded-full text-sm font-semibold shadow-lg animate-pulse-glow">
|
||||
<Zap className="w-4 h-4 inline mr-1" />
|
||||
AI sorting
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scroll indicator */}
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 animate-bounce">
|
||||
<div className="w-6 h-10 rounded-full border-2 border-white/30 flex justify-center pt-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-white/60" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface EmailPreviewProps {
|
||||
category: string
|
||||
color: string
|
||||
sender: string
|
||||
subject: string
|
||||
delay: string
|
||||
}
|
||||
|
||||
function EmailPreview({ category, color, sender, subject, delay }: EmailPreviewProps) {
|
||||
return (
|
||||
<div className={`flex items-center gap-3 bg-white/5 rounded-xl p-3 border border-white/10 opacity-0 animate-[fadeIn_0.5s_ease-out_forwards] ${delay}`}>
|
||||
<div className={`w-2 h-10 rounded-full ${color}`} />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-0.5">
|
||||
<span className="text-sm font-medium text-white truncate">{sender}</span>
|
||||
<span className={`text-xs px-2 py-0.5 rounded ${color} text-white`}>{category}</span>
|
||||
</div>
|
||||
<p className="text-sm text-slate-400 truncate">{subject}</p>
|
||||
</div>
|
||||
<Mail className="w-4 h-4 text-slate-500 flex-shrink-0" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
111
client/src/components/landing/HowItWorks.tsx
Normal file
111
client/src/components/landing/HowItWorks.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
UserPlus,
|
||||
Link2,
|
||||
Sparkles,
|
||||
PartyPopper,
|
||||
ArrowDown
|
||||
} from 'lucide-react'
|
||||
|
||||
const steps = [
|
||||
{
|
||||
icon: UserPlus,
|
||||
step: "01",
|
||||
title: "Create account",
|
||||
description: "Sign up for free in less than 60 seconds. No credit card required.",
|
||||
},
|
||||
{
|
||||
icon: Link2,
|
||||
step: "02",
|
||||
title: "Connect email",
|
||||
description: "Connect Gmail or Outlook with one click. Secure OAuth authentication.",
|
||||
},
|
||||
{
|
||||
icon: Sparkles,
|
||||
step: "03",
|
||||
title: "AI analyzes",
|
||||
description: "Our AI learns your email patterns and creates personalized sorting rules.",
|
||||
},
|
||||
{
|
||||
icon: PartyPopper,
|
||||
step: "04",
|
||||
title: "Enjoy Inbox Zero",
|
||||
description: "Sit back and enjoy a clean inbox – automatically.",
|
||||
},
|
||||
]
|
||||
|
||||
export function HowItWorks() {
|
||||
return (
|
||||
<section id="how-it-works" className="py-24 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Section header */}
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-4">
|
||||
4 steps to a{' '}
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-accent-500">
|
||||
clean inbox
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Get started in minutes – no technical knowledge required.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Steps */}
|
||||
<div className="relative">
|
||||
{/* Connection line */}
|
||||
<div className="hidden lg:block absolute top-1/2 left-0 right-0 h-0.5 bg-gradient-to-r from-primary-200 via-primary-400 to-primary-200 -translate-y-1/2" />
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{steps.map((item, index) => (
|
||||
<StepCard key={index} {...item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="mt-16 text-center">
|
||||
<div className="inline-flex flex-col items-center">
|
||||
<ArrowDown className="w-8 h-8 text-primary-400 animate-bounce mb-4" />
|
||||
<p className="text-slate-600 mb-2">Ready to get started?</p>
|
||||
<a
|
||||
href="/register"
|
||||
className="text-primary-600 font-semibold hover:text-primary-700 transition-colors"
|
||||
>
|
||||
Try it free now →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface StepCardProps {
|
||||
icon: React.ElementType
|
||||
step: string
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
function StepCard({ icon: Icon, step, title, description }: StepCardProps) {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* Card */}
|
||||
<div className="bg-slate-50 rounded-2xl p-6 text-center hover:bg-white hover:shadow-xl transition-all duration-300 border border-transparent hover:border-slate-200">
|
||||
{/* Step number */}
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-gradient-to-r from-primary-500 to-primary-600 text-white text-sm font-bold px-4 py-1 rounded-full shadow-md">
|
||||
{step}
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<div className="w-16 h-16 mx-auto mt-4 mb-4 rounded-2xl bg-white shadow-md flex items-center justify-center">
|
||||
<Icon className="w-8 h-8 text-primary-600" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">{title}</h3>
|
||||
<p className="text-slate-600 text-sm">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
159
client/src/components/landing/Navbar.tsx
Normal file
159
client/src/components/landing/Navbar.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import { useState, useCallback } from 'react'
|
||||
import { Link, useNavigate, useLocation } from 'react-router-dom'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useAuth } from '@/context/AuthContext'
|
||||
import { Menu, X, Mail, Sparkles } from 'lucide-react'
|
||||
|
||||
export function Navbar() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
const { user } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
// Smooth scroll to section
|
||||
const scrollToSection = useCallback((sectionId: string) => {
|
||||
setIsMenuOpen(false)
|
||||
|
||||
// If not on home page, navigate first
|
||||
if (location.pathname !== '/') {
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
const element = document.getElementById(sectionId)
|
||||
element?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}, 100)
|
||||
} else {
|
||||
const element = document.getElementById(sectionId)
|
||||
element?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
}, [location.pathname, navigate])
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-lg border-b border-slate-100">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center gap-2">
|
||||
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-700 flex items-center justify-center">
|
||||
<Mail className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<span className="text-xl font-bold text-slate-900">
|
||||
Email<span className="text-primary-600">Sorter</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden md:flex items-center gap-8">
|
||||
<button
|
||||
onClick={() => scrollToSection('features')}
|
||||
className="text-slate-600 hover:text-slate-900 font-medium transition-colors"
|
||||
>
|
||||
Features
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollToSection('how-it-works')}
|
||||
className="text-slate-600 hover:text-slate-900 font-medium transition-colors"
|
||||
>
|
||||
How it works
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollToSection('pricing')}
|
||||
className="text-slate-600 hover:text-slate-900 font-medium transition-colors"
|
||||
>
|
||||
Pricing
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollToSection('faq')}
|
||||
className="text-slate-600 hover:text-slate-900 font-medium transition-colors"
|
||||
>
|
||||
FAQ
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Desktop CTA */}
|
||||
<div className="hidden md:flex items-center gap-4">
|
||||
{user ? (
|
||||
<Button onClick={() => navigate('/dashboard')}>
|
||||
Dashboard
|
||||
<Sparkles className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button variant="ghost" onClick={() => navigate('/login')}>
|
||||
Sign in
|
||||
</Button>
|
||||
<Button onClick={() => navigate('/register')}>
|
||||
Get started free
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
className="md:hidden p-2.5 rounded-lg hover:bg-slate-100 active:bg-slate-200 touch-manipulation"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
||||
>
|
||||
{isMenuOpen ? (
|
||||
<X className="w-6 h-6 text-slate-600" />
|
||||
) : (
|
||||
<Menu className="w-6 h-6 text-slate-600" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu */}
|
||||
{isMenuOpen && (
|
||||
<div className="md:hidden bg-white border-t border-slate-100 shadow-lg animate-in slide-in-from-top-2 duration-200">
|
||||
<div className="px-3 py-3 space-y-1">
|
||||
<button
|
||||
onClick={() => scrollToSection('features')}
|
||||
className="block w-full text-left px-4 py-3 text-sm font-medium text-slate-700 hover:bg-slate-50 active:bg-slate-100 rounded-lg transition-colors touch-manipulation"
|
||||
>
|
||||
Features
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollToSection('how-it-works')}
|
||||
className="block w-full text-left px-4 py-3 text-sm font-medium text-slate-700 hover:bg-slate-50 active:bg-slate-100 rounded-lg transition-colors touch-manipulation"
|
||||
>
|
||||
How it works
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollToSection('pricing')}
|
||||
className="block w-full text-left px-4 py-3 text-sm font-medium text-slate-700 hover:bg-slate-50 active:bg-slate-100 rounded-lg transition-colors touch-manipulation"
|
||||
>
|
||||
Pricing
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollToSection('faq')}
|
||||
className="block w-full text-left px-4 py-3 text-sm font-medium text-slate-700 hover:bg-slate-50 active:bg-slate-100 rounded-lg transition-colors touch-manipulation"
|
||||
>
|
||||
FAQ
|
||||
</button>
|
||||
<div className="pt-3 mt-3 border-t border-slate-100 space-y-2">
|
||||
{user ? (
|
||||
<Button className="w-full h-11" onClick={() => navigate('/dashboard')}>
|
||||
Dashboard
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full h-11"
|
||||
onClick={() => navigate('/login')}
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
<Button className="w-full h-11" onClick={() => navigate('/register')}>
|
||||
Get started free
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
193
client/src/components/landing/Pricing.tsx
Normal file
193
client/src/components/landing/Pricing.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Check, X, Sparkles } from 'lucide-react'
|
||||
|
||||
const plans = [
|
||||
{
|
||||
name: "Basic",
|
||||
price: "9",
|
||||
period: "/ month",
|
||||
description: "Perfect for getting started",
|
||||
features: [
|
||||
{ text: "1 email account", included: true },
|
||||
{ text: "500 emails / day", included: true },
|
||||
{ text: "Basic categories", included: true },
|
||||
{ text: "Email support", included: true },
|
||||
{ text: "Historical email analysis", included: false },
|
||||
{ text: "Custom rules", included: false },
|
||||
{ text: "Priority support", included: false },
|
||||
],
|
||||
cta: "Start Basic",
|
||||
popular: false,
|
||||
priceId: "price_basic_monthly"
|
||||
},
|
||||
{
|
||||
name: "Pro",
|
||||
price: "19",
|
||||
period: "/ month",
|
||||
description: "For power users",
|
||||
features: [
|
||||
{ text: "3 email accounts", included: true },
|
||||
{ text: "Unlimited emails", included: true },
|
||||
{ text: "All categories", included: true },
|
||||
{ text: "Email support", included: true },
|
||||
{ text: "Historical email analysis", included: true },
|
||||
{ text: "Custom rules", included: true },
|
||||
{ text: "Priority support", included: false },
|
||||
],
|
||||
cta: "Start Pro",
|
||||
popular: true,
|
||||
priceId: "price_pro_monthly"
|
||||
},
|
||||
{
|
||||
name: "Business",
|
||||
price: "49",
|
||||
period: "/ month",
|
||||
description: "For teams & companies",
|
||||
features: [
|
||||
{ text: "10 email accounts", included: true },
|
||||
{ text: "Unlimited emails", included: true },
|
||||
{ text: "All categories", included: true },
|
||||
{ text: "Email + chat support", included: true },
|
||||
{ text: "Historical email analysis", included: true },
|
||||
{ text: "Custom rules", included: true },
|
||||
{ text: "Priority support", included: true },
|
||||
],
|
||||
cta: "Start Business",
|
||||
popular: false,
|
||||
priceId: "price_business_monthly"
|
||||
},
|
||||
]
|
||||
|
||||
export function Pricing() {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<section id="pricing" className="py-24 bg-slate-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Section header */}
|
||||
<div className="text-center mb-16">
|
||||
<Badge className="mb-4">
|
||||
<Sparkles className="w-3 h-3 mr-1" />
|
||||
14-day free trial
|
||||
</Badge>
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-4">
|
||||
Simple, transparent pricing
|
||||
</h2>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Choose the plan that fits you. Cancel anytime, no hidden costs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Pricing cards */}
|
||||
<div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto">
|
||||
{plans.map((plan, index) => (
|
||||
<PricingCard
|
||||
key={index}
|
||||
{...plan}
|
||||
onSelect={() => navigate(`/register?plan=${plan.name.toLowerCase()}`)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* FAQ teaser */}
|
||||
<div className="mt-16 text-center">
|
||||
<p className="text-slate-600">
|
||||
Still have questions?{' '}
|
||||
<button
|
||||
onClick={() => document.getElementById('faq')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="text-primary-600 font-semibold hover:text-primary-700"
|
||||
>
|
||||
Check our FAQ
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface PricingCardProps {
|
||||
name: string
|
||||
price: string
|
||||
period: string
|
||||
description: string
|
||||
features: { text: string; included: boolean }[]
|
||||
cta: string
|
||||
popular: boolean
|
||||
onSelect: () => void
|
||||
}
|
||||
|
||||
function PricingCard({
|
||||
name,
|
||||
price,
|
||||
period,
|
||||
description,
|
||||
features,
|
||||
cta,
|
||||
popular,
|
||||
onSelect
|
||||
}: PricingCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={`relative bg-white rounded-2xl p-8 ${
|
||||
popular
|
||||
? 'ring-2 ring-primary-500 shadow-xl scale-105'
|
||||
: 'border border-slate-200 hover:border-primary-200 hover:shadow-lg'
|
||||
} transition-all duration-300`}
|
||||
>
|
||||
{popular && (
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
||||
<Badge className="bg-primary-500 text-white border-0 shadow-md">
|
||||
Most Popular
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-xl font-bold text-slate-900 mb-1">{name}</h3>
|
||||
<p className="text-sm text-slate-500">{description}</p>
|
||||
</div>
|
||||
|
||||
{/* Price */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="flex items-baseline justify-center">
|
||||
<span className="text-5xl font-extrabold text-slate-900">${price}</span>
|
||||
<span className="text-slate-500 ml-1">{period}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Features */}
|
||||
<ul className="space-y-4 mb-8">
|
||||
{features.map((feature, index) => (
|
||||
<li key={index} className="flex items-center gap-3">
|
||||
{feature.included ? (
|
||||
<div className="w-5 h-5 rounded-full bg-green-100 flex items-center justify-center flex-shrink-0">
|
||||
<Check className="w-3 h-3 text-green-600" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-5 h-5 rounded-full bg-slate-100 flex items-center justify-center flex-shrink-0">
|
||||
<X className="w-3 h-3 text-slate-400" />
|
||||
</div>
|
||||
)}
|
||||
<span className={feature.included ? 'text-slate-700' : 'text-slate-400'}>
|
||||
{feature.text}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* CTA */}
|
||||
<Button
|
||||
className="w-full"
|
||||
variant={popular ? 'default' : 'outline'}
|
||||
size="lg"
|
||||
onClick={onSelect}
|
||||
>
|
||||
{cta}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
67
client/src/components/landing/Testimonials.tsx
Normal file
67
client/src/components/landing/Testimonials.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { CheckCircle2, Clock, Brain, Shield } from 'lucide-react'
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
icon: Clock,
|
||||
title: "Save 2+ hours/week",
|
||||
description: "Less time sorting emails, more time for important tasks.",
|
||||
},
|
||||
{
|
||||
icon: Brain,
|
||||
title: "AI does it automatically",
|
||||
description: "Set up once, then everything runs by itself.",
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: "Privacy first",
|
||||
description: "Your emails stay private. We don't store any content.",
|
||||
},
|
||||
{
|
||||
icon: CheckCircle2,
|
||||
title: "Easy to use",
|
||||
description: "No learning curve. Ready to go in 2 minutes.",
|
||||
},
|
||||
]
|
||||
|
||||
export function Testimonials() {
|
||||
return (
|
||||
<section className="py-20 bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Section header */}
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-white mb-4">
|
||||
Why EmailSorter?
|
||||
</h2>
|
||||
<p className="text-lg text-slate-300 max-w-2xl mx-auto">
|
||||
No more email chaos. Focus on what matters.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Benefits grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{benefits.map((benefit, index) => (
|
||||
<BenefitCard key={index} {...benefit} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
interface BenefitCardProps {
|
||||
icon: React.ElementType
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
function BenefitCard({ icon: Icon, title, description }: BenefitCardProps) {
|
||||
return (
|
||||
<div className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-white/10 hover:bg-white/10 transition-colors">
|
||||
<div className="w-12 h-12 rounded-lg bg-primary-500/20 flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-primary-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-2">{title}</h3>
|
||||
<p className="text-slate-400 text-sm">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user