fix: simplify EmployeeQuestionnaireNew to use invite-only flow

- Remove all authentication and org context dependencies
- Simplify component to work only with invite codes from URL
- Remove complex user/employee matching logic
- Keep exact Figma UI components and styling
- Use only submitViaInvite function for API submissions
- Employees never need to log in, only use invite link
This commit is contained in:
Ra
2025-08-25 14:42:33 -07:00
parent daa89b6cbf
commit 0d7b8d104b
13 changed files with 311 additions and 357 deletions

View File

@@ -1,8 +1,6 @@
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 { useParams } from 'react-router-dom';
import { EmployeeSubmissionAnswers } from '../employeeQuestions';
import { API_URL } from '../constants';
import {
WelcomeScreen,
@@ -15,39 +13,21 @@ import {
} from '../components/figma/FigmaEmployeeForms';
/**
* Enhanced Employee Questionnaire with Exact Figma Design Implementation
* Employee Questionnaire with Invite-Only Flow
*
* 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
* - Company owner invites employees with metadata
* - Employee uses invite code to access questionnaire
* - LLM processing via cloud functions with company context
* - Report generation and Firestore storage
*/
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 = [];
}
// Component state
const [currentStep, setCurrentStep] = useState(1);
const [formData, setFormData] = useState<any>({});
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -55,10 +35,12 @@ const EmployeeQuestionnaire: React.FC = () => {
const [inviteEmployee, setInviteEmployee] = useState<any>(null);
const [isLoadingInvite, setIsLoadingInvite] = useState(false);
// Load invite details if this is an invite flow
// Load invite details on component mount
useEffect(() => {
if (inviteCode) {
loadInviteDetails(inviteCode);
} else {
setError('No invite code provided');
}
}, [inviteCode]);
@@ -94,39 +76,8 @@ const EmployeeQuestionnaire: React.FC = () => {
}
};
// 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];
}
}
// Get employee info from invite data
const currentEmployee = inviteEmployee;
const submitViaInvite = async (answers: EmployeeSubmissionAnswers, inviteCode: string) => {
try {
@@ -176,7 +127,7 @@ const EmployeeQuestionnaire: React.FC = () => {
setError('');
try {
// Convert form data to EMPLOYEE_QUESTIONS format for backend
// Convert form data to answers format for backend
const answers: EmployeeSubmissionAnswers = {};
// Map form data to question IDs
@@ -191,55 +142,14 @@ const EmployeeQuestionnaire: React.FC = () => {
}
});
// 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);
}
// Submit answers via invite flow
const result = await submitViaInvite(answers, inviteCode!);
if (result.success) {
// Show thank you page
setCurrentStep(999);
} else {
setError(result.message || 'Failed to submit questionnaire');
setError(result.error || 'Failed to submit questionnaire');
}
} catch (error) {
console.error('Submission error:', error);
@@ -261,8 +171,8 @@ const EmployeeQuestionnaire: React.FC = () => {
setCurrentStep(currentStep - 1);
};
// Early return for invite flow loading state
if (isInviteFlow && isLoadingInvite) {
// Early return for loading state
if (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">
@@ -276,8 +186,8 @@ const EmployeeQuestionnaire: React.FC = () => {
);
}
// Early return for invite flow error state
if (isInviteFlow && error && currentStep === 1) {
// Early return for error state
if (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">