feat: added counts and instant update and destroy
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
parent
68deb0f810
commit
d07857c011
@ -1,4 +1,18 @@
|
||||
module ApplicationCable
|
||||
class Connection < ActionCable::Connection::Base
|
||||
identified_by :current_user
|
||||
|
||||
def connect
|
||||
self.current_user = find_verified_user
|
||||
end
|
||||
|
||||
private
|
||||
def find_verified_user
|
||||
if (verified_user = env['warden'].user)
|
||||
verified_user
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,9 +1,40 @@
|
||||
class CountChannel < ApplicationCable::Channel
|
||||
def subscribed
|
||||
# stream_from "some_channel"
|
||||
stream_from "counts"
|
||||
end
|
||||
|
||||
def unsubscribed
|
||||
# Any cleanup needed when channel is unsubscribed
|
||||
end
|
||||
|
||||
def plus_one(params)
|
||||
unless current_user.nil?
|
||||
total = Total.find(params["id"])
|
||||
unless total.nil?
|
||||
total.update(count: total.count + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def minus_one(params)
|
||||
unless current_user.nil?
|
||||
total = Total.find(params["id"])
|
||||
unless total.nil?
|
||||
total.update(count: total.count - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.create(total)
|
||||
|
||||
end
|
||||
|
||||
def self.update(total)
|
||||
ActionCable.server.broadcast("counts", {"update": total})
|
||||
end
|
||||
|
||||
def self.destroy(total)
|
||||
ActionCable.server.broadcast("counts", {"destroy": total.id})
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,6 +0,0 @@
|
||||
class Count::HomeController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
end
|
||||
end
|
51
app/controllers/count_controller.rb
Normal file
51
app/controllers/count_controller.rb
Normal file
@ -0,0 +1,51 @@
|
||||
class CountController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
@counts = Total.all
|
||||
end
|
||||
|
||||
def edit
|
||||
@count = Total.find(params[:id])
|
||||
end
|
||||
|
||||
def destroy
|
||||
if Total.find(params[:id]).destroy
|
||||
flash[:notice] = I18n.translate("flashes.count.destroy.success")
|
||||
redirect_to count_index_path
|
||||
else
|
||||
flash[:alert] = I18n.translate("flashes.count.destroy.fail")
|
||||
redirect_to edit_count_path
|
||||
end
|
||||
end
|
||||
|
||||
def new
|
||||
@count = Total.new
|
||||
end
|
||||
|
||||
def update
|
||||
if Total.find(params[:id]).update(validated_params(params))
|
||||
flash[:notice] = I18n.translate("flashes.count.update.success")
|
||||
redirect_to count_index_path
|
||||
else
|
||||
flash[:alert] = I18n.translate("flashes.count.update.fail")
|
||||
redirect_to edit_count_path
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
if Total.new(validated_params(params)).save
|
||||
flash[:notice] = I18n.translate("flashes.count.create.success")
|
||||
redirect_to count_index_path
|
||||
else
|
||||
flash[:alert] = I18n.translate("flashes.count.create.fail")
|
||||
redirect_to new_count_path
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validated_params(params)
|
||||
params.require(:total).permit([:name, :count])
|
||||
end
|
||||
end
|
@ -1,4 +1,5 @@
|
||||
class HomeController < ApplicationController
|
||||
def index
|
||||
@counts = Total.all
|
||||
end
|
||||
end
|
||||
|
2
app/javascript/add_bootstrap.js
Normal file
2
app/javascript/add_bootstrap.js
Normal file
@ -0,0 +1,2 @@
|
||||
import * as bootstrap from "bootstrap"
|
||||
window.bootstrap = require('bootstrap/dist/js/bootstrap.bundle.js');
|
3
app/javascript/add_jquery.js
Normal file
3
app/javascript/add_jquery.js
Normal file
@ -0,0 +1,3 @@
|
||||
import jquery from 'jquery'
|
||||
window.jQuery = jquery
|
||||
window.$ = jquery
|
@ -1,5 +1,7 @@
|
||||
// Entry point for the build script in your package.json
|
||||
import "./add_jquery"
|
||||
import "@hotwired/turbo-rails"
|
||||
import "./controllers"
|
||||
import * as bootstrap from "bootstrap"
|
||||
import "./channels"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import consumer from "./consumer"
|
||||
|
||||
consumer.subscriptions.create("CountChannel", {
|
||||
const count_channel = consumer.subscriptions.create("CountChannel", {
|
||||
connected() {
|
||||
// Called when the subscription is ready for use on the server
|
||||
console.warn("Connected to CountChannel.");
|
||||
@ -12,5 +12,26 @@ consumer.subscriptions.create("CountChannel", {
|
||||
|
||||
received(data) {
|
||||
// Called when there's incoming data on the websocket for this channel
|
||||
}
|
||||
console.log(data)
|
||||
if (data.update !== undefined)
|
||||
this.update(data.update)
|
||||
|
||||
if (data.destroy !== undefined)
|
||||
this.destroy(data.destroy)
|
||||
},
|
||||
|
||||
update(data) {
|
||||
$("[data-count-id='"+ data.id + "'] .count").text(data.count)
|
||||
$("[data-count-id='"+ data.id + "'] .name").text(data.name)
|
||||
},
|
||||
|
||||
destroy(data) {
|
||||
$("[data-count-id='"+ data + "']").remove()
|
||||
},
|
||||
});
|
||||
|
||||
$(document).on('turbo:load', function() {
|
||||
if (count_channel !== undefined && !count_channel.consumer.connection.disconnected) {
|
||||
$("")
|
||||
}
|
||||
})
|
||||
|
30
app/models/total.rb
Normal file
30
app/models/total.rb
Normal file
@ -0,0 +1,30 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: totals
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# count :integer default(0), not null
|
||||
# name :text not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_totals_on_name (name)
|
||||
#
|
||||
class Total < ApplicationRecord
|
||||
validates_uniqueness_of :name
|
||||
validates_length_of :name, in: 1..32
|
||||
|
||||
after_save do
|
||||
CountChannel.create(self)
|
||||
end
|
||||
|
||||
after_update do
|
||||
CountChannel.update(self)
|
||||
end
|
||||
|
||||
after_destroy do
|
||||
CountChannel.destroy(self)
|
||||
end
|
||||
end
|
4
app/views/count/edit.html.erb
Normal file
4
app/views/count/edit.html.erb
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
<h2 class="mt-2"><%= t("fields.edit") %></h2>
|
||||
|
||||
<%= render "count/shared/form" %>
|
@ -1,2 +0,0 @@
|
||||
<h1>Count::Home#index</h1>
|
||||
<p>Find me in app/views/count/home/index.html.erb</p>
|
22
app/views/count/index.html.erb
Normal file
22
app/views/count/index.html.erb
Normal file
@ -0,0 +1,22 @@
|
||||
<div class="mt-2 row row-cols-4">
|
||||
<% @counts.each { |c| %>
|
||||
<div class="col mt-1 mb-2 text-center" data-count-id="<%= c.id %>">
|
||||
<div class="row">
|
||||
<div class="col text-end">
|
||||
<button type="button" class="btn btn-lg btn-outline-danger fw-bold my-auto fs-3 minus">-</button>
|
||||
</div>
|
||||
<span class="col fw-bold fs-1 count"><%= c.count %></span>
|
||||
<div class="col text-start">
|
||||
<button type="button" class="btn btn-lg btn-outline-success fw-bold my-auto fs-3 plus">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fw-light fs-4">
|
||||
<span><%= c.name %></span>
|
||||
<span><%= link_to "", edit_count_path(c), class: "btn btn-sm btn-primary bi-pen-fill" %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="fixed-bottom text-end m-5">
|
||||
<%= link_to "", new_count_path, class: "btn btn-lg btn-success fs-3 bi-plus-lg" %>
|
||||
</div>
|
4
app/views/count/new.html.erb
Normal file
4
app/views/count/new.html.erb
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
<h2 class="mt-2"><%= t("fields.new") %></h2>
|
||||
|
||||
<%= render "count/shared/form" %>
|
17
app/views/count/shared/_form.html.erb
Normal file
17
app/views/count/shared/_form.html.erb
Normal file
@ -0,0 +1,17 @@
|
||||
<div class="mt-2 mb-4">
|
||||
<%= form_for @count, url: @count.id ? count_path : count_index_path, class: "row" do |f| %>
|
||||
<div class="col-2">
|
||||
<%= f.label :name, t("fields.name") %>
|
||||
<%= f.text_field :name, class: "form-control" %>
|
||||
</div>
|
||||
<div class="col-2 mt-2 mb-2">
|
||||
<%= f.label :count, t("fields.count") %>
|
||||
<%= f.number_field :count, class: "form-control" %>
|
||||
</div>
|
||||
<%= f.submit t("fields.save"), class: "btn btn-primary" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @count.id %>
|
||||
<%= button_to "", count_path(@count), method: :delete, class: "bi-trash-fill btn btn-danger" %>
|
||||
<% end %>
|
@ -1,2 +1,10 @@
|
||||
<h1>Home#index</h1>
|
||||
<p>Find me in app/views/home/index.html.erb</p>
|
||||
<div class="mt-2 row row-cols-4">
|
||||
<% @counts.each { |c| %>
|
||||
<div class="col text-center" data-count-id="<%= c.id %>">
|
||||
<span class="fw-bold fs-1 count"><%= c.count %></span>
|
||||
<div class="fw-light fs-4 name">
|
||||
<%= c.name %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
|
||||
<li><%= link_to t("fields.home"), root_path, class: "nav-link px-2 #{current_page?(root_path) ? "text-secondary" : "text-white"}" %></li>
|
||||
<% if current_user %>
|
||||
<li><%= link_to t("fields.count"), count_root_path, class: "nav-link px-2 #{current_page?(count_root_path) ? "text-secondary" : "text-white"}" %></li>
|
||||
<li><%= link_to t("fields.count"), count_index_path, class: "nav-link px-2 #{current_page?(count_index_path) ? "text-secondary" : "text-white"}" %></li>
|
||||
<li><%= link_to t("fields.profile"), profile_root_path, class: "nav-link px-2 #{current_page?(profile_root_path) ? "text-secondary" : "text-white"}" %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
@ -37,12 +37,17 @@
|
||||
|
||||
<div class="container">
|
||||
<% if notice %>
|
||||
<p class="notice"><%= notice %></p>
|
||||
<div class="alert alert-primary" role="alert">
|
||||
<%= notice %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if alert %>
|
||||
<p class="alert"><%= alert %></p>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<%= alert %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= yield %>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -4,4 +4,8 @@ en:
|
||||
login: "Login"
|
||||
logout: "Logout"
|
||||
count: "Count"
|
||||
profile: "Profile"
|
||||
profile: "Profile"
|
||||
edit: "Edit"
|
||||
new: "New"
|
||||
name: "Name"
|
||||
save: "Save"
|
12
config/locales/en/flashes.en.yml
Normal file
12
config/locales/en/flashes.en.yml
Normal file
@ -0,0 +1,12 @@
|
||||
en:
|
||||
flashes:
|
||||
count:
|
||||
update:
|
||||
success: "Count has been successfully saved."
|
||||
fail: "The update failed for the count."
|
||||
create:
|
||||
success: "Count creation was successful."
|
||||
fail: "Count creation failed."
|
||||
destroy:
|
||||
fail: "Count deletion has failed."
|
||||
success: "Count has been deleted successfully."
|
@ -5,3 +5,7 @@ fr:
|
||||
logout: "Se déconnecter"
|
||||
count: "Compte"
|
||||
profile: "Profil"
|
||||
edit: "Éditer"
|
||||
new: "Nouveau"
|
||||
name: "Nom"
|
||||
save: "Sauvegarder"
|
||||
|
12
config/locales/fr/flashes.fr.yml
Normal file
12
config/locales/fr/flashes.fr.yml
Normal file
@ -0,0 +1,12 @@
|
||||
fr:
|
||||
flashes:
|
||||
count:
|
||||
update:
|
||||
success: "Le compte a bien été sauvegardé."
|
||||
fail: "La mise à jour du compte a échoué."
|
||||
create:
|
||||
success: "La création du compte a été faite avec succès."
|
||||
fail: "La création du compte a échoué."
|
||||
destroy:
|
||||
fail: "La suppression du compte a échoué."
|
||||
success: "Le compte a été supprimé avec succès."
|
@ -1,11 +1,10 @@
|
||||
Rails.application.routes.draw do
|
||||
|
||||
namespace :profile do
|
||||
root "home#index"
|
||||
end
|
||||
|
||||
namespace :count do
|
||||
root "home#index"
|
||||
end
|
||||
resources :count, except: [:show]
|
||||
|
||||
root "home#index"
|
||||
|
||||
|
10
db/migrate/20230204135557_create_totals.rb
Normal file
10
db/migrate/20230204135557_create_totals.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class CreateTotals < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :totals do |t|
|
||||
t.integer :count, null: false, default: 0
|
||||
t.text :name, index: true, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
10
db/schema.rb
generated
10
db/schema.rb
generated
@ -10,7 +10,15 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_02_04_125140) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_02_04_135557) do
|
||||
create_table "totals", force: :cascade do |t|
|
||||
t.integer "count", default: 0, null: false
|
||||
t.text "name", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["name"], name: "index_totals_on_name"
|
||||
end
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "email", default: "", null: false
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
|
@ -8,6 +8,7 @@
|
||||
"@rails/actioncable": "^7.0.4-2",
|
||||
"bootstrap": "^5.2.3",
|
||||
"bootstrap-icons": "^1.10.3",
|
||||
"jquery": "^3.6.3",
|
||||
"esbuild": "^0.17.5",
|
||||
"sass": "^1.58.0"
|
||||
},
|
||||
|
@ -266,6 +266,11 @@ is-number@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
jquery@^3.6.3:
|
||||
version "3.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.3.tgz#23ed2ffed8a19e048814f13391a19afcdba160e6"
|
||||
integrity sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
|
Loading…
Reference in New Issue
Block a user