ASSISTANT_DETAILS_COMPONENT.md
Overview
The AssistantDetails component is a comprehensive form interface for creating and editing AI assistants. It provides a unified interface for configuring all essential assistant properties including templates, avatars, access control, and initial messaging.
Purpose
- Assistant Configuration: Complete form for setting up new assistants
- Template Integration: Selection from pre-built assistant templates
- Access Control Management: Configure sharing and permission settings
- Avatar Customization: Visual avatar selection and management
- Initial Message Setup: Define the assistant’s first interaction message
Key Features
Form Fields
- Template Selection: Choose from available assistant templates
- Avatar Configuration: Visual avatar picker with custom options
- Assistant Name: Text input for assistant display name
- Access Control: Multi-type permission system (private, domain, specific users, groups)
- Initial Message: Textarea for welcome message configuration
Access Control Types
- Private: Only creator access
- Domain: Users from specific email domains
- Specific: Individual email addresses
- Groups: Predefined user groups
Template Integration
- Template Preview: Visual template selection interface
- Template Inheritance: Inherits template settings as starting point
- Customization: Override template defaults with custom values
Component Interface
interface AssistantDetailsProps {
assistantName: string; // Current assistant name
assistantAvatar: string; // Current avatar URL
initialMessage: string; // Welcome message text
selectedTemplate: string; // Current template ID
availableTemplates: Template[]; // Available template options
currentUserEmail?: string; // User's email for domain detection
accessControl?: AccessConfig; // Current access configuration
onAssistantNameChange: (name: string) => void; // Name change handler
onAssistantAvatarChange: (avatar: string) => void; // Avatar change handler
onInitialMessageChange: (message: string) => void; // Message change handler
onTemplateChange: (templateId: string) => void; // Template change handler
onAccessControlChange?: (config: AccessConfig) => void; // Access change handler
}
// Access control types
export type AccessType = 'private' | 'domain' | 'specific' | 'domains' | 'group';
export interface AccessConfig {
type: AccessType;
emails?: string[]; // For 'specific' type
domains?: string[]; // For 'domains' type
groups?: string[]; // For 'group' type
}
// Template structure
interface Template {
id: string;
name: string;
description: string;
avatar?: string;
initialMessage?: string;
// Additional template properties...
}
Core Functionality
1. Template Selection Integration
// Template selection through EmissaryChoose component
<div className="w-full">
<Label>Select Assistant Template</Label>
<EmissaryChoose
templates={availableTemplates}
selectedTemplate={selectedTemplate}
onSelect={onTemplateChange}
/>
</div>
// Template change handler propagates to parent
const handleTemplateChange = (templateId: string) => {
onTemplateChange(templateId);
};
2. Avatar Selection Component
// Avatar selector with current avatar display
<AvatarSelector
currentAvatar={assistantAvatar}
onAvatarChange={onAssistantAvatarChange}
/>
// Avatar change handling
const handleAvatarChange = (newAvatar: string) => {
onAssistantAvatarChange(newAvatar);
};
3. Assistant Name Input
// Controlled text input for assistant name
<div className="grid gap-2">
<Label htmlFor="assistantName">Assistant Name</Label>
<Input
id="assistantName"
value={assistantName || ''}
onChange={(e) => onAssistantNameChange(e.target.value)}
className="max-w-full"
placeholder="e.g., Contract Analyzer Bot"
/>
</div>
4. Access Control Management
// Internal state for access control
const [accessType, setAccessType] = useState<AccessType>(userDomain ? 'domain' : 'private');
const [specificEmails, setSpecificEmails] = useState<string[]>([]);
const [specificDomains, setSpecificDomains] = useState<string[]>([]);
const [specificGroups, setSpecificGroups] = useState<string[]>([]);
// Domain extraction from user email
const userDomain = currentUserEmail ? currentUserEmail.split('@')[1] || '' : '';
// Access control component integration
<AccessSelector
value={accessType}
emails={specificEmails}
domains={specificDomains}
groups={specificGroups}
userDomain={userDomain}
onChange={handleAccessChange}
/>
5. Access Control State Management
// Update internal state when accessControl prop changes
useEffect(() => {
if (accessControl) {
setAccessType(accessControl.type);
if (accessControl.type === 'specific' && accessControl.emails) {
setSpecificEmails(accessControl.emails);
}
if (accessControl.type === 'domains' && accessControl.domains) {
setSpecificDomains(accessControl.domains);
}
if (accessControl.type === 'group' && accessControl.groups) {
setSpecificGroups(accessControl.groups);
}
}
}, [accessControl]);
// Handle access configuration changes
const handleAccessChange = (config: AccessConfig) => {
console.log("AccessChange called with config:", config);
setAccessType(config.type);
// Update specific arrays based on type
if (config.type === 'specific' && config.emails) {
setSpecificEmails(config.emails);
} else {
setSpecificEmails([]); // Clear if not specific type
}
if (config.type === 'domains' && config.domains) {
setSpecificDomains(config.domains);
} else {
setSpecificDomains([]); // Clear if not domains type
}
if (config.type === 'group' && config.groups) {
setSpecificGroups(config.groups);
} else {
setSpecificGroups([]); // Clear if not group type
}
// Pass the structured config object up to the parent
if (onAccessControlChange) {
onAccessControlChange(config);
}
};
6. Initial Message Configuration
// Multi-line textarea for initial message
<div className="grid gap-2">
<Label htmlFor="initialMessage">Initial Message</Label>
<Textarea
id="initialMessage"
value={initialMessage || ''}
onChange={(e) => onInitialMessageChange(e.target.value)}
rows={4}
className="w-full resize-y font-serif"
placeholder="The first message the assistant sends to the user."
/>
</div>
7. Default Access Control Setup
// Effect for initial domain setup
useEffect(() => {
// Only set default if type is currently domain and no existing access control
if (userDomain && accessType === 'domain' && !accessControl) {
// Set domain-based access as default for users with organizational emails
handleAccessChange({ type: 'domain'});
}
}, [userDomain, accessType, accessControl]);
State Management
Local State Variables
// Access control internal state
const [accessType, setAccessType] = useState<AccessType>('private');
const [specificEmails, setSpecificEmails] = useState<string[]>([]);
const [specificDomains, setSpecificDomains] = useState<string[]>([]);
const [specificGroups, setSpecificGroups] = useState<string[]>([]);
// Default access type based on user domain
const defaultAccessType = userDomain ? 'domain' : 'private';
Controlled Components Pattern
// All form inputs are controlled components
// Values come from props, changes propagate to parent
// Example: Assistant name control
value={assistantName || ''} // Controlled value
onChange={(e) => onAssistantNameChange(e.target.value)} // Change handler
// Parent component manages the actual state
const [assistantName, setAssistantName] = useState('');
const [assistantAvatar, setAssistantAvatar] = useState('');
const [initialMessage, setInitialMessage] = useState('');
Integration Examples
Basic Usage
// Basic assistant creation form
function CreateAssistantPage() {
const [assistantName, setAssistantName] = useState('');
const [assistantAvatar, setAssistantAvatar] = useState('');
const [initialMessage, setInitialMessage] = useState('');
const [selectedTemplate, setSelectedTemplate] = useState('');
const [accessControl, setAccessControl] = useState<AccessConfig>({ type: 'private' });
return (
<AssistantDetails
assistantName={assistantName}
assistantAvatar={assistantAvatar}
initialMessage={initialMessage}
selectedTemplate={selectedTemplate}
availableTemplates={templates}
currentUserEmail={user?.email}
accessControl={accessControl}
onAssistantNameChange={setAssistantName}
onAssistantAvatarChange={setAssistantAvatar}
onInitialMessageChange={setInitialMessage}
onTemplateChange={setSelectedTemplate}
onAccessControlChange={setAccessControl}
/>
);
}
Edit Mode Integration
// Editing existing assistant
function EditAssistantPage({ assistantId }) {
const [assistant, setAssistant] = useState(null);
useEffect(() => {
// Load existing assistant data
loadAssistant(assistantId).then(setAssistant);
}, [assistantId]);
const handleSave = async () => {
await updateAssistant(assistantId, {
name: assistant.name,
avatar: assistant.avatar,
initialMessage: assistant.initialMessage,
accessControl: assistant.accessControl
});
};
if (!assistant) return <Loading />;
return (
<form onSubmit={handleSave}>
<AssistantDetails
assistantName={assistant.name}
assistantAvatar={assistant.avatar}
initialMessage={assistant.initialMessage}
selectedTemplate={assistant.templateId}
availableTemplates={templates}
currentUserEmail={user?.email}
accessControl={assistant.accessControl}
onAssistantNameChange={(name) => setAssistant({...assistant, name})}
onAssistantAvatarChange={(avatar) => setAssistant({...assistant, avatar})}
onInitialMessageChange={(message) => setAssistant({...assistant, initialMessage: message})}
onTemplateChange={(templateId) => setAssistant({...assistant, templateId})}
onAccessControlChange={(accessControl) => setAssistant({...assistant, accessControl})}
/>
<Button type="submit">Save Changes</Button>
</form>
);
}
Wizard Integration
// Multi-step assistant creation wizard
function AssistantCreationWizard() {
const [currentStep, setCurrentStep] = useState(0);
const [assistantData, setAssistantData] = useState({
name: '',
avatar: '',
initialMessage: '',
templateId: '',
accessControl: { type: 'private' as AccessType }
});
const steps = [
{ title: "Template Selection", component: TemplateStep },
{
title: "Assistant Details",
component: () => (
<AssistantDetails
{...assistantData}
availableTemplates={templates}
currentUserEmail={user?.email}
onAssistantNameChange={(name) => setAssistantData({...assistantData, name})}
onAssistantAvatarChange={(avatar) => setAssistantData({...assistantData, avatar})}
onInitialMessageChange={(initialMessage) => setAssistantData({...assistantData, initialMessage})}
onTemplateChange={(templateId) => setAssistantData({...assistantData, templateId})}
onAccessControlChange={(accessControl) => setAssistantData({...assistantData, accessControl})}
/>
)
},
{ title: "Review & Create", component: ReviewStep }
];
return <WizardContainer steps={steps} currentStep={currentStep} />;
}
Validation and Error Handling
Input Validation
// Validation logic for assistant details
const validateAssistantDetails = (data: AssistantData): ValidationErrors => {
const errors: ValidationErrors = {};
if (!data.name?.trim()) {
errors.name = "Assistant name is required";
}
if (data.name && data.name.length > 100) {
errors.name = "Assistant name must be less than 100 characters";
}
if (!data.templateId) {
errors.template = "Please select a template";
}
if (data.accessControl?.type === 'specific' && !data.accessControl.emails?.length) {
errors.accessControl = "At least one email address is required for specific access";
}
return errors;
};
Error Display Integration
// Error handling in form component
function AssistantDetailsForm() {
const [errors, setErrors] = useState<ValidationErrors>({});
const handleSubmit = (data: AssistantData) => {
const validationErrors = validateAssistantDetails(data);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
// Proceed with submission
submitAssistant(data);
};
return (
<div>
<AssistantDetails {...props} />
{errors.name && <ErrorMessage>{errors.name}</ErrorMessage>}
{errors.template && <ErrorMessage>{errors.template}</ErrorMessage>}
{errors.accessControl && <ErrorMessage>{errors.accessControl}</ErrorMessage>}
</div>
);
}
Sub-Component Dependencies
EmissaryChoose Component
- Purpose: Template selection interface
- Props:
templates,selectedTemplate,onSelect - Features: Visual template preview, template filtering, selection handling
AvatarSelector Component
- Purpose: Avatar selection and customization
- Props:
currentAvatar,onAvatarChange - Features: Gallery view, custom URL support, image validation
AccessSelector Component
- Purpose: Access control configuration
- Props:
value,emails,domains,groups,userDomain,onChange - Features: Multi-type access control, domain detection, user management
Accessibility Features
- Form Labels: Proper label association with form inputs
- Keyboard Navigation: All interactive elements are keyboard accessible
- Screen Reader Support: Semantic HTML and ARIA attributes
- Error Announcements: Validation errors are announced to screen readers
- Focus Management: Logical tab order and focus indicators
Performance Considerations
- Controlled Components: Efficient state updates through parent management
- Debounced Input: Text inputs can be debounced to reduce update frequency
- Memoization: Sub-components can be memoized to prevent unnecessary re-renders
- Lazy Loading: Template and avatar data can be loaded on-demand
- Validation Optimization: Client-side validation reduces server requests
Security Considerations
- Input Sanitization: All user inputs should be sanitized before processing
- Access Control Validation: Server-side validation of access control settings
- Email Validation: Proper email format validation for specific access types
- Domain Verification: Verification of domain-based access permissions
- XSS Prevention: Proper escaping of user-generated content in initial messages