import { useState, useEffect } from 'react' import { Save, User, AlertCircle, X } from 'lucide-react' import { api } from '../utils/api' export default function AdminUserRestrictionsPage() { const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [success, setSuccess] = useState('') const [users, setUsers] = useState([]) const [features, setFeatures] = useState([]) const [selectedUserId, setSelectedUserId] = useState('') const [selectedUser, setSelectedUser] = useState(null) const [restrictions, setRestrictions] = useState([]) const [changes, setChanges] = useState({}) const [saving, setSaving] = useState(false) useEffect(() => { loadInitialData() }, []) useEffect(() => { if (selectedUserId) { loadUserData(selectedUserId) } }, [selectedUserId]) async function loadInitialData() { try { setLoading(true) const [usersData, featuresData] = await Promise.all([ api.adminListProfiles(), api.listFeatures() ]) setUsers(usersData) setFeatures(featuresData.filter(f => f.active)) setError('') } catch (e) { setError(e.message) } finally { setLoading(false) } } async function loadUserData(userId) { try { const [user, restrictionsData] = await Promise.all([ api.adminListProfiles().then(users => users.find(u => u.id === userId)), api.listUserRestrictions(userId) ]) setSelectedUser(user) setRestrictions(restrictionsData) setChanges({}) setError('') } catch (e) { setError(e.message) } } function handleChange(featureId, value, enabled) { const key = featureId const newChanges = { ...changes } // Parse value let parsedValue = null if (value === '' || value === 'unlimited' || value === '∞') { parsedValue = null // unlimited } else if (value === '0' || value === 'disabled') { parsedValue = 0 // disabled } else { const num = parseInt(value) if (!isNaN(num) && num >= 0) { parsedValue = num } else { return // invalid input } } newChanges[key] = { featureId, value: parsedValue, enabled } setChanges(newChanges) } function toggleEnabled(featureId) { const restriction = restrictions.find(r => r.feature_id === featureId) const currentEnabled = restriction ? restriction.enabled : true const currentValue = restriction ? restriction.limit_value : null handleChange(featureId, currentValue === null ? '' : currentValue, !currentEnabled) } async function handleSave() { if (!selectedUserId) return try { setSaving(true) setError('') setSuccess('') // Process changes for (const [featureId, change] of Object.entries(changes)) { const existingRestriction = restrictions.find(r => r.feature_id === featureId) if (existingRestriction) { // Update existing restriction await api.updateUserRestriction(existingRestriction.id, { limit_value: change.value, enabled: change.enabled }) } else { // Create new restriction await api.createUserRestriction({ profile_id: selectedUserId, feature_id: featureId, limit_value: change.value, enabled: change.enabled, reason: 'Admin override' }) } } setSuccess(`${Object.keys(changes).length} Overrides gespeichert`) await loadUserData(selectedUserId) } catch (e) { setError(e.message) } finally { setSaving(false) } } async function handleRemoveRestriction(featureId) { if (!confirm('Override wirklich entfernen? User erhält dann das Standard-Tier-Limit.')) return try { const restriction = restrictions.find(r => r.feature_id === featureId) if (restriction) { await api.deleteUserRestriction(restriction.id) setSuccess('Override entfernt') await loadUserData(selectedUserId) // Remove from changes if exists const newChanges = { ...changes } delete newChanges[featureId] setChanges(newChanges) } } catch (e) { setError(e.message) } } function getCurrentValue(featureId) { // Check if there's a pending change if (featureId in changes) { return changes[featureId].value } // Check existing restriction const restriction = restrictions.find(r => r.feature_id === featureId) return restriction ? restriction.limit_value : null } function isEnabled(featureId) { if (featureId in changes) { return changes[featureId].enabled } const restriction = restrictions.find(r => r.feature_id === featureId) return restriction ? restriction.enabled : true } function hasRestriction(featureId) { return restrictions.some(r => r.feature_id === featureId) } function formatValue(val) { if (val === null || val === undefined) return '' return val.toString() } if (loading) return (
) const hasChanges = Object.keys(changes).length > 0 const categoryGroups = {} features.forEach(f => { if (!categoryGroups[f.category]) categoryGroups[f.category] = [] categoryGroups[f.category].push(f) }) const categoryIcons = { data: '📊', ai: '🤖', export: '📤', integration: '🔗' } const categoryNames = { data: 'DATEN', ai: 'KI', export: 'EXPORT', integration: 'INTEGRATIONEN' } return (
{/* Header */}
User Feature-Overrides
Individuelle Feature-Limits für einzelne User setzen
{/* Info Box */}
Hinweis: User-Overrides haben höchste Priorität und überschreiben Tier-Limits. Nutze dies sparsam für Sonderfälle (z.B. Beta-Tester, Support-Anfragen).
{/* Messages */} {error && (
{error}
)} {success && (
{success}
)} {/* User Selection */}
{/* User Info + Features */} {selectedUser && ( <> {/* User Info Card */}
{selectedUser.name?.charAt(0).toUpperCase()}
{selectedUser.name}
{selectedUser.email || `ID: ${selectedUser.id}`}
Tier: {selectedUser.tier || 'free'}
{/* Features List */}
{Object.entries(categoryGroups).map(([category, categoryFeatures]) => ( <> {/* Category Header */} {/* Feature Rows */} {categoryFeatures.map(feature => { const currentValue = getCurrentValue(feature.id) const enabled = isEnabled(feature.id) const hasOverride = hasRestriction(feature.id) const isChanged = feature.id in changes return ( ) })} ))}
Feature Tier-Limit Override Aktiv Aktion
{categoryIcons[category]} {categoryNames[category] || category}
{feature.name}
{feature.limit_type === 'boolean' ? '(ja/nein)' : `(${feature.reset_period})`}
{/* TODO: Show actual tier limit - for now just show "Standard" */} Standard {feature.limit_type === 'boolean' ? ( ) : ( handleChange(feature.id, e.target.value, enabled)} placeholder="∞" style={{ width: '80px', padding: '6px 8px', border: `1.5px solid ${isChanged ? 'var(--accent)' : hasOverride ? 'var(--border)' : 'var(--border)'}`, borderRadius: 6, textAlign: 'center', fontSize: 13, fontWeight: isChanged ? 600 : 400, background: hasOverride ? 'var(--surface2)' : 'var(--bg)', color: currentValue === 0 ? 'var(--danger)' : currentValue === null ? 'var(--accent)' : 'var(--text1)' }} /> )} {hasOverride ? ( ✓ ) : ( - )} {hasOverride && ( )}
{/* Save Button */} {hasChanges && (
)} {/* Legend */}
Eingabe:
leer oder ∞ = Unbegrenzt 0 = Deaktiviert 1-999999 = Limit-Wert
✓ Aktiv = Override gesetzt, überschreibt Tier-Limit
)}
) }