diff --git a/Gemfile b/Gemfile index 7f95bac..c4a403d 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,7 @@ gem 'rails-i18n' gem 'devise' gem 'devise-i18n' -gem 'simple_form' +gem 'simple_form', '~> 3.1.0.rc2' gem 'simple_form_fancy_uploads', github: 'apeacox/simple_form_fancy_uploads' # Phone validation @@ -45,6 +45,8 @@ gem 'bootswatch-rails' gem 'autoprefixer-rails' gem 'font-awesome-rails' +gem 'nested_form' + group :development do gem 'spring' gem 'spring-commands-rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 97d995f..a86b83f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ GIT remote: git://github.com/apeacox/simple_form_fancy_uploads.git - revision: a135aad8f17cbf219a30ce5a30709406e277f7e0 + revision: bc13fcd62143c1ff4f0ac8e4621c11415b6be6bf specs: - simple_form_fancy_uploads (0.1.0) + simple_form_fancy_uploads (0.1.1) carrierwave rails (>= 4.0.0) simple_form (~> 3.0) @@ -44,7 +44,7 @@ GEM thread_safe (~> 0.1) tzinfo (~> 1.1) arel (5.0.1.20140414130214) - autoprefixer-rails (3.1.0.20140911) + autoprefixer-rails (3.1.1.20141001) execjs bcrypt (3.1.7) bootstrap-sass (3.2.0.2) @@ -64,10 +64,10 @@ GEM capistrano-rails (1.1.2) capistrano (~> 3.1) capistrano-bundler (~> 1.1) - capistrano-rvm (0.1.1) + capistrano-rvm (0.1.2) capistrano (~> 3.0) sshkit (~> 1.2) - capybara (2.4.1) + capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -82,7 +82,7 @@ GEM timers (~> 4.0.0) choice (0.1.6) coderay (1.1.0) - coffee-rails (4.0.1) + coffee-rails (4.1.0) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) coffee-script (2.3.0) @@ -93,13 +93,14 @@ GEM countries (0.9.3) currencies (~> 0.4.2) currencies (0.4.2) - devise (3.3.0) + devise (3.4.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) + responders thread_safe (~> 0.1) warden (~> 1.2.3) - devise-i18n (0.11.1) + devise-i18n (0.11.2) diff-lcs (1.2.5) erubis (2.7.0) execjs (2.2.1) @@ -110,7 +111,7 @@ GEM railties (>= 3.0.0) faker (1.4.3) i18n (~> 0.5) - ffi (1.9.3) + ffi (1.9.6) font-awesome-rails (4.2.0.0) railties (>= 3.2, < 5.0) formatador (0.2.5) @@ -134,7 +135,7 @@ GEM thor (>= 0.14, < 2.0) json (1.8.1) libv8 (3.16.14.7) - listen (2.7.9) + listen (2.7.11) celluloid (>= 0.15.2) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) @@ -142,10 +143,11 @@ GEM mail (2.6.1) mime-types (>= 1.16, < 3) method_source (0.8.2) - mime-types (2.3) + mime-types (2.4.1) mini_portile (0.6.0) - minitest (5.4.1) + minitest (5.4.2) multi_json (1.10.1) + nested_form (0.3.2) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (2.9.1) @@ -153,11 +155,11 @@ GEM mini_portile (= 0.6.0) orm_adapter (0.5.0) pg (0.17.1) - phony (2.3.0) - phony_rails (0.6.2) + phony (2.4.3) + phony_rails (0.7.1) activesupport (>= 3.0) countries (>= 0.8.2) - phony (~> 2.1) + phony (~> 2.4.3) pry (0.10.1) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -195,17 +197,19 @@ GEM rb-inotify (0.9.5) ffi (>= 0.5.0) ref (1.0.5) + responders (1.1.1) + railties (>= 3.2, < 4.2) rmagick (2.13.3) rspec (3.1.0) rspec-core (~> 3.1.0) rspec-expectations (~> 3.1.0) rspec-mocks (~> 3.1.0) - rspec-core (3.1.4) + rspec-core (3.1.7) rspec-support (~> 3.1.0) - rspec-expectations (3.1.1) + rspec-expectations (3.1.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.1.0) - rspec-mocks (3.1.1) + rspec-mocks (3.1.3) rspec-support (~> 3.1.0) rspec-rails (3.1.0) actionpack (>= 3.0) @@ -215,7 +219,7 @@ GEM rspec-expectations (~> 3.1.0) rspec-mocks (~> 3.1.0) rspec-support (~> 3.1.0) - rspec-support (3.1.0) + rspec-support (3.1.2) ruby-graphviz (1.0.9) sass (3.2.19) sass-rails (4.0.3) @@ -223,7 +227,7 @@ GEM sass (~> 3.2.0) sprockets (~> 2.8, <= 2.11.0) sprockets-rails (~> 2.0) - simple_form (3.0.2) + simple_form (3.1.0.rc2) actionpack (~> 4.0) activemodel (~> 4.0) slim (2.0.3) @@ -243,10 +247,10 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.4) + sprockets-rails (2.2.0) actionpack (>= 3.0) activesupport (>= 3.0) - sprockets (~> 2.8) + sprockets (>= 2.8, < 4.0) sqlite3 (1.3.9) sshkit (1.5.1) colorize @@ -293,6 +297,7 @@ DEPENDENCIES globalize (~> 4.0.2) guard-rspec jquery-rails + nested_form pg phony_rails puma @@ -302,7 +307,7 @@ DEPENDENCIES rmagick rspec-rails sass-rails (~> 4.0.3) - simple_form + simple_form (~> 3.1.0.rc2) simple_form_fancy_uploads! slim-rails spring diff --git a/app/assets/javascripts/management/application.js b/app/assets/javascripts/management/application.js index 0400cd5..9119aa9 100644 --- a/app/assets/javascripts/management/application.js +++ b/app/assets/javascripts/management/application.js @@ -1,5 +1,6 @@ //= require jquery //= require jquery_ujs +//= require jquery_nested_form //= require bootstrap-sprockets //= require metisMenu/metisMenu //= require_directory . diff --git a/app/assets/stylesheets/management/_forms.css.scss b/app/assets/stylesheets/management/_forms.css.scss new file mode 100644 index 0000000..644ddd7 --- /dev/null +++ b/app/assets/stylesheets/management/_forms.css.scss @@ -0,0 +1,10 @@ +select.date, select.datetime, select.time { + display: inline; + width: auto; +} + +@media(min-width:768px) { + form .text { + height: 180px; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/management/application.css.scss b/app/assets/stylesheets/management/application.css.scss index 5905962..9997ddd 100644 --- a/app/assets/stylesheets/management/application.css.scss +++ b/app/assets/stylesheets/management/application.css.scss @@ -8,3 +8,4 @@ @import "layout"; @import "navigation"; +@import "forms"; \ No newline at end of file diff --git a/app/controllers/management/conferences_controller.rb b/app/controllers/management/conferences_controller.rb new file mode 100644 index 0000000..76b9c16 --- /dev/null +++ b/app/controllers/management/conferences_controller.rb @@ -0,0 +1,29 @@ +module Management + class ConferencesController < ManagementController + before_action :assign_conference, only: [:edit] + + def new + @conference = Conference.new + 3.times { @conference.tracks.build } + end + + def create + @conference = Conference.new conference_params + @conference.save + render :new + end + + def edit + end + + private + + def assign_conference + @conference = Conference.find params[:id] + end + + def conference_params + params.require(:conference).permit [:title, :email, :start_date, :end_date, :description, tracks_attributes: [:id, :name, :color, :description, :_destroy]] + end + end +end diff --git a/app/inputs/color_input.rb b/app/inputs/color_input.rb new file mode 100644 index 0000000..f46de10 --- /dev/null +++ b/app/inputs/color_input.rb @@ -0,0 +1,6 @@ +class ColorInput < SimpleForm::Inputs::Base + def input(wrapper_options = nil) + merged_input_options = merge_wrapper_options(input_html_options, wrapper_options) + @builder.color_field(attribute_name, merged_input_options) + end +end diff --git a/app/models/conference.rb b/app/models/conference.rb index 224a408..273f0d4 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -12,6 +12,8 @@ class Conference < ActiveRecord::Base has_many :events, through: :tracks has_many :candidate_speakers, through: :events + accepts_nested_attributes_for :tracks + scope :future, -> { where('start_date >= ?', Date.today).order('start_date ASC') } def self.current diff --git a/app/models/track.rb b/app/models/track.rb index 80b8be3..f33bb85 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -3,11 +3,16 @@ class Track < ActiveRecord::Base has_many :events validates :name, presence: true - validates :color, presence: true, format: {with: /\A[a-f0-9]{6}\z/i} + validates :color, presence: true, format: {with: /\A#?[a-f0-9]{6}\z/i} + validates :description, presence: true translates :name, :description def color=(hex_triplet) write_attribute :color, hex_triplet.gsub(/\A#/,'') if hex_triplet end + + def color + "##{read_attribute :color}" + end end diff --git a/app/views/management/conferences/_form.html.slim b/app/views/management/conferences/_form.html.slim new file mode 100644 index 0000000..86490bf --- /dev/null +++ b/app/views/management/conferences/_form.html.slim @@ -0,0 +1,20 @@ += simple_nested_form_for [:management, @conference], wrapper: :horizontal_form, html: { class: 'form-horizontal' } do |f| + .panel.panel-primary + .panel-heading + h1.panel-title = t 'views.conference.info' + .panel-body + .row + .col-lg-12 + = f.input :title + = f.input :email + = f.input :start_date, as: :date + = f.input :end_date, as: :date + = f.input :description + + .row + .col-lg-12 + h2 = Track.model_name.human(count: 2).mb_chars.capitalize + .row + = render partial: 'form_tracks', locals: {form: f} + .panel-footer.text-right + = f.submit class: 'btn btn-primary' diff --git a/app/views/management/conferences/_form_tracks.html.slim b/app/views/management/conferences/_form_tracks.html.slim new file mode 100644 index 0000000..0edc605 --- /dev/null +++ b/app/views/management/conferences/_form_tracks.html.slim @@ -0,0 +1,14 @@ +div#tracks + = form.simple_fields_for :tracks do |ff| + .col-lg-6 + .panel.panel-default + .panel-body + = ff.input :name + = ff.input :color, as: :color + = ff.input :description + .panel-footer.text-right + = ff.link_to_remove t('helpers.destroy', model: Track.model_name.human), class: ['btn', 'btn-danger', ff.object.events.any? ? 'disabled' : nil] +.col-lg-6 + .panel.panel-default + .panel-body.text-right + = form.link_to_add t('helpers.submit.create', model: Track.model_name.human), :tracks, data: {target: '#tracks'}, class: 'btn btn-success' diff --git a/app/views/management/conferences/edit.html.slim b/app/views/management/conferences/edit.html.slim new file mode 100644 index 0000000..85298ef --- /dev/null +++ b/app/views/management/conferences/edit.html.slim @@ -0,0 +1,3 @@ +.row + .col-lg-12 + = render partial: 'form' diff --git a/app/views/management/conferences/new.html.slim b/app/views/management/conferences/new.html.slim new file mode 100644 index 0000000..85298ef --- /dev/null +++ b/app/views/management/conferences/new.html.slim @@ -0,0 +1,3 @@ +.row + .col-lg-12 + = render partial: 'form' diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 6986de9..fac7026 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -22,9 +22,9 @@ SimpleForm.setup do |config| b.use :placeholder ## Optional extensions - # They are disabled unless you pass `f.input EXTENSION_NAME => :lookup` + # They are disabled unless you pass `f.input EXTENSION_NAME => true` # to the input. If so, they will retrieve the values from the model - # if any exists. If you want to enable the lookup for any of those + # if any exists. If you want to enable any of those # extensions by default, you can change `b.optional` to `b.use`. # Calculates maxlength from length validations for string inputs @@ -43,6 +43,12 @@ SimpleForm.setup do |config| b.use :label_input b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :error, wrap_with: { tag: :span, class: :error } + + ## full_messages_for + # If you want to display the full error message for the attribute, you can + # use the component :full_error, like: + # + # b.use :full_error, wrap_with: { tag: :span, class: :error } end # The default wrapper to be used by the FormBuilder. @@ -66,7 +72,7 @@ SimpleForm.setup do |config| config.error_notification_tag = :div # CSS class to add for error notification helper. - config.error_notification_class = 'alert alert-error' + config.error_notification_class = 'error_notification' # ID to add for error notification helper. # config.error_notification_id = nil @@ -92,10 +98,10 @@ SimpleForm.setup do |config| # config.item_wrapper_class = nil # How the label text should be generated altogether with the required text. - # config.label_text = lambda { |label, required| "#{required} #{label}" } + # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } # You can define the class to use on all labels. Default is nil. - config.label_class = 'control-label' + # config.label_class = nil # You can define the class to use on all forms. Default is simple_form. # config.form_class = :simple_form @@ -125,6 +131,10 @@ SimpleForm.setup do |config| # type as key and the wrapper that will be used for all inputs with specified type. # config.wrapper_mappings = { string: :prepend } + # Namespaces where SimpleForm should look for custom input classes that + # override default inputs. + # config.custom_inputs_namespaces << "CustomInputs" + # Default priority for time_zone inputs. # config.time_zone_priority = nil @@ -142,4 +152,14 @@ SimpleForm.setup do |config| # Default class for inputs # config.input_class = nil + + # Define the default class of the input wrapper of the boolean input. + config.boolean_label_class = 'checkbox' + + # Defines if the default input wrapper class should be included in radio + # collection wrappers. + # config.include_default_input_wrapper_class = true + + # Defines which i18n scope will be used in Simple Form. + # config.i18n_scope = 'simple_form' end diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb new file mode 100644 index 0000000..d1cb6e2 --- /dev/null +++ b/config/initializers/simple_form_bootstrap.rb @@ -0,0 +1,136 @@ +# Use this setup block to configure all options available in SimpleForm. +SimpleForm.setup do |config| + config.error_notification_class = 'alert alert-danger' + config.button_class = 'btn btn-default' + config.boolean_label_class = nil + + config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.use :placeholder + b.optional :maxlength + b.optional :pattern + b.optional :min_max + b.optional :readonly + b.use :label, class: 'control-label' + + b.use :input, class: 'form-control' + b.use :error, wrap_with: { tag: 'span', class: 'help-block' } + b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + + config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.use :placeholder + b.optional :maxlength + b.optional :readonly + b.use :label, class: 'control-label' + + b.use :input + b.use :error, wrap_with: { tag: 'span', class: 'help-block' } + b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + + config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.optional :readonly + + b.wrapper tag: 'div', class: 'checkbox' do |ba| + ba.use :label_input + end + + b.use :error, wrap_with: { tag: 'span', class: 'help-block' } + b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + + config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.optional :readonly + b.use :label_input + b.use :error, wrap_with: { tag: 'span', class: 'help-block' } + b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + + config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.use :placeholder + b.optional :maxlength + b.optional :pattern + b.optional :min_max + b.optional :readonly + b.use :label, class: 'col-sm-3 control-label' + + b.wrapper tag: 'div', class: 'col-sm-9' do |ba| + ba.use :input, class: 'form-control' + ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } + ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + end + + config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.use :placeholder + b.optional :maxlength + b.optional :readonly + b.use :label, class: 'col-sm-3 control-label' + + b.wrapper tag: 'div', class: 'col-sm-9' do |ba| + ba.use :input + ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } + ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + end + + config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.optional :readonly + + b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr| + wr.wrapper tag: 'div', class: 'checkbox' do |ba| + ba.use :label_input, class: 'col-sm-9' + end + + wr.use :error, wrap_with: { tag: 'span', class: 'help-block' } + wr.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + end + + config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.optional :readonly + + b.use :label, class: 'col-sm-3 control-label' + + b.wrapper tag: 'div', class: 'col-sm-9' do |ba| + ba.use :input + ba.use :error, wrap_with: { tag: 'span', class: 'help-block' } + ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + end + + config.wrappers :horizontal_submit, tag: 'div', class: 'form-group' do |b| + b.optional :readonly + b.wrapper tag: 'div', class: 'col-sm-offset-2 col-sm-10' do |ba| + ba.use :input + end + end + + config.wrappers :inline_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b| + b.use :html5 + b.use :placeholder + b.optional :maxlength + b.optional :pattern + b.optional :min_max + b.optional :readonly + b.use :label, class: 'sr-only' + + b.use :input, class: 'form-control' + b.use :error, wrap_with: { tag: 'span', class: 'help-block' } + b.use :hint, wrap_with: { tag: 'p', class: 'help-block' } + end + + # Wrappers for forms and inputs using the Bootstrap toolkit. + # Check the Bootstrap docs (http://getbootstrap.com) + # to learn about the different styles for forms and inputs, + # buttons and other elements. + # config.default_wrapper = :vertical_form +end diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 4dca740..521c0d5 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -20,6 +20,9 @@ # available at http://guides.rubyonrails.org/i18n.html. bg: + helpers: + destroy: Премахни %{model} + create_new: Създаване на %{model_name} event_mailer: acceptance_notification: subject: 'Предложението ви за %{conference} за %{submission_type} „%{title}“ е одобрено' @@ -37,8 +40,19 @@ bg: event: one: Предложение other: Предложения - track: Поток от лекции + track: + one: поток от лекции + other: потоци от лекции + conference: + one: конференция + other: конференции attributes: + conference: + email: E-mail + title: Заглавие + start_date: Начална дата + end_date: Крайна дата + description: Описание user: email: E-mail current_password: Текуща парола diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index 0fb11d6..2c62fc9 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -8,6 +8,16 @@ bg: error_notification: default_message: 'Моля, разгледайте посочените грешки във формуляра:' hints: + conference: + title: Заглавието на конференцията + start_date: Денят, в който конференцията започва + end_date: Денят, в който конференцията приключва + description: Описание на конференцията + email: Email на организаторския екип + tracks: + name: Име на потокът + description: Кратко описание + color: Цвят user: email: e-mail адресът Ви. Ще бъде видим само от организаторите password: Парола с дължина между 8 и 128 символа diff --git a/config/locales/views.bg.yml b/config/locales/views.bg.yml index d60d599..5d687fc 100644 --- a/config/locales/views.bg.yml +++ b/config/locales/views.bg.yml @@ -71,4 +71,7 @@ bg: suggestion_and_speaker_count: "%{suggestions} предложения от %{speakers} лектори" lecture_was_successfully_confirmed: "Лекцията беше потвърдена успешно" - workshop_was_successfully_confirmed: "Уъркшопът беше потвърден успешно" \ No newline at end of file + workshop_was_successfully_confirmed: "Уъркшопът беше потвърден успешно" + views: + conference: + info: Информация за конференция \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 636156f..b5ed0e8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,6 +16,12 @@ Rails.application.routes.draw do namespace :management do get '/', to: 'events#index' + resources :conferences do + resources :events do + patch 'state' + end + end + resources :users do member do post 'toggle_admin'