diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d15266e03..e207f267e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -59,6 +59,16 @@ module ApplicationHelper current_page?(path) || (request.path.start_with?(path) && path != "/") end + # Wraps a nav-item hash so a single call performs both halves of a + # beta-gated entry: returns `nil` for users without the flag (so the + # entry never reaches the rendered nav), and stamps `beta: true` on + # the hash for users with the flag (so the partial paints the violet + # dot on the icon). Use inside an `Array#compact` nav-items list. + def beta_gated_nav_item(item) + return nil unless beta_features_enabled? + item.merge(beta: true) + end + # Wrapper around I18n.l to support custom date formats def format_date(object, format = :default, options = {}) date = object.to_date diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 73a6ced37..80f66cd48 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,7 +11,7 @@ else { name: t(".nav.transactions"), path: transactions_path, icon: "credit-card", icon_custom: false, active: page_active?(transactions_path) }, { name: t(".nav.reports"), path: reports_path, icon: "chart-bar", icon_custom: false, active: page_active?(reports_path) }, { name: t(".nav.budgets"), path: budgets_path, icon: "map", icon_custom: false, active: page_active?(budgets_path) }, - (beta_features_enabled? ? { name: t(".nav.goals"), path: goals_path, icon: "piggy-bank", icon_custom: false, active: page_active?(goals_path), beta: true } : nil), + beta_gated_nav_item({ name: t(".nav.goals"), path: goals_path, icon: "piggy-bank", icon_custom: false, active: page_active?(goals_path) }), { name: t(".nav.assistant"), path: chats_path, icon: "icon-assistant", icon_custom: true, active: page_active?(chats_path), mobile_only: true } ].compact end %> diff --git a/docs/llm-guides/gating-a-beta-feature.md b/docs/llm-guides/gating-a-beta-feature.md index df120b31e..bc0b1d5ed 100644 --- a/docs/llm-guides/gating-a-beta-feature.md +++ b/docs/llm-guides/gating-a-beta-feature.md @@ -61,18 +61,18 @@ Same pattern works for dashboard widgets, scoreboard cards, anything that surfac The desktop sidebar rail and the mobile bottom nav both render from `app/views/layouts/shared/_nav_item.html.erb`. The partial accepts an optional `beta:` local — when true, it overlays a violet dot-only pill on the icon so opted-in users can tell at a glance that the rail entry leads to a beta surface. -Build the nav-item hash conditionally inside the `beta_features_enabled?` branch and set `beta: true` on it. The compact form using `Array#compact` keeps the array clean: +Use the `beta_gated_nav_item` helper to wrap the entry. It returns `nil` for non-beta users (so the entry never enters the nav, once `Array#compact` runs) and stamps `beta: true` for opted-in users (so the partial paints the dot). One call, both halves of the gate: ```erb <% mobile_nav_items = [ { name: t(".nav.home"), path: root_path, icon: "pie-chart", icon_custom: false, active: page_active?(root_path) }, { name: t(".nav.transactions"), path: transactions_path, icon: "credit-card", icon_custom: false, active: page_active?(transactions_path) }, - (beta_features_enabled? ? { name: t(".nav.goals"), path: goals_path, icon: "piggy-bank", icon_custom: false, active: page_active?(goals_path), beta: true } : nil), + beta_gated_nav_item({ name: t(".nav.goals"), path: goals_path, icon: "piggy-bank", icon_custom: false, active: page_active?(goals_path) }), { name: t(".nav.assistant"), path: chats_path, icon: "icon-assistant", icon_custom: true, active: page_active?(chats_path), mobile_only: true } ].compact %> ``` -Two things happen from this single change: non-beta users never see the entry (the `nil` gets compacted out) and beta users see the entry with the dot marker (the partial reads `beta:` and renders the pill). You don't need to touch `_nav_item.html.erb` itself. +You don't need to touch `_nav_item.html.erb` or set `beta: true` by hand. Adding a new beta nav entry is one helper call wrapped around the same hash you'd write anyway. ## Marking the feature in the UI @@ -131,7 +131,7 @@ When a feature moves from beta to general availability, removing the gate is a s 1. Drop the `before_action :require_beta_features!` line from the controller. 2. Unwrap the `if beta_features_enabled?` blocks in views. -3. Drop the `DS::Pill` markers from headers and section titles, and drop the `beta: true` flag from the nav-item hash. +3. Drop the `DS::Pill` markers from headers and section titles, and unwrap the `beta_gated_nav_item(...)` call back into a plain nav-item hash. 4. Delete the controller / view tests that exercise the redirect. Grep for `require_beta_features!` and `beta_features_enabled?` near your feature to confirm nothing's left behind.