Last updated: Aug 12, 2025, 11:07 AM UTC

Sasha Organization Setup: UI Implementation Plan

Purpose: Frontend UI implementation for project-specific organization setup with search term capture
Created: 2025-01-09
Dependencies: Related to Claude OAuth Integration Plan


Overview

This document outlines the UI implementation for Sasha's new organization setup flow, enabling local testing without Claude Code integration. The setup transforms from document-centric to search-term-centric, making organization URL/name/topic mandatory while documents become optional.

Architecture Context

User Browser β†’ Sasha Web UI β†’ Backend Services
                                 β”œβ”€β”€ Project Manager
                                 β”œβ”€β”€ Document Processor
                                 └── Mock Research Service (for testing)

Implementation Components

1. Database Schema Updates

File: server/database/init.sql

-- Add to company_profiles table
ALTER TABLE company_profiles ADD COLUMN project_id VARCHAR(36) UNIQUE;
ALTER TABLE company_profiles ADD COLUMN workspace_path TEXT;
ALTER TABLE company_profiles ADD COLUMN search_term TEXT;
ALTER TABLE company_profiles ADD COLUMN search_type TEXT; -- 'url', 'company', or 'topic'

2. Frontend Organization Setup Flow

File: src/components/OrganizationSetupScreen.jsx

Component Structure

OrganizationSetupScreen (Main)
β”œβ”€β”€ SearchTermStep (NEW - Step 1)
β”œβ”€β”€ DocumentUploadStep (Modified - Step 2)  
β”œβ”€β”€ ResearchSessionStep (NEW - Step 3)
└── ReviewStep (Modified - Step 4)

A. SearchTermStep Component (New)

const SearchTermStep = ({ onContinue }) => {
  const [searchTerm, setSearchTerm] = useState('');
  const [searchType, setSearchType] = useState(null);
  
  const analyzeSearchTerm = (term) => {
    if (term.match(/^https?:\/\//)) {
      setSearchType('url');
    } else if (term.match(/\b(Inc|Corp|LLC|Ltd|Company)\b/i)) {
      setSearchType('company');
    } else {
      setSearchType('topic');
    }
  };
  
  return (
    <div className="setup-step">
      <h2>What would you like Sasha to research?</h2>
      <input
        value={searchTerm}
        onChange={(e) => {
          setSearchTerm(e.target.value);
          analyzeSearchTerm(e.target.value);
        }}
        placeholder="Enter website URL, company name, or topic"
        className="search-input"
      />
      
      <div className="examples">
        <p>Examples:</p>
        <ul>
          <li>https://mycompany.com</li>
          <li>ACME Corporation</li>
          <li>Sarah's Consulting Practice</li>
          <li>Machine Learning Research</li>
        </ul>
      </div>
      
      {searchType && (
        <p className="detected-type">
          Detected: {searchType}
        </p>
      )}
      
      <button 
        onClick={() => onContinue(searchTerm, searchType)}
        disabled={!searchTerm}
      >
        Continue β†’
      </button>
    </div>
  );
};

B. DocumentUploadStep Component (Modified)

Key changes:

  • Emphasis on being optional
  • Clear "Skip & Start Research" button
  • Messaging about helping Sasha learn faster
// Modify existing component to:
// 1. Show it's optional prominently
// 2. Add skip button with equal visual weight
// 3. Update messaging

<h3>Help Sasha learn faster (optional)</h3>
<p>Upload documents about {searchTerm} for deeper insights</p>
<button onClick={skipDocuments} className="primary">
  Skip & Start Research β†’
</button>
<button onClick={uploadAndContinue} className="secondary">
  Upload Documents & Continue β†’
</button>

C. ResearchSessionStep Component (New)

const ResearchSessionStep = ({ searchTerm, projectId }) => {
  const [messages, setMessages] = useState([]);
  const [researchComplete, setResearchComplete] = useState(false);
  const [selectedOption, setSelectedOption] = useState('');
  
  useEffect(() => {
    // Start mock research for local testing
    startMockResearch();
  }, []);
  
  const startMockResearch = async () => {
    // Simulate research messages
    const mockMessages = [
      "Starting research on " + searchTerm,
      "Analyzing available information...",
      "Building knowledge base...",
      "Creating organization profile...",
      "Research complete!"
    ];
    
    for (const msg of mockMessages) {
      await new Promise(resolve => setTimeout(resolve, 1000));
      setMessages(prev => [...prev, msg]);
    }
    
    setResearchComplete(true);
  };
  
  return (
    <div className="research-session">
      <div className="chat-area">
        <h3>Setting Up Your Workspace</h3>
        <div className="messages">
          {messages.map((msg, i) => (
            <div key={i} className="message">{msg}</div>
          ))}
        </div>
      </div>
      
      {researchComplete && (
        <div className="completion-options">
          <h3>βœ… Research Complete!</h3>
          <p>Here's what I learned about {searchTerm}:</p>
          
          <div className="findings">
            {/* Mock findings for testing */}
            <ul>
              <li>Organization type identified</li>
              <li>Key information extracted</li>
              <li>Knowledge base created</li>
            </ul>
          </div>
          
          <div className="next-steps">
            <h4>What would you like to explore next?</h4>
            <ol>
              <li>Deep dive into products and features</li>
              <li>Analyze competitive landscape</li>
              <li>Research team and leadership</li>
              <li>Create executive briefing</li>
              <li>Generate customer personas</li>
              <li>Map technical architecture</li>
              <li>Develop marketing strategy</li>
              <li>Something else (describe)</li>
            </ol>
            
            <input
              type="text"
              value={selectedOption}
              onChange={(e) => setSelectedOption(e.target.value)}
              placeholder="Enter 1-8 or describe what you need"
            />
            
            <button onClick={() => handleOptionSelected(selectedOption)}>
              Continue β†’
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

D. ReviewStep Component (Modified)

Remove website field (already captured in Step 1):

  • Remove website input
  • Display search term instead
  • Show search type

E. Main Component Flow Updates

const OrganizationSetupScreen = () => {
  const [currentStep, setCurrentStep] = useState('searchTerm'); // Changed from 'upload'
  const [searchTerm, setSearchTerm] = useState('');
  const [searchType, setSearchType] = useState(null);
  const [projectId, setProjectId] = useState(null);
  const [uploadedFiles, setUploadedFiles] = useState([]);
  
  const handleSearchTermContinue = async (term, type) => {
    setSearchTerm(term);
    setSearchType(type);
    
    // Initialize project
    const response = await fetch('/api/profile/organization/initialize', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ searchTerm: term, searchType: type })
    });
    
    const { projectId } = await response.json();
    setProjectId(projectId);
    setCurrentStep('documents');
  };
  
  // Step routing
  if (currentStep === 'searchTerm') {
    return <SearchTermStep onContinue={handleSearchTermContinue} />;
  }
  
  if (currentStep === 'documents') {
    return <DocumentUploadStep 
      searchTerm={searchTerm}
      projectId={projectId}
      onContinue={() => setCurrentStep('research')}
      onSkip={() => setCurrentStep('research')}
    />;
  }
  
  if (currentStep === 'research') {
    return <ResearchSessionStep 
      searchTerm={searchTerm}
      projectId={projectId}
      onComplete={() => setCurrentStep('review')}
    />;
  }
  
  if (currentStep === 'review') {
    return <ReviewStep
      searchTerm={searchTerm}
      searchType={searchType}
      projectId={projectId}
      onComplete={handleCompleteSetup}
    />;
  }
};

3. Backend API Updates

File: server/routes/profile.js

New Initialize Endpoint

router.post('/organization/initialize', authenticateToken, async (req, res) => {
  const { searchTerm, searchType } = req.body;
  
  // Generate project ID
  const projectId = uuidv4();
  const sanitizedName = searchTerm.toLowerCase()
    .replace(/[^a-z0-9]/g, '-')
    .substring(0, 30);
  
  // Create project-specific upload directory
  const uploadPath = path.join('uploads/projects', projectId, 'onboarding');
  await fs.mkdir(uploadPath, { recursive: true });
  
  // Save initial project data
  companyDb.initializeProject(req.user.id, {
    projectId,
    searchTerm,
    searchType,
    workspacePath: `${sanitizedName}-${projectId}`
  });
  
  res.json({ 
    projectId, 
    searchType,
    uploadPath 
  });
});

Modified Upload Endpoint

// Dynamic upload path based on project
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const projectId = req.params.projectId;
    const uploadPath = path.join('uploads/projects', projectId, 'onboarding');
    cb(null, uploadPath);
  },
  filename: (req, file, cb) => {
    cb(null, `${Date.now()}-${file.originalname}`);
  }
});

router.post('/organization/documents/:projectId', 
  authenticateToken, 
  upload.array('documents', 20), 
  async (req, res) => {
    const { projectId } = req.params;
    // Process documents with project context
});

4. Workspace Manager Updates

File: server/services/workspace-manager.js

export async function setupOrganizationWorkspace(userId, organizationData, projectId) {
  const sanitizedName = organizationData.searchTerm
    .toLowerCase()
    .replace(/[^a-z0-9]/g, '-')
    .substring(0, 30);
    
  const workspacePath = path.join(
    process.env.HOME, 
    '.claude/projects',
    `${sanitizedName}-${projectId}`
  );
  
  // Create project-specific structure
  await fs.mkdir(path.join(workspacePath, 'docs/local'), { recursive: true });
  await fs.mkdir(path.join(workspacePath, 'docs/organization'), { recursive: true });
  await fs.mkdir(path.join(workspacePath, 'docs/guides'), { recursive: true });
  await fs.mkdir(path.join(workspacePath, 'docs/prompts'), { recursive: true });
  
  // Generate CLAUDE.md with search context
  const claudeMd = generateProjectClaudeMd({
    ...organizationData,
    projectId,
    searchTerm: organizationData.searchTerm,
    searchType: organizationData.searchType
  });
  
  await fs.writeFile(path.join(workspacePath, 'CLAUDE.md'), claudeMd);
  
  return workspacePath;
}

5. Mock Research Service (For Testing)

File: server/services/mock-research.js (New)

export class MockResearchService {
  async startResearch(searchTerm, searchType, projectId) {
    const messages = [];
    
    // Simulate research phases
    messages.push(`Initializing research for: ${searchTerm}`);
    await this.delay(1000);
    
    if (searchType === 'url') {
      messages.push(`Fetching website: ${searchTerm}`);
      messages.push('Analyzing site structure...');
      messages.push('Extracting company information...');
    } else if (searchType === 'company') {
      messages.push(`Searching for "${searchTerm}" online...`);
      messages.push('Finding official sources...');
      messages.push('Gathering company data...');
    } else {
      messages.push(`Researching topic: ${searchTerm}`);
      messages.push('Identifying key concepts...');
      messages.push('Building knowledge map...');
    }
    
    await this.delay(2000);
    messages.push('Creating knowledge base documents...');
    messages.push('βœ… Research complete!');
    
    return {
      messages,
      findings: this.generateMockFindings(searchTerm, searchType),
      nextSteps: this.getNextStepOptions()
    };
  }
  
  generateMockFindings(searchTerm, searchType) {
    return {
      organizationName: searchTerm,
      type: searchType,
      keyFindings: [
        'Organization structure identified',
        'Products and services catalogued',
        'Market position analyzed'
      ],
      aiReadiness: 'Moderate',
      documentsCreated: [
        'company-overview.md',
        'team-structure.md',
        'products-services.md'
      ]
    };
  }
  
  getNextStepOptions() {
    return [
      'Deep dive into products and features',
      'Analyze competitive landscape',
      'Research team and leadership',
      'Create executive briefing',
      'Generate customer personas',
      'Map technical architecture',
      'Develop marketing strategy',
      'Something else (describe)'
    ];
  }
  
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

File Structure Changes

New Upload Structure

uploads/
└── projects/
    └── {projectId}/
        └── onboarding/
            └── [uploaded files]

New Workspace Structure

~/.claude/projects/
└── {search-term}-{projectId}/
    β”œβ”€β”€ CLAUDE.md
    └── docs/
        β”œβ”€β”€ local/
        β”œβ”€β”€ organization/
        β”œβ”€β”€ guides/
        └── prompts/

Testing Plan

Local Testing Steps

  1. Start Development Server

    npm run dev
    
  2. Create New User Account

    • Sign up with test credentials
    • Should be redirected to organization setup
  3. Test Search Term Input

    • Try URL: https://example.com
    • Try company: ACME Corporation
    • Try topic: Machine Learning
    • Verify type detection works
  4. Test Document Upload

    • Test skip functionality
    • Test file upload
    • Verify project-specific paths
  5. Test Mock Research

    • Watch simulated messages
    • Verify completion
    • Check numbered options display
  6. Test Project Creation

    • Verify project ID generated
    • Check workspace directory created
    • Confirm upload path is project-specific

Validation Checklist

  • Search term is mandatory
  • Search type correctly detected
  • Project ID generated (UUID format)
  • Upload directory created at uploads/projects/{projectId}/
  • Workspace created at ~/.claude/projects/{name}-{projectId}/
  • Documents upload to project-specific path
  • Mock research completes successfully
  • Numbered options (1-8) displayed
  • Can input custom option
  • Setup completes and redirects properly

State Management Flow

searchTerm β†’ searchType β†’ projectId β†’ documents? β†’ research β†’ complete
    ↓            ↓           ↓           ↓            ↓         ↓
  (Step 1)    (Auto)    (Generated)  (Step 2)    (Step 3)  (Step 4)

UI/UX Considerations

Visual Hierarchy

  1. Search term input - Primary focus
  2. Examples - Secondary, helpful
  3. Skip buttons - Equal weight to continue buttons
  4. Research progress - Live, engaging
  5. Numbered options - Clear, actionable

Responsive Design

  • Mobile-friendly input fields
  • Stackable layout on small screens
  • Touch-friendly buttons
  • Readable text sizes

Loading States

  • Show spinner during project initialization
  • Stream research messages progressively
  • Disable buttons during async operations
  • Clear completion indicators

Next Steps

After UI Implementation

  1. Test complete flow locally
  2. Validate project isolation
  3. Add error handling
  4. Implement real research integration (see Claude OAuth Integration Plan)
  5. Add progress persistence
  6. Create onboarding analytics

Integration Points

  • Claude Code authentication (OAuth/API key)
  • Real research execution
  • WebSocket for live streaming
  • Prompt template system
  • Knowledge base generation

Success Metrics

  • Setup completion rate > 80%
  • Time to complete < 5 minutes
  • Project creation success rate 100%
  • Clear understanding of next steps
  • Proper project isolation verified

Related Documentation