Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions app/controllers/home/video_gallery_controller.rb

This file was deleted.

13 changes: 13 additions & 0 deletions app/controllers/home/video_library_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Home
class VideoLibraryController < ApplicationController
skip_before_action :authenticate_user!

def index
authorize! :home
base = VideoLibrary.where.not(youtube_url: [ nil, "" ]).order(position: :asc, created_at: :desc)
@tutorials = authorized_scope(base, with: HomePolicy).decorate

render "home/video_library/index"
end
end
end
21 changes: 20 additions & 1 deletion app/controllers/tutorials_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ def index
end
end

def video_library
authorize!
if turbo_frame_request?
per_page = params[:number_of_items_per_page].presence || 6
base_scope = authorized_scope(VideoLibrary.all)
filtered = base_scope.search_by_params(params)

@count_display = filtered.size == base_scope.size ? base_scope.size : "#{filtered.count}/#{base_scope.count}"
@video_library = filtered.order(:position).paginate(page: params[:page], per_page: per_page).decorate

render :video_library_lazy
else
@sectors = Sector.published.order(:name)
@category_types = CategoryType.published.general.order(:name).decorate

render :video_library
end
end

def show
@tutorial = @tutorial.decorate
authorize! @tutorial
Expand Down Expand Up @@ -119,7 +138,7 @@ def set_tutorial
# Strong parameters
def tutorial_params
params.require(:tutorial).permit(
:title, :body, :rhino_body, :position, :youtube_url,
:title, :body, :rhino_body, :position, :youtube_url, :type,
:featured, :published, :publicly_visible, :publicly_featured,
category_ids: [],
sector_ids: [],
Expand Down
24 changes: 19 additions & 5 deletions app/models/tutorial.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Tutorial < ApplicationRecord
class VideoLibrary < ApplicationRecord
self.table_name = 'tutorials'

Check failure on line 2 in app/models/tutorial.rb

View workflow job for this annotation

GitHub Actions / rubocop

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.

include Featureable, Publishable, TagFilterable, Trendable, RichTextSearchable

has_rich_text :rhino_body
Expand All @@ -9,9 +11,9 @@
has_many :categories, through: :categorizable_items
has_many :sectors, through: :sectorable_items
# Asset associations
has_one :primary_asset, -> { where(type: "PrimaryAsset") },
has_one :primary_asset, -> { where("type" => "PrimaryAsset") },
as: :owner, class_name: "PrimaryAsset", dependent: :destroy
has_many :gallery_assets, -> { where(type: "GalleryAsset") },
has_many :gallery_assets, -> { where("type" => "GalleryAsset") },
as: :owner, class_name: "GalleryAsset", dependent: :destroy
has_many :assets, as: :owner, dependent: :destroy

Expand All @@ -34,19 +36,22 @@
scope :body, ->(body) { where("body like ?", "%#{ body }%") }
scope :title, ->(title) { where("title like ?", "%#{ title }%") }
scope :tutorial_name, ->(tutorial_name) { title(tutorial_name) }
scope :tutorials, -> { where(type: 'Tutorial') }

Check failure on line 39 in app/models/tutorial.rb

View workflow job for this annotation

GitHub Actions / rubocop

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
scope :podcasts, -> { where(type: 'Podcast') }

Check failure on line 40 in app/models/tutorial.rb

View workflow job for this annotation

GitHub Actions / rubocop

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
scope :intros, -> { where(type: 'Intro') }

Check failure on line 41 in app/models/tutorial.rb

View workflow job for this annotation

GitHub Actions / rubocop

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
scope :with_sector_ids, ->(sector_hash) {
ids = sector_hash.values.reject(&:blank?).map(&:to_i)
return all if ids.empty?
joins(:sectorable_items)
.where(sectorable_items: { sectorable_type: "Tutorial", sector_id: ids })
.where(sectorable_items: { sectorable_type: "VideoLibrary", sector_id: ids })
.distinct
}

scope :with_category_ids, ->(category_hash) {
ids = category_hash.values.reject(&:blank?).map(&:to_i)
return all if ids.empty?
joins(:categorizable_items)
.where(categorizable_items: { categorizable_type: "Tutorial", category_id: ids })
.where(categorizable_items: { categorizable_type: "VideoLibrary", category_id: ids })
.distinct
}

Expand All @@ -73,3 +78,12 @@
resources
end
end

class Tutorial < VideoLibrary
end

class Podcast < VideoLibrary
end

class Intro < VideoLibrary
end
6 changes: 6 additions & 0 deletions app/views/shared/_navbar_menu.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@
<span>Organizations</span>
<% end %>
<% end %>

<%= link_to video_library_path,
class: "flex items-center gap-2 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
<i class="fas fa-circle-play"></i>
<span>Video Library</span>
<% end %>
</div>
</div>

Expand Down
6 changes: 6 additions & 0 deletions app/views/shared/_navbar_menu_mobile.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@
<span>Organizations</span>
<% end %>
<% end %>

<%= link_to video_library_path, class: "flex items-center px-4 py-2 text-sm text-white
hover:text-gray-700 hover:bg-gray-100 w-full space-x-2" do %>
<i class="fas fa-circle-play"></i>
<span>Video Library</span>
<% end %>
</div>
</div>

Expand Down
1 change: 1 addition & 0 deletions app/views/tutorials/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
value: f.object.title } %>
</div>
<div class="admin-only bg-blue-100 rounded grid grid-cols-2 items-center mt-6 md:mt-0 p-3">
<%= f.input :type, as: :select, collection: ['Tutorial', 'Podcast', 'Intro'], prompt: 'Select type' %>
<%= f.input :published, as: :boolean %>
<%= f.input :featured, as: :boolean %>
<%= f.input :publicly_visible, as: :boolean %>
Expand Down
40 changes: 40 additions & 0 deletions app/views/tutorials/video_library.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<% content_for(:page_bg_class, "public") %>
<%= render "shared/public_welcome_banner" %>

<div class="w-full max-w-7xl mx-auto <%= DomainTheme.bg_class_for(:tutorials) %> border border-gray-200 rounded-xl shadow p-6">
<div class="w-full">
<div class="flex items-start justify-between mb-6">
<div class="pr-6">
<h2 class="text-2xl font-semibold mb-2">
Video Library
</h2>

<p class="text-gray-600 max-w-2xl">
Explore our collection of tutorials, podcasts, and introductory videos
</p>
</div>

<div class="text-right text-end">
<% if allowed_to?(:new?, Tutorial) %>
<%= link_to "New #{Tutorial.model_name.human.downcase}",
new_tutorial_path,
class: "admin-only bg-blue-100 btn btn-primary-outline" %>
<% end %>
</div>
</div>

<%= render "search_boxes" %>

<% result_src = video_library_path + "?" + request.query_string %>

<%= turbo_frame_tag "video_library_results", src: result_src do %>
<div class="bg-white rounded-lg shadow-md p-6 mt-4 animate-pulse">
<div class="space-y-4">
<% 3.times do %>
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
<% end %>
</div>
</div>
<% end %>
</div>
</div>
22 changes: 22 additions & 0 deletions app/views/tutorials/video_library_lazy.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<%= turbo_frame_tag "video_library_results" do %>
<div class="bg-white rounded-lg shadow-md p-6 mt-4">
<div class="overflow-x-auto">
<div class="space-y-16">
<% if @video_library.any? %>
<% @video_library.each_with_index do |item, idx| %>
<%= render "tutorial", tutorial: item, show_divider: idx > 0 %>
<% end %>

<!-- Pagination -->
<div class="pagination flex justify-center mt-12">
<%= tailwind_paginate @video_library %>
</div>
<% else %>
<div class="text-gray-500 italic">
No videos found matching your filters.
</div>
<% end %>
</div>
</div>
</div>
<% end %>
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
resources :stories
resources :story_shares, only: [ :index, :show ]
resources :tutorials
get "video_library", to: "tutorials#video_library", as: :video_library
resources :user_forms
resources :windows_types
resources :workshop_ideas
Expand Down Expand Up @@ -176,7 +177,7 @@
resources :stories, only: :index
resources :community_news, only: :index
resources :events, only: :index
resources :video_gallery, only: :index
resources :video_library, only: :index
end

root to: "home#index"
Expand Down
6 changes: 6 additions & 0 deletions db/migrate/20260228174902_add_type_to_tutorials_for_sti.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddTypeToTutorialsForSti < ActiveRecord::Migration[8.0]
def change
add_column :tutorials, :type, :string, default: 'Tutorial', null: false
add_index :tutorials, :type
end
end
27 changes: 26 additions & 1 deletion spec/factories/tutorials.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
FactoryBot.define do
factory :tutorial do
factory :video_library do
title { "MyString" }
body { "MyText" }
featured { false }
published { false }
position { 1 }
youtube_url { "MyString" }
type { 'Tutorial' }

trait :featured do
featured { true }
Expand All @@ -26,5 +27,29 @@
trait :publicly_featured do
publicly_featured { true }
end

trait :tutorial do
type { 'Tutorial' }
end

trait :podcast do
type { 'Podcast' }
end

trait :intro do
type { 'Intro' }
end
end

factory :tutorial, class: 'Tutorial', parent: :video_library do
type { 'Tutorial' }
end

factory :podcast, class: 'Podcast', parent: :video_library do
type { 'Podcast' }
end

factory :intro, class: 'Intro', parent: :video_library do
type { 'Intro' }
end
end
36 changes: 36 additions & 0 deletions spec/models/tutorial_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,40 @@
expect(results).not_to include(draft_tutorial)
end
end

describe '.tutorials' do
let!(:tutorial) { create(:tutorial, title: 'Tutorial 1') }
let!(:podcast) { create(:podcast, title: 'Podcast 1') }
let!(:intro) { create(:intro, title: 'Intro 1') }

it 'returns only Tutorial type records' do
results = VideoLibrary.tutorials
expect(results).to include(tutorial)
expect(results).not_to include(podcast, intro)
end
end

describe '.podcasts' do
let!(:tutorial) { create(:tutorial, title: 'Tutorial 1') }
let!(:podcast) { create(:podcast, title: 'Podcast 1') }
let!(:intro) { create(:intro, title: 'Intro 1') }

it 'returns only Podcast type records' do
results = VideoLibrary.podcasts
expect(results).to include(podcast)
expect(results).not_to include(tutorial, intro)
end
end

describe '.intros' do
let!(:tutorial) { create(:tutorial, title: 'Tutorial 1') }
let!(:podcast) { create(:podcast, title: 'Podcast 1') }
let!(:intro) { create(:intro, title: 'Intro 1') }

it 'returns only Intro type records' do
results = VideoLibrary.intros
expect(results).to include(intro)
expect(results).not_to include(tutorial, podcast)
end
end
end
39 changes: 39 additions & 0 deletions spec/requests/home/video_library_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require "rails_helper"

RSpec.describe "Home::VideoLibrary", type: :request do
describe "GET /home/video_library" do
it "renders a successful response" do
get home_video_library_url
expect(response).to be_successful
end

it "displays all content types with youtube_url" do
tutorial = create(:tutorial, title: 'Tutorial Video', youtube_url: 'https://www.youtube.com/watch?v=1')
podcast = create(:podcast, title: 'Podcast Video', youtube_url: 'https://www.youtube.com/watch?v=2')
intro = create(:intro, title: 'Intro Video', youtube_url: 'https://www.youtube.com/watch?v=3')
get home_video_library_url
expect(response.body).to include(tutorial.title, podcast.title, intro.title)
end

it "excludes records without youtube_url" do
with_url = create(:tutorial, title: 'With URL', youtube_url: 'https://www.youtube.com/watch?v=1')
without_url = create(:podcast, title: 'No URL', youtube_url: nil)
get home_video_library_url
expect(response.body).to include(with_url.title)
expect(response.body).not_to include(without_url.title)
end

it "orders by position ascending, then created_at descending" do
item1 = create(:tutorial, position: 2, youtube_url: 'https://www.youtube.com/watch?v=1')
item2 = create(:podcast, position: 1, youtube_url: 'https://www.youtube.com/watch?v=2')
item3 = create(:intro, position: 1, youtube_url: 'https://www.youtube.com/watch?v=3')
get home_video_library_url
body = response.body
pos2 = body.index(item2.title)
pos3 = body.index(item3.title)
pos1 = body.index(item1.title)
expect(pos2).to be < pos3 unless pos2.nil? || pos3.nil?
expect(pos2).to be < pos1 unless pos2.nil? || pos1.nil?
end
end
end
25 changes: 25 additions & 0 deletions spec/requests/tutorials_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@
get tutorials_url
expect(response).to be_successful
end

it "shows only Tutorial type records" do
tutorial = create(:tutorial, title: 'Test Tutorial')
podcast = create(:podcast, title: 'Test Podcast')
intro = create(:intro, title: 'Test Intro')
get tutorials_url
expect(response.body).to include(tutorial.title)
expect(response.body).not_to include(podcast.title, intro.title)
end
end

describe "GET /video_library" do
it "renders a successful response" do
create(:tutorial)
get video_library_url
expect(response).to be_successful
end

it "shows all content types (Tutorial, Podcast, Intro)" do
tutorial = create(:tutorial, title: 'Test Tutorial')
podcast = create(:podcast, title: 'Test Podcast')
intro = create(:intro, title: 'Test Intro')
get video_library_url
expect(response.body).to include(tutorial.title, podcast.title, intro.title)
end
end

describe "GET /show" do
Expand Down
Loading
Loading