183 lines
8.0 KiB
TypeScript
183 lines
8.0 KiB
TypeScript
import React from 'react';
|
|
import { EmployeeQuestion, EMPLOYEE_QUESTIONS } from '../../employeeQuestions';
|
|
import { Input, Textarea } from './Inputs';
|
|
|
|
interface QuestionInputProps {
|
|
question: EmployeeQuestion;
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
className?: string;
|
|
// For yes/no questions with follow-ups, we need access to all answers and ability to set follow-up
|
|
allAnswers?: Record<string, string>;
|
|
onFollowupChange?: (questionId: string, value: string) => void;
|
|
}
|
|
|
|
// Helper function to find follow-up question for a given question
|
|
const findFollowupQuestion = (questionId: string): EmployeeQuestion | null => {
|
|
return EMPLOYEE_QUESTIONS.find(q => q.followupTo === questionId) || null;
|
|
};
|
|
|
|
export const QuestionInput: React.FC<QuestionInputProps> = ({
|
|
question,
|
|
value,
|
|
onChange,
|
|
className = '',
|
|
allAnswers = {},
|
|
onFollowupChange
|
|
}) => {
|
|
const baseInputClasses = "w-full px-3 py-2 border border-[--border-color] rounded-lg bg-[--background-primary] text-[--text-primary] focus:outline-none focus:ring-2 focus:ring-blue-500";
|
|
|
|
switch (question.type) {
|
|
case 'text':
|
|
return (
|
|
<Input
|
|
type="text"
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={question.placeholder}
|
|
className={className}
|
|
/>
|
|
);
|
|
|
|
case 'textarea':
|
|
return (
|
|
<Textarea
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={question.placeholder}
|
|
className={className}
|
|
rows={4}
|
|
/>
|
|
);
|
|
|
|
case 'yesno':
|
|
const followupQuestion = findFollowupQuestion(question.id);
|
|
const followupValue = followupQuestion ? allAnswers[followupQuestion.id] || '' : '';
|
|
|
|
return (
|
|
<div className={`space-y-4 ${className}`}>
|
|
{/* Yes/No Radio Buttons */}
|
|
<div className="flex gap-4">
|
|
<label className="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
name={question.id}
|
|
value="Yes"
|
|
checked={value === 'Yes'}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
className="w-4 h-4 text-[--accent] border-[--border-color] focus:ring-[--accent]"
|
|
/>
|
|
<span className="text-[--text-primary]">Yes</span>
|
|
</label>
|
|
<label className="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
name={question.id}
|
|
value="No"
|
|
checked={value === 'No'}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
className="w-4 h-4 text-[--accent] border-[--border-color] focus:ring-[--accent]"
|
|
/>
|
|
<span className="text-[--text-primary]">No</span>
|
|
</label>
|
|
</div>
|
|
|
|
{/* Conditional Follow-up Textarea */}
|
|
{followupQuestion && value === 'Yes' && (
|
|
<div className="space-y-2">
|
|
<label className="block text-sm font-medium text-[--text-primary] tracking-[-0.14px]">
|
|
{followupQuestion.prompt}
|
|
</label>
|
|
<Textarea
|
|
value={followupValue}
|
|
onChange={(e) => onFollowupChange?.(followupQuestion.id, e.target.value)}
|
|
placeholder={followupQuestion.placeholder}
|
|
rows={3}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
case 'scale':
|
|
const scaleMin = question.scaleMin || 1;
|
|
const scaleMax = question.scaleMax || 10;
|
|
const currentValue = parseInt(value) || scaleMin;
|
|
|
|
return (
|
|
<div className={`space-y-3 ${className}`}>
|
|
<div className="flex w-full justify-between text-sm text-[--text-secondary]">
|
|
<span>{question.scaleLabels?.min || `${scaleMin}`}</span>
|
|
<span>{question.scaleLabels?.max || `${scaleMax}`}</span>
|
|
</div>
|
|
|
|
{/* Grid container that aligns slider and numbers */}
|
|
<div className="grid grid-cols-[auto_1fr_auto] gap-4 items-center">
|
|
{/* Left label space - dynamically sized */}
|
|
<div className="text-xs -me-12 text-transparent select-none">
|
|
{question.scaleLabels?.min || `${scaleMin}`}
|
|
</div>
|
|
|
|
{/* Slider container */}
|
|
<div className="relative">
|
|
<input
|
|
type="range"
|
|
min={scaleMin}
|
|
max={scaleMax}
|
|
value={currentValue}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider"
|
|
/>
|
|
{/* Numbers positioned absolutely under the slider */}
|
|
<div className="flex justify-between absolute -bottom-5 left-0 right-0 text-xs text-[--text-secondary]">
|
|
{Array.from({ length: scaleMax - scaleMin + 1 }, (_, i) => (
|
|
<span key={i} className="w-4 text-center">
|
|
{scaleMin + i}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Current value badge */}
|
|
<div className="w-12 h-8 bg-[--accent] text-white rounded flex items-center justify-center text-sm font-medium">
|
|
{currentValue}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Add some bottom padding to account for the absolute positioned numbers */}
|
|
<div className="h-4"></div>
|
|
</div>
|
|
);
|
|
|
|
case 'select':
|
|
return (
|
|
<select
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
className={`${baseInputClasses} ${className}`}
|
|
>
|
|
<option value="">Select an option...</option>
|
|
{question.options?.map((option, index) => (
|
|
<option key={index} value={option}>
|
|
{option}
|
|
</option>
|
|
))}
|
|
</select>
|
|
);
|
|
|
|
default:
|
|
return (
|
|
<textarea
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={question.placeholder || "Type your answer here..."}
|
|
className={`${baseInputClasses} min-h-[100px] resize-vertical ${className}`}
|
|
rows={4}
|
|
/>
|
|
);
|
|
}
|
|
};
|
|
|
|
export default QuestionInput;
|