Add politicians question type

This commit is contained in:
Charlotte Van Petegem 2025-05-17 11:58:07 +02:00
parent 4fe218166f
commit 2893f2bc53
Signed by: chvp
SSH key fingerprint: SHA256:s9rb8jBVfdahqWHuBAcHCBP1wmj4eYQXZfqgz4H3E9E
16 changed files with 101 additions and 33 deletions

View file

@ -1 +1,5 @@
/* Entry point for your PostCSS build */ /* Entry point for your PostCSS build */
.politicians-listing {
display: flex;
flex-direction: row;
}

View file

@ -20,4 +20,8 @@ class QuestionsController < ApplicationController
) )
@answer.save! @answer.save!
end end
def handle_politicians_answer
@answer.update!(data: params[:order])
end
end end

View file

@ -1,2 +1,5 @@
module QuestionsHelper module QuestionsHelper
def reorder_with_indices(array, indices)
array.sort {|a1, a2| indices.index(a1[1]) <=> indices.index(a2[1])}
end
end end

View file

@ -1,8 +1,10 @@
// Entry point for the build script in your package.json // Entry point for the build script in your package.json
import initSimpleQuestions from './simple_input.js' import initSimpleQuestions from './simple_input.js'
import initImageQuestions from './image_input.js' import initImageQuestions from './image_input.js'
import initPoliticianQuestions from './politicians_answer.js'
addEventListener("DOMContentLoaded", () => { addEventListener("DOMContentLoaded", () => {
initSimpleQuestions() initSimpleQuestions()
initImageQuestions() initImageQuestions()
initPoliticianQuestions()
}) })

View file

@ -1,4 +1,4 @@
const csrfToken = document.querySelector("[name='csrf-token']").content import { submitValue } from './submit_value.js'
export default function initImageQuestions() { export default function initImageQuestions() {
document.querySelectorAll('[data-behaviour="question_image_input"]').forEach((input) => { document.querySelectorAll('[data-behaviour="question_image_input"]').forEach((input) => {
@ -8,18 +8,11 @@ export default function initImageQuestions() {
const file = input.files[0]; const file = input.files[0];
const fileReader = new FileReader(); const fileReader = new FileReader();
fileReader.onload = (ev) => { fileReader.onload = (ev) => {
fetch(submitUrl, { submitValue(submitUrl, {
method: 'PUT',
headers: {
"x-csrf-token": csrfToken,
"content-type": "application/json",
},
body: JSON.stringify({
filename: file.name, filename: file.name,
mimetype: file.type, mimetype: file.type,
data: ev.target.result.replace(/^data:[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+\/[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+;base64,/, ""), data: ev.target.result.replace(/^data:[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+\/[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+;base64,/, ""),
}) })
})
} }
fileReader.readAsDataURL(file); fileReader.readAsDataURL(file);
const preview = document.querySelector(`#${input.id}_preview`) const preview = document.querySelector(`#${input.id}_preview`)

View file

@ -0,0 +1,20 @@
import { Sortable, Swap } from 'sortablejs'
import { submitValue } from './submit_value.js'
export default function initPoliticianQuestions() {
Sortable.mount(new Swap())
document.querySelectorAll('[data-behaviour="politicians_answer').forEach((list) => {
const submitUrl = list.dataset.submitUrl
Sortable.create(list, {
swap: true,
onUpdate: (event) => {
const newOrder = Array.from(event.to.children).map((el) => Number.parseInt(el.dataset.id))
submitValue(submitUrl, { order: newOrder })
}
})
})
}

View file

@ -1,23 +1,11 @@
import debounce from 'debounce' import { submitValueDebounced } from './submit_value.js'
const csrfToken = document.querySelector("[name='csrf-token']").content
export default function initSimpleQuestions() { export default function initSimpleQuestions() {
document.querySelectorAll('[data-behaviour="question_simple_input"]').forEach((input) => { document.querySelectorAll('[data-behaviour="question_simple_input"]').forEach((input) => {
const submitUrl = input.dataset.submitUrl const submitUrl = input.dataset.submitUrl
const submit = submitValueDebounced()
const handleInput = debounce(() => { input.addEventListener('change', () => submit(submitUrl, {value: input.value}))
fetch(submitUrl, { input.addEventListener('input', () => submit(submitUrl, {value: input.value}))
method: 'PUT',
headers: {
"x-csrf-token": csrfToken,
"content-type": "application/json",
},
body: JSON.stringify({ value: input.value }),
})
}, 300)
input.addEventListener('change', handleInput)
input.addEventListener('input', handleInput)
}) })
} }

View file

@ -0,0 +1,18 @@
import debounce from 'debounce'
const csrfToken = document.querySelector("[name='csrf-token']").content
export function submitValue(url, value) {
fetch(url, {
method: 'PUT',
headers: {
"x-csrf-token": csrfToken,
"content-type": "application/json",
},
body: JSON.stringify(value),
})
}
export function submitValueDebounced() {
return debounce(submitValue, 300)
}

View file

@ -4,6 +4,7 @@
# #
# id :integer not null, primary key # id :integer not null, primary key
# answer_kind :integer not null # answer_kind :integer not null
# data :text
# public_asset_path :string # public_asset_path :string
# question_kind :integer default("simple"), not null # question_kind :integer default("simple"), not null
# text :text not null # text :text not null
@ -25,4 +26,6 @@ class Question < ApplicationRecord
belongs_to :section belongs_to :section
has_one :answer has_one :answer
serialize :data, coder: JSON
end end

View file

@ -1,3 +1,16 @@
<div> <div class="politicians-listing">
This one's going to be hard... <div>
<% question.data["left"].each do |l| %>
<p><%= l %></p>
<% end %>
</div>
<div data-behaviour="politicians_answer"
data-submit-url="<%= answer_question_url(question.id) %>"
>
<% answers = question.data["right"].each.with_index.to_a %>
<% answers = reorder_with_indices(answers, question.answer.data) if question.answer.present? %>
<% answers.each do |a, i| %>
<p data-id="<%= i %>"><%= a %></p>
<% end %>
</div>
</div> </div>

View file

@ -0,0 +1,5 @@
class QuestionAddDataField < ActiveRecord::Migration[8.0]
def change
add_column :questions, :data, :text
end
end

3
db/schema.rb generated
View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_04_26_202909) do ActiveRecord::Schema[8.0].define(version: 2025_05_15_124656) do
create_table "active_storage_attachments", force: :cascade do |t| create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
t.string "record_type", null: false t.string "record_type", null: false
@ -55,6 +55,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_26_202909) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "question_kind", default: 0, null: false t.integer "question_kind", default: 0, null: false
t.string "public_asset_path" t.string "public_asset_path"
t.text "data"
t.index ["section_id"], name: "index_questions_on_section_id" t.index ["section_id"], name: "index_questions_on_section_id"
end end

View file

@ -11,7 +11,8 @@
"postcss": "^8.5.3", "postcss": "^8.5.3",
"postcss-cli": "^11.0.1", "postcss-cli": "^11.0.1",
"postcss-import": "^16.1.0", "postcss-import": "^16.1.0",
"postcss-nesting": "^13.0.1" "postcss-nesting": "^13.0.1",
"sortablejs": "^1.15.6"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.25.3" "esbuild": "^0.25.3"

View file

@ -6,6 +6,7 @@
# #
# id :integer not null, primary key # id :integer not null, primary key
# answer_kind :integer not null # answer_kind :integer not null
# data :text
# public_asset_path :string # public_asset_path :string
# question_kind :integer default("simple"), not null # question_kind :integer default("simple"), not null
# text :text not null # text :text not null
@ -115,7 +116,13 @@ politics_regenpijpen:
politics_schepenen: politics_schepenen:
section: politics section: politics
text: Verbind de schepenen en hun bevoegdheden. text: Verplaats de bevoegdheden zodat ze bij de juiste schepen staan.
data: '<%=
JSON.dump({
left: ["Mathias De Clercq", "Hafsa El-Bazioui", "Astrid De Bruycker", "Sofie Bracke", "Evita Willaert", "Joris Vandenbroucke", "Bram Van Braeckevelt", "Burak Nalli", "Filip Watteeuw", "Cristophe Peeters"].shuffle,
right: ["Burgemeester", "Participatie, Buurtwerk, Mondiale Solidariteit, Facilitair Management en Digitalisering", "Sociale Vooruitgang, Gezondheid, Ouderenbeleid en Cultuur", "Economie, Haven, Handel, Toerisme en Openbare netheid", "Onderwijs, Opvoeding, Jeugd, Gezin en Outreachend Werk", "Mobiliteit, Ruimte, Stadsontwikkeling en Plezier", "Natuur en Groen, Werk en Sociale Economie, Gelijke kansen en Sport", "Personeelsbeleid, Burgerzaken en Dienstverlening", "Wonen, Milieu, Klimaat en Energie", "Financiën, Stedenbouw, Erfgoed en Administratieve vereenvoudiging"].shuffle
})
%>'
answer_kind: <%= Question.answer_kinds[:politicians] %> answer_kind: <%= Question.answer_kinds[:politicians] %>
question_kind: <%= Question.question_kinds[:simple] %> question_kind: <%= Question.question_kinds[:simple] %>

View file

@ -4,6 +4,7 @@
# #
# id :integer not null, primary key # id :integer not null, primary key
# answer_kind :integer not null # answer_kind :integer not null
# data :text
# public_asset_path :string # public_asset_path :string
# question_kind :integer default("simple"), not null # question_kind :integer default("simple"), not null
# text :text not null # text :text not null

View file

@ -559,6 +559,11 @@ slash@^5.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce"
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==
sortablejs@^1.15.6:
version "1.15.6"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.6.tgz#ff93699493f5b8ab8d828f933227b4988df1d393"
integrity sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==
source-map-js@^1.2.1: source-map-js@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"