diff --git a/app/controllers/admin/activities_controller.rb b/app/controllers/admin/activities_controller.rb
index 3c2402992..924966f0e 100644
--- a/app/controllers/admin/activities_controller.rb
+++ b/app/controllers/admin/activities_controller.rb
@@ -21,8 +21,11 @@ def show
end
def create
- @activity = Activity.new(activity_post_params.except(:_destroy))
+ @activity = Activity.new(activity_post_params.except(:_destroy, :notes_options))
+ if @activity.notes_input_type != 'text' && params[:activity][:notes_options].present?
+ @activity.notes = params[:activity][:notes_options].compact_blank.join("\n")
+ end
if @activity.save
# manual call to impressionist, because otherwise the activity doesn't have an id yet
impressionist(@activity)
@@ -63,13 +66,16 @@ def update
@activity = Activity.find(params[:id])
params = activity_post_params
+ if params[:notes_input_type] != 'text' && params[:notes_options].present?
+ params[:notes] = params[:notes_options].compact_blank.join("\n")
+ end
# removing the images from disk
if params[:_destroy] == 'true'
logger.debug('remove poster from activity')
@activity.poster.purge
end
- if @activity.update(params.except(:_destroy))
+ if @activity.update(params.except(:_destroy, :notes_options))
redirect_to(@activity)
else
@recipients = @activity.payment_mail_recipients
@@ -119,6 +125,8 @@ def activity_post_params
:is_seniors,
:participant_limit,
:show_participants,
- :_destroy)
+ :notes_input_type,
+ :_destroy,
+ notes_options: [])
end
end
diff --git a/app/controllers/members/participants_controller.rb b/app/controllers/members/participants_controller.rb
index 4d6906df7..091217ee0 100644
--- a/app/controllers/members/participants_controller.rb
+++ b/app/controllers/members/participants_controller.rb
@@ -28,7 +28,8 @@ def create
end
@member = Member.find(current_user.credentials_id)
- @notes = params[:par_notes]
+ @notes = params[:par_notes]
+
reservist = false
# Deny if already enrolled
diff --git a/app/javascript/src/admin/activities.js b/app/javascript/src/admin/activities.js
index 06b089899..d7608a4fc 100644
--- a/app/javascript/src/admin/activities.js
+++ b/app/javascript/src/admin/activities.js
@@ -540,3 +540,110 @@ function posterHandlers() {
$(this).find('button[type="submit"].wait').addClass("disabled");
});
}
+
+$(document).on("click", "#add-notes-option", function () {
+ var optionIndex = $("#notes-options .col-md-6").length;
+ var optionHtml =
+ '
";
+ $("#notes-options").append(optionHtml);
+});
+
+$(document).on("click", ".delete-option", function () {
+ $(this).closest(".col-md-6").remove();
+});
+
+$(document).on("change", "#activity_notes_input_type", function () {
+ var notesInputType = $(this).val();
+ var notesContainer = $("#notes-container");
+
+ if (notesInputType === "text") {
+ var notesValue = $("#notes-options input")
+ .map(function () {
+ return $(this).val();
+ })
+ .get()
+ .join("\n");
+ notesContainer.html(
+ '",
+ );
+ } else {
+ var optionsHtml = "";
+ var notesValue = $("#activity_notes").val();
+ var optionsArray = [];
+
+ if (notesValue && notesValue.trim() !== "") {
+ optionsArray = notesValue.split("\n");
+ }
+
+ if (optionsArray.length > 0) {
+ var titleValue = optionsArray[0];
+ optionsHtml +=
+ '' +
+ '
' +
+ 'Title' +
+ '' +
+ "
" +
+ "
";
+
+ optionsArray.slice(1).forEach(function (optionValue) {
+ optionsHtml +=
+ '";
+ });
+ } else {
+ optionsHtml +=
+ '' +
+ '
' +
+ 'Title' +
+ '' +
+ "
" +
+ "
" +
+ '";
+ }
+
+ notesContainer.html(
+ '' +
+ optionsHtml +
+ "
" +
+ '' +
+ '
' +
+ '" +
+ "
" +
+ "
",
+ );
+ }
+});
diff --git a/app/javascript/src/members/activities/activity.js b/app/javascript/src/members/activities/activity.js
index 91bcd083d..a6e2d8301 100644
--- a/app/javascript/src/members/activities/activity.js
+++ b/app/javascript/src/members/activities/activity.js
@@ -113,6 +113,19 @@ export class Activity {
has_notes() {
return this.notes.length !== 0;
}
+
+ has_notes_checkboxes() {
+ return find_in_object(this.notes, function (note) {
+ return note.type === "checkbox";
+ });
+ }
+
+ has_notes_radio_buttons() {
+ return find_in_object(this.notes, function (note) {
+ return note.type === "radio";
+ });
+ }
+
are_notes_filled() {
return $.trim(this.notes.val()).length > 0;
}
@@ -184,12 +197,29 @@ Object.defineProperties(Activity.prototype, {
*/
value: function (method) {
var activity = this;
+ var par_notes;
+ var notes = [];
+ console.log(activity.has_notes_checkboxes);
+ if (activity.has_notes_checkboxes()) {
+ console.log(this.notes.find(":checked"));
+ this.notes.each(function () {
+ if (this.checked) {
+ notes.push(this.value);
+ }
+ });
+ par_notes = notes.join(", ");
+ } else if (activity.has_notes_radio_buttons()) {
+ par_notes = this.notes.find(":checked").val();
+ } else {
+ par_notes = this.notes.val();
+ }
+
var request = $.ajax({
url: "/activities/" + activity.id + "/participants",
type: method,
data: {
authenticity_token: this.token,
- par_notes: this.notes.val(),
+ par_notes: par_notes,
},
})
.done(function (response) {
@@ -374,7 +404,7 @@ Object.defineProperties(
},
notes: function () {
- return this.panel.find(".notes");
+ return this.panel.find(".notes"); // .find function returns a jQuery object with the found elements
},
notes_mandatory: function () {
diff --git a/app/models/activity.rb b/app/models/activity.rb
index 0f430faec..bf28d361a 100644
--- a/app/models/activity.rb
+++ b/app/models/activity.rb
@@ -33,8 +33,8 @@ def content_type
end
end
+ enum notes_input_type: { text: 0, checkboxes: 1, radio_buttons: 2 }
validates :notes, presence: true, if: proc { |a| a.notes_public? || a.notes_mandatory? }
-
is_impressionable
before_validation :validate_enrollable
diff --git a/app/views/admin/activities/partials/_edit.html.haml b/app/views/admin/activities/partials/_edit.html.haml
index 159adc5fc..b3e3de8b8 100644
--- a/app/views/admin/activities/partials/_edit.html.haml
+++ b/app/views/admin/activities/partials/_edit.html.haml
@@ -127,16 +127,56 @@
= label(:activity, :organized_by)
.ui-select
= f.select :organized_by, options_for_select(Group.has_members.order(:category, :name).map{ |group| [group.name, group.id] }, @activity.organized_by), :include_blank => '-'
-
.form-group
.row
.col-md-12
= f.label :notes
-
+ .row
+ .col-md-12#notes-container
+ - if @activity.notes_input_type == 'text'
+ = f.text_area :notes, value: @activity.notes, class: 'form-control', id: 'activity-notes'
+ - elsif @activity.notes_input_type == 'checkboxes' || @activity.notes_input_type == 'radio_buttons'
+ .row#notes-options
+ - if @activity.notes.present?
+ - notes_lines = @activity.notes.split("\n")
+ - if notes_lines.length > 1
+ - title = notes_lines.first
+ .col-md-12
+ .input-group.mb-3
+ %span.input-group-text Title
+ = text_field_tag "activity[notes_options][]", title, class: 'form-control'
+ - notes_lines.drop(1).each do |option|
+ .col-md-6
+ .input-group.mb-3
+ = text_field_tag "activity[notes_options][]", option, class: 'form-control'
+ .input-group-append
+ %button.btn.btn-sm.btn-danger.delete-option{ type: 'button' }
+ %i.fa.fa-fw.fa-minus
+ - else
+ .col-md-12
+ .input-group.mb-3
+ %span.input-group-text Title
+ = text_field_tag "activity[notes_options][]", @activity.notes, class: 'form-control'
+ - else
+ .col-md-12
+ .input-group.mb-3
+ %span.input-group-text Title
+ = text_field_tag "activity[notes_options][]", '', class: 'form-control'
+ .col-md-6
+ .input-group.mb-3
+ = text_field_tag "activity[notes_options][]", '', class: 'form-control'
+ .input-group-append
+ %button.btn.btn-sm.btn-danger.delete-option{ type: 'button' }
+ %i.fa.fa-fw.fa-minus
+ .row
+ .col-md-12
+ %button.btn.btn-primary#add-notes-option{ type: 'button' }
+ %i.fa.fa-fw.fa-plus
+ Add Option
.row
.col-md-12
- = f.text_field :notes, :value => @activity.notes, :class => 'form-control', id: "activity-notes"
-
+ = f.label :notes_input_type
+ = f.select :notes_input_type, Activity.notes_input_types.keys.map { |type| [type.humanize, type] }, {}, class: 'form-control'
.row
.col-sm-6
= f.check_box :notes_public, checked: @activity.notes_public
diff --git a/app/views/members/activities/partials/_view.html.haml b/app/views/members/activities/partials/_view.html.haml
index 509b8fbe6..633ec9ceb 100644
--- a/app/views/members/activities/partials/_view.html.haml
+++ b/app/views/members/activities/partials/_view.html.haml
@@ -1,14 +1,13 @@
-
-#
@param {activity} activity
@param {?} @enrollment
@param {member} @member
-.card.panel-activity{ data: {:'activity-id' => activity.id, :'notes-mandatory' => activity.notes_mandatory, :'unenroll-date' => activity.unenroll_date.at_end_of_day.to_i }}
+.card.panel-activity{ data: { :'activity-id' => activity.id, :'notes-mandatory' => activity.notes_mandatory, :'unenroll-date' => activity.unenroll_date.at_end_of_day.to_i }}
.card-header.text-right
- %span.card-title.float-left.activity-title{ :style => 'text-overflow: ellipsis;'}
+ %span.card-title.float-left.activity-title{ style: 'text-overflow: ellipsis;' }
- if view == 'show'
- = link_to ("/activities/") do
+ = link_to("/activities") do
%i.fa.fa-chevron-left
= activity.name
- else
@@ -20,101 +19,115 @@
- if activity.poster.attached?
.media-left.poster-thumbnail.col-12.col-sm-6
%a.show-poster-modal{'data-toggle': 'modal', 'data-target': '#poster-modal'}
- = image_tag activity.thumbnail_representation, class: "small-poster"
+ = image_tag(activity.thumbnail_representation, class: "small-poster")
.mask.d-flex.justify-content-center
- %span.align-self-center{'style': 'color: White'}
+ %span.align-self-center{ style: 'color: White' }
%i.fas.fa-search.fa-3x
.media-body.col-12.col-sm-6
- if activity.start_date != activity.end_date
%strong= I18n.t("activerecord.labels.activities.when")
= l(activity.start_date, format: "%A %d/%m/%Y")
- - if !activity.start_time.nil?
+ - if activity.start_time.present?
= activity.start_time.strftime("%H:%M")
\-
= l(activity.end_date, format: "%A %d/%m/%Y")
- - if !activity.end_time.nil?
+ - if activity.end_time.present?
= activity.end_time.strftime("%H:%M")
- else
%strong= I18n.t("activerecord.labels.activities.date")
= l(activity.start_date, format: "%A %d/%m/%Y")
- - if !activity.start_time.nil?
-
+ - if activity.start_time.present?
+ %br/
%strong= I18n.t("activerecord.labels.activities.time")
= activity.start_time.strftime("%H:%M")
- - if !activity.end_time.nil?
+ - if activity.end_time.present?
\-
= activity.end_time.strftime("%H:%M")
-
+ %br/
%strong= I18n.t("activerecord.attributes.activity.price")
- - if !activity.price.nil? and activity.price != 0
+ - if activity.price.present? && activity.price != 0
€
- = number_with_precision(activity.price, :precision => 2)
-
+ = number_with_precision(activity.price, precision: 2)
+ %br/
- else
= I18n.t("activerecord.missing_value_placeholders.activity.free")
-
- - if !activity.location.nil? && activity.location != ''
+ %br/
+ - if activity.location.present?
%strong= I18n.t("activerecord.attributes.activity.location")
= activity.location
-
- - if !activity.unenroll_date.nil? && activity.open?
+ %br/
+ - if activity.unenroll_date.present? && activity.open?
%strong= I18n.t("activerecord.attributes.activity.unenroll_date")
%span.activity-unenroll
= l(activity.unenroll_date, format: "%A %d/%m/%Y")
23:59
-
- - if !activity.google_event().nil?
- %a{href: activity.google_event(), target: "_blank"}
+ %br/
+ - if activity.google_event.present?
+ %a{ href: activity.google_event, target: "_blank" }
= I18n.t("activerecord.attributes.activity.google_event")
-
+ %br/
- if view == 'index'
- if activity.notes_mandatory && activity.notes.present?
- %span{style: 'color: red; font-weight: bold; display:block; padding-top: 14px'}= I18n.t("members.activities.info.notes_mandatory")
- - if view =='show'
+ %span{ style: 'color: red; font-weight: bold; display:block; padding-top: 14px' }= I18n.t("members.activities.info.notes_mandatory")
+ - if view == 'show'
.card-body.show-activity-topborder
%strong= I18n.t("activerecord.attributes.activity.description")
-
- - if @user.language == 'nl' && !activity.description_nl.blank?
+ %br/
+ - if @user.language == 'nl' && activity.description_nl.present?
= simple_format(activity.description_nl)
- - elsif @user.language == 'en' && !activity.description_en.blank?
+ - elsif @user.language == 'en' && activity.description_en.present?
= simple_format(activity.description_en)
- - elsif !activity.description_nl.blank?
+ - elsif activity.description_nl.present?
= simple_format(activity.description_nl)
- - elsif !activity.description_en.blank?
+ - elsif activity.description_en.present?
= simple_format(activity.description_en)
- else
= I18n.t("activerecord.missing_value_placeholders.activity.description")
- - if !activity.notes.blank?
+ - if activity.notes.present?
.card-body.show-activity-topborder
%strong
- = activity.notes
- %textarea.form-control.notes{:maxlength => '100', :value => (@enrollment.notes unless @enrollment.nil?)}
- = @enrollment.notes unless @enrollment.nil?
+ = activity.notes.split("\n").first
+ - if activity.checkboxes?
+ .form-group
+ - activity.notes.split("\n").drop(1).each do |option|
+ .row
+ .col-sm-6
+ %input.notes{ type: "checkbox", name: "enrollment[notes][]", value: option, checked: !@enrollment.nil? && @enrollment.notes&.include?(option), disabled: !activity.open? }
+ %label= option
+ - elsif activity.radio_buttons?
+ .form-group
+ - activity.notes.split("\n").drop(1).each do |option|
+ .row
+ .col-sm-6
+ %input.notes{ type: "radio", name: "enrollment[notes]", value: option, checked: !@enrollment.nil? && @enrollment.notes == option, disabled: !activity.open?}
+ %label= option
+ - else
+ %textarea.form-control.notes{ maxlength: '100', disabled: !activity.open? }
+ = @enrollment.nil? ? activity.notes : @enrollment.notes
.card-footer.clearfix
.row
- if view == 'index'
- - if !activity.notes.blank?
+ - if activity.notes.present?
= link_to raw("#{I18n.t('members.activities.info.more_info')} "), "/activities/#{ activity.id }", class: "btn btn-secondary more-info col-12 col-sm-6"
- else
= link_to I18n.t('members.activities.info.more_info'), "/activities/#{ activity.id }", class: "btn btn-secondary more-info col-12 col-sm-6"
-
- if activity.notes.present? && view == 'show'
- %button.btn.btn-info.update-enrollment.col-9.col-sm-6{ :class => ('d-none' unless (@member.confirmed_activities.ids.include? activity.id) || (@member.reservist_activities.ids.include? activity.id))}
- = I18n.t("members.activities.actions.update_info")
+ %button.btn.btn-info.update-enrollment.col-12.col-sm-6{ class: ('d-none' unless (@member.confirmed_activities.ids.include?(activity.id) || @member.reservist_activities.ids.include?(activity.id))) }
+ = I18n.t("members.activities.actions.update_info") # confirmed sign-in
- if activity.open?
- - if @member.confirmed_activities.ids.include? activity.id # confirmed sign-in
+ - if @member.confirmed_activities.ids.include?(activity.id)
- button_text = I18n.t("members.activities.actions.unenroll")
- button_class = 'btn-danger col-12 col-sm-6'
- - if !activity.unenroll_date.nil? && activity.unenroll_date < Date.today # confirmed sign-in -- past sign-out deadline
+ - if activity.unenroll_date.present? && activity.unenroll_date < Date.today # confirmed sign-in -- past unenroll date
- button_class += ' disabled'
- - elsif @member.reservist_activities.ids.include? activity.id # signed in as reservist (time is before sign-out deadline so can sign out)
- - button_text = I18n.t("members.activities.actions.reservist_unenroll") # there is no case where you can sign up as reservist after sign-out deadline, as of writing
+ - elsif @member.reservist_activities.ids.include?(activity.id)# signed in as reservist (time is before sign-out deadline so can sign out)
+ - button_text = I18n.t("members.activities.actions.reservist_unenroll") # there is no case where you can sign up as reservist after sign-out deadline, as of writing
- button_class = 'btn-reservistSignout col-12 col-sm-6'
- - elsif activity.participant_limit != nil && activity.participant_limit <= activity.participants.count # not signed in -- but can do so as reservist only
+ - elsif activity.participant_limit.present? && activity.participant_limit <= activity.participants.count # not signed in -- but can do so as reservist only
- button_text = I18n.t("members.activities.actions.reservist_enroll")
- button_class = 'btn-reservistSignup col-12 col-sm-6'
- - else # not signed in -- and but can do so, not as reservist
+ - else # not signed in -- and but can do so, not as reservist
- button_text = I18n.t("members.activities.actions.enroll")
- button_class = 'btn-success col-12 col-sm-6'
- %button.btn.enrollment{ :class => button_class}
+ %button.btn.enrollment{ class: button_class }
= button_text
diff --git a/db/migrate/20240530151252_add_notes_input_type_to_activities.rb b/db/migrate/20240530151252_add_notes_input_type_to_activities.rb
new file mode 100644
index 000000000..26a44076b
--- /dev/null
+++ b/db/migrate/20240530151252_add_notes_input_type_to_activities.rb
@@ -0,0 +1,5 @@
+class AddNotesInputTypeToActivities < ActiveRecord::Migration[6.1]
+ def change
+ add_column :activities, :notes_input_type, :integer, default: 0
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 075e5bfba..5e78ffd55 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -163,7 +163,8 @@ CREATE TABLE public.activities (
open_time time without time zone,
is_sophomores boolean,
is_seniors boolean,
- payable_updated_at date
+ payable_updated_at date,
+ notes_input_type integer DEFAULT 0
);
@@ -1736,6 +1737,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20220221195220'),
('20220406092056'),
('20220524203723'),
-('20240125003700');
+('20240125003700'),
+('20240530151252');