refactor(store): split gatewayStore into specialized domain stores
Major restructuring: - Split monolithic gatewayStore into 5 focused stores: - connectionStore: WebSocket connection and gateway lifecycle - configStore: quickConfig, workspaceInfo, MCP services - agentStore: clones, usage stats, agent management - handStore: hands, approvals, triggers, hand runs - workflowStore: workflows, workflow runs, execution - Update all components to use new stores with selector pattern - Remove
This commit is contained in:
@@ -8,11 +8,8 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
useGatewayStore,
|
||||
type Approval,
|
||||
type ApprovalStatus,
|
||||
} from '../store/gatewayStore';
|
||||
import { useHandStore } from '../store/handStore';
|
||||
import type { Approval, ApprovalStatus } from '../store/handStore';
|
||||
import {
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
@@ -297,8 +294,10 @@ function EmptyState({ filter }: { filter: FilterStatus }) {
|
||||
// === Main ApprovalsPanel Component ===
|
||||
|
||||
export function ApprovalsPanel() {
|
||||
const { approvals, loadApprovals, respondToApproval, isLoading } =
|
||||
useGatewayStore();
|
||||
const approvals = useHandStore((s) => s.approvals);
|
||||
const loadApprovals = useHandStore((s) => s.loadApprovals);
|
||||
const respondToApproval = useHandStore((s) => s.respondToApproval);
|
||||
const isLoading = useHandStore((s) => s.isLoading);
|
||||
const [filter, setFilter] = useState<FilterStatus>('all');
|
||||
const [processingId, setProcessingId] = useState<string | null>(null);
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useGatewayStore } from '../store/gatewayStore';
|
||||
import { useAgentStore } from '../store/agentStore';
|
||||
import { useConnectionStore } from '../store/connectionStore';
|
||||
import { useConfigStore } from '../store/configStore';
|
||||
import { toChatAgent, useChatStore } from '../store/chatStore';
|
||||
import { Bot, Plus, X, Globe, Cat, Search, BarChart2, Sparkles } from 'lucide-react';
|
||||
import { AgentOnboardingWizard } from './AgentOnboardingWizard';
|
||||
import type { Clone } from '../store/agentStore';
|
||||
|
||||
export function CloneManager() {
|
||||
const { clones, loadClones, deleteClone, connectionState, quickConfig } = useGatewayStore();
|
||||
const clones = useAgentStore((s) => s.clones);
|
||||
const loadClones = useAgentStore((s) => s.loadClones);
|
||||
const deleteClone = useAgentStore((s) => s.deleteClone);
|
||||
const connectionState = useConnectionStore((s) => s.connectionState);
|
||||
const quickConfig = useConfigStore((s) => s.quickConfig);
|
||||
const { agents, currentAgent, setCurrentAgent } = useChatStore();
|
||||
const [showWizard, setShowWizard] = useState(false);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useGatewayStore, type Hand } from '../store/gatewayStore';
|
||||
import { useHandStore, type Hand } from '../store/handStore';
|
||||
import { Zap, Loader2, RefreshCw, CheckCircle, XCircle, AlertTriangle } from 'lucide-react';
|
||||
|
||||
interface HandListProps {
|
||||
@@ -42,7 +42,9 @@ const STATUS_LABELS: Record<Hand['status'], string> = {
|
||||
};
|
||||
|
||||
export function HandList({ selectedHandId, onSelectHand }: HandListProps) {
|
||||
const { hands, loadHands, isLoading } = useGatewayStore();
|
||||
const hands = useHandStore((s) => s.hands);
|
||||
const loadHands = useHandStore((s) => s.loadHands);
|
||||
const isLoading = useHandStore((s) => s.isLoading);
|
||||
|
||||
useEffect(() => {
|
||||
loadHands();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useGatewayStore, type Hand, type HandRun } from '../store/gatewayStore';
|
||||
import { useHandStore, type Hand, type HandRun } from '../store/handStore';
|
||||
import {
|
||||
Zap,
|
||||
Loader2,
|
||||
@@ -39,7 +39,12 @@ const RUN_STATUS_CONFIG: Record<string, { label: string; className: string; icon
|
||||
};
|
||||
|
||||
export function HandTaskPanel({ handId, onBack }: HandTaskPanelProps) {
|
||||
const { hands, handRuns, loadHands, loadHandRuns, triggerHand, isLoading } = useGatewayStore();
|
||||
const hands = useHandStore((s) => s.hands);
|
||||
const handRuns = useHandStore((s) => s.handRuns);
|
||||
const loadHands = useHandStore((s) => s.loadHands);
|
||||
const loadHandRuns = useHandStore((s) => s.loadHandRuns);
|
||||
const triggerHand = useHandStore((s) => s.triggerHand);
|
||||
const isLoading = useHandStore((s) => s.isLoading);
|
||||
const { toast } = useToast();
|
||||
const [selectedHand, setSelectedHand] = useState<Hand | null>(null);
|
||||
const [isActivating, setIsActivating] = useState(false);
|
||||
@@ -103,7 +108,7 @@ export function HandTaskPanel({ handId, onBack }: HandTaskPanelProps) {
|
||||
]);
|
||||
} else {
|
||||
// Check for specific error in store
|
||||
const storeError = useGatewayStore.getState().error;
|
||||
const storeError = useHandStore.getState().error;
|
||||
if (storeError?.includes('already active')) {
|
||||
toast(`Hand "${selectedHand.name}" 已在运行中`, 'warning');
|
||||
} else {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { FileText, Globe } from 'lucide-react';
|
||||
import { useGatewayStore } from '../../store/gatewayStore';
|
||||
import { useConfigStore } from '../../store/configStore';
|
||||
import { silentErrorHandler } from '../../lib/error-utils';
|
||||
|
||||
export function MCPServices() {
|
||||
const { quickConfig, saveQuickConfig } = useGatewayStore();
|
||||
const quickConfig = useConfigStore((s) => s.quickConfig);
|
||||
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
|
||||
|
||||
const services = quickConfig.mcpServices || [];
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
import { useGatewayStore } from '../../store/gatewayStore';
|
||||
import { useConfigStore } from '../../store/configStore';
|
||||
import { silentErrorHandler } from '../../lib/error-utils';
|
||||
|
||||
export function Privacy() {
|
||||
const { quickConfig, workspaceInfo, loadWorkspaceInfo, saveQuickConfig } = useGatewayStore();
|
||||
const quickConfig = useConfigStore((s) => s.quickConfig);
|
||||
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
|
||||
const loadWorkspaceInfo = useConfigStore((s) => s.loadWorkspaceInfo);
|
||||
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
|
||||
|
||||
useEffect(() => {
|
||||
loadWorkspaceInfo().catch(silentErrorHandler('Privacy'));
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useGatewayStore } from '../../store/gatewayStore';
|
||||
import { useAgentStore } from '../../store/agentStore';
|
||||
import { useConnectionStore } from '../../store/connectionStore';
|
||||
import { BarChart3, TrendingUp, Clock, Zap } from 'lucide-react';
|
||||
|
||||
export function UsageStats() {
|
||||
const { usageStats, loadUsageStats, connectionState } = useGatewayStore();
|
||||
const usageStats = useAgentStore((s) => s.usageStats);
|
||||
const loadUsageStats = useAgentStore((s) => s.loadUsageStats);
|
||||
const connectionState = useConnectionStore((s) => s.connectionState);
|
||||
const [timeRange, setTimeRange] = useState<'7d' | '30d' | 'all'>('7d');
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useGatewayStore } from '../../store/gatewayStore';
|
||||
import { useConfigStore } from '../../store/configStore';
|
||||
import { silentErrorHandler } from '../../lib/error-utils';
|
||||
|
||||
export function Workspace() {
|
||||
const {
|
||||
quickConfig,
|
||||
workspaceInfo,
|
||||
loadWorkspaceInfo,
|
||||
saveQuickConfig,
|
||||
} = useGatewayStore();
|
||||
const quickConfig = useConfigStore((s) => s.quickConfig);
|
||||
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
|
||||
const loadWorkspaceInfo = useConfigStore((s) => s.loadWorkspaceInfo);
|
||||
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
|
||||
const [projectDir, setProjectDir] = useState('~/.openfang/zclaw-workspace');
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { CloneManager } from './CloneManager';
|
||||
import { TeamList } from './TeamList';
|
||||
import { useGatewayStore } from '../store/gatewayStore';
|
||||
import { useConfigStore } from '../store/configStore';
|
||||
import { containerVariants, defaultTransition } from '../lib/animations';
|
||||
|
||||
export type MainViewType = 'chat' | 'automation' | 'team' | 'swarm' | 'skills';
|
||||
@@ -44,7 +44,7 @@ export function Sidebar({
|
||||
}: SidebarProps) {
|
||||
const [activeTab, setActiveTab] = useState<Tab>('clones');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const userName = useGatewayStore((state) => state.quickConfig.userName) || '用户7141';
|
||||
const userName = useConfigStore((state) => state.quickConfig?.userName) || '用户7141';
|
||||
|
||||
const handleNavClick = (key: Tab, mainView?: MainViewType) => {
|
||||
setActiveTab(key);
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useGatewayStore } from '../store/gatewayStore';
|
||||
import type { Trigger } from '../store/gatewayStore';
|
||||
import { useHandStore } from '../store/handStore';
|
||||
import type { Trigger } from '../store/handStore';
|
||||
import { CreateTriggerModal } from './CreateTriggerModal';
|
||||
import {
|
||||
Zap,
|
||||
@@ -105,7 +105,11 @@ function TriggerCard({ trigger, onToggle, onDelete, isToggling, isDeleting }: Tr
|
||||
}
|
||||
|
||||
export function TriggersPanel() {
|
||||
const { triggers, loadTriggers, isLoading, client, deleteTrigger } = useGatewayStore();
|
||||
const triggers = useHandStore((s) => s.triggers);
|
||||
const loadTriggers = useHandStore((s) => s.loadTriggers);
|
||||
const deleteTrigger = useHandStore((s) => s.deleteTrigger);
|
||||
const isLoading = useHandStore((s) => s.isLoading);
|
||||
const client = useHandStore((s) => s.client);
|
||||
const [togglingTrigger, setTogglingTrigger] = useState<string | null>(null);
|
||||
const [deletingTrigger, setDeletingTrigger] = useState<string | null>(null);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useGatewayStore, type Workflow, type WorkflowRun } from '../store/gatewayStore';
|
||||
import { useWorkflowStore, type Workflow, type WorkflowRun } from '../store/workflowStore';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Clock,
|
||||
@@ -113,7 +113,9 @@ function RunCard({ run, index }: RunCardProps) {
|
||||
}
|
||||
|
||||
export function WorkflowHistory({ workflow, onBack }: WorkflowHistoryProps) {
|
||||
const { loadWorkflowRuns, cancelWorkflow, isLoading } = useGatewayStore();
|
||||
const loadWorkflowRuns = useWorkflowStore((s) => s.loadWorkflowRuns);
|
||||
const cancelWorkflow = useWorkflowStore((s) => s.cancelWorkflow);
|
||||
const isLoading = useWorkflowStore((s) => s.isLoading);
|
||||
const [runs, setRuns] = useState<WorkflowRun[]>([]);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [cancellingRunId, setCancellingRunId] = useState<string | null>(null);
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useGatewayStore } from '../store/gatewayStore';
|
||||
import type { Workflow } from '../store/gatewayStore';
|
||||
import { useWorkflowStore, type Workflow } from '../store/workflowStore';
|
||||
import { WorkflowEditor } from './WorkflowEditor';
|
||||
import { WorkflowHistory } from './WorkflowHistory';
|
||||
import {
|
||||
@@ -236,7 +235,13 @@ function WorkflowRow({ workflow, onExecute, onEdit, onDelete, onHistory, isExecu
|
||||
// === Main WorkflowList Component ===
|
||||
|
||||
export function WorkflowList() {
|
||||
const { workflows, loadWorkflows, executeWorkflow, deleteWorkflow, createWorkflow, updateWorkflow, isLoading } = useGatewayStore();
|
||||
const workflows = useWorkflowStore((s) => s.workflows);
|
||||
const loadWorkflows = useWorkflowStore((s) => s.loadWorkflows);
|
||||
const triggerWorkflow = useWorkflowStore((s) => s.triggerWorkflow);
|
||||
const deleteWorkflow = useWorkflowStore((s) => s.deleteWorkflow);
|
||||
const createWorkflow = useWorkflowStore((s) => s.createWorkflow);
|
||||
const updateWorkflow = useWorkflowStore((s) => s.updateWorkflow);
|
||||
const isLoading = useWorkflowStore((s) => s.isLoading);
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('list');
|
||||
const [executingWorkflowId, setExecutingWorkflowId] = useState<string | null>(null);
|
||||
const [deletingWorkflowId, setDeletingWorkflowId] = useState<string | null>(null);
|
||||
@@ -254,11 +259,11 @@ export function WorkflowList() {
|
||||
const handleExecute = useCallback(async (id: string, input?: Record<string, unknown>) => {
|
||||
setExecutingWorkflowId(id);
|
||||
try {
|
||||
await executeWorkflow(id, input);
|
||||
await triggerWorkflow(id, input);
|
||||
} finally {
|
||||
setExecutingWorkflowId(null);
|
||||
}
|
||||
}, [executeWorkflow]);
|
||||
}, [triggerWorkflow]);
|
||||
|
||||
const handleExecuteClick = useCallback((workflow: Workflow) => {
|
||||
setSelectedWorkflow(workflow);
|
||||
|
||||
Reference in New Issue
Block a user