Newer
Older
cortex-hub / frontend / src / features / agents / components / drilldown / ConfigPanel.js
import React from 'react';

const ConfigPanel = ({ 
    editConfig, 
    setEditConfig, 
    saving, 
    handleSaveConfig, 
    nodes, 
    allSkills, 
    triggers, 
    handleAddTrigger, 
    handleDeleteTrigger, 
    handleFireTrigger, 
    handleFireWebhook,
    newTriggerType, 
    setNewTriggerType, 
    newCronValue, 
    setNewCronValue, 
    newIntervalValue, 
    setNewIntervalValue, 
    newDefaultPrompt, 
    setNewDefaultPrompt,
    creatingTrigger,
    overrideText,
    setOverrideText,
    handleInjectOverride,
    agentId
}) => {
    return (
        <div className="p-6 h-full overflow-y-auto scrollbar-hide space-y-8">
             {/* Injection / Manual Run Section */}
             <div className="bg-gradient-to-br from-indigo-500/10 to-blue-500/10 border border-indigo-500/20 rounded-2xl p-6 shadow-sm">
                <div className="flex items-center gap-3 mb-4">
                    <div className="w-8 h-8 bg-indigo-500 rounded-lg flex items-center justify-center text-white shadow-lg">
                        <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
                    </div>
                    <div>
                        <h3 className="text-sm font-black uppercase tracking-widest text-indigo-700 dark:text-indigo-400">Manual Task Injection</h3>
                        <p className="text-[10px] text-gray-500 dark:text-gray-400 font-mono">Force the agent to process a specific prompt immediately.</p>
                    </div>
                </div>
                <form onSubmit={handleInjectOverride} className="flex gap-2">
                    <input 
                        type="text" 
                        value={overrideText}
                        onChange={(e) => setOverrideText(e.target.value)}
                        placeholder="e.g. 'List all files in /tmp' or 'Analyze current log'"
                        className="flex-1 bg-white/50 dark:bg-black/40 border border-indigo-500/20 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/30 transition-all font-mono"
                    />
                    <button 
                        type="submit"
                        className="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-3 rounded-xl text-xs font-black uppercase tracking-widest transition-all shadow-lg active:scale-95 whitespace-nowrap"
                    >
                        Inject ⚡
                    </button>
                </form>
            </div>

            <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
                {/* Configuration Section */}
                <div className="space-y-6">
                    <div className="flex items-center justify-between">
                        <h3 className="text-sm font-black uppercase tracking-widest text-gray-400">Core Identity</h3>
                        <button 
                            onClick={handleSaveConfig}
                            disabled={saving}
                            className="bg-indigo-600 hover:bg-indigo-700 disabled:bg-gray-400 text-white px-4 py-1.5 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all shadow-md active:scale-95"
                        >
                            {saving ? "Saving..." : "Save Config"}
                        </button>
                    </div>
                    
                    <div className="grid gap-4">
                        <div className="flex flex-col gap-1.5">
                            <label className="text-[10px] uppercase tracking-widest font-black text-gray-500 ml-1">Agent Name</label>
                            <input 
                                type="text" 
                                value={editConfig?.name || ""} 
                                onChange={(e) => setEditConfig({...editConfig, name: e.target.value})}
                                className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/20 transition-all shadow-sm"
                            />
                        </div>

                        <div className="flex flex-col gap-1.5">
                            <label className="text-[10px] uppercase tracking-widest font-black text-gray-500 ml-1">System Prompt / Role</label>
                            <textarea 
                                rows={8}
                                value={editConfig?.system_prompt || ""} 
                                onChange={(e) => setEditConfig({...editConfig, system_prompt: e.target.value})}
                                className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/20 transition-all font-mono shadow-sm leading-relaxed"
                            />
                        </div>

                        <div className="grid grid-cols-2 gap-4">
                            <div className="flex flex-col gap-1.5">
                                <label className="text-[10px] uppercase tracking-widest font-black text-gray-500 ml-1">Max Iterations</label>
                                <input 
                                    type="number" 
                                    value={editConfig?.max_loop_iterations || 20} 
                                    onChange={(e) => setEditConfig({...editConfig, max_loop_iterations: e.target.value})}
                                    className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-2 text-sm font-mono"
                                />
                            </div>
                            <div className="flex flex-col gap-1.5">
                                <label className="text-[10px] uppercase tracking-widest font-black text-gray-500 ml-1">Target Mesh Node</label>
                                <select 
                                    value={editConfig?.mesh_node_id || ""} 
                                    onChange={(e) => setEditConfig({...editConfig, mesh_node_id: e.target.value})}
                                    className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-2 text-sm"
                                >
                                    <option value="hub">Local Hub (Default)</option>
                                    {nodes.map(n => <option key={n.node_id} value={n.node_id}>{n.display_name} ({n.node_id})</option>)}
                                </select>
                            </div>
                        </div>
                    </div>
                </div>

                {/* Triggers & Automation Section */}
                <div className="space-y-6">
                    <h3 className="text-sm font-black uppercase tracking-widest text-gray-400">Automation & Triggers</h3>
                    
                    {/* Add Trigger Panel */}
                    <div className="bg-gray-50 dark:bg-black/20 border border-gray-200 dark:border-white/5 rounded-2xl p-5 space-y-4">
                        <div className="flex gap-4">
                            <div className="flex-1">
                                <label className="text-[9px] uppercase tracking-widest font-black text-gray-500 block mb-1.5">Type</label>
                                <select 
                                    value={newTriggerType} 
                                    onChange={(e) => setNewTriggerType(e.target.value)}
                                    className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2 text-xs"
                                >
                                    <option value="cron">Cron Schedule</option>
                                    <option value="interval">Fixed Interval</option>
                                    <option value="webhook">Webhook API</option>
                                </select>
                            </div>
                            <div className="flex-[2]">
                                <label className="text-[9px] uppercase tracking-widest font-black text-gray-500 block mb-1.5">Value</label>
                                {newTriggerType === 'cron' && (
                                    <input 
                                        type="text" 
                                        value={newCronValue} 
                                        onChange={(e) => setNewCronValue(e.target.value)}
                                        className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2 text-xs font-mono"
                                    />
                                )}
                                {newTriggerType === 'interval' && (
                                    <input 
                                        type="number" 
                                        value={newIntervalValue} 
                                        onChange={(e) => setNewIntervalValue(e.target.value)}
                                        className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2 text-xs font-mono"
                                    />
                                )}
                                {newTriggerType === 'webhook' && (
                                    <div className="text-[10px] text-gray-400 italic pt-2">Webhooks use a unique secure token</div>
                                )}
                            </div>
                        </div>
                        <div className="flex flex-col gap-1.5">
                            <label className="text-[9px] uppercase tracking-widest font-black text-gray-500 block mb-1.5">Default Task Prompt</label>
                            <textarea 
                                value={newDefaultPrompt}
                                onChange={(e) => setNewDefaultPrompt(e.target.value)}
                                placeholder="Prompt to send when triggered..."
                                className="w-full bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg px-3 py-2 text-xs font-mono"
                                rows={2}
                            />
                        </div>
                        <button 
                            onClick={handleAddTrigger}
                            disabled={creatingTrigger}
                            className="w-full bg-gray-900 dark:bg-indigo-600 text-white font-black uppercase tracking-widest text-[10px] py-2.5 rounded-xl shadow-lg hover:scale-[1.02] active:scale-95 transition-all"
                        >
                            {creatingTrigger ? 'Creating...' : 'Register Trigger'}
                        </button>
                    </div>

                    {/* Trigger List */}
                    <div className="space-y-3">
                        {triggers.map(t => (
                            <div key={t.id} className="relative bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700/50 p-4 rounded-xl shadow-sm flex justify-between items-center group hover:border-indigo-500/30 transition-all">
                                <div>
                                    <div className="flex items-center gap-2 mb-1">
                                        <div className={`w-2 h-2 rounded-full ${t.is_active ? 'bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]' : 'bg-gray-400'}`}></div>
                                        <span className="text-[10px] font-black uppercase tracking-widest text-gray-800 dark:text-gray-200">{t.trigger_type}</span>
                                    </div>
                                    <p className="text-[11px] font-mono text-gray-500 dark:text-gray-400">
                                        {t.trigger_type === 'cron' ? t.cron_expression : t.trigger_type === 'interval' ? `Every ${t.interval_seconds}s` : 'REST API Token'}
                                    </p>
                                    <p className="text-[9px] text-gray-400 mt-1 truncate max-w-[200px]">Prompt: {t.default_prompt || '(using session default)'}</p>
                                </div>
                                <div className="flex items-center gap-2">
                                     {t.trigger_type === 'webhook' ? (
                                        <button 
                                            onClick={() => handleFireWebhook(t.webhook_secret, t.default_prompt)}
                                            className="p-2 text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-500/10 rounded-lg transition-colors"
                                            title="Test Webhook"
                                        >
                                            <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
                                        </button>
                                     ) : (
                                        <button 
                                            onClick={() => handleFireTrigger(t.default_prompt)}
                                            className="p-2 text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-500/10 rounded-lg transition-colors"
                                            title="Execute Manually"
                                        >
                                            <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
                                        </button>
                                     )}
                                     <button 
                                        onClick={() => handleDeleteTrigger(t.id)}
                                        className="p-2 text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10 rounded-lg transition-colors"
                                        title="Delete"
                                    >
                                        <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
                                    </button>
                                </div>
                            </div>
                        ))}
                    </div>
                </div>
            </div>
        </div>
    );
};

export default ConfigPanel;