Files
auditly/src/pages/Onboarding.tsx

238 lines
9.1 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { useOrg } from '../contexts/OrgContext';
import { onboardingSteps, OnboardingData, initializeOnboardingData } from '../data/onboardingSteps';
import { secureApiPOST, secureApi } from '../services/secureApi';
import {
FigmaOnboardingIntro,
FigmaOnboardingQuestion,
FigmaOnboardingMultipleChoice,
FigmaOnboardingForm
} from '../components/onboarding/FigmaOnboardingComponents';
const Onboarding: React.FC = () => {
const { org, upsertOrg, generateCompanyWiki } = useOrg();
const { user } = useAuth();
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 - company name and user name
const companyName = formData.companyName;
const yourName = formData.yourName;
return typeof companyName === 'string' && companyName.trim().length > 0 &&
typeof yourName === 'string' && yourName.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 {
await upsertOrg({
...org,
companyName: formData.companyName,
companyLogo: formData.companyLogo,
onboardingData: formData,
onboardingCompleted: true,
updatedAt: Date.now(),
});
navigate('/reports', { replace: true });
} 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.field
? String(formData[currentStep.field as keyof OnboardingData] || '')
: '';
return (
<FigmaOnboardingQuestion
question={currentStep.title || ''}
placeholder={currentStep.placeholder}
value={questionValue}
onChange={(value) => {
if (currentStep.field) {
updateFormData(currentStep.field as keyof OnboardingData, value);
}
}}
onBack={handleBack}
onNext={handleNext}
sectionPosition={sectionInfo.sectionPosition}
totalInSection={sectionInfo.totalInSection}
sectionName={sectionInfo.sectionName}
canProceed={canProceed()}
canSkip={!currentStep.required}
rows={6}
/>
);
case 'multiple_choice':
const multipleChoiceValue = currentStep.field
? String(formData[currentStep.field as keyof OnboardingData] || '')
: '';
return (
<FigmaOnboardingMultipleChoice
question={currentStep.title || ''}
options={currentStep.options || []}
selectedValue={multipleChoiceValue}
onSelect={(value) => {
if (currentStep.field) {
updateFormData(currentStep.field as keyof OnboardingData, value);
}
}}
onBack={handleBack}
onNext={handleNext}
sectionPosition={sectionInfo.sectionPosition}
totalInSection={sectionInfo.totalInSection}
sectionName={sectionInfo.sectionName}
canSkip={!currentStep.required}
/>
);
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;