Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/javascript/components/ui/CreateQuestionForm/Scenario.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react'
import { Form } from 'react-bootstrap'

const Scenario = ({ scenarioText, handleTextChange }) => {
const Scenario = ({ scenarioText, questionText, handleTextChange, data }) => {
// Use questionText if provided (from StimulusCaseStudy), otherwise fall back to scenarioText
const text = questionText !== undefined ? questionText : scenarioText

return (
<>
<Form.Group controlId='scenarioText' className='pr-4'>
Expand All @@ -10,7 +13,7 @@ const Scenario = ({ scenarioText, handleTextChange }) => {
<Form.Control
as='textarea'
rows={5}
value={scenarioText}
value={text || ''}
onChange={handleTextChange}
placeholder='Enter your scenario text here'
className='mr-4 p-2 mb-4'
Expand Down
144 changes: 85 additions & 59 deletions app/javascript/components/ui/CreateQuestionForm/StimulusCaseStudy.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {
useState, useEffect, useCallback, useMemo, useRef
} from 'react'
import { Button } from 'react-bootstrap'
import { Button, Tab, Nav } from 'react-bootstrap'
import { SUBQUESTION_TYPE_NAMES } from '../../../constants/questionTypes'
import Scenario from './Scenario'
import Bowtie from './Bowtie'
Expand Down Expand Up @@ -29,10 +29,12 @@ const StimulusCaseStudy = ({
data.subQuestions.length > 0
? data.subQuestions.map((sq, idx) => ({
...sq,
id: sq.id || Date.now() + idx
id: sq.id || Date.now() + idx,
type: sq.type_name || sq.type || ''
}))
: []
)
const [activeTab, setActiveTab] = useState('main')
const updateTimeout = useRef(null)

const COMPONENT_MAP = useMemo(
Expand Down Expand Up @@ -90,7 +92,7 @@ const StimulusCaseStudy = ({
data: sq.data
}))
})
}, 300)
}, 50)
},
[questionText, onDataChange]
)
Expand All @@ -114,16 +116,14 @@ const StimulusCaseStudy = ({
const updated = prev.map((sq) => {
if (sq.id === id) {
const updatedSq = { ...sq, [key]: value }
// For Essay and File Upload, when data is changed (from ReactQuill),
// wrap the HTML string back into the { html: "..." } format
if (
(sq.type === 'Essay' || sq.type === 'File Upload') &&
key === 'text'
key === 'data' &&
typeof value === 'string'
) {
updatedSq.data = {
html: value
.split('\n')
.map((line, index) => `<p key=${index}>${line}</p>`)
.join('')
}
updatedSq.data = { html: value }
}
return updatedSq
}
Expand Down Expand Up @@ -170,56 +170,82 @@ const StimulusCaseStudy = ({
return (
<div className='stimulus-case-study-form'>
<h3>{questionType} Question</h3>
<QuestionText
questionText={questionText}
handleTextChange={handleTextChange}
/>

<h4>Subquestions</h4>
{subQuestions.map((sq) => {
const QuestionComponent = COMPONENT_MAP[sq.type] || null
return (
<div key={sq.id} className='subquestion'>
<QuestionTypeDropdown
handleQuestionTypeSelection={(type) =>
handleSubQuestionTypeSelection(sq.id, type)
}
QUESTION_TYPE_NAMES={SUBQUESTION_TYPE_NAMES}

<Tab.Container activeKey={activeTab} onSelect={(k) => setActiveTab(k)}>
<Nav variant='tabs' className='mb-3'>
<Nav.Item>
<Nav.Link eventKey='main'>Main Question</Nav.Link>
</Nav.Item>
{subQuestions.map((sq, index) => (
<Nav.Item key={sq.id}>
<Nav.Link eventKey={`subquestion-${sq.id}`}>
{sq.type || 'New'} #{index + 1}
</Nav.Link>
</Nav.Item>
))}
</Nav>

<Tab.Content>
<Tab.Pane eventKey='main'>
<QuestionText
questionText={questionText}
handleTextChange={handleTextChange}
/>
{QuestionComponent && (
<QuestionComponent
questionText={sq.text}
handleTextChange={(e) =>
handleSubQuestionChange(sq.id, 'text', e.target.value)
}
onDataChange={(data) =>
handleSubQuestionChange(sq.id, 'data', data)
}
resetFields={resetFields}
data={sq.data}
/>
)}
<div>
<Button
variant='danger'
className='mt-2'
onClick={() => removeSubQuestion(sq.id)}
>
Remove Subquestion
</Button>
</div>
</div>
)
})}

<Button
type='button'
variant='secondary'
className='mt-3'
onClick={addSubQuestion}
>
Add Subquestion
</Button>
<Button
type='button'
variant='secondary'
className='mt-3'
onClick={addSubQuestion}
>
Add Subquestion
</Button>
</Tab.Pane>

{subQuestions.map((sq, index) => {
const QuestionComponent = COMPONENT_MAP[sq.type] || null
// For Essay and File Upload, extract HTML string from data object
const dataForComponent = (sq.type === 'Essay' || sq.type === 'File Upload') && sq.data?.html
? sq.data.html
: sq.data

return (
<Tab.Pane key={sq.id} eventKey={`subquestion-${sq.id}`}>
<div className='subquestion'>
<h4>Subquestion #{index + 1}</h4>
<QuestionTypeDropdown
handleQuestionTypeSelection={(type) =>
handleSubQuestionTypeSelection(sq.id, type)
}
QUESTION_TYPE_NAMES={SUBQUESTION_TYPE_NAMES}
/>
{QuestionComponent && (
<QuestionComponent
questionText={sq.text}
handleTextChange={(e) =>
handleSubQuestionChange(sq.id, 'text', e.target.value)
}
onDataChange={(data) =>
handleSubQuestionChange(sq.id, 'data', data)
}
resetFields={resetFields}
data={dataForComponent}
questionType={sq.type}
/>
)}
<div className='mt-3'>
<Button
variant='danger'
onClick={() => removeSubQuestion(sq.id)}
>
Remove Subquestion
</Button>
</div>
</div>
</Tab.Pane>
)
})}
</Tab.Content>
</Tab.Container>
</div>
)
}
Expand Down
36 changes: 31 additions & 5 deletions app/javascript/components/ui/CreateQuestionForm/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ const CreateQuestionForm = ({ subjectOptions, question, onSuccess, onCancel }) =
return question.data?.html || ''
}

// For Stimulus Case Study, wrap the array in subQuestions property
if (question.type === 'Question::StimulusCaseStudy') {
return {
text: question.text || '',
subQuestions: Array.isArray(question.data) ? question.data : []
}
}

return question.data || { text: '', subQuestions: [] }
})
const [resetFields, setResetFields] = useState(false)
Expand All @@ -62,7 +70,18 @@ const CreateQuestionForm = ({ subjectOptions, question, onSuccess, onCancel }) =
setResetFields(true)
}

const handleTextChange = (e) => setQuestionText(e.target.value)
const handleTextChange = (e) => {
const newText = e.target.value
setQuestionText(newText)

// For Stimulus Case Study, also update data.text
if (questionType === 'Stimulus Case Study') {
setData(prevData => ({
...prevData,
text: newText
}))
}
}

const handleLevelSelection = (levelData) => setLevel(levelData)

Expand Down Expand Up @@ -205,10 +224,16 @@ const CreateQuestionForm = ({ subjectOptions, question, onSuccess, onCancel }) =
switch (type) {
case 'Essay':
case 'File Upload': {
if (typeof data === 'object' || data === '') {
return true
// Data can be either a string (standalone) or { html: "..." } (subquestion)
if (typeof questionData === 'string') {
// Standalone question: data should be a non-empty string
return questionData === ''
} else if (typeof questionData === 'object' && questionData !== null) {
// Subquestion: data should be { html: "..." } with non-empty html
return !questionData.html || questionData.html.trim() === ''
}
break
// If data is undefined or null, it's invalid
return true
}
case 'Bow Tie': {
const { center, left, right } = questionData || {}
Expand Down Expand Up @@ -301,8 +326,9 @@ const CreateQuestionForm = ({ subjectOptions, question, onSuccess, onCancel }) =
}

if (questionType === 'Stimulus Case Study') {
// Check questionText state directly instead of data.text due to debouncing
if (
!data.text?.trim() ||
!questionText?.trim() ||
!Array.isArray(data.subQuestions) ||
data.subQuestions.length === 0
) {
Expand Down
Loading