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
Start Development Server
npm run devCreate New User Account
- Sign up with test credentials
- Should be redirected to organization setup
Test Search Term Input
- Try URL:
https://example.com - Try company:
ACME Corporation - Try topic:
Machine Learning - Verify type detection works
- Try URL:
Test Document Upload
- Test skip functionality
- Test file upload
- Verify project-specific paths
Test Mock Research
- Watch simulated messages
- Verify completion
- Check numbered options display
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
- Search term input - Primary focus
- Examples - Secondary, helpful
- Skip buttons - Equal weight to continue buttons
- Research progress - Live, engaging
- 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
- Test complete flow locally
- Validate project isolation
- Add error handling
- Implement real research integration (see Claude OAuth Integration Plan)
- Add progress persistence
- 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
- Claude OAuth Integration Plan - For Claude Code integration
- Organization Research Guide - Research methodology
- Prompt Templates - Research prompt templates