Add 'Previously [Day]s' accordion with historical frequency to day detail page #242
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "235-previously-accordion"
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
Adds a "Previously [Day]s" accordion section to the day detail page showing all properties that have historically been serviced on that weekday, sorted by frequency descending. This gives admins quick context on which properties are typically done on a given day.
Changes
app/controllers/days_controller.rb-- Addedload_previouslyprivate method that queries WorkQueueItem history by DOW, joins active properties, groups by property_id, and returns sorted results with counts. Single efficient query with no N+1.app/views/days/_previously_section.html.erb-- New partial using the existing<details>+accordionStimulus controller pattern. Shows property name, address, frequency count, and a "Queued" badge for properties already queued on the viewed date. Empty state when no historical data.app/views/days/show.html.erb-- Renders the new_previously_sectionpartial, passing@previously,@date, and@queue_items.spec/requests/days_spec.rb-- 7 new specs covering: heading text, historical property display, frequency sort order, empty state, queued marking, inactive property exclusion, and frequency count display.Test Plan
spec/requests/days_spec.rbpass (12 existing + 7 new)accordion_controller.js(no JS changes needed)Review Checklist
.merge(Property.active)Related Notes
No documentation changes needed. This is a view-layer feature addition to the existing day detail page.
Related
Self-Review Findings
Correctness -- PASS
load_previouslyusesEXTRACT(DOW FROM work_date)matching the pattern fromWorkQueueItemsController#compute_unqueued_this_week(line 240). PostgreSQL DOW convention (0=Sunday, 6=Saturday) matches Ruby'sDate#wday..joins(:property).merge(Property.active)correctly filters at the DB level so inactive properties never enter the count hash.Property.active.where(id: counts.keys)lookup is redundant with the join filter but is harmless (belt-and-suspenders) and avoids any edge case where a property becomes inactive between the count query and the property load.[-count, client_name.downcase]for stable ordering -- highest frequency first, alphabetical tiebreak.N+1 Queries -- PASS
where(id: counts.keys).index_by(&:id).@queue_itemsalready uses.includes(:property)from the existing code, so thequeued_pidsmap in the partial does not trigger additional loads.Pattern Compliance -- PASS
<details class="section-accordion" data-controller="accordion" data-accordion-key-value="previously-{wday}">matching the exact pattern from_remaining_section.html.erbandindex.html.erb.queued-badgeclass matches existing usage in_other_crew_section.html.erb(line 16).address_line . cityor "No address yet") matches all other views.Test Coverage -- PASS
7 new specs covering all acceptance criteria: heading text, property display, frequency sort, empty state, queued marking, inactive exclusion, frequency count.
No Issues Found
This is ready for human review.
PR #242 Review
DOMAIN REVIEW
Tech stack: Rails 8.1, PostgreSQL, Hotwire (Stimulus), ERB views, RSpec request specs.
Query design (controller): The
load_previouslymethod uses a clean two-phase approach -- grouped COUNT query, then bulk property load viaindex_by. No N+1. TheEXTRACT(DOW FROM work_date)usage is PostgreSQL-specific but the project is PostgreSQL-only (dev, test, and prod), so this is fine. The DOW=0-for-Sunday semantics match Ruby'sDate#wday, which is correct.Sorting: Primary sort by frequency descending, secondary by
client_name.downcasefor stable ordering -- good. Usessort_bywith a two-element array, which is the idiomatic Ruby approach.Accordion pattern: Correctly reuses the existing
accordionStimulus controller with adata-accordion-key-value="previously-<wday>"key. This gives per-weekday localStorage persistence, which is the right granularity.View partial: Follows the established
_*_section.html.erbnaming pattern. Reuses existing CSS classes forsection-accordion,section-heading,queued-badge, andempty-state.Spec coverage: 7 new specs covering heading text, historical display, frequency sort order, empty state, queued marking, inactive exclusion, and frequency count. Good breadth for a view-layer feature.
BLOCKERS
None. No security issues, no unvalidated input, no credentials, no auth/security DRY violations. The query takes
date.wday(an integer derived from a parsed Date), not raw user input. The controller already validates and redirects on invalid date input (existing behavior from PR #240).NITS
Missing CSS for
previously-*classes. The partial introducespreviously-count,previously-list,previously-item,previously-item-info, andpreviously-item-metaclasses, but no CSS is added toapplication.css. Every other section in the app (recent, remaining, last-week, other-crew) has dedicated CSS for layout, spacing, text overflow, and item structure. Without styles, the list items will render as plain unstyled elements -- no flex layout, no text truncation, no spacing between items, no border separators. Thequeued-badgeandsection-accordionclasses are already styled, but the list content itself will look noticeably rough. Recommend adding CSS following the established pattern (compare with.recent-queue-*or.remaining-*blocks inapplication.css). This is cosmetic only and does not block merge, but it will look out of place compared to the rest of the app.is-queuedclass onpreviously-itemhas no CSS rule. The class is applied to<li>elements for queued properties, likely intended to visually differentiate them (e.g., reduced opacity like.other-crew-itematopacity: 0.7), but no.previously-item.is-queuedor.is-queuedrule exists. Currently it does nothing besides serving as a test hook.queued_pidscomputation in the partial. Line<% queued_pids = queue_items.map { |qi| qi.property_id } %>could use&:property_idshorthand for consistency with Ruby conventions:queue_items.map(&:property_id). Minor style nit.Test for singular "1 time" missing. The frequency count spec tests
"2 times"(plural) but doesn't test"1 time"(singular). The view logicentry[:count] == 1 ? "time" : "times"handles this, but a spec confirming the singular form would be more complete. Very minor -- the branching logic is simple.previouslyalso counts the current date's items in the historical tally. If a property is queued for the viewed Tuesday AND was also queued for previous Tuesdays, theEXTRACT(DOW FROM work_date)query counts ALL Tuesdays including the viewed date itself. This means a property queued only for today's Tuesday would show "1 time" in the "Previously" section, which is slightly misleading -- it implies the property was previously serviced on Tuesdays when it was only just added today. Consider adding.where.not(work_date: date)to exclude the current date from the historical count. Non-blocking because the feature is still useful as-is, but worth considering.SOP COMPLIANCE
235-previously-accordionreferences issue #235)Closes #235present in PR bodyPROCESS OBSERVATIONS
Clean, focused PR. Single feature, single issue, no scope creep. The two-query approach in the controller avoids the common Rails trap of loading associations in a loop. Good reuse of existing Stimulus controller rather than creating a new one. The missing CSS is the only rough edge -- the feature will work correctly but won't match the visual polish of the rest of the app.
VERDICT: APPROVED
QA Nit Fixes (commit
9c49509)Addressed all 5 nits from the QA review:
previously-*styles matching the existingremaining-itempattern: list layout, item flex, text overflow, meta sizing. Count badge uses the same accent pill asother-crew-countandremaining-count.is-queuedclass visual -- Addedbackground: var(--color-success-light)rule for.previously-item.is-queuedso queued items get a visible highlight.queue_items.map { |qi| qi.property_id }toqueue_items.map(&:property_id)..where.not(work_date: date)to the historical query so a property queued only for today does not misleadingly appear in "Previously". Added spec verifying this behavior.All 21 specs pass.