Editing a Wizard multistep form after save - Wicked Gem / Rails - ruby-on-rails

I've been going round in circles all day with this. I have a large multi-step form using the Wicked gem and Ruby on Rails. It works perfectly but I can't figure out how to to get back into the form to edit individual entries.
Iim trying to make the ability to go into the client show page, click an individual client and then from there go back into the quote to edit and update it. As the Wicked gem only seems to work with show and update actions, if I try to build a standard edit action Wicked expects to be on a step therefore doesn't work.
I read the I would have to factor the edit action into my show/update actions but I'm having difficulties. Any help would be great thanks!
Clients Controller:
class ClientsController < ApplicationController
before_action :authenticate_user!, only: [:index, :show, :edit]
before_action :set_client, only: [:edit, :show, :update]
def index
#clients = Client.order('created_at DESC').paginate(page: params[:page], per_page: 10)
end
def show; end
def new
#client = Client.new
end
def edit; end
def update
if #client.update_attributes(client_params)
redirect_to client_quotes_path
flash[:success] = 'Client successfully updated'
else
render 'edit'
end
render_wizard #client
end
# After client is completed:
def create
#client = Client.new(client_params)
if #client.valid?
#client.save
session[:current_user_id] = #client.id
ClientMailer.new_client(#client).deliver
redirect_to quotes_path
else
flash[:alert] = 'Sorry, there was a problem with your message. Please contact us directly at ...'
render :new
end
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(:first_name, :last_name, :title, :email, :email_confirmation,
:phone, :time, :reminder, :ref_number, :day, :note, :logs_reminder)
end
end
Quotes Controller:
class QuotesController < ApplicationController
include Wicked::Wizard
before_action :set_client, only: [:show, :update, :quote_success]
steps :profile, :employment, :general_questions, :indemnity_details, :declarations
def show
#client.build_doctor unless #client.doctor.present?
#client.build_dentist unless #client.dentist.present?
#client.old_insurers.build
#client.practice_addresses.build
render_wizard
end
def update
#client.update(client_params)
render_wizard #client
end
def quote_success; end
private
def set_client
current_user = Client.find_by_id(session[:current_user_id])
#client = current_user
end
# After full quote form is completed:
def finish_wizard_path
if #client.valid?
ClientMailer.new_quote(#client).deliver
ClientMailer.new_quote_user_message(#client).deliver
end
quote_success_path
end
end
def client_params
params.require(:client).permit(:name, :email, :email_confirmation, :phone, :date_required,
:title, :first_name, :last_name, :date_of_birth, :nationality, :reg_body, :reg_date, :reg_type, :reg_number,
:qual_place, :qual_year, :post_grad, :membership ...
Routes:
Rails.application.routes.draw do
devise_for :users
root 'clients#new'
get 'client', to: 'clients#new', as: 'client'
post 'client', to: 'clients#create'
get '/client_quotes', to: 'clients#index', as: 'client_quotes'
get '/client_quotes/:id', to: 'clients#show', as: 'client_quote'
get '/client_quotes/:id/edit', to: 'clients#edit', as: 'edit_client_quote'
patch '/client_quotes/:id', to: 'clients#update'
put '/client_quotes/:id', to: 'clients#update'
resources :quotes, only: [:index, :show, :update, :quote_success]
get 'quote-success' => 'quotes#quote_success'
devise_scope :user do
get '/login' => 'devise/sessions#new'
end
end

My solution to this in the end was rather than have the edit form as a multi step wizard, I've joined the form data together in a separate view page and got a traditional route to it as you mention. Not perfect but does the job!

When you are updating it is like you are "editing" the element, so you need to redirect to the wizard when you want to edit and when you call the update method you really would be editing that entry. So call the wicked path.

Related

Error with devise: "You are already signed in" without users in the database (Rails 5)

I am using devise for my clients authentication, however, when I hit the sign up button in my view it throws me that I am already signed in, how so? I don't even have users in my database.
routes.rb
Rails.application.routes.draw do
devise_for :clients, controllers: { registrations: 'clients/registrations', sessions: 'clients/sessions' }
root to: "mainpages#index"
get '/mainpages', controller: 'mainpages', action: 'index'
get '/planes', controller: 'planes', action: 'planes'
resources :planes
My controllers are inside of controllers/clients, At the moment I want to activate all the related with registration and sessions, so here's my registration controller
registrations_controller
class Clients::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
super
#client = Client.new
end
# POST /resource
def create
super
#client = Client.new(client_params)
#Set user perms to provider or to client
if #client.perms == false
#client.perms = "Client"
else
#client.perms = "Provider"
end
#client.status = "Not activated"
if #client.save
return redirect_to :root
end
render 'new'
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
end
# The path used after sign up.
def after_sign_up_path_for(resource)
super(resource)
end
private
def client_params
params.require(:client).permit(:name, :last_name, :email, :country, :username, :city, :address, :date, :perms, :status)
end
end
sessions_controller
# frozen_string_literal: true
class Clients::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
def new
super
end
# POST /resource/sign_in
def create
super
end
# DELETE /resource/sign_out
def destroy
super
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_in_params
devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
end
end
index.html.erb views/mainpages/
<div class="col-sm-6"><h4>Not a member?</h4>
<%= link_to '<p>You can create an account:</p>'.html_safe, controller: "clients/registrations", action:"new" %>
<p class="text-center">
<%= link_to '<i class="fas fa-sign-in-alt big-icon"></i>'.html_safe, controller: "clients/registrations", action:"new" %>
</p>
</div>
I tried to manually sign out by typing localhost:3000/clients/sign_out but I get a routing error, my link to the sign up page doesn't work if I don't get rid of that session, tried to restart the server but I still get the same error.

Displaying item with a specific parameter in the index

I am building a website for poetries.
There are two different type of poetries: famous or amateur.
I built the CRUD functions to display all the poetries (famous and amateur, without distinction) and this is working as intended (see the PoetrisController code below).
Now, I want to give the possibility to the user to choose if he wants to see only the amateur poetries or famous ones.
Basically the user clicks the link "Amateur" or "Famous" in the navbar and he is redirected to a new page listing only amateur or famous poetries.
My question is: should I create another Controller (for example PoetriesFamousController) and creating a index function inside it to display only the famous poetries or there is a way to use the already existing PoetriesController to show only the "famous poetries" if the user clicks the link in the navbar?
PoetriesController:
class PoetriesController < ApplicationController
skip_after_action :verify_authorized, only: [:home, :about, :newsletter, :disclaimer, :new, :create]
skip_before_action :authenticate_user!, only: [:home, :about, :newsletter, :disclaimer, :new, :create]
before_action :set_poetry, only: [:show, :edit, :update, :destroy,]
before_action :authenticate_user!, except: [:index, :amateur_poetries]
def index
if params[:search]
#poetries = policy_scope(Poetry).search(params[:search]).order("created_at DESC").limit(30)
else
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
end
def show
authorize #poetry
end
def new
#poetry = Poetry.new
end
def create
Poetry.create(poetry_params)
redirect_to poetries_path
end
def edit
authorize #poetry
end
def update
#poetry.save
redirect_to poetry_path(#poetry)
end
def destroy
#poetry.destroy
redirect_to poetries_path
end
private
def poetry_params
params.require(:poetry).permit(:title, :author, :body, :poster, :country)
end
def set_poetry
#poetry = Poetry.find(params[:id])
end
end
Poetries.rb
class Poetry < ApplicationRecord
def self.search(search)
where("lower(title) LIKE ? OR lower(author) LIKE ? OR lower(country) LIKE ? OR lower(born) LIKE ?", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%")
end
end
Routes.rb
get 'poetries', to: 'poetries#index', as: :poetries
get "poetries/new", to: "poetries#new"
post "poetries", to: "poetries#create"
get "poetries/:id/edit", to: "poetries#edit"
patch "poetries/:id", to: "poetries#update"
get 'poetries/:id', to: 'poetries#show', as: :poetry
delete "poetries/:id", to: "poetries#destroy"
Here is some idea for your problem
In your view (sample idea)
poetries type:
<%= select_tag :poetries_type, options_for_select(["Famous","Amateur"]), include_blank: true, :class => 'form-control' %>
in your controller:
def index
if params[:search]
if params[:poetries_type] == "Famous"
#poetries = Poetry.famous.search(params[:search]).order("created_at DESC").limit(30)
elsif params[:poetries_type] == "Amateur"
#poetries = Poetry.amateur.search(params[:search]).order("created_at DESC").limit(30)
else
#poetries = Poetry.search(params[:search]).order("created_at DESC").limit(30)
end
else
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
end
Poetries.rb, add two scope for famous an amateur
def self.amateur
where("poster != ?","Admin")
end
def self.famous
where("poster = ?","Admin")
end
The simplest thing would be to add two more actions to your controller.
def famous
#poetries = #get the famous ones
render :index
end
def amateur
#poetries = #get the amateur ones
render :index
end
Then update your routes
get 'poetries', to: 'poetries#index', as: :poetries
get 'poetries/famous', to: 'poetries#famous'
get 'poetries/amateur', to: 'poetries#amateur
# rest of the routes

Why am I getting a recordnotfound error when trying to access an instance in rails?

I have a user profile controller called "userinfo" and it's corresponding view. The userinfo index is the root path. In the homepage(which is the userinfo index), I have a link that takes you to the user profile page. It is giving me this error when I go to the home page:
My routes are:
My userinfos_controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.find(params[:id])
end
def show
#myvideo = Video.last
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
and my view is:
<%= link_to 'profile', userinfors_path(#userinfors) %>
My routes.rb file:
Rails.application.routes.draw do
devise_for :users
resources :userinfos do
resources :videos
end
resources :pages
get '/application/decide' => 'application#decide'
root 'userinfos#index'
get '/userinfos/:id', to: 'userinfos#show', as: 'userinfors'
end
Thanks for any help!
ok, there are multiple errors and you are not following conventions of rails, index is not for what you have used.
Index is used to list all the users and show for a particular one with id passed in params.
Your index path is, as you can see, /userinfos which is correct and it doesn't have any id with it but you are trying to find user with params[:id] which is nil and hence the error.
Lets try out this:
def index
#userinfors = Userinfo.all #pagination is recommended
end
In your index view,
<% #userinfors.each do |userinfor| %>
<%= link_to "#{userinfor.name}'s profile", userinfo_path(userinfor) %>
<% end %>
It should work now.
Please read routing and action controller to get the idea and understand the magic behind rails routing and mvc architecture..

Rails 4: One-to-one controller issue

I'm building an app which consists on sharing résumés. I am using Devise gem. Each user is able to create only one résumé. I made the models and and their relations. Resume belongs_to User and User has_one 'Resume'.
After making the views, I wanted to test my app but I got the error: undefined methodbuild' for nil:NilClass`
Here is my ResumeController and my routes.rb
class ResumeController < ApplicationController
before_action :find_resume, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:show]
def show
# #resume = Resume.find_by(params[:id])
end
def new
#resume = current_user.resume.build
end
def create
#resume = current_user.resume.build(resume_params)
if #resume.save
redirect_to #resume, notice: "resume was successfully created"
else
render 'new'
end
end
def edit
end
def update
if #resume.update(pin_params)
redirect_to #resume, notice: "resume was successfully updated"
else
render 'edit'
end
end
def destroy
#resume.destroy
redirect_to root_path
end
private
def resume_params
params.require(:resume).permit(:title, :description)
end
def find_resume
#resume = resume.find(params[:id])
end
end
Routes.rb
Rails.application.routes.draw do
devise_for :users
resources :resume, except: [:index]
get 'static_pages/index'
root to: "static_pages#index"
end
I just want the user to be able to create only one Resume and then he will be able to share it.
Update: After following messanjah's answer there was another error coming from the _form.html.erb: undefined method resumes_path' for #<#<Class:0x00...>. Here is the gist with forms and model: goo.gl/XvW2LH So you can see all the files closely.
Without more knowledge of where the error is happening, I can only suggest some areas that might be suspect.
To build a has_one relationship, you must use the build_*association* constructor.
def new
#resume = current_user.build_resume
end
def create
#resume = current_user.build_resume(resume_params)
end

Wicked gem not reaching step 1

I've been following step by step example for wicked gem https://github.com/schneems/wicked/wiki/Building-Partial-Objects-Step-by-Step but I'm struggling to make it work
routes.rb
post '/trips/building/build(.:format)', :to => "trips/build#create"
resources :trips do
resources :build, controller: 'trips/build'
end
trips_controller.rb
class TripsController < ApplicationController
include Wicked::Wizard
before_action :set_trip, only: [:show, :update]
steps :basic, :details
def show
render_wizard
end
def create
#trip = Trip.create
redirect_to wizard_path(steps.first, :trip_id => #trip.id
end
def update
#trip.update_attributes(trip_params)
render_wizard #trip
end
private
def set_trip
#trip = Trip.find(params[:trip_id])
end
def trip_params
....
end
end
index.html.erb
<%= link_to 'Create New Trip', '/trips/building/build', :method => :post, :class=>'btn btn-danger'%>
error in console:
Started POST "/trips/building/build" for 127.0.0.1 at 2014-04-16 22:50:20 -0700
ActionController::RoutingError - uninitialized constant Trips
this is driving me crazy...
any thoughts?
Your controller is named incorrectly - your route is pointing to the Trips::BuildController but your controller is defined as the TripsController.
The link you shared defines a Products::BuildController so that's why it works there.

Resources