chore: Docs umstrukturiert, Client-Updates, Scripts nach scripts/
This commit is contained in:
97
AUFRÄUMEN.md
Normal file
97
AUFRÄUMEN.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# 🧹 Aufräumen - Dateien organisieren
|
||||||
|
|
||||||
|
Diese Datei listet alle Dateien auf, die aufgeräumt/entfernt werden können.
|
||||||
|
|
||||||
|
## ✅ Was wurde gemacht
|
||||||
|
|
||||||
|
1. ✅ Webhook-Dokumentation in `docs/deployment/` organisiert
|
||||||
|
2. ✅ Scripts-README aktualisiert
|
||||||
|
3. ✅ Deployment-Dokumentation strukturiert
|
||||||
|
4. ✅ Projekt-Ordnung dokumentiert (`PROJEKT_ORDNUNG.md`)
|
||||||
|
|
||||||
|
## 🗑️ Dateien die entfernt werden können
|
||||||
|
|
||||||
|
### Scripts (veraltete Git-Commit-Scripts)
|
||||||
|
|
||||||
|
Diese können gelöscht werden, da Git-Commits direkt über `git commit` gemacht werden sollten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/git-commit.bat
|
||||||
|
scripts/git-commit.sh
|
||||||
|
scripts/git-commit-fix.bat
|
||||||
|
scripts/FINAL_COMMIT.bat
|
||||||
|
scripts/run-git-commit.ps1
|
||||||
|
scripts/COMMIT_COMMANDS.txt
|
||||||
|
scripts/COMMIT_MESSAGE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Befehl zum Entfernen:**
|
||||||
|
```bash
|
||||||
|
cd scripts
|
||||||
|
rm git-commit.bat git-commit.sh git-commit-fix.bat FINAL_COMMIT.bat run-git-commit.ps1 COMMIT_COMMANDS.txt COMMIT_MESSAGE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dokumentation (kann archiviert werden)
|
||||||
|
|
||||||
|
Diese Task-spezifischen Dokumentationsdateien können in `docs/archive/` verschoben werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docs/development/TASK_5_COMPLETION.md
|
||||||
|
docs/server/TASK_4_COMPLETION_SUMMARY.md
|
||||||
|
docs/development/PROJECT_REVIEW_SUMMARY.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Befehl zum Archivieren:**
|
||||||
|
```bash
|
||||||
|
mkdir -p docs/archive
|
||||||
|
mv docs/development/TASK_5_COMPLETION.md docs/archive/
|
||||||
|
mv docs/server/TASK_4_COMPLETION_SUMMARY.md docs/archive/
|
||||||
|
mv docs/development/PROJECT_REVIEW_SUMMARY.md docs/archive/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Optionale Aufräumarbeiten
|
||||||
|
|
||||||
|
### Temporäre Dateien im Root
|
||||||
|
|
||||||
|
Falls es temporäre Markdown-Dateien im Root gibt (z.B. von mir erstellt), können diese entfernt werden, nachdem die Informationen in die richtige Dokumentation übernommen wurden:
|
||||||
|
|
||||||
|
- `GITEA_WEBHOOK_ZUSAMMENFASSUNG.md` (falls vorhanden)
|
||||||
|
- `GITEA_AUTHORIZATION_HEADER.md` (falls vorhanden)
|
||||||
|
- `DEPLOYMENT_STATUS.md` (falls vorhanden)
|
||||||
|
- `DEPLOYMENT_KONFIGURIERT.md` (falls vorhanden)
|
||||||
|
|
||||||
|
**Hinweis:** Diese sollten bereits in `docs/deployment/` sein.
|
||||||
|
|
||||||
|
## ✅ Wichtige Dateien (NICHT löschen!)
|
||||||
|
|
||||||
|
Diese Dateien müssen bleiben:
|
||||||
|
|
||||||
|
- Alle Dateien in `client/`, `server/`, `docs/` (außer oben genannte)
|
||||||
|
- `README.md`, `STRUCTURE.md`, `PROJEKT_ORDNUNG.md`
|
||||||
|
- Alle Konfigurationsdateien (`.gitignore`, `package.json`, etc.)
|
||||||
|
- Alle aktiven Scripts (`deploy-to-server.mjs`, `setup-*.ps1`)
|
||||||
|
|
||||||
|
## 🎯 Empfehlung
|
||||||
|
|
||||||
|
1. **Sofort entfernen:** Veraltete Git-Commit-Scripts (werden nicht mehr benötigt)
|
||||||
|
2. **Archivieren:** Task-spezifische Dokumentation (für Referenz behalten)
|
||||||
|
3. **Prüfen:** Temporäre Root-Dateien (falls vorhanden, nach Übernahme entfernen)
|
||||||
|
|
||||||
|
## 📝 Nach dem Aufräumen
|
||||||
|
|
||||||
|
Nach dem Aufräumen sollte die Struktur sauberer sein:
|
||||||
|
|
||||||
|
```
|
||||||
|
scripts/
|
||||||
|
├── deploy-to-server.mjs ✅
|
||||||
|
├── setup-appwrite.ps1 ✅
|
||||||
|
├── setup-production.ps1 ✅
|
||||||
|
└── README.md ✅
|
||||||
|
|
||||||
|
docs/
|
||||||
|
├── deployment/ ✅ (vollständig organisiert)
|
||||||
|
├── setup/ ✅
|
||||||
|
├── development/ ✅ (teilweise archiviert)
|
||||||
|
├── server/ ✅ (teilweise archiviert)
|
||||||
|
└── archive/ ✅ (neu, für veraltete Docs)
|
||||||
|
```
|
||||||
142
PROJEKT_ORDNUNG.md
Normal file
142
PROJEKT_ORDNUNG.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# 📁 Projekt-Ordnung und Dateistruktur
|
||||||
|
|
||||||
|
Diese Datei beschreibt die Organisation aller Dateien im Projekt.
|
||||||
|
|
||||||
|
## ✅ Wichtige Dateien (behalten)
|
||||||
|
|
||||||
|
### Root-Verzeichnis
|
||||||
|
|
||||||
|
- **README.md** - Hauptdokumentation des Projekts
|
||||||
|
- **STRUCTURE.md** - Detaillierte Projektstruktur
|
||||||
|
- **.gitignore** - Git-Ignore-Regeln
|
||||||
|
- **.env.example** - Beispiel-Umgebungsvariablen
|
||||||
|
|
||||||
|
### Client (`client/`)
|
||||||
|
|
||||||
|
- Alle Source-Dateien in `src/`
|
||||||
|
- Konfigurationsdateien (`package.json`, `vite.config.ts`, etc.)
|
||||||
|
- **README.md** - Client-spezifische Dokumentation
|
||||||
|
|
||||||
|
### Server (`server/`)
|
||||||
|
|
||||||
|
- Alle Backend-Dateien
|
||||||
|
- **routes/** - API-Routen (inkl. `webhook.mjs` für automatisches Deployment)
|
||||||
|
- **config/** - Konfiguration
|
||||||
|
- **.env** - Umgebungsvariablen (nicht im Git!)
|
||||||
|
|
||||||
|
### Dokumentation (`docs/`)
|
||||||
|
|
||||||
|
- **deployment/** - Deployment-Anleitungen
|
||||||
|
- `GITEA_WEBHOOK_SETUP.md` - Vollständige Webhook-Anleitung
|
||||||
|
- `WEBHOOK_QUICK_START.md` - Schnellstart
|
||||||
|
- `WEBHOOK_AUTHORIZATION.md` - Authentifizierung
|
||||||
|
- `DEPLOYMENT_INSTRUCTIONS.md` - Manuelles Deployment
|
||||||
|
- `PRODUCTION_SETUP.md` - Production-Setup
|
||||||
|
- `PRODUCTION_FIXES.md` - Bekannte Probleme
|
||||||
|
- **setup/** - Setup-Anleitungen
|
||||||
|
- **development/** - Development-Dokumentation
|
||||||
|
- **server/** - Server-Dokumentation
|
||||||
|
|
||||||
|
### Scripts (`scripts/`)
|
||||||
|
|
||||||
|
- **deploy-to-server.mjs** - Deployment-Skript (wird vom Webhook aufgerufen)
|
||||||
|
- **setup-*.ps1** - Setup-Scripts
|
||||||
|
- **README.md** - Scripts-Dokumentation
|
||||||
|
|
||||||
|
### Marketing (`marketing/`)
|
||||||
|
|
||||||
|
- Alle Marketing-Materialien und Anleitungen
|
||||||
|
|
||||||
|
## 🗑️ Kann entfernt werden (temporäre/veraltete Dateien)
|
||||||
|
|
||||||
|
### Scripts (`scripts/`)
|
||||||
|
|
||||||
|
Diese Git-Commit-Scripts sind veraltet und können entfernt werden:
|
||||||
|
|
||||||
|
- `git-commit.bat`
|
||||||
|
- `git-commit.sh`
|
||||||
|
- `git-commit-fix.bat`
|
||||||
|
- `FINAL_COMMIT.bat`
|
||||||
|
- `run-git-commit.ps1`
|
||||||
|
- `COMMIT_COMMANDS.txt`
|
||||||
|
- `COMMIT_MESSAGE.md`
|
||||||
|
|
||||||
|
**Grund:** Git-Commits sollten direkt über `git commit` gemacht werden.
|
||||||
|
|
||||||
|
### Dokumentation (`docs/`)
|
||||||
|
|
||||||
|
Einige temporäre/veraltete Dokumentationsdateien können archiviert werden:
|
||||||
|
|
||||||
|
- `development/TASK_5_COMPLETION.md` - Task-spezifisch, kann archiviert werden
|
||||||
|
- `server/TASK_4_COMPLETION_SUMMARY.md` - Task-spezifisch, kann archiviert werden
|
||||||
|
- `development/PROJECT_REVIEW_SUMMARY.md` - Review-spezifisch, kann archiviert werden
|
||||||
|
|
||||||
|
**Empfehlung:** Verschiebe diese in `docs/archive/` statt zu löschen.
|
||||||
|
|
||||||
|
## 📋 Dateien-Organisation
|
||||||
|
|
||||||
|
### Aktuelle Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
/
|
||||||
|
├── client/ # Frontend
|
||||||
|
├── server/ # Backend
|
||||||
|
├── docs/ # Dokumentation
|
||||||
|
│ ├── deployment/ # Deployment-Docs ✅
|
||||||
|
│ ├── setup/ # Setup-Docs ✅
|
||||||
|
│ ├── development/ # Development-Docs (teilweise archivieren)
|
||||||
|
│ └── server/ # Server-Docs (teilweise archivieren)
|
||||||
|
├── scripts/ # Scripts
|
||||||
|
│ ├── deploy-to-server.mjs ✅
|
||||||
|
│ ├── setup-*.ps1 ✅
|
||||||
|
│ └── [veraltete Git-Scripts] ❌
|
||||||
|
├── marketing/ # Marketing ✅
|
||||||
|
└── README.md # Hauptdokumentation ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧹 Aufräumen-Empfehlungen
|
||||||
|
|
||||||
|
### 1. Veraltete Scripts entfernen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Diese Dateien können gelöscht werden:
|
||||||
|
scripts/git-commit.bat
|
||||||
|
scripts/git-commit.sh
|
||||||
|
scripts/git-commit-fix.bat
|
||||||
|
scripts/FINAL_COMMIT.bat
|
||||||
|
scripts/run-git-commit.ps1
|
||||||
|
scripts/COMMIT_COMMANDS.txt
|
||||||
|
scripts/COMMIT_MESSAGE.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Temporäre Dokumentation archivieren
|
||||||
|
|
||||||
|
Erstelle `docs/archive/` und verschiebe:
|
||||||
|
- `docs/development/TASK_5_COMPLETION.md`
|
||||||
|
- `docs/server/TASK_4_COMPLETION_SUMMARY.md`
|
||||||
|
- `docs/development/PROJECT_REVIEW_SUMMARY.md`
|
||||||
|
|
||||||
|
### 3. README aktualisieren
|
||||||
|
|
||||||
|
Die `scripts/README.md` wurde bereits aktualisiert.
|
||||||
|
|
||||||
|
## ✅ Checkliste
|
||||||
|
|
||||||
|
- [x] Webhook-Dokumentation in `docs/deployment/` organisiert
|
||||||
|
- [x] Scripts-README aktualisiert
|
||||||
|
- [x] Deployment-Dokumentation strukturiert
|
||||||
|
- [ ] Veraltete Scripts entfernen (optional)
|
||||||
|
- [ ] Temporäre Dokumentation archivieren (optional)
|
||||||
|
|
||||||
|
## 📝 Wichtige Hinweise
|
||||||
|
|
||||||
|
1. **`.env` Dateien** sind nie im Git (siehe `.gitignore`)
|
||||||
|
2. **Temporäre Anleitungen** können nach erfolgreicher Einrichtung entfernt werden
|
||||||
|
3. **Task-spezifische Dokumentation** kann archiviert werden, sollte aber nicht gelöscht werden
|
||||||
|
4. **Alle produktiven Dateien** (Code, Konfiguration, aktive Dokumentation) bleiben erhalten
|
||||||
|
|
||||||
|
## 🔄 Regelmäßige Wartung
|
||||||
|
|
||||||
|
- **Monatlich:** Prüfe auf veraltete Scripts/Dokumentation
|
||||||
|
- **Nach großen Features:** Aktualisiere README und Dokumentation
|
||||||
|
- **Nach Deployment:** Entferne temporäre Deployment-Anleitungen (falls nicht mehr benötigt)
|
||||||
25
README.md
25
README.md
@@ -29,9 +29,19 @@ EmailSorter ist eine SaaS-Anwendung, die automatisch E-Mails kategorisiert und s
|
|||||||
│ ├── routes/ # API Routen
|
│ ├── routes/ # API Routen
|
||||||
│ ├── services/ # Business Logic
|
│ ├── services/ # Business Logic
|
||||||
│ └── package.json
|
│ └── package.json
|
||||||
|
├── docs/ # Dokumentation
|
||||||
|
│ ├── setup/ # Setup-Anleitungen
|
||||||
|
│ ├── deployment/ # Deployment-Docs
|
||||||
|
│ ├── development/ # Development-Docs
|
||||||
|
│ └── server/ # Server-Dokumentation
|
||||||
|
├── scripts/ # Hilfs-Scripts
|
||||||
|
│ ├── git-commit.* # Git-Scripts
|
||||||
|
│ └── deploy-build.js # Deployment-Scripts
|
||||||
|
├── marketing/ # Marketing-Materialien
|
||||||
|
│ └── *.md # Marketing-Dokumentation
|
||||||
├── n8n/ # n8n Workflows
|
├── n8n/ # n8n Workflows
|
||||||
│ └── workflows/
|
│ └── workflows/
|
||||||
└── public/ # Legacy Frontend
|
└── README.md # Diese Datei
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@@ -199,6 +209,8 @@ Siehe `n8n/README.md` für Details.
|
|||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
|
Siehe `docs/deployment/` für detaillierte Deployment-Anleitungen.
|
||||||
|
|
||||||
### Frontend (Vercel/Netlify)
|
### Frontend (Vercel/Netlify)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -219,6 +231,17 @@ Aktualisiere die Webhook-URL im Stripe Dashboard auf deine Produktions-URL:
|
|||||||
https://your-domain.com/api/subscription/webhook
|
https://your-domain.com/api/subscription/webhook
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Dokumentation
|
||||||
|
|
||||||
|
Alle Dokumentation befindet sich im `docs/` Ordner:
|
||||||
|
|
||||||
|
- **Setup:** `docs/setup/` - Setup-Anleitungen für Appwrite, OAuth, etc.
|
||||||
|
- **Deployment:** `docs/deployment/` - Production-Setup und Deployment
|
||||||
|
- **Development:** `docs/development/` - Development-Dokumentation
|
||||||
|
- **Server:** `docs/server/` - Server-spezifische Dokumentation
|
||||||
|
|
||||||
|
Siehe `docs/README.md` für eine vollständige Übersicht.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Frontend startet nicht
|
### Frontend startet nicht
|
||||||
|
|||||||
95
STRUCTURE.md
Normal file
95
STRUCTURE.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Projektstruktur-Übersicht
|
||||||
|
|
||||||
|
Diese Datei beschreibt die organisierte Struktur des Projekts.
|
||||||
|
|
||||||
|
## 📁 Hauptverzeichnisse
|
||||||
|
|
||||||
|
### `/client/`
|
||||||
|
React Frontend-Anwendung
|
||||||
|
- `src/` - Quellcode
|
||||||
|
- `public/` - Statische Assets
|
||||||
|
- `package.json` - Frontend-Dependencies
|
||||||
|
|
||||||
|
### `/server/`
|
||||||
|
Node.js Backend-Server
|
||||||
|
- `routes/` - API-Routen
|
||||||
|
- `services/` - Business-Logik
|
||||||
|
- `middleware/` - Express-Middleware
|
||||||
|
- `config/` - Konfiguration
|
||||||
|
- `utils/` - Utility-Funktionen
|
||||||
|
- `package.json` - Backend-Dependencies
|
||||||
|
|
||||||
|
### `/docs/`
|
||||||
|
Alle Dokumentation
|
||||||
|
- `setup/` - Setup-Anleitungen (Appwrite, OAuth, etc.)
|
||||||
|
- `deployment/` - Deployment & Production-Docs
|
||||||
|
- `development/` - Development-Dokumentation
|
||||||
|
- `server/` - Server-spezifische Dokumentation
|
||||||
|
- `examples/` - Beispiel-Code (z.B. starter-for-react)
|
||||||
|
- `legacy/` - Legacy-Dateien
|
||||||
|
|
||||||
|
### `/scripts/`
|
||||||
|
Hilfs-Scripts für Entwicklung & Deployment
|
||||||
|
- Git-Scripts (`git-commit.*`, `run-git-commit.ps1`)
|
||||||
|
- Deployment-Scripts (`deploy-build.js`)
|
||||||
|
- Setup-Scripts (`setup-appwrite.ps1`, `setup-production.ps1`)
|
||||||
|
- Commit-Hilfsdateien (`COMMIT_MESSAGE.md`, `COMMIT_COMMANDS.txt`)
|
||||||
|
|
||||||
|
### `/marketing/`
|
||||||
|
Marketing-Materialien und Dokumentation
|
||||||
|
- Logo-Dateien (SVG)
|
||||||
|
- Marketing-Guides (TikTok, YouTube, Product Hunt, etc.)
|
||||||
|
- Influencer-Templates
|
||||||
|
|
||||||
|
### `/n8n/`
|
||||||
|
n8n Workflow-Konfigurationen
|
||||||
|
- `workflows/` - Workflow-Definitionen
|
||||||
|
|
||||||
|
### `/.kiro/`
|
||||||
|
Kiro-Spezifikationen (Design, Requirements, Tasks)
|
||||||
|
|
||||||
|
## 📄 Root-Dateien
|
||||||
|
|
||||||
|
- `README.md` - Haupt-README mit Projektübersicht
|
||||||
|
- `STRUCTURE.md` - Diese Datei
|
||||||
|
- `.env.example` - Beispiel-Umgebungsvariablen
|
||||||
|
- `.gitignore` - Git-Ignore-Regeln
|
||||||
|
|
||||||
|
## 🎯 Organisationsprinzipien
|
||||||
|
|
||||||
|
1. **Dokumentation zentralisiert:** Alle `.md` Dateien sind in `docs/` organisiert
|
||||||
|
2. **Scripts getrennt:** Alle Scripts sind in `scripts/` gesammelt
|
||||||
|
3. **Sauberes Root:** Root-Verzeichnis enthält nur essenzielle Dateien
|
||||||
|
4. **Klare Kategorien:** Dokumentation nach Themen sortiert (setup, deployment, development)
|
||||||
|
|
||||||
|
## 📝 Wichtige Dateien
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
- `docs/setup/SETUP_GUIDE.md` - Allgemeine Setup-Anleitung
|
||||||
|
- `docs/setup/APPWRITE_SETUP.md` - Appwrite-Konfiguration
|
||||||
|
- `docs/setup/GOOGLE_OAUTH_SETUP.md` - Google OAuth Setup
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
- `docs/deployment/PRODUCTION_SETUP.md` - Production-Server Setup
|
||||||
|
- `docs/deployment/DEPLOYMENT_INSTRUCTIONS.md` - Deployment-Anleitung
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- `docs/development/PROJECT_REVIEW_SUMMARY.md` - Projekt-Review
|
||||||
|
- `docs/development/TESTING_SUMMARY.md` - Testing-Dokumentation
|
||||||
|
|
||||||
|
## 🔧 Scripts-Verwendung
|
||||||
|
|
||||||
|
Alle Scripts befinden sich in `scripts/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Git-Commit (Windows)
|
||||||
|
scripts\git-commit.bat
|
||||||
|
|
||||||
|
# Git-Commit (PowerShell)
|
||||||
|
scripts\run-git-commit.ps1
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
node scripts\deploy-build.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Siehe `scripts/README.md` für Details.
|
||||||
@@ -21,10 +21,10 @@ initAnalytics()
|
|||||||
// Loading spinner component
|
// Loading spinner component
|
||||||
function LoadingSpinner() {
|
function LoadingSpinner() {
|
||||||
return (
|
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="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" />
|
<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 text-sm">Loading...</p>
|
<p className="text-slate-500 dark:text-slate-400 text-sm">Loading...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -41,17 +41,17 @@ export function FAQ() {
|
|||||||
const [openIndex, setOpenIndex] = useState<number | null>(0)
|
const [openIndex, setOpenIndex] = useState<number | null>(0)
|
||||||
|
|
||||||
return (
|
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">
|
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
<div className="text-center mb-16">
|
<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">
|
<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" />
|
<HelpCircle className="w-8 h-8 text-primary-600 dark:text-primary-400" />
|
||||||
</div>
|
</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
|
FAQ
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg text-slate-600">
|
<p className="text-lg text-slate-600 dark:text-slate-400">
|
||||||
Quick answers to common questions.
|
Quick answers to common questions.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,11 +70,11 @@ export function FAQ() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Contact CTA */}
|
{/* Contact CTA */}
|
||||||
<div className="mt-12 text-center p-6 bg-white rounded-2xl border border-slate-200">
|
<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 mb-2">Still have questions?</p>
|
<p className="text-slate-600 dark:text-slate-400 mb-2">Still have questions?</p>
|
||||||
<a
|
<a
|
||||||
href="mailto:support@emailsorter.com"
|
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 →
|
Contact us →
|
||||||
</a>
|
</a>
|
||||||
@@ -93,15 +93,15 @@ interface FAQItemProps {
|
|||||||
|
|
||||||
function FAQItem({ question, answer, isOpen, onClick }: FAQItemProps) {
|
function FAQItem({ question, answer, isOpen, onClick }: FAQItemProps) {
|
||||||
return (
|
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
|
<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}
|
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
|
<ChevronDown
|
||||||
className={cn(
|
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"
|
isOpen && "rotate-180"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -112,7 +112,7 @@ function FAQItem({ question, answer, isOpen, onClick }: FAQItemProps) {
|
|||||||
isOpen ? "max-h-40" : "max-h-0"
|
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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -52,17 +52,17 @@ const features = [
|
|||||||
|
|
||||||
export function Features() {
|
export function Features() {
|
||||||
return (
|
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">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
<div className="text-center mb-16">
|
<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{' '}
|
Everything you need for{' '}
|
||||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-accent-500">
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-accent-500">
|
||||||
Inbox Zero
|
Inbox Zero
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</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
|
EmailSorter combines AI technology with proven email management methods
|
||||||
for maximum productivity.
|
for maximum productivity.
|
||||||
</p>
|
</p>
|
||||||
@@ -77,17 +77,17 @@ export function Features() {
|
|||||||
|
|
||||||
{/* Bottom illustration */}
|
{/* Bottom illustration */}
|
||||||
<div className="mt-20 relative">
|
<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">
|
<div className="grid md:grid-cols-3 gap-8 items-center">
|
||||||
{/* Before */}
|
{/* Before */}
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-red-100 flex items-center justify-center">
|
<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" />
|
<Inbox className="w-10 h-10 text-red-500 dark:text-red-400" />
|
||||||
</div>
|
</div>
|
||||||
<h4 className="font-semibold text-slate-900 mb-1">Before</h4>
|
<h4 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">Before</h4>
|
||||||
<p className="text-sm text-slate-500">Inbox chaos</p>
|
<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">847</div>
|
<div className="mt-3 text-3xl font-bold text-red-500 dark:text-red-400">847</div>
|
||||||
<p className="text-xs text-slate-400">unread emails</p>
|
<p className="text-xs text-slate-400 dark:text-slate-500">unread emails</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Arrow */}
|
{/* Arrow */}
|
||||||
@@ -99,13 +99,13 @@ export function Features() {
|
|||||||
|
|
||||||
{/* After */}
|
{/* After */}
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="w-20 h-20 mx-auto mb-4 rounded-2xl bg-green-100 flex items-center justify-center">
|
<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" />
|
<Inbox className="w-10 h-10 text-green-500 dark:text-green-400" />
|
||||||
</div>
|
</div>
|
||||||
<h4 className="font-semibold text-slate-900 mb-1">After</h4>
|
<h4 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">After</h4>
|
||||||
<p className="text-sm text-slate-500">All sorted</p>
|
<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">12</div>
|
<div className="mt-3 text-3xl font-bold text-green-500 dark:text-green-400">12</div>
|
||||||
<p className="text-xs text-slate-400">important emails</p>
|
<p className="text-xs text-slate-400 dark:text-slate-500">important emails</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,16 +129,16 @@ function FeatureCard({ icon: Icon, title, description, color, index, highlight }
|
|||||||
<div
|
<div
|
||||||
className={`group rounded-2xl p-6 border transition-all duration-300 ${
|
className={`group rounded-2xl p-6 border transition-all duration-300 ${
|
||||||
highlight
|
highlight
|
||||||
? 'bg-gradient-to-br from-white to-slate-50 border-primary-200 hover:border-primary-300 hover:shadow-xl'
|
? '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 border-slate-200 hover:border-primary-200 hover:shadow-lg'
|
: '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` }}
|
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`}>
|
<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" />
|
<Icon className="w-7 h-7 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className={`${highlight ? 'text-2xl' : 'text-xl'} font-semibold text-slate-900 mb-2`}>{title}</h3>
|
<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">{description}</p>
|
<p className="text-slate-600 dark:text-slate-400">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,17 +35,17 @@ const steps = [
|
|||||||
|
|
||||||
export function HowItWorks() {
|
export function HowItWorks() {
|
||||||
return (
|
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">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
<div className="text-center mb-16">
|
<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{' '}
|
4 steps to a{' '}
|
||||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-accent-500">
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-accent-500">
|
||||||
clean inbox
|
clean inbox
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</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.
|
Get started in minutes – no technical knowledge required.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,11 +65,11 @@ export function HowItWorks() {
|
|||||||
{/* CTA */}
|
{/* CTA */}
|
||||||
<div className="mt-16 text-center">
|
<div className="mt-16 text-center">
|
||||||
<div className="inline-flex flex-col items-center">
|
<div className="inline-flex flex-col items-center">
|
||||||
<ArrowDown className="w-8 h-8 text-primary-400 animate-bounce mb-4" />
|
<ArrowDown className="w-8 h-8 text-primary-400 dark:text-primary-500 animate-bounce mb-4" />
|
||||||
<p className="text-slate-600 mb-2">Ready to get started?</p>
|
<p className="text-slate-600 dark:text-slate-400 mb-2">Ready to get started?</p>
|
||||||
<a
|
<a
|
||||||
href="/register"
|
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 →
|
Try it free now →
|
||||||
</a>
|
</a>
|
||||||
@@ -91,20 +91,20 @@ function StepCard({ icon: Icon, step, title, description }: StepCardProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{/* Card */}
|
{/* 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 */}
|
{/* 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}
|
{step}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
<div className="w-16 h-16 mx-auto mt-4 mb-4 rounded-2xl bg-white shadow-md flex items-center justify-center">
|
<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" />
|
<Icon className="w-8 h-8 text-primary-600 dark:text-primary-400" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">{title}</h3>
|
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">{title}</h3>
|
||||||
<p className="text-slate-600 text-sm">{description}</p>
|
<p className="text-slate-600 dark:text-slate-400 text-sm">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export function Pricing() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
return (
|
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">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
<div className="text-center mb-16">
|
<div className="text-center mb-16">
|
||||||
@@ -72,10 +72,10 @@ export function Pricing() {
|
|||||||
<Sparkles className="w-3 h-3 mr-1" />
|
<Sparkles className="w-3 h-3 mr-1" />
|
||||||
14-day free trial
|
14-day free trial
|
||||||
</Badge>
|
</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
|
Simple, transparent pricing
|
||||||
</h2>
|
</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.
|
Choose the plan that fits you. Cancel anytime, no hidden costs.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,11 +93,11 @@ export function Pricing() {
|
|||||||
|
|
||||||
{/* FAQ teaser */}
|
{/* FAQ teaser */}
|
||||||
<div className="mt-16 text-center">
|
<div className="mt-16 text-center">
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-400">
|
||||||
Still have questions?{' '}
|
Still have questions?{' '}
|
||||||
<button
|
<button
|
||||||
onClick={() => document.getElementById('faq')?.scrollIntoView({ behavior: 'smooth' })}
|
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
|
Check our FAQ
|
||||||
</button>
|
</button>
|
||||||
@@ -131,15 +131,15 @@ function PricingCard({
|
|||||||
}: PricingCardProps) {
|
}: PricingCardProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative bg-white rounded-2xl p-8 ${
|
className={`relative bg-white dark:bg-slate-800 rounded-2xl p-8 ${
|
||||||
popular
|
popular
|
||||||
? 'ring-2 ring-primary-500 shadow-xl scale-105'
|
? 'ring-2 ring-primary-500 dark:ring-primary-400 shadow-xl scale-105'
|
||||||
: 'border border-slate-200 hover:border-primary-200 hover:shadow-lg'
|
: 'border border-slate-200 dark:border-slate-700 hover:border-primary-200 dark:hover:border-primary-800 hover:shadow-lg'
|
||||||
} transition-all duration-300`}
|
} transition-all duration-300`}
|
||||||
>
|
>
|
||||||
{popular && (
|
{popular && (
|
||||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
<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
|
Most Popular
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,15 +147,15 @@ function PricingCard({
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<h3 className="text-xl font-bold text-slate-900 mb-1">{name}</h3>
|
<h3 className="text-xl font-bold text-slate-900 dark:text-slate-100 mb-1">{name}</h3>
|
||||||
<p className="text-sm text-slate-500">{description}</p>
|
<p className="text-sm text-slate-500 dark:text-slate-400">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Price */}
|
{/* Price */}
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<div className="flex items-baseline justify-center">
|
<div className="flex items-baseline justify-center">
|
||||||
<span className="text-5xl font-extrabold text-slate-900">${price}</span>
|
<span className="text-5xl font-extrabold text-slate-900 dark:text-slate-100">${price}</span>
|
||||||
<span className="text-slate-500 ml-1">{period}</span>
|
<span className="text-slate-500 dark:text-slate-400 ml-1">{period}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -164,15 +164,15 @@ function PricingCard({
|
|||||||
{features.map((feature, index) => (
|
{features.map((feature, index) => (
|
||||||
<li key={index} className="flex items-center gap-3">
|
<li key={index} className="flex items-center gap-3">
|
||||||
{feature.included ? (
|
{feature.included ? (
|
||||||
<div className="w-5 h-5 rounded-full bg-green-100 flex items-center justify-center flex-shrink-0">
|
<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" />
|
<Check className="w-3 h-3 text-green-600 dark:text-green-400" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-5 h-5 rounded-full bg-slate-100 flex items-center justify-center flex-shrink-0">
|
<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" />
|
<X className="w-3 h-3 text-slate-400 dark:text-slate-500" />
|
||||||
</div>
|
</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}
|
{feature.text}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -18,9 +18,19 @@ export { ID }
|
|||||||
export const auth = {
|
export const auth = {
|
||||||
// Create a new account
|
// Create a new account
|
||||||
async register(email: string, password: string, name?: string) {
|
async register(email: string, password: string, name?: string) {
|
||||||
const user = await account.create(ID.unique(), email, password, name)
|
// #region agent log
|
||||||
await this.login(email, password)
|
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(()=>{});
|
||||||
return user
|
// #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
|
// Login with email and password
|
||||||
|
|||||||
@@ -29,22 +29,22 @@ export function ForgotPassword() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<div className="w-full max-w-md">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
<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">
|
<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" />
|
<Mail className="w-5 h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xl font-bold text-slate-900">
|
<span className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Email<span className="text-primary-600">Sorter</span>
|
Email<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</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">
|
<CardHeader className="text-center pb-2">
|
||||||
<CardTitle className="text-2xl">Passwort vergessen?</CardTitle>
|
<CardTitle className="text-2xl dark:text-slate-100">Passwort vergessen?</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription className="dark:text-slate-400">
|
||||||
{sent
|
{sent
|
||||||
? 'Prüfe dein E-Mail-Postfach'
|
? 'Prüfe dein E-Mail-Postfach'
|
||||||
: 'Gib deine E-Mail-Adresse ein und wir senden dir einen Link zum Zurücksetzen.'
|
: 'Gib deine E-Mail-Adresse ein und wir senden dir einen Link zum Zurücksetzen.'
|
||||||
@@ -54,14 +54,14 @@ export function ForgotPassword() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{sent ? (
|
{sent ? (
|
||||||
<div className="text-center py-8">
|
<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">
|
<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" />
|
<CheckCircle className="w-8 h-8 text-green-600 dark:text-green-400" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-semibold text-slate-900 mb-2">E-Mail gesendet!</h3>
|
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-2">E-Mail gesendet!</h3>
|
||||||
<p className="text-slate-600 mb-6">
|
<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>{email}</strong> gesendet.
|
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>
|
||||||
<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.
|
Keine E-Mail erhalten? Prüfe deinen Spam-Ordner oder versuche es erneut.
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -83,13 +83,13 @@ export function ForgotPassword() {
|
|||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
{error && (
|
{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}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
type="email"
|
type="email"
|
||||||
@@ -98,6 +98,7 @@ export function ForgotPassword() {
|
|||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
|
className="dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ export function ForgotPassword() {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Link
|
<Link
|
||||||
to="/login"
|
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" />
|
<ArrowLeft className="w-4 h-4 inline mr-1" />
|
||||||
Zurück zum Login
|
Zurück zum Login
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ArrowLeft, Building2 } from 'lucide-react'
|
|||||||
|
|
||||||
export function Imprint() {
|
export function Imprint() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-slate-50">
|
<div className="min-h-screen bg-slate-50 dark:bg-slate-900">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-700">
|
<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">
|
<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 */}
|
{/* Content */}
|
||||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
<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 */}
|
{/* Title */}
|
||||||
<div className="flex items-center gap-3 mb-8">
|
<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">
|
<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" />
|
<Building2 className="w-6 h-6 text-primary-600 dark:text-primary-400" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-slate-900">Impressum</h1>
|
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100">Impressum</h1>
|
||||||
<p className="text-slate-500 mt-1">Legal Information</p>
|
<p className="text-slate-500 dark:text-slate-400 mt-1">Legal Information</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content - Placeholder for webklar.com content */}
|
{/* Content - Placeholder for webklar.com content */}
|
||||||
<div className="prose prose-slate max-w-none">
|
<div className="prose prose-slate max-w-none dark:prose-invert">
|
||||||
<p className="text-slate-600 mb-6">
|
<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.
|
<strong>Note:</strong> This imprint is managed by webklar.com. Please refer to their imprint for detailed information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="bg-slate-50 border border-slate-200 rounded-lg p-6 mb-8">
|
<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 mb-4">Information according to § 5 TMG</h2>
|
<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>
|
<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-2">EmailSorter is operated by:</p>
|
||||||
<p className="mb-4">
|
<p className="mb-4">
|
||||||
<strong>webklar.com</strong><br />
|
<strong>webklar.com</strong><br />
|
||||||
Kenso Grimm, Justin Klein
|
Kenso Grimm, Justin Klein
|
||||||
</p>
|
</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:{' '}
|
For complete contact details and legal information, please visit:{' '}
|
||||||
<a
|
<a
|
||||||
href="https://webklar.com/impressum"
|
href="https://webklar.com/impressum"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
webklar.com/impressum
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-6 border-t border-slate-200">
|
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Contact</h3>
|
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Contact</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p>
|
<p>
|
||||||
<strong>Email:</strong>{' '}
|
<strong>Email:</strong>{' '}
|
||||||
<a
|
<a
|
||||||
href="mailto:support@webklar.com"
|
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
|
support@webklar.com
|
||||||
</a>
|
</a>
|
||||||
@@ -77,23 +77,23 @@ export function Imprint() {
|
|||||||
<strong>Phone:</strong>{' '}
|
<strong>Phone:</strong>{' '}
|
||||||
<a
|
<a
|
||||||
href="tel:+4917623726355"
|
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
|
+49 176 23726355
|
||||||
</a>
|
</a>
|
||||||
{' / '}
|
{' / '}
|
||||||
<a
|
<a
|
||||||
href="tel:+491704969375"
|
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
|
+49 170 4969375
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</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:{' '}
|
For questions regarding EmailSorter specifically:{' '}
|
||||||
<a
|
<a
|
||||||
href="mailto:support@emailsorter.com"
|
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
|
support@emailsorter.com
|
||||||
</a>
|
</a>
|
||||||
@@ -101,8 +101,8 @@ export function Imprint() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-6 border-t border-slate-200">
|
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Responsible for Content</h3>
|
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Responsible for Content</h3>
|
||||||
<p>
|
<p>
|
||||||
The content of this website is the responsibility of webklar.com.
|
The content of this website is the responsibility of webklar.com.
|
||||||
For detailed information, please refer to the official imprint at{' '}
|
For detailed information, please refer to the official imprint at{' '}
|
||||||
@@ -110,23 +110,23 @@ export function Imprint() {
|
|||||||
href="https://webklar.com/impressum"
|
href="https://webklar.com/impressum"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
webklar.com/impressum
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-6 border-t border-slate-200">
|
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Liability for Links</h3>
|
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Liability for Links</h3>
|
||||||
<p>
|
<p>
|
||||||
Our website contains links to external websites. We have no influence on the content of these websites.
|
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.
|
Therefore, we cannot assume any liability for these external contents.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-6 border-t border-slate-200">
|
<div className="pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mb-2">Copyright</h3>
|
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-2">Copyright</h3>
|
||||||
<p>
|
<p>
|
||||||
The content and works on this website are subject to German copyright law.
|
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.
|
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>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 pt-6 border-t border-slate-200">
|
<div className="mt-8 pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||||
<p className="text-sm text-slate-500">
|
<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{' '}
|
<strong>Important:</strong> This is a simplified version. For the complete and legally binding imprint, please visit{' '}
|
||||||
<a
|
<a
|
||||||
href="https://webklar.com/impressum"
|
href="https://webklar.com/impressum"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
webklar.com/impressum
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ArrowLeft, Shield } from 'lucide-react'
|
|||||||
|
|
||||||
export function Privacy() {
|
export function Privacy() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-slate-50">
|
<div className="min-h-screen bg-slate-50 dark:bg-slate-900">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-700">
|
<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">
|
<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 */}
|
{/* Content */}
|
||||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
<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 */}
|
{/* Title */}
|
||||||
<div className="flex items-center gap-3 mb-8">
|
<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">
|
<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" />
|
<Shield className="w-6 h-6 text-primary-600 dark:text-primary-400" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-slate-900">Privacy Policy</h1>
|
<h1 className="text-3xl font-bold text-slate-900 dark:text-slate-100">Privacy Policy</h1>
|
||||||
<p className="text-slate-500 mt-1">Last updated: {new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content - Placeholder for webklar.com content */}
|
{/* Content - Placeholder for webklar.com content */}
|
||||||
<div className="prose prose-slate max-w-none">
|
<div className="prose prose-slate max-w-none dark:prose-invert">
|
||||||
<p className="text-slate-600 mb-6">
|
<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.
|
<strong>Note:</strong> This privacy policy is managed by webklar.com. Please refer to their privacy policy for detailed information.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="bg-slate-50 border border-slate-200 rounded-lg p-6 mb-8">
|
<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 mb-4">Data Protection Information</h2>
|
<h2 className="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-4">Data Protection Information</h2>
|
||||||
<p className="text-slate-700 mb-4">
|
<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.
|
EmailSorter is operated by webklar.com. The following privacy policy applies to the use of this website and our services.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">1. Responsible Party</h3>
|
<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 mb-4">
|
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||||
The responsible party for data processing on this website is:
|
The responsible party for data processing on this website is:
|
||||||
</p>
|
</p>
|
||||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
<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 mb-2">
|
<p className="text-slate-700 dark:text-slate-300 mb-2">
|
||||||
<strong>webklar.com</strong><br />
|
<strong>webklar.com</strong><br />
|
||||||
Kenso Grimm, Justin Klein
|
Kenso Grimm, Justin Klein
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-700 mb-2">
|
<p className="text-slate-700 dark:text-slate-300 mb-2">
|
||||||
<strong>Contact:</strong><br />
|
<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 />
|
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 hover:text-primary-700 underline">+49 176 23726355</a>
|
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>
|
||||||
<p className="text-sm text-slate-600 mt-3">
|
<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 hover:text-primary-700 underline">Impressum</Link>.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">2. Data Collection and Processing</h3>
|
<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 mb-4">
|
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||||
When you use EmailSorter, we collect and process the following data:
|
When you use EmailSorter, we collect and process the following data:
|
||||||
</p>
|
</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>Account information (email address, name)</li>
|
||||||
<li>Email metadata (sender, subject, date) for sorting purposes</li>
|
<li>Email metadata (sender, subject, date) for sorting purposes</li>
|
||||||
<li>Usage statistics and preferences</li>
|
<li>Usage statistics and preferences</li>
|
||||||
<li>Payment information (processed securely via Stripe)</li>
|
<li>Payment information (processed securely via Stripe)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">3. Purpose of Data Processing</h3>
|
<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 mb-4">
|
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||||
We process your data exclusively for the following purposes:
|
We process your data exclusively for the following purposes:
|
||||||
</p>
|
</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>Providing and improving the EmailSorter service</li>
|
||||||
<li>Automated email sorting and categorization</li>
|
<li>Automated email sorting and categorization</li>
|
||||||
<li>Processing payments and subscriptions</li>
|
<li>Processing payments and subscriptions</li>
|
||||||
<li>Customer support and communication</li>
|
<li>Customer support and communication</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">4. Data Security</h3>
|
<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 mb-4">
|
<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.
|
We implement appropriate technical and organizational measures to protect your data against unauthorized access, loss, or destruction.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">5. Your Rights</h3>
|
<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 mb-4">
|
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||||
You have the right to:
|
You have the right to:
|
||||||
</p>
|
</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>Access your personal data</li>
|
||||||
<li>Correct inaccurate data</li>
|
<li>Correct inaccurate data</li>
|
||||||
<li>Request deletion of your data</li>
|
<li>Request deletion of your data</li>
|
||||||
@@ -101,14 +101,14 @@ export function Privacy() {
|
|||||||
<li>Data portability</li>
|
<li>Data portability</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">6. Hosting and Third-Party Services</h3>
|
<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 mb-4">
|
<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.
|
<strong>Hosting:</strong> Our website is hosted by Netlify, which acts as a data processor.
|
||||||
</p>
|
</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:
|
We use the following third-party services:
|
||||||
</p>
|
</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>Appwrite:</strong> User authentication and database</li>
|
||||||
<li><strong>Stripe:</strong> Payment processing</li>
|
<li><strong>Stripe:</strong> Payment processing</li>
|
||||||
<li><strong>Mistral AI:</strong> Email categorization</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>
|
<li><strong>Plausible (optional):</strong> Privacy-friendly analytics tool, if enabled</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">6.1. Cookies and Tracking</h3>
|
<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 mb-4">
|
<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),
|
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
|
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.
|
that it is technically or organizationally necessary.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">7. Contact Form Data</h3>
|
<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 mb-4">
|
<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.
|
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.
|
This data will not be shared with third parties without your consent.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 className="text-lg font-semibold text-slate-900 mt-6 mb-3">8. Contact</h3>
|
<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 mb-4">
|
<p className="text-slate-700 dark:text-slate-300 mb-4">
|
||||||
For questions regarding data protection, please contact us:
|
For questions regarding data protection, please contact us:
|
||||||
</p>
|
</p>
|
||||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-4">
|
<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">
|
<p className="text-slate-700 dark:text-slate-300">
|
||||||
<strong>Email:</strong>{' '}
|
<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
|
support@webklar.com
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-700 mt-2">
|
<p className="text-slate-700 dark:text-slate-300 mt-2">
|
||||||
<strong>Phone:</strong>{' '}
|
<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
|
+49 176 23726355
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-slate-600 mt-3">
|
<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 hover:text-primary-700 underline">Impressum</Link>.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 pt-6 border-t border-slate-200">
|
<div className="mt-8 pt-6 border-t border-slate-200 dark:border-slate-700">
|
||||||
<p className="text-sm text-slate-500">
|
<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{' '}
|
<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
|
webklar.com/datenschutz
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -114,30 +114,30 @@ export function Register() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right side - Form */}
|
{/* 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">
|
<div className="w-full max-w-md">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link to="/" className="flex items-center gap-2 mb-8">
|
<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">
|
<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" />
|
<Mail className="w-5 h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xl font-bold text-slate-900">
|
<span className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
E-Mail-<span className="text-primary-600">Sorter</span>
|
E-Mail-<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</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
|
Create account
|
||||||
</h1>
|
</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.
|
Ready to go in less than a minute.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Error message */}
|
{/* Error message */}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-xl flex items-start gap-3">
|
<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 flex-shrink-0 mt-0.5" />
|
<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">{error}</p>
|
<p className="text-sm text-red-600 dark:text-red-300">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -217,16 +217,16 @@ export function Register() {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</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{' '}
|
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 dark:text-primary-400 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">Privacy Policy</a>.
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</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?{' '}
|
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
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -83,24 +83,24 @@ export function ResetPassword() {
|
|||||||
const passwordStrength = getPasswordStrength()
|
const passwordStrength = getPasswordStrength()
|
||||||
|
|
||||||
return (
|
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">
|
<div className="w-full max-w-md">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
<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">
|
<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" />
|
<Mail className="w-5 h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xl font-bold text-slate-900">
|
<span className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Email<span className="text-primary-600">Sorter</span>
|
Email<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</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">
|
<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'}
|
{success ? 'Passwort geändert!' : 'Neues Passwort festlegen'}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription className="dark:text-slate-400">
|
||||||
{success
|
{success
|
||||||
? 'Dein Passwort wurde erfolgreich geändert.'
|
? 'Dein Passwort wurde erfolgreich geändert.'
|
||||||
: 'Wähle ein sicheres neues Passwort für deinen Account.'
|
: 'Wähle ein sicheres neues Passwort für deinen Account.'
|
||||||
@@ -110,10 +110,10 @@ export function ResetPassword() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{success ? (
|
{success ? (
|
||||||
<div className="text-center py-8">
|
<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">
|
<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" />
|
<CheckCircle className="w-8 h-8 text-green-600 dark:text-green-400" />
|
||||||
</div>
|
</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.
|
Du kannst dich jetzt mit deinem neuen Passwort anmelden.
|
||||||
</p>
|
</p>
|
||||||
<Button onClick={() => navigate('/login')} className="w-full">
|
<Button onClick={() => navigate('/login')} className="w-full">
|
||||||
@@ -122,11 +122,11 @@ export function ResetPassword() {
|
|||||||
</div>
|
</div>
|
||||||
) : !userId || !secret ? (
|
) : !userId || !secret ? (
|
||||||
<div className="text-center py-8">
|
<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">
|
<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" />
|
<XCircle className="w-8 h-8 text-red-600 dark:text-red-400" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-semibold text-slate-900 mb-2">Ungültiger Link</h3>
|
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-2">Ungültiger Link</h3>
|
||||||
<p className="text-slate-600 mb-6">
|
<p className="text-slate-600 dark:text-slate-400 mb-6">
|
||||||
Dieser Link zum Zurücksetzen des Passworts ist ungültig oder abgelaufen.
|
Dieser Link zum Zurücksetzen des Passworts ist ungültig oder abgelaufen.
|
||||||
</p>
|
</p>
|
||||||
<Link to="/forgot-password">
|
<Link to="/forgot-password">
|
||||||
@@ -136,13 +136,13 @@ export function ResetPassword() {
|
|||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
{error && (
|
{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}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<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">
|
<div className="relative">
|
||||||
<Input
|
<Input
|
||||||
id="password"
|
id="password"
|
||||||
@@ -152,11 +152,12 @@ export function ResetPassword() {
|
|||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
|
className="dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
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" />}
|
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -172,13 +173,13 @@ export function ResetPassword() {
|
|||||||
className={`h-1 flex-1 rounded-full transition-colors ${
|
className={`h-1 flex-1 rounded-full transition-colors ${
|
||||||
level <= passwordStrength.strength
|
level <= passwordStrength.strength
|
||||||
? passwordStrength.color
|
? passwordStrength.color
|
||||||
: 'bg-slate-200'
|
: 'bg-slate-200 dark:bg-slate-700'
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className={`text-xs ${
|
<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}
|
{passwordStrength.label}
|
||||||
</p>
|
</p>
|
||||||
@@ -187,7 +188,7 @@ export function ResetPassword() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<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
|
<Input
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
type={showPassword ? 'text' : 'password'}
|
type={showPassword ? 'text' : 'password'}
|
||||||
@@ -195,9 +196,10 @@ export function ResetPassword() {
|
|||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
|
className="dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
|
||||||
/>
|
/>
|
||||||
{confirmPassword && password !== confirmPassword && (
|
{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>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -283,17 +283,17 @@ export function Setup() {
|
|||||||
// Show loading while checking accounts
|
// Show loading while checking accounts
|
||||||
if (checkingAccounts) {
|
if (checkingAccounts) {
|
||||||
return (
|
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">
|
<div className="text-center">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-primary-600 mx-auto mb-4" />
|
<Loader2 className="w-8 h-8 animate-spin text-primary-600 dark:text-primary-400 mx-auto mb-4" />
|
||||||
<p className="text-slate-600">Setting up your account...</p>
|
<p className="text-slate-600 dark:text-slate-400">Setting up your account...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<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="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -315,13 +315,13 @@ export function Setup() {
|
|||||||
{/* Success message after checkout */}
|
{/* Success message after checkout */}
|
||||||
{isFromCheckout && (
|
{isFromCheckout && (
|
||||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pt-8">
|
<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="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 flex items-center justify-center flex-shrink-0">
|
<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" />
|
<Check className="w-6 h-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h3 className="font-semibold text-green-900 mb-1">Payment successful!</h3>
|
<h3 className="font-semibold text-green-900 dark:text-green-200 mb-1">Payment successful!</h3>
|
||||||
<p className="text-sm text-green-700">
|
<p className="text-sm text-green-700 dark:text-green-300">
|
||||||
Your subscription is active. Let's connect your email account to get started.
|
Your subscription is active. Let's connect your email account to get started.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -351,23 +351,23 @@ export function Setup() {
|
|||||||
<div
|
<div
|
||||||
className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold transition-all duration-300 ${
|
className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold transition-all duration-300 ${
|
||||||
index < stepIndex
|
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
|
: index === stepIndex
|
||||||
? 'bg-primary-500 text-white ring-4 ring-primary-100 shadow-lg shadow-primary-500/30'
|
? '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 text-slate-400'
|
: 'bg-slate-100 dark:bg-slate-700 text-slate-400 dark:text-slate-500'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{index < stepIndex ? <Check className="w-5 h-5" /> : index + 1}
|
{index < stepIndex ? <Check className="w-5 h-5" /> : index + 1}
|
||||||
</div>
|
</div>
|
||||||
<p className={`mt-2 text-xs font-medium hidden sm:block transition-colors ${
|
<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}
|
{step.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{index < steps.length - 1 && (
|
{index < steps.length - 1 && (
|
||||||
<div className={`w-16 sm:w-24 h-1 mx-2 rounded-full transition-colors duration-500 ${
|
<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>
|
</div>
|
||||||
@@ -376,7 +376,7 @@ export function Setup() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{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" />
|
<AlertCircle className="w-5 h-5 flex-shrink-0" />
|
||||||
<p>{error}</p>
|
<p>{error}</p>
|
||||||
</div>
|
</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">
|
<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" />
|
<Link2 className="w-12 h-12 text-primary-600" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl font-bold text-slate-900 mb-3">Connect your email account</h1>
|
<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 mb-10 max-w-md mx-auto">
|
<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.
|
Choose your email provider. The connection is secure and your data stays private.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -416,10 +416,10 @@ export function Setup() {
|
|||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 flex items-center">
|
<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>
|
||||||
<div className="relative flex justify-center text-sm">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -427,12 +427,12 @@ export function Setup() {
|
|||||||
<button
|
<button
|
||||||
onClick={handleConnectGmail}
|
onClick={handleConnectGmail}
|
||||||
disabled={connecting !== null}
|
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' ? (
|
{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">
|
<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="#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"/>
|
<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>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="font-semibold text-slate-900">Gmail</p>
|
<p className="font-semibold text-slate-900 dark:text-slate-100">Gmail</p>
|
||||||
<p className="text-sm text-slate-500">Google Workspace</p>
|
<p className="text-sm text-slate-500 dark:text-slate-400">Google Workspace</p>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleConnectOutlook}
|
onClick={handleConnectOutlook}
|
||||||
disabled={connecting !== null}
|
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' ? (
|
{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">
|
<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"/>
|
<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>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="font-semibold text-slate-900">Outlook</p>
|
<p className="font-semibold text-slate-900 dark:text-slate-100">Outlook</p>
|
||||||
<p className="text-sm text-slate-500">Microsoft 365</p>
|
<p className="text-sm text-slate-500 dark:text-slate-400">Microsoft 365</p>
|
||||||
</div>
|
</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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-10 p-4 bg-slate-50 rounded-xl max-w-lg mx-auto">
|
<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">
|
<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.
|
🔒 Your data is secure. We don't store email content and only have read access.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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">
|
<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" />
|
<Settings className="w-12 h-12 text-primary-600" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl font-bold text-slate-900 mb-3">Sorting Settings</h1>
|
<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 max-w-md mx-auto">
|
<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.
|
Customize how strictly the AI should sort your emails.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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">
|
<CardContent className="p-8 space-y-8">
|
||||||
<div>
|
<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">
|
<div className="grid grid-cols-3 gap-3">
|
||||||
{[
|
{[
|
||||||
{ id: 'light', name: 'Light', desc: 'Only obvious distractions', emoji: '🌱' },
|
{ id: 'light', name: 'Light', desc: 'Only obvious distractions', emoji: '🌱' },
|
||||||
@@ -506,30 +506,30 @@ export function Setup() {
|
|||||||
onClick={() => setPreferences(p => ({ ...p, sortingStrictness: option.id }))}
|
onClick={() => setPreferences(p => ({ ...p, sortingStrictness: option.id }))}
|
||||||
className={`p-4 rounded-xl border-2 text-center transition-all ${
|
className={`p-4 rounded-xl border-2 text-center transition-all ${
|
||||||
preferences.sortingStrictness === option.id
|
preferences.sortingStrictness === option.id
|
||||||
? 'border-primary-500 bg-primary-50 shadow-lg shadow-primary-500/10'
|
? 'border-primary-500 dark:border-primary-400 bg-primary-50 dark:bg-primary-900/30 shadow-lg shadow-primary-500/10'
|
||||||
: 'border-slate-200 hover:border-slate-300 bg-white'
|
: '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>
|
<span className="text-2xl mb-2 block">{option.emoji}</span>
|
||||||
<p className="font-semibold text-slate-900">{option.name}</p>
|
<p className="font-semibold text-slate-900 dark:text-slate-100">{option.name}</p>
|
||||||
<p className="text-xs text-slate-500 mt-1">{option.desc}</p>
|
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1">{option.desc}</p>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</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>
|
<div>
|
||||||
<p className="font-semibold text-slate-900">Historical emails</p>
|
<p className="font-semibold text-slate-900 dark:text-slate-100">Historical emails</p>
|
||||||
<p className="text-sm text-slate-500">Analyze and sort last 30 days</p>
|
<p className="text-sm text-slate-500 dark:text-slate-400">Analyze and sort last 30 days</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setPreferences(p => ({ ...p, historicalSync: !p.historicalSync }))}
|
onClick={() => setPreferences(p => ({ ...p, historicalSync: !p.historicalSync }))}
|
||||||
className={`w-14 h-8 rounded-full transition-all duration-300 ${
|
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'
|
preferences.historicalSync ? 'translate-x-7' : 'translate-x-1'
|
||||||
}`} />
|
}`} />
|
||||||
</button>
|
</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">
|
<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" />
|
<Zap className="w-12 h-12 text-primary-600" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl font-bold text-slate-900 mb-3">Choose your categories</h1>
|
<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 max-w-md mx-auto">
|
<p className="text-lg text-slate-600 dark:text-slate-400 max-w-md mx-auto">
|
||||||
Which categories should your emails be sorted into?
|
Which categories should your emails be sorted into?
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -558,21 +558,21 @@ export function Setup() {
|
|||||||
onClick={() => toggleCategory(category.id)}
|
onClick={() => toggleCategory(category.id)}
|
||||||
className={`flex items-center gap-4 p-5 rounded-xl border-2 text-left transition-all ${
|
className={`flex items-center gap-4 p-5 rounded-xl border-2 text-left transition-all ${
|
||||||
selectedCategories.includes(category.id)
|
selectedCategories.includes(category.id)
|
||||||
? 'border-primary-500 bg-primary-50 shadow-lg shadow-primary-500/10'
|
? 'border-primary-500 dark:border-primary-400 bg-primary-50 dark:bg-primary-900/30 shadow-lg shadow-primary-500/10'
|
||||||
: 'border-slate-200 bg-white hover:border-slate-300 hover:shadow-md'
|
: '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`}>
|
<div className={`w-12 h-12 rounded-xl ${category.color} flex items-center justify-center text-2xl shadow-lg`}>
|
||||||
{category.icon}
|
{category.icon}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="font-semibold text-slate-900">{category.name}</p>
|
<p className="font-semibold text-slate-900 dark:text-slate-100">{category.name}</p>
|
||||||
<p className="text-sm text-slate-500">{category.description}</p>
|
<p className="text-sm text-slate-500 dark:text-slate-400">{category.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={`w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all ${
|
<div className={`w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all ${
|
||||||
selectedCategories.includes(category.id)
|
selectedCategories.includes(category.id)
|
||||||
? 'border-primary-500 bg-primary-500'
|
? 'border-primary-500 dark:border-primary-400 bg-primary-500 dark:bg-primary-600'
|
||||||
: 'border-slate-300'
|
: 'border-slate-300 dark:border-slate-600'
|
||||||
}`}>
|
}`}>
|
||||||
{selectedCategories.includes(category.id) && <Check className="w-4 h-4 text-white" />}
|
{selectedCategories.includes(category.id) && <Check className="w-4 h-4 text-white" />}
|
||||||
</div>
|
</div>
|
||||||
@@ -580,7 +580,7 @@ export function Setup() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</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.
|
You can change these categories later in settings.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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">
|
<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" />
|
<Sparkles className="w-14 h-14 text-green-600" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl font-bold text-slate-900 mb-4">All set! 🎉</h1>
|
<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 mb-10 max-w-md mx-auto">
|
<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.
|
Your email account is connected. The AI will now start intelligent sorting.
|
||||||
</p>
|
</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="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 flex items-center justify-center shadow-md">
|
<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" />
|
<Mail className="w-7 h-7 text-primary-500 dark:text-primary-400" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<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
|
{connectedProvider === 'gmail' ? 'Gmail' : connectedProvider === 'outlook' ? 'Outlook' : 'Email'} connected
|
||||||
</p>
|
</p>
|
||||||
<p className="text-slate-500">{connectedEmail || user?.email}</p>
|
<p className="text-slate-500 dark:text-slate-400">{connectedEmail || user?.email}</p>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="success" className="text-sm px-3 py-1">Active</Badge>
|
<Badge variant="success" className="text-sm px-3 py-1">Active</Badge>
|
||||||
</div>
|
</div>
|
||||||
@@ -628,7 +628,7 @@ export function Setup() {
|
|||||||
|
|
||||||
{currentStep !== 'connect' && currentStep !== 'complete' && (
|
{currentStep !== 'connect' && currentStep !== 'complete' && (
|
||||||
<div className="flex justify-between max-w-lg mx-auto">
|
<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" />
|
<ArrowLeft className="w-5 h-5 mr-2" />
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -51,26 +51,26 @@ export function VerifyEmail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<div className="w-full max-w-md">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link to="/" className="flex items-center justify-center gap-2 mb-8">
|
<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">
|
<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" />
|
<Mail className="w-5 h-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xl font-bold text-slate-900">
|
<span className="text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Email<span className="text-primary-600">Sorter</span>
|
Email<span className="text-primary-600 dark:text-primary-400">Sorter</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</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">
|
<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 === 'loading' && 'E-Mail wird verifiziert...'}
|
||||||
{status === 'success' && 'E-Mail verifiziert!'}
|
{status === 'success' && 'E-Mail verifiziert!'}
|
||||||
{status === 'error' && 'Verifizierung fehlgeschlagen'}
|
{status === 'error' && 'Verifizierung fehlgeschlagen'}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription className="dark:text-slate-400">
|
||||||
{status === 'loading' && 'Bitte warte einen Moment.'}
|
{status === 'loading' && 'Bitte warte einen Moment.'}
|
||||||
{status === 'success' && 'Deine E-Mail-Adresse wurde erfolgreich bestätigt.'}
|
{status === 'success' && 'Deine E-Mail-Adresse wurde erfolgreich bestätigt.'}
|
||||||
{status === 'error' && error}
|
{status === 'error' && error}
|
||||||
@@ -79,25 +79,25 @@ export function VerifyEmail() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{status === 'loading' && (
|
{status === 'loading' && (
|
||||||
<div className="flex flex-col items-center py-12">
|
<div className="flex flex-col items-center py-12">
|
||||||
<Loader2 className="w-12 h-12 animate-spin text-primary-500 mb-4" />
|
<Loader2 className="w-12 h-12 animate-spin text-primary-500 dark:text-primary-400 mb-4" />
|
||||||
<p className="text-slate-500">Verifizierung läuft...</p>
|
<p className="text-slate-500 dark:text-slate-400">Verifizierung läuft...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{status === 'success' && (
|
{status === 'success' && (
|
||||||
<div className="text-center py-8">
|
<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">
|
<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" />
|
<CheckCircle className="w-10 h-10 text-green-600 dark:text-green-400" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="p-4 bg-green-50 border border-green-100 rounded-xl">
|
<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 font-medium">
|
<p className="text-green-700 dark:text-green-300 font-medium">
|
||||||
Dein Account ist jetzt vollständig aktiviert!
|
Dein Account ist jetzt vollständig aktiviert!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-400">
|
||||||
Du kannst jetzt alle Features von EmailSorter nutzen.
|
Du kannst jetzt alle Features von EmailSorter nutzen.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -110,18 +110,18 @@ export function VerifyEmail() {
|
|||||||
|
|
||||||
{status === 'error' && (
|
{status === 'error' && (
|
||||||
<div className="text-center py-8">
|
<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">
|
<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" />
|
<XCircle className="w-10 h-10 text-red-600 dark:text-red-400" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="p-4 bg-red-50 border border-red-100 rounded-xl">
|
<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">
|
<p className="text-red-700 dark:text-red-300">
|
||||||
{error || 'Der Verifizierungslink ist ungültig oder abgelaufen.'}
|
{error || 'Der Verifizierungslink ist ungültig oder abgelaufen.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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.
|
Falls dein Link abgelaufen ist, kannst du eine neue Verifizierungs-E-Mail anfordern.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -142,9 +142,9 @@ export function VerifyEmail() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Help text */}
|
{/* 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{' '}
|
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
|
support@emailsorter.de
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
60
docs/README.md
Normal file
60
docs/README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Dokumentation
|
||||||
|
|
||||||
|
Diese Dokumentation ist in verschiedene Kategorien unterteilt:
|
||||||
|
|
||||||
|
## 📁 Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── setup/ # Setup-Anleitungen
|
||||||
|
├── deployment/ # Deployment & Production
|
||||||
|
├── development/ # Development-Dokumentation
|
||||||
|
├── server/ # Server-spezifische Docs
|
||||||
|
├── examples/ # Beispiel-Code
|
||||||
|
└── legacy/ # Legacy-Dateien
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Kategorien
|
||||||
|
|
||||||
|
### Setup (`docs/setup/`)
|
||||||
|
- **APPWRITE_SETUP.md** - Appwrite Installation & Konfiguration
|
||||||
|
- **APPWRITE_CORS_SETUP.md** - CORS-Konfiguration für Appwrite
|
||||||
|
- **GOOGLE_OAUTH_SETUP.md** - Google OAuth Setup
|
||||||
|
- **SETUP_GUIDE.md** - Allgemeine Setup-Anleitung
|
||||||
|
- **FAVICON_SETUP.md** - Favicon-Konfiguration
|
||||||
|
|
||||||
|
### Deployment (`docs/deployment/`)
|
||||||
|
- **README.md** - Deployment-Übersicht
|
||||||
|
- **GITEA_WEBHOOK_SETUP.md** - Vollständige Anleitung für automatisches Deployment via Gitea Webhook
|
||||||
|
- **WEBHOOK_QUICK_START.md** - Schnellstart-Anleitung (5 Minuten)
|
||||||
|
- **WEBHOOK_AUTHORIZATION.md** - Webhook-Authentifizierung und Sicherheit
|
||||||
|
- **PRODUCTION_SETUP.md** - Production-Server Setup
|
||||||
|
- **PRODUCTION_FIXES.md** - Production-Fixes & Troubleshooting
|
||||||
|
- **DEPLOYMENT_INSTRUCTIONS.md** - Manuelle Deployment-Anleitungen
|
||||||
|
|
||||||
|
### Development (`docs/development/`)
|
||||||
|
- **GIT_AUTHENTICATION_FIX.md** - Git-Authentifizierung
|
||||||
|
- **PROJECT_RENAME_GUIDE.md** - Projekt-Umbenennung
|
||||||
|
- **PROJECT_REVIEW_SUMMARY.md** - Projekt-Review
|
||||||
|
- **TASK_5_COMPLETION.md** - Task-Completion-Dokumentation
|
||||||
|
- **TESTING_SUMMARY.md** - Testing-Zusammenfassung
|
||||||
|
|
||||||
|
### Server (`docs/server/`)
|
||||||
|
- **CORRECTNESS_VALIDATION.md** - Korrektheits-Validierung
|
||||||
|
- **E2E_TEST_GUIDE.md** - End-to-End Test Guide
|
||||||
|
- **ENDPOINT_VERIFICATION.md** - API-Endpoint-Verifikation
|
||||||
|
- **FRONTEND_VERIFICATION.md** - Frontend-Verifikation
|
||||||
|
- **MANUAL_TEST_CHECKLIST.md** - Manuelle Test-Checkliste
|
||||||
|
- **TASK_4_COMPLETION_SUMMARY.md** - Task 4 Completion
|
||||||
|
|
||||||
|
### Examples (`docs/examples/`)
|
||||||
|
- **starter-for-react/** - React Starter Template (Beispiel)
|
||||||
|
|
||||||
|
### Legacy (`docs/legacy/`)
|
||||||
|
- **public/** - Alte Public-Dateien (falls noch benötigt)
|
||||||
|
|
||||||
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
|
1. **Erstes Setup:** Siehe `docs/setup/SETUP_GUIDE.md`
|
||||||
|
2. **Production Deployment:** Siehe `docs/deployment/PRODUCTION_SETUP.md`
|
||||||
|
3. **Development:** Siehe `docs/development/` für Development-Dokumentation
|
||||||
219
docs/deployment/GITEA_WEBHOOK_SETUP.md
Normal file
219
docs/deployment/GITEA_WEBHOOK_SETUP.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# Gitea Webhook Setup - Automatisches Deployment
|
||||||
|
|
||||||
|
Diese Anleitung erklärt, wie du einen Gitea-Webhook einrichtest, um automatisch zu deployen, wenn Code gepusht wird.
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Der Webhook funktioniert folgendermaßen:
|
||||||
|
1. **Push auf Gitea** → Gitea sendet Webhook-Event an deinen Server
|
||||||
|
2. **Webhook-Handler** empfängt das Event und verifiziert die Signatur
|
||||||
|
3. **Deployment-Skript** wird ausgeführt:
|
||||||
|
- Git Pull (falls auf Server)
|
||||||
|
- Frontend Build (`npm run build`)
|
||||||
|
- Upload auf Production-Server (via SCP/SSH)
|
||||||
|
- Backend Neustart (optional, via PM2)
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- ✅ Gitea-Repository mit deinem Code
|
||||||
|
- ✅ Production-Server mit SSH-Zugriff
|
||||||
|
- ✅ Node.js auf dem Server installiert
|
||||||
|
- ✅ PM2 installiert (optional, für Backend-Neustart)
|
||||||
|
|
||||||
|
## Schritt 1: Webhook-Secret generieren
|
||||||
|
|
||||||
|
Generiere ein sicheres Secret für die Webhook-Signatur-Verification:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generiere ein zufälliges Secret (32 Zeichen)
|
||||||
|
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Speichere dieses Secret sicher - du brauchst es in Schritt 3 und 4.
|
||||||
|
|
||||||
|
## Schritt 2: Environment Variables konfigurieren
|
||||||
|
|
||||||
|
Füge folgende Variablen zu deiner `server/.env` Datei hinzu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Gitea Webhook Secret (aus Schritt 1)
|
||||||
|
GITEA_WEBHOOK_SECRET=dein_generiertes_secret_hier
|
||||||
|
|
||||||
|
# Optional: Authorization Header Token
|
||||||
|
GITEA_WEBHOOK_AUTH_TOKEN=dein_auth_token_hier
|
||||||
|
|
||||||
|
# Server-Deployment (optional, nur wenn automatischer Upload gewünscht)
|
||||||
|
DEPLOY_SERVER_HOST=91.99.156.85
|
||||||
|
DEPLOY_SERVER_USER=root
|
||||||
|
DEPLOY_SERVER_PATH=/var/www/emailsorter
|
||||||
|
DEPLOY_SSH_KEY=/path/to/ssh/private/key # Optional, falls SSH-Key benötigt wird
|
||||||
|
DEPLOY_FRONTEND_PATH=/var/www/emailsorter/client/dist
|
||||||
|
DEPLOY_BACKEND_PATH=/var/www/emailsorter/server
|
||||||
|
|
||||||
|
# PM2 für Backend-Neustart (optional)
|
||||||
|
USE_PM2=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Schritt 3: Webhook in Gitea konfigurieren
|
||||||
|
|
||||||
|
1. **Öffne dein Repository** in Gitea
|
||||||
|
2. Gehe zu **Settings** → **Webhooks**
|
||||||
|
3. Klicke auf **Add Webhook** → **Gitea**
|
||||||
|
4. Fülle die Felder aus:
|
||||||
|
|
||||||
|
- **Target URL:**
|
||||||
|
```
|
||||||
|
https://emailsorter.webklar.com/api/webhook/gitea
|
||||||
|
```
|
||||||
|
(Ersetze mit deiner tatsächlichen Domain)
|
||||||
|
|
||||||
|
- **HTTP Method:** `POST`
|
||||||
|
|
||||||
|
- **Post Content Type:** `application/json`
|
||||||
|
|
||||||
|
- **Secret:**
|
||||||
|
```
|
||||||
|
dein_generiertes_secret_hier
|
||||||
|
```
|
||||||
|
(Das gleiche Secret wie in Schritt 1)
|
||||||
|
|
||||||
|
- **Authorization Header:** (Optional)
|
||||||
|
```
|
||||||
|
Bearer dein_auth_token_hier
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Trigger On:**
|
||||||
|
- ✅ **Push Events** (wichtig!)
|
||||||
|
- Optional: **Create**, **Delete** (falls gewünscht)
|
||||||
|
|
||||||
|
- **Branch Filter:** `main` oder `master` (je nach deinem Standard-Branch)
|
||||||
|
|
||||||
|
5. Klicke auf **Add Webhook**
|
||||||
|
|
||||||
|
## Schritt 4: Webhook testen
|
||||||
|
|
||||||
|
### Option A: Test über Gitea UI
|
||||||
|
|
||||||
|
1. Gehe zurück zu **Settings** → **Webhooks**
|
||||||
|
2. Klicke auf deinen Webhook
|
||||||
|
3. Klicke auf **Test Delivery** → **Push Events**
|
||||||
|
4. Prüfe die Antwort:
|
||||||
|
- ✅ **Status 202** = Webhook empfangen, Deployment gestartet
|
||||||
|
- ❌ **Status 401** = Secret falsch
|
||||||
|
- ❌ **Status 500** = Server-Fehler (prüfe Server-Logs)
|
||||||
|
|
||||||
|
### Option B: Test über Git Push
|
||||||
|
|
||||||
|
1. Mache eine kleine Änderung (z.B. Kommentar in einer Datei)
|
||||||
|
2. Committe und pushe:
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "test: Webhook test"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
3. Prüfe die Server-Logs:
|
||||||
|
```bash
|
||||||
|
# Auf dem Server
|
||||||
|
pm2 logs emailsorter-backend
|
||||||
|
# Oder
|
||||||
|
tail -f /var/log/emailsorter/webhook.log
|
||||||
|
```
|
||||||
|
4. Du solltest sehen:
|
||||||
|
```
|
||||||
|
📥 Gitea Webhook empfangen
|
||||||
|
🚀 Starte Deployment...
|
||||||
|
📦 Baue Frontend...
|
||||||
|
✅ Deployment erfolgreich abgeschlossen
|
||||||
|
```
|
||||||
|
|
||||||
|
## Schritt 5: Deployment-Logs prüfen
|
||||||
|
|
||||||
|
Die Webhook-Handler loggen alle Schritte. Prüfe die Logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PM2 Logs
|
||||||
|
pm2 logs emailsorter-backend
|
||||||
|
|
||||||
|
# Oder direkt im Server
|
||||||
|
tail -f server/logs/webhook.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
### Webhook wird nicht ausgelöst
|
||||||
|
|
||||||
|
- ✅ Prüfe, ob die **Target URL** korrekt ist
|
||||||
|
- ✅ Prüfe, ob der Server erreichbar ist (`curl https://emailsorter.webklar.com/api/webhook/status`)
|
||||||
|
- ✅ Prüfe Gitea-Logs: **Settings** → **Webhooks** → **Delivery Log**
|
||||||
|
|
||||||
|
### "Ungültige Webhook-Signatur" (401)
|
||||||
|
|
||||||
|
- ✅ Prüfe, ob `GITEA_WEBHOOK_SECRET` in `server/.env` gesetzt ist
|
||||||
|
- ✅ Prüfe, ob das Secret in Gitea **genau gleich** ist (keine Leerzeichen!)
|
||||||
|
- ✅ Prüfe, ob der Webhook **"application/json"** als Content-Type verwendet
|
||||||
|
|
||||||
|
### Deployment schlägt fehl
|
||||||
|
|
||||||
|
- ✅ Prüfe Server-Logs für detaillierte Fehlermeldungen
|
||||||
|
- ✅ Prüfe, ob SSH-Zugriff funktioniert: `ssh root@91.99.156.85`
|
||||||
|
- ✅ Prüfe, ob `npm` und `node` auf dem Server installiert sind
|
||||||
|
- ✅ Prüfe, ob die Pfade (`DEPLOY_SERVER_PATH`) korrekt sind
|
||||||
|
|
||||||
|
### Frontend-Build fehlgeschlagen
|
||||||
|
|
||||||
|
- ✅ Prüfe, ob alle Dependencies installiert sind: `cd client && npm install`
|
||||||
|
- ✅ Prüfe, ob `.env.production` korrekt konfiguriert ist
|
||||||
|
- ✅ Prüfe Build-Logs für TypeScript/ESLint-Fehler
|
||||||
|
|
||||||
|
### Backend startet nicht neu
|
||||||
|
|
||||||
|
- ✅ Prüfe, ob PM2 installiert ist: `pm2 --version`
|
||||||
|
- ✅ Prüfe, ob `USE_PM2=true` in `.env` gesetzt ist
|
||||||
|
- ✅ Prüfe PM2-Status: `pm2 list`
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
1. **Webhook-Secret:** Verwende immer ein starkes, zufälliges Secret
|
||||||
|
2. **HTTPS:** Stelle sicher, dass dein Server HTTPS verwendet (Let's Encrypt)
|
||||||
|
3. **Firewall:** Beschränke Webhook-Endpoint auf Gitea-IPs (optional)
|
||||||
|
4. **Rate Limiting:** Der Webhook-Endpoint ist bereits rate-limited
|
||||||
|
5. **Logs:** Prüfe regelmäßig die Webhook-Logs auf verdächtige Aktivitäten
|
||||||
|
|
||||||
|
## Alternative: Lokales Deployment ohne Server-Upload
|
||||||
|
|
||||||
|
Falls du den automatischen Upload auf den Server nicht möchtest, kannst du:
|
||||||
|
|
||||||
|
1. `DEPLOY_SERVER_HOST` **nicht** setzen
|
||||||
|
2. Das Deployment-Skript erstellt nur den Build lokal
|
||||||
|
3. Du lädst die Dateien manuell hoch oder verwendest ein anderes Tool
|
||||||
|
|
||||||
|
Der Webhook wird trotzdem ausgelöst und erstellt den Build, aber überspringt den Upload-Schritt.
|
||||||
|
|
||||||
|
## Manuelles Deployment auslösen
|
||||||
|
|
||||||
|
Du kannst das Deployment auch manuell auslösen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auf dem Server
|
||||||
|
cd /var/www/emailsorter
|
||||||
|
node scripts/deploy-to-server.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
Nach erfolgreichem Setup:
|
||||||
|
|
||||||
|
1. ✅ Teste den Webhook mit einem kleinen Push
|
||||||
|
2. ✅ Prüfe, ob die Website aktualisiert wurde
|
||||||
|
3. ✅ Überwache die Logs für die ersten Deployments
|
||||||
|
4. ✅ Dokumentiere deine spezifische Konfiguration
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Bei Problemen:
|
||||||
|
- Prüfe die Server-Logs
|
||||||
|
- Prüfe Gitea Webhook Delivery Logs
|
||||||
|
- Prüfe die Environment Variables
|
||||||
|
- Teste SSH-Verbindung manuell
|
||||||
51
docs/deployment/PRODUCTION_FIXES.md
Normal file
51
docs/deployment/PRODUCTION_FIXES.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Production Fixes - Wichtige Schritte
|
||||||
|
|
||||||
|
## ✅ Behoben
|
||||||
|
|
||||||
|
1. **Debug-Logs entfernt** - Alle Debug-Logs zu `127.0.0.1:7242` wurden entfernt
|
||||||
|
2. **Favicon-Problem behoben** - `site.webmanifest` verwendet jetzt vorhandene SVG-Dateien
|
||||||
|
|
||||||
|
## ⚠️ Noch zu beheben (im Appwrite Dashboard)
|
||||||
|
|
||||||
|
### 1. Appwrite CORS-Konfiguration
|
||||||
|
|
||||||
|
**Problem:** Appwrite erlaubt nur `https://localhost` statt `https://emailsorter.webklar.com`
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Gehe zu: https://appwrite.webklar.com
|
||||||
|
2. Öffne dein Projekt
|
||||||
|
3. Gehe zu **Settings** → **Platforms** (oder **Web**)
|
||||||
|
4. Füge eine neue Platform hinzu:
|
||||||
|
- **Name:** Production
|
||||||
|
- **Hostname:** `emailsorter.webklar.com`
|
||||||
|
- **Origin:** `https://emailsorter.webklar.com`
|
||||||
|
5. Speichere die Änderungen
|
||||||
|
|
||||||
|
**ODER** bearbeite die existierende Platform und ändere den Hostname/Origin zu `https://emailsorter.webklar.com`
|
||||||
|
|
||||||
|
### 2. Backend-Server (502 Bad Gateway)
|
||||||
|
|
||||||
|
**Problem:** `/api/analytics/track` gibt 502 zurück - Backend-Server läuft nicht
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. SSH zum Server: `ssh user@webklar.com`
|
||||||
|
2. Prüfe ob Server läuft: `pm2 list` oder `ps aux | grep node`
|
||||||
|
3. Falls nicht: Starte den Server:
|
||||||
|
```bash
|
||||||
|
cd /path/to/ANDJJJJJJ/server
|
||||||
|
pm2 start index.mjs --name emailsorter-api
|
||||||
|
pm2 save
|
||||||
|
```
|
||||||
|
4. Prüfe Logs: `pm2 logs emailsorter-api`
|
||||||
|
|
||||||
|
### 3. Build deployen
|
||||||
|
|
||||||
|
Nach dem Commit und Push:
|
||||||
|
1. Kopiere den Inhalt von `client/dist` auf den Web-Server
|
||||||
|
2. Stelle sicher, dass die Dateien unter `https://emailsorter.webklar.com` erreichbar sind
|
||||||
|
|
||||||
|
## Nach allen Fixes
|
||||||
|
|
||||||
|
1. Leere den Browser-Cache (Strg+Shift+R)
|
||||||
|
2. Teste die Website
|
||||||
|
3. Prüfe die Browser-Konsole - sollte keine Fehler mehr zeigen
|
||||||
60
docs/deployment/README.md
Normal file
60
docs/deployment/README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# Deployment-Dokumentation
|
||||||
|
|
||||||
|
Diese Dokumentation beschreibt alle Aspekte des Deployments für E-Mail-Sorter.
|
||||||
|
|
||||||
|
## 📚 Inhaltsverzeichnis
|
||||||
|
|
||||||
|
### Automatisches Deployment
|
||||||
|
|
||||||
|
- **[Gitea Webhook Setup](./GITEA_WEBHOOK_SETUP.md)** - Vollständige Anleitung für automatisches Deployment via Gitea Webhook
|
||||||
|
- **[Webhook Quick Start](./WEBHOOK_QUICK_START.md)** - Schnellstart-Anleitung (5 Minuten)
|
||||||
|
- **[Webhook Authorization](./WEBHOOK_AUTHORIZATION.md)** - Authentifizierung und Sicherheit
|
||||||
|
|
||||||
|
### Manuelles Deployment
|
||||||
|
|
||||||
|
- **[Deployment Instructions](./DEPLOYMENT_INSTRUCTIONS.md)** - Manuelle Deployment-Schritte
|
||||||
|
- **[Production Setup](./PRODUCTION_SETUP.md)** - Production-Server Setup
|
||||||
|
- **[Production Fixes](./PRODUCTION_FIXES.md)** - Bekannte Probleme und Lösungen
|
||||||
|
|
||||||
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
|
Für automatisches Deployment siehe [Webhook Quick Start](./WEBHOOK_QUICK_START.md).
|
||||||
|
|
||||||
|
## 📋 Übersicht
|
||||||
|
|
||||||
|
### Automatisches Deployment (Empfohlen)
|
||||||
|
|
||||||
|
1. **Gitea Webhook einrichten** → Siehe [GITEA_WEBHOOK_SETUP.md](./GITEA_WEBHOOK_SETUP.md)
|
||||||
|
2. **Bei jedem Push** wird automatisch deployed
|
||||||
|
3. **Keine manuellen Schritte** nötig
|
||||||
|
|
||||||
|
### Manuelles Deployment
|
||||||
|
|
||||||
|
1. **Frontend bauen:** `cd client && npm run build`
|
||||||
|
2. **Dateien hochladen** auf Server
|
||||||
|
3. **Backend neustarten** (falls nötig)
|
||||||
|
|
||||||
|
Siehe [DEPLOYMENT_INSTRUCTIONS.md](./DEPLOYMENT_INSTRUCTIONS.md) für Details.
|
||||||
|
|
||||||
|
## 🔧 Konfiguration
|
||||||
|
|
||||||
|
Alle Deployment-Konfigurationen finden sich in `server/.env`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Webhook-Konfiguration
|
||||||
|
GITEA_WEBHOOK_SECRET=...
|
||||||
|
GITEA_WEBHOOK_AUTH_TOKEN=...
|
||||||
|
|
||||||
|
# Server-Deployment
|
||||||
|
DEPLOY_SERVER_HOST=91.99.156.85
|
||||||
|
DEPLOY_SERVER_USER=root
|
||||||
|
DEPLOY_SERVER_PATH=/var/www/emailsorter
|
||||||
|
USE_PM2=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
Bei Problemen:
|
||||||
|
1. Prüfe die Server-Logs
|
||||||
|
2. Siehe [Production Fixes](./PRODUCTION_FIXES.md)
|
||||||
|
3. Prüfe Webhook Delivery Logs in Gitea
|
||||||
83
docs/deployment/WEBHOOK_AUTHORIZATION.md
Normal file
83
docs/deployment/WEBHOOK_AUTHORIZATION.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Webhook Authorization Header - Anleitung
|
||||||
|
|
||||||
|
Der Webhook unterstützt **zwei Authentifizierungsmethoden**:
|
||||||
|
|
||||||
|
1. **Signature-Verification** (Standard, von Gitea)
|
||||||
|
2. **Authorization Header** (Optional, zusätzliche Sicherheit)
|
||||||
|
|
||||||
|
## Option 1: Nur Signature (Standard)
|
||||||
|
|
||||||
|
Das ist die Standard-Methode, die Gitea automatisch verwendet:
|
||||||
|
|
||||||
|
### Konfiguration
|
||||||
|
|
||||||
|
In `server/.env`:
|
||||||
|
```bash
|
||||||
|
GITEA_WEBHOOK_SECRET=dein_secret_hier
|
||||||
|
```
|
||||||
|
|
||||||
|
### In Gitea
|
||||||
|
|
||||||
|
- **Secret:** Trage das gleiche Secret ein
|
||||||
|
- **Authorization Header:** Nicht nötig
|
||||||
|
|
||||||
|
Gitea sendet automatisch den `X-Gitea-Signature` Header.
|
||||||
|
|
||||||
|
## Option 2: Authorization Header (Zusätzliche Sicherheit)
|
||||||
|
|
||||||
|
Falls du zusätzliche Sicherheit möchtest oder den Webhook manuell aufrufst:
|
||||||
|
|
||||||
|
### Schritt 1: Token generieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 2: Server konfigurieren
|
||||||
|
|
||||||
|
In `server/.env`:
|
||||||
|
```bash
|
||||||
|
GITEA_WEBHOOK_SECRET=dein_secret_hier
|
||||||
|
GITEA_WEBHOOK_AUTH_TOKEN=dein_auth_token_hier
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3: In Gitea konfigurieren
|
||||||
|
|
||||||
|
Gitea unterstützt **keine** Authorization Header direkt, aber du kannst:
|
||||||
|
|
||||||
|
#### Option A: Nur Signature verwenden (empfohlen)
|
||||||
|
- Lass `GITEA_WEBHOOK_AUTH_TOKEN` leer
|
||||||
|
- Nur `GITEA_WEBHOOK_SECRET` verwenden
|
||||||
|
|
||||||
|
#### Option B: Manuelle Webhook-Aufrufe
|
||||||
|
Wenn du den Webhook manuell aufrufst (z.B. via curl), verwende:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://emailsorter.webklar.com/api/webhook/gitea \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Gitea-Signature: sha256=..." \
|
||||||
|
-H "Authorization: Bearer dein_auth_token_hier" \
|
||||||
|
-d '{"ref":"refs/heads/main",...}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Option 3: Beide Methoden kombinieren
|
||||||
|
|
||||||
|
Für maximale Sicherheit kannst du beide verwenden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In server/.env
|
||||||
|
GITEA_WEBHOOK_SECRET=secret_fuer_signature
|
||||||
|
GITEA_WEBHOOK_AUTH_TOKEN=token_fuer_auth_header
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Wenn beide gesetzt sind, müssen **beide** passen
|
||||||
|
- Wenn nur eine gesetzt ist, reicht diese
|
||||||
|
|
||||||
|
## Empfehlung
|
||||||
|
|
||||||
|
**Für Gitea-Webhooks:** Verwende nur `GITEA_WEBHOOK_SECRET` (Signature)
|
||||||
|
|
||||||
|
**Für manuelle Aufrufe:** Verwende `GITEA_WEBHOOK_AUTH_TOKEN` (Authorization Header)
|
||||||
|
|
||||||
|
**Für maximale Sicherheit:** Verwende beide
|
||||||
61
docs/deployment/WEBHOOK_QUICK_START.md
Normal file
61
docs/deployment/WEBHOOK_QUICK_START.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Gitea Webhook - Quick Start Guide
|
||||||
|
|
||||||
|
## 🚀 Schnellstart (5 Minuten)
|
||||||
|
|
||||||
|
### Schritt 1: Secret generieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
Kopiere das generierte Secret - du brauchst es gleich!
|
||||||
|
|
||||||
|
### Schritt 2: Server konfigurieren
|
||||||
|
|
||||||
|
Füge zu `server/.env` hinzu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GITEA_WEBHOOK_SECRET=dein_generiertes_secret_hier
|
||||||
|
DEPLOY_SERVER_HOST=91.99.156.85
|
||||||
|
DEPLOY_SERVER_USER=root
|
||||||
|
DEPLOY_SERVER_PATH=/var/www/emailsorter
|
||||||
|
USE_PM2=true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3: Gitea Webhook einrichten
|
||||||
|
|
||||||
|
1. Gehe zu deinem Repository → **Settings** → **Webhooks**
|
||||||
|
2. Klicke **Add Webhook** → **Gitea**
|
||||||
|
3. Fülle aus:
|
||||||
|
- **Target URL:** `https://emailsorter.webklar.com/api/webhook/gitea`
|
||||||
|
- **Secret:** `dein_generiertes_secret_hier` (aus Schritt 1)
|
||||||
|
- **Trigger On:** ✅ **Push Events**
|
||||||
|
- **Branch Filter:** `main` oder `master`
|
||||||
|
4. Klicke **Add Webhook**
|
||||||
|
|
||||||
|
### Schritt 4: Testen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "test: Webhook test"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
Prüfe die Server-Logs - du solltest sehen:
|
||||||
|
```
|
||||||
|
📥 Gitea Webhook empfangen
|
||||||
|
🚀 Starte Deployment...
|
||||||
|
✅ Deployment erfolgreich abgeschlossen
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Fertig!
|
||||||
|
|
||||||
|
Jetzt wird bei jedem Push automatisch deployed!
|
||||||
|
|
||||||
|
## 📚 Weitere Informationen
|
||||||
|
|
||||||
|
Siehe [GITEA_WEBHOOK_SETUP.md](./GITEA_WEBHOOK_SETUP.md) für:
|
||||||
|
- Detaillierte Anleitung
|
||||||
|
- Fehlerbehebung
|
||||||
|
- Sicherheitsbest Practices
|
||||||
|
- Server-Upload Konfiguration
|
||||||
|
Before Width: | Height: | Size: 1012 B After Width: | Height: | Size: 1012 B |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
52
scripts/README.md
Normal file
52
scripts/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Scripts
|
||||||
|
|
||||||
|
Dieser Ordner enthält alle Hilfs-Scripts für das Projekt.
|
||||||
|
|
||||||
|
## 🚀 Aktive Scripts
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
- **deploy-build.js** - Node.js Script für Build-Deployment (veraltet, wird durch Webhook ersetzt)
|
||||||
|
- **deploy-to-server.mjs** - Automatisches Deployment-Skript (wird vom Webhook aufgerufen)
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
- **setup-appwrite.ps1** - Appwrite Setup-Script
|
||||||
|
- **setup-production.ps1** - Production Setup-Script
|
||||||
|
|
||||||
|
## 📦 Veraltete Scripts (können entfernt werden)
|
||||||
|
|
||||||
|
Die folgenden Scripts sind veraltet und werden nicht mehr benötigt:
|
||||||
|
|
||||||
|
- `git-commit.bat` - Git-Commits sollten direkt über Git gemacht werden
|
||||||
|
- `git-commit.sh` - Git-Commits sollten direkt über Git gemacht werden
|
||||||
|
- `git-commit-fix.bat` - Git-Commits sollten direkt über Git gemacht werden
|
||||||
|
- `FINAL_COMMIT.bat` - Git-Commits sollten direkt über Git gemacht werden
|
||||||
|
- `run-git-commit.ps1` - Git-Commits sollten direkt über Git gemacht werden
|
||||||
|
- `COMMIT_COMMANDS.txt` - Temporäre Datei
|
||||||
|
- `COMMIT_MESSAGE.md` - Temporäre Datei
|
||||||
|
|
||||||
|
**Empfehlung:** Diese Dateien können gelöscht werden, da Git-Commits direkt über `git commit` gemacht werden sollten.
|
||||||
|
|
||||||
|
## 📝 Verwendung
|
||||||
|
|
||||||
|
### Deployment-Script (automatisch)
|
||||||
|
|
||||||
|
Das Deployment-Script wird automatisch vom Webhook-Handler aufgerufen. Siehe `docs/deployment/GITEA_WEBHOOK_SETUP.md` für Details.
|
||||||
|
|
||||||
|
### Setup-Scripts
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Appwrite Setup
|
||||||
|
.\setup-appwrite.ps1
|
||||||
|
|
||||||
|
# Production Setup
|
||||||
|
.\setup-production.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Neue Scripts hinzufügen
|
||||||
|
|
||||||
|
Wenn du neue Scripts hinzufügst:
|
||||||
|
1. Dokumentiere sie in dieser README
|
||||||
|
2. Füge sie zu `.gitignore` hinzu, falls sie sensible Daten enthalten
|
||||||
|
3. Stelle sicher, dass sie plattformübergreifend funktionieren (oder dokumentiere Plattform-Anforderungen)
|
||||||
@@ -37,7 +37,7 @@ if (-not $gitExe) {
|
|||||||
Write-Host "Alternativ führe diese Befehle manuell in Git Bash aus:" -ForegroundColor Yellow
|
Write-Host "Alternativ führe diese Befehle manuell in Git Bash aus:" -ForegroundColor Yellow
|
||||||
Write-Host "cd c:\Users\User\Documents\GitHub\ANDJJJJJJ" -ForegroundColor Cyan
|
Write-Host "cd c:\Users\User\Documents\GitHub\ANDJJJJJJ" -ForegroundColor Cyan
|
||||||
Write-Host "git add ." -ForegroundColor Cyan
|
Write-Host "git add ." -ForegroundColor Cyan
|
||||||
Write-Host "git commit -m `"feat: Control Panel Redesign v2.0 - Card-basiertes Layout, Side Panels, Dark Mode Fixes, Volle Breite Layout`"" -ForegroundColor Cyan
|
Write-Host "git commit -m `"feat: Control Panel v2.0 - Cards, Side Panels, Dark Mode, volle Breite`"" -ForegroundColor Cyan
|
||||||
Write-Host "git push" -ForegroundColor Cyan
|
Write-Host "git push" -ForegroundColor Cyan
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ if ($LASTEXITCODE -ne 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Erstelle Commit..." -ForegroundColor Yellow
|
Write-Host "Erstelle Commit..." -ForegroundColor Yellow
|
||||||
& $gitExe commit -m "feat: Control Panel Redesign v2.0 - Card-basiertes Layout, Side Panels, Dark Mode Fixes, Volle Breite Layout"
|
& $gitExe commit -m "feat: Control Panel v2.0 - Cards, Side Panels, Dark Mode, volle Breite"
|
||||||
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "❌ Fehler beim Commit" -ForegroundColor Red
|
Write-Host "❌ Fehler beim Commit" -ForegroundColor Red
|
||||||
Reference in New Issue
Block a user