import React, { useEffect, useMemo, useState } from 'react'; import { ProfileAnswerInput, ProfileQuestionForUser } from '../services/membershipService'; import { answerToComparable, canEditProfileQuestion, isProfileQuestionVisible, ProfileQuestionAnswerValue } from '../utils/profileQuestionLogic'; interface ProfileQuestionsFormProps { title: string; description?: string; questions: ProfileQuestionForUser[]; onSave: (answers: ProfileAnswerInput[]) => Promise; saveLabel?: string; allowAdminManagedEdit?: boolean; surface?: 'member' | 'admin'; } const formatAnswerForDisplay = (question: ProfileQuestionForUser, value: ProfileQuestionAnswerValue): string => { if (value === null || value === undefined || value === '') return 'Not set'; if (question.input_type === 'boolean') return value === true || value === 'true' ? 'Yes' : 'No'; if (question.input_type === 'select') { const matchingOption = question.options.find((option) => option.value === String(value)); return matchingOption?.label || String(value); } return String(value); }; const ProfileQuestionsForm: React.FC = ({ title, description, questions, onSave, saveLabel = 'Save Answers', allowAdminManagedEdit = false, surface = 'admin' }) => { const initialAnswers = useMemo(() => { const values: Record = {}; questions.forEach((question) => { values[question.id] = question.answer ?? null; }); return values; }, [questions]); const [answers, setAnswers] = useState>(initialAnswers); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [search, setSearch] = useState(''); const [page, setPage] = useState(1); const pageSize = 10; useEffect(() => { setAnswers(initialAnswers); setSuccessMessage(null); setError(null); }, [initialAnswers]); const visibleQuestions = useMemo(() => { const byId = new Map(); questions.forEach((question) => byId.set(question.id, question)); return questions.filter((question) => isProfileQuestionVisible(question, byId, answers)); }, [questions, answers]); const filteredQuestions = useMemo(() => { const searchTerm = search.trim().toLowerCase(); return visibleQuestions.filter((question) => { if (!searchTerm) return true; return ( question.label.toLowerCase().includes(searchTerm) || question.key.toLowerCase().includes(searchTerm) || (question.help_text || '').toLowerCase().includes(searchTerm) ); }); }, [visibleQuestions, search]); const totalPages = Math.max(1, Math.ceil(filteredQuestions.length / pageSize)); const paginatedQuestions = useMemo(() => { const safePage = Math.min(page, totalPages); const start = (safePage - 1) * pageSize; return filteredQuestions.slice(start, start + pageSize); }, [filteredQuestions, page, totalPages]); const setAnswerValue = (questionId: number, value: ProfileQuestionAnswerValue) => { setAnswers((prev) => ({ ...prev, [questionId]: value })); setSuccessMessage(null); setError(null); }; useEffect(() => { setPage(1); }, [search]); const handleSave = async () => { setSaving(true); setError(null); setSuccessMessage(null); try { const visibleQuestionIds = new Set(visibleQuestions.map((question) => question.id)); const changedAnswers: ProfileAnswerInput[] = questions .filter((question) => canEditProfileQuestion(question, allowAdminManagedEdit) && visibleQuestionIds.has(question.id)) .filter((question) => answerToComparable(answers[question.id] ?? null) !== answerToComparable(initialAnswers[question.id] ?? null)) .map((question) => ({ question_id: question.id, value: answers[question.id] ?? null })); await onSave(changedAnswers); setSuccessMessage(changedAnswers.length > 0 ? 'Saved successfully.' : 'No changes to save.'); } catch (err: any) { setError(err.response?.data?.detail || err.message || 'Failed to save answers.'); } finally { setSaving(false); } }; const renderField = (question: ProfileQuestionForUser) => { const value = answers[question.id] ?? null; const disabled = !canEditProfileQuestion(question, allowAdminManagedEdit) || saving; if (disabled && !saving) { return
{formatAnswerForDisplay(question, value)}
; } if (question.input_type === 'boolean') { return ( ); } if (question.input_type === 'select') { return ( ); } if (question.input_type === 'date') { return ( setAnswerValue(question.id, event.target.value || null)} disabled={disabled} className="profile-question-input" /> ); } if (question.input_type === 'number') { return ( setAnswerValue(question.id, event.target.value === '' ? null : Number(event.target.value))} disabled={disabled} placeholder={question.placeholder || ''} className="profile-question-input" /> ); } return ( setAnswerValue(question.id, event.target.value || null)} disabled={disabled} placeholder={question.placeholder || ''} className="profile-question-input" /> ); }; return (

{title}

{description &&

{description}

}
setSearch(event.target.value)} className="profile-question-input" />
{error &&
{error}
} {successMessage &&
{successMessage}
} {filteredQuestions.length === 0 ? (

No questions available.

) : (
{paginatedQuestions.map((question) => (
{question.help_text && (

{question.help_text}

)}
{renderField(question)}
{!canEditProfileQuestion(question, allowAdminManagedEdit) && (

This field can only be changed by an admin.

)}
))}
)} {filteredQuestions.length > pageSize && (
Page {Math.min(page, totalPages)} of {totalPages} ({filteredQuestions.length} questions)
)}
); }; export default ProfileQuestionsForm;