how can I show yearly chart on buttonclick in rails - ruby-on-rails

By default I show a chart for monthly data in my view using chartkick and highcharts. i have already prepared a hash for showing yearly chart but how can I show show yearly chart on buttonclick. My html:
<div class="flot-chart">
<div class="flot-chart-content" id="flot-dashboard-chart">
<%= column_chart #chart_by_month ,height: "200px",width: "900px" %>
</div>
</div>
and the button I have added is only month. I will add a chart for yearly data, but how do I show it with button click?
<div class="btn-group">
<button type="button" id="by_month" class="btn btn-xs btn-white">Monthly</button>
</div>

If I understand you, you need to implement the ajax call described in section Say goodbye to timeouts and then add the filter you want in the controller and also js functionality. Do not forget the route.
Some like:
HTML:
<div class="flot-chart">
<div class="flot-chart-content" id="flot-dashboard-chart">
<%= column_chart chart_by_period_path( period: "month") ,height: "200px",width: "900px" , id: "id_g1" %>
</div>
</div>
ROUTE:
...
get 'chart_by_period/(:period)', to: "controller#chart_by_period", as: "chart_by_period"
...
JS:
...
var g1 = Chartkick.charts["id_g1"];
$("button#refresh_graph__year_btn").on('click', function(){
g1.updateData( "/chart_by_period/year");
});
...
CONTROLLER:
def chart_by_period
if params[:period] == "month"
...
output = ....
...
elsif params[:period] == "year"
...
output = ....
...
end
render json: output
end

Related

Rails 6 : Simple calendar + boot strap modal + simple form for

I am building a calendar with the "Simple calendar" gem. I want the user to be able to block some time slots, within a modal.
For a better UX, it should pre-fill the date with the selected date.
I have tried to put <%= date %> as a value, but it looks like the modal only loads the first date that shows, and doesn't increment then, so it always pre-fill the date with the first day, no matter which day I choose.
Any suggestion?
<%= month_calendar events: #bookings+#blocked_times do |date, events| %>
<%= date %>
<button id="button" type="button" data-toggle="modal" data-target="#exampleModal" data-selected-date="25-07-2021" >
<i class="far fa-edit"></i>
</button>
<!-- Modal -->
<%= simple_form_for(#blocked_time, method: :new, url: partner_blocked_times_path(#partner)) do |f| %>
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Block slot</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<%= f.hidden_field :partner_id, value: params[:id]%>
<div class= "date-picker">
<%= f.input :start_time,as: :string %>
</div>
<div class= "date-picker">
<%= f.input :end_time, as: :string%>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<%= f.button :submit, class: 'btn btn-primary' %>
</div>
<% end %>
</div>
</div>
</div>
You can do it with Javascript.
Simply define each button with a data-date attribute
<button id="button" type="button" class="new_event_button" data-toggle="modal" data-target="#exampleModal" data-date="<%= date %>">
<i class="fas fa-calendar-times" data-date="<%= date %>"></i>
</button>
then catch click event and change form according to this value
const startDate = document.getElementById('blocked_time_start_time')
const endDate = document.getElementById('blocked_time_end_time')
var buttons = document.querySelectorAll('button.new_event_button');
Array.from(buttons).forEach(function (button) {
button.addEventListener('click', function(event) {
var date = event.target.getAttribute('data-date');
startDate.value = date;
endDate.value = date;
});
});
simple_calendar allow you to set start_date as the first date of ranges of dates will be rendered on calendar, of course the events (BookedTime) should match with that ranges of dates so you have to query bookedtimes from the selected date.
beside that you also need to create a new BlockedTime for each day with start_time and end_time is that day
# model, i assume that your model name Booking
class Booking < ApplicationRecord
# ...
def self.calendar(partner_id, start_time)
#bookings = #partner.bookings # need to filter from start_time
#blocked_times = BlockedTime.where(partner_id: #partner.id)
.where("start_time >= ?", start_time)
#bookings+#blocked_times
end
end
# view
<%= month_calendar(start_date: selected_date,
events: Booking.calendar(partner_id, selected_date)) do |date, events| %>
<%= date %>
...
<% new_blocked_time = BlockedTime.new(start_time: date, end_time: date) %>
<%= simple_form_for(new_blocked_time ...
...
you could also create a custom-calendars.

Modal for tr - different id index page

I have an index page with different software in a table.
I want to display additional information (in a modal) when we click on a tr.
Everything works but I have the information of a single software that appears in my modal and is the same for each tr.
I would like to display the information of each software in the corresponding modals.
My script :
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$('#exampleModal').modal('show');
}
});
My view :
<% #nonpremium.each do |software| %>
<table>
<tr class="clickable">
<td class="hey1">
<%= link_to software_path(software), class: "no-click" do %>
<%= image_tag software.logo.url(:medium), class:"no-click"%>
<% end %>
</td>
<td class="hey3">
<h6><%= software.slogan %></h6>
<p><%= software.largeslogan %></p>
</td>
</tr>
</table>
<div class="modal fade bd-example-modal-lg" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<%= link_to software.software_name, software_path(software), class:"no-click" %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<% end %>
I tried something like this in my script, but it does not work ..
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$('#exampleModal-<%= #software.id %>').modal('show');
}
});
Thx for you help
EDIT :
Controller/pages
class PagesController < ApplicationController
before_action :click, only: :index
def home
#softwares = Software.all.order(:cached_votes_up => :desc )
#premium = Software.includes(:user).where(users: { subscribed: true }).order("RANDOM()").limit(2)
#nonpremium = #softwares - #premium
end
def search
#softwares = Software.ransack(name_cont: params[:q]).result(distinct: true)
#categories = Category.ransack(name_cont: params[:q]).result(distinct: true)
respond_to do |format|
format.html {}
format.json {
#softwares = #softwares.limit(5)
#categories = #categories.limit(5)
}
end
end
end
EDIT 2 :
I have the desired result by putting in my table the information that I want to recover, then I put a "display: none".
<style>
td.test {
display:none;
}
</style>
<td class="test">
<span><%= software.software_description %></span>
<span><%= get_video_iframe(software.youtube_id) %></span>
</td>
Then I get the information from my table in my script:
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
var description = this.childNodes[3].innerHTML;
var name = this.childNodes[5].innerHTML;
document.getElementById("myModalName").innerHTML = name;
document.getElementById("myModalDesc").innerHTML = description;
$('#exampleModal').modal('show');
}
});
For then displayed in my modal:
...
<div class="modal-body" id="myModalName">
Name
</div>
<div class="modal-body" id="myModalDesc">
Description
</div>
...
There is probably better to do, but being a beginner is how I achieve the desired result.
However I would like to post videos in my modals.
Am I not going to overload my home page by hiding youtube videos with my display: none?
You won't be able to use erb in your script (unless this is in a script tag within your view, in which case your code should work) - better using a data attribute. For example, if you update your tr to the following:
<%= content_tag :tr, class: "clickable", data: { software_id: #software.id } do %>
# the rest of your code within the tr
<% end %>
# Equivalent of using:
# <tr class="clickable" data-software_id="<%= #software.id %>">
This attaches the relevant software_id to the tr in the DOM. You can then use the following in your script, accessing this new attribute:
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$('#exampleModal-' + $(e.target).data('software_id')).modal('show');
}
});
And everything should work as desired.
Let me know how you get on or if you have any questions. Hope this helps!
Edit based on your comment:
That error you're seeing will come because #software is nil and you are, therefore, attempting to call id on nil.
It's a common error, and means to need to ensure #software is correctly set in your controller. If you post your controller code, I might be able to help with this.
Alternatively, you can 'safely' try the method, using #software&.id with newer versions of Ruby / Rails (or #software.try(:id) on older versions). However, that's not likely to be helpful here, more of a side note :)
Edit 2:
So, in your controller, you're not actually assigning the singular #software, rather the plural #softwares:
#softwares = Software.all.order(:cached_votes_up => :desc )
#premium = Software.includes(:user).where(users: { subscribed: true }).order("RANDOM()").limit(2)
#nonpremium = #softwares - #premium
Then, in your view, you're looping through #nonpremium using the local variable software. So, you can either:
assign #software in the controller if it should always use the same data in the modal
go back to the previous option, assigning a data attribute to the tr, which is what I'd recommend. Using that should work, although you'll need to alter the code to use software without the # to address the correct variable.
I.E.
<%= content_tag :tr, class: "clickable", data: { software_id: software.id } do %>
# the rest of your code within the tr
<% end %>
This ensures the script addresses the click based on the element clicked, and pulls the id directly from there, which is within the scope of your software loop.
That do it for ya?

Update model field with button Rails

I have a messaging system in my Rails 5 project where the Message model has an isread field to indicate whether the recipient has read the message yet or not.
I'm using a bootstrap modal to view the message and would like the message's isread field to change to true when the modal is closed.
Could someone explain how to do this from the button_tag or make the button tag call method in the controller to do it?
Something like:
message.isread = true
message.save!
to execute when the "Close" button is pressed from my view:
<div id="modal1<%= index %>" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title"><%= message.subject %></h4>
</div>
<div class="modal-body">
<p><%= message.content %></p>
</div>
<div class="modal-footer">
<%= button_tag "Close", :class => "btn btn-default", "data-dismiss" => "modal" %>
</div>
</div>
</div>
</div>
Thank you!
You can define a new action in your controller which update the attribute isread to true and use the button_to helper
#routes
resources :messages do
post :is_read, on: :member
end
#messages controller
def is_read
#message = Message.find(params[:id])
#message.update(isread: true)
redirect_to ...
end
#view
<%= button_to "Close", is_read_message_path %>
If you want a button click to call a method in your controller you will need to first capture the action of the button click in javascript
$(".btn-default").click(function(){
});
Then you want to do an Ajax call to the controller method
$.ajax({
url: "/message/update_is_read",
type: "POST",
data: {is_read: isRead},
success: function(resp) {
console.log(resp);
},
error: function(resp) {
console.log(resp);
},
});
then in your controller catch it with
def update_is_read
is_read = params[:is_read]
end
Make sure you make add the path to your routes
post '/messages/update_is_read', to: 'messages#update_is_read', as: '/messages/update_is_read'
You can modify the controller code to save.

How do I pass a specific string to my partial depending on the value of another variable also being passed to it?

I have the following partial (_card_brand.html.erb), which looks like this:
<div class="payment-card">
<i class="fa fa-cc-<%= brand.downcase %> payment-icon-big text-success"></i>
</div>
That renders HTML that looks like this:
<div class="payment-card">
<i class="fa fa-cc-visa payment-icon-big text-success"></i>
</div>
The above is rendered with this:
<%= render partial: "subscriptions/card_brand", locals: { brand: current_user.card_brand } %>
What I want to do is change the class text-success, to be either: text-warning, text-primary, text-danger, etc. depending on if the card has brand: visa, amex, mastercard, discovery, etc.
So:
Visa = Success
AMEX = Warning
Mastercard = Primary
Discovery = Danger
Any other cards would be other classes.
How do I elegantly represent that in my view that renders the partial?
You may create a helper and use it so that it will be easy to add new classes as well.
application_helper.rb
CARD_CLASS = {
'visa' => 'success',
'amex' => 'warning',
'mastercard' => 'primary',
'discovery' => 'danger'
}
def payment_class(type)
CARD_CLASS[type.downcase]
end
_card_brand.html.erb
<div class="payment-card">
<i class="fa fa-cc-<%= brand.downcase %> payment-icon-big text-text-<%= payment_class(brand.downcase) %>"></i>
</div>

Add as favorite with no full page refresh

I am working on a Hiragana flashcards app.
I spend nights and days to understand how don't refresh full page when I add a flashcard (hiragana) as a favorite.
Here is the favorite controller
class FavsController < ApplicationController
def index
#favs = Fav.where(user: current_user)
end
def create
#hiragana = Hiragana.find(params[:hiragana_id])
#fav = current_user.favs.new(hiragana: #hiragana)
if not #hiragana.favs.where(user: current_user).take
#fav.save
end
render json: #fav
end
def destroy
#fav = Fav.find(params[:id])
#fav.destroy
redirect_to :back
end
end
I render json in the create method and when I click on view I add only an hash
render view favorite
<% if current_user %>
<div class="hiragana-fav">
<% if hiragana.is_faved_by(current_user) %>
<%= link_to fav_path(hiragana.is_faved_by(current_user)), method: :delete do %>
<i class="fa fa-heart faved faved-on"></i>
<% end %>
<% else %>
<%= link_to hiragana_favs_path(hiragana), method: :post do %>
<i class="fa fa-heart faved faved-off"></i>
<% end %>
<% end %>
</div>
<% end %>
and it is located in hiragana render
<div class="row">
<ul class="list-inline text-center card-frame">
<li>
<div class="card">
<div class="front">
<% if current_user.try(:admin?) %>
<%= link_to hiragana_path(hiragana), class:'trash-hiragana', data: { confirm: 'Are you sure?' }, method: :delete do %>
<%= image_tag("delete-btn.png") %>
<% end %>
<% end %>
<span class="card-question img-popover" data-content="<h4 class='text-center letter-uppercase'><%= hiragana.bigletter.upcase %></h4><p class='text-center'><b><%= hiragana.midletter %></b> comme dans <b><%= hiragana.transcription %></b></p>">
<i class="fa fa-eye fa-lg"></i>
</span>
<div class="card-hiragana hiragana-<%=hiragana.bigletter.downcase.last%>">
<h1><b><%= hiragana.ideo1 %></b></h1>
</div>
<div class="card-katakana">
<p><%= hiragana.ideo2 %></p>
</div>
<%= render 'favs/favorites', hiragana: hiragana %>
</div>
<div class="back">
<div class="col-sm-3 col-xs-4 col-md-3 containerbackcards-<%=hiragana.bigletter.downcase.last%>">
<div class="backcard-hiragana">
<h1><b><%= hiragana.ideo1 %></b></h1>
</div>
<div class="card-bigletter">
<h4><%= hiragana.bigletter.upcase %></h4>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
When I add a card as favorite it gives me a hash like this :
{
id: 64,
user_id: 1,
hiragana_id: 4,
created_at: "2016-02-10T16:37:26.270Z",
updated_at: "2016-02-10T16:37:26.270Z"
}
I just want to have the heart grey to red as favorite, saved and not refresh the entire page. Your explainations are appreciated thank you.
In order to send the request to the controller without the page refreshing you need to use a combination of Ajax and JavaScript.
You use JavaScript to add a click listener to the .faved-on button and to trigger the Ajax request. You will also use JavaScript to prevent the default action occurring when you click the link ie. The Page Refresh
You then use Ajax to send the request to the controller and handle the response.
Your initial JavaScript code looks pretty correct, except you are missing the bit to stop the page from reloading.
See the e.preventDefault(); line below
$(document).ready(function() {
$('.faved-on').click(function(e) { //make sure to pass in the e (the event paramter)
e.preventDefault(); //this is the line you are missing
var fav = $('.faved-off')
//let your ajax handle the rest then
$.ajax({
type: "POST", url: "/hiraganas", dataType: "json",
success: function(data) {
console.log(data);
//change the color of your heart to red here
},
error: function(jqXHR) {
console.error(jqXHR.responseText);
}
});
})
})
I haven't tested your JavaScript but it looks pretty close to correct, I believe its the e.preventDefault(); bit you were missing

Resources