Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
b0d1fa3
Rename Tutorial to Recording with is_tutorial and is_podcast booleans
maebeale Mar 8, 2026
ca54c03
Update views, nav, i18n, and form for Recording model
maebeale Mar 8, 2026
6f102c7
Rename Recording to YoutubeVideo throughout codebase
maebeale Mar 8, 2026
be08ac7
Change Community menu label from Video Library to Video Gallery
maebeale Mar 8, 2026
f9cae01
Rename YoutubeVideo to VideoRecording and filter video_library correctly
maebeale Mar 8, 2026
9a354dc
Fix migration file name to match class name
maebeale Mar 8, 2026
b87c719
Complete VideoRecording refactor: rename Tutorial, add boolean flags,…
maebeale Mar 8, 2026
8ed8d54
Update schema after VideoRecording migration
maebeale Mar 8, 2026
a41d92f
Fix CI failures: string quotes, update test files for VideoRecording
maebeale Mar 8, 2026
e01a799
Fix AhoyTrackable error: update bookmarked_tutorials source_type
maebeale Mar 8, 2026
0d7091d
Implement video_type query parameter filtering for video_recordings i…
maebeale Mar 8, 2026
a765e93
Move video_type dropdown into search_boxes form for better UX
maebeale Mar 8, 2026
f40d094
Update empty state message in index_lazy view
maebeale Mar 8, 2026
622cabb
Update home video gallery path to use video_recordings route
maebeale Mar 8, 2026
4822a9a
Fix route helper: use home_video_recordings_path instead of home_vide…
maebeale Mar 8, 2026
fd452e5
Fix home video gallery section to use VideoRecording model
maebeale Mar 8, 2026
bdd4d9b
Fix VideoRecording model scope: use correct table name
maebeale Mar 8, 2026
536585c
Add home? method to HomePolicy for authorization
maebeale Mar 8, 2026
9c8d04a
Fix partial path in home video_recordings view
maebeale Mar 8, 2026
ab822b5
Create VideoRecordingDecorator for proper decoration of models
maebeale Mar 8, 2026
d52fcbc
Rename is_tutorial to is_instructional and update page title
maebeale Mar 8, 2026
1015159
Fix remaining is_tutorial reference in video_library action
maebeale Mar 8, 2026
bcb85e4
Remove all backwards compatibility for 'tutorial' terminology
maebeale Mar 8, 2026
3b290ee
Remove all remaining Tutorial references throughout codebase
maebeale Mar 8, 2026
6c12b7d
Delete old tutorial_decorator.rb and update filter dropdown labels
maebeale Mar 8, 2026
0e60f84
Update bookmark model SQL queries and rename test file
maebeale Mar 8, 2026
44f9e1d
Consolidate migration: add is_instructional in preview migration
maebeale Mar 8, 2026
e4de2bd
Update seeds: rename is_tutorial to is_instructional
maebeale Mar 8, 2026
d7e8661
Move video type dropdown to same row as title search
maebeale Mar 8, 2026
b17bee7
Change "Browse all videos" link to show all video types
maebeale Mar 8, 2026
daa0209
Show search boxes on instructional videos page and default video type
maebeale Mar 8, 2026
59216ce
Remove hide_search parameter entirely
maebeale Mar 8, 2026
6467854
Rename video_type parameters: tutorials -> instructional, not_tutoria…
maebeale Mar 8, 2026
ca956b5
Simplify description text to prevent awkward line break
maebeale Mar 8, 2026
e56fa6b
Restore original description text with non-breaking space
maebeale Mar 8, 2026
2c2b11e
Increase paragraph max-width to prevent awkward text wrapping
maebeale Mar 8, 2026
8d67e49
Rename bookmarked_tutorials to bookmarked_video_recordings
maebeale Mar 9, 2026
bc6c473
Fix turbo frame IDs: tutorials_results -> video_recordings_results
maebeale Mar 9, 2026
79f19b5
Rename _tutorial partial to _video_recording with updated variable names
maebeale Mar 9, 2026
58543f5
Add video_recordings key to DomainTheme and update view calls
maebeale Mar 9, 2026
da928ee
Move and update video_recordings view spec file
maebeale Mar 9, 2026
a285547
Update en.yml: use video_recording instead of tutorial for i18n
maebeale Mar 9, 2026
5f47484
Remove resources :tutorials from routes
maebeale Mar 9, 2026
fd84996
Rename factory trait: tutorial -> instructional
maebeale Mar 9, 2026
c096c71
Rename spec variables: tutorial -> video_recording
maebeale Mar 9, 2026
37630bd
Update test specs: change all tutorial references to video_recording
maebeale Mar 9, 2026
d5b54ab
Consolidate non-RESTful video_library action into index
maebeale Mar 9, 2026
7e55f8b
Use dynamic model name for video recordings analytics title
maebeale Mar 9, 2026
a6da4b8
Add HTML/ERB formatting guidelines for closing tag brackets
maebeale Mar 9, 2026
18761af
Add HTML/ERB formatting guidelines to CLAUDE.md
maebeale Mar 9, 2026
ecfc8bf
Change video_recording plural name from Tutorials to Videos
maebeale Mar 9, 2026
cb08d8f
Remove redundant table_name declaration from VideoRecording
maebeale Mar 9, 2026
5966006
Remove unused home? method from HomePolicy
maebeale Mar 9, 2026
4599590
Fix rhino_body search to use plain_text_body
maebeale Mar 9, 2026
4aa3c25
Clean up home video_gallery naming
maebeale Mar 9, 2026
ac5e377
Move is_instructional and is_podcast to row below published booleans
maebeale Mar 9, 2026
1596d41
Remove tutorials color mapping from DomainTheme
maebeale Mar 9, 2026
ee08188
Add custom labels to is_instructional and is_podcast inputs
maebeale Mar 9, 2026
e2a5740
Restore full seed data and update tutorials to video_recordings
maebeale Mar 9, 2026
fb6d747
Fix remaining Tutorial references after VideoRecording refactor
maebeale Mar 9, 2026
5c131b3
Revert WindowsType references back to UPPERCASE
maebeale Mar 9, 2026
0d266f0
Update WindowsType references to sentence case
maebeale Mar 9, 2026
6d881a8
Create mixed published state for workshop variations
maebeale Mar 9, 2026
47921fc
Allow promoted variations to have mixed published states
maebeale Mar 9, 2026
0ac644c
Mix instructional flag in video recording seeds
maebeale Mar 9, 2026
397914a
Migrate decorator spec from Tutorial to VideoRecording
maebeale Mar 9, 2026
ecde54d
Update form HTML ids to use video_recording prefix and fix analytics …
maebeale Mar 9, 2026
efa1680
Rename tutorials_routing_spec.rb to video_recordings_routing_spec.rb
maebeale Mar 9, 2026
d5701b3
Rename spec/models/tutorial_spec.rb to video_recording_spec.rb
maebeale Mar 9, 2026
92e0a35
Fix capitalization: 'Video Type' to 'Video type'
maebeale Mar 9, 2026
bf2e396
Add context-aware back button to video show page
maebeale Mar 9, 2026
84fe1ae
Fix text capitalization: 'Video gallery' → 'Video Gallery'
maebeale Mar 9, 2026
e0265a1
Fix back button text: 'Video Gallery' → 'Video gallery'
maebeale Mar 9, 2026
a67fb45
Make top utility link context-aware based on video type
maebeale Mar 9, 2026
12724a3
Update back button styling to match top utility links
maebeale Mar 9, 2026
5b8527a
Fix test failures: add /tutorials route and update Workshop specs for…
maebeale Mar 9, 2026
88f3320
Remove legacy /tutorials route and update routing spec to test video_…
maebeale Mar 9, 2026
da0082a
Fix AddPositionIndexToCategory migration - remove problematic heal_po…
maebeale Mar 9, 2026
d4e1938
Fix db:dev:seed task to run db:seed first for required data
maebeale Mar 9, 2026
46aaf37
Disable email delivery during seeding
maebeale Mar 9, 2026
c245c8a
Change 'Video Gallery' to 'Videos' throughout UI
maebeale Mar 9, 2026
dfb1f3b
Hide video type dropdown in instructional mode and add link to all vi…
maebeale Mar 9, 2026
4aa1db7
Move 'See all Videos' link to top right of card
maebeale Mar 9, 2026
3b1b21c
Make instructional videos page darker background
maebeale Mar 9, 2026
2a449ee
Fix 'See all Videos' link to show all without param
maebeale Mar 9, 2026
48b3d12
Skip Devise confirmation during seeding to prevent mailer errors
maebeale Mar 9, 2026
cf7655d
Skip Devise confirmation for test users without confirmed_at
maebeale Mar 9, 2026
6d2739d
Make youtube_url required on VideoRecording
maebeale Mar 9, 2026
2109fc8
Use i18n model name in VideoRecording controller messages
maebeale Mar 9, 2026
5c254a3
Fix seed failures by disabling Devise mailer during seeding
maebeale Mar 9, 2026
b182f3b
Make 'See all Videos' link default dropdown to 'All videos'
maebeale Mar 9, 2026
d81948a
Apply domain theme coloring to video recording show page
maebeale Mar 9, 2026
314e048
Apply home video card styling to regular index page
maebeale Mar 9, 2026
96c2763
Change video recordings grid back to 2 columns
maebeale Mar 9, 2026
fb43441
Reorganize video card buttons for better layout
maebeale Mar 9, 2026
525699a
Rearrange video card buttons to bookmark on top
maebeale Mar 9, 2026
67b4ce9
Rearrange video card buttons to View and Bookmark on top
maebeale Mar 9, 2026
c564c31
Fix seed failures by removing invalid NullMailer assignment
maebeale Mar 9, 2026
5cb8138
Change video show 'Video gallery' link to 'Videos' with video_type=all
maebeale Mar 9, 2026
9a56e0d
Fix database seed failures by preventing Devise mailer invocation
maebeale Mar 9, 2026
40cbbae
Fix WorkshopVariation seed to handle existing records and set require…
maebeale Mar 9, 2026
bf2064b
Fix form path and model references in video recordings search boxes
maebeale Mar 9, 2026
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
25 changes: 18 additions & 7 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
# PRs
- After completing work, create a pull request using `gh pr create`
- Once the PR is created, prepend the PR number to the branch name (e.g., rename `maebeale/fix-login` to `maebeale/1234-fix-login`) using `git branch -m` and `git push origin -u` with the new name, then delete the old remote branch

# PRs
- After completing work, create a pull request using `gh pr create`
- Once the PR is created, prepend the PR number to the branch name (e.g., rename `maebeale/fix-login` to `maebeale/1234-fix-login`) using `git branch -m` and `git push origin -u` with the new name, then delete the old remote branch
- On every push, update the PR title and description to reflect the current diff

# Code style requirements:
- Use modern Ruby syntax
Expand Down Expand Up @@ -67,8 +64,22 @@ This project uses rubocop-rails-omakase. All code MUST follow these rules:
- No parentheses around conditions: `if foo` not `if (foo)`
- No semicolons to separate statements

# PRs
- On every push, update the PR title and description to reflect the current diff

# Git
- When rebasing onto main, review incoming changes for their intent and flag any oversights — missing tests, incomplete migrations, broken assumptions, or conflicts between the two branches. Check both directions: schema/model changes on either branch that affect views, partials, or layouts on the other (e.g., main redesigned a table's CSS but your branch adds new columns to it, or vice versa)

# HTML/ERB Formatting

## Tag attributes
- When a tag has long attributes, place the closing `>` on the same line as the last attribute
- Do NOT put the closing `>` on its own line
- Example (GOOD):
```erb
<div class="relative z-10 w-full bg-white text-gray-800 py-2 px-4"
id="dropdown">
```
- Example (BAD):
```erb
<div class="relative z-10 w-full bg-white text-gray-800 py-2 px-4"
id="dropdown"
>
```
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ This project uses rubocop-rails-omakase. All code MUST follow these rules:
- **No parentheses around conditions:** `if foo` not `if (foo)`
- **No semicolons** to separate statements

## HTML/ERB Formatting

### Tag Attributes
- **Closing `>` on same line as last attribute** — do not put `>` on its own line
- When attributes span multiple lines, keep the closing `>` with the last attribute
- Good: `<div class="..." id="...">` or `<div class="...\n id="...">`
- Bad: `<div class="...\n id="..."\n >`

## Related Files

When changing a model or controller, check whether these related files need updates:
Expand Down
10 changes: 5 additions & 5 deletions app/controllers/admin/analytics_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def index
@most_viewed_community_news = decorate_with_counts(most_viewed_for_model(CommunityNews, time_scope), :view_count)
@most_viewed_stories = decorate_with_counts(most_viewed_for_model(Story, time_scope), :view_count)
@most_viewed_quotes = decorate_with_counts(most_viewed_for_model(Quote, time_scope), :view_count)
@most_viewed_tutorials = decorate_with_counts(most_viewed_for_model(Tutorial, time_scope), :view_count)
@most_viewed_video_recordings = decorate_with_counts(most_viewed_for_model(VideoRecording, time_scope), :view_count)
@most_viewed_organizations = decorate_with_counts(most_viewed_for_model(Organization, time_scope), :view_count)
@most_viewed_events = decorate_with_counts(most_viewed_for_model(Event, time_scope), :view_count)
@most_viewed_people = decorate_with_counts(most_viewed_for_model(Person, time_scope), :view_count)
Expand All @@ -26,7 +26,7 @@ def index
@most_printed_stories = decorate_with_counts(most_printed_for_model(Story, time_scope), :print_count)
# @most_printed_workshop_variations = decorate_with_counts(most_printed_for_model(WorkshopVariation, time_scope), :print_count)
# @most_printed_quotes = decorate_with_counts(most_printed_for_model(Quote, time_scope), :print_count)
# @most_printed_tutorials = decorate_with_counts(most_printed_for_model(Tutorial, time_scope), :print_count)
# @most_printed_video_recordings = decorate_with_counts(most_printed_for_model(VideoRecording, time_scope), :print_count)
# @most_printed_organizations = decorate_with_counts(most_printed_for_model(Organization, time_scope), :print_count)
# @most_printed_events = decorate_with_counts(most_printed_for_model(Event, time_scope), :print_count)

Expand Down Expand Up @@ -68,9 +68,9 @@ def index
views: all_counts["view.quote"] || 0,
prints: all_counts["print.quote"] || 0
},
tutorials: {
views: all_counts["view.tutorial"] || 0,
prints: all_counts["print.tutorial"] || 0
video_recordings: {
views: all_counts["view.video_recording"] || 0,
prints: all_counts["print.video_recording"] || 0
},
organizations: {
views: all_counts["view.organization"] || 0,
Expand Down
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_recordings_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Home
class VideoRecordingsController < ApplicationController
skip_before_action :authenticate_user!

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

render "home/video_recordings/index"
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/taggings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def index
people: params[:people_page],
organizations: params[:organizations_page],
quotes: params[:quotes_page],
tutorials: params[:tutorials_page]
video_recordings: params[:video_recordings_page]
}

@grouped_tagged_items = TaggingSearchService.call(
Expand Down
130 changes: 0 additions & 130 deletions app/controllers/tutorials_controller.rb

This file was deleted.

143 changes: 143 additions & 0 deletions app/controllers/video_recordings_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
class VideoRecordingsController < ApplicationController
include AhoyTracking, TagAssignable
skip_before_action :authenticate_user!, only: [ :index, :show ]
before_action :set_video_recording, only: [ :show, :edit, :update, :destroy ]

def index
authorize!
@video_type = params[:video_type].presence || "other"

if turbo_frame_request?
per_page = params[:number_of_items_per_page].presence || 6
base_scope = filter_by_video_type(authorized_scope(VideoRecording.all))
filtered = base_scope.search_by_params(params)

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

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

render :index
end
end

def show
@video_recording = @video_recording.decorate
authorize! @video_recording
track_view(@video_recording)
end

def new
@video_recording = VideoRecording.new.decorate
authorize! @video_recording
set_form_variables
end

def edit
@video_recording = @video_recording.decorate
authorize! @video_recording
set_form_variables
end

def create
@video_recording = VideoRecording.new(video_recording_params)
authorize! @video_recording

success = false

VideoRecording.transaction do
if @video_recording.save
assign_associations(@video_recording)
success = true
end
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
Rails.logger.error "VideoRecording create failed: #{e.class} - #{e.message}"
raise ActiveRecord::Rollback
end

if success
redirect_to @video_recording, notice: "#{VideoRecording.model_name.human} was successfully created."
else
@video_recording = @video_recording.decorate
set_form_variables
render :new, status: :unprocessable_content
end
end

def update
authorize! @video_recording

success = false

VideoRecording.transaction do
if @video_recording.update(video_recording_params)
assign_associations(@video_recording)
success = true
end
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
Rails.logger.error "VideoRecording update failed: #{e.class} - #{e.message}"
raise ActiveRecord::Rollback
end

if success
redirect_to @video_recording, notice: "#{VideoRecording.model_name.human} was successfully updated.", status: :see_other
else
@video_recording = @video_recording.decorate
set_form_variables
render :edit, status: :unprocessable_content
end
end

def destroy
authorize! @video_recording
@video_recording.destroy!
redirect_to video_recordings_path, notice: "#{VideoRecording.model_name.human} was successfully destroyed."
end

# Optional hooks for setting variables for forms or index
def set_form_variables
@video_recording.build_primary_asset if @video_recording.primary_asset.blank?
@video_recording.gallery_assets.build
@categories_grouped =
Category
.includes(:category_type)
.published
.order(:position, :name)
.group_by(&:category_type)
.select { |type, _| type.nil? || type.published? }
.sort_by { |type, _| type&.name.to_s.downcase }
@sectors = Sector.published.order(:name)
end

private

def set_video_recording
@video_recording = VideoRecording.find(params[:id])
end

def filter_by_video_type(scope)
case @video_type
when "instructional"
scope.instructional
when "other"
scope.where(is_instructional: false)
else
scope
end
end

# Strong parameters
def video_recording_params
params.require(:video_recording).permit(
:title, :body, :rhino_body, :position, :youtube_url, :is_instructional, :is_podcast,
:featured, :published, :publicly_visible, :publicly_featured,
category_ids: [],
sector_ids: [],
primary_asset_attributes: [ :id, :file, :_destroy ],
gallery_assets_attributes: [ :id, :file, :_destroy ],
)
end
end
Loading
Loading