Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding dnd functionality to change the order of the questions #31

Merged
merged 1 commit into from
Dec 24, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { handleSubmitAllQuestions } from './handlers/handleSubmitAllQuestions'
import { computeQuestionsModified } from './handlers/computeQuestionsModified'
import { handleQuestionUpdate } from './handlers/handleQuestionUpdate'
import { AddQuestionMenu } from './components/AddQuestionMenu'
import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd'

export const ApplicationQuestionConfig = (): JSX.Element => {
const { phaseId } = useParams<{ phaseId: string }>()
Expand Down Expand Up @@ -89,6 +90,24 @@ export const ApplicationQuestionConfig = (): JSX.Element => {
setApplicationQuestions((prev) => prev.filter((q) => q.id !== id))
}

const onDragEnd = (result: DropResult) => {
if (!result.destination) {
return
}

const newQuestions = Array.from(applicationQuestions)
const [reorderedItem] = newQuestions.splice(result.source.index, 1)
newQuestions.splice(result.destination.index, 0, reorderedItem)

// Update order_num for all questions
const updatedQuestions = newQuestions.map((question, index) => ({
...question,
order_num: index + 1,
}))

setApplicationQuestions(updatedQuestions)
}

if (isApplicationFormPending || isApplicationFormError || isMutatePending || isMutateError) {
return (
<div className='space-y-6 max-w-4xl mx-auto'>
Expand Down Expand Up @@ -143,20 +162,38 @@ export const ApplicationQuestionConfig = (): JSX.Element => {
/>
)}
{applicationQuestions.length > 0 ? (
applicationQuestions.map((question, index) => (
<ApplicationQuestionCard
key={question.id}
question={question}
originalQuestion={originalQuestions.find((q) => q.id === question.id)}
index={index}
onUpdate={(updatedQuestion) => {
handleQuestionUpdate(updatedQuestion, setApplicationQuestions)
}}
ref={(el) => (questionRefs.current[index] = el)}
submitAttempted={submitAttempted}
onDelete={handleDeleteQuestion}
/>
))
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId='questions'>
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{applicationQuestions.map((question, index) => (
<Draggable key={question.id} draggableId={question.id} index={index}>
{(providedQuestionItem) => (
<div
ref={providedQuestionItem.innerRef}
{...providedQuestionItem.draggableProps}
{...providedQuestionItem.dragHandleProps}
>
<ApplicationQuestionCard
question={question}
originalQuestion={originalQuestions.find((q) => q.id === question.id)}
index={index}
onUpdate={(updatedQuestion) => {
handleQuestionUpdate(updatedQuestion, setApplicationQuestions)
}}
ref={(el) => (questionRefs.current[index] = el)}
submitAttempted={submitAttempted}
onDelete={handleDeleteQuestion}
/>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
) : (
<Card>
<CardContent className='text-center py-8'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Checkbox } from '@/components/ui/checkbox'
import { ChevronDown, ChevronUp, Trash2 } from 'lucide-react'
import { ChevronDown, ChevronUp, GripVertical, Trash2 } from 'lucide-react'
import { ApplicationQuestionMultiSelect } from '@/interfaces/application_question_multi_select'
import { ApplicationQuestionText } from '@/interfaces/application_question_text'
import {
Expand Down Expand Up @@ -97,16 +97,23 @@ export const ApplicationQuestionCard = forwardRef<
className={`mb-4 ${submitAttempted && !form.formState.isValid ? 'border-red-500' : ''}`}
>
<CardHeader className='cursor-pointer' onClick={() => setIsExpanded(!isExpanded)}>
<CardTitle className='flex justify-between items-center'>
<span>{question.title || `Question ${index + 1}`}</span>
<div className='flex items-center justify-between'>
<div className='flex items-center space-x-2'>
<GripVertical className='cursor-move h-6 w-6 text-muted-foreground' />
<div>
<CardTitle>{question.title || `Untitled Question`}</CardTitle>
<p className='text-sm text-muted-foreground mt-1'>
Question {index + 1}: {isMultiSelect ? 'Multi-select question' : 'Text question'}
</p>
</div>
</div>
<div className='flex items-center space-x-2'>
<QuestionStatusBadge status={status} />
<Button
variant='ghost'
size='sm'
onClick={(e) => {
e.stopPropagation()
// confirmation if question is new as this might result in data loss
if (question.id.startsWith('no-valid-id')) {
onDelete(question.id)
} else {
Expand All @@ -119,10 +126,7 @@ export const ApplicationQuestionCard = forwardRef<
</Button>
{isExpanded ? <ChevronUp className='h-6 w-6' /> : <ChevronDown className='h-6 w-6' />}
</div>
</CardTitle>
<p className='text-sm text-muted-foreground mt-1'>
{isMultiSelect ? 'Multi-select question' : 'Text question'}
</p>
</div>
</CardHeader>
{isExpanded && (
<CardContent>
Expand Down
Loading