mosaicmap/app/admin/common/control.tsx
2025-08-12 21:25:52 +08:00

598 lines
34 KiB
TypeScript

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
import { Checkbox } from "@/components/ui/checkbox"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { ScrollArea } from "@/components/ui/scroll-area"
import { useState } from "react"
import { Config, defaultConfig } from "@/types/config"
import {
Settings,
Volume2,
FlashlightIcon as Brightness4,
Wifi,
Database,
Shield,
Monitor,
Bell,
Server,
Lock,
Zap,
HardDrive,
Cpu,
} from "lucide-react"
export default function Control() {
const [config, setConfig] = useState<Config>(defaultConfig)
const updateConfig = (key: keyof Config, value: any) => {
setConfig(prev => ({
...prev,
[key]: value
}))
}
const updateSliderConfig = (key: keyof Config, value: number[]) => {
updateConfig(key, value[0])
}
return (
<ScrollArea className="flex-1 h-full">
<div className="min-h-screen p-6">
<div className="mx-auto max-w-7xl space-y-6">
<div className="grid gap-6 lg:grid-cols-3">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Volume2 className="h-5 w-5" />
System Controls
</CardTitle>
<CardDescription>Audio, display, and hardware settings</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="volume">Volume: {config.volume}%</Label>
<Slider
id="volume"
value={[config.volume]}
onValueChange={(value) => updateSliderConfig('volume', value)}
max={100}
step={1}
/>
</div>
<div className="space-y-2">
<Label htmlFor="brightness">
<Brightness4 className="inline h-4 w-4 mr-1" />
Brightness: {config.brightness}%
</Label>
<Slider
id="brightness"
value={[config.brightness]}
onValueChange={(value) => updateSliderConfig('brightness', value)}
max={100}
step={1}
/>
</div>
<div className="space-y-2">
<Label htmlFor="temperature">Temperature: {config.temperature}°C</Label>
<Slider
id="temperature"
value={[config.temperature]}
onValueChange={(value) => updateSliderConfig('temperature', value)}
min={16}
max={30}
step={0.5}
/>
</div>
<div className="space-y-3">
<Label>Theme Selection</Label>
<Select value={config.theme} onValueChange={(value) => updateConfig('theme', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="light">Light Theme</SelectItem>
<SelectItem value="dark">Dark Theme</SelectItem>
<SelectItem value="auto">Auto Theme</SelectItem>
<SelectItem value="high-contrast">High Contrast</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
{/* Server Configuration */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Server className="h-5 w-5" />
Server Configuration
</CardTitle>
<CardDescription>Core server and API settings</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="server-name">Server Name</Label>
<Input
id="server-name"
value={config.serverName}
onChange={(e) => updateConfig('serverName', e.target.value)}
placeholder="Enter server name"
/>
</div>
<div className="space-y-2">
<Label htmlFor="api-key">API Key</Label>
<Input
id="api-key"
type="password"
value={config.apiKey}
onChange={(e) => updateConfig('apiKey', e.target.value)}
placeholder="Enter API key"
/>
</div>
<div className="space-y-3">
<Label>Environment</Label>
<Select value={config.environment} onValueChange={(value) => updateConfig('environment', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="development">Development</SelectItem>
<SelectItem value="staging">Staging</SelectItem>
<SelectItem value="production">Production</SelectItem>
<SelectItem value="testing">Testing</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-3">
<Label>Region</Label>
<Select value={config.region} onValueChange={(value) => updateConfig('region', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="us-east-1">US East (N. Virginia)</SelectItem>
<SelectItem value="us-west-2">US West (Oregon)</SelectItem>
<SelectItem value="eu-west-1">Europe (Ireland)</SelectItem>
<SelectItem value="ap-southeast-1">Asia Pacific (Singapore)</SelectItem>
<SelectItem value="ap-northeast-1">Asia Pacific (Tokyo)</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
{/* Performance Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Cpu className="h-5 w-5" />
Performance Settings
</CardTitle>
<CardDescription>Resource allocation and optimization</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="connections">Max Connections: {config.maxConnections}</Label>
<Slider
id="connections"
value={[config.maxConnections]}
onValueChange={(value) => updateSliderConfig('maxConnections', value)}
min={10}
max={500}
step={10}
/>
</div>
<div className="space-y-2">
<Label htmlFor="cache-size">Cache Size: {config.cacheSize}MB</Label>
<Slider
id="cache-size"
value={[config.cacheSize]}
onValueChange={(value) => updateSliderConfig('cacheSize', value)}
min={64}
max={2048}
step={64}
/>
</div>
<div className="space-y-2">
<Label htmlFor="thread-count">Thread Count: {config.threadCount}</Label>
<Slider
id="thread-count"
value={[config.threadCount]}
onValueChange={(value) => updateSliderConfig('threadCount', value)}
min={1}
max={32}
step={1}
/>
</div>
<div className="space-y-2">
<Label htmlFor="memory-limit">Memory Limit: {config.memoryLimit}MB</Label>
<Slider
id="memory-limit"
value={[config.memoryLimit]}
onValueChange={(value) => updateSliderConfig('memoryLimit', value)}
min={512}
max={8192}
step={256}
/>
</div>
<div className="space-y-2">
<Label htmlFor="bandwidth">Network Bandwidth: {config.networkBandwidth}Mbps</Label>
<Slider
id="bandwidth"
value={[config.networkBandwidth]}
onValueChange={(value) => updateSliderConfig('networkBandwidth', value)}
min={10}
max={1000}
step={10}
/>
</div>
</CardContent>
</Card>
{/* Security & Features */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield className="h-5 w-5" />
Security & Features
</CardTitle>
<CardDescription>Security settings and feature toggles</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between">
<Label htmlFor="ssl-enabled" className="flex items-center gap-2">
<Lock className="h-4 w-4" />
SSL Enabled
</Label>
<Switch
id="ssl-enabled"
checked={config.sslEnabled}
onCheckedChange={(checked) => updateConfig('sslEnabled', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="auto-backup">Auto Backup</Label>
<Switch
id="auto-backup"
checked={config.autoBackup}
onCheckedChange={(checked) => updateConfig('autoBackup', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="compression">Compression</Label>
<Switch
id="compression"
checked={config.compressionEnabled}
onCheckedChange={(checked) => updateConfig('compressionEnabled', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="debug-mode">Debug Mode</Label>
<Switch
id="debug-mode"
checked={config.debugMode}
onCheckedChange={(checked) => updateConfig('debugMode', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="maintenance">Maintenance Mode</Label>
<Switch
id="maintenance"
checked={config.maintenanceMode}
onCheckedChange={(checked) => updateConfig('maintenanceMode', checked)}
/>
</div>
<div className="space-y-3">
<Label>Log Level</Label>
<Select value={config.logLevel} onValueChange={(value) => updateConfig('logLevel', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="debug">Debug</SelectItem>
<SelectItem value="info">Info</SelectItem>
<SelectItem value="warn">Warning</SelectItem>
<SelectItem value="error">Error</SelectItem>
<SelectItem value="fatal">Fatal</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
{/* Notifications & Alerts */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Bell className="h-5 w-5" />
Notifications & Alerts
</CardTitle>
<CardDescription>Configure notification preferences</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between">
<Label htmlFor="notifications">Push Notifications</Label>
<Switch
id="notifications"
checked={config.notifications}
onCheckedChange={(checked) => updateConfig('notifications', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="email-alerts">Email Alerts</Label>
<Switch
id="email-alerts"
checked={config.emailAlerts}
onCheckedChange={(checked) => updateConfig('emailAlerts', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="sms-alerts">SMS Alerts</Label>
<Switch
id="sms-alerts"
checked={config.smsAlerts}
onCheckedChange={(checked) => updateConfig('smsAlerts', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="monitoring">System Monitoring</Label>
<Switch
id="monitoring"
checked={config.monitoringEnabled}
onCheckedChange={(checked) => updateConfig('monitoringEnabled', checked)}
/>
</div>
<div className="space-y-3">
<Label>Language</Label>
<Select value={config.language} onValueChange={(value) => updateConfig('language', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="en">English</SelectItem>
<SelectItem value="es">Español</SelectItem>
<SelectItem value="fr">Français</SelectItem>
<SelectItem value="de">Deutsch</SelectItem>
<SelectItem value="zh"></SelectItem>
<SelectItem value="ja"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-3">
<Label>Timezone</Label>
<Select value={config.timezone} onValueChange={(value) => updateConfig('timezone', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="UTC">UTC</SelectItem>
<SelectItem value="EST">Eastern Time</SelectItem>
<SelectItem value="PST">Pacific Time</SelectItem>
<SelectItem value="GMT">Greenwich Mean Time</SelectItem>
<SelectItem value="JST">Japan Standard Time</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
{/* Advanced Configuration */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Database className="h-5 w-5" />
Advanced Configuration
</CardTitle>
<CardDescription>Detailed system configuration options</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="description">System Description</Label>
<Textarea
id="description"
value={config.description}
onChange={(e) => updateConfig('description', e.target.value)}
placeholder="Enter system description..."
rows={3}
/>
</div>
<div className="space-y-3">
<Label>Deployment Strategy</Label>
<RadioGroup value={config.deploymentStrategy} onValueChange={(value) => updateConfig('deploymentStrategy', value)}>
<div className="flex items-center space-x-2">
<RadioGroupItem value="rolling" id="rolling" />
<Label htmlFor="rolling">Rolling Deployment</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="blue-green" id="blue-green" />
<Label htmlFor="blue-green">Blue-Green Deployment</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="canary" id="canary" />
<Label htmlFor="canary">Canary Deployment</Label>
</div>
</RadioGroup>
</div>
<div className="space-y-3">
<Label>Enabled Features</Label>
<div className="space-y-2">
{[
{ id: "analytics", label: "Analytics" },
{ id: "caching", label: "Caching" },
{ id: "cdn", label: "CDN" },
{ id: "load-balancing", label: "Load Balancing" },
{ id: "auto-scaling", label: "Auto Scaling" },
].map((feature) => (
<div key={feature.id} className="flex items-center space-x-2">
<Checkbox
id={feature.id}
checked={config.selectedFeatures.includes(feature.id)}
onCheckedChange={(checked) => {
if (checked) {
updateConfig('selectedFeatures', [...config.selectedFeatures, feature.id])
} else {
updateConfig('selectedFeatures', config.selectedFeatures.filter((f) => f !== feature.id))
}
}}
/>
<Label htmlFor={feature.id}>{feature.label}</Label>
</div>
))}
</div>
</div>
</CardContent>
</Card>
</div>
{/* Status Dashboard */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Monitor className="h-5 w-5" />
System Status Dashboard
</CardTitle>
<CardDescription>Real-time system metrics and performance indicators</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div className="space-y-2">
<Label className="text-sm font-medium flex items-center gap-2">
<Cpu className="h-4 w-4" />
CPU Usage
</Label>
<div className="flex items-center gap-2">
<div className="flex-1 bg-slate-200 rounded-full h-2">
<div className="bg-blue-500 h-2 rounded-full w-3/4"></div>
</div>
<Badge variant="secondary">75%</Badge>
</div>
</div>
<div className="space-y-2">
<Label className="text-sm font-medium flex items-center gap-2">
<HardDrive className="h-4 w-4" />
Memory
</Label>
<div className="flex items-center gap-2">
<div className="flex-1 bg-slate-200 rounded-full h-2">
<div className="bg-green-500 h-2 rounded-full w-1/2"></div>
</div>
<Badge variant="secondary">8.2GB</Badge>
</div>
</div>
<div className="space-y-2">
<Label className="text-sm font-medium flex items-center gap-2">
<Database className="h-4 w-4" />
Storage
</Label>
<div className="flex items-center gap-2">
<div className="flex-1 bg-slate-200 rounded-full h-2">
<div className="bg-orange-500 h-2 rounded-full w-5/6"></div>
</div>
<Badge variant="secondary">456GB</Badge>
</div>
</div>
<div className="space-y-2">
<Label className="text-sm font-medium flex items-center gap-2">
<Wifi className="h-4 w-4" />
Network
</Label>
<div className="flex items-center gap-2">
<div className="flex-1 bg-slate-200 rounded-full h-2">
<div className="bg-purple-500 h-2 rounded-full w-1/3"></div>
</div>
<Badge variant="outline" className="text-green-600 border-green-600">
Online
</Badge>
</div>
</div>
</div>
<Separator className="my-6" />
<div className="flex flex-wrap gap-3">
<Button className="flex items-center gap-2" onClick={() => {
// 这里可以添加保存配置到后端的逻辑
console.log('Applying configuration:', config)
alert('Configuration applied successfully!')
}}>
<Zap className="h-4 w-4" />
Apply All Settings
</Button>
<Button variant="outline" onClick={() => setConfig(defaultConfig)}>Reset to Default</Button>
<Button variant="outline" onClick={() => {
const dataStr = JSON.stringify(config, null, 2)
const dataBlob = new Blob([dataStr], { type: 'application/json' })
const url = URL.createObjectURL(dataBlob)
const link = document.createElement('a')
link.href = url
link.download = 'config.json'
link.click()
URL.revokeObjectURL(url)
}}>Export Configuration</Button>
<Button variant="outline" onClick={() => {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.json'
input.onchange = (e) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (file) {
const reader = new FileReader()
reader.onload = (e) => {
try {
const importedConfig = JSON.parse(e.target?.result as string)
setConfig(importedConfig)
} catch (error) {
console.error('Failed to parse config file:', error)
alert('Invalid configuration file')
}
}
reader.readAsText(file)
}
}
input.click()
}}>Import Configuration</Button>
<Button variant="ghost">View Logs</Button>
<Button variant="ghost">System Health Check</Button>
</div>
</CardContent>
</Card>
</div>
</div>
</ScrollArea>
);
}