UI polish: property detail, edit, filters, week queued indicator #14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "iterate-polish"
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
UI polish pass: week view now shows queued and completed items, property detail/edit pages, service filters on Properties tab, case-insensitive name sort.
Changes
Test Plan
Review Checklist
Related Notes
Closes #8
🤖 Generated with Claude Code
PR #14 Review (Post-Merge)
Scope: UI polish -- property detail/edit pages, week view queued indicator, service filter chips, recents links, case-insensitive sort.
Tech stack: Ruby on Rails, Hotwire (Stimulus + Turbo), vanilla CSS with design tokens.
Files changed: 10 files, +196/-19 lines. Closes #8.
DOMAIN REVIEW
Rails / Controller correctness
Arel.sql("LOWER(client_name)")inproperties_controller.rb:7-- This is safe. The string is a static literal, not interpolated from user input.Arel.sqlis the correct way to pass raw SQL fragments for ordering, and Rails requires it explicitly to prevent accidental injection. No risk here.properties_controller.rbeditaction properly loads@servicesfor the form. Theupdateaction correctly re-loads@servicesin the failure path (format.htmlblock) so the edit form re-renders with checkboxes. Good pattern.property_paramsnow permitsservice_ids: []-- correct for array checkbox submission.respond_toblock inupdatecorrectly handles both HTML (form submission from edit page) and JSON (existing API usage from Stimulus save). Backward-compatible change.weeks_controller.rbrefactor from a single completions query to a split@completions/@queuedhash is clean. Single query, partitioned in Ruby. No N+1.routes.rbcorrectly adds:editto theonly:list. RESTful.Turbo Stream / manage.html.erb consistency
The turbo stream template (
toggle_active.turbo_stream.erb) andmanage.html.erbnow use identical markup:link_to property_path(property)withclass: "manage-item-info"client_nameas<strong>, address inmanage-item-metadata-search-textanddata-service-idsattributesCSS quality
.form-row,.form-field-sm,.property-details,.property-location,.recent-item a,.recent-item-meta,.week-queued) all use design tokens for colors (--color-muted,--color-accent) and spacing (--spacing-sm,--spacing-md,--spacing-lg,--spacing-xs).gap: 0.15remon.recent-item a(line 234) is a raw value rather than a spacing token. Minor -- there is no token for sub---spacing-xsgaps, so this is defensible.font-size: 0.85remon.recent-item-meta(line 240) is a raw value. No font-size tokens exist in the token set, so this matches the existing pattern elsewhere in the file (e.g.,.form-field labeluses0.9rem).flex: 0 0 5remon.form-field-sm(line 149) -- raw value for a fixed-width field. Reasonable for state/zip inputs.ERB templates
show.html.erbandedit.html.erbboth use ERB output tags (<%= %>), which auto-escape HTML by default. No XSS risk in server-rendered views.edit.html.erbusesform_with model:which auto-generates CSRF token. Correct.edit.html.erblabels use hardcodedfor="property_address_line"etc. -- these match Rails' defaultidgeneration forf.text_field :address_line, so they are correct.BLOCKERS
1. XSS via innerHTML in
location_controller.js:132-- MEDIUM SEVERITYThe data source (
p) comes fromlocalStorage("recent_properties"), which is populated from the JSON response of/properties/resolve(line 94-98). The flow:renderRecent()interpolatesp.address_line,p.client_name,p.citydirectly intoinnerHTMLAttack vector: If a property's
address_lineorclient_namecontains<script>or<img onerror=...>, it will execute. While the immediate risk is self-XSS (the user entering the data is the same user seeing it), localStorage persists across sessions and could be poisoned by another XSS vector or shared-device scenario.Fix: Use
textContenton individual elements instead ofinnerHTMLtemplate interpolation:Note: The previous version of this code used
li.textContent(safe). This PR regressed toinnerHTML.2. No test coverage for new functionality
This PR adds:
editaction onPropertiesControllerupdateaction (nowrespond_towith HTML/JSON)weeks_controller.rb(queued hash logic)None of these have controller tests, integration tests, or system tests. The test plan in the PR body is manual-only (checkboxes). Per BLOCKER criteria: "New functionality with zero test coverage."
This is a landscaping side project and the entire codebase appears to have no test suite, so this is flagged for tracking rather than as a gate -- but it is technically a BLOCKER per the review criteria.
NITS
weeks_controller.rb:8--@properties = Property.active.order(:address_line)still sorts byaddress_linewhile the manage page now sorts byLOWER(client_name). Consider whether the week view should also sort by client name for consistency, since the manage page now leads with client name as the primary identifier.show.html.erb-- No back/navigation link. The user lands here from the manage list or recents, but there is no way to go back except the browser back button (or the bottom nav, if it is rendered outside this template). Consider adding a back link.edit.html.erb-- No cancel button. If the user navigates to edit and changes their mind, they must use browser back. A "Cancel" link back toproperty_path(@property)would improve UX.show.html.erb-- Missing@property.serviceseager loading. Theshowaction doesProperty.find(params[:id]), then the template calls@property.services.any?and@property.services.map. This triggers a lazy-load query. Low impact for a single property, but adding.includes(:services)to theshowaction would be cleaner.edit.html.erb-- Same lazy-load observation for@property.service_ids.include?in the checkbox loop. Theeditaction does not eager-load services on the property.location_controller.js:131--a.href = \/properties/${p.id}`-- thep.idvalue comes from localStorage. If corrupted, this could produce an invalid URL. Low risk but worth noting. Consider validatingp.id` is a positive integer.SOP COMPLIANCE
refs/pull/14/head. Cannot confirm branch name format from merged PR metadata. Closes #8, so branch should have been8-*.PROCESS OBSERVATIONS
_manage_item.html.erb.respond_toblock preserves the existing JSON path, so the Stimulus save flow is unaffected.VERDICT: NOT APPROVED
Two blockers:
location_controller.js-- the previous code was safe (textContent), this PR introducedinnerHTMLinterpolation of user-controlled data. Should be fixed.