From ada4b03f92a05871e226663b60ded137c98fba20 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 7 Jul 2025 00:10:33 -0700 Subject: [PATCH] Consolidate attributes editor and fix dashboard image errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Merged duplicate attributes editors into single route - Removed /plan/:content_type/attributes-tailwind route and controller method - Moved modern Tailwind attributes editor to main /attributes route - Fixed dashboard ArgumentError: Nil location provided. Can't build URI - Enhanced HasImageUploads concern with better Active Storage handling - Added nil-safety guards to header_asset_for method - Improved caching logic to prevent nil value storage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/controllers/content_controller.rb | 16 +- app/models/concerns/has_image_uploads.rb | 55 ++- app/views/content/attributes.html.erb | 438 +++++++++++++----- .../content/attributes_tailwind.html.erb | 346 -------------- config/routes.rb | 1 - 5 files changed, 378 insertions(+), 478 deletions(-) delete mode 100644 app/views/content/attributes_tailwind.html.erb diff --git a/app/controllers/content_controller.rb b/app/controllers/content_controller.rb index 9a91b926..175657a3 100644 --- a/app/controllers/content_controller.rb +++ b/app/controllers/content_controller.rb @@ -3,7 +3,7 @@ # TODO: we should probably spin off an Api::ContentController for #api_sort and anything else # api-wise we need class ContentController < ApplicationController - layout 'tailwind', only: [:index, :show, :gallery, :references, :deleted, :attributes_tailwind] + layout 'tailwind', only: [:index, :show, :gallery, :references, :deleted, :attributes] before_action :authenticate_user!, except: [:show, :changelog, :api_sort, :gallery] \ + Rails.application.config.content_types[:all_non_universe].map { |type| type.name.downcase.pluralize.to_sym } @@ -16,7 +16,7 @@ class ContentController < ApplicationController before_action :cache_linkable_content_for_each_content_type, only: [:new, :show, :edit, :index] - before_action :set_attributes_content_type, only: [:attributes, :attributes_tailwind, :export_template, :reset_template] + before_action :set_attributes_content_type, only: [:attributes, :export_template, :reset_template] before_action :set_navbar_color, except: [:api_sort] before_action :set_navbar_actions, except: [:deleted, :api_sort] @@ -500,18 +500,6 @@ class ContentController < ApplicationController @dummy_model = @content_type_class.new end - def attributes_tailwind - @attribute_categories = @content_type_class - .attribute_categories(current_user, show_hidden: true) - .shown_on_template_editor - .order(:position) - - @dummy_model = @content_type_class.new - - # Use the Tailwind layout - render :attributes_tailwind - end - def export_template service = TemplateExportService.new(current_user, @content_type) diff --git a/app/models/concerns/has_image_uploads.rb b/app/models/concerns/has_image_uploads.rb index 813b2350..1ce4cc8f 100644 --- a/app/models/concerns/has_image_uploads.rb +++ b/app/models/concerns/has_image_uploads.rb @@ -35,17 +35,23 @@ module HasImageUploads # If we don't have any uploaded images, we look for saved Basil commissions if result.nil? && respond_to?(:basil_commissions) - result = basil_commissions.where.not(saved_at: nil).includes([:image_attachment]).sample.try(:image) + basil_image = basil_commissions.where.not(saved_at: nil).includes([:image_attachment]).sample.try(:image) + # Handle Active Storage attachments properly + if basil_image.present? && basil_image.respond_to?(:url) + begin + result = basil_image.url + rescue + result = nil + end + end end end - # Cache the result - @random_image_including_private_cache[key] = result + # Cache the result (only cache non-nil results to avoid issues) + @random_image_including_private_cache[key] = result if result.present? - # Finally, if we have no image upload, we return the default image for this type - result = result.presence || header_asset_for(self.class.name) - - result + # Finally, if we have no valid image URL, return the default image for this type + result.presence || header_asset_for(self.class.name) end def first_public_image(format = :medium) @@ -75,7 +81,17 @@ module HasImageUploads # Then check basil commissions if respond_to?(:basil_commissions) pinned_commission = basil_commissions.pinned.where.not(saved_at: nil).includes([:image_attachment]).first - return pinned_commission.try(:image) if pinned_commission.present? + if pinned_commission.present? + basil_image = pinned_commission.try(:image) + # Handle Active Storage attachments properly + if basil_image.present? && basil_image.respond_to?(:url) + begin + return basil_image.url + rescue + return nil + end + end + end end nil @@ -88,7 +104,17 @@ module HasImageUploads if respond_to?(:basil_commissions) pinned_commission = basil_commissions.pinned.where.not(saved_at: nil).includes([:image_attachment]).first - return pinned_commission.try(:image) if pinned_commission.present? + if pinned_commission.present? + basil_image = pinned_commission.try(:image) + # Handle Active Storage attachments properly + if basil_image.present? && basil_image.respond_to?(:url) + begin + return basil_image.url + rescue + return nil + end + end + end end nil @@ -109,9 +135,14 @@ module HasImageUploads # will not work. # # For direct view rendering, we use the relative asset path which works better with image_tag - Rails.env.production? ? - "https://www.notebook.ai" + ActionController::Base.helpers.asset_url("card-headers/#{class_name.downcase.pluralize}.webp") : - ActionController::Base.helpers.asset_path("card-headers/#{class_name.downcase.pluralize}.webp") + asset_filename = "card-headers/#{class_name.downcase.pluralize}.webp" + + result = Rails.env.production? ? + "https://www.notebook.ai" + ActionController::Base.helpers.asset_url(asset_filename) : + ActionController::Base.helpers.asset_path(asset_filename) + + # Ensure we never return nil - provide a fallback + result.presence || (Rails.env.production? ? "https://www.notebook.ai/assets/#{asset_filename}" : "/assets/#{asset_filename}") end end end diff --git a/app/views/content/attributes.html.erb b/app/views/content/attributes.html.erb index dc8e022c..b30ee23b 100644 --- a/app/views/content/attributes.html.erb +++ b/app/views/content/attributes.html.erb @@ -2,117 +2,345 @@ <%= render partial: 'content/components/parallax_header', locals: { content_type: @content_type, content_class: @content_type_class } %> <% end %> - +
+
+ + +
-
- <%= link_to '#' do %> -
-
-
Customize color
- You can recolor this page. + + +
+ +
+ + +
+ +
+ <%= image_tag "card-headers/#{@content_type.downcase.pluralize}.jpg", + class: "hero-bg-image w-full h-full object-cover", + alt: "#{@content_type.titleize} background" %> + +
+
-
- <% end %> -
-
- <%= link_to '#' do %> -
-
-
Customize header image
- You can change the header image. -
-
- <% end %> -
-
---> - -
- <%= link_to attribute_customization_tailwind_path(content_type: @content_type), class: 'btn purple' do %> - starTry the new template editor - <% end %> -
- -
-
    - <%= render partial: 'content/attributes/general_settings', locals: { content_type: @content_type, content_type_class: @content_type_class } %> - - <% @attribute_categories.each do |attribute_category| %> -
  • -
    - menu - <%= attribute_category.icon %> -
    - <%= attribute_category.label %> -
    - <% if attribute_category.hidden? %> - - visibility_off - <% end %> -
    - -
    -
    - <% if attribute_category.hidden? %> -
    - <%= render partial: 'content/attributes/category_visibility_controls', locals: { category: attribute_category } %> + + +
    +
    + +
    +
    + <%= @content_type_class.icon %>
    - - <% else %> -
    - <%= render partial: 'content/attributes/category_fields_customization', locals: { category: attribute_category } %> +
    +

    + <%= @content_type.titleize %> Template Editor +

    +

    + Edit the template used for your <%= @content_type.titleize.downcase %> pages. Adding or removing a field here will modify all of your already-created <%= @content_type.titleize.downcase %> pages also. +

    -
    - <%= render partial: 'content/attributes/category_ui_customization', locals: { category: attribute_category } %> - <%= render partial: 'content/attributes/category_visibility_controls', locals: { category: attribute_category } %> -
    - - <% end %> -
    -
    -
  • - <% end %> -
  • -
    - add - Add another category -
    -
    - <%= form_for(current_user.attribute_categories.build, method: :post) do |f| %> - <%= f.hidden_field :entity_type, value: @content_type %> -
    -
    - <%= f.text_area :label, class: 'materialize-textarea js-category-input' %> - <%= f.label :label, 'Category label' %> -
    -
    - <%= f.submit 'Add new category', class: "btn #{@content_type_class.color}" %>
    +
    +
-
-
- New: Notebook.ai can now suggest additional categories for your pages. -
-

-

- <%= f.button "Show suggestions", class: 'small btn white black-text js-show-category-suggestions' %> -
+ +
+ <% @attribute_categories.each do |category| %> + <%= render partial: 'content/attributes/tailwind/category_card', locals: { + category: category, + content_type_class: @content_type_class, + content_type: @content_type + } %> <% end %> + + +
+
+ add_circle_outline + Add a New Category +
+ + + +
- - -
\ No newline at end of file +
+ + +
+ + +
+ + +
+
+ tune +
+

Configure Your Template

+

+ Select a category or field to customize it, or add a new one to get started. +

+

Tip: You can drag and drop to reorder categories and fields.

+ +
+ + +
+ +
+
+
+ Loading field settings... +
+
+
+ + +
+ +
+
+
+ Loading category settings... +
+
+
+ + +
+ <%= render partial: 'content/attributes/tailwind/general_settings', locals: { + content_type: @content_type, + content_type_class: @content_type_class + } %> +
+ +
+
+
+ + + + + + + \ No newline at end of file diff --git a/app/views/content/attributes_tailwind.html.erb b/app/views/content/attributes_tailwind.html.erb deleted file mode 100644 index b30ee23b..00000000 --- a/app/views/content/attributes_tailwind.html.erb +++ /dev/null @@ -1,346 +0,0 @@ -<%= content_for :full_width_page_header do %> - <%= render partial: 'content/components/parallax_header', locals: { content_type: @content_type, content_class: @content_type_class } %> -<% end %> - -<%= javascript_pack_tag 'template_editor' %> - - -
- - -
-
- - -
-
- - -
- -
- - -
- -
- <%= image_tag "card-headers/#{@content_type.downcase.pluralize}.jpg", - class: "hero-bg-image w-full h-full object-cover", - alt: "#{@content_type.titleize} background" %> - -
-
-
- - -
-
- -
-
- <%= @content_type_class.icon %> -
-
-

- <%= @content_type.titleize %> Template Editor -

-

- Edit the template used for your <%= @content_type.titleize.downcase %> pages. Adding or removing a field here will modify all of your already-created <%= @content_type.titleize.downcase %> pages also. -

-
-
-
-
-
- - -
- <% @attribute_categories.each do |category| %> - <%= render partial: 'content/attributes/tailwind/category_card', locals: { - category: category, - content_type_class: @content_type_class, - content_type: @content_type - } %> - <% end %> - - -
-
- add_circle_outline - Add a New Category -
- - - -
-
-
- - -
- - -
- - -
-
- tune -
-

Configure Your Template

-

- Select a category or field to customize it, or add a new one to get started. -

-

Tip: You can drag and drop to reorder categories and fields.

- -
- - -
- -
-
-
- Loading field settings... -
-
-
- - -
- -
-
-
- Loading category settings... -
-
-
- - -
- <%= render partial: 'content/attributes/tailwind/general_settings', locals: { - content_type: @content_type, - content_type_class: @content_type_class - } %> -
- -
-
-
-
- - - - - - \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 6fc73ef3..154187a1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -338,7 +338,6 @@ Rails.application.routes.draw do # Attributes get ':content_type/attributes', to: 'content#attributes', as: :attribute_customization - get ':content_type/attributes-tailwind', to: 'content#attributes_tailwind', as: :attribute_customization_tailwind get ':content_type/template/export', to: 'content#export_template', as: :export_template delete ':content_type/template/reset', to: 'content#reset_template', as: :reset_template end