From 2893f2bc53a264b4a80afdb2c8fade2d3e1b8716 Mon Sep 17 00:00:00 2001 From: Charlotte Van Petegem Date: Sat, 17 May 2025 11:58:07 +0200 Subject: [PATCH] Add politicians question type --- .../stylesheets/application.postcss.css | 4 ++++ app/controllers/questions_controller.rb | 4 ++++ app/helpers/questions_helper.rb | 3 +++ app/javascript/application.js | 2 ++ app/javascript/image_input.js | 17 +++++----------- app/javascript/politicians_answer.js | 20 +++++++++++++++++++ app/javascript/simple_input.js | 20 ++++--------------- app/javascript/submit_value.js | 18 +++++++++++++++++ app/models/question.rb | 3 +++ .../questions/_politicians_form.html.erb | 17 ++++++++++++++-- .../20250515124656_question_add_data_field.rb | 5 +++++ db/schema.rb | 3 ++- package.json | 3 ++- test/fixtures/questions.yml | 9 ++++++++- test/models/question_test.rb | 1 + yarn.lock | 5 +++++ 16 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 app/javascript/politicians_answer.js create mode 100644 app/javascript/submit_value.js create mode 100644 db/migrate/20250515124656_question_add_data_field.rb diff --git a/app/assets/stylesheets/application.postcss.css b/app/assets/stylesheets/application.postcss.css index 3be5c25..c155eda 100644 --- a/app/assets/stylesheets/application.postcss.css +++ b/app/assets/stylesheets/application.postcss.css @@ -1 +1,5 @@ /* Entry point for your PostCSS build */ +.politicians-listing { + display: flex; + flex-direction: row; +} diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index ae80659..4b56b49 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -20,4 +20,8 @@ class QuestionsController < ApplicationController ) @answer.save! end + + def handle_politicians_answer + @answer.update!(data: params[:order]) + end end diff --git a/app/helpers/questions_helper.rb b/app/helpers/questions_helper.rb index 2eaab4a..aef0af0 100644 --- a/app/helpers/questions_helper.rb +++ b/app/helpers/questions_helper.rb @@ -1,2 +1,5 @@ module QuestionsHelper + def reorder_with_indices(array, indices) + array.sort {|a1, a2| indices.index(a1[1]) <=> indices.index(a2[1])} + end end diff --git a/app/javascript/application.js b/app/javascript/application.js index b95e42c..fe5d93e 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,8 +1,10 @@ // Entry point for the build script in your package.json import initSimpleQuestions from './simple_input.js' import initImageQuestions from './image_input.js' +import initPoliticianQuestions from './politicians_answer.js' addEventListener("DOMContentLoaded", () => { initSimpleQuestions() initImageQuestions() + initPoliticianQuestions() }) diff --git a/app/javascript/image_input.js b/app/javascript/image_input.js index 89a7f4e..00365f2 100644 --- a/app/javascript/image_input.js +++ b/app/javascript/image_input.js @@ -1,4 +1,4 @@ -const csrfToken = document.querySelector("[name='csrf-token']").content +import { submitValue } from './submit_value.js' export default function initImageQuestions() { document.querySelectorAll('[data-behaviour="question_image_input"]').forEach((input) => { @@ -8,17 +8,10 @@ export default function initImageQuestions() { const file = input.files[0]; const fileReader = new FileReader(); fileReader.onload = (ev) => { - fetch(submitUrl, { - method: 'PUT', - headers: { - "x-csrf-token": csrfToken, - "content-type": "application/json", - }, - body: JSON.stringify({ - filename: file.name, - mimetype: file.type, - data: ev.target.result.replace(/^data:[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+\/[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+;base64,/, ""), - }) + submitValue(submitUrl, { + filename: file.name, + mimetype: file.type, + data: ev.target.result.replace(/^data:[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+\/[a-zA-Z0-9!#$%^&\\*_\-+{}|'.`~]+;base64,/, ""), }) } fileReader.readAsDataURL(file); diff --git a/app/javascript/politicians_answer.js b/app/javascript/politicians_answer.js new file mode 100644 index 0000000..f29e457 --- /dev/null +++ b/app/javascript/politicians_answer.js @@ -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 }) + } + }) + }) +} diff --git a/app/javascript/simple_input.js b/app/javascript/simple_input.js index a217db0..32ef152 100644 --- a/app/javascript/simple_input.js +++ b/app/javascript/simple_input.js @@ -1,23 +1,11 @@ -import debounce from 'debounce' - -const csrfToken = document.querySelector("[name='csrf-token']").content +import { submitValueDebounced } from './submit_value.js' export default function initSimpleQuestions() { document.querySelectorAll('[data-behaviour="question_simple_input"]').forEach((input) => { const submitUrl = input.dataset.submitUrl + const submit = submitValueDebounced() - const handleInput = debounce(() => { - fetch(submitUrl, { - 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) + input.addEventListener('change', () => submit(submitUrl, {value: input.value})) + input.addEventListener('input', () => submit(submitUrl, {value: input.value})) }) } diff --git a/app/javascript/submit_value.js b/app/javascript/submit_value.js new file mode 100644 index 0000000..06ae32a --- /dev/null +++ b/app/javascript/submit_value.js @@ -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) +} diff --git a/app/models/question.rb b/app/models/question.rb index 76896d0..265a6c5 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -4,6 +4,7 @@ # # id :integer not null, primary key # answer_kind :integer not null +# data :text # public_asset_path :string # question_kind :integer default("simple"), not null # text :text not null @@ -25,4 +26,6 @@ class Question < ApplicationRecord belongs_to :section has_one :answer + + serialize :data, coder: JSON end diff --git a/app/views/questions/_politicians_form.html.erb b/app/views/questions/_politicians_form.html.erb index 6d0db74..2e6c8c4 100644 --- a/app/views/questions/_politicians_form.html.erb +++ b/app/views/questions/_politicians_form.html.erb @@ -1,3 +1,16 @@ -
- This one's going to be hard... +
+
+ <% question.data["left"].each do |l| %> +

<%= l %>

+ <% end %> +
+
+ <% 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| %> +

<%= a %>

+ <% end %> +
diff --git a/db/migrate/20250515124656_question_add_data_field.rb b/db/migrate/20250515124656_question_add_data_field.rb new file mode 100644 index 0000000..206de8b --- /dev/null +++ b/db/migrate/20250515124656_question_add_data_field.rb @@ -0,0 +1,5 @@ +class QuestionAddDataField < ActiveRecord::Migration[8.0] + def change + add_column :questions, :data, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 838462e..0a765e0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # 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| t.string "name", 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.integer "question_kind", default: 0, null: false t.string "public_asset_path" + t.text "data" t.index ["section_id"], name: "index_questions_on_section_id" end diff --git a/package.json b/package.json index e4646f7..1d83185 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "postcss": "^8.5.3", "postcss-cli": "^11.0.1", "postcss-import": "^16.1.0", - "postcss-nesting": "^13.0.1" + "postcss-nesting": "^13.0.1", + "sortablejs": "^1.15.6" }, "devDependencies": { "esbuild": "^0.25.3" diff --git a/test/fixtures/questions.yml b/test/fixtures/questions.yml index 6490764..2a84cba 100644 --- a/test/fixtures/questions.yml +++ b/test/fixtures/questions.yml @@ -6,6 +6,7 @@ # # id :integer not null, primary key # answer_kind :integer not null +# data :text # public_asset_path :string # question_kind :integer default("simple"), not null # text :text not null @@ -115,7 +116,13 @@ politics_regenpijpen: politics_schepenen: 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] %> question_kind: <%= Question.question_kinds[:simple] %> diff --git a/test/models/question_test.rb b/test/models/question_test.rb index 035485b..938d024 100644 --- a/test/models/question_test.rb +++ b/test/models/question_test.rb @@ -4,6 +4,7 @@ # # id :integer not null, primary key # answer_kind :integer not null +# data :text # public_asset_path :string # question_kind :integer default("simple"), not null # text :text not null diff --git a/yarn.lock b/yarn.lock index 8f4d87c..160c124 100644 --- a/yarn.lock +++ b/yarn.lock @@ -559,6 +559,11 @@ slash@^5.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" 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: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"