Advertisement

← vibesdk

src/components/model-config-tabs.tsx

8.9 KB Download file
import { useState, useCallback } from 'react';
import { Search, RotateCcw, Play, Settings } from 'lucide-react';
import { Tabs } from '@cloudflare/kumo';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { toast } from 'sonner';
import { ConfigCard } from './config-card';
import { ConfigModal } from './config-modal';
import { categorizeAgent } from '@/utils/model-helpers';
import { WORKFLOW_TABS } from '@/lib/constants/workflow-tabs';
import type {
  ModelConfig,
  UserModelConfigWithMetadata,
  ModelConfigUpdate,
  AgentDisplayConfig
} from '@/api-types';

interface ModelConfigTabsProps {
  agentConfigs: AgentDisplayConfig[];
  modelConfigs: Record<string, UserModelConfigWithMetadata>;
  defaultConfigs: Record<string, ModelConfig>;
  loadingConfigs: boolean;
  onSaveConfig: (agentAction: string, config: ModelConfigUpdate) => Promise<void>;
  onTestConfig: (agentAction: string, tempConfig?: ModelConfigUpdate) => Promise<void>;
  onResetConfig: (agentAction: string) => Promise<void>;
  onResetAllConfigs: () => Promise<void>;
  testingConfig: string | null;
  savingConfigs: boolean;
}

export function ModelConfigTabs({
  agentConfigs,
  modelConfigs,
  defaultConfigs,
  loadingConfigs,
  onSaveConfig,
  onTestConfig, 
  onResetConfig,
  onResetAllConfigs,
  testingConfig,
  savingConfigs
}: ModelConfigTabsProps) {
  const [activeTab, setActiveTab] = useState('quickstart');
  const [searchTerm, setSearchTerm] = useState('');
  const [selectedConfigKey, setSelectedConfigKey] = useState<string | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);

  // Filter agent configs by search term
  const filteredAgentConfigs = agentConfigs.filter(config =>
    config.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
    config.description.toLowerCase().includes(searchTerm.toLowerCase())
  );

  // Get agents for a specific tab using dynamic categorization
  const getAgentsForTab = useCallback((tabId: string) => {
    return filteredAgentConfigs.filter(config => 
      categorizeAgent(config.key) === tabId
    );
  }, [filteredAgentConfigs]);

  // Count customized configs per tab
  const getCustomizedCountForTab = useCallback((tabId: string) => {
    const agents = getAgentsForTab(tabId);
    return agents.filter(agent => modelConfigs[agent.key]?.isUserOverride).length;
  }, [getAgentsForTab, modelConfigs]);

  // Handle opening config modal
  const handleConfigureAgent = (agentKey: string) => {
    setSelectedConfigKey(agentKey);
    setIsModalOpen(true);
  };

  // Handle closing config modal
  const handleCloseModal = () => {
    setSelectedConfigKey(null);
    setIsModalOpen(false);
  };

  // Handle bulk test all configured agents
  const handleTestAllConfigured = async () => {
    const customizedConfigs = agentConfigs.filter(config => 
      modelConfigs[config.key]?.isUserOverride
    );
    
    if (customizedConfigs.length === 0) {
      toast.info('No customized configurations to test');
      return;
    }

    let successCount = 0;
    let errorCount = 0;

    for (const config of customizedConfigs) {
      try {
        await onTestConfig(config.key);
        successCount++;
      } catch (error) {
        errorCount++;
      }
    }

    toast.success(`Tested ${customizedConfigs.length} configs: ${successCount} passed, ${errorCount} failed`);
  };

  if (loadingConfigs) {
    return (
      <div className="flex items-center gap-3 p-8">
        <Settings className="h-5 w-5 animate-spin text-text-tertiary" />
        <span className="text-sm text-text-tertiary">Loading model configurations...</span>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Header with controls */}
      <div className="flex flex-col sm:flex-row gap-4 justify-between items-start sm:items-center">
        <div className="flex-1">
          <h4 className="font-medium">Model Configuration Overrides</h4>
          <p className="text-sm text-text-tertiary">
            Customize AI model settings for different operations. Organized by workflow stage.
          </p>
        </div>
        
        <div className="flex flex-col sm:flex-row gap-2 w-full sm:w-auto">
          {/* Search */}
          <div className="relative">
            <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-text-tertiary" />
            <Input
              placeholder="Search configurations..."
              value={searchTerm}
              onChange={(e) => setSearchTerm(e.target.value)}
              className="pl-9 w-full sm:w-64 dark:bg-bg-1 bg-bg-4"
            />
          </div>
          
          {/* Action buttons */}
          <div className="flex gap-2">
            <Button
              variant="outline"
              size="sm"
              onClick={handleTestAllConfigured}
              className="gap-2"
            >
              <Play className="h-4 w-4" />
              Test All
            </Button>
            
            <Button
              variant="outline"
              size="sm"
              onClick={onResetAllConfigs}
              disabled={savingConfigs}
              className="gap-2"
            >
              <RotateCcw className="h-4 w-4" />
              {savingConfigs ? 'Resetting...' : 'Reset All'}
            </Button>
          </div>
        </div>
      </div>

      {/* Tabbed interface */}
      <div className="w-full">
        <Tabs
          value={activeTab}
          onValueChange={setActiveTab}
          tabs={Object.values(WORKFLOW_TABS).map((tab) => {
            const Icon = tab.icon;
            const customizedCount = getCustomizedCountForTab(tab.id);

            return {
              value: tab.id,
              label: (
                <span className="inline-flex items-center gap-2">
                  <Icon className="h-4 w-4" />
                  <span className="hidden sm:inline">{tab.label}</span>
                  {customizedCount > 0 && (
                    <Badge variant="secondary" className="text-xs h-5 w-5 rounded-full p-0 flex items-center justify-center">
                      {customizedCount}
                    </Badge>
                  )}
                </span>
              ),
            };
          })}
        />

        {Object.values(WORKFLOW_TABS).map((tab) => {
          if (tab.id !== activeTab) return null;

          const agents = getAgentsForTab(tab.id);
           
          return (
            <div key={tab.id} className="mt-6">
              <div className="space-y-4">
                {/* Tab description */}
                <div className="text-sm text-text-tertiary">
                  {tab.description} • {agents.length} agent{agents.length !== 1 ? 's' : ''}
                  {getCustomizedCountForTab(tab.id) > 0 && (
                    <span className="ml-2 text-text-primary font-medium">
                      ({getCustomizedCountForTab(tab.id)} customized)
                    </span>
                  )}
                </div>

                {/* Agent config cards */}
                {agents.length === 0 ? (
                  <div className="text-center py-8 text-text-tertiary">
                    {searchTerm ? 'No configurations match your search.' : 'No configurations in this category.'}
                  </div>
                ) : (
                  <div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 gap-8 auto-rows-fr">
                    {agents.map((agent) => (
                      <ConfigCard
                        key={agent.key}
                        agent={agent}
                        userConfig={modelConfigs[agent.key]}
                        defaultConfig={defaultConfigs[agent.key]}
                        onConfigure={() => handleConfigureAgent(agent.key)}
                        onTest={() => onTestConfig(agent.key)}
                        onReset={() => onResetConfig(agent.key)}
                        isTesting={testingConfig === agent.key}
                      />
                    ))}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>

      {/* Configuration Modal */}
      {selectedConfigKey && (
        <ConfigModal
          isOpen={isModalOpen}
          onClose={handleCloseModal}
          agentConfig={agentConfigs.find(a => a.key === selectedConfigKey)!}
          userConfig={modelConfigs[selectedConfigKey]}
          defaultConfig={defaultConfigs[selectedConfigKey]}
          onSave={(config) => onSaveConfig(selectedConfigKey, config)}
          onTest={(tempConfig) => onTestConfig(selectedConfigKey, tempConfig)}
          onReset={() => onResetConfig(selectedConfigKey)}
          isTesting={testingConfig === selectedConfigKey}
        />
      )}
    </div>
  );
}