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

  1. Form Labels: Proper label association with form inputs
  2. Keyboard Navigation: All interactive elements are keyboard accessible
  3. Screen Reader Support: Semantic HTML and ARIA attributes
  4. Error Announcements: Validation errors are announced to screen readers
  5. Focus Management: Logical tab order and focus indicators

Performance Considerations

  1. Controlled Components: Efficient state updates through parent management
  2. Debounced Input: Text inputs can be debounced to reduce update frequency
  3. Memoization: Sub-components can be memoized to prevent unnecessary re-renders
  4. Lazy Loading: Template and avatar data can be loaded on-demand
  5. Validation Optimization: Client-side validation reduces server requests

Security Considerations

  1. Input Sanitization: All user inputs should be sanitized before processing
  2. Access Control Validation: Server-side validation of access control settings
  3. Email Validation: Proper email format validation for specific access types
  4. Domain Verification: Verification of domain-based access permissions
  5. XSS Prevention: Proper escaping of user-generated content in initial messages