首页布局优化前
This commit is contained in:
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user