707 lines
29 KiB
TypeScript
707 lines
29 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { useNavigate, useLocation, useParams } from 'react-router-dom';
|
||
import { useAuth } from '../contexts/AuthContext';
|
||
import { useOrg } from '../contexts/OrgContext';
|
||
import { EMPLOYEE_QUESTIONS, EmployeeSubmissionAnswers } from '../employeeQuestions';
|
||
import { API_URL } from '../constants';
|
||
import {
|
||
WelcomeScreen,
|
||
SectionIntro,
|
||
PersonalInfoForm,
|
||
TextAreaQuestion,
|
||
RatingScaleQuestion,
|
||
YesNoChoice,
|
||
ThankYouPage
|
||
} from '../components/figma/FigmaEmployeeForms';
|
||
|
||
/**
|
||
* Enhanced Employee Questionnaire with Exact Figma Design Implementation
|
||
*
|
||
* Features:
|
||
* - Exact Figma design system styling
|
||
* - Invite-based flow (no authentication required)
|
||
* - Company owner can invite employees with metadata
|
||
* - LLM processing via cloud functions
|
||
* - Report generation with company context
|
||
* - Firestore storage for reports
|
||
*/
|
||
|
||
const EmployeeQuestionnaire: React.FC = () => {
|
||
const navigate = useNavigate();
|
||
const location = useLocation();
|
||
const params = useParams();
|
||
const { user } = useAuth();
|
||
|
||
// Check if this is an invite-based flow (no auth/org needed)
|
||
const inviteCode = params.inviteCode;
|
||
const isInviteFlow = !!inviteCode;
|
||
|
||
// Only use org context for authenticated flows
|
||
let submitEmployeeAnswers, generateEmployeeReport, employees;
|
||
if (!isInviteFlow) {
|
||
const orgContext = useOrg();
|
||
({ submitEmployeeAnswers, generateEmployeeReport, employees } = orgContext);
|
||
} else {
|
||
// For invite flows, we don't need these functions from org context
|
||
submitEmployeeAnswers = null;
|
||
generateEmployeeReport = null;
|
||
employees = [];
|
||
}
|
||
|
||
const [currentStep, setCurrentStep] = useState(1);
|
||
const [formData, setFormData] = useState<any>({});
|
||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||
const [error, setError] = useState('');
|
||
const [inviteEmployee, setInviteEmployee] = useState<any>(null);
|
||
const [isLoadingInvite, setIsLoadingInvite] = useState(false);
|
||
|
||
// Load invite details if this is an invite flow
|
||
useEffect(() => {
|
||
if (inviteCode) {
|
||
loadInviteDetails(inviteCode);
|
||
}
|
||
}, [inviteCode]);
|
||
|
||
const loadInviteDetails = async (code: string) => {
|
||
setIsLoadingInvite(true);
|
||
try {
|
||
const response = await fetch(`${API_URL}/getInvitationStatus?code=${code}`);
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
if (data.used) {
|
||
setError('This invitation has already been used');
|
||
} else if (data.employee) {
|
||
setInviteEmployee(data.employee);
|
||
// Pre-populate form data with invite metadata
|
||
setFormData({
|
||
name: data.employee.name || '',
|
||
email: data.employee.email || '',
|
||
company: data.employee.company || data.employee.department || ''
|
||
});
|
||
setError('');
|
||
} else {
|
||
setError('Invalid invitation data');
|
||
}
|
||
} else {
|
||
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
||
setError(errorData.error || 'Invalid or expired invitation link');
|
||
}
|
||
} catch (err) {
|
||
console.error('Error loading invite details:', err);
|
||
setError('Failed to load invitation details');
|
||
} finally {
|
||
setIsLoadingInvite(false);
|
||
}
|
||
};
|
||
|
||
// Get employee info from multiple sources
|
||
const invitedEmployee = location.state?.invitedEmployee;
|
||
|
||
// Determine current employee - for invite flow, use invite employee data
|
||
let currentEmployee;
|
||
if (isInviteFlow) {
|
||
currentEmployee = inviteEmployee;
|
||
} else {
|
||
// Original auth-based logic
|
||
currentEmployee = invitedEmployee || employees.find(emp => emp.email === user?.email);
|
||
|
||
if (!currentEmployee && user?.email) {
|
||
// Try case-insensitive email matching
|
||
currentEmployee = employees.find(emp =>
|
||
emp.email?.toLowerCase() === user.email?.toLowerCase()
|
||
);
|
||
|
||
if (!currentEmployee && invitedEmployee) {
|
||
currentEmployee = employees.find(emp =>
|
||
emp.name === invitedEmployee.name || emp.id === invitedEmployee.id
|
||
);
|
||
}
|
||
}
|
||
|
||
// Demo mode fallbacks
|
||
if (!currentEmployee && user?.email === 'demo@auditly.local' && employees.length > 0) {
|
||
currentEmployee = employees[employees.length - 1];
|
||
}
|
||
|
||
if (!currentEmployee && employees.length === 1) {
|
||
currentEmployee = employees[0];
|
||
}
|
||
}
|
||
|
||
const submitViaInvite = async (answers: EmployeeSubmissionAnswers, inviteCode: string) => {
|
||
try {
|
||
// First, consume the invite to mark it as used
|
||
const consumeResponse = await fetch(`${API_URL}/consumeInvitation`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ code: inviteCode })
|
||
});
|
||
|
||
if (!consumeResponse.ok) {
|
||
throw new Error('Failed to process invitation');
|
||
}
|
||
|
||
// Get orgId from the consume response
|
||
const consumeData = await consumeResponse.json();
|
||
const orgId = consumeData.orgId;
|
||
|
||
// Submit the questionnaire answers using Cloud Function
|
||
// This will include company onboarding questions and answers for LLM context
|
||
const submitResponse = await fetch(`${API_URL}/submitEmployeeAnswers`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
inviteCode: inviteCode,
|
||
answers: answers,
|
||
orgId: orgId,
|
||
includeCompanyContext: true // Flag to include company Q&A in LLM processing
|
||
})
|
||
});
|
||
|
||
if (!submitResponse.ok) {
|
||
const errorData = await submitResponse.json();
|
||
throw new Error(errorData.error || 'Failed to submit questionnaire');
|
||
}
|
||
|
||
const result = await submitResponse.json();
|
||
return { success: true, reportGenerated: !!result.report };
|
||
} catch (error) {
|
||
console.error('Invite submission error:', error);
|
||
return { success: false, error: error.message };
|
||
}
|
||
};
|
||
|
||
const handleSubmit = async () => {
|
||
setIsSubmitting(true);
|
||
setError('');
|
||
|
||
try {
|
||
// Convert form data to EMPLOYEE_QUESTIONS format for backend
|
||
const answers: EmployeeSubmissionAnswers = {};
|
||
|
||
// Map form data to question IDs
|
||
if (formData.name) answers['full_name'] = formData.name;
|
||
if (formData.email) answers['email'] = formData.email;
|
||
if (formData.company) answers['company_department'] = formData.company;
|
||
|
||
// Add all other form data fields
|
||
Object.keys(formData).forEach(key => {
|
||
if (formData[key] && !answers[key]) {
|
||
answers[key] = formData[key];
|
||
}
|
||
});
|
||
|
||
// Submit answers - different logic for invite vs auth flow
|
||
let result;
|
||
if (isInviteFlow) {
|
||
// Direct API submission for invite flow (no auth needed)
|
||
result = await submitViaInvite(answers, inviteCode);
|
||
} else {
|
||
// Use org context for authenticated flow
|
||
if (!currentEmployee) {
|
||
// Enhanced fallback logic for authenticated users
|
||
if (employees.length > 0) {
|
||
let fallbackEmployee = employees.find(emp =>
|
||
emp.email?.toLowerCase().includes(user?.email?.toLowerCase().split('@')[0] || '')
|
||
);
|
||
|
||
if (!fallbackEmployee) {
|
||
const userDomain = user?.email?.split('@')[1];
|
||
fallbackEmployee = employees.find(emp =>
|
||
emp.email?.split('@')[1] === userDomain
|
||
) || employees[employees.length - 1];
|
||
}
|
||
|
||
const success = await submitEmployeeAnswers(fallbackEmployee.id, answers);
|
||
if (success) {
|
||
try {
|
||
const report = await generateEmployeeReport(fallbackEmployee);
|
||
console.log('Report generated successfully:', report);
|
||
} catch (reportError) {
|
||
console.error('Failed to generate report:', reportError);
|
||
}
|
||
|
||
// Navigate to completion
|
||
setCurrentStep(999); // Thank you page
|
||
return;
|
||
}
|
||
}
|
||
|
||
setError(`We couldn't match your account (${user?.email}) with an employee record. Please contact your administrator.`);
|
||
setIsSubmitting(false);
|
||
return;
|
||
}
|
||
|
||
result = await submitEmployeeAnswers(currentEmployee.id, answers);
|
||
}
|
||
|
||
if (result.success) {
|
||
// Show thank you page
|
||
setCurrentStep(999);
|
||
} else {
|
||
setError(result.message || 'Failed to submit questionnaire');
|
||
}
|
||
} catch (error) {
|
||
console.error('Submission error:', error);
|
||
setError('Failed to submit questionnaire. Please try again.');
|
||
} finally {
|
||
setIsSubmitting(false);
|
||
}
|
||
};
|
||
|
||
const handleNext = (stepData?: any) => {
|
||
if (stepData) {
|
||
const newFormData = { ...formData, ...stepData };
|
||
setFormData(newFormData);
|
||
}
|
||
setCurrentStep(currentStep + 1);
|
||
};
|
||
|
||
const handleBack = () => {
|
||
setCurrentStep(currentStep - 1);
|
||
};
|
||
|
||
// Early return for invite flow loading state
|
||
if (isInviteFlow && isLoadingInvite) {
|
||
return (
|
||
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] py-8 px-4 flex items-center justify-center">
|
||
<div className="max-w-4xl mx-auto text-center">
|
||
<div className="w-16 h-16 bg-[--Brand-Orange] rounded-full flex items-center justify-center font-bold text-Other-White text-2xl mx-auto mb-4">
|
||
A
|
||
</div>
|
||
<h1 className="text-3xl font-bold text-[--Neutrals-NeutralSlate950] mb-4">Loading Your Invitation...</h1>
|
||
<p className="text-[--Neutrals-NeutralSlate500]">Please wait while we verify your invitation.</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Early return for invite flow error state
|
||
if (isInviteFlow && error && currentStep === 1) {
|
||
return (
|
||
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] py-8 px-4 flex items-center justify-center">
|
||
<div className="max-w-4xl mx-auto text-center">
|
||
<div className="w-16 h-16 bg-red-500 rounded-full flex items-center justify-center font-bold text-Other-White text-2xl mx-auto mb-4">
|
||
!
|
||
</div>
|
||
<h1 className="text-3xl font-bold text-[--Neutrals-NeutralSlate950] mb-4">Invitation Error</h1>
|
||
<p className="text-[--Neutrals-NeutralSlate500] mb-6">{error}</p>
|
||
<button
|
||
onClick={() => window.location.href = '/'}
|
||
className="px-6 py-3 bg-[--Brand-Orange] text-Other-White rounded-lg hover:bg-orange-600"
|
||
>
|
||
Return to Homepage
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const renderStep = () => {
|
||
switch (currentStep) {
|
||
case 1:
|
||
return (
|
||
<WelcomeScreen
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 2:
|
||
return (
|
||
<PersonalInfoForm
|
||
formData={{
|
||
email: formData.email || '',
|
||
name: formData.name || '',
|
||
company: formData.company || ''
|
||
}}
|
||
onChange={(data) => setFormData({ ...formData, ...data })}
|
||
onNext={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 3:
|
||
return (
|
||
<SectionIntro
|
||
sectionNumber="1 of 6"
|
||
title="Your Role & Responsibilities"
|
||
description="Let's start by understanding your current role and daily responsibilities."
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 4:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What is your current title and department?"
|
||
value={formData.titleAndDepartment || ''}
|
||
onChange={(value) => setFormData({ ...formData, titleAndDepartment: value })}
|
||
onNext={() => handleNext()}
|
||
currentStep={1}
|
||
totalSteps={7}
|
||
sectionName="Your Role & Responsibilities"
|
||
/>
|
||
);
|
||
|
||
case 5:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="Describe your core daily responsibilities"
|
||
value={formData.dailyResponsibilities || ''}
|
||
onChange={(value) => setFormData({ ...formData, dailyResponsibilities: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={2}
|
||
totalSteps={7}
|
||
sectionName="Your Role & Responsibilities"
|
||
placeholder="Describe what you do on a typical day..."
|
||
/>
|
||
);
|
||
|
||
case 6:
|
||
return (
|
||
<RatingScaleQuestion
|
||
question="How clearly do you understand your role and responsibilities?"
|
||
leftLabel="Not clear"
|
||
rightLabel="Very clear"
|
||
value={formData.roleClarity}
|
||
onChange={(value) => setFormData({ ...formData, roleClarity: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={3}
|
||
totalSteps={7}
|
||
sectionName="Your Role & Responsibilities"
|
||
scale={10}
|
||
/>
|
||
);
|
||
|
||
case 7:
|
||
return (
|
||
<SectionIntro
|
||
sectionNumber="2 of 6"
|
||
title="Output & Accountability"
|
||
description="Let's explore your work output, goals, and accountability measures."
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 8:
|
||
return (
|
||
<RatingScaleQuestion
|
||
question="How would you rate your weekly output (volume & quality)?"
|
||
leftLabel="Very little"
|
||
rightLabel="Very High"
|
||
value={formData.weeklyOutput}
|
||
onChange={(value) => setFormData({ ...formData, weeklyOutput: value })}
|
||
onNext={() => handleNext()}
|
||
currentStep={1}
|
||
totalSteps={7}
|
||
sectionName="Output & Accountability"
|
||
scale={10}
|
||
/>
|
||
);
|
||
|
||
case 9:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What are your top 2–3 recurring deliverables?"
|
||
value={formData.topDeliverables || ''}
|
||
onChange={(value) => setFormData({ ...formData, topDeliverables: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={2}
|
||
totalSteps={7}
|
||
sectionName="Output & Accountability"
|
||
/>
|
||
);
|
||
|
||
case 10:
|
||
return (
|
||
<YesNoChoice
|
||
question="Do you have weekly KPIs or goals?"
|
||
value={formData.hasKPIs}
|
||
onChange={(value) => setFormData({ ...formData, hasKPIs: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={3}
|
||
totalSteps={7}
|
||
sectionName="Output & Accountability"
|
||
/>
|
||
);
|
||
|
||
case 11:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="Who do you report to? How often do you meet/check-in?"
|
||
value={formData.reportingStructure || ''}
|
||
onChange={(value) => setFormData({ ...formData, reportingStructure: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={4}
|
||
totalSteps={7}
|
||
sectionName="Output & Accountability"
|
||
/>
|
||
);
|
||
|
||
case 12:
|
||
return (
|
||
<SectionIntro
|
||
sectionNumber="3 of 6"
|
||
title="Team & Collaboration"
|
||
description="Let's understand your team dynamics and collaboration patterns."
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 13:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="Who do you work most closely with?"
|
||
value={formData.closeCollaborators || ''}
|
||
onChange={(value) => setFormData({ ...formData, closeCollaborators: value })}
|
||
onNext={() => handleNext()}
|
||
currentStep={1}
|
||
totalSteps={7}
|
||
sectionName="Team & Collaboration"
|
||
/>
|
||
);
|
||
|
||
case 14:
|
||
return (
|
||
<RatingScaleQuestion
|
||
question="How would you rate team communication overall?"
|
||
leftLabel="Poor"
|
||
rightLabel="Excellent"
|
||
value={formData.teamCommunication}
|
||
onChange={(value) => setFormData({ ...formData, teamCommunication: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={2}
|
||
totalSteps={7}
|
||
sectionName="Team & Collaboration"
|
||
scale={10}
|
||
/>
|
||
);
|
||
|
||
case 15:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="Do you feel supported by your team? How?"
|
||
value={formData.teamSupport || ''}
|
||
onChange={(value) => setFormData({ ...formData, teamSupport: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={3}
|
||
totalSteps={7}
|
||
sectionName="Team & Collaboration"
|
||
/>
|
||
);
|
||
|
||
case 16:
|
||
return (
|
||
<SectionIntro
|
||
sectionNumber="4 of 6"
|
||
title="Tools & Resources"
|
||
description="Let's examine the tools and resources available to support your work."
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 17:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What tools and software do you currently use?"
|
||
value={formData.currentTools || ''}
|
||
onChange={(value) => setFormData({ ...formData, currentTools: value })}
|
||
onNext={() => handleNext()}
|
||
currentStep={1}
|
||
totalSteps={7}
|
||
sectionName="Tools & Resources"
|
||
/>
|
||
);
|
||
|
||
case 18:
|
||
return (
|
||
<RatingScaleQuestion
|
||
question="How effective are your current tools?"
|
||
leftLabel="Not effective"
|
||
rightLabel="Very effective"
|
||
value={formData.toolEffectiveness}
|
||
onChange={(value) => setFormData({ ...formData, toolEffectiveness: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={2}
|
||
totalSteps={7}
|
||
sectionName="Tools & Resources"
|
||
scale={10}
|
||
/>
|
||
);
|
||
|
||
case 19:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What tools or resources are you missing to do your job more effectively?"
|
||
value={formData.missingTools || ''}
|
||
onChange={(value) => setFormData({ ...formData, missingTools: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={3}
|
||
totalSteps={7}
|
||
sectionName="Tools & Resources"
|
||
/>
|
||
);
|
||
|
||
case 20:
|
||
return (
|
||
<SectionIntro
|
||
sectionNumber="5 of 6"
|
||
title="Skills & Development"
|
||
description="Let's explore your skills, growth opportunities, and career development."
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 21:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What are your key skills and strengths?"
|
||
value={formData.keySkills || ''}
|
||
onChange={(value) => setFormData({ ...formData, keySkills: value })}
|
||
onNext={() => handleNext()}
|
||
currentStep={1}
|
||
totalSteps={7}
|
||
sectionName="Skills & Development"
|
||
/>
|
||
);
|
||
|
||
case 22:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What skills would you like to develop or improve?"
|
||
value={formData.skillDevelopment || ''}
|
||
onChange={(value) => setFormData({ ...formData, skillDevelopment: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={2}
|
||
totalSteps={7}
|
||
sectionName="Skills & Development"
|
||
/>
|
||
);
|
||
|
||
case 23:
|
||
return (
|
||
<YesNoChoice
|
||
question="Are you aware of current training opportunities?"
|
||
value={formData.awareOfTraining}
|
||
onChange={(value) => setFormData({ ...formData, awareOfTraining: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={3}
|
||
totalSteps={7}
|
||
sectionName="Skills & Development"
|
||
/>
|
||
);
|
||
|
||
case 24:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What are your career goals within the company?"
|
||
value={formData.careerGoals || ''}
|
||
onChange={(value) => setFormData({ ...formData, careerGoals: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={4}
|
||
totalSteps={7}
|
||
sectionName="Skills & Development"
|
||
/>
|
||
);
|
||
|
||
case 25:
|
||
return (
|
||
<SectionIntro
|
||
sectionNumber="6 of 6"
|
||
title="Feedback & Improvement"
|
||
description="Finally, let's gather your thoughts on company improvements and overall satisfaction."
|
||
onStart={() => handleNext()}
|
||
/>
|
||
);
|
||
|
||
case 26:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="What improvements would you suggest for the company?"
|
||
value={formData.companyImprovements || ''}
|
||
onChange={(value) => setFormData({ ...formData, companyImprovements: value })}
|
||
onNext={() => handleNext()}
|
||
currentStep={1}
|
||
totalSteps={7}
|
||
sectionName="Feedback & Improvement"
|
||
/>
|
||
);
|
||
|
||
case 27:
|
||
return (
|
||
<RatingScaleQuestion
|
||
question="How satisfied are you with your current job overall?"
|
||
leftLabel="Not satisfied"
|
||
rightLabel="Very satisfied"
|
||
value={formData.jobSatisfaction}
|
||
onChange={(value) => setFormData({ ...formData, jobSatisfaction: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleNext()}
|
||
currentStep={2}
|
||
totalSteps={7}
|
||
sectionName="Feedback & Improvement"
|
||
scale={10}
|
||
/>
|
||
);
|
||
|
||
case 28:
|
||
return (
|
||
<TextAreaQuestion
|
||
question="Any additional feedback or suggestions for the company?"
|
||
value={formData.additionalFeedback || ''}
|
||
onChange={(value) => setFormData({ ...formData, additionalFeedback: value })}
|
||
onBack={() => handleBack()}
|
||
onNext={() => handleSubmit()}
|
||
currentStep={3}
|
||
totalSteps={7}
|
||
sectionName="Feedback & Improvement"
|
||
placeholder="Share any thoughts, suggestions, or feedback..."
|
||
/>
|
||
);
|
||
|
||
case 999: // Thank you page
|
||
return <ThankYouPage />;
|
||
|
||
default:
|
||
return <ThankYouPage />;
|
||
}
|
||
};
|
||
|
||
if (isSubmitting) {
|
||
return (
|
||
<div className="min-h-screen bg-[--Neutrals-NeutralSlate0] py-8 px-4 flex items-center justify-center">
|
||
<div className="max-w-4xl mx-auto text-center">
|
||
<div className="w-16 h-16 bg-[--Brand-Orange] rounded-full flex items-center justify-center font-bold text-Other-White text-2xl mx-auto mb-4 animate-pulse">
|
||
A
|
||
</div>
|
||
<h1 className="text-3xl font-bold text-[--Neutrals-NeutralSlate950] mb-4">Submitting Your Responses...</h1>
|
||
<p className="text-[--Neutrals-NeutralSlate500]">Please wait while we process your assessment and generate your report.</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-white">
|
||
{renderStep()}
|
||
{error && (
|
||
<div className="fixed bottom-4 right-4 bg-red-500 text-white p-4 rounded-lg shadow-lg z-50">
|
||
{error}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default EmployeeQuestionnaire; |