Add nav, work queue, and fix CSRF/mobile polish #6
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/nav-and-work-queue"
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/controllers/work_queue_items_controller.rb: CRUD actions with Turbo Stream responsesapp/views/work_queue_items/: Index, partials, and Turbo Stream templates for create/update/destroyapp/views/layouts/application.html.erb: Bottom nav, viewport-fit=cover for safe-areaapp/assets/stylesheets/application.css: Queue, day-nav, filter chip, and bottom-nav styles; 44px touch targets; safe-area paddingapp/javascript/controllers/filter_controller.js: Search and service filter Stimulus controllerconfig/environments/development.rb: Disable CSRF origin check for Tailscale funnelconfig/routes.rb: work_queue_items resourceRakefile: Fix stale dev port (9999 → 3000)Test Plan
Review Checklist
Related Notes
PR #6 Review
DOMAIN REVIEW
Stack: Rails 8.1, Hotwire (Turbo Streams + Stimulus), vanilla CSS, PostgreSQL
Rails / Controller
Date.parsewithout rescue (work_queue_items_controller.rb, lines 3 and 14):Date.parse(params[:date])will raiseDate::Erroron malformed input (e.g.?date=garbage). This is user-supplied input arriving via query string. Rails will turn this into a 500. Wrap in a begin/rescue or use a safer parse pattern.N+1 on
_property_item.html.erbline 8:property.work_queue_items.where(work_date: date).exists?fires a query per property. The@propertiesquery inindexdoesincludes(:services)but does not include:work_queue_items, so this is an N+1. At scale this will degrade noticeably. Consider preloading or building aSetof queued property IDs in the controller.updateaction does not check.updatereturn value (line 30):@item.update(completed: !@item.completed)-- if the update fails for any reason, the controller still renders the Turbo Stream response as if it succeeded. Should handle the failure case.createposition race condition (line 15):WorkQueueItem.where(work_date: ...).countis not atomic. Two concurrent creates for the same date could get the same position value. Not critical for a single-user app, but worth noting.Migration
uniqueness: { scope: :work_date }onproperty_id, and the controller querieswhere(work_date: @date).order(:position). The migration only creates an index onproperty_id(viat.references). A composite index on[:work_date, :property_id](unique) would enforce the uniqueness constraint at the DB level and optimize the most common query pattern. Currently the uniqueness validation is only enforced at the application level, which is insufficient under concurrent requests.Turbo Streams
create.turbo_stream.erbappends the queue item, replaces the property item to show "Queued" badge, and removes the empty state.destroy.turbo_stream.erbremoves the item and replaces the property to restore the add button.update.turbo_stream.erbreplaces the item in-place for the completion toggle. All target IDs match (dom_id(item),property_{id},queue-list,queue-empty). This is clean Hotwire.CSS / Mobile
CSS uses design tokens consistently. Touch targets are 2.75rem (44px) which meets the WCAG minimum.
env(safe-area-inset-bottom)andviewport-fit=covercorrectly handle iPhone notch/home-bar.page-contentpadding-bottom of 5rem clears the fixed bottom nav. No Tailwind (as required by project preferences).One hardcoded color:
.btn-remove:hover { background: #fef2f2; }(line 400 in CSS). All other colors use tokens. Consider adding a--color-danger-lighttoken for consistency.Stimulus
filter_controller.jsis clean. Targets are properly declared, search is case-insensitive, service filter toggling works via class manipulation. No DOM injection vulnerabilities -- it only readsdatasetattributes and togglesdisplay.CSRF
forgery_protection_origin_check = falseindevelopment.rbis acceptable for the stated use case (Tailscale funnel proxy with origin mismatch). The comment explains the rationale. CSRF token validation still runs. This is dev-only -- production config is unaffected.XSS Surface
_property_item.html.erbline 2:data-search-text="<%= [property.address_line, ...].compact.join(' ') %>"-- ERB's<%= %>auto-escapes by default in Rails, so user content in data attributes is safe here. The output is used as a data attribute value (read by JS for string matching), not rendered as HTML.BLOCKERS
B1: Test coverage is effectively zero.
spec/models/work_queue_item_spec.rbcontains only apendingplaceholder -- no actual tests. This is a new resource with a full CRUD controller, model validations, uniqueness constraint, and Turbo Stream responses. Per BLOCKER criteria: "New functionality with zero test coverage." At minimum, needs:for_today,pending)B2: Unvalidated user input on
params[:date].Date.parse(params[:date])in bothindexandcreateactions will raise an unhandled exception on malformed input. This is a 500 error reachable by any user typing a bad URL. Needs a rescue or validation.NITS
N1. Hardcoded color:
.btn-remove:hoveruses#fef2f2instead of a design token. Add--color-danger-light: #fef2f2to:root.N2. Empty WorkQueue comment block (CSS line 264-266): There is a
Component: WorkQueuesection heading comment with no styles between it and theDayNavcomment. Either remove the empty comment or add a note that the component styles follow below.N3.
properties#indexroute duplication: Routes now have bothresources :properties, only: [:index, ...]androot "properties#index". Both generateGET /propertiesandGET /. This is intentional (root is the homepage) but the explicit:indexin the resources was added in this PR -- verify it is needed for something beyond the root route.N4. Nav active state:
current_page?(work_queue_items_path)will not match when a?date=param is present (e.g. navigating to a different day). The "Today" tab will lose its active highlight when viewing any date other than the default. Consider usingcontroller_name == "work_queue_items"instead.SOP COMPLIANCE
feature/nav-and-work-queue, not5-nav-and-work-queue. Does not follow{issue-number}-{kebab-case-purpose}convention.cdfc891with clear message.PROCESS OBSERVATIONS
VERDICT: NOT APPROVED
Two blockers must be resolved:
params[:date]input gracefully (rescueDate::Error, fall back toDate.currentor return 400).