Implement comprehensive report system with detailed viewing and AI enhancements
- Add detailed report viewing with full-screen ReportDetail component for both company and employee reports - Fix company wiki to display onboarding Q&A in card format matching Figma designs - Exclude company owners from employee submission counts (owners contribute to wiki, not employee data) - Fix employee report generation to include company context (wiki + company report + employee answers) - Fix company report generation to use filtered employee submissions only - Add proper error handling for submission data format variations - Update Firebase functions to use gpt-4o model instead of deprecated gpt-4.1 - Fix UI syntax errors and improve report display functionality - Add comprehensive logging for debugging report generation flow 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,17 +15,28 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const params = useParams();
|
||||
const { user } = useAuth();
|
||||
const { submitEmployeeAnswers, generateEmployeeReport, employees } = useOrg();
|
||||
|
||||
// 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 [answers, setAnswers] = useState<EmployeeSubmissionAnswers>({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [inviteEmployee, setInviteEmployee] = useState<any>(null);
|
||||
const [isLoadingInvite, setIsLoadingInvite] = useState(false);
|
||||
|
||||
// Check if this is an invite-based flow (no auth needed)
|
||||
const inviteCode = params.inviteCode;
|
||||
const isInviteFlow = !!inviteCode;
|
||||
|
||||
// Load invite details if this is an invite flow
|
||||
useEffect(() => {
|
||||
if (inviteCode) {
|
||||
@@ -36,15 +47,24 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
const loadInviteDetails = async (code: string) => {
|
||||
setIsLoadingInvite(true);
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/invitations/${code}`);
|
||||
// Use Cloud Function endpoint for invite status
|
||||
const response = await fetch(`${API_URL}/getInvitationStatus?code=${code}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setInviteEmployee(data.employee);
|
||||
setError('');
|
||||
if (data.used) {
|
||||
setError('This invitation has already been used');
|
||||
} else if (data.employee) {
|
||||
setInviteEmployee(data.employee);
|
||||
setError('');
|
||||
} else {
|
||||
setError('Invalid invitation data');
|
||||
}
|
||||
} else {
|
||||
setError('Invalid or expired invitation link');
|
||||
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);
|
||||
@@ -120,30 +140,39 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const submitViaInvite = async (employee: any, answers: EmployeeSubmissionAnswers, inviteCode: string) => {
|
||||
const submitViaInvite = async (answers: EmployeeSubmissionAnswers, inviteCode: string) => {
|
||||
try {
|
||||
// First, consume the invite to mark it as used
|
||||
const consumeResponse = await fetch(`${API_URL}/api/invitations/${inviteCode}/consume`, {
|
||||
method: 'POST'
|
||||
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');
|
||||
}
|
||||
|
||||
// Submit the questionnaire answers
|
||||
const submitResponse = await fetch(`${API_URL}/api/employee-submissions`, {
|
||||
// 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({
|
||||
employeeId: employee.id,
|
||||
employee: employee,
|
||||
answers: answers
|
||||
inviteCode: inviteCode,
|
||||
answers: answers,
|
||||
orgId: orgId
|
||||
})
|
||||
});
|
||||
|
||||
if (!submitResponse.ok) {
|
||||
throw new Error('Failed to submit questionnaire');
|
||||
const errorData = await submitResponse.json();
|
||||
throw new Error(errorData.error || 'Failed to submit questionnaire');
|
||||
}
|
||||
|
||||
const result = await submitResponse.json();
|
||||
@@ -223,7 +252,7 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
let result;
|
||||
if (isInviteFlow) {
|
||||
// Direct API submission for invite flow (no auth needed)
|
||||
result = await submitViaInvite(currentEmployee, answers, inviteCode);
|
||||
result = await submitViaInvite(answers, inviteCode);
|
||||
} else {
|
||||
// Use org context for authenticated flow
|
||||
result = await submitEmployeeAnswers(currentEmployee.id, answers);
|
||||
@@ -338,7 +367,7 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
<LinearProgress value={getProgressPercentage()} />
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
|
||||
<div className="space-y-6">
|
||||
{visibleQuestions.map((question, index) => (
|
||||
<Question
|
||||
@@ -372,7 +401,7 @@ const EmployeeQuestionnaire: React.FC = () => {
|
||||
disabled={isSubmitting || getProgressPercentage() < 70}
|
||||
className="px-8 py-3"
|
||||
>
|
||||
{isSubmitting ? 'Submitting & Generating Report...' : 'Submit & Generate AI Report'}
|
||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user