diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index fe03b440bf..964f046150 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -274,6 +274,7 @@ Follow the [Stimulus Handbook](https://stimulus.hotwired.dev/handbook/introducti ## PRs +- **"Add prefix X" means prefix the PR title with `X`** — when the user says "add prefix" (e.g. "add prefix MAYBE:"), prepend that literal string to the current PR's title via `gh pr edit --title`, preserving the rest of the title. It always refers to the PR title, not a commit message or file content. - **Always create PRs as drafts** — every PR starts in draft (`gh pr create --draft`), no exceptions. Never open a PR ready for review, and never promote it. Only the user runs `gh pr ready`, manually and intentionally, when they decide the work is ready. - **Push to a draft PR early** — create the draft PR as soon as work begins, rather than keeping changes in a local branch. Push on every commit. - **In a new Conductor workspace, do this immediately** — as the first step of any task, make an initial commit on the workspace branch and open the draft PR right away (before the work is done), then keep pushing on every commit as you go. Don't wait until there's a finished change to show. diff --git a/AGENTS.md b/AGENTS.md index cf151739df..2818984914 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -399,9 +399,12 @@ end ### Tailwind Theme -Custom colors defined in `app/frontend/stylesheets/application.tailwind.css`: +Custom tokens defined in the `@theme` block of `app/frontend/stylesheets/application.tailwind.css`: - `--color-primary: #063b8d` (dark blue) - Standard semantic colors: secondary, danger, warning, info, success +- Sub-`xs` font sizes for dense UI: `text-2xs` (0.625rem) and `text-3xs` (0.55rem) — use these instead of arbitrary `text-[…px]` values + +Tailwind class order is sorted with rustywind via `ai/tw-sort` (Prettier's Tailwind plugin can't parse ERB). Prefer named theme tokens and static class literals over arbitrary/interpolated classes. ## Testing diff --git a/CLAUDE.md b/CLAUDE.md index 15dd1da027..783edf50d6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -284,6 +284,7 @@ Follow the [Stimulus Handbook](https://stimulus.hotwired.dev/handbook/introducti ## PRs +- **"Add prefix X" means prefix the PR title with `X`** — when the user says "add prefix" (e.g. "add prefix MAYBE:"), prepend that literal string to the current PR's title via `gh pr edit --title`, preserving the rest of the title. It always refers to the PR title, not a commit message or file content. - **Always create PRs as drafts** — every PR starts in draft (`gh pr create --draft`), no exceptions. Never open a PR ready for review, and never promote it. Only the user runs `gh pr ready`, manually and intentionally, when they decide the work is ready. - **Push to a draft PR early** — create the draft PR as soon as work begins, rather than keeping changes in a local branch. Push on every commit. - **In a new Conductor workspace, do this immediately** — as the first step of any task, make an initial commit on the workspace branch and open the draft PR right away (before the work is done), then keep pushing on every commit as you go. Don't wait until there's a finished change to show. @@ -384,6 +385,7 @@ See `ai/` directory for executable scripts: | `ai/test_extra [args]` | Full RSpec run: Vite test build + all system specs | | `ai/lint` | Rubocop on all files | | `ai/lint --fix` | Auto-fix lint issues | +| `ai/tw-sort` | Sort Tailwind class order (rustywind) on files changed vs main; `--all` for the whole tree, `--check` to verify without writing | | `ai/server` | Start dev services (web + vite) | | `ai/console` | Rails console | | `ai/routes -g pattern` | Search Rails routes | diff --git a/ai/README.md b/ai/README.md index 8b704d2059..560f0018dc 100644 --- a/ai/README.md +++ b/ai/README.md @@ -10,6 +10,7 @@ Quick-reference scripts for common development tasks. Designed for AI agents and | `ai/test_extra [args]` | Full RSpec run: Vite test build + all the headless-Chrome system specs `ai/test` narrows | | `ai/lint` | Rubocop on all files | | `ai/lint --fix` | Auto-fix lint issues | +| `ai/tw-sort` | Sort Tailwind utility-class order with rustywind. Defaults to files changed vs `origin/main` (gradual convergence); `--all` sorts the whole tree, `--check` verifies without writing (non-zero exit if any file is unsorted) | | `ai/server` | Start all dev services (web + vite) | | `ai/console` | Rails console | | `ai/routes -g pattern` | Search Rails routes | diff --git a/ai/tw-sort b/ai/tw-sort new file mode 100755 index 0000000000..411c78102f --- /dev/null +++ b/ai/tw-sort @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Sort Tailwind utility classes into canonical order with rustywind. +# +# ERB is where nearly all our classes live, and Prettier (with +# prettier-plugin-tailwindcss) can't parse ERB — so we use rustywind, which +# reorders only the class-attribute tokens in place and leaves interpolations +# (<%= ... %>) and the rest of the markup untouched. +# +# Usage: +# ai/tw-sort Sort files changed vs origin/main (gradual convergence) +# ai/tw-sort --check Check those files; non-zero exit if any need sorting +# ai/tw-sort --all Sort the whole tree (app/views + app/frontend/javascript) +# ai/tw-sort --all --check +set -euo pipefail +source "$(dirname "$0")/.ruby-env" + +rustywind="$(dirname "$0")/../node_modules/.bin/rustywind" + +mode="write" +scope="changed" +for arg in "$@"; do + case "$arg" in + --check) mode="check" ;; + --all) scope="all" ;; + *) echo "unknown option: $arg" >&2; exit 2 ;; + esac +done + +flag="--write" +[[ "$mode" == "check" ]] && flag="--check-formatted" + +if [[ "$scope" == "all" ]]; then + exec "$rustywind" "$flag" app/views app/frontend/javascript +fi + +# Changed files only: everything modified vs origin/main plus the working tree. +files=() +while IFS= read -r f; do + [[ -n "$f" && -f "$f" ]] && files+=("$f") +done < <( + { git diff --name-only --diff-filter=ACMR origin/main...HEAD + git diff --name-only --diff-filter=ACMR + git diff --name-only --diff-filter=ACMR --cached + } | sort -u | grep -E '\.(erb|js)$' || true +) + +if [[ ${#files[@]} -eq 0 ]]; then + echo "No changed .erb/.js files to sort." + exit 0 +fi + +exec "$rustywind" "$flag" "${files[@]}" diff --git a/app/frontend/stylesheets/application.tailwind.css b/app/frontend/stylesheets/application.tailwind.css index a40cce757d..4e8f93410f 100644 --- a/app/frontend/stylesheets/application.tailwind.css +++ b/app/frontend/stylesheets/application.tailwind.css @@ -17,6 +17,13 @@ --color-success: var(--color-green-500); --color-transparent: transparent; --font-telefon: "Telefon Bold", sans-serif; + + /* Sub-`text-xs` sizes for dense UI (badges, pills, stat rows). Named tokens + replacing scattered arbitrary font-size values so the tiny type stays + consistent and is adjustable in one place. 2xs (0.625rem/10px) consolidates + the ~10px cluster; 3xs (0.55rem) was the next size down. */ + --text-2xs: 0.625rem; + --text-3xs: 0.55rem; } /* Font face declarations */ diff --git a/app/helpers/organization_helper.rb b/app/helpers/organization_helper.rb index 659ff0f11d..20769ee632 100644 --- a/app/helpers/organization_helper.rb +++ b/app/helpers/organization_helper.rb @@ -56,7 +56,7 @@ def organization_profile_button(organization, truncate_at: nil, subtitle: nil, l label_tag = if label.present? content_tag(:span, label, - class: "absolute top-1 right-1 px-1.5 py-0.5 rounded text-[10px] font-semibold bg-gray-200 text-gray-600") + class: "absolute top-1 right-1 px-1.5 py-0.5 rounded text-2xs font-semibold bg-gray-200 text-gray-600") else "".html_safe end diff --git a/app/views/admin/ahoy_activities/charts.html.erb b/app/views/admin/ahoy_activities/charts.html.erb index ad9ba9eaf8..059a204126 100644 --- a/app/views/admin/ahoy_activities/charts.html.erb +++ b/app/views/admin/ahoy_activities/charts.html.erb @@ -1,6 +1,6 @@ <% content_for(:page_bg_class, "admin-only bg-blue-100") %> -
-
+
+
<% if allowed_to?(:index?, with: Admin::HomePolicy) %> <%= link_to admin_path, class: "inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700" do %> @@ -12,15 +12,15 @@
-
-

Charts

+
+

Charts

-
+
<%= render "admin/shared/active_filters_subheader" %>
-

Filters

+

Filters

<%= form_with url: admin_activities_charts_path, method: :get, local: true, class: "flex items-center gap-4" do %> <%= render "admin/shared/audience_dropdown" %> @@ -46,7 +46,7 @@
-
+
<% has_users = @audience_labels.include?("users") has_staff = @audience_labels.include?("staff") audience_note = if has_users && has_staff @@ -63,55 +63,55 @@ else "non-admin accounts" end %> -

+

Portal usage <% if audience_note %> - (<%= audience_note %>) + (<%= audience_note %>) <% end %>

-
+
<%= link_to users_path, class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Total users

+ +

Total users

-
<%= number_with_delimiter(@filtered_user_count) %>
+
<%= number_with_delimiter(@filtered_user_count) %>
<% if @audience_labels.include?("users") && @audience_labels.include?("staff") %> -
All user accounts in the system
-
<%= number_with_delimiter(@non_staff_users) %> non-admin · <%= number_with_delimiter(@staff_users) %> admin
+
All user accounts in the system
+
<%= number_with_delimiter(@non_staff_users) %> non-admin · <%= number_with_delimiter(@staff_users) %> admin
<% elsif @audience_labels.include?("staff") %> -
Admin accounts
-
of <%= number_with_delimiter(@total_users) %> total
+
Admin accounts
+
of <%= number_with_delimiter(@total_users) %> total
<% else %> -
Non-admin accounts
-
of <%= number_with_delimiter(@total_users) %> total
+
Non-admin accounts
+
of <%= number_with_delimiter(@total_users) %> total
<% end %>
<% end %> <%= link_to users_path(invited: "true"), class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Invited users

+ +

Invited users

-
<%= number_with_delimiter(@portal_access_users) %>
-
Sent the welcome instructions email
+
<%= number_with_delimiter(@portal_access_users) %>
+
Sent the welcome instructions email
<% if @has_access_users != @portal_access_users %> -
<%= @has_access_users %> have access of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
+
<%= @has_access_users %> have access of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
<% else %> -
of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
+
of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
<% end %>
<% end %> <%= link_to users_path(authenticated: "true"), class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Authenticated users

+ +

Authenticated users

-
<%= number_with_delimiter(@authenticated_users) %>
-
Logged in at least once
+
<%= number_with_delimiter(@authenticated_users) %>
+
Logged in at least once
<% if @confirmed_users != @authenticated_users %> -
<%= @confirmed_users %> confirmed of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
+
<%= @confirmed_users %> confirmed of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
<% else %> -
of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
+
of <%= number_with_delimiter(@filtered_user_count) %> <%= audience_count_label %>
<% end %>
<% end %> @@ -131,40 +131,40 @@
-
-

Engagement patterns

-
+
+

Engagement patterns

+
<%= link_to admin_activities_visits_path, class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Total visits

+ +

Total visits

-
<%= number_with_delimiter(scoped_visits.count) %>
-
Sessions in selected period
+
<%= number_with_delimiter(scoped_visits.count) %>
+
Sessions in selected period
<% if @authenticated_visits > 0 && @public_visits > 0 %> -
<%= number_with_delimiter(@authenticated_visits) %> authenticated · <%= number_with_delimiter(@public_visits) %> public
+
<%= number_with_delimiter(@authenticated_visits) %> authenticated · <%= number_with_delimiter(@public_visits) %> public
<% end %>
<% end %> <%= link_to admin_activities_events_path, class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Total activities

+ +

Total activities

-
<%= number_with_delimiter(scoped_events.count) %>
-
Actions in selected period
+
<%= number_with_delimiter(scoped_events.count) %>
+
Actions in selected period
<% if @authenticated_visits > 0 && @public_visits > 0 %> -
<%= number_with_delimiter(@authenticated_visits) %> authenticated · <%= number_with_delimiter(@public_visits) %> public
+
<%= number_with_delimiter(@authenticated_visits) %> authenticated · <%= number_with_delimiter(@public_visits) %> public
<% end %>
<% end %> -
-

Avg activities per visit

+
+

Avg activities per visit

<%= @avg_events_per_visit %>
-
Actions per session
+
Actions per session
<% if @authenticated_visits > 0 && @public_visits > 0 %> -
<%= number_with_delimiter(@authenticated_visits) %> authenticated · <%= number_with_delimiter(@public_visits) %> public
+
<%= number_with_delimiter(@authenticated_visits) %> authenticated · <%= number_with_delimiter(@public_visits) %> public
<% end %>
@@ -192,19 +192,19 @@ <% end %>
-
-

Avg session duration

+
+

Avg session duration

<%= @avg_session_minutes %>
-
Minutes per session
+
Minutes per session
-
-

Public activities

+
+

Public activities

<%= @public_events_pct %>%
-
Activities from public visitors
-
<%= @public_visits_pct %>% of visits are public
+
Activities from public visitors
+
<%= @public_visits_pct %>% of visits are public
@@ -230,7 +230,7 @@ <% max_val = @heatmap_data.values.max.to_i %> <% max_val = 1 if max_val == 0 %>
- +
<% 7.times do %><% end %> @@ -239,18 +239,18 @@ <% days.each do |d| %> - + <% end %> <% (0..23).each do |hour| %> - + <% (1..7).each do |dow| %> <% val = @heatmap_data[[hour, dow]] || 0 %> <% opacity = (val.to_f / max_val).round(2) %> - - diff --git a/app/views/events/_dashboard_money_row.html.erb b/app/views/events/_dashboard_money_row.html.erb index c3d9e20c83..1979b93d12 100644 --- a/app/views/events/_dashboard_money_row.html.erb +++ b/app/views/events/_dashboard_money_row.html.erb @@ -27,7 +27,7 @@ <% if icon_bg.present? %> - + <% else %> @@ -37,7 +37,7 @@ <%= dollars_from_cents(amount_cents) %> - + <% if registrants.any? %> @@ -53,7 +53,7 @@ <% if person_cents %> <%= dollars_from_cents(person_cents) %> <% end %> - + <% end %> @@ -64,6 +64,6 @@ <% end %> <%= link_to filter_path, class: "shrink-0 flex h-7 items-center text-gray-300 hover:text-gray-500", title: "View #{label.downcase} registrants" do %> - + <% end %> diff --git a/app/views/events/_headcount_header.html.erb b/app/views/events/_headcount_header.html.erb index cca8575dc6..936d1d4ffc 100644 --- a/app/views/events/_headcount_header.html.erb +++ b/app/views/events/_headcount_header.html.erb @@ -20,7 +20,7 @@ (claims no space) until hover, so the label keeps its full width and only truncates to make room for the icon on hover. %> <% end %> diff --git a/app/views/events/_organization_row.html.erb b/app/views/events/_organization_row.html.erb index 6fcb5abfc1..ac228a90e5 100644 --- a/app/views/events/_organization_row.html.erb +++ b/app/views/events/_organization_row.html.erb @@ -5,17 +5,17 @@ arrow sits after the percentage, matching the other breakdown cards. %> <% is_scholarship = row[:scholarship_count].positive? %> <% path = row[:path] %> - transition-colors" + transition-colors" <% if path.present? %>onclick="window.location='<%= path %>'"<% end %>> - - diff --git a/app/views/events/_participation_kpi.html.erb b/app/views/events/_participation_kpi.html.erb index 78521f120b..be37d8bad5 100644 --- a/app/views/events/_participation_kpi.html.erb +++ b/app/views/events/_participation_kpi.html.erb @@ -5,12 +5,12 @@ <%= content_tag tag, href: href, class: class_names("block rounded-xl border p-4", card_class, "relative transition-shadow hover:shadow-md" => href.present?) do %> <% if href %> - + <% end %> -
href.present?) %>"><%= label %>
+
href.present?) %>"><%= label %>
<%= number_with_delimiter(value) %>
<% if note %> -
(<%= note %>)
+
(<%= note %>)
<% end %> <% delta = participation_delta(value, local_assigns[:prior]) %> <% if delta %> diff --git a/app/views/events/_recipient_card.html.erb b/app/views/events/_recipient_card.html.erb index ae63dd75b1..06a8b66aa7 100644 --- a/app/views/events/_recipient_card.html.erb +++ b/app/views/events/_recipient_card.html.erb @@ -20,7 +20,7 @@ } %>
" - class="scroll-mt-24 bg-white border <%= DomainTheme.border_class_for(:scholarships) %> rounded-xl shadow-sm overflow-hidden break-inside-avoid" + class="scroll-mt-24 border bg-white <%= DomainTheme.border_class_for(:scholarships) %> break-inside-avoid overflow-hidden rounded-xl shadow-sm" data-controller="expandable-card" data-action="expandable-cards:expandAll@window->expandable-card#expand expandable-cards:collapseAll@window->expandable-card#collapse"> <%# Recipient header — avatar + name, with title/org beneath the name and the @@ -74,7 +74,7 @@ title: "View #{person.name}'s registration", class: "shrink-0 inline-flex items-center gap-1 text-sm text-gray-400 hover:text-gray-600" do %> - + <% end %> <% end %>
@@ -132,7 +132,7 @@
- Scholarship + Scholarship <%= dollars_from_cents(scholarship.amount_cents) %> @@ -143,7 +143,7 @@ class: "inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-800 hover:underline" do %> Funded by <%= scholarship.grant.funder_name %> - + <% end %> <% else %> @@ -158,7 +158,7 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:underline", target: "_blank", rel: "noopener" do %> Edit - + <% end %> <% end %>
@@ -172,7 +172,7 @@ <% answers.each do |answer| %>
<%= display_question_label(answer.form_field, answer) %>
-
<%= resolve_answer_text(answer.form_field, answer.submitted_answer) %>
+
<%= resolve_answer_text(answer.form_field, answer.submitted_answer) %>
<% end %> diff --git a/app/views/events/_registrants_results.html.erb b/app/views/events/_registrants_results.html.erb index 0aadb4da90..04b508fba7 100644 --- a/app/views/events/_registrants_results.html.erb +++ b/app/views/events/_registrants_results.html.erb @@ -1,8 +1,8 @@ <%= turbo_frame_tag :registrants_results do %> <%# Scholarship column shows by default only when the event charges a fee. %> <% scholarship_on = @event.cost_cents.to_i > 0 %> -
-
+
+
<% current_filter = params[:attendance_status].present? ? nil : (params[:status_filter].presence || "active") %>
<% end %> - - - + + <% readiness = @readiness[registration.id] %> - - @@ -512,7 +512,7 @@
<%= d %><%= d %>
<%= "#{hour}:00" %><%= "#{hour}:00" %> @@ -269,9 +269,9 @@
-
-

Workshop analytics

-
+
+

Workshop analytics

+
<%= chart_card("Workshop search: category types") { bar_chart(@ws_category_types) } %> <%= chart_card("Workshop search: categories") { bar_chart(@ws_category_names) } %> <%= chart_card("Workshop search: sectors") { bar_chart(@ws_sectors) } %> @@ -286,9 +286,9 @@
-
-

Resource analytics

-
+
+

Resource analytics

+
<%= chart_card("Resource search: keywords") { bar_chart(@rs_keywords) } %> <%= chart_card("Resource search: kinds") { bar_chart(@rs_kinds) } %> <%= chart_card("Resource discovery funnel") { column_chart(@rs_funnel) } %> @@ -297,27 +297,27 @@
-
-

Tag analytics

-
+
+

Tag analytics

+
<%= link_to tags_path, class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Tags page views

+ +

Tags page views

-
<%= number_with_delimiter(@tags_page_views) %>
+
<%= number_with_delimiter(@tags_page_views) %>
<% end %> <%= link_to taggings_path, class: "group relative bg-white border border-gray-200 rounded-xl shadow-sm p-6 hover:bg-blue-50 hover:border-blue-300 hover:shadow-md transition-all" do %> - -

Taggings page views

+ +

Taggings page views

-
<%= number_with_delimiter(@taggings_page_views) %>
+
<%= number_with_delimiter(@taggings_page_views) %>
<% end %> -
-

Tagging searches

+
+

Tagging searches

<%= number_with_delimiter(@tagging_searches) %>
@@ -329,13 +329,13 @@ <%= chart_card("Tagging search origin") { pie_chart(@tagging_search_origin) } %> -
-

Top filter combinations

+
+

Top filter combinations

    <% @top_filter_combos.each do |label, count| %> -
  • - <%= count %> - × +
  • + <%= count %> + × <%= label %>
  • <% end %> @@ -346,9 +346,9 @@
-
-

Content discovery

-
+
+

Content discovery

+
<%= chart_card("Content types people view most") do %> <%= pie_chart( scoped_events @@ -376,33 +376,33 @@
-
-

Content creation

-
-
-

Ideas submitted

+
+

Content creation

+
+
+

Ideas submitted

<%= number_with_delimiter(@ideas_submitted) %>
-
-

Ideas promoted

+
+

Ideas promoted

<%= number_with_delimiter(@ideas_promoted) %>
-
Converted to published content
+
Converted to published content
-
-

Avg ideas per person

+
+

Avg ideas per person

<%= @avg_ideas_per_person %>
-
-

Total content created

+
+

Total content created

    <% @total_generated_content.each do |row| %> <% if row == [ :spacer ] %> @@ -411,7 +411,7 @@ <% label, total, idea_count, admin_count, promoted = row %>
  • <%= total %> - × + × <%= label %> <% if idea_count.present? && admin_count.present? %> <% p = promoted || 0 %> @@ -426,24 +426,24 @@
-
-

User-generated content

+
+

User-generated content

    <% @user_generated_content.each do |label, count, promoted| %>
  • <%= count %> - × + × <%= label %> <% if promoted.present? && promoted > 0 %> - (<%= promoted %> promoted) + (<%= promoted %> promoted) <% end %>
  • <% end %>
-
-

Admin-generated content

+
+

Admin-generated content

    <% @admin_generated_content.each do |row| %> <% if row == [ :spacer ] %> @@ -452,10 +452,10 @@ <% label, count, promoted = row %>
  • <%= count %> - × + × <%= label %> <% if promoted.present? && promoted > 0 %> - (incl <%= promoted %> promoted ideas) + (incl <%= promoted %> promoted ideas) <% end %>
  • <% end %> @@ -467,9 +467,9 @@
-
-

Referrals & technical

-
+
+

Referrals & technical

+
<%= chart_card("Top referrer → landing pages") do %> <%= bar_chart @top_referrer_landing, library: { indexAxis: "y" } %> <% end %> diff --git a/app/views/admin/analytics/index.html.erb b/app/views/admin/analytics/index.html.erb index fd3faa9e66..d44250f51e 100644 --- a/app/views/admin/analytics/index.html.erb +++ b/app/views/admin/analytics/index.html.erb @@ -1,6 +1,6 @@ <% content_for(:page_bg_class, "admin-only bg-blue-100") %> -
-
+
+
<% if allowed_to?(:index?, with: Admin::HomePolicy) %> <%= link_to admin_path, class: "inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700" do %> @@ -12,15 +12,15 @@
-
-

Counts

+
+

Counts

-
+
<%= render "admin/shared/active_filters_subheader" %>
-

Filters

+

Filters

<%= form_with url: admin_activities_counts_path, method: :get, local: true, class: "flex items-center gap-4" do |f| %> <%= render "admin/shared/audience_dropdown" %> @@ -44,22 +44,21 @@
-
-

+
+

View counts

-
+
<% @summary.each do |label, data| %> <%= link_to polymorphic_path(label.to_s.classify.constantize), class: "block group" do %> -
- -
+
+ +
<%= label.to_s.humanize %>
-
+
<%= number_with_delimiter(data[:views]) %>
@@ -79,9 +78,9 @@

-
-

Workshops

-
+
+

Workshops

+
<%= render "admin/analytics/popular_list", title: "Most viewed", records: @most_viewed_workshops, @@ -102,9 +101,9 @@
-
-

Resources

-
+
+

Resources

+
<%= render "admin/analytics/popular_list", title: "Most viewed", records: @most_viewed_resources, @@ -128,9 +127,9 @@
-
-

<%= CommunityNews.model_name.human.pluralize %>

-
+
+

<%= CommunityNews.model_name.human.pluralize %>

+
<%= render "admin/analytics/popular_list", title: "Most viewed", records: @most_viewed_community_news, @@ -151,9 +150,9 @@
-
-

Stories

-
+
+

Stories

+
<%= render "admin/analytics/popular_list", title: "Most viewed", records: @most_viewed_stories, @@ -174,9 +173,9 @@
-
-

Other

-
+
+

Other

+
<%= render "admin/analytics/popular_list", title: "Events", records: @most_viewed_events, @@ -230,12 +229,12 @@
-
-

+
+

No engagement

-
+
<%= render "zero_list", title: "Workshops", records: @zero_engagement_workshops, diff --git a/app/views/allocations/_allocatable_link.html.erb b/app/views/allocations/_allocatable_link.html.erb index d1778a06d2..4565a12303 100644 --- a/app/views/allocations/_allocatable_link.html.erb +++ b/app/views/allocations/_allocatable_link.html.erb @@ -14,7 +14,7 @@ <%= descriptor[:subtitle] %> <% end %> - + <% end %> <% else %> diff --git a/app/views/allocations/_allocated_to.html.erb b/app/views/allocations/_allocated_to.html.erb index 8b9034b026..48021a4f3b 100644 --- a/app/views/allocations/_allocated_to.html.erb +++ b/app/views/allocations/_allocated_to.html.erb @@ -2,12 +2,12 @@ allocatable is an event registration, the whole block is a button to its admin page with a jump-link icon in the top-right corner. ---%>
- Allocated to: + Allocated to: <% if allocatable.is_a?(EventRegistration) %> <%= link_to edit_event_registration_path(allocatable), class: "relative block rounded-lg border border-gray-300 bg-white px-3 py-2 pr-8 transition-colors hover:border-gray-400 hover:bg-gray-50", data: { turbo_frame: "_top" } do %> - + Event registration for <%= allocatable.registrant&.full_name %> <%= allocatable.event.title %> · <%= allocatable.event.decorate.times(display_day: true, display_date: true) %> <% end %> diff --git a/app/views/comments/_aggregated_comment.html.erb b/app/views/comments/_aggregated_comment.html.erb index 146b6b59bd..66f5cb7753 100644 --- a/app/views/comments/_aggregated_comment.html.erb +++ b/app/views/comments/_aggregated_comment.html.erb @@ -20,7 +20,7 @@ <%= render "comments/flag_toggle", commentable: comment.commentable, comment: comment.object %>
diff --git a/app/views/continuing_education_registrations/_payment_history.html.erb b/app/views/continuing_education_registrations/_payment_history.html.erb index b96408c4b0..738af1ae03 100644 --- a/app/views/continuing_education_registrations/_payment_history.html.erb +++ b/app/views/continuing_education_registrations/_payment_history.html.erb @@ -13,14 +13,14 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:underline", target: "_blank", rel: "noopener" do %> View all - + <% end %>
-
CE cost
+
CE cost
<% if params[:admin] == "true" %>
<%= number_field_tag "continuing_education_registration[cost_dollars]", @@ -35,14 +35,14 @@ <%= link_to allocations_path(allocatable_sgid: ce_registration.to_sgid.to_s, return_to: "ce_registration"), target: "_blank", rel: "noopener", class: "rounded-lg border border-gray-300 bg-gray-50 px-4 py-3 block transition hover:border-gray-400 hover:shadow-sm" do %> -
Amount allocated
+
Amount allocated
<%= dollars_from_cents(paid_cents) %>
<% end %> <% amount_due = due_cents > 0 %> <%= link_to allocations_path(allocatable_sgid: ce_registration.to_sgid.to_s, return_to: "ce_registration"), target: "_blank", rel: "noopener", class: "rounded-lg border px-4 py-3 block transition hover:shadow-sm #{amount_due ? "border-amber-300 bg-amber-50 hover:border-amber-400" : "border-gray-300 bg-gray-50 hover:border-gray-400"}" do %> -
Amount due
+
Amount due
"> <% if amount_due %> diff --git a/app/views/continuing_education_registrations/edit.html.erb b/app/views/continuing_education_registrations/edit.html.erb index 77b8ac40a9..ed89bc09ed 100644 --- a/app/views/continuing_education_registrations/edit.html.erb +++ b/app/views/continuing_education_registrations/edit.html.erb @@ -2,9 +2,9 @@ <% registration = @ce_registration.event_registration %> <% license = @ce_registration.professional_license %> -
+
<%# Top bar: back link + secondary links, matching the scholarship edit page %> -
+
<%= link_to ce_registration_return_path(registration), class: "text-sm text-gray-500 hover:text-gray-700" do %> <%= ce_registration_return_label %> <% end %> @@ -15,7 +15,7 @@ <%= render "shared/badge", label: "Registrant's CE page", classes: "bg-sky-100 text-sky-700 border-sky-200", - icon: "fa-solid fa-id-card text-[0.6rem]", + icon: "fa-solid fa-id-card text-2xs", href: registration_ce_path(registration.slug, return_to: "ce_registration"), target: "_blank", rel: "noopener" %> <%= link_to "Home", root_path, class: "text-sm text-gray-500 hover:text-gray-700" %> @@ -50,7 +50,7 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-transparent hover:text-gray-600 hover:underline", target: "_blank", rel: "noopener" do %> View all - + <% end %> <% end %>
@@ -69,14 +69,14 @@ partial: "comments/comment_fields", data: { association_insertion_node: "#comment-list", association_insertion_method: "append" }, class: "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer" do %> - + Add comment <% end %> diff --git a/app/views/event_registrations/_attendance_status_badge.html.erb b/app/views/event_registrations/_attendance_status_badge.html.erb index 768ce79e32..126cfaa745 100644 --- a/app/views/event_registrations/_attendance_status_badge.html.erb +++ b/app/views/event_registrations/_attendance_status_badge.html.erb @@ -1,7 +1,7 @@ <% deco = registration.decorate %>
<%= form_with model: registration, url: event_registration_path(registration), method: :patch, data: { turbo_frame: "_top" } do |f| %> -
+
<%= f.select :status, EventRegistration::ATTENDANCE_STATUSES.map { |s| [EventRegistration.new(status: s).attendance_status_label, s] }, @@ -10,7 +10,7 @@ onchange: "this.form.requestSubmit()", "aria-label": "Change attendance status" %> <%= registration.attendance_status_label %> - +
<% end %>
diff --git a/app/views/event_registrations/_continuing_education.html.erb b/app/views/event_registrations/_continuing_education.html.erb index 04f3d56270..5c9cf62bea 100644 --- a/app/views/event_registrations/_continuing_education.html.erb +++ b/app/views/event_registrations/_continuing_education.html.erb @@ -17,9 +17,9 @@ <%= link_to new_continuing_education_registration_path(allocatable_sgid: event_registration.to_sgid.to_s, return_to: "registration"), class: "inline-flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700", target: "_blank", rel: "noopener" do %> - + Add CE registration - + <% end %>
<% else %> @@ -33,7 +33,7 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:underline", target: "_blank", rel: "noopener" do %> Edit - + <% end %>
diff --git a/app/views/event_registrations/_form.html.erb b/app/views/event_registrations/_form.html.erb index f658ccb844..f5b965b516 100644 --- a/app/views/event_registrations/_form.html.erb +++ b/app/views/event_registrations/_form.html.erb @@ -63,7 +63,7 @@ end %> <% account_box = capture do %> - User account + User account <% end %> <% if allowed_to?(:show?, account) %> <%= link_to user_path(account), class: "#{box_base} #{account_border}", title: "User account: #{account_status}", data: { turbo_frame: "_top" } do %> @@ -75,12 +75,12 @@ <% elsif allowed_to?(:new?, User) %> <%= link_to new_user_path(person_id: f.object.registrant_id, event_registration_id: f.object.id), class: "#{box_base} border-gray-300 text-gray-600", title: "Create a user account", data: { turbo_frame: "_top" } do %> - Create user + Create user <% end %> <% else %>
- No account + No account
<% end %> @@ -88,9 +88,9 @@
Registration status
@@ -122,7 +122,7 @@ data: { turbo_frame: "_top" } do %> View ticket - + <% end %> <% end %> @@ -134,7 +134,7 @@ data: { turbo_frame: "_top" } do %> View form submission<%= " ##{i + 1}" if submissions.size > 1 %> - + <% end %> <% end %> @@ -210,7 +210,7 @@ data-org-toggle-target="addButton" data-action="org-toggle#showAddForm" class="inline-flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer"> - + Connect organization
@@ -287,7 +287,7 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-transparent hover:text-gray-600 hover:underline", target: "_blank", rel: "noopener" do %> View all - + <% end %> <% end %>
@@ -306,7 +306,7 @@ partial: "comments/comment_fields", data: { association_insertion_node: "#comment-list", association_insertion_method: "append" }, class: "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer" do %> - + Add comment <% end %> @@ -315,7 +315,7 @@ class="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 cursor-pointer" data-action="edit-toggle#toggle" > - + Edit comments diff --git a/app/views/event_registrations/_org_affiliation_pills.html.erb b/app/views/event_registrations/_org_affiliation_pills.html.erb index 991ebc581b..fd6c5d78ca 100644 --- a/app/views/event_registrations/_org_affiliation_pills.html.erb +++ b/app/views/event_registrations/_org_affiliation_pills.html.erb @@ -6,7 +6,7 @@ <% position = submitted_position.to_s.strip %> <% is_submitted_org = submitted_org_name.to_s.strip.present? && org.name.to_s.strip.casecmp?(submitted_org_name.to_s.strip) %>
- Affiliations: + Affiliations: <% affiliations.each do |a| %> <% a_active = active.call(a) %> <% color = a_active && !local_assigns[:neutral] ? "bg-green-50 text-green-700 border-green-200" : "bg-gray-100 text-gray-600 border-gray-200" %> diff --git a/app/views/event_registrations/_payment_history.html.erb b/app/views/event_registrations/_payment_history.html.erb index 13f5674696..b70a92646b 100644 --- a/app/views/event_registrations/_payment_history.html.erb +++ b/app/views/event_registrations/_payment_history.html.erb @@ -16,7 +16,7 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:underline after:absolute after:inset-0 after:content-['']", target: "_blank", rel: "noopener" do %> View all - + <% end %>
diff --git a/app/views/event_registrations/_readiness_badge.html.erb b/app/views/event_registrations/_readiness_badge.html.erb index 22f89d3224..f112001ad2 100644 --- a/app/views/event_registrations/_readiness_badge.html.erb +++ b/app/views/event_registrations/_readiness_badge.html.erb @@ -20,6 +20,6 @@
<%= render "shared/badge", label: label, classes: style, icon: "fas #{icon}", title: tooltip %> <% if subtext.present? %> - <%= subtext %> + <%= subtext %> <% end %>
diff --git a/app/views/event_registrations/_scholarship.html.erb b/app/views/event_registrations/_scholarship.html.erb index 88ce06e026..1e6a7fce30 100644 --- a/app/views/event_registrations/_scholarship.html.erb +++ b/app/views/event_registrations/_scholarship.html.erb @@ -31,7 +31,7 @@ class: "ml-auto inline-flex items-center gap-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:underline", target: "_blank", rel: "noopener" do %> Edit - + <% end %>
@@ -80,9 +80,9 @@ <%= link_to new_scholarship_path(allocatable_sgid: event_registration.to_sgid.to_s, return_to: "registration"), class: "inline-flex items-center gap-1.5 self-start rounded-md px-2 py-1 text-xs font-medium text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700", target: "_blank", rel: "noopener" do %> - + Add scholarship - + <% end %>
<% end %> diff --git a/app/views/event_registrations/link_organization.html.erb b/app/views/event_registrations/link_organization.html.erb index baebbfd995..d396b5c79b 100644 --- a/app/views/event_registrations/link_organization.html.erb +++ b/app/views/event_registrations/link_organization.html.erb @@ -53,7 +53,7 @@ data: { turbo_frame: "_top" } do %>
-

Organization: <%= entry[:org_name].presence || "—" %><% if entry[:organization]&.city_state.present? %> · <%= entry[:organization].city_state %><% end %><%# Mirror the roster's amber "Pending" chip so it's clear why this registrant was flagged — shown only while nothing is linked. %><% if entry[:org_name].present? && @linked_organizations.none? %> <%= render "shared/badge", label: "Pending", classes: "bg-amber-50 text-amber-700 border-amber-200", icon: "fa-solid fa-circle-exclamation text-[0.65rem]", extra: "ml-1", title: "The registrant submitted this organization, but none is linked to the registration yet." %><% end %>

+

Organization: <%= entry[:org_name].presence || "—" %><% if entry[:organization]&.city_state.present? %> · <%= entry[:organization].city_state %><% end %><%# Mirror the roster's amber "Pending" chip so it's clear why this registrant was flagged — shown only while nothing is linked. %><% if entry[:org_name].present? && @linked_organizations.none? %> <%= render "shared/badge", label: "Pending", classes: "bg-amber-50 text-amber-700 border-amber-200", icon: "fa-solid fa-circle-exclamation text-2xs", extra: "ml-1", title: "The registrant submitted this organization, but none is linked to the registration yet." %><% end %>

Position / title: <%= entry[:position].presence || "—" %>

<% end %> diff --git a/app/views/events/_attendance_day_form.html.erb b/app/views/events/_attendance_day_form.html.erb index efe9b50394..09b760418e 100644 --- a/app/views/events/_attendance_day_form.html.erb +++ b/app/views/events/_attendance_day_form.html.erb @@ -36,6 +36,6 @@
<%= link_to "Cancel", cancel_path, class: "text-xs text-gray-500 hover:text-gray-700" %> - Leave the sign-out blank to leave a session open. + Leave the sign-out blank to leave a session open.
<% end %> diff --git a/app/views/events/_attendance_sessions.html.erb b/app/views/events/_attendance_sessions.html.erb index 217d912c19..6770520a81 100644 --- a/app/views/events/_attendance_sessions.html.erb +++ b/app/views/events/_attendance_sessions.html.erb @@ -32,7 +32,7 @@ <%= link_to attendance_event_path(@event, **state, edit: cell, anchor: cell), class: "ml-1 rounded px-1.5 py-0.5 text-xs font-medium text-teal-700 opacity-50 transition-opacity hover:bg-teal-50 hover:underline group-hover:opacity-100 focus:opacity-100", "aria-label": "Edit #{row.name}'s times for #{date.strftime("%b %-d")}" do %> - Edit + Edit <% end %>
<% end %> diff --git a/app/views/events/_attendance_stat_row.html.erb b/app/views/events/_attendance_stat_row.html.erb index bef26851e6..1d2d866da9 100644 --- a/app/views/events/_attendance_stat_row.html.erb +++ b/app/views/events/_attendance_stat_row.html.erb @@ -23,7 +23,7 @@ <%= count %> <%= label %> - + <% if registrants.any? %>
    @@ -33,7 +33,7 @@ title: [ registrant.name, *@dashboard.organization_names_by_registrant[registrant.id] ].join(" · "), class: "group/rowlink flex items-center justify-between gap-2 rounded px-1 -mx-1 py-0.5 hover:bg-gray-50" do %> <%= registrant.name %> - + <% end %> <% end %> @@ -43,6 +43,6 @@ <% end %> <%= link_to filter_path, class: "shrink-0 flex h-7 items-center text-gray-300 hover:text-gray-500", title: "View #{label.downcase} registrants" do %> - + <% end %>

diff --git a/app/views/events/_breakdown_card.html.erb b/app/views/events/_breakdown_card.html.erb index 9c1c538f1b..3fa581c30b 100644 --- a/app/views/events/_breakdown_card.html.erb +++ b/app/views/events/_breakdown_card.html.erb @@ -56,7 +56,7 @@ <% if path %>onclick="window.location='<%= path %>'"<% end %>>
<% if chart == :pie %> - + <% end %> <% if path %> <%= link_to label, path, class: "group-hover:underline" %> @@ -64,11 +64,11 @@ <%= label %> <% end %> + <%= count %> · <%= total.zero? ? 0 : (count * 100.0 / total).round(1) %>% <%# Jump-link affordance: invisible (white) until the row is hovered, matching the dashboard rows. %> <% if path %> - + <% end %>
- + + <% if is_scholarship %> " aria-hidden="true"> <% end %> <% if row[:high_profile] %> - + <% end %> <% if path.present? %> <%= link_to row[:name], path, class: "group-hover:underline align-middle" %> @@ -23,11 +23,11 @@ <%= row[:name] %> <% end %> + "><%= row[:count] %> · <%= total.zero? ? 0 : (row[:count] * 100.0 / total).round(1) %>% <%# Jump-link affordance: invisible (white) until the row is hovered, matching the other breakdown cards. %> <% if path.present? %> - + <% end %>
" data-column-toggle-col="scholarship" data-sort-value="<%= scholarship_sort %>"> + " data-column-toggle-col="scholarship" data-sort-value="<%= scholarship_sort %>"> <% if (s = scholarship) %> <% if s.tasks_completed? %> <%= render "shared/badge", @@ -429,7 +429,7 @@ <% if @event.cost_cents.to_i > 0 %> <% paid_cents = registration.allocations_sum %> <% due_cents = @event.cost_cents - paid_cents %> - + <% is_paid = registration.paid_in_full? %> <% is_discounted = !is_paid && registration.discounted? %> <% is_partial = !is_discounted && registration.partially_paid? %> @@ -460,7 +460,7 @@ title: badge_title %> <% if !is_paid && registration.intends_to_pay? %> -
+
Intends to pay
@@ -468,9 +468,9 @@ <% payment_badges = registration.decorate.payment_badges %> <% if payment_badges.any? %> -
<%= render "event_registrations/attendance_status_badge", registration: registration %><%= render "event_registrations/attendance_status_badge", registration: registration %> + <%= render "event_registrations/readiness_badge", status: readiness.status, label: readiness.status_label, issues: readiness.status_issues, subtext: readiness.status_reason %> <%= link_to "Edit", edit_event_registration_path(registration, return_to: "registrants"), class: "text-gray-500 hover:text-gray-700 underline", data: { turbo_frame: "_top" } %>
<% else %> -

No registrants found.

+

No registrants found.

<% end %>
<% end %> diff --git a/app/views/events/_registration_ticket_callout_fields.html.erb b/app/views/events/_registration_ticket_callout_fields.html.erb index 95b1d4dfe1..10a0278f0b 100644 --- a/app/views/events/_registration_ticket_callout_fields.html.erb +++ b/app/views/events/_registration_ticket_callout_fields.html.erb @@ -89,7 +89,7 @@ toggles `hidden`). Defaults open when published, closed otherwise; render the matching initial state to avoid a flash before the controller connects. `mt-5` keeps the preview card clear of the controls above it. %> -
space-y-3 mt-5 cursor-auto select-auto" data-expandable-card-target="body"> +
mt-5 cursor-auto space-y-3 select-auto" data-expandable-card-target="body"> <% if f.object.staff? %>
@@ -122,9 +122,9 @@ identity (title + presentation) on the left, its page content on the right. %> -
+
-
+
<%= f.label :title, "Title", class: "block text-xs font-medium text-gray-600 mb-0.5" %><%= f.text_field :title, required: true, class: "w-full rounded border-gray-300 bg-white shadow-sm px-2 py-1 text-sm" %>
@@ -154,7 +154,7 @@
- + Type, color, and icon @@ -202,7 +202,7 @@ <% unless f.object.payment? %>
> - + Visibility @@ -229,7 +229,7 @@ <%# RIGHT COLUMN: linked resources. Opened on load only when resources are saved. %>
> - + Linked resources @@ -240,13 +240,13 @@ <%# Column headers — mirror the row's flex + 3-col grid (plus a spacer for the remove button) so they line up. Hidden on mobile, where the fields stack to one column. %> -