首页布局优化前

This commit is contained in:
iven
2026-03-17 23:26:16 +08:00
parent 74dbf42644
commit e262200f1e
89 changed files with 2266 additions and 2120 deletions

View File

@@ -9,13 +9,34 @@
import { useState, useEffect, useCallback } from 'react';
import { useGatewayStore, type Hand, type HandRequirement } from '../store/gatewayStore';
import { Zap, RefreshCw, ChevronRight, CheckCircle, XCircle, Loader2, AlertTriangle, Settings } from 'lucide-react';
import { Zap, RefreshCw, ChevronRight, CheckCircle, XCircle, Loader2, AlertTriangle, Settings, Play } from 'lucide-react';
import { BrowserHandCard } from './BrowserHand';
import type { HandParameter } from '../types/hands';
import { HAND_DEFINITIONS } from '../types/hands';
import { HandParamsForm } from './HandParamsForm';
// === Status Badge Component ===
type HandStatus = 'idle' | 'running' | 'needs_approval' | 'error' | 'unavailable' | 'setup_needed';
// === Parameter Validation Helper ===
function validateAllParameters(
parameters: HandParameter[],
values: Record<string, unknown>
): Record<string, string> {
const errors: Record<string, string> = {};
parameters.forEach(param => {
if (param.required) {
const value = values[param.name];
if (value === undefined || value === null || value === '') {
errors[param.name] = `${param.label} is required`;
}
}
});
return errors;
}
interface StatusConfig {
label: string;
className: string;
@@ -117,6 +138,57 @@ interface HandDetailsModalProps {
}
function HandDetailsModal({ hand, isOpen, onClose, onActivate, isActivating }: HandDetailsModalProps) {
// Get Hand parameters from definitions
const handDefinition = HAND_DEFINITIONS.find(h => h.id === hand.id);
const parameters: HandParameter[] = handDefinition?.parameters || [];
// Form state for parameters
const [paramValues, setParamValues] = useState<Record<string, unknown>>({});
const [paramErrors, setParamErrors] = useState<Record<string, string>>({});
const [showParamsForm, setShowParamsForm] = useState(false);
// Initialize default values
useEffect(() => {
if (parameters.length > 0) {
const defaults: Record<string, unknown> = {};
parameters.forEach(p => {
if (p.defaultValue !== undefined) {
defaults[p.name] = p.defaultValue;
}
});
setParamValues(defaults);
}
}, [parameters]);
// Reset form when modal opens/closes
useEffect(() => {
if (isOpen) {
setShowParamsForm(false);
setParamErrors({});
}
}, [isOpen]);
const handleActivateClick = useCallback(() => {
if (parameters.length > 0 && !showParamsForm) {
// Show params form first
setShowParamsForm(true);
return;
}
// Validate parameters if showing form
if (showParamsForm) {
const errors = validateAllParameters(parameters, paramValues);
setParamErrors(errors);
if (Object.keys(errors).length > 0) {
return;
}
// Pass parameters to onActivate
onActivate();
} else {
onActivate();
}
}, [parameters, showParamsForm, paramValues, onActivate]);
if (!isOpen) return null;
const canActivate = hand.status === 'idle' || hand.status === 'setup_needed';
@@ -210,6 +282,23 @@ function HandDetailsModal({ hand, isOpen, onClose, onActivate, isActivating }: H
</div>
)}
{/* Parameters Form (shown when activating) */}
{showParamsForm && parameters.length > 0 && (
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-3">
<h3 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-3">
</h3>
<HandParamsForm
parameters={parameters}
values={paramValues}
onChange={setParamValues}
errors={paramErrors}
disabled={isActivating}
presetKey={`hand-${hand.id}`}
/>
</div>
)}
{/* Dashboard Metrics */}
{hand.metrics && hand.metrics.length > 0 && (
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-3">
@@ -234,13 +323,13 @@ function HandDetailsModal({ hand, isOpen, onClose, onActivate, isActivating }: H
{/* Footer */}
<div className="flex items-center justify-end gap-2 p-4 border-t border-gray-200 dark:border-gray-700">
<button
onClick={onClose}
onClick={showParamsForm ? () => setShowParamsForm(false) : onClose}
className="px-4 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
>
{showParamsForm ? '返回' : '关闭'}
</button>
<button
onClick={onActivate}
onClick={handleActivateClick}
disabled={!canActivate || hasUnmetRequirements || isActivating}
className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
@@ -254,6 +343,11 @@ function HandDetailsModal({ hand, isOpen, onClose, onActivate, isActivating }: H
<Settings className="w-4 h-4" />
</>
) : showParamsForm ? (
<>
<Play className="w-4 h-4" />
</>
) : (
<>
<Zap className="w-4 h-4" />