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 { FigmaRatingScale, FigmaTextArea, FigmaNavigationButtons } from '../components/figma/FigmaQuestion'; import { FigmaMultipleChoice } from '../components/figma/FigmaMultipleChoice'; // Icon SVG Component - From EmployeeFormsController const AuditlyIcon: React.FC = () => ( ); // Section Intro Component - From EmployeeFormsController const SectionIntro: React.FC<{ sectionNumber: string; title: string; description: string; onStart: () => void; imageUrl?: string; }> = ({ sectionNumber, title, description, onStart, imageUrl = "https://placehold.co/560x682" }) => { return (
{sectionNumber}
{title}
{description}
{title}
); }; // Step 1: Welcome & Role Information - From EmployeeFormsController const EmployeeFormStep1: React.FC<{ onNext: (data: any) => void }> = ({ onNext }) => { const [formData, setFormData] = useState({ name: '', role: '', department: '' }); const handleSubmit = () => { onNext(formData); }; return (
Welcome to the Auditly Employee Assessment
Let's learn about your role, contribution and help us get a better understand of how you work best.
Your Role & Output
Tell us about your current role and what you work on
Your Name
*
setFormData({ ...formData, name: e.target.value })} className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500 outline-none" placeholder="Enter your full name" />
What is your role at the company?
*
setFormData({ ...formData, role: e.target.value })} className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500 outline-none" placeholder="e.g. Software Engineer, Marketing Manager" />
What department do you work in?
*
setFormData({ ...formData, department: e.target.value })} className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500 outline-none" placeholder="e.g. Engineering, Sales, Marketing" />
); }; // Main Controller Component with Backend Integration - Merged Logic 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({}); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(''); const [inviteEmployee, setInviteEmployee] = useState(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); 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 const submitResponse = await fetch(`${API_URL}/submitEmployeeAnswers`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ inviteCode: inviteCode, answers: answers, orgId: orgId }) }); 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; if (formData.title_department) answers['title_department'] = formData.title_department; // 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(38); // 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(38); } 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); } if (currentStep === 37) { // Submit form data here handleSubmit(); } else { setCurrentStep(currentStep + 1); } }; const handleBack = () => { setCurrentStep(currentStep - 1); }; // Early return for invite flow loading state if (isInviteFlow && isLoadingInvite) { return (
A

Loading Your Invitation...

Please wait while we verify your invitation.

); } // Early return for invite flow error state if (isInviteFlow && error && currentStep === 1) { return (
!

Invitation Error

{error}

); } const renderStep = () => { // NOTE: Step components need to be imported from EmployeeFormsController // For now, showing placeholder that preserves all 38 steps switch (currentStep) { case 1: return ; case 2: return ( handleNext()} /> ); case 3: return

Step 3 - Personal Information Form

; case 4: return

Step 4 - Email Validation

; case 5: return

Step 5 - Department Details

; case 6: return

Step 6 - Role Description

; case 7: return

Step 7 - Experience Level

; case 8: return

Step 8 - Skills Assessment

; case 9: return ( handleNext()} /> ); case 10: return

Step 10 - Team Dynamics

; case 11: return

Step 11 - Communication Style

; case 12: return

Step 12 - Work Preferences

; case 13: return

Step 13 - Collaboration Rating

; case 14: return

Step 14 - Remote Work

; case 15: return

Step 15 - Work-Life Balance

; case 16: return ( handleNext()} /> ); case 17: return

Step 17 - Goal Setting

; case 18: return

Step 18 - Achievement Rating

; case 19: return

Step 19 - Performance Metrics

; case 20: return

Step 20 - Career Aspirations

; case 21: return ( handleNext()} /> ); case 22: return

Step 22 - Learning Style

; case 23: return

Step 23 - Skill Development

; case 24: return

Step 24 - Training Preferences

; case 25: return ( handleNext()} /> ); case 26: return

Step 26 - Feedback Style

; case 27: return

Step 27 - Recognition Preferences

; case 28: return

Step 28 - Performance Reviews

; case 29: return

Step 29 - Manager Relationship

; case 30: return ( handleNext()} /> ); case 31: return

Step 31 - Problem Solving

; case 32: return

Step 32 - Innovation Rating

; case 33: return

Step 33 - Creative Thinking

; case 34: return ( handleNext()} /> ); case 35: return

Step 35 - Leadership Style

; case 36: return

Step 36 - Organizational Structure

; case 37: return

Step 37 - Final Feedback

; case 38: return (
Thank you! Your assessment has been submitted!
Your responses have been recorded and your AI-powered performance report will be generated shortly.
Thank you
); default: return
Form completed!
; } }; return (
{renderStep()} {error && (
{error}
)}
); }; export default EmployeeQuestionnaire;