RoR passing meeting information into a modal - ruby-on-rails

I have a calendar that has meetings fed onto it.
Controller:
def index
#meetings = current_user.meetings.all
end
View
<%= render "calendar", meetings: #meetings %>
Rendering:
<%= month_calendar events: meetings do |date, meetings| %>
<%= date %>
<% meetings.each do |meeting| %>
<div>
<% unless meeting.complete == true%>
<% if meeting.end_time.to_date < Date.today %>
<%= link_to "❗️ #{meeting.name}", meeting, class: "link" %>
<% elsif meeting.end_time.to_date == Date.today %>
<%= link_to "🔔 #{meeting.name}", meeting, class: "link" %>
<% else meeting.end_time.to_date > Date.today %>
<%= link_to meeting.name, meeting, class: "link" %>
<% end %>
<% end %>
</div>
<% end %>
<% end %>
I have managed to get the link to fire a modal, but cannot figure out how to pass in the singular meeting information.
The below code shows links as individual meetings, but the modal only comes up with the first meeting information on all links:
<% if meeting.end_time.to_date < Date.today %>
<%= link_to "❗️ #{meeting.name}", meeting, {:remote => true, 'data-bs-toggle' => "modal", 'data-bs-target' => '#task_modal'} %>
<div class="modal fade" id="task_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Task name: <%= meeting.name %><br>Urgency Level: <%= meeting.priority %></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<%= meeting.body %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<% end %>

Related

How can I use ajax to display Bootstrap 4 modal in Rails 6 app?

Currently I have a modal rendering for each piece on the page. It works but I don't want to render a modal for each piece each time the page is loaded.
Here is the code where my modal is rendered now:
site/gallery.html.erb
<% #pieces.each_slice(3) do |pieces| %>
<div class="row">
<% pieces.each do |piece| %>
<div class="col-lg-4">
<%= image_tag (piece.images.joins(:blob).order('active_storage_blobs.filename ASC').first if piece.images.attached?), data: { toggle: "modal", target: "#modal#{piece.id}" }, class:"img-thumbnail mx-auto d-block img-size" %>
<p><%= piece.name %></p>
<p><%= piece.description %></p>
<%= render 'site/modal', piece: piece %>
</div>
<% end %>
</div>
<% end %>
Here is the modal partial:
site/_modal.html.erb
<div class="modal fade" id="modal<%=piece.id%>" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalLabel"><%= piece.name %></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
//code involving video and images for the piece
</div>
<div class="modal-footer">
<% if piece.quantity == 1 %>
<%= button_to 'Purchase', checkout_create_path, method: :post, params: {id: piece.id}, remote: true, class:"btn btn-dark" %>
<% else %>
<p>SOLD</p>
<% end %>
</div>
</div>
</div>
</div>
What I believe I need to do is add remote: true and pass the piece.id through my image_tag link somehow. I think I would need a modal.js.erb with something along these lines in there
$('modal').html('<%= j render "site/modal", piece: #piece %>');
$('#modal<%=piece.id%>').modal('show');
And then render the modal at the top of the gallery.html.erb page. I'm not quite sure how to go about this though
EDIT
I've made some progress with trying to use AJAX to display the modals.
I've moved my modal into the pieces directory and created a javascript file to go with it.
_piece_modal.html.erb
<div class="modal fade" id="piece-modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
//modal content here
</div>
</div>
</div>
piece_modal.js.erb
$('body').append('<%= j render "piece_modal", piece: #piece %>');
$('#piece-modal').modal('show');
In my pieces controller
def piece_modal
#piece = Piece.find_by(params[:piece])
end
In my routes
get 'piece_modal' => 'pieces#piece_modal'
And here is the loop I'm using images to link to my modals
<% #pieces.each_slice(3) do |pieces| %>
<div class="row">
<% pieces.each do |piece| %>
<div class="col-lg-4">
<%= link_to piece_modal_path(piece), remote: true do %>
<%= image_tag (piece.images.joins(:blob).order('active_storage_blobs.filename ASC').first if piece.images.attached?), class:"img-thumbnail mx-auto d-block img-size" %>
<% end %>
<p><%= piece.name %></p>
<p><%= piece.description %></p>
</div>
<% end %>
</div>
<% end %>
The modal displays now but it only displays images from the first piece in the loop. How can I get it to display the correct content when I click on different images?
Okay I found a way for the modal to load dynamically with some ajax.
I changed up my link_to like this
<%= link_to piece_modal_path(piece_id: piece.id), remote: true do %>
I made this change in the pieces controller
def piece_modal
#piece = Piece.find(params[:piece_id])
end
And I made this change to my piece_modal.js.erb
$('body').append('<%= j render "piece_modal", piece: #piece %>');
$('#piece-modal').modal('show');
$('#piece-modal').on('hidden.bs.modal', function () {
$(this).remove();
});
So now instead of each piece on the site rendering it's own modal, only one gets rendered when I click on the image of the piece. On hide though I have to remove it from the DOM or else the modal will stay rendered with the properties of the first piece I click on.
You have written your code like:
<%= image_tag (piece.images.joins(:blob).order('active_storage_blobs.filename ASC').first if piece.images.attached?), class:"img-thumbnail mx-auto d-block img-size" %>
But notice that you use .first which will always return the first property.
You could do something like this instead:
<% #pieces.each_slice(3) do |pieces| %>
<div class="row">
<% pieces.each_with_index do |piece, index| %>
<div class="col-lg-4">
<%= link_to piece_modal_path(piece), remote: true do %>
<%= image_tag (piece.images.joins(:blob).order('active_storage_blobs.filename ASC')[index] if piece.images.attached?), class:"img-thumbnail mx-auto d-block img-size" %>
<% end %>
<p><%= piece.name %></p>
<p><%= piece.description %></p>
</div>
<% end %>
</div>
<% end %>
p/s: I'm writing this on the fly and didn't actually test it.

Rails: Will paginate with current_user

I'm currently trying to implement will_paginate into my application, the problem that I'm currently facing is that I want to display it to different users, as users can have many wines and orders.
I'm looking for something to combine the current_user.wines with Wine.paginate..
I currently don't have any idea how to implement this
My wine controller:
def index
#wines = current_user.wines
#wines = Wine.paginate(page: params[:page])
end
As you can see I'm currently overriding #wines...
My wine model:
class Wine < ApplicationRecord
enum instant: {Reservieren: 0, Sofort: 1}
belongs_to :user
self.per_page = 1
end
My index view:
<div class="container-small">
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
Deine Weine
</div>
<div class="panel-body">
<% if !current_user.admin? %>
<% current_user.favorites.each do |favorite| %>
<div class="row">
<div class="col-md-2">
<%= image_tag favorite.wine.cover_photo(:thumb) %>
</div>
<div class="col-md-7">
<h4><%= favorite.wine.wine_name %></h4>
</div>
<div class="col-md-3 right">
<%= link_to "Bestellen", wine_path(#wines), class: "btn btn-form" %>
</div>
</div>
<hr/>
<% end %>
<% else %>
<% #wines.each do |wine| %>
<div class="row">
<div class="col-md-2">
<%= image_tag wine.cover_photo(:thumb) %>
</div>
<div class="col-md-7">
<h4><%= wine.wine_name %></h4>
</div>
<div class="col-md-3 right">
<%= link_to "Bearbeiten", details_wine_path(wine), class: "btn btn-form" %>
</div>
</div>
<hr/>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<%= will_paginate(#wines) %>
UPDATE
Now it's working in the wine controller, but when I want to implement it into the reservations controller, into the your_orders and your_reservations methods, it's not showing up in the view. I'm not getting an error, when I call <%= will_paginate(#wines) %>
Reservations view:
<div class="row" id="orders">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
Alle Bestellungen
</div>
<div class="panel-body">
<% #wines.each do |wine| %>
<% wine.reservations.each do |reservation| %>
<div class="row">
<% if reservation.Bearbeitung? %>
<div class="col-md-3">
Reserviert am<br/>
<%= reservation.start_date.strftime('%v') %>
</div>
<% else %>
<div class="col-md-3">
Erwartet am<br/>
<%= reservation.start_date.strftime('%v') %>
</div>
<% end %>
<div class="col-md-2">
<p><%= reservation.status %></p>
<div class="form-inline">
<% if reservation.Bearbeitung? %>
<%= link_to approve_reservation_path(reservation), method: :post do %> <i class="fa fa-thumbs-up fa-lg"></i> <% end %> |
<%= link_to decline_reservation_path(reservation), method: :post do %> <i class="fa fa-thumbs-down fa-lg"></i> <% end %>
<% end %>
</div>
</div>
<div class="col-md-4">
<%= reservation.bottle %>x
<%= link_to reservation.wine.wine_name, wine_path(reservation.wine) %><br/>
Gesamt: <%= reservation.total %>€
</div>
<div class="col-md-3 pull-right">
Lieferung an<br/><br/>
<span>
<%= link_to user_path(reservation.user) do %>
<%= reservation.user.fullname %><br/>
<% end %>
<%= reservation.user.location %>
</span>
</div>
</div><!-- row -->
<hr/>
<% end %>
<% end %>
</div><!-- panel body -->
</div><!-- panel default -->
</div><!-- col md 12 -->
</div><!-- row -->
</div><!-- row -->
</div><!-- container -->
<%= will_paginate(#wines) %>
Reservations Controller:
def your_orders
#orders = current_user.reservations.order(start_date: :asc)
end
def your_reservations
#wines = current_user.wines.paginate(page: params[:page])
redirect_to root_path unless current_user.admin == true
#count = Reservation.count(:all)
#total = Reservation.sum(:total)
end

Ask phone number validation only once by session

I integrated phone number verification in my app with this tutorial
here, the problem is when the visitor go on a profil from my results page and back to the results page the modal of the phone number verification reappears
I would know how to ask a phone number only once
My phone verification view
<div class="modal show" id="myModalpin" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title"> Vérification téléphone</h4>
</div>
<div class="modal-body">
<div id="send-pin">
<h4>Nous allons vous envoyer un code par sms afin de vérifier votre numéro <br> Est-ce le bon numéro ?</h4>
<%= form_for PhoneNumber.new, remote: true do |f| %>
<div class="form-group">
<%= f.text_field :phone_number %>
</div>
<%= f.submit "Envoyer", class: "btn btn-danger", id: 'send-pin-link' %>
<% end %>
</div>
<div id="verify-pin">
<h3>Entrer votre code de comfirmation</h3>
<%= form_tag phone_numbers_verify_path, remote: true do |f| %>
<%= hidden_field_tag 'hidden_phone_number', '' %>
<div class="form-group">
<%= text_field_tag :pin %>
</div>
<%= submit_tag "Verifier", class: "btn btn-primary" %>
<% end %>
</div>
<div id="status-box" class="alert alert-success">
<p id="status-message">Status: Haven’t done anything yet</p>
</div>
</div>
</div>
</div>
</div>
<% content_for(:after_js) do %>
<%= javascript_tag do %>
$(window).on('load',function(){
$('#myModalpin').modal('show');
});
<% end %>
<% end %>
A quick and a bit dirty solution would be to add condition to the execution of your javascript.
Let say that your phone object is in #phone variable created in your controller.
then you can transform your code with
<% content_for(:after_js) do %>
<%= javascript_tag do %>
$(window).on('load',function(){
<% if #phone.nil? %>
$('#myModalpin').modal('show');
<% end %>
});
<% end %>
<% end %>
A bit cleaner would be to put it in a dedicated view file with the extension .js.erb

rails leaving out some parts from fragment caching

I have a rails 4 app using pundit gem for authorization. If I do russian-doll fragment caching like the code below, the conditional statement used for authorization will be also cached, which is not good, since edit/delete buttons should only be available for the post.user.
What is the good way to get around this? Should I split the cache into smaller parts or is there a way to exclude some parts of the caching? What's the rails convention in this case?
index.html.erb
<% cache ["posts-index", #posts.map(&:id), #posts.map(&:updated_at).max, #posts.map {|post| post.user.profile.updated_at}.max] do %>
<%= render #posts %>
<% end %>
_post.html.erb
<% cache ['post', post, post.user.profile ] do %>
<div class="row>
<div class="col-md-2">
<%= link_to user_path(post.user) do %>
<%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
<% end %>
</div>
<div class="col-md-8">
<span class="post-user-name"><%= post.user.full_name %></span>
<span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
<div class="post-body">
<%= post.body %>
</div>
<div class="col-md-2" style="text-align:right;">
<!--############### THIS IS THE PART THAT SHOULD NOT BE CACHED #############-->
<% if policy(post).edit? && policy(post).delete? %>
<li class="dropdown">
<ul class = "dropdown-menu dropdown-menu-right">
<li>
<%= link_to "Edit Post", edit_post_path(post), remote: true, type: "button", 'data-toggle' => "modal", 'data-target' => "#updatepost_#{post.id}" %>
</li>
<li>
Delete Post
</li>
</ul>
</li>
<% end %>
<!--########################## UNTIL HERE ############################-->
</div>
</div>
<div class = "row comment-top-row" style="padding-bottom:10px;">
<div class="col-md-12 post-comment-form">
<%= render partial: 'posts/post_comments/post_comment_form', locals: { post: post } %>
</div>
</div>
<div class = "row">
<div class="col-md-12 post-comment-insert-<%= post.id%>">
<%= render partial: 'posts/post_comments/post_comment', collection: post.post_comments.ordered.included, as: :post_comment, locals: {post: post} %>
</div>
</div>
<% if policy(post).edit? %>
<div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<!-- FORM GETS RENDERED HERE VIA JS -->
</div>
<% end %>
<% if policy(post).delete? %>
<div class="modal fade" id="deletepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
......
</div>
<% end %>
<% end %>
Russian Doll Caching is simple but handy way for caching, there's no complex options or convention to exclude part of fragment from it. Above that, It's more relative to cache strategies. Here are two strategies for this user specific situation:
Rearrange individually and Cache fragments manually, which I don't recommend. Because it's more complex and doesn't leverage the advantages of Russian Doll Caching. Not so maintainable as well. Here is an example:
index.html.erb
<% # pull out cache %>
<%= render #posts %>
_post.html.erb
<% cache post %>
<%= # first part %>
<% end %>
<% # without cache %>
<%= # user specific part %>
<% cache post %>
<%= # third part %>
<% end %>
Preferred way: Add current_user as part of cache_key, which means you will have as many fragment caches as approximately your users and the fragments will automatically invalidate whenever the post or the user has changed their fingerprint. This is more elegant and maintainable. Here is an example:
index.html.erb
<% cache ["posts-index", #posts.map(&:id), #posts.map(&:updated_at).max, #posts.map {|post| post.user.profile.updated_at}.max] do %>
<%= render #posts %>
<% end %>
_post.html.erb
<% cache ['post', post, post.user.profile, current_user ] do %>
<div class="row>
<div class="col-md-2">
<%= link_to user_path(post.user) do %>
<%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
<% end %>
</div>
<div class="col-md-8">
<span class="post-user-name"><%= post.user.full_name %></span>
<span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
<div class="post-body">
<%= post.body %>
</div>
<div class="col-md-2" style="text-align:right;">
<% if policy(post).edit? && policy(post).delete? %>
<li class="dropdown">
<ul class = "dropdown-menu dropdown-menu-right">
<li>
<%= link_to "Edit Post", edit_post_path(post), remote: true, type: "button", 'data-toggle' => "modal", 'data-target' => "#updatepost_#{post.id}" %>
</li>
<li>
Delete Post
</li>
</ul>
</li>
<% end %>
</div>
</div>
<div class = "row comment-top-row" style="padding-bottom:10px;">
<div class="col-md-12 post-comment-form">
<%= render partial: 'posts/post_comments/post_comment_form', locals: { post: post } %>
</div>
</div>
<div class = "row">
<div class="col-md-12 post-comment-insert-<%= post.id%>">
<%= render partial: 'posts/post_comments/post_comment', collection: post.post_comments.ordered.included, as: :post_comment, locals: {post: post} %>
</div>
</div>
<% if policy(post).edit? %>
<div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<!-- FORM GETS RENDERED HERE VIA JS -->
</div>
<% end %>
<% if policy(post).delete? %>
<div class="modal fade" id="deletepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
......
</div>
<% end %>
<% end %>

carrierwave AJAX upload with remotipart

In my rails4 I would like to submit a form via AJAX that has a file uploaded via carrierwave. As far as I know it can be done via remotipart, but can't figure it out how I can do that. (Remotipart installed properly since it's working with refile gem that I also use in the app.)
With the current code below the form gets submitted according to the logs and it says the create.js.erb is rendered, but the code in the create.js.erb file never runs.
Rendered posts/create.js.erb (387.0ms)
What should I do?
form
<%= form_for #post, remote: true, method: :post, :html => { :multipart => true, class: "post-create-form" } do |f| %>
<img id="img_prev" style="margin-bottom:10px;" width=300 height=200 src="#" class="img-thumbnail hidden"/>
<%= f.text_area :body, placeholder: "Share something useful..", class: "form-control post-create-body" %>
<%= f.button "SHARE", class: "btn btn-primary btn-sm btn-create-post", data: {disable_with: "<i class='fa fa-spinner fa-spin'></i> Saving..."} %>
<span class="btn btn-success btn-sm btn-message-file">Upload Image
<%= f.file_field :post_image, class: "choose-message-file", id: "avatar-upload" %>
</span>
<%= f.hidden_field :post_image_cache %>
<% end %>
create.js.erb
$('ul.errors').hide();
$('.alert-danger').hide();
$('.post-index').prepend('<%= j render #post %>');
$('.post-create-body').val('');
$('.btn-create-post').prop('disabled',true);
_post.html.erb
<div class="col-md-2">
<%= link_to user_path(post.user) do %>
<%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
<% end %>
</div>
<div class="col-md-8">
<div class="post-info">
<%= link_to user_path(post.user) do %>
<span class="post-user-name"><%= post.user.full_name %></span>
<% end %>
<span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
</div>
<div class="post-body">
<%= simple_format(find_links(h post.body)) %>
</div>
<div class="post-image" data-postlink="<%= post_path(post) %>">
<% if post.post_image? %>
<button data-toggle="modal" data-target="#post-image-modal">
<%= image_tag post.post_image.url(:base_thumb) %>
</button>
<% end %>
</div>
</div>
UPDATE:
The problem is connected to updating the post object. The update form code can be found in the _post.html.erb, so that's why <%= j render #post %> didn't work. Still don't know what the problem is.
So what works:
1. creating post that has no image
2. updating post that has no image
3. updating post that has image BUT image is not edited
Not working:
1. creating post that has image
2. updating post that has image which is edited
<%= form_for post, method: :patch, remote: true, :html => { :multipart => true } do |f| %>
<div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content" style="text-align:left">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="edittaskmodalohhhhh">Edit Task</h4>
</div>
<div class="modal-body">
<div class="alert alert-danger" style="display:none">
<ul class="errors" style="display:none">
<%= render 'layouts/error_messages', object: f.object %>
</ul>
</div>
<div class="field form-group">
<%= f.file_field :post_image, id: "avatar-upload-update" %>
<%= f.hidden_field :post_image_cache %>
</div>
<div class="field form-group">
<%= f.text_area :body, class: "form-control edit-post-body" %>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" id="updatepostclose">Close</button>
<%= f.button "Update post", class: 'btn btn-primary edit-post-submit', data: {disable_with: "<i class='fa fa-spinner fa-spin'></i> Saving..."} %>
</div>
</div>
</div>
</div>
<% end %>
update.js.erb
$("ul.errors").html("");
<% if #post.errors.any? %>
$('.alert-danger').show();
$('ul.errors').show();
<% #post.errors.full_messages.each do |message| %>
$("ul.errors").append($("<li />").html("<%= message.html_safe %>"));
<% end %>
<% else %>
$('ul.errors').hide();
$('.alert-danger').hide();
$('#updatepost_<%= #post.id %>').modal('hide');
$post = $('<%= j render #post %>');
editPostHideDanger($post);
$('#post_<%= #post.id %>').remove();
$(".new-post-insert").prepend($post);
$("html, body").animate({ scrollTop: 0 }, "slow");
<% end %>
There was some issue (still don't know what) with the bootstrap modals. I changed the structure of the modals and now it's working fine.

Resources