Skip to content

Conversation

@dgilperez
Copy link

@dgilperez dgilperez commented Jan 13, 2026

As per #186 (reply in thread)

Changes:

  • Add name_with_indent method to Category model using non-breaking spaces (U+00A0) for HTML-safe indentation of subcategories
  • Update all category select dropdowns to use alphabetically_by_hierarchy scope and name_with_indent for consistent hierarchical display
  • Add ml-4 margin to subcategory rows in the dropdown component
  • Add tests for name_with_indent and alphabetically_by_hierarchy

Summary by CodeRabbit

  • New Features

    • Category labels now display hierarchical indentation and parents precede children across selection interfaces.
  • UI

    • Multiple select dropdowns replaced with combobox inputs for smoother selection.
    • Selection bar icon colors and hover styles updated for clarity.
  • Localization

    • Bulk transaction edit dialog and transfer category strings added to translations.
  • Tests

    • Added tests for indented category labels and hierarchy-aware ordering.

✏️ Tip: You can customize this high-level summary in your review settings.

@dosubot
Copy link

dosubot bot commented Jan 13, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

Adds Category#name_with_indent and switches category option sources to hierarchy-aware ordering and indented labels across models, views, rule option builders, fixtures, tests, and localization keys.

Changes

Cohort / File(s) Summary
Category model
app/models/category.rb
Added name_with_indent returning plain name for roots or name prefixed with four non‑breaking spaces for subcategories.
Category dropdown row
app/views/category/dropdowns/_row.html.erb
Added conditional ml-4 CSS class when category.subcategory?.
Transaction forms & pages
app/views/transactions/_form.html.erb, app/views/transactions/show.html.erb, app/views/transactions/bulk_updates/new.html.erb
Replaced category selects with comboboxes using alphabetically_by_hierarchy and name_with_indent; bulk updates view strings switched to i18n.
Transfer show view
app/views/transfers/show.html.erb
Category select now uses alphabetically_by_hierarchy with :name_with_indent; label/blank text moved to i18n keys.
Rule option builders
app/models/rule/action_executor/set_transaction_category.rb, app/models/rule/condition_filter/transaction_category.rb
options now returns [name_with_indent, id] from family.categories.alphabetically_by_hierarchy instead of plain name/id pluck.
Rule views
app/views/rule/actions/_action.html.erb, app/views/rule/conditions/_condition.html.erb
Switched select-type inputs from select to combobox within a div.form-field.combobox.
Controller
app/controllers/transactions_controller.rb
@income_categories / @expense_categories now sourced from alphabetically_by_hierarchy.
Tests & fixtures
test/models/category_test.rb, test/models/rule_test.rb, test/fixtures/categories.yml
Added fixtures for parent/child categories and tests for name_with_indent and hierarchy-aware ordering/options.
Locales
config/locales/views/transactions/en.yml, config/locales/views/transfers/en.yml
Added translation keys used by bulk updates and transfers views.
UI tweak
app/views/transactions/_selection_bar.html.erb
Adjusted icon/link color classes (removed text-secondary, changed icon colors/hover behavior).

Sequence Diagram(s)

(omitted — changes are view/model/localization focused and do not introduce new multi-component sequential control flow)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • sokie
  • jjmata

Poem

🐇 I tuck names in gentle spaces bright,
Parents lead and children sit just right,
Dropdowns hum in ordered rows,
Indents hop where hierarchy grows,
A tiny nibble — the UI feels light. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: adding visual hierarchy for subcategories in category select dropdowns through indentation and hierarchical ordering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @app/views/transactions/bulk_updates/new.html.erb:
- Line 15: The collection_select call using form.collection_select (the
form.collection_select :category_id,
Current.family.categories.alphabetically_by_hierarchy, :id, :name_with_indent,
...) contains hardcoded user strings "Select a category" and "Category"; replace
these with i18n lookups via the t() helper (use descriptive translation keys,
add entries to your locale file for the prompt and label), e.g. change the
prompt option to t('...') and the label option to t('...'), and ensure the
chosen translation keys are added to the appropriate locale YAML so the UI text
is localized.

In @app/views/transfers/show.html.erb:
- Line 75: Replace the hardcoded user-facing strings in the collection_select
call with i18n lookups: update the label ("Category") and include_blank
("Uncategorized") options in the f.collection_select for :category_id to use
t(...) instead of literal strings (e.g., label: t('transfers.form.category') and
include_blank: t('transfers.form.uncategorized')) so the UI text is localized;
keep the same options and data attribute keys (f.collection_select,
:category_id, @categories.alphabetically_by_hierarchy, :id, :name_with_indent,
selected: @transfer.outflow_transaction.category&.id,
"data-auto-submit-form-target": "auto").
🧹 Nitpick comments (1)
test/models/category_test.rb (1)

34-54: Good test coverage for the new functionality.

The tests appropriately verify both the name_with_indent method and the alphabetically_by_hierarchy scope using fixtures for base cases, following project conventions.

Optionally, consider adding an edge-case test to verify the scope correctly orders a parent before its child even when the child's name would alphabetically precede the parent's (e.g., parent "Zebra" with child "Apple"). Create this on-the-fly within the test rather than in fixtures to confirm the COALESCE logic works as intended across different name ordering scenarios.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 425570c and 2f6b42a.

📒 Files selected for processing (7)
  • app/models/category.rb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • test/models/category_test.rb
🧰 Additional context used
📓 Path-based instructions (20)
**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rb: Application supports two modes: 'managed' and 'self_hosted' via Rails.application.config.app_mode
Use Current.user and Current.family instead of current_user / current_family for authentication context
Optimize database queries with proper indexes to prevent N+1 queries using includes/joins

**/*.rb: Use 2-space indentation for Ruby code
Use snake_case for method and variable names in Ruby
Use CamelCase for class and module names in Ruby
Run bin/rubocop before pushing code; use -A flag to auto-correct safe style violations

**/*.rb: Use Current.user for the current user. Do NOT use current_user.
Use Current.family for the current family. Do NOT use current_family.

Files:

  • app/models/category.rb
  • test/models/category_test.rb
app/models/**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/models/**/*.rb: Keep business logic in models using skinny controllers and fat models pattern
Store all monetary values in base currency (user's primary currency) and use Money objects for conversion
Use ActiveRecord validations for form convenience and complex business logic validations
Use Rails concerns and POROs for organizing business logic instead of services folder

app/models/**/*.rb: Models should answer questions about themselves: use account.balance_series not AccountSeries.new(account).call
Store all monetary values in base currency (user's primary currency). Use Money objects for currency conversion and formatting.

Files:

  • app/models/category.rb
**/*.{rb,erb}

📄 CodeRabbit inference engine (.cursor/rules/general-rules.mdc)

**/*.{rb,erb}: Use Current.user for accessing the current user. Do NOT use current_user
Use Current.family for accessing the current family. Do NOT use current_family

Files:

  • app/models/category.rb
  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • test/models/category_test.rb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.{rb,js,erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Files:

  • app/models/category.rb
  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • test/models/category_test.rb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.{rb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Use ActiveRecord validations for complex validations and business logic. Simple validations may be mirrored in ActiveRecord for form error handling convenience, but prioritize client-side form validation when possible

Files:

  • app/models/category.rb
  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • test/models/category_test.rb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
app/**/*.rb

📄 CodeRabbit inference engine (AGENTS.md)

Follow Rails conventions for folder and file names in the app directory

Files:

  • app/models/category.rb
**/*.{erb,rb}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,rb}: All user-facing strings must use localization (i18n). Always use t() helper. Update locale files for each new or changed element.
Use i18n interpolation for dynamic content: t("users.greeting", name: user.name)
Use Rails pluralization in i18n: t("transactions.count", count: @transactions.count)

Files:

  • app/models/category.rb
  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • test/models/category_test.rb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
{app/**/*.{erb,html},app/javascript/**/*.js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Prefer Hotwire-first approach: use Turbo + Stimulus for reactive UI without heavy JavaScript

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
app/views/**/_*.html.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use partials only for simple, static HTML with minimal logic in specific contexts

app/views/**/_*.html.erb: Use underscore prefix for partial files (e.g., _trend_change.html.erb, _form_errors.html.erb, _sync_indicator.html.erb)
Place context-specific partials in relevant controller view directory (e.g., accounts/_account_sidebar_tabs.html.erb)

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
app/views/**/*.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/views/**/*.erb: Always use the icon helper for icons in views, never use lucide_icon directly
Use Tailwind design tokens (e.g., text-primary, bg-container, border-secondary) instead of raw color names
Pass data to Stimulus controllers via data-*-value attributes instead of inline JavaScript
Prefer semantic HTML elements (dialog, details, summary) over JavaScript components
Use Turbo Frames for page sections instead of client-side solutions
Use server-side formatting for currencies, numbers, and dates instead of client-side

app/views/**/*.erb: Avoid heavy logic in ERB views; prefer helpers and components instead
Ensure ERB views pass erb-lint checks as configured in .erb_lint.yml

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.{css,erb}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Tailwind CSS v4.x with custom design system for styling

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.html.erb

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

**/*.html.erb: Prefer native HTML elements over JavaScript-based components. Use semantic HTML: for modals,

for disclosures
Leverage Turbo frames to break up pages instead of JavaScript-driven client-side solutions
Use the icon helper from application_helper.rb for icons. Never use lucide_icon helper directly

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
{app/views/**,app/helpers/**,app/javascript/controllers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

{app/views/**,app/helpers/**,app/javascript/controllers/**}: Reference maybe-design-system.css for base primitives, functional tokens, and component tokens before writing styles
Prefer using functional design system tokens (e.g., text-primary, bg-container, border-primary) from maybe-design-system.css instead of raw Tailwind color values

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
{app/views/**,app/helpers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

Always generate semantic HTML

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.{erb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/view_conventions.mdc)

**/*.{erb,html.erb}: Use ViewComponents when: element has complex logic or styling patterns, will be reused across multiple views/contexts, needs structured styling with variants/sizes, requires interactive behavior or Stimulus controllers, has configurable slots or complex APIs, or needs accessibility features or ARIA support
Use Partials when: element is primarily static HTML with minimal logic, used in only one or few specific contexts, is simple template content, doesn't need variants/sizes/complex configuration, or is more about content organization than reusable functionality
Prefer components over partials: if a component is available in app/components, use it; if not, look for a partial; if neither exists, decide between component or partial based on complexity and reusability criteria
Keep domain logic out of views: compute values like button classes, conditional logic, and data transformations in the component file, not the template file
Always use the declarative approach when integrating Stimulus controllers in views: the ERB template should declare what happens using data-* attributes, and the Stimulus controller should respond
Component controllers in app/components/ should only be used within their component templates; global controllers in app/javascript/controllers/ can be used across any view
Pass data from Rails to Stimulus using data-*-value attributes, not inline JavaScript

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.erb

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.erb: In view templates, use native HTML elements preferred over JS components: use <dialog> for modals, <details><summary> for disclosures
Leverage Turbo frames for page sections over client-side solutions in views
Always use server-side formatting for currencies, numbers, and dates in views
Always use icon helper in application_helper.rb. NEVER use lucide_icon directly in templates.
Always generate semantic HTML in view templates
Keep domain logic OUT of view templates. Logic belongs in component files, not template files.

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.{erb,js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,js,ts,tsx}: Use query params for state management over localStorage/sessions in views
In TailwindCSS, always reference app/assets/tailwind/maybe-design-system.css for primitives and tokens
Use functional tokens defined in design system (e.g., text-primary instead of text-white, bg-container instead of bg-white)
Pass data to Stimulus controllers via data-*-value attributes, not inline JavaScript

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
**/*.{js,ts,tsx,erb}

📄 CodeRabbit inference engine (CLAUDE.md)

In Stimulus controllers, use Declarative Actions (HTML declares what happens). Example: data-action="click->toggle#toggle"

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
test/**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

test/**/*.rb: Use Minitest with fixtures (never RSpec or FactoryBot) for testing
Use mocha gem for mocks and VCR for external API tests
Use fixtures for base cases in tests (2-3 per model) and create edge cases on-the-fly within test context
Test critical code paths only: test Commands for correct params, Queries for output

Write tests using Minitest and fixtures as the testing framework

test/**/*.rb: Always use Minitest and fixtures for testing, NEVER rspec or factories
For tests that require a large number of fixture records, use Rails helpers to help create the records needed for the test, then inline the creation instead of defining them in fixtures
Distinguish between commands and query methods. Test output of query methods; test that commands were called with the correct params.
Never test the implementation details of one class in another class's test suite
Use the mocha gem for stubs and mocks
Always prefer OpenStruct when creating mock instances, or in complex cases, a mock class
Only mock what's necessary. If you're not testing return values, don't mock a return value.

test/**/*.rb: Use Minitest with fixtures for testing. NEVER use RSpec or factories.
Use mocha gem for stubs and mocks. Prefer OpenStruct for mock instances. Only mock what's necessary.

Files:

  • test/models/category_test.rb
test/**/*_test.rb

📄 CodeRabbit inference engine (AGENTS.md)

Name test files with *_test.rb suffix and mirror the app/ directory structure

Files:

  • test/models/category_test.rb
🧠 Learnings (12)
📓 Common learnings
Learnt from: jjmata
Repo: we-promise/sure PR: 326
File: app/models/category_import.rb:10-10
Timestamp: 2025-11-14T14:22:37.076Z
Learning: In the CategoryImport model (app/models/category_import.rb), the import process implements a "last write wins" strategy where importing a category.csv file completely overrides existing category structures, including parent relationships. Setting category.parent = nil in the first pass is intentional behavior to ensure the CSV becomes the source of truth.
📚 Learning: 2025-11-14T14:22:37.076Z
Learnt from: jjmata
Repo: we-promise/sure PR: 326
File: app/models/category_import.rb:10-10
Timestamp: 2025-11-14T14:22:37.076Z
Learning: In the CategoryImport model (app/models/category_import.rb), the import process implements a "last write wins" strategy where importing a category.csv file completely overrides existing category structures, including parent relationships. Setting category.parent = nil in the first pass is intentional behavior to ensure the CSV becomes the source of truth.

Applied to files:

  • app/models/category.rb
  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • test/models/category_test.rb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2026-01-09T12:25:59.839Z
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:25:59.839Z
Learning: Do not compare hard-coded strings to identify the 'Uncategorized Investments' category in Ruby/Rails code (e.g., ct.category.name != "Uncategorized Investments"). Localization/internationalization can change strings. Prefer: (1) a model-level predicate such as ct.category.uncategorized_investment? or (2) compare by identity with a canonical category, e.g., ct.category != Category.uncategorized_investments. This improves maintainability and correctness across translations. Apply this pattern to Ruby files that perform category checks (controllers, models, or elsewhere).

Applied to files:

  • app/models/category.rb
  • test/models/category_test.rb
📚 Learning: 2026-01-09T12:26:07.143Z
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:26:07.143Z
Learning: When checking if a category is "Uncategorized Investments" in app/controllers/pages_controller.rb (or elsewhere), avoid comparing against the hard-coded string ct.category.name != "Uncategorized Investments" because it will break when localization is implemented. Instead, use a model-level predicate like ct.category.uncategorized_investment? or compare object identity like ct.category != Category.uncategorized_investments.

Applied to files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to config/locales/**/*.yml : Organize i18n keys hierarchically by feature: `accounts.index.title`, `transactions.form.amount_label`

Applied to files:

  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-11-24T16:56:13.406Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/project-conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:13.406Z
Learning: Applies to **/*.{rb,js,erb} : Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Applied to files:

  • app/views/transactions/_form.html.erb
📚 Learning: 2025-12-19T22:25:57.508Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T22:25:57.508Z
Learning: Applies to test/**/*_test.rb : Name test files with `*_test.rb` suffix and mirror the `app/` directory structure

Applied to files:

  • test/models/category_test.rb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to test/**/*.rb : Use fixtures for base cases in tests (2-3 per model) and create edge cases on-the-fly within test context

Applied to files:

  • test/models/category_test.rb
📚 Learning: 2025-11-24T16:57:02.574Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-24T16:57:02.574Z
Learning: Applies to test/**/*.rb : Never test the implementation details of one class in another class's test suite

Applied to files:

  • test/models/category_test.rb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.{erb,rb} : Use Rails pluralization in i18n: `t("transactions.count", count: transactions.count)`

Applied to files:

  • app/views/transactions/show.html.erb
📚 Learning: 2025-11-24T16:55:59.156Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/general-rules.mdc:0-0
Timestamp: 2025-11-24T16:55:59.156Z
Learning: Applies to **/*.{rb,erb} : Use `Current.family` for accessing the current family. Do NOT use `current_family`

Applied to files:

  • app/views/transactions/show.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.rb : Use `Current.family` for the current family. Do NOT use `current_family`.

Applied to files:

  • app/views/transactions/show.html.erb
🧬 Code graph analysis (2)
app/models/category.rb (1)
app/models/budget_category.rb (2)
  • subcategory? (64-66)
  • name (48-50)
test/models/category_test.rb (1)
app/models/category.rb (1)
  • name_with_indent (168-170)
🔇 Additional comments (4)
app/models/category.rb (1)

168-170: LGTM!

Clean implementation that follows the existing name_with_parent pattern. Using non-breaking spaces (\u00A0) is appropriate for HTML-safe indentation that won't collapse in the browser.

app/views/transactions/_form.html.erb (1)

27-27: LGTM!

The update correctly applies the hierarchical ordering and indented display to the category dropdown while maintaining proper i18n for the prompt and label.

app/views/category/dropdowns/_row.html.erb (1)

4-8: LGTM! Clean implementation of visual hierarchy.

The conditional ml-4 class for subcategories works well alongside the existing "corner-down-right" icon indicator. Using CSS margin here (rather than name_with_indent) is appropriate since this partial renders custom HTML rows with icons and menus, unlike the standard <select> elements in other views.

app/views/transactions/show.html.erb (1)

81-88: LGTM! Hierarchical category display implemented correctly.

The use of alphabetically_by_hierarchy scope ensures parents appear before their children, and name_with_indent provides the visual indentation in the select dropdown. The non-breaking spaces in name_with_indent will render correctly in the HTML <option> elements. Correctly uses Current.family per coding guidelines.

@jjmata jjmata self-requested a review January 13, 2026 10:42
@jjmata jjmata added this to the v0.6.7 milestone Jan 13, 2026
@dgilperez dgilperez force-pushed the fix/category-hierarchy-in-dropdowns branch from 2f6b42a to 54537f8 Compare January 13, 2026 10:44
Changes:
- Add `name_with_indent` method to Category model using non-breaking
  spaces (U+00A0) for HTML-safe indentation of subcategories
- Update category select dropdowns to use `alphabetically_by_hierarchy`
  scope and `name_with_indent` for consistent hierarchical display
- Add `ml-4` margin to subcategory rows in the dropdown component
- Add tests for `name_with_indent` and `alphabetically_by_hierarchy`
- Localize hardcoded strings in bulk_updates and transfers views

Affected views:
- transactions/show, _form, bulk_updates/new
- transfers/show
- category/dropdowns/_row
@dgilperez dgilperez force-pushed the fix/category-hierarchy-in-dropdowns branch from 54537f8 to 9e125c4 Compare January 13, 2026 11:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/views/transactions/_form.html.erb (1)

26-27: LGTM! Verified that income_categories and expense_categories are ActiveRecord relations with the correct classification filters. The alphabetically_by_hierarchy scope properly orders categories with parent awareness, and name_with_indent correctly adds indentation for visual hierarchy. i18n keys are properly structured.

Minor note: The controller applies .alphabetically to these collections, but the view's .alphabetically_by_hierarchy scope will override that ordering. Consider removing .alphabetically from the controller since it becomes unused.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54537f8 and 9e125c4.

📒 Files selected for processing (9)
  • app/models/category.rb
  • app/views/category/dropdowns/_row.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • config/locales/views/transactions/en.yml
  • config/locales/views/transfers/en.yml
  • test/models/category_test.rb
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/transactions/show.html.erb
  • app/views/transfers/show.html.erb
  • config/locales/views/transactions/en.yml
  • test/models/category_test.rb
🧰 Additional context used
📓 Path-based instructions (20)
config/**

📄 CodeRabbit inference engine (AGENTS.md)

Place configuration files in config/ directory

Files:

  • config/locales/views/transfers/en.yml
config/locales/**/*.yml

📄 CodeRabbit inference engine (CLAUDE.md)

Organize i18n keys hierarchically by feature: accounts.index.title, transactions.form.amount_label

Files:

  • config/locales/views/transfers/en.yml
{app/**/*.{erb,html},app/javascript/**/*.js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Prefer Hotwire-first approach: use Turbo + Stimulus for reactive UI without heavy JavaScript

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
app/views/**/_*.html.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use partials only for simple, static HTML with minimal logic in specific contexts

app/views/**/_*.html.erb: Use underscore prefix for partial files (e.g., _trend_change.html.erb, _form_errors.html.erb, _sync_indicator.html.erb)
Place context-specific partials in relevant controller view directory (e.g., accounts/_account_sidebar_tabs.html.erb)

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
app/views/**/*.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/views/**/*.erb: Always use the icon helper for icons in views, never use lucide_icon directly
Use Tailwind design tokens (e.g., text-primary, bg-container, border-secondary) instead of raw color names
Pass data to Stimulus controllers via data-*-value attributes instead of inline JavaScript
Prefer semantic HTML elements (dialog, details, summary) over JavaScript components
Use Turbo Frames for page sections instead of client-side solutions
Use server-side formatting for currencies, numbers, and dates instead of client-side

app/views/**/*.erb: Avoid heavy logic in ERB views; prefer helpers and components instead
Ensure ERB views pass erb-lint checks as configured in .erb_lint.yml

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.{css,erb}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Tailwind CSS v4.x with custom design system for styling

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.{rb,erb}

📄 CodeRabbit inference engine (.cursor/rules/general-rules.mdc)

**/*.{rb,erb}: Use Current.user for accessing the current user. Do NOT use current_user
Use Current.family for accessing the current family. Do NOT use current_family

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/models/category.rb
**/*.html.erb

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

**/*.html.erb: Prefer native HTML elements over JavaScript-based components. Use semantic HTML: for modals,

for disclosures
Leverage Turbo frames to break up pages instead of JavaScript-driven client-side solutions
Use the icon helper from application_helper.rb for icons. Never use lucide_icon helper directly

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.{rb,js,erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/models/category.rb
**/*.{rb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Use ActiveRecord validations for complex validations and business logic. Simple validations may be mirrored in ActiveRecord for form error handling convenience, but prioritize client-side form validation when possible

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/models/category.rb
{app/views/**,app/helpers/**,app/javascript/controllers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

{app/views/**,app/helpers/**,app/javascript/controllers/**}: Reference maybe-design-system.css for base primitives, functional tokens, and component tokens before writing styles
Prefer using functional design system tokens (e.g., text-primary, bg-container, border-primary) from maybe-design-system.css instead of raw Tailwind color values

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
{app/views/**,app/helpers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

Always generate semantic HTML

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.{erb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/view_conventions.mdc)

**/*.{erb,html.erb}: Use ViewComponents when: element has complex logic or styling patterns, will be reused across multiple views/contexts, needs structured styling with variants/sizes, requires interactive behavior or Stimulus controllers, has configurable slots or complex APIs, or needs accessibility features or ARIA support
Use Partials when: element is primarily static HTML with minimal logic, used in only one or few specific contexts, is simple template content, doesn't need variants/sizes/complex configuration, or is more about content organization than reusable functionality
Prefer components over partials: if a component is available in app/components, use it; if not, look for a partial; if neither exists, decide between component or partial based on complexity and reusability criteria
Keep domain logic out of views: compute values like button classes, conditional logic, and data transformations in the component file, not the template file
Always use the declarative approach when integrating Stimulus controllers in views: the ERB template should declare what happens using data-* attributes, and the Stimulus controller should respond
Component controllers in app/components/ should only be used within their component templates; global controllers in app/javascript/controllers/ can be used across any view
Pass data from Rails to Stimulus using data-*-value attributes, not inline JavaScript

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.erb

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.erb: In view templates, use native HTML elements preferred over JS components: use <dialog> for modals, <details><summary> for disclosures
Leverage Turbo frames for page sections over client-side solutions in views
Always use server-side formatting for currencies, numbers, and dates in views
Always use icon helper in application_helper.rb. NEVER use lucide_icon directly in templates.
Always generate semantic HTML in view templates
Keep domain logic OUT of view templates. Logic belongs in component files, not template files.

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.{erb,js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,js,ts,tsx}: Use query params for state management over localStorage/sessions in views
In TailwindCSS, always reference app/assets/tailwind/maybe-design-system.css for primitives and tokens
Use functional tokens defined in design system (e.g., text-primary instead of text-white, bg-container instead of bg-white)
Pass data to Stimulus controllers via data-*-value attributes, not inline JavaScript

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.{erb,rb}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,rb}: All user-facing strings must use localization (i18n). Always use t() helper. Update locale files for each new or changed element.
Use i18n interpolation for dynamic content: t("users.greeting", name: user.name)
Use Rails pluralization in i18n: t("transactions.count", count: @transactions.count)

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
  • app/models/category.rb
**/*.{js,ts,tsx,erb}

📄 CodeRabbit inference engine (CLAUDE.md)

In Stimulus controllers, use Declarative Actions (HTML declares what happens). Example: data-action="click->toggle#toggle"

Files:

  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rb: Application supports two modes: 'managed' and 'self_hosted' via Rails.application.config.app_mode
Use Current.user and Current.family instead of current_user / current_family for authentication context
Optimize database queries with proper indexes to prevent N+1 queries using includes/joins

**/*.rb: Use 2-space indentation for Ruby code
Use snake_case for method and variable names in Ruby
Use CamelCase for class and module names in Ruby
Run bin/rubocop before pushing code; use -A flag to auto-correct safe style violations

**/*.rb: Use Current.user for the current user. Do NOT use current_user.
Use Current.family for the current family. Do NOT use current_family.

Files:

  • app/models/category.rb
app/models/**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/models/**/*.rb: Keep business logic in models using skinny controllers and fat models pattern
Store all monetary values in base currency (user's primary currency) and use Money objects for conversion
Use ActiveRecord validations for form convenience and complex business logic validations
Use Rails concerns and POROs for organizing business logic instead of services folder

app/models/**/*.rb: Models should answer questions about themselves: use account.balance_series not AccountSeries.new(account).call
Store all monetary values in base currency (user's primary currency). Use Money objects for currency conversion and formatting.

Files:

  • app/models/category.rb
app/**/*.rb

📄 CodeRabbit inference engine (AGENTS.md)

Follow Rails conventions for folder and file names in the app directory

Files:

  • app/models/category.rb
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to config/locales/**/*.yml : Organize i18n keys hierarchically by feature: `accounts.index.title`, `transactions.form.amount_label`
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to config/locales/**/*.yml : Organize i18n keys hierarchically by feature: `accounts.index.title`, `transactions.form.amount_label`

Applied to files:

  • config/locales/views/transfers/en.yml
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.{erb,rb} : All user-facing strings must use localization (i18n). Always use `t()` helper. Update locale files for each new or changed element.

Applied to files:

  • config/locales/views/transfers/en.yml
📚 Learning: 2026-01-09T12:26:07.143Z
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:26:07.143Z
Learning: When checking if a category is "Uncategorized Investments" in app/controllers/pages_controller.rb (or elsewhere), avoid comparing against the hard-coded string ct.category.name != "Uncategorized Investments" because it will break when localization is implemented. Instead, use a model-level predicate like ct.category.uncategorized_investment? or compare object identity like ct.category != Category.uncategorized_investments.

Applied to files:

  • config/locales/views/transfers/en.yml
  • app/views/transactions/_form.html.erb
  • app/views/category/dropdowns/_row.html.erb
📚 Learning: 2025-12-18T07:48:15.175Z
Learnt from: jjmata
Repo: we-promise/sure PR: 464
File: config/locales/views/simplefin_items/zh-CN.yml:36-36
Timestamp: 2025-12-18T07:48:15.175Z
Learning: Enforce the exact branding: always use 'SimpleFIN' (FIN in all caps) and never 'SimpleFin' when referring to the Simple Financial Interchange protocol or SimpleFIN Bridge service across all locale files under config/locales/views. This applies to code, documentation, and translations. Update zh-CN.yml and other locale files as needed; perform a project-wide search/replace to fix inconsistent references and add a review check to catch any future deviations.

Applied to files:

  • config/locales/views/transfers/en.yml
📚 Learning: 2025-12-18T08:32:32.373Z
Learnt from: jjmata
Repo: we-promise/sure PR: 464
File: config/locales/views/simplefin_items/zh-CN.yml:36-36
Timestamp: 2025-12-18T08:32:32.373Z
Learning: In YAML locale files, ensure vendor branding (SimpleFIN) appears only in end-user visible strings (UI text, translations, user-facing messages). Internal keys such as logs, class names, and variable identifiers should not include the vendor branding and can use simpler naming like SimpleFin or simplefin. Apply this broadly to all locale files under config/locales.

Applied to files:

  • config/locales/views/transfers/en.yml
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.{erb,rb} : Use Rails pluralization in i18n: `t("transactions.count", count: transactions.count)`

Applied to files:

  • app/views/transactions/_form.html.erb
📚 Learning: 2025-11-24T16:56:13.406Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/project-conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:13.406Z
Learning: Applies to **/*.{rb,js,erb} : Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Applied to files:

  • app/views/transactions/_form.html.erb
📚 Learning: 2025-11-14T14:22:37.076Z
Learnt from: jjmata
Repo: we-promise/sure PR: 326
File: app/models/category_import.rb:10-10
Timestamp: 2025-11-14T14:22:37.076Z
Learning: In the CategoryImport model (app/models/category_import.rb), the import process implements a "last write wins" strategy where importing a category.csv file completely overrides existing category structures, including parent relationships. Setting category.parent = nil in the first pass is intentional behavior to ensure the CSV becomes the source of truth.

Applied to files:

  • app/views/category/dropdowns/_row.html.erb
📚 Learning: 2025-08-22T18:30:26.758Z
Learnt from: jjmata
Repo: we-promise/sure PR: 116
File: app/views/family_exports/_list.html.erb:16-28
Timestamp: 2025-08-22T18:30:26.758Z
Learning: In the family exports UI (app/views/family_exports/_list.html.erb), raw color utilities like bg-green-500/5, text-green-500, bg-red-500/5, text-red-500 are preferred over design system tokens for status indicators to maintain UI legibility and consistency with the existing import UI patterns. The user has indicated that design token replacements would make the UI illegible in this context.

Applied to files:

  • app/views/category/dropdowns/_row.html.erb
📚 Learning: 2026-01-09T12:25:59.839Z
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:25:59.839Z
Learning: Do not compare hard-coded strings to identify the 'Uncategorized Investments' category in Ruby/Rails code (e.g., ct.category.name != "Uncategorized Investments"). Localization/internationalization can change strings. Prefer: (1) a model-level predicate such as ct.category.uncategorized_investment? or (2) compare by identity with a canonical category, e.g., ct.category != Category.uncategorized_investments. This improves maintainability and correctness across translations. Apply this pattern to Ruby files that perform category checks (controllers, models, or elsewhere).

Applied to files:

  • app/models/category.rb
🧬 Code graph analysis (1)
app/models/category.rb (1)
app/models/budget_category.rb (1)
  • subcategory? (64-66)
🔇 Additional comments (4)
config/locales/views/transfers/en.yml (1)

21-22: LGTM!

The new i18n keys follow the hierarchical organization pattern (transfers.show.category_label) and are consistent with the existing naming conventions in this file (e.g., note_label, note_placeholder). Based on learnings and coding guidelines, this properly uses feature-based hierarchy.

app/views/category/dropdowns/_row.html.erb (1)

4-8: LGTM!

Clean implementation of visual hierarchy using Tailwind's ml-4 class for subcategory indentation. The conditional class pattern is idiomatic, and this complements the existing corner-down-right icon indicator for subcategories.

app/models/category.rb (2)

21-26: LGTM!

The alphabetically_by_hierarchy scope correctly implements hierarchical ordering:

  1. Groups categories by parent name (or own name for roots) via COALESCE
  2. Places roots before their subcategories via the IS NOT NULL ordering
  3. Sorts alphabetically within each group

This ensures the expected display order: Parent A → Child A1 → Child A2 → Parent B → Child B1, etc.


168-172: LGTM!

Good use of non-breaking spaces (\u00A0) for HTML-safe indentation in select dropdowns. Unlike regular spaces, these won't collapse in HTML rendering. The comment clearly documents the purpose.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

<%= f.fields_for :entryable do |ef| %>
<% categories = params[:nature] == "inflow" ? income_categories : expense_categories %>
<%= ef.collection_select :category_id, categories, :id, :name, { prompt: t(".category_prompt"), label: t(".category") } %>
<%= ef.collection_select :category_id, categories.alphabetically_by_hierarchy, :id, :name_with_indent, { prompt: t(".category_prompt"), label: t(".category") } %>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chained ordering prevents hierarchical category display in form

Medium Severity

The TransactionsController#new sets @income_categories and @expense_categories with .alphabetically already applied. The view then calls .alphabetically_by_hierarchy on these relations. In Rails, chaining .order() calls appends rather than replaces the ordering, so the original ORDER BY name from .alphabetically takes precedence. This causes categories to be sorted purely alphabetically by their own name instead of being grouped with parents followed by their children. The hierarchical ordering intended by this PR won't work for the new transaction form, though other views like bulk_updates/new.html.erb and transactions/show.html.erb that call .alphabetically_by_hierarchy directly on Current.family.categories work correctly.

Fix in Cursor Fix in Web

Copy link
Collaborator

@jjmata jjmata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subcategories already displayed like this:

Image

And I don't see any changes here in the Rules UI (which is one of the ones I know needed treatment):

Image

Can you include before/after screenshots to help the review, @dgilperez? 🙏

@dgilperez
Copy link
Author

dgilperez commented Jan 13, 2026

Nice, we are looking at different places ... I fixed the edit transaction one, didn't find the Rules UI.

Before:
Captura de pantalla 2026-01-13 a las 19 22 11

After (different data, sorry):
Captura de pantalla 2026-01-13 a las 20 38 15

Nvtl, I am tackling that other view as well now

@dgilperez
Copy link
Author

dgilperez commented Jan 13, 2026

We can also go and unify the UI and use a autocomplete feature as your first screenshot. I didn't notice that existed already.

@jjmata
Copy link
Collaborator

jjmata commented Jan 13, 2026

I fixed the edit transaction one, didn't find the Rules UI.

But edit transactions was already working, right? I actually saw the layout added width that showed a scrollbar.

@jjmata
Copy link
Collaborator

jjmata commented Jan 13, 2026

This is on app.sure.am right now:
Screenshot 2026-01-13 at 8 50 52 PM

Extends the visual hierarchy (indented subcategories) to rule condition
filters and action executors, matching the transaction form behavior.
@dgilperez
Copy link
Author

But edit transactions was already working, right? I actually saw the layout added width that showed a scrollbar.

Aha, so again we are looking at different edit paths. I fixed (and found, I didn't noticed the category was clickable) this flow: When you list transactions, select one, use the edit button on the floating bottom bar, the right sidebar select is flat and not sorted properly (that's the first commit or second).

@jjmata
Copy link
Collaborator

jjmata commented Jan 13, 2026

But edit transactions was already working, right? I actually saw the layout added width that showed a scrollbar.

Aha, so again we are looking at different edit paths. I fixed (and found, I didn't noticed the category was clickable) this flow: When you list transactions, select one, use the edit button on the floating bottom bar, the right sidebar select is flat and not sorted properly (that's the first commit or second).

Sooo many code-paths touch categories ... thanks for being thorough! 🙏

@dgilperez
Copy link
Author

Sooo many code-paths touch categories ... thanks for being thorough! 🙏

Thanks! and yes, it can be confusing, but in this case I think both paths are reasonable ...

I was thinking about the autocomplete. My first thought was to use the one you showed me everywhere, but that will not fit well with the regular forms. We'd need to use a simpler one, ie a new component. I am looking at the codebase to see if I can reuse something, but otherwise I'd leave it indented for now ... what do you think?

PS: The edit button in the floating bar is also broken in dark mode, its black over black ... shall I fix it in this PR or a new one?

@dgilperez
Copy link
Author

Captura de pantalla 2026-01-13 a las 21 07 49

This now

Replaces plain select dropdowns with searchable combobox for category
selection in rule actions and conditions. Works for initial render
(editing existing rules). Dynamic type changes still use plain select.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
app/views/rule/actions/_action.html.erb (1)

34-37: Template still uses plain form.select while initial render uses form.combobox.

The selectTemplate (used by Stimulus when the action type changes dynamically) still renders a plain form.select, creating a UX inconsistency with the initial combobox render on line 20. Users will see a rich combobox initially but a plain select after changing action types.

The commit message acknowledges this limitation ("dynamic type changes still fall back to plain select"). If this is intentional for now, consider adding a comment here noting the TODO, or update the template to use combobox for consistency.

Suggested comment to document the limitation
  <%# Templates for different input types - these will be cloned and used by the Stimulus controller %>
+ <%# TODO: Update to use combobox for consistency with initial render (line 20) %>
  <template data-rule--actions-target="selectTemplate">
    <span class="font-medium text-primary uppercase text-xs">to</span>
    <%= form.select :value, [], {} %>
  </template>
app/views/rule/conditions/_condition.html.erb (2)

32-32: Hardcoded placeholder string should use i18n.

Per coding guidelines, all user-facing strings must use localization. The placeholder "Enter a value" should use the t() helper.

Suggested fix
-        <%= form.text_field :value, placeholder: "Enter a value" %>
+        <%= form.text_field :value, placeholder: t("rules.conditions.value_placeholder") %>

Add the corresponding key to your locale file:

# config/locales/en.yml (or views/rules/en.yml)
en:
  rules:
    conditions:
      value_placeholder: "Enter a value"

Based on coding guidelines: "All user-facing strings must use localization (i18n). Always use t() helper."


11-13: Consider i18n for the "and" prefix label.

The hardcoded "and" string should use the t() helper for localization consistency.

Suggested fix
    <div class="pl-2" data-condition-prefix>
-     <span class="font-medium uppercase text-xs">and</span>
+     <span class="font-medium uppercase text-xs"><%= t("rules.conditions.and_prefix") %></span>
    </div>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 790129b and 00a9df3.

📒 Files selected for processing (2)
  • app/views/rule/actions/_action.html.erb
  • app/views/rule/conditions/_condition.html.erb
🧰 Additional context used
📓 Path-based instructions (15)
{app/**/*.{erb,html},app/javascript/**/*.js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Prefer Hotwire-first approach: use Turbo + Stimulus for reactive UI without heavy JavaScript

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
app/views/**/_*.html.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use partials only for simple, static HTML with minimal logic in specific contexts

app/views/**/_*.html.erb: Use underscore prefix for partial files (e.g., _trend_change.html.erb, _form_errors.html.erb, _sync_indicator.html.erb)
Place context-specific partials in relevant controller view directory (e.g., accounts/_account_sidebar_tabs.html.erb)

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
app/views/**/*.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/views/**/*.erb: Always use the icon helper for icons in views, never use lucide_icon directly
Use Tailwind design tokens (e.g., text-primary, bg-container, border-secondary) instead of raw color names
Pass data to Stimulus controllers via data-*-value attributes instead of inline JavaScript
Prefer semantic HTML elements (dialog, details, summary) over JavaScript components
Use Turbo Frames for page sections instead of client-side solutions
Use server-side formatting for currencies, numbers, and dates instead of client-side

app/views/**/*.erb: Avoid heavy logic in ERB views; prefer helpers and components instead
Ensure ERB views pass erb-lint checks as configured in .erb_lint.yml

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{css,erb}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Tailwind CSS v4.x with custom design system for styling

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{rb,erb}

📄 CodeRabbit inference engine (.cursor/rules/general-rules.mdc)

**/*.{rb,erb}: Use Current.user for accessing the current user. Do NOT use current_user
Use Current.family for accessing the current family. Do NOT use current_family

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.html.erb

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

**/*.html.erb: Prefer native HTML elements over JavaScript-based components. Use semantic HTML: for modals,

for disclosures
Leverage Turbo frames to break up pages instead of JavaScript-driven client-side solutions
Use the icon helper from application_helper.rb for icons. Never use lucide_icon helper directly

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{rb,js,erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{rb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Use ActiveRecord validations for complex validations and business logic. Simple validations may be mirrored in ActiveRecord for form error handling convenience, but prioritize client-side form validation when possible

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
{app/views/**,app/helpers/**,app/javascript/controllers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

{app/views/**,app/helpers/**,app/javascript/controllers/**}: Reference maybe-design-system.css for base primitives, functional tokens, and component tokens before writing styles
Prefer using functional design system tokens (e.g., text-primary, bg-container, border-primary) from maybe-design-system.css instead of raw Tailwind color values

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
{app/views/**,app/helpers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

Always generate semantic HTML

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{erb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/view_conventions.mdc)

**/*.{erb,html.erb}: Use ViewComponents when: element has complex logic or styling patterns, will be reused across multiple views/contexts, needs structured styling with variants/sizes, requires interactive behavior or Stimulus controllers, has configurable slots or complex APIs, or needs accessibility features or ARIA support
Use Partials when: element is primarily static HTML with minimal logic, used in only one or few specific contexts, is simple template content, doesn't need variants/sizes/complex configuration, or is more about content organization than reusable functionality
Prefer components over partials: if a component is available in app/components, use it; if not, look for a partial; if neither exists, decide between component or partial based on complexity and reusability criteria
Keep domain logic out of views: compute values like button classes, conditional logic, and data transformations in the component file, not the template file
Always use the declarative approach when integrating Stimulus controllers in views: the ERB template should declare what happens using data-* attributes, and the Stimulus controller should respond
Component controllers in app/components/ should only be used within their component templates; global controllers in app/javascript/controllers/ can be used across any view
Pass data from Rails to Stimulus using data-*-value attributes, not inline JavaScript

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.erb

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.erb: In view templates, use native HTML elements preferred over JS components: use <dialog> for modals, <details><summary> for disclosures
Leverage Turbo frames for page sections over client-side solutions in views
Always use server-side formatting for currencies, numbers, and dates in views
Always use icon helper in application_helper.rb. NEVER use lucide_icon directly in templates.
Always generate semantic HTML in view templates
Keep domain logic OUT of view templates. Logic belongs in component files, not template files.

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{erb,js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,js,ts,tsx}: Use query params for state management over localStorage/sessions in views
In TailwindCSS, always reference app/assets/tailwind/maybe-design-system.css for primitives and tokens
Use functional tokens defined in design system (e.g., text-primary instead of text-white, bg-container instead of bg-white)
Pass data to Stimulus controllers via data-*-value attributes, not inline JavaScript

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{erb,rb}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,rb}: All user-facing strings must use localization (i18n). Always use t() helper. Update locale files for each new or changed element.
Use i18n interpolation for dynamic content: t("users.greeting", name: user.name)
Use Rails pluralization in i18n: t("transactions.count", count: @transactions.count)

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
**/*.{js,ts,tsx,erb}

📄 CodeRabbit inference engine (CLAUDE.md)

In Stimulus controllers, use Declarative Actions (HTML declares what happens). Example: data-action="click->toggle#toggle"

Files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to config/locales/**/*.yml : Organize i18n keys hierarchically by feature: `accounts.index.title`, `transactions.form.amount_label`
Learnt from: jjmata
Repo: we-promise/sure PR: 326
File: app/models/category_import.rb:10-10
Timestamp: 2025-11-14T14:22:37.076Z
Learning: In the CategoryImport model (app/models/category_import.rb), the import process implements a "last write wins" strategy where importing a category.csv file completely overrides existing category structures, including parent relationships. Setting category.parent = nil in the first pass is intentional behavior to ensure the CSV becomes the source of truth.
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:26:07.143Z
Learning: When checking if a category is "Uncategorized Investments" in app/controllers/pages_controller.rb (or elsewhere), avoid comparing against the hard-coded string ct.category.name != "Uncategorized Investments" because it will break when localization is implemented. Instead, use a model-level predicate like ct.category.uncategorized_investment? or compare object identity like ct.category != Category.uncategorized_investments.
📚 Learning: 2025-11-24T16:56:13.406Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/project-conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:13.406Z
Learning: Applies to **/*.html.erb : Prefer native HTML elements over JavaScript-based components. Use semantic HTML: <dialog> for modals, <details><summary> for disclosures

Applied to files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.erb : In view templates, use native HTML elements preferred over JS components: use `<dialog>` for modals, `<details><summary>` for disclosures

Applied to files:

  • app/views/rule/conditions/_condition.html.erb
  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-09-23T22:22:00.149Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/stimulus_conventions.mdc:0-0
Timestamp: 2025-09-23T22:22:00.149Z
Learning: Applies to app/{views,components}/**/*.html.erb : In ERB views, wire Stimulus interactions declaratively using data-action to call controller methods

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-11-24T16:56:51.982Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/stimulus_conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:51.982Z
Learning: Applies to **/*_controller.{js,ts} : Use declarative actions in Stimulus controllers, not imperative event listeners - define actions in ERB templates using data-action attributes instead of binding events in the controller's connect() method

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to app/views/**/*.erb : Prefer semantic HTML elements (dialog, details, summary) over JavaScript components

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.{js,ts,tsx,erb} : In Stimulus controllers, use Declarative Actions (HTML declares what happens). Example: `data-action="click->toggle#toggle"`

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to app/views/**/*.erb : Pass data to Stimulus controllers via data-*-value attributes instead of inline JavaScript

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-09-23T22:23:22.963Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/view_conventions.mdc:0-0
Timestamp: 2025-09-23T22:23:22.963Z
Learning: Applies to app/{views,components}/**/*.html.erb : Integrate Stimulus declaratively in ERB: templates declare data-controller/actions/targets; controllers respond to those declarations

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-11-24T16:57:20.318Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/view_conventions.mdc:0-0
Timestamp: 2025-11-24T16:57:20.318Z
Learning: Applies to **/*.{erb,html.erb} : Always use the declarative approach when integrating Stimulus controllers in views: the ERB template should declare what happens using data-* attributes, and the Stimulus controller should respond

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-09-23T22:23:22.963Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/view_conventions.mdc:0-0
Timestamp: 2025-09-23T22:23:22.963Z
Learning: Applies to app/{views,components}/**/*.html.erb : Pass data from Rails to Stimulus using data-*-value attributes, not inline JavaScript

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-11-24T16:57:20.318Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/view_conventions.mdc:0-0
Timestamp: 2025-11-24T16:57:20.318Z
Learning: Applies to **/*.{erb,html.erb} : Pass data from Rails to Stimulus using data-*-value attributes, not inline JavaScript

Applied to files:

  • app/views/rule/actions/_action.html.erb
📚 Learning: 2025-09-23T22:23:22.963Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/view_conventions.mdc:0-0
Timestamp: 2025-09-23T22:23:22.963Z
Learning: Applies to app/views/**/*.html.erb : Use ViewComponents when elements have complex logic/styling, are reused across contexts, need variants/slots/accessibility, or require Stimulus interactivity

Applied to files:

  • app/views/rule/actions/_action.html.erb
🔇 Additional comments (2)
app/views/rule/actions/_action.html.erb (1)

19-20: Good: Combobox integration for initial render looks correct.

The form.combobox with the || [] fallback is a solid defensive pattern for handling nil options.

app/views/rule/conditions/_condition.html.erb (1)

26-27: LGTM! Combobox integration for condition values.

The switch to form.combobox for select-type filters is clean and consistent with the actions partial change. This will provide a better searchable experience for category selection.

Wrap combobox in 'form-field combobox' div to apply the dark mode
styles defined in application.css.
@dgilperez
Copy link
Author

Ok, this is definitely better. Looking into the other forms now.

Captura de pantalla 2026-01-13 a las 21 13 19 Captura de pantalla 2026-01-13 a las 21 13 24

- transactions/_form.html.erb: New transaction form
- transactions/bulk_updates/new.html.erb: Bulk update drawer
- transactions/show.html.erb: Edit transaction drawer (with auto-submit)

All category dropdowns now use hotwire_combobox for searchable selection
with hierarchical display and dark mode support.
Controller now uses alphabetically_by_hierarchy instead of alphabetically
to avoid conflicting ORDER BY clauses when chained in the view.
@dgilperez
Copy link
Author

dgilperez commented Jan 13, 2026

New transaction (expense / income) ✅
Edit transaction ✅
New rule (then side) ✅
Rule edit ✅

New rule (if side) -> this is a different situation, it needs more refactoring work and I'd just move on as it is (indented).

I'd say this is ready to UI-test

Icons now use white color on the dark bg-gray-900 bar, with dark text
on hover when background becomes light.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @app/views/transactions/bulk_updates/new.html.erb:
- Around line 15-23: The view is passing HTML attributes (class:, multiple:) in
the options hash for form.collection_select and form.select so they never reach
Rails as html_options; update the calls (and ensure
StyledFormBuilder#collection_select and #select forward attributes correctly) to
pass html attributes as the html_options argument (e.g., move class: into the
5th/6th param for collection_select and multiple: into the 4th param for select)
so Rails renders them as element attributes; adjust the StyledFormBuilder
implementations to accept an options and html_options signature and forward
html_options to super.
🧹 Nitpick comments (2)
app/views/transactions/_selection_bar.html.erb (1)

8-20: Prefer design tokens over raw text-gray-900 for icon hover, and avoid mixed color mechanisms

Line 14 and Line 19 use class: "group-hover:text-gray-900" (raw palette) while the rest of the styling leans on tokens like hover:bg-inverse. Consider switching the hover color to a token (e.g., group-hover:text-primary) and driving icon color consistently via classes (or consistently via color:), to keep dark-mode/theming behavior predictable. As per coding guidelines, prefer design system tokens in views.

Proposed diff (tokenize hover color; drive color via classes)
-      <%= icon "pencil-line", color: "white", class: "group-hover:text-gray-900" %>
+      <%= icon "pencil-line", class: "text-inverse group-hover:text-primary" %>

-        <%= icon "trash-2", color: "white", class: "group-hover:text-gray-900" %>
+        <%= icon "trash-2", class: "text-inverse group-hover:text-primary" %>
app/views/transactions/show.html.erb (1)

81-89: Move category options construction to controller or helper + ensure uncategorized can be selected

The Current.family.categories.alphabetically_by_hierarchy.map { |c| [c.name_with_indent, c.id] } pattern is duplicated across at least three views (show.html.erb, _form.html.erb, bulk_updates/new.html.erb). Prepare this in the controller or a helper method instead.

Additionally, the placeholder: t(".uncategorized") provides visual guidance but doesn't enable selecting uncategorized (nil value). You must explicitly include a nil option in the array (e.g., [t(".uncategorized"), nil] prepended to the options) to allow users to clear the category, similar to how collection_select uses include_blank: in other parts of the codebase (e.g., transfers/show.html.erb).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00a9df3 and 45b7a80.

📒 Files selected for processing (7)
  • app/controllers/transactions_controller.rb
  • app/views/rule/actions/_action.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/transactions/show.html.erb
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/views/rule/actions/_action.html.erb
🧰 Additional context used
📓 Path-based instructions (18)
{app/**/*.{erb,html},app/javascript/**/*.js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Prefer Hotwire-first approach: use Turbo + Stimulus for reactive UI without heavy JavaScript

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
app/views/**/_*.html.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use partials only for simple, static HTML with minimal logic in specific contexts

app/views/**/_*.html.erb: Use underscore prefix for partial files (e.g., _trend_change.html.erb, _form_errors.html.erb, _sync_indicator.html.erb)
Place context-specific partials in relevant controller view directory (e.g., accounts/_account_sidebar_tabs.html.erb)

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/rule/conditions/_condition.html.erb
app/views/**/*.erb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/views/**/*.erb: Always use the icon helper for icons in views, never use lucide_icon directly
Use Tailwind design tokens (e.g., text-primary, bg-container, border-secondary) instead of raw color names
Pass data to Stimulus controllers via data-*-value attributes instead of inline JavaScript
Prefer semantic HTML elements (dialog, details, summary) over JavaScript components
Use Turbo Frames for page sections instead of client-side solutions
Use server-side formatting for currencies, numbers, and dates instead of client-side

app/views/**/*.erb: Avoid heavy logic in ERB views; prefer helpers and components instead
Ensure ERB views pass erb-lint checks as configured in .erb_lint.yml

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{css,erb}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Tailwind CSS v4.x with custom design system for styling

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{rb,erb}

📄 CodeRabbit inference engine (.cursor/rules/general-rules.mdc)

**/*.{rb,erb}: Use Current.user for accessing the current user. Do NOT use current_user
Use Current.family for accessing the current family. Do NOT use current_family

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/controllers/transactions_controller.rb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.html.erb

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

**/*.html.erb: Prefer native HTML elements over JavaScript-based components. Use semantic HTML: for modals,

for disclosures
Leverage Turbo frames to break up pages instead of JavaScript-driven client-side solutions
Use the icon helper from application_helper.rb for icons. Never use lucide_icon helper directly

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{rb,js,erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/controllers/transactions_controller.rb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{rb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/project-conventions.mdc)

Use ActiveRecord validations for complex validations and business logic. Simple validations may be mirrored in ActiveRecord for form error handling convenience, but prioritize client-side form validation when possible

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/controllers/transactions_controller.rb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
{app/views/**,app/helpers/**,app/javascript/controllers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

{app/views/**,app/helpers/**,app/javascript/controllers/**}: Reference maybe-design-system.css for base primitives, functional tokens, and component tokens before writing styles
Prefer using functional design system tokens (e.g., text-primary, bg-container, border-primary) from maybe-design-system.css instead of raw Tailwind color values

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
{app/views/**,app/helpers/**}

📄 CodeRabbit inference engine (.cursor/rules/ui-ux-design-guidelines.mdc)

Always generate semantic HTML

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{erb,html.erb}

📄 CodeRabbit inference engine (.cursor/rules/view_conventions.mdc)

**/*.{erb,html.erb}: Use ViewComponents when: element has complex logic or styling patterns, will be reused across multiple views/contexts, needs structured styling with variants/sizes, requires interactive behavior or Stimulus controllers, has configurable slots or complex APIs, or needs accessibility features or ARIA support
Use Partials when: element is primarily static HTML with minimal logic, used in only one or few specific contexts, is simple template content, doesn't need variants/sizes/complex configuration, or is more about content organization than reusable functionality
Prefer components over partials: if a component is available in app/components, use it; if not, look for a partial; if neither exists, decide between component or partial based on complexity and reusability criteria
Keep domain logic out of views: compute values like button classes, conditional logic, and data transformations in the component file, not the template file
Always use the declarative approach when integrating Stimulus controllers in views: the ERB template should declare what happens using data-* attributes, and the Stimulus controller should respond
Component controllers in app/components/ should only be used within their component templates; global controllers in app/javascript/controllers/ can be used across any view
Pass data from Rails to Stimulus using data-*-value attributes, not inline JavaScript

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.erb

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.erb: In view templates, use native HTML elements preferred over JS components: use <dialog> for modals, <details><summary> for disclosures
Leverage Turbo frames for page sections over client-side solutions in views
Always use server-side formatting for currencies, numbers, and dates in views
Always use icon helper in application_helper.rb. NEVER use lucide_icon directly in templates.
Always generate semantic HTML in view templates
Keep domain logic OUT of view templates. Logic belongs in component files, not template files.

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{erb,js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,js,ts,tsx}: Use query params for state management over localStorage/sessions in views
In TailwindCSS, always reference app/assets/tailwind/maybe-design-system.css for primitives and tokens
Use functional tokens defined in design system (e.g., text-primary instead of text-white, bg-container instead of bg-white)
Pass data to Stimulus controllers via data-*-value attributes, not inline JavaScript

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{erb,rb}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{erb,rb}: All user-facing strings must use localization (i18n). Always use t() helper. Update locale files for each new or changed element.
Use i18n interpolation for dynamic content: t("users.greeting", name: user.name)
Use Rails pluralization in i18n: t("transactions.count", count: @transactions.count)

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/controllers/transactions_controller.rb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.{js,ts,tsx,erb}

📄 CodeRabbit inference engine (CLAUDE.md)

In Stimulus controllers, use Declarative Actions (HTML declares what happens). Example: data-action="click->toggle#toggle"

Files:

  • app/views/transactions/_selection_bar.html.erb
  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
  • app/views/transactions/show.html.erb
**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rb: Application supports two modes: 'managed' and 'self_hosted' via Rails.application.config.app_mode
Use Current.user and Current.family instead of current_user / current_family for authentication context
Optimize database queries with proper indexes to prevent N+1 queries using includes/joins

**/*.rb: Use 2-space indentation for Ruby code
Use snake_case for method and variable names in Ruby
Use CamelCase for class and module names in Ruby
Run bin/rubocop before pushing code; use -A flag to auto-correct safe style violations

**/*.rb: Use Current.user for the current user. Do NOT use current_user.
Use Current.family for the current family. Do NOT use current_family.

Files:

  • app/controllers/transactions_controller.rb
app/controllers/**/*.rb

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Rails strong parameters and CSRF protection throughout the application

Files:

  • app/controllers/transactions_controller.rb
app/**/*.rb

📄 CodeRabbit inference engine (AGENTS.md)

Follow Rails conventions for folder and file names in the app directory

Files:

  • app/controllers/transactions_controller.rb
🧠 Learnings (22)
📓 Common learnings
Learnt from: jjmata
Repo: we-promise/sure PR: 326
File: app/models/category_import.rb:10-10
Timestamp: 2025-11-14T14:22:37.076Z
Learning: In the CategoryImport model (app/models/category_import.rb), the import process implements a "last write wins" strategy where importing a category.csv file completely overrides existing category structures, including parent relationships. Setting category.parent = nil in the first pass is intentional behavior to ensure the CSV becomes the source of truth.
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to config/locales/**/*.yml : Organize i18n keys hierarchically by feature: `accounts.index.title`, `transactions.form.amount_label`
📚 Learning: 2025-08-22T18:30:26.758Z
Learnt from: jjmata
Repo: we-promise/sure PR: 116
File: app/views/family_exports/_list.html.erb:16-28
Timestamp: 2025-08-22T18:30:26.758Z
Learning: In the family exports UI (app/views/family_exports/_list.html.erb), raw color utilities like bg-green-500/5, text-green-500, bg-red-500/5, text-red-500 are preferred over design system tokens for status indicators to maintain UI legibility and consistency with the existing import UI patterns. The user has indicated that design token replacements would make the UI illegible in this context.

Applied to files:

  • app/views/transactions/_selection_bar.html.erb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to app/views/**/*.erb : Use Tailwind design tokens (e.g., text-primary, bg-container, border-secondary) instead of raw color names

Applied to files:

  • app/views/transactions/_selection_bar.html.erb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to app/views/**/*.erb : Use Turbo Frames for page sections instead of client-side solutions

Applied to files:

  • app/views/transactions/_selection_bar.html.erb
📚 Learning: 2025-11-24T16:56:13.406Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/project-conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:13.406Z
Learning: Applies to **/*.html.erb : Leverage Turbo frames to break up pages instead of JavaScript-driven client-side solutions

Applied to files:

  • app/views/transactions/_selection_bar.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.erb : Leverage Turbo frames for page sections over client-side solutions in views

Applied to files:

  • app/views/transactions/_selection_bar.html.erb
📚 Learning: 2026-01-09T12:26:07.143Z
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:26:07.143Z
Learning: When checking if a category is "Uncategorized Investments" in app/controllers/pages_controller.rb (or elsewhere), avoid comparing against the hard-coded string ct.category.name != "Uncategorized Investments" because it will break when localization is implemented. Instead, use a model-level predicate like ct.category.uncategorized_investment? or compare object identity like ct.category != Category.uncategorized_investments.

Applied to files:

  • app/views/transactions/_form.html.erb
  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-11-14T14:22:37.076Z
Learnt from: jjmata
Repo: we-promise/sure PR: 326
File: app/models/category_import.rb:10-10
Timestamp: 2025-11-14T14:22:37.076Z
Learning: In the CategoryImport model (app/models/category_import.rb), the import process implements a "last write wins" strategy where importing a category.csv file completely overrides existing category structures, including parent relationships. Setting category.parent = nil in the first pass is intentional behavior to ensure the CSV becomes the source of truth.

Applied to files:

  • app/views/transactions/_form.html.erb
  • app/controllers/transactions_controller.rb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.{erb,rb} : All user-facing strings must use localization (i18n). Always use `t()` helper. Update locale files for each new or changed element.

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.{erb,rb} : Use Rails pluralization in i18n: `t("transactions.count", count: transactions.count)`

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to config/locales/**/*.yml : Organize i18n keys hierarchically by feature: `accounts.index.title`, `transactions.form.amount_label`

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-10-07T09:51:02.265Z
Learnt from: jjmata
Repo: we-promise/sure PR: 189
File: app/views/password_resets/new.html.erb:14-14
Timestamp: 2025-10-07T09:51:02.265Z
Learning: In the we-promise/sure repository, use i18n methods (like `t()` or `I18n.t`) for all user-facing strings to maintain proper internationalization infrastructure. While translations should be provided, extensive localization work is not a priority at this stage of development.

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-10-01T11:40:36.711Z
Learnt from: jjmata
Repo: we-promise/sure PR: 173
File: app/mailers/email_confirmation_mailer.rb:9-9
Timestamp: 2025-10-01T11:40:36.711Z
Learning: In the we-promise/sure repository, i18n (internationalization) is used throughout the application for translations. When making branding configurable (e.g., product names), it's appropriate to add parameters like `product: product_name` to existing `t()` translation calls rather than replacing them with hardcoded strings.

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-11-24T16:56:13.406Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/project-conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:13.406Z
Learning: Applies to **/*.{rb,js,erb} : Format currencies, numbers, dates, and other values server-side, then pass to Stimulus controllers for display only

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to app/views/**/*.erb : Use server-side formatting for currencies, numbers, and dates instead of client-side

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
📚 Learning: 2025-11-24T16:56:13.406Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/project-conventions.mdc:0-0
Timestamp: 2025-11-24T16:56:13.406Z
Learning: Applies to **/*.html.erb : Prefer native HTML elements over JavaScript-based components. Use semantic HTML: <dialog> for modals, <details><summary> for disclosures

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.erb : In view templates, use native HTML elements preferred over JS components: use `<dialog>` for modals, `<details><summary>` for disclosures

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
📚 Learning: 2025-11-24T16:54:59.198Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T16:54:59.198Z
Learning: Applies to app/views/**/*.erb : Prefer semantic HTML elements (dialog, details, summary) over JavaScript components

Applied to files:

  • app/views/transactions/bulk_updates/new.html.erb
  • app/views/rule/conditions/_condition.html.erb
📚 Learning: 2026-01-09T12:25:59.839Z
Learnt from: jjmata
Repo: we-promise/sure PR: 593
File: app/controllers/pages_controller.rb:200-203
Timestamp: 2026-01-09T12:25:59.839Z
Learning: Do not compare hard-coded strings to identify the 'Uncategorized Investments' category in Ruby/Rails code (e.g., ct.category.name != "Uncategorized Investments"). Localization/internationalization can change strings. Prefer: (1) a model-level predicate such as ct.category.uncategorized_investment? or (2) compare by identity with a canonical category, e.g., ct.category != Category.uncategorized_investments. This improves maintainability and correctness across translations. Apply this pattern to Ruby files that perform category checks (controllers, models, or elsewhere).

Applied to files:

  • app/controllers/transactions_controller.rb
📚 Learning: 2025-11-24T16:55:59.156Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/general-rules.mdc:0-0
Timestamp: 2025-11-24T16:55:59.156Z
Learning: Applies to **/*.{rb,erb} : Use `Current.family` for accessing the current family. Do NOT use `current_family`

Applied to files:

  • app/controllers/transactions_controller.rb
📚 Learning: 2026-01-10T19:11:44.566Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T19:11:44.566Z
Learning: Applies to **/*.rb : Use `Current.family` for the current family. Do NOT use `current_family`.

Applied to files:

  • app/controllers/transactions_controller.rb
📚 Learning: 2025-11-24T16:56:44.818Z
Learnt from: CR
Repo: we-promise/sure PR: 0
File: .cursor/rules/self_improve.mdc:0-0
Timestamp: 2025-11-24T16:56:44.818Z
Learning: Document breaking changes in rules

Applied to files:

  • app/views/rule/conditions/_condition.html.erb
🔇 Additional comments (6)
app/controllers/transactions_controller.rb (1)

6-10: Controller category lists now match hierarchy-aware UI

Line 8-9 switching to alphabetically_by_hierarchy aligns new with the indented/hierarchical option rendering across the UI.

app/views/rule/conditions/_condition.html.erb (1)

25-36: Combobox value input: format is compatible and properly implemented

The condition.options implementation is correct and compatible with form.combobox. Verification shows:

  • Only 2 select filters exist (TransactionCategory, TransactionMerchant), both properly override options
  • Format is consistent: Both return [label, value] tuples matching the pattern used elsewhere (e.g., app/views/transactions/bulk_updates/new.html.erb with categories)
  • ID values (not strings): Both filters correctly use integer IDs as values: [c.name_with_indent, c.id] and [name, id] via pluck
  • hotwire_combobox compatibility: The gem is established in the codebase and handles keyboard navigation and selection behavior natively

No action needed; the implementation is sound.

app/views/transactions/_form.html.erb (1)

26-33: Verify placeholder allows clearing category for optional field

Transaction.category is optional (optional: true) with no presence validation, so category_id can be nil. However, the combobox is initialized with only mapped category options—no explicit blank choice. While a placeholder is provided, hotwire_combobox v0.4.0 may not create a selectable blank option from placeholder alone. Confirm the combobox allows users to clear/unset the category, or add an explicit blank option (e.g., include_blank: true or a leading blank entry in the categories array).

Optionally, extract categories.map { |c| [c.name_with_indent, c.id] } as a helper or pass it from the controller to avoid duplication across transaction forms.

app/views/transactions/bulk_updates/new.html.erb (3)

2-2: Nice: relative i18n key for the drawer header title.


7-13: LGTM: disclosure titles + date label localized; Date.current max constraint is fine.


29-30: Good: footer button texts localized; actions unchanged.

Comment on lines +15 to +23
<div class="form-field combobox">
<%= form.combobox :category_id,
Current.family.categories.alphabetically_by_hierarchy.map { |c| [c.name_with_indent, c.id] },
label: t(".category"),
placeholder: t(".category_prompt") %>
</div>
<%= form.collection_select :merchant_id, Current.family.available_merchants.alphabetically, :id, :name, { prompt: t(".merchant_prompt"), label: t(".merchant"), class: "text-subdued" } %>
<%= form.select :tag_ids, Current.family.tags.alphabetically.pluck(:name, :id), { include_blank: t(".none"), multiple: true, label: t(".tags"), container_class: "h-40" } %>
<%= form.text_area :notes, label: t(".notes"), placeholder: t(".notes_placeholder"), rows: 5 %>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where styled_form_with is defined and which builder it uses
rg -n --hidden --glob='!.git/**' '\bstyled_form_with\b' -S

Repository: we-promise/sure

Length of output: 9983


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate custom FormBuilder(s) and any overrides for select/collection_select
rg -n --hidden --glob='!.git/**' 'class .*FormBuilder\b|def (collection_select|select)\b' -S

Repository: we-promise/sure

Length of output: 397


🏁 Script executed:

cat -n app/helpers/styled_form_builder.rb

Repository: we-promise/sure

Length of output: 5654


Fix parameter positioning for collection_select and select helpers.

The StyledFormBuilder's select and collection_select methods pass the options hash unchanged to Rails' parent FormBuilder methods. HTML attributes like class: and multiple: must be in the html_options parameter (the 6th and 4th parameters respectively), not in options, or they won't be applied to the rendered HTML elements.

Proposed fix
-            <%= form.collection_select :merchant_id, Current.family.available_merchants.alphabetically, :id, :name, { prompt: t(".merchant_prompt"), label: t(".merchant"), class: "text-subdued" } %>
-            <%= form.select :tag_ids, Current.family.tags.alphabetically.pluck(:name, :id), { include_blank: t(".none"), multiple: true, label: t(".tags"), container_class: "h-40" } %>
+            <%= form.collection_select :merchant_id,
+                  Current.family.available_merchants.alphabetically,
+                  :id,
+                  :name,
+                  { prompt: t(".merchant_prompt"), label: t(".merchant") },
+                  { class: "text-subdued" } %>
+
+            <%= form.select :tag_ids,
+                  Current.family.tags.alphabetically.pluck(:name, :id),
+                  { include_blank: t(".none"), label: t(".tags"), container_class: "h-40" },
+                  { multiple: true } %>
🤖 Prompt for AI Agents
In @app/views/transactions/bulk_updates/new.html.erb around lines 15 - 23, The
view is passing HTML attributes (class:, multiple:) in the options hash for
form.collection_select and form.select so they never reach Rails as
html_options; update the calls (and ensure StyledFormBuilder#collection_select
and #select forward attributes correctly) to pass html attributes as the
html_options argument (e.g., move class: into the 5th/6th param for
collection_select and multiple: into the 4th param for select) so Rails renders
them as element attributes; adjust the StyledFormBuilder implementations to
accept an options and html_options signature and forward html_options to super.

@jjmata
Copy link
Collaborator

jjmata commented Jan 19, 2026

Would love to UI-test this like you said @dgilperez ... once the tests pass. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants