-
-
-
System Imports
-
- You should import files in the following order:
-
-
- - Volunteers
- - Supervisors
- - Cases
-
-
-
-
- <%= content_tag :div,
- role: "tabpanel",
- id: "volunteer",
- class: [
- "tab-pane",
- "fade",
- ("show" if @import_type == "volunteer"),
- ("active" if @import_type == "volunteer")
- ].compact,
- aria: {labelledby: "volunteer-tab"} do %>
-
- <%= render "volunteers" %>
- <%- end %>
- <%= content_tag :div,
- role: "tabpanel",
- id: "supervisor",
- class: [
- "tab-pane",
- "fade",
- ("show" if @import_type == "supervisor"),
- ("active" if @import_type == "supervisor")
- ].compact,
- aria: {labelledby: "supervisor-tab"} do %>
-
- <%= render "supervisors" %>
- <%- end %>
- <%= content_tag :div,
- role: "tabpanel",
- id: "casa-case",
- class: [
- "tab-pane",
- "fade",
- ("show" if @import_type == "casa_case"),
- ("active" if @import_type == "casa_case")
- ].compact,
- aria: {labelledby: "casa-case-tab"} do %>
-
- <%= render "cases" %>
- <%- end %>
-
-
-
+
+ <% if @import_error %>
+
+
CSV import error
+
<%= sanitize @import_error %>
+ <% end %>
+
+
+
+
+
+
+ <% case @import_type %>
+ <% when "supervisor" %>
+ <%= render "supervisors" %>
+ <% when "casa_case" %>
+ <%= render "cases" %>
+ <% else %>
+ <%= render "volunteers" %>
+ <% end %>
- <%= render "csv_error_modal", {import_error: @import_error} if @import_error %>
diff --git a/app/views/judges/_form.html.erb b/app/views/judges/_form.html.erb
index 3a42e21d42..a23f5c3a4c 100644
--- a/app/views/judges/_form.html.erb
+++ b/app/views/judges/_form.html.erb
@@ -1,36 +1 @@
-
-
-
-
- <%= form_with(model: judge, local: true) do |form| %>
-
- <%= render "/shared/error_messages", resource: judge %>
-
-
- <%= form.label :name, "Name" %>
- <%= form.text_field :name, class: "form-control", required: true %>
-
-
-
- <%= form.check_box :active, class: 'form-check-input' %>
- <%= form.label :active, "Active?", class: 'form-check-label' %>
-
-
-
- <%= button_tag(type: "submit", class: "btn-sm main-btn primary-btn btn-hover") do %>
- Submit
- <% end %>
-
- <% end %>
-
-
+<%= render "shared/settings_form", model: judge, title: title, show_active: true %>
diff --git a/app/views/judges/new.html.erb b/app/views/judges/new.html.erb
index 0e6ad887c2..c4a3b58ea0 100644
--- a/app/views/judges/new.html.erb
+++ b/app/views/judges/new.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: {title: "New Judge", judge: @judge} %>
+<%= render partial: "form", locals: {title: "New judge", judge: @judge} %>
diff --git a/app/views/languages/_form.html.erb b/app/views/languages/_form.html.erb
index a1f8643d6f..b2dcf7c6a4 100644
--- a/app/views/languages/_form.html.erb
+++ b/app/views/languages/_form.html.erb
@@ -1,36 +1 @@
-
-
-
- <%= form_with(model: language, local: true) do |form| %>
-
- <%= render "/shared/error_messages", resource: language %>
-
-
-
- A list of languages volunteers can add to their profile to let supervisors and admins know they can speak the language.
-
-
-
-
-
- <%= form.label :name, "Name" %>
- <%= form.text_field :name, class: "form-control", required: true %>
-
-
-
- <%= button_tag(type: "submit", class: "btn-sm main-btn primary-btn btn-hover") do %>
- Submit
- <% end %>
-
- <% end %>
-
+<%= render "shared/settings_form", model: language, title: title, description: "A list of languages volunteers can add to their profile to let supervisors and admins know they can speak the language." %>
diff --git a/app/views/languages/edit.html.erb b/app/views/languages/edit.html.erb
index fcd0f8d36d..ecefb0ed71 100644
--- a/app/views/languages/edit.html.erb
+++ b/app/views/languages/edit.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: { title: "Edit Language", language: @language } %>
+<%= render partial: "form", locals: { title: "Edit language", language: @language } %>
diff --git a/app/views/languages/new.html.erb b/app/views/languages/new.html.erb
index b942c45ba4..7980320e69 100644
--- a/app/views/languages/new.html.erb
+++ b/app/views/languages/new.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: { title: "New Language", language: @language } %>
+<%= render partial: "form", locals: { title: "New language", language: @language } %>
diff --git a/app/views/layouts/_all_casa_admin_sidebar.html.erb b/app/views/layouts/_all_casa_admin_sidebar.html.erb
deleted file mode 100644
index 733fabdf5f..0000000000
--- a/app/views/layouts/_all_casa_admin_sidebar.html.erb
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
diff --git a/app/views/layouts/_casa_banner.html.erb b/app/views/layouts/_casa_banner.html.erb
new file mode 100644
index 0000000000..70950e5a0d
--- /dev/null
+++ b/app/views/layouts/_casa_banner.html.erb
@@ -0,0 +1,9 @@
+<%# Org announcement banner for the casa_app (Tailwind) shell. Uses the same
+ @active_banner + dismiss controller as the legacy layout. %>
+<% if @active_banner %>
+
+
+
<%= @active_banner.content %>
+
Dismiss
+
+<% end %>
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
deleted file mode 100644
index 75ccef2185..0000000000
--- a/app/views/layouts/_header.html.erb
+++ /dev/null
@@ -1,115 +0,0 @@
-
-<%= render 'layouts/flash_messages' %>
diff --git a/app/views/layouts/_impersonation_banner.html.erb b/app/views/layouts/_impersonation_banner.html.erb
new file mode 100644
index 0000000000..cc0cd9e7d8
--- /dev/null
+++ b/app/views/layouts/_impersonation_banner.html.erb
@@ -0,0 +1,13 @@
+<%# Impersonation banner for the casa_app shell. Shown only while an admin/supervisor is
+ impersonating another user (pretender's current_user != true_user). The whole bar is the
+ "stop impersonating" control. Keeps the `.header` hook the system specs assert against.
+ Amber-950 on amber-400 is ~8:1 (AA). %>
+<% if current_user != true_user %>
+
+<% end %>
diff --git a/app/views/layouts/_sidebar.html.erb b/app/views/layouts/_sidebar.html.erb
deleted file mode 100644
index 22e2cbbbc0..0000000000
--- a/app/views/layouts/_sidebar.html.erb
+++ /dev/null
@@ -1,76 +0,0 @@
-
diff --git a/app/views/layouts/all_casa_admin.html.erb b/app/views/layouts/all_casa_admin.html.erb
new file mode 100644
index 0000000000..ae5fab3b04
--- /dev/null
+++ b/app/views/layouts/all_casa_admin.html.erb
@@ -0,0 +1,84 @@
+
+
+
+
+
+
<%= content_for?(:page_title) ? "#{content_for(:page_title)} \u00b7 CASA" : "All CASA Admin" %>
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+ <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
+ <%= javascript_include_tag "all_casa_admin", "data-turbo-track": "reload", defer: true %>
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
+
+
+
Skip to main content
+
+
+
+
+
+
+
+
+
+
+ <% if flash.any? { |_, message| message.present? } %>
+
+ <% end %>
+ <%= yield %>
+
+
+
+
+
+
+
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
deleted file mode 100644
index 29d59e4d22..0000000000
--- a/app/views/layouts/application.html.erb
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
-
CASA Volunteer Tracking
-
-
-
-
- <%= csrf_meta_tags %>
- <%= csp_meta_tag %>
-
- <%= og_tag :title, content: "CASA Volunteer Tracking" %>
- <%= og_tag :description, content: "Volunteer activity tracking for CASA volunteers, supervisors, and administrators." %>
- <%= og_tag :url, content: root_url %>
- <%= og_tag :image, content: image_url('login.jpg') %>
-
- <%= render 'shared/favicons' %>
-
-
- <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
- <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
-
- <% if all_casa_admin_signed_in? %>
- <%= javascript_include_tag "all_casa_admin", "data-turbo-track": "reload", defer: true %>
- <% end %>
-
-
-
-
-
-
-
-
-<% if all_casa_admin_signed_in? %>
- <%= render 'layouts/all_casa_admin_sidebar' %>
-<% elsif signed_in? %>
- <%= render 'layouts/sidebar' %>
-<% end %>
-
-
-<% if all_casa_admin_signed_in? %>
-<% elsif signed_in? %>
-<%= render 'layouts/header' %>
-<% end %>
-
-
-
-
- <%= render "layouts/components/notifier" %>
-
-
-
-
-
-
-
diff --git a/app/views/layouts/casa_app.html.erb b/app/views/layouts/casa_app.html.erb
new file mode 100644
index 0000000000..0f4c4eea5a
--- /dev/null
+++ b/app/views/layouts/casa_app.html.erb
@@ -0,0 +1,117 @@
+
+
+
+
+
+
<%= content_for?(:page_title) ? "#{content_for(:page_title)} · CASA Volunteer Tracker" : "CASA Volunteer Tracker" %>
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+ <%# window.timeout (minutes) drives src/session_timeout_poller.js; @timeout_duration =
+ current_user.timeout_in (set in ApplicationController). Must precede the deferred bundle. %>
+ <% if @timeout_duration %><% end %>
+ <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
+
+
+ <% initials = avatar_initials(display_person(current_user)) %>
+ <% unread = current_user.notifications.unread.count %>
+ <% role_badge_class = {"Volunteer" => "bg-sky-50 text-sky-700", "Supervisor" => "bg-violet-50 text-violet-700", "Casa Admin" => "bg-amber-50 text-amber-700"}[current_role] || "bg-slate-100 text-slate-600" %>
+
+
Skip to main content
+
+
+
+
+
+
+
+ <%= render "layouts/impersonation_banner" %>
+ <%# relative z-[25] keeps the top bar (and its account/notification dropdowns) above page
+ content: the account menu is an absolute panel inside this header, and relying on the
+ header's backdrop-blur stacking context alone let any page element with its own stacking
+ context (a positioned z-* toolbar, a transform/hover-lift card, a native control) tie it
+ and win by DOM order — painting a page button over the open dropdown. 25 sits above all
+ page content (which tops out at z-20) yet BELOW the mobile nav scrim (z-30) so the drawer
+ still dims the header, and below the sidebar drawer (z-40) and native
+
+
+
diff --git a/app/views/layouts/casa_auth.html.erb b/app/views/layouts/casa_auth.html.erb
new file mode 100644
index 0000000000..b249e4180e
--- /dev/null
+++ b/app/views/layouts/casa_auth.html.erb
@@ -0,0 +1,59 @@
+
+
+
+
+
+
<%= content_for?(:page_title) ? "#{content_for(:page_title)} · CASA Volunteer Tracker" : "CASA Volunteer Tracker" %>
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+ <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
+
+
+
+
+
+
+
+
+
+ CV
+ CASA Volunteer Tracker
+
+ <% if flash.any? { |_, message| message.present? } %>
+
+ <% flash.each do |type, message| %>
+ <% next if message.blank? %>
+ <% flash_classes = ["alert", type.to_s].uniq.join(" ") %>
+
" class="<%= flash_classes %> rounded-lg border px-4 py-3 text-sm <%= type.to_s == "notice" ? "border-emerald-200 bg-emerald-50 text-emerald-800" : "border-amber-200 bg-amber-50 text-amber-800" %>"><%= message %>
+ <% end %>
+
+ <% end %>
+ <%= yield %>
+
+
+
+
+
+
diff --git a/app/views/layouts/component_preview.html.erb b/app/views/layouts/component_preview.html.erb
new file mode 100644
index 0000000000..2c9148ba49
--- /dev/null
+++ b/app/views/layouts/component_preview.html.erb
@@ -0,0 +1,18 @@
+
+<%# Layout for ViewComponent previews (/rails/view_components/...). Loads the app JS
+ bundle so Stimulus controllers connect (previews are interactive + system-spec'd) and
+ tailwind.css so components render with their real styling. %>
+
+
+
+
+
Component preview
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+ <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
+
+
+ <%= yield %>
+
+
diff --git a/app/views/layouts/devise.html.erb b/app/views/layouts/devise.html.erb
deleted file mode 100644
index 7799ff8df5..0000000000
--- a/app/views/layouts/devise.html.erb
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
CASA Volunteer Tracker
-
- <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
- <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
-
- <% if all_casa_admin_signed_in? %>
- <%= javascript_include_tag "all_casa_admin", "data-turbo-track": "reload", defer: true %>
- <% end %>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Welcome Back
-
- Sign in to your Existing account to continue
-
-
-
-
-
-
-
- <%= yield %>
-
-
-
-
-
-
-
-
diff --git a/app/views/layouts/error.html.erb b/app/views/layouts/error.html.erb
new file mode 100644
index 0000000000..5143d0c469
--- /dev/null
+++ b/app/views/layouts/error.html.erb
@@ -0,0 +1,17 @@
+
+<%# Minimal logged-out tailwind shell for standalone/public pages that have no signed-in user
+ or org sidebar (the error test page; a natural home for styled error pages later). No app JS —
+ it loads only the compiled design-system CSS + Figtree. %>
+
+
+
+
+
<%= content_for?(:page_title) ? yield(:page_title) : "CASA" %>
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+ <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
+
+
+ <%= yield %>
+
+
diff --git a/app/views/learning_hour_topics/_form.html.erb b/app/views/learning_hour_topics/_form.html.erb
index 71e5e46ff1..f65f26d5a5 100644
--- a/app/views/learning_hour_topics/_form.html.erb
+++ b/app/views/learning_hour_topics/_form.html.erb
@@ -1,31 +1 @@
-
-
-
-
- <%= form_with(model: learning_hour_topic, local: true) do |form| %>
-
- <%= render "/shared/error_messages", resource: learning_hour_topic %>
-
-
- <%= form.label :name, "Name" %>
- <%= form.text_field :name, class: "form-control", required: true %>
-
-
-
- <%= button_tag(type: "submit", class: "btn-sm main-btn primary-btn btn-hover") do %>
- Submit
- <% end %>
-
- <% end %>
-
-
+<%= render "shared/settings_form", model: learning_hour_topic, title: title %>
diff --git a/app/views/learning_hour_topics/edit.html.erb b/app/views/learning_hour_topics/edit.html.erb
index 4c6ab7fc4f..6a87aff230 100644
--- a/app/views/learning_hour_topics/edit.html.erb
+++ b/app/views/learning_hour_topics/edit.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: {title: "Learning Topic", learning_hour_topic: @learning_hour_topic} %>
+<%= render partial: "form", locals: {title: "Learning topic", learning_hour_topic: @learning_hour_topic} %>
diff --git a/app/views/learning_hour_topics/new.html.erb b/app/views/learning_hour_topics/new.html.erb
index c8749bb6e3..a3d80b0ee1 100644
--- a/app/views/learning_hour_topics/new.html.erb
+++ b/app/views/learning_hour_topics/new.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: {title: "New Learning Topic", learning_hour_topic: @learning_hour_topic} %>
+<%= render partial: "form", locals: {title: "New learning topic", learning_hour_topic: @learning_hour_topic} %>
diff --git a/app/views/learning_hour_types/_form.html.erb b/app/views/learning_hour_types/_form.html.erb
index aca8d234c9..be4f3212d5 100644
--- a/app/views/learning_hour_types/_form.html.erb
+++ b/app/views/learning_hour_types/_form.html.erb
@@ -1,36 +1 @@
-
-
-
-
- <%= form_with(model: learning_hour_type, local: true) do |form| %>
-
- <%= render "/shared/error_messages", resource: learning_hour_type %>
-
-
- <%= form.label :name, "Name" %>
- <%= form.text_field :name, class: "form-control", required: true %>
-
-
-
- <%= form.check_box :active, class: 'form-check-input' %>
- <%= form.label :active, "Active?", class: 'form-check-label' %>
-
-
-
- <%= button_tag(type: "submit", class: "btn-sm main-btn primary-btn btn-hover") do %>
- Submit
- <% end %>
-
- <% end %>
-
-
+<%= render "shared/settings_form", model: learning_hour_type, title: title, show_active: true %>
diff --git a/app/views/learning_hour_types/edit.html.erb b/app/views/learning_hour_types/edit.html.erb
index 3837dfe291..ebb5cf50ee 100644
--- a/app/views/learning_hour_types/edit.html.erb
+++ b/app/views/learning_hour_types/edit.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: {title: "Type of Learning", learning_hour_type: @learning_hour_type} %>
+<%= render partial: "form", locals: {title: "Type of learning", learning_hour_type: @learning_hour_type} %>
diff --git a/app/views/learning_hour_types/new.html.erb b/app/views/learning_hour_types/new.html.erb
index e0ec9349b8..fecb24b13b 100644
--- a/app/views/learning_hour_types/new.html.erb
+++ b/app/views/learning_hour_types/new.html.erb
@@ -1 +1 @@
-<%= render partial: "form", locals: {title: "New Type of Learning", learning_hour_type: @learning_hour_type} %>
+<%= render partial: "form", locals: {title: "New type of learning", learning_hour_type: @learning_hour_type} %>
diff --git a/app/views/learning_hours/_confirm_note.html.erb b/app/views/learning_hours/_confirm_note.html.erb
deleted file mode 100644
index 99263c3b6d..0000000000
--- a/app/views/learning_hours/_confirm_note.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-
diff --git a/app/views/learning_hours/_form.html.erb b/app/views/learning_hours/_form.html.erb
index c38e793ac2..f3b4da8646 100644
--- a/app/views/learning_hours/_form.html.erb
+++ b/app/views/learning_hours/_form.html.erb
@@ -1,71 +1,69 @@
-<%= form_with(model: learning_hour,
- url: learning_hour.persisted? ? learning_hour_path(learning_hour) : learning_hours_path,
- local: true,
- html: { id: "learning-hours-form"}) do |form| %>
-
-
- <%= render "/shared/error_messages", resource: learning_hour %>
-
+<%# Learning-hours new/edit form (casa_app). The Hour(s)/Minute(s) duration inputs are a
+ Tailwind twin of Form::HourMinuteDurationComponent (left untouched — it is still used by the
+ Bootstrap case-contacts form). Field labels + submit-button labels are spec-locked (queued
+ for the sentence-case sweep). Duration fields default to 0 so leaving one blank submits 0. %>
+<% card = "rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6" %>
+<% label_class = "mb-1.5 block text-sm font-medium text-slate-700" %>
+<% input_class = "block w-full rounded-lg border border-slate-300 px-3.5 py-2.5 text-sm text-slate-900 shadow-sm placeholder:text-slate-500 focus:border-brand-500 focus:ring-2 focus:ring-brand-500/30 focus:outline-none" %>
+<% select_class = "block w-full appearance-none rounded-lg border border-slate-300 bg-white py-2.5 pl-3.5 pr-9 text-sm text-slate-900 shadow-sm focus:border-brand-500 focus:ring-2 focus:ring-brand-500/30 focus:outline-none" %>
+<% chevron = "bi bi-chevron-down pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-500" %>
+<%= form_with model: learning_hour, url: learning_hour.persisted? ? learning_hour_path(learning_hour) : learning_hours_path, html: {id: "learning-hours-form"} do |form| %>
+ <%= render "shared/form_errors", resource: learning_hour %>
<%= form.hidden_field :user_id, value: current_user.id %>
-