-
Notifications
You must be signed in to change notification settings - Fork 10
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
Vignettes #737
Open
f-buerckel
wants to merge
108
commits into
feature/vignettes-longterm
Choose a base branch
from
feature/vignette
base: feature/vignettes-longterm
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,573
−11
Open
Vignettes #737
Changes from all commits
Commits
Show all changes
108 commits
Select commit
Hold shift + click to select a range
80d9f7f
add action_text
f-buerckel 63c9672
add questionaire and slide model for vignettes
f-buerckel 0664a27
add basic views for create, list and edit questionnaire and slides
f-buerckel a1f4762
add modal create vignette panel
f-buerckel d59dbf0
Update action of slide
f-buerckel 12ef9b5
add import statement of trix in application.js
f-buerckel 0002f39
rebased dev into vignette
f-buerckel 7ed2702
fix active storage direct upload routing error
f-buerckel b9176b0
fix activeStorage routing bug and add libsvips42
f-buerckel 695ab13
add question model to vignettes and plain javascript approach for dyn…
f-buerckel c772b2e
add stimulus
f-buerckel c754ba1
add multiple choice option for questionnaire
f-buerckel d2497f7
Merge branch 'temporary-multipleChoiceQuestion' into feature/vignette
f-buerckel b220ab9
ensure correct post request for dynamic form
f-buerckel 80ad4cf
add video support for action text
f-buerckel bb9c05e
add position attribute to slides
f-buerckel 700bd0d
add answer model for vignettes
f-buerckel 7c56f4f
improve video playback view
f-buerckel a782b5f
feat: take vignette questionnaire
f-buerckel b931d18
Track slide statistics for questionnaire answers
f-buerckel 67cac52
Add route to export questionnaire answers as csv
f-buerckel 02050b0
Add likert scale as vignette question type
f-buerckel b8e409f
add info slide version 1
f-buerckel fbbe137
-Add validation of position in questionnaire#take
f-buerckel 9762e3e
-Add validations for title and question text;
f-buerckel 2d37ed7
-Add icon support to info slides
f-buerckel 6e383eb
correct date on migration
f-buerckel afa8891
removed unused views and routes
f-buerckel d820472
-minor bug fixes
f-buerckel e871872
remove leftovers from stimulus
f-buerckel 9cf7638
fixed some rubocop errors
f-buerckel c37d9b1
fix more rubocop violations
f-buerckel 674a781
fix all spec rubocop violations
f-buerckel e829f57
fix more spec rubocop violations
f-buerckel 9bf7b6d
fix more spec rubocop violations
f-buerckel a9726f1
condense db migrations files
f-buerckel c5a385b
add ERD-diagram for vignettes
f-buerckel c8cb9f4
add version to active_storage_validations gem
f-buerckel 11f0375
fix merge conflict of gemfile
f-buerckel b971ce4
bundle install for gemfile
f-buerckel 86b27e1
Merge branch 'feature/vignettes-longterm' into feature/vignette
Splines 9636aa2
Merge vignette answer migrations together
Splines d6cd90b
Init card design for slides
Splines b71cf8a
Style "Next slide" button & add slide shadow
Splines 0cd77ad
Render info slides as modals
Splines 5dd66a0
Only show info slide section if there is any
Splines 3ca04e3
Redesign text area & add validation
Splines a3bb4e0
Check validations on form submit
Splines b23bfe7
Move answer section to card footer & restyle submit button
Splines debbb4a
Use object-oriented model for slide statistics
Splines 553c035
Remove console errors
Splines 288fc00
Early return if not text question
Splines 47f857e
Add design for likert scale
Splines cf76766
Swap main content and additional information
Splines bbc6782
Fix alignment of additional information on small screens
Splines bc2003b
Improve accessibility for open info slide & submit button
Splines 8889949
Make likert scale input required
Splines 1c48ebe
Redesign multiple choice questions
Splines 7375014
Fix indentation of ERB syntax
Splines f9436de
Init UI support for sortable slides
Splines 4e5ba22
Add TODO where slide was not moved at all
Splines dc60ef2
Edit slide view: get basic dynamic loading of edit form to work
Splines f4f2aba
Render slide edit form dynamically into accordion
Splines 0974a68
Remove unnecessary console logs
Splines 6fa43c5
Redesign add new slide button
Splines 1270ab6
Add option to add new slide (at least to display the new slide creati…
Splines 61d49d5
Design text field (for edit slide)
Splines ff6c395
Start rewriting question types logic
Splines eb90a01
Redesign question type selection & use Bootstrap collapse
Splines 3497567
Let backend populate select field & handle question field state
Splines 2edf6ac
Make question field state update more explicit
Splines 32d8c81
Start simplifying multiple choice options view
Splines ff50fc0
Reimplement adding new multiple choice option
Splines 584b349
Implement removing a multiple choice option
Splines 3903f64
Make sure remove button also works for dynamically added items
Splines 363888e
Pin Tom Select & use bootstrap 5 CSS styles
Splines 2638982
Use Tom Select for info slide selection & add form-floating
Splines f248fc2
Disable sortable dragging on accordion body
Splines ae20f2a
Remove unused ghost class on sortable
Splines 6f15393
Use tom select for question type selection
Splines 09f4daf
Don't prevent mouse clicks for filtered sortable items
Splines f29367a
Improve accordion slide opening (spinner & flickering)
Splines 4169992
Ignore shown.bs.collapse for multiple choice options
Splines 95fc4ef
Improve rendering of loading spinner
Splines f7651c8
Add hint for dragging slides
Splines 255b5c4
Fix redundant line continuation
Splines d5b60ac
make questionnnaires part of course
f-buerckel cad182f
Merge branch 'feature/vignette' of github.com:MaMpf-HD/mampf into fea…
f-buerckel 10f4706
Merge pull request #753 from MaMpf-HD/vignettes-ui
f-buerckel de97b7b
fix docker compose command in just docker-reseed
f-buerckel 15369ef
add locking mechanism to vignettes
f-buerckel c3b02f0
Make questionnaire part of lecture
f-buerckel d6a8b23
make vignettes accessible from lecture
f-buerckel aa8d4e4
reworked routes to not use vignettes prefix
f-buerckel 24ebaae
Add authentication for vignette controller actions
f-buerckel c17e83f
fix bug where vignette answer not stored correctly
f-buerckel 56bfe4d
Remove unreachable code for questionnaire function
f-buerckel ecd1d09
fix rubocop warnings
f-buerckel 65238e7
Add update slide function for questionnaire
f-buerckel 3f94297
Remove unused routes and views
f-buerckel 3b206d3
Add delete button for questionnaire and slides
f-buerckel 59d7886
Use a floating form for create new vignette title
Splines 2af894c
Use modal-footer & respect MaMpf "Cancel vs. Submit" button ordering
Splines caa37b9
Only show "drag slides" tip if there are at least 2 slides
Splines 9691e6a
Remove "New Slide" title
Splines 7b32faa
Don't show spinner when creating a new slide
Splines 4ca39d9
Add backend validations for actions after publish
f-buerckel 1b8bb4b
Merge branch 'feature/vignette' of github.com:MaMpf-HD/mampf into fea…
f-buerckel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--require spec_helper | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,6 +123,7 @@ | |
"justfile", | ||
"katex", | ||
"lightgray", | ||
"likert", | ||
"localroot", | ||
"mailcatcher", | ||
"mampf", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
//= link_tree ../images | ||
//= link_directory ../javascripts/vignettes .js | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use application.js instead (or rather I'm not sure what the difference between this manifest.js and the other file is and will check it out) |
||
//= link_directory ../javascripts .js | ||
//= link_directory ../stylesheets .css | ||
//= link commontator/manifest.js | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// When using turbolinks, the event is not fired on redirect | ||
$(document).ready(function () { | ||
const vignetteSlideList = $("#vignettes-slides-accordion"); | ||
createSortableVignetteSlides(vignetteSlideList); | ||
registerVignetteSlideListeners(vignetteSlideList); | ||
registerNewSlideButtonListener(vignetteSlideList); | ||
}); | ||
|
||
function createSortableVignetteSlides(vignetteSlideList) { | ||
Sortable.create(vignetteSlideList.get(0), { | ||
animation: 150, | ||
filter: ".accordion-collapse", | ||
preventOnFilter: false, | ||
onEnd: function (evt) { | ||
if (evt.oldIndex == evt.newIndex) return; | ||
|
||
let questionnaire_id = evt.target.dataset.questionnaireId; | ||
$.ajax({ | ||
url: `/questionnaires/${questionnaire_id}/update_slide_position`, | ||
method: "PATCH", | ||
data: { | ||
old_position: evt.oldIndex, | ||
new_position: evt.newIndex, | ||
}, | ||
success: function (_response) { | ||
console.log(`Slide was moved from position ${evt.oldIndex} to ${evt.newIndex}`); | ||
}, | ||
error: function (xhr, status, error) { | ||
console.error(`Failed to update position: ${error}`); | ||
}, | ||
}); | ||
}, | ||
}); | ||
} | ||
|
||
function registerVignetteSlideListeners(vignetteSlideList) { | ||
$(".vignette-accordion-collapse").on("shown.bs.collapse", function (evt) { | ||
const slideId = evt.target.dataset.slideId; | ||
if (slideId === undefined) { | ||
// multiple choice options are also collapsible | ||
// and trigger shown.bs.collapse | ||
return; | ||
} | ||
|
||
const questionnaireId = vignetteSlideList.attr("data-questionnaire-id"); | ||
if (questionnaireId === undefined) { | ||
console.error("Questionnaire id is missing"); | ||
return; | ||
} | ||
|
||
const loadingSpinner = $(`#vignette-slide-loading-${slideId}`); | ||
let spinnerTimeout = setTimeout(function () { | ||
loadingSpinner.show(); | ||
}, 100); // avoid flickering when loading is fast | ||
|
||
$.ajax({ | ||
url: Routes.edit_questionnaire_slide_path(questionnaireId, slideId), | ||
method: "GET", | ||
dataType: "html", | ||
success: function (response) { | ||
$(vignetteSlideList).find(".slides-edit-form-container").html(""); | ||
const editFormContainer = $(`#slide-edit-form-container-${slideId}`); | ||
editFormContainer.hide().html(response).ready(function () { | ||
clearTimeout(spinnerTimeout); | ||
loadingSpinner.hide(); | ||
editFormContainer.show(); | ||
}); | ||
}, | ||
error: function (xhr, status, error) { | ||
clearTimeout(spinnerTimeout); | ||
console.error(`Failed to load slide edit form: ${error}`); | ||
}, | ||
}); | ||
}); | ||
} | ||
|
||
function registerNewSlideButtonListener(vignetteSlideList) { | ||
$("#vignettes-new-slide-btn").click(function () { | ||
$(this).prop("disabled", true); | ||
|
||
const questionnaireId = vignetteSlideList.attr("data-questionnaire-id"); | ||
if (questionnaireId === undefined) { | ||
console.error("Questionnaire id is missing"); | ||
return; | ||
} | ||
|
||
$.ajax({ | ||
url: Routes.new_questionnaire_slide_path(questionnaireId), | ||
method: "GET", | ||
dataType: "html", | ||
success: function (response) { | ||
$(vignetteSlideList).append(response); | ||
const newSlide = $(vignetteSlideList).children().last(); | ||
openAccordionItem(newSlide); | ||
}, | ||
error: function (xhr, status, error) { | ||
console.error(`Failed to load new slide form: ${error}`); | ||
}, | ||
}); | ||
}); | ||
} | ||
|
||
function openAccordionItem($item) { | ||
new bootstrap.Collapse($item.find(".collapse"), { | ||
toggle: true, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
$(document).ready(function () { | ||
new TomSelect("#vignettes-linked-info-slides", { | ||
plugins: ["remove_button"], | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
var QUESTION_TYPE_SELECT_ID = "#vignettes-question-type-select"; | ||
|
||
$(document).ready(function () { | ||
handleQuestionTypes(); | ||
updateQuestionFieldState($(QUESTION_TYPE_SELECT_ID).val()); | ||
new TomSelect(QUESTION_TYPE_SELECT_ID, { allowEmptyOption: true }); | ||
handleMultipleChoiceEditor(); | ||
}); | ||
|
||
function handleQuestionTypes() { | ||
const questionTypeDropdown = $(QUESTION_TYPE_SELECT_ID); | ||
|
||
questionTypeDropdown.on("change", function (event) { | ||
updateQuestionFieldState(event.target.value); | ||
}); | ||
} | ||
|
||
function updateQuestionFieldState(selectedName) { | ||
const multipleChoiceField = $("#vignette-edit-multiple-choice"); | ||
const questionTextField = $("#vignette-question-text"); | ||
|
||
// Type "No question" | ||
if (selectedName === "") { | ||
questionTextField.find("textarea").val(""); | ||
questionTextField.collapse("hide"); | ||
} | ||
else { | ||
questionTextField.collapse("show"); | ||
} | ||
|
||
if (selectedName === "Vignettes::MultipleChoiceQuestion") { | ||
multipleChoiceField.collapse("show"); | ||
} | ||
else { | ||
multipleChoiceField.collapse("hide"); | ||
} | ||
} | ||
|
||
function handleMultipleChoiceEditor() { | ||
// Adding an option | ||
$("#vignette-multiple-choice-add").click(function (_evt) { | ||
const template = $("#vignette-multiple-choice-options-template"); | ||
const newOptionHtml = template.html(); | ||
const uniqueId = new Date().getTime(); | ||
const newBlockHtml = newOptionHtml.replace(/NEW_RECORD/g, uniqueId); | ||
$("#vignette-multiple-choice-options").append(newBlockHtml); | ||
}); | ||
|
||
// Removing an option | ||
$(document).on("click", ".remove-vignette-mc-option", function (evt) { | ||
const parentDiv = $(evt.target).closest("div"); | ||
parentDiv.find(".vignette-mc-hidden-destroy").val("1"); | ||
parentDiv.removeClass("d-flex").hide(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
var VIGNETTE_FORM_ID = "#vignettes-answer-form"; | ||
function shouldRegisterVignette() { | ||
return $(VIGNETTE_FORM_ID).length > 0; | ||
} | ||
|
||
$(document).on("turbolinks:load", () => { | ||
if (!shouldRegisterVignette()) { | ||
return; | ||
} | ||
|
||
registerSubmitHandler(); | ||
registerTextAnswerValidator(); | ||
|
||
const stats = new VignetteSlideStatistics(); | ||
registerStatisticsHandler(stats); | ||
}); | ||
|
||
function registerSubmitHandler() { | ||
$(VIGNETTE_FORM_ID).submit((event) => { | ||
let isValid = false; | ||
isValid = validateTextAnswer(); | ||
|
||
if (!isValid) { | ||
event.preventDefault(); | ||
return false; | ||
} | ||
}); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// Text Answer Fields | ||
//////////////////////////////////////////////////////////////////////////////// | ||
var TEXT_ANSWER_ID = "vignettes_answer_text"; | ||
|
||
function registerTextAnswerValidator() { | ||
const textBody = document.getElementById(TEXT_ANSWER_ID); | ||
if (!textBody) { | ||
return; | ||
} | ||
$(textBody).on("input", () => { | ||
validateTextAnswer(); | ||
}); | ||
} | ||
|
||
function validateTextAnswer() { | ||
const textBody = document.getElementById(TEXT_ANSWER_ID); | ||
if (!textBody) { | ||
return true; | ||
} | ||
|
||
let isValid = false; | ||
|
||
const validityState = textBody.validity; | ||
if (validityState.tooShort) { | ||
const tooShortMessage = textBody.dataset.tooShortMessage; | ||
textBody.setCustomValidity(tooShortMessage); | ||
} | ||
else if (validityState.valueMissing) { | ||
const valueMissingMessage = textBody.dataset.valueMissingMessage; | ||
textBody.setCustomValidity(valueMissingMessage); | ||
} | ||
else { | ||
// render input valid, so that form will submit | ||
textBody.setCustomValidity(""); | ||
isValid = true; | ||
} | ||
|
||
textBody.reportValidity(); | ||
return isValid; | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// Statistics | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
class VignetteSlideStatistics { | ||
slideStartTime = new Date(); | ||
totalSlideTime = 0; | ||
|
||
infoSlideAccessCounts = {}; | ||
infoSlideStartTime = null; | ||
infoSlideTimes = {}; | ||
|
||
increaseInfoSlideAccessCount(index) { | ||
this.infoSlideAccessCounts[index] ??= 0; | ||
this.infoSlideAccessCounts[index]++; | ||
} | ||
|
||
freezeSlideTime() { | ||
if (this.slideStartTime === null) { | ||
console.error("Attempted to freeze slide time when it was already frozen"); | ||
return; | ||
} | ||
this.totalSlideTime += (Date.now() - this.slideStartTime); | ||
this.slideStartTime = null; | ||
} | ||
|
||
unfreezeSlideTime() { | ||
this.slideStartTime = Date.now(); | ||
} | ||
|
||
startInfoSlideTimer() { | ||
this.infoSlideStartTime = Date.now(); | ||
} | ||
|
||
stopInfoSlideTimer(index) { | ||
if (this.infoSlideStartTime === null) { | ||
console.error("Attempted to stop info slide timer when it was already stopped"); | ||
return; | ||
} | ||
const timeOnInfoSlide = (Date.now() - this.infoSlideStartTime); | ||
this.infoSlideTimes[index] = (this.infoSlideTimes[index] || 0.0) + timeOnInfoSlide; | ||
this.infoSlideStartTime = null; | ||
} | ||
|
||
postProcessTimes() { | ||
for (let key in this.infoSlideTimes) { | ||
this.infoSlideTimes[key] = Math.floor(this.infoSlideTimes[key] / 1000); | ||
} | ||
this.totalSlideTime = Math.floor(this.totalSlideTime / 1000); | ||
} | ||
} | ||
|
||
function registerStatisticsHandler(stats) { | ||
// Info Slide - Opening | ||
const openInfoSlideButtons = $(".open-info-slide-btn"); | ||
if (!openInfoSlideButtons) { | ||
return; | ||
} | ||
openInfoSlideButtons.each(function () { | ||
const index = $(this).attr("data-info-slide-index"); | ||
$(this).click(() => { | ||
stats.freezeSlideTime(); | ||
stats.increaseInfoSlideAccessCount(index); | ||
stats.startInfoSlideTimer(); | ||
}); | ||
}); | ||
|
||
// Info Slide - Closing | ||
const infoSlideModals = $(".vignette-info-slide-modal"); | ||
if (!infoSlideModals) { | ||
console.error("No info slide modals found"); | ||
return; | ||
} | ||
infoSlideModals.each(function () { | ||
$(this).on("hidden.bs.modal", function () { | ||
const index = $(this).attr("data-info-slide-index"); | ||
stats.stopInfoSlideTimer(index); | ||
stats.unfreezeSlideTime(); | ||
}); | ||
}); | ||
|
||
// Form Submission | ||
$(VIGNETTE_FORM_ID).submit(() => { | ||
// Take rest of time into account | ||
if (stats.slideStartTime) { | ||
stats.freezeSlideTime(); | ||
} | ||
|
||
stats.postProcessTimes(); | ||
|
||
// Transfer results to hidden form-fields | ||
const timeOnSlideField = $("#time-on-slide-field"); | ||
const timeOnInfoSlidesField = $("#time-on-info-slides-field"); | ||
const infoSlidesAccessCountField = $("#info-slides-access-count-field"); | ||
timeOnSlideField.val(stats.totalSlideTime); | ||
timeOnInfoSlidesField.val(JSON.stringify(stats.infoSlideTimes)); | ||
infoSlidesAccessCountField.val(JSON.stringify(stats.infoSlideAccessCounts)); | ||
}); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? I do see the advantage that this means the spec_helper is automatically included. However, this is already done in the rails_helper by requiring it explicitly. All you have to do is therefore include the rails helper which is done in every of our test files.