diff --git a/app/controllers/public/volunteers_controller.rb b/app/controllers/public/volunteers_controller.rb new file mode 100644 index 0000000..303b188 --- /dev/null +++ b/app/controllers/public/volunteers_controller.rb @@ -0,0 +1,41 @@ +module Public + class VolunteersController < Public::ApplicationController + def new + @volunteer = current_conference.volunteers.build + end + + def edit + @volunteer = current_conference.volunteers.find_by! unique_id: params[:id] + end + + def create + @volunteer = current_conference.volunteers.build volunteer_params + if @volunteer.save + flash[:notice] = I18n.t('views.volunteers.successful_application') + redirect_to edit_volunteer_path(@volunteer.unique_id) + else + render :new, status: :unprocessable_entity + end + end + + def update + @volunteer = current_conference.volunteers.find_by! unique_id: params[:id] + if @volunteer.update volunteer_params + flash[:notice] = I18n.t('views.volunteers.successful_application_edit') + redirect_to edit_volunteer_path(@volunteer.unique_id) + else + render :edit, status: :unprocessable_entity + end + end + + private + + def volunteer_params + params.require(:volunteer).permit( + :name, :picture, :email, :phone, :tshirt_size, :tshirt_cut, + :food_preferences, :previous_experience, :notes, :language, + volunteer_team_ids: [] + ) + end + end +end diff --git a/app/mailers/volunteer_mailer.rb b/app/mailers/volunteer_mailer.rb new file mode 100644 index 0000000..d5fbc9a --- /dev/null +++ b/app/mailers/volunteer_mailer.rb @@ -0,0 +1,19 @@ +# coding: utf-8 +class VolunteerMailer < ActionMailer::Base + def team_notification(new_volunteer) + @volunteer = new_volunteer + + mail(to: @volunteer.conference.email, + subject: "Нов доброволец – #{@volunteer.name} <#{@volunteer.email}> за екипи #{@volunteer.volunteer_teams.map(&:name).join(', ')}") + end + + def volunteer_notification(new_volunteer) + @volunteer = new_volunteer + I18n.locale = @volunteer.language + mail(to: @volunteer.email, + reply_to: @volunteer.conference.email, + from: 'no-reply@openfest.org', + subject: I18n.t('volunteer_mailer.success_notification.subject', + conference_name: @volunteer.conference.title)) + end +end diff --git a/app/models/conference.rb b/app/models/conference.rb index 0aaecc6..db2a61b 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -13,6 +13,7 @@ class Conference < ActiveRecord::Base has_many :event_types has_many :events has_many :volunteer_teams + has_many :volunteers has_one :call_for_participation, dependent: :destroy has_many :participants, class_name: 'User', through: :events has_many :slots, through: :halls diff --git a/app/models/volunteer.rb b/app/models/volunteer.rb new file mode 100644 index 0000000..9848efd --- /dev/null +++ b/app/models/volunteer.rb @@ -0,0 +1,38 @@ +class Volunteer < ActiveRecord::Base + TSHIRT_SIZES = [:s, :m, :l, :xl, :xxl, :xxxl] + TSHIRT_CUTS = [:unisex, :female] + FOOD_PREFERENCES = [:none, :vegetarian, :vegan] + + attachment :picture, type: :image + + validates :name, :language, :tshirt_size, :tshirt_cut, :food_preferences, presence: true + validates :tshirt_size, inclusion: {in: TSHIRT_SIZES.map(&:to_s)} + validates :tshirt_cut, inclusion: {in: TSHIRT_CUTS.map(&:to_s)} + validates :food_preferences, inclusion: {in: FOOD_PREFERENCES.map(&:to_s)} + validates :email, format: {with: /\A[^@]+@[^@]+\z/}, presence: true + validates :phone, presence: true, format: {with: /\A[+\- \(\)0-9]+\z/} + validates :volunteer_teams, presence: true + + phony_normalize :phone, default_country_code: 'BG' + + belongs_to :conference + has_and_belongs_to_many :volunteer_teams + + before_create :assign_unique_id + after_create :send_notification_to_organizers + after_create :send_notification_to_volunteer + + private + + def assign_unique_id + self.unique_id = Digest::SHA256.hexdigest(SecureRandom.uuid) + end + + def send_notification_to_organizers + VolunteerMailer.team_notification(self).deliver_later + end + + def send_notification_to_volunteer + VolunteerMailer.volunteer_notification(self).deliver_later + end +end diff --git a/app/views/volunteer_mailer/team_notification.text.erb b/app/views/volunteer_mailer/team_notification.text.erb new file mode 100644 index 0000000..5d0583f --- /dev/null +++ b/app/views/volunteer_mailer/team_notification.text.erb @@ -0,0 +1,22 @@ +Здравейте, + +<%= @volunteer.name %> <<%= @volunteer.email %>> изпрати кандидатура за доброволец. + +Екипи: <%= @volunteer.volunteer_teams.map(&:name).join(', ') %> + +Снимка: <%= attachment_url(@volunteer, :picture, host: "https://#{@volunteer.conference.host_name}/") %> +Език: <%= @volunteer.language %> +Телефон: <%= @volunteer.phone %> +Размер на тениска: <%= @volunteer.tshirt_size %> +Кройка на тениска: <%= @volunteer.tshirt_cut %> +Предпочитания за храна: <%= @volunteer.food_preferences %> +<% if @volunteer.previous_experience.present? -%> + +Предходен опит: +<%= @volunteer.previous_experience %> +<% end -%> +<% if @volunteer.notes.present? -%> + +Бележки: +<%= @volunteer.notes %> +<% end -%> diff --git a/app/views/volunteer_mailer/volunteer_notification.bg.text.erb b/app/views/volunteer_mailer/volunteer_notification.bg.text.erb new file mode 100644 index 0000000..fbae00f --- /dev/null +++ b/app/views/volunteer_mailer/volunteer_notification.bg.text.erb @@ -0,0 +1,7 @@ +Здравейте, + +Кандидатурата Ви беше получена. + +Можете да я редактирате по всяко време от този адрес: + +<%= edit_volunteer_url(@volunteer.unique_id, host: @volunteer.conference.host_name, protocol: 'https') %> diff --git a/app/views/volunteer_mailer/volunteer_notification.en.text.erb b/app/views/volunteer_mailer/volunteer_notification.en.text.erb new file mode 100644 index 0000000..525e68e --- /dev/null +++ b/app/views/volunteer_mailer/volunteer_notification.en.text.erb @@ -0,0 +1,7 @@ +Hello, + +Your application for volunteership was received. + +You can edit it at any time on this address: + +<%= edit_volunteer_url(@volunteer.unique_id, host: @volunteer.conference.host_name, protocol: 'https') %> diff --git a/config/locales/bg.yml b/config/locales/bg.yml index b93c98e..195710b 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -5,6 +5,9 @@ bg: event: create: "Изпрати предложение" update: "Обнови предложението" + volunteer: + create: "Изпрати кандидатура" + update: "Обнови кандидатурата" actions: are_you_sure: "Сигурен ли си?" create: @@ -61,6 +64,16 @@ bg: picture: "Снимка" public_email: "Публичен e-mail" twitter: Twitter акаунт + volunteer: + picture: Снимка + name: Име + email: E-mail + phone: Телефон + tshirt_size: Размер тениска + tshirt_cut: Кройка на тениска + food_preferences: Предпочитана храна + previous_experience: Предишен опит + notes: Бележки track: color: "Цвят" description: "Описание" @@ -96,6 +109,10 @@ bg: invalid: "не е валиден e-mail адрес" password_confirmation: confirmation: "не съответства на паролата" + volunteer: + attributes: + picture: + invalid_content_type: "невалиден формат на снимката" models: conference: one: "конференция" @@ -130,7 +147,7 @@ bg: user: one: "Потребител" other: "Потребители" - volunteership: + volunteer: one: "доброволец" other: "доброволци" workshop: @@ -160,6 +177,9 @@ bg: event_mailer: acceptance_notification: subject: "Предложението Ви за провеждане на %{submission_type} „%{title}“ на %{conference} е одобрено" + volunteer_mailer: + success_notification: + subject: "Кандидатурата Ви за доброволец за %{conference_name} беше получена" event_states: approved: one: "Одобрено" @@ -233,6 +253,22 @@ bg: simple_form: error_notification: default_message: "Моля, разгледайте посочените грешки във формуляра:" + options: + volunteer: + tshirt_size: + s: S + m: M + l: L + xl: XL + xxl: XXL + xxxl: XXXL + tshirt_cut: + unisex: Унисекс + female: Дамска + food_preferences: + none: Нищо специфично + vegetarian: Вегетарианец + vegan: Веган hints: conference: description: "Описание на конференцията" @@ -262,6 +298,11 @@ bg: picture: "Ваша снимка" public_email: E-mail адрес, който ще бъде видим за посетителите twitter: "Потребителското Ви име в Twitter" + volunteer: + email: "Е-mail адресът Ви, който ще бъде видим само от организаторите" + phone: "Мобилният Ви телефон, който ще бъде видим само за организаторите" + picture: "Ваша снимка в jpeg, png или gif формат" + volunteer_teams: "Доброволческите екипи, от които искате да сте част. Подробни описания на екипите можете да намерите тук" user: email: e-mail адресът Ви. Ще бъде видим само от организаторите password: "Парола с дължина между 8 и 128 символа" @@ -302,12 +343,15 @@ bg: info: "Информация за потребител" welcome: submit_event: "Предложи %{event_type}" - volunteerships: - apply: Кандидатствай + volunteers: + new_volunteer_title: Кандидатствай за доброволец + edit_volunteer_title: "Кандидатура за доброволец на %{name}" + apply: Кандидатствай за доброволец withdraw_application: Оттегли кандидатурата си - you_successfully_applied_for: "Успешно изпратихте кандидатура за екип „%{team}“" + successful_application: "Успешно изпратихте кандидатурата си" + successful_application_edit: "Успешно редактирахте кандидатурата си" error_occurred_while_applying: "Възникна грешка и кандидатурата Ви не беше изпратена" - you_successfully_retracted_your_application_for: "Успешно оттеглихте кандидатурата си за екип „%{team}“" + you_successfully_retracted_your_application_for: "Успешно оттеглихте кандидатурата си“" welcome: "Добре дошли, %{name}" what_we_ask: "Бихме искали да получим предложенията Ви за лекции и уъркшопи, принадлежащи към следните категории до 4 октомври 2015г.:" workshop_was_successfully_confirmed: "Уъркшопът беше потвърден успешно" diff --git a/config/routes.rb b/config/routes.rb index 2262ce7..2464b72 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,6 +9,7 @@ Rails.application.routes.draw do get :confirm end end + resources :volunteers resources :volunteerships, only: [:index, :destroy] resources :volunteer_teams, only: [] do resource :volunteership, only: :create diff --git a/db/migrate/20151021162134_create_volunteers.rb b/db/migrate/20151021162134_create_volunteers.rb new file mode 100644 index 0000000..643e5b7 --- /dev/null +++ b/db/migrate/20151021162134_create_volunteers.rb @@ -0,0 +1,20 @@ +class CreateVolunteers < ActiveRecord::Migration + def change + create_table :volunteers do |t| + t.string :name + t.string :picture_id + t.string :email + t.string :unique_id + t.string :phone + t.string :tshirt_size + t.string :tshirt_cut + t.string :food_preferences + t.text :previous_experience + t.text :notes + + t.timestamps null: false + end + + add_index :volunteers, :unique_id, unique: true + end +end diff --git a/db/migrate/20151021183039_create_join_table_volunteer_volunteer_team.rb b/db/migrate/20151021183039_create_join_table_volunteer_volunteer_team.rb new file mode 100644 index 0000000..ac8448e --- /dev/null +++ b/db/migrate/20151021183039_create_join_table_volunteer_volunteer_team.rb @@ -0,0 +1,8 @@ +class CreateJoinTableVolunteerVolunteerTeam < ActiveRecord::Migration + def change + create_join_table :volunteers, :volunteer_teams do |t| + t.index [:volunteer_id, :volunteer_team_id], name: 'volunteer_id_volunteer_team_id' + t.index [:volunteer_team_id, :volunteer_id], name: 'volunteer_team_id_volunteer_id' + end + end +end diff --git a/db/migrate/20151021185100_add_conference_id_to_volunteers.rb b/db/migrate/20151021185100_add_conference_id_to_volunteers.rb new file mode 100644 index 0000000..c07da37 --- /dev/null +++ b/db/migrate/20151021185100_add_conference_id_to_volunteers.rb @@ -0,0 +1,5 @@ +class AddConferenceIdToVolunteers < ActiveRecord::Migration + def change + add_reference :volunteers, :conference, index: true, foreign_key: true + end +end diff --git a/db/migrate/20151021190854_add_language_to_volunteers.rb b/db/migrate/20151021190854_add_language_to_volunteers.rb new file mode 100644 index 0000000..2e2036a --- /dev/null +++ b/db/migrate/20151021190854_add_language_to_volunteers.rb @@ -0,0 +1,5 @@ +class AddLanguageToVolunteers < ActiveRecord::Migration + def change + add_column :volunteers, :language, :string + end +end diff --git a/lib/initfest/assets/stylesheets/initfest/application.scss b/lib/initfest/assets/stylesheets/initfest/application.scss index 41d685a..6ad27c7 100644 --- a/lib/initfest/assets/stylesheets/initfest/application.scss +++ b/lib/initfest/assets/stylesheets/initfest/application.scss @@ -36,15 +36,49 @@ footer { font-size: 1.7em; } -@media all and (max-width: 600px) { - .input label, .input input, .input span, .input .hint, .help-block, .input .error { - float: none; - display: block; - margin: 0; - } - -} - strong { font-weight: 600; -} \ No newline at end of file +} + +span.radio { + label { + width: auto; + } +} + +span.radio + span.radio { + label { + padding-left: 15px; + } +} + +label.check_boxes { + height: 3em; +} + +@media all and (max-width: 600px) { + .input label, .input input, .input span, .input .hint, .help-block, .input .error { + float: none; + display: block; + margin: 0; + } + + span.radio, span.checkbox { + padding-top: 0.4em; + + label { + display: inline; + padding-left: 0; + input { + display: inline-block; + margin-right: 0.5em; + } + } + } + + span.radio + span.radio { + label { + padding-left: 0; + } + } +} diff --git a/lib/initfest/views/public/volunteers/_form.slim b/lib/initfest/views/public/volunteers/_form.slim new file mode 100644 index 0000000..8cdf55e --- /dev/null +++ b/lib/initfest/views/public/volunteers/_form.slim @@ -0,0 +1,21 @@ +- form_url = @volunteer.new_record? ? volunteers_path : volunteer_path(@volunteer.unique_id) + += simple_form_for @volunteer, wrapper: :default, url: form_url do |f| + = f.error_notification + + .form-inputs + .input + = attachment_image_tag(@volunteer, :picture, :fill, 150, 150) if @volunteer.picture.present? + = f.input :picture, as: :attachment, direct: true, wrapper: false + = f.input :name, autofocus: true + = f.input :email + = f.input :phone, input_html: {value: @volunteer.phone.try(:phony_formatted, format: :international)} + = f.association :volunteer_teams, as: :check_boxes, wrapper: :default + = f.input :language, as: :radio_buttons, collection: locale_collection, include_blank: false, wrapper: :default, checked: (@volunteer.language.presence || I18n.locale) + = f.input :tshirt_size, collection: Volunteer::TSHIRT_SIZES, as: :radio_buttons, wrapper: :default, checked: (@volunteer.tshirt_size.presence || :m) + = f.input :tshirt_cut, collection: Volunteer::TSHIRT_CUTS, as: :radio_buttons, wrapper: :default, checked: (@volunteer.tshirt_cut.presence || :unisex) + = f.input :food_preferences, collection: Volunteer::FOOD_PREFERENCES, as: :radio_buttons, wrapper: :default, checked: (@volunteer.food_preferences.presence || :none) + = f.input :previous_experience + = f.input :notes + .form-actions + = f.button :submit diff --git a/lib/initfest/views/public/volunteers/edit.slim b/lib/initfest/views/public/volunteers/edit.slim new file mode 100644 index 0000000..c6701b0 --- /dev/null +++ b/lib/initfest/views/public/volunteers/edit.slim @@ -0,0 +1,5 @@ +- content_for(:title) { t 'views.volunteers.edit_volunteer_title', name: @volunteer.name } + +h1 = t 'views.volunteers.edit_volunteer_title', name: @volunteer.name + += render 'form' diff --git a/lib/initfest/views/public/volunteers/new.slim b/lib/initfest/views/public/volunteers/new.slim new file mode 100644 index 0000000..b316b88 --- /dev/null +++ b/lib/initfest/views/public/volunteers/new.slim @@ -0,0 +1,5 @@ +- content_for(:title) { t 'views.volunteers.new_volunteer_title' } + +h1 = t 'views.volunteers.new_volunteer_title' + += render 'form'