Add property picker dropdown to Today tab #66
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/property-picker-dropdown"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Changes
app/views/work_queue_items/index.html.erb: Replaced quick-add form with property-picker combobox dropdownapp/javascript/controllers/property_picker_controller.js: New Stimulus controller for open/close/filter/selectapp/assets/stylesheets/application.css: Added picker-dropdown, picker-option stylesapp/controllers/work_queue_items_controller.rb: Added@servicesto index action for service filter chipsapp/javascript/controllers/filter_controller.js: Removed unusedclearInputmethodconfig/puma.rb: Madeactivate_control_appdev-only to fix CI crashesTest Plan
Review Checklist
Related Notes
PR #66 Review
DOMAIN REVIEW
Stack identified: Ruby on Rails 8, Hotwired Stimulus, ERB templates, vanilla CSS, Puma, Woodpecker CI.
Stimulus controller (
property_picker_controller.js)connect()binds the document click listener,disconnect()removes it. No memory leak.close()method correctly guards withthis.element.contains(event.target)to avoid closing when clicking inside the picker.filter()is clean and correct: downcases both query and target text, handles empty query by showing all.select()properly guards against double-queuing withdataset.queued === "true".is-queuedclass and badge before the form submit response) is reasonable for this use case since the server will respond with a redirect or Turbo Stream anyway.View (
index.html.erb)data-search-text="<%= [...].compact.join(' ') %>"-- ERB<%= %>auto-escapes HTML by default in Rails. No XSS risk here.property.work_queue_items.where(work_date: @date).exists?-- this fires a new SQL query per property despite@propertiesalready including:work_queue_itemsvia.includes(:work_queue_items). The.where()on an already-loaded association bypasses the eager-loaded collection and hits the DB. This is an N+1 query. The same pattern already existed in_property_item.html.erbline 11, so this is pre-existing tech debt, not introduced by this PR. However, this PR now has TWO locations doing it (picker dropdown + property list partial), doubling the N+1 hit. Consider replacing withproperty.work_queue_items.any? { |wqi| wqi.work_date == @date }to use the already-loaded collection instead.style="display:none"is a pragmatic approach for programmatic submission. Acceptable.Controller (
work_queue_items_controller.rb)@services = Service.order(:name)-- straightforward query, no concerns.CSS
--spacing-md,--color-surface,--radius, etc.) consistently. No magic numbers.max-height: 60vhfor the dropdown is reasonable for mobile-first (this is a landscaping field app).-webkit-overflow-scrolling: touchis deprecated but harmless. Modern iOS handles momentum scrolling natively.z-index: 100-- adequate for a dropdown. No z-index escalation concerns in this codebase.Puma config
activate_control_app if ENV.fetch("RAILS_ENV", "development") == "development"-- good fix. The control app socket was likely causing CI crashes when Puma ran in test mode. The guard is correct.Filter controller cleanup
clearInputremoval is safe. The old quick-add form that triggeredturbo:submit-end->filter#clearInputno longer exists in the view -- the new property picker handles its own input clearing inselect().BLOCKERS
1. No test coverage for new functionality
This PR adds a new Stimulus controller, changes the view structure, adds
@servicesto the controller, and changes the property-adding UX flow. The existing request spec atspec/requests/work_queue_items_spec.rbtests basic CRUD but does not cover:@servicesassignment inindex(verify service filter chips render)The PR's Test Plan consists entirely of manual checkboxes. The project has a CI pipeline that runs
rspec-- new view behavior should have at least a request spec verifying the picker dropdown renders with properties and the service filter chips appear. This is a BLOCKER per the review criteria: "New functionality with zero test coverage."NITS
N+1 query (pre-existing but doubled):
property.work_queue_items.where(work_date: @date).exists?appears at line 25 in the picker and line 11 in_property_item.html.erb. Both bypass eager-loaded data. Considerproperty.work_queue_items.any? { |wqi| wqi.work_date == @date }to use the already-loaded association. Not a blocker since it is pre-existing, but this PR doubles the impact.Deprecated CSS property:
-webkit-overflow-scrolling: touch(line 804 of application.css) is deprecated. It is harmless but could be removed for cleanliness.Accessibility -- no
:hoverstate on picker options:.picker-option:activeis styled but there is no:hoverstate. On non-touch devices, users have no visual feedback when hovering over options. Adding.picker-option:hover { background: var(--color-accent-light); }would improve discoverability.Accessibility -- no ARIA attributes: The combobox pattern (input + dropdown list) should ideally have
role="combobox",aria-expanded,aria-controls, androle="listbox"/role="option"on the list and items. Not a blocker for an internal tool, but worth noting for future iteration.Keyboard navigation missing: The picker has no keyboard support (arrow keys to navigate options, Enter to select, Escape to close). Again, not a blocker for an internal mobile-first tool, but a gap.
quick-add-formCSS orphaned: The.quick-add-formstyles at lines 769-783 ofapplication.cssare now unused since the quick-add form was replaced by the property picker. Consider removing to avoid dead CSS accumulation.SOP COMPLIANCE
feature/property-picker-dropdown, not65-property-picker-dropdown. Convention requires{issue-number}-{kebab-case-purpose}.PROCESS OBSERVATIONS
createaction is a conservative approach. The main risk is the optimistic UI inselect()-- if the form submission fails, the option shows "Queued" but the item was not actually added. There is no error handling for submission failure in the Stimulus controller.VERDICT: NOT APPROVED
One blocker: no automated test coverage for new functionality. Add at least a request spec verifying the picker dropdown renders with property data and service filter chips on
GET /today.PR #66 Review (Re-review)
DOMAIN REVIEW
Stack: Ruby on Rails 8 / Stimulus / Turbo / vanilla CSS -- standard Rails monolith patterns.
Controller (
work_queue_items_controller.rb)@queued_property_idsuses.pluck(:property_id).to_set-- efficient single-column query, Set for O(1) lookup. Good fix from round 1._property_item.html.erbnow uses@queued_property_ids.include?(property.id)instead of the N+1property.work_queue_items.where(work_date: date).exists?. Correct.@queue_itemsand@queued_property_idsboth hitWorkQueueItem.where(work_date: @date)-- two queries where one could serve both. Not a blocker, but a minor optimization opportunity.Stimulus controller (
property_picker_controller.js)connect()/disconnect()properly bind and unbind the global click listener. No memory leak.close()guards againsteventbeing undefined (called without args) -- correct.select()has the error rollback viaturbo:submit-endwith{ once: true }-- good fix from round 1.filter()usestoLowerCase()on both query andsearchText-- case-insensitive matching works.form.requestSubmit()rather thanform.submit(), which correctly fires Turbo's submit handlers. Correct.View (
index.html.erb)role="combobox",aria-expanded,aria-controls,aria-autocomplete="list"on input.role="listbox"on the dropdown,role="option"on each<li>. This is the WAI-ARIA combobox pattern. Good fix from round 1.data-search-textuses<%= %>which auto-escapes HTML entities -- no XSS risk.property_idinput is set programmatically by the Stimulus controller beforerequestSubmit()-- clean separation.CSS (
application.css).quick-add-formstyles removed, replaced with.property-picker,.picker-dropdown,.picker-optionfamily. No orphaned rules remain.--spacing-*,--color-*,--radius) consistently. No magic numbers except60vhfor max-height (reasonable) andz-index: 100(matches the existingz-index: 100on.bottom-nav).-webkit-overflow-scrolling: touchfor iOS momentum scrolling -- appropriate for mobile-first app.Puma config (
config/puma.rb)activate_control_app if ENV.fetch("RAILS_ENV", "development") == "development"-- restricts control app to dev only. Fixes CI crashes. Reasonable, thoughRails.env.development?would be more idiomatic Rails. TheENV.fetchapproach works because Puma config loads before the full Rails environment, so this is actually the correct pattern here.Application controller (
application_controller.rb)allow_browser versions: :modern unless Rails.env.test?-- skips browser version check in test env. This prevents test failures from user-agent rejection. Standard practice.Filter controller (
filter_controller.js)clearInput()method which was bound to the old quick-add form'sturbo:submit-endevent. The old form no longer exists, so this is correct cleanup.Tests (
spec/requests/work_queue_items_spec.rb)data-controller, ARIA roles, and property name.data-queued="true"appears when a property is in the queue.BLOCKERS
None. All blockers from round 1 have been addressed:
@queued_property_idspreload.turbo:submit-endhandler reverts optimistic UI on failure.NITS
PR body inaccuracy: The Changes section states "Added
@servicesto index action for service filter chips" but the diff does not show@servicesbeing added. The actual change is@queued_property_ids. Minor copy error in the PR description.Duplicate query opportunity (lines 4-5 of
work_queue_items_controller.rb):@queue_itemsand@queued_property_idsboth queryWorkQueueItem.where(work_date: @date). Could derive the set from the already-loaded items:@queued_property_ids = @queue_items.map { |i| i.property_id }.to_set. Saves one DB query. Non-blocking.aria-selectedis always"false": The combobox pattern typically updatesaria-selected="true"on the focused/active option during keyboard navigation. Since keyboard navigation is not implemented yet (only mouse click), this is fine for now but worth noting for a future iteration.No keyboard navigation: The combobox has no arrow-key or Enter-key support. Users relying on keyboard-only interaction cannot navigate the dropdown. Not a blocker for this PR, but should be tracked as a follow-up accessibility item.
z-index: 100shared with bottom-nav: Both.picker-dropdownand.bottom-navusez-index: 100. If the picker dropdown ever overlaps the bottom nav area on small screens, they could conflict. The dropdown isposition: absoluterelative to.property-pickerandmax-height: 60vh, so overlap is unlikely but worth noting.SOP COMPLIANCE
feature/property-picker-dropdown, not65-property-picker-dropdown. Convention calls for{issue-number}-{kebab-case-purpose}.PROCESS OBSERVATIONS
feature/prefix instead of65-prefix) is a recurring pattern. Not blocking merge, but worth enforcing in future PRs.@servicesthat does not match the diff. Keeping PR descriptions accurate helps future archaeology.VERDICT: APPROVED
All blockers from round 1 are resolved. The code is correct, accessible, and well-structured. The nits above are non-blocking suggestions for future improvement.