import React, { useState, useEffect } from 'react'; import { API } from '../../api/API'; import { toast } from 'sonner'; interface Question { id: string; sectionName: string; questionText: string; inputType: 'text' | 'yesno' | 'file' | 'number' | 'select' | 'mcq' | 'radio' | 'textarea' | 'email'; options?: any; questionOptions?: any[]; // From backend inclusion weight: number; order: number; isMandatory: boolean; } interface QuestionnaireFormProps { applicationId: string; onComplete?: () => void; readOnly?: boolean; existingResponses?: any[]; publicMode?: boolean; // New prop initialQuestions?: Question[]; // New prop to avoid re-fetching } const QuestionnaireForm: React.FC = ({ applicationId, onComplete, readOnly = false, existingResponses, publicMode = false, initialQuestions }) => { const [questions, setQuestions] = useState(initialQuestions || []); const [responses, setResponses] = useState>({}); const [loading, setLoading] = useState(!initialQuestions); const [submitting, setSubmitting] = useState(false); useEffect(() => { if (!initialQuestions) { fetchQuestionnaire(); } if (existingResponses) { const initialResponses: any = {}; existingResponses.forEach(r => { initialResponses[r.questionId] = r.responseValue; }); setResponses(initialResponses); } }, [existingResponses, initialQuestions]); const fetchQuestionnaire = async () => { try { // In public mode, we shouldn't fetch "latest" as it requires auth. // Public page should provide initialQuestions. // But if we ever needed to fetch, we'd need a public endpoint for just questions or rely on the parent. if (publicMode) { setLoading(false); return; } const res: any = await API.getLatestQuestionnaire(); if (res?.data?.data?.questions) { // Normalize questions const normalized = res.data.data.questions.map((q: any) => ({ ...q, inputType: (q.inputType === 'mcq') ? 'select' : q.inputType })); setQuestions(normalized); } } catch (error) { console.error(error); toast.error('Failed to load questionnaire'); } finally { setLoading(false); } }; const handleInputChange = (questionId: string, value: any) => { if (readOnly) return; setResponses(prev => ({ ...prev, [questionId]: value })); }; const handleFileChange = (questionId: string, e: React.ChangeEvent) => { if (readOnly) return; if (e.target.files && e.target.files[0]) { const file = e.target.files[0]; if (file.size > 5 * 1024 * 1024) { toast.error("File size must be less than 5MB"); return; } const reader = new FileReader(); reader.onloadend = () => { handleInputChange(questionId, reader.result); }; reader.readAsDataURL(file); } }; const handleSubmit = async () => { // Skip mandatory check for 'file' type as per user request to bypass const missing = questions.filter(q => q.isMandatory && q.inputType !== 'file' && !responses[q.id]); if (missing.length > 0) { toast.error(`Please answer all mandatory questions. Missing: ${missing.length}`); return; } try { setSubmitting(true); const payload = Object.entries(responses).map(([qId, val]) => ({ questionId: qId, value: val })); if (publicMode) { await API.submitPublicResponse({ applicationId, responses: payload }); } else { await API.submitQuestionnaireResponse({ applicationId, responses: payload }); } toast.success('Responses submitted successfully'); if (onComplete) onComplete(); } catch (error) { console.error(error); toast.error('Failed to submit responses'); } finally { setSubmitting(false); } }; if (loading) return
Loading questionnaire...
; if (questions.length === 0) return
No active questionnaire found.
; const sections = questions.reduce((acc, q) => { if (!acc[q.sectionName]) acc[q.sectionName] = []; acc[q.sectionName].push(q); return acc; }, {} as Record); return (
Royal Enfield

ROYAL ENFIELD

Dealership Partner Application

Dealership Assessment Questionnaire

{Object.entries(sections).map(([sectionName, sectionQuestions]) => (

{sectionName}

{sectionQuestions.map(q => (
{(q.inputType === 'text' || q.inputType === 'email') && ( handleInputChange(q.id, e.target.value)} value={responses[q.id] || ''} disabled={readOnly} /> )} {q.inputType === 'textarea' && (