Casadesign - DRAFT DO NOT MERGE#7051
Draft
giacoelho wants to merge 233 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>
…orific strip) The #supervisor_volunteer_supervisor_id options render honorific-free names (formatted_name / NamePresentation.strip_honorific), but the spec compared against the raw display_name. Faker::Name sometimes generates a "Rev."/"Dr." prefix, so the assertion passed or failed depending on the seed -- the pre-existing rspec (and docker) CI failure. Compare option text against the stripped names so it matches the view deterministically. Non-system suite now 2925 examples, 0 failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…below content) Pagination was placed three different ways: inside the table card (learning-hours), below with an mt-4 wrapper (volunteers/casa_cases/case_contacts), and below bare (reimbursements) -- so the divider and spacing differed per page. Industry standard: a footer bar directly below the results -- "Showing X-Y of Z" left, page controls right, a top divider, edge-aligned to the content. Responsive pages hide the desktop table card on mobile, so pagination can't live inside it; the one placement that works everywhere is below the content. shared/_pagination now owns that (mt-4 gap + border-t + pt-4, edge-aligned, no horizontal inset), and every call site renders it bare -- dropped the mt-2/mt-4 wrappers and moved the learning-hours copy out of its card. Verified identical on volunteers / reimbursements / learning-hours (16px gap, left-aligned to the content edge, 1px divider, 16px padding). View specs 6/0; erb_lint clean; design.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he below-card bar) Reviewer feedback: the below-card bar (mt-4 gap + divider + pt-4) required too much scrolling; use the industry-standard in-table footer (like learning-hours). So: - shared/_pagination is back to a compact in-card footer (border-t + px-4 + pt-3, no external gap). - learning-hours pagination moved back INSIDE its card. - Responsive pages (reimbursements / volunteers / casa_cases): pagination now renders INSIDE the desktop table card as its footer, plus a `md:hidden` copy below the mobile card list (the desktop card is `hidden md:block`, so an in-card-only render would vanish on mobile). Verified headless: desktop shows exactly ONE pagination, inside the table card at its bottom (learning-hours / reimbursements / volunteers / cases); mobile shows the md:hidden copy below the card list. View + system specs 33/0; erb_lint clean; design.md guidance corrected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eimbursements card py-2 The in-card footer used pt-3 with no bottom padding, so the page numbers sat ~8-9px from the card bottom -- and ~1px on reimbursements, whose desktop table card was missing the py-2 inset the other table cards have -- so it read as cramped. Footer is now symmetric py-3, and the reimbursements table card gets py-2 to match. Verified headless: all four table pages identical -- footer padTop/padBottom 12px, ~21px under the page numbers (was ~8-9). View specs 3/0; erb_lint clean; design.md updated (pt-3 -> py-3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, not py-2) The footer was symmetric (py-3), but the table card's py-2 inset added 8px BELOW the footer (under pb-3), so the page numbers sat ~12px from the divider but ~20px from the card's bottom edge -- top padding < bottom. Change the four paginated table cards from py-2 to pt-2 (keep the top inset for the table, drop the bottom one) so the footer's py-3 is the only padding around the numbers. Verified headless: 13px above == 13px below the numbers on learning-hours / reimbursements / volunteers / cases. View specs 6/0; erb_lint clean. Documented in design.md (a footered table card uses pt-2, not py-2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ame links (supervisors index) The "Volunteers without supervisors" table was a read-only list whose only action was an unlabeled name link to the volunteer's edit page -- it read as purposeless. Make it a worklist: - Inline per-row "Assign supervisor": a supervisor <select> (@active_supervisors) + Assign button posting to supervisor_volunteers#create (the same endpoint the volunteer edit page uses; it redirects back via request.referer, so the volunteer drops off the list on success). Verified end-to-end -- assigning removes the volunteer from the list. - Make the volunteer name obviously clickable: dark identifying text (the design system keeps person names dark, not brand links) with a PERSISTENT underline -- a non-color cue (WCAG 1.4.1), slate-800 on white is 14.7:1 (1.4.3), + a focus ring (2.4.7). Was hover-only underline, which read as plain text. design.md Names guidance updated. supervisors index + supervisor_volunteers specs 26/0; erb_lint + standardrb clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e_link_class helper
Sweep the hover-only-underline person-name (and dark in-text) links to a PERSISTENT underline so
they're discoverably clickable -- WCAG 1.4.1 (non-color cue), slate-800 on white 14.7:1 (1.4.3), focus
ring (2.4.7). Add a `name_link_class` helper (matching the button_classes pattern) and point every dark
name link at it: learning-hours (2), supervisors (roster + _manage_volunteers + unassigned + assigned-
to names), volunteers, casa_admins, casa_cases (show + _volunteer_assignment), other_duties. Callers
prepend the font size/weight (e.g. "font-medium #{name_link_class}").
Brand-colored entity links (case numbers, court dates) are unchanged. Verified: helper classes render
on /volunteers + /supervisors with no interpolation leak; swept-page system specs 79/0; erb_lint +
standardrb clean; design.md Names guidance points at the helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ss + document both link patterns The volunteer + admin dashboards rendered case-number (record) links in DARK (text-slate-800) -- inconsistent with the brand-colored case-number links everywhere else. Add a `record_link_class` helper (brand-600 + hover underline + focus ring, the counterpart to name_link_class) and reconcile the 4 dashboard case-number links to it. design.md now documents the two (and only two) in-content link treatments: dark `name_link_class` for people, brand `record_link_class` for records (case number / court date), with the WCAG rationale -- brand color is the cue in a links-only cell; add a persistent underline for a record link that sits inline in body text (brand-on-text is under 3:1). Verified: volunteer dashboard renders 9 brand case-number links, 0 dark; dashboard spec 4/0; erb_lint + standardrb clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(match row-action pattern) The inline "Assign supervisor" column read wrong: left-aligned, and Assign was a primary CTA -- over-emphasized for a repeated per-row action and inconsistent with every other table's row buttons. Match the pattern: - Right-align the column: header text-right + cell text-right + the form flex justify-end (the form's right edge now sits at the cell's content edge, filling the column like the other action columns). - Assign button -> the tertiary ghost (bi-person-plus + "Assign"), same style as Edit/Detail view/ Impersonate app-wide, not a filled CTA. Still type=submit (behavior unchanged). design.md's Tertiary (ghost) note now states that EVERY table row action -- including a form-submit like Assign -- uses the ghost (never a primary/secondary CTA), and the whole trailing column is right-aligned. Verified headless: header/form right-aligned (form right edge at the 16px cell padding), button bg transparent + slate-600 (ghost, not primary). supervisors index + assign-endpoint specs 26/0; erb_lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…), not colored text links The casa_org settings tables were the drift the previous confirmation claimed was fixed but wasn't: Edit was a brand-colored text link and Delete a rose text link, unlike every other table's tertiary- ghost row actions. Centralize the ghost in a `ghost_class(:neutral|:danger)` helper (single source of truth, Tailwind-scannable literals -- the same reason button_classes exists) and convert both: - Edit -> ghost_class + bi-pencil (matches Edit / Detail view / Impersonate app-wide) - Delete -> ghost_class(:danger) + bi-trash via the confirm dialog (matches case_groups) 20 Edit links + 4 Delete triggers converted; 0 old-style remain. Pixel-verified: Edit bg transparent / slate-600 / bi-pencil, Delete bg transparent / rose-600 / bi-trash, both 28px; no 375px overflow. erb_lint + standardrb clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ss helper (single source) Follow-up to the settings-table fix: eliminate the remaining copy-pasted ghost strings so the tertiary ghost has ONE definition and can't drift again. - 4 views (other_duties, supervisors, volunteers, casa_admins) dropped their inline `ghost` local and now call `ghost_class` -- byte-identical output, pure refactor. - case_groups dropped its `action_link` local; Edit -> ghost_class, Delete -> ghost_class(:danger). This also normalizes its buttons from the outlier `px-2.5 py-1.5` to the canonical `px-2 py-1`. - design.md's Tertiary (ghost) note now documents the helper + its :neutral/:danger variants and says to call the helper, never hand-write the string. All pages render 200 with the ghost classes present and 0 errors; erb_lint clean. ghost_class output was pixel-verified on the settings page (neutral: transparent/slate-600/28px; danger: transparent/ rose-600/28px), and every migrated view emits that same helper string. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… helper (app-wide sweep) The app-wide audit (the step I skipped before) turned up more row-action drift beyond casa_org: - Edit brand-text links -> ghost_class + bi-pencil: mileage_rates, hearing_types checklist items, all_casa_admins/casa_orgs/show. - Delete rose-text links -> ghost_class(:danger) + bi-trash (behavior/confirm preserved): users/_languages, hearing_types checklist items (x2). - Hand-rolled slate-ghost class strings -> ghost_class: casa_cases/index (Edit, desktop + mobile), supervisors/index (mobile cases-card Edit). These looked right but bypassed the helper. 0 old-style row actions remain in the touched files; erb_lint clean; pages render 200 with 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ashboard View, remove-expense, filter toggles) Finishing the app-wide ghost sweep so ghost_class is the ONLY definition: - dashboard/supervisor "View" row action: was a brand-text link -> ghost_class + bi-eye (recedes like Detail view elsewhere; sits beside the Send reminder button as a proper tertiary). - shared/_additional_expense_form Remove: was a hand-rolled slate ghost missing inline-flex -> ghost_class (JS hook + aria-label preserved). design.md already specified this as the slate ghost. - case_contacts + case_contacts_new_design filter toggles: ghost-shaped toolbar toggles at the outlier px-2.5 py-1.5 -> ghost_class (px-2 py-1); data-action/aria/content preserved. Full app-wide audit now reports 0 in every drift category (brand/rose text actions, hand-rolled ghost strings, inline ghost locals). Only the distinct grid h-8 w-8 icon-button in _actions remains (a separate component). erb_lint clean; pages render 200 with 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sign.md line 333) Reported: the settings Delete button was red. design.md already decided the destructive tertiary ghost is "slate, not the old rose that read as too jarring" (the remove-expense decision) -- I contradicted it by inventing a ghost_class(:danger) rose variant (following case_groups' OLD rose precedent + the generic delete=red convention) and missed the note. - ghost_class collapsed to slate-only, no variant, so rose can't be reintroduced. - 9 delete call sites (casa_org contact_topics/custom_org_links, case_groups, hearing_types checklist items, users languages) -> the slate ghost. The bi-trash icon + "Delete" label + confirm dialog carry the danger, not the color. Prominent destructive CTAs stay button_classes(:danger_outline). - design.md's ghost note corrected: one slate ink, delete included; never rose. Pixel-verified: settings Delete now slate-600 (identical ink to Edit), transparent bg. The only rose left on that page is the user-menu "Sign out" link (intentional nav). delete-flow specs 41/0; helper standardrb + erb_lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…est) -- industry standard Reported: the destructive hover was inconsistent (gray in some tables, rose in others). My prior fix over-corrected to all-gray, which strips the danger affordance (Delete then looks like Edit). The industry standard for a repeated destructive action is neutral-at-rest + danger-on-hover (GitHub, Gmail, Linear): it avoids the jarring wall of red (design.md line 333) AND signals danger at the point of action. So: slate at rest (your first point) + rose on hover (your consistency point). - ghost_class(:neutral | :danger): BOTH slate at rest; :neutral hovers gray, :danger hovers rose. The variant is back, but correct this time -- rose on HOVER, never at rest. - Every destructive row action -> :danger (rose hover), consistently: casa_org contact_topics / custom_org_links, case_groups, hearing_types checklist items, user languages, remove-expense, and the case-contact delete icon (aligned to the same rose-700 hover). - Edit / Detail view / View / Assign / filters stay :neutral (gray hover). - design.md (ghost note + line 333) updated to the slate-at-rest + rose-on-hover standard. Verified: Delete carries hover:bg-rose-50 hover:text-rose-700 (12) and Edit hover:bg-slate-100 (128); all three hover rules compiled in tailwind.css (gated in @media (hover:hover), which is why headless forcePseudoState can't render them). Delete-flow specs 41/0; erb_lint + standardrb clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…NT + "Volunteer since"
The "Assigned cases" column was a link-list that was usually empty (case and supervisor assignments are
independent, but unsupervised volunteers tend to be new/caseless) and whose links pulled the user out
of the assign flow. Replace it with more useful triage data:
- "Cases": a compact count ("1 case" / "3 cases", em-dash for none). When >0 it's the real signal --
an unsupervised volunteer carrying cases is a live oversight gap. Scannable, stays in the flow.
- "Volunteer since": join date (I18n :full), always populated -- the urgency signal (how long they've
waited for a supervisor).
Controller preloads casa_cases (.includes) so the count is N+1-free.
Verified via CDP: heads Volunteer / Cases / Volunteer since / Assign; rows show "1 case" (seeded) and
em-dash for 0-case volunteers; dates render. supervisors/index spec 14/0; erb_lint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… supervisors" The assign dropdown lists EVERY active supervisor in the org -- unbounded, and large CASA chapters run dozens to 100+ -- so a native <select> repeated per row doesn't scale. Convert it to the existing searchable-select TomSelect (type-ahead), reused from the court-report case picker. - searchable_select_controller: add a configurable dropdownParent (default null -> court-report unchanged). The assign picker sits inside the table's overflow-x-auto + overflow-hidden card, so it passes dropdown-parent-value="body" and the menu renders on <body> instead of being clipped. - Minimal fallback class (block w-full): the tom-select theme owns .ts-control's border/padding, and TomSelect copies the select's classes to .ts-wrapper, so a bordered class double-borders. Dropped the manual chevron (the .ts-wrapper::after caret handles it). - design.md: documented the searchable single-select pattern (when to type-ahead + the dropdownParent/overflow + minimal-fallback caveats). Verified via CDP: enhanced (3 wrappers), 8 options, search "Alice" -> ["Alice Nguyen"], "Zzzz" -> 0, dropdown on <body> (not clipped), no double border (ctrl 1 / wrap 0), selection updates the underlying value. court-report + supervisors index + assign-endpoint specs 44/0; erb_lint + standardjs clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ty state, clear x, disabled-until-picked Follow-up on the type-ahead's affordance (it read as a plain preselected dropdown: no hint it was searchable, no empty state, no clear, unexplained default): - Loads BLANK with a "Search supervisors…" placeholder (searchable-select placeholder-value) -- signals it's typeable, is the empty state, and removes the confusing auto-selected first supervisor. A leading blank <option> defaults the native select to empty; hidden from the menu (.option[data-value=""]). - Assign is DISABLED until a supervisor is picked (searchable-select toggle-submit-value disables the form's submit; re-disables on clear), so you can't assign no one; + disabled: styles on the button. - CLEAR (x): clear_button plugin; the x replaces the chevron on focus/hover (hide ::after then) so they never overlap (casa doesn't load the bundled bootstrap clear-button theme). - design.md: documented all of it under the searchable single-select pattern. Verified via CDP: blank+placeholder on load; button disabled -> enabled on pick -> disabled on clear; x shows on focus/hover with the chevron hidden, idle shows the chevron with x hidden; empty option hidden; search filters; menu on <body>. court-report + supervisors + assign specs 44/0; erb_lint + standardjs clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, clear search affordance Follow-up on the picker looking off (uneven padding, too wide, not obviously a type-ahead): - Padding: the single-select content wasn't vertically centered -- the selected item stretched to the full content box so the text rode high (empty was 7/17). align-items:center on the single .ts-control (+ align-self:center on the item/input) -> symmetric 12/12 in both empty and selected states. - Width: 208px (w-52) was too wide -> w-48 (192px). - Search affordance: a leading magnifier (base64-SVG background on .ts-wrapper.single .ts-control + padding-left) + a clearer "Search supervisors" placeholder in the design-standard slate-400, so it reads as a type-ahead search field. Verified via CDP + darkest-pixel: icon painted (0.39 = slate-500 magnifier), placeholder fits + is slate-400, both states symmetric (12/12), width 192px, item sits after the icon. design.md documents the search-field look. court-report + supervisors index specs 32/0; erb_lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-align the caret Reported: the "Search supervisors" copy wasn't visible and the caret sat ~1/3 in. Root cause: the blank <option value=""> plus allowEmptyOption:true made TomSelect treat "" as a SELECTED item -- it hid the input (and its placeholder) off-screen (left:-10000px) and showed an empty item, so the field read as blank with the caret pushed inward. Fix: a placeholder picker now sets allowEmptyOption:false (in searchable_select, keyed off placeholder-value so the court-report picker is unchanged). The empty option is no longer an item, so the input stays on-screen showing the placeholder and the caret sits at 33px -- right after the search icon (left-aligned). Verified via CDP + pixel read: control children are just input + clear-button (no empty item), input on-screen at 33px, value empty + Assign disabled, placeholder paints (10% dark text pixels in the copy region). court-report + supervisors specs 32/0; standardjs clean. design.md notes the gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The design.md edit for the type-ahead fix (3154e6e) failed to apply due to a malformed editor payload, so the code shipped without the doc. Record it: a placeholder picker must set allowEmptyOption:false, else the blank <option> becomes a selected item and TomSelect hides the input + placeholder off-screen (the "invisible copy / caret pushed ~1/3 in" bug). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eft-aligned Reported: clicking the field put the caret ~1/3 in, and making a selection dropped it to a 2nd line (growing the field) -- abnormal for a single-select. Cause: TomSelect's input carries `min-width: 7rem !important`, so a selected name + the input exceed the ~128px usable width of the 192px field and wrap under flex-wrap:wrap. Fix (scoped to .ts-wrapper.single): flex-wrap:nowrap + input min-width:0 + item truncation (overflow/ellipsis). The item + input stay on one line (input shrinks, long names ellipsize) and the caret sits at 33px -- right after the search icon. Verified via CDP: empty-focused caret at 33px, control 42px; selected+focused stays 42px (no wrap) even with a 45-char stress name (the item ellipsizes). court-report + supervisors specs 32/0. design.md documents the one-line rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aret left on focus, icon gone on select) Reported: the caret still wasn't left-aligned on click, and the search icon lingered after a selection -- neither is standard type-ahead behavior, and the icon-after-select wasn't the direction given. Cause: the magnifier was on .ts-wrapper.single always, so its padding-left offset the caret on focus and the icon stayed once a value was chosen. Fix: scope the icon to .ts-wrapper.single:not(.focus):not(.has-items) -- shown ONLY when empty and unfocused. On focus (typing) or with a value selected it's a plain field: no icon, caret/text at the normal left (~10px, the control's base padding). Verified via CDP -- resting empty: icon, content at 33px; focused empty: no icon, caret at 11px (left); selected: no icon, name at 11px. All stay one line (42px). court-report + supervisors specs 32/0. design.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g (px-3.5 / 14px)
Follow-up: align the single-select's non-icon left padding to the design-system text inputs (px-3.5 =
14px) rather than the ts-control's 10px base, so the caret/text lines up with the other inputs.
`.ts-wrapper.single .ts-control { padding-left: 0.875rem !important }` (the resting-empty icon state
still overrides to 2rem). Verified: focused + selected caret at 15px (14px padding), resting-empty icon
at 32px, all one line (42px).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ement mapping A trackable map of the Bootstrap -> Tailwind migration, built by inspecting the code (not just the design.md checklist): every page-rendering controller resolves to a Tailwind shell (casa_app / casa_auth / all_casa_admin / own), and a Bootstrap-marker scan found only dead (0-render) legacy files, mailers, and false positives -- no live Bootstrap page remains. Includes the shells, per-area page coverage, the element/pattern mapping (card / button / DataTable / modal / form / select / flash / dropdown / names / links), and the loose ends (dead files to delete, mailers out of scope by ADR 0007, the help-link item, /health ops page). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…the migration map Verified 0 references across views / rb / js / specs before deleting (the "still used by legacy court-date pages" comment on _court_order_list was stale -- court dates render the casa_app twin casa_cases/_court_orders). Deleted: - shared/_court_order_form + _court_order_list (Bootstrap court-order sub-form; twin is casa_cases/_court_orders) - casa_cases/_thank_you_modal + case_contacts/_confirm_note_content_dialog (legacy Bootstrap modals; Dialog:: replaced them) - layouts/_mobile_navbar (legacy nav; casa_app has its own shell) - devise/shared/_links + all_casa_admins/shared/_links (legacy auth link partials) Kept (my first map was wrong): shared/_emancipation_link is LIVE (rendered by CasaCaseDecorator) but still legacy-styled (main-btn primary-btn btn-sm) -> a migration item, not a deletion. layouts/footers/_logged_in is app-dead but covered by footer.html.erb_spec.rb -> deleting it needs that spec removed too (deferred). MIGRATION-MAP.md loose-ends corrected accordingly. Smoke specs (court_dates/new, sessions/new, case_contacts/new) 46/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e migration map The footer partials (layouts/footers/_logged_in + _not_logged_in) render in no layout -- only spec/views/layouts/footer.html.erb_spec.rb exercised them. Deleted all three. Their links (Ruby For Good / Report a site issue / SMS Terms & Conditions) survive on the all_casa_admin layout footer + the static landing; casa_app itself has no footer (a pre-existing gap, noted in the map). Also corrected the map's emancipation note: _emancipation_link is rendered by CasaCaseDecorator#transition_aged_youth, which no view calls (only the decorator spec) -- the migrated cases index built its own Yes/No transition column and dropped the quick-link. So it's dead code, not a live element; left pending a delete-vs-re-add decision rather than migrating styling no one sees. Decorator + cases-index specs 18/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d but unreachable) Reported as a serious miss: admin / mileage rates / banners / imports appeared "missing". They were migrated (Tailwind, casa_app) but linked NOWHERE in the casa_app nav -- reachable only by typing the URL. A full reachability sweep confirmed 6 orphaned pages; this wires the 4 admin ones into a new "Administration" section on the Settings page (Settings is admin-only, so no per-link gating needed). Verified: the 4 links render and /casa_admins, /mileage_rates, /banners, /imports all return 200. MIGRATION-MAP.md corrected: my earlier "all migrated" claim checked Tailwind-on-a-shell, not navigability -- that was the real miss. Remaining orphans recorded: other_duties (conditional volunteer feature) and emancipation_checklists#index (index-only), placement TBD. casa_org/edit spec: only the pre-existing Twilio :63 flake fails (unrelated, parked); the additive section broke nothing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pation checklists) -- 0 orphans left Closes out the reachability sweep from the previous commit. other_duties: now a sidebar item (gated by policy(OtherDuty).index?, which already includes the org's other_duties_enabled toggle) + @active_nav in the controller so the sidebar highlights. emancipation_checklists#index: a volunteer-only link in the Cases page header (see_emancipation_checklist? is volunteers-only, so it can't live in the admin/supervisor More menu). Verified role-gated: admin sees "Other duties" in the sidebar but not the volunteer-only emancipation link (/other_duties 200); volunteer sees both, and /emancipation_checklists reaches the page (302 -> redirect to their single transitioning case, per the controller). other_duties + cases-index specs 4/0. All 6 orphaned pages are now linked; MIGRATION-MAP.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…is the point) 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.