chore: Docs umstrukturiert, Client-Updates, Scripts nach scripts/
This commit is contained in:
@@ -1,105 +0,0 @@
|
||||
# Favicon Setup Anleitung
|
||||
|
||||
Die Favicon-Dateien wurden erstellt. Um alle Formate zu generieren, folge diesen Schritten:
|
||||
|
||||
## Erstellte Dateien
|
||||
|
||||
✅ `favicon.svg` - Modernes SVG Favicon (bereits erstellt)
|
||||
✅ `apple-touch-icon.svg` - SVG für Apple Touch Icon (bereits erstellt)
|
||||
✅ `site.webmanifest` - Web App Manifest (bereits erstellt)
|
||||
|
||||
## Noch zu erstellen (PNG/ICO)
|
||||
|
||||
Du musst die folgenden PNG/ICO-Dateien aus dem SVG erstellen:
|
||||
|
||||
### Option 1: Online Converter verwenden
|
||||
|
||||
1. Gehe zu einem dieser Tools:
|
||||
- https://realfavicongenerator.net/ (Empfohlen - generiert alle Formate)
|
||||
- https://www.zenlytools.com/svg-to-ico
|
||||
- https://svg-to-ico.org/
|
||||
|
||||
2. Lade `favicon.svg` hoch
|
||||
|
||||
3. Generiere folgende Dateien:
|
||||
- `favicon.ico` (16x16, 32x32, 48x48)
|
||||
- `favicon-16x16.png`
|
||||
- `favicon-32x32.png`
|
||||
- `apple-touch-icon.png` (180x180)
|
||||
- `favicon-192x192.png` (für Web Manifest)
|
||||
- `favicon-512x512.png` (für Web Manifest)
|
||||
|
||||
4. Speichere alle generierten Dateien im `client/public/` Ordner
|
||||
|
||||
### Option 2: Mit ImageMagick (Command Line)
|
||||
|
||||
```bash
|
||||
# Installiere ImageMagick (falls nicht vorhanden)
|
||||
# Windows: choco install imagemagick
|
||||
# Mac: brew install imagemagick
|
||||
# Linux: sudo apt-get install imagemagick
|
||||
|
||||
cd client/public
|
||||
|
||||
# Erstelle PNG-Varianten
|
||||
magick favicon.svg -resize 16x16 favicon-16x16.png
|
||||
magick favicon.svg -resize 32x32 favicon-32x32.png
|
||||
magick apple-touch-icon.svg -resize 180x180 apple-touch-icon.png
|
||||
magick favicon.svg -resize 192x192 favicon-192x192.png
|
||||
magick favicon.svg -resize 512x512 favicon-512x512.png
|
||||
|
||||
# Erstelle ICO (mehrere Größen)
|
||||
magick favicon.svg -define icon:auto-resize=16,32,48 favicon.ico
|
||||
```
|
||||
|
||||
### Option 3: Mit Online Favicon Generator (Empfohlen)
|
||||
|
||||
1. Gehe zu: https://realfavicongenerator.net/
|
||||
2. Klicke auf "Select your Favicon image"
|
||||
3. Lade `favicon.svg` hoch
|
||||
4. Konfiguriere die Optionen:
|
||||
- iOS: Apple Touch Icon aktivieren
|
||||
- Android Chrome: Manifest aktivieren
|
||||
- Windows Metro: Optional
|
||||
5. Klicke auf "Generate your Favicons and HTML code"
|
||||
6. Lade das ZIP herunter
|
||||
7. Extrahiere alle Dateien in `client/public/`
|
||||
8. Kopiere die generierten `<link>` Tags in `index.html` (falls nötig)
|
||||
|
||||
## Verifizierung
|
||||
|
||||
Nach dem Erstellen aller Dateien:
|
||||
|
||||
1. Starte den Dev-Server: `npm run dev`
|
||||
2. Öffne die Seite im Browser
|
||||
3. Prüfe den Browser-Tab - das Favicon sollte angezeigt werden
|
||||
4. Teste auf Mobile:
|
||||
- iOS Safari: Zum Home-Bildschirm hinzufügen → Icon sollte erscheinen
|
||||
- Android Chrome: Installiere als PWA → Icon sollte erscheinen
|
||||
|
||||
## Dateien im public/ Ordner
|
||||
|
||||
Nach Abschluss sollten folgende Dateien vorhanden sein:
|
||||
|
||||
```
|
||||
client/public/
|
||||
├── favicon.svg ✅
|
||||
├── favicon.ico (zu erstellen)
|
||||
├── favicon-16x16.png (zu erstellen)
|
||||
├── favicon-32x32.png (zu erstellen)
|
||||
├── apple-touch-icon.png (zu erstellen)
|
||||
├── favicon-192x192.png (zu erstellen)
|
||||
├── favicon-512x512.png (zu erstellen)
|
||||
├── apple-touch-icon.svg ✅
|
||||
└── site.webmanifest ✅
|
||||
```
|
||||
|
||||
## Browser-Kompatibilität
|
||||
|
||||
- **Chrome/Edge**: Verwendet `favicon.svg` oder `favicon.ico`
|
||||
- **Firefox**: Verwendet `favicon.svg` oder `favicon.ico`
|
||||
- **Safari (Desktop)**: Verwendet `favicon.ico` oder PNG
|
||||
- **Safari (iOS)**: Verwendet `apple-touch-icon.png`
|
||||
- **Android Chrome**: Verwendet Icons aus `site.webmanifest`
|
||||
|
||||
Die aktuelle Konfiguration in `index.html` unterstützt alle modernen Browser!
|
||||
@@ -21,10 +21,10 @@ initAnalytics()
|
||||
// Loading spinner component
|
||||
function LoadingSpinner() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-50">
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-50 dark:bg-slate-900">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-10 h-10 border-4 border-primary-200 border-t-primary-600 rounded-full animate-spin" />
|
||||
<p className="text-slate-500 text-sm">Loading...</p>
|
||||
<div className="w-10 h-10 border-4 border-primary-200 dark:border-primary-800 border-t-primary-600 dark:border-t-primary-400 rounded-full animate-spin" />
|
||||
<p className="text-slate-500 dark:text-slate-400 text-sm">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -41,17 +41,17 @@ export function FAQ() {
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(0)
|
||||
|
||||
return (
|
||||
<section id="faq" className="py-24 bg-slate-50">
|
||||
<section id="faq" className="py-24 bg-slate-50 dark:bg-slate-900">
|
||||
<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 className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary-100 dark:bg-primary-900/30 mb-6">
|
||||
<HelpCircle className="w-8 h-8 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-4">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
||||
FAQ
|
||||
</h2>
|
||||
<p className="text-lg text-slate-600">
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400">
|
||||
Quick answers to common questions.
|
||||
</p>
|
||||
</div>
|
||||
@@ -70,11 +70,11 @@ export function FAQ() {
|
||||
</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>
|
||||
<div className="mt-12 text-center p-6 bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700">
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-2">Still have questions?</p>
|
||||
<a
|
||||
href="mailto:support@emailsorter.com"
|
||||
className="text-primary-600 font-semibold hover:text-primary-700"
|
||||
className="text-primary-600 dark:text-primary-400 font-semibold hover:text-primary-700 dark:hover:text-primary-300"
|
||||
>
|
||||
Contact us →
|
||||
</a>
|
||||
@@ -93,15 +93,15 @@ interface FAQItemProps {
|
||||
|
||||
function FAQItem({ question, answer, isOpen, onClick }: FAQItemProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
<div className="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
|
||||
<button
|
||||
className="w-full px-6 py-4 text-left flex items-center justify-between hover:bg-slate-50 transition-colors"
|
||||
className="w-full px-6 py-4 text-left flex items-center justify-between hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="font-semibold text-slate-900 pr-4">{question}</span>
|
||||
<span className="font-semibold text-slate-900 dark:text-slate-100 pr-4">{question}</span>
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"w-5 h-5 text-slate-400 transition-transform duration-200 flex-shrink-0",
|
||||
"w-5 h-5 text-slate-400 dark:text-slate-500 transition-transform duration-200 flex-shrink-0",
|
||||
isOpen && "rotate-180"
|
||||
)}
|
||||
/>
|
||||
@@ -112,7 +112,7 @@ function FAQItem({ question, answer, isOpen, onClick }: FAQItemProps) {
|
||||
isOpen ? "max-h-40" : "max-h-0"
|
||||
)}
|
||||
>
|
||||
<p className="px-6 pb-4 text-slate-600">{answer}</p>
|
||||
<p className="px-6 pb-4 text-slate-600 dark:text-slate-400">{answer}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -52,17 +52,17 @@ const features = [
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section id="features" className="py-24 bg-slate-50">
|
||||
<section id="features" className="py-24 bg-slate-50 dark:bg-slate-900">
|
||||
<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">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 dark:text-slate-100 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">
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
|
||||
EmailSorter combines AI technology with proven email management methods
|
||||
for maximum productivity.
|
||||
</p>
|
||||
@@ -77,17 +77,17 @@ export function Features() {
|
||||
|
||||
{/* 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="bg-white dark:bg-slate-800 rounded-3xl border border-slate-200 dark:border-slate-700 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 className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<Inbox className="w-10 h-10 text-red-500 dark:text-red-400" />
|
||||
</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>
|
||||
<h4 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">Before</h4>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">Inbox chaos</p>
|
||||
<div className="mt-3 text-3xl font-bold text-red-500 dark:text-red-400">847</div>
|
||||
<p className="text-xs text-slate-400 dark:text-slate-500">unread emails</p>
|
||||
</div>
|
||||
|
||||
{/* Arrow */}
|
||||
@@ -99,13 +99,13 @@ export function Features() {
|
||||
|
||||
{/* 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 className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||
<Inbox className="w-10 h-10 text-green-500 dark:text-green-400" />
|
||||
</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>
|
||||
<h4 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">After</h4>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">All sorted</p>
|
||||
<div className="mt-3 text-3xl font-bold text-green-500 dark:text-green-400">12</div>
|
||||
<p className="text-xs text-slate-400 dark:text-slate-500">important emails</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,16 +129,16 @@ function FeatureCard({ icon: Icon, title, description, color, index, highlight }
|
||||
<div
|
||||
className={`group rounded-2xl p-6 border transition-all duration-300 ${
|
||||
highlight
|
||||
? 'bg-gradient-to-br from-white to-slate-50 border-primary-200 hover:border-primary-300 hover:shadow-xl'
|
||||
: 'bg-white border-slate-200 hover:border-primary-200 hover:shadow-lg'
|
||||
? 'bg-gradient-to-br from-white dark:from-slate-800 to-slate-50 dark:to-slate-800/50 border-primary-200 dark:border-primary-800 hover:border-primary-300 dark:hover:border-primary-700 hover:shadow-xl'
|
||||
: 'bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700 hover:border-primary-200 dark:hover:border-primary-800 hover:shadow-lg'
|
||||
}`}
|
||||
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 shadow-lg`}>
|
||||
<Icon className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<h3 className={`${highlight ? 'text-2xl' : 'text-xl'} font-semibold text-slate-900 mb-2`}>{title}</h3>
|
||||
<p className="text-slate-600">{description}</p>
|
||||
<h3 className={`${highlight ? 'text-2xl' : 'text-xl'} font-semibold text-slate-900 dark:text-slate-100 mb-2`}>{title}</h3>
|
||||
<p className="text-slate-600 dark:text-slate-400">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,17 +35,17 @@ const steps = [
|
||||
|
||||
export function HowItWorks() {
|
||||
return (
|
||||
<section id="how-it-works" className="py-24 bg-white">
|
||||
<section id="how-it-works" className="py-24 bg-white dark:bg-slate-900">
|
||||
<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">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 dark:text-slate-100 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">
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
|
||||
Get started in minutes – no technical knowledge required.
|
||||
</p>
|
||||
</div>
|
||||
@@ -65,11 +65,11 @@ export function HowItWorks() {
|
||||
{/* 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>
|
||||
<ArrowDown className="w-8 h-8 text-primary-400 dark:text-primary-500 animate-bounce mb-4" />
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-2">Ready to get started?</p>
|
||||
<a
|
||||
href="/register"
|
||||
className="text-primary-600 font-semibold hover:text-primary-700 transition-colors"
|
||||
className="text-primary-600 dark:text-primary-400 font-semibold hover:text-primary-700 dark:hover:text-primary-300 transition-colors"
|
||||
>
|
||||
Try it free now →
|
||||
</a>
|
||||
@@ -91,20 +91,20 @@ 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">
|
||||
<div className="bg-slate-50 dark:bg-slate-800 rounded-2xl p-6 text-center hover:bg-white dark:hover:bg-slate-700 hover:shadow-xl transition-all duration-300 border border-transparent hover:border-slate-200 dark:hover:border-slate-600">
|
||||
{/* 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">
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-gradient-to-r from-primary-500 to-primary-600 dark:from-primary-600 dark:to-primary-700 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 className="w-16 h-16 mx-auto mt-4 mb-4 rounded-2xl bg-white dark:bg-slate-700 shadow-md flex items-center justify-center">
|
||||
<Icon className="w-8 h-8 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">{title}</h3>
|
||||
<p className="text-slate-600 text-sm">{description}</p>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">{title}</h3>
|
||||
<p className="text-slate-600 dark:text-slate-400 text-sm">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ export function Pricing() {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<section id="pricing" className="py-24 bg-slate-50">
|
||||
<section id="pricing" className="py-24 bg-slate-50 dark:bg-slate-900">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Section header */}
|
||||
<div className="text-center mb-16">
|
||||
@@ -72,10 +72,10 @@ export function Pricing() {
|
||||
<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">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">
|
||||
Simple, transparent pricing
|
||||
</h2>
|
||||
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
|
||||
Choose the plan that fits you. Cancel anytime, no hidden costs.
|
||||
</p>
|
||||
</div>
|
||||
@@ -93,11 +93,11 @@ export function Pricing() {
|
||||
|
||||
{/* FAQ teaser */}
|
||||
<div className="mt-16 text-center">
|
||||
<p className="text-slate-600">
|
||||
<p className="text-slate-600 dark:text-slate-400">
|
||||
Still have questions?{' '}
|
||||
<button
|
||||
onClick={() => document.getElementById('faq')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="text-primary-600 font-semibold hover:text-primary-700"
|
||||
className="text-primary-600 dark:text-primary-400 font-semibold hover:text-primary-700 dark:hover:text-primary-300"
|
||||
>
|
||||
Check our FAQ
|
||||
</button>
|
||||
@@ -131,15 +131,15 @@ function PricingCard({
|
||||
}: PricingCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={`relative bg-white rounded-2xl p-8 ${
|
||||
className={`relative bg-white dark:bg-slate-800 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'
|
||||
? 'ring-2 ring-primary-500 dark:ring-primary-400 shadow-xl scale-105'
|
||||
: 'border border-slate-200 dark:border-slate-700 hover:border-primary-200 dark:hover:border-primary-800 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">
|
||||
<Badge className="bg-primary-500 dark:bg-primary-600 text-white border-0 shadow-md">
|
||||
Most Popular
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -147,15 +147,15 @@ function PricingCard({
|
||||
|
||||
{/* 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>
|
||||
<h3 className="text-xl font-bold text-slate-900 dark:text-slate-100 mb-1">{name}</h3>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">{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>
|
||||
<span className="text-5xl font-extrabold text-slate-900 dark:text-slate-100">${price}</span>
|
||||
<span className="text-slate-500 dark:text-slate-400 ml-1">{period}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -164,15 +164,15 @@ function PricingCard({
|
||||
{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 className="w-5 h-5 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center flex-shrink-0">
|
||||
<Check className="w-3 h-3 text-green-600 dark:text-green-400" />
|
||||
</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 className="w-5 h-5 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center flex-shrink-0">
|
||||
<X className="w-3 h-3 text-slate-400 dark:text-slate-500" />
|
||||
</div>
|
||||
)}
|
||||
<span className={feature.included ? 'text-slate-700' : 'text-slate-400'}>
|
||||
<span className={feature.included ? 'text-slate-700 dark:text-slate-300' : 'text-slate-400 dark:text-slate-500'}>
|
||||
{feature.text}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -18,9 +18,19 @@ export { ID }
|
||||
export const auth = {
|
||||
// Create a new account
|
||||
async register(email: string, password: string, name?: string) {
|
||||
const user = await account.create(ID.unique(), email, password, name)
|
||||
await this.login(email, password)
|
||||
return user
|
||||
// #region agent log
|
||||
fetch('http://127.0.0.1:7242/ingest/4fa7412d-6f79-4871-8728-29c37c9e5772',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'appwrite.ts:20',message:'register called',data:{endpoint:APPWRITE_ENDPOINT,origin:window.location.origin,email},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
|
||||
// #endregion
|
||||
try {
|
||||
const user = await account.create(ID.unique(), email, password, name)
|
||||
await this.login(email, password)
|
||||
return user
|
||||
} catch (error) {
|
||||
// #region agent log
|
||||
fetch('http://127.0.0.1:7242/ingest/4fa7412d-6f79-4871-8728-29c37c9e5772',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'appwrite.ts:23',message:'register error',data:{errorMessage:error instanceof Error ? error.message : 'Unknown error'},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
|
||||
// #endregion
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// Login with email and password
|
||||
|
||||
@@ -29,22 +29,22 @@ export function ForgotPassword() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center p-4">
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
||||
<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 className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||
Email<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<Card className="shadow-xl border-0">
|
||||
<Card className="shadow-xl border-0 dark:bg-slate-800 dark:border-slate-700">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<CardTitle className="text-2xl">Passwort vergessen?</CardTitle>
|
||||
<CardDescription>
|
||||
<CardTitle className="text-2xl dark:text-slate-100">Passwort vergessen?</CardTitle>
|
||||
<CardDescription className="dark:text-slate-400">
|
||||
{sent
|
||||
? 'Prüfe dein E-Mail-Postfach'
|
||||
: 'Gib deine E-Mail-Adresse ein und wir senden dir einen Link zum Zurücksetzen.'
|
||||
@@ -54,14 +54,14 @@ export function ForgotPassword() {
|
||||
<CardContent>
|
||||
{sent ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-8 h-8 text-green-600" />
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||
<CheckCircle className="w-8 h-8 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<h3 className="font-semibold text-slate-900 mb-2">E-Mail gesendet!</h3>
|
||||
<p className="text-slate-600 mb-6">
|
||||
Wir haben dir eine E-Mail mit einem Link zum Zurücksetzen deines Passworts an <strong>{email}</strong> gesendet.
|
||||
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-2">E-Mail gesendet!</h3>
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-6">
|
||||
Wir haben dir eine E-Mail mit einem Link zum Zurücksetzen deines Passworts an <strong className="text-slate-900 dark:text-slate-100">{email}</strong> gesendet.
|
||||
</p>
|
||||
<p className="text-sm text-slate-500 mb-6">
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 mb-6">
|
||||
Keine E-Mail erhalten? Prüfe deinen Spam-Ordner oder versuche es erneut.
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
@@ -83,13 +83,13 @@ export function ForgotPassword() {
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{error && (
|
||||
<div className="p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
|
||||
<div className="p-3 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-300 text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">E-Mail-Adresse</Label>
|
||||
<Label htmlFor="email" className="dark:text-slate-200">E-Mail-Adresse</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
@@ -98,6 +98,7 @@ export function ForgotPassword() {
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
autoFocus
|
||||
className="dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -115,7 +116,7 @@ export function ForgotPassword() {
|
||||
<div className="text-center">
|
||||
<Link
|
||||
to="/login"
|
||||
className="text-sm text-primary-600 hover:text-primary-700"
|
||||
className="text-sm text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 inline mr-1" />
|
||||
Zurück zum Login
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ArrowLeft, Building2 } from 'lucide-react'
|
||||
|
||||
export function Imprint() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
<div className="min-h-screen bg-slate-50 dark:bg-slate-900">
|
||||
{/* Header */}
|
||||
<header className="bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-700">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
@@ -19,56 +19,56 @@ export function Imprint() {
|
||||
|
||||
{/* Content */}
|
||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-8 md:p-12">
|
||||
<div className="bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-8 md:p-12">
|
||||
{/* Title */}
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="w-12 h-12 rounded-lg bg-primary-100 flex items-center justify-center">
|
||||
<Building2 className="w-6 h-6 text-primary-600" />
|
||||
<div className="w-12 h-12 rounded-lg bg-primary-100 dark:bg-primary-900/30 flex items-center justify-center">
|
||||
<Building2 className="w-6 h-6 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-slate-900">Impressum</h1>
|
||||
<p className="text-slate-500 mt-1">Legal Information</p>
|
||||
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100">Impressum</h1>
|
||||
<p className="text-slate-500 dark:text-slate-400 mt-1">Legal Information</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content - Placeholder for webklar.com content */}
|
||||
<div className="prose prose-slate max-w-none">
|
||||
<p className="text-slate-600 mb-6">
|
||||
<div className="prose prose-slate max-w-none dark:prose-invert">
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-6">
|
||||
<strong>Note:</strong> This imprint is managed by webklar.com. Please refer to their imprint for detailed information.
|
||||
</p>
|
||||
|
||||
<div className="bg-slate-50 border border-slate-200 rounded-lg p-6 mb-8">
|
||||
<h2 className="text-xl font-semibold text-slate-900 mb-4">Information according to § 5 TMG</h2>
|
||||
<div className="bg-slate-50 dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700 rounded-lg p-6 mb-8">
|
||||
<h2 className="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-4">Information according to § 5 TMG</h2>
|
||||
|
||||
<div className="space-y-6 text-slate-700">
|
||||
<div className="space-y-6 text-slate-700 dark:text-slate-300">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Operator</h3>
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Operator</h3>
|
||||
<p className="mb-2">EmailSorter is operated by:</p>
|
||||
<p className="mb-4">
|
||||
<strong>webklar.com</strong><br />
|
||||
Kenso Grimm, Justin Klein
|
||||
</p>
|
||||
<p className="text-sm text-slate-600 mb-4">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400 mb-4">
|
||||
For complete contact details and legal information, please visit:{' '}
|
||||
<a
|
||||
href="https://webklar.com/impressum"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
webklar.com/impressum
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-slate-200">
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Contact</h3>
|
||||
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Contact</h3>
|
||||
<div className="space-y-2">
|
||||
<p>
|
||||
<strong>Email:</strong>{' '}
|
||||
<a
|
||||
href="mailto:support@webklar.com"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
support@webklar.com
|
||||
</a>
|
||||
@@ -77,23 +77,23 @@ export function Imprint() {
|
||||
<strong>Phone:</strong>{' '}
|
||||
<a
|
||||
href="tel:+4917623726355"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
+49 176 23726355
|
||||
</a>
|
||||
{' / '}
|
||||
<a
|
||||
href="tel:+491704969375"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
+49 170 4969375
|
||||
</a>
|
||||
</p>
|
||||
<p className="mt-4 text-sm text-slate-600">
|
||||
<p className="mt-4 text-sm text-slate-600 dark:text-slate-400">
|
||||
For questions regarding EmailSorter specifically:{' '}
|
||||
<a
|
||||
href="mailto:support@emailsorter.com"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
support@emailsorter.com
|
||||
</a>
|
||||
@@ -101,8 +101,8 @@ export function Imprint() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-slate-200">
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Responsible for Content</h3>
|
||||
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Responsible for Content</h3>
|
||||
<p>
|
||||
The content of this website is the responsibility of webklar.com.
|
||||
For detailed information, please refer to the official imprint at{' '}
|
||||
@@ -110,23 +110,23 @@ export function Imprint() {
|
||||
href="https://webklar.com/impressum"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
webklar.com/impressum
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-slate-200">
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Liability for Links</h3>
|
||||
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Liability for Links</h3>
|
||||
<p>
|
||||
Our website contains links to external websites. We have no influence on the content of these websites.
|
||||
Therefore, we cannot assume any liability for these external contents.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-slate-200">
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Copyright</h3>
|
||||
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Copyright</h3>
|
||||
<p>
|
||||
The content and works on this website are subject to German copyright law.
|
||||
Reproduction, processing, distribution, and any form of commercialization require the written consent of the respective author or creator.
|
||||
@@ -134,14 +134,14 @@ export function Imprint() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 pt-6 border-t border-slate-200">
|
||||
<p className="text-sm text-slate-500">
|
||||
<div className="mt-8 pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">
|
||||
<strong>Important:</strong> This is a simplified version. For the complete and legally binding imprint, please visit{' '}
|
||||
<a
|
||||
href="https://webklar.com/impressum"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary-600 hover:text-primary-700 underline"
|
||||
className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline"
|
||||
>
|
||||
webklar.com/impressum
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ArrowLeft, Shield } from 'lucide-react'
|
||||
|
||||
export function Privacy() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
<div className="min-h-screen bg-slate-50 dark:bg-slate-900">
|
||||
{/* Header */}
|
||||
<header className="bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-700">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
@@ -19,81 +19,81 @@ export function Privacy() {
|
||||
|
||||
{/* Content */}
|
||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-8 md:p-12">
|
||||
<div className="bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-8 md:p-12">
|
||||
{/* Title */}
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="w-12 h-12 rounded-lg bg-primary-100 flex items-center justify-center">
|
||||
<Shield className="w-6 h-6 text-primary-600" />
|
||||
<div className="w-12 h-12 rounded-lg bg-primary-100 dark:bg-primary-900/30 flex items-center justify-center">
|
||||
<Shield className="w-6 h-6 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-slate-900">Privacy Policy</h1>
|
||||
<p className="text-slate-500 mt-1">Last updated: {new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</p>
|
||||
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100">Privacy Policy</h1>
|
||||
<p className="text-slate-500 dark:text-slate-400 mt-1">Last updated: {new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content - Placeholder for webklar.com content */}
|
||||
<div className="prose prose-slate max-w-none">
|
||||
<p className="text-slate-600 mb-6">
|
||||
<div className="prose prose-slate max-w-none dark:prose-invert">
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-6">
|
||||
<strong>Note:</strong> This privacy policy is managed by webklar.com. Please refer to their privacy policy for detailed information.
|
||||
</p>
|
||||
|
||||
<div className="bg-slate-50 border border-slate-200 rounded-lg p-6 mb-8">
|
||||
<h2 className="text-xl font-semibold text-slate-900 mb-4">Data Protection Information</h2>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<div className="bg-slate-50 dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700 rounded-lg p-6 mb-8">
|
||||
<h2 className="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-4">Data Protection Information</h2>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
EmailSorter is operated by webklar.com. The following privacy policy applies to the use of this website and our services.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">1. Responsible Party</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">1. Responsible Party</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
The responsible party for data processing on this website is:
|
||||
</p>
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
||||
<p className="text-slate-700 mb-2">
|
||||
<div className="bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg p-4 mb-4">
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-2">
|
||||
<strong>webklar.com</strong><br />
|
||||
Kenso Grimm, Justin Klein
|
||||
</p>
|
||||
<p className="text-slate-700 mb-2">
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-2">
|
||||
<strong>Contact:</strong><br />
|
||||
Email: <a href="mailto:support@webklar.com" className="text-primary-600 hover:text-primary-700 underline">support@webklar.com</a><br />
|
||||
Phone: <a href="tel:+4917623726355" className="text-primary-600 hover:text-primary-700 underline">+49 176 23726355</a>
|
||||
Email: <a href="mailto:support@webklar.com" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">support@webklar.com</a><br />
|
||||
Phone: <a href="tel:+4917623726355" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">+49 176 23726355</a>
|
||||
</p>
|
||||
<p className="text-sm text-slate-600 mt-3">
|
||||
For complete contact details, please refer to the <Link to="/imprint" className="text-primary-600 hover:text-primary-700 underline">Impressum</Link>.
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400 mt-3">
|
||||
For complete contact details, please refer to the <Link to="/imprint" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">Impressum</Link>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">2. Data Collection and Processing</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">2. Data Collection and Processing</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
When you use EmailSorter, we collect and process the following data:
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-slate-700 mb-4 space-y-2 ml-4">
|
||||
<ul className="list-disc list-inside text-slate-700 dark:text-slate-300 mb-4 space-y-2 ml-4">
|
||||
<li>Account information (email address, name)</li>
|
||||
<li>Email metadata (sender, subject, date) for sorting purposes</li>
|
||||
<li>Usage statistics and preferences</li>
|
||||
<li>Payment information (processed securely via Stripe)</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">3. Purpose of Data Processing</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">3. Purpose of Data Processing</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
We process your data exclusively for the following purposes:
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-slate-700 mb-4 space-y-2 ml-4">
|
||||
<ul className="list-disc list-inside text-slate-700 dark:text-slate-300 mb-4 space-y-2 ml-4">
|
||||
<li>Providing and improving the EmailSorter service</li>
|
||||
<li>Automated email sorting and categorization</li>
|
||||
<li>Processing payments and subscriptions</li>
|
||||
<li>Customer support and communication</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">4. Data Security</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">4. Data Security</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
We implement appropriate technical and organizational measures to protect your data against unauthorized access, loss, or destruction.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">5. Your Rights</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">5. Your Rights</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
You have the right to:
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-slate-700 mb-4 space-y-2 ml-4">
|
||||
<ul className="list-disc list-inside text-slate-700 dark:text-slate-300 mb-4 space-y-2 ml-4">
|
||||
<li>Access your personal data</li>
|
||||
<li>Correct inaccurate data</li>
|
||||
<li>Request deletion of your data</li>
|
||||
@@ -101,14 +101,14 @@ export function Privacy() {
|
||||
<li>Data portability</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">6. Hosting and Third-Party Services</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">6. Hosting and Third-Party Services</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
<strong>Hosting:</strong> Our website is hosted by Netlify, which acts as a data processor.
|
||||
</p>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
We use the following third-party services:
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-slate-700 mb-4 space-y-2 ml-4">
|
||||
<ul className="list-disc list-inside text-slate-700 dark:text-slate-300 mb-4 space-y-2 ml-4">
|
||||
<li><strong>Appwrite:</strong> User authentication and database</li>
|
||||
<li><strong>Stripe:</strong> Payment processing</li>
|
||||
<li><strong>Mistral AI:</strong> Email categorization</li>
|
||||
@@ -116,45 +116,45 @@ export function Privacy() {
|
||||
<li><strong>Plausible (optional):</strong> Privacy-friendly analytics tool, if enabled</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">6.1. Cookies and Tracking</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">6.1. Cookies and Tracking</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
We do not use external fonts or unnecessary cookies. If we use any tracking tools (such as Plausible),
|
||||
they are privacy-friendly and do not store personal data. We only process personal data to the extent
|
||||
that it is technically or organizationally necessary.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">7. Contact Form Data</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">7. Contact Form Data</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
Data that you send to us via contact forms will be stored and used for processing your inquiry.
|
||||
This data will not be shared with third parties without your consent.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">8. Contact</h3>
|
||||
<p className="text-slate-700 mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mt-6 mb-3">8. Contact</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||
For questions regarding data protection, please contact us:
|
||||
</p>
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
||||
<p className="text-slate-700">
|
||||
<div className="bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg p-4 mb-4">
|
||||
<p className="text-slate-700 dark:text-slate-300">
|
||||
<strong>Email:</strong>{' '}
|
||||
<a href="mailto:support@webklar.com" className="text-primary-600 hover:text-primary-700 underline">
|
||||
<a href="mailto:support@webklar.com" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">
|
||||
support@webklar.com
|
||||
</a>
|
||||
</p>
|
||||
<p className="text-slate-700 mt-2">
|
||||
<p className="text-slate-700 dark:text-slate-300 mt-2">
|
||||
<strong>Phone:</strong>{' '}
|
||||
<a href="tel:+4917623726355" className="text-primary-600 hover:text-primary-700 underline">
|
||||
<a href="tel:+4917623726355" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">
|
||||
+49 176 23726355
|
||||
</a>
|
||||
</p>
|
||||
<p className="text-sm text-slate-600 mt-3">
|
||||
For complete contact details, please refer to the <Link to="/imprint" className="text-primary-600 hover:text-primary-700 underline">Impressum</Link>.
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400 mt-3">
|
||||
For complete contact details, please refer to the <Link to="/imprint" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">Impressum</Link>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 pt-6 border-t border-slate-200">
|
||||
<p className="text-sm text-slate-500">
|
||||
<div className="mt-8 pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">
|
||||
<strong>Important:</strong> This is a simplified version. For the complete and legally binding privacy policy, please visit{' '}
|
||||
<a href="https://webklar.com/datenschutz" target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-700 underline">
|
||||
<a href="https://webklar.com/datenschutz" target="_blank" rel="noopener noreferrer" className="text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300 underline">
|
||||
webklar.com/datenschutz
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -114,30 +114,30 @@ export function Register() {
|
||||
</div>
|
||||
|
||||
{/* Right side - Form */}
|
||||
<div className="flex-1 flex items-center justify-center px-4 sm:px-6 lg:px-8 bg-white">
|
||||
<div className="flex-1 flex items-center justify-center px-4 sm:px-6 lg:px-8 bg-white dark:bg-slate-900">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center gap-2 mb-8">
|
||||
<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">
|
||||
E-Mail-<span className="text-primary-600">Sorter</span>
|
||||
<span className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||
E-Mail-<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<h1 className="text-3xl font-bold text-slate-900 mb-2">
|
||||
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100 mb-2">
|
||||
Create account
|
||||
</h1>
|
||||
<p className="text-slate-600 mb-8">
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-8">
|
||||
Ready to go in less than a minute.
|
||||
</p>
|
||||
|
||||
{/* Error message */}
|
||||
{error && (
|
||||
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-xl flex items-start gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-sm text-red-600">{error}</p>
|
||||
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-xl flex items-start gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-red-500 dark:text-red-400 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-sm text-red-600 dark:text-red-300">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -217,16 +217,16 @@ export function Register() {
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<p className="text-xs text-slate-500 text-center">
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 text-center">
|
||||
By signing up, you agree to our{' '}
|
||||
<a href="#" className="text-primary-600 hover:underline">Terms of Service</a> and{' '}
|
||||
<a href="#" className="text-primary-600 hover:underline">Privacy Policy</a>.
|
||||
<a href="#" className="text-primary-600 dark:text-primary-400 hover:underline">Terms of Service</a> and{' '}
|
||||
<a href="#" className="text-primary-600 dark:text-primary-400 hover:underline">Privacy Policy</a>.
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<p className="mt-8 text-center text-slate-600">
|
||||
<p className="mt-8 text-center text-slate-600 dark:text-slate-400">
|
||||
Already have an account?{' '}
|
||||
<Link to="/login" className="text-primary-600 font-semibold hover:text-primary-700">
|
||||
<Link to="/login" className="text-primary-600 dark:text-primary-400 font-semibold hover:text-primary-700 dark:hover:text-primary-300">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -83,24 +83,24 @@ export function ResetPassword() {
|
||||
const passwordStrength = getPasswordStrength()
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center p-4">
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
||||
<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 className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||
Email<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<Card className="shadow-xl border-0">
|
||||
<Card className="shadow-xl border-0 dark:bg-slate-800 dark:border-slate-700">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<CardTitle className="text-2xl">
|
||||
<CardTitle className="text-2xl dark:text-slate-100">
|
||||
{success ? 'Passwort geändert!' : 'Neues Passwort festlegen'}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
<CardDescription className="dark:text-slate-400">
|
||||
{success
|
||||
? 'Dein Passwort wurde erfolgreich geändert.'
|
||||
: 'Wähle ein sicheres neues Passwort für deinen Account.'
|
||||
@@ -110,10 +110,10 @@ export function ResetPassword() {
|
||||
<CardContent>
|
||||
{success ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-8 h-8 text-green-600" />
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||
<CheckCircle className="w-8 h-8 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<p className="text-slate-600 mb-6">
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-6">
|
||||
Du kannst dich jetzt mit deinem neuen Passwort anmelden.
|
||||
</p>
|
||||
<Button onClick={() => navigate('/login')} className="w-full">
|
||||
@@ -122,11 +122,11 @@ export function ResetPassword() {
|
||||
</div>
|
||||
) : !userId || !secret ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-red-100 flex items-center justify-center">
|
||||
<XCircle className="w-8 h-8 text-red-600" />
|
||||
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<XCircle className="w-8 h-8 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
<h3 className="font-semibold text-slate-900 mb-2">Ungültiger Link</h3>
|
||||
<p className="text-slate-600 mb-6">
|
||||
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-2">Ungültiger Link</h3>
|
||||
<p className="text-slate-600 dark:text-slate-400 mb-6">
|
||||
Dieser Link zum Zurücksetzen des Passworts ist ungültig oder abgelaufen.
|
||||
</p>
|
||||
<Link to="/forgot-password">
|
||||
@@ -136,13 +136,13 @@ export function ResetPassword() {
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{error && (
|
||||
<div className="p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
|
||||
<div className="p-3 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-300 text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Neues Passwort</Label>
|
||||
<Label htmlFor="password" className="dark:text-slate-200">Neues Passwort</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="password"
|
||||
@@ -152,11 +152,12 @@ export function ResetPassword() {
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
autoFocus
|
||||
className="dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 dark:text-slate-500 hover:text-slate-600 dark:hover:text-slate-300"
|
||||
>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
@@ -172,13 +173,13 @@ export function ResetPassword() {
|
||||
className={`h-1 flex-1 rounded-full transition-colors ${
|
||||
level <= passwordStrength.strength
|
||||
? passwordStrength.color
|
||||
: 'bg-slate-200'
|
||||
: 'bg-slate-200 dark:bg-slate-700'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className={`text-xs ${
|
||||
passwordStrength.strength < 3 ? 'text-red-500' : 'text-green-600'
|
||||
passwordStrength.strength < 3 ? 'text-red-500 dark:text-red-400' : 'text-green-600 dark:text-green-400'
|
||||
}`}>
|
||||
{passwordStrength.label}
|
||||
</p>
|
||||
@@ -187,7 +188,7 @@ export function ResetPassword() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirmPassword">Passwort bestätigen</Label>
|
||||
<Label htmlFor="confirmPassword" className="dark:text-slate-200">Passwort bestätigen</Label>
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
@@ -195,9 +196,10 @@ export function ResetPassword() {
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
className="dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
|
||||
/>
|
||||
{confirmPassword && password !== confirmPassword && (
|
||||
<p className="text-xs text-red-500">Passwörter stimmen nicht überein</p>
|
||||
<p className="text-xs text-red-500 dark:text-red-400">Passwörter stimmen nicht überein</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -283,17 +283,17 @@ export function Setup() {
|
||||
// Show loading while checking accounts
|
||||
if (checkingAccounts) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-white flex items-center justify-center">
|
||||
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-white dark:from-slate-900 dark:to-slate-800 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-primary-600 mx-auto mb-4" />
|
||||
<p className="text-slate-600">Setting up your account...</p>
|
||||
<Loader2 className="w-8 h-8 animate-spin text-primary-600 dark:text-primary-400 mx-auto mb-4" />
|
||||
<p className="text-slate-600 dark:text-slate-400">Setting up your account...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-white">
|
||||
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-white dark:from-slate-900 dark:to-slate-800">
|
||||
<header className="bg-white/80 dark:bg-slate-900/80 backdrop-blur-sm border-b border-slate-200 dark:border-slate-700 sticky top-0 z-40">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -315,13 +315,13 @@ export function Setup() {
|
||||
{/* Success message after checkout */}
|
||||
{isFromCheckout && (
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pt-8">
|
||||
<div className="bg-green-50 border border-green-200 rounded-xl p-6 mb-6 flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-full bg-green-500 flex items-center justify-center flex-shrink-0">
|
||||
<div className="bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-xl p-6 mb-6 flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-full bg-green-500 dark:bg-green-600 flex items-center justify-center flex-shrink-0">
|
||||
<Check className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-green-900 mb-1">Payment successful!</h3>
|
||||
<p className="text-sm text-green-700">
|
||||
<h3 className="font-semibold text-green-900 dark:text-green-200 mb-1">Payment successful!</h3>
|
||||
<p className="text-sm text-green-700 dark:text-green-300">
|
||||
Your subscription is active. Let's connect your email account to get started.
|
||||
</p>
|
||||
</div>
|
||||
@@ -351,23 +351,23 @@ export function Setup() {
|
||||
<div
|
||||
className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold transition-all duration-300 ${
|
||||
index < stepIndex
|
||||
? 'bg-green-500 text-white shadow-lg shadow-green-500/30'
|
||||
? 'bg-green-500 dark:bg-green-600 text-white shadow-lg shadow-green-500/30'
|
||||
: index === stepIndex
|
||||
? 'bg-primary-500 text-white ring-4 ring-primary-100 shadow-lg shadow-primary-500/30'
|
||||
: 'bg-slate-100 text-slate-400'
|
||||
? 'bg-primary-500 dark:bg-primary-600 text-white ring-4 ring-primary-100 dark:ring-primary-900/50 shadow-lg shadow-primary-500/30'
|
||||
: 'bg-slate-100 dark:bg-slate-700 text-slate-400 dark:text-slate-500'
|
||||
}`}
|
||||
>
|
||||
{index < stepIndex ? <Check className="w-5 h-5" /> : index + 1}
|
||||
</div>
|
||||
<p className={`mt-2 text-xs font-medium hidden sm:block transition-colors ${
|
||||
index <= stepIndex ? 'text-slate-900' : 'text-slate-400'
|
||||
index <= stepIndex ? 'text-slate-900 dark:text-slate-100' : 'text-slate-400 dark:text-slate-500'
|
||||
}`}>
|
||||
{step.title}
|
||||
</p>
|
||||
</div>
|
||||
{index < steps.length - 1 && (
|
||||
<div className={`w-16 sm:w-24 h-1 mx-2 rounded-full transition-colors duration-500 ${
|
||||
index < stepIndex ? 'bg-green-500' : 'bg-slate-200'
|
||||
index < stepIndex ? 'bg-green-500 dark:bg-green-600' : 'bg-slate-200 dark:bg-slate-700'
|
||||
}`} />
|
||||
)}
|
||||
</div>
|
||||
@@ -376,7 +376,7 @@ export function Setup() {
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-xl flex items-center gap-3 text-red-700">
|
||||
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-xl flex items-center gap-3 text-red-700 dark:text-red-300">
|
||||
<AlertCircle className="w-5 h-5 flex-shrink-0" />
|
||||
<p>{error}</p>
|
||||
</div>
|
||||
@@ -388,8 +388,8 @@ export function Setup() {
|
||||
<div className="w-24 h-24 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-primary-100 to-primary-200 flex items-center justify-center shadow-xl shadow-primary-500/10">
|
||||
<Link2 className="w-12 h-12 text-primary-600" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-slate-900 mb-3">Connect your email account</h1>
|
||||
<p className="text-lg text-slate-600 mb-10 max-w-md mx-auto">
|
||||
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100 mb-3">Connect your email account</h1>
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400 mb-10 max-w-md mx-auto">
|
||||
Choose your email provider. The connection is secure and your data stays private.
|
||||
</p>
|
||||
|
||||
@@ -416,10 +416,10 @@ export function Setup() {
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-slate-300"></div>
|
||||
<div className="w-full border-t border-slate-300 dark:border-slate-600"></div>
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="px-4 bg-white text-slate-500">Or connect your inbox</span>
|
||||
<span className="px-4 bg-white dark:bg-slate-800 text-slate-500 dark:text-slate-400">Or connect your inbox</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -427,12 +427,12 @@ export function Setup() {
|
||||
<button
|
||||
onClick={handleConnectGmail}
|
||||
disabled={connecting !== null}
|
||||
className="flex items-center gap-4 p-6 bg-white rounded-2xl border-2 border-slate-200 hover:border-red-300 hover:shadow-xl hover:shadow-red-500/10 transition-all text-left group disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="flex items-center gap-4 p-6 bg-white dark:bg-slate-800 rounded-2xl border-2 border-slate-200 dark:border-slate-700 hover:border-red-300 dark:hover:border-red-600 hover:shadow-xl hover:shadow-red-500/10 transition-all text-left group disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{connecting === 'gmail' ? (
|
||||
<Loader2 className="w-12 h-12 animate-spin text-red-500" />
|
||||
<Loader2 className="w-12 h-12 animate-spin text-red-500 dark:text-red-400" />
|
||||
) : (
|
||||
<div className="w-12 h-12 rounded-xl bg-red-50 flex items-center justify-center group-hover:bg-red-100 transition-colors">
|
||||
<div className="w-12 h-12 rounded-xl bg-red-50 dark:bg-red-900/30 flex items-center justify-center group-hover:bg-red-100 dark:group-hover:bg-red-900/50 transition-colors">
|
||||
<svg viewBox="0 0 24 24" className="w-7 h-7">
|
||||
<path fill="#EA4335" d="M5.26 9.71L12 14.04l6.74-4.33-6.74-4.33z"/>
|
||||
<path fill="#34A853" d="M12 14.04l6.74-4.33v7.65c0 .7-.57 1.26-1.26 1.26H6.52c-.7 0-1.26-.57-1.26-1.26V9.71l6.74 4.33z"/>
|
||||
@@ -442,37 +442,37 @@ export function Setup() {
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-slate-900">Gmail</p>
|
||||
<p className="text-sm text-slate-500">Google Workspace</p>
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">Gmail</p>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">Google Workspace</p>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-slate-400 group-hover:text-red-500 group-hover:translate-x-1 transition-all" />
|
||||
<ChevronRight className="w-5 h-5 text-slate-400 dark:text-slate-500 group-hover:text-red-500 dark:group-hover:text-red-400 group-hover:translate-x-1 transition-all" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleConnectOutlook}
|
||||
disabled={connecting !== null}
|
||||
className="flex items-center gap-4 p-6 bg-white rounded-2xl border-2 border-slate-200 hover:border-blue-300 hover:shadow-xl hover:shadow-blue-500/10 transition-all text-left group disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="flex items-center gap-4 p-6 bg-white dark:bg-slate-800 rounded-2xl border-2 border-slate-200 dark:border-slate-700 hover:border-blue-300 dark:hover:border-blue-600 hover:shadow-xl hover:shadow-blue-500/10 transition-all text-left group disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{connecting === 'outlook' ? (
|
||||
<Loader2 className="w-12 h-12 animate-spin text-blue-500" />
|
||||
<Loader2 className="w-12 h-12 animate-spin text-blue-500 dark:text-blue-400" />
|
||||
) : (
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-50 flex items-center justify-center group-hover:bg-blue-100 transition-colors">
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-50 dark:bg-blue-900/30 flex items-center justify-center group-hover:bg-blue-100 dark:group-hover:bg-blue-900/50 transition-colors">
|
||||
<svg viewBox="0 0 24 24" className="w-7 h-7">
|
||||
<path fill="#0078D4" d="M11.5 3v8.5H3V3h8.5zm1 0H21v8.5h-8.5V3zM3 12.5h8.5V21H3v-8.5zm9.5 0H21V21h-8.5v-8.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-slate-900">Outlook</p>
|
||||
<p className="text-sm text-slate-500">Microsoft 365</p>
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">Outlook</p>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">Microsoft 365</p>
|
||||
</div>
|
||||
<ChevronRight className="w-5 h-5 text-slate-400 group-hover:text-blue-500 group-hover:translate-x-1 transition-all" />
|
||||
<ChevronRight className="w-5 h-5 text-slate-400 dark:text-slate-500 group-hover:text-blue-500 dark:group-hover:text-blue-400 group-hover:translate-x-1 transition-all" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 p-4 bg-slate-50 rounded-xl max-w-lg mx-auto">
|
||||
<p className="text-sm text-slate-500">
|
||||
<div className="mt-10 p-4 bg-slate-50 dark:bg-slate-800/50 rounded-xl max-w-lg mx-auto">
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">
|
||||
🔒 Your data is secure. We don't store email content and only have read access.
|
||||
</p>
|
||||
</div>
|
||||
@@ -485,16 +485,16 @@ export function Setup() {
|
||||
<div className="w-24 h-24 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-primary-100 to-primary-200 flex items-center justify-center shadow-xl shadow-primary-500/10">
|
||||
<Settings className="w-12 h-12 text-primary-600" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-slate-900 mb-3">Sorting Settings</h1>
|
||||
<p className="text-lg text-slate-600 max-w-md mx-auto">
|
||||
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100 mb-3">Sorting Settings</h1>
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400 max-w-md mx-auto">
|
||||
Customize how strictly the AI should sort your emails.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card className="max-w-lg mx-auto shadow-xl border-0">
|
||||
<Card className="max-w-lg mx-auto shadow-xl border-0 dark:bg-slate-800 dark:border-slate-700">
|
||||
<CardContent className="p-8 space-y-8">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-slate-900 mb-4">Sorting Intensity</label>
|
||||
<label className="block text-sm font-semibold text-slate-900 dark:text-slate-100 mb-4">Sorting Intensity</label>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[
|
||||
{ id: 'light', name: 'Light', desc: 'Only obvious distractions', emoji: '🌱' },
|
||||
@@ -506,30 +506,30 @@ export function Setup() {
|
||||
onClick={() => setPreferences(p => ({ ...p, sortingStrictness: option.id }))}
|
||||
className={`p-4 rounded-xl border-2 text-center transition-all ${
|
||||
preferences.sortingStrictness === option.id
|
||||
? 'border-primary-500 bg-primary-50 shadow-lg shadow-primary-500/10'
|
||||
: 'border-slate-200 hover:border-slate-300 bg-white'
|
||||
? 'border-primary-500 dark:border-primary-400 bg-primary-50 dark:bg-primary-900/30 shadow-lg shadow-primary-500/10'
|
||||
: 'border-slate-200 dark:border-slate-700 hover:border-slate-300 dark:hover:border-slate-600 bg-white dark:bg-slate-800'
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl mb-2 block">{option.emoji}</span>
|
||||
<p className="font-semibold text-slate-900">{option.name}</p>
|
||||
<p className="text-xs text-slate-500 mt-1">{option.desc}</p>
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">{option.name}</p>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1">{option.desc}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-5 bg-gradient-to-r from-slate-50 to-slate-100 rounded-xl">
|
||||
<div className="flex items-center justify-between p-5 bg-gradient-to-r from-slate-50 to-slate-100 dark:from-slate-800 dark:to-slate-700 rounded-xl">
|
||||
<div>
|
||||
<p className="font-semibold text-slate-900">Historical emails</p>
|
||||
<p className="text-sm text-slate-500">Analyze and sort last 30 days</p>
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">Historical emails</p>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">Analyze and sort last 30 days</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setPreferences(p => ({ ...p, historicalSync: !p.historicalSync }))}
|
||||
className={`w-14 h-8 rounded-full transition-all duration-300 ${
|
||||
preferences.historicalSync ? 'bg-primary-500 shadow-lg shadow-primary-500/30' : 'bg-slate-300'
|
||||
preferences.historicalSync ? 'bg-primary-500 dark:bg-primary-600 shadow-lg shadow-primary-500/30' : 'bg-slate-300 dark:bg-slate-600'
|
||||
}`}
|
||||
>
|
||||
<div className={`w-6 h-6 bg-white rounded-full shadow-md transition-transform duration-300 ${
|
||||
<div className={`w-6 h-6 bg-white dark:bg-slate-200 rounded-full shadow-md transition-transform duration-300 ${
|
||||
preferences.historicalSync ? 'translate-x-7' : 'translate-x-1'
|
||||
}`} />
|
||||
</button>
|
||||
@@ -545,8 +545,8 @@ export function Setup() {
|
||||
<div className="w-24 h-24 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-primary-100 to-primary-200 flex items-center justify-center shadow-xl shadow-primary-500/10">
|
||||
<Zap className="w-12 h-12 text-primary-600" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-slate-900 mb-3">Choose your categories</h1>
|
||||
<p className="text-lg text-slate-600 max-w-md mx-auto">
|
||||
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100 mb-3">Choose your categories</h1>
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400 max-w-md mx-auto">
|
||||
Which categories should your emails be sorted into?
|
||||
</p>
|
||||
</div>
|
||||
@@ -558,21 +558,21 @@ export function Setup() {
|
||||
onClick={() => toggleCategory(category.id)}
|
||||
className={`flex items-center gap-4 p-5 rounded-xl border-2 text-left transition-all ${
|
||||
selectedCategories.includes(category.id)
|
||||
? 'border-primary-500 bg-primary-50 shadow-lg shadow-primary-500/10'
|
||||
: 'border-slate-200 bg-white hover:border-slate-300 hover:shadow-md'
|
||||
? 'border-primary-500 dark:border-primary-400 bg-primary-50 dark:bg-primary-900/30 shadow-lg shadow-primary-500/10'
|
||||
: 'border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 hover:border-slate-300 dark:hover:border-slate-600 hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<div className={`w-12 h-12 rounded-xl ${category.color} flex items-center justify-center text-2xl shadow-lg`}>
|
||||
{category.icon}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-slate-900">{category.name}</p>
|
||||
<p className="text-sm text-slate-500">{category.description}</p>
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">{category.name}</p>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">{category.description}</p>
|
||||
</div>
|
||||
<div className={`w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all ${
|
||||
selectedCategories.includes(category.id)
|
||||
? 'border-primary-500 bg-primary-500'
|
||||
: 'border-slate-300'
|
||||
? 'border-primary-500 dark:border-primary-400 bg-primary-500 dark:bg-primary-600'
|
||||
: 'border-slate-300 dark:border-slate-600'
|
||||
}`}>
|
||||
{selectedCategories.includes(category.id) && <Check className="w-4 h-4 text-white" />}
|
||||
</div>
|
||||
@@ -580,7 +580,7 @@ export function Setup() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="text-center text-sm text-slate-500 mt-6">
|
||||
<p className="text-center text-sm text-slate-500 dark:text-slate-400 mt-6">
|
||||
You can change these categories later in settings.
|
||||
</p>
|
||||
</div>
|
||||
@@ -591,20 +591,20 @@ export function Setup() {
|
||||
<div className="w-28 h-28 mx-auto mb-8 rounded-full bg-gradient-to-br from-green-100 to-green-200 flex items-center justify-center shadow-2xl shadow-green-500/20 animate-pulse">
|
||||
<Sparkles className="w-14 h-14 text-green-600" />
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-slate-900 mb-4">All set! 🎉</h1>
|
||||
<p className="text-xl text-slate-600 mb-10 max-w-md mx-auto">
|
||||
<h1 className="text-4xl font-bold text-slate-900 dark:text-slate-100 mb-4">All set! 🎉</h1>
|
||||
<p className="text-xl text-slate-600 dark:text-slate-400 mb-10 max-w-md mx-auto">
|
||||
Your email account is connected. The AI will now start intelligent sorting.
|
||||
</p>
|
||||
|
||||
<div className="inline-flex items-center gap-4 p-5 bg-gradient-to-r from-slate-50 to-slate-100 rounded-2xl mb-10 shadow-lg">
|
||||
<div className="w-14 h-14 rounded-xl bg-white flex items-center justify-center shadow-md">
|
||||
<Mail className="w-7 h-7 text-primary-500" />
|
||||
<div className="inline-flex items-center gap-4 p-5 bg-gradient-to-r from-slate-50 to-slate-100 dark:from-slate-800 dark:to-slate-700 rounded-2xl mb-10 shadow-lg">
|
||||
<div className="w-14 h-14 rounded-xl bg-white dark:bg-slate-700 flex items-center justify-center shadow-md">
|
||||
<Mail className="w-7 h-7 text-primary-500 dark:text-primary-400" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="font-semibold text-slate-900 text-lg">
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100 text-lg">
|
||||
{connectedProvider === 'gmail' ? 'Gmail' : connectedProvider === 'outlook' ? 'Outlook' : 'Email'} connected
|
||||
</p>
|
||||
<p className="text-slate-500">{connectedEmail || user?.email}</p>
|
||||
<p className="text-slate-500 dark:text-slate-400">{connectedEmail || user?.email}</p>
|
||||
</div>
|
||||
<Badge variant="success" className="text-sm px-3 py-1">Active</Badge>
|
||||
</div>
|
||||
@@ -628,7 +628,7 @@ export function Setup() {
|
||||
|
||||
{currentStep !== 'connect' && currentStep !== 'complete' && (
|
||||
<div className="flex justify-between max-w-lg mx-auto">
|
||||
<Button variant="ghost" onClick={handleBack} className="text-slate-600">
|
||||
<Button variant="ghost" onClick={handleBack} className="text-slate-600 dark:text-slate-400">
|
||||
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||
Back
|
||||
</Button>
|
||||
|
||||
@@ -51,26 +51,26 @@ export function VerifyEmail() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center p-4">
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
||||
<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 className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||
Email<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<Card className="shadow-xl border-0">
|
||||
<Card className="shadow-xl border-0 dark:bg-slate-800 dark:border-slate-700">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<CardTitle className="text-2xl">
|
||||
<CardTitle className="text-2xl dark:text-slate-100">
|
||||
{status === 'loading' && 'E-Mail wird verifiziert...'}
|
||||
{status === 'success' && 'E-Mail verifiziert!'}
|
||||
{status === 'error' && 'Verifizierung fehlgeschlagen'}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
<CardDescription className="dark:text-slate-400">
|
||||
{status === 'loading' && 'Bitte warte einen Moment.'}
|
||||
{status === 'success' && 'Deine E-Mail-Adresse wurde erfolgreich bestätigt.'}
|
||||
{status === 'error' && error}
|
||||
@@ -79,25 +79,25 @@ export function VerifyEmail() {
|
||||
<CardContent>
|
||||
{status === 'loading' && (
|
||||
<div className="flex flex-col items-center py-12">
|
||||
<Loader2 className="w-12 h-12 animate-spin text-primary-500 mb-4" />
|
||||
<p className="text-slate-500">Verifizierung läuft...</p>
|
||||
<Loader2 className="w-12 h-12 animate-spin text-primary-500 dark:text-primary-400 mb-4" />
|
||||
<p className="text-slate-500 dark:text-slate-400">Verifizierung läuft...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'success' && (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-20 h-20 mx-auto mb-6 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-10 h-10 text-green-600" />
|
||||
<div className="w-20 h-20 mx-auto mb-6 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||
<CheckCircle className="w-10 h-10 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-green-50 border border-green-100 rounded-xl">
|
||||
<p className="text-green-700 font-medium">
|
||||
<div className="p-4 bg-green-50 dark:bg-green-900/30 border border-green-100 dark:border-green-800 rounded-xl">
|
||||
<p className="text-green-700 dark:text-green-300 font-medium">
|
||||
Dein Account ist jetzt vollständig aktiviert!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-slate-600">
|
||||
<p className="text-slate-600 dark:text-slate-400">
|
||||
Du kannst jetzt alle Features von EmailSorter nutzen.
|
||||
</p>
|
||||
|
||||
@@ -110,18 +110,18 @@ export function VerifyEmail() {
|
||||
|
||||
{status === 'error' && (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-20 h-20 mx-auto mb-6 rounded-full bg-red-100 flex items-center justify-center">
|
||||
<XCircle className="w-10 h-10 text-red-600" />
|
||||
<div className="w-20 h-20 mx-auto mb-6 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<XCircle className="w-10 h-10 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-red-50 border border-red-100 rounded-xl">
|
||||
<p className="text-red-700">
|
||||
<div className="p-4 bg-red-50 dark:bg-red-900/30 border border-red-100 dark:border-red-800 rounded-xl">
|
||||
<p className="text-red-700 dark:text-red-300">
|
||||
{error || 'Der Verifizierungslink ist ungültig oder abgelaufen.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-slate-600 text-sm">
|
||||
<p className="text-slate-600 dark:text-slate-400 text-sm">
|
||||
Falls dein Link abgelaufen ist, kannst du eine neue Verifizierungs-E-Mail anfordern.
|
||||
</p>
|
||||
|
||||
@@ -142,9 +142,9 @@ export function VerifyEmail() {
|
||||
</Card>
|
||||
|
||||
{/* Help text */}
|
||||
<p className="text-center text-sm text-slate-500 mt-6">
|
||||
<p className="text-center text-sm text-slate-500 dark:text-slate-400 mt-6">
|
||||
Probleme? Kontaktiere uns unter{' '}
|
||||
<a href="mailto:support@emailsorter.de" className="text-primary-600 hover:underline">
|
||||
<a href="mailto:support@emailsorter.de" className="text-primary-600 dark:text-primary-400 hover:underline">
|
||||
support@emailsorter.de
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user