Skip to content

Instantly share code, notes, and snippets.

@moosh3
Created April 30, 2025 23:19
Show Gist options
  • Save moosh3/1c7119dc32397c16ff3f4196306ba7a5 to your computer and use it in GitHub Desktop.
Save moosh3/1c7119dc32397c16ff3f4196306ba7a5 to your computer and use it in GitHub Desktop.
import React, { useState } from 'react';
import { X, Clock, FileText, Plus, Trash2 } from 'lucide-react';
import Button from './ui/Button';
import TextArea from './ui/TextArea';
import { agentIconMap } from '../utils/data';
import ScheduleBuilder from './scheduling/ScheduleBuilder';
import { Schedule, ScheduleValidationError, ContentBucket } from '../types';
interface Tool {
id: string;
name: string;
description: string;
enabled: boolean;
}
interface AgentSettingsModalProps {
isOpen: boolean;
onClose: () => void;
agent: {
name: string;
description: string;
icon: string;
instructions: string;
schedule?: Schedule;
contentBuckets?: ContentBucket[];
};
onSave: (settings: {
name: string;
description: string;
icon: string;
instructions: string;
tools: Tool[];
schedule?: Schedule;
contentBuckets: ContentBucket[];
}) => void;
availableContentBuckets: ContentBucket[];
}
const defaultTools: Tool[] = [
{
id: 'salesforce',
name: 'Salesforce',
description: 'Search for calls in Gong for integrations',
enabled: true,
},
{
id: 'slack',
name: 'Slack',
description: 'Search for messages in Slack for company',
enabled: true,
},
{
id: 'jira',
name: 'Jira',
description: 'Search the Jira release tracking product roadmap',
enabled: true,
},
{
id: 'gong',
name: 'Gong',
description: 'Track customer feature history',
enabled: true,
},
];
const AgentSettingsModal: React.FC<AgentSettingsModalProps> = ({
isOpen,
onClose,
agent,
onSave,
availableContentBuckets,
}) => {
const [name, setName] = useState(agent.name);
const [description, setDescription] = useState(agent.description);
const [selectedIcon, setSelectedIcon] = useState(agent.icon);
const [instructions, setInstructions] = useState(agent.instructions);
const [showIconSelector, setShowIconSelector] = useState(false);
const [tools, setTools] = useState<Tool[]>(defaultTools);
const [schedule, setSchedule] = useState<Schedule>(
agent.schedule || {
enabled: false,
frequency: 'daily',
interval: 1,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
}
);
const [scheduleErrors, setScheduleErrors] = useState<ScheduleValidationError[]>([]);
const [selectedBuckets, setSelectedBuckets] = useState<Set<string>>(
new Set(agent.contentBuckets?.map(bucket => bucket.id) || [])
);
if (!isOpen) return null;
const handleToolToggle = (toolId: string) => {
setTools(tools.map(tool =>
tool.id === toolId ? { ...tool, enabled: !tool.enabled } : tool
));
};
const handleBucketToggle = (bucketId: string) => {
const newSelected = new Set(selectedBuckets);
if (newSelected.has(bucketId)) {
newSelected.delete(bucketId);
} else {
newSelected.add(bucketId);
}
setSelectedBuckets(newSelected);
};
const handleSave = () => {
if (schedule.enabled && scheduleErrors.length > 0) {
return;
}
const selectedBucketObjects = availableContentBuckets.filter(
bucket => selectedBuckets.has(bucket.id)
);
onSave({
name,
description,
icon: selectedIcon,
instructions,
tools: tools.filter(tool => tool.enabled),
schedule: schedule.enabled ? schedule : undefined,
contentBuckets: selectedBucketObjects,
});
onClose();
};
const IconComponent = agentIconMap[selectedIcon] || agentIconMap.default;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div className="relative w-[90vw] max-w-2xl max-h-[90vh] rounded-xl bg-background shadow-xl">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<h2 className="text-xl font-semibold text-text">Agent settings</h2>
<button
onClick={onClose}
className="rounded-md p-1 hover:bg-background-secondary"
>
<X className="h-5 w-5 text-text-secondary" />
</button>
</div>
<div className="overflow-y-auto p-6" style={{ maxHeight: 'calc(90vh - 140px)' }}>
<div className="space-y-6">
<div>
<label className="mb-1 block text-sm font-medium text-text">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
placeholder="Enter agent name"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-text">
Description
</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
placeholder="Enter agent description"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-text">
Icon
</label>
<div className="relative">
<button
onClick={() => setShowIconSelector(!showIconSelector)}
className="flex items-center space-x-2 rounded-md border border-border bg-background px-4 py-2 text-sm text-text hover:bg-background-secondary"
>
<IconComponent className="h-5 w-5 text-primary" />
<span>Change Icon</span>
</button>
{showIconSelector && (
<div className="absolute left-0 right-0 top-full z-10 mt-1 grid max-h-48 grid-cols-6 gap-2 overflow-y-auto rounded-md border border-border bg-background p-2 shadow-lg">
{Object.entries(agentIconMap).map(([key, Icon]) => (
<button
key={key}
onClick={() => {
setSelectedIcon(key);
setShowIconSelector(false);
}}
className={`flex items-center justify-center rounded-md p-2 hover:bg-background-secondary ${
selectedIcon === key ? 'bg-background-tertiary' : ''
}`}
>
<Icon className="h-5 w-5 text-primary" />
</button>
))}
</div>
)}
</div>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-text">
Instructions
</label>
<TextArea
value={instructions}
onChange={(e) => setInstructions(e.target.value)}
placeholder="Give instructions to your agent..."
className="min-h-[150px]"
/>
</div>
<div>
<h3 className="mb-4 flex items-center space-x-2 text-base font-medium text-text">
<FileText className="h-5 w-5" />
<span>Content Buckets</span>
</h3>
<div className="space-y-4">
{availableContentBuckets.map((bucket) => (
<div
key={bucket.id}
className="flex items-center justify-between rounded-lg border border-border bg-background-secondary p-4"
>
<div>
<h4 className="text-sm font-medium text-text">{bucket.name}</h4>
<p className="text-sm text-text-secondary">{bucket.description}</p>
<p className="mt-1 text-xs text-text-tertiary">
{bucket.files.length} files
</p>
</div>
<label className="relative inline-flex cursor-pointer items-center">
<input
type="checkbox"
checked={selectedBuckets.has(bucket.id)}
onChange={() => handleBucketToggle(bucket.id)}
className="peer sr-only"
/>
<div className="h-6 w-11 rounded-full bg-background-tertiary after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-border after:bg-white after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:ring-2 peer-focus:ring-primary"></div>
</label>
</div>
))}
</div>
</div>
<div>
<h3 className="mb-4 text-base font-medium text-text">Tools</h3>
<div className="space-y-4">
{tools.map((tool) => (
<div
key={tool.id}
className="flex items-center justify-between rounded-lg border border-border bg-background-secondary p-4"
>
<div>
<h4 className="text-sm font-medium text-text">{tool.name}</h4>
<p className="text-sm text-text-secondary">{tool.description}</p>
</div>
<label className="relative inline-flex cursor-pointer items-center">
<input
type="checkbox"
checked={tool.enabled}
onChange={() => handleToolToggle(tool.id)}
className="peer sr-only"
/>
<div className="h-6 w-11 rounded-full bg-background-tertiary after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-border after:bg-white after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:ring-2 peer-focus:ring-primary"></div>
</label>
</div>
))}
</div>
</div>
<div>
<h3 className="mb-4 flex items-center space-x-2 text-base font-medium text-text">
<Clock className="h-5 w-5" />
<span>Schedule</span>
</h3>
<div className="rounded-lg border border-border bg-background-secondary p-6">
<ScheduleBuilder
value={schedule}
onChange={setSchedule}
onValidationError={setScheduleErrors}
/>
</div>
</div>
</div>
</div>
<div className="border-t border-border bg-background p-6">
<div className="flex justify-end space-x-3">
<Button variant="outline" onClick={onClose}>
Cancel
</Button>
<Button
variant="primary"
onClick={handleSave}
disabled={
!name.trim() ||
!description.trim() ||
!instructions.trim() ||
(schedule.enabled && scheduleErrors.length > 0)
}
>
Save changes
</Button>
</div>
</div>
</div>
</div>
);
};
export default AgentSettingsModal;
import React, { useState } from 'react';
import { ArrowLeft, Play, Clock, Terminal, Settings } from 'lucide-react';
import Button from '../components/ui/Button';
import AgentSettingsModal from '../components/AgentSettingsModal';
import { Agent, AgentVariable } from '../types';
interface RunConfigProps {
agent: Agent;
onBack: () => void;
onRun: (config: {
variables: Record<string, string>;
streamLogs: boolean;
scheduledTime?: Date;
}) => void;
}
const RunConfig: React.FC<RunConfigProps> = ({ agent, onBack, onRun }) => {
const [variables, setVariables] = useState<Record<string, string>>({});
const [streamLogs, setStreamLogs] = useState(true);
const [executionType, setExecutionType] = useState<'immediate' | 'scheduled'>('immediate');
const [scheduledTime, setScheduledTime] = useState<string>('');
const [errors, setErrors] = useState<Record<string, string>>({});
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
const validateForm = () => {
const newErrors: Record<string, string> = {};
// Validate variables
if (agent.variables) {
agent.variables.forEach(variable => {
if (!variables[variable.name]?.trim()) {
newErrors[variable.name] = 'This field is required';
}
});
}
// Validate scheduled time
if (executionType === 'scheduled') {
if (!scheduledTime) {
newErrors.scheduledTime = 'Scheduled time is required';
} else {
const selectedTime = new Date(scheduledTime);
if (selectedTime <= new Date()) {
newErrors.scheduledTime = 'Scheduled time must be in the future';
}
}
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleRun = () => {
if (!validateForm()) return;
onRun({
variables,
streamLogs,
...(executionType === 'scheduled' ? { scheduledTime: new Date(scheduledTime) } : {}),
});
};
const handleVariableChange = (name: string, value: string) => {
setVariables(prev => ({ ...prev, [name]: value }));
if (errors[name]) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[name];
return newErrors;
});
}
};
// Get minimum datetime for scheduler (current time + 5 minutes)
const getMinDateTime = () => {
const date = new Date();
date.setMinutes(date.getMinutes() + 5);
return date.toISOString().slice(0, 16);
};
return (
<div className="flex h-full flex-col bg-background">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<div className="flex items-center">
<button
onClick={onBack}
className="mr-4 rounded-md p-1 hover:bg-background-secondary"
>
<ArrowLeft className="h-5 w-5 text-text-secondary" />
</button>
<div>
<h1 className="text-xl font-semibold text-text">Run Configuration</h1>
<p className="text-sm text-text-secondary">{agent.name}</p>
</div>
</div>
<Button
variant="outline"
icon={<Settings className="h-4 w-4" />}
onClick={() => setIsSettingsModalOpen(true)}
>
Settings
</Button>
</div>
<div className="flex-1 overflow-y-auto p-6">
<div className="mx-auto max-w-2xl space-y-8">
{agent.variables && agent.variables.length > 0 && (
<div>
<h2 className="mb-4 text-lg font-medium text-text">Variables</h2>
<div className="space-y-4 rounded-lg border border-border bg-background-secondary p-6">
{agent.variables.map((variable: AgentVariable) => (
<div key={variable.name}>
<label className="mb-1 block text-sm font-medium text-text">
{variable.name}
</label>
<p className="mb-2 text-sm text-text-secondary">
{variable.description}
</p>
<input
type="text"
value={variables[variable.name] || ''}
onChange={(e) => handleVariableChange(variable.name, e.target.value)}
className={`w-full rounded-md border ${
errors[variable.name] ? 'border-red-500' : 'border-border'
} bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary`}
placeholder={`Enter ${variable.name}`}
/>
{errors[variable.name] && (
<p className="mt-1 text-sm text-red-500">{errors[variable.name]}</p>
)}
</div>
))}
</div>
</div>
)}
<div>
<h2 className="mb-4 text-lg font-medium text-text">Execution Settings</h2>
<div className="space-y-6 rounded-lg border border-border bg-background-secondary p-6">
<div>
<label className="mb-4 flex items-center space-x-2">
<input
type="checkbox"
checked={streamLogs}
onChange={(e) => setStreamLogs(e.target.checked)}
className="h-4 w-4 rounded border-border text-primary focus:ring-primary"
/>
<span className="text-sm font-medium text-text">Stream Logs</span>
<Terminal className="ml-1 h-4 w-4 text-text-secondary" />
</label>
<p className="text-sm text-text-secondary">
View logs in real-time as the agent executes
</p>
</div>
<div className="space-y-4">
<div>
<label className="mb-2 block text-sm font-medium text-text">
Execution Timing
</label>
<div className="space-y-2">
<label className="flex items-center space-x-2">
<input
type="radio"
value="immediate"
checked={executionType === 'immediate'}
onChange={(e) => setExecutionType(e.target.value as 'immediate' | 'scheduled')}
className="h-4 w-4 border-border text-primary focus:ring-primary"
/>
<span className="text-sm text-text">Run immediately</span>
</label>
<label className="flex items-center space-x-2">
<input
type="radio"
value="scheduled"
checked={executionType === 'scheduled'}
onChange={(e) => setExecutionType(e.target.value as 'immediate' | 'scheduled')}
className="h-4 w-4 border-border text-primary focus:ring-primary"
/>
<span className="text-sm text-text">Schedule for later</span>
</label>
</div>
</div>
{executionType === 'scheduled' && (
<div>
<label className="mb-1 block text-sm font-medium text-text">
Scheduled Time
</label>
<input
type="datetime-local"
value={scheduledTime}
onChange={(e) => setScheduledTime(e.target.value)}
min={getMinDateTime()}
className={`w-full rounded-md border ${
errors.scheduledTime ? 'border-red-500' : 'border-border'
} bg-background px-4 py-2 text-sm text-text focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary`}
/>
{errors.scheduledTime && (
<p className="mt-1 text-sm text-red-500">{errors.scheduledTime}</p>
)}
</div>
)}
</div>
</div>
</div>
</div>
</div>
<div className="border-t border-border bg-background p-6">
<div className="mx-auto max-w-2xl">
<Button
variant="primary"
onClick={handleRun}
icon={executionType === 'immediate' ? <Play className="h-4 w-4" /> : <Clock className="h-4 w-4" />}
className="w-full"
>
{executionType === 'immediate' ? 'Run Agent' : 'Schedule Run'}
</Button>
</div>
</div>
<AgentSettingsModal
isOpen={isSettingsModalOpen}
onClose={() => setIsSettingsModalOpen(false)}
agent={{
name: agent.name,
description: agent.description,
icon: agent.icon || 'default',
instructions: agent.instructions || '',
schedule: agent.schedule,
}}
onSave={(settings) => {
// Handle saving the updated settings
setIsSettingsModalOpen(false);
}}
availableContentBuckets={[]}
/>
</div>
);
};
export default RunConfig;
import React, { useState } from 'react';
import { ArrowLeft, Play, Clock, Terminal, Settings } from 'lucide-react';
import Button from '../components/ui/Button';
import TeamSettingsModal from '../components/TeamSettingsModal';
import { Team, AgentVariable } from '../types';
interface TeamRunConfigProps {
team: Team;
onBack: () => void;
onRun: (config: {
teamVariables: Record<string, string>;
agentVariables: Record<string, Record<string, string>>;
streamLogs: boolean;
scheduledTime?: Date;
}) => void;
}
const TeamRunConfig: React.FC<TeamRunConfigProps> = ({ team, onBack, onRun }) => {
const [teamVariables, setTeamVariables] = useState<Record<string, string>>({});
const [agentVariables, setAgentVariables] = useState<Record<string, Record<string, string>>>({});
const [streamLogs, setStreamLogs] = useState(true);
const [executionType, setExecutionType] = useState<'immediate' | 'scheduled'>('immediate');
const [scheduledTime, setScheduledTime] = useState<string>('');
const [errors, setErrors] = useState<Record<string, string>>({});
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
const validateForm = () => {
const newErrors: Record<string, string> = {};
// Validate team variables
if (team.variables) {
team.variables.forEach(variable => {
if (!teamVariables[variable.name]?.trim()) {
newErrors[`team-${variable.name}`] = 'This field is required';
}
});
}
// Validate agent variables
team.agents.forEach(agent => {
if (agent.variables) {
agent.variables.forEach(variable => {
if (!agentVariables[agent.id]?.[variable.name]?.trim()) {
newErrors[`${agent.id}-${variable.name}`] = 'This field is required';
}
});
}
});
// Validate scheduled time
if (executionType === 'scheduled') {
if (!scheduledTime) {
newErrors.scheduledTime = 'Scheduled time is required';
} else {
const selectedTime = new Date(scheduledTime);
if (selectedTime <= new Date()) {
newErrors.scheduledTime = 'Scheduled time must be in the future';
}
}
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleRun = () => {
if (!validateForm()) return;
onRun({
teamVariables,
agentVariables,
streamLogs,
...(executionType === 'scheduled' ? { scheduledTime: new Date(scheduledTime) } : {}),
});
};
const handleTeamVariableChange = (name: string, value: string) => {
setTeamVariables(prev => ({
...prev,
[name]: value,
}));
const errorKey = `team-${name}`;
if (errors[errorKey]) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[errorKey];
return newErrors;
});
}
};
const handleAgentVariableChange = (agentId: string, name: string, value: string) => {
setAgentVariables(prev => ({
...prev,
[agentId]: {
...(prev[agentId] || {}),
[name]: value,
},
}));
const errorKey = `${agentId}-${name}`;
if (errors[errorKey]) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[errorKey];
return newErrors;
});
}
};
// Get minimum datetime for scheduler (current time + 5 minutes)
const getMinDateTime = () => {
const date = new Date();
date.setMinutes(date.getMinutes() + 5);
return date.toISOString().slice(0, 16);
};
return (
<div className="flex h-full flex-col bg-background">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<div className="flex items-center">
<button
onClick={onBack}
className="mr-4 rounded-md p-1 hover:bg-background-secondary"
>
<ArrowLeft className="h-5 w-5 text-text-secondary" />
</button>
<div>
<h1 className="text-xl font-semibold text-text">Team Run Configuration</h1>
<p className="text-sm text-text-secondary">{team.name}</p>
</div>
</div>
<Button
variant="outline"
icon={<Settings className="h-4 w-4" />}
onClick={() => setIsSettingsModalOpen(true)}
>
Settings
</Button>
</div>
<div className="flex-1 overflow-y-auto p-6">
<div className="mx-auto max-w-2xl space-y-8">
{team.variables && team.variables.length > 0 && (
<div>
<h2 className="mb-4 text-lg font-medium text-text">Team Variables</h2>
<div className="space-y-4 rounded-lg border border-border bg-background-secondary p-6">
{team.variables.map((variable: AgentVariable) => (
<div key={variable.name}>
<label className="mb-1 block text-sm font-medium text-text">
{variable.name}
</label>
<p className="mb-2 text-sm text-text-secondary">
{variable.description}
</p>
<input
type="text"
value={teamVariables[variable.name] || ''}
onChange={(e) => handleTeamVariableChange(variable.name, e.target.value)}
className={`w-full rounded-md border ${
errors[`team-${variable.name}`] ? 'border-red-500' : 'border-border'
} bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary`}
placeholder={`Enter ${variable.name}`}
/>
{errors[`team-${variable.name}`] && (
<p className="mt-1 text-sm text-red-500">{errors[`team-${variable.name}`]}</p>
)}
</div>
))}
</div>
</div>
)}
{team.agents.map(agent => (
agent.variables && agent.variables.length > 0 && (
<div key={agent.id}>
<h2 className="mb-4 text-lg font-medium text-text">{agent.name} Variables</h2>
<div className="space-y-4 rounded-lg border border-border bg-background-secondary p-6">
{agent.variables.map((variable: AgentVariable) => (
<div key={variable.name}>
<label className="mb-1 block text-sm font-medium text-text">
{variable.name}
</label>
<p className="mb-2 text-sm text-text-secondary">
{variable.description}
</p>
<input
type="text"
value={agentVariables[agent.id]?.[variable.name] || ''}
onChange={(e) => handleAgentVariableChange(agent.id, variable.name, e.target.value)}
className={`w-full rounded-md border ${
errors[`${agent.id}-${variable.name}`] ? 'border-red-500' : 'border-border'
} bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary`}
placeholder={`Enter ${variable.name}`}
/>
{errors[`${agent.id}-${variable.name}`] && (
<p className="mt-1 text-sm text-red-500">{errors[`${agent.id}-${variable.name}`]}</p>
)}
</div>
))}
</div>
</div>
)
))}
<div>
<h2 className="mb-4 text-lg font-medium text-text">Execution Settings</h2>
<div className="space-y-6 rounded-lg border border-border bg-background-secondary p-6">
<div>
<label className="mb-4 flex items-center space-x-2">
<input
type="checkbox"
checked={streamLogs}
onChange={(e) => setStreamLogs(e.target.checked)}
className="h-4 w-4 rounded border-border text-primary focus:ring-primary"
/>
<span className="text-sm font-medium text-text">Stream Logs</span>
</label>
<p className="text-sm text-text-secondary">
View logs in real-time as the team executes
</p>
</div>
<div className="space-y-4">
<div>
<label className="mb-2 block text-sm font-medium text-text">
Execution Timing
</label>
<div className="space-y-2">
<label className="flex items-center space-x-2">
<input
type="radio"
value="immediate"
checked={executionType === 'immediate'}
onChange={(e) => setExecutionType(e.target.value as 'immediate' | 'scheduled')}
className="h-4 w-4 border-border text-primary focus:ring-primary"
/>
<span className="text-sm text-text">Run immediately</span>
</label>
<label className="flex items-center space-x-2">
<input
type="radio"
value="scheduled"
checked={executionType === 'scheduled'}
onChange={(e) => setExecutionType(e.target.value as 'immediate' | 'scheduled')}
className="h-4 w-4 border-border text-primary focus:ring-primary"
/>
<span className="text-sm text-text">Schedule for later</span>
</label>
</div>
</div>
{executionType === 'scheduled' && (
<div>
<label className="mb-1 block text-sm font-medium text-text">
Scheduled Time
</label>
<input
type="datetime-local"
value={scheduledTime}
onChange={(e) => setScheduledTime(e.target.value)}
min={getMinDateTime()}
className={`w-full rounded-md border ${
errors.scheduledTime ? 'border-red-500' : 'border-border'
} bg-background px-4 py-2 text-sm text-text focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary`}
/>
{errors.scheduledTime && (
<p className="mt-1 text-sm text-red-500">{errors.scheduledTime}</p>
)}
</div>
)}
</div>
</div>
</div>
</div>
</div>
<div className="border-t border-border bg-background p-6">
<div className="mx-auto max-w-2xl">
<Button
variant="primary"
onClick={handleRun}
icon={executionType === 'immediate' ? <Play className="h-4 w-4" /> : <Clock className="h-4 w-4" />}
className="w-full"
>
{executionType === 'immediate' ? 'Run Team' : 'Schedule Run'}
</Button>
</div>
</div>
<TeamSettingsModal
isOpen={isSettingsModalOpen}
onClose={() => setIsSettingsModalOpen(false)}
team={team}
onSave={(settings) => {
// Handle saving the updated settings
setIsSettingsModalOpen(false);
}}
/>
</div>
);
};
export default TeamRunConfig;
import React, { useState } from 'react';
import { X, Plus, Trash2, Bot } from 'lucide-react';
import Button from './ui/Button';
import { Team, Agent, AgentVariable } from '../types';
import { agentTemplates, userAgents } from '../utils/data';
interface TeamSettingsModalProps {
isOpen: boolean;
onClose: () => void;
team: Team;
onSave: (settings: {
name: string;
description: string;
type: 'route' | 'coordinate' | 'collaborate';
agents: Agent[];
variables: AgentVariable[];
}) => void;
}
const TeamSettingsModal: React.FC<TeamSettingsModalProps> = ({
isOpen,
onClose,
team,
onSave,
}) => {
const [name, setName] = useState(team.name);
const [description, setDescription] = useState(team.description);
const [type, setType] = useState(team.type);
const [selectedAgents, setSelectedAgents] = useState<Agent[]>(team.agents);
const [showAgentSelect, setShowAgentSelect] = useState(false);
const [variables, setVariables] = useState<AgentVariable[]>(team.variables || []);
if (!isOpen) return null;
const allAgents = [...agentTemplates, ...userAgents];
const handleAddVariable = () => {
setVariables([...variables, { name: '', description: '' }]);
};
const handleRemoveVariable = (index: number) => {
setVariables(variables.filter((_, i) => i !== index));
};
const handleVariableChange = (index: number, field: 'name' | 'description', value: string) => {
setVariables(variables.map((variable, i) =>
i === index ? { ...variable, [field]: value } : variable
));
};
const handleSave = () => {
onSave({
name,
description,
type,
agents: selectedAgents,
variables,
});
onClose();
};
const typeDescriptions = {
route: 'Agents work independently on assigned tasks',
coordinate: 'Agents work in sequence, passing results to the next agent',
collaborate: 'Agents work together, sharing context and knowledge'
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div className="w-[90vw] max-w-2xl rounded-xl bg-background shadow-xl">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<h2 className="text-xl font-semibold text-text">Team settings</h2>
<button
onClick={onClose}
className="rounded-md p-1 hover:bg-background-secondary"
>
<X className="h-5 w-5 text-text-secondary" />
</button>
</div>
<div className="overflow-y-auto p-6" style={{ maxHeight: 'calc(90vh - 140px)' }}>
<div className="space-y-6">
<div>
<label className="mb-1 block text-sm font-medium text-text">
Team Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
placeholder="Enter team name"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-text">
Description
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
placeholder="Describe the team's purpose"
rows={3}
/>
</div>
<div>
<label className="mb-2 block text-sm font-medium text-text">
Team Type
</label>
<div className="grid grid-cols-3 gap-4">
{(['route', 'coordinate', 'collaborate'] as const).map((t) => (
<button
key={t}
onClick={() => setType(t)}
className={`flex flex-col items-center rounded-lg border p-4 text-center transition-colors ${
type === t
? 'border-primary bg-primary/5'
: 'border-border hover:border-primary/50'
}`}
>
<span className="mb-2 text-sm font-medium capitalize text-text">
{t}
</span>
<p className="text-xs text-text-secondary">
{typeDescriptions[t]}
</p>
</button>
))}
</div>
</div>
<div>
<div className="mb-2 flex items-center justify-between">
<label className="text-sm font-medium text-text">
Team Variables
</label>
<Button
variant="outline"
size="sm"
onClick={handleAddVariable}
icon={<Plus className="h-4 w-4" />}
>
Add Variable
</Button>
</div>
<div className="space-y-3">
{variables.map((variable, index) => (
<div
key={index}
className="rounded-lg border border-border bg-background-secondary p-4"
>
<div className="mb-3 flex items-center justify-between">
<h4 className="text-sm font-medium text-text">
Variable {index + 1}
</h4>
<button
onClick={() => handleRemoveVariable(index)}
className="rounded-md p-1 text-text-secondary hover:bg-background hover:text-red-500"
>
<Trash2 className="h-4 w-4" />
</button>
</div>
<div className="space-y-3">
<div>
<label className="mb-1 block text-sm font-medium text-text">
Name
</label>
<input
type="text"
value={variable.name}
onChange={(e) => handleVariableChange(index, 'name', e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
placeholder="e.g., api_key"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-text">
Description
</label>
<input
type="text"
value={variable.description}
onChange={(e) => handleVariableChange(index, 'description', e.target.value)}
className="w-full rounded-md border border-border bg-background px-4 py-2 text-sm text-text placeholder-text-tertiary focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
placeholder="e.g., API key for external service"
/>
</div>
</div>
</div>
))}
</div>
</div>
<div>
<div className="mb-4 flex items-center justify-between">
<label className="text-sm font-medium text-text">
Team Agents ({selectedAgents.length}/5)
</label>
<Button
variant="outline"
size="sm"
onClick={() => setShowAgentSelect(true)}
disabled={selectedAgents.length >= 5}
>
Add Agent
</Button>
</div>
<div className="space-y-2">
{selectedAgents.map((agent) => (
<div
key={agent.id}
className="flex items-center justify-between rounded-lg border border-border bg-background-secondary p-3"
>
<div className="flex items-center space-x-3">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/10">
<Bot className="h-4 w-4 text-primary" />
</div>
<div>
<div className="font-medium text-text">{agent.name}</div>
<div className="text-sm text-text-secondary">
{agent.description}
</div>
</div>
</div>
<button
onClick={() => setSelectedAgents(selectedAgents.filter(a => a.id !== agent.id))}
className="text-text-secondary hover:text-text"
>
<X className="h-4 w-4" />
</button>
</div>
))}
</div>
</div>
</div>
</div>
<div className="border-t border-border bg-background p-6">
<div className="flex justify-end space-x-3">
<Button variant="outline" onClick={onClose}>
Cancel
</Button>
<Button
variant="primary"
onClick={handleSave}
disabled={
!name.trim() ||
!description.trim() ||
selectedAgents.length === 0 ||
variables.some(v => !v.name.trim() || !v.description.trim())
}
>
Save changes
</Button>
</div>
</div>
</div>
</div>
);
};
export default TeamSettingsModal;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment