fix: AdminUserRestrictionsPage - use exact TierLimitsPage input system
- formatValue: NULL → '' (empty field with placeholder ∞) - handleChange: Accept ONLY '∞' or 'unlimited' (no other formats) - Input styling: Green only for '∞', empty fields normal color - Simplified legend: Only ∞ or unlimited accepted - Boolean features: Toggle buttons with 1/0 values - Add package-lock.json to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
917c8937cf
commit
85f5938d7d
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -61,4 +61,4 @@ tmp/
|
||||||
|
|
||||||
#.claude Konfiguration
|
#.claude Konfiguration
|
||||||
.claude/
|
.claude/
|
||||||
.claude/settings.local.json
|
.claude/settings.local.jsonfrontend/package-lock.json
|
||||||
|
|
|
||||||
|
|
@ -77,37 +77,48 @@ export default function AdminUserRestrictionsPage() {
|
||||||
function handleChange(featureId, value) {
|
function handleChange(featureId, value) {
|
||||||
const newChanges = { ...changes }
|
const newChanges = { ...changes }
|
||||||
|
|
||||||
// Empty string means: remove override (if exists) or do nothing
|
// Empty string means: remove override
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
newChanges[featureId] = { action: 'remove' }
|
newChanges[featureId] = { action: 'remove', tempValue: '' }
|
||||||
} else {
|
setChanges(newChanges)
|
||||||
// Parse value
|
return
|
||||||
let parsedValue = null
|
|
||||||
const lowerValue = value.toLowerCase().trim()
|
|
||||||
|
|
||||||
// Accept multiple formats for unlimited
|
|
||||||
if (lowerValue === 'unlimited' || lowerValue === 'unbegrenzt' ||
|
|
||||||
value === '∞' || lowerValue === 'inf' || lowerValue === '999999') {
|
|
||||||
parsedValue = null
|
|
||||||
} else if (value === '0') {
|
|
||||||
parsedValue = 0
|
|
||||||
} else {
|
|
||||||
const num = parseInt(value)
|
|
||||||
if (!isNaN(num) && num >= 0) {
|
|
||||||
parsedValue = num
|
|
||||||
} else {
|
|
||||||
return // invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newChanges[featureId] = { action: 'set', value: parsedValue }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse value (EXACTLY like TierLimitsPage)
|
||||||
|
let parsedValue = null
|
||||||
|
if (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, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newChanges[featureId] = { action: 'set', value: parsedValue, tempValue: value }
|
||||||
setChanges(newChanges)
|
setChanges(newChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToggle(featureId, currentEnabled) {
|
function handleToggle(featureId) {
|
||||||
|
// Get current state
|
||||||
|
const restriction = restrictions.find(r => r.feature_id === featureId)
|
||||||
|
let currentValue = restriction?.limit_value ?? null
|
||||||
|
|
||||||
|
// Check if there's a pending change
|
||||||
|
if (featureId in changes && changes[featureId].action === 'set') {
|
||||||
|
currentValue = changes[featureId].value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle between 1 (enabled) and 0 (disabled)
|
||||||
|
const isCurrentlyEnabled = currentValue !== 0 && currentValue !== '0'
|
||||||
|
const newValue = isCurrentlyEnabled ? 0 : 1
|
||||||
|
|
||||||
const newChanges = { ...changes }
|
const newChanges = { ...changes }
|
||||||
newChanges[featureId] = { action: 'toggle', enabled: !currentEnabled }
|
newChanges[featureId] = { action: 'set', value: newValue, tempValue: newValue.toString() }
|
||||||
setChanges(newChanges)
|
setChanges(newChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,24 +158,6 @@ export default function AdminUserRestrictionsPage() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
changeCount++
|
changeCount++
|
||||||
} else if (change.action === 'toggle') {
|
|
||||||
// Toggle enabled state
|
|
||||||
if (existingRestriction) {
|
|
||||||
await api.updateUserRestriction(existingRestriction.id, {
|
|
||||||
enabled: change.enabled
|
|
||||||
})
|
|
||||||
changeCount++
|
|
||||||
} else {
|
|
||||||
// Create new restriction with toggle state
|
|
||||||
await api.createUserRestriction({
|
|
||||||
profile_id: selectedUserId,
|
|
||||||
feature_id: featureId,
|
|
||||||
limit_value: change.enabled ? 1 : 0,
|
|
||||||
enabled: true,
|
|
||||||
reason: 'Admin override'
|
|
||||||
})
|
|
||||||
changeCount++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,28 +175,42 @@ export default function AdminUserRestrictionsPage() {
|
||||||
if (featureId in changes) {
|
if (featureId in changes) {
|
||||||
const change = changes[featureId]
|
const change = changes[featureId]
|
||||||
if (change.action === 'remove') return ''
|
if (change.action === 'remove') return ''
|
||||||
if (change.action === 'set') return change.value === null ? '' : change.value
|
if (change.action === 'set') {
|
||||||
if (change.action === 'toggle') return change.enabled ? 1 : 0
|
// Use tempValue for display if available, otherwise format the value
|
||||||
|
return change.tempValue !== undefined ? change.tempValue : formatValue(change.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show existing restriction value (or empty if no restriction)
|
// Show existing restriction value (or empty if no restriction)
|
||||||
const restriction = restrictions.find(r => r.feature_id === featureId)
|
const restriction = restrictions.find(r => r.feature_id === featureId)
|
||||||
if (!restriction) return '' // No override = empty input
|
if (!restriction) return '' // No override = empty input
|
||||||
return restriction.limit_value === null ? '' : restriction.limit_value
|
return formatValue(restriction.limit_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValue(val) {
|
||||||
|
if (val === '' || val === null || val === undefined) return ''
|
||||||
|
if (val === '∞' || val === 'unlimited') return '∞'
|
||||||
|
if (val === 0 || val === '0') return '0'
|
||||||
|
return val.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getToggleState(featureId) {
|
function getToggleState(featureId) {
|
||||||
// Check pending changes first
|
// Check pending changes first
|
||||||
if (featureId in changes && changes[featureId].action === 'toggle') {
|
if (featureId in changes && changes[featureId].action === 'set') {
|
||||||
return changes[featureId].enabled
|
const val = changes[featureId].value
|
||||||
|
return val !== 0 && val !== '0'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check existing restriction
|
// Check existing restriction
|
||||||
const restriction = restrictions.find(r => r.feature_id === featureId)
|
const restriction = restrictions.find(r => r.feature_id === featureId)
|
||||||
if (!restriction) return true // Default: enabled
|
if (!restriction) {
|
||||||
|
// No override: use tier default
|
||||||
|
const tierLimit = tierLimits[featureId]
|
||||||
|
return tierLimit !== 0 && tierLimit !== '0'
|
||||||
|
}
|
||||||
|
|
||||||
// For boolean features: limit_value determines state
|
// For boolean features: limit_value determines state
|
||||||
return restriction.limit_value !== 0
|
return restriction.limit_value !== 0 && restriction.limit_value !== '0'
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasOverride(featureId) {
|
function hasOverride(featureId) {
|
||||||
|
|
@ -427,7 +434,7 @@ export default function AdminUserRestrictionsPage() {
|
||||||
<td style={{ padding: '12px 16px', textAlign: 'center' }}>
|
<td style={{ padding: '12px 16px', textAlign: 'center' }}>
|
||||||
{feature.limit_type === 'boolean' ? (
|
{feature.limit_type === 'boolean' ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleToggle(feature.id, toggleState)}
|
onClick={() => handleToggle(feature.id)}
|
||||||
style={{
|
style={{
|
||||||
padding: '6px 16px',
|
padding: '6px 16px',
|
||||||
border: `2px solid ${toggleState ? 'var(--accent)' : 'var(--border)'}`,
|
border: `2px solid ${toggleState ? 'var(--accent)' : 'var(--border)'}`,
|
||||||
|
|
@ -448,7 +455,7 @@ export default function AdminUserRestrictionsPage() {
|
||||||
type="text"
|
type="text"
|
||||||
value={displayValue}
|
value={displayValue}
|
||||||
onChange={(e) => handleChange(feature.id, e.target.value)}
|
onChange={(e) => handleChange(feature.id, e.target.value)}
|
||||||
placeholder={override ? "Wert..." : ""}
|
placeholder="∞"
|
||||||
style={{
|
style={{
|
||||||
width: '100px',
|
width: '100px',
|
||||||
padding: '6px 8px',
|
padding: '6px 8px',
|
||||||
|
|
@ -457,8 +464,9 @@ export default function AdminUserRestrictionsPage() {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: override || changed ? 600 : 400,
|
fontWeight: override || changed ? 600 : 400,
|
||||||
background: override ? 'var(--accent-light)' : 'var(--bg)',
|
background: override || changed ? 'var(--accent-light)' : 'var(--bg)',
|
||||||
color: displayValue === 0 || displayValue === '0' ? 'var(--danger)' : 'var(--text1)'
|
color: displayValue === '0' ? 'var(--danger)' :
|
||||||
|
displayValue === '∞' ? 'var(--accent)' : 'var(--text1)'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -495,12 +503,12 @@ export default function AdminUserRestrictionsPage() {
|
||||||
marginTop: 16, padding: 12, background: 'var(--surface2)',
|
marginTop: 16, padding: 12, background: 'var(--surface2)',
|
||||||
borderRadius: 8, fontSize: 12, color: 'var(--text3)'
|
borderRadius: 8, fontSize: 12, color: 'var(--text3)'
|
||||||
}}>
|
}}>
|
||||||
<strong>Eingabe:</strong>
|
<strong>Eingabe (Count-Features):</strong>
|
||||||
<div style={{ marginTop: 8, display: 'flex', gap: 24, flexWrap: 'wrap' }}>
|
<div style={{ marginTop: 8, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
||||||
<span><strong>Leer</strong> = Tier-Standard nutzen (kein Override)</span>
|
<span><strong style={{ color: 'var(--accent)' }}>∞</strong> oder <strong>unlimited</strong> = Unbegrenzt</span>
|
||||||
<span><strong style={{ color: 'var(--danger)' }}>0</strong> = Feature deaktiviert</span>
|
<span><strong style={{ color: 'var(--danger)' }}>0</strong> = Feature deaktiviert</span>
|
||||||
<span><strong>unbegrenzt, inf, 999999</strong> = Unbegrenzt</span>
|
<span><strong>1+</strong> = Limit-Wert</span>
|
||||||
<span><strong>1-999998</strong> = Limit-Wert</span>
|
<span><strong>Leer</strong> = kein Override (Tier-Standard)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user