diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index b42ccb06..52d5f5ca 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -11,7 +11,6 @@ linters: # Offense count: 945 LineLength: exclude: - - "app/views/admin/booths/_form.html.haml" - "app/views/admin/booths/index.html.haml" - "app/views/admin/booths/show.html.haml" - "app/views/admin/campaigns/_form.html.haml" @@ -109,6 +108,9 @@ linters: - "app/views/admin/versions/_object_desc_and_link.html.haml" - "app/views/admin/versions/index.html.haml" - "app/views/admin/volunteers/index.html.haml" + - "app/views/booths/_form.html.haml" + - "app/views/booths/index.html.haml" + - "app/views/booths/show.html.haml" - "app/views/commercials/edit.html.haml" - "app/views/commercials/new.html.haml" - "app/views/conference_registrations/_form.html.haml" @@ -369,6 +371,7 @@ linters: - "app/views/admin/tracks/show.html.haml" - "app/views/admin/venues/show.html.haml" - "app/views/admin/versions/index.html.haml" + - "app/views/booths/index.html.haml" - "app/views/conference_registrations/_form.html.haml" - "app/views/conference_registrations/_ticket.html.haml" - "app/views/conference_registrations/show.html.haml" @@ -397,6 +400,7 @@ linters: - "app/views/admin/schedules/_day_tab.html.haml" - "app/views/admin/schedules/_event.html.haml" - "app/views/admin/versions/_object_desc_and_link.html.haml" + - "app/views/booths/index.html.haml" - "app/views/schedules/_carousel.html.haml" # Offense count: 29 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 7a9ab03c..8d752446 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -333,6 +333,8 @@ Metrics/MethodLength: # Configuration parameters: CountComments. Metrics/ModuleLength: Max: 159 + Exclude: + - 'app/helpers/format_helper.rb' # Offense count: 15 Metrics/PerceivedComplexity: diff --git a/app/controllers/admin/booths_controller.rb b/app/controllers/admin/booths_controller.rb index f5096c34..e5b55552 100644 --- a/app/controllers/admin/booths_controller.rb +++ b/app/controllers/admin/booths_controller.rb @@ -7,9 +7,13 @@ module Admin def show; end - def new; end + def new + @url = admin_conference_booths_path(@conference.short_title) + end def create + @url = admin_conference_booths_path(@conference.short_title) + @booth = @conference.booths.new(booth_params) @booth.submitter = current_user @@ -23,9 +27,13 @@ module Admin end end - def edit; end + def edit + @url = admin_conference_booth_path(@conference.short_title, @booth.id) + end def update + @url = admin_conference_booth_path(@conference.short_title, @booth.id) + @booth.update_attributes(booth_params) if @booth.save @@ -38,16 +46,6 @@ module Admin end end - def destroy - if @booth.destroy - redirect_to admin_conference_booths_path, - notice: 'Booth successfully destroyed.' - else - redirect_to admin_conference_booths_path, - error: "Booth couldn't be deleted. #{@booth.errors.full_messages.join('. ')}." - end - end - def accept update_state(:accept, 'Booth accepted!') end diff --git a/app/controllers/booths_controller.rb b/app/controllers/booths_controller.rb new file mode 100644 index 00000000..99fde3bc --- /dev/null +++ b/app/controllers/booths_controller.rb @@ -0,0 +1,97 @@ +class BoothsController < ApplicationController + before_action :authenticate_user! + load_resource :conference, find_by: :short_title + load_and_authorize_resource through: :conference + skip_authorize_resource only: [:withdraw, :confirm, :restart] + + def index + @booths = current_user.booths.where(conference_id: @conference.id).uniq + end + + def show; end + + def new + @url = conference_booths_path(@conference.short_title) + end + + def create + @url = conference_booths_path(@conference.short_title) + + @booth.submitter = current_user + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully created.' + else + flash[:error] = "Creating booth failed. #{@booth.errors.full_messages.to_sentence}." + render :new + end + end + + def edit + @url = conference_booth_path(@conference.short_title, @booth.id) + end + + def update + @url = conference_booth_path(@conference.short_title, @booth.id) + @booth.update_attributes(booth_params) + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully updated!' + else + flash[:error] = "Booth could not be updated. #{@booth.errors.full_messages.to_sentence}." + end + end + + def destroy; end + + def withdraw + authorize! :update, @booth + @url = conference_booth_path(@conference.short_title, @booth.id) + + @booth.withdraw! + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully withdrawn' + else + flash[:error] = "Booth could not be withdrawn. #{@booth.errors.full_messages.to_sentence}." + end + end + + def confirm + authorize! :update, @booth + @url = conference_booth_path(@conference.short_title, @booth.id) + + @booth.confirm! + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully confirmed' + else + flash[:error] = "Booth could not be confirmed. #{@booth.errors.full_messages.to_sentence}." + end + end + + def restart + authorize! :update, @booth + @url = conference_booth_path(@conference.short_title, @booth.id) + + @booth.restart! + + if @booth.save + redirect_to conference_booths_path, + notice: 'Booth successfully re-submitted' + else + flash[:error] = "Booth could not be re-submitted. #{@booth.errors.full_messages.to_sentence}." + end + end + + private + + def booth_params + params.require(:booth).permit(:title, :description, :reasoning, :state, :picture, :conference_id, + :created_at, :updated_at, :submitter_relationship, :website_url, responsible_ids: []) + end +end diff --git a/app/helpers/format_helper.rb b/app/helpers/format_helper.rb index 334c70bf..65578ba6 100644 --- a/app/helpers/format_helper.rb +++ b/app/helpers/format_helper.rb @@ -15,6 +15,19 @@ module FormatHelper end end + def booth_status_icon(booth) + case booth.state + when 'new', 'to_reject', 'to_accept' + 'fa-eye' + when 'accepted' + 'fa-check text-muted' + when 'confirmed' + 'fa-check text-success' + when 'rejected', 'withdrawn', 'canceled' + 'fa-ban' + end + end + def event_progress_color(progress) progress = progress.to_i if progress == 100 diff --git a/app/models/ability.rb b/app/models/ability.rb index afc51a68..0ebabfdc 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -75,6 +75,12 @@ class Ability can [:new, :create], Payment, user_id: user.id can [:index, :show], PhysicalTicket, user: user + can [:new, :create], Booth + + can [:edit, :update, :index, :show], Booth do |booth| + booth.users.include?(user) + end + can [:create, :destroy], Subscription, user_id: user.id can [:new, :create], Event do |event| diff --git a/app/models/booth.rb b/app/models/booth.rb index 5c20550a..853bca9e 100644 --- a/app/models/booth.rb +++ b/app/models/booth.rb @@ -35,12 +35,16 @@ class Booth < ActiveRecord::Base state :to_reject state :rejected state :canceled + state :confirmed event :restart do - transitions to: :new, from: [:withdrawn, :to_accept, :to_reject, :canceled] + transitions to: :new, from: [:withdrawn, :rejected, :canceled] end event :withdraw do - transitions to: :withdrawn, from: [:new, :to_accept, :accepted, :to_reject, :rejected] + transitions to: :withdrawn, from: [:new, :to_accept, :accepted, :to_reject, :rejected, :confirmed] + end + event :confirm do + transitions to: :confirmed, from: [:accepted] end event :to_accept do transitions to: :to_accept, from: [:new, :to_reject] @@ -55,7 +59,7 @@ class Booth < ActiveRecord::Base transitions to: :rejected, from: [:new, :to_reject] end event :cancel do - transitions to: :canceled, from: [:accepted, :rejected, :to_accept, :to_reject] + transitions to: :canceled, from: [:accepted, :rejected, :to_accept, :to_reject, :confirmed] end end diff --git a/app/views/admin/booths/_form.html.haml b/app/views/admin/booths/_form.html.haml deleted file mode 100644 index f70079fa..00000000 --- a/app/views/admin/booths/_form.html.haml +++ /dev/null @@ -1,33 +0,0 @@ -.row - .col-md-12 - .page-header - %title Request a Booth -.row - .col-md-8 - = semantic_form_for(@booth, url: @booth.new_record? ? admin_conference_booths_path(@conference.short_title) : admin_conference_booth_path(@conference.short_title, @booth.id), html: { multipart: true }) do |f| - = f.input :title, as: :string, autofocus: true, required: true - = f.input :description, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, - hint: 'This field becomes public upon request acceptance' - = f.input :reasoning, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, - label: 'How it fits the conference' - = f.input :submitter_relationship, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, - label: 'Submitter\'s relation', - hint: 'e.g. employee, comunity manager, etc' - = f.input :website_url - = responsibles_selector_input f - = image_tag f.object.picture.thumb.url if f.object.picture? - = f.input :picture - - %p.text-right - - if @booth.new_record? - = f.submit 'Create Booth Request', class: 'btn btn-success' - - else - = f.submit 'Update Booth Request', class: 'btn btn-success' - -:javascript - $(document).ready(function() { - $('#booth_responsible_ids').selectize({ - plugins: ['remove_button'], - minItems: 2 - } ) - }); diff --git a/app/views/admin/booths/edit.html.haml b/app/views/admin/booths/edit.html.haml index 9f9a5a61..0cac17e7 100644 --- a/app/views/admin/booths/edit.html.haml +++ b/app/views/admin/booths/edit.html.haml @@ -2,4 +2,4 @@ Editing = @booth.title -= render 'form' += render 'booths/form' diff --git a/app/views/admin/booths/index.html.haml b/app/views/admin/booths/index.html.haml index 15833bd7..27f505ad 100644 --- a/app/views/admin/booths/index.html.haml +++ b/app/views/admin/booths/index.html.haml @@ -41,8 +41,9 @@ = link_to booth.submitter.name, admin_user_path(booth.submitter) if booth.submitter %td .responsibles - - booth.responsibles.each do |responsible| + - booth.responsibles.each_with_index do |responsible, i| = link_to responsible.name, admin_user_path(responsible) + = ", " unless i == booth.responsibles.length - 1 %td .btn-group %button{ type: 'button', class: 'btn btn-link dropdown-toggle', 'data-toggle' => 'dropdown' } @@ -51,9 +52,5 @@ %ul.dropdown-menu{ role: 'menu' } = render 'change_state_dropdown', booth: booth %td - .btn-group{ role: "group" } - = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, booth.id), - class: 'btn btn-primary' - = link_to 'Delete', admin_conference_booth_path(@conference.short_title, booth.id), - method: :delete, class: 'btn btn-danger', - data: {confirm: "Do you really want to delete this booth request?"} + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, booth.id), + class: 'btn btn-primary' diff --git a/app/views/admin/booths/new.html.haml b/app/views/admin/booths/new.html.haml index db1c4736..99ec9b20 100644 --- a/app/views/admin/booths/new.html.haml +++ b/app/views/admin/booths/new.html.haml @@ -1,3 +1,3 @@ %h1 New booth -= render 'form' += render 'booths/form' diff --git a/app/views/admin/booths/show.html.haml b/app/views/admin/booths/show.html.haml index 8c692bc4..6ed823e5 100644 --- a/app/views/admin/booths/show.html.haml +++ b/app/views/admin/booths/show.html.haml @@ -39,12 +39,13 @@ %td.col-md-2 %b Responsibles %td - - @booth.responsibles.each do |responsibles| + - @booth.responsibles.each_with_index do |responsibles, i| .responsibles = link_to responsibles.name, admin_user_path(responsibles) ( = responsibles.email ) + = " , " unless i == @booth.responsibles.length - 1 %tr %td.col-md-2 %b Submitted on diff --git a/app/views/booths/_form.html.haml b/app/views/booths/_form.html.haml new file mode 100644 index 00000000..55a51432 --- /dev/null +++ b/app/views/booths/_form.html.haml @@ -0,0 +1,30 @@ +.container + .row + .col-md-8 + = semantic_form_for(@booth, url: @url, html: { multipart: true }) do |f| + = f.input :title, as: :string, autofocus: true, required: true + = f.input :description, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + hint: 'This field becomes public upon request acceptance' + = f.input :reasoning, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + label: 'How it fits the conference' + = f.input :submitter_relationship, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + label: 'Submitter\'s relation', + hint: 'e.g. employee, comunity manager, etc' + = f.input :website_url + = responsibles_selector_input f + = image_tag f.object.picture.thumb.url if f.object.picture? + = f.input :picture + + %p.text-right + - if @booth.new_record? + = f.submit 'Create Booth Request', class: 'btn btn-success' + - else + = f.submit 'Update Booth Request', class: 'btn btn-success' + + :javascript + $(document).ready(function() { + $('#booth_responsible_ids').selectize({ + plugins: ['remove_button'], + minItems: 2 + } ) + }); diff --git a/app/views/booths/edit.html.haml b/app/views/booths/edit.html.haml new file mode 100644 index 00000000..9f60196d --- /dev/null +++ b/app/views/booths/edit.html.haml @@ -0,0 +1,6 @@ +.container + %h1 + Editing + = @booth.title + + = render 'form' diff --git a/app/views/booths/index.html.haml b/app/views/booths/index.html.haml new file mode 100644 index 00000000..d6c392dd --- /dev/null +++ b/app/views/booths/index.html.haml @@ -0,0 +1,51 @@ +.container + .row + .col-md-12.page-header + %h1 + Your booth requests for + = @conference.title + .row + .col-md-12 + .margin-booth-table + %table.table.table-striped.table-hover + %thead + %th + %b State + %th + %b Logo + %th + %b Title + %th + %b Actions + - @booths.each do |booth| + %tr + %td{ style: "padding:20px 8px 20px 8px;" } + - if (booth.state == 'to_accept' || booth.state == 'to_reject') + - show_state = 'new' + - else + - show_state = booth.state + %span{ title: show_state, class: "fa #{booth_status_icon(booth)}" } + %td + - if booth.logo_link + = image_tag(booth.picture.thumb.url, width: '20%') + %td + = link_to booth.title, conference_booth_path(@conference.short_title, booth) + %td + -if can? :edit, booth + = link_to 'Edit', edit_conference_booth_path(@conference.short_title, booth.id), + class: 'btn btn-default' + - if booth.transition_possible? :withdraw + = link_to 'Withdraw', + withdraw_conference_booth_path(@conference.short_title, booth), + method: :patch, class: 'btn btn-mini btn-warning', id: "withdraw_booth_#{booth.id}", + data: { confirm: 'Are you sure you really want to withdraw this request?' } + - if booth.transition_possible? :confirm + = link_to 'Confirm', + confirm_conference_booth_path(@conference.short_title, booth), + method: :patch, class: 'btn btn-mini btn-success', id: "confirm_booth_#{booth.id}" + - if booth.transition_possible? :restart + = link_to 'Re-submit', + restart_conference_booth_path(@conference.short_title, booth), + method: :patch, class: 'btn btn-mini btn-success', id: "restart_booth_#{booth.id}" + .pull-right + = link_to 'Add Booth', new_conference_booth_path(@conference.short_title), class: 'button btn btn-primary' diff --git a/app/views/booths/new.html.haml b/app/views/booths/new.html.haml new file mode 100644 index 00000000..169efd17 --- /dev/null +++ b/app/views/booths/new.html.haml @@ -0,0 +1,4 @@ +.container + %h1 Request a booth + + = render 'form' diff --git a/app/views/booths/show.html.haml b/app/views/booths/show.html.haml new file mode 100644 index 00000000..6b12229d --- /dev/null +++ b/app/views/booths/show.html.haml @@ -0,0 +1,60 @@ +.container + .row + .col-md-12 + %h3 + - if @booth.logo_link + = image_tag(@booth.picture.thumb.url, size: '20%', alt: '') + = @booth.title + .btn-group.pull-right + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' + + .row + .col-md-12 + %table.table + %tr + %td.col-md-2 + %b Description + %td + = markdown(@booth.description) + %tr + %td.col-md-2 + %b Reasoning + %td + = markdown(@booth.reasoning) + %tr + %td.col-md-2 + %b Website + %td + - if @booth.website_url.present? + = link_to @booth.website_url, @booth.website_url + %tr + %td.col-md-2 + %b Submitter + %td + = link_to @booth.submitter.name, user_path(@booth.submitter) + %tr + %td.col-md-2 + %b Submitter's relationship + %td + = @booth.submitter_relationship + %tr + %td.col-md-2 + %b Responsibles + %td + - @booth.responsibles.each_with_index do |responsibles, i| + .responsibles + = link_to responsibles.name, user_path(responsibles) + ( + = responsibles.email + ) + = ", " unless i == @booth.responsibles.length - 1 + %tr + %td.col-md-2 + %b Submitted on + %td + = @booth.created_at + %tr + %td.col-md-2 + %b Last updated on + %td + = @booth.updated_at diff --git a/config/routes.rb b/config/routes.rb index 5a17d314..be507d35 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -41,7 +41,6 @@ Osem::Application.routes.draw do member do patch :accept patch :restart - patch :withdrawn patch :to_accept patch :reject patch :reset @@ -131,6 +130,13 @@ Osem::Application.routes.draw do end resources :organizations, only: [:index] resources :conferences, only: [:index, :show] do + resources :booths do + member do + patch :withdraw + patch :confirm + patch :restart + end + end resource :program, only: [] do resources :proposals, except: :destroy do get 'commercials/render_commercial' => 'commercials#render_commercial' diff --git a/spec/controllers/admin/booths_controller_spec.rb b/spec/controllers/admin/booths_controller_spec.rb index 569862d1..37004531 100644 --- a/spec/controllers/admin/booths_controller_spec.rb +++ b/spec/controllers/admin/booths_controller_spec.rb @@ -126,23 +126,5 @@ describe Admin::BoothsController do end end end - - describe 'DELETE #destroy' do - context 'deletes successfully' do - before { delete :destroy, id: booth.id, conference_id: conference.short_title } - - it 'booth deleted' do - expect(Booth.count).to eq(0) - end - - it 'redirects to admin booth index path' do - expect(response).to redirect_to(admin_conference_booths_path) - end - - it 'show success message' do - expect(flash[:notice]).to match('Booth successfully destroyed.') - end - end - end end end diff --git a/spec/controllers/booths_controller_spec.rb b/spec/controllers/booths_controller_spec.rb new file mode 100644 index 00000000..243bc6e2 --- /dev/null +++ b/spec/controllers/booths_controller_spec.rb @@ -0,0 +1,110 @@ +require 'spec_helper' + +describe BoothsController do + + let(:user) { create(:user) } + let(:conference) { create(:conference) } + let(:booth) { create(:booth, title: 'Title', conference: conference) } + + context 'user is signed in with submitter role' do + before :each do + sign_in booth.submitter + end + + describe 'GET index' do + before { get :index, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booths)).to eq([booth]) + end + + it 'renders index template' do + expect(response).to render_template('index') + end + end + + describe 'GET #new' do + before { get :new, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booth)).to be_a_new(Booth) + end + + it 'renders new template' do + expect(response).to render_template('new') + end + end + + describe 'POST #create' do + context 'successfully created' do + before { post :create, booth: attributes_for(:booth), conference_id: conference.short_title } + + it 'creates a new booth' do + expect(Booth.count).to_not eq(0) + end + + it 'redirects to booth index' do + expect(response).to redirect_to(conference_booths_path) + end + + it 'has responsibles' do + expect(booth.responsibles.count).to_not eq(0) + end + + it 'shows success message' do + expect(flash[:notice]).to match('Booth successfully created.') + end + end + + context 'create action fails' do + before { post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title } + + it 'does not create any record' do + expected = expect do + post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title + end + expected.to_not change(Booth, :count) + end + + it 'redirects to new' do + expect(response).to render_template('new') + end + + it 'shows flash message' do + expect(flash[:error]).to eq("Creating booth failed. Title can't be blank.") + end + end + end + + describe 'GET #edit' do + before { get :edit, id: booth.id, conference_id: conference.short_title } + + it 'renders edit template' do + expect(response).to render_template('edit') + end + + it 'assigns booth variable' do + expect(assigns(:booth)).to eq booth + end + end + + describe 'PATCH #update' do + context 'updates suchessfully' do + before { patch :update, id: booth.id, booth: attributes_for(:booth, title: 'different'), conference_id: conference.short_title } + + it 'redirects to booth index path' do + expect(response).to redirect_to conference_booths_path + end + + it 'shows success message' do + expect(flash[:notice]).to match 'Booth successfully updated!' + end + + it 'updates booth' do + booth.reload + expect(booth.title).to eq('different') + end + end + end + end +end diff --git a/spec/factories/booths.rb b/spec/factories/booths.rb index b1cd1956..1ac3ff87 100644 --- a/spec/factories/booths.rb +++ b/spec/factories/booths.rb @@ -8,6 +8,7 @@ FactoryGirl.define do conference + submitter { create(:user) } responsible_ids { [create(:user).id] } end end diff --git a/spec/models/booth_spec.rb b/spec/models/booth_spec.rb index 5e86a393..287653ac 100644 --- a/spec/models/booth_spec.rb +++ b/spec/models/booth_spec.rb @@ -33,16 +33,17 @@ describe 'Booth' do end end - states = [:new, :withdrawn, :to_accept, :accepted, :to_reject, :rejected, :canceled] + states = [:new, :withdrawn, :to_accept, :accepted, :to_reject, :rejected, :canceled, :confirmed] transitions = [:restart, :withdraw, :accept, :reject, :to_accept, :to_reject, :cancel] - states_transitions = { new: { restart: false, withdraw: true, accept: true, to_accept: true, to_reject: true, reject: true, cancel: false }, - withdrawn: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false }, - to_accept: { restart: true, withdraw: true, accept: true, to_accept: false, to_reject: true, reject: false, cancel: true }, - to_reject: { restart: true, withdraw: true, accept: false, to_accept: true, to_reject: false, reject: true, cancel: true }, - accepted: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true }, - rejected: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true }, - canceled: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false } } + states_transitions = { new: { restart: false, withdraw: true, accept: true, to_accept: true, to_reject: true, reject: true, cancel: false, confirm: false }, + withdrawn: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false, confirm: false }, + to_accept: { restart: false, withdraw: true, accept: true, to_accept: false, to_reject: true, reject: false, cancel: true, confirm: false }, + to_reject: { restart: false, withdraw: true, accept: false, to_accept: true, to_reject: false, reject: true, cancel: true, confirm: false }, + accepted: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true, confirm: true }, + rejected: { restart: true, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true, confirm: false }, + canceled: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false, confirm: false }, + confirmed: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true, confirm: false } } states.each do |state| transitions.each do |transition|