Skip to content

Casadesign - DRAFT DO NOT MERGE#7051

Draft
giacoelho wants to merge 163 commits into
mainfrom
casadesign
Draft

Casadesign - DRAFT DO NOT MERGE#7051
giacoelho wants to merge 163 commits into
mainfrom
casadesign

Conversation

@giacoelho

Copy link
Copy Markdown
Collaborator

This is a work in progress. Will flag when it is ready to merge.

gia and others added 12 commits July 9, 2026 17:49
Introduce Tailwind v4 alongside the existing Bootstrap stack so the UI can be
modernized page-by-page without breaking current views.

- Add tailwindcss + @tailwindcss/cli (v4), build:tailwind[:dev] npm scripts,
  and a `tw` process in Procfile.dev. Output lands in app/assets/builds, which
  Sprockets already serves via the manifest's link_directory.
- Add app/assets/stylesheets/tailwind.css: CSS-first design system (Inter font
  + an indigo "brand" palette) — the visual direction for the redesign.
- New Tailwind-only layout casa_auth.html.erb (split brand panel + form),
  used by both the user and all-casa sign-in controllers.
- Redesign devise/sessions/new.html.erb in a clean, modern Stripe/Airbnb style
  and wire up remember-me properly.

Scoped to sign-in only; every other page keeps the Bootstrap `application`
layout untouched. First step of an incremental migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approved font direction: swap the design-system typeface and the auth
layout's webfont link from Inter to Figtree (warmer humanist sans).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- design.md: living design-system doc (Tailwind v4 approach, Figtree type scale,
  color tokens, icon-tile pattern, component + empty-state specs, app-shell spec,
  migration checklist). Codifies the icon-tile treatment (icons on a soft colored
  background) for stat/status icons and reserves initial-avatars for people only.
- CLAUDE.md: point to design.md and record the casadesign commit/push-per-checkpoint
  workflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New Tailwind-only layout: left sidebar with Pundit-guarded nav mirroring the
existing routes, plus a top bar (notifications, profile menu, sign out). Coexists
with the Bootstrap `application` layout — redesigned authenticated pages opt in via
`layout "casa_app"`. Icons via Bootstrap Icons CDN + Figtree (per design.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Supervisors now land on a real dashboard (DashboardController#show renders it in the
casa_app shell) instead of the Bootstrap volunteers table:

- SupervisorDashboard service builds per-volunteer follow-up status + summary stats
  from the supervisor's assigned volunteers.
- View: KPI cards, a "Needs your attention" list (icon-tile treatment per design
  feedback — replaces the ringed avatars), and a roster with color-coded
  contact-status pills.
- Empty states handled: no volunteers -> welcome + CTAs; nobody behind -> all
  caught up. The full volunteer management table remains at /volunteers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Feedback from design review:
- Avatar initials strip honorific prefixes (Mrs./Mr./…) and use first + last name
  only, via a shared `avatar_initials` helper (removed the duplicate in the service).
- Top-bar profile menu is triggered by the avatar alone; the name lives in the
  dropdown, not the trigger.
- Page header is the page name ("Dashboard") + <title>, not a greeting.

Accessibility (WCAG AA targets):
- Skip-to-content link, landmark labels, aria-labels on icon-only controls,
  aria-current on the active nav item, table scope + sr-only caption, visible
  focus rings, contrast bumps (slate-400 -> 500/600), role=status/alert on flash.

Responsive:
- Sidebar becomes an off-canvas drawer below lg with an accessible toggle
  (button + aria-expanded, Escape/backdrop to close) and a responsive grid/padding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Names now render as first + last only (no Mr./Mrs./...) wherever a person is shown
in the redesign — the shell sidebar/profile menu and the supervisor dashboard
(names and aria-labels), plus the avatar initials.

- New `NamePresentation.strip_honorific` + a `display_person(user)` view helper.
- Presentation-only: the stored `display_name` is left untouched. (An attempted
  `User#display_name` override was reverted — a security spec requires display_name
  to round-trip raw input unchanged, e.g. dangerous strings.)
- Documented in design.md + CLAUDE.md so it's applied on every page going forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bottom-of-sidebar user block (avatar + name + role) duplicated the identity
already shown by the top-right avatar account menu, and was non-interactive. Drop
it; the top-right menu is the single source for identity + account actions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "CV" placeholder brand mark wasn't a value-add at 36px (the org name already
identifies the chapter) and using the real org logo there would add image/variant
infrastructure for little gain. Show the org name alone in the sidebar header; the
full logo stays for contexts with room (sign-in, court reports). design.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the rest of the auth flow in line with the redesigned sign-in:
- Forgot password (passwords/new), reset password (passwords/edit), and
  accept-invitation (invitations/edit) rebuilt in Tailwind, matching the sign-in's
  inputs/buttons/error styling. CASA's email-or-phone reset is preserved.
- Users::PasswordsController uses `layout "casa_auth"`; the invitation-accept action
  renders in casa_auth (admin invite `new`/`create` untouched).
- casa_auth `<title>` is now driven by `content_for(:page_title)`; each page sets its
  own title (Sign in / Reset your password / Choose a new password / Accept your invitation).

Verified all three render 200 in the new layout (accept-invitation via a valid token).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply the honorific-free naming rule to the existing Bootstrap UI, not just the
redesign:
- 37 `.display_name` person-name sites across ~20 legacy views now render via the
  `formatted_name` helper (org names, form-value symbols, and the checkbox-id slug
  line were intentionally excluded).
- The 4 DataTables (volunteer, supervisor, case-contact, reimbursement) strip
  honorifics on their Ruby name outputs (display_name + supervisor_name); SQL
  select/order strings are untouched.
- Added a `formatted_name(name)` string helper alongside `display_person`, both
  backed by `NamePresentation`.

Verified /supervisors, /volunteers, /casa_cases render 200 with zero honorifics;
datatable classes load clean; standardrb + rendering green. Docs updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The header helper text becomes a short single sentence with no em-dash; the org
name already appears in the sidebar header, so it's no longer repeated here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added dependencies Touches dependency files javascript Touches JavaScript code ruby Touches Ruby code erb Touches ERB templates labels Jul 9, 2026
@giacoelho
giacoelho marked this pull request as draft July 9, 2026 23:01
gia and others added 3 commits July 10, 2026 15:34
DashboardController#show now renders the casadesign supervisor dashboard
(SupervisorDashboard + casa_app layout) for supervisors, but the request
spec still asserted the old redirect to the volunteers overview and was
failing on the branch. Assert the dashboard renders and lists an assigned
volunteer instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…app shell

First page-by-page migration off the legacy Bootstrap UI, following design.md.
The notifications page is linked straight from the new shell (top-bar bell +
account menu), so it was the most visible remaining Bootstrap seam.

- Render NotificationsController#index in the casa_app layout.
- Restyle the index, NotificationComponent, and patch-notes partial onto the
  design system: card list, leading icon tiles, unread dot, and a cold-start
  empty state. Switch Font Awesome (fas fa-*) to Bootstrap Icons (bi-*), which
  is the icon set actually loaded on Tailwind pages.
- Move the specs off Bootstrap-class selectors onto semantic data-* hooks
  (data-notification-list-item, data-read / data-unread-dot) without weakening
  behavior coverage. All notification/patch-note ordering, counts, and empty
  states remain asserted.

Verified: component, view, request (renders the full casa_app layout), and
system specs all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… backlog

- design.md: reframe as the permanent source of truth to refer to for all UI
  work. Add a "Design decisions (rationale)" log capturing the why behind the
  system (Tailwind-alongside-Bootstrap, layout opt-in, indigo/slate, Figtree,
  bi-* icons, icon-tile-vs-avatar, org-name-only sidebar, presentation-only
  honorific stripping, triage dashboards, empty-state taxonomy, a11y bar,
  build/output), a repeatable "Migrating a page" playbook, and a migration
  status that points at the backlog.
- design-todo.md: new prioritized, phased backlog (the "what's left") for the
  page-by-page migration off Bootstrap, plus cross-cutting infra and known
  pre-existing debt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the 🧪 Tests Tests label Jul 10, 2026
gia and others added 6 commits July 10, 2026 15:54
Second app-shell leaf page (linked from the account menu). Rebuilds the whole
edit-profile screen on the design system while preserving every behavior.

- Render UsersController in the casa_app layout.
- Rewrite edit.html.erb into cards: details, account, security, communication
  preferences, plus the volunteer languages table — using the documented input,
  button, and checkbox patterns.
- Replace the Bootstrap collapse accordions (Change Password / Change Email)
  with a small `disclosure` Stimulus controller (real <button> trigger, toggles
  a hidden panel, syncs aria-expanded) instead of relying on Bootstrap JS.
- Add a reusable Tailwind `shared/_form_errors` partial (the legacy
  `shared/_error_messages` stays for Bootstrap pages); it keeps the
  `@custom_error_header` hook so "... this password change ..." still renders.
- Preserve all field labels, ids, and JS-hook classes
  (toggle-email/sms-notifications, save-preference, password-new/-confirmation,
  submit-password, current_password_email, toggle-sms-notification-event) so
  require_communication_preference.js and password_confirmation.js keep working.

Verified: users request spec + the full edit system spec pass (79 examples);
erb-lint, standardrb, and StandardJS clean; JS bundle builds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rofile done

Add the two components introduced by the edit-profile migration (the Tailwind
form-error summary and the disclosure/collapsible-panel pattern) to design.md,
and tick edit profile in both the design.md status and design-todo.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rd subtitle

- Add a role badge (current_role as a brand-tinted pill) to the casa_app account
  menu header, under name + email — the one place identity metadata lives, and a
  parity with the role line the legacy header used to show. Industry-standard
  placement for a multi-role internal tool (keeps the sidebar/top-bar chrome clean).
- Supervisor dashboard subtitle -> "Track volunteer progress at a glance."
- Document the role badge in design.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The person-badge icon added no signal, so remove it. Colour the pill by role
using the design system's non-semantic accent tints — Volunteer = sky (blue),
Supervisor = violet (purple), Casa Admin = amber — so roles read apart at a
glance; unknown roles fall back to slate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pt WCAG AA

- Drop the shouty `uppercase` transform from all design-system labels (account
  details, dashboard + languages table headers, patch-note category, auth "or"
  divider); render them sentence case. Sentence-case isolated copy ("My
  languages", "Learning hours this year").
- Bump muted label text from slate-400 (fails AA) to slate-500/600 for contrast.
- Redesign the form-error summary (shared/_form_errors): a role="alert" card with
  a leading icon and a tidy bulleted list instead of the plain block; keeps the
  heading text + #error_explanation so specs and behavior are unchanged.
- design.md: add Sentence case + Accessibility (WCAG 2.1 AA) as standing
  conventions, and fix the muted-text token. design-todo: track the app-wide
  interactive-label sentence-case pass and a slate-400 contrast audit.

Interactive button/field labels shared across pages (Update Profile, Change
Password, field labels) are spec-coupled across users/volunteers/all-casa-admin
and a shared example, so they're deferred to a holistic pass (tracked).

Verified: 109 examples across the touched pages pass; erb-lint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per design feedback: one sentence, no bullets, dashboard-style icon tile.
- shared/_form_errors now renders "Unable to save: <messages via to_sentence>"
  inside a rose danger icon tile (bg-rose-100 text-rose-600, bi-exclamation-
  triangle) matching the dashboard, dropping the old heading + bulleted list.
- Update edit_spec assertions from the Rails "N errors prohibited this X from
  being saved:" text to "Unable to save".
- design.md: refresh the Form errors component note.

Verified: 79 users edit + request examples pass; erb-lint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gia and others added 30 commits July 20, 2026 18:28
design.md's rule is "never text-slate-400 for text" (it fails AA on white).
Fix the readable violations, leaving decorative aria-hidden icons alone:

- case_contacts_new_design index: the "No date" label + the — / medium-type
  placeholders (table + mobile card) -> slate-500 (matches the slate-500 "—"
  placeholder pattern used on the cases index).
- case-contact form: the "last used" meta timestamp -> slate-500.

Left as-is: text-slate-400 on decorative aria-hidden empty-state icons
(WCAG-exempt) and the supervisor-dashboard aria-hidden "—" (sr-only labelled).
Pills/badges reviewed: text-{color}-700 on a -50 tint (and slate-600 on
slate-100) all meet AA.

erb_lint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire up axe-core-rspec and add spec/system/accessibility/axe_spec.rb (headless
chrome :js) asserting be_axe_clean on 13 migrated pages (dashboard, cases
index/show/edit, case contacts, volunteers, supervisors, reports, learning
hours, reimbursements, admins, org settings, and the sign-in page).

That surfaced four critical select-name violations (custom/JS-enhanced selects
with no accessible name); fixed:

- reports index: the four filter multiselects had <label>s whose `for` pointed
  at the default field id, not the overridden spec-locked multiple-select-fieldN
  id -> associate each label with the actual id.
- _month_year_select: the month/year <select_tag>s shared one non-associated
  <label>; give each an aria-label ("<field> month" / "<field> year"). Fixes the
  youth birth month/year + date-in-care selects everywhere the partial is used.
- Form::MultipleSelectComponent: the TomSelect ts-hidden-accessible <select> had
  no accessible name; add an aria-label derived from the placeholder term.

All 13 axe examples green; casa_cases new/edit (52) + case_contacts new (28) +
the component spec still green; erb_lint + standardrb clean. design.md's Forms
a11y note now covers accessible names for overridden-id / JS-enhanced controls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The first sweep left the settings page in Title Case (a stale comment even said
it was "kept verbatim" to avoid touching specs). Apply the design.md sentence-case
rule to casa_org/edit + its partials: section headings ("Organization features",
"Manage contact types/court details/learning hours/case placement types"), the
entity-list headings ("Contact types", "Hearing types", "Sent emails", "Mailer
type", "Time sent", "Custom links", "Display text", ...), the feature toggles
("Enable learning topic", "Enable other duties", "Volunteers can add other
expenses"), the Twilio field labels ("Twilio phone number", "Twilio account SID",
"Twilio API key SID/secret"), and "Download current template". Proper nouns/
acronyms kept (CASA, Twilio, SID, API); the Sent-emails table row *data* is left
as-is.

Coupled specs updated (casa_orgs/edit view spec + casa_org/edit system spec).
20 examples green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reports: the "Quick exports" button labels + their disable-with feedback
("Mileage report" / "Downloading mileage report", ... "Placements report"), the
"Case contacts report" section heading, its filter labels ("Assigned to",
"Contact type", "Contact type group"), and the "Downloading report" submit
feedback. Court reports: the "Court reports" card heading, the "Download court
report as a .docx" trigger (now matching the dialog title), and "Generate report".

Coupled specs updated in lockstep (reports + court-report system specs, the
court-report view spec's h6 + docx-button regex, and the shared
case_court_report_helpers "Download court report" click). 63 examples green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Volunteer-assignment buttons ("Unassign volunteer", "Activate volunteer",
"Show old contacts", "Hide old contacts"), the "See all placements" link, and
the court-orders twin's "Court order type" label (shared by casa_cases/edit and
court_dates via casa_cases/_court_orders).

Coupled specs updated: casa_cases/edit + court_dates/edit system specs
("Unassign volunteer", "Court order type") and casa_cases/show view spec
("See all placements"). Green (the lone edit_spec:341 red was a confirmed
headless-chrome flake — passes in isolation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A thorough repo-wide pass over the remaining user-facing Title Case the earlier
sweep missed. My first scans only caught single-line text nodes + a few quoted
strings; the misses were mostly BLOCK-form button labels (text between `do %>`
and `<% end %>`, i.e. `</i> Label` on its own line) and labels inside Ruby
arrays (the all-CASA sidebar nav) — so I re-scanned with four patterns (text
nodes, quoted strings, text-after-tag-at-EOL, and indented-pure-text) until zero
remained.

Fixed across ~40 views/components: page titles + headings, the all-CASA sidebar
nav ("Patch notes", "Edit profile", "Feature flags", "PG dashboard", "Log out"),
"New/Create/Manage/Edit" buttons (admins, supervisors, volunteers, banners, case
groups), the reminder/followup buttons in BOTH followup partials + both reminder
forms ("Make/Resolve/Send reminder"), the court-report modal ("Generate court
report", "Generate report"), case-show actions ("Download all", "Edit case
details"), the export buttons ("Download report", ...), "Add another discussion
topic/expense", "Download checklist", "Set implementation status", the landing
page marketing headings, etc.

Kept (not chrome/labels): calendar-event titles (data), the `Casa Admin`
role-map key (tied to current_role), free-form org data, testimonial job titles,
the legacy `shared/_court_order_list` (has a casa_app twin), and proper nouns/
acronyms (CASA, Twilio, SID/API/CSV/SMS). Removed two stale "kept verbatim /
Title Case for specs" comments.

Coupled specs updated in lockstep (37 files, incl. the shared
fill_in_case_contact_fields + case_court_report helpers). Verified green across
the affected view/request/system suites; the only reds are pre-existing and
unrelated (deep_link's stale `.actions`/`#log-in` sign-in selectors; the
"unauthorized redirect -> /" routing in export_data/case_contacts-edit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… lesson

Note that round 1 was incomplete (missed block-form button labels, Ruby-array
labels, page titles, spec/support helpers) and document the four grep patterns
to use so an audit catches every instance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…percase)

design.md pins `thead th = text-xs font-semibold text-slate-600` (what
sortable_header emits), but many tables had drifted to an ALL-CAPS eyebrow style
(`uppercase tracking-wide text-slate-500`) that contradicts the no-uppercase-on-
labels rule and rendered headers in caps — reimbursements queue, placements,
mileage rates, banners, court_dates/show, emancipation checklists, the all-CASA
tables, and every casa_org settings table. Replace all 51 occurrences with the
canonical token (also covers the case-contact contact-type group `<h4>`s;
placements/show `<dt>` fact labels drop uppercase but stay `text-slate-500`).

Padding stays per-table (it matches each table's cells). Capybara reads DOM text
(not the CSS transform), so no assertions change; refreshed the now-stale
mileage_rates_spec comment. Made design.md's "no uppercase transform" explicit in
the Tables section. Representative view/system specs green (22).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the header-token convergence. Two remaining inconsistencies:

- Padding: index/show tables mixed px-3/px-4; the settings + all-CASA admin
  tables used a tighter px-3 py-2 (th) / px-3 py-2.5 (td). Converge every table's
  th AND td (and the empty-state cell) on the canonical px-4 py-3 that
  design.md + sortable_header already specify, so headers line up with cells.
- Vertical alignment: <th>s inherited the browser `vertical-align: middle`
  default, so when one column's header wrapped to two lines the single-line
  headers floated in the centre with stray space. Add `align-top` to every <th>
  (the tokens, the settings/admin inline headers, and sortable_header) so headers
  anchor to a consistent top line. (Body <td> vertical-align left per-table --
  that's separate from the header question.)

Left the volunteers select-all checkbox column at px-3 (a deliberately narrow,
empty-header column). CSS-only; representative table specs green (27). design.md
Tables section updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finishes the table-consistency pass: <td> vertical alignment was mixed
(11 tables align-top, 5 align-middle, casa_cases index + the settings/admin
inline cells had none). Converge every data cell on align-top so a row with one
wrapped cell keeps the rest anchored to the top instead of floating mid-row —
matching the headers (now align-top) and design.md.

Left casa_cases/show's inline add-to-calendar span at align-middle (it centres
an inline-block button next to text, not a table cell). CSS-only; render specs
green (26).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The stranded Bootstrap->casa_auth auth specs surfaced two real bugs on the
migrated sign-in page:

- devise/sessions/new had a `remember_me` checkbox, but User isn't
  :rememberable (devise modules: database_authenticatable, invitable,
  recoverable, validatable, timeoutable, trackable, confirmable). On a FAILED
  login the re-render rebuilt the resource with the remember_me param and 500'd
  (ActiveModel::UnknownAttributeError). Removed the field.
- casa_auth rendered no flash at all, so sign-in errors ("Invalid email or
  password"), the inactive-account message, and sign-out notices were invisible.
  Added a flash region to the shell (same emerald/amber + role + `.alert`
  pattern as casa_app).

Specs: the auth-flow system specs (login, sessions/new, deep_link, all_casa
sign-in) still drove the old Bootstrap sign-in via `.actions` / `#log-in` and
asserted "Login" / "Log In"; updated them to the real submit ("Sign in" /
"Log in") and copy ("Log in" landing link, "Welcome back" sign-in heading).
34 auth examples green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ApplicationController#not_authorized deliberately redirect_to(root_url), and for
a signed-in user "/" renders the authenticated :user root (dashboard#show) AT
"/" — it never falls through to static#index's role-redirect. So an unauthorized
signed-in user lands on "/", but these pre-existing specs still asserted the old
role paths (casa_cases_path / supervisors_path). Update them to root_path (where
the app actually + deliberately lands them; changing that target is a product
decision, not a spec one). Also fixed a stale "Sign Out" -> "Sign out" (the
account-menu label) in all_casa_admin_spec.

reports/export_data, case_contacts/edit, all_casa_admin specs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full-suite run surfaced 15 reds; 13 fixed here:

- devise/passwords/new (8): stale devise-default labels — "Forgot your
  password?" -> "Forgot password?", "Send me reset password instructions" ->
  "Send reset instructions", "Change my password" -> "Update password",
  "Log In" -> "Sign in"; dropped the old Bootstrap "N errors prohibited" header
  assertion (the casa_auth page renders an inline error list; the error text
  itself is still asserted).
- banner_helper (2): #conditionally_add_hidden_class returns Tailwind "hidden"
  now, not Bootstrap "d-none".
- volunteers/edit impersonate (1): the banner shows formatted_name (honorific
  stripped, per the design rule), so gave the supervisor an explicit
  honorific-free display_name instead of the Faker default.
- casa_cases/index (1): retired the obsolete test of the removed Bootstrap
  "#ddmenu_my-cases" sidebar dropdown (the casa_app nav is flat links).
- learning_hours/volunteers/show (1): a coupled spec my round-2 sentence-case
  sweep missed — it still asserted "'s Learning Hours"; also dropped the stale
  "spec-locked" comment.

Left (pre-existing, unrelated to this work): truncated_text_component preview
spec (Lookbook preview JS) and timeout_warning (window.timeout isn't wired into
casa_app) — reported separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o 180)

The Bootstrap->casa_app migration dropped the inline script that set
window.timeout, so src/session_timeout_poller.js always fell back to its
`|| 180` default and ignored the user's configured timeout_in. Re-render it in
casa_app's head, before the deferred bundle, from @timeout_duration
(= current_user.timeout_in, already set by ApplicationController#set_timeout_duration).

Unit is minutes: the poller does `timeout * 60 * 1000` and the jest test sets
window.timeout in minutes, so render `.in_minutes.to_i` (180 for a 3h timeout),
`.to_json` for erb-safe script interpolation. Fixed timeout_warning_spec, which
asserted `.in_seconds` (10800) — that never matched the minutes the poller uses.

(all_casa_admin has the same latent pattern but resolves to the same global
config.timeout_in = 3.hours it already defaults to, so no behavior change there.)

erb_lint clean; spec green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The truncated_text preview spec failed because ViewComponent previews rendered
without the JS bundle, so the truncated-text Stimulus controller never connected
and "read more" did nothing. Add a component_preview layout that loads the
application bundle + tailwind, and point ViewComponent at it via
`config.view_component.previews.default_layout` (the 4.x key -- not the
`default_preview_layout` guess).

That surfaced a real component bug: TruncatedTextComponent + its controller used
Bootstrap `d-none` / `text-bold` / `d-inline`, which don't exist in the Tailwind
build -- so on the case-contact card (a live page) the [read more]/[hide] swap
was silently broken (both showed; the toggle no-op'd). Converted to `hidden` /
`font-bold`.

Spec green; jest 133 green (the read_more/case_button_states jest tests exercise
different code); builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record that the full suite was last at 3451 examples / 15 failures (all fixed,
verified in isolation) and note to re-run it end-to-end to confirm 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o ops

Move the platform activity charts off the public /health page and into the
authenticated all-CASA console, closing a cross-org exposure: the charts query
every chapter's data, but /health is public and unauthenticated.

- Extract HealthController's chart math into a MetricsReport query object that
  scopes to the whole platform by default, or to one CasaOrg when given
  (CaseContact via its dedicated `casa_org` scope; LoginActivity via the joined
  users.casa_org_id). This is the shared engine for the new Metrics/Analytics
  pages. Cross-org isolation verified (A=3, B=1, global=4).
- AllCasaAdmins::MetricsController#index renders the platform charts behind the
  existing all-CASA auth, nav'd as "Metrics" in the console sidebar.
- Shared metrics/_dashboard partial (range filter + 3 charts) so the upcoming
  per-chapter Analytics page reuses identical chart math.
- Rename HealthHelper -> MetricsHelper (its home is now Metrics/Analytics) and
  parameterize the range filter's base path.
- Slim /health to what it's for: a minimal, self-contained ops status page (HTML)
  plus the latest_deploy_time JSON. Drop the unused legacy JSON analytics
  endpoints and retire the now-dead metrics layout + JS bundle.

Charts render via the metric_* helpers + chart-hover controller, both already in
the application bundle, so no separate JS bundle is needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New AnalyticsController#index renders the same three activity charts as the
all-CASA Metrics console, but MetricsReport is scoped to current_organization
so each chapter sees only its own data. Gated by AnalyticsPolicy (admins and
supervisors, matching the Reports audience); nav'd as "Analytics" beside Reports
in the casa_app sidebar.

Specs: AnalyticsPolicy (admin/supervisor allowed, volunteer denied), a request
spec (roles + signed-out redirect + range preset), and a MetricsReport unit spec
asserting cross-org isolation (org A sees 3, org B sees 1, unscoped sees 4).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three headline numbers above the charts, scoped to the signed-in chapter:
contacts this month (with a month-over-month delta badge), active volunteers,
and cases needing contact. Reuses AdminDashboard for the volunteer/needs-contact
figures (the app's canonical 14-day "no recent contact" definition, batched for
org scale) and adds contacts_this_month / contacts_previous_month to MetricsReport
for the delta. Metrics (all-CASA) stays charts-only; KPIs are chapter-actionable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The KPI cards had drifted from the documented "KPI stat card" pattern (missing
the semantic icon tile, wrong label/meta colors, an ad-hoc amber number and a
bespoke delta pill). Reconcile them to the exact token used by the dashboard
cards: icon tile -> text-3xl slate-900 number -> text-sm slate-500 label ->
text-xs slate-500 meta; "cases needing contact" now mirrors the dashboard's
amber attention treatment; the contact delta is a colored meta line (arrow +
signed number + "vs last month", never color-only).

Document the Metrics/Analytics feature in design.md (Charts now built in
MetricsHelper and rendered on Metrics/Analytics; KPI stat card variants; a
Migration status entry) and design-todo.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rack-test system specs covering the sidebar nav → page flow: an all-CASA admin
reaches Metrics and sees the platform charts; an admin reaches Analytics and sees
the KPI cards + charts; a supervisor can reach it; a volunteer sees no Analytics
nav link. (Charts are server-rendered SVG, so no JS driver is needed.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Found by the full-suite run: truncated_text_component_spec still asserted
`span.text-bold`, but the component was migrated to `span.font-bold` (the earlier
d-none->hidden / text-bold->font-bold change) without updating the spec. Update
both assertions to match the rendered class. Also record the full-suite result
in the backlog (3470 examples; the other 4 reds are headless-chrome flakes that
pass in isolation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3470 examples; 1 real stale-spec red fixed (truncated-text), the other 4 are
headless-chrome flakes that pass in isolation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…alytics pages

Converge the page vertical rhythm on index/list pages so the filter->table
and section gaps match instead of drifting page to page:

- volunteers: drop the reserved `min-h-[2.5rem]` band under the filter (a
  persistent ~68px empty gap above the table when nothing is selected). Put
  `mb-4` on the Manage button so the gap collapses to 16px, matching the
  cases index; the button still reveals on selection and pushes the table
  down only then.
- supervisors: filter `mb-5` -> `mb-4` (16px), matching the other rosters.
- analytics/metrics: range filter gains `mt-6` (it butted flush at 0px under
  the KPI row / subtitle); chart sections 2 & 3 `mt-5` -> `mt-6` so the whole
  dashboard reads as one uniform 24px column.
- design.md: document the page vertical rhythm and the anti-patterns.

Pixel-verified over CDP: filter->table = 16px on volunteers / cases /
supervisors / reimbursements; analytics above-filter = 24px, section gaps
= 24/24. Green: volunteers, supervisors, analytics, metrics system specs +
axe (volunteers/supervisors/reimbursements); standardrb + erb_lint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ged emoji

The case-contact card put a 36px medium-type icon in a full-height gutter
(`flex items-start`), indenting the title, secondary text, answers and notes
~48px right of the card edge. Move the icon into the header row inline with
the title (smaller 32px `h-8 w-8` tile) so the body spans the card's full
width, flush-left to the `p-5` edge — industry standard: a leading icon
adorns the header, it is not a body gutter.

Also drop the decorative transition-aged emoji from the case-number heading
(`display_case_number` returns the plain case number). It's data-as-emoji,
which design.md already bans; no other view renders it.

Pixel-verified (CDP): body indent 48px -> 0; icon 36 -> 32px; heading
"CINA-08-1001" (emoji gone). Green: case_contacts index system spec +
decorator spec (20 examples) + axe; standardrb + erb_lint clean.
design.md: documented the leading-icon card-header pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ed title)

TruncatedTextComponent (the case-contact answer/notes detail lines) hard-coded
a `font-bold` label and set no font size, so it inherited 16px — making the
detail labels 16px/700, heavier AND bigger than the card's own 16px/600 title.
The detail shouted and the title whispered.

Align it to design.md's fact/detail-list convention: `text-sm` container, a
muted `font-medium text-slate-500` label, a regular `text-slate-800` value, and
a real `brand-600` `[read more]` link. Now the title (16px / 600 / slate-900)
is the sole anchor and every supporting/detail line is `text-sm`.

Pixel-verified (computed style): answer label 16px/700 -> 14px/500/slate-500;
value 16px -> 14px; read-more 16px/plain-text -> 14px/500/brand-600. Green:
truncated_text component + system specs + case_contacts index (19 examples);
erb_lint + standardrb clean. Updated the component label spec + design.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(kill read-more spam)

Every topic answer and the notes were each their own line-clamped
TruncatedTextComponent with a separate "[read more]", so reading a single
contact took several clicks (one per line). Replace them with ONE native
<details> "Show details" toggle per card that reveals the full answers + notes
at once as a dl (muted `text-xs font-semibold` dt / dark `whitespace-pre-line`
dd) -- matching the sibling new-design table's expandable detail panel.

Industry standard: detail is progressively disclosed with a single per-card
control, not fragmented behind N micro-toggles.

Specs: the two :js tests that pinned the old truncation are updated -- the
"contact notes" test now drives the single Show-details expand; the case-filter
test asserts on the always-visible section case numbers / an empty list rather
than collapsed notes. Full case_contacts index spec (16) + axe green; erb_lint
+ standardrb clean. design.md: documented the disclosure pattern and answered
the divider question.

Note: TruncatedTextComponent is now unused (left in place; safe to delete later).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…a-card) + sentence-case default contact types

The "Show details" panel boxed topic answers + notes in their own rounded-xl
border card *inside* the case-contact card -- a card-in-a-card, which isn't a
pattern in the app. Replace the box with a `border-t border-slate-100 pt-3`
divider on the <details> (matching metric_data_table's "View as table"
disclosure) and render the dl unboxed.

Separately, default contact types still displayed Title Case ("Fictive Kin",
"Social Services") though DEFAULT_CONTACT_TYPE_GROUPS is already sentence-cased:
generate_for_org! find_or_creates by name and never renames, so orgs seeded
before the constant change kept the old names. Add the missing after_party task
to rename them -- matching only case-variants of a shipped default so
org-renamed / custom names are left alone.

WCAG: the disclosure is a native <details>/<summary> (keyboard + screen-reader
expanded state), aria-hidden chevron, AA-contrast summary + labels; axe green.

Verified: case_contacts index spec (16) + axe green; erb_lint + standardrb
clean; ran the task on dev (renamed 3 groups + 39 types to sentence case).
design.md: documented the nested-card divider rule + constant-needs-migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Only conflict was app/views/case_contacts/_case_contact.html.erb: main (#7055)
added a reimbursement status badge to the Bootstrap card while casadesign had
rewritten the card in Tailwind. Kept the casadesign design and folded the new
feature in as a casadesign pill (amber = pending, emerald = complete) using the
merged reimbursement_status_text / _badge_type decorator methods, so main's new
specs (card shows "Reimbursement Pending"/"Complete") still pass.

Everything else auto-merged: dependency bumps (caxlsx 4.5, friendly_id 5.7,
strong_migrations 2.8, fontawesome 7.3, etc.) alongside casadesign's axe/Tailwind
deps; reimbursement controller + specs; decorator (medium_icon + reimbursement_*).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tack topic notes

Three UX fixes on the case-contact form:

- Relevant case(s): on edit (@case_contact.active?) the model requires
  draft_case_ids and the picker only ever offered the single case the contact
  belongs to, so the removable multiselect only let the user dismiss a required,
  fixed parent. Render it read-only (static chip(s) + hidden draft_case_ids);
  keep the editable TomSelect multiselect only for new / draft contacts.

- Topic answers: replace the 50/50 grid (topic | squeezed notes) with a stacked
  row — topic select on top, full-width notes textarea below, since the note is
  the substance and deserves the width.

- Delete: the standalone danger_outline button becomes a subtle ghost remove
  (design-system "recede for repeated-row actions"), keeping the visible "Delete"
  text + confirmDestroyAndRemove hook the spec drives.

All Stimulus/DOM hooks preserved. Green: contact_topic_answers / edit / new /
additional_expenses system specs (2 new_spec failures were Chrome-crash flakes,
pass in isolation); erb_lint clean. design.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Touches dependency files erb Touches ERB templates javascript Touches JavaScript code pundit Touches Pundit authorization policies ruby Touches Ruby code 🧪 Tests Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants