commit before mass find + replace
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user