mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 04:31:13 -05:00
Fix migration for admin settings
This commit is contained in:
@@ -14,7 +14,7 @@ import { useAdminSettings } from '@/hooks/useAdminSettings';
|
||||
import { NovuMigrationUtility } from '@/components/admin/NovuMigrationUtility';
|
||||
import { TestDataGenerator } from '@/components/admin/TestDataGenerator';
|
||||
import { IntegrationTestRunner } from '@/components/admin/IntegrationTestRunner';
|
||||
import { Loader2, Save, Clock, Users, Bell, Shield, Settings, Trash2, Plug, AlertTriangle, Lock, TestTube } from 'lucide-react';
|
||||
import { Loader2, Save, Clock, Users, Bell, Shield, Settings, Trash2, Plug, AlertTriangle, Lock, TestTube, RefreshCw, Info, AlertCircle } from 'lucide-react';
|
||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||
|
||||
export default function AdminSettings() {
|
||||
@@ -372,6 +372,249 @@ export default function AdminSettings() {
|
||||
);
|
||||
}
|
||||
|
||||
// Retry settings
|
||||
if (setting.setting_key === 'retry.max_attempts') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<RefreshCw className="w-4 h-4 text-blue-500" />
|
||||
<Label className="text-base font-medium">Maximum Retry Attempts</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
How many times to retry failed operations (entity/photo submissions)
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">1 attempt</SelectItem>
|
||||
<SelectItem value="2">2 attempts</SelectItem>
|
||||
<SelectItem value="3">3 attempts</SelectItem>
|
||||
<SelectItem value="5">5 attempts</SelectItem>
|
||||
<SelectItem value="10">10 attempts</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {localValue}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.setting_key === 'retry.base_delay') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-green-500" />
|
||||
<Label className="text-base font-medium">Initial Retry Delay</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Milliseconds to wait before first retry attempt
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="100">100ms (very fast)</SelectItem>
|
||||
<SelectItem value="500">500ms</SelectItem>
|
||||
<SelectItem value="1000">1 second</SelectItem>
|
||||
<SelectItem value="2000">2 seconds</SelectItem>
|
||||
<SelectItem value="5000">5 seconds</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {localValue}ms</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.setting_key === 'retry.max_delay') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-orange-500" />
|
||||
<Label className="text-base font-medium">Maximum Retry Delay</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Maximum delay between retry attempts (exponential backoff cap)
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="5000">5 seconds</SelectItem>
|
||||
<SelectItem value="10000">10 seconds</SelectItem>
|
||||
<SelectItem value="20000">20 seconds</SelectItem>
|
||||
<SelectItem value="30000">30 seconds</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {localValue}ms</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.setting_key === 'retry.backoff_multiplier') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<RefreshCw className="w-4 h-4 text-purple-500" />
|
||||
<Label className="text-base font-medium">Backoff Multiplier</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Growth rate for exponential backoff between retries
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseFloat(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1.5">1.5x</SelectItem>
|
||||
<SelectItem value="2">2x</SelectItem>
|
||||
<SelectItem value="2.5">2.5x</SelectItem>
|
||||
<SelectItem value="3">3x</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {localValue}x</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.setting_key === 'circuit_breaker.failure_threshold') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertTriangle className="w-4 h-4 text-orange-500" />
|
||||
<Label className="text-base font-medium">Circuit Breaker Threshold</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Number of consecutive failures before blocking all requests temporarily
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="3">3 failures</SelectItem>
|
||||
<SelectItem value="5">5 failures</SelectItem>
|
||||
<SelectItem value="10">10 failures</SelectItem>
|
||||
<SelectItem value="15">15 failures</SelectItem>
|
||||
<SelectItem value="20">20 failures</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {localValue}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.setting_key === 'circuit_breaker.reset_timeout') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-red-500" />
|
||||
<Label className="text-base font-medium">Circuit Reset Timeout</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
How long to wait before testing if service has recovered
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="30000">30 seconds</SelectItem>
|
||||
<SelectItem value="60000">1 minute</SelectItem>
|
||||
<SelectItem value="120000">2 minutes</SelectItem>
|
||||
<SelectItem value="300000">5 minutes</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {Math.floor(Number(localValue) / 1000)}s</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.setting_key === 'circuit_breaker.monitoring_window') {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-yellow-500" />
|
||||
<Label className="text-base font-medium">Monitoring Window</Label>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Time window to track failures for circuit breaker
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<Select value={localValue?.toString()} onValueChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
setLocalValue(numValue);
|
||||
updateSetting(setting.setting_key, numValue);
|
||||
}}>
|
||||
<SelectTrigger className="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="60000">1 minute</SelectItem>
|
||||
<SelectItem value="120000">2 minutes</SelectItem>
|
||||
<SelectItem value="180000">3 minutes</SelectItem>
|
||||
<SelectItem value="300000">5 minutes</SelectItem>
|
||||
<SelectItem value="600000">10 minutes</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Badge variant="outline">Current: {Math.floor(Number(localValue) / 60000)}min</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Helper to check if value is boolean
|
||||
const isBooleanSetting = (value: any) => {
|
||||
return value === true || value === false ||
|
||||
@@ -503,7 +746,7 @@ export default function AdminSettings() {
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="moderation" className="space-y-6">
|
||||
<TabsList className="grid w-full grid-cols-6">
|
||||
<TabsList className="grid w-full grid-cols-7">
|
||||
<TabsTrigger value="moderation" className="flex items-center gap-2">
|
||||
<Shield className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Moderation</span>
|
||||
@@ -516,6 +759,10 @@ export default function AdminSettings() {
|
||||
<Bell className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Notifications</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="resilience" className="flex items-center gap-2">
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Resilience</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="system" className="flex items-center gap-2">
|
||||
<Settings className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">System</span>
|
||||
@@ -612,6 +859,61 @@ export default function AdminSettings() {
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="resilience">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<RefreshCw className="w-5 h-5" />
|
||||
Resilience & Retry Configuration
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Configure automatic retry behavior and circuit breaker settings for handling transient failures
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="bg-blue-50 dark:bg-blue-950 p-4 rounded-lg border border-blue-200 dark:border-blue-800">
|
||||
<div className="flex items-start gap-3">
|
||||
<Info className="w-5 h-5 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="text-sm text-blue-900 dark:text-blue-100">
|
||||
<p className="font-medium mb-2">About Retry & Circuit Breaker</p>
|
||||
<ul className="space-y-1 text-blue-800 dark:text-blue-200">
|
||||
<li>• <strong>Retry Logic:</strong> Automatically retries failed operations (network issues, timeouts)</li>
|
||||
<li>• <strong>Circuit Breaker:</strong> Prevents system overload by blocking requests during outages</li>
|
||||
<li>• <strong>When to adjust:</strong> Increase retries for unstable networks, decrease for fast-fail scenarios</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">Retry Settings</h3>
|
||||
{getSettingsByCategory('system')
|
||||
.filter(s => s.setting_key.startsWith('retry.'))
|
||||
.map(setting => <SettingInput key={setting.id} setting={setting} />)}
|
||||
</div>
|
||||
|
||||
<div className="border-t pt-6 space-y-4">
|
||||
<h3 className="text-lg font-semibold">Circuit Breaker Settings</h3>
|
||||
{getSettingsByCategory('system')
|
||||
.filter(s => s.setting_key.startsWith('circuit_breaker.'))
|
||||
.map(setting => <SettingInput key={setting.id} setting={setting} />)}
|
||||
</div>
|
||||
|
||||
<div className="bg-yellow-50 dark:bg-yellow-950 p-4 rounded-lg border border-yellow-200 dark:border-yellow-800">
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-yellow-600 dark:text-yellow-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="text-sm text-yellow-900 dark:text-yellow-100">
|
||||
<p className="font-medium mb-1">Configuration Changes</p>
|
||||
<p className="text-yellow-800 dark:text-yellow-200">
|
||||
Settings take effect immediately but may be cached for up to 5 minutes in active sessions. Consider monitoring error logs after changes to verify behavior.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="system">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -624,8 +926,8 @@ export default function AdminSettings() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{getSettingsByCategory('system').length > 0 ? (
|
||||
getSettingsByCategory('system').map((setting) => (
|
||||
{getSettingsByCategory('system').filter(s => !s.setting_key.startsWith('retry.') && !s.setting_key.startsWith('circuit_breaker.')).length > 0 ? (
|
||||
getSettingsByCategory('system').filter(s => !s.setting_key.startsWith('retry.') && !s.setting_key.startsWith('circuit_breaker.')).map((setting) => (
|
||||
<SettingInput key={setting.id} setting={setting} />
|
||||
))
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user