Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
96db43d
Refactor CreateQuestionForm
laritakr Jan 9, 2026
234e205
WIP modify form for editing questions
laritakr Jan 9, 2026
f0ad4db
Add modal for editing questions
laritakr Jan 9, 2026
572f4cf
🎁 Edit question renders correct question form
sjproctor Jan 9, 2026
a996d3b
Merge branch 'main' into i499-edit-questions
sjproctor Jan 15, 2026
717a13b
💄 Linting fixes with yarn lint:all --fix
sjproctor Jan 15, 2026
d9ca989
🎁 Display question image on edit form
sjproctor Jan 15, 2026
65dddea
🎁 Allows fetch for put and patch requests
sjproctor Jan 15, 2026
a35a6ca
🎁 Populates answer fields in edit question modal
sjproctor Jan 15, 2026
fb59e85
🎁 Matching question type allows edit modal
sjproctor Jan 15, 2026
6e27821
🎁 Typeahead subject for edit questions
sjproctor Jan 15, 2026
615e9ca
🎁 Adds update route and controller action
sjproctor Jan 15, 2026
c9714f0
🐛 Question metadata maintains two column layout
sjproctor Jan 15, 2026
db6e9c2
🎁 Removes modal footer on edit view
sjproctor Jan 15, 2026
ae8a2a3
🎁 Dynamic button text for edit question modal
sjproctor Jan 15, 2026
fcb4b20
💄 Linting with yarn lint:all --fix
sjproctor Jan 15, 2026
fd1d808
Merge branch 'main' into i499-edit-questions
laritakr Jan 20, 2026
19df917
🧹 Add Cancel button to the modal
kirkkwang Jan 20, 2026
d052e22
🧹 Move edit and delete buttons around
kirkkwang Jan 20, 2026
308e489
🧹 Allow edit of alt text and deletion of images
kirkkwang Jan 20, 2026
129d310
Allow editing Essay & Upload questions
laritakr Jan 20, 2026
60dec62
Correct mapping for Stimulus case study
laritakr Jan 20, 2026
9b2928f
🎁 Add WYSIWYG editor for Essay and Upload
kirkkwang Jan 20, 2026
49078f4
🎁 Remove edit button from stimulus case study
sjproctor Jan 20, 2026
618ea82
💄 Copilot suggestions
sjproctor Jan 21, 2026
3408375
:lipstick: Copilot review
ShanaLMoore Jan 21, 2026
48ea01c
Removed the unused filtered_ids parameter
ShanaLMoore Jan 21, 2026
fdf8747
es lint fix
ShanaLMoore Jan 21, 2026
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
16 changes: 16 additions & 0 deletions app/assets/stylesheets/_create-question-form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
@import '../../../node_modules/bootstrap/scss/mixins';

.create-question-form {
.question-form-main {
max-width: 70%;
min-width: 0;
}

.question-form-sidebar {
min-width: 250px;
width: 30%;
}

@include media-breakpoint-down(md) {
.question-body {
margin-top: .5rem !important;
Expand All @@ -14,5 +24,11 @@
.tag-section {
margin: 0 !important;
}
.question-form-main {
max-width: 100%;
}
.question-form-sidebar {
width: 100%;
}
}
}
71 changes: 69 additions & 2 deletions app/controllers/api/questions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,50 @@ def create
render json: { errors: [e.message] }, status: :unprocessable_entity
end

def update
question = Question.find_by(id: params[:id])

if question.nil?
render json: { errors: ['Question not found.'] }, status: :not_found
return
end

return unless validate_permissions(question)

processed_params = process_question_params(question_params)
update_question_in_transaction(question, processed_params)
rescue ArgumentError => e
render json: { errors: [e.message] }, status: :unprocessable_entity
end

# rubocop:disable Metrics/MethodLength
def update_question_in_transaction(question, processed_params)
# Wrap all updates in a transaction for atomicity
Question.transaction do
# Clear existing associations before updating
question.keywords.clear
question.subjects.clear

# Update question attributes
question.assign_attributes(processed_params.except(:keywords, :subjects, :images, :alt_text, :deleted_image_ids, :existing_images))
question.level = nil if question.level.blank?

# Handle associations
handle_question_associations(question)

unless question.save
render json: { errors: question.errors.full_messages }, status: :unprocessable_entity
raise ActiveRecord::Rollback
end

handle_image_changes!
end

# Only render success if we got here without rolling back
render json: { message: 'Question updated successfully!', id: question.id }, status: :ok if question.persisted? && question.errors.empty?
end
Comment thread
sjproctor marked this conversation as resolved.
# rubocop:enable Metrics/MethodLength

def destroy
question = Question.find_by(id: params[:id])

Expand All @@ -68,7 +112,7 @@ def handle_delete_action(success, success_key, failure_key)

def validate_permissions(question)
unless question.user_id == current_user.id || current_user.admin?
render json: { errors: ['You do not have permission to delete this question.'] }, status: :forbidden
render json: { errors: ['You do not have permission to perform this action on this question.'] }, status: :forbidden
return false
end
true
Expand Down Expand Up @@ -97,6 +141,27 @@ def handle_question_associations(question)
handle_subjects(question)
end

def handle_image_changes!
deleted_image_ids = question_params[:deleted_image_ids]
existing_images = question_params[:existing_images]&.map(&:to_h)

return if deleted_image_ids.blank? && existing_images.blank?

# Batch delete images to avoid N+1 queries
Image.where(id: deleted_image_ids).destroy_all if deleted_image_ids.present?

# Batch fetch and update existing images to avoid N+1 queries
return if existing_images.blank?

image_ids = existing_images.pluck('id').compact
images_by_id = Image.where(id: image_ids).index_by(&:id)

existing_images.each do |existing_image|
image = images_by_id[existing_image['id'].to_i]
image&.update(alt_text: existing_image['alt_text'])
end
end

##
# Processes and normalizes the question parameters.
#
Expand Down Expand Up @@ -593,7 +658,9 @@ def question_params
images: [],
keywords: [],
subjects: [],
alt_text: []
alt_text: [],
deleted_image_ids: [],
existing_images: [:id, :alt_text]
)
end
end
Expand Down
Loading
Loading