Casadesign - DRAFT DO NOT MERGE#7051
Draft
giacoelho wants to merge 163 commits into
Draft
Conversation
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>
giacoelho
requested review from
FireLemons,
compwron and
elasticspoon
as code owners
July 9, 2026 23:01
giacoelho
marked this pull request as draft
July 9, 2026 23:01
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>
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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a work in progress. Will flag when it is ready to merge.