commit before mass find + replace

This commit is contained in:
Ra
2025-08-24 16:18:58 -07:00
parent f2145edf56
commit 1ed3e16ff6
28 changed files with 4850 additions and 1181 deletions

View File

@@ -1,392 +1,244 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useOrg } from '../contexts/OrgContext';
import { EnhancedFigmaQuestion, FigmaQuestionCard, EnhancedFigmaInput } from '../components/figma/EnhancedFigmaQuestion';
import { FigmaInput, FigmaSelect } from '../components/figma/FigmaInput';
import { FigmaMultipleChoice } from '../components/figma/FigmaMultipleChoice';
import { StoredImage } from '../services/imageStorageService';
interface OnboardingData {
// Step 0: Company Details
companyName: string;
yourName: string;
// Step 1: Company Size
companySize: string;
// Step 2: Mission
mission: string;
// Later steps
industry: string;
description: string;
vision: string;
values: string[];
foundingYear: string;
evolution: string;
majorMilestones: string;
advantages: string;
vulnerabilities: string;
competitors: string;
marketPosition: string;
currentChallenges: string[];
shortTermGoals: string;
longTermGoals: string;
keyMetrics: string;
cultureDescription: string;
workEnvironment: string;
leadershipStyle: string;
communicationStyle: string;
additionalContext: string;
}
const Onboarding: React.FC = () => {
const { org, upsertOrg, generateCompanyWiki } = useOrg();
const navigate = useNavigate();
useEffect(() => {
if (org?.onboardingCompleted) {
navigate('/reports', { replace: true });
}
}, [org, navigate]);
const [step, setStep] = useState(0);
const [isGeneratingReport, setIsGeneratingReport] = useState(false);
const [companyLogo, setCompanyLogo] = useState<StoredImage | null>(null);
const [formData, setFormData] = useState<OnboardingData>({
companyName: org?.name || '',
yourName: '',
companySize: '',
mission: '',
industry: '',
description: '',
vision: '',
values: [],
foundingYear: '',
evolution: '',
majorMilestones: '',
advantages: '',
vulnerabilities: '',
competitors: '',
marketPosition: '',
currentChallenges: [],
shortTermGoals: '',
longTermGoals: '',
keyMetrics: '',
cultureDescription: '',
workEnvironment: '',
leadershipStyle: '',
communicationStyle: '',
additionalContext: ''
});
const steps = [
{
title: 'Company Details',
description: 'Basic information about your company'
},
{
title: 'Company Size',
description: 'How many people work at your company'
},
{
title: 'Mission Statement',
description: 'What is the mission of your company'
},
{
title: 'Vision & Values',
description: 'Your company\'s vision and core values'
},
{
title: 'Company History',
description: 'How your company has evolved'
},
{
title: 'Market Position',
description: 'Your competitive landscape'
},
{
title: 'Goals & Challenges',
description: 'Current objectives and obstacles'
},
{
title: 'Team & Culture',
description: 'Your work environment and culture'
}
];
const handleNext = async () => {
if (isGeneratingReport) return;
if (step < steps.length - 1) {
setStep(prev => prev + 1);
return;
}
// Final step: persist org & generate report
setIsGeneratingReport(true);
try {
const companyWiki = {
name: formData.companyName,
industry: formData.industry,
size: formData.companySize,
description: formData.description,
mission: formData.mission,
vision: formData.vision,
values: formData.values.join(','),
foundingYear: formData.foundingYear,
evolution: formData.evolution,
majorMilestones: formData.majorMilestones,
advantages: formData.advantages,
vulnerabilities: formData.vulnerabilities,
competitors: formData.competitors,
marketPosition: formData.marketPosition,
currentChallenges: formData.currentChallenges.join(','),
shortTermGoals: formData.shortTermGoals,
longTermGoals: formData.longTermGoals,
keyMetrics: formData.keyMetrics,
cultureDescription: formData.cultureDescription,
workEnvironment: formData.workEnvironment,
leadershipStyle: formData.leadershipStyle,
communicationStyle: formData.communicationStyle,
additionalContext: formData.additionalContext,
onboardingCompleted: true
};
const newCompanyData = {
name: formData.companyName,
createdAt: Date.now(),
foundingYear: formData.foundingYear,
description: formData.description,
size: formData.companySize
};
await upsertOrg(newCompanyData);
await generateCompanyWiki({ ...newOrgData, orgId: org!.orgId });
setTimeout(() => {
navigate('/reports', { replace: true });
}, 100);
} catch (error) {
console.error('Error completing onboarding:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
alert(`There was an error completing the setup: ${errorMessage}. Please check the console for more details and try again.`);
} finally {
setIsGeneratingReport(false);
}
};
const handleBack = () => {
if (step > 0) setStep(step - 1);
};
const updateFormData = (field: keyof OnboardingData, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const handleImageUploaded = (image: StoredImage | null) => {
setCompanyLogo(image);
};
const canProceed = () => {
switch (step) {
case 0: // Company Details
return formData.companyName.trim().length > 0 && formData.yourName.trim().length > 0;
case 1: // Company Size
return formData.companySize.length > 0;
case 2: // Mission
return formData.mission.trim().length > 0;
case 3: // Vision & Values
return formData.vision.trim().length > 0;
case 4: // History
return formData.evolution.trim().length > 0;
case 5: // Market Position
return formData.advantages.trim().length > 0;
case 6: // Goals
return formData.shortTermGoals.trim().length > 0;
case 7: // Culture
return formData.cultureDescription.trim().length > 0;
default:
return false;
}
};
const renderStepContent = () => {
switch (step) {
case 0: // Company Details
return (
<div className="self-stretch flex flex-col justify-start items-start gap-6">
<FigmaInput
label="Your Name"
placeholder="John Doe"
value={formData.yourName}
onChange={(e) => updateFormData('yourName', e.target.value)}
required
/>
<FigmaInput
label="Company Name"
placeholder="Doe Enterprises"
value={formData.companyName}
onChange={(e) => updateFormData('companyName', e.target.value)}
required
/>
</div>
);
case 1: // Company Size
return (
<FigmaMultipleChoice
options={['1-10', '10-25', '25-50', '50-100', '100+']}
selectedValue={formData.companySize}
onSelect={(value) => updateFormData('companySize', value)}
/>
);
case 2: // Mission
return (
<FigmaQuestionCard
question="What is the mission of your company?"
description="Description about the question"
>
<EnhancedFigmaInput
placeholder="Type your answer...."
value={formData.mission}
onChange={(value) => updateFormData('mission', value)}
multiline
rows={6}
/>
</FigmaQuestionCard>
);
case 3: // Vision & Values
return (
<div className="space-y-6">
<FigmaQuestionCard
question="What is your company's vision?"
description="Where do you see your company in the future?"
>
<EnhancedFigmaInput
placeholder="Type your answer...."
value={formData.vision}
onChange={(value) => updateFormData('vision', value)}
multiline
rows={4}
/>
</FigmaQuestionCard>
</div>
);
case 4: // History
return (
<FigmaQuestionCard
question="How has your company evolved?"
description="Tell us about your company's journey and evolution"
>
<EnhancedFigmaInput
placeholder="Type your answer...."
value={formData.evolution}
onChange={(value) => updateFormData('evolution', value)}
multiline
rows={6}
/>
</FigmaQuestionCard>
);
case 5: // Market Position
return (
<FigmaQuestionCard
question="What are your competitive advantages?"
description="What gives your company a competitive edge?"
>
<EnhancedFigmaInput
placeholder="Type your answer...."
value={formData.advantages}
onChange={(value) => updateFormData('advantages', value)}
multiline
rows={6}
/>
</FigmaQuestionCard>
);
case 6: // Goals
return (
<FigmaQuestionCard
question="What are your short-term goals?"
description="What are your priorities for the next 6-12 months?"
>
<EnhancedFigmaInput
placeholder="Type your answer...."
value={formData.shortTermGoals}
onChange={(value) => updateFormData('shortTermGoals', value)}
multiline
rows={6}
/>
</FigmaQuestionCard>
);
case 7: // Culture
return (
<FigmaQuestionCard
question="Describe your company culture"
description="What's it like to work at your company?"
>
<EnhancedFigmaInput
placeholder="Type your answer...."
value={formData.cultureDescription}
onChange={(value) => updateFormData('cultureDescription', value)}
multiline
rows={6}
/>
</FigmaQuestionCard>
);
default:
return null;
}
};
// Question text for each step
const getQuestionText = () => {
switch (step) {
case 0: return 'Company Details';
case 1: return `How many people work at ${formData.companyName || '[Company Name]'}?`;
case 2: return 'What is the mission of your company?';
case 3: return 'What is your company\'s vision?';
case 4: return 'How has your company evolved?';
case 5: return 'What are your competitive advantages?';
case 6: return 'What are your short-term goals?';
case 7: return 'Describe your company culture';
default: return 'Onboarding';
}
};
return (
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] flex items-center justify-center">
<EnhancedFigmaQuestion
question={getQuestionText()}
currentStep={step + 1}
totalSteps={steps.length}
stepTitle={steps[step].title}
onBack={handleBack}
onNext={handleNext}
nextDisabled={!canProceed() || isGeneratingReport}
backDisabled={step === 0}
showBackButton={step > 0}
nextText={
isGeneratingReport
? 'Generating...'
: step === steps.length - 1
? 'Complete Setup'
: 'Next'
}
// Image upload props
orgId={org?.orgId}
onImageUploaded={handleImageUploaded}
currentImage={companyLogo}
>
{renderStepContent()}
</EnhancedFigmaQuestion>
</div>
);
};
export default Onboarding;
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useOrg } from '../contexts/OrgContext';
import { onboardingSteps, OnboardingData, initializeOnboardingData } from '../data/onboardingSteps';
import { secureApiPOST } from '../services/secureApi';
import {
FigmaOnboardingIntro,
FigmaOnboardingQuestion,
FigmaOnboardingMultipleChoice,
FigmaOnboardingForm
} from '../components/onboarding/FigmaOnboardingComponents';
const Onboarding: React.FC = () => {
const { org, upsertOrg, generateCompanyWiki } = useOrg();
const navigate = useNavigate();
useEffect(() => {
if (org?.onboardingCompleted) {
navigate('/reports', { replace: true });
}
}, [org, navigate]);
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [isGeneratingReport, setIsGeneratingReport] = useState(false);
const [formData, setFormData] = useState<OnboardingData>(initializeOnboardingData());
const currentStep = onboardingSteps[currentStepIndex];
const totalSteps = onboardingSteps.length;
const updateFormData = (field: keyof OnboardingData, value: string | string[]) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const canProceed = () => {
if (!currentStep) return false;
switch (currentStep.type) {
case 'form':
// Check required fields for form step - using companyDetails field
const companyDetails = formData.companyDetails;
return typeof companyDetails === 'string' && companyDetails.trim().length > 0;
case 'question':
// Check if field is filled
if (currentStep.field) {
const fieldValue = formData[currentStep.field as keyof OnboardingData];
return Array.isArray(fieldValue) ? fieldValue.length > 0 : String(fieldValue || '').trim().length > 0;
}
return false;
case 'multiple_choice':
// Check if option is selected
if (currentStep.field) {
const fieldValue = formData[currentStep.field as keyof OnboardingData];
return String(fieldValue || '').trim().length > 0;
}
return false;
case 'intro':
return true;
default:
return false;
}
};
const handleNext = async () => {
if (isGeneratingReport) return;
if (currentStepIndex < totalSteps - 1) {
setCurrentStepIndex(prev => prev + 1);
return;
}
// Final step: submit all data and complete onboarding
setIsGeneratingReport(true);
try {
// Submit the complete onboarding data
const response = await secureApiPOST('onboarding/complete', formData);
if (response.success) {
// Update org with completion status
const updatedOrg = {
...org,
name: formData.companyName,
onboardingCompleted: true,
updatedAt: Date.now()
};
await upsertOrg(updatedOrg);
// Navigate to reports
navigate('/reports', { replace: true });
} else {
throw new Error(response.error || 'Failed to complete onboarding');
}
} catch (error) {
console.error('Error completing onboarding:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
alert(`There was an error completing the setup: ${errorMessage}. Please check the console for more details and try again.`);
} finally {
setIsGeneratingReport(false);
}
};
const handleBack = () => {
if (currentStepIndex > 0) {
setCurrentStepIndex(prev => prev - 1);
}
};
const handleLogoUpload = (file: File) => {
// Create object URL for preview
const logoUrl = URL.createObjectURL(file);
updateFormData('companyLogo', logoUrl);
};
const getSectionInfo = () => {
const sectionSteps = onboardingSteps.filter(step => step.section === currentStep.section);
const currentSectionIndex = sectionSteps.findIndex(step => step.id === currentStep.id);
const sectionName = currentStep.sectionName || `Section ${currentStep.section}`;
return {
sectionPosition: currentStep.section,
totalInSection: sectionSteps.length,
sectionName,
stepInSection: currentSectionIndex + 1
};
};
const renderStepContent = () => {
if (!currentStep) return null;
const sectionInfo = getSectionInfo();
switch (currentStep.type) {
case 'intro':
return (
<FigmaOnboardingIntro
section={currentStep.section}
totalSections={7}
title={currentStep.sectionName || `Section ${currentStep.section}`}
description={currentStep.description || ''}
onStart={handleNext}
/>
);
case 'form':
return (
<FigmaOnboardingForm
companyName={formData.companyName}
yourName={formData.yourName}
companyLogo={formData.companyLogo}
onCompanyNameChange={(value) => updateFormData('companyName', value)}
onYourNameChange={(value) => updateFormData('yourName', value)}
onLogoUpload={handleLogoUpload}
onNext={handleNext}
canProceed={canProceed()}
/>
);
case 'question':
const questionValue = currentStep.fieldMapping
? String(formData[currentStep.fieldMapping as keyof OnboardingData] || '')
: '';
return (
<FigmaOnboardingQuestion
question={currentStep.question || ''}
placeholder={currentStep.placeholder}
value={questionValue}
onChange={(value) => {
if (currentStep.fieldMapping) {
updateFormData(currentStep.fieldMapping as keyof OnboardingData, value);
}
}}
onBack={handleBack}
onNext={handleNext}
sectionPosition={sectionInfo.sectionPosition}
totalInSection={sectionInfo.totalInSection}
sectionName={sectionInfo.sectionName}
canProceed={canProceed()}
canSkip={currentStep.optional}
rows={6}
/>
);
case 'multiple_choice':
const multipleChoiceValue = currentStep.fieldMapping
? String(formData[currentStep.fieldMapping as keyof OnboardingData] || '')
: '';
return (
<FigmaOnboardingMultipleChoice
question={currentStep.question || ''}
options={currentStep.options || []}
selectedValue={multipleChoiceValue}
onSelect={(value) => {
if (currentStep.fieldMapping) {
updateFormData(currentStep.fieldMapping as keyof OnboardingData, value);
}
}}
onBack={handleBack}
onNext={handleNext}
sectionPosition={sectionInfo.sectionPosition}
totalInSection={sectionInfo.totalInSection}
sectionName={sectionInfo.sectionName}
canSkip={currentStep.optional}
/>
);
default:
return (
<div className="text-center">
<p>Step type "{currentStep.type}" not implemented</p>
</div>
);
}
};
// Show a loading state while generating report
if (isGeneratingReport) {
return (
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] flex items-center justify-center">
<div className="text-center">
<div className="text-2xl font-medium text-[--Neutrals-NeutralSlate950] mb-4">
Completing your setup...
</div>
<div className="text-base text-[--Neutrals-NeutralSlate500]">
Please wait while we process your information.
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] flex items-center justify-center overflow-hidden">
{renderStepContent()}
</div>
);
};
export default Onboarding;