feat: AI Control Settings mit Category Control und Company Labels
MAJOR FEATURES: - AI Control Tab in Settings hinzugefügt mit vollständiger KI-Steuerung - Category Control: Benutzer können Kategorien aktivieren/deaktivieren und Aktionen pro Kategorie festlegen (Keep in Inbox, Archive & Mark Read, Star) - Company Labels: Automatische Erkennung bekannter Firmen (Amazon, Google, Microsoft, etc.) und optionale benutzerdefinierte Company Labels - Auto-Detect Companies Toggle: Automatische Label-Erstellung für bekannte Firmen UI/UX VERBESSERUNGEN: - Sorting Rules Tab entfernt (war zu verwirrend) - Save Buttons nach oben rechts verschoben (Category Control und Company Labels) - Company Labels Section: Custom Labels sind jetzt in einem ausklappbaren Details-Element (Optional) - Verbesserte Beschreibungen und Klarheit in der UI BACKEND ÄNDERUNGEN: - Neue API Endpoints: /api/preferences/ai-control (GET/POST) und /api/preferences/company-labels (GET/POST/DELETE) - AI Sorter Service erweitert: detectCompany(), matchesCompanyLabel(), getCategoryAction(), getEnabledCategories() - Database Service: Default-Werte und Merge-Logik für erweiterte User Preferences - Email Routes: Integration der neuen AI Control Einstellungen in Gmail und Outlook Sortierung - Label-Erstellung: Nur für enabledCategories, Custom Company Labels mit orange Farbe (#ff9800) FRONTEND ÄNDERUNGEN: - Neue TypeScript Types: client/src/types/settings.ts (AIControlSettings, CompanyLabel, CategoryInfo, KnownCompany) - Settings.tsx: Komplett überarbeitet mit AI Control Tab, Category Toggles, Company Labels Management - API Client erweitert: getAIControlSettings(), saveAIControlSettings(), getCompanyLabels(), saveCompanyLabel(), deleteCompanyLabel() - Debug-Logs hinzugefügt für Troubleshooting (main.tsx, App.tsx, Settings.tsx) BUGFIXES: - JSX Syntax-Fehler behoben: Fehlende schließende </div> Tags in Company Labels Section - TypeScript Typ-Fehler behoben: saved.data null-check für Company Labels - Struktur-Fehler behoben: Conditional Blocks korrekt verschachtelt TECHNISCHE DETAILS: - 9 Kategorien verfügbar: VIP, Clients, Invoices, Newsletter, Promotions, Social, Security, Calendar, Review - Company Labels unterstützen Bedingungen wie 'from:amazon.com OR from:amazon.de' - Priorisierung: 1) Custom Company Labels, 2) Auto-Detected Companies, 3) AI Categorization - Deaktivierte Kategorien werden automatisch als 'review' kategorisiert
This commit is contained in:
@@ -106,6 +106,105 @@ app.post('/api/preferences', asyncHandler(async (req, res) => {
|
||||
respond.success(res, null, 'Einstellungen gespeichert')
|
||||
}))
|
||||
|
||||
/**
|
||||
* GET /api/preferences/ai-control
|
||||
* Get AI Control settings
|
||||
*/
|
||||
app.get('/api/preferences/ai-control', asyncHandler(async (req, res) => {
|
||||
const { userId } = req.query
|
||||
if (!userId) throw new ValidationError('userId is required')
|
||||
|
||||
const prefs = await userPreferences.getByUser(userId)
|
||||
const preferences = prefs?.preferences || userPreferences.getDefaults()
|
||||
|
||||
respond.success(res, {
|
||||
enabledCategories: preferences.enabledCategories || [],
|
||||
categoryActions: preferences.categoryActions || {},
|
||||
autoDetectCompanies: preferences.autoDetectCompanies !== undefined ? preferences.autoDetectCompanies : true,
|
||||
})
|
||||
}))
|
||||
|
||||
/**
|
||||
* POST /api/preferences/ai-control
|
||||
* Save AI Control settings
|
||||
*/
|
||||
app.post('/api/preferences/ai-control', asyncHandler(async (req, res) => {
|
||||
const { userId, enabledCategories, categoryActions, autoDetectCompanies } = req.body
|
||||
if (!userId) throw new ValidationError('userId is required')
|
||||
|
||||
const updates = {}
|
||||
if (enabledCategories !== undefined) updates.enabledCategories = enabledCategories
|
||||
if (categoryActions !== undefined) updates.categoryActions = categoryActions
|
||||
if (autoDetectCompanies !== undefined) updates.autoDetectCompanies = autoDetectCompanies
|
||||
|
||||
await userPreferences.upsert(userId, updates)
|
||||
respond.success(res, null, 'AI Control settings saved')
|
||||
}))
|
||||
|
||||
/**
|
||||
* GET /api/preferences/company-labels
|
||||
* Get company labels
|
||||
*/
|
||||
app.get('/api/preferences/company-labels', asyncHandler(async (req, res) => {
|
||||
const { userId } = req.query
|
||||
if (!userId) throw new ValidationError('userId is required')
|
||||
|
||||
const prefs = await userPreferences.getByUser(userId)
|
||||
const preferences = prefs?.preferences || userPreferences.getDefaults()
|
||||
|
||||
respond.success(res, preferences.companyLabels || [])
|
||||
}))
|
||||
|
||||
/**
|
||||
* POST /api/preferences/company-labels
|
||||
* Save/Update company label
|
||||
*/
|
||||
app.post('/api/preferences/company-labels', asyncHandler(async (req, res) => {
|
||||
const { userId, companyLabel } = req.body
|
||||
if (!userId) throw new ValidationError('userId is required')
|
||||
if (!companyLabel) throw new ValidationError('companyLabel is required')
|
||||
|
||||
const prefs = await userPreferences.getByUser(userId)
|
||||
const preferences = prefs?.preferences || userPreferences.getDefaults()
|
||||
|
||||
const companyLabels = preferences.companyLabels || []
|
||||
|
||||
// Generate ID if not provided
|
||||
if (!companyLabel.id) {
|
||||
companyLabel.id = `label_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
}
|
||||
|
||||
// Update or add label
|
||||
const existingIndex = companyLabels.findIndex(l => l.id === companyLabel.id)
|
||||
if (existingIndex >= 0) {
|
||||
companyLabels[existingIndex] = companyLabel
|
||||
} else {
|
||||
companyLabels.push(companyLabel)
|
||||
}
|
||||
|
||||
await userPreferences.upsert(userId, { companyLabels })
|
||||
respond.success(res, companyLabel, 'Company label saved')
|
||||
}))
|
||||
|
||||
/**
|
||||
* DELETE /api/preferences/company-labels/:id
|
||||
* Delete company label
|
||||
*/
|
||||
app.delete('/api/preferences/company-labels/:id', asyncHandler(async (req, res) => {
|
||||
const { userId } = req.query
|
||||
const { id } = req.params
|
||||
if (!userId) throw new ValidationError('userId is required')
|
||||
if (!id) throw new ValidationError('label id is required')
|
||||
|
||||
const prefs = await userPreferences.getByUser(userId)
|
||||
const preferences = prefs?.preferences || userPreferences.getDefaults()
|
||||
|
||||
const companyLabels = (preferences.companyLabels || []).filter(l => l.id !== id)
|
||||
|
||||
await userPreferences.upsert(userId, { companyLabels })
|
||||
respond.success(res, null, 'Company label deleted')
|
||||
}))
|
||||
|
||||
// Legacy Stripe webhook endpoint
|
||||
app.use('/stripe', stripeRoutes)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user