Finish remaining question types

This commit is contained in:
Charlotte Van Petegem 2025-05-20 18:28:20 +02:00
parent 0d54f756d8
commit 5bd1e821f7
Signed by: chvp
SSH key fingerprint: SHA256:s9rb8jBVfdahqWHuBAcHCBP1wmj4eYQXZfqgz4H3E9E
20 changed files with 431 additions and 8 deletions

View file

@ -3,3 +3,38 @@
display: flex;
flex-direction: row;
}
.acrostic-row {
display: flex;
flex-direction: row;
}
.acrostic-offset,.acrostic-number {
display: inline-block;
width: 2rem;
height: 2rem;
border: 2px solid transparent;
}
.acrostic-input {
padding: 0;
width: 2rem;
height: 2rem;
text-transform: uppercase;
text-align: center;
}
.acrostic-column {
background-color: lightgrey;
}
.connections-grid {
display: grid;
grid-template-columns: 25% 25% 25% 25%;
}
.connections-element {
border: 1px solid black;
margin: 0.5em;
padding: 1em;
}

View file

@ -24,4 +24,16 @@ class QuestionsController < ApplicationController
def handle_politicians_answer
@answer.update!(data: params[:order])
end
def handle_acrostic_answer
@answer.update!(data: params[:data])
end
def handle_connections_answer
@answer.update!(data: params[:order])
end
def handle_lyrics_answer
@answer.update!(data: params[:data])
end
end

View file

@ -4,6 +4,7 @@ class SectionsController < ApplicationController
end
def show
@sections = Section.all
@section = Section.find(params[:id])
@questions = @section.questions.includes(:answer)
end

View file

@ -0,0 +1,39 @@
import { submitValueDebounced } from './submit_value.js'
export default function initAcrosticQuestions() {
document.querySelectorAll('[data-behaviour="question_acrostic"]').forEach((acrostic) => {
const submitUrl = acrostic.dataset.submitUrl
const _submit = submitValueDebounced()
const allInputs = Array.from(acrostic.querySelectorAll(".acrostic-input"))
function submit() {
const data = allInputs.map((input) => input.value)
_submit(submitUrl, {data})
}
allInputs.forEach((input) => {
const index = Number.parseInt(input.dataset.index)
const previous = acrostic.querySelector(`.acrostic-input[data-index="${index - 1}"]`)
const next = acrostic.querySelector(`.acrostic-input[data-index="${index + 1}"]`)
input.addEventListener('input', (event) => {
if (event.data) {
input.value = event.data[0]
}
submit()
if (event.data) {
if (next) {
next.focus()
}
} else {
if (previous) {
previous.focus()
}
}
})
input.addEventListener('focus', () => {
input.select()
})
})
})
}

View file

@ -2,9 +2,19 @@
import initSimpleQuestions from './simple_input.js'
import initImageQuestions from './image_input.js'
import initPoliticianQuestions from './politicians_answer.js'
import initAcrosticQuestions from './acrostic_answer.js'
import initConnectionsQuestions from './connections_answer.js'
import initLyricsQuestions from './lyrics_answer.js'
import { Sortable, Swap } from 'sortablejs'
Sortable.mount(new Swap())
addEventListener("DOMContentLoaded", () => {
initSimpleQuestions()
initImageQuestions()
initPoliticianQuestions()
initAcrosticQuestions()
initConnectionsQuestions()
initLyricsQuestions()
})

View file

@ -0,0 +1,19 @@
import { Sortable } from 'sortablejs'
import { submitValue } from './submit_value.js'
export default function initConnectionsQuestions() {
document.querySelectorAll('[data-behaviour="connections_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

@ -0,0 +1,21 @@
import { submitValueDebounced } from './submit_value.js'
export default function initLyricsQuestions() {
document.querySelectorAll('[data-behaviour="lyrics_answer"]').forEach((lyrics) => {
const submitUrl = lyrics.dataset.submitUrl
const _submit = submitValueDebounced()
const allInputs = Array.from(lyrics.querySelectorAll(".lyrics-input"))
function submit() {
const data = allInputs.map((input) => input.value)
_submit(submitUrl, {data})
}
allInputs.forEach((input) => {
const index = Number.parseInt(input.dataset.index)
input.addEventListener('input', () => submit())
input.addEventListener('change', () => submit())
})
})
}

View file

@ -1,11 +1,8 @@
import { Sortable, Swap } from 'sortablejs'
import { Sortable } 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

View file

@ -21,8 +21,8 @@
# section_id (section_id => sections.id)
#
class Question < ApplicationRecord
enum :answer_kind, { simple: 0, image: 1, politicians: 2 }, prefix: true
enum :question_kind, { simple: 0, video: 1 }, prefix: true
enum :answer_kind, { simple: 0, image: 1, politicians: 2, acrostic: 3, connections: 4, lyrics: 5 }, prefix: true
enum :question_kind, { simple: 0, video: 1, acrostic: 3 }, prefix: true
belongs_to :section
has_one :answer

View file

@ -0,0 +1,22 @@
<% max_position = question.data.map { |c| c["position"] }.max %>
<% index = -1 %>
<div class="acrostic-puzzle"
data-behaviour="question_acrostic"
data-submit-url="<%= answer_question_url(question.id) %>"
>
<% question.data.each.with_index do |constraints, i| %>
<div class="acrostic-row">
<span class="acrostic-number"><%= i + 1 %></span>
<% (max_position - constraints["position"]).times do %>
<span class="acrostic-offset"></span>
<% end %>
<% (constraints["position"] - 1).times do %>
<%= render partial: "questions/acrostic_input", locals: {on_column: false, index: (index += 1), answer_data: question.answer&.data} %>
<% end %>
<%= render partial: "questions/acrostic_input", locals: {on_column: true, index: (index += 1), answer_data: question.answer&.data} %>
<% (constraints["length"] - constraints["position"]).times do %>
<%= render partial: "questions/acrostic_input", locals: {on_column: false, index: (index += 1), answer_data: question.answer&.data} %>
<% end %>
</div>
<% end %>
</div>

View file

@ -0,0 +1,6 @@
<%= tag.input class: {"acrostic-input": true, "acrostic-column": on_column},
data: {index: index},
value: answer_data&.then { |it| it[index] },
type: "text",
autocomplete: "off"
%>

View file

@ -0,0 +1,3 @@
<% question.text.lines.each.with_index do |q, i| %>
<p><strong><%= i + 1 %>.</strong> <%= q %></p>
<% end %>

View file

@ -0,0 +1,11 @@
<div
class="connections-grid"
data-behaviour="connections_answer"
data-submit-url="<%= answer_question_url(question.id) %>"
>
<% answers = question.data.each.with_index.to_a %>
<% answers = reorder_with_indices(answers, question.answer.data) if question.answer.present? %>
<% answers.each do |a, i| %>
<span class="connections-element" data-id="<%= i %>"><%= a %></span>
<% end %>
</div>

View file

@ -0,0 +1,15 @@
<% index = -1 %>
<div data-behaviour="lyrics_answer"
data-submit-url="<%= answer_question_url(question.id) %>"
>
<% question.data.lines.each do |l| %>
<% parts = l.split "{}", -1 %>
<p>
<%= render partial: "questions/lyrics_given_part", locals: {text: parts[0]} %>
<% parts[1..].each do |text| %>
<%= render partial: "questions/lyrics_input", locals: {index: (index += 1), answer_data: question.answer&.data} %>
<%= render partial: "questions/lyrics_given_part", locals: {text: text} %>
<% end %>
</p>
<% end %>
</div>

View file

@ -0,0 +1,3 @@
<% if text.present? %>
<span><%= text %></span>
<% end %>

View file

@ -0,0 +1,6 @@
<%= tag.input class: "lyrics-input",
data: {index: index},
value: answer_data&.then { |it| it[index] },
type: "text",
autocomplete: "off"
%>

View file

@ -1,3 +1,8 @@
<%= link_to sections_url do %>Home<% end %>
<% @sections.each do |section| %>
<%= link_to section.title, section_url(section) %>
<% end %>
<h4><%= @section.title %></h4>
<p><%= @section.description %></p>

View file

@ -421,3 +421,205 @@ photos_sint_kruis_winkel:
text: De grens met Zelzate.
answer_kind: <%= Question.answer_kinds[:image] %>
question_kind: <%= Question.question_kinds[:simple] %>
acrostic_acrostic:
section: acrostic
text: |
Wat is het oudste publiek toegankelijke museum van België?
Waar is Chokri Ben Chika tegen?
Waar kan je met je oldtimer binnen met een dagpas?
Wat is de bijnaam van de Gentenaar?
Wie kan je bewonderen in Poppentheater Pedrolino?
Welke dag gaan jullie het slechtst slapen?
Welke mini Gentse Feesten gaan door rond 15 augustus?
data: '<%= JSON.dump([
{ position: 3, length: 3 },
{ position: 5, length: 8 },
{ position: 3, length: 3 },
{ position: 7, length: 14 },
{ position: 4, length: 14 },
{ position: 3, length: 9 },
{ position: 11, length: 16 },
]) %>'
answer_kind: <%= Question.answer_kinds[:acrostic] %>
question_kind: <%= Question.question_kinds[:acrostic] %>
connections_buildings:
section: connections
text: Gebouwen
data: '<%=
JSON.dump(
["Huwelijk", "Burcht", "Middeleeuwen", "Nieuw slot", "UGent", "Wetenschap", "Museum", "Plantentuin", "Hotel", "Matexi", "Provincie", "Defensie"].shuffle
)
%>'
answer_kind: <%= Question.answer_kinds[:connections] %>
question_kind: <%= Question.question_kinds[:simple] %>
connections_food:
section: connections
text: Eten
data: '<%=
JSON.dump(
["Bouillon", "Kip", "Kervel", "Brood", "Neus", "Kegel", "Framboos", "Kraam", "Kaneel", "Bruine suiker", "Hondsdolheid", "Sint-Hubertus"].shuffle
)
%>'
answer_kind: <%= Question.answer_kinds[:connections] %>
question_kind: <%= Question.question_kinds[:simple] %>
connections_sport:
section: connections
text: Sport
data: '<%=
JSON.dump(
["Marathon", "Tokyo", "Somalië", "1989", "5K", "Roeien", "Nachez", "Blaarmeersen", "Voetbal", "Baro", "2013", "Arteveldestadion"].shuffle
)
%>'
answer_kind: <%= Question.answer_kinds[:connections] %>
question_kind: <%= Question.question_kinds[:simple] %>
translations_lousbirge:
section: translations
text: "we goan eu in Lousbirge steeke"
answer_kind: <%= Question.answer_kinds[:simple] %>
question_kind: <%= Question.question_kinds[:simple] %>
translations_alvekoate:
section: translations
text: "da weete mijn kluuten uuk en 't zijn gîen alvekoate"
answer_kind: <%= Question.answer_kinds[:simple] %>
question_kind: <%= Question.question_kinds[:simple] %>
translations_deursteeke:
section: translations
text: "de koarte deursteeke"
answer_kind: <%= Question.answer_kinds[:simple] %>
question_kind: <%= Question.question_kinds[:simple] %>
translations_dampuurte:
section: translations
text: "'k goa eu mee eu muile teege de Dampuurte plakke, dadde viertien doage bruine ziepe schijt"
answer_kind: <%= Question.answer_kinds[:simple] %>
question_kind: <%= Question.question_kinds[:simple] %>
translations_wielekes:
section: translations
text: "o mijn tante wielekes g'hat, 't was een kerre"
answer_kind: <%= Question.answer_kinds[:simple] %>
question_kind: <%= Question.question_kinds[:simple] %>
translations_soepe:
section: translations
text: "veur em schept God den dag en moed'r schept de soepe"
answer_kind: <%= Question.answer_kinds[:simple] %>
question_kind: <%= Question.question_kinds[:simple] %>
music_one:
section: music
text: ""
data: '<%= JSON.dump <<END.strip
Matthias, ons klein {} gij die boas zijt hier in {}
De stad heeft {} gekozen, 74%
Nee nee, nee nee {}
Nee, we gaan dat hier niet doen
Nee nee, nee nee {}
Nee, we gaan dat hier niet doen
{} mag vertrekken, meid {} uw voeten niet
En gij gaat op dubbel-date met {} en {}
Nee nee, nee nee {}
Nee, we gaan dat hier niet doen
Nee nee, nee nee {}
Nee, we gaan dat hier niet doen
Vandaag is de perfecte dag, {} die heeft gezegd
Als het moet dan zit mijn {} wel ne keer opzij
Nee nee, nee nee {}
Nee, we gaan dat hier niet doen
Nee nee, nee nee {}
Nee, we gaan dat hier niet doen
END
%>'
answer_kind: <%= Question.answer_kinds[:lyrics] %>
question_kind: <%= Question.question_kinds[:simple] %>
music_two:
section: music
text: ""
data: '<%= JSON.dump <<END.strip
De {} regeert het land
Beter dan ooit tevoren
{} heeft het licht gezien
Ze zegt
{} gaat verloren
Voorlopig gaan we nog even door
Op het {} pad
Het {} spoor
{} als ik vind je overal
Op de arbeidsmarkt
In dit {}
Sterren komen
Sterren gaan
Alleen {} blijft bestaan
{} heeft nooit afgezien
Ze vraagt
Kun jij nog {}
END
%>'
answer_kind: <%= Question.answer_kinds[:lyrics] %>
question_kind: <%= Question.question_kinds[:simple] %>
music_three:
section: music
text: ""
data: '<%= JSON.dump <<END.strip
Ik ben nie al te zot van t {}
Maar k vange gere {}
Marblen en {} kan ik wel
Maar daarin ben ik nie fel
k Zie tegenwoordig overal
En ook al in mijn {}
Jongens {} op nen bal
Maar k spele t liefst van al
Mee mijne {}
En zijne {}
Hij goot omhoge
t Es t ziene weert
k Geve maar klauwe
Op mijn gemak
k Heb nog {} bollekens
In mijne zak
END
%>'
answer_kind: <%= Question.answer_kinds[:lyrics] %>
question_kind: <%= Question.question_kinds[:simple] %>
music_four:
section: music
text: ""
data: '<%= JSON.dump <<END.strip
k è u liere kenne, oan de {} in ne café
Gij woar guul {}, ik oa ne moat of draaie mee
k woare nog moar binne en k keek een beetse rond
En k zag u ginter zitte, k krege t {} in mijne mond
Ge dronk van uwe {} en k zaaie: "jonges ezuu een {}!"
k moest iets onderneme, want ge woar veur maa
k wiste nie wa zegge, dat es van mijn {} nie
Moar k ben mij kome zette en k zaaie: "meiske, {} hier!"
Scheetse, loetse, bolleke, zoetse
Lieveke, schatse, {}
Wilde mee maa {} oastemblieft?
Want scheetse, loetse, bolleke, zoetse
Lieveke, schatse, {}
k è liever een {} dan een {}!
END
%>'
answer_kind: <%= Question.answer_kinds[:lyrics] %>
question_kind: <%= Question.question_kinds[:simple] %>

View file

@ -41,3 +41,19 @@ world:
photos:
title: Fotoronde
description: In deze ronde moet je met elk van de vermelde dingen een foto nemen.
acrostic:
title: Verborgen hint
description: Vul alle vragen juist in en vind de verborgen hint. Leestekens en spaties moet je weglaten.
connections:
title: Steekronde
description: Verplaats de termen zodat de bijeenhorende begrippen op één rij staan.
translations:
title: Gentse gezegden
description: Vertaal de volgende Gentse gezegden
music:
title: Muziekronde
description: Vul de onderstaande liedjesteksten aan.