From 06591d00256cf1227b0a151a203a19bb9696f547 Mon Sep 17 00:00:00 2001 From: Teal Bauer <git@teal.is> Date: Fri, 24 May 2024 11:41:28 +0200 Subject: [PATCH] Add turbo/hotwire, fix routing --- Gemfile | 4 ++ Gemfile.lock | 16 ++++++++ app/assets/config/manifest.js | 2 + app/controllers/assignments_controller.rb | 8 ++++ app/controllers/conferences_controller.rb | 1 + app/controllers/sessions_controller.rb | 40 +++++++++++++++++++ app/helpers/assignments_helper.rb | 2 + app/javascript/application.js | 3 ++ app/javascript/controllers/application.js | 9 +++++ .../controllers/hello_controller.js | 7 ++++ app/javascript/controllers/index.js | 11 +++++ app/models/assignment.rb | 2 + app/models/conference.rb | 4 ++ app/models/session.rb | 4 ++ app/views/assignments/index.html.erb | 10 +++++ app/views/assignments/show.html.erb | 4 ++ app/views/conferences/show.html.erb | 11 +++-- app/views/layouts/application.html.erb | 1 + app/views/sessions/show.html.erb | 34 ++++++++++++++++ bin/importmap | 4 ++ config/importmap.rb | 7 ++++ config/routes.rb | 18 +++++++-- ...me_format_to_session_format_in_sessions.rb | 5 +++ db/schema.rb | 4 +- db/seeds.rb | 4 ++ .../assignments_controller_test.rb | 8 ++++ vendor/javascript/.keep | 0 27 files changed, 215 insertions(+), 8 deletions(-) create mode 100644 app/controllers/assignments_controller.rb create mode 100644 app/helpers/assignments_helper.rb create mode 100644 app/javascript/application.js create mode 100644 app/javascript/controllers/application.js create mode 100644 app/javascript/controllers/hello_controller.js create mode 100644 app/javascript/controllers/index.js create mode 100644 app/views/assignments/index.html.erb create mode 100644 app/views/assignments/show.html.erb create mode 100644 app/views/sessions/show.html.erb create mode 100755 bin/importmap create mode 100644 config/importmap.rb create mode 100644 db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb create mode 100644 test/controllers/assignments_controller_test.rb create mode 100644 vendor/javascript/.keep diff --git a/Gemfile b/Gemfile index 2712516..4717b9b 100644 --- a/Gemfile +++ b/Gemfile @@ -61,3 +61,7 @@ gem "solid_queue" gem "httparty" gem "tailwindcss-rails", "~> 2.6" + +gem "hotwire-rails", "~> 0.1.3" + +gem "importmap-rails", "~> 2.0" diff --git a/Gemfile.lock b/Gemfile.lock index 6d767c6..a07fabb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,11 +108,19 @@ GEM raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) + hotwire-rails (0.1.3) + rails (>= 6.0.0) + stimulus-rails + turbo-rails httparty (0.21.0) mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) i18n (1.14.4) concurrent-ruby (~> 1.0) + importmap-rails (2.0.1) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) io-console (0.7.2) irb (1.12.0) rdoc @@ -229,6 +237,8 @@ GEM sqlite3 (1.7.3-aarch64-linux) sqlite3 (1.7.3-arm64-darwin) sqlite3 (1.7.3-x86_64-linux) + stimulus-rails (1.3.3) + railties (>= 6.0.0) stringio (3.1.0) strscan (3.1.0) tailwindcss-rails (2.6.0-aarch64-linux) @@ -239,6 +249,10 @@ GEM railties (>= 7.0.0) thor (1.3.1) timeout (0.4.1) + turbo-rails (2.0.5) + actionpack (>= 6.0.0) + activejob (>= 6.0.0) + railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) web-console (4.2.1) @@ -264,7 +278,9 @@ DEPENDENCIES bootsnap capybara debug + hotwire-rails (~> 0.1.3) httparty + importmap-rails (~> 2.0) jbuilder puma (>= 5.0) rails (~> 7.1.2) diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index 338a0e8..d39ca55 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,3 +1,5 @@ //= link_tree ../images //= link_directory ../stylesheets .css //= link_tree ../builds +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb new file mode 100644 index 0000000..8209076 --- /dev/null +++ b/app/controllers/assignments_controller.rb @@ -0,0 +1,8 @@ +class AssignmentsController < ApplicationController + def index + @assignments = Assignment.all + if params[:user_id] + @assignments = @assignments.where(user_id: params[:user_id]) + end + end +end diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 3614abd..038e590 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -7,5 +7,6 @@ class ConferencesController < ApplicationController @conference = Conference.find_by(slug: params[:slug]) @sessions = @conference.sessions.where.not(starts_at: nil).includes(:stage).order(:starts_at) @sessions_by_date = @sessions.group_by{ |x| x.starts_at.to_date } + @users = User.all end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 47fa91b..5f107b0 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -16,4 +16,44 @@ class SessionsController < ApplicationController # Further filtering options can be added here end + + def show + @conference = Conference.find_by(slug: params[:slug]) + @session = Session.includes(:stage).find_by(ref_id: params[:ref_id]) + @users = User.all + end + + def assign_user + @session = Session.find_by(ref_id: params[:ref_id]) + @conference = Conference.find_by(slug: params[:slug]) + @user = User.find(params[:user_id]) + @assignment = @session.assignments.new(session: @session, user: @user) + @users = User.all + + if @assignment.save + flash.now[:success] = 'User assigned successfully.' + respond_to do |format| + # format.turbo_stream + format.html { redirect_to conference_session_path(@session.conference, @session), success: 'User assigned successfully.' } + end + else + flash.now[:alert] = 'Failed to assign user.' + respond_to do |format| + # format.turbo_stream { render :show, status: :unprocessable_entity } + format.html { render :show, status: :unprocessable_entity, alert: 'Failed to assign user.' } + end + end + end + + def unassign_user + @session = Session.find_by(ref_id: params[:ref_id]) + @user = User.find(params[:user_id]) + @assignment = Assignment.find_by(session: @session, user: @user) + + if @assignment&.destroy + redirect_to conference_session_path(@session.conference, @session), notice: 'User removed successfully.' + else + redirect_to conference_session_path(@session.conference, @session), alert: 'Failed to remove user.' + end + end end diff --git a/app/helpers/assignments_helper.rb b/app/helpers/assignments_helper.rb new file mode 100644 index 0000000..6f7c33b --- /dev/null +++ b/app/helpers/assignments_helper.rb @@ -0,0 +1,2 @@ +module AssignmentsHelper +end diff --git a/app/javascript/application.js b/app/javascript/application.js new file mode 100644 index 0000000..e239947 --- /dev/null +++ b/app/javascript/application.js @@ -0,0 +1,3 @@ +// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails +import "controllers" +import "@hotwired/turbo-rails" diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js new file mode 100644 index 0000000..1213e85 --- /dev/null +++ b/app/javascript/controllers/application.js @@ -0,0 +1,9 @@ +import { Application } from "@hotwired/stimulus" + +const application = Application.start() + +// Configure Stimulus development experience +application.debug = false +window.Stimulus = application + +export { application } diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js new file mode 100644 index 0000000..5975c07 --- /dev/null +++ b/app/javascript/controllers/hello_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.element.textContent = "Hello World!" + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js new file mode 100644 index 0000000..54ad4ca --- /dev/null +++ b/app/javascript/controllers/index.js @@ -0,0 +1,11 @@ +// Import and register all your controllers from the importmap under controllers/* + +import { application } from "controllers/application" + +// Eager load all controllers defined in the import map under controllers/**/*_controller +import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" +eagerLoadControllersFrom("controllers", application) + +// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) +// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" +// lazyLoadControllersFrom("controllers", application) diff --git a/app/models/assignment.rb b/app/models/assignment.rb index e8bb8a3..b6db132 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -1,4 +1,6 @@ class Assignment < ApplicationRecord belongs_to :user belongs_to :session + + validates :user_id, uniqueness: { scope: :session_id, message: "has already been assigned to this session" } end diff --git a/app/models/conference.rb b/app/models/conference.rb index 07e4145..40b40a5 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -6,4 +6,8 @@ class Conference < ApplicationRecord def days (starts_at.to_date..ends_at.to_date) end + + def to_param + slug + end end diff --git a/app/models/session.rb b/app/models/session.rb index 687e226..6a18ee7 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -5,4 +5,8 @@ class Session < ApplicationRecord has_many :users, through: :assignments validates :ref_id, uniqueness: { scope: :conference_id } + + def to_param + ref_id + end end diff --git a/app/views/assignments/index.html.erb b/app/views/assignments/index.html.erb new file mode 100644 index 0000000..139f949 --- /dev/null +++ b/app/views/assignments/index.html.erb @@ -0,0 +1,10 @@ +<div> + <% @assignments.group_by(&:user).each do |user, assignments| %> + <h4><%= user.name %></h4> + <ul> + <% assignments.each do |assignment| %> + <li><%= link_to assignment.session.title, assignment.session %></li> + <% end %> + </ul> + <% end %> +</div> \ No newline at end of file diff --git a/app/views/assignments/show.html.erb b/app/views/assignments/show.html.erb new file mode 100644 index 0000000..6c53075 --- /dev/null +++ b/app/views/assignments/show.html.erb @@ -0,0 +1,4 @@ +<div> + <h1 class="font-bold text-4xl">Assignments#show</h1> + <p>Find me in app/views/assignments/show.html.erb</p> +</div> diff --git a/app/views/conferences/show.html.erb b/app/views/conferences/show.html.erb index 3c73243..911fe41 100644 --- a/app/views/conferences/show.html.erb +++ b/app/views/conferences/show.html.erb @@ -52,10 +52,15 @@ current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance <h4><%= stage.name %></h4> <div class="stage-sessions"> <% sessions.each do |session| %> - <div class="session" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour%>px;"> - <h4><%= session.title %></h4> + <div class="session" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour%>px; overflow: scroll;"> + <h4><%= link_to session.title, [@conference, session] %></h4> <p class="session-time"><%= session.starts_at.strftime('%H:%M') %> - <%= session.ends_at.strftime('%H:%M') %></p> - <div class="session-desc"><%= session.description.html_safe %></div> + <%#<div class="session-desc"><%= session.description.html_safe %><%#/div>%> + <%= form_with url: assign_user_session_path(@conference, session), method: :post, local: true do |f| %> + <%= f.label :user_id, "Assign User" %> + <%= f.select :user_id, options_from_collection_for_select(@users, :id, :name) %> + <%= f.submit "Assign" %> + <% end %> </div> <% end %> </div> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index f02f8a2..9f79c2f 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -8,6 +8,7 @@ <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> <%= stylesheet_link_tag "application" %> + <%= javascript_importmap_tags %> </head> <body> diff --git a/app/views/sessions/show.html.erb b/app/views/sessions/show.html.erb new file mode 100644 index 0000000..32b19c4 --- /dev/null +++ b/app/views/sessions/show.html.erb @@ -0,0 +1,34 @@ +<div> + <h6><%= @session.conference.name %></h6> + <h1><%= @session.title %></h1> + <h2><%= @session.language %> <%= @session.is_interpreted ? "(live interpretation)" : "" %></h2> + <h2><%= @session.starts_at.strftime("%Y-%m-%d") %> <%= @session.starts_at.strftime("%H:%M") %> – <%= @session.ends_at.strftime("%H:%M") %></h2> + <h2><%= @session.stage.name %>· <%= @session.session_format %> · <%= @session.track %></h2> + <p><%= @session.description.html_safe %></p> + + <h3>Assigned Users</h3> + <ul> + <% @session.users.each do |user| %> + <li> + <%= user.name %> + <%= link_to '[Remove]', unassign_user_session_path([@session.conference, @session], user_id: user.id), data: { turbo_method: :delete, confirm: 'Are you sure?' } %> + </li> + <% end %> + </ul> + + <%= form_with url: assign_user_session_path([@session.conference, @session]), method: :post, local: true do |f| %> + <%= f.label :user_id, "Assign User" %> + <%= f.select :user_id, options_from_collection_for_select(@users, :id, :name) %> + <%= f.submit "Assign" %> + <div id="flash"> + <% flash.each do |key, value| %> + <div class="alert alert-<%= key %>"><%= value %></div> + <% end %> + </div> + <% if @assignment&.errors&.any? %> + <div class="alert alert-danger"> + <%= @assignment.errors.full_messages.join(", ") %> + </div> + <% end %> + <% end %> +</div> \ No newline at end of file diff --git a/bin/importmap b/bin/importmap new file mode 100755 index 0000000..36502ab --- /dev/null +++ b/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/config/importmap.rb b/config/importmap.rb new file mode 100644 index 0000000..cb0480c --- /dev/null +++ b/config/importmap.rb @@ -0,0 +1,7 @@ +# Pin npm packages by running ./bin/importmap + +pin "application" +pin "@hotwired/stimulus", to: "stimulus.min.js" +pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" +pin_all_from "app/javascript/controllers", under: "controllers" +pin "@hotwired/turbo-rails", to: "turbo.min.js" diff --git a/config/routes.rb b/config/routes.rb index a6369e4..2521790 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,4 @@ Rails.application.routes.draw do - get 'sessions/index' # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. @@ -10,7 +9,20 @@ Rails.application.routes.draw do # root "posts#index" root "conferences#index" - resources :conferences, path: '/', param: :slug do - resources :sessions, only: [:index] + resources :conferences, param: :slug do + resources :sessions do + resource :assigments + end end + + resources :assignments + resources :sessions, param: :ref_id + + # get 'conferences/:slug', to: 'conferences#show', as: :conference + # get 'conferences/:slug/:date', to: 'conferences#show', as: :conference_day + # get 'conferences/:slug/session/:ref_id', to: 'sessions#show', as: :conference_session + # post 'conferences/:slug/session/:ref_id/assignments', to: 'sessions#assign_user', as: :assign_user_session + # delete 'conferences/:slug/session/:ref_id/assignments', to: 'sessions#unassign_user', as: :unassign_user_session + # get 'assignments', to: 'assignments#index', as: :assignments + # get 'assignments/:user_id', to: 'assignments#index', as: :user_assignments end diff --git a/db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb b/db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb new file mode 100644 index 0000000..3bc825c --- /dev/null +++ b/db/migrate/20240524091736_rename_format_to_session_format_in_sessions.rb @@ -0,0 +1,5 @@ +class RenameFormatToSessionFormatInSessions < ActiveRecord::Migration[7.1] + def change + rename_column :sessions, :format, :session_format + end +end diff --git a/db/schema.rb b/db/schema.rb index cb01004..eff275d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_04_06_184312) do +ActiveRecord::Schema[7.1].define(version: 2024_05_24_091736) do create_table "assignments", force: :cascade do |t| t.integer "user_id", null: false t.integer "session_id", null: false @@ -39,7 +39,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_06_184312) do t.string "language" t.string "status" t.text "description" - t.string "format" + t.string "session_format" t.string "track" t.boolean "is_interpreted" t.datetime "starts_at" diff --git a/db/seeds.rb b/db/seeds.rb index 42df632..766011b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -29,3 +29,7 @@ Conference.find_or_create_by!(slug: "rp2024") do |c| } c.import_job_class = "republica_2023_or_later" end + +User.find_or_create_by!(email: "teal@teal.is") do |user| + user.name = "Teal" +end diff --git a/test/controllers/assignments_controller_test.rb b/test/controllers/assignments_controller_test.rb new file mode 100644 index 0000000..e733d3a --- /dev/null +++ b/test/controllers/assignments_controller_test.rb @@ -0,0 +1,8 @@ +require "test_helper" + +class AssignmentsControllerTest < ActionDispatch::IntegrationTest + test "should get show" do + get assignments_show_url + assert_response :success + end +end diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep new file mode 100644 index 0000000..e69de29 -- GitLab